xref: /linux/sound/soc/bcm/bcm63xx-pcm-whistler.c (revision 0ea5c948cb64bab5bc7a5516774eb8536f05aa0d)
188eb404cSKevin Li // SPDX-License-Identifier: GPL-2.0-or-later
288eb404cSKevin Li // linux/sound/bcm/bcm63xx-pcm-whistler.c
388eb404cSKevin Li // BCM63xx whistler pcm interface
488eb404cSKevin Li // Copyright (c) 2020 Broadcom Corporation
588eb404cSKevin Li // Author: Kevin-Ke Li <kevin-ke.li@broadcom.com>
688eb404cSKevin Li 
788eb404cSKevin Li #include <linux/dma-mapping.h>
888eb404cSKevin Li #include <linux/io.h>
95de035c2SLad Prabhakar #include <linux/irq.h>
1088eb404cSKevin Li #include <linux/module.h>
1188eb404cSKevin Li #include <sound/pcm_params.h>
1288eb404cSKevin Li #include <linux/regmap.h>
1388eb404cSKevin Li #include <linux/of_device.h>
1488eb404cSKevin Li #include <sound/soc.h>
1588eb404cSKevin Li #include "bcm63xx-i2s.h"
1688eb404cSKevin Li 
1788eb404cSKevin Li 
1888eb404cSKevin Li struct i2s_dma_desc {
1988eb404cSKevin Li 	unsigned char *dma_area;
2088eb404cSKevin Li 	dma_addr_t dma_addr;
2188eb404cSKevin Li 	unsigned int dma_len;
2288eb404cSKevin Li };
2388eb404cSKevin Li 
2488eb404cSKevin Li struct bcm63xx_runtime_data {
2588eb404cSKevin Li 	int dma_len;
2688eb404cSKevin Li 	dma_addr_t dma_addr;
2788eb404cSKevin Li 	dma_addr_t dma_addr_next;
2888eb404cSKevin Li };
2988eb404cSKevin Li 
3088eb404cSKevin Li static const struct snd_pcm_hardware bcm63xx_pcm_hardware = {
3188eb404cSKevin Li 	.info = SNDRV_PCM_INFO_MMAP |
3288eb404cSKevin Li 		SNDRV_PCM_INFO_MMAP_VALID |
3388eb404cSKevin Li 		SNDRV_PCM_INFO_INTERLEAVED |
3488eb404cSKevin Li 		SNDRV_PCM_INFO_PAUSE |
3588eb404cSKevin Li 		SNDRV_PCM_INFO_RESUME,
3688eb404cSKevin Li 	.formats = SNDRV_PCM_FMTBIT_S32_LE, /* support S32 only */
3788eb404cSKevin Li 	.period_bytes_max = 8192 - 32,
3888eb404cSKevin Li 	.periods_min = 1,
3988eb404cSKevin Li 	.periods_max = PAGE_SIZE/sizeof(struct i2s_dma_desc),
4088eb404cSKevin Li 	.buffer_bytes_max = 128 * 1024,
4188eb404cSKevin Li 	.fifo_size = 32,
4288eb404cSKevin Li };
4388eb404cSKevin Li 
bcm63xx_pcm_hw_params(struct snd_soc_component * component,struct snd_pcm_substream * substream,struct snd_pcm_hw_params * params)4488eb404cSKevin Li static int bcm63xx_pcm_hw_params(struct snd_soc_component *component,
4588eb404cSKevin Li 				 struct snd_pcm_substream *substream,
4688eb404cSKevin Li 				 struct snd_pcm_hw_params *params)
4788eb404cSKevin Li {
4888eb404cSKevin Li 	struct i2s_dma_desc *dma_desc;
49*aa435567SKuninori Morimoto 	struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
5088eb404cSKevin Li 
5188eb404cSKevin Li 	dma_desc = kzalloc(sizeof(*dma_desc), GFP_NOWAIT);
5288eb404cSKevin Li 	if (!dma_desc)
5388eb404cSKevin Li 		return -ENOMEM;
5488eb404cSKevin Li 
55*aa435567SKuninori Morimoto 	snd_soc_dai_set_dma_data(snd_soc_rtd_to_cpu(rtd, 0), substream, dma_desc);
5688eb404cSKevin Li 
5788eb404cSKevin Li 	return 0;
5888eb404cSKevin Li }
5988eb404cSKevin Li 
bcm63xx_pcm_hw_free(struct snd_soc_component * component,struct snd_pcm_substream * substream)6088eb404cSKevin Li static int bcm63xx_pcm_hw_free(struct snd_soc_component *component,
6188eb404cSKevin Li 			struct snd_pcm_substream *substream)
6288eb404cSKevin Li {
6388eb404cSKevin Li 	struct i2s_dma_desc	*dma_desc;
64*aa435567SKuninori Morimoto 	struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
6588eb404cSKevin Li 
66*aa435567SKuninori Morimoto 	dma_desc = snd_soc_dai_get_dma_data(snd_soc_rtd_to_cpu(rtd, 0), substream);
6788eb404cSKevin Li 	kfree(dma_desc);
6888eb404cSKevin Li 
6988eb404cSKevin Li 	return 0;
7088eb404cSKevin Li }
7188eb404cSKevin Li 
bcm63xx_pcm_trigger(struct snd_soc_component * component,struct snd_pcm_substream * substream,int cmd)7288eb404cSKevin Li static int bcm63xx_pcm_trigger(struct snd_soc_component *component,
7388eb404cSKevin Li 			       struct snd_pcm_substream *substream, int cmd)
7488eb404cSKevin Li {
7588eb404cSKevin Li 	int ret = 0;
7688eb404cSKevin Li 	struct snd_soc_pcm_runtime *rtd;
7788eb404cSKevin Li 	struct bcm_i2s_priv *i2s_priv;
7888eb404cSKevin Li 	struct regmap   *regmap_i2s;
7988eb404cSKevin Li 
80*aa435567SKuninori Morimoto 	rtd = snd_soc_substream_to_rtd(substream);
81*aa435567SKuninori Morimoto 	i2s_priv = dev_get_drvdata(snd_soc_rtd_to_cpu(rtd, 0)->dev);
8288eb404cSKevin Li 	regmap_i2s = i2s_priv->regmap_i2s;
8388eb404cSKevin Li 
8488eb404cSKevin Li 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
8588eb404cSKevin Li 		switch (cmd) {
8688eb404cSKevin Li 		case SNDRV_PCM_TRIGGER_START:
8788eb404cSKevin Li 			regmap_update_bits(regmap_i2s,
8888eb404cSKevin Li 					   I2S_TX_IRQ_EN,
8988eb404cSKevin Li 					   I2S_TX_DESC_OFF_INTR_EN,
9088eb404cSKevin Li 					   I2S_TX_DESC_OFF_INTR_EN);
9188eb404cSKevin Li 			regmap_update_bits(regmap_i2s,
9288eb404cSKevin Li 					   I2S_TX_CFG,
9388eb404cSKevin Li 					   I2S_TX_ENABLE_MASK,
9488eb404cSKevin Li 					   I2S_TX_ENABLE);
9588eb404cSKevin Li 			break;
9688eb404cSKevin Li 		case SNDRV_PCM_TRIGGER_STOP:
9788eb404cSKevin Li 		case SNDRV_PCM_TRIGGER_SUSPEND:
9888eb404cSKevin Li 		case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
9988eb404cSKevin Li 			regmap_write(regmap_i2s,
10088eb404cSKevin Li 				     I2S_TX_IRQ_EN,
10188eb404cSKevin Li 				     0);
10288eb404cSKevin Li 			regmap_update_bits(regmap_i2s,
10388eb404cSKevin Li 					   I2S_TX_CFG,
10488eb404cSKevin Li 					   I2S_TX_ENABLE_MASK,
10588eb404cSKevin Li 					   0);
10688eb404cSKevin Li 			break;
10788eb404cSKevin Li 		default:
10888eb404cSKevin Li 			ret = -EINVAL;
10988eb404cSKevin Li 		}
11088eb404cSKevin Li 	} else {
11188eb404cSKevin Li 		switch (cmd) {
11288eb404cSKevin Li 		case SNDRV_PCM_TRIGGER_START:
11388eb404cSKevin Li 			regmap_update_bits(regmap_i2s,
11488eb404cSKevin Li 					   I2S_RX_IRQ_EN,
11588eb404cSKevin Li 					   I2S_RX_DESC_OFF_INTR_EN_MSK,
11688eb404cSKevin Li 					   I2S_RX_DESC_OFF_INTR_EN);
11788eb404cSKevin Li 			regmap_update_bits(regmap_i2s,
11888eb404cSKevin Li 					   I2S_RX_CFG,
11988eb404cSKevin Li 					   I2S_RX_ENABLE_MASK,
12088eb404cSKevin Li 					   I2S_RX_ENABLE);
12188eb404cSKevin Li 			break;
12288eb404cSKevin Li 		case SNDRV_PCM_TRIGGER_STOP:
12388eb404cSKevin Li 		case SNDRV_PCM_TRIGGER_SUSPEND:
12488eb404cSKevin Li 		case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
12588eb404cSKevin Li 			regmap_update_bits(regmap_i2s,
12688eb404cSKevin Li 					   I2S_RX_IRQ_EN,
12788eb404cSKevin Li 					   I2S_RX_DESC_OFF_INTR_EN_MSK,
12888eb404cSKevin Li 					   0);
12988eb404cSKevin Li 			regmap_update_bits(regmap_i2s,
13088eb404cSKevin Li 					   I2S_RX_CFG,
13188eb404cSKevin Li 					   I2S_RX_ENABLE_MASK,
13288eb404cSKevin Li 					   0);
13388eb404cSKevin Li 			break;
13488eb404cSKevin Li 		default:
13588eb404cSKevin Li 			ret = -EINVAL;
13688eb404cSKevin Li 		}
13788eb404cSKevin Li 	}
13888eb404cSKevin Li 	return ret;
13988eb404cSKevin Li }
14088eb404cSKevin Li 
bcm63xx_pcm_prepare(struct snd_soc_component * component,struct snd_pcm_substream * substream)14188eb404cSKevin Li static int bcm63xx_pcm_prepare(struct snd_soc_component *component,
14288eb404cSKevin Li 			struct snd_pcm_substream *substream)
14388eb404cSKevin Li {
14488eb404cSKevin Li 	struct i2s_dma_desc	*dma_desc;
14588eb404cSKevin Li 	struct regmap		*regmap_i2s;
14688eb404cSKevin Li 	struct bcm_i2s_priv	*i2s_priv;
147*aa435567SKuninori Morimoto 	struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
14888eb404cSKevin Li 	struct snd_pcm_runtime *runtime = substream->runtime;
14988eb404cSKevin Li 	uint32_t regaddr_desclen, regaddr_descaddr;
15088eb404cSKevin Li 
151*aa435567SKuninori Morimoto 	dma_desc = snd_soc_dai_get_dma_data(snd_soc_rtd_to_cpu(rtd, 0), substream);
15288eb404cSKevin Li 	dma_desc->dma_len  = snd_pcm_lib_period_bytes(substream);
15388eb404cSKevin Li 	dma_desc->dma_addr = runtime->dma_addr;
15488eb404cSKevin Li 	dma_desc->dma_area = runtime->dma_area;
15588eb404cSKevin Li 
15688eb404cSKevin Li 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
15788eb404cSKevin Li 		regaddr_desclen = I2S_TX_DESC_IFF_LEN;
15888eb404cSKevin Li 		regaddr_descaddr = I2S_TX_DESC_IFF_ADDR;
15988eb404cSKevin Li 	} else {
16088eb404cSKevin Li 		regaddr_desclen = I2S_RX_DESC_IFF_LEN;
16188eb404cSKevin Li 		regaddr_descaddr = I2S_RX_DESC_IFF_ADDR;
16288eb404cSKevin Li 	}
16388eb404cSKevin Li 
164*aa435567SKuninori Morimoto 	i2s_priv = dev_get_drvdata(snd_soc_rtd_to_cpu(rtd, 0)->dev);
16588eb404cSKevin Li 	regmap_i2s = i2s_priv->regmap_i2s;
16688eb404cSKevin Li 
16788eb404cSKevin Li 	regmap_write(regmap_i2s, regaddr_desclen, dma_desc->dma_len);
16888eb404cSKevin Li 	regmap_write(regmap_i2s, regaddr_descaddr, dma_desc->dma_addr);
16988eb404cSKevin Li 
17088eb404cSKevin Li 	return 0;
17188eb404cSKevin Li }
17288eb404cSKevin Li 
17388eb404cSKevin Li static snd_pcm_uframes_t
bcm63xx_pcm_pointer(struct snd_soc_component * component,struct snd_pcm_substream * substream)17488eb404cSKevin Li bcm63xx_pcm_pointer(struct snd_soc_component *component,
17588eb404cSKevin Li 		struct snd_pcm_substream *substream)
17688eb404cSKevin Li {
17788eb404cSKevin Li 	snd_pcm_uframes_t x;
17888eb404cSKevin Li 	struct bcm63xx_runtime_data *prtd = substream->runtime->private_data;
17988eb404cSKevin Li 
18076385a66STakashi Iwai 	if (!prtd->dma_addr_next)
18188eb404cSKevin Li 		prtd->dma_addr_next = substream->runtime->dma_addr;
18288eb404cSKevin Li 
18388eb404cSKevin Li 	x = bytes_to_frames(substream->runtime,
18488eb404cSKevin Li 		prtd->dma_addr_next - substream->runtime->dma_addr);
18588eb404cSKevin Li 
18688eb404cSKevin Li 	return x == substream->runtime->buffer_size ? 0 : x;
18788eb404cSKevin Li }
18888eb404cSKevin Li 
bcm63xx_pcm_open(struct snd_soc_component * component,struct snd_pcm_substream * substream)18988eb404cSKevin Li static int bcm63xx_pcm_open(struct snd_soc_component *component,
19088eb404cSKevin Li 			struct snd_pcm_substream *substream)
19188eb404cSKevin Li {
19288eb404cSKevin Li 	int ret = 0;
19388eb404cSKevin Li 	struct snd_pcm_runtime *runtime = substream->runtime;
19488eb404cSKevin Li 	struct bcm63xx_runtime_data *prtd;
19588eb404cSKevin Li 
19688eb404cSKevin Li 	runtime->hw = bcm63xx_pcm_hardware;
19788eb404cSKevin Li 	ret = snd_pcm_hw_constraint_step(runtime, 0,
19888eb404cSKevin Li 					 SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 32);
19988eb404cSKevin Li 	if (ret)
20088eb404cSKevin Li 		goto out;
20188eb404cSKevin Li 
20288eb404cSKevin Li 	ret = snd_pcm_hw_constraint_step(runtime, 0,
20388eb404cSKevin Li 					 SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 32);
20488eb404cSKevin Li 	if (ret)
20588eb404cSKevin Li 		goto out;
20688eb404cSKevin Li 
20788eb404cSKevin Li 	ret = snd_pcm_hw_constraint_integer(runtime,
20888eb404cSKevin Li 					    SNDRV_PCM_HW_PARAM_PERIODS);
20988eb404cSKevin Li 	if (ret < 0)
21088eb404cSKevin Li 		goto out;
21188eb404cSKevin Li 
21288eb404cSKevin Li 	ret = -ENOMEM;
21388eb404cSKevin Li 	prtd = kzalloc(sizeof(*prtd), GFP_KERNEL);
21488eb404cSKevin Li 	if (!prtd)
21588eb404cSKevin Li 		goto out;
21688eb404cSKevin Li 
21788eb404cSKevin Li 	runtime->private_data = prtd;
21888eb404cSKevin Li 	return 0;
21988eb404cSKevin Li out:
22088eb404cSKevin Li 	return ret;
22188eb404cSKevin Li }
22288eb404cSKevin Li 
bcm63xx_pcm_close(struct snd_soc_component * component,struct snd_pcm_substream * substream)22388eb404cSKevin Li static int bcm63xx_pcm_close(struct snd_soc_component *component,
22488eb404cSKevin Li 			struct snd_pcm_substream *substream)
22588eb404cSKevin Li {
22688eb404cSKevin Li 	struct snd_pcm_runtime *runtime = substream->runtime;
22788eb404cSKevin Li 	struct bcm63xx_runtime_data *prtd = runtime->private_data;
22888eb404cSKevin Li 
22988eb404cSKevin Li 	kfree(prtd);
23088eb404cSKevin Li 	return 0;
23188eb404cSKevin Li }
23288eb404cSKevin Li 
i2s_dma_isr(int irq,void * bcm_i2s_priv)23388eb404cSKevin Li static irqreturn_t i2s_dma_isr(int irq, void *bcm_i2s_priv)
23488eb404cSKevin Li {
23588eb404cSKevin Li 	unsigned int availdepth, ifflevel, offlevel, int_status, val_1, val_2;
23688eb404cSKevin Li 	struct bcm63xx_runtime_data *prtd;
23788eb404cSKevin Li 	struct snd_pcm_substream *substream;
23888eb404cSKevin Li 	struct snd_pcm_runtime *runtime;
23988eb404cSKevin Li 	struct regmap *regmap_i2s;
24088eb404cSKevin Li 	struct i2s_dma_desc *dma_desc;
24188eb404cSKevin Li 	struct snd_soc_pcm_runtime *rtd;
24288eb404cSKevin Li 	struct bcm_i2s_priv *i2s_priv;
24388eb404cSKevin Li 
24488eb404cSKevin Li 	i2s_priv = (struct bcm_i2s_priv *)bcm_i2s_priv;
24588eb404cSKevin Li 	regmap_i2s = i2s_priv->regmap_i2s;
24688eb404cSKevin Li 
24788eb404cSKevin Li 	/* rx */
24888eb404cSKevin Li 	regmap_read(regmap_i2s, I2S_RX_IRQ_CTL, &int_status);
24988eb404cSKevin Li 
25088eb404cSKevin Li 	if (int_status & I2S_RX_DESC_OFF_INTR_EN_MSK) {
25188eb404cSKevin Li 		substream = i2s_priv->capture_substream;
25288eb404cSKevin Li 		runtime = substream->runtime;
253*aa435567SKuninori Morimoto 		rtd = snd_soc_substream_to_rtd(substream);
25488eb404cSKevin Li 		prtd = runtime->private_data;
255*aa435567SKuninori Morimoto 		dma_desc = snd_soc_dai_get_dma_data(snd_soc_rtd_to_cpu(rtd, 0), substream);
25688eb404cSKevin Li 
25788eb404cSKevin Li 		offlevel = (int_status & I2S_RX_DESC_OFF_LEVEL_MASK) >>
25888eb404cSKevin Li 			   I2S_RX_DESC_OFF_LEVEL_SHIFT;
25988eb404cSKevin Li 		while (offlevel) {
26088eb404cSKevin Li 			regmap_read(regmap_i2s, I2S_RX_DESC_OFF_ADDR, &val_1);
26188eb404cSKevin Li 			regmap_read(regmap_i2s, I2S_RX_DESC_OFF_LEN, &val_2);
26288eb404cSKevin Li 			offlevel--;
26388eb404cSKevin Li 		}
26488eb404cSKevin Li 		prtd->dma_addr_next = val_1 + val_2;
26588eb404cSKevin Li 		ifflevel = (int_status & I2S_RX_DESC_IFF_LEVEL_MASK) >>
26688eb404cSKevin Li 			   I2S_RX_DESC_IFF_LEVEL_SHIFT;
26788eb404cSKevin Li 
26888eb404cSKevin Li 		availdepth = I2S_DESC_FIFO_DEPTH - ifflevel;
26988eb404cSKevin Li 		while (availdepth) {
27088eb404cSKevin Li 			dma_desc->dma_addr +=
27188eb404cSKevin Li 					snd_pcm_lib_period_bytes(substream);
27288eb404cSKevin Li 			dma_desc->dma_area +=
27388eb404cSKevin Li 					snd_pcm_lib_period_bytes(substream);
27488eb404cSKevin Li 			if (dma_desc->dma_addr - runtime->dma_addr >=
27588eb404cSKevin Li 						runtime->dma_bytes) {
27688eb404cSKevin Li 				dma_desc->dma_addr = runtime->dma_addr;
27788eb404cSKevin Li 				dma_desc->dma_area = runtime->dma_area;
27888eb404cSKevin Li 			}
27988eb404cSKevin Li 
28088eb404cSKevin Li 			prtd->dma_addr = dma_desc->dma_addr;
28188eb404cSKevin Li 			regmap_write(regmap_i2s, I2S_RX_DESC_IFF_LEN,
28288eb404cSKevin Li 				     snd_pcm_lib_period_bytes(substream));
28388eb404cSKevin Li 			regmap_write(regmap_i2s, I2S_RX_DESC_IFF_ADDR,
28488eb404cSKevin Li 				     dma_desc->dma_addr);
28588eb404cSKevin Li 			availdepth--;
28688eb404cSKevin Li 		}
28788eb404cSKevin Li 
28888eb404cSKevin Li 		snd_pcm_period_elapsed(substream);
28988eb404cSKevin Li 
29088eb404cSKevin Li 		/* Clear interrupt by writing 0 */
29188eb404cSKevin Li 		regmap_update_bits(regmap_i2s, I2S_RX_IRQ_CTL,
29288eb404cSKevin Li 				   I2S_RX_INTR_MASK, 0);
29388eb404cSKevin Li 	}
29488eb404cSKevin Li 
29588eb404cSKevin Li 	/* tx */
29688eb404cSKevin Li 	regmap_read(regmap_i2s, I2S_TX_IRQ_CTL, &int_status);
29788eb404cSKevin Li 
29888eb404cSKevin Li 	if (int_status & I2S_TX_DESC_OFF_INTR_EN_MSK) {
29988eb404cSKevin Li 		substream = i2s_priv->play_substream;
30088eb404cSKevin Li 		runtime = substream->runtime;
301*aa435567SKuninori Morimoto 		rtd = snd_soc_substream_to_rtd(substream);
30288eb404cSKevin Li 		prtd = runtime->private_data;
303*aa435567SKuninori Morimoto 		dma_desc = snd_soc_dai_get_dma_data(snd_soc_rtd_to_cpu(rtd, 0), substream);
30488eb404cSKevin Li 
30588eb404cSKevin Li 		offlevel = (int_status & I2S_TX_DESC_OFF_LEVEL_MASK) >>
30688eb404cSKevin Li 			   I2S_TX_DESC_OFF_LEVEL_SHIFT;
30788eb404cSKevin Li 		while (offlevel) {
30888eb404cSKevin Li 			regmap_read(regmap_i2s, I2S_TX_DESC_OFF_ADDR, &val_1);
30988eb404cSKevin Li 			regmap_read(regmap_i2s, I2S_TX_DESC_OFF_LEN,  &val_2);
31088eb404cSKevin Li 			prtd->dma_addr_next = val_1 + val_2;
31188eb404cSKevin Li 			offlevel--;
31288eb404cSKevin Li 		}
31388eb404cSKevin Li 
31488eb404cSKevin Li 		ifflevel = (int_status & I2S_TX_DESC_IFF_LEVEL_MASK) >>
31588eb404cSKevin Li 			I2S_TX_DESC_IFF_LEVEL_SHIFT;
31688eb404cSKevin Li 		availdepth = I2S_DESC_FIFO_DEPTH - ifflevel;
31788eb404cSKevin Li 
31888eb404cSKevin Li 		while (availdepth) {
31988eb404cSKevin Li 			dma_desc->dma_addr +=
32088eb404cSKevin Li 					snd_pcm_lib_period_bytes(substream);
32188eb404cSKevin Li 			dma_desc->dma_area +=
32288eb404cSKevin Li 					snd_pcm_lib_period_bytes(substream);
32388eb404cSKevin Li 
32488eb404cSKevin Li 			if (dma_desc->dma_addr - runtime->dma_addr >=
32588eb404cSKevin Li 							runtime->dma_bytes) {
32688eb404cSKevin Li 				dma_desc->dma_addr = runtime->dma_addr;
32788eb404cSKevin Li 				dma_desc->dma_area = runtime->dma_area;
32888eb404cSKevin Li 			}
32988eb404cSKevin Li 
33088eb404cSKevin Li 			prtd->dma_addr = dma_desc->dma_addr;
33188eb404cSKevin Li 			regmap_write(regmap_i2s, I2S_TX_DESC_IFF_LEN,
33288eb404cSKevin Li 				snd_pcm_lib_period_bytes(substream));
33388eb404cSKevin Li 			regmap_write(regmap_i2s, I2S_TX_DESC_IFF_ADDR,
33488eb404cSKevin Li 					dma_desc->dma_addr);
33588eb404cSKevin Li 			availdepth--;
33688eb404cSKevin Li 		}
33788eb404cSKevin Li 
33888eb404cSKevin Li 		snd_pcm_period_elapsed(substream);
33988eb404cSKevin Li 
34088eb404cSKevin Li 		/* Clear interrupt by writing 0 */
34188eb404cSKevin Li 		regmap_update_bits(regmap_i2s, I2S_TX_IRQ_CTL,
34288eb404cSKevin Li 				   I2S_TX_INTR_MASK, 0);
34388eb404cSKevin Li 	}
34488eb404cSKevin Li 
34588eb404cSKevin Li 	return IRQ_HANDLED;
34688eb404cSKevin Li }
34788eb404cSKevin Li 
bcm63xx_soc_pcm_new(struct snd_soc_component * component,struct snd_soc_pcm_runtime * rtd)34888eb404cSKevin Li static int bcm63xx_soc_pcm_new(struct snd_soc_component *component,
34988eb404cSKevin Li 		struct snd_soc_pcm_runtime *rtd)
35088eb404cSKevin Li {
35188eb404cSKevin Li 	struct snd_pcm *pcm = rtd->pcm;
35288eb404cSKevin Li 	struct bcm_i2s_priv *i2s_priv;
35388eb404cSKevin Li 	int ret;
35488eb404cSKevin Li 
355*aa435567SKuninori Morimoto 	i2s_priv = dev_get_drvdata(snd_soc_rtd_to_cpu(rtd, 0)->dev);
35688eb404cSKevin Li 
35788eb404cSKevin Li 	of_dma_configure(pcm->card->dev, pcm->card->dev->of_node, 1);
35888eb404cSKevin Li 
35988eb404cSKevin Li 	ret = dma_coerce_mask_and_coherent(pcm->card->dev, DMA_BIT_MASK(32));
36088eb404cSKevin Li 	if (ret)
36113ce4d8fSTakashi Iwai 		return ret;
36288eb404cSKevin Li 
36313ce4d8fSTakashi Iwai 	if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream)
36488eb404cSKevin Li 		i2s_priv->play_substream =
36588eb404cSKevin Li 			pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
36613ce4d8fSTakashi Iwai 	if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream)
36788eb404cSKevin Li 		i2s_priv->capture_substream =
36888eb404cSKevin Li 			pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream;
36988eb404cSKevin Li 
37013ce4d8fSTakashi Iwai 	return snd_pcm_set_fixed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV_WC,
37113ce4d8fSTakashi Iwai 					    pcm->card->dev,
37213ce4d8fSTakashi Iwai 					    bcm63xx_pcm_hardware.buffer_bytes_max);
37388eb404cSKevin Li }
37488eb404cSKevin Li 
37588eb404cSKevin Li static const struct snd_soc_component_driver bcm63xx_soc_platform = {
37688eb404cSKevin Li 	.open = bcm63xx_pcm_open,
37788eb404cSKevin Li 	.close = bcm63xx_pcm_close,
37888eb404cSKevin Li 	.hw_params = bcm63xx_pcm_hw_params,
37988eb404cSKevin Li 	.hw_free = bcm63xx_pcm_hw_free,
38088eb404cSKevin Li 	.prepare = bcm63xx_pcm_prepare,
38188eb404cSKevin Li 	.trigger = bcm63xx_pcm_trigger,
38288eb404cSKevin Li 	.pointer = bcm63xx_pcm_pointer,
38388eb404cSKevin Li 	.pcm_construct = bcm63xx_soc_pcm_new,
38488eb404cSKevin Li };
38588eb404cSKevin Li 
bcm63xx_soc_platform_probe(struct platform_device * pdev,struct bcm_i2s_priv * i2s_priv)38688eb404cSKevin Li int bcm63xx_soc_platform_probe(struct platform_device *pdev,
38788eb404cSKevin Li 			       struct bcm_i2s_priv *i2s_priv)
38888eb404cSKevin Li {
38988eb404cSKevin Li 	int ret;
39088eb404cSKevin Li 
3915de035c2SLad Prabhakar 	ret = platform_get_irq(pdev, 0);
3925de035c2SLad Prabhakar 	if (ret < 0)
3935de035c2SLad Prabhakar 		return ret;
39488eb404cSKevin Li 
3955de035c2SLad Prabhakar 	ret = devm_request_irq(&pdev->dev, ret, i2s_dma_isr,
3965de035c2SLad Prabhakar 			       irq_get_trigger_type(ret), "i2s_dma", (void *)i2s_priv);
39788eb404cSKevin Li 	if (ret) {
39888eb404cSKevin Li 		dev_err(&pdev->dev,
39988eb404cSKevin Li 			"i2s_init: failed to request interrupt.ret=%d\n", ret);
40088eb404cSKevin Li 		return ret;
40188eb404cSKevin Li 	}
40288eb404cSKevin Li 
40388eb404cSKevin Li 	return devm_snd_soc_register_component(&pdev->dev,
40488eb404cSKevin Li 					&bcm63xx_soc_platform, NULL, 0);
40588eb404cSKevin Li }
40688eb404cSKevin Li 
bcm63xx_soc_platform_remove(struct platform_device * pdev)40788eb404cSKevin Li int bcm63xx_soc_platform_remove(struct platform_device *pdev)
40888eb404cSKevin Li {
40988eb404cSKevin Li 	return 0;
41088eb404cSKevin Li }
41188eb404cSKevin Li 
41288eb404cSKevin Li MODULE_AUTHOR("Kevin,Li <kevin-ke.li@broadcom.com>");
41388eb404cSKevin Li MODULE_DESCRIPTION("Broadcom DSL XPON ASOC PCM Interface");
41488eb404cSKevin Li MODULE_LICENSE("GPL v2");
415