.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
---|
1 | 2 | /* |
---|
2 | 3 | * Copyright (C) 2012, Analog Devices Inc. |
---|
3 | 4 | * Author: Lars-Peter Clausen <lars@metafoo.de> |
---|
.. | .. |
---|
7 | 8 | * mxs-pcm.c, Copyright (C) 2011 Freescale Semiconductor, Inc. |
---|
8 | 9 | * ep93xx-pcm.c, Copyright (C) 2006 Lennert Buytenhek <buytenh@wantstofly.org> |
---|
9 | 10 | * Copyright (C) 2006 Applied Data Systems |
---|
10 | | - * |
---|
11 | | - * This program is free software; you can redistribute it and/or modify it |
---|
12 | | - * under the terms of the GNU General Public License as published by the |
---|
13 | | - * Free Software Foundation; either version 2 of the License, or (at your |
---|
14 | | - * option) any later version. |
---|
15 | | - * |
---|
16 | | - * You should have received a copy of the GNU General Public License along |
---|
17 | | - * with this program; if not, write to the Free Software Foundation, Inc., |
---|
18 | | - * 675 Mass Ave, Cambridge, MA 02139, USA. |
---|
19 | | - * |
---|
20 | 11 | */ |
---|
21 | 12 | #include <linux/module.h> |
---|
22 | 13 | #include <linux/init.h> |
---|
.. | .. |
---|
27 | 18 | #include <sound/soc.h> |
---|
28 | 19 | |
---|
29 | 20 | #include <sound/dmaengine_pcm.h> |
---|
| 21 | +#include "pcm_local.h" |
---|
30 | 22 | |
---|
31 | 23 | struct dmaengine_pcm_runtime_data { |
---|
32 | 24 | struct dma_chan *dma_chan; |
---|
.. | .. |
---|
134 | 126 | } |
---|
135 | 127 | |
---|
136 | 128 | slave_config->slave_id = dma_data->slave_id; |
---|
| 129 | + slave_config->peripheral_config = dma_data->peripheral_config; |
---|
| 130 | + slave_config->peripheral_size = dma_data->peripheral_size; |
---|
137 | 131 | } |
---|
138 | 132 | EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_set_config_from_dai_data); |
---|
139 | 133 | |
---|
140 | 134 | static void dmaengine_pcm_dma_complete(void *arg) |
---|
141 | 135 | { |
---|
| 136 | + unsigned int new_pos; |
---|
142 | 137 | struct snd_pcm_substream *substream = arg; |
---|
143 | | - struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream); |
---|
| 138 | + struct dmaengine_pcm_runtime_data *prtd; |
---|
144 | 139 | |
---|
145 | | - prtd->pos += snd_pcm_lib_period_bytes(substream); |
---|
146 | | - if (prtd->pos >= snd_pcm_lib_buffer_bytes(substream)) |
---|
147 | | - prtd->pos = 0; |
---|
| 140 | + snd_pcm_stream_lock_irq(substream); |
---|
| 141 | + if (PCM_RUNTIME_CHECK(substream)) { |
---|
| 142 | + snd_pcm_stream_unlock_irq(substream); |
---|
| 143 | + return; |
---|
| 144 | + } |
---|
| 145 | + |
---|
| 146 | + prtd = substream_to_prtd(substream); |
---|
| 147 | + |
---|
| 148 | + new_pos = prtd->pos + snd_pcm_lib_period_bytes(substream); |
---|
| 149 | + if (new_pos >= snd_pcm_lib_buffer_bytes(substream)) |
---|
| 150 | + new_pos = 0; |
---|
| 151 | + prtd->pos = new_pos; |
---|
| 152 | + snd_pcm_stream_unlock_irq(substream); |
---|
148 | 153 | |
---|
149 | 154 | snd_pcm_period_elapsed(substream); |
---|
150 | 155 | } |
---|
.. | .. |
---|
249 | 254 | snd_pcm_uframes_t snd_dmaengine_pcm_pointer(struct snd_pcm_substream *substream) |
---|
250 | 255 | { |
---|
251 | 256 | struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream); |
---|
| 257 | + struct snd_pcm_runtime *runtime = substream->runtime; |
---|
252 | 258 | struct dma_tx_state state; |
---|
253 | 259 | unsigned int buf_size; |
---|
254 | 260 | unsigned int pos = 0; |
---|
.. | .. |
---|
258 | 264 | if (state.residue > 0 && state.residue <= buf_size) |
---|
259 | 265 | pos = buf_size - state.residue; |
---|
260 | 266 | |
---|
261 | | - return bytes_to_frames(substream->runtime, pos); |
---|
| 267 | + runtime->delay = bytes_to_frames(runtime, |
---|
| 268 | + state.in_flight_bytes); |
---|
| 269 | + |
---|
| 270 | + return bytes_to_frames(runtime, pos); |
---|
262 | 271 | } |
---|
263 | 272 | EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_pointer); |
---|
264 | 273 | |
---|
.. | .. |
---|
358 | 367 | EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_close); |
---|
359 | 368 | |
---|
360 | 369 | /** |
---|
361 | | - * snd_dmaengine_pcm_release_chan_close - Close a dmaengine based PCM substream and release channel |
---|
| 370 | + * snd_dmaengine_pcm_close_release_chan - Close a dmaengine based PCM |
---|
| 371 | + * substream and release channel |
---|
362 | 372 | * @substream: PCM substream |
---|
363 | 373 | * |
---|
364 | 374 | * Releases the DMA channel associated with the PCM substream. |
---|
.. | .. |
---|
375 | 385 | } |
---|
376 | 386 | EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_close_release_chan); |
---|
377 | 387 | |
---|
| 388 | +/** |
---|
| 389 | + * snd_dmaengine_pcm_refine_runtime_hwparams - Refine runtime hw params |
---|
| 390 | + * @substream: PCM substream |
---|
| 391 | + * @dma_data: DAI DMA data |
---|
| 392 | + * @hw: PCM hw params |
---|
| 393 | + * @chan: DMA channel to use for data transfers |
---|
| 394 | + * |
---|
| 395 | + * Returns 0 on success, a negative error code otherwise. |
---|
| 396 | + * |
---|
| 397 | + * This function will query DMA capability, then refine the pcm hardware |
---|
| 398 | + * parameters. |
---|
| 399 | + */ |
---|
| 400 | +int snd_dmaengine_pcm_refine_runtime_hwparams( |
---|
| 401 | + struct snd_pcm_substream *substream, |
---|
| 402 | + struct snd_dmaengine_dai_dma_data *dma_data, |
---|
| 403 | + struct snd_pcm_hardware *hw, |
---|
| 404 | + struct dma_chan *chan) |
---|
| 405 | +{ |
---|
| 406 | + struct dma_slave_caps dma_caps; |
---|
| 407 | + u32 addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) | |
---|
| 408 | + BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) | |
---|
| 409 | + BIT(DMA_SLAVE_BUSWIDTH_4_BYTES); |
---|
| 410 | + snd_pcm_format_t i; |
---|
| 411 | + int ret = 0; |
---|
| 412 | + |
---|
| 413 | + if (!hw || !chan || !dma_data) |
---|
| 414 | + return -EINVAL; |
---|
| 415 | + |
---|
| 416 | + ret = dma_get_slave_caps(chan, &dma_caps); |
---|
| 417 | + if (ret == 0) { |
---|
| 418 | + if (dma_caps.cmd_pause && dma_caps.cmd_resume) |
---|
| 419 | + hw->info |= SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME; |
---|
| 420 | + if (dma_caps.residue_granularity <= DMA_RESIDUE_GRANULARITY_SEGMENT) |
---|
| 421 | + hw->info |= SNDRV_PCM_INFO_BATCH; |
---|
| 422 | + |
---|
| 423 | + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) |
---|
| 424 | + addr_widths = dma_caps.dst_addr_widths; |
---|
| 425 | + else |
---|
| 426 | + addr_widths = dma_caps.src_addr_widths; |
---|
| 427 | + } |
---|
| 428 | + |
---|
| 429 | + /* |
---|
| 430 | + * If SND_DMAENGINE_PCM_DAI_FLAG_PACK is set keep |
---|
| 431 | + * hw.formats set to 0, meaning no restrictions are in place. |
---|
| 432 | + * In this case it's the responsibility of the DAI driver to |
---|
| 433 | + * provide the supported format information. |
---|
| 434 | + */ |
---|
| 435 | + if (!(dma_data->flags & SND_DMAENGINE_PCM_DAI_FLAG_PACK)) |
---|
| 436 | + /* |
---|
| 437 | + * Prepare formats mask for valid/allowed sample types. If the |
---|
| 438 | + * dma does not have support for the given physical word size, |
---|
| 439 | + * it needs to be masked out so user space can not use the |
---|
| 440 | + * format which produces corrupted audio. |
---|
| 441 | + * In case the dma driver does not implement the slave_caps the |
---|
| 442 | + * default assumption is that it supports 1, 2 and 4 bytes |
---|
| 443 | + * widths. |
---|
| 444 | + */ |
---|
| 445 | + pcm_for_each_format(i) { |
---|
| 446 | + int bits = snd_pcm_format_physical_width(i); |
---|
| 447 | + |
---|
| 448 | + /* |
---|
| 449 | + * Enable only samples with DMA supported physical |
---|
| 450 | + * widths |
---|
| 451 | + */ |
---|
| 452 | + switch (bits) { |
---|
| 453 | + case 8: |
---|
| 454 | + case 16: |
---|
| 455 | + case 24: |
---|
| 456 | + case 32: |
---|
| 457 | + case 64: |
---|
| 458 | + if (addr_widths & (1 << (bits / 8))) |
---|
| 459 | + hw->formats |= pcm_format_to_bits(i); |
---|
| 460 | + break; |
---|
| 461 | + default: |
---|
| 462 | + /* Unsupported types */ |
---|
| 463 | + break; |
---|
| 464 | + } |
---|
| 465 | + } |
---|
| 466 | + |
---|
| 467 | + return ret; |
---|
| 468 | +} |
---|
| 469 | +EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_refine_runtime_hwparams); |
---|
| 470 | + |
---|
378 | 471 | MODULE_LICENSE("GPL"); |
---|