From 54b0bc7bac9ebd548fe8155f99f823fa56edc336 Mon Sep 17 00:00:00 2001 From: Sugar Zhang Date: Tue, 28 Mar 2023 22:15:51 +0800 Subject: [PATCH] ASoC: rockchip: sai: Add support for CLK-ALWAYS-ON quirks This patch add support for keeping BCLK / FSYNC always on. it's required by some devices, such as HDMI, PA, etc. For example: on HDMI situation There are some TVs require maintaining N/CTS packets or AUDS packets to keep audio logic active, otherwise, the first tone may be lost. In order to optimize the user experience, we need to ensure continuous transmission of N/CTS and AUDS packets from the HDMI-TX, so that the SINK TV devices can maintain audio logic activation, promptly process audio data, and achieve the completeness of the first tone. We init a 48k I2S-STANDARD clock timing as default. Change-Id: I298b0ad2d53bdc41927f567c2af481f2a0bd5422 Signed-off-by: Sugar Zhang --- sound/soc/rockchip/rockchip_sai.c | 62 +++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/sound/soc/rockchip/rockchip_sai.c b/sound/soc/rockchip/rockchip_sai.c index 729778676f84..ac63cb110221 100644 --- a/sound/soc/rockchip/rockchip_sai.c +++ b/sound/soc/rockchip/rockchip_sai.c @@ -26,6 +26,9 @@ #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, FPW_ONE_SLOT_WIDTH, @@ -44,6 +47,7 @@ struct rk_sai_dev { 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; bool has_capture; @@ -53,6 +57,16 @@ struct rk_sai_dev { bool is_clk_auto; }; +static const struct sai_of_quirks { + char *quirk; + int id; +} of_quirks[] = { + { + .quirk = "rockchip,always-on", + .id = QUIRK_ALWAYS_ON, + }, +}; + static int sai_runtime_suspend(struct device *dev) { struct rk_sai_dev *sai = dev_get_drvdata(dev); @@ -1154,6 +1168,50 @@ static irqreturn_t rockchip_sai_isr(int irq, void *devid) 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 (device_property_read_bool(sai->dev, 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; @@ -1212,6 +1270,10 @@ static int rockchip_sai_probe(struct platform_device *pdev) return PTR_ERR(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);