162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci// linux/sound/bcm/bcm63xx-pcm-whistler.c
362306a36Sopenharmony_ci// BCM63xx whistler pcm interface
462306a36Sopenharmony_ci// Copyright (c) 2020 Broadcom Corporation
562306a36Sopenharmony_ci// Author: Kevin-Ke Li <kevin-ke.li@broadcom.com>
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#include <linux/dma-mapping.h>
862306a36Sopenharmony_ci#include <linux/io.h>
962306a36Sopenharmony_ci#include <linux/irq.h>
1062306a36Sopenharmony_ci#include <linux/module.h>
1162306a36Sopenharmony_ci#include <sound/pcm_params.h>
1262306a36Sopenharmony_ci#include <linux/regmap.h>
1362306a36Sopenharmony_ci#include <linux/of_device.h>
1462306a36Sopenharmony_ci#include <sound/soc.h>
1562306a36Sopenharmony_ci#include "bcm63xx-i2s.h"
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_cistruct i2s_dma_desc {
1962306a36Sopenharmony_ci	unsigned char *dma_area;
2062306a36Sopenharmony_ci	dma_addr_t dma_addr;
2162306a36Sopenharmony_ci	unsigned int dma_len;
2262306a36Sopenharmony_ci};
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_cistruct bcm63xx_runtime_data {
2562306a36Sopenharmony_ci	int dma_len;
2662306a36Sopenharmony_ci	dma_addr_t dma_addr;
2762306a36Sopenharmony_ci	dma_addr_t dma_addr_next;
2862306a36Sopenharmony_ci};
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_cistatic const struct snd_pcm_hardware bcm63xx_pcm_hardware = {
3162306a36Sopenharmony_ci	.info = SNDRV_PCM_INFO_MMAP |
3262306a36Sopenharmony_ci		SNDRV_PCM_INFO_MMAP_VALID |
3362306a36Sopenharmony_ci		SNDRV_PCM_INFO_INTERLEAVED |
3462306a36Sopenharmony_ci		SNDRV_PCM_INFO_PAUSE |
3562306a36Sopenharmony_ci		SNDRV_PCM_INFO_RESUME,
3662306a36Sopenharmony_ci	.formats = SNDRV_PCM_FMTBIT_S32_LE, /* support S32 only */
3762306a36Sopenharmony_ci	.period_bytes_max = 8192 - 32,
3862306a36Sopenharmony_ci	.periods_min = 1,
3962306a36Sopenharmony_ci	.periods_max = PAGE_SIZE/sizeof(struct i2s_dma_desc),
4062306a36Sopenharmony_ci	.buffer_bytes_max = 128 * 1024,
4162306a36Sopenharmony_ci	.fifo_size = 32,
4262306a36Sopenharmony_ci};
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_cistatic int bcm63xx_pcm_hw_params(struct snd_soc_component *component,
4562306a36Sopenharmony_ci				 struct snd_pcm_substream *substream,
4662306a36Sopenharmony_ci				 struct snd_pcm_hw_params *params)
4762306a36Sopenharmony_ci{
4862306a36Sopenharmony_ci	struct i2s_dma_desc *dma_desc;
4962306a36Sopenharmony_ci	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci	dma_desc = kzalloc(sizeof(*dma_desc), GFP_NOWAIT);
5262306a36Sopenharmony_ci	if (!dma_desc)
5362306a36Sopenharmony_ci		return -ENOMEM;
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci	snd_soc_dai_set_dma_data(asoc_rtd_to_cpu(rtd, 0), substream, dma_desc);
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci	return 0;
5862306a36Sopenharmony_ci}
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_cistatic int bcm63xx_pcm_hw_free(struct snd_soc_component *component,
6162306a36Sopenharmony_ci			struct snd_pcm_substream *substream)
6262306a36Sopenharmony_ci{
6362306a36Sopenharmony_ci	struct i2s_dma_desc	*dma_desc;
6462306a36Sopenharmony_ci	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci	dma_desc = snd_soc_dai_get_dma_data(asoc_rtd_to_cpu(rtd, 0), substream);
6762306a36Sopenharmony_ci	kfree(dma_desc);
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci	return 0;
7062306a36Sopenharmony_ci}
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_cistatic int bcm63xx_pcm_trigger(struct snd_soc_component *component,
7362306a36Sopenharmony_ci			       struct snd_pcm_substream *substream, int cmd)
7462306a36Sopenharmony_ci{
7562306a36Sopenharmony_ci	int ret = 0;
7662306a36Sopenharmony_ci	struct snd_soc_pcm_runtime *rtd;
7762306a36Sopenharmony_ci	struct bcm_i2s_priv *i2s_priv;
7862306a36Sopenharmony_ci	struct regmap   *regmap_i2s;
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	rtd = asoc_substream_to_rtd(substream);
8162306a36Sopenharmony_ci	i2s_priv = dev_get_drvdata(asoc_rtd_to_cpu(rtd, 0)->dev);
8262306a36Sopenharmony_ci	regmap_i2s = i2s_priv->regmap_i2s;
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
8562306a36Sopenharmony_ci		switch (cmd) {
8662306a36Sopenharmony_ci		case SNDRV_PCM_TRIGGER_START:
8762306a36Sopenharmony_ci			regmap_update_bits(regmap_i2s,
8862306a36Sopenharmony_ci					   I2S_TX_IRQ_EN,
8962306a36Sopenharmony_ci					   I2S_TX_DESC_OFF_INTR_EN,
9062306a36Sopenharmony_ci					   I2S_TX_DESC_OFF_INTR_EN);
9162306a36Sopenharmony_ci			regmap_update_bits(regmap_i2s,
9262306a36Sopenharmony_ci					   I2S_TX_CFG,
9362306a36Sopenharmony_ci					   I2S_TX_ENABLE_MASK,
9462306a36Sopenharmony_ci					   I2S_TX_ENABLE);
9562306a36Sopenharmony_ci			break;
9662306a36Sopenharmony_ci		case SNDRV_PCM_TRIGGER_STOP:
9762306a36Sopenharmony_ci		case SNDRV_PCM_TRIGGER_SUSPEND:
9862306a36Sopenharmony_ci		case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
9962306a36Sopenharmony_ci			regmap_write(regmap_i2s,
10062306a36Sopenharmony_ci				     I2S_TX_IRQ_EN,
10162306a36Sopenharmony_ci				     0);
10262306a36Sopenharmony_ci			regmap_update_bits(regmap_i2s,
10362306a36Sopenharmony_ci					   I2S_TX_CFG,
10462306a36Sopenharmony_ci					   I2S_TX_ENABLE_MASK,
10562306a36Sopenharmony_ci					   0);
10662306a36Sopenharmony_ci			break;
10762306a36Sopenharmony_ci		default:
10862306a36Sopenharmony_ci			ret = -EINVAL;
10962306a36Sopenharmony_ci		}
11062306a36Sopenharmony_ci	} else {
11162306a36Sopenharmony_ci		switch (cmd) {
11262306a36Sopenharmony_ci		case SNDRV_PCM_TRIGGER_START:
11362306a36Sopenharmony_ci			regmap_update_bits(regmap_i2s,
11462306a36Sopenharmony_ci					   I2S_RX_IRQ_EN,
11562306a36Sopenharmony_ci					   I2S_RX_DESC_OFF_INTR_EN_MSK,
11662306a36Sopenharmony_ci					   I2S_RX_DESC_OFF_INTR_EN);
11762306a36Sopenharmony_ci			regmap_update_bits(regmap_i2s,
11862306a36Sopenharmony_ci					   I2S_RX_CFG,
11962306a36Sopenharmony_ci					   I2S_RX_ENABLE_MASK,
12062306a36Sopenharmony_ci					   I2S_RX_ENABLE);
12162306a36Sopenharmony_ci			break;
12262306a36Sopenharmony_ci		case SNDRV_PCM_TRIGGER_STOP:
12362306a36Sopenharmony_ci		case SNDRV_PCM_TRIGGER_SUSPEND:
12462306a36Sopenharmony_ci		case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
12562306a36Sopenharmony_ci			regmap_update_bits(regmap_i2s,
12662306a36Sopenharmony_ci					   I2S_RX_IRQ_EN,
12762306a36Sopenharmony_ci					   I2S_RX_DESC_OFF_INTR_EN_MSK,
12862306a36Sopenharmony_ci					   0);
12962306a36Sopenharmony_ci			regmap_update_bits(regmap_i2s,
13062306a36Sopenharmony_ci					   I2S_RX_CFG,
13162306a36Sopenharmony_ci					   I2S_RX_ENABLE_MASK,
13262306a36Sopenharmony_ci					   0);
13362306a36Sopenharmony_ci			break;
13462306a36Sopenharmony_ci		default:
13562306a36Sopenharmony_ci			ret = -EINVAL;
13662306a36Sopenharmony_ci		}
13762306a36Sopenharmony_ci	}
13862306a36Sopenharmony_ci	return ret;
13962306a36Sopenharmony_ci}
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_cistatic int bcm63xx_pcm_prepare(struct snd_soc_component *component,
14262306a36Sopenharmony_ci			struct snd_pcm_substream *substream)
14362306a36Sopenharmony_ci{
14462306a36Sopenharmony_ci	struct i2s_dma_desc	*dma_desc;
14562306a36Sopenharmony_ci	struct regmap		*regmap_i2s;
14662306a36Sopenharmony_ci	struct bcm_i2s_priv	*i2s_priv;
14762306a36Sopenharmony_ci	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
14862306a36Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
14962306a36Sopenharmony_ci	uint32_t regaddr_desclen, regaddr_descaddr;
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	dma_desc = snd_soc_dai_get_dma_data(asoc_rtd_to_cpu(rtd, 0), substream);
15262306a36Sopenharmony_ci	dma_desc->dma_len  = snd_pcm_lib_period_bytes(substream);
15362306a36Sopenharmony_ci	dma_desc->dma_addr = runtime->dma_addr;
15462306a36Sopenharmony_ci	dma_desc->dma_area = runtime->dma_area;
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
15762306a36Sopenharmony_ci		regaddr_desclen = I2S_TX_DESC_IFF_LEN;
15862306a36Sopenharmony_ci		regaddr_descaddr = I2S_TX_DESC_IFF_ADDR;
15962306a36Sopenharmony_ci	} else {
16062306a36Sopenharmony_ci		regaddr_desclen = I2S_RX_DESC_IFF_LEN;
16162306a36Sopenharmony_ci		regaddr_descaddr = I2S_RX_DESC_IFF_ADDR;
16262306a36Sopenharmony_ci	}
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	i2s_priv = dev_get_drvdata(asoc_rtd_to_cpu(rtd, 0)->dev);
16562306a36Sopenharmony_ci	regmap_i2s = i2s_priv->regmap_i2s;
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	regmap_write(regmap_i2s, regaddr_desclen, dma_desc->dma_len);
16862306a36Sopenharmony_ci	regmap_write(regmap_i2s, regaddr_descaddr, dma_desc->dma_addr);
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci	return 0;
17162306a36Sopenharmony_ci}
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_cistatic snd_pcm_uframes_t
17462306a36Sopenharmony_cibcm63xx_pcm_pointer(struct snd_soc_component *component,
17562306a36Sopenharmony_ci		struct snd_pcm_substream *substream)
17662306a36Sopenharmony_ci{
17762306a36Sopenharmony_ci	snd_pcm_uframes_t x;
17862306a36Sopenharmony_ci	struct bcm63xx_runtime_data *prtd = substream->runtime->private_data;
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	if (!prtd->dma_addr_next)
18162306a36Sopenharmony_ci		prtd->dma_addr_next = substream->runtime->dma_addr;
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	x = bytes_to_frames(substream->runtime,
18462306a36Sopenharmony_ci		prtd->dma_addr_next - substream->runtime->dma_addr);
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	return x == substream->runtime->buffer_size ? 0 : x;
18762306a36Sopenharmony_ci}
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_cistatic int bcm63xx_pcm_open(struct snd_soc_component *component,
19062306a36Sopenharmony_ci			struct snd_pcm_substream *substream)
19162306a36Sopenharmony_ci{
19262306a36Sopenharmony_ci	int ret = 0;
19362306a36Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
19462306a36Sopenharmony_ci	struct bcm63xx_runtime_data *prtd;
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci	runtime->hw = bcm63xx_pcm_hardware;
19762306a36Sopenharmony_ci	ret = snd_pcm_hw_constraint_step(runtime, 0,
19862306a36Sopenharmony_ci					 SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 32);
19962306a36Sopenharmony_ci	if (ret)
20062306a36Sopenharmony_ci		goto out;
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	ret = snd_pcm_hw_constraint_step(runtime, 0,
20362306a36Sopenharmony_ci					 SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 32);
20462306a36Sopenharmony_ci	if (ret)
20562306a36Sopenharmony_ci		goto out;
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci	ret = snd_pcm_hw_constraint_integer(runtime,
20862306a36Sopenharmony_ci					    SNDRV_PCM_HW_PARAM_PERIODS);
20962306a36Sopenharmony_ci	if (ret < 0)
21062306a36Sopenharmony_ci		goto out;
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci	ret = -ENOMEM;
21362306a36Sopenharmony_ci	prtd = kzalloc(sizeof(*prtd), GFP_KERNEL);
21462306a36Sopenharmony_ci	if (!prtd)
21562306a36Sopenharmony_ci		goto out;
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci	runtime->private_data = prtd;
21862306a36Sopenharmony_ci	return 0;
21962306a36Sopenharmony_ciout:
22062306a36Sopenharmony_ci	return ret;
22162306a36Sopenharmony_ci}
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_cistatic int bcm63xx_pcm_close(struct snd_soc_component *component,
22462306a36Sopenharmony_ci			struct snd_pcm_substream *substream)
22562306a36Sopenharmony_ci{
22662306a36Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
22762306a36Sopenharmony_ci	struct bcm63xx_runtime_data *prtd = runtime->private_data;
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci	kfree(prtd);
23062306a36Sopenharmony_ci	return 0;
23162306a36Sopenharmony_ci}
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_cistatic irqreturn_t i2s_dma_isr(int irq, void *bcm_i2s_priv)
23462306a36Sopenharmony_ci{
23562306a36Sopenharmony_ci	unsigned int availdepth, ifflevel, offlevel, int_status, val_1, val_2;
23662306a36Sopenharmony_ci	struct bcm63xx_runtime_data *prtd;
23762306a36Sopenharmony_ci	struct snd_pcm_substream *substream;
23862306a36Sopenharmony_ci	struct snd_pcm_runtime *runtime;
23962306a36Sopenharmony_ci	struct regmap *regmap_i2s;
24062306a36Sopenharmony_ci	struct i2s_dma_desc *dma_desc;
24162306a36Sopenharmony_ci	struct snd_soc_pcm_runtime *rtd;
24262306a36Sopenharmony_ci	struct bcm_i2s_priv *i2s_priv;
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci	i2s_priv = (struct bcm_i2s_priv *)bcm_i2s_priv;
24562306a36Sopenharmony_ci	regmap_i2s = i2s_priv->regmap_i2s;
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci	/* rx */
24862306a36Sopenharmony_ci	regmap_read(regmap_i2s, I2S_RX_IRQ_CTL, &int_status);
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci	if (int_status & I2S_RX_DESC_OFF_INTR_EN_MSK) {
25162306a36Sopenharmony_ci		substream = i2s_priv->capture_substream;
25262306a36Sopenharmony_ci		runtime = substream->runtime;
25362306a36Sopenharmony_ci		rtd = asoc_substream_to_rtd(substream);
25462306a36Sopenharmony_ci		prtd = runtime->private_data;
25562306a36Sopenharmony_ci		dma_desc = snd_soc_dai_get_dma_data(asoc_rtd_to_cpu(rtd, 0), substream);
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci		offlevel = (int_status & I2S_RX_DESC_OFF_LEVEL_MASK) >>
25862306a36Sopenharmony_ci			   I2S_RX_DESC_OFF_LEVEL_SHIFT;
25962306a36Sopenharmony_ci		while (offlevel) {
26062306a36Sopenharmony_ci			regmap_read(regmap_i2s, I2S_RX_DESC_OFF_ADDR, &val_1);
26162306a36Sopenharmony_ci			regmap_read(regmap_i2s, I2S_RX_DESC_OFF_LEN, &val_2);
26262306a36Sopenharmony_ci			offlevel--;
26362306a36Sopenharmony_ci		}
26462306a36Sopenharmony_ci		prtd->dma_addr_next = val_1 + val_2;
26562306a36Sopenharmony_ci		ifflevel = (int_status & I2S_RX_DESC_IFF_LEVEL_MASK) >>
26662306a36Sopenharmony_ci			   I2S_RX_DESC_IFF_LEVEL_SHIFT;
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci		availdepth = I2S_DESC_FIFO_DEPTH - ifflevel;
26962306a36Sopenharmony_ci		while (availdepth) {
27062306a36Sopenharmony_ci			dma_desc->dma_addr +=
27162306a36Sopenharmony_ci					snd_pcm_lib_period_bytes(substream);
27262306a36Sopenharmony_ci			dma_desc->dma_area +=
27362306a36Sopenharmony_ci					snd_pcm_lib_period_bytes(substream);
27462306a36Sopenharmony_ci			if (dma_desc->dma_addr - runtime->dma_addr >=
27562306a36Sopenharmony_ci						runtime->dma_bytes) {
27662306a36Sopenharmony_ci				dma_desc->dma_addr = runtime->dma_addr;
27762306a36Sopenharmony_ci				dma_desc->dma_area = runtime->dma_area;
27862306a36Sopenharmony_ci			}
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci			prtd->dma_addr = dma_desc->dma_addr;
28162306a36Sopenharmony_ci			regmap_write(regmap_i2s, I2S_RX_DESC_IFF_LEN,
28262306a36Sopenharmony_ci				     snd_pcm_lib_period_bytes(substream));
28362306a36Sopenharmony_ci			regmap_write(regmap_i2s, I2S_RX_DESC_IFF_ADDR,
28462306a36Sopenharmony_ci				     dma_desc->dma_addr);
28562306a36Sopenharmony_ci			availdepth--;
28662306a36Sopenharmony_ci		}
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci		snd_pcm_period_elapsed(substream);
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci		/* Clear interrupt by writing 0 */
29162306a36Sopenharmony_ci		regmap_update_bits(regmap_i2s, I2S_RX_IRQ_CTL,
29262306a36Sopenharmony_ci				   I2S_RX_INTR_MASK, 0);
29362306a36Sopenharmony_ci	}
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci	/* tx */
29662306a36Sopenharmony_ci	regmap_read(regmap_i2s, I2S_TX_IRQ_CTL, &int_status);
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci	if (int_status & I2S_TX_DESC_OFF_INTR_EN_MSK) {
29962306a36Sopenharmony_ci		substream = i2s_priv->play_substream;
30062306a36Sopenharmony_ci		runtime = substream->runtime;
30162306a36Sopenharmony_ci		rtd = asoc_substream_to_rtd(substream);
30262306a36Sopenharmony_ci		prtd = runtime->private_data;
30362306a36Sopenharmony_ci		dma_desc = snd_soc_dai_get_dma_data(asoc_rtd_to_cpu(rtd, 0), substream);
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci		offlevel = (int_status & I2S_TX_DESC_OFF_LEVEL_MASK) >>
30662306a36Sopenharmony_ci			   I2S_TX_DESC_OFF_LEVEL_SHIFT;
30762306a36Sopenharmony_ci		while (offlevel) {
30862306a36Sopenharmony_ci			regmap_read(regmap_i2s, I2S_TX_DESC_OFF_ADDR, &val_1);
30962306a36Sopenharmony_ci			regmap_read(regmap_i2s, I2S_TX_DESC_OFF_LEN,  &val_2);
31062306a36Sopenharmony_ci			prtd->dma_addr_next = val_1 + val_2;
31162306a36Sopenharmony_ci			offlevel--;
31262306a36Sopenharmony_ci		}
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci		ifflevel = (int_status & I2S_TX_DESC_IFF_LEVEL_MASK) >>
31562306a36Sopenharmony_ci			I2S_TX_DESC_IFF_LEVEL_SHIFT;
31662306a36Sopenharmony_ci		availdepth = I2S_DESC_FIFO_DEPTH - ifflevel;
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci		while (availdepth) {
31962306a36Sopenharmony_ci			dma_desc->dma_addr +=
32062306a36Sopenharmony_ci					snd_pcm_lib_period_bytes(substream);
32162306a36Sopenharmony_ci			dma_desc->dma_area +=
32262306a36Sopenharmony_ci					snd_pcm_lib_period_bytes(substream);
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci			if (dma_desc->dma_addr - runtime->dma_addr >=
32562306a36Sopenharmony_ci							runtime->dma_bytes) {
32662306a36Sopenharmony_ci				dma_desc->dma_addr = runtime->dma_addr;
32762306a36Sopenharmony_ci				dma_desc->dma_area = runtime->dma_area;
32862306a36Sopenharmony_ci			}
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci			prtd->dma_addr = dma_desc->dma_addr;
33162306a36Sopenharmony_ci			regmap_write(regmap_i2s, I2S_TX_DESC_IFF_LEN,
33262306a36Sopenharmony_ci				snd_pcm_lib_period_bytes(substream));
33362306a36Sopenharmony_ci			regmap_write(regmap_i2s, I2S_TX_DESC_IFF_ADDR,
33462306a36Sopenharmony_ci					dma_desc->dma_addr);
33562306a36Sopenharmony_ci			availdepth--;
33662306a36Sopenharmony_ci		}
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci		snd_pcm_period_elapsed(substream);
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci		/* Clear interrupt by writing 0 */
34162306a36Sopenharmony_ci		regmap_update_bits(regmap_i2s, I2S_TX_IRQ_CTL,
34262306a36Sopenharmony_ci				   I2S_TX_INTR_MASK, 0);
34362306a36Sopenharmony_ci	}
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci	return IRQ_HANDLED;
34662306a36Sopenharmony_ci}
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_cistatic int bcm63xx_soc_pcm_new(struct snd_soc_component *component,
34962306a36Sopenharmony_ci		struct snd_soc_pcm_runtime *rtd)
35062306a36Sopenharmony_ci{
35162306a36Sopenharmony_ci	struct snd_pcm *pcm = rtd->pcm;
35262306a36Sopenharmony_ci	struct bcm_i2s_priv *i2s_priv;
35362306a36Sopenharmony_ci	int ret;
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci	i2s_priv = dev_get_drvdata(asoc_rtd_to_cpu(rtd, 0)->dev);
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci	of_dma_configure(pcm->card->dev, pcm->card->dev->of_node, 1);
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci	ret = dma_coerce_mask_and_coherent(pcm->card->dev, DMA_BIT_MASK(32));
36062306a36Sopenharmony_ci	if (ret)
36162306a36Sopenharmony_ci		return ret;
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_ci	if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream)
36462306a36Sopenharmony_ci		i2s_priv->play_substream =
36562306a36Sopenharmony_ci			pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
36662306a36Sopenharmony_ci	if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream)
36762306a36Sopenharmony_ci		i2s_priv->capture_substream =
36862306a36Sopenharmony_ci			pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream;
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci	return snd_pcm_set_fixed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV_WC,
37162306a36Sopenharmony_ci					    pcm->card->dev,
37262306a36Sopenharmony_ci					    bcm63xx_pcm_hardware.buffer_bytes_max);
37362306a36Sopenharmony_ci}
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_cistatic const struct snd_soc_component_driver bcm63xx_soc_platform = {
37662306a36Sopenharmony_ci	.open = bcm63xx_pcm_open,
37762306a36Sopenharmony_ci	.close = bcm63xx_pcm_close,
37862306a36Sopenharmony_ci	.hw_params = bcm63xx_pcm_hw_params,
37962306a36Sopenharmony_ci	.hw_free = bcm63xx_pcm_hw_free,
38062306a36Sopenharmony_ci	.prepare = bcm63xx_pcm_prepare,
38162306a36Sopenharmony_ci	.trigger = bcm63xx_pcm_trigger,
38262306a36Sopenharmony_ci	.pointer = bcm63xx_pcm_pointer,
38362306a36Sopenharmony_ci	.pcm_construct = bcm63xx_soc_pcm_new,
38462306a36Sopenharmony_ci};
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ciint bcm63xx_soc_platform_probe(struct platform_device *pdev,
38762306a36Sopenharmony_ci			       struct bcm_i2s_priv *i2s_priv)
38862306a36Sopenharmony_ci{
38962306a36Sopenharmony_ci	int ret;
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_ci	ret = platform_get_irq(pdev, 0);
39262306a36Sopenharmony_ci	if (ret < 0)
39362306a36Sopenharmony_ci		return ret;
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ci	ret = devm_request_irq(&pdev->dev, ret, i2s_dma_isr,
39662306a36Sopenharmony_ci			       irq_get_trigger_type(ret), "i2s_dma", (void *)i2s_priv);
39762306a36Sopenharmony_ci	if (ret) {
39862306a36Sopenharmony_ci		dev_err(&pdev->dev,
39962306a36Sopenharmony_ci			"i2s_init: failed to request interrupt.ret=%d\n", ret);
40062306a36Sopenharmony_ci		return ret;
40162306a36Sopenharmony_ci	}
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci	return devm_snd_soc_register_component(&pdev->dev,
40462306a36Sopenharmony_ci					&bcm63xx_soc_platform, NULL, 0);
40562306a36Sopenharmony_ci}
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ciint bcm63xx_soc_platform_remove(struct platform_device *pdev)
40862306a36Sopenharmony_ci{
40962306a36Sopenharmony_ci	return 0;
41062306a36Sopenharmony_ci}
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_ciMODULE_AUTHOR("Kevin,Li <kevin-ke.li@broadcom.com>");
41362306a36Sopenharmony_ciMODULE_DESCRIPTION("Broadcom DSL XPON ASOC PCM Interface");
41462306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
415