189dd0842SJon Smirl /* 289dd0842SJon Smirl * Freescale MPC5200 PSC DMA 389dd0842SJon Smirl * ALSA SoC Platform driver 489dd0842SJon Smirl * 589dd0842SJon Smirl * Copyright (C) 2008 Secret Lab Technologies Ltd. 6dbcc3475SJon Smirl * Copyright (C) 2009 Jon Smirl, Digispeaker 789dd0842SJon Smirl */ 889dd0842SJon Smirl 989dd0842SJon Smirl #include <linux/module.h> 1089dd0842SJon Smirl #include <linux/of_device.h> 1189dd0842SJon Smirl 1289dd0842SJon Smirl #include <sound/soc.h> 1389dd0842SJon Smirl 1489dd0842SJon Smirl #include <sysdev/bestcomm/bestcomm.h> 1589dd0842SJon Smirl #include <sysdev/bestcomm/gen_bd.h> 1689dd0842SJon Smirl #include <asm/mpc52xx_psc.h> 1789dd0842SJon Smirl 1889dd0842SJon Smirl #include "mpc5200_dma.h" 1989dd0842SJon Smirl 2089dd0842SJon Smirl /* 2189dd0842SJon Smirl * Interrupt handlers 2289dd0842SJon Smirl */ 23cebe7767SJon Smirl static irqreturn_t psc_dma_status_irq(int irq, void *_psc_dma) 2489dd0842SJon Smirl { 25cebe7767SJon Smirl struct psc_dma *psc_dma = _psc_dma; 26cebe7767SJon Smirl struct mpc52xx_psc __iomem *regs = psc_dma->psc_regs; 2789dd0842SJon Smirl u16 isr; 2889dd0842SJon Smirl 2989dd0842SJon Smirl isr = in_be16(®s->mpc52xx_psc_isr); 3089dd0842SJon Smirl 3189dd0842SJon Smirl /* Playback underrun error */ 32cebe7767SJon Smirl if (psc_dma->playback.active && (isr & MPC52xx_PSC_IMR_TXEMP)) 33cebe7767SJon Smirl psc_dma->stats.underrun_count++; 3489dd0842SJon Smirl 3589dd0842SJon Smirl /* Capture overrun error */ 36cebe7767SJon Smirl if (psc_dma->capture.active && (isr & MPC52xx_PSC_IMR_ORERR)) 37cebe7767SJon Smirl psc_dma->stats.overrun_count++; 3889dd0842SJon Smirl 39dbcc3475SJon Smirl out_8(®s->command, MPC52xx_PSC_RST_ERR_STAT); 4089dd0842SJon Smirl 4189dd0842SJon Smirl return IRQ_HANDLED; 4289dd0842SJon Smirl } 4389dd0842SJon Smirl 4489dd0842SJon Smirl /** 45cebe7767SJon Smirl * psc_dma_bcom_enqueue_next_buffer - Enqueue another audio buffer 4689dd0842SJon Smirl * @s: pointer to stream private data structure 4789dd0842SJon Smirl * 4889dd0842SJon Smirl * Enqueues another audio period buffer into the bestcomm queue. 4989dd0842SJon Smirl * 5089dd0842SJon Smirl * Note: The routine must only be called when there is space available in 5189dd0842SJon Smirl * the queue. Otherwise the enqueue will fail and the audio ring buffer 5289dd0842SJon Smirl * will get out of sync 5389dd0842SJon Smirl */ 54cebe7767SJon Smirl static void psc_dma_bcom_enqueue_next_buffer(struct psc_dma_stream *s) 5589dd0842SJon Smirl { 5689dd0842SJon Smirl struct bcom_bd *bd; 5789dd0842SJon Smirl 5889dd0842SJon Smirl /* Prepare and enqueue the next buffer descriptor */ 5989dd0842SJon Smirl bd = bcom_prepare_next_buffer(s->bcom_task); 6089dd0842SJon Smirl bd->status = s->period_bytes; 618f159d72SGrant Likely bd->data[0] = s->runtime->dma_addr + (s->period_next * s->period_bytes); 6289dd0842SJon Smirl bcom_submit_next_buffer(s->bcom_task, NULL); 6389dd0842SJon Smirl 6489dd0842SJon Smirl /* Update for next period */ 658f159d72SGrant Likely s->period_next = (s->period_next + 1) % s->runtime->periods; 6689dd0842SJon Smirl } 6789dd0842SJon Smirl 6889dd0842SJon Smirl /* Bestcomm DMA irq handler */ 69dbcc3475SJon Smirl static irqreturn_t psc_dma_bcom_irq_tx(int irq, void *_psc_dma_stream) 7089dd0842SJon Smirl { 71cebe7767SJon Smirl struct psc_dma_stream *s = _psc_dma_stream; 7289dd0842SJon Smirl 73dbcc3475SJon Smirl spin_lock(&s->psc_dma->lock); 7489dd0842SJon Smirl /* For each finished period, dequeue the completed period buffer 7589dd0842SJon Smirl * and enqueue a new one in it's place. */ 7689dd0842SJon Smirl while (bcom_buffer_done(s->bcom_task)) { 7789dd0842SJon Smirl bcom_retrieve_buffer(s->bcom_task, NULL, NULL); 78dbcc3475SJon Smirl 798f159d72SGrant Likely s->period_current = (s->period_current+1) % s->runtime->periods; 80c4878274SGrant Likely s->period_count++; 81d56b6eb6SGrant Likely 82d56b6eb6SGrant Likely psc_dma_bcom_enqueue_next_buffer(s); 8389dd0842SJon Smirl } 84dbcc3475SJon Smirl spin_unlock(&s->psc_dma->lock); 8589dd0842SJon Smirl 8689dd0842SJon Smirl /* If the stream is active, then also inform the PCM middle layer 8789dd0842SJon Smirl * of the period finished event. */ 8889dd0842SJon Smirl if (s->active) 8989dd0842SJon Smirl snd_pcm_period_elapsed(s->stream); 9089dd0842SJon Smirl 9189dd0842SJon Smirl return IRQ_HANDLED; 9289dd0842SJon Smirl } 9389dd0842SJon Smirl 94dbcc3475SJon Smirl static irqreturn_t psc_dma_bcom_irq_rx(int irq, void *_psc_dma_stream) 9589dd0842SJon Smirl { 96dbcc3475SJon Smirl struct psc_dma_stream *s = _psc_dma_stream; 9789dd0842SJon Smirl 98dbcc3475SJon Smirl spin_lock(&s->psc_dma->lock); 99dbcc3475SJon Smirl /* For each finished period, dequeue the completed period buffer 100dbcc3475SJon Smirl * and enqueue a new one in it's place. */ 101dbcc3475SJon Smirl while (bcom_buffer_done(s->bcom_task)) { 102dbcc3475SJon Smirl bcom_retrieve_buffer(s->bcom_task, NULL, NULL); 10389dd0842SJon Smirl 1048f159d72SGrant Likely s->period_current = (s->period_current+1) % s->runtime->periods; 105c4878274SGrant Likely s->period_count++; 106dbcc3475SJon Smirl 107dbcc3475SJon Smirl psc_dma_bcom_enqueue_next_buffer(s); 10889dd0842SJon Smirl } 109dbcc3475SJon Smirl spin_unlock(&s->psc_dma->lock); 110dbcc3475SJon Smirl 111dbcc3475SJon Smirl /* If the stream is active, then also inform the PCM middle layer 112dbcc3475SJon Smirl * of the period finished event. */ 113dbcc3475SJon Smirl if (s->active) 114dbcc3475SJon Smirl snd_pcm_period_elapsed(s->stream); 115dbcc3475SJon Smirl 116dbcc3475SJon Smirl return IRQ_HANDLED; 11789dd0842SJon Smirl } 11889dd0842SJon Smirl 119dbcc3475SJon Smirl static int psc_dma_hw_free(struct snd_pcm_substream *substream) 12089dd0842SJon Smirl { 12189dd0842SJon Smirl snd_pcm_set_runtime_buffer(substream, NULL); 12289dd0842SJon Smirl return 0; 12389dd0842SJon Smirl } 12489dd0842SJon Smirl 12589dd0842SJon Smirl /** 126cebe7767SJon Smirl * psc_dma_trigger: start and stop the DMA transfer. 12789dd0842SJon Smirl * 12889dd0842SJon Smirl * This function is called by ALSA to start, stop, pause, and resume the DMA 12989dd0842SJon Smirl * transfer of data. 13089dd0842SJon Smirl */ 131dbcc3475SJon Smirl static int psc_dma_trigger(struct snd_pcm_substream *substream, int cmd) 13289dd0842SJon Smirl { 13389dd0842SJon Smirl struct snd_soc_pcm_runtime *rtd = substream->private_data; 134cebe7767SJon Smirl struct psc_dma *psc_dma = rtd->dai->cpu_dai->private_data; 13589dd0842SJon Smirl struct snd_pcm_runtime *runtime = substream->runtime; 1361d8222e8SGrant Likely struct psc_dma_stream *s = to_psc_dma_stream(substream, psc_dma); 137cebe7767SJon Smirl struct mpc52xx_psc __iomem *regs = psc_dma->psc_regs; 13889dd0842SJon Smirl u16 imr; 13989dd0842SJon Smirl unsigned long flags; 140dbcc3475SJon Smirl int i; 14189dd0842SJon Smirl 14289dd0842SJon Smirl switch (cmd) { 14389dd0842SJon Smirl case SNDRV_PCM_TRIGGER_START: 144c4878274SGrant Likely dev_dbg(psc_dma->dev, "START: stream=%i fbits=%u ps=%u #p=%u\n", 145c4878274SGrant Likely substream->pstr->stream, runtime->frame_bits, 146c4878274SGrant Likely (int)runtime->period_size, runtime->periods); 14789dd0842SJon Smirl s->period_bytes = frames_to_bytes(runtime, 14889dd0842SJon Smirl runtime->period_size); 1498f159d72SGrant Likely s->period_next = 0; 1508f159d72SGrant Likely s->period_current = 0; 15189dd0842SJon Smirl s->active = 1; 152c4878274SGrant Likely s->period_count = 0; 153dbcc3475SJon Smirl s->runtime = runtime; 154dbcc3475SJon Smirl 155dbcc3475SJon Smirl /* Fill up the bestcomm bd queue and enable DMA. 156dbcc3475SJon Smirl * This will begin filling the PSC's fifo. 157dbcc3475SJon Smirl */ 158dbcc3475SJon Smirl spin_lock_irqsave(&psc_dma->lock, flags); 159dbcc3475SJon Smirl 160d56b6eb6SGrant Likely if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) 161dbcc3475SJon Smirl bcom_gen_bd_rx_reset(s->bcom_task); 162d56b6eb6SGrant Likely else 163d56b6eb6SGrant Likely bcom_gen_bd_tx_reset(s->bcom_task); 164d56b6eb6SGrant Likely 165dbcc3475SJon Smirl for (i = 0; i < runtime->periods; i++) 166dbcc3475SJon Smirl if (!bcom_queue_full(s->bcom_task)) 167dbcc3475SJon Smirl psc_dma_bcom_enqueue_next_buffer(s); 16889dd0842SJon Smirl 16989dd0842SJon Smirl bcom_enable(s->bcom_task); 170cebe7767SJon Smirl spin_unlock_irqrestore(&psc_dma->lock, flags); 17189dd0842SJon Smirl 172dbcc3475SJon Smirl out_8(®s->command, MPC52xx_PSC_RST_ERR_STAT); 173dbcc3475SJon Smirl 17489dd0842SJon Smirl break; 17589dd0842SJon Smirl 17689dd0842SJon Smirl case SNDRV_PCM_TRIGGER_STOP: 177c4878274SGrant Likely dev_dbg(psc_dma->dev, "STOP: stream=%i periods_count=%i\n", 178c4878274SGrant Likely substream->pstr->stream, s->period_count); 17989dd0842SJon Smirl s->active = 0; 18089dd0842SJon Smirl 181dbcc3475SJon Smirl spin_lock_irqsave(&psc_dma->lock, flags); 18289dd0842SJon Smirl bcom_disable(s->bcom_task); 183dbcc3475SJon Smirl if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) 184dbcc3475SJon Smirl bcom_gen_bd_rx_reset(s->bcom_task); 185dbcc3475SJon Smirl else 186dbcc3475SJon Smirl bcom_gen_bd_tx_reset(s->bcom_task); 187dbcc3475SJon Smirl spin_unlock_irqrestore(&psc_dma->lock, flags); 18889dd0842SJon Smirl 18989dd0842SJon Smirl break; 19089dd0842SJon Smirl 19189dd0842SJon Smirl default: 192c4878274SGrant Likely dev_dbg(psc_dma->dev, "unhandled trigger: stream=%i cmd=%i\n", 193c4878274SGrant Likely substream->pstr->stream, cmd); 19489dd0842SJon Smirl return -EINVAL; 19589dd0842SJon Smirl } 19689dd0842SJon Smirl 19789dd0842SJon Smirl /* Update interrupt enable settings */ 19889dd0842SJon Smirl imr = 0; 199cebe7767SJon Smirl if (psc_dma->playback.active) 20089dd0842SJon Smirl imr |= MPC52xx_PSC_IMR_TXEMP; 201cebe7767SJon Smirl if (psc_dma->capture.active) 20289dd0842SJon Smirl imr |= MPC52xx_PSC_IMR_ORERR; 203dbcc3475SJon Smirl out_be16(®s->isr_imr.imr, psc_dma->imr | imr); 20489dd0842SJon Smirl 20589dd0842SJon Smirl return 0; 20689dd0842SJon Smirl } 20789dd0842SJon Smirl 20889dd0842SJon Smirl 20989dd0842SJon Smirl /* --------------------------------------------------------------------- 21089dd0842SJon Smirl * The PSC DMA 'ASoC platform' driver 21189dd0842SJon Smirl * 21289dd0842SJon Smirl * Can be referenced by an 'ASoC machine' driver 21389dd0842SJon Smirl * This driver only deals with the audio bus; it doesn't have any 21489dd0842SJon Smirl * interaction with the attached codec 21589dd0842SJon Smirl */ 21689dd0842SJon Smirl 217dbcc3475SJon Smirl static const struct snd_pcm_hardware psc_dma_hardware = { 21889dd0842SJon Smirl .info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | 21989dd0842SJon Smirl SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | 22089dd0842SJon Smirl SNDRV_PCM_INFO_BATCH, 22189dd0842SJon Smirl .formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_BE | 22289dd0842SJon Smirl SNDRV_PCM_FMTBIT_S24_BE | SNDRV_PCM_FMTBIT_S32_BE, 22389dd0842SJon Smirl .rate_min = 8000, 22489dd0842SJon Smirl .rate_max = 48000, 225dbcc3475SJon Smirl .channels_min = 1, 22689dd0842SJon Smirl .channels_max = 2, 22789dd0842SJon Smirl .period_bytes_max = 1024 * 1024, 22889dd0842SJon Smirl .period_bytes_min = 32, 22989dd0842SJon Smirl .periods_min = 2, 23089dd0842SJon Smirl .periods_max = 256, 23189dd0842SJon Smirl .buffer_bytes_max = 2 * 1024 * 1024, 232dbcc3475SJon Smirl .fifo_size = 512, 23389dd0842SJon Smirl }; 23489dd0842SJon Smirl 235dbcc3475SJon Smirl static int psc_dma_open(struct snd_pcm_substream *substream) 23689dd0842SJon Smirl { 237dbcc3475SJon Smirl struct snd_pcm_runtime *runtime = substream->runtime; 23889dd0842SJon Smirl struct snd_soc_pcm_runtime *rtd = substream->private_data; 239cebe7767SJon Smirl struct psc_dma *psc_dma = rtd->dai->cpu_dai->private_data; 240cebe7767SJon Smirl struct psc_dma_stream *s; 241dbcc3475SJon Smirl int rc; 24289dd0842SJon Smirl 243dbcc3475SJon Smirl dev_dbg(psc_dma->dev, "psc_dma_open(substream=%p)\n", substream); 24489dd0842SJon Smirl 24589dd0842SJon Smirl if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) 246cebe7767SJon Smirl s = &psc_dma->capture; 24789dd0842SJon Smirl else 248cebe7767SJon Smirl s = &psc_dma->playback; 24989dd0842SJon Smirl 250dbcc3475SJon Smirl snd_soc_set_runtime_hwparams(substream, &psc_dma_hardware); 251dbcc3475SJon Smirl 252dbcc3475SJon Smirl rc = snd_pcm_hw_constraint_integer(runtime, 253dbcc3475SJon Smirl SNDRV_PCM_HW_PARAM_PERIODS); 254dbcc3475SJon Smirl if (rc < 0) { 255dbcc3475SJon Smirl dev_err(substream->pcm->card->dev, "invalid buffer size\n"); 256dbcc3475SJon Smirl return rc; 257dbcc3475SJon Smirl } 25889dd0842SJon Smirl 25989dd0842SJon Smirl s->stream = substream; 26089dd0842SJon Smirl return 0; 26189dd0842SJon Smirl } 26289dd0842SJon Smirl 263dbcc3475SJon Smirl static int psc_dma_close(struct snd_pcm_substream *substream) 26489dd0842SJon Smirl { 26589dd0842SJon Smirl struct snd_soc_pcm_runtime *rtd = substream->private_data; 266cebe7767SJon Smirl struct psc_dma *psc_dma = rtd->dai->cpu_dai->private_data; 267cebe7767SJon Smirl struct psc_dma_stream *s; 26889dd0842SJon Smirl 269dbcc3475SJon Smirl dev_dbg(psc_dma->dev, "psc_dma_close(substream=%p)\n", substream); 27089dd0842SJon Smirl 27189dd0842SJon Smirl if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) 272cebe7767SJon Smirl s = &psc_dma->capture; 27389dd0842SJon Smirl else 274cebe7767SJon Smirl s = &psc_dma->playback; 27589dd0842SJon Smirl 276dbcc3475SJon Smirl if (!psc_dma->playback.active && 277dbcc3475SJon Smirl !psc_dma->capture.active) { 278dbcc3475SJon Smirl 279dbcc3475SJon Smirl /* Disable all interrupts and reset the PSC */ 280dbcc3475SJon Smirl out_be16(&psc_dma->psc_regs->isr_imr.imr, psc_dma->imr); 281dbcc3475SJon Smirl out_8(&psc_dma->psc_regs->command, 4 << 4); /* reset error */ 282dbcc3475SJon Smirl } 28389dd0842SJon Smirl s->stream = NULL; 28489dd0842SJon Smirl return 0; 28589dd0842SJon Smirl } 28689dd0842SJon Smirl 28789dd0842SJon Smirl static snd_pcm_uframes_t 288dbcc3475SJon Smirl psc_dma_pointer(struct snd_pcm_substream *substream) 28989dd0842SJon Smirl { 29089dd0842SJon Smirl struct snd_soc_pcm_runtime *rtd = substream->private_data; 291cebe7767SJon Smirl struct psc_dma *psc_dma = rtd->dai->cpu_dai->private_data; 292cebe7767SJon Smirl struct psc_dma_stream *s; 29389dd0842SJon Smirl dma_addr_t count; 29489dd0842SJon Smirl 29589dd0842SJon Smirl if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) 296cebe7767SJon Smirl s = &psc_dma->capture; 29789dd0842SJon Smirl else 298cebe7767SJon Smirl s = &psc_dma->playback; 29989dd0842SJon Smirl 3008f159d72SGrant Likely count = s->period_current * s->period_bytes; 30189dd0842SJon Smirl 30289dd0842SJon Smirl return bytes_to_frames(substream->runtime, count); 30389dd0842SJon Smirl } 30489dd0842SJon Smirl 305dbcc3475SJon Smirl static int 306dbcc3475SJon Smirl psc_dma_hw_params(struct snd_pcm_substream *substream, 307dbcc3475SJon Smirl struct snd_pcm_hw_params *params) 308dbcc3475SJon Smirl { 309dbcc3475SJon Smirl snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); 310dbcc3475SJon Smirl 311dbcc3475SJon Smirl return 0; 312dbcc3475SJon Smirl } 313dbcc3475SJon Smirl 314dbcc3475SJon Smirl static struct snd_pcm_ops psc_dma_ops = { 315dbcc3475SJon Smirl .open = psc_dma_open, 316dbcc3475SJon Smirl .close = psc_dma_close, 317dbcc3475SJon Smirl .hw_free = psc_dma_hw_free, 31889dd0842SJon Smirl .ioctl = snd_pcm_lib_ioctl, 319dbcc3475SJon Smirl .pointer = psc_dma_pointer, 320dbcc3475SJon Smirl .trigger = psc_dma_trigger, 321dbcc3475SJon Smirl .hw_params = psc_dma_hw_params, 32289dd0842SJon Smirl }; 32389dd0842SJon Smirl 324dbcc3475SJon Smirl static u64 psc_dma_dmamask = 0xffffffff; 325dbcc3475SJon Smirl static int psc_dma_new(struct snd_card *card, struct snd_soc_dai *dai, 32689dd0842SJon Smirl struct snd_pcm *pcm) 32789dd0842SJon Smirl { 32889dd0842SJon Smirl struct snd_soc_pcm_runtime *rtd = pcm->private_data; 329dbcc3475SJon Smirl struct psc_dma *psc_dma = rtd->dai->cpu_dai->private_data; 330dbcc3475SJon Smirl size_t size = psc_dma_hardware.buffer_bytes_max; 33189dd0842SJon Smirl int rc = 0; 33289dd0842SJon Smirl 333dbcc3475SJon Smirl dev_dbg(rtd->socdev->dev, "psc_dma_new(card=%p, dai=%p, pcm=%p)\n", 33489dd0842SJon Smirl card, dai, pcm); 33589dd0842SJon Smirl 33689dd0842SJon Smirl if (!card->dev->dma_mask) 337dbcc3475SJon Smirl card->dev->dma_mask = &psc_dma_dmamask; 33889dd0842SJon Smirl if (!card->dev->coherent_dma_mask) 33989dd0842SJon Smirl card->dev->coherent_dma_mask = 0xffffffff; 34089dd0842SJon Smirl 34189dd0842SJon Smirl if (pcm->streams[0].substream) { 342dbcc3475SJon Smirl rc = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, pcm->card->dev, 343dbcc3475SJon Smirl size, &pcm->streams[0].substream->dma_buffer); 34489dd0842SJon Smirl if (rc) 34589dd0842SJon Smirl goto playback_alloc_err; 34689dd0842SJon Smirl } 34789dd0842SJon Smirl 34889dd0842SJon Smirl if (pcm->streams[1].substream) { 349dbcc3475SJon Smirl rc = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, pcm->card->dev, 350dbcc3475SJon Smirl size, &pcm->streams[1].substream->dma_buffer); 35189dd0842SJon Smirl if (rc) 35289dd0842SJon Smirl goto capture_alloc_err; 35389dd0842SJon Smirl } 35489dd0842SJon Smirl 355dbcc3475SJon Smirl if (rtd->socdev->card->codec->ac97) 356dbcc3475SJon Smirl rtd->socdev->card->codec->ac97->private_data = psc_dma; 357dbcc3475SJon Smirl 35889dd0842SJon Smirl return 0; 35989dd0842SJon Smirl 36089dd0842SJon Smirl capture_alloc_err: 36189dd0842SJon Smirl if (pcm->streams[0].substream) 36289dd0842SJon Smirl snd_dma_free_pages(&pcm->streams[0].substream->dma_buffer); 363dbcc3475SJon Smirl 36489dd0842SJon Smirl playback_alloc_err: 36589dd0842SJon Smirl dev_err(card->dev, "Cannot allocate buffer(s)\n"); 366dbcc3475SJon Smirl 36789dd0842SJon Smirl return -ENOMEM; 36889dd0842SJon Smirl } 36989dd0842SJon Smirl 370dbcc3475SJon Smirl static void psc_dma_free(struct snd_pcm *pcm) 37189dd0842SJon Smirl { 37289dd0842SJon Smirl struct snd_soc_pcm_runtime *rtd = pcm->private_data; 37389dd0842SJon Smirl struct snd_pcm_substream *substream; 37489dd0842SJon Smirl int stream; 37589dd0842SJon Smirl 376dbcc3475SJon Smirl dev_dbg(rtd->socdev->dev, "psc_dma_free(pcm=%p)\n", pcm); 37789dd0842SJon Smirl 37889dd0842SJon Smirl for (stream = 0; stream < 2; stream++) { 37989dd0842SJon Smirl substream = pcm->streams[stream].substream; 38089dd0842SJon Smirl if (substream) { 38189dd0842SJon Smirl snd_dma_free_pages(&substream->dma_buffer); 38289dd0842SJon Smirl substream->dma_buffer.area = NULL; 38389dd0842SJon Smirl substream->dma_buffer.addr = 0; 38489dd0842SJon Smirl } 38589dd0842SJon Smirl } 38689dd0842SJon Smirl } 38789dd0842SJon Smirl 388dbcc3475SJon Smirl struct snd_soc_platform mpc5200_audio_dma_platform = { 38989dd0842SJon Smirl .name = "mpc5200-psc-audio", 390dbcc3475SJon Smirl .pcm_ops = &psc_dma_ops, 391dbcc3475SJon Smirl .pcm_new = &psc_dma_new, 392dbcc3475SJon Smirl .pcm_free = &psc_dma_free, 39389dd0842SJon Smirl }; 394dbcc3475SJon Smirl EXPORT_SYMBOL_GPL(mpc5200_audio_dma_platform); 39589dd0842SJon Smirl 396dbcc3475SJon Smirl int mpc5200_audio_dma_create(struct of_device *op) 397dbcc3475SJon Smirl { 398dbcc3475SJon Smirl phys_addr_t fifo; 399dbcc3475SJon Smirl struct psc_dma *psc_dma; 400dbcc3475SJon Smirl struct resource res; 401dbcc3475SJon Smirl int size, irq, rc; 402dbcc3475SJon Smirl const __be32 *prop; 403dbcc3475SJon Smirl void __iomem *regs; 40433d7f778SJulia Lawall int ret; 405dbcc3475SJon Smirl 406dbcc3475SJon Smirl /* Fetch the registers and IRQ of the PSC */ 407dbcc3475SJon Smirl irq = irq_of_parse_and_map(op->node, 0); 408dbcc3475SJon Smirl if (of_address_to_resource(op->node, 0, &res)) { 409dbcc3475SJon Smirl dev_err(&op->dev, "Missing reg property\n"); 410dbcc3475SJon Smirl return -ENODEV; 411dbcc3475SJon Smirl } 412dbcc3475SJon Smirl regs = ioremap(res.start, 1 + res.end - res.start); 413dbcc3475SJon Smirl if (!regs) { 414dbcc3475SJon Smirl dev_err(&op->dev, "Could not map registers\n"); 415dbcc3475SJon Smirl return -ENODEV; 416dbcc3475SJon Smirl } 417dbcc3475SJon Smirl 418dbcc3475SJon Smirl /* Allocate and initialize the driver private data */ 419dbcc3475SJon Smirl psc_dma = kzalloc(sizeof *psc_dma, GFP_KERNEL); 420dbcc3475SJon Smirl if (!psc_dma) { 42133d7f778SJulia Lawall ret = -ENOMEM; 42233d7f778SJulia Lawall goto out_unmap; 423dbcc3475SJon Smirl } 424dbcc3475SJon Smirl 425dbcc3475SJon Smirl /* Get the PSC ID */ 426dbcc3475SJon Smirl prop = of_get_property(op->node, "cell-index", &size); 42733d7f778SJulia Lawall if (!prop || size < sizeof *prop) { 42833d7f778SJulia Lawall ret = -ENODEV; 42933d7f778SJulia Lawall goto out_free; 43033d7f778SJulia Lawall } 431dbcc3475SJon Smirl 432dbcc3475SJon Smirl spin_lock_init(&psc_dma->lock); 4330827d6baSGrant Likely mutex_init(&psc_dma->mutex); 434dbcc3475SJon Smirl psc_dma->id = be32_to_cpu(*prop); 435dbcc3475SJon Smirl psc_dma->irq = irq; 436dbcc3475SJon Smirl psc_dma->psc_regs = regs; 437dbcc3475SJon Smirl psc_dma->fifo_regs = regs + sizeof *psc_dma->psc_regs; 438dbcc3475SJon Smirl psc_dma->dev = &op->dev; 439dbcc3475SJon Smirl psc_dma->playback.psc_dma = psc_dma; 440dbcc3475SJon Smirl psc_dma->capture.psc_dma = psc_dma; 441dbcc3475SJon Smirl snprintf(psc_dma->name, sizeof psc_dma->name, "PSC%u", psc_dma->id); 442dbcc3475SJon Smirl 443dbcc3475SJon Smirl /* Find the address of the fifo data registers and setup the 444dbcc3475SJon Smirl * DMA tasks */ 445dbcc3475SJon Smirl fifo = res.start + offsetof(struct mpc52xx_psc, buffer.buffer_32); 446dbcc3475SJon Smirl psc_dma->capture.bcom_task = 447dbcc3475SJon Smirl bcom_psc_gen_bd_rx_init(psc_dma->id, 10, fifo, 512); 448dbcc3475SJon Smirl psc_dma->playback.bcom_task = 449dbcc3475SJon Smirl bcom_psc_gen_bd_tx_init(psc_dma->id, 10, fifo); 450dbcc3475SJon Smirl if (!psc_dma->capture.bcom_task || 451dbcc3475SJon Smirl !psc_dma->playback.bcom_task) { 452dbcc3475SJon Smirl dev_err(&op->dev, "Could not allocate bestcomm tasks\n"); 45333d7f778SJulia Lawall ret = -ENODEV; 45433d7f778SJulia Lawall goto out_free; 455dbcc3475SJon Smirl } 456dbcc3475SJon Smirl 457dbcc3475SJon Smirl /* Disable all interrupts and reset the PSC */ 458dbcc3475SJon Smirl out_be16(&psc_dma->psc_regs->isr_imr.imr, psc_dma->imr); 459dbcc3475SJon Smirl /* reset receiver */ 460dbcc3475SJon Smirl out_8(&psc_dma->psc_regs->command, MPC52xx_PSC_RST_RX); 461dbcc3475SJon Smirl /* reset transmitter */ 462dbcc3475SJon Smirl out_8(&psc_dma->psc_regs->command, MPC52xx_PSC_RST_TX); 463dbcc3475SJon Smirl /* reset error */ 464dbcc3475SJon Smirl out_8(&psc_dma->psc_regs->command, MPC52xx_PSC_RST_ERR_STAT); 465dbcc3475SJon Smirl /* reset mode */ 466dbcc3475SJon Smirl out_8(&psc_dma->psc_regs->command, MPC52xx_PSC_SEL_MODE_REG_1); 467dbcc3475SJon Smirl 468dbcc3475SJon Smirl /* Set up mode register; 469dbcc3475SJon Smirl * First write: RxRdy (FIFO Alarm) generates rx FIFO irq 470dbcc3475SJon Smirl * Second write: register Normal mode for non loopback 471dbcc3475SJon Smirl */ 472dbcc3475SJon Smirl out_8(&psc_dma->psc_regs->mode, 0); 473dbcc3475SJon Smirl out_8(&psc_dma->psc_regs->mode, 0); 474dbcc3475SJon Smirl 475dbcc3475SJon Smirl /* Set the TX and RX fifo alarm thresholds */ 476dbcc3475SJon Smirl out_be16(&psc_dma->fifo_regs->rfalarm, 0x100); 477dbcc3475SJon Smirl out_8(&psc_dma->fifo_regs->rfcntl, 0x4); 478dbcc3475SJon Smirl out_be16(&psc_dma->fifo_regs->tfalarm, 0x100); 479dbcc3475SJon Smirl out_8(&psc_dma->fifo_regs->tfcntl, 0x7); 480dbcc3475SJon Smirl 481dbcc3475SJon Smirl /* Lookup the IRQ numbers */ 482dbcc3475SJon Smirl psc_dma->playback.irq = 483dbcc3475SJon Smirl bcom_get_task_irq(psc_dma->playback.bcom_task); 484dbcc3475SJon Smirl psc_dma->capture.irq = 485dbcc3475SJon Smirl bcom_get_task_irq(psc_dma->capture.bcom_task); 486dbcc3475SJon Smirl 487dbcc3475SJon Smirl rc = request_irq(psc_dma->irq, &psc_dma_status_irq, IRQF_SHARED, 488dbcc3475SJon Smirl "psc-dma-status", psc_dma); 489dbcc3475SJon Smirl rc |= request_irq(psc_dma->capture.irq, 490dbcc3475SJon Smirl &psc_dma_bcom_irq_rx, IRQF_SHARED, 491dbcc3475SJon Smirl "psc-dma-capture", &psc_dma->capture); 492dbcc3475SJon Smirl rc |= request_irq(psc_dma->playback.irq, 493dbcc3475SJon Smirl &psc_dma_bcom_irq_tx, IRQF_SHARED, 494dbcc3475SJon Smirl "psc-dma-playback", &psc_dma->playback); 495dbcc3475SJon Smirl if (rc) { 49633d7f778SJulia Lawall ret = -ENODEV; 49733d7f778SJulia Lawall goto out_irq; 498dbcc3475SJon Smirl } 499dbcc3475SJon Smirl 500dbcc3475SJon Smirl /* Save what we've done so it can be found again later */ 501dbcc3475SJon Smirl dev_set_drvdata(&op->dev, psc_dma); 502dbcc3475SJon Smirl 503dbcc3475SJon Smirl /* Tell the ASoC OF helpers about it */ 504dbcc3475SJon Smirl return snd_soc_register_platform(&mpc5200_audio_dma_platform); 50533d7f778SJulia Lawall out_irq: 50633d7f778SJulia Lawall free_irq(psc_dma->irq, psc_dma); 50733d7f778SJulia Lawall free_irq(psc_dma->capture.irq, &psc_dma->capture); 50833d7f778SJulia Lawall free_irq(psc_dma->playback.irq, &psc_dma->playback); 50933d7f778SJulia Lawall out_free: 51033d7f778SJulia Lawall kfree(psc_dma); 51133d7f778SJulia Lawall out_unmap: 51233d7f778SJulia Lawall iounmap(regs); 51333d7f778SJulia Lawall return ret; 514dbcc3475SJon Smirl } 515dbcc3475SJon Smirl EXPORT_SYMBOL_GPL(mpc5200_audio_dma_create); 516dbcc3475SJon Smirl 517dbcc3475SJon Smirl int mpc5200_audio_dma_destroy(struct of_device *op) 518dbcc3475SJon Smirl { 519dbcc3475SJon Smirl struct psc_dma *psc_dma = dev_get_drvdata(&op->dev); 520dbcc3475SJon Smirl 521dbcc3475SJon Smirl dev_dbg(&op->dev, "mpc5200_audio_dma_destroy()\n"); 522dbcc3475SJon Smirl 523dbcc3475SJon Smirl snd_soc_unregister_platform(&mpc5200_audio_dma_platform); 524dbcc3475SJon Smirl 525dbcc3475SJon Smirl bcom_gen_bd_rx_release(psc_dma->capture.bcom_task); 526dbcc3475SJon Smirl bcom_gen_bd_tx_release(psc_dma->playback.bcom_task); 527dbcc3475SJon Smirl 528dbcc3475SJon Smirl /* Release irqs */ 529dbcc3475SJon Smirl free_irq(psc_dma->irq, psc_dma); 530dbcc3475SJon Smirl free_irq(psc_dma->capture.irq, &psc_dma->capture); 531dbcc3475SJon Smirl free_irq(psc_dma->playback.irq, &psc_dma->playback); 532dbcc3475SJon Smirl 533dbcc3475SJon Smirl iounmap(psc_dma->psc_regs); 534dbcc3475SJon Smirl kfree(psc_dma); 535dbcc3475SJon Smirl dev_set_drvdata(&op->dev, NULL); 536dbcc3475SJon Smirl 537dbcc3475SJon Smirl return 0; 538dbcc3475SJon Smirl } 539dbcc3475SJon Smirl EXPORT_SYMBOL_GPL(mpc5200_audio_dma_destroy); 540dbcc3475SJon Smirl 541dbcc3475SJon Smirl MODULE_AUTHOR("Grant Likely <grant.likely@secretlab.ca>"); 542dbcc3475SJon Smirl MODULE_DESCRIPTION("Freescale MPC5200 PSC in DMA mode ASoC Driver"); 543dbcc3475SJon Smirl MODULE_LICENSE("GPL"); 544