From 1f93a7dfd1f8d5ff7a5c53246c7534fe2332d6f4 Mon Sep 17 00:00:00 2001 From: hc <hc@nodka.com> Date: Mon, 11 Dec 2023 02:46:07 +0000 Subject: [PATCH] add audio --- kernel/sound/soc/rockchip/rockchip_sai.c | 251 ++++++++++++++++++++++++++++++++++++++++++++++---- 1 files changed, 230 insertions(+), 21 deletions(-) diff --git a/kernel/sound/soc/rockchip/rockchip_sai.c b/kernel/sound/soc/rockchip/rockchip_sai.c index c626142..3e50ba0 100644 --- a/kernel/sound/soc/rockchip/rockchip_sai.c +++ b/kernel/sound/soc/rockchip/rockchip_sai.c @@ -27,6 +27,8 @@ #define MAXBURST_PER_FIFO 8 #define DEFAULT_FS 48000 +#define TIMEOUT_US 1000 +#define WAIT_TIME_MS_MAX 10000 #define QUIRK_ALWAYS_ON BIT(0) enum fpw_mode { @@ -45,6 +47,7 @@ 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 wait_time[SNDRV_PCM_STREAM_LAST + 1]; unsigned int tx_lanes; unsigned int rx_lanes; unsigned int quirks; @@ -54,6 +57,7 @@ bool has_playback; bool is_master_mode; bool is_tdm; + bool is_clk_auto; }; static const struct sai_of_quirks { @@ -80,11 +84,26 @@ SAI_XFER_FSS_DIS); ret = regmap_read_poll_timeout_atomic(sai->regmap, SAI_XFER, val, - (val & SAI_XFER_FS_IDLE), 10, 100); + (val & SAI_XFER_FS_IDLE), 10, TIMEOUT_US); if (ret < 0) dev_warn(sai->dev, "Failed to idle FS\n"); regcache_cache_only(sai->regmap, true); + /* + * After FS idle, should wait at least 2 BCLK cycle to make sure + * the CLK gate operation done, and then disable mclk. + * + * Otherwise, the BCLK is still ungated. once the mclk is enabled, + * there maybe a risk that a few BCLK cycle leak. especially for + * low speed situation, such as 8k samplerate. + * + * The best way is to use delay per samplerate, but, the max time + * is quite a tiny value, so, let's make it simple to use the max + * time. + * + * The max BCLK cycle time is: 31us @ 8K-8Bit (64K BCLK) + */ + udelay(40); clk_disable_unprepare(sai->mclk); clk_disable_unprepare(sai->hclk); @@ -110,7 +129,7 @@ if (ret) goto err_regmap; - if (sai->is_master_mode) + if (sai->quirks & QUIRK_ALWAYS_ON && sai->is_master_mode) regmap_update_bits(sai->regmap, SAI_XFER, SAI_XFER_CLK_MASK | SAI_XFER_FSS_MASK, @@ -203,7 +222,7 @@ regmap_update_bits(sai->regmap, SAI_CLR, clr, clr); ret = regmap_read_poll_timeout_atomic(sai->regmap, SAI_CLR, val, - !(val & clr), 10, 100); + !(val & clr), 10, TIMEOUT_US); if (ret < 0) { dev_warn(sai->dev, "Failed to clear %u\n", clr); goto reset; @@ -249,7 +268,7 @@ regmap_update_bits(sai->regmap, SAI_XFER, msk, val); ret = regmap_read_poll_timeout_atomic(sai->regmap, SAI_XFER, val, - (val & idle), 10, 100); + (val & idle), 10, TIMEOUT_US); if (ret < 0) dev_warn(sai->dev, "Failed to idle stream %d\n", stream); @@ -433,6 +452,7 @@ val = SAI_XCR_VDW(24); break; case SNDRV_PCM_FORMAT_S32_LE: + case SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE: val = SAI_XCR_VDW(32); break; default: @@ -473,6 +493,8 @@ if (sai->is_master_mode) { bclk_rate = sai->fw_ratio * slot_width * ch_per_lane * params_rate(params); + if (sai->is_clk_auto) + clk_set_rate(sai->mclk, bclk_rate); mclk_rate = clk_get_rate(sai->mclk); if (mclk_rate < bclk_rate) { dev_err(sai->dev, "Mismatch mclk: %u, expected %u at least\n", @@ -484,6 +506,22 @@ regmap_update_bits(sai->regmap, SAI_CKR, SAI_CKR_MDIV_MASK, SAI_CKR_MDIV(div_bclk)); + /* + * Should wait for one BCLK ready after DIV and then ungate + * output clk to achieve the clean clk. + * + * The best way is to use delay per samplerate, but, the max time + * is quite a tiny value, so, let's make it simple to use the max + * time. + * + * The max BCLK cycle time is: 15.6us @ 8K-8Bit (64K BCLK) + */ + udelay(20); + regmap_update_bits(sai->regmap, SAI_XFER, + SAI_XFER_CLK_MASK | + SAI_XFER_FSS_MASK, + SAI_XFER_CLK_EN | + SAI_XFER_FSS_EN); } return 0; @@ -520,7 +558,7 @@ struct rk_sai_dev *sai = snd_soc_dai_get_drvdata(dai); int ret; - if (!freq) + if (!freq || sai->is_clk_auto) return 0; ret = clk_set_rate(sai->mclk, freq); @@ -545,11 +583,15 @@ struct snd_soc_dai *dai) { struct rk_sai_dev *sai = snd_soc_dai_get_drvdata(dai); + int stream = substream->stream; - if (sai->substreams[substream->stream]) + if (sai->substreams[stream]) return -EBUSY; - sai->substreams[substream->stream] = substream; + if (sai->wait_time[stream]) + substream->wait_time = msecs_to_jiffies(sai->wait_time[stream]); + + sai->substreams[stream] = substream; return 0; } @@ -746,7 +788,8 @@ dai->playback.formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | - SNDRV_PCM_FMTBIT_S32_LE; + SNDRV_PCM_FMTBIT_S32_LE | + SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE; sai->playback_dma_data.addr = res->start + SAI_TXDR; sai->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; @@ -761,7 +804,8 @@ dai->capture.formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | - SNDRV_PCM_FMTBIT_S32_LE; + SNDRV_PCM_FMTBIT_S32_LE | + SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE; sai->capture_dma_data.addr = res->start + SAI_RXDR; sai->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; @@ -794,8 +838,8 @@ static const char * const vdj_text[] = { "Right J", "Left J" }; static const char * const sbw_text[] = { - " 0", " 0", " 0", " 0", " 0", " 0", " 0", " 8", - " 9", "10", "11", "12", "13", "14", "15", "16", + "0", "0", "0", "0", "0", "0", "0", "8", + "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "30", "31", "32", }; @@ -803,7 +847,7 @@ static DECLARE_TLV_DB_SCALE(rmss_tlv, 0, 128, 0); -static const char * const mss_text[] = { "Master", "Slave" }; +static const char * const mss_text[] = { "Slave", "Master" }; static const char * const ckp_text[] = { "Normal", "Inverted" }; @@ -850,7 +894,8 @@ static SOC_ENUM_SINGLE_DECL(tmono_switch, SAI_MONO_CR, 0, mono_text); /* CKR */ -static SOC_ENUM_SINGLE_DECL(mss_switch, SAI_CKR, 2, mss_text); +static const struct soc_enum mss_switch = + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(mss_text), mss_text); static SOC_ENUM_SINGLE_DECL(sp_switch, SAI_CKR, 1, ckp_text); static SOC_ENUM_SINGLE_DECL(fp_switch, SAI_CKR, 0, ckp_text); @@ -981,6 +1026,154 @@ return 1; } +static int rockchip_sai_mss_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->is_master_mode; + + return 0; +} + +static int rockchip_sai_mss_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); + bool mss; + + /* MUST: do not update mode while stream is running */ + if (snd_soc_component_active(component)) + return -EPERM; + + mss = !!ucontrol->value.enumerated.item[0]; + if (mss == sai->is_master_mode) + return 0; + + sai->is_master_mode = mss; + + pm_runtime_get_sync(sai->dev); + if (sai->is_master_mode) { + /* Switch from Slave to Master */ + regmap_update_bits(sai->regmap, SAI_CKR, + SAI_CKR_MSS_MASK, + SAI_CKR_MSS_MASTER); + regmap_update_bits(sai->regmap, SAI_XFER, + SAI_XFER_CLK_MASK | + SAI_XFER_FSS_MASK, + SAI_XFER_CLK_EN | + SAI_XFER_FSS_EN); + } else { + /* Switch from Master to Slave */ + regmap_update_bits(sai->regmap, SAI_CKR, + SAI_CKR_MSS_MASK, + SAI_CKR_MSS_SLAVE); + regmap_update_bits(sai->regmap, SAI_XFER, + SAI_XFER_CLK_MASK | + SAI_XFER_FSS_MASK, + SAI_XFER_CLK_DIS | + SAI_XFER_FSS_DIS); + } + pm_runtime_put(sai->dev); + + return 1; +} + +static int rockchip_sai_clk_auto_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.integer.value[0] = sai->is_clk_auto; + + return 0; +} + +static int rockchip_sai_clk_auto_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); + bool clk_auto = ucontrol->value.integer.value[0]; + + if (clk_auto == sai->is_clk_auto) + return 0; + + sai->is_clk_auto = clk_auto; + + return 1; +} + +static int rockchip_sai_wait_time_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = WAIT_TIME_MS_MAX; + uinfo->value.integer.step = 1; + + return 0; +} + +static int rockchip_sai_rd_wait_time_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.integer.value[0] = sai->wait_time[SNDRV_PCM_STREAM_CAPTURE]; + + return 0; +} + +static int rockchip_sai_rd_wait_time_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); + + if (ucontrol->value.integer.value[0] > WAIT_TIME_MS_MAX) + return -EINVAL; + + sai->wait_time[SNDRV_PCM_STREAM_CAPTURE] = ucontrol->value.integer.value[0]; + + return 1; +} + +static int rockchip_sai_wr_wait_time_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.integer.value[0] = sai->wait_time[SNDRV_PCM_STREAM_PLAYBACK]; + + return 0; +} + +static int rockchip_sai_wr_wait_time_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); + + if (ucontrol->value.integer.value[0] > WAIT_TIME_MS_MAX) + return -EINVAL; + + sai->wait_time[SNDRV_PCM_STREAM_PLAYBACK] = ucontrol->value.integer.value[0]; + + return 1; +} + +#define SAI_PCM_WAIT_TIME(xname, xhandler_get, xhandler_put) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_PCM, .name = xname, \ + .info = rockchip_sai_wait_time_info, \ + .get = xhandler_get, .put = xhandler_put } + static DECLARE_TLV_DB_SCALE(fs_shift_tlv, 0, 8192, 0); static const struct snd_kcontrol_new rockchip_sai_controls[] = { @@ -1012,7 +1205,8 @@ SOC_ENUM("Receive Mono Switch", rmono_switch), SOC_ENUM("Transmit Mono Switch", tmono_switch), - SOC_ENUM("Master / Slave Mode Select", mss_switch), + SOC_ENUM_EXT("Master / Slave Mode Select", mss_switch, + rockchip_sai_mss_get, rockchip_sai_mss_put), SOC_ENUM("Sclk Polarity", sp_switch), SOC_ENUM("Frame Sync Polarity", fp_switch), @@ -1039,6 +1233,17 @@ 0, 8192, 0, fs_shift_tlv), SOC_SINGLE_TLV("Receive Frame Shift Select", SAI_RX_SHIFT, 0, 8192, 0, fs_shift_tlv), + + SOC_SINGLE_BOOL_EXT("Clk Auto Switch", 0, + rockchip_sai_clk_auto_get, + rockchip_sai_clk_auto_put), + + SAI_PCM_WAIT_TIME("PCM Read Wait Time MS", + rockchip_sai_rd_wait_time_get, + rockchip_sai_rd_wait_time_put), + SAI_PCM_WAIT_TIME("PCM Write Wait Time MS", + rockchip_sai_wr_wait_time_get, + rockchip_sai_wr_wait_time_put), }; static const struct snd_soc_component_driver rockchip_sai_component = { @@ -1110,7 +1315,7 @@ 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)) + if (device_property_read_bool(sai->dev, of_quirks[i].quirk)) sai->quirks |= of_quirks[i].id; if (sai->quirks & QUIRK_ALWAYS_ON) @@ -1134,6 +1339,8 @@ sai->dev = &pdev->dev; sai->fw_ratio = 1; + /* match to register default */ + sai->is_master_mode = true; dev_set_drvdata(&pdev->dev, sai); sai->rst_h = devm_reset_control_get_optional_exclusive(&pdev->dev, "h"); @@ -1153,7 +1360,7 @@ if (IS_ERR(sai->regmap)) return PTR_ERR(sai->regmap); - irq = platform_get_irq(pdev, 0); + irq = platform_get_irq_optional(pdev, 0); if (irq > 0) { ret = devm_request_irq(&pdev->dev, irq, rockchip_sai_isr, IRQF_SHARED, node->name, sai); @@ -1196,6 +1403,11 @@ if (ret) goto err_runtime_suspend; + if (device_property_read_bool(&pdev->dev, "rockchip,no-dmaengine")) { + dev_info(&pdev->dev, "Used for Multi-DAI\n"); + return 0; + } + ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0); if (ret) goto err_runtime_suspend; @@ -1233,13 +1445,10 @@ static int rockchip_sai_resume(struct device *dev) { struct rk_sai_dev *sai = dev_get_drvdata(dev); - int ret = pm_runtime_get_sync(dev); + int ret = pm_runtime_resume_and_get(dev); - if (ret < 0) { - pm_runtime_put_noidle(dev); + if (ret < 0) return ret; - } - ret = regcache_sync(sai->regmap); pm_runtime_put(dev); -- Gitblit v1.6.2