linux/sound/soc/au1x/dbdma2.c
<<
>>
Prefs
   1/*
   2 * Au12x0/Au1550 PSC ALSA ASoC audio support.
   3 *
   4 * (c) 2007-2008 MSC Vertriebsges.m.b.H.,
   5 *      Manuel Lauss <manuel.lauss@gmail.com>
   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 * DMA glue for Au1x-PSC audio.
  12 *
  13 */
  14
  15
  16#include <linux/module.h>
  17#include <linux/init.h>
  18#include <linux/platform_device.h>
  19#include <linux/slab.h>
  20#include <linux/dma-mapping.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 <asm/mach-au1x00/au1000.h>
  28#include <asm/mach-au1x00/au1xxx_dbdma.h>
  29#include <asm/mach-au1x00/au1xxx_psc.h>
  30
  31#include "psc.h"
  32
  33/*#define PCM_DEBUG*/
  34
  35#define MSG(x...)       printk(KERN_INFO "au1xpsc_pcm: " x)
  36#ifdef PCM_DEBUG
  37#define DBG             MSG
  38#else
  39#define DBG(x...)       do {} while (0)
  40#endif
  41
  42struct au1xpsc_audio_dmadata {
  43        /* DDMA control data */
  44        unsigned int ddma_id;           /* DDMA direction ID for this PSC */
  45        u32 ddma_chan;                  /* DDMA context */
  46
  47        /* PCM context (for irq handlers) */
  48        struct snd_pcm_substream *substream;
  49        unsigned long curr_period;      /* current segment DDMA is working on */
  50        unsigned long q_period;         /* queue period(s) */
  51        dma_addr_t dma_area;            /* address of queued DMA area */
  52        dma_addr_t dma_area_s;          /* start address of DMA area */
  53        unsigned long pos;              /* current byte position being played */
  54        unsigned long periods;          /* number of SG segments in total */
  55        unsigned long period_bytes;     /* size in bytes of one SG segment */
  56
  57        /* runtime data */
  58        int msbits;
  59};
  60
  61/*
  62 * These settings are somewhat okay, at least on my machine audio plays
  63 * almost skip-free. Especially the 64kB buffer seems to help a LOT.
  64 */
  65#define AU1XPSC_PERIOD_MIN_BYTES        1024
  66#define AU1XPSC_BUFFER_MIN_BYTES        65536
  67
  68/* PCM hardware DMA capabilities - platform specific */
  69static const struct snd_pcm_hardware au1xpsc_pcm_hardware = {
  70        .info             = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
  71                            SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BATCH,
  72        .period_bytes_min = AU1XPSC_PERIOD_MIN_BYTES,
  73        .period_bytes_max = 4096 * 1024 - 1,
  74        .periods_min      = 2,
  75        .periods_max      = 4096,       /* 2 to as-much-as-you-like */
  76        .buffer_bytes_max = 4096 * 1024 - 1,
  77        .fifo_size        = 16,         /* fifo entries of AC97/I2S PSC */
  78};
  79
  80static void au1x_pcm_queue_tx(struct au1xpsc_audio_dmadata *cd)
  81{
  82        au1xxx_dbdma_put_source(cd->ddma_chan, cd->dma_area,
  83                                cd->period_bytes, DDMA_FLAGS_IE);
  84
  85        /* update next-to-queue period */
  86        ++cd->q_period;
  87        cd->dma_area += cd->period_bytes;
  88        if (cd->q_period >= cd->periods) {
  89                cd->q_period = 0;
  90                cd->dma_area = cd->dma_area_s;
  91        }
  92}
  93
  94static void au1x_pcm_queue_rx(struct au1xpsc_audio_dmadata *cd)
  95{
  96        au1xxx_dbdma_put_dest(cd->ddma_chan, cd->dma_area,
  97                              cd->period_bytes, DDMA_FLAGS_IE);
  98
  99        /* update next-to-queue period */
 100        ++cd->q_period;
 101        cd->dma_area += cd->period_bytes;
 102        if (cd->q_period >= cd->periods) {
 103                cd->q_period = 0;
 104                cd->dma_area = cd->dma_area_s;
 105        }
 106}
 107
 108static void au1x_pcm_dmatx_cb(int irq, void *dev_id)
 109{
 110        struct au1xpsc_audio_dmadata *cd = dev_id;
 111
 112        cd->pos += cd->period_bytes;
 113        if (++cd->curr_period >= cd->periods) {
 114                cd->pos = 0;
 115                cd->curr_period = 0;
 116        }
 117        snd_pcm_period_elapsed(cd->substream);
 118        au1x_pcm_queue_tx(cd);
 119}
 120
 121static void au1x_pcm_dmarx_cb(int irq, void *dev_id)
 122{
 123        struct au1xpsc_audio_dmadata *cd = dev_id;
 124
 125        cd->pos += cd->period_bytes;
 126        if (++cd->curr_period >= cd->periods) {
 127                cd->pos = 0;
 128                cd->curr_period = 0;
 129        }
 130        snd_pcm_period_elapsed(cd->substream);
 131        au1x_pcm_queue_rx(cd);
 132}
 133
 134static void au1x_pcm_dbdma_free(struct au1xpsc_audio_dmadata *pcd)
 135{
 136        if (pcd->ddma_chan) {
 137                au1xxx_dbdma_stop(pcd->ddma_chan);
 138                au1xxx_dbdma_reset(pcd->ddma_chan);
 139                au1xxx_dbdma_chan_free(pcd->ddma_chan);
 140                pcd->ddma_chan = 0;
 141                pcd->msbits = 0;
 142        }
 143}
 144
 145/* in case of missing DMA ring or changed TX-source / RX-dest bit widths,
 146 * allocate (or reallocate) a 2-descriptor DMA ring with bit depth according
 147 * to ALSA-supplied sample depth.  This is due to limitations in the dbdma api
 148 * (cannot adjust source/dest widths of already allocated descriptor ring).
 149 */
 150static int au1x_pcm_dbdma_realloc(struct au1xpsc_audio_dmadata *pcd,
 151                                 int stype, int msbits)
 152{
 153        /* DMA only in 8/16/32 bit widths */
 154        if (msbits == 24)
 155                msbits = 32;
 156
 157        /* check current config: correct bits and descriptors allocated? */
 158        if ((pcd->ddma_chan) && (msbits == pcd->msbits))
 159                goto out;       /* all ok! */
 160
 161        au1x_pcm_dbdma_free(pcd);
 162
 163        if (stype == SNDRV_PCM_STREAM_CAPTURE)
 164                pcd->ddma_chan = au1xxx_dbdma_chan_alloc(pcd->ddma_id,
 165                                        DSCR_CMD0_ALWAYS,
 166                                        au1x_pcm_dmarx_cb, (void *)pcd);
 167        else
 168                pcd->ddma_chan = au1xxx_dbdma_chan_alloc(DSCR_CMD0_ALWAYS,
 169                                        pcd->ddma_id,
 170                                        au1x_pcm_dmatx_cb, (void *)pcd);
 171
 172        if (!pcd->ddma_chan)
 173                return -ENOMEM;
 174
 175        au1xxx_dbdma_set_devwidth(pcd->ddma_chan, msbits);
 176        au1xxx_dbdma_ring_alloc(pcd->ddma_chan, 2);
 177
 178        pcd->msbits = msbits;
 179
 180        au1xxx_dbdma_stop(pcd->ddma_chan);
 181        au1xxx_dbdma_reset(pcd->ddma_chan);
 182
 183out:
 184        return 0;
 185}
 186
 187static inline struct au1xpsc_audio_dmadata *to_dmadata(struct snd_pcm_substream *ss)
 188{
 189        struct snd_soc_pcm_runtime *rtd = ss->private_data;
 190        struct au1xpsc_audio_dmadata *pcd =
 191                                snd_soc_platform_get_drvdata(rtd->platform);
 192        return &pcd[ss->stream];
 193}
 194
 195static int au1xpsc_pcm_hw_params(struct snd_pcm_substream *substream,
 196                                 struct snd_pcm_hw_params *params)
 197{
 198        struct snd_pcm_runtime *runtime = substream->runtime;
 199        struct au1xpsc_audio_dmadata *pcd;
 200        int stype, ret;
 201
 202        ret = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params));
 203        if (ret < 0)
 204                goto out;
 205
 206        stype = substream->stream;
 207        pcd = to_dmadata(substream);
 208
 209        DBG("runtime->dma_area = 0x%08lx dma_addr_t = 0x%08lx dma_size = %d "
 210            "runtime->min_align %d\n",
 211                (unsigned long)runtime->dma_area,
 212                (unsigned long)runtime->dma_addr, runtime->dma_bytes,
 213                runtime->min_align);
 214
 215        DBG("bits %d  frags %d  frag_bytes %d  is_rx %d\n", params->msbits,
 216                params_periods(params), params_period_bytes(params), stype);
 217
 218        ret = au1x_pcm_dbdma_realloc(pcd, stype, params->msbits);
 219        if (ret) {
 220                MSG("DDMA channel (re)alloc failed!\n");
 221                goto out;
 222        }
 223
 224        pcd->substream = substream;
 225        pcd->period_bytes = params_period_bytes(params);
 226        pcd->periods = params_periods(params);
 227        pcd->dma_area_s = pcd->dma_area = runtime->dma_addr;
 228        pcd->q_period = 0;
 229        pcd->curr_period = 0;
 230        pcd->pos = 0;
 231
 232        ret = 0;
 233out:
 234        return ret;
 235}
 236
 237static int au1xpsc_pcm_hw_free(struct snd_pcm_substream *substream)
 238{
 239        snd_pcm_lib_free_pages(substream);
 240        return 0;
 241}
 242
 243static int au1xpsc_pcm_prepare(struct snd_pcm_substream *substream)
 244{
 245        struct au1xpsc_audio_dmadata *pcd = to_dmadata(substream);
 246
 247        au1xxx_dbdma_reset(pcd->ddma_chan);
 248
 249        if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
 250                au1x_pcm_queue_rx(pcd);
 251                au1x_pcm_queue_rx(pcd);
 252        } else {
 253                au1x_pcm_queue_tx(pcd);
 254                au1x_pcm_queue_tx(pcd);
 255        }
 256
 257        return 0;
 258}
 259
 260static int au1xpsc_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
 261{
 262        u32 c = to_dmadata(substream)->ddma_chan;
 263
 264        switch (cmd) {
 265        case SNDRV_PCM_TRIGGER_START:
 266        case SNDRV_PCM_TRIGGER_RESUME:
 267                au1xxx_dbdma_start(c);
 268                break;
 269        case SNDRV_PCM_TRIGGER_STOP:
 270        case SNDRV_PCM_TRIGGER_SUSPEND:
 271                au1xxx_dbdma_stop(c);
 272                break;
 273        default:
 274                return -EINVAL;
 275        }
 276        return 0;
 277}
 278
 279static snd_pcm_uframes_t
 280au1xpsc_pcm_pointer(struct snd_pcm_substream *substream)
 281{
 282        return bytes_to_frames(substream->runtime, to_dmadata(substream)->pos);
 283}
 284
 285static int au1xpsc_pcm_open(struct snd_pcm_substream *substream)
 286{
 287        struct au1xpsc_audio_dmadata *pcd = to_dmadata(substream);
 288        struct snd_soc_pcm_runtime *rtd = substream->private_data;
 289        int stype = substream->stream, *dmaids;
 290
 291        dmaids = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
 292        if (!dmaids)
 293                return -ENODEV; /* whoa, has ordering changed? */
 294
 295        pcd->ddma_id = dmaids[stype];
 296
 297        snd_soc_set_runtime_hwparams(substream, &au1xpsc_pcm_hardware);
 298        return 0;
 299}
 300
 301static int au1xpsc_pcm_close(struct snd_pcm_substream *substream)
 302{
 303        au1x_pcm_dbdma_free(to_dmadata(substream));
 304        return 0;
 305}
 306
 307static struct snd_pcm_ops au1xpsc_pcm_ops = {
 308        .open           = au1xpsc_pcm_open,
 309        .close          = au1xpsc_pcm_close,
 310        .ioctl          = snd_pcm_lib_ioctl,
 311        .hw_params      = au1xpsc_pcm_hw_params,
 312        .hw_free        = au1xpsc_pcm_hw_free,
 313        .prepare        = au1xpsc_pcm_prepare,
 314        .trigger        = au1xpsc_pcm_trigger,
 315        .pointer        = au1xpsc_pcm_pointer,
 316};
 317
 318static int au1xpsc_pcm_new(struct snd_soc_pcm_runtime *rtd)
 319{
 320        struct snd_card *card = rtd->card->snd_card;
 321        struct snd_pcm *pcm = rtd->pcm;
 322
 323        snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
 324                card->dev, AU1XPSC_BUFFER_MIN_BYTES, (4096 * 1024) - 1);
 325
 326        return 0;
 327}
 328
 329/* au1xpsc audio platform */
 330static struct snd_soc_platform_driver au1xpsc_soc_platform = {
 331        .ops            = &au1xpsc_pcm_ops,
 332        .pcm_new        = au1xpsc_pcm_new,
 333};
 334
 335static int au1xpsc_pcm_drvprobe(struct platform_device *pdev)
 336{
 337        struct au1xpsc_audio_dmadata *dmadata;
 338
 339        dmadata = devm_kzalloc(&pdev->dev,
 340                               2 * sizeof(struct au1xpsc_audio_dmadata),
 341                               GFP_KERNEL);
 342        if (!dmadata)
 343                return -ENOMEM;
 344
 345        platform_set_drvdata(pdev, dmadata);
 346
 347        return snd_soc_register_platform(&pdev->dev, &au1xpsc_soc_platform);
 348}
 349
 350static int au1xpsc_pcm_drvremove(struct platform_device *pdev)
 351{
 352        snd_soc_unregister_platform(&pdev->dev);
 353
 354        return 0;
 355}
 356
 357static struct platform_driver au1xpsc_pcm_driver = {
 358        .driver = {
 359                .name   = "au1xpsc-pcm",
 360        },
 361        .probe          = au1xpsc_pcm_drvprobe,
 362        .remove         = au1xpsc_pcm_drvremove,
 363};
 364
 365module_platform_driver(au1xpsc_pcm_driver);
 366
 367MODULE_LICENSE("GPL");
 368MODULE_DESCRIPTION("Au12x0/Au1550 PSC Audio DMA driver");
 369MODULE_AUTHOR("Manuel Lauss");
 370