diff options
Diffstat (limited to 'sound/soc/dwc/designware_pcm.c')
-rw-r--r-- | sound/soc/dwc/designware_pcm.c | 284 |
1 files changed, 0 insertions, 284 deletions
diff --git a/sound/soc/dwc/designware_pcm.c b/sound/soc/dwc/designware_pcm.c deleted file mode 100644 index 459ec861e6b6..000000000000 --- a/sound/soc/dwc/designware_pcm.c +++ /dev/null @@ -1,284 +0,0 @@ -/* - * ALSA SoC Synopsys PIO PCM for I2S driver - * - * sound/soc/dwc/designware_pcm.c - * - * Copyright (C) 2016 Synopsys - * Jose Abreu <joabreu@synopsys.com> - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. - */ - -#include <linux/io.h> -#include <linux/rcupdate.h> -#include <sound/pcm.h> -#include <sound/pcm_params.h> -#include "local.h" - -#define BUFFER_BYTES_MAX (3 * 2 * 8 * PERIOD_BYTES_MIN) -#define PERIOD_BYTES_MIN 4096 -#define PERIODS_MIN 2 - -#define dw_pcm_tx_fn(sample_bits) \ -static unsigned int dw_pcm_tx_##sample_bits(struct dw_i2s_dev *dev, \ - struct snd_pcm_runtime *runtime, unsigned int tx_ptr, \ - bool *period_elapsed) \ -{ \ - const u##sample_bits (*p)[2] = (void *)runtime->dma_area; \ - unsigned int period_pos = tx_ptr % runtime->period_size; \ - int i; \ -\ - for (i = 0; i < dev->fifo_th; i++) { \ - iowrite32(p[tx_ptr][0], dev->i2s_base + LRBR_LTHR(0)); \ - iowrite32(p[tx_ptr][1], dev->i2s_base + RRBR_RTHR(0)); \ - period_pos++; \ - if (++tx_ptr >= runtime->buffer_size) \ - tx_ptr = 0; \ - } \ - *period_elapsed = period_pos >= runtime->period_size; \ - return tx_ptr; \ -} - -#define dw_pcm_rx_fn(sample_bits) \ -static unsigned int dw_pcm_rx_##sample_bits(struct dw_i2s_dev *dev, \ - struct snd_pcm_runtime *runtime, unsigned int rx_ptr, \ - bool *period_elapsed) \ -{ \ - u##sample_bits (*p)[2] = (void *)runtime->dma_area; \ - unsigned int period_pos = rx_ptr % runtime->period_size; \ - int i; \ -\ - for (i = 0; i < dev->fifo_th; i++) { \ - p[rx_ptr][0] = ioread32(dev->i2s_base + LRBR_LTHR(0)); \ - p[rx_ptr][1] = ioread32(dev->i2s_base + RRBR_RTHR(0)); \ - period_pos++; \ - if (++rx_ptr >= runtime->buffer_size) \ - rx_ptr = 0; \ - } \ - *period_elapsed = period_pos >= runtime->period_size; \ - return rx_ptr; \ -} - -dw_pcm_tx_fn(16); -dw_pcm_tx_fn(32); -dw_pcm_rx_fn(16); -dw_pcm_rx_fn(32); - -#undef dw_pcm_tx_fn -#undef dw_pcm_rx_fn - -static const struct snd_pcm_hardware dw_pcm_hardware = { - .info = SNDRV_PCM_INFO_INTERLEAVED | - SNDRV_PCM_INFO_MMAP | - SNDRV_PCM_INFO_MMAP_VALID | - SNDRV_PCM_INFO_BLOCK_TRANSFER, - .rates = SNDRV_PCM_RATE_32000 | - SNDRV_PCM_RATE_44100 | - SNDRV_PCM_RATE_48000, - .rate_min = 32000, - .rate_max = 48000, - .formats = SNDRV_PCM_FMTBIT_S16_LE | - SNDRV_PCM_FMTBIT_S24_LE | - SNDRV_PCM_FMTBIT_S32_LE, - .channels_min = 2, - .channels_max = 2, - .buffer_bytes_max = BUFFER_BYTES_MAX, - .period_bytes_min = PERIOD_BYTES_MIN, - .period_bytes_max = BUFFER_BYTES_MAX / PERIODS_MIN, - .periods_min = PERIODS_MIN, - .periods_max = BUFFER_BYTES_MAX / PERIOD_BYTES_MIN, - .fifo_size = 16, -}; - -static void dw_pcm_transfer(struct dw_i2s_dev *dev, bool push) -{ - struct snd_pcm_substream *substream; - bool active, period_elapsed; - - rcu_read_lock(); - if (push) - substream = rcu_dereference(dev->tx_substream); - else - substream = rcu_dereference(dev->rx_substream); - active = substream && snd_pcm_running(substream); - if (active) { - unsigned int ptr; - unsigned int new_ptr; - - if (push) { - ptr = READ_ONCE(dev->tx_ptr); - new_ptr = dev->tx_fn(dev, substream->runtime, ptr, - &period_elapsed); - cmpxchg(&dev->tx_ptr, ptr, new_ptr); - } else { - ptr = READ_ONCE(dev->rx_ptr); - new_ptr = dev->rx_fn(dev, substream->runtime, ptr, - &period_elapsed); - cmpxchg(&dev->rx_ptr, ptr, new_ptr); - } - - if (period_elapsed) - snd_pcm_period_elapsed(substream); - } - rcu_read_unlock(); -} - -void dw_pcm_push_tx(struct dw_i2s_dev *dev) -{ - dw_pcm_transfer(dev, true); -} -EXPORT_SYMBOL_GPL(dw_pcm_push_tx); - -void dw_pcm_pop_rx(struct dw_i2s_dev *dev) -{ - dw_pcm_transfer(dev, false); -} -EXPORT_SYMBOL_GPL(dw_pcm_pop_rx); - -static int dw_pcm_open(struct snd_pcm_substream *substream) -{ - struct snd_pcm_runtime *runtime = substream->runtime; - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(rtd->cpu_dai); - - snd_soc_set_runtime_hwparams(substream, &dw_pcm_hardware); - snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); - runtime->private_data = dev; - - return 0; -} - -static int dw_pcm_close(struct snd_pcm_substream *substream) -{ - synchronize_rcu(); - return 0; -} - -static int dw_pcm_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *hw_params) -{ - struct snd_pcm_runtime *runtime = substream->runtime; - struct dw_i2s_dev *dev = runtime->private_data; - int ret; - - switch (params_channels(hw_params)) { - case 2: - break; - default: - dev_err(dev->dev, "invalid channels number\n"); - return -EINVAL; - } - - switch (params_format(hw_params)) { - case SNDRV_PCM_FORMAT_S16_LE: - dev->tx_fn = dw_pcm_tx_16; - dev->rx_fn = dw_pcm_rx_16; - break; - case SNDRV_PCM_FORMAT_S24_LE: - case SNDRV_PCM_FORMAT_S32_LE: - dev->tx_fn = dw_pcm_tx_32; - dev->rx_fn = dw_pcm_rx_32; - break; - default: - dev_err(dev->dev, "invalid format\n"); - return -EINVAL; - } - - ret = snd_pcm_lib_malloc_pages(substream, - params_buffer_bytes(hw_params)); - if (ret < 0) - return ret; - else - return 0; -} - -static int dw_pcm_hw_free(struct snd_pcm_substream *substream) -{ - return snd_pcm_lib_free_pages(substream); -} - -static int dw_pcm_trigger(struct snd_pcm_substream *substream, int cmd) -{ - struct snd_pcm_runtime *runtime = substream->runtime; - struct dw_i2s_dev *dev = runtime->private_data; - int ret = 0; - - switch (cmd) { - case SNDRV_PCM_TRIGGER_START: - case SNDRV_PCM_TRIGGER_RESUME: - case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - WRITE_ONCE(dev->tx_ptr, 0); - rcu_assign_pointer(dev->tx_substream, substream); - } else { - WRITE_ONCE(dev->rx_ptr, 0); - rcu_assign_pointer(dev->rx_substream, substream); - } - break; - case SNDRV_PCM_TRIGGER_STOP: - case SNDRV_PCM_TRIGGER_SUSPEND: - case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - rcu_assign_pointer(dev->tx_substream, NULL); - else - rcu_assign_pointer(dev->rx_substream, NULL); - break; - default: - ret = -EINVAL; - break; - } - - return ret; -} - -static snd_pcm_uframes_t dw_pcm_pointer(struct snd_pcm_substream *substream) -{ - struct snd_pcm_runtime *runtime = substream->runtime; - struct dw_i2s_dev *dev = runtime->private_data; - snd_pcm_uframes_t pos; - - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - pos = READ_ONCE(dev->tx_ptr); - else - pos = READ_ONCE(dev->rx_ptr); - - return pos < runtime->buffer_size ? pos : 0; -} - -static int dw_pcm_new(struct snd_soc_pcm_runtime *rtd) -{ - size_t size = dw_pcm_hardware.buffer_bytes_max; - - return snd_pcm_lib_preallocate_pages_for_all(rtd->pcm, - SNDRV_DMA_TYPE_CONTINUOUS, - snd_dma_continuous_data(GFP_KERNEL), size, size); -} - -static void dw_pcm_free(struct snd_pcm *pcm) -{ - snd_pcm_lib_preallocate_free_for_all(pcm); -} - -static const struct snd_pcm_ops dw_pcm_ops = { - .open = dw_pcm_open, - .close = dw_pcm_close, - .ioctl = snd_pcm_lib_ioctl, - .hw_params = dw_pcm_hw_params, - .hw_free = dw_pcm_hw_free, - .trigger = dw_pcm_trigger, - .pointer = dw_pcm_pointer, -}; - -static const struct snd_soc_platform_driver dw_pcm_platform = { - .pcm_new = dw_pcm_new, - .pcm_free = dw_pcm_free, - .ops = &dw_pcm_ops, -}; - -int dw_pcm_register(struct platform_device *pdev) -{ - return devm_snd_soc_register_platform(&pdev->dev, &dw_pcm_platform); -} -EXPORT_SYMBOL_GPL(dw_pcm_register); |