#include <linux/init.h>
 #include <linux/io.h>
 #include <linux/interrupt.h>
+#include <linux/mfd/syscon.h>
 #include <linux/module.h>
 #include <linux/reset.h>
 #include <linux/slab.h>
        else
                i2s_write_reg(dev->i2s_base, IRER, 1);
 
-       if (dev->use_pio)
+       /* I2S needs to enable IRQ to make a handshake with DMAC on the JH7110 SoC */
+       if (dev->use_pio || dev->is_jh7110)
                i2s_enable_irqs(dev, substream->stream, config->chan_nr);
        else
                i2s_enable_dma(dev, substream->stream);
        else
                i2s_write_reg(dev->i2s_base, IRER, 0);
 
-       if (dev->use_pio)
+       if (dev->use_pio || dev->is_jh7110)
                i2s_disable_irqs(dev, substream->stream, 8);
        else
                i2s_disable_dma(dev, substream->stream);
        }
 }
 
+static int dw_i2s_startup(struct snd_pcm_substream *substream,
+                         struct snd_soc_dai *cpu_dai)
+{
+       struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(cpu_dai);
+
+       if (dev->is_jh7110) {
+               struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+               struct snd_soc_dai_link *dai_link = rtd->dai_link;
+
+               dai_link->trigger_stop = SND_SOC_TRIGGER_ORDER_LDC;
+       }
+
+       return 0;
+}
+
 static void dw_i2s_config(struct dw_i2s_dev *dev, int stream)
 {
        u32 ch_reg;
 
 static const struct snd_soc_dai_ops dw_i2s_dai_ops = {
        .probe          = dw_i2s_dai_probe,
+       .startup        = dw_i2s_startup,
        .hw_params      = dw_i2s_hw_params,
        .prepare        = dw_i2s_prepare,
        .trigger        = dw_i2s_trigger,
 
        if (dev->quirks & DW_I2S_QUIRK_16BIT_IDX_OVERRIDE)
                idx = 1;
-       /* Set DMA slaves info */
-       dev->play_dma_data.pd.data = pdata->play_dma_data;
-       dev->capture_dma_data.pd.data = pdata->capture_dma_data;
-       dev->play_dma_data.pd.addr = res->start + I2S_TXDMA;
-       dev->capture_dma_data.pd.addr = res->start + I2S_RXDMA;
-       dev->play_dma_data.pd.max_burst = 16;
-       dev->capture_dma_data.pd.max_burst = 16;
-       dev->play_dma_data.pd.addr_width = bus_widths[idx];
-       dev->capture_dma_data.pd.addr_width = bus_widths[idx];
-       dev->play_dma_data.pd.filter = pdata->filter;
-       dev->capture_dma_data.pd.filter = pdata->filter;
+
+       if (dev->is_jh7110) {
+               /* Use platform data and snd_dmaengine_dai_dma_data struct at the same time */
+               u32 comp2 = i2s_read_reg(dev->i2s_base, I2S_COMP_PARAM_2);
+               u32 idx2;
+
+               if (COMP1_TX_ENABLED(comp1)) {
+                       idx2 = COMP1_TX_WORDSIZE_0(comp1);
+                       dev->play_dma_data.dt.addr = res->start + I2S_TXDMA;
+                       dev->play_dma_data.dt.fifo_size = dev->fifo_th * 2 *
+                               (fifo_width[idx2]) >> 8;
+                       dev->play_dma_data.dt.maxburst = 16;
+               }
+               if (COMP1_RX_ENABLED(comp1)) {
+                       idx2 = COMP2_RX_WORDSIZE_0(comp2);
+                       dev->capture_dma_data.dt.addr = res->start + I2S_RXDMA;
+                       dev->capture_dma_data.dt.fifo_size = dev->fifo_th * 2 *
+                               (fifo_width[idx2] >> 8);
+                       dev->capture_dma_data.dt.maxburst = 16;
+               }
+       } else {
+               /* Set DMA slaves info */
+               dev->play_dma_data.pd.data = pdata->play_dma_data;
+               dev->capture_dma_data.pd.data = pdata->capture_dma_data;
+               dev->play_dma_data.pd.addr = res->start + I2S_TXDMA;
+               dev->capture_dma_data.pd.addr = res->start + I2S_RXDMA;
+               dev->play_dma_data.pd.max_burst = 16;
+               dev->capture_dma_data.pd.max_burst = 16;
+               dev->play_dma_data.pd.addr_width = bus_widths[idx];
+               dev->capture_dma_data.pd.addr_width = bus_widths[idx];
+               dev->play_dma_data.pd.filter = pdata->filter;
+               dev->capture_dma_data.pd.filter = pdata->filter;
+       }
 
        return 0;
 }
 
 }
 
