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
  25#include <sound/core.h>
  26#include <sound/initval.h>
  27#include <sound/pcm.h>
  28#include <sound/pcm_params.h>
  29#include <sound/soc.h>
  30
  31#include <mach/dma.h>
  32
  33#include "imx-ssi.h"
  34
  35struct imx_pcm_runtime_data {
  36        int period_bytes;
  37        int periods;
  38        int dma;
  39        unsigned long offset;
  40        unsigned long size;
  41        void *buf;
  42        int period_time;
  43        struct dma_async_tx_descriptor *desc;
  44        struct dma_chan *dma_chan;
  45        struct imx_dma_data dma_data;
  46};
  47
  48static void audio_dma_irq(void *data)
  49{
  50        struct snd_pcm_substream *substream = (struct snd_pcm_substream *)data;
  51        struct snd_pcm_runtime *runtime = substream->runtime;
  52        struct imx_pcm_runtime_data *iprtd = runtime->private_data;
  53
  54        iprtd->offset += iprtd->period_bytes;
  55        iprtd->offset %= iprtd->period_bytes * iprtd->periods;
  56
  57        snd_pcm_period_elapsed(substream);
  58}
  59
  60static bool filter(struct dma_chan *chan, void *param)
  61{
  62        struct imx_pcm_runtime_data *iprtd = param;
  63
  64        if (!imx_dma_is_general_purpose(chan))
  65                return false;
  66
  67        chan->private = &iprtd->dma_data;
  68
  69        return true;
  70}
  71
  72static int imx_ssi_dma_alloc(struct snd_pcm_substream *substream,
  73                                struct snd_pcm_hw_params *params)
  74{
  75        struct snd_soc_pcm_runtime *rtd = substream->private_data;
  76        struct imx_pcm_dma_params *dma_params;
  77        struct snd_pcm_runtime *runtime = substream->runtime;
  78        struct imx_pcm_runtime_data *iprtd = runtime->private_data;
  79        struct dma_slave_config slave_config;
  80        dma_cap_mask_t mask;
  81        enum dma_slave_buswidth buswidth;
  82        int ret;
  83
  84        dma_params = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
  85
  86        iprtd->dma_data.peripheral_type = IMX_DMATYPE_SSI;
  87        iprtd->dma_data.priority = DMA_PRIO_HIGH;
  88        iprtd->dma_data.dma_request = dma_params->dma;
  89
  90        /* Try to grab a DMA channel */
  91        dma_cap_zero(mask);
  92        dma_cap_set(DMA_SLAVE, mask);
  93        iprtd->dma_chan = dma_request_channel(mask, filter, iprtd);
  94        if (!iprtd->dma_chan)
  95                return -EINVAL;
  96
  97        switch (params_format(params)) {
  98        case SNDRV_PCM_FORMAT_S16_LE:
  99                buswidth = DMA_SLAVE_BUSWIDTH_2_BYTES;
 100                break;
 101        case SNDRV_PCM_FORMAT_S20_3LE:
 102        case SNDRV_PCM_FORMAT_S24_LE:
 103                buswidth = DMA_SLAVE_BUSWIDTH_4_BYTES;
 104                break;
 105        default:
 106                return 0;
 107        }
 108
 109        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
 110                slave_config.direction = DMA_TO_DEVICE;
 111                slave_config.dst_addr = dma_params->dma_addr;
 112                slave_config.dst_addr_width = buswidth;
 113                slave_config.dst_maxburst = dma_params->burstsize;
 114        } else {
 115                slave_config.direction = DMA_FROM_DEVICE;
 116                slave_config.src_addr = dma_params->dma_addr;
 117                slave_config.src_addr_width = buswidth;
 118                slave_config.src_maxburst = dma_params->burstsize;
 119        }
 120
 121        ret = dmaengine_slave_config(iprtd->dma_chan, &slave_config);
 122        if (ret)
 123                return ret;
 124
 125        return 0;
 126}
 127
 128static int snd_imx_pcm_hw_params(struct snd_pcm_substream *substream,
 129                                struct snd_pcm_hw_params *params)
 130{
 131        struct snd_soc_pcm_runtime *rtd = substream->private_data;
 132        struct snd_pcm_runtime *runtime = substream->runtime;
 133        struct imx_pcm_runtime_data *iprtd = runtime->private_data;
 134        unsigned long dma_addr;
 135        struct dma_chan *chan;
 136        struct imx_pcm_dma_params *dma_params;
 137        int ret;
 138
 139        dma_params = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
 140        ret = imx_ssi_dma_alloc(substream, params);
 141        if (ret)
 142                return ret;
 143        chan = iprtd->dma_chan;
 144
 145        iprtd->size = params_buffer_bytes(params);
 146        iprtd->periods = params_periods(params);
 147        iprtd->period_bytes = params_period_bytes(params);
 148        iprtd->offset = 0;
 149        iprtd->period_time = HZ / (params_rate(params) /
 150                        params_period_size(params));
 151
 152        snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
 153
 154        dma_addr = runtime->dma_addr;
 155
 156        iprtd->buf = (unsigned int *)substream->dma_buffer.area;
 157
 158        iprtd->desc = chan->device->device_prep_dma_cyclic(chan, dma_addr,
 159                        iprtd->period_bytes * iprtd->periods,
 160                        iprtd->period_bytes,
 161                        substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?
 162                        DMA_TO_DEVICE : DMA_FROM_DEVICE);
 163        if (!iprtd->desc) {
 164                dev_err(&chan->dev->device, "cannot prepare slave dma\n");
 165                return -EINVAL;
 166        }
 167
 168        iprtd->desc->callback = audio_dma_irq;
 169        iprtd->desc->callback_param = substream;
 170
 171        return 0;
 172}
 173
 174static int snd_imx_pcm_hw_free(struct snd_pcm_substream *substream)
 175{
 176        struct snd_pcm_runtime *runtime = substream->runtime;
 177        struct imx_pcm_runtime_data *iprtd = runtime->private_data;
 178
 179        if (iprtd->dma_chan) {
 180                dma_release_channel(iprtd->dma_chan);
 181                iprtd->dma_chan = NULL;
 182        }
 183
 184        return 0;
 185}
 186
 187static int snd_imx_pcm_prepare(struct snd_pcm_substream *substream)
 188{
 189        struct snd_soc_pcm_runtime *rtd = substream->private_data;
 190        struct imx_pcm_dma_params *dma_params;
 191
 192        dma_params = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
 193
 194        return 0;
 195}
 196
 197static int snd_imx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
 198{
 199        struct snd_pcm_runtime *runtime = substream->runtime;
 200        struct imx_pcm_runtime_data *iprtd = runtime->private_data;
 201
 202        switch (cmd) {
 203        case SNDRV_PCM_TRIGGER_START:
 204        case SNDRV_PCM_TRIGGER_RESUME:
 205        case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
 206                dmaengine_submit(iprtd->desc);
 207
 208                break;
 209
 210        case SNDRV_PCM_TRIGGER_STOP:
 211        case SNDRV_PCM_TRIGGER_SUSPEND:
 212        case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
 213                dmaengine_terminate_all(iprtd->dma_chan);
 214
 215                break;
 216        default:
 217                return -EINVAL;
 218        }
 219
 220        return 0;
 221}
 222
 223static snd_pcm_uframes_t snd_imx_pcm_pointer(struct snd_pcm_substream *substream)
 224{
 225        struct snd_pcm_runtime *runtime = substream->runtime;
 226        struct imx_pcm_runtime_data *iprtd = runtime->private_data;
 227
 228        pr_debug("%s: %ld %ld\n", __func__, iprtd->offset,
 229                        bytes_to_frames(substream->runtime, iprtd->offset));
 230
 231        return bytes_to_frames(substream->runtime, iprtd->offset);
 232}
 233
 234static struct snd_pcm_hardware snd_imx_hardware = {
 235        .info = SNDRV_PCM_INFO_INTERLEAVED |
 236                SNDRV_PCM_INFO_BLOCK_TRANSFER |
 237                SNDRV_PCM_INFO_MMAP |
 238                SNDRV_PCM_INFO_MMAP_VALID |
 239                SNDRV_PCM_INFO_PAUSE |
 240                SNDRV_PCM_INFO_RESUME,
 241        .formats = SNDRV_PCM_FMTBIT_S16_LE,
 242        .rate_min = 8000,
 243        .channels_min = 2,
 244        .channels_max = 2,
 245        .buffer_bytes_max = IMX_SSI_DMABUF_SIZE,
 246        .period_bytes_min = 128,
 247        .period_bytes_max = 65535, /* Limited by SDMA engine */
 248        .periods_min = 2,
 249        .periods_max = 255,
 250        .fifo_size = 0,
 251};
 252
 253static int snd_imx_open(struct snd_pcm_substream *substream)
 254{
 255        struct snd_pcm_runtime *runtime = substream->runtime;
 256        struct imx_pcm_runtime_data *iprtd;
 257        int ret;
 258
 259        iprtd = kzalloc(sizeof(*iprtd), GFP_KERNEL);
 260        if (iprtd == NULL)
 261                return -ENOMEM;
 262        runtime->private_data = iprtd;
 263
 264        ret = snd_pcm_hw_constraint_integer(substream->runtime,
 265                        SNDRV_PCM_HW_PARAM_PERIODS);
 266        if (ret < 0) {
 267                kfree(iprtd);
 268                return ret;
 269        }
 270
 271        snd_soc_set_runtime_hwparams(substream, &snd_imx_hardware);
 272
 273        return 0;
 274}
 275
 276static int snd_imx_close(struct snd_pcm_substream *substream)
 277{
 278        struct snd_pcm_runtime *runtime = substream->runtime;
 279        struct imx_pcm_runtime_data *iprtd = runtime->private_data;
 280
 281        kfree(iprtd);
 282
 283        return 0;
 284}
 285
 286static struct snd_pcm_ops imx_pcm_ops = {
 287        .open           = snd_imx_open,
 288        .close          = snd_imx_close,
 289        .ioctl          = snd_pcm_lib_ioctl,
 290        .hw_params      = snd_imx_pcm_hw_params,
 291        .hw_free        = snd_imx_pcm_hw_free,
 292        .prepare        = snd_imx_pcm_prepare,
 293        .trigger        = snd_imx_pcm_trigger,
 294        .pointer        = snd_imx_pcm_pointer,
 295        .mmap           = snd_imx_pcm_mmap,
 296};
 297
 298static struct snd_soc_platform_driver imx_soc_platform_mx2 = {
 299        .ops            = &imx_pcm_ops,
 300        .pcm_new        = imx_pcm_new,
 301        .pcm_free       = imx_pcm_free,
 302};
 303
 304static int __devinit imx_soc_platform_probe(struct platform_device *pdev)
 305{
 306        struct imx_ssi *ssi = platform_get_drvdata(pdev);
 307
 308        ssi->dma_params_tx.burstsize = 6;
 309        ssi->dma_params_rx.burstsize = 4;
 310
 311        return snd_soc_register_platform(&pdev->dev, &imx_soc_platform_mx2);
 312}
 313
 314static int __devexit imx_soc_platform_remove(struct platform_device *pdev)
 315{
 316        snd_soc_unregister_platform(&pdev->dev);
 317        return 0;
 318}
 319
 320static struct platform_driver imx_pcm_driver = {
 321        .driver = {
 322                        .name = "imx-pcm-audio",
 323                        .owner = THIS_MODULE,
 324        },
 325        .probe = imx_soc_platform_probe,
 326        .remove = __devexit_p(imx_soc_platform_remove),
 327};
 328
 329static int __init snd_imx_pcm_init(void)
 330{
 331        return platform_driver_register(&imx_pcm_driver);
 332}
 333module_init(snd_imx_pcm_init);
 334
 335static void __exit snd_imx_pcm_exit(void)
 336{
 337        platform_driver_unregister(&imx_pcm_driver);
 338}
 339module_exit(snd_imx_pcm_exit);
 340MODULE_LICENSE("GPL");
 341MODULE_ALIAS("platform:imx-pcm-audio");
 342