asoc: tegra: Add PCM driver for TDM mode
Nitin Pai [Thu, 26 Apr 2012 13:34:54 +0000 (18:34 +0530)]
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 <npai@nvidia.com>
Reviewed-on: http://git-master/r/99146
Reviewed-by: Simone Willett <swillett@nvidia.com>
Tested-by: Simone Willett <swillett@nvidia.com>

sound/soc/tegra/Makefile
sound/soc/tegra/tegra_pcm.c
sound/soc/tegra/tegra_pcm.h
sound/soc/tegra/tegra_tdm_pcm.c [new file with mode: 0644]

index d546046..9c4346a 100644 (file)
@@ -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
index 65d89c8..89fd3ff 100644 (file)
@@ -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);
index 5737ea7..7fe2278 100644 (file)
@@ -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 (file)
index 0000000..ae33bdb
--- /dev/null
@@ -0,0 +1,160 @@
+/*
+ * tegra_tdm_pcm.c - Tegra TDM PCM driver
+ *
+ * Author: Nitin Pai <npai@nvidia.com>
+ * Copyright (C) 2010 - NVIDIA, Inc.
+ *
+ * Based on code copyright/by:
+ *
+ * Copyright (c) 2009-2010, NVIDIA Corporation.
+ * Scott Peterson <speterson@nvidia.com>
+ * Stephen Warren <swarren@nvidia.com>
+ * Vijay Mali <vmali@nvidia.com>
+ *
+ * Copyright (C) 2010 Google, Inc.
+ * Iliyan Malchev <malchev@google.com>
+ *
+ * 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 <linux/module.h>
+#include <linux/dma-mapping.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+#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 <npai@nvidia.com>");
+MODULE_DESCRIPTION("Tegra PCM ASoC driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" DRV_NAME);