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         * If SND_DMAENGINE_PCM_DAI_FLAG_PACK is set keep
 167         * hw.formats set to 0, meaning no restrictions are in place.
 168         * In this case it's the responsibility of the DAI driver to
 169         * provide the supported format information.
 170         */
 171        if (!(dma_data->flags & SND_DMAENGINE_PCM_DAI_FLAG_PACK))
 172                /*
 173                 * Prepare formats mask for valid/allowed sample types. If the
 174                 * dma does not have support for the given physical word size,
 175                 * it needs to be masked out so user space can not use the
 176                 * format which produces corrupted audio.
 177                 * In case the dma driver does not implement the slave_caps the
 178                 * default assumption is that it supports 1, 2 and 4 bytes
 179                 * widths.
 180                 */
 181                for (i = 0; i <= SNDRV_PCM_FORMAT_LAST; i++) {
 182                        int bits = snd_pcm_format_physical_width(i);
 183
 184                        /*
 185                         * Enable only samples with DMA supported physical
 186                         * widths
 187                         */
 188                        switch (bits) {
 189                        case 8:
 190                        case 16:
 191                        case 24:
 192                        case 32:
 193                        case 64:
 194                                if (addr_widths & (1 << (bits / 8)))
 195                                        hw.formats |= (1LL << i);
 196                                break;
 197                        default:
 198                                /* Unsupported types */
 199                                break;
 200                        }
 201                }
 202
 203        return snd_soc_set_runtime_hwparams(substream, &hw);
 204}
 205
 206static int dmaengine_pcm_open(struct snd_pcm_substream *substream)
 207{
 208        struct snd_soc_pcm_runtime *rtd = substream->private_data;
 209        struct dmaengine_pcm *pcm = soc_platform_to_pcm(rtd->platform);
 210        struct dma_chan *chan = pcm->chan[substream->stream];
 211        int ret;
 212
 213        ret = dmaengine_pcm_set_runtime_hwparams(substream);
 214        if (ret)
 215                return ret;
 216
 217        return snd_dmaengine_pcm_open(substream, chan);
 218}
 219
 220static struct dma_chan *dmaengine_pcm_compat_request_channel(
 221        struct snd_soc_pcm_runtime *rtd,
 222        struct snd_pcm_substream *substream)
 223{
 224        struct dmaengine_pcm *pcm = soc_platform_to_pcm(rtd->platform);
 225        struct snd_dmaengine_dai_dma_data *dma_data;
 226        dma_filter_fn fn = NULL;
 227
 228        dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
 229
 230        if ((pcm->flags & SND_DMAENGINE_PCM_FLAG_HALF_DUPLEX) && pcm->chan[0])
 231                return pcm->chan[0];
 232
 233        if (pcm->config && pcm->config->compat_request_channel)
 234                return pcm->config->compat_request_channel(rtd, substream);
 235
 236        if (pcm->config)
 237                fn = pcm->config->compat_filter_fn;
 238
 239        return snd_dmaengine_pcm_request_channel(fn, dma_data->filter_data);
 240}
 241
 242static bool dmaengine_pcm_can_report_residue(struct device *dev,
 243        struct dma_chan *chan)
 244{
 245        struct dma_slave_caps dma_caps;
 246        int ret;
 247
 248        ret = dma_get_slave_caps(chan, &dma_caps);
 249        if (ret != 0) {
 250                dev_warn(dev, "Failed to get DMA channel capabilities, falling back to period counting: %d\n",
 251                         ret);
 252                return false;
 253        }
 254
 255        if (dma_caps.residue_granularity == DMA_RESIDUE_GRANULARITY_DESCRIPTOR)
 256                return false;
 257
 258        return true;
 259}
 260
 261static int dmaengine_pcm_new(struct snd_soc_pcm_runtime *rtd)
 262{
 263        struct dmaengine_pcm *pcm = soc_platform_to_pcm(rtd->platform);
 264        const struct snd_dmaengine_pcm_config *config = pcm->config;
 265        struct device *dev = rtd->platform->dev;
 266        struct snd_dmaengine_dai_dma_data *dma_data;
 267        struct snd_pcm_substream *substream;
 268        size_t prealloc_buffer_size;
 269        size_t max_buffer_size;
 270        unsigned int i;
 271        int ret;
 272
 273        if (config && config->prealloc_buffer_size) {
 274                prealloc_buffer_size = config->prealloc_buffer_size;
 275                max_buffer_size = config->pcm_hardware->buffer_bytes_max;
 276        } else {
 277                prealloc_buffer_size = 512 * 1024;
 278                max_buffer_size = SIZE_MAX;
 279        }
 280
 281
 282        for (i = SNDRV_PCM_STREAM_PLAYBACK; i <= SNDRV_PCM_STREAM_CAPTURE; i++) {
 283                substream = rtd->pcm->streams[i].substream;
 284                if (!substream)
 285                        continue;
 286
 287                dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
 288
 289                if (!pcm->chan[i] &&
 290                    (pcm->flags & SND_DMAENGINE_PCM_FLAG_CUSTOM_CHANNEL_NAME))
 291                        pcm->chan[i] = dma_request_slave_channel(dev,
 292                                dma_data->chan_name);
 293
 294                if (!pcm->chan[i] && (pcm->flags & SND_DMAENGINE_PCM_FLAG_COMPAT)) {
 295                        pcm->chan[i] = dmaengine_pcm_compat_request_channel(rtd,
 296                                substream);
 297                }
 298
 299                if (!pcm->chan[i]) {
 300                        dev_err(rtd->platform->dev,
 301                                "Missing dma channel for stream: %d\n", i);
 302                        return -EINVAL;
 303                }
 304
 305                ret = snd_pcm_lib_preallocate_pages(substream,
 306                                SNDRV_DMA_TYPE_DEV_IRAM,
 307                                dmaengine_dma_dev(pcm, substream),
 308                                prealloc_buffer_size,
 309                                max_buffer_size);
 310                if (ret)
 311                        return ret;
 312
 313                if (!dmaengine_pcm_can_report_residue(dev, pcm->chan[i]))
 314                        pcm->flags |= SND_DMAENGINE_PCM_FLAG_NO_RESIDUE;
 315        }
 316
 317        return 0;
 318}
 319
 320static snd_pcm_uframes_t dmaengine_pcm_pointer(
 321        struct snd_pcm_substream *substream)
 322{
 323        struct snd_soc_pcm_runtime *rtd = substream->private_data;
 324        struct dmaengine_pcm *pcm = soc_platform_to_pcm(rtd->platform);
 325
 326        if (pcm->flags & SND_DMAENGINE_PCM_FLAG_NO_RESIDUE)
 327                return snd_dmaengine_pcm_pointer_no_residue(substream);
 328        else
 329                return snd_dmaengine_pcm_pointer(substream);
 330}
 331
 332static const struct snd_pcm_ops dmaengine_pcm_ops = {
 333        .open           = dmaengine_pcm_open,
 334        .close          = snd_dmaengine_pcm_close,
 335        .ioctl          = snd_pcm_lib_ioctl,
 336        .hw_params      = dmaengine_pcm_hw_params,
 337        .hw_free        = snd_pcm_lib_free_pages,
 338        .trigger        = snd_dmaengine_pcm_trigger,
 339        .pointer        = dmaengine_pcm_pointer,
 340};
 341
 342static const struct snd_soc_platform_driver dmaengine_pcm_platform = {
 343        .component_driver = {
 344                .probe_order = SND_SOC_COMP_ORDER_LATE,
 345        },
 346        .ops            = &dmaengine_pcm_ops,
 347        .pcm_new        = dmaengine_pcm_new,
 348};
 349
 350static const char * const dmaengine_pcm_dma_channel_names[] = {
 351        [SNDRV_PCM_STREAM_PLAYBACK] = "tx",
 352        [SNDRV_PCM_STREAM_CAPTURE] = "rx",
 353};
 354
 355static int dmaengine_pcm_request_chan_of(struct dmaengine_pcm *pcm,
 356        struct device *dev, const struct snd_dmaengine_pcm_config *config)
 357{
 358        unsigned int i;
 359        const char *name;
 360        struct dma_chan *chan;
 361
 362        if ((pcm->flags & (SND_DMAENGINE_PCM_FLAG_NO_DT |
 363                           SND_DMAENGINE_PCM_FLAG_CUSTOM_CHANNEL_NAME)) ||
 364            !dev->of_node)
 365                return 0;
 366
 367        if (config && config->dma_dev) {
 368                /*
 369                 * If this warning is seen, it probably means that your Linux
 370                 * device structure does not match your HW device structure.
 371                 * It would be best to refactor the Linux device structure to
 372                 * correctly match the HW structure.
 373                 */
 374                dev_warn(dev, "DMA channels sourced from device %s",
 375                         dev_name(config->dma_dev));
 376                dev = config->dma_dev;
 377        }
 378
 379        for (i = SNDRV_PCM_STREAM_PLAYBACK; i <= SNDRV_PCM_STREAM_CAPTURE;
 380             i++) {
 381                if (pcm->flags & SND_DMAENGINE_PCM_FLAG_HALF_DUPLEX)
 382                        name = "rx-tx";
 383                else
 384                        name = dmaengine_pcm_dma_channel_names[i];
 385                if (config && config->chan_names[i])
 386                        name = config->chan_names[i];
 387                chan = dma_request_slave_channel_reason(dev, name);
 388                if (IS_ERR(chan)) {
 389                        if (PTR_ERR(chan) == -EPROBE_DEFER)
 390                                return -EPROBE_DEFER;
 391                        pcm->chan[i] = NULL;
 392                } else {
 393                        pcm->chan[i] = chan;
 394                }
 395                if (pcm->flags & SND_DMAENGINE_PCM_FLAG_HALF_DUPLEX)
 396                        break;
 397        }
 398
 399        if (pcm->flags & SND_DMAENGINE_PCM_FLAG_HALF_DUPLEX)
 400                pcm->chan[1] = pcm->chan[0];
 401
 402        return 0;
 403}
 404
 405static void dmaengine_pcm_release_chan(struct dmaengine_pcm *pcm)
 406{
 407        unsigned int i;
 408
 409        for (i = SNDRV_PCM_STREAM_PLAYBACK; i <= SNDRV_PCM_STREAM_CAPTURE;
 410             i++) {
 411                if (!pcm->chan[i])
 412                        continue;
 413                dma_release_channel(pcm->chan[i]);
 414                if (pcm->flags & SND_DMAENGINE_PCM_FLAG_HALF_DUPLEX)
 415                        break;
 416        }
 417}
 418
 419/**
 420 * snd_dmaengine_pcm_register - Register a dmaengine based PCM device
 421 * @dev: The parent device for the PCM device
 422 * @config: Platform specific PCM configuration
 423 * @flags: Platform specific quirks
 424 */
 425int snd_dmaengine_pcm_register(struct device *dev,
 426        const struct snd_dmaengine_pcm_config *config, unsigned int flags)
 427{
 428        struct dmaengine_pcm *pcm;
 429        int ret;
 430
 431        pcm = kzalloc(sizeof(*pcm), GFP_KERNEL);
 432        if (!pcm)
 433                return -ENOMEM;
 434
 435        pcm->config = config;
 436        pcm->flags = flags;
 437
 438        ret = dmaengine_pcm_request_chan_of(pcm, dev, config);
 439        if (ret)
 440                goto err_free_dma;
 441
 442        ret = snd_soc_add_platform(dev, &pcm->platform,
 443                &dmaengine_pcm_platform);
 444        if (ret)
 445                goto err_free_dma;
 446
 447        return 0;
 448
 449err_free_dma:
 450        dmaengine_pcm_release_chan(pcm);
 451        kfree(pcm);
 452        return ret;
 453}
 454EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_register);
 455
 456/**
 457 * snd_dmaengine_pcm_unregister - Removes a dmaengine based PCM device
 458 * @dev: Parent device the PCM was register with
 459 *
 460 * Removes a dmaengine based PCM device previously registered with
 461 * snd_dmaengine_pcm_register.
 462 */
 463void snd_dmaengine_pcm_unregister(struct device *dev)
 464{
 465        struct snd_soc_platform *platform;
 466        struct dmaengine_pcm *pcm;
 467
 468        platform = snd_soc_lookup_platform(dev);
 469        if (!platform)
 470                return;
 471
 472        pcm = soc_platform_to_pcm(platform);
 473
 474        snd_soc_remove_platform(platform);
 475        dmaengine_pcm_release_chan(pcm);
 476        kfree(pcm);
 477}
 478EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_unregister);
 479
 480MODULE_LICENSE("GPL");
 481