linux/sound/soc/au1x/dma.c
<<
>>
Prefs
   1/*
   2 * Au1000/Au1500/Au1100 Audio DMA support.
   3 *
   4 * (c) 2011 Manuel Lauss <manuel.lauss@googlemail.com>
   5 *
   6 * copied almost verbatim from the old ALSA driver, written by
   7 *                      Charles Eidsness <charles@cooper-street.com>
   8 */
   9
  10#include <linux/module.h>
  11#include <linux/init.h>
  12#include <linux/platform_device.h>
  13#include <linux/slab.h>
  14#include <linux/dma-mapping.h>
  15#include <sound/core.h>
  16#include <sound/pcm.h>
  17#include <sound/pcm_params.h>
  18#include <sound/soc.h>
  19#include <asm/mach-au1x00/au1000.h>
  20#include <asm/mach-au1x00/au1000_dma.h>
  21
  22#include "psc.h"
  23
  24struct pcm_period {
  25        u32 start;
  26        u32 relative_end;       /* relative to start of buffer */
  27        struct pcm_period *next;
  28};
  29
  30struct audio_stream {
  31        struct snd_pcm_substream *substream;
  32        int dma;
  33        struct pcm_period *buffer;
  34        unsigned int period_size;
  35        unsigned int periods;
  36};
  37
  38struct alchemy_pcm_ctx {
  39        struct audio_stream stream[2];  /* playback & capture */
  40};
  41
  42static void au1000_release_dma_link(struct audio_stream *stream)
  43{
  44        struct pcm_period *pointer;
  45        struct pcm_period *pointer_next;
  46
  47        stream->period_size = 0;
  48        stream->periods = 0;
  49        pointer = stream->buffer;
  50        if (!pointer)
  51                return;
  52        do {
  53                pointer_next = pointer->next;
  54                kfree(pointer);
  55                pointer = pointer_next;
  56        } while (pointer != stream->buffer);
  57        stream->buffer = NULL;
  58}
  59
  60static int au1000_setup_dma_link(struct audio_stream *stream,
  61                                 unsigned int period_bytes,
  62                                 unsigned int periods)
  63{
  64        struct snd_pcm_substream *substream = stream->substream;
  65        struct snd_pcm_runtime *runtime = substream->runtime;
  66        struct pcm_period *pointer;
  67        unsigned long dma_start;
  68        int i;
  69
  70        dma_start = virt_to_phys(runtime->dma_area);
  71
  72        if (stream->period_size == period_bytes &&
  73            stream->periods == periods)
  74                return 0; /* not changed */
  75
  76        au1000_release_dma_link(stream);
  77
  78        stream->period_size = period_bytes;
  79        stream->periods = periods;
  80
  81        stream->buffer = kmalloc(sizeof(struct pcm_period), GFP_KERNEL);
  82        if (!stream->buffer)
  83                return -ENOMEM;
  84        pointer = stream->buffer;
  85        for (i = 0; i < periods; i++) {
  86                pointer->start = (u32)(dma_start + (i * period_bytes));
  87                pointer->relative_end = (u32) (((i+1) * period_bytes) - 0x1);
  88                if (i < periods - 1) {
  89                        pointer->next = kmalloc(sizeof(struct pcm_period),
  90                                                GFP_KERNEL);
  91                        if (!pointer->next) {
  92                                au1000_release_dma_link(stream);
  93                                return -ENOMEM;
  94                        }
  95                        pointer = pointer->next;
  96                }
  97        }
  98        pointer->next = stream->buffer;
  99        return 0;
 100}
 101
 102static void au1000_dma_stop(struct audio_stream *stream)
 103{
 104        if (stream->buffer)
 105                disable_dma(stream->dma);
 106}
 107
 108static void au1000_dma_start(struct audio_stream *stream)
 109{
 110        if (!stream->buffer)
 111                return;
 112
 113        init_dma(stream->dma);
 114        if (get_dma_active_buffer(stream->dma) == 0) {
 115                clear_dma_done0(stream->dma);
 116                set_dma_addr0(stream->dma, stream->buffer->start);
 117                set_dma_count0(stream->dma, stream->period_size >> 1);
 118                set_dma_addr1(stream->dma, stream->buffer->next->start);
 119                set_dma_count1(stream->dma, stream->period_size >> 1);
 120        } else {
 121                clear_dma_done1(stream->dma);
 122                set_dma_addr1(stream->dma, stream->buffer->start);
 123                set_dma_count1(stream->dma, stream->period_size >> 1);
 124                set_dma_addr0(stream->dma, stream->buffer->next->start);
 125                set_dma_count0(stream->dma, stream->period_size >> 1);
 126        }
 127        enable_dma_buffers(stream->dma);
 128        start_dma(stream->dma);
 129}
 130
 131static irqreturn_t au1000_dma_interrupt(int irq, void *ptr)
 132{
 133        struct audio_stream *stream = (struct audio_stream *)ptr;
 134        struct snd_pcm_substream *substream = stream->substream;
 135
 136        switch (get_dma_buffer_done(stream->dma)) {
 137        case DMA_D0:
 138                stream->buffer = stream->buffer->next;
 139                clear_dma_done0(stream->dma);
 140                set_dma_addr0(stream->dma, stream->buffer->next->start);
 141                set_dma_count0(stream->dma, stream->period_size >> 1);
 142                enable_dma_buffer0(stream->dma);
 143                break;
 144        case DMA_D1:
 145                stream->buffer = stream->buffer->next;
 146                clear_dma_done1(stream->dma);
 147                set_dma_addr1(stream->dma, stream->buffer->next->start);
 148                set_dma_count1(stream->dma, stream->period_size >> 1);
 149                enable_dma_buffer1(stream->dma);
 150                break;
 151        case (DMA_D0 | DMA_D1):
 152                pr_debug("DMA %d missed interrupt.\n", stream->dma);
 153                au1000_dma_stop(stream);
 154                au1000_dma_start(stream);
 155                break;
 156        case (~DMA_D0 & ~DMA_D1):
 157                pr_debug("DMA %d empty irq.\n", stream->dma);
 158        }
 159        snd_pcm_period_elapsed(substream);
 160        return IRQ_HANDLED;
 161}
 162
 163static const struct snd_pcm_hardware alchemy_pcm_hardware = {
 164        .info             = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
 165                            SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BATCH,
 166        .period_bytes_min = 1024,
 167        .period_bytes_max = 16 * 1024 - 1,
 168        .periods_min      = 4,
 169        .periods_max      = 255,
 170        .buffer_bytes_max = 128 * 1024,
 171        .fifo_size        = 16,
 172};
 173
 174static inline struct alchemy_pcm_ctx *ss_to_ctx(struct snd_pcm_substream *ss)
 175{
 176        struct snd_soc_pcm_runtime *rtd = ss->private_data;
 177        return snd_soc_platform_get_drvdata(rtd->platform);
 178}
 179
 180static inline struct audio_stream *ss_to_as(struct snd_pcm_substream *ss)
 181{
 182        struct alchemy_pcm_ctx *ctx = ss_to_ctx(ss);
 183        return &(ctx->stream[ss->stream]);
 184}
 185
 186static int alchemy_pcm_open(struct snd_pcm_substream *substream)
 187{
 188        struct alchemy_pcm_ctx *ctx = ss_to_ctx(substream);
 189        struct snd_soc_pcm_runtime *rtd = substream->private_data;
 190        int *dmaids, s = substream->stream;
 191        char *name;
 192
 193        dmaids = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
 194        if (!dmaids)
 195                return -ENODEV; /* whoa, has ordering changed? */
 196
 197        /* DMA setup */
 198        name = (s == SNDRV_PCM_STREAM_PLAYBACK) ? "audio-tx" : "audio-rx";
 199        ctx->stream[s].dma = request_au1000_dma(dmaids[s], name,
 200                                        au1000_dma_interrupt, 0,
 201                                        &ctx->stream[s]);
 202        set_dma_mode(ctx->stream[s].dma,
 203                     get_dma_mode(ctx->stream[s].dma) & ~DMA_NC);
 204
 205        ctx->stream[s].substream = substream;
 206        ctx->stream[s].buffer = NULL;
 207        snd_soc_set_runtime_hwparams(substream, &alchemy_pcm_hardware);
 208
 209        return 0;
 210}
 211
 212static int alchemy_pcm_close(struct snd_pcm_substream *substream)
 213{
 214        struct alchemy_pcm_ctx *ctx = ss_to_ctx(substream);
 215        int stype = substream->stream;
 216
 217        ctx->stream[stype].substream = NULL;
 218        free_au1000_dma(ctx->stream[stype].dma);
 219
 220        return 0;
 221}
 222
 223static int alchemy_pcm_hw_params(struct snd_pcm_substream *substream,
 224                                 struct snd_pcm_hw_params *hw_params)
 225{
 226        struct audio_stream *stream = ss_to_as(substream);
 227        int err;
 228
 229        err = snd_pcm_lib_malloc_pages(substream,
 230                                       params_buffer_bytes(hw_params));
 231        if (err < 0)
 232                return err;
 233        err = au1000_setup_dma_link(stream,
 234                                    params_period_bytes(hw_params),
 235                                    params_periods(hw_params));
 236        if (err)
 237                snd_pcm_lib_free_pages(substream);
 238
 239        return err;
 240}
 241
 242static int alchemy_pcm_hw_free(struct snd_pcm_substream *substream)
 243{
 244        struct audio_stream *stream = ss_to_as(substream);
 245        au1000_release_dma_link(stream);
 246        return snd_pcm_lib_free_pages(substream);
 247}
 248
 249static int alchemy_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
 250{
 251        struct audio_stream *stream = ss_to_as(substream);
 252        int err = 0;
 253
 254        switch (cmd) {
 255        case SNDRV_PCM_TRIGGER_START:
 256                au1000_dma_start(stream);
 257                break;
 258        case SNDRV_PCM_TRIGGER_STOP:
 259                au1000_dma_stop(stream);
 260                break;
 261        default:
 262                err = -EINVAL;
 263                break;
 264        }
 265        return err;
 266}
 267
 268static snd_pcm_uframes_t alchemy_pcm_pointer(struct snd_pcm_substream *ss)
 269{
 270        struct audio_stream *stream = ss_to_as(ss);
 271        long location;
 272
 273        location = get_dma_residue(stream->dma);
 274        location = stream->buffer->relative_end - location;
 275        if (location == -1)
 276                location = 0;
 277        return bytes_to_frames(ss->runtime, location);
 278}
 279
 280static struct snd_pcm_ops alchemy_pcm_ops = {
 281        .open                   = alchemy_pcm_open,
 282        .close                  = alchemy_pcm_close,
 283        .ioctl                  = snd_pcm_lib_ioctl,
 284        .hw_params              = alchemy_pcm_hw_params,
 285        .hw_free                = alchemy_pcm_hw_free,
 286        .trigger                = alchemy_pcm_trigger,
 287        .pointer                = alchemy_pcm_pointer,
 288};
 289
 290static int alchemy_pcm_new(struct snd_soc_pcm_runtime *rtd)
 291{
 292        struct snd_pcm *pcm = rtd->pcm;
 293
 294        snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS,
 295                snd_dma_continuous_data(GFP_KERNEL), 65536, (4096 * 1024) - 1);
 296
 297        return 0;
 298}
 299
 300static struct snd_soc_platform_driver alchemy_pcm_soc_platform = {
 301        .ops            = &alchemy_pcm_ops,
 302        .pcm_new        = alchemy_pcm_new,
 303};
 304
 305static int alchemy_pcm_drvprobe(struct platform_device *pdev)
 306{
 307        struct alchemy_pcm_ctx *ctx;
 308
 309        ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL);
 310        if (!ctx)
 311                return -ENOMEM;
 312
 313        platform_set_drvdata(pdev, ctx);
 314
 315        return devm_snd_soc_register_platform(&pdev->dev,
 316                                              &alchemy_pcm_soc_platform);
 317}
 318
 319static struct platform_driver alchemy_pcmdma_driver = {
 320        .driver = {
 321                .name   = "alchemy-pcm-dma",
 322        },
 323        .probe          = alchemy_pcm_drvprobe,
 324};
 325
 326module_platform_driver(alchemy_pcmdma_driver);
 327
 328MODULE_LICENSE("GPL");
 329MODULE_DESCRIPTION("Au1000/Au1500/Au1100 Audio DMA driver");
 330MODULE_AUTHOR("Manuel Lauss");
 331