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                                                       struct snd_soc_component *component)
 187{
 188        struct au1xpsc_audio_dmadata *pcd = snd_soc_component_get_drvdata(component);
 189        return &pcd[ss->stream];
 190}
 191
 192static int au1xpsc_pcm_hw_params(struct snd_soc_component *component,
 193                                 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        stype = substream->stream;
 201        pcd = to_dmadata(substream, component);
 202
 203        DBG("runtime->dma_area = 0x%08lx dma_addr_t = 0x%08lx dma_size = %zu "
 204            "runtime->min_align %lu\n",
 205                (unsigned long)runtime->dma_area,
 206                (unsigned long)runtime->dma_addr, runtime->dma_bytes,
 207                runtime->min_align);
 208
 209        DBG("bits %d  frags %d  frag_bytes %d  is_rx %d\n", params->msbits,
 210                params_periods(params), params_period_bytes(params), stype);
 211
 212        ret = au1x_pcm_dbdma_realloc(pcd, stype, params->msbits);
 213        if (ret) {
 214                MSG("DDMA channel (re)alloc failed!\n");
 215                goto out;
 216        }
 217
 218        pcd->substream = substream;
 219        pcd->period_bytes = params_period_bytes(params);
 220        pcd->periods = params_periods(params);
 221        pcd->dma_area_s = pcd->dma_area = runtime->dma_addr;
 222        pcd->q_period = 0;
 223        pcd->curr_period = 0;
 224        pcd->pos = 0;
 225
 226        ret = 0;
 227out:
 228        return ret;
 229}
 230
 231static int au1xpsc_pcm_prepare(struct snd_soc_component *component,
 232                               struct snd_pcm_substream *substream)
 233{
 234        struct au1xpsc_audio_dmadata *pcd = to_dmadata(substream, component);
 235
 236        au1xxx_dbdma_reset(pcd->ddma_chan);
 237
 238        if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
 239                au1x_pcm_queue_rx(pcd);
 240                au1x_pcm_queue_rx(pcd);
 241        } else {
 242                au1x_pcm_queue_tx(pcd);
 243                au1x_pcm_queue_tx(pcd);
 244        }
 245
 246        return 0;
 247}
 248
 249static int au1xpsc_pcm_trigger(struct snd_soc_component *component,
 250                               struct snd_pcm_substream *substream, int cmd)
 251{
 252        u32 c = to_dmadata(substream, component)->ddma_chan;
 253
 254        switch (cmd) {
 255        case SNDRV_PCM_TRIGGER_START:
 256        case SNDRV_PCM_TRIGGER_RESUME:
 257                au1xxx_dbdma_start(c);
 258                break;
 259        case SNDRV_PCM_TRIGGER_STOP:
 260        case SNDRV_PCM_TRIGGER_SUSPEND:
 261                au1xxx_dbdma_stop(c);
 262                break;
 263        default:
 264                return -EINVAL;
 265        }
 266        return 0;
 267}
 268
 269static snd_pcm_uframes_t
 270au1xpsc_pcm_pointer(struct snd_soc_component *component,
 271                    struct snd_pcm_substream *substream)
 272{
 273        return bytes_to_frames(substream->runtime,
 274                               to_dmadata(substream, component)->pos);
 275}
 276
 277static int au1xpsc_pcm_open(struct snd_soc_component *component,
 278                            struct snd_pcm_substream *substream)
 279{
 280        struct au1xpsc_audio_dmadata *pcd = to_dmadata(substream, component);
 281        struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
 282        int stype = substream->stream, *dmaids;
 283
 284        dmaids = snd_soc_dai_get_dma_data(asoc_rtd_to_cpu(rtd, 0), substream);
 285        if (!dmaids)
 286                return -ENODEV; /* whoa, has ordering changed? */
 287
 288        pcd->ddma_id = dmaids[stype];
 289
 290        snd_soc_set_runtime_hwparams(substream, &au1xpsc_pcm_hardware);
 291        return 0;
 292}
 293
 294static int au1xpsc_pcm_close(struct snd_soc_component *component,
 295                             struct snd_pcm_substream *substream)
 296{
 297        au1x_pcm_dbdma_free(to_dmadata(substream, component));
 298        return 0;
 299}
 300
 301static int au1xpsc_pcm_new(struct snd_soc_component *component,
 302                           struct snd_soc_pcm_runtime *rtd)
 303{
 304        struct snd_card *card = rtd->card->snd_card;
 305        struct snd_pcm *pcm = rtd->pcm;
 306
 307        snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV,
 308                card->dev, AU1XPSC_BUFFER_MIN_BYTES, (4096 * 1024) - 1);
 309
 310        return 0;
 311}
 312
 313/* au1xpsc audio platform */
 314static struct snd_soc_component_driver au1xpsc_soc_component = {
 315        .name           = DRV_NAME,
 316        .open           = au1xpsc_pcm_open,
 317        .close          = au1xpsc_pcm_close,
 318        .hw_params      = au1xpsc_pcm_hw_params,
 319        .prepare        = au1xpsc_pcm_prepare,
 320        .trigger        = au1xpsc_pcm_trigger,
 321        .pointer        = au1xpsc_pcm_pointer,
 322        .pcm_construct  = au1xpsc_pcm_new,
 323};
 324
 325static int au1xpsc_pcm_drvprobe(struct platform_device *pdev)
 326{
 327        struct au1xpsc_audio_dmadata *dmadata;
 328
 329        dmadata = devm_kcalloc(&pdev->dev,
 330                               2, sizeof(struct au1xpsc_audio_dmadata),
 331                               GFP_KERNEL);
 332        if (!dmadata)
 333                return -ENOMEM;
 334
 335        platform_set_drvdata(pdev, dmadata);
 336
 337        return devm_snd_soc_register_component(&pdev->dev,
 338                                        &au1xpsc_soc_component, NULL, 0);
 339}
 340
 341static struct platform_driver au1xpsc_pcm_driver = {
 342        .driver = {
 343                .name   = "au1xpsc-pcm",
 344        },
 345        .probe          = au1xpsc_pcm_drvprobe,
 346};
 347
 348module_platform_driver(au1xpsc_pcm_driver);
 349
 350MODULE_LICENSE("GPL");
 351MODULE_DESCRIPTION("Au12x0/Au1550 PSC Audio DMA driver");
 352MODULE_AUTHOR("Manuel Lauss");
 353