+/* clocks initialization with master mode on JH7110 SoC */
+static int jh7110_i2s_crg_master_init(struct dw_i2s_dev *dev)
+{
+       static struct clk_bulk_data clks[] = {
+               { .id = "mclk" },
+               { .id = "mclk_ext" },
+               { .id = "mclk_inner" },
+               { .id = "apb" },
+               { .id = "i2sclk" },
+       };
+       struct reset_control *resets = devm_reset_control_array_get_exclusive(dev->dev);
+       int ret;
+       struct clk *pclk;
+       struct clk *bclk_mst;
+       struct clk *mclk;
+       struct clk *mclk_ext;
+       struct clk *mclk_inner;
+
+       if (IS_ERR(resets))
+               return dev_err_probe(dev->dev, PTR_ERR(resets), "failed to get i2s resets\n");
+
+       ret = clk_bulk_get(dev->dev, ARRAY_SIZE(clks), clks);
+       if (ret)
+               return dev_err_probe(dev->dev, ret, "failed to get i2s clocks\n");
+
+       mclk = clks[0].clk;
+       mclk_ext = clks[1].clk;
+       mclk_inner = clks[2].clk;
+       pclk = clks[3].clk;
+       bclk_mst = clks[4].clk;
+
+       ret = clk_prepare_enable(pclk);
+       if (ret)
+               goto exit;
+
+       /* Use inner mclk first and avoid uninitialized gpio for external mclk */
+       ret = clk_set_parent(mclk, mclk_inner);
+       if (ret)
+               goto err_dis_pclk;
+
+       ret = clk_prepare_enable(bclk_mst);
+       if (ret)
+               goto err_dis_pclk;
+
+       /* deassert resets before set clock parent */
+       ret = reset_control_deassert(resets);
+       if (ret)
+               goto err_dis_all;
+
+       /* external clock (12.288MHz) for Audio */
+       ret = clk_set_parent(mclk, mclk_ext);
+       if (ret)
+               goto err_dis_all;
+
+       /* i2sclk will be got and enabled repeatedly later and should be disabled now. */
+       clk_disable_unprepare(bclk_mst);
+       clk_bulk_put(ARRAY_SIZE(clks), clks);
+       dev->is_jh7110 = true;
+
+       return 0;
+
+err_dis_all:
+       clk_disable_unprepare(bclk_mst);
+err_dis_pclk:
+       clk_disable_unprepare(pclk);
+exit:
+       clk_bulk_put(ARRAY_SIZE(clks), clks);
+       return ret;
+}
+
+/* clocks initialization with slave mode on JH7110 SoC */
+static int jh7110_i2s_crg_slave_init(struct dw_i2s_dev *dev)
+{
+       static struct clk_bulk_data clks[] = {
+               { .id = "mclk" },
+               { .id = "mclk_ext" },
+               { .id = "apb" },
+               { .id = "bclk_ext" },
+               { .id = "lrck_ext" },
+               { .id = "bclk" },
+               { .id = "lrck" },
+               { .id = "mclk_inner" },
+               { .id = "i2sclk" },
+       };
+       struct reset_control *resets = devm_reset_control_array_get_exclusive(dev->dev);
+       int ret;
+       struct clk *pclk;
+       struct clk *bclk_mst;
+       struct clk *bclk_ext;
+       struct clk *lrck_ext;
+       struct clk *bclk;
+       struct clk *lrck;
+       struct clk *mclk;
+       struct clk *mclk_ext;
+       struct clk *mclk_inner;
+
+       if (IS_ERR(resets))
+               return dev_err_probe(dev->dev, PTR_ERR(resets), "failed to get i2s resets\n");
+
+       ret = clk_bulk_get(dev->dev, ARRAY_SIZE(clks), clks);
+       if (ret)
+               return dev_err_probe(dev->dev, ret, "failed to get i2s clocks\n");
+
+       mclk = clks[0].clk;
+       mclk_ext = clks[1].clk;
+       pclk = clks[2].clk;
+       bclk_ext = clks[3].clk;
+       lrck_ext = clks[4].clk;
+       bclk = clks[5].clk;
+       lrck = clks[6].clk;
+       mclk_inner = clks[7].clk;
+       bclk_mst = clks[8].clk;
+
+       ret = clk_prepare_enable(pclk);
+       if (ret)
+               goto exit;
+
+       ret = clk_set_parent(mclk, mclk_inner);
+       if (ret)
+               goto err_dis_pclk;
+
+       ret = clk_prepare_enable(bclk_mst);
+       if (ret)
+               goto err_dis_pclk;
+
+       ret = reset_control_deassert(resets);
+       if (ret)
+               goto err_dis_all;
+
+       /* The sources of BCLK and LRCK are the external codec. */
+       ret = clk_set_parent(bclk, bclk_ext);
+       if (ret)
+               goto err_dis_all;
+
+       ret = clk_set_parent(lrck, lrck_ext);
+       if (ret)
+               goto err_dis_all;
+
+       ret = clk_set_parent(mclk, mclk_ext);
+       if (ret)
+               goto err_dis_all;
+
+       /* The i2sclk will be got and enabled repeatedly later and should be disabled now. */
+       clk_disable_unprepare(bclk_mst);
+       clk_bulk_put(ARRAY_SIZE(clks), clks);
+       dev->is_jh7110 = true;
+
+       return 0;
+
+err_dis_all:
+       clk_disable_unprepare(bclk_mst);
+err_dis_pclk:
+       clk_disable_unprepare(pclk);
+exit:
+       clk_bulk_put(ARRAY_SIZE(clks), clks);
+       return ret;
+}
+
+/* Special syscon initialization about RX channel with slave mode on JH7110 SoC */
+static int jh7110_i2srx_crg_init(struct dw_i2s_dev *dev)
+{
+       struct regmap *regmap;
+       unsigned int args[2];
+
+       regmap = syscon_regmap_lookup_by_phandle_args(dev->dev->of_node,
+                                                     "starfive,syscon",
+                                                     2, args);
+       if (IS_ERR(regmap))
+               return dev_err_probe(dev->dev, PTR_ERR(regmap), "getting the regmap failed\n");
+
+       /* Enable I2Srx with syscon register, args[0]: offset, args[1]: mask */
+       regmap_update_bits(regmap, args[0], args[1], args[1]);
+
+       return jh7110_i2s_crg_slave_init(dev);
+}
+
+static int jh7110_i2stx0_clk_cfg(struct i2s_clk_config_data *config)
+{
+       struct dw_i2s_dev *dev = container_of(config, struct dw_i2s_dev, config);
+       u32 bclk_rate = config->sample_rate * 64;
+
+       return clk_set_rate(dev->clk, bclk_rate);
+}
+
 static int dw_i2s_probe(struct platform_device *pdev)
 {
        const struct i2s_platform_data *pdata = of_device_get_match_data(&pdev->dev);
        if (IS_ERR(dev->i2s_base))
                return PTR_ERR(dev->i2s_base);
 
-       dev->reset = devm_reset_control_array_get_optional_shared(&pdev->dev);
-       if (IS_ERR(dev->reset))
-               return PTR_ERR(dev->reset);
+       dev->dev = &pdev->dev;
+       dev->is_jh7110 = false;
+       if (pdata) {
+               if (pdata->i2s_pd_init) {
+                       ret = pdata->i2s_pd_init(dev);
+                       if (ret)
+                               return ret;
+               }
+       }
 
-       ret = reset_control_deassert(dev->reset);
-       if (ret)
-               return ret;
+       if (!dev->is_jh7110) {
+               dev->reset = devm_reset_control_array_get_optional_shared(&pdev->dev);
+               if (IS_ERR(dev->reset))
+                       return PTR_ERR(dev->reset);
 
-       dev->dev = &pdev->dev;
+               ret = reset_control_deassert(dev->reset);
+               if (ret)
+                       return ret;
+       }
 
        irq = platform_get_irq_optional(pdev, 0);
        if (irq >= 0) {
                goto err_clk_disable;
        }
 
-       if (!pdata) {
+       if (!pdata || dev->is_jh7110) {
                if (irq >= 0) {
                        ret = dw_pcm_register(pdev);
                        dev->use_pio = true;
 }
 
 #ifdef CONFIG_OF
+static const struct i2s_platform_data jh7110_i2stx0_data = {
+       .cap = DWC_I2S_PLAY | DW_I2S_MASTER,
+       .channel = TWO_CHANNEL_SUPPORT,
+       .snd_fmts = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE,
+       .snd_rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000,
+       .i2s_clk_cfg = jh7110_i2stx0_clk_cfg,
+       .i2s_pd_init = jh7110_i2s_crg_master_init,
+};
+
+static const struct i2s_platform_data jh7110_i2stx1_data = {
+       .cap = DWC_I2S_PLAY | DW_I2S_SLAVE,
+       .channel = TWO_CHANNEL_SUPPORT,
+       .snd_fmts = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE,
+       .snd_rates = SNDRV_PCM_RATE_8000_192000,
+       .i2s_pd_init = jh7110_i2s_crg_slave_init,
+};
+
+static const struct i2s_platform_data jh7110_i2srx_data = {
+       .cap = DWC_I2S_RECORD | DW_I2S_SLAVE,
+       .channel = TWO_CHANNEL_SUPPORT,
+       .snd_fmts = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE,
+       .snd_rates = SNDRV_PCM_RATE_8000_192000,
+       .i2s_pd_init = jh7110_i2srx_crg_init,
+};
+
 static const struct of_device_id dw_i2s_of_match[] = {
        { .compatible = "snps,designware-i2s",   },
+       { .compatible = "starfive,jh7110-i2stx0", .data = &jh7110_i2stx0_data, },
+       { .compatible = "starfive,jh7110-i2stx1", .data = &jh7110_i2stx1_data,},
+       { .compatible = "starfive,jh7110-i2srx", .data = &jh7110_i2srx_data,},
        {},
 };