linux/sound/soc/omap/omap-pcm.c
<<
>>
Prefs
   1/*
   2 * omap-pcm.c  --  ALSA PCM interface for the OMAP SoC
   3 *
   4 * Copyright (C) 2008 Nokia Corporation
   5 *
   6 * Contact: Jarkko Nikula <jhnikula@gmail.com>
   7 *          Peter Ujfalusi <peter.ujfalusi@ti.com>
   8 *
   9 * This program is free software; you can redistribute it and/or
  10 * modify it under the terms of the GNU General Public License
  11 * version 2 as published by the Free Software Foundation.
  12 *
  13 * This program is distributed in the hope that it will be useful, but
  14 * WITHOUT ANY WARRANTY; without even the implied warranty of
  15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  16 * General Public License for more details.
  17 *
  18 * You should have received a copy of the GNU General Public License
  19 * along with this program; if not, write to the Free Software
  20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
  21 * 02110-1301 USA
  22 *
  23 */
  24
  25#include <linux/dma-mapping.h>
  26#include <linux/slab.h>
  27#include <sound/core.h>
  28#include <sound/pcm.h>
  29#include <sound/pcm_params.h>
  30#include <sound/soc.h>
  31
  32#include <plat/dma.h>
  33#include "omap-pcm.h"
  34
  35static const struct snd_pcm_hardware omap_pcm_hardware = {
  36        .info                   = SNDRV_PCM_INFO_MMAP |
  37                                  SNDRV_PCM_INFO_MMAP_VALID |
  38                                  SNDRV_PCM_INFO_INTERLEAVED |
  39                                  SNDRV_PCM_INFO_PAUSE |
  40                                  SNDRV_PCM_INFO_RESUME |
  41                                  SNDRV_PCM_INFO_NO_PERIOD_WAKEUP,
  42        .formats                = SNDRV_PCM_FMTBIT_S16_LE |
  43                                  SNDRV_PCM_FMTBIT_S32_LE,
  44        .period_bytes_min       = 32,
  45        .period_bytes_max       = 64 * 1024,
  46        .periods_min            = 2,
  47        .periods_max            = 255,
  48        .buffer_bytes_max       = 128 * 1024,
  49};
  50
  51struct omap_runtime_data {
  52        spinlock_t                      lock;
  53        struct omap_pcm_dma_data        *dma_data;
  54        int                             dma_ch;
  55        int                             period_index;
  56};
  57
  58static void omap_pcm_dma_irq(int ch, u16 stat, void *data)
  59{
  60        struct snd_pcm_substream *substream = data;
  61        struct snd_pcm_runtime *runtime = substream->runtime;
  62        struct omap_runtime_data *prtd = runtime->private_data;
  63        unsigned long flags;
  64
  65        if ((cpu_is_omap1510())) {
  66                /*
  67                 * OMAP1510 doesn't fully support DMA progress counter
  68                 * and there is no software emulation implemented yet,
  69                 * so have to maintain our own progress counters
  70                 * that can be used by omap_pcm_pointer() instead.
  71                 */
  72                spin_lock_irqsave(&prtd->lock, flags);
  73                if ((stat == OMAP_DMA_LAST_IRQ) &&
  74                                (prtd->period_index == runtime->periods - 1)) {
  75                        /* we are in sync, do nothing */
  76                        spin_unlock_irqrestore(&prtd->lock, flags);
  77                        return;
  78                }
  79                if (prtd->period_index >= 0) {
  80                        if (stat & OMAP_DMA_BLOCK_IRQ) {
  81                                /* end of buffer reached, loop back */
  82                                prtd->period_index = 0;
  83                        } else if (stat & OMAP_DMA_LAST_IRQ) {
  84                                /* update the counter for the last period */
  85                                prtd->period_index = runtime->periods - 1;
  86                        } else if (++prtd->period_index >= runtime->periods) {
  87                                /* end of buffer missed? loop back */
  88                                prtd->period_index = 0;
  89                        }
  90                }
  91                spin_unlock_irqrestore(&prtd->lock, flags);
  92        }
  93
  94        snd_pcm_period_elapsed(substream);
  95}
  96
  97/* this may get called several times by oss emulation */
  98static int omap_pcm_hw_params(struct snd_pcm_substream *substream,
  99                              struct snd_pcm_hw_params *params)
 100{
 101        struct snd_pcm_runtime *runtime = substream->runtime;
 102        struct snd_soc_pcm_runtime *rtd = substream->private_data;
 103        struct omap_runtime_data *prtd = runtime->private_data;
 104        struct omap_pcm_dma_data *dma_data;
 105
 106        int err = 0;
 107
 108        dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
 109
 110        /* return if this is a bufferless transfer e.g.
 111         * codec <--> BT codec or GSM modem -- lg FIXME */
 112        if (!dma_data)
 113                return 0;
 114
 115        snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
 116        runtime->dma_bytes = params_buffer_bytes(params);
 117
 118        if (prtd->dma_data)
 119                return 0;
 120        prtd->dma_data = dma_data;
 121        err = omap_request_dma(dma_data->dma_req, dma_data->name,
 122                               omap_pcm_dma_irq, substream, &prtd->dma_ch);
 123        if (!err) {
 124                /*
 125                 * Link channel with itself so DMA doesn't need any
 126                 * reprogramming while looping the buffer
 127                 */
 128                omap_dma_link_lch(prtd->dma_ch, prtd->dma_ch);
 129        }
 130
 131        return err;
 132}
 133
 134static int omap_pcm_hw_free(struct snd_pcm_substream *substream)
 135{
 136        struct snd_pcm_runtime *runtime = substream->runtime;
 137        struct omap_runtime_data *prtd = runtime->private_data;
 138
 139        if (prtd->dma_data == NULL)
 140                return 0;
 141
 142        omap_dma_unlink_lch(prtd->dma_ch, prtd->dma_ch);
 143        omap_free_dma(prtd->dma_ch);
 144        prtd->dma_data = NULL;
 145
 146        snd_pcm_set_runtime_buffer(substream, NULL);
 147
 148        return 0;
 149}
 150
 151static int omap_pcm_prepare(struct snd_pcm_substream *substream)
 152{
 153        struct snd_pcm_runtime *runtime = substream->runtime;
 154        struct omap_runtime_data *prtd = runtime->private_data;
 155        struct omap_pcm_dma_data *dma_data = prtd->dma_data;
 156        struct omap_dma_channel_params dma_params;
 157        int bytes;
 158
 159        /* return if this is a bufferless transfer e.g.
 160         * codec <--> BT codec or GSM modem -- lg FIXME */
 161        if (!prtd->dma_data)
 162                return 0;
 163
 164        memset(&dma_params, 0, sizeof(dma_params));
 165        dma_params.data_type                    = dma_data->data_type;
 166        dma_params.trigger                      = dma_data->dma_req;
 167        dma_params.sync_mode                    = dma_data->sync_mode;
 168        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
 169                dma_params.src_amode            = OMAP_DMA_AMODE_POST_INC;
 170                dma_params.dst_amode            = OMAP_DMA_AMODE_CONSTANT;
 171                dma_params.src_or_dst_synch     = OMAP_DMA_DST_SYNC;
 172                dma_params.src_start            = runtime->dma_addr;
 173                dma_params.dst_start            = dma_data->port_addr;
 174                dma_params.dst_port             = OMAP_DMA_PORT_MPUI;
 175                dma_params.dst_fi               = dma_data->packet_size;
 176        } else {
 177                dma_params.src_amode            = OMAP_DMA_AMODE_CONSTANT;
 178                dma_params.dst_amode            = OMAP_DMA_AMODE_POST_INC;
 179                dma_params.src_or_dst_synch     = OMAP_DMA_SRC_SYNC;
 180                dma_params.src_start            = dma_data->port_addr;
 181                dma_params.dst_start            = runtime->dma_addr;
 182                dma_params.src_port             = OMAP_DMA_PORT_MPUI;
 183                dma_params.src_fi               = dma_data->packet_size;
 184        }
 185        /*
 186         * Set DMA transfer frame size equal to ALSA period size and frame
 187         * count as no. of ALSA periods. Then with DMA frame interrupt enabled,
 188         * we can transfer the whole ALSA buffer with single DMA transfer but
 189         * still can get an interrupt at each period bounary
 190         */
 191        bytes = snd_pcm_lib_period_bytes(substream);
 192        dma_params.elem_count   = bytes >> dma_data->data_type;
 193        dma_params.frame_count  = runtime->periods;
 194        omap_set_dma_params(prtd->dma_ch, &dma_params);
 195
 196        if ((cpu_is_omap1510()))
 197                omap_enable_dma_irq(prtd->dma_ch, OMAP_DMA_FRAME_IRQ |
 198                              OMAP_DMA_LAST_IRQ | OMAP_DMA_BLOCK_IRQ);
 199        else if (!substream->runtime->no_period_wakeup)
 200                omap_enable_dma_irq(prtd->dma_ch, OMAP_DMA_FRAME_IRQ);
 201
 202        if (!(cpu_class_is_omap1())) {
 203                omap_set_dma_src_burst_mode(prtd->dma_ch,
 204                                                OMAP_DMA_DATA_BURST_16);
 205                omap_set_dma_dest_burst_mode(prtd->dma_ch,
 206                                                OMAP_DMA_DATA_BURST_16);
 207        }
 208
 209        return 0;
 210}
 211
 212static int omap_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
 213{
 214        struct snd_pcm_runtime *runtime = substream->runtime;
 215        struct omap_runtime_data *prtd = runtime->private_data;
 216        struct omap_pcm_dma_data *dma_data = prtd->dma_data;
 217        unsigned long flags;
 218        int ret = 0;
 219
 220        spin_lock_irqsave(&prtd->lock, flags);
 221        switch (cmd) {
 222        case SNDRV_PCM_TRIGGER_START:
 223        case SNDRV_PCM_TRIGGER_RESUME:
 224        case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
 225                prtd->period_index = 0;
 226                /* Configure McBSP internal buffer usage */
 227                if (dma_data->set_threshold)
 228                        dma_data->set_threshold(substream);
 229
 230                omap_start_dma(prtd->dma_ch);
 231                break;
 232
 233        case SNDRV_PCM_TRIGGER_STOP:
 234        case SNDRV_PCM_TRIGGER_SUSPEND:
 235        case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
 236                prtd->period_index = -1;
 237                omap_stop_dma(prtd->dma_ch);
 238                break;
 239        default:
 240                ret = -EINVAL;
 241        }
 242        spin_unlock_irqrestore(&prtd->lock, flags);
 243
 244        return ret;
 245}
 246
 247static snd_pcm_uframes_t omap_pcm_pointer(struct snd_pcm_substream *substream)
 248{
 249        struct snd_pcm_runtime *runtime = substream->runtime;
 250        struct omap_runtime_data *prtd = runtime->private_data;
 251        dma_addr_t ptr;
 252        snd_pcm_uframes_t offset;
 253
 254        if (cpu_is_omap1510()) {
 255                offset = prtd->period_index * runtime->period_size;
 256        } else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
 257                ptr = omap_get_dma_dst_pos(prtd->dma_ch);
 258                offset = bytes_to_frames(runtime, ptr - runtime->dma_addr);
 259        } else {
 260                ptr = omap_get_dma_src_pos(prtd->dma_ch);
 261                offset = bytes_to_frames(runtime, ptr - runtime->dma_addr);
 262        }
 263
 264        if (offset >= runtime->buffer_size)
 265                offset = 0;
 266
 267        return offset;
 268}
 269
 270static int omap_pcm_open(struct snd_pcm_substream *substream)
 271{
 272        struct snd_pcm_runtime *runtime = substream->runtime;
 273        struct omap_runtime_data *prtd;
 274        int ret;
 275
 276        snd_soc_set_runtime_hwparams(substream, &omap_pcm_hardware);
 277
 278        /* Ensure that buffer size is a multiple of period size */
 279        ret = snd_pcm_hw_constraint_integer(runtime,
 280                                            SNDRV_PCM_HW_PARAM_PERIODS);
 281        if (ret < 0)
 282                goto out;
 283
 284        prtd = kzalloc(sizeof(*prtd), GFP_KERNEL);
 285        if (prtd == NULL) {
 286                ret = -ENOMEM;
 287                goto out;
 288        }
 289        spin_lock_init(&prtd->lock);
 290        runtime->private_data = prtd;
 291
 292out:
 293        return ret;
 294}
 295
 296static int omap_pcm_close(struct snd_pcm_substream *substream)
 297{
 298        struct snd_pcm_runtime *runtime = substream->runtime;
 299
 300        kfree(runtime->private_data);
 301        return 0;
 302}
 303
 304static int omap_pcm_mmap(struct snd_pcm_substream *substream,
 305        struct vm_area_struct *vma)
 306{
 307        struct snd_pcm_runtime *runtime = substream->runtime;
 308
 309        return dma_mmap_writecombine(substream->pcm->card->dev, vma,
 310                                     runtime->dma_area,
 311                                     runtime->dma_addr,
 312                                     runtime->dma_bytes);
 313}
 314
 315static struct snd_pcm_ops omap_pcm_ops = {
 316        .open           = omap_pcm_open,
 317        .close          = omap_pcm_close,
 318        .ioctl          = snd_pcm_lib_ioctl,
 319        .hw_params      = omap_pcm_hw_params,
 320        .hw_free        = omap_pcm_hw_free,
 321        .prepare        = omap_pcm_prepare,
 322        .trigger        = omap_pcm_trigger,
 323        .pointer        = omap_pcm_pointer,
 324        .mmap           = omap_pcm_mmap,
 325};
 326
 327static u64 omap_pcm_dmamask = DMA_BIT_MASK(64);
 328
 329static int omap_pcm_preallocate_dma_buffer(struct snd_pcm *pcm,
 330        int stream)
 331{
 332        struct snd_pcm_substream *substream = pcm->streams[stream].substream;
 333        struct snd_dma_buffer *buf = &substream->dma_buffer;
 334        size_t size = omap_pcm_hardware.buffer_bytes_max;
 335
 336        buf->dev.type = SNDRV_DMA_TYPE_DEV;
 337        buf->dev.dev = pcm->card->dev;
 338        buf->private_data = NULL;
 339        buf->area = dma_alloc_writecombine(pcm->card->dev, size,
 340                                           &buf->addr, GFP_KERNEL);
 341        if (!buf->area)
 342                return -ENOMEM;
 343
 344        buf->bytes = size;
 345        return 0;
 346}
 347
 348static void omap_pcm_free_dma_buffers(struct snd_pcm *pcm)
 349{
 350        struct snd_pcm_substream *substream;
 351        struct snd_dma_buffer *buf;
 352        int stream;
 353
 354        for (stream = 0; stream < 2; stream++) {
 355                substream = pcm->streams[stream].substream;
 356                if (!substream)
 357                        continue;
 358
 359                buf = &substream->dma_buffer;
 360                if (!buf->area)
 361                        continue;
 362
 363                dma_free_writecombine(pcm->card->dev, buf->bytes,
 364                                      buf->area, buf->addr);
 365                buf->area = NULL;
 366        }
 367}
 368
 369static int omap_pcm_new(struct snd_card *card, 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 = &omap_pcm_dmamask;
 376        if (!card->dev->coherent_dma_mask)
 377                card->dev->coherent_dma_mask = DMA_BIT_MASK(64);
 378
 379        if (dai->driver->playback.channels_min) {
 380                ret = omap_pcm_preallocate_dma_buffer(pcm,
 381                        SNDRV_PCM_STREAM_PLAYBACK);
 382                if (ret)
 383                        goto out;
 384        }
 385
 386        if (dai->driver->capture.channels_min) {
 387                ret = omap_pcm_preallocate_dma_buffer(pcm,
 388                        SNDRV_PCM_STREAM_CAPTURE);
 389                if (ret)
 390                        goto out;
 391        }
 392
 393out:
 394        return ret;
 395}
 396
 397static struct snd_soc_platform_driver omap_soc_platform = {
 398        .ops            = &omap_pcm_ops,
 399        .pcm_new        = omap_pcm_new,
 400        .pcm_free       = omap_pcm_free_dma_buffers,
 401};
 402
 403static __devinit int omap_pcm_probe(struct platform_device *pdev)
 404{
 405        return snd_soc_register_platform(&pdev->dev,
 406                        &omap_soc_platform);
 407}
 408
 409static int __devexit omap_pcm_remove(struct platform_device *pdev)
 410{
 411        snd_soc_unregister_platform(&pdev->dev);
 412        return 0;
 413}
 414
 415static struct platform_driver omap_pcm_driver = {
 416        .driver = {
 417                        .name = "omap-pcm-audio",
 418                        .owner = THIS_MODULE,
 419        },
 420
 421        .probe = omap_pcm_probe,
 422        .remove = __devexit_p(omap_pcm_remove),
 423};
 424
 425static int __init snd_omap_pcm_init(void)
 426{
 427        return platform_driver_register(&omap_pcm_driver);
 428}
 429module_init(snd_omap_pcm_init);
 430
 431static void __exit snd_omap_pcm_exit(void)
 432{
 433        platform_driver_unregister(&omap_pcm_driver);
 434}
 435module_exit(snd_omap_pcm_exit);
 436
 437MODULE_AUTHOR("Jarkko Nikula <jhnikula@gmail.com>");
 438MODULE_DESCRIPTION("OMAP PCM DMA module");
 439MODULE_LICENSE("GPL");
 440