From e3e12f52b214121840b44c91de5b3e5af5d3eb84 Mon Sep 17 00:00:00 2001 From: hc <hc@nodka.com> Date: Mon, 06 Nov 2023 03:04:41 +0000 Subject: [PATCH] rk3568 rt init --- kernel/sound/soc/rockchip/rockchip_sai.c | 226 ++++++++++++++++++++++++++++++++++++++++++++++++++------ 1 files changed, 201 insertions(+), 25 deletions(-) diff --git a/kernel/sound/soc/rockchip/rockchip_sai.c b/kernel/sound/soc/rockchip/rockchip_sai.c index fa0a8bb..c626142 100644 --- a/kernel/sound/soc/rockchip/rockchip_sai.c +++ b/kernel/sound/soc/rockchip/rockchip_sai.c @@ -24,6 +24,10 @@ #define FW_RATIO_MAX 8 #define FW_RATIO_MIN 1 +#define MAXBURST_PER_FIFO 8 + +#define DEFAULT_FS 48000 +#define QUIRK_ALWAYS_ON BIT(0) enum fpw_mode { FPW_ONE_BCLK_WIDTH, @@ -41,14 +45,28 @@ struct snd_dmaengine_dai_dma_data capture_dma_data; struct snd_dmaengine_dai_dma_data playback_dma_data; struct snd_pcm_substream *substreams[SNDRV_PCM_STREAM_LAST + 1]; + unsigned int tx_lanes; + unsigned int rx_lanes; + unsigned int quirks; enum fpw_mode fpw; - int fw_ratio; + int fw_ratio; bool has_capture; bool has_playback; bool is_master_mode; + bool is_tdm; }; -static int sai_runtime_suspend(struct device *dev) +static const struct sai_of_quirks { + char *quirk; + int id; +} of_quirks[] = { + { + .quirk = "rockchip,always-on", + .id = QUIRK_ALWAYS_ON, + }, +}; + +static int rockchip_sai_runtime_suspend(struct device *dev) { struct rk_sai_dev *sai = dev_get_drvdata(dev); unsigned int val; @@ -68,14 +86,19 @@ regcache_cache_only(sai->regmap, true); clk_disable_unprepare(sai->mclk); + clk_disable_unprepare(sai->hclk); return 0; } -static int sai_runtime_resume(struct device *dev) +static int rockchip_sai_runtime_resume(struct device *dev) { struct rk_sai_dev *sai = dev_get_drvdata(dev); int ret; + + ret = clk_prepare_enable(sai->hclk); + if (ret) + goto err_hclk; ret = clk_prepare_enable(sai->mclk); if (ret) @@ -99,6 +122,8 @@ err_regmap: clk_disable_unprepare(sai->mclk); err_mclk: + clk_disable_unprepare(sai->hclk); +err_hclk: return ret; } @@ -359,19 +384,42 @@ return ret; } +static unsigned int rockchip_sai_lanes_auto(struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct rk_sai_dev *sai = snd_soc_dai_get_drvdata(dai); + unsigned int lanes = 1; + + if (!sai->is_tdm) + lanes = DIV_ROUND_UP(params_channels(params), 2); + + return lanes; +} + static int rockchip_sai_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { struct rk_sai_dev *sai = snd_soc_dai_get_drvdata(dai); + struct snd_dmaengine_dai_dma_data *dma_data; unsigned int mclk_rate, bclk_rate, div_bclk; unsigned int ch_per_lane, lanes, slot_width; unsigned int val, fscr, reg; - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + dma_data = snd_soc_dai_get_dma_data(dai, substream); + dma_data->maxburst = MAXBURST_PER_FIFO * params_channels(params) / 2; + + lanes = rockchip_sai_lanes_auto(params, dai); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { reg = SAI_TXCR; - else + if (sai->tx_lanes) + lanes = sai->tx_lanes; + } else { reg = SAI_RXCR; + if (sai->rx_lanes) + lanes = sai->rx_lanes; + } switch (params_format(params)) { case SNDRV_PCM_FORMAT_S8: @@ -391,12 +439,13 @@ return -EINVAL; } - regmap_update_bits(sai->regmap, reg, SAI_XCR_VDW_MASK, val); + val |= SAI_XCR_CSR(lanes); + + regmap_update_bits(sai->regmap, reg, SAI_XCR_VDW_MASK | SAI_XCR_CSR_MASK, val); regmap_read(sai->regmap, reg, &val); slot_width = SAI_XCR_SBW_V(val); - lanes = SAI_XCR_CSR_V(val); ch_per_lane = params_channels(params) / lanes; regmap_update_bits(sai->regmap, reg, SAI_XCR_SNB_MASK, @@ -525,6 +574,8 @@ regmap_update_bits(sai->regmap, SAI_RXCR, SAI_XCR_SBW_MASK, SAI_XCR_SBW(slot_width)); pm_runtime_put(dai->dev); + + sai->is_tdm = true; return 0; } @@ -699,7 +750,7 @@ sai->playback_dma_data.addr = res->start + SAI_TXDR; sai->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; - sai->playback_dma_data.maxburst = 8; + sai->playback_dma_data.maxburst = MAXBURST_PER_FIFO; } if (sai->has_capture) { @@ -714,7 +765,7 @@ sai->capture_dma_data.addr = res->start + SAI_RXDR; sai->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; - sai->capture_dma_data.maxburst = 8; + sai->capture_dma_data.maxburst = MAXBURST_PER_FIFO; } regmap_update_bits(sai->regmap, SAI_DMACR, SAI_DMACR_TDL_MASK, @@ -728,8 +779,8 @@ return 0; } -static const char * const tcsr_text[] = { "SDOx1", "SDOx2", "SDOx3", "SDOx4" }; -static const char * const rcsr_text[] = { "SDIx1", "SDIx2", "SDIx3", "SDIx4" }; +static const char * const tx_lanes_text[] = { "Auto", "SDOx1", "SDOx2", "SDOx3", "SDOx4" }; +static const char * const rx_lanes_text[] = { "Auto", "SDIx1", "SDIx2", "SDIx3", "SDIx4" }; static const char * const edge_text[] = { "Rising Edge", "Dual Edge" }; static const char * const edge_shift_text[] = { "Normal", "Shift 1 Edge" }; @@ -771,7 +822,8 @@ /* TXCR */ static SOC_ENUM_SINGLE_DECL(tsft_enum, SAI_TXCR, 22, edge_shift_text); -static SOC_ENUM_SINGLE_DECL(tcsr_enum, SAI_TXCR, 20, tcsr_text); +static const struct soc_enum tx_lanes_enum = + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(tx_lanes_text), tx_lanes_text); static SOC_ENUM_SINGLE_DECL(tsjm_enum, SAI_TXCR, 19, sjm_text); static SOC_ENUM_SINGLE_DECL(tfbm_enum, SAI_TXCR, 18, fbm_text); static SOC_ENUM_SINGLE_DECL(tvdj_enum, SAI_TXCR, 10, vdj_text); @@ -786,7 +838,8 @@ /* RXCR */ static SOC_ENUM_SINGLE_DECL(rsft_enum, SAI_RXCR, 22, edge_shift_text); -static SOC_ENUM_SINGLE_DECL(rcsr_enum, SAI_RXCR, 20, rcsr_text); +static const struct soc_enum rx_lanes_enum = + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(rx_lanes_text), rx_lanes_text); static SOC_ENUM_SINGLE_DECL(rsjm_enum, SAI_RXCR, 19, sjm_text); static SOC_ENUM_SINGLE_DECL(rfbm_enum, SAI_RXCR, 18, fbm_text); static SOC_ENUM_SINGLE_DECL(rvdj_enum, SAI_RXCR, 10, vdj_text); @@ -874,19 +927,75 @@ return 1; } +static int rockchip_sai_tx_lanes_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct rk_sai_dev *sai = snd_soc_component_get_drvdata(component); + + ucontrol->value.enumerated.item[0] = sai->tx_lanes; + + return 0; +} + +static int rockchip_sai_tx_lanes_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct rk_sai_dev *sai = snd_soc_component_get_drvdata(component); + int num; + + num = ucontrol->value.enumerated.item[0]; + if (num >= ARRAY_SIZE(tx_lanes_text)) + return -EINVAL; + + sai->tx_lanes = num; + + return 1; +} + +static int rockchip_sai_rx_lanes_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct rk_sai_dev *sai = snd_soc_component_get_drvdata(component); + + ucontrol->value.enumerated.item[0] = sai->rx_lanes; + + return 0; +} + +static int rockchip_sai_rx_lanes_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct rk_sai_dev *sai = snd_soc_component_get_drvdata(component); + int num; + + num = ucontrol->value.enumerated.item[0]; + if (num >= ARRAY_SIZE(rx_lanes_text)) + return -EINVAL; + + sai->rx_lanes = num; + + return 1; +} + static DECLARE_TLV_DB_SCALE(fs_shift_tlv, 0, 8192, 0); static const struct snd_kcontrol_new rockchip_sai_controls[] = { SOC_ENUM("Transmit Edge Shift", tsft_enum), - SOC_ENUM("Transmit SDOx Select", tcsr_enum), + SOC_ENUM_EXT("Transmit SDOx Select", tx_lanes_enum, + rockchip_sai_tx_lanes_get, rockchip_sai_tx_lanes_put), SOC_ENUM("Transmit Store Justified Mode", tsjm_enum), SOC_ENUM("Transmit First Bit Mode", tfbm_enum), SOC_ENUM("Transmit Valid Data Justified", tvdj_enum), SOC_ENUM("Transmit Slot Bit Width", tsbw_enum), SOC_ENUM("Receive Edge Shift", rsft_enum), - SOC_ENUM("Receive SDIx Select", rcsr_enum), + SOC_ENUM_EXT("Receive SDIx Select", rx_lanes_enum, + rockchip_sai_rx_lanes_get, rockchip_sai_rx_lanes_put), SOC_ENUM("Receive Store Justified Mode", rsjm_enum), SOC_ENUM("Receive First Bit Mode", rfbm_enum), SOC_ENUM("Receive Valid Data Justified", rvdj_enum), @@ -966,6 +1075,50 @@ return IRQ_HANDLED; } +static int rockchip_sai_keep_clk_always_on(struct rk_sai_dev *sai) +{ + unsigned int mclk_rate, bclk_rate, div_bclk; + + sai->is_master_mode = true; + + /* init I2S fmt default */ + rockchip_sai_fmt_create(sai, SND_SOC_DAIFMT_I2S); + + regmap_update_bits(sai->regmap, SAI_FSCR, + SAI_FSCR_FW_MASK | + SAI_FSCR_FPW_MASK, + SAI_FSCR_FW(64) | + SAI_FSCR_FPW(32)); + + mclk_rate = clk_get_rate(sai->mclk); + bclk_rate = DEFAULT_FS * 64; + div_bclk = DIV_ROUND_CLOSEST(mclk_rate, bclk_rate); + + regmap_update_bits(sai->regmap, SAI_CKR, SAI_CKR_MDIV_MASK, + SAI_CKR_MDIV(div_bclk)); + + pm_runtime_forbid(sai->dev); + + dev_info(sai->dev, "CLK-ALWAYS-ON: mclk: %d, bclk: %d, fsync: %d\n", + mclk_rate, bclk_rate, DEFAULT_FS); + + return 0; +} + +static int rockchip_sai_parse_quirks(struct rk_sai_dev *sai) +{ + int ret = 0, i = 0; + + for (i = 0; i < ARRAY_SIZE(of_quirks); i++) + if (of_property_read_bool(sai->dev->of_node, of_quirks[i].quirk)) + sai->quirks |= of_quirks[i].id; + + if (sai->quirks & QUIRK_ALWAYS_ON) + ret = rockchip_sai_keep_clk_always_on(sai); + + return ret; +} + static int rockchip_sai_probe(struct platform_device *pdev) { struct device_node *node = pdev->dev.of_node; @@ -1022,13 +1175,13 @@ return PTR_ERR(sai->hclk); } - ret = clk_prepare_enable(sai->hclk); + ret = rockchip_sai_parse_quirks(sai); if (ret) return ret; pm_runtime_enable(&pdev->dev); if (!pm_runtime_enabled(&pdev->dev)) { - ret = sai_runtime_resume(&pdev->dev); + ret = rockchip_sai_runtime_resume(&pdev->dev); if (ret) goto err_runtime_disable; } @@ -1051,29 +1204,52 @@ err_runtime_suspend: if (!pm_runtime_status_suspended(&pdev->dev)) - sai_runtime_suspend(&pdev->dev); + rockchip_sai_runtime_suspend(&pdev->dev); err_runtime_disable: pm_runtime_disable(&pdev->dev); - clk_disable_unprepare(sai->hclk); return ret; } static int rockchip_sai_remove(struct platform_device *pdev) { - struct rk_sai_dev *sai = dev_get_drvdata(&pdev->dev); - pm_runtime_disable(&pdev->dev); if (!pm_runtime_status_suspended(&pdev->dev)) - sai_runtime_suspend(&pdev->dev); - - clk_disable_unprepare(sai->hclk); + rockchip_sai_runtime_suspend(&pdev->dev); return 0; } +#ifdef CONFIG_PM_SLEEP +static int rockchip_sai_suspend(struct device *dev) +{ + struct rk_sai_dev *sai = dev_get_drvdata(dev); + + regcache_mark_dirty(sai->regmap); + + return 0; +} + +static int rockchip_sai_resume(struct device *dev) +{ + struct rk_sai_dev *sai = dev_get_drvdata(dev); + int ret = pm_runtime_get_sync(dev); + + if (ret < 0) { + pm_runtime_put_noidle(dev); + return ret; + } + + ret = regcache_sync(sai->regmap); + pm_runtime_put(dev); + + return ret; +} +#endif /* CONFIG_PM_SLEEP */ + static const struct dev_pm_ops rockchip_sai_pm_ops = { - SET_RUNTIME_PM_OPS(sai_runtime_suspend, sai_runtime_resume, NULL) + SET_RUNTIME_PM_OPS(rockchip_sai_runtime_suspend, rockchip_sai_runtime_resume, NULL) + SET_SYSTEM_SLEEP_PM_OPS(rockchip_sai_suspend, rockchip_sai_resume) }; static struct platform_driver rockchip_sai_driver = { -- Gitblit v1.6.2