static int mxs_pcm_close(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; struct mxs_runtime_data *prtd = runtime->private_data; int desc_num = mxs_pcm_hardware.periods_max; int desc; int timeo = 20; static LIST_HEAD(list); mxs_dma_disable(prtd->dma_ch); /* Wait until the DMA chain is finished. */ while (mxs_dma_read_semaphore(prtd->dma_ch)) { if (!timeo--) break; pr_debug("The sema is not zero now\n"); msleep(10); } if (timeo <= 0) pr_warn("Is the DMA channel dead?\n"); /* Free DMA irq */ free_irq(prtd->params->irq, substream); mxs_dma_get_cooked(prtd->dma_ch, &list); /* Free DMA channel*/ for (desc = 0; desc < desc_num; desc++) mxs_dma_free_desc(prtd->dma_desc_array[desc]); mxs_dma_release(prtd->dma_ch, mxs_pcm_dev); /* Free private runtime data */ kfree(prtd); return 0; }
/* * Enable a DMA channel. * * If the given channel has any DMA descriptors on its active list, this * function causes the DMA hardware to begin processing them. * * This function marks the DMA channel as "busy," whether or not there are any * descriptors to process. */ static int mxs_dma_enable(int channel) { struct apbh_dma *apbh = apbh_dma; unsigned int sem; struct mxs_dma_chan *pchan; struct mxs_dma_desc *pdesc; int channel_bit, ret; ret = mxs_dma_validate_chan(channel); if (ret) return ret; pchan = mxs_dma_channels + channel; if (pchan->pending_num == 0) { pchan->flags |= MXS_DMA_FLAGS_BUSY; return 0; } pdesc = list_first_entry(&pchan->active, struct mxs_dma_desc, node); if (pdesc == NULL) return -EFAULT; if (pchan->flags & MXS_DMA_FLAGS_BUSY) { if (!(pdesc->cmd.data & MXS_DMA_DESC_CHAIN)) return 0; sem = mxs_dma_read_semaphore(channel); if (sem == 0) return 0; if (sem == 1) { pdesc = list_entry(pdesc->node.next, struct mxs_dma_desc, node); if (apbh_dma_is_imx23(apbh)) writel(mxs_dma_cmd_address(pdesc), apbh->regs + HW_APBHX_CHn_NXTCMDAR_MX23(channel)); else writel(mxs_dma_cmd_address(pdesc), apbh->regs + HW_APBHX_CHn_NXTCMDAR_MX28(channel)); } if (apbh_dma_is_imx23(apbh)) writel(pchan->pending_num, apbh->regs + HW_APBHX_CHn_SEMA_MX23(channel)); else writel(pchan->pending_num, apbh->regs + HW_APBHX_CHn_SEMA_MX28(channel)); pchan->active_num += pchan->pending_num; pchan->pending_num = 0; } else {
/* * Enable a DMA channel. * * If the given channel has any DMA descriptors on its active list, this * function causes the DMA hardware to begin processing them. * * This function marks the DMA channel as "busy," whether or not there are any * descriptors to process. */ static int mxs_dma_enable(int channel) { struct mxs_apbh_regs *apbh_regs = (struct mxs_apbh_regs *)MXS_APBH_BASE; unsigned int sem; struct mxs_dma_chan *pchan; struct mxs_dma_desc *pdesc; int ret; ret = mxs_dma_validate_chan(channel); if (ret) return ret; pchan = mxs_dma_channels + channel; if (pchan->pending_num == 0) { pchan->flags |= MXS_DMA_FLAGS_BUSY; return 0; } pdesc = list_first_entry(&pchan->active, struct mxs_dma_desc, node); if (pdesc == NULL) return -EFAULT; if (pchan->flags & MXS_DMA_FLAGS_BUSY) { if (!(pdesc->cmd.data & MXS_DMA_DESC_CHAIN)) return 0; sem = mxs_dma_read_semaphore(channel); if (sem == 0) return 0; if (sem == 1) { pdesc = list_entry(pdesc->node.next, struct mxs_dma_desc, node); writel(mxs_dma_cmd_address(pdesc), &apbh_regs->ch[channel].hw_apbh_ch_nxtcmdar); } writel(pchan->pending_num, &apbh_regs->ch[channel].hw_apbh_ch_sema); pchan->active_num += pchan->pending_num; pchan->pending_num = 0; } else {