linux/sound/soc/atmel/atmel-pcm-pdc.c
<<
>>
Prefs
   1/*
   2 * atmel-pcm.c  --  ALSA PCM interface for the Atmel atmel SoC.
   3 *
   4 *  Copyright (C) 2005 SAN People
   5 *  Copyright (C) 2008 Atmel
   6 *
   7 * Authors: Sedji Gaouaou <sedji.gaouaou@atmel.com>
   8 *
   9 * Based on at91-pcm. by:
  10 * Frank Mandarino <fmandarino@endrelia.com>
  11 * Copyright 2006 Endrelia Technologies Inc.
  12 *
  13 * Based on pxa2xx-pcm.c by:
  14 *
  15 * Author:      Nicolas Pitre
  16 * Created:     Nov 30, 2004
  17 * Copyright:   (C) 2004 MontaVista Software, Inc.
  18 *
  19 * This program is free software; you can redistribute it and/or modify
  20 * it under the terms of the GNU General Public License as published by
  21 * the Free Software Foundation; either version 2 of the License, or
  22 * (at your option) any later version.
  23 *
  24 * This program is distributed in the hope that it will be useful,
  25 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  26 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  27 * GNU General Public License for more details.
  28 *
  29 * You should have received a copy of the GNU General Public License
  30 * along with this program; if not, write to the Free Software
  31 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  32 */
  33
  34#include <linux/module.h>
  35#include <linux/init.h>
  36#include <linux/platform_device.h>
  37#include <linux/slab.h>
  38#include <linux/dma-mapping.h>
  39#include <linux/atmel_pdc.h>
  40#include <linux/atmel-ssc.h>
  41
  42#include <sound/core.h>
  43#include <sound/pcm.h>
  44#include <sound/pcm_params.h>
  45#include <sound/soc.h>
  46
  47#include "atmel-pcm.h"
  48
  49
  50static int atmel_pcm_preallocate_dma_buffer(struct snd_pcm *pcm,
  51        int stream)
  52{
  53        struct snd_pcm_substream *substream = pcm->streams[stream].substream;
  54        struct snd_dma_buffer *buf = &substream->dma_buffer;
  55        size_t size = ATMEL_SSC_DMABUF_SIZE;
  56
  57        buf->dev.type = SNDRV_DMA_TYPE_DEV;
  58        buf->dev.dev = pcm->card->dev;
  59        buf->private_data = NULL;
  60        buf->area = dma_alloc_coherent(pcm->card->dev, size,
  61                        &buf->addr, GFP_KERNEL);
  62        pr_debug("atmel-pcm: alloc dma buffer: area=%p, addr=%p, size=%zu\n",
  63                        (void *)buf->area, (void *)(long)buf->addr, size);
  64
  65        if (!buf->area)
  66                return -ENOMEM;
  67
  68        buf->bytes = size;
  69        return 0;
  70}
  71
  72static int atmel_pcm_mmap(struct snd_pcm_substream *substream,
  73        struct vm_area_struct *vma)
  74{
  75        return remap_pfn_range(vma, vma->vm_start,
  76                       substream->dma_buffer.addr >> PAGE_SHIFT,
  77                       vma->vm_end - vma->vm_start, vma->vm_page_prot);
  78}
  79
  80static int atmel_pcm_new(struct snd_soc_pcm_runtime *rtd)
  81{
  82        struct snd_card *card = rtd->card->snd_card;
  83        struct snd_pcm *pcm = rtd->pcm;
  84        int ret;
  85
  86        ret = dma_coerce_mask_and_coherent(card->dev, DMA_BIT_MASK(32));
  87        if (ret)
  88                return ret;
  89
  90        if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) {
  91                pr_debug("atmel-pcm: allocating PCM playback DMA buffer\n");
  92                ret = atmel_pcm_preallocate_dma_buffer(pcm,
  93                        SNDRV_PCM_STREAM_PLAYBACK);
  94                if (ret)
  95                        goto out;
  96        }
  97
  98        if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) {
  99                pr_debug("atmel-pcm: allocating PCM capture DMA buffer\n");
 100                ret = atmel_pcm_preallocate_dma_buffer(pcm,
 101                        SNDRV_PCM_STREAM_CAPTURE);
 102                if (ret)
 103                        goto out;
 104        }
 105 out:
 106        return ret;
 107}
 108
 109static void atmel_pcm_free(struct snd_pcm *pcm)
 110{
 111        struct snd_pcm_substream *substream;
 112        struct snd_dma_buffer *buf;
 113        int stream;
 114
 115        for (stream = 0; stream < 2; stream++) {
 116                substream = pcm->streams[stream].substream;
 117                if (!substream)
 118                        continue;
 119
 120                buf = &substream->dma_buffer;
 121                if (!buf->area)
 122                        continue;
 123                dma_free_coherent(pcm->card->dev, buf->bytes,
 124                                  buf->area, buf->addr);
 125                buf->area = NULL;
 126        }
 127}
 128
 129/*--------------------------------------------------------------------------*\
 130 * Hardware definition
 131\*--------------------------------------------------------------------------*/
 132/* TODO: These values were taken from the AT91 platform driver, check
 133 *       them against real values for AT32
 134 */
 135static const struct snd_pcm_hardware atmel_pcm_hardware = {
 136        .info                   = SNDRV_PCM_INFO_MMAP |
 137                                  SNDRV_PCM_INFO_MMAP_VALID |
 138                                  SNDRV_PCM_INFO_INTERLEAVED |
 139                                  SNDRV_PCM_INFO_PAUSE,
 140        .period_bytes_min       = 32,
 141        .period_bytes_max       = 8192,
 142        .periods_min            = 2,
 143        .periods_max            = 1024,
 144        .buffer_bytes_max       = ATMEL_SSC_DMABUF_SIZE,
 145};
 146
 147
 148/*--------------------------------------------------------------------------*\
 149 * Data types
 150\*--------------------------------------------------------------------------*/
 151struct atmel_runtime_data {
 152        struct atmel_pcm_dma_params *params;
 153        dma_addr_t dma_buffer;          /* physical address of dma buffer */
 154        dma_addr_t dma_buffer_end;      /* first address beyond DMA buffer */
 155        size_t period_size;
 156
 157        dma_addr_t period_ptr;          /* physical address of next period */
 158};
 159
 160/*--------------------------------------------------------------------------*\
 161 * ISR
 162\*--------------------------------------------------------------------------*/
 163static void atmel_pcm_dma_irq(u32 ssc_sr,
 164        struct snd_pcm_substream *substream)
 165{
 166        struct atmel_runtime_data *prtd = substream->runtime->private_data;
 167        struct atmel_pcm_dma_params *params = prtd->params;
 168        static int count;
 169
 170        count++;
 171
 172        if (ssc_sr & params->mask->ssc_endbuf) {
 173                pr_warn("atmel-pcm: buffer %s on %s (SSC_SR=%#x, count=%d)\n",
 174                                substream->stream == SNDRV_PCM_STREAM_PLAYBACK
 175                                ? "underrun" : "overrun",
 176                                params->name, ssc_sr, count);
 177
 178                /* re-start the PDC */
 179                ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR,
 180                           params->mask->pdc_disable);
 181                prtd->period_ptr += prtd->period_size;
 182                if (prtd->period_ptr >= prtd->dma_buffer_end)
 183                        prtd->period_ptr = prtd->dma_buffer;
 184
 185                ssc_writex(params->ssc->regs, params->pdc->xpr,
 186                           prtd->period_ptr);
 187                ssc_writex(params->ssc->regs, params->pdc->xcr,
 188                           prtd->period_size / params->pdc_xfer_size);
 189                ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR,
 190                           params->mask->pdc_enable);
 191        }
 192
 193        if (ssc_sr & params->mask->ssc_endx) {
 194                /* Load the PDC next pointer and counter registers */
 195                prtd->period_ptr += prtd->period_size;
 196                if (prtd->period_ptr >= prtd->dma_buffer_end)
 197                        prtd->period_ptr = prtd->dma_buffer;
 198
 199                ssc_writex(params->ssc->regs, params->pdc->xnpr,
 200                           prtd->period_ptr);
 201                ssc_writex(params->ssc->regs, params->pdc->xncr,
 202                           prtd->period_size / params->pdc_xfer_size);
 203        }
 204
 205        snd_pcm_period_elapsed(substream);
 206}
 207
 208
 209/*--------------------------------------------------------------------------*\
 210 * PCM operations
 211\*--------------------------------------------------------------------------*/
 212static int atmel_pcm_hw_params(struct snd_pcm_substream *substream,
 213        struct snd_pcm_hw_params *params)
 214{
 215        struct snd_pcm_runtime *runtime = substream->runtime;
 216        struct atmel_runtime_data *prtd = runtime->private_data;
 217        struct snd_soc_pcm_runtime *rtd = substream->private_data;
 218
 219        /* this may get called several times by oss emulation
 220         * with different params */
 221
 222        snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
 223        runtime->dma_bytes = params_buffer_bytes(params);
 224
 225        prtd->params = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
 226        prtd->params->dma_intr_handler = atmel_pcm_dma_irq;
 227
 228        prtd->dma_buffer = runtime->dma_addr;
 229        prtd->dma_buffer_end = runtime->dma_addr + runtime->dma_bytes;
 230        prtd->period_size = params_period_bytes(params);
 231
 232        pr_debug("atmel-pcm: "
 233                "hw_params: DMA for %s initialized "
 234                "(dma_bytes=%zu, period_size=%zu)\n",
 235                prtd->params->name,
 236                runtime->dma_bytes,
 237                prtd->period_size);
 238        return 0;
 239}
 240
 241static int atmel_pcm_hw_free(struct snd_pcm_substream *substream)
 242{
 243        struct atmel_runtime_data *prtd = substream->runtime->private_data;
 244        struct atmel_pcm_dma_params *params = prtd->params;
 245
 246        if (params != NULL) {
 247                ssc_writex(params->ssc->regs, SSC_PDC_PTCR,
 248                           params->mask->pdc_disable);
 249                prtd->params->dma_intr_handler = NULL;
 250        }
 251
 252        return 0;
 253}
 254
 255static int atmel_pcm_prepare(struct snd_pcm_substream *substream)
 256{
 257        struct atmel_runtime_data *prtd = substream->runtime->private_data;
 258        struct atmel_pcm_dma_params *params = prtd->params;
 259
 260        ssc_writex(params->ssc->regs, SSC_IDR,
 261                   params->mask->ssc_endx | params->mask->ssc_endbuf);
 262        ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR,
 263                   params->mask->pdc_disable);
 264        return 0;
 265}
 266
 267static int atmel_pcm_trigger(struct snd_pcm_substream *substream,
 268        int cmd)
 269{
 270        struct snd_pcm_runtime *rtd = substream->runtime;
 271        struct atmel_runtime_data *prtd = rtd->private_data;
 272        struct atmel_pcm_dma_params *params = prtd->params;
 273        int ret = 0;
 274
 275        pr_debug("atmel-pcm:buffer_size = %ld,"
 276                "dma_area = %p, dma_bytes = %zu\n",
 277                rtd->buffer_size, rtd->dma_area, rtd->dma_bytes);
 278
 279        switch (cmd) {
 280        case SNDRV_PCM_TRIGGER_START:
 281                prtd->period_ptr = prtd->dma_buffer;
 282
 283                ssc_writex(params->ssc->regs, params->pdc->xpr,
 284                           prtd->period_ptr);
 285                ssc_writex(params->ssc->regs, params->pdc->xcr,
 286                           prtd->period_size / params->pdc_xfer_size);
 287
 288                prtd->period_ptr += prtd->period_size;
 289                ssc_writex(params->ssc->regs, params->pdc->xnpr,
 290                           prtd->period_ptr);
 291                ssc_writex(params->ssc->regs, params->pdc->xncr,
 292                           prtd->period_size / params->pdc_xfer_size);
 293
 294                pr_debug("atmel-pcm: trigger: "
 295                        "period_ptr=%lx, xpr=%u, "
 296                        "xcr=%u, xnpr=%u, xncr=%u\n",
 297                        (unsigned long)prtd->period_ptr,
 298                        ssc_readx(params->ssc->regs, params->pdc->xpr),
 299                        ssc_readx(params->ssc->regs, params->pdc->xcr),
 300                        ssc_readx(params->ssc->regs, params->pdc->xnpr),
 301                        ssc_readx(params->ssc->regs, params->pdc->xncr));
 302
 303                ssc_writex(params->ssc->regs, SSC_IER,
 304                           params->mask->ssc_endx | params->mask->ssc_endbuf);
 305                ssc_writex(params->ssc->regs, SSC_PDC_PTCR,
 306                           params->mask->pdc_enable);
 307
 308                pr_debug("sr=%u imr=%u\n",
 309                        ssc_readx(params->ssc->regs, SSC_SR),
 310                        ssc_readx(params->ssc->regs, SSC_IER));
 311                break;          /* SNDRV_PCM_TRIGGER_START */
 312
 313        case SNDRV_PCM_TRIGGER_STOP:
 314        case SNDRV_PCM_TRIGGER_SUSPEND:
 315        case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
 316                ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR,
 317                           params->mask->pdc_disable);
 318                break;
 319
 320        case SNDRV_PCM_TRIGGER_RESUME:
 321        case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
 322                ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR,
 323                           params->mask->pdc_enable);
 324                break;
 325
 326        default:
 327                ret = -EINVAL;
 328        }
 329
 330        return ret;
 331}
 332
 333static snd_pcm_uframes_t atmel_pcm_pointer(
 334        struct snd_pcm_substream *substream)
 335{
 336        struct snd_pcm_runtime *runtime = substream->runtime;
 337        struct atmel_runtime_data *prtd = runtime->private_data;
 338        struct atmel_pcm_dma_params *params = prtd->params;
 339        dma_addr_t ptr;
 340        snd_pcm_uframes_t x;
 341
 342        ptr = (dma_addr_t) ssc_readx(params->ssc->regs, params->pdc->xpr);
 343        x = bytes_to_frames(runtime, ptr - prtd->dma_buffer);
 344
 345        if (x == runtime->buffer_size)
 346                x = 0;
 347
 348        return x;
 349}
 350
 351static int atmel_pcm_open(struct snd_pcm_substream *substream)
 352{
 353        struct snd_pcm_runtime *runtime = substream->runtime;
 354        struct atmel_runtime_data *prtd;
 355        int ret = 0;
 356
 357        snd_soc_set_runtime_hwparams(substream, &atmel_pcm_hardware);
 358
 359        /* ensure that buffer size is a multiple of period size */
 360        ret = snd_pcm_hw_constraint_integer(runtime,
 361                                                SNDRV_PCM_HW_PARAM_PERIODS);
 362        if (ret < 0)
 363                goto out;
 364
 365        prtd = kzalloc(sizeof(struct atmel_runtime_data), GFP_KERNEL);
 366        if (prtd == NULL) {
 367                ret = -ENOMEM;
 368                goto out;
 369        }
 370        runtime->private_data = prtd;
 371
 372 out:
 373        return ret;
 374}
 375
 376static int atmel_pcm_close(struct snd_pcm_substream *substream)
 377{
 378        struct atmel_runtime_data *prtd = substream->runtime->private_data;
 379
 380        kfree(prtd);
 381        return 0;
 382}
 383
 384static struct snd_pcm_ops atmel_pcm_ops = {
 385        .open           = atmel_pcm_open,
 386        .close          = atmel_pcm_close,
 387        .ioctl          = snd_pcm_lib_ioctl,
 388        .hw_params      = atmel_pcm_hw_params,
 389        .hw_free        = atmel_pcm_hw_free,
 390        .prepare        = atmel_pcm_prepare,
 391        .trigger        = atmel_pcm_trigger,
 392        .pointer        = atmel_pcm_pointer,
 393        .mmap           = atmel_pcm_mmap,
 394};
 395
 396static struct snd_soc_platform_driver atmel_soc_platform = {
 397        .ops            = &atmel_pcm_ops,
 398        .pcm_new        = atmel_pcm_new,
 399        .pcm_free       = atmel_pcm_free,
 400};
 401
 402int atmel_pcm_pdc_platform_register(struct device *dev)
 403{
 404        return snd_soc_register_platform(dev, &atmel_soc_platform);
 405}
 406EXPORT_SYMBOL(atmel_pcm_pdc_platform_register);
 407
 408void atmel_pcm_pdc_platform_unregister(struct device *dev)
 409{
 410        snd_soc_unregister_platform(dev);
 411}
 412EXPORT_SYMBOL(atmel_pcm_pdc_platform_unregister);
 413
 414MODULE_AUTHOR("Sedji Gaouaou <sedji.gaouaou@atmel.com>");
 415MODULE_DESCRIPTION("Atmel PCM module");
 416MODULE_LICENSE("GPL");
 417