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.