11edfc248SAndra Danciu // SPDX-License-Identifier: GPL 21edfc248SAndra Danciu // 31edfc248SAndra Danciu // Freescale MPC5200 PSC DMA 41edfc248SAndra Danciu // ALSA SoC Platform driver 51edfc248SAndra Danciu // 61edfc248SAndra Danciu // Copyright (C) 2008 Secret Lab Technologies Ltd. 71edfc248SAndra Danciu // Copyright (C) 2009 Jon Smirl, Digispeaker 889dd0842SJon Smirl 989dd0842SJon Smirl #include <linux/module.h> 1089dd0842SJon Smirl #include <linux/of_device.h> 1107a38b1bSPaul Gortmaker #include <linux/dma-mapping.h> 125a0e3ad6STejun Heo #include <linux/slab.h> 135af50730SRob Herring #include <linux/of_address.h> 145af50730SRob Herring #include <linux/of_irq.h> 15f0fba2adSLiam Girdwood #include <linux/of_platform.h> 1689dd0842SJon Smirl 1789dd0842SJon Smirl #include <sound/soc.h> 1889dd0842SJon Smirl 199a322993SPhilippe De Muyter #include <linux/fsl/bestcomm/bestcomm.h> 209a322993SPhilippe De Muyter #include <linux/fsl/bestcomm/gen_bd.h> 2189dd0842SJon Smirl #include <asm/mpc52xx_psc.h> 2289dd0842SJon Smirl 2389dd0842SJon Smirl #include "mpc5200_dma.h" 2489dd0842SJon Smirl 2560bceb80SKuninori Morimoto #define DRV_NAME "mpc5200_dma" 2660bceb80SKuninori Morimoto 2789dd0842SJon Smirl /* 2889dd0842SJon Smirl * Interrupt handlers 2989dd0842SJon Smirl */ 30cebe7767SJon Smirl static irqreturn_t psc_dma_status_irq(int irq, void *_psc_dma) 3189dd0842SJon Smirl { 32cebe7767SJon Smirl struct psc_dma *psc_dma = _psc_dma; 33cebe7767SJon Smirl struct mpc52xx_psc __iomem *regs = psc_dma->psc_regs; 3489dd0842SJon Smirl u16 isr; 3589dd0842SJon Smirl 3689dd0842SJon Smirl isr = in_be16(®s->mpc52xx_psc_isr); 3789dd0842SJon Smirl 3889dd0842SJon Smirl /* Playback underrun error */ 39cebe7767SJon Smirl if (psc_dma->playback.active && (isr & MPC52xx_PSC_IMR_TXEMP)) 40cebe7767SJon Smirl psc_dma->stats.underrun_count++; 4189dd0842SJon Smirl 4289dd0842SJon Smirl /* Capture overrun error */ 43cebe7767SJon Smirl if (psc_dma->capture.active && (isr & MPC52xx_PSC_IMR_ORERR)) 44cebe7767SJon Smirl psc_dma->stats.overrun_count++; 4589dd0842SJon Smirl 46dbcc3475SJon Smirl out_8(®s->command, MPC52xx_PSC_RST_ERR_STAT); 4789dd0842SJon Smirl 4889dd0842SJon Smirl return IRQ_HANDLED; 4989dd0842SJon Smirl } 5089dd0842SJon Smirl 5189dd0842SJon Smirl /** 52cebe7767SJon Smirl * psc_dma_bcom_enqueue_next_buffer - Enqueue another audio buffer 5389dd0842SJon Smirl * @s: pointer to stream private data structure 5489dd0842SJon Smirl * 5589dd0842SJon Smirl * Enqueues another audio period buffer into the bestcomm queue. 5689dd0842SJon Smirl * 5789dd0842SJon Smirl * Note: The routine must only be called when there is space available in 5889dd0842SJon Smirl * the queue. Otherwise the enqueue will fail and the audio ring buffer 5989dd0842SJon Smirl * will get out of sync 6089dd0842SJon Smirl */ 61cebe7767SJon Smirl static void psc_dma_bcom_enqueue_next_buffer(struct psc_dma_stream *s) 6289dd0842SJon Smirl { 6389dd0842SJon Smirl struct bcom_bd *bd; 6489dd0842SJon Smirl 6589dd0842SJon Smirl /* Prepare and enqueue the next buffer descriptor */ 6689dd0842SJon Smirl bd = bcom_prepare_next_buffer(s->bcom_task); 6789dd0842SJon Smirl bd->status = s->period_bytes; 688f159d72SGrant Likely bd->data[0] = s->runtime->dma_addr + (s->period_next * s->period_bytes); 6989dd0842SJon Smirl bcom_submit_next_buffer(s->bcom_task, NULL); 7089dd0842SJon Smirl 7189dd0842SJon Smirl /* Update for next period */ 728f159d72SGrant Likely s->period_next = (s->period_next + 1) % s->runtime->periods; 7389dd0842SJon Smirl } 7489dd0842SJon Smirl 7589dd0842SJon Smirl /* Bestcomm DMA irq handler */ 76a68cc8daSGrant Likely static irqreturn_t psc_dma_bcom_irq(int irq, void *_psc_dma_stream) 7789dd0842SJon Smirl { 78dbcc3475SJon Smirl struct psc_dma_stream *s = _psc_dma_stream; 7989dd0842SJon Smirl 80dbcc3475SJon Smirl spin_lock(&s->psc_dma->lock); 81dbcc3475SJon Smirl /* For each finished period, dequeue the completed period buffer 82dbcc3475SJon Smirl * and enqueue a new one in it's place. */ 83dbcc3475SJon Smirl while (bcom_buffer_done(s->bcom_task)) { 84dbcc3475SJon Smirl bcom_retrieve_buffer(s->bcom_task, NULL, NULL); 8589dd0842SJon Smirl 868f159d72SGrant Likely s->period_current = (s->period_current+1) % s->runtime->periods; 87c4878274SGrant Likely s->period_count++; 88dbcc3475SJon Smirl 89dbcc3475SJon Smirl psc_dma_bcom_enqueue_next_buffer(s); 9089dd0842SJon Smirl } 91dbcc3475SJon Smirl spin_unlock(&s->psc_dma->lock); 92dbcc3475SJon Smirl 93dbcc3475SJon Smirl /* If the stream is active, then also inform the PCM middle layer 94dbcc3475SJon Smirl * of the period finished event. */ 95dbcc3475SJon Smirl if (s->active) 96dbcc3475SJon Smirl snd_pcm_period_elapsed(s->stream); 97dbcc3475SJon Smirl 98dbcc3475SJon Smirl return IRQ_HANDLED; 9989dd0842SJon Smirl } 10089dd0842SJon Smirl 101dbcc3475SJon Smirl static int psc_dma_hw_free(struct snd_pcm_substream *substream) 10289dd0842SJon Smirl { 10389dd0842SJon Smirl snd_pcm_set_runtime_buffer(substream, NULL); 10489dd0842SJon Smirl return 0; 10589dd0842SJon Smirl } 10689dd0842SJon Smirl 10789dd0842SJon Smirl /** 108cebe7767SJon Smirl * psc_dma_trigger: start and stop the DMA transfer. 10989dd0842SJon Smirl * 11089dd0842SJon Smirl * This function is called by ALSA to start, stop, pause, and resume the DMA 11189dd0842SJon Smirl * transfer of data. 11289dd0842SJon Smirl */ 113dbcc3475SJon Smirl static int psc_dma_trigger(struct snd_pcm_substream *substream, int cmd) 11489dd0842SJon Smirl { 11589dd0842SJon Smirl struct snd_soc_pcm_runtime *rtd = substream->private_data; 116f0fba2adSLiam Girdwood struct psc_dma *psc_dma = snd_soc_dai_get_drvdata(rtd->cpu_dai); 11789dd0842SJon Smirl struct snd_pcm_runtime *runtime = substream->runtime; 1181d8222e8SGrant Likely struct psc_dma_stream *s = to_psc_dma_stream(substream, psc_dma); 119cebe7767SJon Smirl struct mpc52xx_psc __iomem *regs = psc_dma->psc_regs; 12089dd0842SJon Smirl u16 imr; 12189dd0842SJon Smirl unsigned long flags; 122dbcc3475SJon Smirl int i; 12389dd0842SJon Smirl 12489dd0842SJon Smirl switch (cmd) { 12589dd0842SJon Smirl case SNDRV_PCM_TRIGGER_START: 126c4878274SGrant Likely dev_dbg(psc_dma->dev, "START: stream=%i fbits=%u ps=%u #p=%u\n", 127c4878274SGrant Likely substream->pstr->stream, runtime->frame_bits, 128c4878274SGrant Likely (int)runtime->period_size, runtime->periods); 12989dd0842SJon Smirl s->period_bytes = frames_to_bytes(runtime, 13089dd0842SJon Smirl runtime->period_size); 1318f159d72SGrant Likely s->period_next = 0; 1328f159d72SGrant Likely s->period_current = 0; 13389dd0842SJon Smirl s->active = 1; 134c4878274SGrant Likely s->period_count = 0; 135dbcc3475SJon Smirl s->runtime = runtime; 136dbcc3475SJon Smirl 137dbcc3475SJon Smirl /* Fill up the bestcomm bd queue and enable DMA. 138dbcc3475SJon Smirl * This will begin filling the PSC's fifo. 139dbcc3475SJon Smirl */ 140dbcc3475SJon Smirl spin_lock_irqsave(&psc_dma->lock, flags); 141dbcc3475SJon Smirl 142d56b6eb6SGrant Likely if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) 143dbcc3475SJon Smirl bcom_gen_bd_rx_reset(s->bcom_task); 144d56b6eb6SGrant Likely else 145d56b6eb6SGrant Likely bcom_gen_bd_tx_reset(s->bcom_task); 146d56b6eb6SGrant Likely 147dbcc3475SJon Smirl for (i = 0; i < runtime->periods; i++) 148dbcc3475SJon Smirl if (!bcom_queue_full(s->bcom_task)) 149dbcc3475SJon Smirl psc_dma_bcom_enqueue_next_buffer(s); 15089dd0842SJon Smirl 15189dd0842SJon Smirl bcom_enable(s->bcom_task); 152cebe7767SJon Smirl spin_unlock_irqrestore(&psc_dma->lock, flags); 15389dd0842SJon Smirl 154dbcc3475SJon Smirl out_8(®s->command, MPC52xx_PSC_RST_ERR_STAT); 155dbcc3475SJon Smirl 15689dd0842SJon Smirl break; 15789dd0842SJon Smirl 15889dd0842SJon Smirl case SNDRV_PCM_TRIGGER_STOP: 159c4878274SGrant Likely dev_dbg(psc_dma->dev, "STOP: stream=%i periods_count=%i\n", 160c4878274SGrant Likely substream->pstr->stream, s->period_count); 16189dd0842SJon Smirl s->active = 0; 16289dd0842SJon Smirl 163dbcc3475SJon Smirl spin_lock_irqsave(&psc_dma->lock, flags); 16489dd0842SJon Smirl bcom_disable(s->bcom_task); 165dbcc3475SJon Smirl if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) 166dbcc3475SJon Smirl bcom_gen_bd_rx_reset(s->bcom_task); 167dbcc3475SJon Smirl else 168dbcc3475SJon Smirl bcom_gen_bd_tx_reset(s->bcom_task); 169dbcc3475SJon Smirl spin_unlock_irqrestore(&psc_dma->lock, flags); 17089dd0842SJon Smirl 17189dd0842SJon Smirl break; 17289dd0842SJon Smirl 17389dd0842SJon Smirl default: 174c4878274SGrant Likely dev_dbg(psc_dma->dev, "unhandled trigger: stream=%i cmd=%i\n", 175c4878274SGrant Likely substream->pstr->stream, cmd); 17689dd0842SJon Smirl return -EINVAL; 17789dd0842SJon Smirl } 17889dd0842SJon Smirl 17989dd0842SJon Smirl /* Update interrupt enable settings */ 18089dd0842SJon Smirl imr = 0; 181cebe7767SJon Smirl if (psc_dma->playback.active) 18289dd0842SJon Smirl imr |= MPC52xx_PSC_IMR_TXEMP; 183cebe7767SJon Smirl if (psc_dma->capture.active) 18489dd0842SJon Smirl imr |= MPC52xx_PSC_IMR_ORERR; 185dbcc3475SJon Smirl out_be16(®s->isr_imr.imr, psc_dma->imr | imr); 18689dd0842SJon Smirl 18789dd0842SJon Smirl return 0; 18889dd0842SJon Smirl } 18989dd0842SJon Smirl 19089dd0842SJon Smirl 19189dd0842SJon Smirl /* --------------------------------------------------------------------- 19289dd0842SJon Smirl * The PSC DMA 'ASoC platform' driver 19389dd0842SJon Smirl * 19489dd0842SJon Smirl * Can be referenced by an 'ASoC machine' driver 19589dd0842SJon Smirl * This driver only deals with the audio bus; it doesn't have any 19689dd0842SJon Smirl * interaction with the attached codec 19789dd0842SJon Smirl */ 19889dd0842SJon Smirl 199dbcc3475SJon Smirl static const struct snd_pcm_hardware psc_dma_hardware = { 20089dd0842SJon Smirl .info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | 20189dd0842SJon Smirl SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | 20289dd0842SJon Smirl SNDRV_PCM_INFO_BATCH, 20389dd0842SJon Smirl .formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_BE | 20489dd0842SJon Smirl SNDRV_PCM_FMTBIT_S24_BE | SNDRV_PCM_FMTBIT_S32_BE, 20589dd0842SJon Smirl .period_bytes_max = 1024 * 1024, 20689dd0842SJon Smirl .period_bytes_min = 32, 20789dd0842SJon Smirl .periods_min = 2, 20889dd0842SJon Smirl .periods_max = 256, 20989dd0842SJon Smirl .buffer_bytes_max = 2 * 1024 * 1024, 210dbcc3475SJon Smirl .fifo_size = 512, 21189dd0842SJon Smirl }; 21289dd0842SJon Smirl 213dbcc3475SJon Smirl static int psc_dma_open(struct snd_pcm_substream *substream) 21489dd0842SJon Smirl { 215dbcc3475SJon Smirl struct snd_pcm_runtime *runtime = substream->runtime; 21689dd0842SJon Smirl struct snd_soc_pcm_runtime *rtd = substream->private_data; 217f0fba2adSLiam Girdwood struct psc_dma *psc_dma = snd_soc_dai_get_drvdata(rtd->cpu_dai); 218cebe7767SJon Smirl struct psc_dma_stream *s; 219dbcc3475SJon Smirl int rc; 22089dd0842SJon Smirl 221dbcc3475SJon Smirl dev_dbg(psc_dma->dev, "psc_dma_open(substream=%p)\n", substream); 22289dd0842SJon Smirl 22389dd0842SJon Smirl if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) 224cebe7767SJon Smirl s = &psc_dma->capture; 22589dd0842SJon Smirl else 226cebe7767SJon Smirl s = &psc_dma->playback; 22789dd0842SJon Smirl 228dbcc3475SJon Smirl snd_soc_set_runtime_hwparams(substream, &psc_dma_hardware); 229dbcc3475SJon Smirl 230dbcc3475SJon Smirl rc = snd_pcm_hw_constraint_integer(runtime, 231dbcc3475SJon Smirl SNDRV_PCM_HW_PARAM_PERIODS); 232dbcc3475SJon Smirl if (rc < 0) { 233dbcc3475SJon Smirl dev_err(substream->pcm->card->dev, "invalid buffer size\n"); 234dbcc3475SJon Smirl return rc; 235dbcc3475SJon Smirl } 23689dd0842SJon Smirl 23789dd0842SJon Smirl s->stream = substream; 23889dd0842SJon Smirl return 0; 23989dd0842SJon Smirl } 24089dd0842SJon Smirl 241dbcc3475SJon Smirl static int psc_dma_close(struct snd_pcm_substream *substream) 24289dd0842SJon Smirl { 24389dd0842SJon Smirl struct snd_soc_pcm_runtime *rtd = substream->private_data; 244f0fba2adSLiam Girdwood struct psc_dma *psc_dma = snd_soc_dai_get_drvdata(rtd->cpu_dai); 245cebe7767SJon Smirl struct psc_dma_stream *s; 24689dd0842SJon Smirl 247dbcc3475SJon Smirl dev_dbg(psc_dma->dev, "psc_dma_close(substream=%p)\n", substream); 24889dd0842SJon Smirl 24989dd0842SJon Smirl if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) 250cebe7767SJon Smirl s = &psc_dma->capture; 25189dd0842SJon Smirl else 252cebe7767SJon Smirl s = &psc_dma->playback; 25389dd0842SJon Smirl 254dbcc3475SJon Smirl if (!psc_dma->playback.active && 255dbcc3475SJon Smirl !psc_dma->capture.active) { 256dbcc3475SJon Smirl 257dbcc3475SJon Smirl /* Disable all interrupts and reset the PSC */ 258dbcc3475SJon Smirl out_be16(&psc_dma->psc_regs->isr_imr.imr, psc_dma->imr); 259dbcc3475SJon Smirl out_8(&psc_dma->psc_regs->command, 4 << 4); /* reset error */ 260dbcc3475SJon Smirl } 26189dd0842SJon Smirl s->stream = NULL; 26289dd0842SJon Smirl return 0; 26389dd0842SJon Smirl } 26489dd0842SJon Smirl 26589dd0842SJon Smirl static snd_pcm_uframes_t 266dbcc3475SJon Smirl psc_dma_pointer(struct snd_pcm_substream *substream) 26789dd0842SJon Smirl { 26889dd0842SJon Smirl struct snd_soc_pcm_runtime *rtd = substream->private_data; 269f0fba2adSLiam Girdwood struct psc_dma *psc_dma = snd_soc_dai_get_drvdata(rtd->cpu_dai); 270cebe7767SJon Smirl struct psc_dma_stream *s; 27189dd0842SJon Smirl dma_addr_t count; 27289dd0842SJon Smirl 27389dd0842SJon Smirl if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) 274cebe7767SJon Smirl s = &psc_dma->capture; 27589dd0842SJon Smirl else 276cebe7767SJon Smirl s = &psc_dma->playback; 27789dd0842SJon Smirl 2788f159d72SGrant Likely count = s->period_current * s->period_bytes; 27989dd0842SJon Smirl 28089dd0842SJon Smirl return bytes_to_frames(substream->runtime, count); 28189dd0842SJon Smirl } 28289dd0842SJon Smirl 283dbcc3475SJon Smirl static int 284dbcc3475SJon Smirl psc_dma_hw_params(struct snd_pcm_substream *substream, 285dbcc3475SJon Smirl struct snd_pcm_hw_params *params) 286dbcc3475SJon Smirl { 287dbcc3475SJon Smirl snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); 288dbcc3475SJon Smirl 289dbcc3475SJon Smirl return 0; 290dbcc3475SJon Smirl } 291dbcc3475SJon Smirl 292b6ed0720SArvind Yadav static const struct snd_pcm_ops psc_dma_ops = { 293dbcc3475SJon Smirl .open = psc_dma_open, 294dbcc3475SJon Smirl .close = psc_dma_close, 295dbcc3475SJon Smirl .hw_free = psc_dma_hw_free, 29689dd0842SJon Smirl .ioctl = snd_pcm_lib_ioctl, 297dbcc3475SJon Smirl .pointer = psc_dma_pointer, 298dbcc3475SJon Smirl .trigger = psc_dma_trigger, 299dbcc3475SJon Smirl .hw_params = psc_dma_hw_params, 30089dd0842SJon Smirl }; 30189dd0842SJon Smirl 302552d1ef6SLiam Girdwood static int psc_dma_new(struct snd_soc_pcm_runtime *rtd) 30389dd0842SJon Smirl { 304552d1ef6SLiam Girdwood struct snd_card *card = rtd->card->snd_card; 30560bceb80SKuninori Morimoto struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, DRV_NAME); 306552d1ef6SLiam Girdwood struct snd_soc_dai *dai = rtd->cpu_dai; 307552d1ef6SLiam Girdwood struct snd_pcm *pcm = rtd->pcm; 308dbcc3475SJon Smirl size_t size = psc_dma_hardware.buffer_bytes_max; 309c9bd5e69SRussell King int rc; 31089dd0842SJon Smirl 31160bceb80SKuninori Morimoto dev_dbg(component->dev, "psc_dma_new(card=%p, dai=%p, pcm=%p)\n", 31289dd0842SJon Smirl card, dai, pcm); 31389dd0842SJon Smirl 314c9bd5e69SRussell King rc = dma_coerce_mask_and_coherent(card->dev, DMA_BIT_MASK(32)); 315c9bd5e69SRussell King if (rc) 316c9bd5e69SRussell King return rc; 31789dd0842SJon Smirl 3186296914cSJoachim Eastwood if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) { 319dbcc3475SJon Smirl rc = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, pcm->card->dev, 3206296914cSJoachim Eastwood size, &pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream->dma_buffer); 32189dd0842SJon Smirl if (rc) 32289dd0842SJon Smirl goto playback_alloc_err; 32389dd0842SJon Smirl } 32489dd0842SJon Smirl 3256296914cSJoachim Eastwood if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) { 326dbcc3475SJon Smirl rc = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, pcm->card->dev, 3276296914cSJoachim Eastwood size, &pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream->dma_buffer); 32889dd0842SJon Smirl if (rc) 32989dd0842SJon Smirl goto capture_alloc_err; 33089dd0842SJon Smirl } 33189dd0842SJon Smirl 33289dd0842SJon Smirl return 0; 33389dd0842SJon Smirl 33489dd0842SJon Smirl capture_alloc_err: 3356296914cSJoachim Eastwood if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) 3366296914cSJoachim Eastwood snd_dma_free_pages(&pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream->dma_buffer); 337dbcc3475SJon Smirl 33889dd0842SJon Smirl playback_alloc_err: 33989dd0842SJon Smirl dev_err(card->dev, "Cannot allocate buffer(s)\n"); 340dbcc3475SJon Smirl 34189dd0842SJon Smirl return -ENOMEM; 34289dd0842SJon Smirl } 34389dd0842SJon Smirl 344dbcc3475SJon Smirl static void psc_dma_free(struct snd_pcm *pcm) 34589dd0842SJon Smirl { 34689dd0842SJon Smirl struct snd_soc_pcm_runtime *rtd = pcm->private_data; 34760bceb80SKuninori Morimoto struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, DRV_NAME); 34889dd0842SJon Smirl struct snd_pcm_substream *substream; 34989dd0842SJon Smirl int stream; 35089dd0842SJon Smirl 35160bceb80SKuninori Morimoto dev_dbg(component->dev, "psc_dma_free(pcm=%p)\n", pcm); 35289dd0842SJon Smirl 35389dd0842SJon Smirl for (stream = 0; stream < 2; stream++) { 35489dd0842SJon Smirl substream = pcm->streams[stream].substream; 35589dd0842SJon Smirl if (substream) { 35689dd0842SJon Smirl snd_dma_free_pages(&substream->dma_buffer); 35789dd0842SJon Smirl substream->dma_buffer.area = NULL; 35889dd0842SJon Smirl substream->dma_buffer.addr = 0; 35989dd0842SJon Smirl } 36089dd0842SJon Smirl } 36189dd0842SJon Smirl } 36289dd0842SJon Smirl 36360bceb80SKuninori Morimoto static const struct snd_soc_component_driver mpc5200_audio_dma_component = { 36460bceb80SKuninori Morimoto .name = DRV_NAME, 365f0fba2adSLiam Girdwood .ops = &psc_dma_ops, 366dbcc3475SJon Smirl .pcm_new = &psc_dma_new, 367dbcc3475SJon Smirl .pcm_free = &psc_dma_free, 36889dd0842SJon Smirl }; 36989dd0842SJon Smirl 370f515b673SEric Millbrandt int mpc5200_audio_dma_create(struct platform_device *op) 371dbcc3475SJon Smirl { 372dbcc3475SJon Smirl phys_addr_t fifo; 373dbcc3475SJon Smirl struct psc_dma *psc_dma; 374dbcc3475SJon Smirl struct resource res; 375dbcc3475SJon Smirl int size, irq, rc; 376dbcc3475SJon Smirl const __be32 *prop; 377dbcc3475SJon Smirl void __iomem *regs; 37833d7f778SJulia Lawall int ret; 379dbcc3475SJon Smirl 380dbcc3475SJon Smirl /* Fetch the registers and IRQ of the PSC */ 38161c7a080SGrant Likely irq = irq_of_parse_and_map(op->dev.of_node, 0); 38261c7a080SGrant Likely if (of_address_to_resource(op->dev.of_node, 0, &res)) { 383dbcc3475SJon Smirl dev_err(&op->dev, "Missing reg property\n"); 384dbcc3475SJon Smirl return -ENODEV; 385dbcc3475SJon Smirl } 38628f65c11SJoe Perches regs = ioremap(res.start, resource_size(&res)); 387dbcc3475SJon Smirl if (!regs) { 388dbcc3475SJon Smirl dev_err(&op->dev, "Could not map registers\n"); 389dbcc3475SJon Smirl return -ENODEV; 390dbcc3475SJon Smirl } 391dbcc3475SJon Smirl 392dbcc3475SJon Smirl /* Allocate and initialize the driver private data */ 393dbcc3475SJon Smirl psc_dma = kzalloc(sizeof *psc_dma, GFP_KERNEL); 394dbcc3475SJon Smirl if (!psc_dma) { 39533d7f778SJulia Lawall ret = -ENOMEM; 39633d7f778SJulia Lawall goto out_unmap; 397dbcc3475SJon Smirl } 398dbcc3475SJon Smirl 399dbcc3475SJon Smirl /* Get the PSC ID */ 40061c7a080SGrant Likely prop = of_get_property(op->dev.of_node, "cell-index", &size); 40133d7f778SJulia Lawall if (!prop || size < sizeof *prop) { 40233d7f778SJulia Lawall ret = -ENODEV; 40333d7f778SJulia Lawall goto out_free; 40433d7f778SJulia Lawall } 405dbcc3475SJon Smirl 406dbcc3475SJon Smirl spin_lock_init(&psc_dma->lock); 4070827d6baSGrant Likely mutex_init(&psc_dma->mutex); 408dbcc3475SJon Smirl psc_dma->id = be32_to_cpu(*prop); 409dbcc3475SJon Smirl psc_dma->irq = irq; 410dbcc3475SJon Smirl psc_dma->psc_regs = regs; 411dbcc3475SJon Smirl psc_dma->fifo_regs = regs + sizeof *psc_dma->psc_regs; 412dbcc3475SJon Smirl psc_dma->dev = &op->dev; 413dbcc3475SJon Smirl psc_dma->playback.psc_dma = psc_dma; 414dbcc3475SJon Smirl psc_dma->capture.psc_dma = psc_dma; 415dbcc3475SJon Smirl snprintf(psc_dma->name, sizeof psc_dma->name, "PSC%u", psc_dma->id); 416dbcc3475SJon Smirl 417dbcc3475SJon Smirl /* Find the address of the fifo data registers and setup the 418dbcc3475SJon Smirl * DMA tasks */ 419dbcc3475SJon Smirl fifo = res.start + offsetof(struct mpc52xx_psc, buffer.buffer_32); 420dbcc3475SJon Smirl psc_dma->capture.bcom_task = 421dbcc3475SJon Smirl bcom_psc_gen_bd_rx_init(psc_dma->id, 10, fifo, 512); 422dbcc3475SJon Smirl psc_dma->playback.bcom_task = 423dbcc3475SJon Smirl bcom_psc_gen_bd_tx_init(psc_dma->id, 10, fifo); 424dbcc3475SJon Smirl if (!psc_dma->capture.bcom_task || 425dbcc3475SJon Smirl !psc_dma->playback.bcom_task) { 426dbcc3475SJon Smirl dev_err(&op->dev, "Could not allocate bestcomm tasks\n"); 42733d7f778SJulia Lawall ret = -ENODEV; 42833d7f778SJulia Lawall goto out_free; 429dbcc3475SJon Smirl } 430dbcc3475SJon Smirl 431dbcc3475SJon Smirl /* Disable all interrupts and reset the PSC */ 432dbcc3475SJon Smirl out_be16(&psc_dma->psc_regs->isr_imr.imr, psc_dma->imr); 433dbcc3475SJon Smirl /* reset receiver */ 434dbcc3475SJon Smirl out_8(&psc_dma->psc_regs->command, MPC52xx_PSC_RST_RX); 435dbcc3475SJon Smirl /* reset transmitter */ 436dbcc3475SJon Smirl out_8(&psc_dma->psc_regs->command, MPC52xx_PSC_RST_TX); 437dbcc3475SJon Smirl /* reset error */ 438dbcc3475SJon Smirl out_8(&psc_dma->psc_regs->command, MPC52xx_PSC_RST_ERR_STAT); 439dbcc3475SJon Smirl /* reset mode */ 440dbcc3475SJon Smirl out_8(&psc_dma->psc_regs->command, MPC52xx_PSC_SEL_MODE_REG_1); 441dbcc3475SJon Smirl 442dbcc3475SJon Smirl /* Set up mode register; 443dbcc3475SJon Smirl * First write: RxRdy (FIFO Alarm) generates rx FIFO irq 444dbcc3475SJon Smirl * Second write: register Normal mode for non loopback 445dbcc3475SJon Smirl */ 446dbcc3475SJon Smirl out_8(&psc_dma->psc_regs->mode, 0); 447dbcc3475SJon Smirl out_8(&psc_dma->psc_regs->mode, 0); 448dbcc3475SJon Smirl 449dbcc3475SJon Smirl /* Set the TX and RX fifo alarm thresholds */ 450dbcc3475SJon Smirl out_be16(&psc_dma->fifo_regs->rfalarm, 0x100); 451dbcc3475SJon Smirl out_8(&psc_dma->fifo_regs->rfcntl, 0x4); 452dbcc3475SJon Smirl out_be16(&psc_dma->fifo_regs->tfalarm, 0x100); 453dbcc3475SJon Smirl out_8(&psc_dma->fifo_regs->tfcntl, 0x7); 454dbcc3475SJon Smirl 455dbcc3475SJon Smirl /* Lookup the IRQ numbers */ 456dbcc3475SJon Smirl psc_dma->playback.irq = 457dbcc3475SJon Smirl bcom_get_task_irq(psc_dma->playback.bcom_task); 458dbcc3475SJon Smirl psc_dma->capture.irq = 459dbcc3475SJon Smirl bcom_get_task_irq(psc_dma->capture.bcom_task); 460dbcc3475SJon Smirl 461dbcc3475SJon Smirl rc = request_irq(psc_dma->irq, &psc_dma_status_irq, IRQF_SHARED, 462dbcc3475SJon Smirl "psc-dma-status", psc_dma); 463a68cc8daSGrant Likely rc |= request_irq(psc_dma->capture.irq, &psc_dma_bcom_irq, IRQF_SHARED, 464dbcc3475SJon Smirl "psc-dma-capture", &psc_dma->capture); 465a68cc8daSGrant Likely rc |= request_irq(psc_dma->playback.irq, &psc_dma_bcom_irq, IRQF_SHARED, 466dbcc3475SJon Smirl "psc-dma-playback", &psc_dma->playback); 467dbcc3475SJon Smirl if (rc) { 46833d7f778SJulia Lawall ret = -ENODEV; 46933d7f778SJulia Lawall goto out_irq; 470dbcc3475SJon Smirl } 471dbcc3475SJon Smirl 472dbcc3475SJon Smirl /* Save what we've done so it can be found again later */ 473dbcc3475SJon Smirl dev_set_drvdata(&op->dev, psc_dma); 474dbcc3475SJon Smirl 475dbcc3475SJon Smirl /* Tell the ASoC OF helpers about it */ 47660bceb80SKuninori Morimoto return devm_snd_soc_register_component(&op->dev, 47760bceb80SKuninori Morimoto &mpc5200_audio_dma_component, NULL, 0); 47833d7f778SJulia Lawall out_irq: 47933d7f778SJulia Lawall free_irq(psc_dma->irq, psc_dma); 48033d7f778SJulia Lawall free_irq(psc_dma->capture.irq, &psc_dma->capture); 48133d7f778SJulia Lawall free_irq(psc_dma->playback.irq, &psc_dma->playback); 48233d7f778SJulia Lawall out_free: 48333d7f778SJulia Lawall kfree(psc_dma); 48433d7f778SJulia Lawall out_unmap: 48533d7f778SJulia Lawall iounmap(regs); 48633d7f778SJulia Lawall return ret; 487dbcc3475SJon Smirl } 488f515b673SEric Millbrandt EXPORT_SYMBOL_GPL(mpc5200_audio_dma_create); 489dbcc3475SJon Smirl 490f515b673SEric Millbrandt int mpc5200_audio_dma_destroy(struct platform_device *op) 491dbcc3475SJon Smirl { 492dbcc3475SJon Smirl struct psc_dma *psc_dma = dev_get_drvdata(&op->dev); 493dbcc3475SJon Smirl 494dbcc3475SJon Smirl dev_dbg(&op->dev, "mpc5200_audio_dma_destroy()\n"); 495dbcc3475SJon Smirl 496dbcc3475SJon Smirl bcom_gen_bd_rx_release(psc_dma->capture.bcom_task); 497dbcc3475SJon Smirl bcom_gen_bd_tx_release(psc_dma->playback.bcom_task); 498dbcc3475SJon Smirl 499dbcc3475SJon Smirl /* Release irqs */ 500dbcc3475SJon Smirl free_irq(psc_dma->irq, psc_dma); 501dbcc3475SJon Smirl free_irq(psc_dma->capture.irq, &psc_dma->capture); 502dbcc3475SJon Smirl free_irq(psc_dma->playback.irq, &psc_dma->playback); 503dbcc3475SJon Smirl 504dbcc3475SJon Smirl iounmap(psc_dma->psc_regs); 505dbcc3475SJon Smirl kfree(psc_dma); 506dbcc3475SJon Smirl dev_set_drvdata(&op->dev, NULL); 507dbcc3475SJon Smirl 508dbcc3475SJon Smirl return 0; 509dbcc3475SJon Smirl } 510f515b673SEric Millbrandt EXPORT_SYMBOL_GPL(mpc5200_audio_dma_destroy); 511dbcc3475SJon Smirl 512dbcc3475SJon Smirl MODULE_AUTHOR("Grant Likely <grant.likely@secretlab.ca>"); 513dbcc3475SJon Smirl MODULE_DESCRIPTION("Freescale MPC5200 PSC in DMA mode ASoC Driver"); 514dbcc3475SJon Smirl MODULE_LICENSE("GPL"); 515