linux/sound/soc/soc-generic-dmaengine-pcm.c
<<
>>
Prefs
   1/*
   2 *  Copyright (C) 2013, Analog Devices Inc.
   3 *      Author: Lars-Peter Clausen <lars@metafoo.de>
   4 *
   5 *  This program is free software; you can redistribute it and/or modify it
   6 *  under  the terms of the GNU General  Public License as published by the
   7 *  Free Software Foundation;  either version 2 of the License, or (at your
   8 *  option) any later version.
   9 *
  10 *  You should have received a copy of the GNU General Public License along
  11 *  with this program; if not, write to the Free Software Foundation, Inc.,
  12 *  675 Mass Ave, Cambridge, MA 02139, USA.
  13 *
  14 */
  15#include <linux/module.h>
  16#include <linux/init.h>
  17#include <linux/dmaengine.h>
  18#include <linux/slab.h>
  19#include <sound/pcm.h>
  20#include <sound/pcm_params.h>
  21#include <sound/soc.h>
  22#include <linux/dma-mapping.h>
  23#include <linux/of.h>
  24
  25#include <sound/dmaengine_pcm.h>
  26
  27/*
  28 * The platforms dmaengine driver does not support reporting the amount of
  29 * bytes that are still left to transfer.
  30 */
  31#define SND_DMAENGINE_PCM_FLAG_NO_RESIDUE BIT(31)
  32
  33struct dmaengine_pcm {
  34        struct dma_chan *chan[SNDRV_PCM_STREAM_LAST + 1];
  35        const struct snd_dmaengine_pcm_config *config;
  36        struct snd_soc_platform platform;
  37        unsigned int flags;
  38};
  39
  40static struct dmaengine_pcm *soc_platform_to_pcm(struct snd_soc_platform *p)
  41{
  42        return container_of(p, struct dmaengine_pcm, platform);
  43}
  44
  45static struct device *dmaengine_dma_dev(struct dmaengine_pcm *pcm,
  46        struct snd_pcm_substream *substream)
  47{
  48        if (!pcm->chan[substream->stream])
  49                return NULL;
  50
  51        return pcm->chan[substream->stream]->device->dev;
  52}
  53
  54/**
  55 * snd_dmaengine_pcm_prepare_slave_config() - Generic prepare_slave_config callback
  56 * @substream: PCM substream
  57 * @params: hw_params
  58 * @slave_config: DMA slave config to prepare
  59 *
  60 * This function can be used as a generic prepare_slave_config callback for
  61 * platforms which make use of the snd_dmaengine_dai_dma_data struct for their
  62 * DAI DMA data. Internally the function will first call
  63 * snd_hwparams_to_dma_slave_config to fill in the slave config based on the
  64 * hw_params, followed by snd_dmaengine_set_config_from_dai_data to fill in the
  65 * remaining fields based on the DAI DMA data.
  66 */
  67int snd_dmaengine_pcm_prepare_slave_config(struct snd_pcm_substream *substream,
  68        struct snd_pcm_hw_params *params, struct dma_slave_config *slave_config)
  69{
  70        struct snd_soc_pcm_runtime *rtd = substream->private_data;
  71        struct snd_dmaengine_dai_dma_data *dma_data;
  72        int ret;
  73
  74        dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
  75
  76        ret = snd_hwparams_to_dma_slave_config(substream, params, slave_config);
  77        if (ret)
  78                return ret;
  79
  80        snd_dmaengine_pcm_set_config_from_dai_data(substream, dma_data,
  81                slave_config);
  82
  83        return 0;
  84}
  85EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_prepare_slave_config);
  86
  87static int dmaengine_pcm_hw_params(struct snd_pcm_substream *substream,
  88        struct snd_pcm_hw_params *params)
  89{
  90        struct snd_soc_pcm_runtime *rtd = substream->private_data;
  91        struct dmaengine_pcm *pcm = soc_platform_to_pcm(rtd->platform);
  92        struct dma_chan *chan = snd_dmaengine_pcm_get_chan(substream);
  93        int (*prepare_slave_config)(struct snd_pcm_substream *substream,
  94                        struct snd_pcm_hw_params *params,
  95                        struct dma_slave_config *slave_config);
  96        struct dma_slave_config slave_config;
  97        int ret;
  98
  99        memset(&slave_config, 0, sizeof(slave_config));
 100
 101        if (!pcm->config)
 102                prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config;
 103        else
 104                prepare_slave_config = pcm->config->prepare_slave_config;
 105
 106        if (prepare_slave_config) {
 107                ret = prepare_slave_config(substream, params, &slave_config);
 108                if (ret)
 109                        return ret;
 110
 111                ret = dmaengine_slave_config(chan, &slave_config);
 112                if (ret)
 113                        return ret;
 114        }
 115
 116        return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params));
 117}
 118
 119static int dmaengine_pcm_set_runtime_hwparams(struct snd_pcm_substream *substream)
 120{
 121        struct snd_soc_pcm_runtime *rtd = substream->private_data;
 122        struct dmaengine_pcm *pcm = soc_platform_to_pcm(rtd->platform);
 123        struct device *dma_dev = dmaengine_dma_dev(pcm, substream);
 124        struct dma_chan *chan = pcm->chan[substream->stream];
 125        struct snd_dmaengine_dai_dma_data *dma_data;
 126        struct dma_slave_caps dma_caps;
 127        struct snd_pcm_hardware hw;
 128        u32 addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) |
 129                          BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) |
 130                          BIT(DMA_SLAVE_BUSWIDTH_4_BYTES);
 131        int i, ret;
 132
 133        if (pcm->config && pcm->config->pcm_hardware)
 134                return snd_soc_set_runtime_hwparams(substream,
 135                                pcm->config->pcm_hardware);
 136
 137        dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
 138
 139        memset(&hw, 0, sizeof(hw));
 140        hw.info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
 141                        SNDRV_PCM_INFO_INTERLEAVED;
 142        hw.periods_min = 2;
 143        hw.periods_max = UINT_MAX;
 144        hw.period_bytes_min = 256;
 145        hw.period_bytes_max = dma_get_max_seg_size(dma_dev);
 146        hw.buffer_bytes_max = SIZE_MAX;
 147        hw.fifo_size = dma_data->fifo_size;
 148
 149        if (pcm->flags & SND_DMAENGINE_PCM_FLAG_NO_RESIDUE)
 150                hw.info |= SNDRV_PCM_INFO_BATCH;
 151
 152        ret = dma_get_slave_caps(chan, &dma_caps);
 153        if (ret == 0) {
 154                if (dma_caps.cmd_pause)
 155                        hw.info |= SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME;
 156                if (dma_caps.residue_granularity <= DMA_RESIDUE_GRANULARITY_SEGMENT)
 157                        hw.info |= SNDRV_PCM_INFO_BATCH;
 158
 159                if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
 160                        addr_widths = dma_caps.dst_addr_widths;
 161                else
 162                        addr_widths = dma_caps.src_addr_widths;
 163        }
 164
 165        /*
 166         * Prepare formats mask for valid/allowed sample types. If the dma does
 167         * not have support for the given physical word size, it needs to be
 168         * masked out so user space can not use the format which produces
 169         * corrupted audio.
 170         * In case the dma driver does not implement the slave_caps the default
 171         * assumption is that it supports 1, 2 and 4 bytes widths.
 172         */
 173        for (i = 0; i <= SNDRV_PCM_FORMAT_LAST; i++) {
 174                int bits = snd_pcm_format_physical_width(i);
 175
 176                /* Enable only samples with DMA supported physical widths */
 177                switch (bits) {
 178                case 8:
 179                case 16:
 180                case 24:
 181                case 32:
 182                case 64:
 183                        if (addr_widths & (1 << (bits / 8)))
 184                                hw.formats |= (1LL << i);
 185                        break;
 186                default:
 187                        /* Unsupported types */
 188                        break;
 189                }
 190        }
 191
 192        return snd_soc_set_runtime_hwparams(substream, &hw);
 193}
 194
 195static int dmaengine_pcm_open(struct snd_pcm_substream *substream)
 196{
 197        struct snd_soc_pcm_runtime *rtd = substream->private_data;
 198        struct dmaengine_pcm *pcm = soc_platform_to_pcm(rtd->platform);
 199        struct dma_chan *chan = pcm->chan[substream->stream];
 200        int ret;
 201
 202        ret = dmaengine_pcm_set_runtime_hwparams(substream);
 203        if (ret)
 204                return ret;
 205
 206        return snd_dmaengine_pcm_open(substream, chan);
 207}
 208
 209static struct dma_chan *dmaengine_pcm_compat_request_channel(
 210        struct snd_soc_pcm_runtime *rtd,
 211        struct snd_pcm_substream *substream)
 212{
 213        struct dmaengine_pcm *pcm = soc_platform_to_pcm(rtd->platform);
 214        struct snd_dmaengine_dai_dma_data *dma_data;
 215        dma_filter_fn fn = NULL;
 216
 217        dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
 218
 219        if ((pcm->flags & SND_DMAENGINE_PCM_FLAG_HALF_DUPLEX) && pcm->chan[0])
 220                return pcm->chan[0];
 221
 222        if (pcm->config && pcm->config->compat_request_channel)
 223                return pcm->config->compat_request_channel(rtd, substream);
 224
 225        if (pcm->config)
 226                fn = pcm->config->compat_filter_fn;
 227
 228        return snd_dmaengine_pcm_request_channel(fn, dma_data->filter_data);
 229}
 230
 231static bool dmaengine_pcm_can_report_residue(struct device *dev,
 232        struct dma_chan *chan)
 233{
 234        struct dma_slave_caps dma_caps;
 235        int ret;
 236
 237        ret = dma_get_slave_caps(chan, &dma_caps);
 238        if (ret != 0) {
 239                dev_warn(dev, "Failed to get DMA channel capabilities, falling back to period counting: %d\n",
 240                         ret);
 241                return false;
 242        }
 243
 244        if (dma_caps.residue_granularity == DMA_RESIDUE_GRANULARITY_DESCRIPTOR)
 245                return false;
 246
 247        return true;
 248}
 249
 250static int dmaengine_pcm_new(struct snd_soc_pcm_runtime *rtd)
 251{
 252        struct dmaengine_pcm *pcm = soc_platform_to_pcm(rtd->platform);
 253        const struct snd_dmaengine_pcm_config *config = pcm->config;
 254        struct device *dev = rtd->platform->dev;
 255        struct snd_dmaengine_dai_dma_data *dma_data;
 256        struct snd_pcm_substream *substream;
 257        size_t prealloc_buffer_size;
 258        size_t max_buffer_size;
 259        unsigned int i;
 260        int ret;
 261
 262        if (config && config->prealloc_buffer_size) {
 263                prealloc_buffer_size = config->prealloc_buffer_size;
 264                max_buffer_size = config->pcm_hardware->buffer_bytes_max;
 265        } else {
 266                prealloc_buffer_size = 512 * 1024;
 267                max_buffer_size = SIZE_MAX;
 268        }
 269
 270
 271        for (i = SNDRV_PCM_STREAM_PLAYBACK; i <= SNDRV_PCM_STREAM_CAPTURE; i++) {
 272                substream = rtd->pcm->streams[i].substream;
 273                if (!substream)
 274                        continue;
 275
 276                dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
 277
 278                if (!pcm->chan[i] &&
 279                    (pcm->flags & SND_DMAENGINE_PCM_FLAG_CUSTOM_CHANNEL_NAME))
 280                        pcm->chan[i] = dma_request_slave_channel(dev,
 281                                dma_data->chan_name);
 282
 283                if (!pcm->chan[i] && (pcm->flags & SND_DMAENGINE_PCM_FLAG_COMPAT)) {
 284                        pcm->chan[i] = dmaengine_pcm_compat_request_channel(rtd,
 285                                substream);
 286                }
 287
 288                if (!pcm->chan[i]) {
 289                        dev_err(rtd->platform->dev,
 290                                "Missing dma channel for stream: %d\n", i);
 291                        return -EINVAL;
 292                }
 293
 294                ret = snd_pcm_lib_preallocate_pages(substream,
 295                                SNDRV_DMA_TYPE_DEV_IRAM,
 296                                dmaengine_dma_dev(pcm, substream),
 297                                prealloc_buffer_size,
 298                                max_buffer_size);
 299                if (ret)
 300                        return ret;
 301
 302                if (!dmaengine_pcm_can_report_residue(dev, pcm->chan[i]))
 303                        pcm->flags |= SND_DMAENGINE_PCM_FLAG_NO_RESIDUE;
 304        }
 305
 306        return 0;
 307}
 308
 309static snd_pcm_uframes_t dmaengine_pcm_pointer(
 310        struct snd_pcm_substream *substream)
 311{
 312        struct snd_soc_pcm_runtime *rtd = substream->private_data;
 313        struct dmaengine_pcm *pcm = soc_platform_to_pcm(rtd->platform);
 314
 315        if (pcm->flags & SND_DMAENGINE_PCM_FLAG_NO_RESIDUE)
 316                return snd_dmaengine_pcm_pointer_no_residue(substream);
 317        else
 318                return snd_dmaengine_pcm_pointer(substream);
 319}
 320
 321static const struct snd_pcm_ops dmaengine_pcm_ops = {
 322        .open           = dmaengine_pcm_open,
 323        .close          = snd_dmaengine_pcm_close,
 324        .ioctl          = snd_pcm_lib_ioctl,
 325        .hw_params      = dmaengine_pcm_hw_params,
 326        .hw_free        = snd_pcm_lib_free_pages,
 327        .trigger        = snd_dmaengine_pcm_trigger,
 328        .pointer        = dmaengine_pcm_pointer,
 329};
 330
 331static const struct snd_soc_platform_driver dmaengine_pcm_platform = {
 332        .component_driver = {
 333                .probe_order = SND_SOC_COMP_ORDER_LATE,
 334        },
 335        .ops            = &dmaengine_pcm_ops,
 336        .pcm_new        = dmaengine_pcm_new,
 337};
 338
 339static const char * const dmaengine_pcm_dma_channel_names[] = {
 340        [SNDRV_PCM_STREAM_PLAYBACK] = "tx",
 341        [SNDRV_PCM_STREAM_CAPTURE] = "rx",
 342};
 343
 344static int dmaengine_pcm_request_chan_of(struct dmaengine_pcm *pcm,
 345        struct device *dev, const struct snd_dmaengine_pcm_config *config)
 346{
 347        unsigned int i;
 348        const char *name;
 349        struct dma_chan *chan;
 350
 351        if ((pcm->flags & (SND_DMAENGINE_PCM_FLAG_NO_DT |
 352                           SND_DMAENGINE_PCM_FLAG_CUSTOM_CHANNEL_NAME)) ||
 353            !dev->of_node)
 354                return 0;
 355
 356        if (config && config->dma_dev) {
 357                /*
 358                 * If this warning is seen, it probably means that your Linux
 359                 * device structure does not match your HW device structure.
 360                 * It would be best to refactor the Linux device structure to
 361                 * correctly match the HW structure.
 362                 */
 363                dev_warn(dev, "DMA channels sourced from device %s",
 364                         dev_name(config->dma_dev));
 365                dev = config->dma_dev;
 366        }
 367
 368        for (i = SNDRV_PCM_STREAM_PLAYBACK; i <= SNDRV_PCM_STREAM_CAPTURE;
 369             i++) {
 370                if (pcm->flags & SND_DMAENGINE_PCM_FLAG_HALF_DUPLEX)
 371                        name = "rx-tx";
 372                else
 373                        name = dmaengine_pcm_dma_channel_names[i];
 374                if (config && config->chan_names[i])
 375                        name = config->chan_names[i];
 376                chan = dma_request_slave_channel_reason(dev, name);
 377                if (IS_ERR(chan)) {
 378                        if (PTR_ERR(chan) == -EPROBE_DEFER)
 379                                return -EPROBE_DEFER;
 380                        pcm->chan[i] = NULL;
 381                } else {
 382                        pcm->chan[i] = chan;
 383                }
 384                if (pcm->flags & SND_DMAENGINE_PCM_FLAG_HALF_DUPLEX)
 385                        break;
 386        }
 387
 388        if (pcm->flags & SND_DMAENGINE_PCM_FLAG_HALF_DUPLEX)
 389                pcm->chan[1] = pcm->chan[0];
 390
 391        return 0;
 392}
 393
 394static void dmaengine_pcm_release_chan(struct dmaengine_pcm *pcm)
 395{
 396        unsigned int i;
 397
 398        for (i = SNDRV_PCM_STREAM_PLAYBACK; i <= SNDRV_PCM_STREAM_CAPTURE;
 399             i++) {
 400                if (!pcm->chan[i])
 401                        continue;
 402                dma_release_channel(pcm->chan[i]);
 403                if (pcm->flags & SND_DMAENGINE_PCM_FLAG_HALF_DUPLEX)
 404                        break;
 405        }
 406}
 407
 408/**
 409 * snd_dmaengine_pcm_register - Register a dmaengine based PCM device
 410 * @dev: The parent device for the PCM device
 411 * @config: Platform specific PCM configuration
 412 * @flags: Platform specific quirks
 413 */
 414int snd_dmaengine_pcm_register(struct device *dev,
 415        const struct snd_dmaengine_pcm_config *config, unsigned int flags)
 416{
 417        struct dmaengine_pcm *pcm;
 418        int ret;
 419
 420        pcm = kzalloc(sizeof(*pcm), GFP_KERNEL);
 421        if (!pcm)
 422                return -ENOMEM;
 423
 424        pcm->config = config;
 425        pcm->flags = flags;
 426
 427        ret = dmaengine_pcm_request_chan_of(pcm, dev, config);
 428        if (ret)
 429                goto err_free_dma;
 430
 431        ret = snd_soc_add_platform(dev, &pcm->platform,
 432                &dmaengine_pcm_platform);
 433        if (ret)
 434                goto err_free_dma;
 435
 436        return 0;
 437
 438err_free_dma:
 439        dmaengine_pcm_release_chan(pcm);
 440        kfree(pcm);
 441        return ret;
 442}
 443EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_register);
 444
 445/**
 446 * snd_dmaengine_pcm_unregister - Removes a dmaengine based PCM device
 447 * @dev: Parent device the PCM was register with
 448 *
 449 * Removes a dmaengine based PCM device previously registered with
 450 * snd_dmaengine_pcm_register.
 451 */
 452void snd_dmaengine_pcm_unregister(struct device *dev)
 453{
 454        struct snd_soc_platform *platform;
 455        struct dmaengine_pcm *pcm;
 456
 457        platform = snd_soc_lookup_platform(dev);
 458        if (!platform)
 459                return;
 460
 461        pcm = soc_platform_to_pcm(platform);
 462
 463        snd_soc_remove_platform(platform);
 464        dmaengine_pcm_release_chan(pcm);
 465        kfree(pcm);
 466}
 467EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_unregister);
 468
 469MODULE_LICENSE("GPL");
 470