linux/sound/soc/kirkwood/kirkwood-dma.c
<<
>>
Prefs
   1/*
   2 * kirkwood-dma.c
   3 *
   4 * (c) 2010 Arnaud Patard <apatard@mandriva.com>
   5 * (c) 2010 Arnaud Patard <arnaud.patard@rtp-net.org>
   6 *
   7 *  This program is free software; you can redistribute  it and/or modify it
   8 *  under  the terms of  the GNU General  Public License as published by the
   9 *  Free Software Foundation;  either version 2 of the  License, or (at your
  10 *  option) any later version.
  11 */
  12
  13#include <linux/init.h>
  14#include <linux/module.h>
  15#include <linux/device.h>
  16#include <linux/io.h>
  17#include <linux/slab.h>
  18#include <linux/interrupt.h>
  19#include <linux/dma-mapping.h>
  20#include <linux/mbus.h>
  21#include <sound/soc.h>
  22#include "kirkwood.h"
  23
  24static struct kirkwood_dma_data *kirkwood_priv(struct snd_pcm_substream *subs)
  25{
  26        struct snd_soc_pcm_runtime *soc_runtime = subs->private_data;
  27        return snd_soc_dai_get_drvdata(soc_runtime->cpu_dai);
  28}
  29
  30static const struct snd_pcm_hardware kirkwood_dma_snd_hw = {
  31        .info = SNDRV_PCM_INFO_INTERLEAVED |
  32                SNDRV_PCM_INFO_MMAP |
  33                SNDRV_PCM_INFO_MMAP_VALID |
  34                SNDRV_PCM_INFO_BLOCK_TRANSFER |
  35                SNDRV_PCM_INFO_PAUSE |
  36                SNDRV_PCM_INFO_NO_PERIOD_WAKEUP,
  37        .buffer_bytes_max       = KIRKWOOD_SND_MAX_BUFFER_BYTES,
  38        .period_bytes_min       = KIRKWOOD_SND_MIN_PERIOD_BYTES,
  39        .period_bytes_max       = KIRKWOOD_SND_MAX_PERIOD_BYTES,
  40        .periods_min            = KIRKWOOD_SND_MIN_PERIODS,
  41        .periods_max            = KIRKWOOD_SND_MAX_PERIODS,
  42        .fifo_size              = 0,
  43};
  44
  45static irqreturn_t kirkwood_dma_irq(int irq, void *dev_id)
  46{
  47        struct kirkwood_dma_data *priv = dev_id;
  48        unsigned long mask, status, cause;
  49
  50        mask = readl(priv->io + KIRKWOOD_INT_MASK);
  51        status = readl(priv->io + KIRKWOOD_INT_CAUSE) & mask;
  52
  53        cause = readl(priv->io + KIRKWOOD_ERR_CAUSE);
  54        if (unlikely(cause)) {
  55                printk(KERN_WARNING "%s: got err interrupt 0x%lx\n",
  56                                __func__, cause);
  57                writel(cause, priv->io + KIRKWOOD_ERR_CAUSE);
  58        }
  59
  60        /* we've enabled only bytes interrupts ... */
  61        if (status & ~(KIRKWOOD_INT_CAUSE_PLAY_BYTES | \
  62                        KIRKWOOD_INT_CAUSE_REC_BYTES)) {
  63                printk(KERN_WARNING "%s: unexpected interrupt %lx\n",
  64                        __func__, status);
  65                return IRQ_NONE;
  66        }
  67
  68        /* ack int */
  69        writel(status, priv->io + KIRKWOOD_INT_CAUSE);
  70
  71        if (status & KIRKWOOD_INT_CAUSE_PLAY_BYTES)
  72                snd_pcm_period_elapsed(priv->substream_play);
  73
  74        if (status & KIRKWOOD_INT_CAUSE_REC_BYTES)
  75                snd_pcm_period_elapsed(priv->substream_rec);
  76
  77        return IRQ_HANDLED;
  78}
  79
  80static void
  81kirkwood_dma_conf_mbus_windows(void __iomem *base, int win,
  82                               unsigned long dma,
  83                               const struct mbus_dram_target_info *dram)
  84{
  85        int i;
  86
  87        /* First disable and clear windows */
  88        writel(0, base + KIRKWOOD_AUDIO_WIN_CTRL_REG(win));
  89        writel(0, base + KIRKWOOD_AUDIO_WIN_BASE_REG(win));
  90
  91        /* try to find matching cs for current dma address */
  92        for (i = 0; i < dram->num_cs; i++) {
  93                const struct mbus_dram_window *cs = dram->cs + i;
  94                if ((cs->base & 0xffff0000) < (dma & 0xffff0000)) {
  95                        writel(cs->base & 0xffff0000,
  96                                base + KIRKWOOD_AUDIO_WIN_BASE_REG(win));
  97                        writel(((cs->size - 1) & 0xffff0000) |
  98                                (cs->mbus_attr << 8) |
  99                                (dram->mbus_dram_target_id << 4) | 1,
 100                                base + KIRKWOOD_AUDIO_WIN_CTRL_REG(win));
 101                }
 102        }
 103}
 104
 105static int kirkwood_dma_open(struct snd_pcm_substream *substream)
 106{
 107        int err;
 108        struct snd_pcm_runtime *runtime = substream->runtime;
 109        struct kirkwood_dma_data *priv = kirkwood_priv(substream);
 110        const struct mbus_dram_target_info *dram;
 111        unsigned long addr;
 112
 113        snd_soc_set_runtime_hwparams(substream, &kirkwood_dma_snd_hw);
 114
 115        /* Ensure that all constraints linked to dma burst are fulfilled */
 116        err = snd_pcm_hw_constraint_minmax(runtime,
 117                        SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
 118                        priv->burst * 2,
 119                        KIRKWOOD_AUDIO_BUF_MAX-1);
 120        if (err < 0)
 121                return err;
 122
 123        err = snd_pcm_hw_constraint_step(runtime, 0,
 124                        SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
 125                        priv->burst);
 126        if (err < 0)
 127                return err;
 128
 129        err = snd_pcm_hw_constraint_step(substream->runtime, 0,
 130                         SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
 131                         priv->burst);
 132        if (err < 0)
 133                return err;
 134
 135        if (!priv->substream_play && !priv->substream_rec) {
 136                err = request_irq(priv->irq, kirkwood_dma_irq, IRQF_SHARED,
 137                                  "kirkwood-i2s", priv);
 138                if (err)
 139                        return -EBUSY;
 140
 141                /*
 142                 * Enable Error interrupts. We're only ack'ing them but
 143                 * it's useful for diagnostics
 144                 */
 145                writel((unsigned int)-1, priv->io + KIRKWOOD_ERR_MASK);
 146        }
 147
 148        dram = mv_mbus_dram_info();
 149        addr = substream->dma_buffer.addr;
 150        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
 151                if (priv->substream_play)
 152                        return -EBUSY;
 153                priv->substream_play = substream;
 154                kirkwood_dma_conf_mbus_windows(priv->io,
 155                        KIRKWOOD_PLAYBACK_WIN, addr, dram);
 156        } else {
 157                if (priv->substream_rec)
 158                        return -EBUSY;
 159                priv->substream_rec = substream;
 160                kirkwood_dma_conf_mbus_windows(priv->io,
 161                        KIRKWOOD_RECORD_WIN, addr, dram);
 162        }
 163
 164        return 0;
 165}
 166
 167static int kirkwood_dma_close(struct snd_pcm_substream *substream)
 168{
 169        struct kirkwood_dma_data *priv = kirkwood_priv(substream);
 170
 171        if (!priv)
 172                return 0;
 173
 174        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
 175                priv->substream_play = NULL;
 176        else
 177                priv->substream_rec = NULL;
 178
 179        if (!priv->substream_play && !priv->substream_rec) {
 180                writel(0, priv->io + KIRKWOOD_ERR_MASK);
 181                free_irq(priv->irq, priv);
 182        }
 183
 184        return 0;
 185}
 186
 187static int kirkwood_dma_hw_params(struct snd_pcm_substream *substream,
 188                                struct snd_pcm_hw_params *params)
 189{
 190        struct snd_pcm_runtime *runtime = substream->runtime;
 191
 192        snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
 193        runtime->dma_bytes = params_buffer_bytes(params);
 194
 195        return 0;
 196}
 197
 198static int kirkwood_dma_hw_free(struct snd_pcm_substream *substream)
 199{
 200        snd_pcm_set_runtime_buffer(substream, NULL);
 201        return 0;
 202}
 203
 204static int kirkwood_dma_prepare(struct snd_pcm_substream *substream)
 205{
 206        struct snd_pcm_runtime *runtime = substream->runtime;
 207        struct kirkwood_dma_data *priv = kirkwood_priv(substream);
 208        unsigned long size, count;
 209
 210        /* compute buffer size in term of "words" as requested in specs */
 211        size = frames_to_bytes(runtime, runtime->buffer_size);
 212        size = (size>>2)-1;
 213        count = snd_pcm_lib_period_bytes(substream);
 214
 215        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
 216                writel(count, priv->io + KIRKWOOD_PLAY_BYTE_INT_COUNT);
 217                writel(runtime->dma_addr, priv->io + KIRKWOOD_PLAY_BUF_ADDR);
 218                writel(size, priv->io + KIRKWOOD_PLAY_BUF_SIZE);
 219        } else {
 220                writel(count, priv->io + KIRKWOOD_REC_BYTE_INT_COUNT);
 221                writel(runtime->dma_addr, priv->io + KIRKWOOD_REC_BUF_ADDR);
 222                writel(size, priv->io + KIRKWOOD_REC_BUF_SIZE);
 223        }
 224
 225
 226        return 0;
 227}
 228
 229static snd_pcm_uframes_t kirkwood_dma_pointer(struct snd_pcm_substream
 230                                                *substream)
 231{
 232        struct kirkwood_dma_data *priv = kirkwood_priv(substream);
 233        snd_pcm_uframes_t count;
 234
 235        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
 236                count = bytes_to_frames(substream->runtime,
 237                        readl(priv->io + KIRKWOOD_PLAY_BYTE_COUNT));
 238        else
 239                count = bytes_to_frames(substream->runtime,
 240                        readl(priv->io + KIRKWOOD_REC_BYTE_COUNT));
 241
 242        return count;
 243}
 244
 245static const struct snd_pcm_ops kirkwood_dma_ops = {
 246        .open =         kirkwood_dma_open,
 247        .close =        kirkwood_dma_close,
 248        .ioctl =        snd_pcm_lib_ioctl,
 249        .hw_params =    kirkwood_dma_hw_params,
 250        .hw_free =      kirkwood_dma_hw_free,
 251        .prepare =      kirkwood_dma_prepare,
 252        .pointer =      kirkwood_dma_pointer,
 253};
 254
 255static int kirkwood_dma_preallocate_dma_buffer(struct snd_pcm *pcm,
 256                int stream)
 257{
 258        struct snd_pcm_substream *substream = pcm->streams[stream].substream;
 259        struct snd_dma_buffer *buf = &substream->dma_buffer;
 260        size_t size = kirkwood_dma_snd_hw.buffer_bytes_max;
 261
 262        buf->dev.type = SNDRV_DMA_TYPE_DEV;
 263        buf->dev.dev = pcm->card->dev;
 264        buf->area = dma_alloc_coherent(pcm->card->dev, size,
 265                        &buf->addr, GFP_KERNEL);
 266        if (!buf->area)
 267                return -ENOMEM;
 268        buf->bytes = size;
 269        buf->private_data = NULL;
 270
 271        return 0;
 272}
 273
 274static int kirkwood_dma_new(struct snd_soc_pcm_runtime *rtd)
 275{
 276        struct snd_card *card = rtd->card->snd_card;
 277        struct snd_pcm *pcm = rtd->pcm;
 278        int ret;
 279
 280        ret = dma_coerce_mask_and_coherent(card->dev, DMA_BIT_MASK(32));
 281        if (ret)
 282                return ret;
 283
 284        if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) {
 285                ret = kirkwood_dma_preallocate_dma_buffer(pcm,
 286                                SNDRV_PCM_STREAM_PLAYBACK);
 287                if (ret)
 288                        return ret;
 289        }
 290
 291        if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) {
 292                ret = kirkwood_dma_preallocate_dma_buffer(pcm,
 293                                SNDRV_PCM_STREAM_CAPTURE);
 294                if (ret)
 295                        return ret;
 296        }
 297
 298        return 0;
 299}
 300
 301static void kirkwood_dma_free_dma_buffers(struct snd_pcm *pcm)
 302{
 303        struct snd_pcm_substream *substream;
 304        struct snd_dma_buffer *buf;
 305        int stream;
 306
 307        for (stream = 0; stream < 2; stream++) {
 308                substream = pcm->streams[stream].substream;
 309                if (!substream)
 310                        continue;
 311                buf = &substream->dma_buffer;
 312                if (!buf->area)
 313                        continue;
 314
 315                dma_free_coherent(pcm->card->dev, buf->bytes,
 316                                buf->area, buf->addr);
 317                buf->area = NULL;
 318        }
 319}
 320
 321const struct snd_soc_component_driver kirkwood_soc_component = {
 322        .name           = DRV_NAME,
 323        .ops            = &kirkwood_dma_ops,
 324        .pcm_new        = kirkwood_dma_new,
 325        .pcm_free       = kirkwood_dma_free_dma_buffers,
 326};
 327