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