linux/sound/soc/soc-generic-dmaengine-pcm.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2//
   3//  Copyright (C) 2013, Analog Devices Inc.
   4//      Author: Lars-Peter Clausen <lars@metafoo.de>
   5
   6#include <linux/module.h>
   7#include <linux/init.h>
   8#include <linux/dmaengine.h>
   9#include <linux/slab.h>
  10#include <sound/pcm.h>
  11#include <sound/pcm_params.h>
  12#include <sound/soc.h>
  13#include <linux/dma-mapping.h>
  14#include <linux/of.h>
  15
  16#include <sound/dmaengine_pcm.h>
  17
  18/*
  19 * The platforms dmaengine driver does not support reporting the amount of
  20 * bytes that are still left to transfer.
  21 */
  22#define SND_DMAENGINE_PCM_FLAG_NO_RESIDUE BIT(31)
  23
  24static struct device *dmaengine_dma_dev(struct dmaengine_pcm *pcm,
  25        struct snd_pcm_substream *substream)
  26{
  27        if (!pcm->chan[substream->stream])
  28                return NULL;
  29
  30        return pcm->chan[substream->stream]->device->dev;
  31}
  32
  33/**
  34 * snd_dmaengine_pcm_prepare_slave_config() - Generic prepare_slave_config callback
  35 * @substream: PCM substream
  36 * @params: hw_params
  37 * @slave_config: DMA slave config to prepare
  38 *
  39 * This function can be used as a generic prepare_slave_config callback for
  40 * platforms which make use of the snd_dmaengine_dai_dma_data struct for their
  41 * DAI DMA data. Internally the function will first call
  42 * snd_hwparams_to_dma_slave_config to fill in the slave config based on the
  43 * hw_params, followed by snd_dmaengine_set_config_from_dai_data to fill in the
  44 * remaining fields based on the DAI DMA data.
  45 */
  46int snd_dmaengine_pcm_prepare_slave_config(struct snd_pcm_substream *substream,
  47        struct snd_pcm_hw_params *params, struct dma_slave_config *slave_config)
  48{
  49        struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
  50        struct snd_dmaengine_dai_dma_data *dma_data;
  51        int ret;
  52
  53        if (rtd->num_cpus > 1) {
  54                dev_err(rtd->dev,
  55                        "%s doesn't support Multi CPU yet\n", __func__);
  56                return -EINVAL;
  57        }
  58
  59        dma_data = snd_soc_dai_get_dma_data(asoc_rtd_to_cpu(rtd, 0), substream);
  60
  61        ret = snd_hwparams_to_dma_slave_config(substream, params, slave_config);
  62        if (ret)
  63                return ret;
  64
  65        snd_dmaengine_pcm_set_config_from_dai_data(substream, dma_data,
  66                slave_config);
  67
  68        return 0;
  69}
  70EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_prepare_slave_config);
  71
  72static int dmaengine_pcm_hw_params(struct snd_soc_component *component,
  73                                   struct snd_pcm_substream *substream,
  74                                   struct snd_pcm_hw_params *params)
  75{
  76        struct dmaengine_pcm *pcm = soc_component_to_pcm(component);
  77        struct dma_chan *chan = snd_dmaengine_pcm_get_chan(substream);
  78        int (*prepare_slave_config)(struct snd_pcm_substream *substream,
  79                        struct snd_pcm_hw_params *params,
  80                        struct dma_slave_config *slave_config);
  81        struct dma_slave_config slave_config;
  82
  83        memset(&slave_config, 0, sizeof(slave_config));
  84
  85        if (!pcm->config)
  86                prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config;
  87        else
  88                prepare_slave_config = pcm->config->prepare_slave_config;
  89
  90        if (prepare_slave_config) {
  91                int ret = prepare_slave_config(substream, params, &slave_config);
  92                if (ret)
  93                        return ret;
  94
  95                ret = dmaengine_slave_config(chan, &slave_config);
  96                if (ret)
  97                        return ret;
  98        }
  99
 100        return 0;
 101}
 102
 103static int
 104dmaengine_pcm_set_runtime_hwparams(struct snd_soc_component *component,
 105                                   struct snd_pcm_substream *substream)
 106{
 107        struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
 108        struct dmaengine_pcm *pcm = soc_component_to_pcm(component);
 109        struct device *dma_dev = dmaengine_dma_dev(pcm, substream);
 110        struct dma_chan *chan = pcm->chan[substream->stream];
 111        struct snd_dmaengine_dai_dma_data *dma_data;
 112        struct snd_pcm_hardware hw;
 113
 114        if (rtd->num_cpus > 1) {
 115                dev_err(rtd->dev,
 116                        "%s doesn't support Multi CPU yet\n", __func__);
 117                return -EINVAL;
 118        }
 119
 120        if (pcm->config && pcm->config->pcm_hardware)
 121                return snd_soc_set_runtime_hwparams(substream,
 122                                pcm->config->pcm_hardware);
 123
 124        dma_data = snd_soc_dai_get_dma_data(asoc_rtd_to_cpu(rtd, 0), substream);
 125
 126        memset(&hw, 0, sizeof(hw));
 127        hw.info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
 128                        SNDRV_PCM_INFO_INTERLEAVED;
 129        hw.periods_min = 2;
 130        hw.periods_max = UINT_MAX;
 131        hw.period_bytes_min = 256;
 132        hw.period_bytes_max = dma_get_max_seg_size(dma_dev);
 133        hw.buffer_bytes_max = SIZE_MAX;
 134        hw.fifo_size = dma_data->fifo_size;
 135
 136        if (pcm->flags & SND_DMAENGINE_PCM_FLAG_NO_RESIDUE)
 137                hw.info |= SNDRV_PCM_INFO_BATCH;
 138
 139        /**
 140         * FIXME: Remove the return value check to align with the code
 141         * before adding snd_dmaengine_pcm_refine_runtime_hwparams
 142         * function.
 143         */
 144        snd_dmaengine_pcm_refine_runtime_hwparams(substream,
 145                                                  dma_data,
 146                                                  &hw,
 147                                                  chan);
 148
 149        return snd_soc_set_runtime_hwparams(substream, &hw);
 150}
 151
 152static int dmaengine_pcm_open(struct snd_soc_component *component,
 153                              struct snd_pcm_substream *substream)
 154{
 155        struct dmaengine_pcm *pcm = soc_component_to_pcm(component);
 156        struct dma_chan *chan = pcm->chan[substream->stream];
 157        int ret;
 158
 159        ret = dmaengine_pcm_set_runtime_hwparams(component, substream);
 160        if (ret)
 161                return ret;
 162
 163        return snd_dmaengine_pcm_open(substream, chan);
 164}
 165
 166static int dmaengine_pcm_close(struct snd_soc_component *component,
 167                               struct snd_pcm_substream *substream)
 168{
 169        return snd_dmaengine_pcm_close(substream);
 170}
 171
 172static int dmaengine_pcm_trigger(struct snd_soc_component *component,
 173                                 struct snd_pcm_substream *substream, int cmd)
 174{
 175        return snd_dmaengine_pcm_trigger(substream, cmd);
 176}
 177
 178static struct dma_chan *dmaengine_pcm_compat_request_channel(
 179        struct snd_soc_component *component,
 180        struct snd_soc_pcm_runtime *rtd,
 181        struct snd_pcm_substream *substream)
 182{
 183        struct dmaengine_pcm *pcm = soc_component_to_pcm(component);
 184        struct snd_dmaengine_dai_dma_data *dma_data;
 185        dma_filter_fn fn = NULL;
 186
 187        if (rtd->num_cpus > 1) {
 188                dev_err(rtd->dev,
 189                        "%s doesn't support Multi CPU yet\n", __func__);
 190                return NULL;
 191        }
 192
 193        dma_data = snd_soc_dai_get_dma_data(asoc_rtd_to_cpu(rtd, 0), substream);
 194
 195        if ((pcm->flags & SND_DMAENGINE_PCM_FLAG_HALF_DUPLEX) && pcm->chan[0])
 196                return pcm->chan[0];
 197
 198        if (pcm->config && pcm->config->compat_request_channel)
 199                return pcm->config->compat_request_channel(rtd, substream);
 200
 201        if (pcm->config)
 202                fn = pcm->config->compat_filter_fn;
 203
 204        return snd_dmaengine_pcm_request_channel(fn, dma_data->filter_data);
 205}
 206
 207static bool dmaengine_pcm_can_report_residue(struct device *dev,
 208        struct dma_chan *chan)
 209{
 210        struct dma_slave_caps dma_caps;
 211        int ret;
 212
 213        ret = dma_get_slave_caps(chan, &dma_caps);
 214        if (ret != 0) {
 215                dev_warn(dev, "Failed to get DMA channel capabilities, falling back to period counting: %d\n",
 216                         ret);
 217                return false;
 218        }
 219
 220        if (dma_caps.residue_granularity == DMA_RESIDUE_GRANULARITY_DESCRIPTOR)
 221                return false;
 222
 223        return true;
 224}
 225
 226static int dmaengine_pcm_new(struct snd_soc_component *component,
 227                             struct snd_soc_pcm_runtime *rtd)
 228{
 229        struct dmaengine_pcm *pcm = soc_component_to_pcm(component);
 230        const struct snd_dmaengine_pcm_config *config = pcm->config;
 231        struct device *dev = component->dev;
 232        size_t prealloc_buffer_size;
 233        size_t max_buffer_size;
 234        unsigned int i;
 235
 236        if (config && config->prealloc_buffer_size) {
 237                prealloc_buffer_size = config->prealloc_buffer_size;
 238                max_buffer_size = config->pcm_hardware->buffer_bytes_max;
 239        } else {
 240                prealloc_buffer_size = 512 * 1024;
 241                max_buffer_size = SIZE_MAX;
 242        }
 243
 244        for_each_pcm_streams(i) {
 245                struct snd_pcm_substream *substream = rtd->pcm->streams[i].substream;
 246                if (!substream)
 247                        continue;
 248
 249                if (!pcm->chan[i] && config && config->chan_names[i])
 250                        pcm->chan[i] = dma_request_slave_channel(dev,
 251                                config->chan_names[i]);
 252
 253                if (!pcm->chan[i] && (pcm->flags & SND_DMAENGINE_PCM_FLAG_COMPAT)) {
 254                        pcm->chan[i] = dmaengine_pcm_compat_request_channel(
 255                                component, rtd, substream);
 256                }
 257
 258                if (!pcm->chan[i]) {
 259                        dev_err(component->dev,
 260                                "Missing dma channel for stream: %d\n", i);
 261                        return -EINVAL;
 262                }
 263
 264                snd_pcm_set_managed_buffer(substream,
 265                                SNDRV_DMA_TYPE_DEV_IRAM,
 266                                dmaengine_dma_dev(pcm, substream),
 267                                prealloc_buffer_size,
 268                                max_buffer_size);
 269
 270                if (!dmaengine_pcm_can_report_residue(dev, pcm->chan[i]))
 271                        pcm->flags |= SND_DMAENGINE_PCM_FLAG_NO_RESIDUE;
 272
 273                if (rtd->pcm->streams[i].pcm->name[0] == '\0') {
 274                        strscpy_pad(rtd->pcm->streams[i].pcm->name,
 275                                    rtd->pcm->streams[i].pcm->id,
 276                                    sizeof(rtd->pcm->streams[i].pcm->name));
 277                }
 278        }
 279
 280        return 0;
 281}
 282
 283static snd_pcm_uframes_t dmaengine_pcm_pointer(
 284        struct snd_soc_component *component,
 285        struct snd_pcm_substream *substream)
 286{
 287        struct dmaengine_pcm *pcm = soc_component_to_pcm(component);
 288
 289        if (pcm->flags & SND_DMAENGINE_PCM_FLAG_NO_RESIDUE)
 290                return snd_dmaengine_pcm_pointer_no_residue(substream);
 291        else
 292                return snd_dmaengine_pcm_pointer(substream);
 293}
 294
 295static int dmaengine_copy_user(struct snd_soc_component *component,
 296                               struct snd_pcm_substream *substream,
 297                               int channel, unsigned long hwoff,
 298                               void __user *buf, unsigned long bytes)
 299{
 300        struct snd_pcm_runtime *runtime = substream->runtime;
 301        struct dmaengine_pcm *pcm = soc_component_to_pcm(component);
 302        int (*process)(struct snd_pcm_substream *substream,
 303                       int channel, unsigned long hwoff,
 304                       void *buf, unsigned long bytes) = pcm->config->process;
 305        bool is_playback = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
 306        void *dma_ptr = runtime->dma_area + hwoff +
 307                        channel * (runtime->dma_bytes / runtime->channels);
 308
 309        if (is_playback)
 310                if (copy_from_user(dma_ptr, buf, bytes))
 311                        return -EFAULT;
 312
 313        if (process) {
 314                int ret = process(substream, channel, hwoff, (__force void *)buf, bytes);
 315                if (ret < 0)
 316                        return ret;
 317        }
 318
 319        if (!is_playback)
 320                if (copy_to_user(buf, dma_ptr, bytes))
 321                        return -EFAULT;
 322
 323        return 0;
 324}
 325
 326static const struct snd_soc_component_driver dmaengine_pcm_component = {
 327        .name           = SND_DMAENGINE_PCM_DRV_NAME,
 328        .probe_order    = SND_SOC_COMP_ORDER_LATE,
 329        .open           = dmaengine_pcm_open,
 330        .close          = dmaengine_pcm_close,
 331        .hw_params      = dmaengine_pcm_hw_params,
 332        .trigger        = dmaengine_pcm_trigger,
 333        .pointer        = dmaengine_pcm_pointer,
 334        .pcm_construct  = dmaengine_pcm_new,
 335};
 336
 337static const struct snd_soc_component_driver dmaengine_pcm_component_process = {
 338        .name           = SND_DMAENGINE_PCM_DRV_NAME,
 339        .probe_order    = SND_SOC_COMP_ORDER_LATE,
 340        .open           = dmaengine_pcm_open,
 341        .close          = dmaengine_pcm_close,
 342        .hw_params      = dmaengine_pcm_hw_params,
 343        .trigger        = dmaengine_pcm_trigger,
 344        .pointer        = dmaengine_pcm_pointer,
 345        .copy_user      = dmaengine_copy_user,
 346        .pcm_construct  = dmaengine_pcm_new,
 347};
 348
 349static const char * const dmaengine_pcm_dma_channel_names[] = {
 350        [SNDRV_PCM_STREAM_PLAYBACK] = "tx",
 351        [SNDRV_PCM_STREAM_CAPTURE] = "rx",
 352};
 353
 354static int dmaengine_pcm_request_chan_of(struct dmaengine_pcm *pcm,
 355        struct device *dev, const struct snd_dmaengine_pcm_config *config)
 356{
 357        unsigned int i;
 358        const char *name;
 359        struct dma_chan *chan;
 360
 361        if ((pcm->flags & SND_DMAENGINE_PCM_FLAG_NO_DT) || (!dev->of_node &&
 362            !(config && config->dma_dev && config->dma_dev->of_node)))
 363                return 0;
 364
 365        if (config && config->dma_dev) {
 366                /*
 367                 * If this warning is seen, it probably means that your Linux
 368                 * device structure does not match your HW device structure.
 369                 * It would be best to refactor the Linux device structure to
 370                 * correctly match the HW structure.
 371                 */
 372                dev_warn(dev, "DMA channels sourced from device %s",
 373                         dev_name(config->dma_dev));
 374                dev = config->dma_dev;
 375        }
 376
 377        for_each_pcm_streams(i) {
 378                if (pcm->flags & SND_DMAENGINE_PCM_FLAG_HALF_DUPLEX)
 379                        name = "rx-tx";
 380                else
 381                        name = dmaengine_pcm_dma_channel_names[i];
 382                if (config && config->chan_names[i])
 383                        name = config->chan_names[i];
 384                chan = dma_request_chan(dev, name);
 385                if (IS_ERR(chan)) {
 386                        /*
 387                         * Only report probe deferral errors, channels
 388                         * might not be present for devices that
 389                         * support only TX or only RX.
 390                         */
 391                        if (PTR_ERR(chan) == -EPROBE_DEFER)
 392                                return -EPROBE_DEFER;
 393                        pcm->chan[i] = NULL;
 394                } else {
 395                        pcm->chan[i] = chan;
 396                }
 397                if (pcm->flags & SND_DMAENGINE_PCM_FLAG_HALF_DUPLEX)
 398                        break;
 399        }
 400
 401        if (pcm->flags & SND_DMAENGINE_PCM_FLAG_HALF_DUPLEX)
 402                pcm->chan[1] = pcm->chan[0];
 403
 404        return 0;
 405}
 406
 407static void dmaengine_pcm_release_chan(struct dmaengine_pcm *pcm)
 408{
 409        unsigned int i;
 410
 411        for_each_pcm_streams(i) {
 412                if (!pcm->chan[i])
 413                        continue;
 414                dma_release_channel(pcm->chan[i]);
 415                if (pcm->flags & SND_DMAENGINE_PCM_FLAG_HALF_DUPLEX)
 416                        break;
 417        }
 418}
 419
 420/**
 421 * snd_dmaengine_pcm_register - Register a dmaengine based PCM device
 422 * @dev: The parent device for the PCM device
 423 * @config: Platform specific PCM configuration
 424 * @flags: Platform specific quirks
 425 */
 426int snd_dmaengine_pcm_register(struct device *dev,
 427        const struct snd_dmaengine_pcm_config *config, unsigned int flags)
 428{
 429        const struct snd_soc_component_driver *driver;
 430        struct dmaengine_pcm *pcm;
 431        int ret;
 432
 433        pcm = kzalloc(sizeof(*pcm), GFP_KERNEL);
 434        if (!pcm)
 435                return -ENOMEM;
 436
 437#ifdef CONFIG_DEBUG_FS
 438        pcm->component.debugfs_prefix = "dma";
 439#endif
 440        pcm->config = config;
 441        pcm->flags = flags;
 442
 443        ret = dmaengine_pcm_request_chan_of(pcm, dev, config);
 444        if (ret)
 445                goto err_free_dma;
 446
 447        if (config && config->process)
 448                driver = &dmaengine_pcm_component_process;
 449        else
 450                driver = &dmaengine_pcm_component;
 451
 452        ret = snd_soc_component_initialize(&pcm->component, driver, dev);
 453        if (ret)
 454                goto err_free_dma;
 455
 456        ret = snd_soc_add_component(&pcm->component, NULL, 0);
 457        if (ret)
 458                goto err_free_dma;
 459
 460        return 0;
 461
 462err_free_dma:
 463        dmaengine_pcm_release_chan(pcm);
 464        kfree(pcm);
 465        return ret;
 466}
 467EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_register);
 468
 469/**
 470 * snd_dmaengine_pcm_unregister - Removes a dmaengine based PCM device
 471 * @dev: Parent device the PCM was register with
 472 *
 473 * Removes a dmaengine based PCM device previously registered with
 474 * snd_dmaengine_pcm_register.
 475 */
 476void snd_dmaengine_pcm_unregister(struct device *dev)
 477{
 478        struct snd_soc_component *component;
 479        struct dmaengine_pcm *pcm;
 480
 481        component = snd_soc_lookup_component(dev, SND_DMAENGINE_PCM_DRV_NAME);
 482        if (!component)
 483                return;
 484
 485        pcm = soc_component_to_pcm(component);
 486
 487        snd_soc_unregister_component_by_driver(dev, component->driver);
 488        dmaengine_pcm_release_chan(pcm);
 489        kfree(pcm);
 490}
 491EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_unregister);
 492
 493MODULE_LICENSE("GPL");
 494