linux/sound/arm/pxa2xx-pcm-lib.c
<<
>>
Prefs
   1/*
   2 * This program is free software; you can redistribute it and/or modify
   3 * it under the terms of the GNU General Public License version 2 as
   4 * published by the Free Software Foundation.
   5 */
   6
   7#include <linux/slab.h>
   8#include <linux/module.h>
   9#include <linux/dma-mapping.h>
  10#include <linux/dmaengine.h>
  11#include <linux/dma/pxa-dma.h>
  12
  13#include <sound/core.h>
  14#include <sound/pcm.h>
  15#include <sound/pcm_params.h>
  16#include <sound/pxa2xx-lib.h>
  17#include <sound/dmaengine_pcm.h>
  18
  19#include "pxa2xx-pcm.h"
  20
  21static const struct snd_pcm_hardware pxa2xx_pcm_hardware = {
  22        .info                   = SNDRV_PCM_INFO_MMAP |
  23                                  SNDRV_PCM_INFO_MMAP_VALID |
  24                                  SNDRV_PCM_INFO_INTERLEAVED |
  25                                  SNDRV_PCM_INFO_PAUSE |
  26                                  SNDRV_PCM_INFO_RESUME,
  27        .formats                = SNDRV_PCM_FMTBIT_S16_LE |
  28                                        SNDRV_PCM_FMTBIT_S24_LE |
  29                                        SNDRV_PCM_FMTBIT_S32_LE,
  30        .period_bytes_min       = 32,
  31        .period_bytes_max       = 8192 - 32,
  32        .periods_min            = 1,
  33        .periods_max            = 256,
  34        .buffer_bytes_max       = 128 * 1024,
  35        .fifo_size              = 32,
  36};
  37
  38int __pxa2xx_pcm_hw_params(struct snd_pcm_substream *substream,
  39                                struct snd_pcm_hw_params *params)
  40{
  41        struct dma_chan *chan = snd_dmaengine_pcm_get_chan(substream);
  42        struct snd_soc_pcm_runtime *rtd = substream->private_data;
  43        struct snd_dmaengine_dai_dma_data *dma_params;
  44        struct dma_slave_config config;
  45        int ret;
  46
  47        dma_params = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
  48        if (!dma_params)
  49                return 0;
  50
  51        ret = snd_hwparams_to_dma_slave_config(substream, params, &config);
  52        if (ret)
  53                return ret;
  54
  55        snd_dmaengine_pcm_set_config_from_dai_data(substream,
  56                        snd_soc_dai_get_dma_data(rtd->cpu_dai, substream),
  57                        &config);
  58
  59        ret = dmaengine_slave_config(chan, &config);
  60        if (ret)
  61                return ret;
  62
  63        snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
  64
  65        return 0;
  66}
  67EXPORT_SYMBOL(__pxa2xx_pcm_hw_params);
  68
  69int __pxa2xx_pcm_hw_free(struct snd_pcm_substream *substream)
  70{
  71        snd_pcm_set_runtime_buffer(substream, NULL);
  72        return 0;
  73}
  74EXPORT_SYMBOL(__pxa2xx_pcm_hw_free);
  75
  76int pxa2xx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
  77{
  78        return snd_dmaengine_pcm_trigger(substream, cmd);
  79}
  80EXPORT_SYMBOL(pxa2xx_pcm_trigger);
  81
  82snd_pcm_uframes_t
  83pxa2xx_pcm_pointer(struct snd_pcm_substream *substream)
  84{
  85        return snd_dmaengine_pcm_pointer(substream);
  86}
  87EXPORT_SYMBOL(pxa2xx_pcm_pointer);
  88
  89int __pxa2xx_pcm_prepare(struct snd_pcm_substream *substream)
  90{
  91        return 0;
  92}
  93EXPORT_SYMBOL(__pxa2xx_pcm_prepare);
  94
  95int __pxa2xx_pcm_open(struct snd_pcm_substream *substream)
  96{
  97        struct snd_soc_pcm_runtime *rtd = substream->private_data;
  98        struct snd_pcm_runtime *runtime = substream->runtime;
  99        struct snd_dmaengine_dai_dma_data *dma_params;
 100        int ret;
 101
 102        runtime->hw = pxa2xx_pcm_hardware;
 103
 104        dma_params = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
 105        if (!dma_params)
 106                return 0;
 107
 108        /*
 109         * For mysterious reasons (and despite what the manual says)
 110         * playback samples are lost if the DMA count is not a multiple
 111         * of the DMA burst size.  Let's add a rule to enforce that.
 112         */
 113        ret = snd_pcm_hw_constraint_step(runtime, 0,
 114                SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 32);
 115        if (ret)
 116                return ret;
 117
 118        ret = snd_pcm_hw_constraint_step(runtime, 0,
 119                SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 32);
 120        if (ret)
 121                return ret;
 122
 123        ret = snd_pcm_hw_constraint_integer(runtime,
 124                                            SNDRV_PCM_HW_PARAM_PERIODS);
 125        if (ret < 0)
 126                return ret;
 127
 128        return snd_dmaengine_pcm_open_request_chan(substream,
 129                                        pxad_filter_fn,
 130                                        dma_params->filter_data);
 131}
 132EXPORT_SYMBOL(__pxa2xx_pcm_open);
 133
 134int __pxa2xx_pcm_close(struct snd_pcm_substream *substream)
 135{
 136        return snd_dmaengine_pcm_close_release_chan(substream);
 137}
 138EXPORT_SYMBOL(__pxa2xx_pcm_close);
 139
 140int pxa2xx_pcm_mmap(struct snd_pcm_substream *substream,
 141        struct vm_area_struct *vma)
 142{
 143        struct snd_pcm_runtime *runtime = substream->runtime;
 144        return dma_mmap_wc(substream->pcm->card->dev, vma, runtime->dma_area,
 145                           runtime->dma_addr, runtime->dma_bytes);
 146}
 147EXPORT_SYMBOL(pxa2xx_pcm_mmap);
 148
 149int pxa2xx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
 150{
 151        struct snd_pcm_substream *substream = pcm->streams[stream].substream;
 152        struct snd_dma_buffer *buf = &substream->dma_buffer;
 153        size_t size = pxa2xx_pcm_hardware.buffer_bytes_max;
 154        buf->dev.type = SNDRV_DMA_TYPE_DEV;
 155        buf->dev.dev = pcm->card->dev;
 156        buf->private_data = NULL;
 157        buf->area = dma_alloc_wc(pcm->card->dev, size, &buf->addr, GFP_KERNEL);
 158        if (!buf->area)
 159                return -ENOMEM;
 160        buf->bytes = size;
 161        return 0;
 162}
 163EXPORT_SYMBOL(pxa2xx_pcm_preallocate_dma_buffer);
 164
 165void pxa2xx_pcm_free_dma_buffers(struct snd_pcm *pcm)
 166{
 167        struct snd_pcm_substream *substream;
 168        struct snd_dma_buffer *buf;
 169        int stream;
 170
 171        for (stream = 0; stream < 2; stream++) {
 172                substream = pcm->streams[stream].substream;
 173                if (!substream)
 174                        continue;
 175                buf = &substream->dma_buffer;
 176                if (!buf->area)
 177                        continue;
 178                dma_free_wc(pcm->card->dev, buf->bytes, buf->area, buf->addr);
 179                buf->area = NULL;
 180        }
 181}
 182EXPORT_SYMBOL(pxa2xx_pcm_free_dma_buffers);
 183
 184MODULE_AUTHOR("Nicolas Pitre");
 185MODULE_DESCRIPTION("Intel PXA2xx sound library");
 186MODULE_LICENSE("GPL");
 187