18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci// 38c2ecf20Sopenharmony_ci// Freescale MPC5200 PSC DMA 48c2ecf20Sopenharmony_ci// ALSA SoC Platform driver 58c2ecf20Sopenharmony_ci// 68c2ecf20Sopenharmony_ci// Copyright (C) 2008 Secret Lab Technologies Ltd. 78c2ecf20Sopenharmony_ci// Copyright (C) 2009 Jon Smirl, Digispeaker 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/module.h> 108c2ecf20Sopenharmony_ci#include <linux/of_device.h> 118c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 128c2ecf20Sopenharmony_ci#include <linux/slab.h> 138c2ecf20Sopenharmony_ci#include <linux/of_address.h> 148c2ecf20Sopenharmony_ci#include <linux/of_irq.h> 158c2ecf20Sopenharmony_ci#include <linux/of_platform.h> 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#include <sound/soc.h> 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#include <linux/fsl/bestcomm/bestcomm.h> 208c2ecf20Sopenharmony_ci#include <linux/fsl/bestcomm/gen_bd.h> 218c2ecf20Sopenharmony_ci#include <asm/mpc52xx_psc.h> 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci#include "mpc5200_dma.h" 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#define DRV_NAME "mpc5200_dma" 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci/* 288c2ecf20Sopenharmony_ci * Interrupt handlers 298c2ecf20Sopenharmony_ci */ 308c2ecf20Sopenharmony_cistatic irqreturn_t psc_dma_status_irq(int irq, void *_psc_dma) 318c2ecf20Sopenharmony_ci{ 328c2ecf20Sopenharmony_ci struct psc_dma *psc_dma = _psc_dma; 338c2ecf20Sopenharmony_ci struct mpc52xx_psc __iomem *regs = psc_dma->psc_regs; 348c2ecf20Sopenharmony_ci u16 isr; 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci isr = in_be16(®s->mpc52xx_psc_isr); 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci /* Playback underrun error */ 398c2ecf20Sopenharmony_ci if (psc_dma->playback.active && (isr & MPC52xx_PSC_IMR_TXEMP)) 408c2ecf20Sopenharmony_ci psc_dma->stats.underrun_count++; 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci /* Capture overrun error */ 438c2ecf20Sopenharmony_ci if (psc_dma->capture.active && (isr & MPC52xx_PSC_IMR_ORERR)) 448c2ecf20Sopenharmony_ci psc_dma->stats.overrun_count++; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci out_8(®s->command, MPC52xx_PSC_RST_ERR_STAT); 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci return IRQ_HANDLED; 498c2ecf20Sopenharmony_ci} 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci/** 528c2ecf20Sopenharmony_ci * psc_dma_bcom_enqueue_next_buffer - Enqueue another audio buffer 538c2ecf20Sopenharmony_ci * @s: pointer to stream private data structure 548c2ecf20Sopenharmony_ci * 558c2ecf20Sopenharmony_ci * Enqueues another audio period buffer into the bestcomm queue. 568c2ecf20Sopenharmony_ci * 578c2ecf20Sopenharmony_ci * Note: The routine must only be called when there is space available in 588c2ecf20Sopenharmony_ci * the queue. Otherwise the enqueue will fail and the audio ring buffer 598c2ecf20Sopenharmony_ci * will get out of sync 608c2ecf20Sopenharmony_ci */ 618c2ecf20Sopenharmony_cistatic void psc_dma_bcom_enqueue_next_buffer(struct psc_dma_stream *s) 628c2ecf20Sopenharmony_ci{ 638c2ecf20Sopenharmony_ci struct bcom_bd *bd; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci /* Prepare and enqueue the next buffer descriptor */ 668c2ecf20Sopenharmony_ci bd = bcom_prepare_next_buffer(s->bcom_task); 678c2ecf20Sopenharmony_ci bd->status = s->period_bytes; 688c2ecf20Sopenharmony_ci bd->data[0] = s->runtime->dma_addr + (s->period_next * s->period_bytes); 698c2ecf20Sopenharmony_ci bcom_submit_next_buffer(s->bcom_task, NULL); 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci /* Update for next period */ 728c2ecf20Sopenharmony_ci s->period_next = (s->period_next + 1) % s->runtime->periods; 738c2ecf20Sopenharmony_ci} 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci/* Bestcomm DMA irq handler */ 768c2ecf20Sopenharmony_cistatic irqreturn_t psc_dma_bcom_irq(int irq, void *_psc_dma_stream) 778c2ecf20Sopenharmony_ci{ 788c2ecf20Sopenharmony_ci struct psc_dma_stream *s = _psc_dma_stream; 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci spin_lock(&s->psc_dma->lock); 818c2ecf20Sopenharmony_ci /* For each finished period, dequeue the completed period buffer 828c2ecf20Sopenharmony_ci * and enqueue a new one in it's place. */ 838c2ecf20Sopenharmony_ci while (bcom_buffer_done(s->bcom_task)) { 848c2ecf20Sopenharmony_ci bcom_retrieve_buffer(s->bcom_task, NULL, NULL); 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci s->period_current = (s->period_current+1) % s->runtime->periods; 878c2ecf20Sopenharmony_ci s->period_count++; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci psc_dma_bcom_enqueue_next_buffer(s); 908c2ecf20Sopenharmony_ci } 918c2ecf20Sopenharmony_ci spin_unlock(&s->psc_dma->lock); 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci /* If the stream is active, then also inform the PCM middle layer 948c2ecf20Sopenharmony_ci * of the period finished event. */ 958c2ecf20Sopenharmony_ci if (s->active) 968c2ecf20Sopenharmony_ci snd_pcm_period_elapsed(s->stream); 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci return IRQ_HANDLED; 998c2ecf20Sopenharmony_ci} 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_cistatic int psc_dma_hw_free(struct snd_soc_component *component, 1028c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream) 1038c2ecf20Sopenharmony_ci{ 1048c2ecf20Sopenharmony_ci snd_pcm_set_runtime_buffer(substream, NULL); 1058c2ecf20Sopenharmony_ci return 0; 1068c2ecf20Sopenharmony_ci} 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci/** 1098c2ecf20Sopenharmony_ci * psc_dma_trigger: start and stop the DMA transfer. 1108c2ecf20Sopenharmony_ci * @component: triggered component 1118c2ecf20Sopenharmony_ci * @substream: triggered substream 1128c2ecf20Sopenharmony_ci * @cmd: triggered command 1138c2ecf20Sopenharmony_ci * 1148c2ecf20Sopenharmony_ci * This function is called by ALSA to start, stop, pause, and resume the DMA 1158c2ecf20Sopenharmony_ci * transfer of data. 1168c2ecf20Sopenharmony_ci */ 1178c2ecf20Sopenharmony_cistatic int psc_dma_trigger(struct snd_soc_component *component, 1188c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream, int cmd) 1198c2ecf20Sopenharmony_ci{ 1208c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 1218c2ecf20Sopenharmony_ci struct psc_dma *psc_dma = snd_soc_dai_get_drvdata(asoc_rtd_to_cpu(rtd, 0)); 1228c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 1238c2ecf20Sopenharmony_ci struct psc_dma_stream *s = to_psc_dma_stream(substream, psc_dma); 1248c2ecf20Sopenharmony_ci struct mpc52xx_psc __iomem *regs = psc_dma->psc_regs; 1258c2ecf20Sopenharmony_ci u16 imr; 1268c2ecf20Sopenharmony_ci unsigned long flags; 1278c2ecf20Sopenharmony_ci int i; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci switch (cmd) { 1308c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 1318c2ecf20Sopenharmony_ci dev_dbg(psc_dma->dev, "START: stream=%i fbits=%u ps=%u #p=%u\n", 1328c2ecf20Sopenharmony_ci substream->pstr->stream, runtime->frame_bits, 1338c2ecf20Sopenharmony_ci (int)runtime->period_size, runtime->periods); 1348c2ecf20Sopenharmony_ci s->period_bytes = frames_to_bytes(runtime, 1358c2ecf20Sopenharmony_ci runtime->period_size); 1368c2ecf20Sopenharmony_ci s->period_next = 0; 1378c2ecf20Sopenharmony_ci s->period_current = 0; 1388c2ecf20Sopenharmony_ci s->active = 1; 1398c2ecf20Sopenharmony_ci s->period_count = 0; 1408c2ecf20Sopenharmony_ci s->runtime = runtime; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci /* Fill up the bestcomm bd queue and enable DMA. 1438c2ecf20Sopenharmony_ci * This will begin filling the PSC's fifo. 1448c2ecf20Sopenharmony_ci */ 1458c2ecf20Sopenharmony_ci spin_lock_irqsave(&psc_dma->lock, flags); 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) 1488c2ecf20Sopenharmony_ci bcom_gen_bd_rx_reset(s->bcom_task); 1498c2ecf20Sopenharmony_ci else 1508c2ecf20Sopenharmony_ci bcom_gen_bd_tx_reset(s->bcom_task); 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci for (i = 0; i < runtime->periods; i++) 1538c2ecf20Sopenharmony_ci if (!bcom_queue_full(s->bcom_task)) 1548c2ecf20Sopenharmony_ci psc_dma_bcom_enqueue_next_buffer(s); 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci bcom_enable(s->bcom_task); 1578c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&psc_dma->lock, flags); 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci out_8(®s->command, MPC52xx_PSC_RST_ERR_STAT); 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci break; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 1648c2ecf20Sopenharmony_ci dev_dbg(psc_dma->dev, "STOP: stream=%i periods_count=%i\n", 1658c2ecf20Sopenharmony_ci substream->pstr->stream, s->period_count); 1668c2ecf20Sopenharmony_ci s->active = 0; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci spin_lock_irqsave(&psc_dma->lock, flags); 1698c2ecf20Sopenharmony_ci bcom_disable(s->bcom_task); 1708c2ecf20Sopenharmony_ci if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) 1718c2ecf20Sopenharmony_ci bcom_gen_bd_rx_reset(s->bcom_task); 1728c2ecf20Sopenharmony_ci else 1738c2ecf20Sopenharmony_ci bcom_gen_bd_tx_reset(s->bcom_task); 1748c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&psc_dma->lock, flags); 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci break; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci default: 1798c2ecf20Sopenharmony_ci dev_dbg(psc_dma->dev, "unhandled trigger: stream=%i cmd=%i\n", 1808c2ecf20Sopenharmony_ci substream->pstr->stream, cmd); 1818c2ecf20Sopenharmony_ci return -EINVAL; 1828c2ecf20Sopenharmony_ci } 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci /* Update interrupt enable settings */ 1858c2ecf20Sopenharmony_ci imr = 0; 1868c2ecf20Sopenharmony_ci if (psc_dma->playback.active) 1878c2ecf20Sopenharmony_ci imr |= MPC52xx_PSC_IMR_TXEMP; 1888c2ecf20Sopenharmony_ci if (psc_dma->capture.active) 1898c2ecf20Sopenharmony_ci imr |= MPC52xx_PSC_IMR_ORERR; 1908c2ecf20Sopenharmony_ci out_be16(®s->isr_imr.imr, psc_dma->imr | imr); 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci return 0; 1938c2ecf20Sopenharmony_ci} 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci/* --------------------------------------------------------------------- 1978c2ecf20Sopenharmony_ci * The PSC DMA 'ASoC platform' driver 1988c2ecf20Sopenharmony_ci * 1998c2ecf20Sopenharmony_ci * Can be referenced by an 'ASoC machine' driver 2008c2ecf20Sopenharmony_ci * This driver only deals with the audio bus; it doesn't have any 2018c2ecf20Sopenharmony_ci * interaction with the attached codec 2028c2ecf20Sopenharmony_ci */ 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_cistatic const struct snd_pcm_hardware psc_dma_hardware = { 2058c2ecf20Sopenharmony_ci .info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | 2068c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | 2078c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_BATCH, 2088c2ecf20Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_BE | 2098c2ecf20Sopenharmony_ci SNDRV_PCM_FMTBIT_S24_BE | SNDRV_PCM_FMTBIT_S32_BE, 2108c2ecf20Sopenharmony_ci .period_bytes_max = 1024 * 1024, 2118c2ecf20Sopenharmony_ci .period_bytes_min = 32, 2128c2ecf20Sopenharmony_ci .periods_min = 2, 2138c2ecf20Sopenharmony_ci .periods_max = 256, 2148c2ecf20Sopenharmony_ci .buffer_bytes_max = 2 * 1024 * 1024, 2158c2ecf20Sopenharmony_ci .fifo_size = 512, 2168c2ecf20Sopenharmony_ci}; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_cistatic int psc_dma_open(struct snd_soc_component *component, 2198c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream) 2208c2ecf20Sopenharmony_ci{ 2218c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 2228c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 2238c2ecf20Sopenharmony_ci struct psc_dma *psc_dma = snd_soc_dai_get_drvdata(asoc_rtd_to_cpu(rtd, 0)); 2248c2ecf20Sopenharmony_ci struct psc_dma_stream *s; 2258c2ecf20Sopenharmony_ci int rc; 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci dev_dbg(psc_dma->dev, "psc_dma_open(substream=%p)\n", substream); 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) 2308c2ecf20Sopenharmony_ci s = &psc_dma->capture; 2318c2ecf20Sopenharmony_ci else 2328c2ecf20Sopenharmony_ci s = &psc_dma->playback; 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci snd_soc_set_runtime_hwparams(substream, &psc_dma_hardware); 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci rc = snd_pcm_hw_constraint_integer(runtime, 2378c2ecf20Sopenharmony_ci SNDRV_PCM_HW_PARAM_PERIODS); 2388c2ecf20Sopenharmony_ci if (rc < 0) { 2398c2ecf20Sopenharmony_ci dev_err(substream->pcm->card->dev, "invalid buffer size\n"); 2408c2ecf20Sopenharmony_ci return rc; 2418c2ecf20Sopenharmony_ci } 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci s->stream = substream; 2448c2ecf20Sopenharmony_ci return 0; 2458c2ecf20Sopenharmony_ci} 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_cistatic int psc_dma_close(struct snd_soc_component *component, 2488c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream) 2498c2ecf20Sopenharmony_ci{ 2508c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 2518c2ecf20Sopenharmony_ci struct psc_dma *psc_dma = snd_soc_dai_get_drvdata(asoc_rtd_to_cpu(rtd, 0)); 2528c2ecf20Sopenharmony_ci struct psc_dma_stream *s; 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci dev_dbg(psc_dma->dev, "psc_dma_close(substream=%p)\n", substream); 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) 2578c2ecf20Sopenharmony_ci s = &psc_dma->capture; 2588c2ecf20Sopenharmony_ci else 2598c2ecf20Sopenharmony_ci s = &psc_dma->playback; 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci if (!psc_dma->playback.active && 2628c2ecf20Sopenharmony_ci !psc_dma->capture.active) { 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci /* Disable all interrupts and reset the PSC */ 2658c2ecf20Sopenharmony_ci out_be16(&psc_dma->psc_regs->isr_imr.imr, psc_dma->imr); 2668c2ecf20Sopenharmony_ci out_8(&psc_dma->psc_regs->command, 4 << 4); /* reset error */ 2678c2ecf20Sopenharmony_ci } 2688c2ecf20Sopenharmony_ci s->stream = NULL; 2698c2ecf20Sopenharmony_ci return 0; 2708c2ecf20Sopenharmony_ci} 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_cistatic snd_pcm_uframes_t 2738c2ecf20Sopenharmony_cipsc_dma_pointer(struct snd_soc_component *component, 2748c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream) 2758c2ecf20Sopenharmony_ci{ 2768c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 2778c2ecf20Sopenharmony_ci struct psc_dma *psc_dma = snd_soc_dai_get_drvdata(asoc_rtd_to_cpu(rtd, 0)); 2788c2ecf20Sopenharmony_ci struct psc_dma_stream *s; 2798c2ecf20Sopenharmony_ci dma_addr_t count; 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) 2828c2ecf20Sopenharmony_ci s = &psc_dma->capture; 2838c2ecf20Sopenharmony_ci else 2848c2ecf20Sopenharmony_ci s = &psc_dma->playback; 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci count = s->period_current * s->period_bytes; 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci return bytes_to_frames(substream->runtime, count); 2898c2ecf20Sopenharmony_ci} 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_cistatic int psc_dma_hw_params(struct snd_soc_component *component, 2928c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream, 2938c2ecf20Sopenharmony_ci struct snd_pcm_hw_params *params) 2948c2ecf20Sopenharmony_ci{ 2958c2ecf20Sopenharmony_ci snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci return 0; 2988c2ecf20Sopenharmony_ci} 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_cistatic int psc_dma_new(struct snd_soc_component *component, 3018c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *rtd) 3028c2ecf20Sopenharmony_ci{ 3038c2ecf20Sopenharmony_ci struct snd_card *card = rtd->card->snd_card; 3048c2ecf20Sopenharmony_ci struct snd_soc_dai *dai = asoc_rtd_to_cpu(rtd, 0); 3058c2ecf20Sopenharmony_ci struct snd_pcm *pcm = rtd->pcm; 3068c2ecf20Sopenharmony_ci size_t size = psc_dma_hardware.buffer_bytes_max; 3078c2ecf20Sopenharmony_ci int rc; 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci dev_dbg(component->dev, "psc_dma_new(card=%p, dai=%p, pcm=%p)\n", 3108c2ecf20Sopenharmony_ci card, dai, pcm); 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci rc = dma_coerce_mask_and_coherent(card->dev, DMA_BIT_MASK(32)); 3138c2ecf20Sopenharmony_ci if (rc) 3148c2ecf20Sopenharmony_ci return rc; 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) { 3178c2ecf20Sopenharmony_ci rc = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, pcm->card->dev, 3188c2ecf20Sopenharmony_ci size, &pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream->dma_buffer); 3198c2ecf20Sopenharmony_ci if (rc) 3208c2ecf20Sopenharmony_ci goto playback_alloc_err; 3218c2ecf20Sopenharmony_ci } 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) { 3248c2ecf20Sopenharmony_ci rc = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, pcm->card->dev, 3258c2ecf20Sopenharmony_ci size, &pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream->dma_buffer); 3268c2ecf20Sopenharmony_ci if (rc) 3278c2ecf20Sopenharmony_ci goto capture_alloc_err; 3288c2ecf20Sopenharmony_ci } 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci return 0; 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci capture_alloc_err: 3338c2ecf20Sopenharmony_ci if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) 3348c2ecf20Sopenharmony_ci snd_dma_free_pages(&pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream->dma_buffer); 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci playback_alloc_err: 3378c2ecf20Sopenharmony_ci dev_err(card->dev, "Cannot allocate buffer(s)\n"); 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci return -ENOMEM; 3408c2ecf20Sopenharmony_ci} 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_cistatic void psc_dma_free(struct snd_soc_component *component, 3438c2ecf20Sopenharmony_ci struct snd_pcm *pcm) 3448c2ecf20Sopenharmony_ci{ 3458c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream; 3468c2ecf20Sopenharmony_ci int stream; 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci dev_dbg(component->dev, "psc_dma_free(pcm=%p)\n", pcm); 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci for (stream = 0; stream < 2; stream++) { 3518c2ecf20Sopenharmony_ci substream = pcm->streams[stream].substream; 3528c2ecf20Sopenharmony_ci if (substream) { 3538c2ecf20Sopenharmony_ci snd_dma_free_pages(&substream->dma_buffer); 3548c2ecf20Sopenharmony_ci substream->dma_buffer.area = NULL; 3558c2ecf20Sopenharmony_ci substream->dma_buffer.addr = 0; 3568c2ecf20Sopenharmony_ci } 3578c2ecf20Sopenharmony_ci } 3588c2ecf20Sopenharmony_ci} 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_cistatic const struct snd_soc_component_driver mpc5200_audio_dma_component = { 3618c2ecf20Sopenharmony_ci .name = DRV_NAME, 3628c2ecf20Sopenharmony_ci .open = psc_dma_open, 3638c2ecf20Sopenharmony_ci .close = psc_dma_close, 3648c2ecf20Sopenharmony_ci .hw_free = psc_dma_hw_free, 3658c2ecf20Sopenharmony_ci .pointer = psc_dma_pointer, 3668c2ecf20Sopenharmony_ci .trigger = psc_dma_trigger, 3678c2ecf20Sopenharmony_ci .hw_params = psc_dma_hw_params, 3688c2ecf20Sopenharmony_ci .pcm_construct = psc_dma_new, 3698c2ecf20Sopenharmony_ci .pcm_destruct = psc_dma_free, 3708c2ecf20Sopenharmony_ci}; 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ciint mpc5200_audio_dma_create(struct platform_device *op) 3738c2ecf20Sopenharmony_ci{ 3748c2ecf20Sopenharmony_ci phys_addr_t fifo; 3758c2ecf20Sopenharmony_ci struct psc_dma *psc_dma; 3768c2ecf20Sopenharmony_ci struct resource res; 3778c2ecf20Sopenharmony_ci int size, irq, rc; 3788c2ecf20Sopenharmony_ci const __be32 *prop; 3798c2ecf20Sopenharmony_ci void __iomem *regs; 3808c2ecf20Sopenharmony_ci int ret; 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci /* Fetch the registers and IRQ of the PSC */ 3838c2ecf20Sopenharmony_ci irq = irq_of_parse_and_map(op->dev.of_node, 0); 3848c2ecf20Sopenharmony_ci if (of_address_to_resource(op->dev.of_node, 0, &res)) { 3858c2ecf20Sopenharmony_ci dev_err(&op->dev, "Missing reg property\n"); 3868c2ecf20Sopenharmony_ci return -ENODEV; 3878c2ecf20Sopenharmony_ci } 3888c2ecf20Sopenharmony_ci regs = ioremap(res.start, resource_size(&res)); 3898c2ecf20Sopenharmony_ci if (!regs) { 3908c2ecf20Sopenharmony_ci dev_err(&op->dev, "Could not map registers\n"); 3918c2ecf20Sopenharmony_ci return -ENODEV; 3928c2ecf20Sopenharmony_ci } 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci /* Allocate and initialize the driver private data */ 3958c2ecf20Sopenharmony_ci psc_dma = kzalloc(sizeof *psc_dma, GFP_KERNEL); 3968c2ecf20Sopenharmony_ci if (!psc_dma) { 3978c2ecf20Sopenharmony_ci ret = -ENOMEM; 3988c2ecf20Sopenharmony_ci goto out_unmap; 3998c2ecf20Sopenharmony_ci } 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci /* Get the PSC ID */ 4028c2ecf20Sopenharmony_ci prop = of_get_property(op->dev.of_node, "cell-index", &size); 4038c2ecf20Sopenharmony_ci if (!prop || size < sizeof *prop) { 4048c2ecf20Sopenharmony_ci ret = -ENODEV; 4058c2ecf20Sopenharmony_ci goto out_free; 4068c2ecf20Sopenharmony_ci } 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci spin_lock_init(&psc_dma->lock); 4098c2ecf20Sopenharmony_ci mutex_init(&psc_dma->mutex); 4108c2ecf20Sopenharmony_ci psc_dma->id = be32_to_cpu(*prop); 4118c2ecf20Sopenharmony_ci psc_dma->irq = irq; 4128c2ecf20Sopenharmony_ci psc_dma->psc_regs = regs; 4138c2ecf20Sopenharmony_ci psc_dma->fifo_regs = regs + sizeof *psc_dma->psc_regs; 4148c2ecf20Sopenharmony_ci psc_dma->dev = &op->dev; 4158c2ecf20Sopenharmony_ci psc_dma->playback.psc_dma = psc_dma; 4168c2ecf20Sopenharmony_ci psc_dma->capture.psc_dma = psc_dma; 4178c2ecf20Sopenharmony_ci snprintf(psc_dma->name, sizeof psc_dma->name, "PSC%u", psc_dma->id); 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci /* Find the address of the fifo data registers and setup the 4208c2ecf20Sopenharmony_ci * DMA tasks */ 4218c2ecf20Sopenharmony_ci fifo = res.start + offsetof(struct mpc52xx_psc, buffer.buffer_32); 4228c2ecf20Sopenharmony_ci psc_dma->capture.bcom_task = 4238c2ecf20Sopenharmony_ci bcom_psc_gen_bd_rx_init(psc_dma->id, 10, fifo, 512); 4248c2ecf20Sopenharmony_ci psc_dma->playback.bcom_task = 4258c2ecf20Sopenharmony_ci bcom_psc_gen_bd_tx_init(psc_dma->id, 10, fifo); 4268c2ecf20Sopenharmony_ci if (!psc_dma->capture.bcom_task || 4278c2ecf20Sopenharmony_ci !psc_dma->playback.bcom_task) { 4288c2ecf20Sopenharmony_ci dev_err(&op->dev, "Could not allocate bestcomm tasks\n"); 4298c2ecf20Sopenharmony_ci ret = -ENODEV; 4308c2ecf20Sopenharmony_ci goto out_free; 4318c2ecf20Sopenharmony_ci } 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci /* Disable all interrupts and reset the PSC */ 4348c2ecf20Sopenharmony_ci out_be16(&psc_dma->psc_regs->isr_imr.imr, psc_dma->imr); 4358c2ecf20Sopenharmony_ci /* reset receiver */ 4368c2ecf20Sopenharmony_ci out_8(&psc_dma->psc_regs->command, MPC52xx_PSC_RST_RX); 4378c2ecf20Sopenharmony_ci /* reset transmitter */ 4388c2ecf20Sopenharmony_ci out_8(&psc_dma->psc_regs->command, MPC52xx_PSC_RST_TX); 4398c2ecf20Sopenharmony_ci /* reset error */ 4408c2ecf20Sopenharmony_ci out_8(&psc_dma->psc_regs->command, MPC52xx_PSC_RST_ERR_STAT); 4418c2ecf20Sopenharmony_ci /* reset mode */ 4428c2ecf20Sopenharmony_ci out_8(&psc_dma->psc_regs->command, MPC52xx_PSC_SEL_MODE_REG_1); 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci /* Set up mode register; 4458c2ecf20Sopenharmony_ci * First write: RxRdy (FIFO Alarm) generates rx FIFO irq 4468c2ecf20Sopenharmony_ci * Second write: register Normal mode for non loopback 4478c2ecf20Sopenharmony_ci */ 4488c2ecf20Sopenharmony_ci out_8(&psc_dma->psc_regs->mode, 0); 4498c2ecf20Sopenharmony_ci out_8(&psc_dma->psc_regs->mode, 0); 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci /* Set the TX and RX fifo alarm thresholds */ 4528c2ecf20Sopenharmony_ci out_be16(&psc_dma->fifo_regs->rfalarm, 0x100); 4538c2ecf20Sopenharmony_ci out_8(&psc_dma->fifo_regs->rfcntl, 0x4); 4548c2ecf20Sopenharmony_ci out_be16(&psc_dma->fifo_regs->tfalarm, 0x100); 4558c2ecf20Sopenharmony_ci out_8(&psc_dma->fifo_regs->tfcntl, 0x7); 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci /* Lookup the IRQ numbers */ 4588c2ecf20Sopenharmony_ci psc_dma->playback.irq = 4598c2ecf20Sopenharmony_ci bcom_get_task_irq(psc_dma->playback.bcom_task); 4608c2ecf20Sopenharmony_ci psc_dma->capture.irq = 4618c2ecf20Sopenharmony_ci bcom_get_task_irq(psc_dma->capture.bcom_task); 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci rc = request_irq(psc_dma->irq, &psc_dma_status_irq, IRQF_SHARED, 4648c2ecf20Sopenharmony_ci "psc-dma-status", psc_dma); 4658c2ecf20Sopenharmony_ci rc |= request_irq(psc_dma->capture.irq, &psc_dma_bcom_irq, IRQF_SHARED, 4668c2ecf20Sopenharmony_ci "psc-dma-capture", &psc_dma->capture); 4678c2ecf20Sopenharmony_ci rc |= request_irq(psc_dma->playback.irq, &psc_dma_bcom_irq, IRQF_SHARED, 4688c2ecf20Sopenharmony_ci "psc-dma-playback", &psc_dma->playback); 4698c2ecf20Sopenharmony_ci if (rc) { 4708c2ecf20Sopenharmony_ci ret = -ENODEV; 4718c2ecf20Sopenharmony_ci goto out_irq; 4728c2ecf20Sopenharmony_ci } 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci /* Save what we've done so it can be found again later */ 4758c2ecf20Sopenharmony_ci dev_set_drvdata(&op->dev, psc_dma); 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci /* Tell the ASoC OF helpers about it */ 4788c2ecf20Sopenharmony_ci return devm_snd_soc_register_component(&op->dev, 4798c2ecf20Sopenharmony_ci &mpc5200_audio_dma_component, NULL, 0); 4808c2ecf20Sopenharmony_ciout_irq: 4818c2ecf20Sopenharmony_ci free_irq(psc_dma->irq, psc_dma); 4828c2ecf20Sopenharmony_ci free_irq(psc_dma->capture.irq, &psc_dma->capture); 4838c2ecf20Sopenharmony_ci free_irq(psc_dma->playback.irq, &psc_dma->playback); 4848c2ecf20Sopenharmony_ciout_free: 4858c2ecf20Sopenharmony_ci kfree(psc_dma); 4868c2ecf20Sopenharmony_ciout_unmap: 4878c2ecf20Sopenharmony_ci iounmap(regs); 4888c2ecf20Sopenharmony_ci return ret; 4898c2ecf20Sopenharmony_ci} 4908c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(mpc5200_audio_dma_create); 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ciint mpc5200_audio_dma_destroy(struct platform_device *op) 4938c2ecf20Sopenharmony_ci{ 4948c2ecf20Sopenharmony_ci struct psc_dma *psc_dma = dev_get_drvdata(&op->dev); 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci dev_dbg(&op->dev, "mpc5200_audio_dma_destroy()\n"); 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci bcom_gen_bd_rx_release(psc_dma->capture.bcom_task); 4998c2ecf20Sopenharmony_ci bcom_gen_bd_tx_release(psc_dma->playback.bcom_task); 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci /* Release irqs */ 5028c2ecf20Sopenharmony_ci free_irq(psc_dma->irq, psc_dma); 5038c2ecf20Sopenharmony_ci free_irq(psc_dma->capture.irq, &psc_dma->capture); 5048c2ecf20Sopenharmony_ci free_irq(psc_dma->playback.irq, &psc_dma->playback); 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ci iounmap(psc_dma->psc_regs); 5078c2ecf20Sopenharmony_ci kfree(psc_dma); 5088c2ecf20Sopenharmony_ci dev_set_drvdata(&op->dev, NULL); 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci return 0; 5118c2ecf20Sopenharmony_ci} 5128c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(mpc5200_audio_dma_destroy); 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_ciMODULE_AUTHOR("Grant Likely <grant.likely@secretlab.ca>"); 5158c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Freescale MPC5200 PSC in DMA mode ASoC Driver"); 5168c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 517