From 05e59e5fb0064c97a1c10921ecd549f2d4a58565 Mon Sep 17 00:00:00 2001
From: hc <hc@nodka.com>
Date: Wed, 09 Oct 2024 06:14:40 +0000
Subject: [PATCH] add REDIRECT
---
kernel/sound/soc/rockchip/rockchip_pdm.c | 205 ++++++++++++++++++++++++++++++++++++++++----------
1 files changed, 162 insertions(+), 43 deletions(-)
diff --git a/kernel/sound/soc/rockchip/rockchip_pdm.c b/kernel/sound/soc/rockchip/rockchip_pdm.c
index ae529a6..ad5e8b7 100644
--- a/kernel/sound/soc/rockchip/rockchip_pdm.c
+++ b/kernel/sound/soc/rockchip/rockchip_pdm.c
@@ -10,6 +10,7 @@
#include <linux/clk/rockchip.h>
#include <linux/of.h>
#include <linux/of_device.h>
+#include <linux/pinctrl/consumer.h>
#include <linux/pm_runtime.h>
#include <linux/rational.h>
#include <linux/regmap.h>
@@ -30,8 +31,10 @@
#define PDM_FILTER_DELAY_MS_MIN (20)
#define PDM_FILTER_DELAY_MS_MAX (1000)
#define PDM_CLK_SHIFT_PPM_MAX (1000000) /* 1 ppm */
-#define CLK_PPM_MIN (-1000)
-#define CLK_PPM_MAX (1000)
+#define CLK_PPM_MIN (-1000)
+#define CLK_PPM_MAX (1000)
+
+#define QUIRK_ALWAYS_ON BIT(0)
enum rk_pdm_version {
RK_PDM_RK3229,
@@ -48,11 +51,14 @@
struct regmap *regmap;
struct snd_dmaengine_dai_dma_data capture_dma_data;
struct reset_control *reset;
+ struct pinctrl *pinctrl;
+ struct pinctrl_state *clk_state;
unsigned int start_delay_ms;
unsigned int filter_delay_ms;
enum rk_pdm_version version;
unsigned int clk_root_rate;
unsigned int clk_root_initial_rate;
+ unsigned int quirks;
int clk_ppm;
bool clk_calibrate;
};
@@ -92,6 +98,16 @@
{ 4, 12000 },
{ 4, 11025 },
{ 4, 8000 },
+};
+
+static const struct pdm_of_quirks {
+ char *quirk;
+ int id;
+} of_quirks[] = {
+ {
+ .quirk = "rockchip,always-on",
+ .id = QUIRK_ALWAYS_ON,
+ },
};
static unsigned int get_pdm_clk(struct rk_pdm_dev *pdm, unsigned int sr,
@@ -505,8 +521,8 @@
static int rockchip_pdm_start_delay_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol);
- struct rk_pdm_dev *pdm = snd_soc_dai_get_drvdata(dai);
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct rk_pdm_dev *pdm = snd_soc_component_get_drvdata(component);
ucontrol->value.integer.value[0] = pdm->start_delay_ms;
@@ -516,8 +532,8 @@
static int rockchip_pdm_start_delay_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol);
- struct rk_pdm_dev *pdm = snd_soc_dai_get_drvdata(dai);
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct rk_pdm_dev *pdm = snd_soc_component_get_drvdata(component);
if ((ucontrol->value.integer.value[0] < PDM_START_DELAY_MS_MIN) ||
(ucontrol->value.integer.value[0] > PDM_START_DELAY_MS_MAX))
@@ -543,8 +559,8 @@
static int rockchip_pdm_filter_delay_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol);
- struct rk_pdm_dev *pdm = snd_soc_dai_get_drvdata(dai);
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct rk_pdm_dev *pdm = snd_soc_component_get_drvdata(component);
ucontrol->value.integer.value[0] = pdm->filter_delay_ms;
@@ -554,8 +570,8 @@
static int rockchip_pdm_filter_delay_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol);
- struct rk_pdm_dev *pdm = snd_soc_dai_get_drvdata(dai);
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct rk_pdm_dev *pdm = snd_soc_component_get_drvdata(component);
if ((ucontrol->value.integer.value[0] < PDM_FILTER_DELAY_MS_MIN) ||
(ucontrol->value.integer.value[0] > PDM_FILTER_DELAY_MS_MAX))
@@ -566,7 +582,31 @@
return 1;
}
+static const char * const rpaths_text[] = {
+ "From SDI0", "From SDI1", "From SDI2", "From SDI3" };
+
+static SOC_ENUM_SINGLE_DECL(rpath3_enum, PDM_CLK_CTRL, 14, rpaths_text);
+static SOC_ENUM_SINGLE_DECL(rpath2_enum, PDM_CLK_CTRL, 12, rpaths_text);
+static SOC_ENUM_SINGLE_DECL(rpath1_enum, PDM_CLK_CTRL, 10, rpaths_text);
+static SOC_ENUM_SINGLE_DECL(rpath0_enum, PDM_CLK_CTRL, 8, rpaths_text);
+
+static const char * const hpf_cutoff_text[] = {
+ "3.79Hz", "60Hz", "243Hz", "493Hz",
+};
+
+static SOC_ENUM_SINGLE_DECL(hpf_cutoff_enum, PDM_HPF_CTRL,
+ 0, hpf_cutoff_text);
+
static const struct snd_kcontrol_new rockchip_pdm_controls[] = {
+ SOC_ENUM("Receive PATH3 Source Select", rpath3_enum),
+ SOC_ENUM("Receive PATH2 Source Select", rpath2_enum),
+ SOC_ENUM("Receive PATH1 Source Select", rpath1_enum),
+ SOC_ENUM("Receive PATH0 Source Select", rpath0_enum),
+
+ SOC_ENUM("HPF Cutoff", hpf_cutoff_enum),
+ SOC_SINGLE("HPFL Switch", PDM_HPF_CTRL, 3, 1, 0),
+ SOC_SINGLE("HPFR Switch", PDM_HPF_CTRL, 2, 1, 0),
+
{
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = "PDM Start Delay Ms",
@@ -600,8 +640,8 @@
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol);
- struct rk_pdm_dev *pdm = snd_soc_dai_get_drvdata(dai);
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct rk_pdm_dev *pdm = snd_soc_component_get_drvdata(component);
ucontrol->value.integer.value[0] = pdm->clk_ppm;
@@ -611,8 +651,8 @@
static int rockchip_pdm_clk_compensation_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol);
- struct rk_pdm_dev *pdm = snd_soc_dai_get_drvdata(dai);
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct rk_pdm_dev *pdm = snd_soc_component_get_drvdata(component);
int ppm = ucontrol->value.integer.value[0];
@@ -637,10 +677,12 @@
struct rk_pdm_dev *pdm = to_info(dai);
dai->capture_dma_data = &pdm->capture_dma_data;
- snd_soc_add_dai_controls(dai, rockchip_pdm_controls,
- ARRAY_SIZE(rockchip_pdm_controls));
+
if (pdm->clk_calibrate)
- snd_soc_add_dai_controls(dai, &rockchip_pdm_compensation_control, 1);
+ snd_soc_add_component_controls(dai->component,
+ &rockchip_pdm_compensation_control,
+ 1);
+
return 0;
}
@@ -721,7 +763,34 @@
static const struct snd_soc_component_driver rockchip_pdm_component = {
.name = "rockchip-pdm",
+ .controls = rockchip_pdm_controls,
+ .num_controls = ARRAY_SIZE(rockchip_pdm_controls),
};
+
+static int rockchip_pdm_pinctrl_select_clk_state(struct device *dev)
+{
+ struct rk_pdm_dev *pdm = dev_get_drvdata(dev);
+
+ if (IS_ERR_OR_NULL(pdm->pinctrl) || !pdm->clk_state)
+ return 0;
+
+ /*
+ * A necessary delay to make sure the correct
+ * frac div has been applied when resume from
+ * power down.
+ */
+ udelay(10);
+
+ /*
+ * Must disable the clk to avoid clk glitch
+ * when pinctrl switch from gpio to pdm clk.
+ */
+ clk_disable_unprepare(pdm->clk);
+ pinctrl_select_state(pdm->pinctrl, pdm->clk_state);
+ clk_prepare_enable(pdm->clk);
+
+ return 0;
+}
static int rockchip_pdm_runtime_suspend(struct device *dev)
{
@@ -730,6 +799,8 @@
regcache_cache_only(pdm->regmap, true);
clk_disable_unprepare(pdm->clk);
clk_disable_unprepare(pdm->hclk);
+
+ pinctrl_pm_select_idle_state(dev);
return 0;
}
@@ -740,26 +811,31 @@
int ret;
ret = clk_prepare_enable(pdm->clk);
- if (ret) {
- dev_err(pdm->dev, "clock enable failed %d\n", ret);
- return ret;
- }
+ if (ret)
+ goto err_clk;
ret = clk_prepare_enable(pdm->hclk);
- if (ret) {
- dev_err(pdm->dev, "hclock enable failed %d\n", ret);
- return ret;
- }
+ if (ret)
+ goto err_hclk;
- rockchip_pdm_rxctrl(pdm, 0);
regcache_cache_only(pdm->regmap, false);
regcache_mark_dirty(pdm->regmap);
ret = regcache_sync(pdm->regmap);
- if (ret) {
- clk_disable_unprepare(pdm->clk);
- clk_disable_unprepare(pdm->hclk);
- }
+ if (ret)
+ goto err_regmap;
+
+ rockchip_pdm_rxctrl(pdm, 0);
+
+ rockchip_pdm_pinctrl_select_clk_state(dev);
+
return 0;
+
+err_regmap:
+ clk_disable_unprepare(pdm->hclk);
+err_hclk:
+ clk_disable_unprepare(pdm->clk);
+err_clk:
+ return ret;
}
static bool rockchip_pdm_wr_reg(struct device *dev, unsigned int reg)
@@ -894,6 +970,29 @@
return 0;
}
+static int rockchip_pdm_keep_clk_always_on(struct rk_pdm_dev *pdm)
+{
+ pm_runtime_forbid(pdm->dev);
+
+ dev_info(pdm->dev, "CLK-ALWAYS-ON: samplerate: %d\n", PDM_DEFAULT_RATE);
+
+ return 0;
+}
+
+static int rockchip_pdm_parse_quirks(struct rk_pdm_dev *pdm)
+{
+ int ret = 0, i = 0;
+
+ for (i = 0; i < ARRAY_SIZE(of_quirks); i++)
+ if (device_property_read_bool(pdm->dev, of_quirks[i].quirk))
+ pdm->quirks |= of_quirks[i].id;
+
+ if (pdm->quirks & QUIRK_ALWAYS_ON)
+ ret = rockchip_pdm_keep_clk_always_on(pdm);
+
+ return ret;
+}
+
static int rockchip_pdm_probe(struct platform_device *pdev)
{
struct device_node *node = pdev->dev.of_node;
@@ -933,6 +1032,15 @@
pdm->dev = &pdev->dev;
dev_set_drvdata(&pdev->dev, pdm);
+ pdm->pinctrl = devm_pinctrl_get(&pdev->dev);
+ if (!IS_ERR_OR_NULL(pdm->pinctrl)) {
+ pdm->clk_state = pinctrl_lookup_state(pdm->pinctrl, "clk");
+ if (IS_ERR(pdm->clk_state)) {
+ pdm->clk_state = NULL;
+ dev_dbg(pdm->dev, "Have no clk pinctrl state\n");
+ }
+ }
+
pdm->start_delay_ms = PDM_START_DELAY_MS_DEFAULT;
pdm->filter_delay_ms = PDM_FILTER_DELAY_MS_MIN;
@@ -959,6 +1067,27 @@
if (ret)
return ret;
+ rockchip_pdm_set_samplerate(pdm, PDM_DEFAULT_RATE);
+ rockchip_pdm_rxctrl(pdm, 0);
+
+ ret = rockchip_pdm_path_parse(pdm, node);
+ if (ret != 0 && ret != -ENOENT)
+ goto err_clk;
+
+ ret = rockchip_pdm_parse_quirks(pdm);
+ if (ret)
+ goto err_clk;
+
+ /*
+ * MUST: after pm_runtime_enable step, any register R/W
+ * should be wrapped with pm_runtime_get_sync/put.
+ *
+ * Another approach is to enable the regcache true to
+ * avoid access HW registers.
+ *
+ * Alternatively, performing the registers R/W before
+ * pm_runtime_enable is also a good option.
+ */
pm_runtime_enable(&pdev->dev);
if (!pm_runtime_enabled(&pdev->dev)) {
ret = rockchip_pdm_runtime_resume(&pdev->dev);
@@ -975,13 +1104,6 @@
goto err_suspend;
}
- rockchip_pdm_set_samplerate(pdm, PDM_DEFAULT_RATE);
- rockchip_pdm_rxctrl(pdm, 0);
-
- ret = rockchip_pdm_path_parse(pdm, node);
- if (ret != 0 && ret != -ENOENT)
- goto err_suspend;
-
if (of_property_read_bool(node, "rockchip,no-dmaengine")) {
dev_info(&pdev->dev, "Used for Multi-DAI\n");
return 0;
@@ -993,6 +1115,8 @@
goto err_suspend;
}
+ clk_disable_unprepare(pdm->hclk);
+
return 0;
err_suspend:
@@ -1000,7 +1124,7 @@
rockchip_pdm_runtime_suspend(&pdev->dev);
err_pm_disable:
pm_runtime_disable(&pdev->dev);
-
+err_clk:
clk_disable_unprepare(pdm->hclk);
return ret;
@@ -1008,14 +1132,9 @@
static int rockchip_pdm_remove(struct platform_device *pdev)
{
- struct rk_pdm_dev *pdm = dev_get_drvdata(&pdev->dev);
-
pm_runtime_disable(&pdev->dev);
if (!pm_runtime_status_suspended(&pdev->dev))
rockchip_pdm_runtime_suspend(&pdev->dev);
-
- clk_disable_unprepare(pdm->clk);
- clk_disable_unprepare(pdm->hclk);
return 0;
}
--
Gitblit v1.6.2