static irqreturn_t mxs_pcm_dma_irq(int irq, void *dev_id) { struct snd_pcm_substream *substream = dev_id; struct snd_pcm_runtime *runtime = substream->runtime; struct mxs_runtime_data *prtd = substream->runtime->private_data; struct mxs_dma_info dma_info; void *pdma; unsigned long prev_appl_offset, appl_count, cont, appl_ptr_bytes; mxs_dma_get_info(prtd->dma_ch, &dma_info); if (dma_info.status) { printk(KERN_WARNING "%s: DMA audio channel %d (%s) error\n", __func__, prtd->params->dma_ch, prtd->params->name); mxs_dma_ack_irq(prtd->dma_ch); } else { if ((prtd->params->dma_ch == MXS_DMA_CHANNEL_AHB_APBX_SPDIF) && (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) && (runtime->access == SNDRV_PCM_ACCESS_MMAP_INTERLEAVED) && ((prtd->format == SNDRV_PCM_FORMAT_S24_LE) || (prtd->format == SNDRV_PCM_FORMAT_S20_3LE))) { appl_ptr_bytes = frames_to_bytes(runtime, runtime->control->appl_ptr); appl_count = appl_ptr_bytes - prtd->appl_ptr_bytes; prev_appl_offset = prtd->appl_ptr_bytes % prtd->dma_totsize; cont = prtd->dma_totsize - prev_appl_offset; if (appl_count > cont) { pdma = runtime->dma_area + prev_appl_offset; memmove(pdma + 1, pdma, cont - 1); pdma = runtime->dma_area; memmove(pdma + 1, pdma, appl_count - cont - 1); } else { pdma = runtime->dma_area + prev_appl_offset; memmove(pdma + 1, pdma, appl_count - 1); } prtd->appl_ptr_bytes = appl_ptr_bytes; } mxs_dma_ack_irq(prtd->dma_ch); snd_pcm_period_elapsed(substream); } return IRQ_HANDLED; }
static int mxs_pcm_dma_request(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; struct snd_soc_pcm_runtime *rtd = substream->private_data; struct mxs_runtime_data *prtd = runtime->private_data; struct mxs_pcm_dma_params *dma_data = snd_soc_dai_get_dma_data(rtd->dai->cpu_dai, substream); int desc_num = mxs_pcm_hardware.periods_max; int desc; int ret; if (!dma_data) return -ENODEV; prtd->params = dma_data; prtd->dma_ch = dma_data->dma_ch; ret = mxs_dma_request(prtd->dma_ch, mxs_pcm_dev, prtd->params->name); if (ret) { printk(KERN_ERR "%s: Failed to request DMA channel (%d:%d)\n", __func__, dma_data->dma_bus, dma_data->dma_ch); return ret; } /* Allocate memory for data and pio DMA descriptors */ for (desc = 0; desc < desc_num; desc++) { prtd->dma_desc_array[desc] = mxs_dma_alloc_desc(); if (prtd->dma_desc_array[desc] == NULL) { printk(KERN_ERR"%s Unable to allocate DMA command %d\n", __func__, desc); goto err; } } ret = request_irq(prtd->params->irq, mxs_pcm_dma_irq, 0, "MXS PCM DMA", substream); if (ret) { printk(KERN_ERR "%s: Unable to request DMA irq %d\n", __func__, prtd->params->irq); goto err; } /* Enable completion interrupt */ mxs_dma_ack_irq(prtd->dma_ch); mxs_dma_enable_irq(prtd->dma_ch, 1); return 0; err: while (--desc >= 0) mxs_dma_free_desc(prtd->dma_desc_array[desc]); mxs_dma_release(prtd->dma_ch, mxs_pcm_dev); return ret; }
static irqreturn_t hsadc_dma_isr(int irq, void * p) { struct mxs_hsadc_data *pd = (struct mxs_hsadc_data *)p; mxs_dma_ack_irq(pd->dma_ch); mxs_dma_cooked(pd->dma_ch, NULL); //dev_dbg(pd->dev, "dma\n"); wake_up_interruptible(&pd->r_wait); return IRQ_HANDLED; }
int submit_request(struct mxs_hsadc_data* pdx, unsigned long count) { unsigned long sample_count; memset(pdx->buf, 0, count); pdx->desc->cmd.cmd.bits.bytes = count; pdx->desc->cmd.cmd.bits.pio_words = 0; pdx->desc->cmd.cmd.bits.wait4end = 1; pdx->desc->cmd.cmd.bits.dec_sem = 1; pdx->desc->cmd.cmd.bits.irq = 1; pdx->desc->cmd.cmd.bits.command = DMA_WRITE; pdx->desc->cmd.address = pdx->buf_phy; if(mxs_dma_desc_append(pdx->dma_ch, pdx->desc)) { return -EINVAL; } // // byte(s) to sample count // sample_count = adc_sample_percision == 8 ? count : (count >> 1); // 10-bit & 12-bit mode a sample word is two bytes size writel(sample_count, pdx->hsadc_base + HSADC_SEQUENCE_SAMPLES_NUM); writel(1, pdx->hsadc_base + HSADC_SEQUENCE_NUM); writel(1<<31 | 1<<30 | 1<<29, pdx->hsadc_base + HSADC_CTRL1); // enable irq mxs_dma_reset(pdx->dma_ch); mxs_dma_ack_irq(pdx->dma_ch); mxs_dma_enable_irq(pdx->dma_ch, 1); if(mxs_dma_enable(pdx->dma_ch)) { return -EINVAL; } writel(RUN, pdx->hsadc_base + HSADC_CTRL0_SET); writel(SOFT_TRIGGER, pdx->hsadc_base + HSADC_CTRL0_SET); return 0; }
static int mxs_pcm_trigger(struct snd_pcm_substream *substream, int cmd) { struct snd_pcm_runtime *runtime = substream->runtime; struct mxs_runtime_data *prtd = substream->runtime->private_data; int ret = 0; switch (cmd) { case SNDRV_PCM_TRIGGER_START: if ((prtd->params->dma_ch == MXS_DMA_CHANNEL_AHB_APBX_SPDIF) && (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) && (runtime->access == SNDRV_PCM_ACCESS_MMAP_INTERLEAVED) && ((prtd->format == SNDRV_PCM_FORMAT_S24_LE) || (prtd->format == SNDRV_PCM_FORMAT_S20_3LE))) { prtd->appl_ptr_bytes = frames_to_bytes(runtime, runtime->control->appl_ptr); memmove(runtime->dma_area + 1, runtime->dma_area, prtd->appl_ptr_bytes - 1); } mxs_dma_enable(prtd->dma_ch); break; case SNDRV_PCM_TRIGGER_STOP: mxs_pcm_stop(substream); break; case SNDRV_PCM_TRIGGER_RESUME: if (mxs_pm_get_target() == PM_SUSPEND_MEM) { mxs_dma_reset(prtd->dma_ch); mxs_dma_ack_irq(prtd->dma_ch); mxs_dma_enable_irq(prtd->dma_ch, 1); mxs_pcm_prepare(substream); if ((prtd->params->dma_ch == MXS_DMA_CHANNEL_AHB_APBX_SPDIF) && (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) && (runtime->access == SNDRV_PCM_ACCESS_MMAP_INTERLEAVED) && ((prtd->format == SNDRV_PCM_FORMAT_S24_LE) || (prtd->format == SNDRV_PCM_FORMAT_S20_3LE))) { prtd->appl_ptr_bytes = frames_to_bytes(runtime, runtime->control->appl_ptr); memmove(runtime->dma_area + 1, runtime->dma_area, prtd->appl_ptr_bytes - 1); } mxs_dma_enable(prtd->dma_ch); } else mxs_dma_unfreeze(prtd->dma_ch); break; case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: mxs_dma_unfreeze(prtd->dma_ch); break; case SNDRV_PCM_TRIGGER_SUSPEND: if (mxs_pm_get_target() == PM_SUSPEND_MEM) { mxs_pcm_stop(substream); mdelay(30); } else { mxs_dma_freeze(prtd->dma_ch); mdelay(30); } break; case SNDRV_PCM_TRIGGER_PAUSE_PUSH: mxs_dma_freeze(prtd->dma_ch); mdelay(30); break; default: ret = -EINVAL; break; } return ret; }