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