linux/sound/soc/atmel/atmel-pcm-dma.c
<<
>>
Prefs
   1/*
   2 * atmel-pcm-dma.c  --  ALSA PCM DMA support for the Atmel SoC.
   3 *
   4 *  Copyright (C) 2012 Atmel
   5 *
   6 * Author: Bo Shen <voice.shen@atmel.com>
   7 *
   8 * Based on atmel-pcm by:
   9 * Sedji Gaouaou <sedji.gaouaou@atmel.com>
  10 * Copyright 2008 Atmel
  11 *
  12 * This program is free software; you can redistribute it and/or modify
  13 * it under the terms of the GNU General Public License as published by
  14 * the Free Software Foundation; either version 2 of the License, or
  15 * (at your option) any later version.
  16 *
  17 * This program is distributed in the hope that it will be useful,
  18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  20 * GNU General Public License for more details.
  21 *
  22 * You should have received a copy of the GNU General Public License
  23 * along with this program; if not, write to the Free Software
  24 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  25 */
  26
  27#include <linux/module.h>
  28#include <linux/init.h>
  29#include <linux/platform_device.h>
  30#include <linux/slab.h>
  31#include <linux/dma-mapping.h>
  32#include <linux/dmaengine.h>
  33#include <linux/atmel-ssc.h>
  34#include <linux/platform_data/dma-atmel.h>
  35
  36#include <sound/core.h>
  37#include <sound/pcm.h>
  38#include <sound/pcm_params.h>
  39#include <sound/soc.h>
  40#include <sound/dmaengine_pcm.h>
  41
  42#include "atmel-pcm.h"
  43
  44/*--------------------------------------------------------------------------*\
  45 * Hardware definition
  46\*--------------------------------------------------------------------------*/
  47static const struct snd_pcm_hardware atmel_pcm_dma_hardware = {
  48        .info                   = SNDRV_PCM_INFO_MMAP |
  49                                  SNDRV_PCM_INFO_MMAP_VALID |
  50                                  SNDRV_PCM_INFO_INTERLEAVED |
  51                                  SNDRV_PCM_INFO_RESUME |
  52                                  SNDRV_PCM_INFO_PAUSE,
  53        .period_bytes_min       = 256,          /* lighting DMA overhead */
  54        .period_bytes_max       = 2 * 0xffff,   /* if 2 bytes format */
  55        .periods_min            = 8,
  56        .periods_max            = 1024,         /* no limit */
  57        .buffer_bytes_max       = 512 * 1024,
  58};
  59
  60/**
  61 * atmel_pcm_dma_irq: SSC interrupt handler for DMAENGINE enabled SSC
  62 *
  63 * We use DMAENGINE to send/receive data to/from SSC so this ISR is only to
  64 * check if any overrun occured.
  65 */
  66static void atmel_pcm_dma_irq(u32 ssc_sr,
  67        struct snd_pcm_substream *substream)
  68{
  69        struct snd_soc_pcm_runtime *rtd = substream->private_data;
  70        struct atmel_pcm_dma_params *prtd;
  71
  72        prtd = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
  73
  74        if (ssc_sr & prtd->mask->ssc_error) {
  75                if (snd_pcm_running(substream))
  76                        pr_warn("atmel-pcm: buffer %s on %s (SSC_SR=%#x)\n",
  77                                substream->stream == SNDRV_PCM_STREAM_PLAYBACK
  78                                ? "underrun" : "overrun", prtd->name,
  79                                ssc_sr);
  80
  81                /* stop RX and capture: will be enabled again at restart */
  82                ssc_writex(prtd->ssc->regs, SSC_CR, prtd->mask->ssc_disable);
  83                snd_pcm_stop_xrun(substream);
  84
  85                /* now drain RHR and read status to remove xrun condition */
  86                ssc_readx(prtd->ssc->regs, SSC_RHR);
  87                ssc_readx(prtd->ssc->regs, SSC_SR);
  88        }
  89}
  90
  91static int atmel_pcm_configure_dma(struct snd_pcm_substream *substream,
  92        struct snd_pcm_hw_params *params, struct dma_slave_config *slave_config)
  93{
  94        struct snd_soc_pcm_runtime *rtd = substream->private_data;
  95        struct atmel_pcm_dma_params *prtd;
  96        struct ssc_device *ssc;
  97        int ret;
  98
  99        prtd = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
 100        ssc = prtd->ssc;
 101
 102        ret = snd_hwparams_to_dma_slave_config(substream, params, slave_config);
 103        if (ret) {
 104                pr_err("atmel-pcm: hwparams to dma slave configure failed\n");
 105                return ret;
 106        }
 107
 108        slave_config->dst_addr = ssc->phybase + SSC_THR;
 109        slave_config->dst_maxburst = 1;
 110
 111        slave_config->src_addr = ssc->phybase + SSC_RHR;
 112        slave_config->src_maxburst = 1;
 113
 114        prtd->dma_intr_handler = atmel_pcm_dma_irq;
 115
 116        return 0;
 117}
 118
 119static const struct snd_dmaengine_pcm_config atmel_dmaengine_pcm_config = {
 120        .prepare_slave_config = atmel_pcm_configure_dma,
 121        .pcm_hardware = &atmel_pcm_dma_hardware,
 122        .prealloc_buffer_size = 64 * 1024,
 123};
 124
 125int atmel_pcm_dma_platform_register(struct device *dev)
 126{
 127        return snd_dmaengine_pcm_register(dev, &atmel_dmaengine_pcm_config, 0);
 128}
 129EXPORT_SYMBOL(atmel_pcm_dma_platform_register);
 130
 131void atmel_pcm_dma_platform_unregister(struct device *dev)
 132{
 133        snd_dmaengine_pcm_unregister(dev);
 134}
 135EXPORT_SYMBOL(atmel_pcm_dma_platform_unregister);
 136
 137MODULE_AUTHOR("Bo Shen <voice.shen@atmel.com>");
 138MODULE_DESCRIPTION("Atmel DMA based PCM module");
 139MODULE_LICENSE("GPL");
 140