linux/sound/soc/cirrus/ep93xx-pcm.c
<<
>>
Prefs
   1/*
   2 * linux/sound/arm/ep93xx-pcm.c - EP93xx ALSA PCM interface
   3 *
   4 * Copyright (C) 2006 Lennert Buytenhek <buytenh@wantstofly.org>
   5 * Copyright (C) 2006 Applied Data Systems
   6 *
   7 * Rewritten for the SoC audio subsystem (Based on PXA2xx code):
   8 *   Copyright (c) 2008 Ryan Mallon
   9 *
  10 * This program is free software; you can redistribute it and/or modify
  11 * it under the terms of the GNU General Public License version 2 as
  12 * published by the Free Software Foundation.
  13 */
  14
  15#include <linux/module.h>
  16#include <linux/init.h>
  17#include <linux/device.h>
  18#include <linux/slab.h>
  19#include <linux/dmaengine.h>
  20#include <linux/dma-mapping.h>
  21
  22#include <sound/core.h>
  23#include <sound/pcm.h>
  24#include <sound/pcm_params.h>
  25#include <sound/soc.h>
  26#include <sound/dmaengine_pcm.h>
  27
  28#include <linux/platform_data/dma-ep93xx.h>
  29#include <mach/hardware.h>
  30#include <mach/ep93xx-regs.h>
  31
  32#include "ep93xx-pcm.h"
  33
  34static const struct snd_pcm_hardware ep93xx_pcm_hardware = {
  35        .info                   = (SNDRV_PCM_INFO_MMAP          |
  36                                   SNDRV_PCM_INFO_MMAP_VALID    |
  37                                   SNDRV_PCM_INFO_INTERLEAVED   |
  38                                   SNDRV_PCM_INFO_BLOCK_TRANSFER),
  39                                   
  40        .rates                  = SNDRV_PCM_RATE_8000_192000,
  41        .rate_min               = SNDRV_PCM_RATE_8000,
  42        .rate_max               = SNDRV_PCM_RATE_192000,
  43        
  44        .formats                = (SNDRV_PCM_FMTBIT_S16_LE |
  45                                   SNDRV_PCM_FMTBIT_S24_LE |
  46                                   SNDRV_PCM_FMTBIT_S32_LE),
  47        
  48        .buffer_bytes_max       = 131072,
  49        .period_bytes_min       = 32,
  50        .period_bytes_max       = 32768,
  51        .periods_min            = 1,
  52        .periods_max            = 32,
  53        .fifo_size              = 32,
  54};
  55
  56static bool ep93xx_pcm_dma_filter(struct dma_chan *chan, void *filter_param)
  57{
  58        struct ep93xx_dma_data *data = filter_param;
  59
  60        if (data->direction == ep93xx_dma_chan_direction(chan)) {
  61                chan->private = data;
  62                return true;
  63        }
  64
  65        return false;
  66}
  67
  68static int ep93xx_pcm_open(struct snd_pcm_substream *substream)
  69{
  70        struct snd_soc_pcm_runtime *rtd = substream->private_data;
  71        struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
  72        struct ep93xx_pcm_dma_params *dma_params;
  73        struct ep93xx_dma_data *dma_data;
  74        int ret;
  75
  76        snd_soc_set_runtime_hwparams(substream, &ep93xx_pcm_hardware);
  77
  78        dma_data = kmalloc(sizeof(*dma_data), GFP_KERNEL);
  79        if (!dma_data)
  80                return -ENOMEM;
  81
  82        dma_params = snd_soc_dai_get_dma_data(cpu_dai, substream);
  83        dma_data->port = dma_params->dma_port;
  84        dma_data->name = dma_params->name;
  85        dma_data->direction = snd_pcm_substream_to_dma_direction(substream);
  86
  87        ret = snd_dmaengine_pcm_open(substream, ep93xx_pcm_dma_filter, dma_data);
  88        if (ret) {
  89                kfree(dma_data);
  90                return ret;
  91        }
  92
  93        snd_dmaengine_pcm_set_data(substream, dma_data);
  94
  95        return 0;
  96}
  97
  98static int ep93xx_pcm_close(struct snd_pcm_substream *substream)
  99{
 100        struct dma_data *dma_data = snd_dmaengine_pcm_get_data(substream);
 101
 102        snd_dmaengine_pcm_close(substream);
 103        kfree(dma_data);
 104        return 0;
 105}
 106
 107static int ep93xx_pcm_hw_params(struct snd_pcm_substream *substream,
 108                                struct snd_pcm_hw_params *params)
 109{
 110        snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
 111
 112        return 0;
 113}
 114
 115static int ep93xx_pcm_hw_free(struct snd_pcm_substream *substream)
 116{
 117        snd_pcm_set_runtime_buffer(substream, NULL);
 118        return 0;
 119}
 120
 121static int ep93xx_pcm_mmap(struct snd_pcm_substream *substream,
 122                           struct vm_area_struct *vma)
 123{
 124        struct snd_pcm_runtime *runtime = substream->runtime;
 125
 126        return dma_mmap_writecombine(substream->pcm->card->dev, vma,
 127                                     runtime->dma_area,
 128                                     runtime->dma_addr,
 129                                     runtime->dma_bytes);
 130}
 131
 132static struct snd_pcm_ops ep93xx_pcm_ops = {
 133        .open           = ep93xx_pcm_open,
 134        .close          = ep93xx_pcm_close,
 135        .ioctl          = snd_pcm_lib_ioctl,
 136        .hw_params      = ep93xx_pcm_hw_params,
 137        .hw_free        = ep93xx_pcm_hw_free,
 138        .trigger        = snd_dmaengine_pcm_trigger,
 139        .pointer        = snd_dmaengine_pcm_pointer_no_residue,
 140        .mmap           = ep93xx_pcm_mmap,
 141};
 142
 143static int ep93xx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
 144{
 145        struct snd_pcm_substream *substream = pcm->streams[stream].substream;
 146        struct snd_dma_buffer *buf = &substream->dma_buffer;
 147        size_t size = ep93xx_pcm_hardware.buffer_bytes_max;
 148
 149        buf->dev.type = SNDRV_DMA_TYPE_DEV;
 150        buf->dev.dev = pcm->card->dev;
 151        buf->private_data = NULL;
 152        buf->area = dma_alloc_writecombine(pcm->card->dev, size,
 153                                           &buf->addr, GFP_KERNEL);
 154        buf->bytes = size;
 155
 156        return (buf->area == NULL) ? -ENOMEM : 0;
 157}
 158
 159static void ep93xx_pcm_free_dma_buffers(struct snd_pcm *pcm)
 160{
 161        struct snd_pcm_substream *substream;
 162        struct snd_dma_buffer *buf;
 163        int stream;
 164
 165        for (stream = 0; stream < 2; stream++) {                
 166                substream = pcm->streams[stream].substream;
 167                if (!substream)
 168                        continue;
 169                
 170                buf = &substream->dma_buffer;
 171                if (!buf->area)
 172                        continue;
 173
 174                dma_free_writecombine(pcm->card->dev, buf->bytes, buf->area,
 175                                      buf->addr);
 176                buf->area = NULL;
 177        }
 178}
 179
 180static u64 ep93xx_pcm_dmamask = DMA_BIT_MASK(32);
 181
 182static int ep93xx_pcm_new(struct snd_soc_pcm_runtime *rtd)
 183{
 184        struct snd_card *card = rtd->card->snd_card;
 185        struct snd_pcm *pcm = rtd->pcm;
 186        int ret = 0;
 187
 188        if (!card->dev->dma_mask)
 189                card->dev->dma_mask = &ep93xx_pcm_dmamask;
 190        if (!card->dev->coherent_dma_mask)
 191                card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
 192
 193        if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) {
 194                ret = ep93xx_pcm_preallocate_dma_buffer(pcm,
 195                                        SNDRV_PCM_STREAM_PLAYBACK);
 196                if (ret)
 197                        return ret;
 198        }
 199
 200        if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) {
 201                ret = ep93xx_pcm_preallocate_dma_buffer(pcm,
 202                                        SNDRV_PCM_STREAM_CAPTURE);
 203                if (ret)
 204                        return ret;
 205        }
 206
 207        return 0;
 208}
 209
 210static struct snd_soc_platform_driver ep93xx_soc_platform = {
 211        .ops            = &ep93xx_pcm_ops,
 212        .pcm_new        = &ep93xx_pcm_new,
 213        .pcm_free       = &ep93xx_pcm_free_dma_buffers,
 214};
 215
 216static int ep93xx_soc_platform_probe(struct platform_device *pdev)
 217{
 218        return snd_soc_register_platform(&pdev->dev, &ep93xx_soc_platform);
 219}
 220
 221static int ep93xx_soc_platform_remove(struct platform_device *pdev)
 222{
 223        snd_soc_unregister_platform(&pdev->dev);
 224        return 0;
 225}
 226
 227static struct platform_driver ep93xx_pcm_driver = {
 228        .driver = {
 229                        .name = "ep93xx-pcm-audio",
 230                        .owner = THIS_MODULE,
 231        },
 232
 233        .probe = ep93xx_soc_platform_probe,
 234        .remove = ep93xx_soc_platform_remove,
 235};
 236
 237module_platform_driver(ep93xx_pcm_driver);
 238
 239MODULE_AUTHOR("Ryan Mallon");
 240MODULE_DESCRIPTION("EP93xx ALSA PCM interface");
 241MODULE_LICENSE("GPL");
 242MODULE_ALIAS("platform:ep93xx-pcm-audio");
 243
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.