From c9745283a2edd49e829aec21716245ecf40cf8ea Mon Sep 17 00:00:00 2001 From: Sugar Zhang Date: Thu, 4 May 2023 19:05:14 +0800 Subject: [PATCH] ASoC: rockchip: i2s: 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. Signed-off-by: Sugar Zhang Change-Id: Icdf2101c4117bc0ec4c05343e7317477cc02d6c0 --- sound/soc/rockchip/rockchip_i2s.c | 69 +++++++++++++++++++++++++++++-- 1 file changed, 66 insertions(+), 3 deletions(-) diff --git a/sound/soc/rockchip/rockchip_i2s.c b/sound/soc/rockchip/rockchip_i2s.c index 7d7265e3f9ae..fea2fab50772 100644 --- a/sound/soc/rockchip/rockchip_i2s.c +++ b/sound/soc/rockchip/rockchip_i2s.c @@ -28,6 +28,11 @@ #define CLK_PPM_MIN (-1000) #define CLK_PPM_MAX (1000) +#define DEFAULT_MCLK_FS 256 +#define DEFAULT_FS 48000 + +#define QUIRK_ALWAYS_ON BIT(0) + struct rk_i2s_pins { u32 reg_offset; u32 shift; @@ -70,6 +75,17 @@ struct rk_i2s_dev { int clk_ppm; bool mclk_calibrate; + unsigned int quirks; +}; + +static struct i2s_of_quirks { + char *quirk; + int id; +} of_quirks[] = { + { + .quirk = "rockchip,always-on", + .id = QUIRK_ALWAYS_ON, + }, }; static int i2s_pinctrl_select_bclk_on(struct rk_i2s_dev *i2s) @@ -196,7 +212,7 @@ static int rockchip_snd_txctrl(struct rk_i2s_dev *i2s, int on) if (ret < 0) goto end; - if (!i2s->rx_start) { + if (!i2s->rx_start && !(i2s->quirks & QUIRK_ALWAYS_ON)) { ret = regmap_update_bits(i2s->regmap, I2S_XFER, I2S_XFER_TXS_START | I2S_XFER_RXS_START, I2S_XFER_TXS_STOP | I2S_XFER_RXS_STOP); @@ -241,7 +257,7 @@ static int rockchip_snd_rxctrl(struct rk_i2s_dev *i2s, int on) if (ret < 0) goto end; - if (!i2s->tx_start) { + if (!i2s->tx_start && !(i2s->quirks & QUIRK_ALWAYS_ON)) { ret = regmap_update_bits(i2s->regmap, I2S_XFER, I2S_XFER_TXS_START | I2S_XFER_RXS_START, I2S_XFER_TXS_STOP | I2S_XFER_RXS_STOP); @@ -893,6 +909,38 @@ static int rockchip_i2s_init_dai(struct rk_i2s_dev *i2s, struct resource *res, return 0; } +static int rockchip_i2s_keep_clk_always_on(struct rk_i2s_dev *i2s) +{ + unsigned int mclk_rate = DEFAULT_FS * DEFAULT_MCLK_FS; + unsigned int bclk_rate = i2s->bclk_ratio * DEFAULT_FS; + unsigned int div_lrck = i2s->bclk_ratio; + unsigned int div_bclk; + + div_bclk = DIV_ROUND_CLOSEST(mclk_rate, bclk_rate); + + /* assign generic freq */ + clk_set_rate(i2s->mclk, mclk_rate); + + regmap_update_bits(i2s->regmap, I2S_CKR, + I2S_CKR_MDIV_MASK, + I2S_CKR_MDIV(div_bclk)); + regmap_update_bits(i2s->regmap, I2S_CKR, + I2S_CKR_TSD_MASK | + I2S_CKR_RSD_MASK, + I2S_CKR_TSD(div_lrck) | + I2S_CKR_RSD(div_lrck)); + regmap_update_bits(i2s->regmap, I2S_XFER, + I2S_XFER_TXS_START | I2S_XFER_RXS_START, + I2S_XFER_TXS_START | I2S_XFER_RXS_START); + + pm_runtime_forbid(i2s->dev); + + dev_info(i2s->dev, "CLK-ALWAYS-ON: mclk: %d, bclk: %d, fsync: %d\n", + mclk_rate, bclk_rate, DEFAULT_FS); + + return 0; +} + static int rockchip_i2s_probe(struct platform_device *pdev) { struct device_node *node = pdev->dev.of_node; @@ -901,7 +949,7 @@ static int rockchip_i2s_probe(struct platform_device *pdev) struct snd_soc_dai_driver *dai; struct resource *res; void __iomem *regs; - int ret; + int ret, i; i2s = devm_kzalloc(&pdev->dev, sizeof(*i2s), GFP_KERNEL); if (!i2s) @@ -919,6 +967,10 @@ static int rockchip_i2s_probe(struct platform_device *pdev) i2s->pins = of_id->data; } + for (i = 0; i < ARRAY_SIZE(of_quirks); i++) + if (device_property_read_bool(i2s->dev, of_quirks[i].quirk)) + i2s->quirks |= of_quirks[i].id; + regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res); if (IS_ERR(regs)) return PTR_ERR(regs); @@ -983,6 +1035,17 @@ static int rockchip_i2s_probe(struct platform_device *pdev) if (ret) goto err_clk; + /* + * CLK_ALWAYS_ON should be placed after all registers write done, + * because this situation will enable XFER bit which will make + * some registers(depend on XFER) write failed. + */ + if (i2s->quirks & QUIRK_ALWAYS_ON) { + ret = rockchip_i2s_keep_clk_always_on(i2s); + if (ret) + goto err_clk; + } + /* * MUST: after pm_runtime_enable step, any register R/W * should be wrapped with pm_runtime_get_sync/put.