diff options
author | Xingyu Wu <xingyu.wu@starfivetech.com> | 2022-11-22 06:42:34 +0300 |
---|---|---|
committer | Xingyu Wu <xingyu.wu@starfivetech.com> | 2022-12-08 12:52:48 +0300 |
commit | 1f6659ddf77ca6bf6697b4f6c0b52f023aa76099 (patch) | |
tree | b360ce47bc3be0ab2f9f4b63212de1515377a2fe | |
parent | dd20de9a3661420c07075d9cf8c95fbf6bb08755 (diff) | |
download | linux-1f6659ddf77ca6bf6697b4f6c0b52f023aa76099.tar.xz |
sound:starfive:Move playback and capture driver as slave to starfive I2S
Move playback and capture driver as slave from snps I2S merge to starfive I2S.
Signed-off-by: Xingyu Wu <xingyu.wu@starfivetech.com>
-rw-r--r-- | arch/riscv/configs/starfive_jh7110_defconfig | 2 | ||||
-rwxr-xr-x | sound/soc/dwc/Kconfig | 8 | ||||
-rw-r--r-- | sound/soc/dwc/dwc-i2s.c | 2 | ||||
-rw-r--r-- | sound/soc/starfive/starfive_i2s.c | 321 | ||||
-rw-r--r-- | sound/soc/starfive/starfive_i2s.h | 4 |
5 files changed, 265 insertions, 72 deletions
diff --git a/arch/riscv/configs/starfive_jh7110_defconfig b/arch/riscv/configs/starfive_jh7110_defconfig index d74cb1d86966..aefef68d1a88 100644 --- a/arch/riscv/configs/starfive_jh7110_defconfig +++ b/arch/riscv/configs/starfive_jh7110_defconfig @@ -207,8 +207,6 @@ CONFIG_SOUND=y CONFIG_SND=y CONFIG_SND_USB_AUDIO=y CONFIG_SND_SOC=y -CONFIG_SND_DESIGNWARE_I2S=y -CONFIG_SND_DESIGNWARE_I2S_STARFIVE_JH7110=y CONFIG_SND_SOC_STARFIVE=y CONFIG_SND_SOC_STARFIVE_PWMDAC=y CONFIG_SND_SOC_STARFIVE_PDM=y diff --git a/sound/soc/dwc/Kconfig b/sound/soc/dwc/Kconfig index 6b249e16dc08..71a58f7ac13a 100755 --- a/sound/soc/dwc/Kconfig +++ b/sound/soc/dwc/Kconfig @@ -18,11 +18,3 @@ config SND_DESIGNWARE_PCM This functionality is specially suited for I2S devices that don't have DMA support. -config SND_DESIGNWARE_I2S_STARFIVE_JH7110 - bool "Synopsys I2S Device Driver for Starfive JH7110 SOC platform" - depends on SND_DESIGNWARE_I2S - help - Say Y or N if you want to use on Starfive JH7110 SOC platform. - - This functionality is specially suited for I2S devices that run on - Starfive JH7110 SOC platform. diff --git a/sound/soc/dwc/dwc-i2s.c b/sound/soc/dwc/dwc-i2s.c index 2fd832021de7..b53e6c8e4bba 100644 --- a/sound/soc/dwc/dwc-i2s.c +++ b/sound/soc/dwc/dwc-i2s.c @@ -1250,8 +1250,6 @@ static int dw_i2s_remove(struct platform_device *pdev) #ifdef CONFIG_OF static const struct of_device_id dw_i2s_of_match[] = { - { .compatible = "starfive,jh7110-i2stx-4ch1", }, - { .compatible = "starfive,jh7110-i2srx", }, { .compatible = "snps,designware-i2stx-4ch0", }, {}, }; diff --git a/sound/soc/starfive/starfive_i2s.c b/sound/soc/starfive/starfive_i2s.c index 00dbdae033bb..b0c198e83bc1 100644 --- a/sound/soc/starfive/starfive_i2s.c +++ b/sound/soc/starfive/starfive_i2s.c @@ -224,6 +224,8 @@ static int dw_i2s_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai_link *dai_link = rtd->dai_link; dai_link->stop_dma_first = 1; + config->chan_nr = params_channels(params); + config->sample_rate = params_rate(params); if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) playback = false; @@ -232,6 +234,11 @@ static int dw_i2s_hw_params(struct snd_pcm_substream *substream, switch (params_format(params)) { case SNDRV_PCM_FORMAT_S16_LE: + if ((config->sample_rate == 8000) && playback && + (dev->capability & DW_I2S_SLAVE)) { + dev_warn(dev->dev, "I2S: unsupported 8000 rate with S16_LE, Stereo if use wm8960.\n"); + } + config->data_width = 16; dev->ccr = 0x00; dev->xfer_resolution = 0x02; @@ -256,21 +263,7 @@ static int dw_i2s_hw_params(struct snd_pcm_substream *substream, return -EINVAL; } - if (playback) { - switch (params_rate(params)) { - case 32000: - bclk_rate = 2048000; - break; - case 48000: - bclk_rate = 3072000; - break; - default: - dev_err(dai->dev, "%d rate not supported\n", - params_rate(params)); - ret = -EINVAL; - goto err_return; - } - } else { + if (dev->capability & DW_I2S_MASTER) { switch (params_rate(params)) { case 8000: bclk_rate = 512000; @@ -281,6 +274,12 @@ static int dw_i2s_hw_params(struct snd_pcm_substream *substream, case 16000: bclk_rate = 1024000; break; + case 32000: + bclk_rate = 2048000; + break; + case 48000: + bclk_rate = 3072000; + break; default: dev_err(dai->dev, "%d rate not supported\n", params_rate(params)); @@ -289,8 +288,6 @@ static int dw_i2s_hw_params(struct snd_pcm_substream *substream, } } - config->chan_nr = params_channels(params); - switch (config->chan_nr) { case EIGHT_CHANNEL_SUPPORT: case SIX_CHANNEL_SUPPORT: @@ -312,8 +309,6 @@ static int dw_i2s_hw_params(struct snd_pcm_substream *substream, i2s_write_reg(dev->i2s_base, CCR, dev->ccr); - config->sample_rate = params_rate(params); - if (dev->capability & DW_I2S_MASTER) { if (dev->i2s_clk_cfg) { ret = dev->i2s_clk_cfg(config); @@ -433,10 +428,8 @@ static int dw_i2s_runtime_suspend(struct device *dev) { struct dw_i2s_dev *dw_dev = dev_get_drvdata(dev); - if (dw_dev->capability & DW_I2S_MASTER) { - clk_disable_unprepare(dw_dev->clk_i2s_bclk_mst); - clk_disable_unprepare(dw_dev->clk_i2s_apb); - } + clk_disable_unprepare(dw_dev->clk_i2s_bclk_mst); + clk_disable_unprepare(dw_dev->clk_i2s_apb); return 0; } @@ -446,18 +439,16 @@ static int dw_i2s_runtime_resume(struct device *dev) struct dw_i2s_dev *dw_dev = dev_get_drvdata(dev); int ret; - if (dw_dev->capability & DW_I2S_MASTER) { - ret = clk_prepare_enable(dw_dev->clk_i2s_apb); - if (ret) { - dev_err(dw_dev->dev, "Failed to enable clk_i2s_apb\n"); - return ret; - } + ret = clk_prepare_enable(dw_dev->clk_i2s_apb); + if (ret) { + dev_err(dw_dev->dev, "Failed to enable clk_i2s_apb\n"); + return ret; + } - ret = clk_prepare_enable(dw_dev->clk_i2s_bclk_mst); - if (ret) { - dev_err(dw_dev->dev, "Failed to enable clk_i2s_lrck\n"); - return ret; - } + ret = clk_prepare_enable(dw_dev->clk_i2s_bclk_mst); + if (ret) { + dev_err(dw_dev->dev, "Failed to enable clk_i2s_lrck\n"); + return ret; } return 0; @@ -499,7 +490,7 @@ static const struct snd_soc_component_driver dw_i2s_component = { .resume = dw_i2s_resume, }; -static int dw_i2srx_clk_init(struct platform_device *pdev, struct dw_i2s_dev *dev) +static int dw_i2srx_mst_clk_init(struct platform_device *pdev, struct dw_i2s_dev *dev) { int ret = 0; @@ -565,6 +556,98 @@ exit: return ret; } +static int dw_i2srx_clk_init(struct platform_device *pdev, struct dw_i2s_dev *dev) +{ + int ret = 0; + + static struct clk_bulk_data clks[] = { + { .id = "3ch-apb" }, + { .id = "bclk_mst" }, + { .id = "3ch-lrck" }, + { .id = "rx-bclk" }, + { .id = "rx-lrck" }, + { .id = "bclk-ext" }, + { .id = "lrck-ext" }, + }; + + ret = devm_clk_bulk_get(&pdev->dev, ARRAY_SIZE(clks), clks); + if (ret) { + dev_err(&pdev->dev, "%s: failed to get audio_subsys clocks\n", __func__); + return ret; + } + + dev->clk_i2s_apb = clks[0].clk; + dev->clk_i2s_bclk_mst = clks[1].clk; + dev->clk_i2s_lrck_mst = clks[2].clk; + dev->clk_i2s_bclk = clks[3].clk; + dev->clk_i2s_lrck = clks[4].clk; + dev->clk_bclk_ext = clks[5].clk; + dev->clk_lrck_ext = clks[6].clk; + + ret = clk_prepare_enable(dev->clk_i2s_apb); + if (ret) { + dev_err(&pdev->dev, "%s: failed to enable clk_i2s_apb\n", __func__); + goto disable_apb_clk; + } + + ret = clk_prepare_enable(dev->clk_i2s_bclk_mst); + if (ret) { + dev_err(&pdev->dev, "%s: failed to enable CLK_ADC_BCLK\n", __func__); + goto disable_bclk; + } + + dev->rst_i2s_apb = devm_reset_control_array_get_exclusive(&pdev->dev); + if (IS_ERR(dev->rst_i2s_apb)) { + dev_err(&pdev->dev, "%s: failed to get rsts reset control\n", __func__); + goto disable_rst; + } + + ret = reset_control_assert(dev->rst_i2s_apb); + if (ret) { + dev_err(&pdev->dev, "%s: failed to reset control assert rstc_rx\n", __func__); + goto disable_rst; + } + udelay(5); + ret = reset_control_deassert(dev->rst_i2s_apb); + if (ret) { + dev_err(&pdev->dev, "%s: failed to reset control deassert rstc_rx\n", __func__); + goto disable_rst; + } + + /*i2srx_3ch_adc_enable*/ + regmap_update_bits(dev->syscon_base, dev->syscon_offset_18, + 0x1 << 1, 0x1 << 1); + + /*set i2sdin_sel*/ + regmap_update_bits(dev->syscon_base, dev->syscon_offset_34, + (0x1 << 10) | (0x1 << 14) | (0x1<<17), (0x0<<10) | (0x0<<14) | (0x0<<17)); + + ret = clk_set_parent(dev->clk_i2s_bclk, dev->clk_bclk_ext); + if (ret) { + dev_err(&pdev->dev, "%s: failed to set clk_i2s_bclk parent to bclk_ext.\n", + __func__); + goto disable_parent; + } + + ret = clk_set_parent(dev->clk_i2s_lrck, dev->clk_lrck_ext); + if (ret) { + dev_err(&pdev->dev, "%s: failed to set clk_i2s_lrck parent to lrck_ext.\n", + __func__); + goto disable_parent; + } + + return 0; + +disable_parent: +disable_rst: + clk_disable_unprepare(dev->clk_i2s_bclk_mst); +disable_bclk: + clk_disable_unprepare(dev->clk_i2s_apb); +disable_apb_clk: + + return ret; +} + static int dw_i2stx_4ch0_clk_init(struct platform_device *pdev, struct dw_i2s_dev *dev) { int ret = 0; @@ -655,6 +738,93 @@ exit: return ret; } +static int dw_i2stx_4ch1_clk_init(struct platform_device *pdev, struct dw_i2s_dev *dev) +{ + int ret = 0; + + static struct clk_bulk_data clks[] = { + { .id = "bclk_mst" }, + { .id = "lrck_mst" }, + { .id = "mclk" }, + { .id = "4chbclk" }, + { .id = "4chlrck" }, + { .id = "clk_apb" }, + { .id = "mclk_ext" }, + { .id = "bclk_ext" }, + { .id = "lrck_ext" }, + }; + + ret = devm_clk_bulk_get(&pdev->dev, ARRAY_SIZE(clks), clks); + if (ret) { + dev_err(&pdev->dev, "%s: failed to get i2s clocks\n", __func__); + goto exit; + } + + dev->clk_i2s_bclk_mst = clks[0].clk; + dev->clk_i2s_lrck_mst = clks[1].clk; + dev->clk_mclk = clks[2].clk; + dev->clk_i2s_bclk = clks[3].clk; + dev->clk_i2s_lrck = clks[4].clk; + dev->clk_i2s_apb = clks[5].clk; + dev->clk_mclk_ext = clks[6].clk; + dev->clk_bclk_ext = clks[7].clk; + dev->clk_lrck_ext = clks[8].clk; + + ret = clk_prepare_enable(dev->clk_i2s_apb); + if (ret) { + dev_err(&pdev->dev, "%s: failed to enable clk_i2s_apb\n", __func__); + goto exit; + } + + ret = clk_prepare_enable(dev->clk_i2s_bclk_mst); + if (ret) { + dev_err(&pdev->dev, "%s: failed to enable clk_i2s_bclk_mst\n", __func__); + goto disable_bclk; + } + + dev->rst_i2s_apb = devm_reset_control_array_get_exclusive(&pdev->dev); + if (IS_ERR(dev->rst_i2s_apb)) { + dev_err(&pdev->dev, "%s: failed to get rsts reset control\n", __func__); + goto disable_rst; + } + + ret = reset_control_assert(dev->rst_i2s_apb); + if (ret) { + dev_err(&pdev->dev, "%s: failed to reset control assert rsts\n", __func__); + goto disable_rst; + } + + ret = reset_control_deassert(dev->rst_i2s_apb); + if (ret) { + dev_err(&pdev->dev, "%s: failed to reset control deassert rsts\n", __func__); + goto disable_rst; + } + + ret = clk_set_parent(dev->clk_i2s_bclk, dev->clk_bclk_ext); + if (ret) { + dev_err(&pdev->dev, "%s: failed to set clk_i2s_bclk parent to bclk_ext.\n", + __func__); + goto disable_parent; + } + + ret = clk_set_parent(dev->clk_i2s_lrck, dev->clk_lrck_ext); + if (ret) { + dev_err(&pdev->dev, "%s: failed to set clk_i2s_lrck parent to lrck_ext.\n", + __func__); + goto disable_parent; + } + + return 0; + +disable_parent: +disable_rst: + clk_disable_unprepare(dev->clk_i2s_bclk_mst); +disable_bclk: + clk_disable_unprepare(dev->clk_i2s_apb); +exit: + return ret; +} + /* * The following tables allow a direct lookup of various parameters * defined in the I2S block's configuration in terms of sound system @@ -749,8 +919,13 @@ static int dw_configure_dai(struct dw_i2s_dev *dev, dw_i2s_dai->capture.rates = rates; } - dev_dbg(dev->dev, "designware: i2s master mode supported\n"); - dev->capability |= DW_I2S_MASTER; + if (COMP1_MODE_EN(comp1) || dev->is_master) { + dev_err(dev->dev, "designware: i2s master mode supported\n"); + dev->capability |= DW_I2S_MASTER; + } else { + dev_err(dev->dev, "designware: i2s slave mode supported\n"); + dev->capability |= DW_I2S_SLAVE; + } dev->fifo_th = fifo_depth / 2; dev_dbg(dev->dev, "fifo_th :%d\n", dev->fifo_th); @@ -804,11 +979,7 @@ static int dw_configure_dai_by_dt(struct dw_i2s_dev *dev, if (WARN_ON(idx >= ARRAY_SIZE(bus_widths))) return -EINVAL; - if (COMP1_TX_ENABLED(comp1)) - ret = dw_configure_dai(dev, dw_i2s_dai, - SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000); - if (COMP1_RX_ENABLED(comp1)) - ret = dw_configure_dai(dev, dw_i2s_dai, SNDRV_PCM_RATE_8000_192000); + ret = dw_configure_dai(dev, dw_i2s_dai, dev->susport_rate); if (ret < 0) return ret; @@ -826,8 +997,10 @@ static int dw_configure_dai_by_dt(struct dw_i2s_dev *dev, idx2 = COMP2_RX_WORDSIZE_0(comp2); /* force change to 1 */ - idx = 1; - idx2 = 1; + if (dev->is_master) { + idx = 1; + idx2 = 1; + } dev->capability |= DWC_I2S_RECORD; dev->capture_dma_data.dt.addr = res->start + I2S_RXDMA; dev->capture_dma_data.dt.addr_width = bus_widths[idx]; @@ -906,14 +1079,44 @@ static int dw_i2s_probe(struct platform_device *pdev) regmap_update_bits(dev->syscon_base, dev->syscon_offset_34, AUDIO_SDIN_MUX_MASK, I2SRX_DATA_SRC_PDM); + ret = dw_i2srx_mst_clk_init(pdev, dev); + if (ret < 0) + goto err_init; + dev->is_master = true; + dev->susport_rate = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | + SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000; + } else if (of_device_is_compatible(np, "starfive,jh7110-i2srx")) { + ret = of_parse_phandle_with_fixed_args(dev->dev->of_node, + "starfive,sys-syscon", 2, 0, &args); + if (ret) { + dev_err(dev->dev, "Failed to parse starfive,sys-syscon\n"); + return -EINVAL; + } + dev->syscon_base = syscon_node_to_regmap(args.np); + of_node_put(args.np); + if (IS_ERR(dev->syscon_base)) + return PTR_ERR(dev->syscon_base); + + dev->syscon_offset_18 = args.args[0]; + dev->syscon_offset_34 = args.args[1]; ret = dw_i2srx_clk_init(pdev, dev); if (ret < 0) - goto err_clk_disable; + goto err_init; + dev->is_master = false; + dev->susport_rate = SNDRV_PCM_RATE_8000_192000; } else if (of_device_is_compatible(np, "starfive,jh7110-i2stx-4ch0")) { /* hdmi audio */ ret = dw_i2stx_4ch0_clk_init(pdev, dev); if (ret < 0) - goto err_clk_disable; + goto err_init; + dev->is_master = true; + dev->susport_rate = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000; + } else if (of_device_is_compatible(np, "starfive,jh7110-i2stx-4ch1")) { + ret = dw_i2stx_4ch1_clk_init(pdev, dev); + if (ret < 0) + goto err_init; + dev->is_master = false; + dev->susport_rate = SNDRV_PCM_RATE_8000_192000; } dev->i2s_reg_comp1 = I2S_COMP_PARAM_1; @@ -933,14 +1136,15 @@ static int dw_i2s_probe(struct platform_device *pdev) ret = dw_configure_dai_by_dt(dev, dw_i2s_dai, res); } if (ret < 0) - return ret; + goto err_clk_disable; if (dev->capability & DW_I2S_MASTER) { if (pdata) { dev->i2s_clk_cfg = pdata->i2s_clk_cfg; if (!dev->i2s_clk_cfg) { dev_err(&pdev->dev, "no clock configure method\n"); - return -ENODEV; + ret = -ENODEV; + goto err_clk_disable; } } } @@ -973,19 +1177,16 @@ static int dw_i2s_probe(struct platform_device *pdev) pm_runtime_enable(&pdev->dev); #ifdef CONFIG_PM - if (dev->capability & DW_I2S_MASTER) { - clk_disable_unprepare(dev->clk_i2s_bclk_mst); - clk_disable_unprepare(dev->clk_i2s_apb); - } + clk_disable_unprepare(dev->clk_i2s_bclk_mst); + clk_disable_unprepare(dev->clk_i2s_apb); #endif return 0; err_clk_disable: - if (dev->capability & DW_I2S_MASTER) { - clk_disable_unprepare(dev->clk_i2s_bclk_mst); - clk_disable_unprepare(dev->clk_i2s_apb); - } + clk_disable_unprepare(dev->clk_i2s_bclk_mst); + clk_disable_unprepare(dev->clk_i2s_apb); +err_init: return ret; } @@ -993,10 +1194,8 @@ static int dw_i2s_remove(struct platform_device *pdev) { struct dw_i2s_dev *dev = dev_get_drvdata(&pdev->dev); - if (dev->capability & DW_I2S_MASTER) { - clk_disable_unprepare(dev->clk_i2s_bclk_mst); - clk_disable_unprepare(dev->clk_i2s_apb); - } + clk_disable_unprepare(dev->clk_i2s_bclk_mst); + clk_disable_unprepare(dev->clk_i2s_apb); pm_runtime_disable(&pdev->dev); return 0; @@ -1005,7 +1204,9 @@ static int dw_i2s_remove(struct platform_device *pdev) #ifdef CONFIG_OF static const struct of_device_id dw_i2s_of_match[] = { { .compatible = "starfive,jh7110-i2srx-master", }, + { .compatible = "starfive,jh7110-i2srx", }, { .compatible = "starfive,jh7110-i2stx-4ch0", }, + { .compatible = "starfive,jh7110-i2stx-4ch1", }, {}, }; diff --git a/sound/soc/starfive/starfive_i2s.h b/sound/soc/starfive/starfive_i2s.h index 108412d4b95b..8effed739fdd 100644 --- a/sound/soc/starfive/starfive_i2s.h +++ b/sound/soc/starfive/starfive_i2s.h @@ -117,6 +117,8 @@ struct dw_i2s_dev { struct clk *clk_mclk; struct clk *clk_mclk_ext; struct clk *clk_mclk_inner; + struct clk *clk_bclk_ext; + struct clk *clk_lrck_ext; struct reset_control *rst_i2s_apb; struct reset_control *rst_i2s_bclk; @@ -138,6 +140,8 @@ struct dw_i2s_dev { bool *period_elapsed); unsigned int tx_ptr; unsigned int rx_ptr; + bool is_master; + unsigned int susport_rate; }; #if IS_ENABLED(CONFIG_SND_DESIGNWARE_PCM) |