1ac097cacSAndra Danciu // SPDX-License-Identifier: GPL-2.0-only 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 1016d1048bcSKuninori Morimoto static int psc_dma_hw_free(struct snd_soc_component *component, 1026d1048bcSKuninori Morimoto struct snd_pcm_substream *substream) 10389dd0842SJon Smirl { 10489dd0842SJon Smirl snd_pcm_set_runtime_buffer(substream, NULL); 10589dd0842SJon Smirl return 0; 10689dd0842SJon Smirl } 10789dd0842SJon Smirl 10889dd0842SJon Smirl /** 109cebe7767SJon Smirl * psc_dma_trigger: start and stop the DMA transfer. 11089dd0842SJon Smirl * 11189dd0842SJon Smirl * This function is called by ALSA to start, stop, pause, and resume the DMA 11289dd0842SJon Smirl * transfer of data. 11389dd0842SJon Smirl */ 1146d1048bcSKuninori Morimoto static int psc_dma_trigger(struct snd_soc_component *component, 1156d1048bcSKuninori Morimoto struct snd_pcm_substream *substream, int cmd) 11689dd0842SJon Smirl { 1179f5f078aSKuninori Morimoto struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 11817198ae7SKuninori Morimoto struct psc_dma *psc_dma = snd_soc_dai_get_drvdata(asoc_rtd_to_cpu(rtd, 0)); 11989dd0842SJon Smirl struct snd_pcm_runtime *runtime = substream->runtime; 1201d8222e8SGrant Likely struct psc_dma_stream *s = to_psc_dma_stream(substream, psc_dma); 121cebe7767SJon Smirl struct mpc52xx_psc __iomem *regs = psc_dma->psc_regs; 12289dd0842SJon Smirl u16 imr; 12389dd0842SJon Smirl unsigned long flags; 124dbcc3475SJon Smirl int i; 12589dd0842SJon Smirl 12689dd0842SJon Smirl switch (cmd) { 12789dd0842SJon Smirl case SNDRV_PCM_TRIGGER_START: 128c4878274SGrant Likely dev_dbg(psc_dma->dev, "START: stream=%i fbits=%u ps=%u #p=%u\n", 129c4878274SGrant Likely substream->pstr->stream, runtime->frame_bits, 130c4878274SGrant Likely (int)runtime->period_size, runtime->periods); 13189dd0842SJon Smirl s->period_bytes = frames_to_bytes(runtime, 13289dd0842SJon Smirl runtime->period_size); 1338f159d72SGrant Likely s->period_next = 0; 1348f159d72SGrant Likely s->period_current = 0; 13589dd0842SJon Smirl s->active = 1; 136c4878274SGrant Likely s->period_count = 0; 137dbcc3475SJon Smirl s->runtime = runtime; 138dbcc3475SJon Smirl 139dbcc3475SJon Smirl /* Fill up the bestcomm bd queue and enable DMA. 140dbcc3475SJon Smirl * This will begin filling the PSC's fifo. 141dbcc3475SJon Smirl */ 142dbcc3475SJon Smirl spin_lock_irqsave(&psc_dma->lock, flags); 143dbcc3475SJon Smirl 144d56b6eb6SGrant Likely if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) 145dbcc3475SJon Smirl bcom_gen_bd_rx_reset(s->bcom_task); 146d56b6eb6SGrant Likely else 147d56b6eb6SGrant Likely bcom_gen_bd_tx_reset(s->bcom_task); 148d56b6eb6SGrant Likely 149dbcc3475SJon Smirl for (i = 0; i < runtime->periods; i++) 150dbcc3475SJon Smirl if (!bcom_queue_full(s->bcom_task)) 151dbcc3475SJon Smirl psc_dma_bcom_enqueue_next_buffer(s); 15289dd0842SJon Smirl 15389dd0842SJon Smirl bcom_enable(s->bcom_task); 154cebe7767SJon Smirl spin_unlock_irqrestore(&psc_dma->lock, flags); 15589dd0842SJon Smirl 156dbcc3475SJon Smirl out_8(®s->command, MPC52xx_PSC_RST_ERR_STAT); 157dbcc3475SJon Smirl 15889dd0842SJon Smirl break; 15989dd0842SJon Smirl 16089dd0842SJon Smirl case SNDRV_PCM_TRIGGER_STOP: 161c4878274SGrant Likely dev_dbg(psc_dma->dev, "STOP: stream=%i periods_count=%i\n", 162c4878274SGrant Likely substream->pstr->stream, s->period_count); 16389dd0842SJon Smirl s->active = 0; 16489dd0842SJon Smirl 165dbcc3475SJon Smirl spin_lock_irqsave(&psc_dma->lock, flags); 16689dd0842SJon Smirl bcom_disable(s->bcom_task); 167dbcc3475SJon Smirl if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) 168dbcc3475SJon Smirl bcom_gen_bd_rx_reset(s->bcom_task); 169dbcc3475SJon Smirl else 170dbcc3475SJon Smirl bcom_gen_bd_tx_reset(s->bcom_task); 171dbcc3475SJon Smirl spin_unlock_irqrestore(&psc_dma->lock, flags); 17289dd0842SJon Smirl 17389dd0842SJon Smirl break; 17489dd0842SJon Smirl 17589dd0842SJon Smirl default: 176c4878274SGrant Likely dev_dbg(psc_dma->dev, "unhandled trigger: stream=%i cmd=%i\n", 177c4878274SGrant Likely substream->pstr->stream, cmd); 17889dd0842SJon Smirl return -EINVAL; 17989dd0842SJon Smirl } 18089dd0842SJon Smirl 18189dd0842SJon Smirl /* Update interrupt enable settings */ 18289dd0842SJon Smirl imr = 0; 183cebe7767SJon Smirl if (psc_dma->playback.active) 18489dd0842SJon Smirl imr |= MPC52xx_PSC_IMR_TXEMP; 185cebe7767SJon Smirl if (psc_dma->capture.active) 18689dd0842SJon Smirl imr |= MPC52xx_PSC_IMR_ORERR; 187dbcc3475SJon Smirl out_be16(®s->isr_imr.imr, psc_dma->imr | imr); 18889dd0842SJon Smirl 18989dd0842SJon Smirl return 0; 19089dd0842SJon Smirl } 19189dd0842SJon Smirl 19289dd0842SJon Smirl 19389dd0842SJon Smirl /* --------------------------------------------------------------------- 19489dd0842SJon Smirl * The PSC DMA 'ASoC platform' driver 19589dd0842SJon Smirl * 19689dd0842SJon Smirl * Can be referenced by an 'ASoC machine' driver 19789dd0842SJon Smirl * This driver only deals with the audio bus; it doesn't have any 19889dd0842SJon Smirl * interaction with the attached codec 19989dd0842SJon Smirl */ 20089dd0842SJon Smirl 201dbcc3475SJon Smirl static const struct snd_pcm_hardware psc_dma_hardware = { 20289dd0842SJon Smirl .info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | 20389dd0842SJon Smirl SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | 20489dd0842SJon Smirl SNDRV_PCM_INFO_BATCH, 20589dd0842SJon Smirl .formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_BE | 20689dd0842SJon Smirl SNDRV_PCM_FMTBIT_S24_BE | SNDRV_PCM_FMTBIT_S32_BE, 20789dd0842SJon Smirl .period_bytes_max = 1024 * 1024, 20889dd0842SJon Smirl .period_bytes_min = 32, 20989dd0842SJon Smirl .periods_min = 2, 21089dd0842SJon Smirl .periods_max = 256, 21189dd0842SJon Smirl .buffer_bytes_max = 2 * 1024 * 1024, 212dbcc3475SJon Smirl .fifo_size = 512, 21389dd0842SJon Smirl }; 21489dd0842SJon Smirl 2156d1048bcSKuninori Morimoto static int psc_dma_open(struct snd_soc_component *component, 2166d1048bcSKuninori Morimoto struct snd_pcm_substream *substream) 21789dd0842SJon Smirl { 218dbcc3475SJon Smirl struct snd_pcm_runtime *runtime = substream->runtime; 2199f5f078aSKuninori Morimoto struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 22017198ae7SKuninori Morimoto struct psc_dma *psc_dma = snd_soc_dai_get_drvdata(asoc_rtd_to_cpu(rtd, 0)); 221cebe7767SJon Smirl struct psc_dma_stream *s; 222dbcc3475SJon Smirl int rc; 22389dd0842SJon Smirl 224dbcc3475SJon Smirl dev_dbg(psc_dma->dev, "psc_dma_open(substream=%p)\n", substream); 22589dd0842SJon Smirl 22689dd0842SJon Smirl if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) 227cebe7767SJon Smirl s = &psc_dma->capture; 22889dd0842SJon Smirl else 229cebe7767SJon Smirl s = &psc_dma->playback; 23089dd0842SJon Smirl 231dbcc3475SJon Smirl snd_soc_set_runtime_hwparams(substream, &psc_dma_hardware); 232dbcc3475SJon Smirl 233dbcc3475SJon Smirl rc = snd_pcm_hw_constraint_integer(runtime, 234dbcc3475SJon Smirl SNDRV_PCM_HW_PARAM_PERIODS); 235dbcc3475SJon Smirl if (rc < 0) { 236dbcc3475SJon Smirl dev_err(substream->pcm->card->dev, "invalid buffer size\n"); 237dbcc3475SJon Smirl return rc; 238dbcc3475SJon Smirl } 23989dd0842SJon Smirl 24089dd0842SJon Smirl s->stream = substream; 24189dd0842SJon Smirl return 0; 24289dd0842SJon Smirl } 24389dd0842SJon Smirl 2446d1048bcSKuninori Morimoto static int psc_dma_close(struct snd_soc_component *component, 2456d1048bcSKuninori Morimoto struct snd_pcm_substream *substream) 24689dd0842SJon Smirl { 2479f5f078aSKuninori Morimoto struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 24817198ae7SKuninori Morimoto struct psc_dma *psc_dma = snd_soc_dai_get_drvdata(asoc_rtd_to_cpu(rtd, 0)); 249cebe7767SJon Smirl struct psc_dma_stream *s; 25089dd0842SJon Smirl 251dbcc3475SJon Smirl dev_dbg(psc_dma->dev, "psc_dma_close(substream=%p)\n", substream); 25289dd0842SJon Smirl 25389dd0842SJon Smirl if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) 254cebe7767SJon Smirl s = &psc_dma->capture; 25589dd0842SJon Smirl else 256cebe7767SJon Smirl s = &psc_dma->playback; 25789dd0842SJon Smirl 258dbcc3475SJon Smirl if (!psc_dma->playback.active && 259dbcc3475SJon Smirl !psc_dma->capture.active) { 260dbcc3475SJon Smirl 261dbcc3475SJon Smirl /* Disable all interrupts and reset the PSC */ 262dbcc3475SJon Smirl out_be16(&psc_dma->psc_regs->isr_imr.imr, psc_dma->imr); 263dbcc3475SJon Smirl out_8(&psc_dma->psc_regs->command, 4 << 4); /* reset error */ 264dbcc3475SJon Smirl } 26589dd0842SJon Smirl s->stream = NULL; 26689dd0842SJon Smirl return 0; 26789dd0842SJon Smirl } 26889dd0842SJon Smirl 26989dd0842SJon Smirl static snd_pcm_uframes_t 2706d1048bcSKuninori Morimoto psc_dma_pointer(struct snd_soc_component *component, 2716d1048bcSKuninori Morimoto struct snd_pcm_substream *substream) 27289dd0842SJon Smirl { 2739f5f078aSKuninori Morimoto struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 27417198ae7SKuninori Morimoto struct psc_dma *psc_dma = snd_soc_dai_get_drvdata(asoc_rtd_to_cpu(rtd, 0)); 275cebe7767SJon Smirl struct psc_dma_stream *s; 27689dd0842SJon Smirl dma_addr_t count; 27789dd0842SJon Smirl 27889dd0842SJon Smirl if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) 279cebe7767SJon Smirl s = &psc_dma->capture; 28089dd0842SJon Smirl else 281cebe7767SJon Smirl s = &psc_dma->playback; 28289dd0842SJon Smirl 2838f159d72SGrant Likely count = s->period_current * s->period_bytes; 28489dd0842SJon Smirl 28589dd0842SJon Smirl return bytes_to_frames(substream->runtime, count); 28689dd0842SJon Smirl } 28789dd0842SJon Smirl 2886d1048bcSKuninori Morimoto static int psc_dma_hw_params(struct snd_soc_component *component, 2896d1048bcSKuninori Morimoto struct snd_pcm_substream *substream, 290dbcc3475SJon Smirl struct snd_pcm_hw_params *params) 291dbcc3475SJon Smirl { 292dbcc3475SJon Smirl snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); 293dbcc3475SJon Smirl 294dbcc3475SJon Smirl return 0; 295dbcc3475SJon Smirl } 296dbcc3475SJon Smirl 2976d1048bcSKuninori Morimoto static int psc_dma_new(struct snd_soc_component *component, 2986d1048bcSKuninori Morimoto struct snd_soc_pcm_runtime *rtd) 29989dd0842SJon Smirl { 300552d1ef6SLiam Girdwood struct snd_card *card = rtd->card->snd_card; 30117198ae7SKuninori Morimoto struct snd_soc_dai *dai = asoc_rtd_to_cpu(rtd, 0); 302552d1ef6SLiam Girdwood struct snd_pcm *pcm = rtd->pcm; 303dbcc3475SJon Smirl size_t size = psc_dma_hardware.buffer_bytes_max; 304c9bd5e69SRussell King int rc; 30589dd0842SJon Smirl 30660bceb80SKuninori Morimoto dev_dbg(component->dev, "psc_dma_new(card=%p, dai=%p, pcm=%p)\n", 30789dd0842SJon Smirl card, dai, pcm); 30889dd0842SJon Smirl 309c9bd5e69SRussell King rc = dma_coerce_mask_and_coherent(card->dev, DMA_BIT_MASK(32)); 310c9bd5e69SRussell King if (rc) 311c9bd5e69SRussell King return rc; 31289dd0842SJon Smirl 3136296914cSJoachim Eastwood if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) { 314dbcc3475SJon Smirl rc = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, pcm->card->dev, 3156296914cSJoachim Eastwood size, &pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream->dma_buffer); 31689dd0842SJon Smirl if (rc) 31789dd0842SJon Smirl goto playback_alloc_err; 31889dd0842SJon Smirl } 31989dd0842SJon Smirl 3206296914cSJoachim Eastwood if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) { 321dbcc3475SJon Smirl rc = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, pcm->card->dev, 3226296914cSJoachim Eastwood size, &pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream->dma_buffer); 32389dd0842SJon Smirl if (rc) 32489dd0842SJon Smirl goto capture_alloc_err; 32589dd0842SJon Smirl } 32689dd0842SJon Smirl 32789dd0842SJon Smirl return 0; 32889dd0842SJon Smirl 32989dd0842SJon Smirl capture_alloc_err: 3306296914cSJoachim Eastwood if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) 3316296914cSJoachim Eastwood snd_dma_free_pages(&pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream->dma_buffer); 332dbcc3475SJon Smirl 33389dd0842SJon Smirl playback_alloc_err: 33489dd0842SJon Smirl dev_err(card->dev, "Cannot allocate buffer(s)\n"); 335dbcc3475SJon Smirl 33689dd0842SJon Smirl return -ENOMEM; 33789dd0842SJon Smirl } 33889dd0842SJon Smirl 3396d1048bcSKuninori Morimoto static void psc_dma_free(struct snd_soc_component *component, 3406d1048bcSKuninori Morimoto struct snd_pcm *pcm) 34189dd0842SJon Smirl { 34289dd0842SJon Smirl struct snd_soc_pcm_runtime *rtd = pcm->private_data; 34389dd0842SJon Smirl struct snd_pcm_substream *substream; 34489dd0842SJon Smirl int stream; 34589dd0842SJon Smirl 34660bceb80SKuninori Morimoto dev_dbg(component->dev, "psc_dma_free(pcm=%p)\n", pcm); 34789dd0842SJon Smirl 34889dd0842SJon Smirl for (stream = 0; stream < 2; stream++) { 34989dd0842SJon Smirl substream = pcm->streams[stream].substream; 35089dd0842SJon Smirl if (substream) { 35189dd0842SJon Smirl snd_dma_free_pages(&substream->dma_buffer); 35289dd0842SJon Smirl substream->dma_buffer.area = NULL; 35389dd0842SJon Smirl substream->dma_buffer.addr = 0; 35489dd0842SJon Smirl } 35589dd0842SJon Smirl } 35689dd0842SJon Smirl } 35789dd0842SJon Smirl 35860bceb80SKuninori Morimoto static const struct snd_soc_component_driver mpc5200_audio_dma_component = { 35960bceb80SKuninori Morimoto .name = DRV_NAME, 3606d1048bcSKuninori Morimoto .open = psc_dma_open, 3616d1048bcSKuninori Morimoto .close = psc_dma_close, 3626d1048bcSKuninori Morimoto .hw_free = psc_dma_hw_free, 3636d1048bcSKuninori Morimoto .pointer = psc_dma_pointer, 3646d1048bcSKuninori Morimoto .trigger = psc_dma_trigger, 3656d1048bcSKuninori Morimoto .hw_params = psc_dma_hw_params, 3666d1048bcSKuninori Morimoto .pcm_construct = psc_dma_new, 3676d1048bcSKuninori Morimoto .pcm_destruct = 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