linux/sound/soc/at32/at32-pcm.c
<<
>>
Prefs
   1/* sound/soc/at32/at32-pcm.c
   2 * ASoC PCM interface for Atmel AT32 SoC
   3 *
   4 * Copyright (C) 2008 Long Range Systems
   5 *    Geoffrey Wossum <gwossum@acm.org>
   6 *
   7 * This program is free software; you can redistribute it and/or modify
   8 * it under the terms of the GNU General Public License version 2 as
   9 * published by the Free Software Foundation.
  10 *
  11 * Note that this is basically a port of the sound/soc/at91-pcm.c to
  12 * the AVR32 kernel.  Thanks to Frank Mandarino for that code.
  13 */
  14
  15#include <linux/module.h>
  16#include <linux/init.h>
  17#include <linux/platform_device.h>
  18#include <linux/slab.h>
  19#include <linux/dma-mapping.h>
  20#include <linux/atmel_pdc.h>
  21
  22#include <sound/core.h>
  23#include <sound/pcm.h>
  24#include <sound/pcm_params.h>
  25#include <sound/soc.h>
  26
  27#include "at32-pcm.h"
  28
  29
  30
  31/*--------------------------------------------------------------------------*\
  32 * Hardware definition
  33\*--------------------------------------------------------------------------*/
  34/* TODO: These values were taken from the AT91 platform driver, check
  35 *       them against real values for AT32
  36 */
  37static const struct snd_pcm_hardware at32_pcm_hardware = {
  38        .info = (SNDRV_PCM_INFO_MMAP |
  39                 SNDRV_PCM_INFO_MMAP_VALID |
  40                 SNDRV_PCM_INFO_INTERLEAVED |
  41                 SNDRV_PCM_INFO_BLOCK_TRANSFER |
  42                 SNDRV_PCM_INFO_PAUSE),
  43
  44        .formats = SNDRV_PCM_FMTBIT_S16,
  45        .period_bytes_min = 32,
  46        .period_bytes_max = 8192,       /* 512 frames * 16 bytes / frame */
  47        .periods_min = 2,
  48        .periods_max = 1024,
  49        .buffer_bytes_max = 32 * 1024,
  50};
  51
  52
  53
  54/*--------------------------------------------------------------------------*\
  55 * Data types
  56\*--------------------------------------------------------------------------*/
  57struct at32_runtime_data {
  58        struct at32_pcm_dma_params *params;
  59        dma_addr_t dma_buffer;  /* physical address of DMA buffer */
  60        dma_addr_t dma_buffer_end; /* first address beyond DMA buffer */
  61        size_t period_size;
  62
  63        dma_addr_t period_ptr;  /* physical address of next period */
  64        int periods;            /* period index of period_ptr */
  65
  66        /* Save PDC registers (for power management) */
  67        u32 pdc_xpr_save;
  68        u32 pdc_xcr_save;
  69        u32 pdc_xnpr_save;
  70        u32 pdc_xncr_save;
  71};
  72
  73
  74
  75/*--------------------------------------------------------------------------*\
  76 * Helper functions
  77\*--------------------------------------------------------------------------*/
  78static int at32_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
  79{
  80        struct snd_pcm_substream *substream = pcm->streams[stream].substream;
  81        struct snd_dma_buffer *dmabuf = &substream->dma_buffer;
  82        size_t size = at32_pcm_hardware.buffer_bytes_max;
  83
  84        dmabuf->dev.type = SNDRV_DMA_TYPE_DEV;
  85        dmabuf->dev.dev = pcm->card->dev;
  86        dmabuf->private_data = NULL;
  87        dmabuf->area = dma_alloc_coherent(pcm->card->dev, size,
  88                                          &dmabuf->addr, GFP_KERNEL);
  89        pr_debug("at32_pcm: preallocate_dma_buffer: "
  90                 "area=%p, addr=%p, size=%ld\n",
  91                 (void *)dmabuf->area, (void *)dmabuf->addr, size);
  92
  93        if (!dmabuf->area)
  94                return -ENOMEM;
  95
  96        dmabuf->bytes = size;
  97        return 0;
  98}
  99
 100
 101
 102/*--------------------------------------------------------------------------*\
 103 * ISR
 104\*--------------------------------------------------------------------------*/
 105static void at32_pcm_dma_irq(u32 ssc_sr, struct snd_pcm_substream *substream)
 106{
 107        struct snd_pcm_runtime *rtd = substream->runtime;
 108        struct at32_runtime_data *prtd = rtd->private_data;
 109        struct at32_pcm_dma_params *params = prtd->params;
 110        static int count;
 111
 112        count++;
 113        if (ssc_sr & params->mask->ssc_endbuf) {
 114                pr_warning("at32-pcm: buffer %s on %s (SSC_SR=%#x, count=%d)\n",
 115                           substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?
 116                           "underrun" : "overrun", params->name, ssc_sr, count);
 117
 118                /* re-start the PDC */
 119                ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR,
 120                           params->mask->pdc_disable);
 121                prtd->period_ptr += prtd->period_size;
 122                if (prtd->period_ptr >= prtd->dma_buffer_end)
 123                        prtd->period_ptr = prtd->dma_buffer;
 124
 125
 126                ssc_writex(params->ssc->regs, params->pdc->xpr,
 127                           prtd->period_ptr);
 128                ssc_writex(params->ssc->regs, params->pdc->xcr,
 129                           prtd->period_size / params->pdc_xfer_size);
 130                ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR,
 131                           params->mask->pdc_enable);
 132        }
 133
 134
 135        if (ssc_sr & params->mask->ssc_endx) {
 136                /* Load the PDC next pointer and counter registers */
 137                prtd->period_ptr += prtd->period_size;
 138                if (prtd->period_ptr >= prtd->dma_buffer_end)
 139                        prtd->period_ptr = prtd->dma_buffer;
 140                ssc_writex(params->ssc->regs, params->pdc->xnpr,
 141                           prtd->period_ptr);
 142                ssc_writex(params->ssc->regs, params->pdc->xncr,
 143                           prtd->period_size / params->pdc_xfer_size);
 144        }
 145
 146
 147        snd_pcm_period_elapsed(substream);
 148}
 149
 150
 151
 152/*--------------------------------------------------------------------------*\
 153 * PCM operations
 154\*--------------------------------------------------------------------------*/
 155static int at32_pcm_hw_params(struct snd_pcm_substream *substream,
 156                              struct snd_pcm_hw_params *params)
 157{
 158        struct snd_pcm_runtime *runtime = substream->runtime;
 159        struct at32_runtime_data *prtd = runtime->private_data;
 160        struct snd_soc_pcm_runtime *rtd = substream->private_data;
 161
 162        /* this may get called several times by oss emulation
 163         * with different params
 164         */
 165        snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
 166        runtime->dma_bytes = params_buffer_bytes(params);
 167
 168        prtd->params = rtd->dai->cpu_dai->dma_data;
 169        prtd->params->dma_intr_handler = at32_pcm_dma_irq;
 170
 171        prtd->dma_buffer = runtime->dma_addr;
 172        prtd->dma_buffer_end = runtime->dma_addr + runtime->dma_bytes;
 173        prtd->period_size = params_period_bytes(params);
 174
 175        pr_debug("hw_params: DMA for %s initialized "
 176                 "(dma_bytes=%ld, period_size=%ld)\n",
 177                 prtd->params->name, runtime->dma_bytes, prtd->period_size);
 178
 179        return 0;
 180}
 181
 182
 183
 184static int at32_pcm_hw_free(struct snd_pcm_substream *substream)
 185{
 186        struct at32_runtime_data *prtd = substream->runtime->private_data;
 187        struct at32_pcm_dma_params *params = prtd->params;
 188
 189        if (params != NULL) {
 190                ssc_writex(params->ssc->regs, SSC_PDC_PTCR,
 191                           params->mask->pdc_disable);
 192                prtd->params->dma_intr_handler = NULL;
 193        }
 194
 195        return 0;
 196}
 197
 198
 199
 200static int at32_pcm_prepare(struct snd_pcm_substream *substream)
 201{
 202        struct at32_runtime_data *prtd = substream->runtime->private_data;
 203        struct at32_pcm_dma_params *params = prtd->params;
 204
 205        ssc_writex(params->ssc->regs, SSC_IDR,
 206                   params->mask->ssc_endx | params->mask->ssc_endbuf);
 207        ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR,
 208                   params->mask->pdc_disable);
 209
 210        return 0;
 211}
 212
 213
 214static int at32_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
 215{
 216        struct snd_pcm_runtime *rtd = substream->runtime;
 217        struct at32_runtime_data *prtd = rtd->private_data;
 218        struct at32_pcm_dma_params *params = prtd->params;
 219        int ret = 0;
 220
 221        pr_debug("at32_pcm_trigger: buffer_size = %ld, "
 222                 "dma_area = %p, dma_bytes = %ld\n",
 223                 rtd->buffer_size, rtd->dma_area, rtd->dma_bytes);
 224
 225        switch (cmd) {
 226        case SNDRV_PCM_TRIGGER_START:
 227                prtd->period_ptr = prtd->dma_buffer;
 228
 229                ssc_writex(params->ssc->regs, params->pdc->xpr,
 230                           prtd->period_ptr);
 231                ssc_writex(params->ssc->regs, params->pdc->xcr,
 232                           prtd->period_size / params->pdc_xfer_size);
 233
 234                prtd->period_ptr += prtd->period_size;
 235                ssc_writex(params->ssc->regs, params->pdc->xnpr,
 236                           prtd->period_ptr);
 237                ssc_writex(params->ssc->regs, params->pdc->xncr,
 238                           prtd->period_size / params->pdc_xfer_size);
 239
 240                pr_debug("trigger: period_ptr=%lx, xpr=%x, "
 241                         "xcr=%d, xnpr=%x, xncr=%d\n",
 242                         (unsigned long)prtd->period_ptr,
 243                         ssc_readx(params->ssc->regs, params->pdc->xpr),
 244                         ssc_readx(params->ssc->regs, params->pdc->xcr),
 245                         ssc_readx(params->ssc->regs, params->pdc->xnpr),
 246                         ssc_readx(params->ssc->regs, params->pdc->xncr));
 247
 248                ssc_writex(params->ssc->regs, SSC_IER,
 249                           params->mask->ssc_endx | params->mask->ssc_endbuf);
 250                ssc_writex(params->ssc->regs, SSC_PDC_PTCR,
 251                           params->mask->pdc_enable);
 252
 253                pr_debug("sr=%x, imr=%x\n",
 254                         ssc_readx(params->ssc->regs, SSC_SR),
 255                         ssc_readx(params->ssc->regs, SSC_IER));
 256                break;          /* SNDRV_PCM_TRIGGER_START */
 257
 258
 259
 260        case SNDRV_PCM_TRIGGER_STOP:
 261        case SNDRV_PCM_TRIGGER_SUSPEND:
 262        case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
 263                ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR,
 264                           params->mask->pdc_disable);
 265                break;
 266
 267
 268        case SNDRV_PCM_TRIGGER_RESUME:
 269        case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
 270                ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR,
 271                           params->mask->pdc_enable);
 272                break;
 273
 274        default:
 275                ret = -EINVAL;
 276        }
 277
 278        return ret;
 279}
 280
 281
 282
 283static snd_pcm_uframes_t at32_pcm_pointer(struct snd_pcm_substream *substream)
 284{
 285        struct snd_pcm_runtime *runtime = substream->runtime;
 286        struct at32_runtime_data *prtd = runtime->private_data;
 287        struct at32_pcm_dma_params *params = prtd->params;
 288        dma_addr_t ptr;
 289        snd_pcm_uframes_t x;
 290
 291        ptr = (dma_addr_t) ssc_readx(params->ssc->regs, params->pdc->xpr);
 292        x = bytes_to_frames(runtime, ptr - prtd->dma_buffer);
 293
 294        if (x == runtime->buffer_size)
 295                x = 0;
 296
 297        return x;
 298}
 299
 300
 301
 302static int at32_pcm_open(struct snd_pcm_substream *substream)
 303{
 304        struct snd_pcm_runtime *runtime = substream->runtime;
 305        struct at32_runtime_data *prtd;
 306        int ret = 0;
 307
 308        snd_soc_set_runtime_hwparams(substream, &at32_pcm_hardware);
 309
 310        /* ensure that buffer size is a multiple of period size */
 311        ret = snd_pcm_hw_constraint_integer(runtime,
 312                                            SNDRV_PCM_HW_PARAM_PERIODS);
 313        if (ret < 0)
 314                goto out;
 315
 316        prtd = kzalloc(sizeof(*prtd), GFP_KERNEL);
 317        if (prtd == NULL) {
 318                ret = -ENOMEM;
 319                goto out;
 320        }
 321        runtime->private_data = prtd;
 322
 323
 324out:
 325        return ret;
 326}
 327
 328
 329
 330static int at32_pcm_close(struct snd_pcm_substream *substream)
 331{
 332        struct at32_runtime_data *prtd = substream->runtime->private_data;
 333
 334        kfree(prtd);
 335        return 0;
 336}
 337
 338
 339static int at32_pcm_mmap(struct snd_pcm_substream *substream,
 340                         struct vm_area_struct *vma)
 341{
 342        return remap_pfn_range(vma, vma->vm_start,
 343                               substream->dma_buffer.addr >> PAGE_SHIFT,
 344                               vma->vm_end - vma->vm_start, vma->vm_page_prot);
 345}
 346
 347
 348
 349static struct snd_pcm_ops at32_pcm_ops = {
 350        .open = at32_pcm_open,
 351        .close = at32_pcm_close,
 352        .ioctl = snd_pcm_lib_ioctl,
 353        .hw_params = at32_pcm_hw_params,
 354        .hw_free = at32_pcm_hw_free,
 355        .prepare = at32_pcm_prepare,
 356        .trigger = at32_pcm_trigger,
 357        .pointer = at32_pcm_pointer,
 358        .mmap = at32_pcm_mmap,
 359};
 360
 361
 362
 363/*--------------------------------------------------------------------------*\
 364 * ASoC platform driver
 365\*--------------------------------------------------------------------------*/
 366static u64 at32_pcm_dmamask = 0xffffffff;
 367
 368static int at32_pcm_new(struct snd_card *card,
 369                        struct snd_soc_dai *dai,
 370                        struct snd_pcm *pcm)
 371{
 372        int ret = 0;
 373
 374        if (!card->dev->dma_mask)
 375                card->dev->dma_mask = &at32_pcm_dmamask;
 376        if (!card->dev->coherent_dma_mask)
 377                card->dev->coherent_dma_mask = 0xffffffff;
 378
 379        if (dai->playback.channels_min) {
 380                ret = at32_pcm_preallocate_dma_buffer(
 381                          pcm, SNDRV_PCM_STREAM_PLAYBACK);
 382                if (ret)
 383                        goto out;
 384        }
 385
 386        if (dai->capture.channels_min) {
 387                pr_debug("at32-pcm: Allocating PCM capture DMA buffer\n");
 388                ret = at32_pcm_preallocate_dma_buffer(
 389                          pcm, SNDRV_PCM_STREAM_CAPTURE);
 390                if (ret)
 391                        goto out;
 392        }
 393
 394
 395out:
 396        return ret;
 397}
 398
 399
 400
 401static void at32_pcm_free_dma_buffers(struct snd_pcm *pcm)
 402{
 403        struct snd_pcm_substream *substream;
 404        struct snd_dma_buffer *buf;
 405        int stream;
 406
 407        for (stream = 0; stream < 2; stream++) {
 408                substream = pcm->streams[stream].substream;
 409                if (substream == NULL)
 410                        continue;
 411
 412                buf = &substream->dma_buffer;
 413                if (!buf->area)
 414                        continue;
 415                dma_free_coherent(pcm->card->dev, buf->bytes,
 416                                  buf->area, buf->addr);
 417                buf->area = NULL;
 418        }
 419}
 420
 421
 422
 423#ifdef CONFIG_PM
 424static int at32_pcm_suspend(struct platform_device *pdev,
 425                            struct snd_soc_dai *dai)
 426{
 427        struct snd_pcm_runtime *runtime = dai->runtime;
 428        struct at32_runtime_data *prtd;
 429        struct at32_pcm_dma_params *params;
 430
 431        if (runtime == NULL)
 432                return 0;
 433        prtd = runtime->private_data;
 434        params = prtd->params;
 435
 436        /* Disable the PDC and save the PDC registers */
 437        ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR,
 438                   params->mask->pdc_disable);
 439
 440        prtd->pdc_xpr_save = ssc_readx(params->ssc->regs, params->pdc->xpr);
 441        prtd->pdc_xcr_save = ssc_readx(params->ssc->regs, params->pdc->xcr);
 442        prtd->pdc_xnpr_save = ssc_readx(params->ssc->regs, params->pdc->xnpr);
 443        prtd->pdc_xncr_save = ssc_readx(params->ssc->regs, params->pdc->xncr);
 444
 445        return 0;
 446}
 447
 448
 449
 450static int at32_pcm_resume(struct platform_device *pdev,
 451                           struct snd_soc_dai *dai)
 452{
 453        struct snd_pcm_runtime *runtime = dai->runtime;
 454        struct at32_runtime_data *prtd;
 455        struct at32_pcm_dma_params *params;
 456
 457        if (runtime == NULL)
 458                return 0;
 459        prtd = runtime->private_data;
 460        params = prtd->params;
 461
 462        /* Restore the PDC registers and enable the PDC */
 463        ssc_writex(params->ssc->regs, params->pdc->xpr, prtd->pdc_xpr_save);
 464        ssc_writex(params->ssc->regs, params->pdc->xcr, prtd->pdc_xcr_save);
 465        ssc_writex(params->ssc->regs, params->pdc->xnpr, prtd->pdc_xnpr_save);
 466        ssc_writex(params->ssc->regs, params->pdc->xncr, prtd->pdc_xncr_save);
 467
 468        ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR, params->mask->pdc_enable);
 469        return 0;
 470}
 471#else /* CONFIG_PM */
 472#  define at32_pcm_suspend      NULL
 473#  define at32_pcm_resume       NULL
 474#endif /* CONFIG_PM */
 475
 476
 477
 478struct snd_soc_platform at32_soc_platform = {
 479        .name = "at32-audio",
 480        .pcm_ops = &at32_pcm_ops,
 481        .pcm_new = at32_pcm_new,
 482        .pcm_free = at32_pcm_free_dma_buffers,
 483        .suspend = at32_pcm_suspend,
 484        .resume = at32_pcm_resume,
 485};
 486EXPORT_SYMBOL_GPL(at32_soc_platform);
 487
 488
 489
 490MODULE_AUTHOR("Geoffrey Wossum <gwossum@acm.org>");
 491MODULE_DESCRIPTION("Atmel AT32 PCM module");
 492MODULE_LICENSE("GPL");
 493
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.