18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci// linux/sound/bcm/bcm63xx-pcm-whistler.c
38c2ecf20Sopenharmony_ci// BCM63xx whistler pcm interface
48c2ecf20Sopenharmony_ci// Copyright (c) 2020 Broadcom Corporation
58c2ecf20Sopenharmony_ci// Author: Kevin-Ke Li <kevin-ke.li@broadcom.com>
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h>
88c2ecf20Sopenharmony_ci#include <linux/io.h>
98c2ecf20Sopenharmony_ci#include <linux/module.h>
108c2ecf20Sopenharmony_ci#include <sound/pcm_params.h>
118c2ecf20Sopenharmony_ci#include <linux/regmap.h>
128c2ecf20Sopenharmony_ci#include <linux/of_device.h>
138c2ecf20Sopenharmony_ci#include <sound/soc.h>
148c2ecf20Sopenharmony_ci#include "bcm63xx-i2s.h"
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_cistruct i2s_dma_desc {
188c2ecf20Sopenharmony_ci	unsigned char *dma_area;
198c2ecf20Sopenharmony_ci	dma_addr_t dma_addr;
208c2ecf20Sopenharmony_ci	unsigned int dma_len;
218c2ecf20Sopenharmony_ci};
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_cistruct bcm63xx_runtime_data {
248c2ecf20Sopenharmony_ci	int dma_len;
258c2ecf20Sopenharmony_ci	dma_addr_t dma_addr;
268c2ecf20Sopenharmony_ci	dma_addr_t dma_addr_next;
278c2ecf20Sopenharmony_ci};
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_cistatic const struct snd_pcm_hardware bcm63xx_pcm_hardware = {
308c2ecf20Sopenharmony_ci	.info = SNDRV_PCM_INFO_MMAP |
318c2ecf20Sopenharmony_ci		SNDRV_PCM_INFO_MMAP_VALID |
328c2ecf20Sopenharmony_ci		SNDRV_PCM_INFO_INTERLEAVED |
338c2ecf20Sopenharmony_ci		SNDRV_PCM_INFO_PAUSE |
348c2ecf20Sopenharmony_ci		SNDRV_PCM_INFO_RESUME,
358c2ecf20Sopenharmony_ci	.formats = SNDRV_PCM_FMTBIT_S32_LE, /* support S32 only */
368c2ecf20Sopenharmony_ci	.period_bytes_max = 8192 - 32,
378c2ecf20Sopenharmony_ci	.periods_min = 1,
388c2ecf20Sopenharmony_ci	.periods_max = PAGE_SIZE/sizeof(struct i2s_dma_desc),
398c2ecf20Sopenharmony_ci	.buffer_bytes_max = 128 * 1024,
408c2ecf20Sopenharmony_ci	.fifo_size = 32,
418c2ecf20Sopenharmony_ci};
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_cistatic int bcm63xx_pcm_hw_params(struct snd_soc_component *component,
448c2ecf20Sopenharmony_ci				 struct snd_pcm_substream *substream,
458c2ecf20Sopenharmony_ci				 struct snd_pcm_hw_params *params)
468c2ecf20Sopenharmony_ci{
478c2ecf20Sopenharmony_ci	struct i2s_dma_desc *dma_desc;
488c2ecf20Sopenharmony_ci	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
498c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci	snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
528c2ecf20Sopenharmony_ci	runtime->dma_bytes = params_buffer_bytes(params);
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci	dma_desc = kzalloc(sizeof(*dma_desc), GFP_NOWAIT);
558c2ecf20Sopenharmony_ci	if (!dma_desc)
568c2ecf20Sopenharmony_ci		return -ENOMEM;
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci	snd_soc_dai_set_dma_data(asoc_rtd_to_cpu(rtd, 0), substream, dma_desc);
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci	return 0;
618c2ecf20Sopenharmony_ci}
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_cistatic int bcm63xx_pcm_hw_free(struct snd_soc_component *component,
648c2ecf20Sopenharmony_ci			struct snd_pcm_substream *substream)
658c2ecf20Sopenharmony_ci{
668c2ecf20Sopenharmony_ci	struct i2s_dma_desc	*dma_desc;
678c2ecf20Sopenharmony_ci	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	dma_desc = snd_soc_dai_get_dma_data(asoc_rtd_to_cpu(rtd, 0), substream);
708c2ecf20Sopenharmony_ci	kfree(dma_desc);
718c2ecf20Sopenharmony_ci	snd_pcm_set_runtime_buffer(substream, NULL);
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci	return 0;
748c2ecf20Sopenharmony_ci}
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_cistatic int bcm63xx_pcm_trigger(struct snd_soc_component *component,
778c2ecf20Sopenharmony_ci			       struct snd_pcm_substream *substream, int cmd)
788c2ecf20Sopenharmony_ci{
798c2ecf20Sopenharmony_ci	int ret = 0;
808c2ecf20Sopenharmony_ci	struct snd_soc_pcm_runtime *rtd;
818c2ecf20Sopenharmony_ci	struct bcm_i2s_priv *i2s_priv;
828c2ecf20Sopenharmony_ci	struct regmap   *regmap_i2s;
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci	rtd = asoc_substream_to_rtd(substream);
858c2ecf20Sopenharmony_ci	i2s_priv = dev_get_drvdata(asoc_rtd_to_cpu(rtd, 0)->dev);
868c2ecf20Sopenharmony_ci	regmap_i2s = i2s_priv->regmap_i2s;
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
898c2ecf20Sopenharmony_ci		switch (cmd) {
908c2ecf20Sopenharmony_ci		case SNDRV_PCM_TRIGGER_START:
918c2ecf20Sopenharmony_ci			regmap_update_bits(regmap_i2s,
928c2ecf20Sopenharmony_ci					   I2S_TX_IRQ_EN,
938c2ecf20Sopenharmony_ci					   I2S_TX_DESC_OFF_INTR_EN,
948c2ecf20Sopenharmony_ci					   I2S_TX_DESC_OFF_INTR_EN);
958c2ecf20Sopenharmony_ci			regmap_update_bits(regmap_i2s,
968c2ecf20Sopenharmony_ci					   I2S_TX_CFG,
978c2ecf20Sopenharmony_ci					   I2S_TX_ENABLE_MASK,
988c2ecf20Sopenharmony_ci					   I2S_TX_ENABLE);
998c2ecf20Sopenharmony_ci			break;
1008c2ecf20Sopenharmony_ci		case SNDRV_PCM_TRIGGER_STOP:
1018c2ecf20Sopenharmony_ci		case SNDRV_PCM_TRIGGER_SUSPEND:
1028c2ecf20Sopenharmony_ci		case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
1038c2ecf20Sopenharmony_ci			regmap_write(regmap_i2s,
1048c2ecf20Sopenharmony_ci				     I2S_TX_IRQ_EN,
1058c2ecf20Sopenharmony_ci				     0);
1068c2ecf20Sopenharmony_ci			regmap_update_bits(regmap_i2s,
1078c2ecf20Sopenharmony_ci					   I2S_TX_CFG,
1088c2ecf20Sopenharmony_ci					   I2S_TX_ENABLE_MASK,
1098c2ecf20Sopenharmony_ci					   0);
1108c2ecf20Sopenharmony_ci			break;
1118c2ecf20Sopenharmony_ci		default:
1128c2ecf20Sopenharmony_ci			ret = -EINVAL;
1138c2ecf20Sopenharmony_ci		}
1148c2ecf20Sopenharmony_ci	} else {
1158c2ecf20Sopenharmony_ci		switch (cmd) {
1168c2ecf20Sopenharmony_ci		case SNDRV_PCM_TRIGGER_START:
1178c2ecf20Sopenharmony_ci			regmap_update_bits(regmap_i2s,
1188c2ecf20Sopenharmony_ci					   I2S_RX_IRQ_EN,
1198c2ecf20Sopenharmony_ci					   I2S_RX_DESC_OFF_INTR_EN_MSK,
1208c2ecf20Sopenharmony_ci					   I2S_RX_DESC_OFF_INTR_EN);
1218c2ecf20Sopenharmony_ci			regmap_update_bits(regmap_i2s,
1228c2ecf20Sopenharmony_ci					   I2S_RX_CFG,
1238c2ecf20Sopenharmony_ci					   I2S_RX_ENABLE_MASK,
1248c2ecf20Sopenharmony_ci					   I2S_RX_ENABLE);
1258c2ecf20Sopenharmony_ci			break;
1268c2ecf20Sopenharmony_ci		case SNDRV_PCM_TRIGGER_STOP:
1278c2ecf20Sopenharmony_ci		case SNDRV_PCM_TRIGGER_SUSPEND:
1288c2ecf20Sopenharmony_ci		case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
1298c2ecf20Sopenharmony_ci			regmap_update_bits(regmap_i2s,
1308c2ecf20Sopenharmony_ci					   I2S_RX_IRQ_EN,
1318c2ecf20Sopenharmony_ci					   I2S_RX_DESC_OFF_INTR_EN_MSK,
1328c2ecf20Sopenharmony_ci					   0);
1338c2ecf20Sopenharmony_ci			regmap_update_bits(regmap_i2s,
1348c2ecf20Sopenharmony_ci					   I2S_RX_CFG,
1358c2ecf20Sopenharmony_ci					   I2S_RX_ENABLE_MASK,
1368c2ecf20Sopenharmony_ci					   0);
1378c2ecf20Sopenharmony_ci			break;
1388c2ecf20Sopenharmony_ci		default:
1398c2ecf20Sopenharmony_ci			ret = -EINVAL;
1408c2ecf20Sopenharmony_ci		}
1418c2ecf20Sopenharmony_ci	}
1428c2ecf20Sopenharmony_ci	return ret;
1438c2ecf20Sopenharmony_ci}
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_cistatic int bcm63xx_pcm_prepare(struct snd_soc_component *component,
1468c2ecf20Sopenharmony_ci			struct snd_pcm_substream *substream)
1478c2ecf20Sopenharmony_ci{
1488c2ecf20Sopenharmony_ci	struct i2s_dma_desc	*dma_desc;
1498c2ecf20Sopenharmony_ci	struct regmap		*regmap_i2s;
1508c2ecf20Sopenharmony_ci	struct bcm_i2s_priv	*i2s_priv;
1518c2ecf20Sopenharmony_ci	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
1528c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
1538c2ecf20Sopenharmony_ci	uint32_t regaddr_desclen, regaddr_descaddr;
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci	dma_desc = snd_soc_dai_get_dma_data(asoc_rtd_to_cpu(rtd, 0), substream);
1568c2ecf20Sopenharmony_ci	dma_desc->dma_len  = snd_pcm_lib_period_bytes(substream);
1578c2ecf20Sopenharmony_ci	dma_desc->dma_addr = runtime->dma_addr;
1588c2ecf20Sopenharmony_ci	dma_desc->dma_area = runtime->dma_area;
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
1618c2ecf20Sopenharmony_ci		regaddr_desclen = I2S_TX_DESC_IFF_LEN;
1628c2ecf20Sopenharmony_ci		regaddr_descaddr = I2S_TX_DESC_IFF_ADDR;
1638c2ecf20Sopenharmony_ci	} else {
1648c2ecf20Sopenharmony_ci		regaddr_desclen = I2S_RX_DESC_IFF_LEN;
1658c2ecf20Sopenharmony_ci		regaddr_descaddr = I2S_RX_DESC_IFF_ADDR;
1668c2ecf20Sopenharmony_ci	}
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci	i2s_priv = dev_get_drvdata(asoc_rtd_to_cpu(rtd, 0)->dev);
1698c2ecf20Sopenharmony_ci	regmap_i2s = i2s_priv->regmap_i2s;
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci	regmap_write(regmap_i2s, regaddr_desclen, dma_desc->dma_len);
1728c2ecf20Sopenharmony_ci	regmap_write(regmap_i2s, regaddr_descaddr, dma_desc->dma_addr);
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci	return 0;
1758c2ecf20Sopenharmony_ci}
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_cistatic snd_pcm_uframes_t
1788c2ecf20Sopenharmony_cibcm63xx_pcm_pointer(struct snd_soc_component *component,
1798c2ecf20Sopenharmony_ci		struct snd_pcm_substream *substream)
1808c2ecf20Sopenharmony_ci{
1818c2ecf20Sopenharmony_ci	snd_pcm_uframes_t x;
1828c2ecf20Sopenharmony_ci	struct bcm63xx_runtime_data *prtd = substream->runtime->private_data;
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci	if (!prtd->dma_addr_next)
1858c2ecf20Sopenharmony_ci		prtd->dma_addr_next = substream->runtime->dma_addr;
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci	x = bytes_to_frames(substream->runtime,
1888c2ecf20Sopenharmony_ci		prtd->dma_addr_next - substream->runtime->dma_addr);
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci	return x == substream->runtime->buffer_size ? 0 : x;
1918c2ecf20Sopenharmony_ci}
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_cistatic int bcm63xx_pcm_mmap(struct snd_soc_component *component,
1948c2ecf20Sopenharmony_ci				struct snd_pcm_substream *substream,
1958c2ecf20Sopenharmony_ci				struct vm_area_struct *vma)
1968c2ecf20Sopenharmony_ci{
1978c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci	return  dma_mmap_wc(substream->pcm->card->dev, vma,
2008c2ecf20Sopenharmony_ci			    runtime->dma_area,
2018c2ecf20Sopenharmony_ci			    runtime->dma_addr,
2028c2ecf20Sopenharmony_ci			    runtime->dma_bytes);
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci}
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_cistatic int bcm63xx_pcm_open(struct snd_soc_component *component,
2078c2ecf20Sopenharmony_ci			struct snd_pcm_substream *substream)
2088c2ecf20Sopenharmony_ci{
2098c2ecf20Sopenharmony_ci	int ret = 0;
2108c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
2118c2ecf20Sopenharmony_ci	struct bcm63xx_runtime_data *prtd;
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci	runtime->hw = bcm63xx_pcm_hardware;
2148c2ecf20Sopenharmony_ci	ret = snd_pcm_hw_constraint_step(runtime, 0,
2158c2ecf20Sopenharmony_ci					 SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 32);
2168c2ecf20Sopenharmony_ci	if (ret)
2178c2ecf20Sopenharmony_ci		goto out;
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci	ret = snd_pcm_hw_constraint_step(runtime, 0,
2208c2ecf20Sopenharmony_ci					 SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 32);
2218c2ecf20Sopenharmony_ci	if (ret)
2228c2ecf20Sopenharmony_ci		goto out;
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci	ret = snd_pcm_hw_constraint_integer(runtime,
2258c2ecf20Sopenharmony_ci					    SNDRV_PCM_HW_PARAM_PERIODS);
2268c2ecf20Sopenharmony_ci	if (ret < 0)
2278c2ecf20Sopenharmony_ci		goto out;
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ci	ret = -ENOMEM;
2308c2ecf20Sopenharmony_ci	prtd = kzalloc(sizeof(*prtd), GFP_KERNEL);
2318c2ecf20Sopenharmony_ci	if (!prtd)
2328c2ecf20Sopenharmony_ci		goto out;
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci	runtime->private_data = prtd;
2358c2ecf20Sopenharmony_ci	return 0;
2368c2ecf20Sopenharmony_ciout:
2378c2ecf20Sopenharmony_ci	return ret;
2388c2ecf20Sopenharmony_ci}
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_cistatic int bcm63xx_pcm_close(struct snd_soc_component *component,
2418c2ecf20Sopenharmony_ci			struct snd_pcm_substream *substream)
2428c2ecf20Sopenharmony_ci{
2438c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
2448c2ecf20Sopenharmony_ci	struct bcm63xx_runtime_data *prtd = runtime->private_data;
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci	kfree(prtd);
2478c2ecf20Sopenharmony_ci	return 0;
2488c2ecf20Sopenharmony_ci}
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_cistatic irqreturn_t i2s_dma_isr(int irq, void *bcm_i2s_priv)
2518c2ecf20Sopenharmony_ci{
2528c2ecf20Sopenharmony_ci	unsigned int availdepth, ifflevel, offlevel, int_status, val_1, val_2;
2538c2ecf20Sopenharmony_ci	struct bcm63xx_runtime_data *prtd;
2548c2ecf20Sopenharmony_ci	struct snd_pcm_substream *substream;
2558c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime;
2568c2ecf20Sopenharmony_ci	struct regmap *regmap_i2s;
2578c2ecf20Sopenharmony_ci	struct i2s_dma_desc *dma_desc;
2588c2ecf20Sopenharmony_ci	struct snd_soc_pcm_runtime *rtd;
2598c2ecf20Sopenharmony_ci	struct bcm_i2s_priv *i2s_priv;
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ci	i2s_priv = (struct bcm_i2s_priv *)bcm_i2s_priv;
2628c2ecf20Sopenharmony_ci	regmap_i2s = i2s_priv->regmap_i2s;
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci	/* rx */
2658c2ecf20Sopenharmony_ci	regmap_read(regmap_i2s, I2S_RX_IRQ_CTL, &int_status);
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci	if (int_status & I2S_RX_DESC_OFF_INTR_EN_MSK) {
2688c2ecf20Sopenharmony_ci		substream = i2s_priv->capture_substream;
2698c2ecf20Sopenharmony_ci		runtime = substream->runtime;
2708c2ecf20Sopenharmony_ci		rtd = asoc_substream_to_rtd(substream);
2718c2ecf20Sopenharmony_ci		prtd = runtime->private_data;
2728c2ecf20Sopenharmony_ci		dma_desc = snd_soc_dai_get_dma_data(asoc_rtd_to_cpu(rtd, 0), substream);
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci		offlevel = (int_status & I2S_RX_DESC_OFF_LEVEL_MASK) >>
2758c2ecf20Sopenharmony_ci			   I2S_RX_DESC_OFF_LEVEL_SHIFT;
2768c2ecf20Sopenharmony_ci		while (offlevel) {
2778c2ecf20Sopenharmony_ci			regmap_read(regmap_i2s, I2S_RX_DESC_OFF_ADDR, &val_1);
2788c2ecf20Sopenharmony_ci			regmap_read(regmap_i2s, I2S_RX_DESC_OFF_LEN, &val_2);
2798c2ecf20Sopenharmony_ci			offlevel--;
2808c2ecf20Sopenharmony_ci		}
2818c2ecf20Sopenharmony_ci		prtd->dma_addr_next = val_1 + val_2;
2828c2ecf20Sopenharmony_ci		ifflevel = (int_status & I2S_RX_DESC_IFF_LEVEL_MASK) >>
2838c2ecf20Sopenharmony_ci			   I2S_RX_DESC_IFF_LEVEL_SHIFT;
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_ci		availdepth = I2S_DESC_FIFO_DEPTH - ifflevel;
2868c2ecf20Sopenharmony_ci		while (availdepth) {
2878c2ecf20Sopenharmony_ci			dma_desc->dma_addr +=
2888c2ecf20Sopenharmony_ci					snd_pcm_lib_period_bytes(substream);
2898c2ecf20Sopenharmony_ci			dma_desc->dma_area +=
2908c2ecf20Sopenharmony_ci					snd_pcm_lib_period_bytes(substream);
2918c2ecf20Sopenharmony_ci			if (dma_desc->dma_addr - runtime->dma_addr >=
2928c2ecf20Sopenharmony_ci						runtime->dma_bytes) {
2938c2ecf20Sopenharmony_ci				dma_desc->dma_addr = runtime->dma_addr;
2948c2ecf20Sopenharmony_ci				dma_desc->dma_area = runtime->dma_area;
2958c2ecf20Sopenharmony_ci			}
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_ci			prtd->dma_addr = dma_desc->dma_addr;
2988c2ecf20Sopenharmony_ci			regmap_write(regmap_i2s, I2S_RX_DESC_IFF_LEN,
2998c2ecf20Sopenharmony_ci				     snd_pcm_lib_period_bytes(substream));
3008c2ecf20Sopenharmony_ci			regmap_write(regmap_i2s, I2S_RX_DESC_IFF_ADDR,
3018c2ecf20Sopenharmony_ci				     dma_desc->dma_addr);
3028c2ecf20Sopenharmony_ci			availdepth--;
3038c2ecf20Sopenharmony_ci		}
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_ci		snd_pcm_period_elapsed(substream);
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci		/* Clear interrupt by writing 0 */
3088c2ecf20Sopenharmony_ci		regmap_update_bits(regmap_i2s, I2S_RX_IRQ_CTL,
3098c2ecf20Sopenharmony_ci				   I2S_RX_INTR_MASK, 0);
3108c2ecf20Sopenharmony_ci	}
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_ci	/* tx */
3138c2ecf20Sopenharmony_ci	regmap_read(regmap_i2s, I2S_TX_IRQ_CTL, &int_status);
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_ci	if (int_status & I2S_TX_DESC_OFF_INTR_EN_MSK) {
3168c2ecf20Sopenharmony_ci		substream = i2s_priv->play_substream;
3178c2ecf20Sopenharmony_ci		runtime = substream->runtime;
3188c2ecf20Sopenharmony_ci		rtd = asoc_substream_to_rtd(substream);
3198c2ecf20Sopenharmony_ci		prtd = runtime->private_data;
3208c2ecf20Sopenharmony_ci		dma_desc = snd_soc_dai_get_dma_data(asoc_rtd_to_cpu(rtd, 0), substream);
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ci		offlevel = (int_status & I2S_TX_DESC_OFF_LEVEL_MASK) >>
3238c2ecf20Sopenharmony_ci			   I2S_TX_DESC_OFF_LEVEL_SHIFT;
3248c2ecf20Sopenharmony_ci		while (offlevel) {
3258c2ecf20Sopenharmony_ci			regmap_read(regmap_i2s, I2S_TX_DESC_OFF_ADDR, &val_1);
3268c2ecf20Sopenharmony_ci			regmap_read(regmap_i2s, I2S_TX_DESC_OFF_LEN,  &val_2);
3278c2ecf20Sopenharmony_ci			prtd->dma_addr_next = val_1 + val_2;
3288c2ecf20Sopenharmony_ci			offlevel--;
3298c2ecf20Sopenharmony_ci		}
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_ci		ifflevel = (int_status & I2S_TX_DESC_IFF_LEVEL_MASK) >>
3328c2ecf20Sopenharmony_ci			I2S_TX_DESC_IFF_LEVEL_SHIFT;
3338c2ecf20Sopenharmony_ci		availdepth = I2S_DESC_FIFO_DEPTH - ifflevel;
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ci		while (availdepth) {
3368c2ecf20Sopenharmony_ci			dma_desc->dma_addr +=
3378c2ecf20Sopenharmony_ci					snd_pcm_lib_period_bytes(substream);
3388c2ecf20Sopenharmony_ci			dma_desc->dma_area +=
3398c2ecf20Sopenharmony_ci					snd_pcm_lib_period_bytes(substream);
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_ci			if (dma_desc->dma_addr - runtime->dma_addr >=
3428c2ecf20Sopenharmony_ci							runtime->dma_bytes) {
3438c2ecf20Sopenharmony_ci				dma_desc->dma_addr = runtime->dma_addr;
3448c2ecf20Sopenharmony_ci				dma_desc->dma_area = runtime->dma_area;
3458c2ecf20Sopenharmony_ci			}
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci			prtd->dma_addr = dma_desc->dma_addr;
3488c2ecf20Sopenharmony_ci			regmap_write(regmap_i2s, I2S_TX_DESC_IFF_LEN,
3498c2ecf20Sopenharmony_ci				snd_pcm_lib_period_bytes(substream));
3508c2ecf20Sopenharmony_ci			regmap_write(regmap_i2s, I2S_TX_DESC_IFF_ADDR,
3518c2ecf20Sopenharmony_ci					dma_desc->dma_addr);
3528c2ecf20Sopenharmony_ci			availdepth--;
3538c2ecf20Sopenharmony_ci		}
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ci		snd_pcm_period_elapsed(substream);
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_ci		/* Clear interrupt by writing 0 */
3588c2ecf20Sopenharmony_ci		regmap_update_bits(regmap_i2s, I2S_TX_IRQ_CTL,
3598c2ecf20Sopenharmony_ci				   I2S_TX_INTR_MASK, 0);
3608c2ecf20Sopenharmony_ci	}
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
3638c2ecf20Sopenharmony_ci}
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_cistatic int bcm63xx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
3668c2ecf20Sopenharmony_ci{
3678c2ecf20Sopenharmony_ci	struct snd_pcm_substream *substream = pcm->streams[stream].substream;
3688c2ecf20Sopenharmony_ci	struct snd_dma_buffer *buf = &substream->dma_buffer;
3698c2ecf20Sopenharmony_ci	size_t size = bcm63xx_pcm_hardware.buffer_bytes_max;
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_ci	buf->dev.type = SNDRV_DMA_TYPE_DEV;
3728c2ecf20Sopenharmony_ci	buf->dev.dev = pcm->card->dev;
3738c2ecf20Sopenharmony_ci	buf->private_data = NULL;
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_ci	buf->area = dma_alloc_wc(pcm->card->dev,
3768c2ecf20Sopenharmony_ci				 size, &buf->addr,
3778c2ecf20Sopenharmony_ci				 GFP_KERNEL);
3788c2ecf20Sopenharmony_ci	if (!buf->area)
3798c2ecf20Sopenharmony_ci		return -ENOMEM;
3808c2ecf20Sopenharmony_ci	buf->bytes = size;
3818c2ecf20Sopenharmony_ci	return 0;
3828c2ecf20Sopenharmony_ci}
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_cistatic int bcm63xx_soc_pcm_new(struct snd_soc_component *component,
3858c2ecf20Sopenharmony_ci		struct snd_soc_pcm_runtime *rtd)
3868c2ecf20Sopenharmony_ci{
3878c2ecf20Sopenharmony_ci	struct snd_pcm *pcm = rtd->pcm;
3888c2ecf20Sopenharmony_ci	struct bcm_i2s_priv *i2s_priv;
3898c2ecf20Sopenharmony_ci	int ret;
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_ci	i2s_priv = dev_get_drvdata(asoc_rtd_to_cpu(rtd, 0)->dev);
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_ci	of_dma_configure(pcm->card->dev, pcm->card->dev->of_node, 1);
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_ci	ret = dma_coerce_mask_and_coherent(pcm->card->dev, DMA_BIT_MASK(32));
3968c2ecf20Sopenharmony_ci	if (ret)
3978c2ecf20Sopenharmony_ci		goto out;
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_ci	if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) {
4008c2ecf20Sopenharmony_ci		ret = bcm63xx_pcm_preallocate_dma_buffer(pcm,
4018c2ecf20Sopenharmony_ci						 SNDRV_PCM_STREAM_PLAYBACK);
4028c2ecf20Sopenharmony_ci		if (ret)
4038c2ecf20Sopenharmony_ci			goto out;
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_ci		i2s_priv->play_substream =
4068c2ecf20Sopenharmony_ci			pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
4078c2ecf20Sopenharmony_ci	}
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_ci	if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) {
4108c2ecf20Sopenharmony_ci		ret = bcm63xx_pcm_preallocate_dma_buffer(pcm,
4118c2ecf20Sopenharmony_ci					SNDRV_PCM_STREAM_CAPTURE);
4128c2ecf20Sopenharmony_ci		if (ret)
4138c2ecf20Sopenharmony_ci			goto out;
4148c2ecf20Sopenharmony_ci		i2s_priv->capture_substream =
4158c2ecf20Sopenharmony_ci			pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream;
4168c2ecf20Sopenharmony_ci	}
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_ciout:
4198c2ecf20Sopenharmony_ci	return ret;
4208c2ecf20Sopenharmony_ci}
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_cistatic void bcm63xx_pcm_free_dma_buffers(struct snd_soc_component *component,
4238c2ecf20Sopenharmony_ci			 struct snd_pcm *pcm)
4248c2ecf20Sopenharmony_ci{
4258c2ecf20Sopenharmony_ci	int stream;
4268c2ecf20Sopenharmony_ci	struct snd_dma_buffer *buf;
4278c2ecf20Sopenharmony_ci	struct snd_pcm_substream *substream;
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_ci	for (stream = 0; stream < 2; stream++) {
4308c2ecf20Sopenharmony_ci		substream = pcm->streams[stream].substream;
4318c2ecf20Sopenharmony_ci		if (!substream)
4328c2ecf20Sopenharmony_ci			continue;
4338c2ecf20Sopenharmony_ci		buf = &substream->dma_buffer;
4348c2ecf20Sopenharmony_ci		if (!buf->area)
4358c2ecf20Sopenharmony_ci			continue;
4368c2ecf20Sopenharmony_ci		dma_free_wc(pcm->card->dev, buf->bytes,
4378c2ecf20Sopenharmony_ci					buf->area, buf->addr);
4388c2ecf20Sopenharmony_ci		buf->area = NULL;
4398c2ecf20Sopenharmony_ci	}
4408c2ecf20Sopenharmony_ci}
4418c2ecf20Sopenharmony_ci
4428c2ecf20Sopenharmony_cistatic const struct snd_soc_component_driver bcm63xx_soc_platform = {
4438c2ecf20Sopenharmony_ci	.open = bcm63xx_pcm_open,
4448c2ecf20Sopenharmony_ci	.close = bcm63xx_pcm_close,
4458c2ecf20Sopenharmony_ci	.hw_params = bcm63xx_pcm_hw_params,
4468c2ecf20Sopenharmony_ci	.hw_free = bcm63xx_pcm_hw_free,
4478c2ecf20Sopenharmony_ci	.prepare = bcm63xx_pcm_prepare,
4488c2ecf20Sopenharmony_ci	.trigger = bcm63xx_pcm_trigger,
4498c2ecf20Sopenharmony_ci	.pointer = bcm63xx_pcm_pointer,
4508c2ecf20Sopenharmony_ci	.mmap = bcm63xx_pcm_mmap,
4518c2ecf20Sopenharmony_ci	.pcm_construct = bcm63xx_soc_pcm_new,
4528c2ecf20Sopenharmony_ci	.pcm_destruct = bcm63xx_pcm_free_dma_buffers,
4538c2ecf20Sopenharmony_ci};
4548c2ecf20Sopenharmony_ci
4558c2ecf20Sopenharmony_ciint bcm63xx_soc_platform_probe(struct platform_device *pdev,
4568c2ecf20Sopenharmony_ci			       struct bcm_i2s_priv *i2s_priv)
4578c2ecf20Sopenharmony_ci{
4588c2ecf20Sopenharmony_ci	int ret;
4598c2ecf20Sopenharmony_ci
4608c2ecf20Sopenharmony_ci	i2s_priv->r_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
4618c2ecf20Sopenharmony_ci	if (!i2s_priv->r_irq) {
4628c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "Unable to get register irq resource.\n");
4638c2ecf20Sopenharmony_ci		return -ENODEV;
4648c2ecf20Sopenharmony_ci	}
4658c2ecf20Sopenharmony_ci
4668c2ecf20Sopenharmony_ci	ret = devm_request_irq(&pdev->dev, i2s_priv->r_irq->start, i2s_dma_isr,
4678c2ecf20Sopenharmony_ci			i2s_priv->r_irq->flags, "i2s_dma", (void *)i2s_priv);
4688c2ecf20Sopenharmony_ci	if (ret) {
4698c2ecf20Sopenharmony_ci		dev_err(&pdev->dev,
4708c2ecf20Sopenharmony_ci			"i2s_init: failed to request interrupt.ret=%d\n", ret);
4718c2ecf20Sopenharmony_ci		return ret;
4728c2ecf20Sopenharmony_ci	}
4738c2ecf20Sopenharmony_ci
4748c2ecf20Sopenharmony_ci	return devm_snd_soc_register_component(&pdev->dev,
4758c2ecf20Sopenharmony_ci					&bcm63xx_soc_platform, NULL, 0);
4768c2ecf20Sopenharmony_ci}
4778c2ecf20Sopenharmony_ci
4788c2ecf20Sopenharmony_ciint bcm63xx_soc_platform_remove(struct platform_device *pdev)
4798c2ecf20Sopenharmony_ci{
4808c2ecf20Sopenharmony_ci	return 0;
4818c2ecf20Sopenharmony_ci}
4828c2ecf20Sopenharmony_ci
4838c2ecf20Sopenharmony_ciMODULE_AUTHOR("Kevin,Li <kevin-ke.li@broadcom.com>");
4848c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Broadcom DSL XPON ASOC PCM Interface");
4858c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
486