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