From f70575805708cabdedea7498aaa3f710fde4d920 Mon Sep 17 00:00:00 2001 From: hc <hc@nodka.com> Date: Wed, 31 Jan 2024 03:29:01 +0000 Subject: [PATCH] add lvds1024*800 --- 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