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