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