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