18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
28c2ecf20Sopenharmony_ci//
38c2ecf20Sopenharmony_ci// idma.c - I2S0 internal DMA driver
48c2ecf20Sopenharmony_ci//
58c2ecf20Sopenharmony_ci// Copyright (c) 2011 Samsung Electronics Co., Ltd.
68c2ecf20Sopenharmony_ci//		http://www.samsung.com
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
98c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
108c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h>
118c2ecf20Sopenharmony_ci#include <linux/slab.h>
128c2ecf20Sopenharmony_ci#include <linux/module.h>
138c2ecf20Sopenharmony_ci#include <sound/pcm.h>
148c2ecf20Sopenharmony_ci#include <sound/pcm_params.h>
158c2ecf20Sopenharmony_ci#include <sound/soc.h>
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci#include "i2s.h"
188c2ecf20Sopenharmony_ci#include "idma.h"
198c2ecf20Sopenharmony_ci#include "i2s-regs.h"
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci#define ST_RUNNING		(1<<0)
228c2ecf20Sopenharmony_ci#define ST_OPENED		(1<<1)
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_cistatic const struct snd_pcm_hardware idma_hardware = {
258c2ecf20Sopenharmony_ci	.info = SNDRV_PCM_INFO_INTERLEAVED |
268c2ecf20Sopenharmony_ci		    SNDRV_PCM_INFO_BLOCK_TRANSFER |
278c2ecf20Sopenharmony_ci		    SNDRV_PCM_INFO_MMAP |
288c2ecf20Sopenharmony_ci		    SNDRV_PCM_INFO_MMAP_VALID |
298c2ecf20Sopenharmony_ci		    SNDRV_PCM_INFO_PAUSE |
308c2ecf20Sopenharmony_ci		    SNDRV_PCM_INFO_RESUME,
318c2ecf20Sopenharmony_ci	.buffer_bytes_max = MAX_IDMA_BUFFER,
328c2ecf20Sopenharmony_ci	.period_bytes_min = 128,
338c2ecf20Sopenharmony_ci	.period_bytes_max = MAX_IDMA_PERIOD,
348c2ecf20Sopenharmony_ci	.periods_min = 1,
358c2ecf20Sopenharmony_ci	.periods_max = 2,
368c2ecf20Sopenharmony_ci};
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_cistruct idma_ctrl {
398c2ecf20Sopenharmony_ci	spinlock_t	lock;
408c2ecf20Sopenharmony_ci	int		state;
418c2ecf20Sopenharmony_ci	dma_addr_t	start;
428c2ecf20Sopenharmony_ci	dma_addr_t	pos;
438c2ecf20Sopenharmony_ci	dma_addr_t	end;
448c2ecf20Sopenharmony_ci	dma_addr_t	period;
458c2ecf20Sopenharmony_ci	dma_addr_t	periodsz;
468c2ecf20Sopenharmony_ci	void		*token;
478c2ecf20Sopenharmony_ci	void		(*cb)(void *dt, int bytes_xfer);
488c2ecf20Sopenharmony_ci};
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_cistatic struct idma_info {
518c2ecf20Sopenharmony_ci	spinlock_t	lock;
528c2ecf20Sopenharmony_ci	void		 __iomem  *regs;
538c2ecf20Sopenharmony_ci	dma_addr_t	lp_tx_addr;
548c2ecf20Sopenharmony_ci} idma;
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_cistatic int idma_irq;
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_cistatic void idma_getpos(dma_addr_t *src)
598c2ecf20Sopenharmony_ci{
608c2ecf20Sopenharmony_ci	*src = idma.lp_tx_addr +
618c2ecf20Sopenharmony_ci		(readl(idma.regs + I2STRNCNT) & 0xffffff) * 4;
628c2ecf20Sopenharmony_ci}
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_cistatic int idma_enqueue(struct snd_pcm_substream *substream)
658c2ecf20Sopenharmony_ci{
668c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
678c2ecf20Sopenharmony_ci	struct idma_ctrl *prtd = substream->runtime->private_data;
688c2ecf20Sopenharmony_ci	u32 val;
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci	spin_lock(&prtd->lock);
718c2ecf20Sopenharmony_ci	prtd->token = (void *) substream;
728c2ecf20Sopenharmony_ci	spin_unlock(&prtd->lock);
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci	/* Internal DMA Level0 Interrupt Address */
758c2ecf20Sopenharmony_ci	val = idma.lp_tx_addr + prtd->periodsz;
768c2ecf20Sopenharmony_ci	writel(val, idma.regs + I2SLVL0ADDR);
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci	/* Start address0 of I2S internal DMA operation. */
798c2ecf20Sopenharmony_ci	val = idma.lp_tx_addr;
808c2ecf20Sopenharmony_ci	writel(val, idma.regs + I2SSTR0);
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci	/*
838c2ecf20Sopenharmony_ci	 * Transfer block size for I2S internal DMA.
848c2ecf20Sopenharmony_ci	 * Should decide transfer size before start dma operation
858c2ecf20Sopenharmony_ci	 */
868c2ecf20Sopenharmony_ci	val = readl(idma.regs + I2SSIZE);
878c2ecf20Sopenharmony_ci	val &= ~(I2SSIZE_TRNMSK << I2SSIZE_SHIFT);
888c2ecf20Sopenharmony_ci	val |= (((runtime->dma_bytes >> 2) &
898c2ecf20Sopenharmony_ci			I2SSIZE_TRNMSK) << I2SSIZE_SHIFT);
908c2ecf20Sopenharmony_ci	writel(val, idma.regs + I2SSIZE);
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci	val = readl(idma.regs + I2SAHB);
938c2ecf20Sopenharmony_ci	val |= AHB_INTENLVL0;
948c2ecf20Sopenharmony_ci	writel(val, idma.regs + I2SAHB);
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci	return 0;
978c2ecf20Sopenharmony_ci}
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_cistatic void idma_setcallbk(struct snd_pcm_substream *substream,
1008c2ecf20Sopenharmony_ci				void (*cb)(void *, int))
1018c2ecf20Sopenharmony_ci{
1028c2ecf20Sopenharmony_ci	struct idma_ctrl *prtd = substream->runtime->private_data;
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci	spin_lock(&prtd->lock);
1058c2ecf20Sopenharmony_ci	prtd->cb = cb;
1068c2ecf20Sopenharmony_ci	spin_unlock(&prtd->lock);
1078c2ecf20Sopenharmony_ci}
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_cistatic void idma_control(int op)
1108c2ecf20Sopenharmony_ci{
1118c2ecf20Sopenharmony_ci	u32 val = readl(idma.regs + I2SAHB);
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci	spin_lock(&idma.lock);
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci	switch (op) {
1168c2ecf20Sopenharmony_ci	case LPAM_DMA_START:
1178c2ecf20Sopenharmony_ci		val |= (AHB_INTENLVL0 | AHB_DMAEN);
1188c2ecf20Sopenharmony_ci		break;
1198c2ecf20Sopenharmony_ci	case LPAM_DMA_STOP:
1208c2ecf20Sopenharmony_ci		val &= ~(AHB_INTENLVL0 | AHB_DMAEN);
1218c2ecf20Sopenharmony_ci		break;
1228c2ecf20Sopenharmony_ci	default:
1238c2ecf20Sopenharmony_ci		spin_unlock(&idma.lock);
1248c2ecf20Sopenharmony_ci		return;
1258c2ecf20Sopenharmony_ci	}
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci	writel(val, idma.regs + I2SAHB);
1288c2ecf20Sopenharmony_ci	spin_unlock(&idma.lock);
1298c2ecf20Sopenharmony_ci}
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_cistatic void idma_done(void *id, int bytes_xfer)
1328c2ecf20Sopenharmony_ci{
1338c2ecf20Sopenharmony_ci	struct snd_pcm_substream *substream = id;
1348c2ecf20Sopenharmony_ci	struct idma_ctrl *prtd = substream->runtime->private_data;
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci	if (prtd && (prtd->state & ST_RUNNING))
1378c2ecf20Sopenharmony_ci		snd_pcm_period_elapsed(substream);
1388c2ecf20Sopenharmony_ci}
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_cistatic int idma_hw_params(struct snd_soc_component *component,
1418c2ecf20Sopenharmony_ci			  struct snd_pcm_substream *substream,
1428c2ecf20Sopenharmony_ci			  struct snd_pcm_hw_params *params)
1438c2ecf20Sopenharmony_ci{
1448c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
1458c2ecf20Sopenharmony_ci	struct idma_ctrl *prtd = substream->runtime->private_data;
1468c2ecf20Sopenharmony_ci	u32 mod = readl(idma.regs + I2SMOD);
1478c2ecf20Sopenharmony_ci	u32 ahb = readl(idma.regs + I2SAHB);
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci	ahb |= (AHB_DMARLD | AHB_INTMASK);
1508c2ecf20Sopenharmony_ci	mod |= MOD_TXS_IDMA;
1518c2ecf20Sopenharmony_ci	writel(ahb, idma.regs + I2SAHB);
1528c2ecf20Sopenharmony_ci	writel(mod, idma.regs + I2SMOD);
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci	snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
1558c2ecf20Sopenharmony_ci	runtime->dma_bytes = params_buffer_bytes(params);
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	prtd->start = prtd->pos = runtime->dma_addr;
1588c2ecf20Sopenharmony_ci	prtd->period = params_periods(params);
1598c2ecf20Sopenharmony_ci	prtd->periodsz = params_period_bytes(params);
1608c2ecf20Sopenharmony_ci	prtd->end = runtime->dma_addr + runtime->dma_bytes;
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	idma_setcallbk(substream, idma_done);
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci	return 0;
1658c2ecf20Sopenharmony_ci}
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_cistatic int idma_hw_free(struct snd_soc_component *component,
1688c2ecf20Sopenharmony_ci			struct snd_pcm_substream *substream)
1698c2ecf20Sopenharmony_ci{
1708c2ecf20Sopenharmony_ci	snd_pcm_set_runtime_buffer(substream, NULL);
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci	return 0;
1738c2ecf20Sopenharmony_ci}
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_cistatic int idma_prepare(struct snd_soc_component *component,
1768c2ecf20Sopenharmony_ci			struct snd_pcm_substream *substream)
1778c2ecf20Sopenharmony_ci{
1788c2ecf20Sopenharmony_ci	struct idma_ctrl *prtd = substream->runtime->private_data;
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci	prtd->pos = prtd->start;
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci	/* flush the DMA channel */
1838c2ecf20Sopenharmony_ci	idma_control(LPAM_DMA_STOP);
1848c2ecf20Sopenharmony_ci	idma_enqueue(substream);
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci	return 0;
1878c2ecf20Sopenharmony_ci}
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_cistatic int idma_trigger(struct snd_soc_component *component,
1908c2ecf20Sopenharmony_ci			struct snd_pcm_substream *substream, int cmd)
1918c2ecf20Sopenharmony_ci{
1928c2ecf20Sopenharmony_ci	struct idma_ctrl *prtd = substream->runtime->private_data;
1938c2ecf20Sopenharmony_ci	int ret = 0;
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci	spin_lock(&prtd->lock);
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci	switch (cmd) {
1988c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_RESUME:
1998c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_START:
2008c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
2018c2ecf20Sopenharmony_ci		prtd->state |= ST_RUNNING;
2028c2ecf20Sopenharmony_ci		idma_control(LPAM_DMA_START);
2038c2ecf20Sopenharmony_ci		break;
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_SUSPEND:
2068c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_STOP:
2078c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
2088c2ecf20Sopenharmony_ci		prtd->state &= ~ST_RUNNING;
2098c2ecf20Sopenharmony_ci		idma_control(LPAM_DMA_STOP);
2108c2ecf20Sopenharmony_ci		break;
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci	default:
2138c2ecf20Sopenharmony_ci		ret = -EINVAL;
2148c2ecf20Sopenharmony_ci		break;
2158c2ecf20Sopenharmony_ci	}
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci	spin_unlock(&prtd->lock);
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci	return ret;
2208c2ecf20Sopenharmony_ci}
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_cistatic snd_pcm_uframes_t
2238c2ecf20Sopenharmony_ciidma_pointer(struct snd_soc_component *component,
2248c2ecf20Sopenharmony_ci	     struct snd_pcm_substream *substream)
2258c2ecf20Sopenharmony_ci{
2268c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
2278c2ecf20Sopenharmony_ci	struct idma_ctrl *prtd = runtime->private_data;
2288c2ecf20Sopenharmony_ci	dma_addr_t src;
2298c2ecf20Sopenharmony_ci	unsigned long res;
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci	spin_lock(&prtd->lock);
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_ci	idma_getpos(&src);
2348c2ecf20Sopenharmony_ci	res = src - prtd->start;
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ci	spin_unlock(&prtd->lock);
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci	return bytes_to_frames(substream->runtime, res);
2398c2ecf20Sopenharmony_ci}
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_cistatic int idma_mmap(struct snd_soc_component *component,
2428c2ecf20Sopenharmony_ci		     struct snd_pcm_substream *substream,
2438c2ecf20Sopenharmony_ci	struct vm_area_struct *vma)
2448c2ecf20Sopenharmony_ci{
2458c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
2468c2ecf20Sopenharmony_ci	unsigned long size, offset;
2478c2ecf20Sopenharmony_ci	int ret;
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_ci	/* From snd_pcm_lib_mmap_iomem */
2508c2ecf20Sopenharmony_ci	vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
2518c2ecf20Sopenharmony_ci	size = vma->vm_end - vma->vm_start;
2528c2ecf20Sopenharmony_ci	offset = vma->vm_pgoff << PAGE_SHIFT;
2538c2ecf20Sopenharmony_ci	ret = io_remap_pfn_range(vma, vma->vm_start,
2548c2ecf20Sopenharmony_ci			(runtime->dma_addr + offset) >> PAGE_SHIFT,
2558c2ecf20Sopenharmony_ci			size, vma->vm_page_prot);
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci	return ret;
2588c2ecf20Sopenharmony_ci}
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_cistatic irqreturn_t iis_irq(int irqno, void *dev_id)
2618c2ecf20Sopenharmony_ci{
2628c2ecf20Sopenharmony_ci	struct idma_ctrl *prtd = (struct idma_ctrl *)dev_id;
2638c2ecf20Sopenharmony_ci	u32 iisahb, val, addr;
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_ci	iisahb  = readl(idma.regs + I2SAHB);
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci	val = (iisahb & AHB_LVL0INT) ? AHB_CLRLVL0INT : 0;
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ci	if (val) {
2708c2ecf20Sopenharmony_ci		iisahb |= val;
2718c2ecf20Sopenharmony_ci		writel(iisahb, idma.regs + I2SAHB);
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci		addr = readl(idma.regs + I2SLVL0ADDR) - idma.lp_tx_addr;
2748c2ecf20Sopenharmony_ci		addr += prtd->periodsz;
2758c2ecf20Sopenharmony_ci		addr %= (u32)(prtd->end - prtd->start);
2768c2ecf20Sopenharmony_ci		addr += idma.lp_tx_addr;
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_ci		writel(addr, idma.regs + I2SLVL0ADDR);
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci		if (prtd->cb)
2818c2ecf20Sopenharmony_ci			prtd->cb(prtd->token, prtd->period);
2828c2ecf20Sopenharmony_ci	}
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
2858c2ecf20Sopenharmony_ci}
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_cistatic int idma_open(struct snd_soc_component *component,
2888c2ecf20Sopenharmony_ci		     struct snd_pcm_substream *substream)
2898c2ecf20Sopenharmony_ci{
2908c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
2918c2ecf20Sopenharmony_ci	struct idma_ctrl *prtd;
2928c2ecf20Sopenharmony_ci	int ret;
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci	snd_soc_set_runtime_hwparams(substream, &idma_hardware);
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ci	prtd = kzalloc(sizeof(struct idma_ctrl), GFP_KERNEL);
2978c2ecf20Sopenharmony_ci	if (prtd == NULL)
2988c2ecf20Sopenharmony_ci		return -ENOMEM;
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_ci	ret = request_irq(idma_irq, iis_irq, 0, "i2s", prtd);
3018c2ecf20Sopenharmony_ci	if (ret < 0) {
3028c2ecf20Sopenharmony_ci		pr_err("fail to claim i2s irq , ret = %d\n", ret);
3038c2ecf20Sopenharmony_ci		kfree(prtd);
3048c2ecf20Sopenharmony_ci		return ret;
3058c2ecf20Sopenharmony_ci	}
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci	spin_lock_init(&prtd->lock);
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ci	runtime->private_data = prtd;
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ci	return 0;
3128c2ecf20Sopenharmony_ci}
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_cistatic int idma_close(struct snd_soc_component *component,
3158c2ecf20Sopenharmony_ci		      struct snd_pcm_substream *substream)
3168c2ecf20Sopenharmony_ci{
3178c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
3188c2ecf20Sopenharmony_ci	struct idma_ctrl *prtd = runtime->private_data;
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ci	free_irq(idma_irq, prtd);
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ci	if (!prtd)
3238c2ecf20Sopenharmony_ci		pr_err("idma_close called with prtd == NULL\n");
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_ci	kfree(prtd);
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ci	return 0;
3288c2ecf20Sopenharmony_ci}
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_cistatic void idma_free(struct snd_soc_component *component,
3318c2ecf20Sopenharmony_ci		      struct snd_pcm *pcm)
3328c2ecf20Sopenharmony_ci{
3338c2ecf20Sopenharmony_ci	struct snd_pcm_substream *substream;
3348c2ecf20Sopenharmony_ci	struct snd_dma_buffer *buf;
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci	substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
3378c2ecf20Sopenharmony_ci	if (!substream)
3388c2ecf20Sopenharmony_ci		return;
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_ci	buf = &substream->dma_buffer;
3418c2ecf20Sopenharmony_ci	if (!buf->area)
3428c2ecf20Sopenharmony_ci		return;
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_ci	iounmap((void __iomem *)buf->area);
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_ci	buf->area = NULL;
3478c2ecf20Sopenharmony_ci	buf->addr = 0;
3488c2ecf20Sopenharmony_ci}
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_cistatic int preallocate_idma_buffer(struct snd_pcm *pcm, int stream)
3518c2ecf20Sopenharmony_ci{
3528c2ecf20Sopenharmony_ci	struct snd_pcm_substream *substream = pcm->streams[stream].substream;
3538c2ecf20Sopenharmony_ci	struct snd_dma_buffer *buf = &substream->dma_buffer;
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ci	buf->dev.dev = pcm->card->dev;
3568c2ecf20Sopenharmony_ci	buf->private_data = NULL;
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_ci	/* Assign PCM buffer pointers */
3598c2ecf20Sopenharmony_ci	buf->dev.type = SNDRV_DMA_TYPE_CONTINUOUS;
3608c2ecf20Sopenharmony_ci	buf->addr = idma.lp_tx_addr;
3618c2ecf20Sopenharmony_ci	buf->bytes = idma_hardware.buffer_bytes_max;
3628c2ecf20Sopenharmony_ci	buf->area = (unsigned char * __force)ioremap(buf->addr, buf->bytes);
3638c2ecf20Sopenharmony_ci	if (!buf->area)
3648c2ecf20Sopenharmony_ci		return -ENOMEM;
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci	return 0;
3678c2ecf20Sopenharmony_ci}
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_cistatic int idma_new(struct snd_soc_component *component,
3708c2ecf20Sopenharmony_ci		    struct snd_soc_pcm_runtime *rtd)
3718c2ecf20Sopenharmony_ci{
3728c2ecf20Sopenharmony_ci	struct snd_card *card = rtd->card->snd_card;
3738c2ecf20Sopenharmony_ci	struct snd_pcm *pcm = rtd->pcm;
3748c2ecf20Sopenharmony_ci	int ret;
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci	ret = dma_coerce_mask_and_coherent(card->dev, DMA_BIT_MASK(32));
3778c2ecf20Sopenharmony_ci	if (ret)
3788c2ecf20Sopenharmony_ci		return ret;
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci	if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) {
3818c2ecf20Sopenharmony_ci		ret = preallocate_idma_buffer(pcm,
3828c2ecf20Sopenharmony_ci				SNDRV_PCM_STREAM_PLAYBACK);
3838c2ecf20Sopenharmony_ci	}
3848c2ecf20Sopenharmony_ci
3858c2ecf20Sopenharmony_ci	return ret;
3868c2ecf20Sopenharmony_ci}
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_civoid idma_reg_addr_init(void __iomem *regs, dma_addr_t addr)
3898c2ecf20Sopenharmony_ci{
3908c2ecf20Sopenharmony_ci	spin_lock_init(&idma.lock);
3918c2ecf20Sopenharmony_ci	idma.regs = regs;
3928c2ecf20Sopenharmony_ci	idma.lp_tx_addr = addr;
3938c2ecf20Sopenharmony_ci}
3948c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(idma_reg_addr_init);
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_cistatic const struct snd_soc_component_driver asoc_idma_platform = {
3978c2ecf20Sopenharmony_ci	.open		= idma_open,
3988c2ecf20Sopenharmony_ci	.close		= idma_close,
3998c2ecf20Sopenharmony_ci	.trigger	= idma_trigger,
4008c2ecf20Sopenharmony_ci	.pointer	= idma_pointer,
4018c2ecf20Sopenharmony_ci	.mmap		= idma_mmap,
4028c2ecf20Sopenharmony_ci	.hw_params	= idma_hw_params,
4038c2ecf20Sopenharmony_ci	.hw_free	= idma_hw_free,
4048c2ecf20Sopenharmony_ci	.prepare	= idma_prepare,
4058c2ecf20Sopenharmony_ci	.pcm_construct	= idma_new,
4068c2ecf20Sopenharmony_ci	.pcm_destruct	= idma_free,
4078c2ecf20Sopenharmony_ci};
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_cistatic int asoc_idma_platform_probe(struct platform_device *pdev)
4108c2ecf20Sopenharmony_ci{
4118c2ecf20Sopenharmony_ci	idma_irq = platform_get_irq(pdev, 0);
4128c2ecf20Sopenharmony_ci	if (idma_irq < 0)
4138c2ecf20Sopenharmony_ci		return idma_irq;
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_ci	return devm_snd_soc_register_component(&pdev->dev, &asoc_idma_platform,
4168c2ecf20Sopenharmony_ci					       NULL, 0);
4178c2ecf20Sopenharmony_ci}
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_cistatic struct platform_driver asoc_idma_driver = {
4208c2ecf20Sopenharmony_ci	.driver = {
4218c2ecf20Sopenharmony_ci		.name = "samsung-idma",
4228c2ecf20Sopenharmony_ci	},
4238c2ecf20Sopenharmony_ci
4248c2ecf20Sopenharmony_ci	.probe = asoc_idma_platform_probe,
4258c2ecf20Sopenharmony_ci};
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_cimodule_platform_driver(asoc_idma_driver);
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_ciMODULE_AUTHOR("Jaswinder Singh, <jassisinghbrar@gmail.com>");
4308c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Samsung ASoC IDMA Driver");
4318c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
432