|
@@ -36,6 +36,7 @@
|
|
|
#include <sound/pcm.h>
|
|
|
#include <sound/pcm_params.h>
|
|
|
#include <sound/soc.h>
|
|
|
+#include <sound/dmaengine_pcm.h>
|
|
|
|
|
|
#include "tegra_pcm.h"
|
|
|
|
|
@@ -56,6 +57,7 @@ static const struct snd_pcm_hardware tegra_pcm_hardware = {
|
|
|
.fifo_size = 4,
|
|
|
};
|
|
|
|
|
|
+#if defined(CONFIG_TEGRA_SYSTEM_DMA)
|
|
|
static void tegra_pcm_queue_dma(struct tegra_runtime_data *prtd)
|
|
|
{
|
|
|
struct snd_pcm_substream *substream = prtd->substream;
|
|
@@ -285,6 +287,119 @@ static struct snd_pcm_ops tegra_pcm_ops = {
|
|
|
.pointer = tegra_pcm_pointer,
|
|
|
.mmap = tegra_pcm_mmap,
|
|
|
};
|
|
|
+#else
|
|
|
+static int tegra_pcm_open(struct snd_pcm_substream *substream)
|
|
|
+{
|
|
|
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
|
|
+ struct device *dev = rtd->platform->dev;
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ /* Set HW params now that initialization is complete */
|
|
|
+ snd_soc_set_runtime_hwparams(substream, &tegra_pcm_hardware);
|
|
|
+
|
|
|
+ ret = snd_dmaengine_pcm_open(substream, NULL, NULL);
|
|
|
+ if (ret) {
|
|
|
+ dev_err(dev, "dmaengine pcm open failed with err %d\n", ret);
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int tegra_pcm_close(struct snd_pcm_substream *substream)
|
|
|
+{
|
|
|
+ snd_dmaengine_pcm_close(substream);
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int tegra_pcm_hw_params(struct snd_pcm_substream *substream,
|
|
|
+ struct snd_pcm_hw_params *params)
|
|
|
+{
|
|
|
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
|
|
+ struct device *dev = rtd->platform->dev;
|
|
|
+ struct dma_chan *chan = snd_dmaengine_pcm_get_chan(substream);
|
|
|
+ struct tegra_pcm_dma_params *dmap;
|
|
|
+ struct dma_slave_config slave_config;
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ dmap = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
|
|
|
+
|
|
|
+ ret = snd_hwparams_to_dma_slave_config(substream, params,
|
|
|
+ &slave_config);
|
|
|
+ if (ret) {
|
|
|
+ dev_err(dev, "hw params config failed with err %d\n", ret);
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
|
|
+ slave_config.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
|
|
|
+ slave_config.dst_addr = dmap->addr;
|
|
|
+ slave_config.src_maxburst = 0;
|
|
|
+ } else {
|
|
|
+ slave_config.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
|
|
|
+ slave_config.src_addr = dmap->addr;
|
|
|
+ slave_config.dst_maxburst = 0;
|
|
|
+ }
|
|
|
+ slave_config.slave_id = dmap->req_sel;
|
|
|
+
|
|
|
+ ret = dmaengine_slave_config(chan, &slave_config);
|
|
|
+ if (ret < 0) {
|
|
|
+ dev_err(dev, "dma slave config failed with err %d\n", ret);
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
+
|
|
|
+ snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int tegra_pcm_hw_free(struct snd_pcm_substream *substream)
|
|
|
+{
|
|
|
+ snd_pcm_set_runtime_buffer(substream, NULL);
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int tegra_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
|
|
|
+{
|
|
|
+ switch (cmd) {
|
|
|
+ case SNDRV_PCM_TRIGGER_START:
|
|
|
+ case SNDRV_PCM_TRIGGER_RESUME:
|
|
|
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
|
|
|
+ return snd_dmaengine_pcm_trigger(substream,
|
|
|
+ SNDRV_PCM_TRIGGER_START);
|
|
|
+
|
|
|
+ case SNDRV_PCM_TRIGGER_STOP:
|
|
|
+ case SNDRV_PCM_TRIGGER_SUSPEND:
|
|
|
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
|
|
|
+ return snd_dmaengine_pcm_trigger(substream,
|
|
|
+ SNDRV_PCM_TRIGGER_STOP);
|
|
|
+ default:
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int tegra_pcm_mmap(struct snd_pcm_substream *substream,
|
|
|
+ struct vm_area_struct *vma)
|
|
|
+{
|
|
|
+ struct snd_pcm_runtime *runtime = substream->runtime;
|
|
|
+
|
|
|
+ return dma_mmap_writecombine(substream->pcm->card->dev, vma,
|
|
|
+ runtime->dma_area,
|
|
|
+ runtime->dma_addr,
|
|
|
+ runtime->dma_bytes);
|
|
|
+}
|
|
|
+
|
|
|
+static struct snd_pcm_ops tegra_pcm_ops = {
|
|
|
+ .open = tegra_pcm_open,
|
|
|
+ .close = tegra_pcm_close,
|
|
|
+ .ioctl = snd_pcm_lib_ioctl,
|
|
|
+ .hw_params = tegra_pcm_hw_params,
|
|
|
+ .hw_free = tegra_pcm_hw_free,
|
|
|
+ .trigger = tegra_pcm_trigger,
|
|
|
+ .pointer = snd_dmaengine_pcm_pointer,
|
|
|
+ .mmap = tegra_pcm_mmap,
|
|
|
+};
|
|
|
+#endif
|
|
|
|
|
|
static int tegra_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
|
|
|
{
|