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; }
/* * Stop circular DMA descriptor list * We should not stop DMA in a middle of current transaction once we receive * stop request from ALSA core. This function finds the next DMA descriptor * and set it up to decrement DMA channel semaphore. So the current transaction * is the last data transfer. */ static void mxs_pcm_stop(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; struct mxs_runtime_data *prtd = runtime->private_data; struct mxs_dma_info dma_info; int desc; int periods_num = prtd->dma_totsize / prtd->dma_period; /* Freez DMA channel for a moment */ mxs_dma_freeze(prtd->dma_ch); mxs_dma_get_info(prtd->dma_ch, &dma_info); desc = (dma_info.buf_addr - runtime->dma_addr) / prtd->dma_period; if (desc >= periods_num) desc = 0; else if (desc < 0) desc = 0; /* Set up the next descriptor to decrement DMA channel sempahore */ prtd->dma_desc_array[(desc + 1)%periods_num]->cmd.cmd.bits.bytes = 0; prtd->dma_desc_array[(desc + 1)%periods_num]->cmd.cmd.bits.pio_words = \ 0; prtd->dma_desc_array[(desc + 1)%periods_num]->cmd.cmd.bits.dec_sem = 1; prtd->dma_desc_array[(desc + 1)%periods_num]->cmd.cmd.bits.irq = 0; prtd->dma_desc_array[(desc + 1)%periods_num]->cmd.cmd.bits.command = \ NO_DMA_XFER; mxs_dma_unfreeze(prtd->dma_ch); mxs_dma_disable(prtd->dma_ch); }
static int __devexit mxs_hsadc_remove(struct platform_device *pdev) { struct mxs_hsadc_data *pd = platform_get_drvdata(pdev); u32 ctrl = 0; hsadc_cleanup_cdev(pd); // clean cdev interface if (pd->buf_phy) dma_free_coherent(NULL, DMA_BUF_SIZE, pd->buf, pd->buf_phy); if (pd->desc) mxs_dma_free_desc(pd->desc); mxs_dma_enable_irq(pd->dma_ch, 0); ctrl = readl(pd->hsadc_base + HSADC_CTRL1); ctrl &= ~(1<<31 | 1<<30 | 1<<29); // disable irq writel(ctrl, pd->hsadc_base + HSADC_CTRL1); mxs_dma_disable(pd->dma_ch); mxs_dma_release(pd->dma_ch, pd->dev); free_irq(pd->dev_irq, pd); free_irq(pd->dma_irq, pd); clk_disable(pd->hsadc_clk); clk_disable(pd->pwm_clk); clk_disable(pd->ref_hsadc_clk); clk_put(pd->hsadc_clk); clk_put(pd->pwm_clk); clk_put(pd->ref_hsadc_clk); iounmap(pd->pwm_base); iounmap(pd->hsadc_base); platform_set_drvdata(pdev, NULL); kfree(pd); #if HSADC_DEBUG printk(KERN_INFO "%s> driver removed.\n", HSADC_DEVICE_NAME); #endif return 0; }
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; static LIST_HEAD(list); mxs_dma_disable(prtd->dma_ch); /* 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; }
static int __devinit mxs_hsadc_probe(struct platform_device *pdev) { struct mxs_hsadc_data *pd; struct resource *res; int rlevel = 0; pd = kzalloc(sizeof(*pd), GFP_KERNEL); if (pd) rlevel++; else goto quit; pd->dev = &pdev->dev; platform_set_drvdata(pdev, pd); pd->pdev = pdev; rlevel++; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (res == NULL) goto quit; pd->hsadc_base = ioremap(res->start, res->end - res->start); if (pd->hsadc_base) rlevel++; else goto quit; res = platform_get_resource(pdev, IORESOURCE_MEM, 1); if (res == NULL) goto quit; pd->pwm_base = ioremap(res->start, res->end - res->start); if (pd->pwm_base) rlevel++; else goto quit; res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); if (res) pd->dev_irq = res->start; else goto quit; res = platform_get_resource(pdev, IORESOURCE_IRQ, 1); if (res) pd->dma_irq = res->start; else goto quit; res = platform_get_resource(pdev, IORESOURCE_DMA, 0); if (res) pd->dma_ch = res->start; else goto quit; pd->ref_hsadc_clk = clk_get(NULL, "ref_hsadc"); if (pd->ref_hsadc_clk) rlevel++; else goto quit; pd->hsadc_clk = clk_get(NULL, "hsadc"); if (pd->hsadc_clk) rlevel++; else goto quit; pd->pwm_clk = clk_get(NULL, "pwm"); if (pd->pwm_clk) rlevel++; else goto quit; clk_enable(pd->ref_hsadc_clk); clk_enable(pd->hsadc_clk); clk_enable(pd->pwm_clk); rlevel++; clk_set_rate(pd->ref_hsadc_clk, REF_HSADC_FREQ); clk_set_rate(pd->hsadc_clk, HSADC_FREQ); if (request_irq(pd->dma_irq, hsadc_dma_isr, 0, "hsadc dma", pd)) goto quit; else rlevel++; if (request_irq(pd->dev_irq, hsadc_isr, 0, "hsadc irq", pd)) goto quit; else rlevel++; if (mxs_dma_request(pd->dma_ch, pd->dev, "hsadc")) goto quit; else rlevel++; mxs_dma_disable(pd->dma_ch); pd->desc = mxs_dma_alloc_desc(); if (pd->desc==NULL) goto quit; memset(&pd->desc->cmd, 0, sizeof(pd->desc->cmd)); rlevel++; pd->buf = dma_alloc_coherent(NULL, DMA_BUF_SIZE, &pd->buf_phy, GFP_KERNEL); if(!pd->buf) goto quit; rlevel++; if(hsadc_init_cdev(pd)) goto quit; #if HSADC_DEBUG printk(KERN_INFO "%s> probe successed.\n", HSADC_DEVICE_NAME); #endif return 0; quit: pr_err("%s quit at rlevel %d\n", __func__, rlevel); switch (rlevel) { case 14: hsadc_cleanup_cdev(pd); case 13: if (pd->buf_phy) dma_free_coherent(NULL, DMA_BUF_SIZE, pd->buf, pd->buf_phy); case 12: if (pd->desc) mxs_dma_free_desc(pd->desc); case 11: mxs_dma_release(pd->dma_ch, pd->dev); case 10: free_irq(pd->dev_irq, pd); case 9: free_irq(pd->dma_irq, pd); case 8: clk_disable(pd->pwm_clk); clk_disable(pd->hsadc_clk); clk_disable(pd->ref_hsadc_clk); case 7: clk_put(pd->pwm_clk); case 6: clk_put(pd->hsadc_clk); case 5: clk_put(pd->ref_hsadc_clk); case 4: iounmap(pd->pwm_base); case 3: iounmap(pd->hsadc_base); case 2: platform_set_drvdata(pdev, NULL); case 1: kfree(pd); case 0: default: return -ENODEV; } }