linux/sound/soc/spear/spear_pcm.c
<<
>>
Prefs
   1/*
   2 * ALSA PCM interface for ST SPEAr Processors
   3 *
   4 * sound/soc/spear/spear_pcm.c
   5 *
   6 * Copyright (C) 2012 ST Microelectronics
   7 * Rajeev Kumar<rajeev-dlh.kumar@st.com>
   8 *
   9 * This file is licensed under the terms of the GNU General Public
  10 * License version 2. This program is licensed "as is" without any
  11 * warranty of any kind, whether express or implied.
  12 */
  13
  14#include <linux/module.h>
  15#include <linux/dmaengine.h>
  16#include <linux/dma-mapping.h>
  17#include <linux/init.h>
  18#include <linux/platform_device.h>
  19#include <linux/scatterlist.h>
  20#include <linux/slab.h>
  21#include <sound/core.h>
  22#include <sound/dmaengine_pcm.h>
  23#include <sound/pcm.h>
  24#include <sound/pcm_params.h>
  25#include <sound/soc.h>
  26#include <sound/spear_dma.h>
  27
  28struct snd_pcm_hardware spear_pcm_hardware = {
  29        .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER |
  30                 SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
  31                 SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME),
  32        .buffer_bytes_max = 16 * 1024, /* max buffer size */
  33        .period_bytes_min = 2 * 1024, /* 1 msec data minimum period size */
  34        .period_bytes_max = 2 * 1024, /* maximum period size */
  35        .periods_min = 1, /* min # periods */
  36        .periods_max = 8, /* max # of periods */
  37        .fifo_size = 0, /* fifo size in bytes */
  38};
  39
  40static int spear_pcm_hw_params(struct snd_pcm_substream *substream,
  41                struct snd_pcm_hw_params *params)
  42{
  43        snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
  44
  45        return 0;
  46}
  47
  48static int spear_pcm_hw_free(struct snd_pcm_substream *substream)
  49{
  50        snd_pcm_set_runtime_buffer(substream, NULL);
  51
  52        return 0;
  53}
  54
  55static int spear_pcm_open(struct snd_pcm_substream *substream)
  56{
  57        struct snd_soc_pcm_runtime *rtd = substream->private_data;
  58
  59        struct spear_dma_data *dma_data = (struct spear_dma_data *)
  60                snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
  61        int ret;
  62
  63        ret = snd_soc_set_runtime_hwparams(substream, &spear_pcm_hardware);
  64        if (ret)
  65                return ret;
  66
  67        ret = snd_dmaengine_pcm_open(substream, dma_data->filter, dma_data);
  68        if (ret)
  69                return ret;
  70
  71        snd_dmaengine_pcm_set_data(substream, dma_data);
  72
  73        return 0;
  74}
  75
  76static int spear_pcm_close(struct snd_pcm_substream *substream)
  77{
  78
  79        snd_dmaengine_pcm_close(substream);
  80
  81        return 0;
  82}
  83
  84static int spear_pcm_mmap(struct snd_pcm_substream *substream,
  85                struct vm_area_struct *vma)
  86{
  87        struct snd_pcm_runtime *runtime = substream->runtime;
  88
  89        return dma_mmap_writecombine(substream->pcm->card->dev, vma,
  90                        runtime->dma_area, runtime->dma_addr,
  91                        runtime->dma_bytes);
  92}
  93
  94static struct snd_pcm_ops spear_pcm_ops = {
  95        .open           = spear_pcm_open,
  96        .close          = spear_pcm_close,
  97        .ioctl          = snd_pcm_lib_ioctl,
  98        .hw_params      = spear_pcm_hw_params,
  99        .hw_free        = spear_pcm_hw_free,
 100        .trigger        = snd_dmaengine_pcm_trigger,
 101        .pointer        = snd_dmaengine_pcm_pointer,
 102        .mmap           = spear_pcm_mmap,
 103};
 104
 105static int
 106spear_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream,
 107                size_t size)
 108{
 109        struct snd_pcm_substream *substream = pcm->streams[stream].substream;
 110        struct snd_dma_buffer *buf = &substream->dma_buffer;
 111
 112        buf->dev.type = SNDRV_DMA_TYPE_DEV;
 113        buf->dev.dev = pcm->card->dev;
 114        buf->private_data = NULL;
 115
 116        buf->area = dma_alloc_writecombine(pcm->card->dev, size,
 117                        &buf->addr, GFP_KERNEL);
 118        if (!buf->area)
 119                return -ENOMEM;
 120
 121        dev_info(buf->dev.dev,
 122                        " preallocate_dma_buffer: area=%p, addr=%p, size=%d\n",
 123                        (void *)buf->area, (void *)buf->addr, size);
 124
 125        buf->bytes = size;
 126        return 0;
 127}
 128
 129static void spear_pcm_free(struct snd_pcm *pcm)
 130{
 131        struct snd_pcm_substream *substream;
 132        struct snd_dma_buffer *buf;
 133        int stream;
 134
 135        for (stream = 0; stream < 2; stream++) {
 136                substream = pcm->streams[stream].substream;
 137                if (!substream)
 138                        continue;
 139
 140                buf = &substream->dma_buffer;
 141                if (!buf || !buf->area)
 142                        continue;
 143
 144                dma_free_writecombine(pcm->card->dev, buf->bytes,
 145                                buf->area, buf->addr);
 146                buf->area = NULL;
 147        }
 148}
 149
 150static u64 spear_pcm_dmamask = DMA_BIT_MASK(32);
 151
 152static int spear_pcm_new(struct snd_card *card,
 153                struct snd_soc_dai *dai, struct snd_pcm *pcm)
 154{
 155        int ret;
 156
 157        if (!card->dev->dma_mask)
 158                card->dev->dma_mask = &spear_pcm_dmamask;
 159        if (!card->dev->coherent_dma_mask)
 160                card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
 161
 162        if (dai->driver->playback.channels_min) {
 163                ret = spear_pcm_preallocate_dma_buffer(pcm,
 164                                SNDRV_PCM_STREAM_PLAYBACK,
 165                                spear_pcm_hardware.buffer_bytes_max);
 166                if (ret)
 167                        return ret;
 168        }
 169
 170        if (dai->driver->capture.channels_min) {
 171                ret = spear_pcm_preallocate_dma_buffer(pcm,
 172                                SNDRV_PCM_STREAM_CAPTURE,
 173                                spear_pcm_hardware.buffer_bytes_max);
 174                if (ret)
 175                        return ret;
 176        }
 177
 178        return 0;
 179}
 180
 181struct snd_soc_platform_driver spear_soc_platform = {
 182        .ops            =       &spear_pcm_ops,
 183        .pcm_new        =       spear_pcm_new,
 184        .pcm_free       =       spear_pcm_free,
 185};
 186
 187static int __devinit spear_soc_platform_probe(struct platform_device *pdev)
 188{
 189        return snd_soc_register_platform(&pdev->dev, &spear_soc_platform);
 190}
 191
 192static int __devexit spear_soc_platform_remove(struct platform_device *pdev)
 193{
 194        snd_soc_unregister_platform(&pdev->dev);
 195
 196        return 0;
 197}
 198
 199static struct platform_driver spear_pcm_driver = {
 200        .driver = {
 201                .name = "spear-pcm-audio",
 202                .owner = THIS_MODULE,
 203        },
 204
 205        .probe = spear_soc_platform_probe,
 206        .remove = __devexit_p(spear_soc_platform_remove),
 207};
 208
 209module_platform_driver(spear_pcm_driver);
 210
 211MODULE_AUTHOR("Rajeev Kumar <rajeev-dlh.kumar@st.com>");
 212MODULE_DESCRIPTION("SPEAr PCM DMA module");
 213MODULE_LICENSE("GPL");
 214MODULE_ALIAS("platform:spear-pcm-audio");
 215