linux/sound/soc/imx/imx-pcm-dma-mx2.c
<<
>>
Prefs
   1/*
   2 * imx-pcm-dma-mx2.c  --  ALSA Soc Audio Layer
   3 *
   4 * Copyright 2009 Sascha Hauer <s.hauer@pengutronix.de>
   5 *
   6 * This code is based on code copyrighted by Freescale,
   7 * Liam Girdwood, Javier Martin and probably others.
   8 *
   9 *  This program is free software; you can redistribute  it and/or modify it
  10 *  under  the terms of  the GNU General  Public License as published by the
  11 *  Free Software Foundation;  either version 2 of the  License, or (at your
  12 *  option) any later version.
  13 */
  14#include <linux/clk.h>
  15#include <linux/delay.h>
  16#include <linux/device.h>
  17#include <linux/dma-mapping.h>
  18#include <linux/init.h>
  19#include <linux/interrupt.h>
  20#include <linux/module.h>
  21#include <linux/platform_device.h>
  22#include <linux/slab.h>
  23#include <linux/dmaengine.h>
  24#include <linux/types.h>
  25
  26#include <sound/core.h>
  27#include <sound/initval.h>
  28#include <sound/pcm.h>
  29#include <sound/pcm_params.h>
  30#include <sound/soc.h>
  31#include <sound/dmaengine_pcm.h>
  32
  33#include <mach/dma.h>
  34
  35#include "imx-pcm.h"
  36
  37static bool filter(struct dma_chan *chan, void *param)
  38{
  39        if (!imx_dma_is_general_purpose(chan))
  40                return false;
  41
  42        chan->private = param;
  43
  44        return true;
  45}
  46
  47static int snd_imx_pcm_hw_params(struct snd_pcm_substream *substream,
  48                                struct snd_pcm_hw_params *params)
  49{
  50        struct snd_soc_pcm_runtime *rtd = substream->private_data;
  51        struct dma_chan *chan = snd_dmaengine_pcm_get_chan(substream);
  52        struct imx_pcm_dma_params *dma_params;
  53        struct dma_slave_config slave_config;
  54        int ret;
  55
  56        dma_params = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
  57
  58        ret = snd_hwparams_to_dma_slave_config(substream, params, &slave_config);
  59        if (ret)
  60                return ret;
  61
  62        slave_config.device_fc = false;
  63
  64        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
  65                slave_config.dst_addr = dma_params->dma_addr;
  66                slave_config.dst_maxburst = dma_params->burstsize;
  67        } else {
  68                slave_config.src_addr = dma_params->dma_addr;
  69                slave_config.src_maxburst = dma_params->burstsize;
  70        }
  71
  72        ret = dmaengine_slave_config(chan, &slave_config);
  73        if (ret)
  74                return ret;
  75
  76        snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
  77
  78        return 0;
  79}
  80
  81static struct snd_pcm_hardware snd_imx_hardware = {
  82        .info = SNDRV_PCM_INFO_INTERLEAVED |
  83                SNDRV_PCM_INFO_BLOCK_TRANSFER |
  84                SNDRV_PCM_INFO_MMAP |
  85                SNDRV_PCM_INFO_MMAP_VALID |
  86                SNDRV_PCM_INFO_PAUSE |
  87                SNDRV_PCM_INFO_RESUME,
  88        .formats = SNDRV_PCM_FMTBIT_S16_LE,
  89        .rate_min = 8000,
  90        .channels_min = 2,
  91        .channels_max = 2,
  92        .buffer_bytes_max = IMX_SSI_DMABUF_SIZE,
  93        .period_bytes_min = 128,
  94        .period_bytes_max = 65535, /* Limited by SDMA engine */
  95        .periods_min = 2,
  96        .periods_max = 255,
  97        .fifo_size = 0,
  98};
  99
 100static int snd_imx_open(struct snd_pcm_substream *substream)
 101{
 102        struct snd_soc_pcm_runtime *rtd = substream->private_data;
 103        struct imx_pcm_dma_params *dma_params;
 104        struct imx_dma_data *dma_data;
 105        int ret;
 106
 107        snd_soc_set_runtime_hwparams(substream, &snd_imx_hardware);
 108
 109        dma_params = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
 110
 111        dma_data = kzalloc(sizeof(*dma_data), GFP_KERNEL);
 112        dma_data->peripheral_type = IMX_DMATYPE_SSI;
 113        dma_data->priority = DMA_PRIO_HIGH;
 114        dma_data->dma_request = dma_params->dma;
 115
 116        ret = snd_dmaengine_pcm_open(substream, filter, dma_data);
 117        if (ret) {
 118                kfree(dma_data);
 119                return 0;
 120        }
 121
 122        snd_dmaengine_pcm_set_data(substream, dma_data);
 123
 124        return 0;
 125}
 126
 127static int snd_imx_close(struct snd_pcm_substream *substream)
 128{
 129        struct imx_dma_data *dma_data = snd_dmaengine_pcm_get_data(substream);
 130
 131        snd_dmaengine_pcm_close(substream);
 132        kfree(dma_data);
 133
 134        return 0;
 135}
 136
 137static struct snd_pcm_ops imx_pcm_ops = {
 138        .open           = snd_imx_open,
 139        .close          = snd_imx_close,
 140        .ioctl          = snd_pcm_lib_ioctl,
 141        .hw_params      = snd_imx_pcm_hw_params,
 142        .trigger        = snd_dmaengine_pcm_trigger,
 143        .pointer        = snd_dmaengine_pcm_pointer,
 144        .mmap           = snd_imx_pcm_mmap,
 145};
 146
 147static struct snd_soc_platform_driver imx_soc_platform_mx2 = {
 148        .ops            = &imx_pcm_ops,
 149        .pcm_new        = imx_pcm_new,
 150        .pcm_free       = imx_pcm_free,
 151};
 152
 153static int __devinit imx_soc_platform_probe(struct platform_device *pdev)
 154{
 155        return snd_soc_register_platform(&pdev->dev, &imx_soc_platform_mx2);
 156}
 157
 158static int __devexit imx_soc_platform_remove(struct platform_device *pdev)
 159{
 160        snd_soc_unregister_platform(&pdev->dev);
 161        return 0;
 162}
 163
 164static struct platform_driver imx_pcm_driver = {
 165        .driver = {
 166                        .name = "imx-pcm-audio",
 167                        .owner = THIS_MODULE,
 168        },
 169        .probe = imx_soc_platform_probe,
 170        .remove = __devexit_p(imx_soc_platform_remove),
 171};
 172
 173module_platform_driver(imx_pcm_driver);
 174MODULE_LICENSE("GPL");
 175MODULE_ALIAS("platform:imx-pcm-audio");
 176