From 24c1b0906d11919aae53d840b385be356183b4be Mon Sep 17 00:00:00 2001 From: Nitin Pai Date: Thu, 26 Apr 2012 19:04:54 +0530 Subject: asoc: tegra: Add PCM driver for TDM mode Modified existing pcm driver to take dma mode/ hw structure. Exported the functions needed for other pcm mode driver. Added new TDM mode hw param structure. Added pass SINGLE/DOUBLE buffered dma mode params. Bug 948478 Change-Id: I58309d52748f813b3303a8d6a052fbb6cc7ca87a Signed-off-by: Nitin Pai Reviewed-on: http://git-master/r/99146 Reviewed-by: Simone Willett Tested-by: Simone Willett --- sound/soc/tegra/Makefile | 2 + sound/soc/tegra/tegra_pcm.c | 50 +++++++++---- sound/soc/tegra/tegra_pcm.h | 15 ++++ sound/soc/tegra/tegra_tdm_pcm.c | 160 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 211 insertions(+), 16 deletions(-) create mode 100644 sound/soc/tegra/tegra_tdm_pcm.c (limited to 'sound') diff --git a/sound/soc/tegra/Makefile b/sound/soc/tegra/Makefile index d546046d1e1d..9c4346aa265c 100644 --- a/sound/soc/tegra/Makefile +++ b/sound/soc/tegra/Makefile @@ -2,6 +2,7 @@ GCOV_PROFILE := y # Tegra platform Support snd-soc-tegra-pcm-objs := tegra_pcm.o +snd-soc-tegra-tdm-pcm-objs := tegra_tdm_pcm.o snd-soc-tegra20-spdif-objs := tegra20_spdif.o snd-soc-tegra-utils-objs += tegra_asoc_utils.o snd-soc-tegra20-das-objs := tegra20_das.o @@ -13,6 +14,7 @@ snd-soc-tegra30-dam-objs := tegra30_dam.o obj-$(CONFIG_SND_SOC_TEGRA) += snd-soc-tegra-pcm.o obj-$(CONFIG_SND_SOC_TEGRA) += snd-soc-tegra-utils.o +obj-$(CONFIG_SND_SOC_TEGRA) += snd-soc-tegra-tdm-pcm.o obj-$(CONFIG_SND_SOC_TEGRA20_DAS) += snd-soc-tegra20-das.o obj-$(CONFIG_SND_SOC_TEGRA20_I2S) += snd-soc-tegra20-i2s.o obj-$(CONFIG_SND_SOC_TEGRA30_AHUB) += snd-soc-tegra30-ahub.o diff --git a/sound/soc/tegra/tegra_pcm.c b/sound/soc/tegra/tegra_pcm.c index 65d89c8b1280..89fd3ff8fb15 100644 --- a/sound/soc/tegra/tegra_pcm.c +++ b/sound/soc/tegra/tegra_pcm.c @@ -131,7 +131,9 @@ static void setup_dma_rx_request(struct tegra_dma_req *req, req->req_sel = dmap->req_sel; } -static int tegra_pcm_open(struct snd_pcm_substream *substream) +int tegra_pcm_allocate(struct snd_pcm_substream *substream, + int dma_mode, + const struct snd_pcm_hardware *pcm_hardware) { struct snd_pcm_runtime *runtime = substream->runtime; struct tegra_runtime_data *prtd; @@ -157,7 +159,7 @@ static int tegra_pcm_open(struct snd_pcm_substream *substream) prtd->dma_req[i].dev = prtd; prtd->dma_chan = tegra_dma_allocate_channel( - TEGRA_DMA_MODE_CONTINUOUS_SINGLE, + dma_mode, "pcm"); if (prtd->dma_chan == NULL) { ret = -ENOMEM; @@ -166,7 +168,7 @@ static int tegra_pcm_open(struct snd_pcm_substream *substream) } /* Set HW params now that initialization is complete */ - snd_soc_set_runtime_hwparams(substream, &tegra_pcm_hardware); + snd_soc_set_runtime_hwparams(substream, pcm_hardware); /* Ensure period size is multiple of 8 */ ret = snd_pcm_hw_constraint_step(runtime, 0, @@ -192,7 +194,15 @@ err: return ret; } -static int tegra_pcm_close(struct snd_pcm_substream *substream) +static int tegra_pcm_open(struct snd_pcm_substream *substream) +{ + return tegra_pcm_allocate(substream, + TEGRA_DMA_MODE_CONTINUOUS_SINGLE, + &tegra_pcm_hardware); + +} + +int tegra_pcm_close(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; struct tegra_runtime_data *prtd = runtime->private_data; @@ -205,7 +215,7 @@ static int tegra_pcm_close(struct snd_pcm_substream *substream) return 0; } -static int tegra_pcm_hw_params(struct snd_pcm_substream *substream, +int tegra_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_pcm_runtime *runtime = substream->runtime; @@ -235,14 +245,14 @@ static int tegra_pcm_hw_params(struct snd_pcm_substream *substream, return 0; } -static int tegra_pcm_hw_free(struct snd_pcm_substream *substream) +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) +int tegra_pcm_trigger(struct snd_pcm_substream *substream, int cmd) { struct snd_pcm_runtime *runtime = substream->runtime; struct tegra_runtime_data *prtd = runtime->private_data; @@ -284,7 +294,7 @@ static int tegra_pcm_trigger(struct snd_pcm_substream *substream, int cmd) return 0; } -static snd_pcm_uframes_t tegra_pcm_pointer(struct snd_pcm_substream *substream) +snd_pcm_uframes_t tegra_pcm_pointer(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; struct tegra_runtime_data *prtd = runtime->private_data; @@ -297,7 +307,7 @@ static snd_pcm_uframes_t tegra_pcm_pointer(struct snd_pcm_substream *substream) bytes_to_frames(runtime, dma_transfer_count); } -static int tegra_pcm_mmap(struct snd_pcm_substream *substream, +int tegra_pcm_mmap(struct snd_pcm_substream *substream, struct vm_area_struct *vma) { struct snd_pcm_runtime *runtime = substream->runtime; @@ -319,11 +329,11 @@ static struct snd_pcm_ops tegra_pcm_ops = { .mmap = tegra_pcm_mmap, }; -static int tegra_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream) +static int tegra_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, + int stream , size_t size) { struct snd_pcm_substream *substream = pcm->streams[stream].substream; struct snd_dma_buffer *buf = &substream->dma_buffer; - size_t size = tegra_pcm_hardware.buffer_bytes_max; buf->area = dma_alloc_writecombine(pcm->card->dev, size, &buf->addr, GFP_KERNEL); @@ -338,7 +348,7 @@ static int tegra_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream) return 0; } -static void tegra_pcm_deallocate_dma_buffer(struct snd_pcm *pcm, int stream) +void tegra_pcm_deallocate_dma_buffer(struct snd_pcm *pcm, int stream) { struct snd_pcm_substream *substream; struct snd_dma_buffer *buf; @@ -358,7 +368,7 @@ static void tegra_pcm_deallocate_dma_buffer(struct snd_pcm *pcm, int stream) static u64 tegra_dma_mask = DMA_BIT_MASK(32); -static int tegra_pcm_new(struct snd_soc_pcm_runtime *rtd) +int tegra_pcm_dma_allocate(struct snd_soc_pcm_runtime *rtd, size_t size) { struct snd_card *card = rtd->card->snd_card; struct snd_soc_dai *dai = rtd->cpu_dai; @@ -372,14 +382,16 @@ static int tegra_pcm_new(struct snd_soc_pcm_runtime *rtd) if (dai->driver->playback.channels_min) { ret = tegra_pcm_preallocate_dma_buffer(pcm, - SNDRV_PCM_STREAM_PLAYBACK); + SNDRV_PCM_STREAM_PLAYBACK, + size); if (ret) goto err; } if (dai->driver->capture.channels_min) { ret = tegra_pcm_preallocate_dma_buffer(pcm, - SNDRV_PCM_STREAM_CAPTURE); + SNDRV_PCM_STREAM_CAPTURE, + size); if (ret) goto err_free_play; } @@ -392,7 +404,13 @@ err: return ret; } -static void tegra_pcm_free(struct snd_pcm *pcm) +int tegra_pcm_new(struct snd_soc_pcm_runtime *rtd) +{ + return tegra_pcm_dma_allocate(rtd , + tegra_pcm_hardware.buffer_bytes_max); +} + +void tegra_pcm_free(struct snd_pcm *pcm) { tegra_pcm_deallocate_dma_buffer(pcm, SNDRV_PCM_STREAM_CAPTURE); tegra_pcm_deallocate_dma_buffer(pcm, SNDRV_PCM_STREAM_PLAYBACK); diff --git a/sound/soc/tegra/tegra_pcm.h b/sound/soc/tegra/tegra_pcm.h index 5737ea7ca0bc..7fe22788004b 100644 --- a/sound/soc/tegra/tegra_pcm.h +++ b/sound/soc/tegra/tegra_pcm.h @@ -55,4 +55,19 @@ struct tegra_runtime_data { int dma_req_count; }; +int tegra_pcm_trigger(struct snd_pcm_substream *substream, int cmd); +int tegra_pcm_allocate(struct snd_pcm_substream *substream, + int dma_mode, + const struct snd_pcm_hardware *pcm_hardware); +int tegra_pcm_close(struct snd_pcm_substream *substream); +int tegra_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params); +int tegra_pcm_trigger(struct snd_pcm_substream *substream, int cmd); +int tegra_pcm_mmap(struct snd_pcm_substream *substream, + struct vm_area_struct *vma); +int tegra_pcm_dma_allocate(struct snd_soc_pcm_runtime *rtd, size_t size); +void tegra_pcm_free(struct snd_pcm *pcm); +snd_pcm_uframes_t tegra_pcm_pointer(struct snd_pcm_substream *substream); +int tegra_pcm_hw_free(struct snd_pcm_substream *substream); + #endif diff --git a/sound/soc/tegra/tegra_tdm_pcm.c b/sound/soc/tegra/tegra_tdm_pcm.c new file mode 100644 index 000000000000..ae33bdbc588c --- /dev/null +++ b/sound/soc/tegra/tegra_tdm_pcm.c @@ -0,0 +1,160 @@ +/* + * tegra_tdm_pcm.c - Tegra TDM PCM driver + * + * Author: Nitin Pai + * Copyright (C) 2010 - NVIDIA, Inc. + * + * Based on code copyright/by: + * + * Copyright (c) 2009-2010, NVIDIA Corporation. + * Scott Peterson + * Stephen Warren + * Vijay Mali + * + * Copyright (C) 2010 Google, Inc. + * Iliyan Malchev + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "tegra_pcm.h" + +#define DRV_NAME "tegra-tdm-pcm-audio" + +static const struct snd_pcm_hardware tegra_tdm_pcm_hardware = { + .info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_RESUME | + SNDRV_PCM_INFO_INTERLEAVED, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = 8, + .channels_max = 16, + .period_bytes_min = 16 * 1024, + .period_bytes_max = 16 * 1024, + .periods_min = 4, + .periods_max = 4, + .buffer_bytes_max = 16 * 4 * 1024, + .fifo_size = 4, +}; + +static int tegra_tdm_pcm_open(struct snd_pcm_substream *substream) +{ + return tegra_pcm_allocate(substream, + TEGRA_DMA_MODE_CONTINUOUS_DOUBLE, + &tegra_tdm_pcm_hardware); + +} + +static int tegra_tdm_pcm_close(struct snd_pcm_substream *substream) +{ + return tegra_pcm_close(substream); +} + +static int tegra_tdm_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + return tegra_pcm_hw_params(substream, params); +} + +static int tegra_tdm_pcm_hw_free(struct snd_pcm_substream *substream) +{ + return tegra_pcm_hw_free(substream); +} + +static int tegra_tdm_pcm_trigger(struct snd_pcm_substream *substream, int cmd) +{ + return tegra_pcm_trigger(substream, cmd); +} + +static int tegra_tdm_pcm_mmap(struct snd_pcm_substream *substream, + struct vm_area_struct *vma) +{ + return tegra_pcm_mmap(substream, vma); +} + +static struct snd_pcm_ops tegra_tdm_pcm_ops = { + .open = tegra_tdm_pcm_open, + .close = tegra_tdm_pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = tegra_tdm_pcm_hw_params, + .hw_free = tegra_tdm_pcm_hw_free, + .trigger = tegra_tdm_pcm_trigger, + .pointer = tegra_pcm_pointer, + .mmap = tegra_tdm_pcm_mmap, +}; + +static int tegra_tdm_pcm_new(struct snd_soc_pcm_runtime *rtd) +{ + return tegra_pcm_dma_allocate(rtd , + tegra_tdm_pcm_hardware.buffer_bytes_max); +} + +static void tegra_tdm_pcm_free(struct snd_pcm *pcm) +{ + return tegra_pcm_free(pcm); +} + +struct snd_soc_platform_driver tegra_tdm_pcm_platform = { + .ops = &tegra_tdm_pcm_ops, + .pcm_new = tegra_tdm_pcm_new, + .pcm_free = tegra_tdm_pcm_free, +}; + +static int __devinit tegra_tdm_pcm_platform_probe(struct platform_device *pdev) +{ + return snd_soc_register_platform(&pdev->dev, &tegra_tdm_pcm_platform); +} + +static int __devexit tegra_tdm_pcm_platform_remove(struct platform_device *pdev) +{ + snd_soc_unregister_platform(&pdev->dev); + return 0; +} + +static struct platform_driver tegra_tdm_pcm_driver = { + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + }, + .probe = tegra_tdm_pcm_platform_probe, + .remove = __devexit_p(tegra_tdm_pcm_platform_remove), +}; + +static int __init snd_tegra_tdm_pcm_init(void) +{ + return platform_driver_register(&tegra_tdm_pcm_driver); +} +module_init(snd_tegra_tdm_pcm_init); + +static void __exit snd_tegra_tdm_pcm_exit(void) +{ + platform_driver_unregister(&tegra_tdm_pcm_driver); +} +module_exit(snd_tegra_tdm_pcm_exit); + +MODULE_AUTHOR("Nitin Pai "); +MODULE_DESCRIPTION("Tegra PCM ASoC driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:" DRV_NAME); -- cgit v1.2.3