static int atmel_pcm_prepare(struct snd_pcm_substream *substream) { struct atmel_runtime_data *prtd = substream->runtime->private_data; struct atmel_pcm_dma_params *params = prtd->params; ssc_writex(params->ssc->regs, SSC_IDR, params->mask->ssc_endx | params->mask->ssc_endbuf); ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR, params->mask->pdc_disable); return 0; }
/** * atmel_pcm_dma_irq: SSC interrupt handler for DMAENGINE enabled SSC * * We use DMAENGINE to send/receive data to/from SSC so this ISR is only to * check if any overrun occured. */ static void atmel_pcm_dma_irq(u32 ssc_sr, struct snd_pcm_substream *substream) { struct atmel_pcm_dma_params *prtd; prtd = snd_dmaengine_pcm_get_data(substream); if (ssc_sr & prtd->mask->ssc_error) { if (snd_pcm_running(substream)) pr_warn("atmel-pcm: buffer %s on %s (SSC_SR=%#x)\n", substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? "underrun" : "overrun", prtd->name, ssc_sr); /* stop RX and capture: will be enabled again at restart */ ssc_writex(prtd->ssc->regs, SSC_CR, prtd->mask->ssc_disable); snd_pcm_stream_lock(substream); snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN); snd_pcm_stream_unlock(substream); /* now drain RHR and read status to remove xrun condition */ ssc_readx(prtd->ssc->regs, SSC_RHR); ssc_readx(prtd->ssc->regs, SSC_SR); } }
static int atmel_pcm_resume(struct snd_soc_dai *dai) { struct snd_pcm_runtime *runtime = dai->runtime; struct atmel_runtime_data *prtd; struct atmel_pcm_dma_params *params; if (!runtime) return 0; prtd = runtime->private_data; params = prtd->params; /* restore the PDC registers and enable the PDC */ ssc_writex(params->ssc->regs, params->pdc->xpr, prtd->pdc_xpr_save); ssc_writex(params->ssc->regs, params->pdc->xcr, prtd->pdc_xcr_save); ssc_writex(params->ssc->regs, params->pdc->xnpr, prtd->pdc_xnpr_save); ssc_writex(params->ssc->regs, params->pdc->xncr, prtd->pdc_xncr_save); ssc_writel(params->ssc->regs, PDC_PTCR, params->mask->pdc_enable); return 0; }
static int at32_pcm_resume(struct platform_device *pdev, struct snd_soc_dai *dai) { struct snd_pcm_runtime *runtime = dai->runtime; struct at32_runtime_data *prtd; struct at32_pcm_dma_params *params; if (runtime == NULL) return 0; prtd = runtime->private_data; params = prtd->params; /* Restore the PDC registers and enable the PDC */ ssc_writex(params->ssc->regs, params->pdc->xpr, prtd->pdc_xpr_save); ssc_writex(params->ssc->regs, params->pdc->xcr, prtd->pdc_xcr_save); ssc_writex(params->ssc->regs, params->pdc->xnpr, prtd->pdc_xnpr_save); ssc_writex(params->ssc->regs, params->pdc->xncr, prtd->pdc_xncr_save); ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR, params->mask->pdc_enable); return 0; }
static int atmel_pcm_hw_free(struct snd_pcm_substream *substream) { struct atmel_runtime_data *prtd = substream->runtime->private_data; struct atmel_pcm_dma_params *params = prtd->params; if (params != NULL) { ssc_writex(params->ssc->regs, SSC_PDC_PTCR, params->mask->pdc_disable); prtd->params->dma_intr_handler = NULL; } return 0; }
/*--------------------------------------------------------------------------*\ * ISR \*--------------------------------------------------------------------------*/ static void atmel_pcm_dma_irq(u32 ssc_sr, struct snd_pcm_substream *substream) { struct atmel_runtime_data *prtd = substream->runtime->private_data; struct atmel_pcm_dma_params *params = prtd->params; static int count; count++; if (ssc_sr & params->mask->ssc_endbuf) { pr_warning("atmel-pcm: buffer %s on %s" " (SSC_SR=%#x, count=%d)\n", substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? "underrun" : "overrun", params->name, ssc_sr, count); /* re-start the PDC */ ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR, params->mask->pdc_disable); prtd->period_ptr += prtd->period_size; if (prtd->period_ptr >= prtd->dma_buffer_end) prtd->period_ptr = prtd->dma_buffer; ssc_writex(params->ssc->regs, params->pdc->xpr, prtd->period_ptr); ssc_writex(params->ssc->regs, params->pdc->xcr, prtd->period_size / params->pdc_xfer_size); ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR, params->mask->pdc_enable); } if (ssc_sr & params->mask->ssc_endx) { /* Load the PDC next pointer and counter registers */ prtd->period_ptr += prtd->period_size; if (prtd->period_ptr >= prtd->dma_buffer_end) prtd->period_ptr = prtd->dma_buffer; ssc_writex(params->ssc->regs, params->pdc->xnpr, prtd->period_ptr); ssc_writex(params->ssc->regs, params->pdc->xncr, prtd->period_size / params->pdc_xfer_size); } snd_pcm_period_elapsed(substream); }
static int at32_pcm_suspend(struct platform_device *pdev, struct snd_soc_dai *dai) { struct snd_pcm_runtime *runtime = dai->runtime; struct at32_runtime_data *prtd; struct at32_pcm_dma_params *params; if (runtime == NULL) return 0; prtd = runtime->private_data; params = prtd->params; /* Disable the PDC and save the PDC registers */ ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR, params->mask->pdc_disable); prtd->pdc_xpr_save = ssc_readx(params->ssc->regs, params->pdc->xpr); prtd->pdc_xcr_save = ssc_readx(params->ssc->regs, params->pdc->xcr); prtd->pdc_xnpr_save = ssc_readx(params->ssc->regs, params->pdc->xnpr); prtd->pdc_xncr_save = ssc_readx(params->ssc->regs, params->pdc->xncr); return 0; }
static int atmel_pcm_trigger(struct snd_pcm_substream *substream, int cmd) { struct snd_pcm_runtime *rtd = substream->runtime; struct atmel_runtime_data *prtd = rtd->private_data; struct atmel_pcm_dma_params *params = prtd->params; int ret = 0; pr_debug("atmel-pcm:buffer_size = %ld," "dma_area = %p, dma_bytes = %u\n", rtd->buffer_size, rtd->dma_area, rtd->dma_bytes); switch (cmd) { case SNDRV_PCM_TRIGGER_START: prtd->period_ptr = prtd->dma_buffer; ssc_writex(params->ssc->regs, params->pdc->xpr, prtd->period_ptr); ssc_writex(params->ssc->regs, params->pdc->xcr, prtd->period_size / params->pdc_xfer_size); prtd->period_ptr += prtd->period_size; ssc_writex(params->ssc->regs, params->pdc->xnpr, prtd->period_ptr); ssc_writex(params->ssc->regs, params->pdc->xncr, prtd->period_size / params->pdc_xfer_size); pr_debug("atmel-pcm: trigger: " "period_ptr=%lx, xpr=%u, " "xcr=%u, xnpr=%u, xncr=%u\n", (unsigned long)prtd->period_ptr, ssc_readx(params->ssc->regs, params->pdc->xpr), ssc_readx(params->ssc->regs, params->pdc->xcr), ssc_readx(params->ssc->regs, params->pdc->xnpr), ssc_readx(params->ssc->regs, params->pdc->xncr)); ssc_writex(params->ssc->regs, SSC_IER, params->mask->ssc_endx | params->mask->ssc_endbuf); ssc_writex(params->ssc->regs, SSC_PDC_PTCR, params->mask->pdc_enable); pr_debug("sr=%u imr=%u\n", ssc_readx(params->ssc->regs, SSC_SR), ssc_readx(params->ssc->regs, SSC_IER)); break; /* SNDRV_PCM_TRIGGER_START */ case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR, params->mask->pdc_disable); break; case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR, params->mask->pdc_enable); break; default: ret = -EINVAL; } return ret; }