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
  24#include <sound/core.h>
  25#include <sound/initval.h>
  26#include <sound/pcm.h>
  27#include <sound/pcm_params.h>
  28#include <sound/soc.h>
  29
  30#include <mach/dma-mx1-mx2.h>
  31
  32#include "imx-ssi.h"
  33
  34struct imx_pcm_runtime_data {
  35        int sg_count;
  36        struct scatterlist *sg_list;
  37        int period;
  38        int periods;
  39        unsigned long dma_addr;
  40        int dma;
  41        struct snd_pcm_substream *substream;
  42        unsigned long offset;
  43        unsigned long size;
  44        unsigned long period_cnt;
  45        void *buf;
  46        int period_time;
  47};
  48
  49/* Called by the DMA framework when a period has elapsed */
  50static void imx_ssi_dma_progression(int channel, void *data,
  51                                        struct scatterlist *sg)
  52{
  53        struct snd_pcm_substream *substream = data;
  54        struct snd_pcm_runtime *runtime = substream->runtime;
  55        struct imx_pcm_runtime_data *iprtd = runtime->private_data;
  56
  57        if (!sg)
  58                return;
  59
  60        runtime = iprtd->substream->runtime;
  61
  62        iprtd->offset = sg->dma_address - runtime->dma_addr;
  63
  64        snd_pcm_period_elapsed(iprtd->substream);
  65}
  66
  67static void imx_ssi_dma_callback(int channel, void *data)
  68{
  69        pr_err("%s shouldn't be called\n", __func__);
  70}
  71
  72static void snd_imx_dma_err_callback(int channel, void *data, int err)
  73{
  74        struct snd_pcm_substream *substream = data;
  75        struct snd_soc_pcm_runtime *rtd = substream->private_data;
  76        struct imx_pcm_dma_params *dma_params = 
  77                snd_soc_dai_get_dma_data(rtd->dai->cpu_dai, substream);
  78        struct snd_pcm_runtime *runtime = substream->runtime;
  79        struct imx_pcm_runtime_data *iprtd = runtime->private_data;
  80        int ret;
  81
  82        pr_err("DMA timeout on channel %d -%s%s%s%s\n",
  83                 channel,
  84                 err & IMX_DMA_ERR_BURST ?    " burst" : "",
  85                 err & IMX_DMA_ERR_REQUEST ?  " request" : "",
  86                 err & IMX_DMA_ERR_TRANSFER ? " transfer" : "",
  87                 err & IMX_DMA_ERR_BUFFER ?   " buffer" : "");
  88
  89        imx_dma_disable(iprtd->dma);
  90        ret = imx_dma_setup_sg(iprtd->dma, iprtd->sg_list, iprtd->sg_count,
  91                        IMX_DMA_LENGTH_LOOP, dma_params->dma_addr,
  92                        substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?
  93                        DMA_MODE_WRITE : DMA_MODE_READ);
  94        if (!ret)
  95                imx_dma_enable(iprtd->dma);
  96}
  97
  98static int imx_ssi_dma_alloc(struct snd_pcm_substream *substream)
  99{
 100        struct snd_soc_pcm_runtime *rtd = substream->private_data;
 101        struct imx_pcm_dma_params *dma_params;
 102        struct snd_pcm_runtime *runtime = substream->runtime;
 103        struct imx_pcm_runtime_data *iprtd = runtime->private_data;
 104        int ret;
 105
 106        dma_params = snd_soc_dai_get_dma_data(rtd->dai->cpu_dai, substream);
 107
 108        iprtd->dma = imx_dma_request_by_prio(DRV_NAME, DMA_PRIO_HIGH);
 109        if (iprtd->dma < 0) {
 110                pr_err("Failed to claim the audio DMA\n");
 111                return -ENODEV;
 112        }
 113
 114        ret = imx_dma_setup_handlers(iprtd->dma,
 115                                imx_ssi_dma_callback,
 116                                snd_imx_dma_err_callback, substream);
 117        if (ret)
 118                goto out;
 119
 120        ret = imx_dma_setup_progression_handler(iprtd->dma,
 121                        imx_ssi_dma_progression);
 122        if (ret) {
 123                pr_err("Failed to setup the DMA handler\n");
 124                goto out;
 125        }
 126
 127        ret = imx_dma_config_channel(iprtd->dma,
 128                        IMX_DMA_MEMSIZE_16 | IMX_DMA_TYPE_FIFO,
 129                        IMX_DMA_MEMSIZE_32 | IMX_DMA_TYPE_LINEAR,
 130                        dma_params->dma, 1);
 131        if (ret < 0) {
 132                pr_err("Cannot configure DMA channel: %d\n", ret);
 133                goto out;
 134        }
 135
 136        imx_dma_config_burstlen(iprtd->dma, dma_params->burstsize * 2);
 137
 138        return 0;
 139out:
 140        imx_dma_free(iprtd->dma);
 141        return ret;
 142}
 143
 144static int snd_imx_pcm_hw_params(struct snd_pcm_substream *substream,
 145                                struct snd_pcm_hw_params *params)
 146{
 147        struct snd_pcm_runtime *runtime = substream->runtime;
 148        struct imx_pcm_runtime_data *iprtd = runtime->private_data;
 149        int i;
 150        unsigned long dma_addr;
 151
 152        imx_ssi_dma_alloc(substream);
 153
 154        iprtd->size = params_buffer_bytes(params);
 155        iprtd->periods = params_periods(params);
 156        iprtd->period = params_period_bytes(params);
 157        iprtd->offset = 0;
 158        iprtd->period_time = HZ / (params_rate(params) /
 159                        params_period_size(params));
 160
 161        snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
 162
 163        if (iprtd->sg_count != iprtd->periods) {
 164                kfree(iprtd->sg_list);
 165
 166                iprtd->sg_list = kcalloc(iprtd->periods + 1,
 167                                sizeof(struct scatterlist), GFP_KERNEL);
 168                if (!iprtd->sg_list)
 169                        return -ENOMEM;
 170                iprtd->sg_count = iprtd->periods + 1;
 171        }
 172
 173        sg_init_table(iprtd->sg_list, iprtd->sg_count);
 174        dma_addr = runtime->dma_addr;
 175
 176        for (i = 0; i < iprtd->periods; i++) {
 177                iprtd->sg_list[i].page_link = 0;
 178                iprtd->sg_list[i].offset = 0;
 179                iprtd->sg_list[i].dma_address = dma_addr;
 180                iprtd->sg_list[i].length = iprtd->period;
 181                dma_addr += iprtd->period;
 182        }
 183
 184        /* close the loop */
 185        iprtd->sg_list[iprtd->sg_count - 1].offset = 0;
 186        iprtd->sg_list[iprtd->sg_count - 1].length = 0;
 187        iprtd->sg_list[iprtd->sg_count - 1].page_link =
 188                        ((unsigned long) iprtd->sg_list | 0x01) & ~0x02;
 189        return 0;
 190}
 191
 192static int snd_imx_pcm_hw_free(struct snd_pcm_substream *substream)
 193{
 194        struct snd_pcm_runtime *runtime = substream->runtime;
 195        struct imx_pcm_runtime_data *iprtd = runtime->private_data;
 196
 197        if (iprtd->dma >= 0) {
 198                imx_dma_free(iprtd->dma);
 199                iprtd->dma = -EINVAL;
 200        }
 201
 202        kfree(iprtd->sg_list);
 203        iprtd->sg_list = NULL;
 204
 205        return 0;
 206}
 207
 208static int snd_imx_pcm_prepare(struct snd_pcm_substream *substream)
 209{
 210        struct snd_pcm_runtime *runtime = substream->runtime;
 211        struct snd_soc_pcm_runtime *rtd = substream->private_data;
 212        struct imx_pcm_dma_params *dma_params;
 213        struct imx_pcm_runtime_data *iprtd = runtime->private_data;
 214        int err;
 215
 216        dma_params = snd_soc_dai_get_dma_data(rtd->dai->cpu_dai, substream);
 217
 218        iprtd->substream = substream;
 219        iprtd->buf = (unsigned int *)substream->dma_buffer.area;
 220        iprtd->period_cnt = 0;
 221
 222        pr_debug("%s: buf: %p period: %d periods: %d\n",
 223                        __func__, iprtd->buf, iprtd->period, iprtd->periods);
 224
 225        err = imx_dma_setup_sg(iprtd->dma, iprtd->sg_list, iprtd->sg_count,
 226                        IMX_DMA_LENGTH_LOOP, dma_params->dma_addr,
 227                        substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?
 228                        DMA_MODE_WRITE : DMA_MODE_READ);
 229        if (err)
 230                return err;
 231
 232        return 0;
 233}
 234
 235static int snd_imx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
 236{
 237        struct snd_pcm_runtime *runtime = substream->runtime;
 238        struct imx_pcm_runtime_data *iprtd = runtime->private_data;
 239
 240        switch (cmd) {
 241        case SNDRV_PCM_TRIGGER_START:
 242        case SNDRV_PCM_TRIGGER_RESUME:
 243        case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
 244                imx_dma_enable(iprtd->dma);
 245
 246                break;
 247
 248        case SNDRV_PCM_TRIGGER_STOP:
 249        case SNDRV_PCM_TRIGGER_SUSPEND:
 250        case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
 251                imx_dma_disable(iprtd->dma);
 252
 253                break;
 254        default:
 255                return -EINVAL;
 256        }
 257
 258        return 0;
 259}
 260
 261static snd_pcm_uframes_t snd_imx_pcm_pointer(struct snd_pcm_substream *substream)
 262{
 263        struct snd_pcm_runtime *runtime = substream->runtime;
 264        struct imx_pcm_runtime_data *iprtd = runtime->private_data;
 265
 266        return bytes_to_frames(substream->runtime, iprtd->offset);
 267}
 268
 269static struct snd_pcm_hardware snd_imx_hardware = {
 270        .info = SNDRV_PCM_INFO_INTERLEAVED |
 271                SNDRV_PCM_INFO_BLOCK_TRANSFER |
 272                SNDRV_PCM_INFO_MMAP |
 273                SNDRV_PCM_INFO_MMAP_VALID |
 274                SNDRV_PCM_INFO_PAUSE |
 275                SNDRV_PCM_INFO_RESUME,
 276        .formats = SNDRV_PCM_FMTBIT_S16_LE,
 277        .rate_min = 8000,
 278        .channels_min = 2,
 279        .channels_max = 2,
 280        .buffer_bytes_max = IMX_SSI_DMABUF_SIZE,
 281        .period_bytes_min = 128,
 282        .period_bytes_max = 16 * 1024,
 283        .periods_min = 2,
 284        .periods_max = 255,
 285        .fifo_size = 0,
 286};
 287
 288static int snd_imx_open(struct snd_pcm_substream *substream)
 289{
 290        struct snd_pcm_runtime *runtime = substream->runtime;
 291        struct imx_pcm_runtime_data *iprtd;
 292        int ret;
 293
 294        iprtd = kzalloc(sizeof(*iprtd), GFP_KERNEL);
 295        runtime->private_data = iprtd;
 296
 297        ret = snd_pcm_hw_constraint_integer(substream->runtime,
 298                        SNDRV_PCM_HW_PARAM_PERIODS);
 299        if (ret < 0)
 300                return ret;
 301
 302        snd_soc_set_runtime_hwparams(substream, &snd_imx_hardware);
 303        return 0;
 304}
 305
 306static struct snd_pcm_ops imx_pcm_ops = {
 307        .open           = snd_imx_open,
 308        .ioctl          = snd_pcm_lib_ioctl,
 309        .hw_params      = snd_imx_pcm_hw_params,
 310        .hw_free        = snd_imx_pcm_hw_free,
 311        .prepare        = snd_imx_pcm_prepare,
 312        .trigger        = snd_imx_pcm_trigger,
 313        .pointer        = snd_imx_pcm_pointer,
 314        .mmap           = snd_imx_pcm_mmap,
 315};
 316
 317static struct snd_soc_platform imx_soc_platform_dma = {
 318        .name           = "imx-audio",
 319        .pcm_ops        = &imx_pcm_ops,
 320        .pcm_new        = imx_pcm_new,
 321        .pcm_free       = imx_pcm_free,
 322};
 323
 324struct snd_soc_platform *imx_ssi_dma_mx2_init(struct platform_device *pdev,
 325                struct imx_ssi *ssi)
 326{
 327        ssi->dma_params_tx.burstsize = DMA_TXFIFO_BURST;
 328        ssi->dma_params_rx.burstsize = DMA_RXFIFO_BURST;
 329
 330        return &imx_soc_platform_dma;
 331}
 332
 333
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.