linux/sound/soc/at91/at91-pcm.c
<<
>>
Prefs
   1/*
   2 * at91-pcm.c -- ALSA PCM interface for the Atmel AT91 SoC
   3 *
   4 * Author:      Frank Mandarino <fmandarino@endrelia.com>
   5 *              Endrelia Technologies Inc.
   6 * Created:     Mar 3, 2006
   7 *
   8 * Based on pxa2xx-pcm.c by:
   9 *
  10 * Author:      Nicolas Pitre
  11 * Created:     Nov 30, 2004
  12 * Copyright:   (C) 2004 MontaVista Software, Inc.
  13 *
  14 * This program is free software; you can redistribute it and/or modify
  15 * it under the terms of the GNU General Public License version 2 as
  16 * published by the Free Software Foundation.
  17 */
  18
  19#include <linux/module.h>
  20#include <linux/init.h>
  21#include <linux/platform_device.h>
  22#include <linux/slab.h>
  23#include <linux/dma-mapping.h>
  24#include <linux/atmel_pdc.h>
  25
  26#include <sound/core.h>
  27#include <sound/pcm.h>
  28#include <sound/pcm_params.h>
  29#include <sound/soc.h>
  30
  31#include <mach/hardware.h>
  32#include <mach/at91_ssc.h>
  33
  34#include "at91-pcm.h"
  35
  36#if 0
  37#define DBG(x...)       printk(KERN_INFO "at91-pcm: " x)
  38#else
  39#define DBG(x...)
  40#endif
  41
  42static const struct snd_pcm_hardware at91_pcm_hardware = {
  43        .info                   = SNDRV_PCM_INFO_MMAP |
  44                                  SNDRV_PCM_INFO_MMAP_VALID |
  45                                  SNDRV_PCM_INFO_INTERLEAVED |
  46                                  SNDRV_PCM_INFO_PAUSE,
  47        .formats                = SNDRV_PCM_FMTBIT_S16_LE,
  48        .period_bytes_min       = 32,
  49        .period_bytes_max       = 8192,
  50        .periods_min            = 2,
  51        .periods_max            = 1024,
  52        .buffer_bytes_max       = 32 * 1024,
  53};
  54
  55struct at91_runtime_data {
  56        struct at91_pcm_dma_params *params;
  57        dma_addr_t dma_buffer;                  /* physical address of dma buffer */
  58        dma_addr_t dma_buffer_end;              /* first address beyond DMA buffer */
  59        size_t period_size;
  60        dma_addr_t period_ptr;                  /* physical address of next period */
  61        u32 pdc_xpr_save;                       /* PDC register save */
  62        u32 pdc_xcr_save;
  63        u32 pdc_xnpr_save;
  64        u32 pdc_xncr_save;
  65};
  66
  67static void at91_pcm_dma_irq(u32 ssc_sr,
  68        struct snd_pcm_substream *substream)
  69{
  70        struct at91_runtime_data *prtd = substream->runtime->private_data;
  71        struct at91_pcm_dma_params *params = prtd->params;
  72        static int count = 0;
  73
  74        count++;
  75
  76        if (ssc_sr & params->mask->ssc_endbuf) {
  77
  78                printk(KERN_WARNING
  79                        "at91-pcm: buffer %s on %s (SSC_SR=%#x, count=%d)\n",
  80                        substream->stream == SNDRV_PCM_STREAM_PLAYBACK
  81                                ? "underrun" : "overrun",
  82                        params->name, ssc_sr, count);
  83
  84                /* re-start the PDC */
  85                at91_ssc_write(params->ssc_base + ATMEL_PDC_PTCR, params->mask->pdc_disable);
  86
  87                prtd->period_ptr += prtd->period_size;
  88                if (prtd->period_ptr >= prtd->dma_buffer_end) {
  89                        prtd->period_ptr = prtd->dma_buffer;
  90                }
  91
  92                at91_ssc_write(params->ssc_base + params->pdc->xpr, prtd->period_ptr);
  93                at91_ssc_write(params->ssc_base + params->pdc->xcr,
  94                                prtd->period_size / params->pdc_xfer_size);
  95
  96                at91_ssc_write(params->ssc_base + ATMEL_PDC_PTCR, params->mask->pdc_enable);
  97        }
  98
  99        if (ssc_sr & params->mask->ssc_endx) {
 100
 101                /* Load the PDC next pointer and counter registers */
 102                prtd->period_ptr += prtd->period_size;
 103                if (prtd->period_ptr >= prtd->dma_buffer_end) {
 104                        prtd->period_ptr = prtd->dma_buffer;
 105                }
 106                at91_ssc_write(params->ssc_base + params->pdc->xnpr,
 107                               prtd->period_ptr);
 108                at91_ssc_write(params->ssc_base + params->pdc->xncr,
 109                                prtd->period_size / params->pdc_xfer_size);
 110        }
 111
 112        snd_pcm_period_elapsed(substream);
 113}
 114
 115static int at91_pcm_hw_params(struct snd_pcm_substream *substream,
 116        struct snd_pcm_hw_params *params)
 117{
 118        struct snd_pcm_runtime *runtime = substream->runtime;
 119        struct at91_runtime_data *prtd = runtime->private_data;
 120        struct snd_soc_pcm_runtime *rtd = substream->private_data;
 121
 122        /* this may get called several times by oss emulation
 123         * with different params */
 124
 125        snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
 126        runtime->dma_bytes = params_buffer_bytes(params);
 127
 128        prtd->params = rtd->dai->cpu_dai->dma_data;
 129        prtd->params->dma_intr_handler = at91_pcm_dma_irq;
 130
 131        prtd->dma_buffer = runtime->dma_addr;
 132        prtd->dma_buffer_end = runtime->dma_addr + runtime->dma_bytes;
 133        prtd->period_size = params_period_bytes(params);
 134
 135        DBG("hw_params: DMA for %s initialized (dma_bytes=%d, period_size=%d)\n",
 136                prtd->params->name, runtime->dma_bytes, prtd->period_size);
 137        return 0;
 138}
 139
 140static int at91_pcm_hw_free(struct snd_pcm_substream *substream)
 141{
 142        struct at91_runtime_data *prtd = substream->runtime->private_data;
 143        struct at91_pcm_dma_params *params = prtd->params;
 144
 145        if (params != NULL) {
 146                at91_ssc_write(params->ssc_base + ATMEL_PDC_PTCR, params->mask->pdc_disable);
 147                prtd->params->dma_intr_handler = NULL;
 148        }
 149
 150        return 0;
 151}
 152
 153static int at91_pcm_prepare(struct snd_pcm_substream *substream)
 154{
 155        struct at91_runtime_data *prtd = substream->runtime->private_data;
 156        struct at91_pcm_dma_params *params = prtd->params;
 157
 158        at91_ssc_write(params->ssc_base + AT91_SSC_IDR,
 159                        params->mask->ssc_endx | params->mask->ssc_endbuf);
 160
 161        at91_ssc_write(params->ssc_base + ATMEL_PDC_PTCR, params->mask->pdc_disable);
 162        return 0;
 163}
 164
 165static int at91_pcm_trigger(struct snd_pcm_substream *substream,
 166        int cmd)
 167{
 168        struct at91_runtime_data *prtd = substream->runtime->private_data;
 169        struct at91_pcm_dma_params *params = prtd->params;
 170        int ret = 0;
 171
 172        switch (cmd) {
 173        case SNDRV_PCM_TRIGGER_START:
 174                prtd->period_ptr = prtd->dma_buffer;
 175
 176                at91_ssc_write(params->ssc_base + params->pdc->xpr, prtd->period_ptr);
 177                at91_ssc_write(params->ssc_base + params->pdc->xcr,
 178                                prtd->period_size / params->pdc_xfer_size);
 179
 180                prtd->period_ptr += prtd->period_size;
 181                at91_ssc_write(params->ssc_base + params->pdc->xnpr, prtd->period_ptr);
 182                at91_ssc_write(params->ssc_base + params->pdc->xncr,
 183                                prtd->period_size / params->pdc_xfer_size);
 184
 185                DBG("trigger: period_ptr=%lx, xpr=%lx, xcr=%ld, xnpr=%lx, xncr=%ld\n",
 186                        (unsigned long) prtd->period_ptr,
 187                        at91_ssc_read(params->ssc_base + params->pdc->xpr),
 188                        at91_ssc_read(params->ssc_base + params->pdc->xcr),
 189                        at91_ssc_read(params->ssc_base + params->pdc->xnpr),
 190                        at91_ssc_read(params->ssc_base + params->pdc->xncr));
 191
 192                at91_ssc_write(params->ssc_base + AT91_SSC_IER,
 193                        params->mask->ssc_endx | params->mask->ssc_endbuf);
 194
 195                at91_ssc_write(params->ssc_base + ATMEL_PDC_PTCR,
 196                        params->mask->pdc_enable);
 197
 198                DBG("sr=%lx imr=%lx\n",
 199                    at91_ssc_read(params->ssc_base + AT91_SSC_SR),
 200                    at91_ssc_read(params->ssc_base + AT91_SSC_IMR));
 201                break;
 202
 203        case SNDRV_PCM_TRIGGER_STOP:
 204        case SNDRV_PCM_TRIGGER_SUSPEND:
 205        case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
 206                at91_ssc_write(params->ssc_base + ATMEL_PDC_PTCR, params->mask->pdc_disable);
 207                break;
 208
 209        case SNDRV_PCM_TRIGGER_RESUME:
 210        case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
 211                at91_ssc_write(params->ssc_base + ATMEL_PDC_PTCR, params->mask->pdc_enable);
 212                break;
 213
 214        default:
 215                ret = -EINVAL;
 216        }
 217
 218        return ret;
 219}
 220
 221static snd_pcm_uframes_t at91_pcm_pointer(
 222        struct snd_pcm_substream *substream)
 223{
 224        struct snd_pcm_runtime *runtime = substream->runtime;
 225        struct at91_runtime_data *prtd = runtime->private_data;
 226        struct at91_pcm_dma_params *params = prtd->params;
 227        dma_addr_t ptr;
 228        snd_pcm_uframes_t x;
 229
 230        ptr = (dma_addr_t) at91_ssc_read(params->ssc_base + params->pdc->xpr);
 231        x = bytes_to_frames(runtime, ptr - prtd->dma_buffer);
 232
 233        if (x == runtime->buffer_size)
 234                x = 0;
 235        return x;
 236}
 237
 238static int at91_pcm_open(struct snd_pcm_substream *substream)
 239{
 240        struct snd_pcm_runtime *runtime = substream->runtime;
 241        struct at91_runtime_data *prtd;
 242        int ret = 0;
 243
 244        snd_soc_set_runtime_hwparams(substream, &at91_pcm_hardware);
 245
 246        /* ensure that buffer size is a multiple of period size */
 247        ret = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
 248        if (ret < 0)
 249                goto out;
 250
 251        prtd = kzalloc(sizeof(struct at91_runtime_data), GFP_KERNEL);
 252        if (prtd == NULL) {
 253                ret = -ENOMEM;
 254                goto out;
 255        }
 256        runtime->private_data = prtd;
 257
 258 out:
 259        return ret;
 260}
 261
 262static int at91_pcm_close(struct snd_pcm_substream *substream)
 263{
 264        struct at91_runtime_data *prtd = substream->runtime->private_data;
 265
 266        kfree(prtd);
 267        return 0;
 268}
 269
 270static int at91_pcm_mmap(struct snd_pcm_substream *substream,
 271        struct vm_area_struct *vma)
 272{
 273        struct snd_pcm_runtime *runtime = substream->runtime;
 274
 275        return dma_mmap_writecombine(substream->pcm->card->dev, vma,
 276                                     runtime->dma_area,
 277                                     runtime->dma_addr,
 278                                     runtime->dma_bytes);
 279}
 280
 281struct snd_pcm_ops at91_pcm_ops = {
 282        .open           = at91_pcm_open,
 283        .close          = at91_pcm_close,
 284        .ioctl          = snd_pcm_lib_ioctl,
 285        .hw_params      = at91_pcm_hw_params,
 286        .hw_free        = at91_pcm_hw_free,
 287        .prepare        = at91_pcm_prepare,
 288        .trigger        = at91_pcm_trigger,
 289        .pointer        = at91_pcm_pointer,
 290        .mmap           = at91_pcm_mmap,
 291};
 292
 293static int at91_pcm_preallocate_dma_buffer(struct snd_pcm *pcm,
 294        int stream)
 295{
 296        struct snd_pcm_substream *substream = pcm->streams[stream].substream;
 297        struct snd_dma_buffer *buf = &substream->dma_buffer;
 298        size_t size = at91_pcm_hardware.buffer_bytes_max;
 299
 300        buf->dev.type = SNDRV_DMA_TYPE_DEV;
 301        buf->dev.dev = pcm->card->dev;
 302        buf->private_data = NULL;
 303        buf->area = dma_alloc_writecombine(pcm->card->dev, size,
 304                                           &buf->addr, GFP_KERNEL);
 305
 306        DBG("preallocate_dma_buffer: area=%p, addr=%p, size=%d\n",
 307                (void *) buf->area,
 308                (void *) buf->addr,
 309                size);
 310
 311        if (!buf->area)
 312                return -ENOMEM;
 313
 314        buf->bytes = size;
 315        return 0;
 316}
 317
 318static u64 at91_pcm_dmamask = 0xffffffff;
 319
 320static int at91_pcm_new(struct snd_card *card,
 321        struct snd_soc_dai *dai, struct snd_pcm *pcm)
 322{
 323        int ret = 0;
 324
 325        if (!card->dev->dma_mask)
 326                card->dev->dma_mask = &at91_pcm_dmamask;
 327        if (!card->dev->coherent_dma_mask)
 328                card->dev->coherent_dma_mask = 0xffffffff;
 329
 330        if (dai->playback.channels_min) {
 331                ret = at91_pcm_preallocate_dma_buffer(pcm,
 332                        SNDRV_PCM_STREAM_PLAYBACK);
 333                if (ret)
 334                        goto out;
 335        }
 336
 337        if (dai->capture.channels_min) {
 338                ret = at91_pcm_preallocate_dma_buffer(pcm,
 339                        SNDRV_PCM_STREAM_CAPTURE);
 340                if (ret)
 341                        goto out;
 342        }
 343 out:
 344        return ret;
 345}
 346
 347static void at91_pcm_free_dma_buffers(struct snd_pcm *pcm)
 348{
 349        struct snd_pcm_substream *substream;
 350        struct snd_dma_buffer *buf;
 351        int stream;
 352
 353        for (stream = 0; stream < 2; stream++) {
 354                substream = pcm->streams[stream].substream;
 355                if (!substream)
 356                        continue;
 357
 358                buf = &substream->dma_buffer;
 359                if (!buf->area)
 360                        continue;
 361
 362                dma_free_writecombine(pcm->card->dev, buf->bytes,
 363                                      buf->area, buf->addr);
 364                buf->area = NULL;
 365        }
 366}
 367
 368#ifdef CONFIG_PM
 369static int at91_pcm_suspend(struct platform_device *pdev,
 370        struct snd_soc_dai *dai)
 371{
 372        struct snd_pcm_runtime *runtime = dai->runtime;
 373        struct at91_runtime_data *prtd;
 374        struct at91_pcm_dma_params *params;
 375
 376        if (!runtime)
 377                return 0;
 378
 379        prtd = runtime->private_data;
 380        params = prtd->params;
 381
 382        /* disable the PDC and save the PDC registers */
 383
 384        at91_ssc_write(params->ssc_base + ATMEL_PDC_PTCR, params->mask->pdc_disable);
 385
 386        prtd->pdc_xpr_save  = at91_ssc_read(params->ssc_base + params->pdc->xpr);
 387        prtd->pdc_xcr_save  = at91_ssc_read(params->ssc_base + params->pdc->xcr);
 388        prtd->pdc_xnpr_save = at91_ssc_read(params->ssc_base + params->pdc->xnpr);
 389        prtd->pdc_xncr_save = at91_ssc_read(params->ssc_base + params->pdc->xncr);
 390
 391        return 0;
 392}
 393
 394static int at91_pcm_resume(struct platform_device *pdev,
 395        struct snd_soc_dai *dai)
 396{
 397        struct snd_pcm_runtime *runtime = dai->runtime;
 398        struct at91_runtime_data *prtd;
 399        struct at91_pcm_dma_params *params;
 400
 401        if (!runtime)
 402                return 0;
 403
 404        prtd = runtime->private_data;
 405        params = prtd->params;
 406
 407        /* restore the PDC registers and enable the PDC */
 408        at91_ssc_write(params->ssc_base + params->pdc->xpr,  prtd->pdc_xpr_save);
 409        at91_ssc_write(params->ssc_base + params->pdc->xcr,  prtd->pdc_xcr_save);
 410        at91_ssc_write(params->ssc_base + params->pdc->xnpr, prtd->pdc_xnpr_save);
 411        at91_ssc_write(params->ssc_base + params->pdc->xncr, prtd->pdc_xncr_save);
 412
 413        at91_ssc_write(params->ssc_base + ATMEL_PDC_PTCR, params->mask->pdc_enable);
 414        return 0;
 415}
 416#else
 417#define at91_pcm_suspend        NULL
 418#define at91_pcm_resume         NULL
 419#endif
 420
 421struct snd_soc_platform at91_soc_platform = {
 422        .name           = "at91-audio",
 423        .pcm_ops        = &at91_pcm_ops,
 424        .pcm_new        = at91_pcm_new,
 425        .pcm_free       = at91_pcm_free_dma_buffers,
 426        .suspend        = at91_pcm_suspend,
 427        .resume         = at91_pcm_resume,
 428};
 429
 430EXPORT_SYMBOL_GPL(at91_soc_platform);
 431
 432MODULE_AUTHOR("Frank Mandarino <fmandarino@endrelia.com>");
 433MODULE_DESCRIPTION("Atmel AT91 PCM module");
 434MODULE_LICENSE("GPL");
 435
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.