diff --git a/sound/soc/tegra/Kconfig b/sound/soc/tegra/Kconfig index c1c8e955f4d31b2a79be704d7dda736bd1e24010..7b6a1ebd197a616443dfde45f94152d23c721337 100644 --- a/sound/soc/tegra/Kconfig +++ b/sound/soc/tegra/Kconfig @@ -1,7 +1,8 @@ config SND_SOC_TEGRA tristate "SoC Audio for the Tegra System-on-Chip" - depends on ARCH_TEGRA && TEGRA_SYSTEM_DMA + depends on ARCH_TEGRA && (TEGRA_SYSTEM_DMA || TEGRA20_APB_DMA) select REGMAP_MMIO + select SND_SOC_DMAENGINE_PCM if TEGRA20_APB_DMA help Say Y or M here if you want support for SoC audio on Tegra. diff --git a/sound/soc/tegra/tegra_pcm.c b/sound/soc/tegra/tegra_pcm.c index 127348dc09b10d36782f5e3538bbefd02f494612..5658bcec1931ce5a76a75a32b020730f41da8c13 100644 --- a/sound/soc/tegra/tegra_pcm.c +++ b/sound/soc/tegra/tegra_pcm.c @@ -36,6 +36,7 @@ #include #include #include +#include #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) { diff --git a/sound/soc/tegra/tegra_pcm.h b/sound/soc/tegra/tegra_pcm.h index 985d418a35e7a7e5e092ddd6b0eac36f8c73cf5b..a3a450352dcf7cb005ce410503a53f9445e61615 100644 --- a/sound/soc/tegra/tegra_pcm.h +++ b/sound/soc/tegra/tegra_pcm.h @@ -40,6 +40,7 @@ struct tegra_pcm_dma_params { unsigned long req_sel; }; +#if defined(CONFIG_TEGRA_SYSTEM_DMA) struct tegra_runtime_data { struct snd_pcm_substream *substream; spinlock_t lock; @@ -51,6 +52,7 @@ struct tegra_runtime_data { struct tegra_dma_req dma_req[2]; struct tegra_dma_channel *dma_chan; }; +#endif int tegra_pcm_platform_register(struct device *dev); void tegra_pcm_platform_unregister(struct device *dev);