ASoC: tegra-virtual: Tegra virtual driver
Aniket Bahadarpurkar [Wed, 25 Jun 2014 09:16:52 +0000 (14:16 +0530)]
Create a new tegra-virtual driver. AHUB component
drivers are used from tegra-alt.

Bug 1501272

Change-Id: Ic203b75d7b7b6d540cc7a651fafe75d08fd65a19
Signed-off-by: Aniket Bahadarpurkar <aniketb@nvidia.com>
Reviewed-on: http://git-master/r/428264
Reviewed-by: Automatic_Commit_Validation_User
Reviewed-by: Paresh Anandathirtha <paresha@nvidia.com>
Reviewed-by: Deepak Kamurthy <dkamurthy@nvidia.com>
Reviewed-by: Arun Shamanna Lakshmi <aruns@nvidia.com>
GVS: Gerrit_Virtual_Submit

sound/soc/Kconfig
sound/soc/Makefile
sound/soc/tegra-virtual/Kconfig [new file with mode: 0644]
sound/soc/tegra-virtual/Makefile [new file with mode: 0644]
sound/soc/tegra-virtual/tegra124_virt_apbif_master.c [new file with mode: 0644]
sound/soc/tegra-virtual/tegra124_virt_apbif_master.h [new file with mode: 0644]
sound/soc/tegra-virtual/tegra124_virt_apbif_slave.c [new file with mode: 0644]
sound/soc/tegra-virtual/tegra124_virt_apbif_slave.h [new file with mode: 0644]
sound/soc/tegra-virtual/tegra_virt_utils.c [new file with mode: 0644]
sound/soc/tegra-virtual/tegra_virt_utils.h [new file with mode: 0644]
sound/soc/tegra-virtual/tegra_virt_vcm30t124_slave.c [new file with mode: 0644]

index 638033f..69cc0bf 100644 (file)
@@ -53,6 +53,7 @@ source "sound/soc/s6000/Kconfig"
 source "sound/soc/sh/Kconfig"
 source "sound/soc/tegra/Kconfig"
 source "sound/soc/tegra-alt/Kconfig"
+source "sound/soc/tegra-virtual/Kconfig"
 source "sound/soc/txx9/Kconfig"
 source "sound/soc/ux500/Kconfig"
 
index 3d14cb8..4e999c9 100644 (file)
@@ -31,5 +31,6 @@ obj-$(CONFIG_SND_SOC) += s6000/
 obj-$(CONFIG_SND_SOC)  += sh/
 obj-$(CONFIG_SND_SOC)  += tegra/
 obj-$(CONFIG_SND_SOC)  += tegra-alt/
+obj-$(CONFIG_SND_SOC)  += tegra-virtual/
 obj-$(CONFIG_SND_SOC)  += txx9/
 obj-$(CONFIG_SND_SOC)  += ux500/
diff --git a/sound/soc/tegra-virtual/Kconfig b/sound/soc/tegra-virtual/Kconfig
new file mode 100644 (file)
index 0000000..d0888df
--- /dev/null
@@ -0,0 +1,84 @@
+config SND_SOC_TEGRA_VIRT_MASTER
+       tristate "Virtual DAPM-based SoC audio support for the Tegra System-on-Chip"
+       depends on ARCH_TEGRA
+       select REGMAP_MMIO
+       help
+               Say Y or M here if you want support for SoC audio on Tegra, using the
+               virtual driver that exposes to user-space the full routing capabilities
+               of the AHUB (Audio HUB) hardware module. Audio routing is modified with
+               the help of amixer utility, provided by ALSA, from user-space.
+
+config SND_SOC_TEGRA_124_OR_LATER
+  def_bool y
+       depends on ARCH_TEGRA
+       depends on ARCH_TEGRA_12x_SOC
+       help
+               Say Y or M here if the module depends upon tegra124 or later SoC.
+               Virtual driver needs this as the driver support is available only
+               for T124 or later SoC. For this option to work, all the dependencies
+               should be selected in defconfig file.
+
+config SND_SOC_TEGRA124_VIRT_APBIF_MASTER
+       tristate "Tegra124 APBIF master"
+       depends on SND_SOC_TEGRA_124_OR_LATER
+       help
+               Say Y or M if you want to add support for Tegra124_virt APBIF master module.
+               APBIF_MASTER module does not exposes any dai link. This module performs
+               config_link clock settings. These are required for all the AHUB component
+               operation.
+
+config SND_SOC_TEGRA124_VIRT_APBIF_SLAVE
+       tristate "Tegra124 APBIF slave"
+       depends on SND_SOC_TEGRA_124_OR_LATER
+       depends on MACH_P1859
+       help
+               Say Y or M if you want to add support for Tegra124 APBIF slave module.
+               APBIF_SLAVE module exposes only apbif dai links to apbdma. This module
+               can be build independent of APBIF_MASTER module. This module provides
+               a pcm interface to slave card in audio virtualization use case.
+
+config SND_SOC_TEGRA_VCM30T124_MASTER
+  tristate "SoC Audio support for VCM30_T124"
+       help
+               Say Y or M here if you want to add support for SoC audio on the
+               TEGRA VCM30_T124 using wm8731 and ad1937 codecs or using ak4618
+               and ad1937. This is specifically required for virtualization use
+               case to choose only machine driver obj file.
+
+config SND_SOC_TEGRA_VIRT_VCM30T124_MASTER
+       tristate "SoC Audio master for VCM30_T124"
+       depends on SND_SOC_TEGRA_VIRT_MASTER
+       depends on MACH_P1859
+       select SND_SOC_TEGRA30_XBAR_ALT
+       select SND_SOC_TEGRA30_I2S_ALT
+       select SND_SOC_TEGRA30_DAM_ALT
+       select SND_SOC_TEGRA30_SPDIF_ALT
+       select SND_SOC_TEGRA114_AMX_ALT
+       select SND_SOC_TEGRA114_ADX_ALT
+       select SND_SOC_TEGRA124_VIRT_APBIF_MASTER
+       select SND_SOC_TEGRA124_AFC_ALT
+       select SND_SOC_TEGRA_ALT
+       select SND_SOC_TEGRA_VCM30T124_MASTER
+       select SND_SOC_AD193X
+       select SND_SOC_WM8731
+       select SND_SOC_AK4618
+       select SND_SOC_SPDIF
+       select SND_SOC_TEGRA_ASOC_HWDEP_ALT
+       help
+               Say Y or M here if you want to add master support for SoC audio on the
+               TEGRA VCM30_T124 using wm8731 and ad1937 codecs. This Kconfig automatically
+               selects dependancies. All the AHUB codec drivers are selected from tegra-alt
+               driver. Only APBIF driver is different.
+
+config SND_SOC_TEGRA_VIRT_VCM30T124_SLAVE
+       tristate "SoC Audio Slave for VCM30_T124"
+       depends on TEGRA20_APB_DMA
+       depends on MACH_P1859
+       select SND_SOC_TEGRA_ALT
+       select SND_SOC_TEGRA124_VIRT_APBIF_SLAVE
+       select SND_SOC_SPDIF
+       help
+               Say Y or M here if you want to add slave interface for SoC audio on the
+               TEGRA. This option automatically selects all the required dependancies.
+               pcm driver is selected from tegra-alt. Selecting APBIF_SLAVE option
+               enables apbif dai link interface.
diff --git a/sound/soc/tegra-virtual/Makefile b/sound/soc/tegra-virtual/Makefile
new file mode 100644 (file)
index 0000000..0598bd3
--- /dev/null
@@ -0,0 +1,24 @@
+GCOV_PROFILE := y
+
+subdir-ccflags-y := -Werror
+ccflags-y += -I$(src)/../tegra-alt
+ccflags-y += -I$(src)/../codecs
+
+# Tegra platform Support
+snd-soc-tegra124-virt-slave-apbif-objs := tegra124_virt_apbif_slave.o
+snd-soc-tegra124-virt-master-apbif-objs := tegra124_virt_apbif_master.o
+snd-soc-tegra-virt-utils-objs := tegra_virt_utils.o
+snd-soc-tegra-alt-machine-objs := ../tegra-alt/tegra_asoc_machine_alt.o
+
+obj-$(CONFIG_SND_SOC_TEGRA124_VIRT_APBIF_SLAVE) += snd-soc-tegra124-virt-slave-apbif.o
+obj-$(CONFIG_SND_SOC_TEGRA124_VIRT_APBIF_SLAVE) += snd-soc-tegra-virt-utils.o
+obj-$(CONFIG_SND_SOC_TEGRA124_VIRT_APBIF_MASTER) += snd-soc-tegra124-virt-master-apbif.o
+
+# Tegra machine Support for slave
+snd-soc-tegra-virt-vcm30t124-slave-objs := tegra_virt_vcm30t124_slave.o
+obj-$(CONFIG_SND_SOC_TEGRA_VIRT_VCM30T124_SLAVE) += snd-soc-tegra-virt-vcm30t124-slave.o
+
+# Tegra machine Support for master
+snd-soc-tegra-virt-vcm30t124-master-objs := ../tegra-alt/tegra_vcm30t124_alt.o
+obj-$(CONFIG_SND_SOC_TEGRA_VCM30T124_MASTER) += snd-soc-tegra-virt-vcm30t124-master.o
+obj-$(CONFIG_SND_SOC_TEGRA_VCM30T124_MASTER) += snd-soc-tegra-alt-machine.o
diff --git a/sound/soc/tegra-virtual/tegra124_virt_apbif_master.c b/sound/soc/tegra-virtual/tegra124_virt_apbif_master.c
new file mode 100644 (file)
index 0000000..d5309e3
--- /dev/null
@@ -0,0 +1,305 @@
+/*
+ * tegra124_virt_apbif_master.c - Tegra virtual APBIF master driver
+ *
+ * Copyright (c) 2011-2014 NVIDIA CORPORATION.  All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>.
+ */
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <mach/clk.h>
+#include <sound/soc.h>
+#include <sound/pcm_params.h>
+
+#include "tegra30_xbar_alt.h"
+#include "tegra124_virt_apbif_master.h"
+
+#define DRV_NAME "tegra30-ahub-apbif"
+
+int tegra30_apbif_i2s_rx_fifo_is_enabled(int i2s_id)
+{
+       int val = 0;
+       return val;
+}
+EXPORT_SYMBOL_GPL(tegra30_apbif_i2s_rx_fifo_is_enabled);
+
+int tegra30_apbif_i2s_tx_fifo_is_enabled(int i2s_id)
+{
+       int val = 0;
+       return val;
+}
+EXPORT_SYMBOL_GPL(tegra30_apbif_i2s_tx_fifo_is_enabled);
+
+int tegra30_apbif_i2s_rx_fifo_is_empty(int i2s_id)
+{
+       int val = 0;
+       return val;
+}
+EXPORT_SYMBOL_GPL(tegra30_apbif_i2s_rx_fifo_is_empty);
+
+int tegra30_apbif_i2s_tx_fifo_is_empty(int i2s_id)
+{
+       int val = 0;
+       return val;
+}
+EXPORT_SYMBOL_GPL(tegra30_apbif_i2s_tx_fifo_is_empty);
+int tegra30_apbif_i2s_underrun_interrupt_status_clear(int i2s_id)
+{
+       return 0;
+}
+EXPORT_SYMBOL_GPL(tegra30_apbif_i2s_underrun_interrupt_status_clear);
+
+int tegra30_apbif_i2s_overrun_interrupt_status_clear(int i2s_id)
+{
+       return 0;
+}
+EXPORT_SYMBOL_GPL(tegra30_apbif_i2s_overrun_interrupt_status_clear);
+
+int tegra30_apbif_i2s_underrun_interrupt_status(int i2s_id)
+{
+       return 0;
+}
+EXPORT_SYMBOL_GPL(tegra30_apbif_i2s_underrun_interrupt_status);
+
+int tegra30_apbif_i2s_overrun_interrupt_status(int i2s_id)
+{
+       return 0;
+}
+EXPORT_SYMBOL_GPL(tegra30_apbif_i2s_overrun_interrupt_status);
+
+static int tegra124_virt_apbif_runtime_suspend(struct device *dev)
+{
+       struct tegra124_virt_apbif *apbif = dev_get_drvdata(dev);
+
+       clk_disable(apbif->clk);
+
+       return 0;
+}
+
+static int tegra124_virt_apbif_runtime_resume(struct device *dev)
+{
+       struct tegra124_virt_apbif *apbif = dev_get_drvdata(dev);
+       int ret;
+
+       ret = clk_enable(apbif->clk);
+       if (ret) {
+               dev_err(dev, "clk_enable failed: %d\n", ret);
+               return ret;
+       }
+
+       return 0;
+}
+
+#define CLK_LIST_MASK_TEGRA30 BIT(0)
+#define CLK_LIST_MASK_TEGRA114 BIT(1)
+#define CLK_LIST_MASK_TEGRA124 BIT(2)
+
+#define CLK_LIST_MASK_TEGRA30_OR_LATER \
+               (CLK_LIST_MASK_TEGRA30 | CLK_LIST_MASK_TEGRA114 |\
+               CLK_LIST_MASK_TEGRA124)
+#define CLK_LIST_MASK_TEGRA114_OR_LATER \
+               (CLK_LIST_MASK_TEGRA114 | CLK_LIST_MASK_TEGRA124)
+
+static const struct {
+       const char *clk_name;
+       unsigned int clk_list_mask;
+} configlink_clocks[] = {
+       { "i2s0", CLK_LIST_MASK_TEGRA30_OR_LATER },
+       { "i2s1", CLK_LIST_MASK_TEGRA30_OR_LATER },
+       { "i2s2", CLK_LIST_MASK_TEGRA30_OR_LATER },
+       { "i2s3", CLK_LIST_MASK_TEGRA30_OR_LATER },
+       { "i2s4", CLK_LIST_MASK_TEGRA30_OR_LATER },
+       { "dam0", CLK_LIST_MASK_TEGRA30_OR_LATER },
+       { "dam1", CLK_LIST_MASK_TEGRA30_OR_LATER },
+       { "dam2", CLK_LIST_MASK_TEGRA30_OR_LATER },
+       { "spdif_in", CLK_LIST_MASK_TEGRA30_OR_LATER },
+       { "amx", CLK_LIST_MASK_TEGRA114_OR_LATER },
+       { "adx", CLK_LIST_MASK_TEGRA114_OR_LATER },
+       { "amx1", CLK_LIST_MASK_TEGRA124 },
+       { "adx1", CLK_LIST_MASK_TEGRA124 },
+       { "afc0", CLK_LIST_MASK_TEGRA124 },
+       { "afc1", CLK_LIST_MASK_TEGRA124 },
+       { "afc2", CLK_LIST_MASK_TEGRA124 },
+       { "afc3", CLK_LIST_MASK_TEGRA124 },
+       { "afc4", CLK_LIST_MASK_TEGRA124 },
+       { "afc5", CLK_LIST_MASK_TEGRA124 },
+};
+
+static struct of_dev_auxdata tegra124_virt_apbif_auxdata[] = {
+       OF_DEV_AUXDATA("nvidia,tegra124-i2s", 0x70301000,
+               "tegra30-i2s.0", NULL),
+       OF_DEV_AUXDATA("nvidia,tegra124-i2s", 0x70301100,
+               "tegra30-i2s.1", NULL),
+       OF_DEV_AUXDATA("nvidia,tegra124-i2s", 0x70301200,
+               "tegra30-i2s.2", NULL),
+       OF_DEV_AUXDATA("nvidia,tegra124-i2s", 0x70301300,
+               "tegra30-i2s.3", NULL),
+       OF_DEV_AUXDATA("nvidia,tegra124-i2s", 0x70301400,
+               "tegra30-i2s.4", NULL),
+       OF_DEV_AUXDATA("nvidia,tegra124-dam", 0x70302000,
+               "tegra30-dam.0", NULL),
+       OF_DEV_AUXDATA("nvidia,tegra124-dam", 0x70302200,
+               "tegra30-dam.1", NULL),
+       OF_DEV_AUXDATA("nvidia,tegra124-dam", 0x70302400,
+               "tegra30-dam.2", NULL),
+       OF_DEV_AUXDATA("nvidia,tegra124-spdif", 0x70306000,
+               "tegra30-spdif", NULL),
+       OF_DEV_AUXDATA("nvidia,tegra124-virt-amx", 0x70303000,
+               "tegra124-amx.0", NULL),
+       OF_DEV_AUXDATA("nvidia,tegra124-virt-amx", 0x70303100,
+               "tegra124-amx.1", NULL),
+       OF_DEV_AUXDATA("nvidia,tegra124-adx", 0x70303800,
+               "tegra124-adx.0", NULL),
+       OF_DEV_AUXDATA("nvidia,tegra124-adx", 0x70303900,
+               "tegra124-adx.1", NULL),
+       OF_DEV_AUXDATA("nvidia,tegra124-afc", 0x70307000,
+               "tegra124-afc.0", NULL),
+       OF_DEV_AUXDATA("nvidia,tegra124-afc", 0x70307100,
+               "tegra124-afc.1", NULL),
+       OF_DEV_AUXDATA("nvidia,tegra124-afc", 0x70307200,
+               "tegra124-afc.2", NULL),
+       OF_DEV_AUXDATA("nvidia,tegra124-afc", 0x70307300,
+               "tegra124-afc.3", NULL),
+       OF_DEV_AUXDATA("nvidia,tegra124-afc", 0x70307400,
+               "tegra124-afc.4", NULL),
+       OF_DEV_AUXDATA("nvidia,tegra124-afc", 0x70307500,
+               "tegra124-afc.5", NULL),
+       {},
+};
+
+static struct platform_device_info tegra30_xbar_device_info = {
+       .name = "tegra30-ahub-xbar",
+       .id = -1,
+};
+
+static const struct of_device_id tegra124_virt_apbif_virt_of_match[] = {
+       { .compatible = "nvidia,tegra124-virt-ahub-master" },
+       {},
+};
+
+static int tegra124_virt_apbif_probe(struct platform_device *pdev)
+{
+       int i;
+       struct clk *clk;
+       int ret;
+       struct tegra124_virt_apbif *apbif;
+
+       /*
+        * The TEGRA_AHUB APBIF hosts a register bus: the "configlink".
+        * For this to operate correctly, all devices on this bus must
+        * be out of reset.
+        * Ensure that here.
+        */
+       for (i = 0; i < ARRAY_SIZE(configlink_clocks); i++) {
+               if (!(configlink_clocks[i].clk_list_mask &
+                                       CLK_LIST_MASK_TEGRA124))
+                       continue;
+               clk = devm_clk_get(&pdev->dev, configlink_clocks[i].clk_name);
+               if (IS_ERR(clk)) {
+                       dev_err(&pdev->dev, "Can't get clock %s\n",
+                               configlink_clocks[i].clk_name);
+                       ret = PTR_ERR(clk);
+                       goto err;
+               }
+               tegra_periph_reset_deassert(clk);
+               devm_clk_put(&pdev->dev, clk);
+       }
+
+       apbif = devm_kzalloc(&pdev->dev, sizeof(*apbif), GFP_KERNEL);
+       if (!apbif) {
+               dev_err(&pdev->dev, "Can't allocate tegra124_virt_apbif\n");
+               ret = -ENOMEM;
+               goto err;
+       }
+
+       dev_set_drvdata(&pdev->dev, apbif);
+
+       apbif->clk = devm_clk_get(&pdev->dev, "apbif");
+       if (IS_ERR(apbif->clk)) {
+               dev_err(&pdev->dev, "Can't retrieve clock\n");
+               ret = PTR_ERR(apbif->clk);
+               goto err;
+       }
+
+       pm_runtime_enable(&pdev->dev);
+       if (!pm_runtime_enabled(&pdev->dev)) {
+               ret = tegra124_virt_apbif_runtime_resume(&pdev->dev);
+               if (ret)
+                       goto err_pm_disable;
+       }
+
+       tegra30_xbar_device_info.res = platform_get_resource(pdev,
+                                               IORESOURCE_MEM, 1);
+       if (!tegra30_xbar_device_info.res) {
+               dev_err(&pdev->dev, "No memory resource for xbar\n");
+               goto err_suspend;
+       }
+       tegra30_xbar_device_info.num_res = 1;
+       tegra30_xbar_device_info.parent = &pdev->dev;
+       platform_device_register_full(&tegra30_xbar_device_info);
+
+       of_platform_populate(pdev->dev.of_node, NULL,
+               tegra124_virt_apbif_auxdata, &pdev->dev);
+
+       return 0;
+
+err_suspend:
+       if (!pm_runtime_status_suspended(&pdev->dev))
+               tegra124_virt_apbif_runtime_suspend(&pdev->dev);
+err_pm_disable:
+       pm_runtime_disable(&pdev->dev);
+err:
+       return ret;
+}
+
+static int tegra124_virt_apbif_remove(struct platform_device *pdev)
+{
+       struct tegra124_virt_apbif *apbif = dev_get_drvdata(&pdev->dev);
+
+       pm_runtime_disable(&pdev->dev);
+       if (!pm_runtime_status_suspended(&pdev->dev))
+               tegra124_virt_apbif_runtime_suspend(&pdev->dev);
+
+       devm_clk_put(&pdev->dev, apbif->clk);
+
+       return 0;
+}
+
+static const struct dev_pm_ops tegra124_virt_apbif_pm_ops = {
+       SET_RUNTIME_PM_OPS(tegra124_virt_apbif_runtime_suspend,
+                          tegra124_virt_apbif_runtime_resume, NULL)
+};
+
+static struct platform_driver tegra124_virt_apbif_driver = {
+       .probe = tegra124_virt_apbif_probe,
+       .remove = tegra124_virt_apbif_remove,
+       .driver = {
+               .name = DRV_NAME,
+               .owner = THIS_MODULE,
+               .of_match_table = tegra124_virt_apbif_virt_of_match,
+               .pm = &tegra124_virt_apbif_pm_ops,
+       },
+};
+module_platform_driver(tegra124_virt_apbif_driver);
+
+MODULE_AUTHOR("Aniket Bahadarpurkar <aniketb@nvidia.com>");
+MODULE_DESCRIPTION("Tegra124 virt APBIF master driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/sound/soc/tegra-virtual/tegra124_virt_apbif_master.h b/sound/soc/tegra-virtual/tegra124_virt_apbif_master.h
new file mode 100644 (file)
index 0000000..300ff07
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * tegra124_virt_apbif_master.h - Header file for
+ *             tegra124_virt_apbif_master driver
+ *
+ * Copyright (c) 2011-2014 NVIDIA CORPORATION.  All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __TEGRA124_VIRT_APBIF_MASTER_H__
+#define __TEGRA124_VIRT_APBIF_MASTER_H__
+
+int tegra30_apbif_i2s_rx_fifo_is_enabled(int i2s_id);
+int tegra30_apbif_i2s_tx_fifo_is_enabled(int i2s_id);
+int tegra30_apbif_i2s_rx_fifo_is_empty(int i2s_id);
+int tegra30_apbif_i2s_tx_fifo_is_empty(int i2s_id);
+
+struct tegra124_virt_apbif_soc_data {
+               unsigned int num_ch;
+               unsigned int clk_list_mask;
+               void (*set_audio_cif)(struct regmap *map,
+                       unsigned int reg,
+                       struct tegra30_xbar_cif_conf *cif_conf);
+};
+
+struct tegra124_virt_apbif {
+       struct clk *clk;
+       /* regmap for APBIF */
+       struct regmap *regmap[2];
+       struct tegra_alt_pcm_dma_params *capture_dma_data;
+       struct tegra_alt_pcm_dma_params *playback_dma_data;
+       const struct tegra124_virt_apbif_soc_data *soc_data;
+};
+
+#endif
diff --git a/sound/soc/tegra-virtual/tegra124_virt_apbif_slave.c b/sound/soc/tegra-virtual/tegra124_virt_apbif_slave.c
new file mode 100644 (file)
index 0000000..e6f1669
--- /dev/null
@@ -0,0 +1,461 @@
+/*
+ * tegra124_virt_apbif_slave.c - Tegra APBIF slave driver
+ *
+ * Copyright (c) 2011-2014 NVIDIA CORPORATION.  All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>.
+ */
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/slab.h>
+#include <sound/soc.h>
+#include <sound/pcm_params.h>
+
+#include "tegra_pcm_alt.h"
+#include "tegra124_virt_apbif_slave.h"
+
+#define DRV_NAME       "tegra124-virt-ahub-slave"
+
+static int tegra124_virt_apbif_hw_params(struct snd_pcm_substream *substream,
+                                struct snd_pcm_hw_params *params,
+                                struct snd_soc_dai *dai)
+{
+       struct device *dev = dai->dev;
+       struct tegra124_virt_apbif_slave *apbif_slave =
+               snd_soc_dai_get_drvdata(dai);
+       struct tegra124_virt_apbif_slave_data *data = &apbif_slave->slave_data;
+       struct tegra124_virt_audio_cif *cif_conf = &data->cif;
+       struct slave_remap_add *phandle = &(data->phandle);
+       int ret = 0, value;
+
+       data->apbif_id = dai->id;
+
+       /* find amx channel for latest amixer settings */
+       tegra_find_amx_info((unsigned long)data);
+
+       /* initialize the audio cif */
+       cif_conf->audio_channels = params_channels(params);
+       cif_conf->client_channels = params_channels(params);
+
+       switch (params_format(params)) {
+       case SNDRV_PCM_FORMAT_S8:
+               cif_conf->audio_bits = AUDIO_BITS_8;
+               cif_conf->client_bits = AUDIO_BITS_8;
+               break;
+       case SNDRV_PCM_FORMAT_S16_LE:
+               cif_conf->audio_bits = AUDIO_BITS_16;
+               cif_conf->client_bits = AUDIO_BITS_16;
+               break;
+       case SNDRV_PCM_FORMAT_S24_LE:
+               cif_conf->audio_bits = AUDIO_BITS_24;
+               cif_conf->client_bits = AUDIO_BITS_24;
+               break;
+       case SNDRV_PCM_FORMAT_S32_LE:
+               cif_conf->audio_bits = AUDIO_BITS_32;
+               cif_conf->client_bits = AUDIO_BITS_32;
+               break;
+       default:
+               dev_err(dev, "Wrong format!\n");
+               return -EINVAL;
+       }
+       cif_conf->direction = substream->stream;
+       cif_conf->threshold = 0;
+       cif_conf->expand = 0;
+       cif_conf->stereo_conv = 0;
+       cif_conf->replicate = 0;
+       cif_conf->truncate = 0;
+       cif_conf->mono_conv = 0;
+
+       /* set the audio cif */
+       value = (cif_conf->threshold << 24) |
+                       ((cif_conf->audio_channels - 1) << 20) |
+                       ((cif_conf->client_channels - 1) << 16) |
+                       (cif_conf->audio_bits << 12) |
+                       (cif_conf->client_bits << 8) |
+                       (cif_conf->expand << 6) |
+                       (cif_conf->stereo_conv << 4) |
+                       (cif_conf->replicate << 3) |
+                       (cif_conf->direction << 2) |
+                       (cif_conf->truncate << 1) |
+                       (cif_conf->mono_conv << 0);
+
+       if (!cif_conf->direction)
+               reg_write(phandle->apbif_base[data->apbif_id],
+                                 TEGRA_AHUB_CIF_TX_CTRL, value);
+       else
+               reg_write(phandle->apbif_base[data->apbif_id],
+                                TEGRA_AHUB_CIF_RX_CTRL, value);
+
+       return ret;
+}
+
+static void tegra124_virt_apbif_start_playback(struct snd_soc_dai *dai)
+{
+       struct tegra124_virt_apbif_slave *apbif_slave =
+               snd_soc_dai_get_drvdata(dai);
+       struct tegra124_virt_apbif_slave_data *data = &apbif_slave->slave_data;
+       struct slave_remap_add *phandle = &(data->phandle);
+       int value;
+
+       /* enable the amx in channel */
+       if (data->amx_id < AMX_MAX_INSTANCE) {
+               value = reg_read(phandle->amx_base[data->amx_id],
+                                               TEGRA_AMX_IN_CH_CTRL);
+               value |= (TEGRA_AMX_IN_CH_ENABLE << data->amx_in_channel);
+               reg_write(phandle->amx_base[data->amx_id],
+                                               TEGRA_AMX_IN_CH_CTRL,   value);
+       }
+
+       /* enable APBIF TX bit */
+       value = reg_read(phandle->apbif_base[data->apbif_id],
+                                               TEGRA_AHUB_CHANNEL_CTRL);
+       value |= TEGRA_AHUB_CHANNEL_CTRL_TX_EN;
+       value |= ((7 << TEGRA_AHUB_CHANNEL_CTRL_TX_THRESHOLD_SHIFT) |
+                                       TEGRA_AHUB_CHANNEL_CTRL_TX_PACK_EN |
+                                       TEGRA_AHUB_CHANNEL_CTRL_TX_PACK_16);
+       reg_write(phandle->apbif_base[data->apbif_id],
+               TEGRA_AHUB_CHANNEL_CTRL, value);
+
+}
+
+static void tegra124_virt_apbif_stop_playback(struct snd_soc_dai *dai)
+{
+       struct tegra124_virt_apbif_slave *apbif_slave =
+               snd_soc_dai_get_drvdata(dai);
+       struct tegra124_virt_apbif_slave_data *data = &apbif_slave->slave_data;
+       struct slave_remap_add *phandle = &(data->phandle);
+       int value;
+
+       /* disable the amx in channel */
+       if (data->amx_id < AMX_MAX_INSTANCE) {
+               value = reg_read(phandle->amx_base[data->amx_id],
+                                               TEGRA_AMX_IN_CH_CTRL);
+               value &= ~(TEGRA_AMX_IN_CH_ENABLE << data->amx_in_channel);
+               reg_write(phandle->amx_base[data->amx_id],
+                                               TEGRA_AMX_IN_CH_CTRL,   value);
+       }
+
+       /* disable APBIF TX bit */
+       value = reg_read(phandle->apbif_base[data->apbif_id],
+                                       TEGRA_AHUB_CHANNEL_CTRL);
+       value &= ~(TEGRA_AHUB_CHANNEL_CTRL_TX_EN);
+       reg_write(phandle->apbif_base[data->apbif_id],
+                                       TEGRA_AHUB_CHANNEL_CTRL, value);
+}
+
+static void tegra124_virt_apbif_start_capture(struct snd_soc_dai *dai)
+{
+       struct tegra124_virt_apbif_slave *apbif_slave =
+               snd_soc_dai_get_drvdata(dai);
+       struct tegra124_virt_apbif_slave_data *data = &apbif_slave->slave_data;
+       struct slave_remap_add *phandle = &(data->phandle);
+       int value;
+
+       /*enable APBIF RX bit*/
+       value = reg_read(phandle->apbif_base[data->apbif_id],
+                                               TEGRA_AHUB_CHANNEL_CTRL);
+       value |= TEGRA_AHUB_CHANNEL_CTRL_RX_EN;
+       value |= ((7 << TEGRA_AHUB_CHANNEL_CTRL_RX_THRESHOLD_SHIFT) |
+                                       TEGRA_AHUB_CHANNEL_CTRL_RX_PACK_EN |
+                                       TEGRA_AHUB_CHANNEL_CTRL_RX_PACK_16);
+       reg_write(phandle->apbif_base[data->apbif_id],
+               TEGRA_AHUB_CHANNEL_CTRL, value);
+}
+
+static void tegra124_virt_apbif_stop_capture(struct snd_soc_dai *dai)
+{
+       struct tegra124_virt_apbif_slave *apbif_slave =
+               snd_soc_dai_get_drvdata(dai);
+       struct tegra124_virt_apbif_slave_data *data = &apbif_slave->slave_data;
+       struct slave_remap_add *phandle = &(data->phandle);
+       int value;
+
+       /* disable APBIF RX bit */
+       value = reg_read(phandle->apbif_base[data->apbif_id],
+                                               TEGRA_AHUB_CHANNEL_CTRL);
+       value &= ~(TEGRA_AHUB_CHANNEL_CTRL_RX_EN);
+       reg_write(phandle->apbif_base[data->apbif_id],
+               TEGRA_AHUB_CHANNEL_CTRL, value);
+}
+
+static int tegra124_virt_apbif_trigger(struct snd_pcm_substream *substream,
+               int cmd, struct snd_soc_dai *dai)
+{
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+       case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+       case SNDRV_PCM_TRIGGER_RESUME:
+               if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+                       tegra124_virt_apbif_start_playback(dai);
+               else
+                       tegra124_virt_apbif_start_capture(dai);
+               break;
+       case SNDRV_PCM_TRIGGER_STOP:
+       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+       case SNDRV_PCM_TRIGGER_SUSPEND:
+               if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+                       tegra124_virt_apbif_stop_playback(dai);
+               else
+                       tegra124_virt_apbif_stop_capture(dai);
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static struct snd_soc_dai_ops tegra124_virt_apbif_dai_ops = {
+       .hw_params      = tegra124_virt_apbif_hw_params,
+       .trigger        = tegra124_virt_apbif_trigger,
+};
+
+static int tegra124_virt_apbif_dai_probe(struct snd_soc_dai *dai)
+{
+       struct tegra124_virt_apbif_slave *apbif =
+               snd_soc_dai_get_drvdata(dai);
+
+       dai->capture_dma_data = &apbif->capture_dma_data[dai->id];
+       dai->playback_dma_data = &apbif->playback_dma_data[dai->id];
+
+       return 0;
+}
+
+#define APBIF_DAI(id)                                                  \
+       {                                                       \
+               .name = "SLAVE APBIF" #id,                              \
+               .probe = tegra124_virt_apbif_dai_probe,         \
+               .playback = {                                   \
+                       .stream_name = "Playback " #id,         \
+                       .channels_min = 1,                      \
+                       .channels_max = 16,                     \
+                       .rates = SNDRV_PCM_RATE_8000_96000,     \
+                       .formats = SNDRV_PCM_FMTBIT_S8 | \
+                               SNDRV_PCM_FMTBIT_S16_LE | \
+                               SNDRV_PCM_FMTBIT_S24_LE | \
+                               SNDRV_PCM_FMTBIT_S32_LE,        \
+               },                                              \
+               .capture = {                                    \
+                       .stream_name = "Capture " #id,          \
+                       .channels_min = 1,                      \
+                       .channels_max = 16,                     \
+                       .rates = SNDRV_PCM_RATE_8000_96000,     \
+                       .formats = SNDRV_PCM_FMTBIT_S8 | \
+                               SNDRV_PCM_FMTBIT_S16_LE | \
+                               SNDRV_PCM_FMTBIT_S24_LE | \
+                               SNDRV_PCM_FMTBIT_S32_LE,        \
+               },                                              \
+               .ops = &tegra124_virt_apbif_dai_ops,                    \
+       }
+
+static struct snd_soc_dai_driver tegra124_apbif_dais[] = {
+       APBIF_DAI(0),
+       APBIF_DAI(1),
+       APBIF_DAI(2),
+       APBIF_DAI(3),
+       APBIF_DAI(4),
+       APBIF_DAI(5),
+       APBIF_DAI(6),
+       APBIF_DAI(7),
+       APBIF_DAI(8),
+       APBIF_DAI(9),
+};
+
+static struct platform_device spdif_dit = {
+       .name = "spdif-dit",
+       .id = -1,
+};
+
+struct of_dev_auxdata tegra124_virt_apbif_slave_auxdata[] = {
+       OF_DEV_AUXDATA("nvidia,tegra124-virt-machine-slave", 0x0,
+                               "tegra124-virt-machine-slave", NULL),
+       {}
+};
+
+static const struct snd_soc_component_driver tegra124_virt_apbif_dai_driver = {
+       .name           = DRV_NAME,
+};
+
+static const struct of_device_id tegra124_virt_apbif_virt_slave_of_match[] = {
+       { .compatible = "nvidia,tegra124-virt-ahub-slave", },
+       {},
+};
+
+static int tegra124_virt_apbif_probe(struct platform_device *pdev)
+{
+       int i, ret;
+       struct tegra124_virt_apbif_slave *apbif_slave;
+       struct slave_remap_add *phandle;
+       u32 of_dma[20][2];
+
+       apbif_slave = devm_kzalloc(&pdev->dev,
+                                       sizeof(*apbif_slave),   GFP_KERNEL);
+       if (!apbif_slave) {
+               dev_err(&pdev->dev, "Can't allocate tegra124_virt_apbif\n");
+               ret = -ENOMEM;
+               goto err;
+       }
+
+       dev_set_drvdata(&pdev->dev, apbif_slave);
+
+       platform_device_register(&spdif_dit);
+
+       apbif_slave->capture_dma_data = devm_kzalloc(&pdev->dev,
+                       sizeof(struct tegra_alt_pcm_dma_params) *
+                               MAX_APBIF_IDS,
+                       GFP_KERNEL);
+       if (!apbif_slave->capture_dma_data) {
+               dev_err(&pdev->dev, "Can't allocate tegra_alt_pcm_dma_params\n");
+               ret = -ENOMEM;
+               goto err;
+       }
+
+       apbif_slave->playback_dma_data = devm_kzalloc(&pdev->dev,
+                       sizeof(struct tegra_alt_pcm_dma_params) *
+                               MAX_APBIF_IDS,
+                       GFP_KERNEL);
+       if (!apbif_slave->playback_dma_data) {
+               dev_err(&pdev->dev, "Can't allocate tegra_alt_pcm_dma_params\n");
+               ret = -ENOMEM;
+               goto err;
+       }
+
+       if (of_property_read_u32_array(pdev->dev.of_node,
+                               "dmas",
+                               &of_dma[0][0],
+                               MAX_APBIF_IDS * 2) < 0) {
+                       dev_err(&pdev->dev,
+                       "Missing property nvidia,dma-request-selector\n");
+                       ret = -ENODEV;
+               goto err;
+       }
+
+       /* default DAI number is 4 */
+       for (i = 0; i < MAX_APBIF_IDS; i++) {
+               if (i < APBIF_ID_4) {
+                       apbif_slave->playback_dma_data[i].addr =
+                       TEGRA_APBIF_BASE + TEGRA_APBIF_CHANNEL_TXFIFO +
+                       (i * TEGRA_APBIF_CHANNEL_TXFIFO_STRIDE);
+
+                       apbif_slave->capture_dma_data[i].addr =
+                       TEGRA_APBIF_BASE + TEGRA_APBIF_CHANNEL_RXFIFO +
+                       (i * TEGRA_APBIF_CHANNEL_RXFIFO_STRIDE);
+               } else {
+                       apbif_slave->playback_dma_data[i].addr =
+                       TEGRA_APBIF2_BASE2 + TEGRA_APBIF_CHANNEL_TXFIFO +
+                       ((i - APBIF_ID_4) * TEGRA_APBIF_CHANNEL_TXFIFO_STRIDE);
+
+                       apbif_slave->capture_dma_data[i].addr =
+                       TEGRA_APBIF2_BASE2 + TEGRA_APBIF_CHANNEL_RXFIFO +
+                       ((i - APBIF_ID_4) * TEGRA_APBIF_CHANNEL_RXFIFO_STRIDE);
+               }
+
+               apbif_slave->playback_dma_data[i].wrap = 4;
+               apbif_slave->playback_dma_data[i].width = 32;
+               apbif_slave->playback_dma_data[i].req_sel =
+                       of_dma[(i * 2) + 1][1];
+
+               if (of_property_read_string_index(pdev->dev.of_node,
+                       "dma-names",
+                       (i * 2) + 1,
+                       &apbif_slave->playback_dma_data[i].chan_name) < 0) {
+                               dev_err(&pdev->dev,
+                               "Missing property nvidia,dma-names\n");
+                               ret = -ENODEV;
+                               goto err;
+               }
+
+               apbif_slave->capture_dma_data[i].wrap = 4;
+               apbif_slave->capture_dma_data[i].width = 32;
+               apbif_slave->capture_dma_data[i].req_sel = of_dma[(i * 2)][1];
+               if (of_property_read_string_index(pdev->dev.of_node,
+                       "dma-names",
+                       (i * 2),
+                       &apbif_slave->capture_dma_data[i].chan_name) < 0) {
+                               dev_err(&pdev->dev,
+                                                               "Missing property nvidia,dma-names\n");
+                               ret = -ENODEV;
+                               goto err;
+               }
+       }
+
+       ret = snd_soc_register_component(&pdev->dev,
+                                       &tegra124_virt_apbif_dai_driver,
+                                       tegra124_apbif_dais,
+                                       ARRAY_SIZE(tegra124_apbif_dais));
+       if (ret) {
+               dev_err(&pdev->dev, "Could not register DAIs %d: %d\n",
+                       i, ret);
+               ret = -ENOMEM;
+               goto err;
+       }
+
+       ret = tegra_alt_pcm_platform_register(&pdev->dev);
+       if (ret) {
+               dev_err(&pdev->dev, "Could not register PCM: %d\n", ret);
+               goto err_unregister_dais;
+       }
+       phandle = &(apbif_slave->slave_data.phandle);
+
+       ret = create_ioremap(&pdev->dev, phandle);
+       if (ret)
+                       goto err_unregister_dais;
+
+       of_platform_populate(pdev->dev.of_node, NULL,
+                                       tegra124_virt_apbif_slave_auxdata,
+                                       &pdev->dev);
+
+       return 0;
+
+err_unregister_dais:
+       snd_soc_unregister_component(&pdev->dev);
+err:
+       return ret;
+}
+
+static int tegra124_virt_apbif_remove(struct platform_device *pdev)
+{
+       struct tegra124_virt_apbif_slave *apbif_slave =
+               dev_get_drvdata(&pdev->dev);
+       struct slave_remap_add *phandle = &(apbif_slave->slave_data.phandle);
+
+       remove_ioremap(&pdev->dev, phandle);
+
+       tegra_alt_pcm_platform_unregister(&pdev->dev);
+
+       snd_soc_unregister_component(&pdev->dev);
+
+       return 0;
+}
+
+static struct platform_driver tegra124_virt_apbif_virt_slave_driver = {
+       .probe = tegra124_virt_apbif_probe,
+       .remove = tegra124_virt_apbif_remove,
+       .driver = {
+               .name = DRV_NAME,
+               .owner = THIS_MODULE,
+               .of_match_table =
+                       of_match_ptr(tegra124_virt_apbif_virt_slave_of_match),
+       },
+};
+module_platform_driver(tegra124_virt_apbif_virt_slave_driver);
+
+MODULE_AUTHOR("Aniket Bahadarpurkar <aniketb@nvidia.com>");
+MODULE_DESCRIPTION("Tegra124 virt APBIF Slave driver");
+MODULE_LICENSE("GPL v2");
+MODULE_DEVICE_TABLE(of, tegra124_virt_apbif_virt_slave_of_match);
+MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/sound/soc/tegra-virtual/tegra124_virt_apbif_slave.h b/sound/soc/tegra-virtual/tegra124_virt_apbif_slave.h
new file mode 100644 (file)
index 0000000..6ef93b6
--- /dev/null
@@ -0,0 +1,133 @@
+/*
+ * tegra124_virt_apbif_slave.h - Header file for
+ *    tegra124_virt_apbif_slave driver
+ *
+ * Copyright (c) 2014 NVIDIA CORPORATION.  All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __TEGRA124_VIRT_APBIF_SLAVE_H__
+#define __TEGRA124_VIRT_APBIF_SLAVE_H__
+
+#include "tegra_virt_utils.h"
+
+#define TEGRA_APBIF_BASE               0x70300000
+#define TEGRA_APBIF2_BASE2             0x70300200
+
+/* TEGRA_AHUB_CHANNEL_TXFIFO */
+
+#define TEGRA_APBIF_CHANNEL_TXFIFO                     0xc
+#define TEGRA_APBIF_CHANNEL_TXFIFO_STRIDE              0x20
+#define TEGRA_APBIF_CHANNEL_TXFIFO_COUNT               4
+
+/* TEGRA_AHUB_CHANNEL_RXFIFO */
+
+#define TEGRA_APBIF_CHANNEL_RXFIFO                     0x10
+#define TEGRA_APBIF_CHANNEL_RXFIFO_STRIDE              0x20
+#define TEGRA_APBIF_CHANNEL_RXFIFO_COUNT               4
+
+#define TEGRA_AHUB_CHANNEL_CTRL                        0x0
+#define TEGRA_AHUB_CHANNEL_CTRL_TX_EN                  (1 << 31)
+#define TEGRA_AHUB_CHANNEL_CTRL_RX_EN                  (1 << 30)
+
+#define TEGRA_AHUB_CHANNEL_CTRL_TX_THRESHOLD_SHIFT     16
+#define TEGRA_AHUB_CHANNEL_CTRL_RX_THRESHOLD_SHIFT     8
+
+/* TEGRA_AHUB_APBIF_INT_SET */
+
+#define TEGRA_AHUB_APBIF_INT_SET                               0x104
+
+#define TEGRA_APBIF_CHANNEL0_STRIDE    0x20
+#define TEGRA_APBIF_CHANNEL0_COUNT     4
+
+#define TEGRA_APBIF_AUDIOCIF_STRIDE            0x20
+#define TEGRA_APBIF_AUDIOCIF_COUNT             4
+
+/* TEGRA_AHUB_CHANNEL_CTRL */
+
+#define TEGRA_AHUB_CHANNEL_CTRL                        0x0
+#define TEGRA_AHUB_CHANNEL_CTRL_STRIDE         0x20
+#define TEGRA_AHUB_CHANNEL_CTRL_COUNT                  4
+#define TEGRA_AHUB_CHANNEL_CTRL_TX_EN                  (1 << 31)
+#define TEGRA_AHUB_CHANNEL_CTRL_RX_EN                  (1 << 30)
+#define TEGRA_AHUB_CHANNEL_CTRL_LOOPBACK               (1 << 29)
+
+#define TEGRA_AHUB_CHANNEL_CTRL_TX_PACK_EN             (1 << 6)
+
+#define PACK_16                                        3
+
+#define TEGRA_AHUB_CHANNEL_CTRL_TX_PACK_SHIFT          4
+#define TEGRA_AHUB_CHANNEL_CTRL_TX_PACK_16     \
+               (PACK_16 << TEGRA_AHUB_CHANNEL_CTRL_TX_PACK_SHIFT)
+
+#define TEGRA_AHUB_CHANNEL_CTRL_RX_PACK_EN             (1 << 2)
+
+#define TEGRA_AHUB_CHANNEL_CTRL_RX_PACK_SHIFT          0
+#define TEGRA_AHUB_CHANNEL_CTRL_RX_PACK_16 \
+               (PACK_16 << TEGRA_AHUB_CHANNEL_CTRL_RX_PACK_SHIFT)
+
+/* TEGRA_AHUB_CIF_TX_CTRL */
+#define TEGRA_AHUB_CIF_TX_CTRL                 0x14
+
+/* TEGRA_AHUB_CIF_RX_CTRL */
+#define TEGRA_AHUB_CIF_RX_CTRL                 0x18
+
+/* AMX regs */
+#define TEGRA_AMX_IN_CH_CTRL           0x04
+#define TEGRA_AMX_IN_CH_ENABLE         1
+
+/* Audio bit width */
+enum {
+       AUDIO_BITS_4 = 0,
+       AUDIO_BITS_8,
+       AUDIO_BITS_12,
+       AUDIO_BITS_16,
+       AUDIO_BITS_20,
+       AUDIO_BITS_24,
+       AUDIO_BITS_28,
+       AUDIO_BITS_32,
+};
+
+/* Audio cif definition */
+struct tegra124_virt_audio_cif {
+       unsigned int threshold;
+       unsigned int audio_channels;
+       unsigned int client_channels;
+       unsigned int audio_bits;
+       unsigned int client_bits;
+       unsigned int expand;
+       unsigned int stereo_conv;
+       unsigned int replicate;
+       unsigned int direction;
+       unsigned int truncate;
+       unsigned int mono_conv;
+};
+
+/* slave data */
+struct tegra124_virt_apbif_slave_data {
+       unsigned int apbif_id;
+       unsigned int amx_id;
+       unsigned int amx_in_channel;
+       struct tegra124_virt_audio_cif cif;
+       struct slave_remap_add phandle;
+};
+
+/* slave */
+struct tegra124_virt_apbif_slave {
+       struct tegra_alt_pcm_dma_params *capture_dma_data;
+       struct tegra_alt_pcm_dma_params *playback_dma_data;
+       struct tegra124_virt_apbif_slave_data slave_data;
+};
+
+#endif
diff --git a/sound/soc/tegra-virtual/tegra_virt_utils.c b/sound/soc/tegra-virtual/tegra_virt_utils.c
new file mode 100644 (file)
index 0000000..8b038da
--- /dev/null
@@ -0,0 +1,239 @@
+/*
+ * tegra_virt_utils.c - Utilities for tegra124_virt_apbif_slave
+ *
+ * Copyright (c) 2011-2014 NVIDIA CORPORATION.  All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/io.h>
+#include "tegra_virt_utils.h"
+#include <linux/string.h>
+
+const resource_size_t apbif_phy_base[MAX_APBIF_IDS] = {
+       TEGRA_APBIF_BASE_ADR(0),
+       TEGRA_APBIF_BASE_ADR(1),
+       TEGRA_APBIF_BASE_ADR(2),
+       TEGRA_APBIF_BASE_ADR(3),
+       TEGRA_APBIF_BASE_ADR(4),
+       TEGRA_APBIF_BASE_ADR(5),
+       TEGRA_APBIF_BASE_ADR(6),
+       TEGRA_APBIF_BASE_ADR(7),
+       TEGRA_APBIF_BASE_ADR(8),
+       TEGRA_APBIF_BASE_ADR(9),
+};
+
+const resource_size_t amx_phy_base[AMX_MAX_INSTANCE] = {
+       TEGRA_AMX_BASE(0),
+       TEGRA_AMX_BASE(1),
+};
+
+const resource_size_t audio_amx_offset[AMX_TOTAL_CHANNEL] = {
+       TEGRA_AUDIO_AMX_OFFSET(0),
+       TEGRA_AUDIO_AMX_OFFSET(1),
+       TEGRA_AUDIO_AMX_OFFSET(2),
+       TEGRA_AUDIO_AMX_OFFSET(3),
+       TEGRA_AUDIO_AMX_OFFSET(4),
+       TEGRA_AUDIO_AMX_OFFSET(5),
+       TEGRA_AUDIO_AMX_OFFSET(6),
+       TEGRA_AUDIO_AMX_OFFSET(7),
+};
+
+/*
+       reg_write: write value to address
+       base_address: base_address
+       reg:offset
+       val:value to be written
+ */
+void reg_write(void *base_address,
+                               unsigned int reg, unsigned int val)
+{
+       writel(val, base_address+reg);
+}
+
+/*
+       reg_read: read value from address
+       base_address: base_address
+       reg:offset
+       return value read from the address
+ */
+unsigned int reg_read(void *base_address, unsigned int reg)
+{
+       unsigned int val = 0;
+
+       val = readl(base_address + reg);
+
+       return val;
+}
+
+/*
+       create_ioremap: create ioremapped address for a particular device.
+       slave_remap_add: structure storing ioremapped addresses.
+ */
+int create_ioremap(struct device *dev, struct slave_remap_add *phandle)
+{
+       int i;
+       memset(phandle->apbif_base, 0,
+               sizeof(phandle->apbif_base[0])*MAX_APBIF_IDS);
+       memset(phandle->amx_base, 0,
+               sizeof(phandle->amx_base[0])*AMX_MAX_INSTANCE);
+       phandle->audio_amx_base = NULL;
+
+       for (i = 0; i < MAX_APBIF_IDS; i++) {
+               phandle->apbif_base[i] = devm_ioremap(dev, apbif_phy_base[i],
+                       TEGRA_ABPIF_UNIT_SIZE);
+               if (phandle->apbif_base[i] == NULL)
+                       goto remap_fail;
+       }
+
+       for (i = 0; i < AMX_MAX_INSTANCE; i++) {
+               phandle->amx_base[i] = devm_ioremap(dev, amx_phy_base[i],
+                       TEGRA_AMX_UNIT_SIZE);
+               if (phandle->amx_base[i] == NULL)
+                       goto remap_fail;
+       }
+
+       phandle->audio_amx_base = devm_ioremap(dev, TEGRA_AUDIO_BASE,
+                                       TEGRA_AUDIO_SIZE);
+       if (phandle->audio_amx_base == NULL)
+               goto remap_fail;
+
+       return 0;
+
+remap_fail:
+       remove_ioremap(dev, phandle);
+       return -1;
+}
+
+/*
+remove_ioremap: unmap ioremapped addresses.
+*/
+void remove_ioremap(struct device *dev, struct slave_remap_add *phandle)
+{
+       int i;
+       for (i = 0; i < MAX_APBIF_IDS; i++) {
+               if (phandle->apbif_base[i] != NULL) {
+                       devm_iounmap(dev,
+                               (void __iomem *)(phandle->apbif_base[i]));
+                       phandle->apbif_base[i] = NULL;
+               }
+       }
+
+       for (i = 0; i < AMX_MAX_INSTANCE; i++) {
+               if (phandle->amx_base[i] != NULL) {
+                       devm_iounmap(dev,
+                               (void __iomem *)(phandle->amx_base[i]));
+                       phandle->amx_base[i] = NULL;
+               }
+       }
+
+       if (phandle->audio_amx_base != NULL) {
+               devm_iounmap(dev,
+                       (void __iomem *)(phandle->audio_amx_base));
+               phandle->audio_amx_base = NULL;
+       }
+
+}
+
+/*
+tegra_find_amx_channel: Find amx_channel index based on
+  xbar register value.
+       value contains xbar register value for a given receiver
+       amx     */
+static unsigned int tegra_find_amx_channel(unsigned int value)
+{
+       unsigned int  apbif_id = MAX_APBIF_IDS;
+       switch (value) {
+       case APBIF_TX0:
+               apbif_id = APBIF_ID_0;
+               break;
+       case APBIF_TX1:
+               apbif_id = APBIF_ID_1;
+               break;
+       case APBIF_TX2:
+               apbif_id = APBIF_ID_2;
+               break;
+       case APBIF_TX3:
+               apbif_id = APBIF_ID_3;
+               break;
+       case APBIF_TX4:
+               apbif_id = APBIF_ID_4;
+               break;
+       case APBIF_TX5:
+               apbif_id = APBIF_ID_5;
+               break;
+       case APBIF_TX6:
+               apbif_id = APBIF_ID_6;
+               break;
+       case APBIF_TX7:
+               apbif_id = APBIF_ID_7;
+               break;
+       case APBIF_TX8:
+               apbif_id = APBIF_ID_8;
+               break;
+       case APBIF_TX9:
+               apbif_id = APBIF_ID_9;
+               break;
+       case I2S0_TX0: /* TBD */
+       case I2S1_TX0: /* TBD */
+       case I2S2_TX0: /* TBD */
+       case I2S3_TX0: /* TBD */
+       case I2S4_TX0: /* TBD */
+       case DAM0_TX0: /* TBD */
+       case DAM1_TX0: /* TBD */
+       case DAM2_TX0: /* TBD */
+       case SPDIF_TX0: /* TBD */
+       case SPDIF_TX1: /* TBD */
+       case AMX0_TX0: /* TBD */
+       case ADX0_TX0: /* TBD */
+       case ADX0_TX1: /* TBD */
+       case ADX0_TX2: /* TBD */
+       case ADX0_TX3: /* TBD */
+               pr_warn("AMX connection is not supported\n");
+               break;
+       default:
+               break;
+       }
+       return apbif_id;
+}
+
+/*
+tegra_find_amx_info: Find amx index and particular
+amx channel index used by apbif_id under consideration
+        */
+void tegra_find_amx_info(unsigned long arg)
+{
+
+       int amx_idx, ch_idx;
+       unsigned int value;
+       unsigned int reg;
+       struct tegra_virt_utils_data *data =
+                       (struct tegra_virt_utils_data *) arg;
+       struct slave_remap_add *phandle = &(data->phandle);
+
+       for (amx_idx = 0; amx_idx < AMX_MAX_INSTANCE; amx_idx++) {
+               for (ch_idx = 0; ch_idx < AMX_MAX_CHANNEL; ch_idx++) {
+                       reg =
+                       audio_amx_offset[AMX_MAX_CHANNEL*amx_idx + ch_idx];
+                       value = reg_read(phandle->audio_amx_base, reg);
+                       if (data->apbif_id == tegra_find_amx_channel(value))
+                               break;
+               }
+               if (ch_idx < AMX_MAX_CHANNEL)
+                       break;
+       }
+
+       data->amx_id = amx_idx;
+       data->amx_in_channel = ch_idx;
+       return;
+}
diff --git a/sound/soc/tegra-virtual/tegra_virt_utils.h b/sound/soc/tegra-virtual/tegra_virt_utils.h
new file mode 100644 (file)
index 0000000..febb0b7
--- /dev/null
@@ -0,0 +1,144 @@
+/*
+ * tegra_virt_utils.h - Utilities for tegra124_virt_apbif_slave
+ *
+ * Copyright (c) 2011-2014 NVIDIA CORPORATION.  All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef __TEGRA_VIRT_UTILS_H__
+#define __TEGRA_VIRT_UTILS_H__
+
+#define TEGRA_APBIF0_BASE              0x70300000
+#define TEGRA_APBIF4_BASE              0x70300200
+#define TEGRA_AMX0_BASE                        0x70303000
+#define TEGRA_AUDIO_BASE               0x70300800
+#define TEGRA_AUDIO_AMX0_OFFSET                0x5c
+#define TEGRA_AUDIO_AMX1_OFFSET                0x78
+#define TEGRA_AUDIO_AMX_UNIT_SIZE              0x004
+#define TEGRA_AUDIO_SIZE                               0x200
+
+/* define the unit sizes */
+#define TEGRA_ABPIF_UNIT_SIZE  0x20
+#define TEGRA_AMX_UNIT_SIZE            0x100
+
+#define TEGRA_APBIF_BASE_ADR(id)       \
+       (resource_size_t)((id < 4) ? \
+       (TEGRA_APBIF0_BASE + id * TEGRA_ABPIF_UNIT_SIZE) : \
+       (TEGRA_APBIF4_BASE + (id - 4) * TEGRA_ABPIF_UNIT_SIZE))
+
+#define TEGRA_AMX_BASE(id)             \
+       (resource_size_t)(TEGRA_AMX0_BASE + id * TEGRA_AMX_UNIT_SIZE)
+#define TEGRA_AUDIO_AMX_OFFSET(id)     \
+       (resource_size_t)((id < 4) ? \
+               (TEGRA_AUDIO_AMX0_OFFSET + id * TEGRA_AUDIO_AMX_UNIT_SIZE) : \
+                       (TEGRA_AUDIO_AMX1_OFFSET + \
+                        (id - 4) * TEGRA_AUDIO_AMX_UNIT_SIZE))
+
+#define AMX_MAX_CHANNEL   4
+#define AMX_TOTAL_CHANNEL 8
+
+/* Mask to find AMX Connections */
+#define APBIF_TX0 0x00000001
+#define APBIF_TX1 0x00000002
+#define APBIF_TX2 0x00000004
+#define APBIF_TX3 0x00000008
+#define I2S0_TX0  0x00000010
+#define I2S1_TX0  0x00000020
+#define I2S2_TX0  0x00000040
+#define I2S3_TX0  0x00000080
+#define I2S4_TX0  0x00000100
+#define DAM0_TX0  0x00000200
+#define DAM1_TX0  0x00000400
+#define DAM2_TX0  0x00000800
+#define SPDIF_TX0 0x00001000
+#define SPDIF_TX1 0x00002000
+#define APBIF_TX4 0x00004000
+#define APBIF_TX5 0x00008000
+#define APBIF_TX6 0x00010000
+#define APBIF_TX7 0x00020000
+#define APBIF_TX8 0x00040000
+#define APBIF_TX9 0x00080000
+#define AMX0_TX0  0x00100000
+#define ADX0_TX0  0x00200000
+#define ADX0_TX1  0x00400000
+#define ADX0_TX2  0x00800000
+#define ADX0_TX3  0x01000000
+
+/* AHUB modules to program */
+enum {
+       APBIF = 0,
+       AMX,
+       AUDIO_AMX
+};
+
+/* AMX ids */
+enum {
+       AMX_INSTANCE_0 = 0,
+       AMX_INSTANCE_1,
+       AMX_MAX_INSTANCE
+};
+
+/* APBIF ids */
+enum {
+       APBIF_ID_0 = 0,
+       APBIF_ID_1,
+       APBIF_ID_2,
+       APBIF_ID_3,
+       APBIF_ID_4,
+       APBIF_ID_5,
+       APBIF_ID_6,
+       APBIF_ID_7,
+       APBIF_ID_8,
+       APBIF_ID_9,
+       MAX_APBIF_IDS
+};
+
+/* utils data */
+/* Audio cif definition */
+struct tegra_virt_cif {
+       unsigned int threshold;
+       unsigned int audio_channels;
+       unsigned int client_channels;
+       unsigned int audio_bits;
+       unsigned int client_bits;
+       unsigned int expand;
+       unsigned int stereo_conv;
+       unsigned int replicate;
+       unsigned int direction;
+       unsigned int truncate;
+       unsigned int mono_conv;
+};
+
+struct slave_remap_add {
+       void *apbif_base[MAX_APBIF_IDS];
+       void *amx_base[AMX_MAX_INSTANCE];
+       void *audio_amx_base;
+};
+
+struct tegra_virt_utils_data {
+       unsigned int apbif_id;
+       unsigned int amx_id;
+       unsigned int amx_in_channel;
+       struct tegra_virt_cif cif;
+       struct slave_remap_add phandle;
+};
+
+void reg_write(void *base_address,
+                               unsigned int reg, unsigned int val);
+unsigned int reg_read(void *base_address,
+                               unsigned int reg);
+void tegra_find_amx_info(unsigned long data);
+int create_ioremap(struct device *dev, struct slave_remap_add *phandle);
+void remove_ioremap(struct device *dev, struct slave_remap_add *phandle);
+
+#endif
diff --git a/sound/soc/tegra-virtual/tegra_virt_vcm30t124_slave.c b/sound/soc/tegra-virtual/tegra_virt_vcm30t124_slave.c
new file mode 100644 (file)
index 0000000..5e67d06
--- /dev/null
@@ -0,0 +1,236 @@
+/*
+ * tegra_virt_vcm30t124_slave.c - Tegra VCM30 T124 slave Machine driver
+ *
+ * Copyright (c) 2014 NVIDIA CORPORATION.  All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <sound/soc.h>
+#include <linux/of_platform.h>
+
+#define DRV_NAME "tegra124-virt-machine-slave"
+
+#define SLAVE_NAME(i) "SLAVE AUDIO" #i
+#define STREAM_NAME "playback"
+#define CODEC_NAME "spdif-dit"
+#define LINK_CPU_NAME   "tegra124-virt-ahub-slave"
+#define CPU_DAI_NAME(i) "SLAVE APBIF" #i
+#define CODEC_DAI_NAME "dit-hifi"
+#define PLATFORM_NAME LINK_CPU_NAME
+
+static struct snd_soc_pcm_stream default_params = {
+       .rate_min = 48000,
+       .rate_max = 48000,
+       .channels_min = 2,
+       .channels_max = 2,
+};
+
+static struct snd_soc_dai_link tegra_vcm30t124_slave_links[] = {
+       {
+               /* 0 */
+               .name = SLAVE_NAME(0),
+               .stream_name = STREAM_NAME,
+               .codec_name = CODEC_NAME,
+               .cpu_name = LINK_CPU_NAME,
+               .cpu_dai_name = CPU_DAI_NAME(0),
+               .codec_dai_name = CODEC_DAI_NAME,
+               .platform_name = PLATFORM_NAME,
+       },
+       {
+               /* 1 */
+               .name = SLAVE_NAME(1),
+               .stream_name = STREAM_NAME,
+               .codec_name = CODEC_NAME,
+               .cpu_name = LINK_CPU_NAME,
+               .cpu_dai_name = CPU_DAI_NAME(1),
+               .codec_dai_name = CODEC_DAI_NAME,
+               .platform_name = PLATFORM_NAME,
+       },
+       {
+               /* 2 */
+               .name = SLAVE_NAME(2),
+               .stream_name = STREAM_NAME,
+               .codec_name = CODEC_NAME,
+               .cpu_name = LINK_CPU_NAME,
+               .cpu_dai_name = CPU_DAI_NAME(2),
+               .codec_dai_name = CODEC_DAI_NAME,
+               .platform_name = PLATFORM_NAME,
+       },
+       {
+               /* 3 */
+               .name = SLAVE_NAME(3),
+               .stream_name = STREAM_NAME,
+               .codec_name = CODEC_NAME,
+               .cpu_name = LINK_CPU_NAME,
+               .cpu_dai_name = CPU_DAI_NAME(3),
+               .codec_dai_name = CODEC_DAI_NAME,
+               .platform_name = PLATFORM_NAME,
+       },
+       {
+               /* 4 */
+               .name = SLAVE_NAME(4),
+               .stream_name = STREAM_NAME,
+               .codec_name = CODEC_NAME,
+               .cpu_name = LINK_CPU_NAME,
+               .cpu_dai_name = CPU_DAI_NAME(4),
+               .codec_dai_name = CODEC_DAI_NAME,
+               .platform_name = PLATFORM_NAME,
+       },
+       {
+               /* 5 */
+               .name = SLAVE_NAME(5),
+               .stream_name = STREAM_NAME,
+               .codec_name = CODEC_NAME,
+               .cpu_name = LINK_CPU_NAME,
+               .cpu_dai_name = CPU_DAI_NAME(5),
+               .codec_dai_name = CODEC_DAI_NAME,
+               .platform_name = PLATFORM_NAME,
+       },
+       {
+               /* 6 */
+               .name = SLAVE_NAME(6),
+               .stream_name = STREAM_NAME,
+               .codec_name = CODEC_NAME,
+               .cpu_name = LINK_CPU_NAME,
+               .cpu_dai_name = CPU_DAI_NAME(6),
+               .codec_dai_name = CODEC_DAI_NAME,
+               .platform_name = PLATFORM_NAME,
+       },
+       {
+               /* 7 */
+               .name = SLAVE_NAME(7),
+               .stream_name = STREAM_NAME,
+               .codec_name = CODEC_NAME,
+               .cpu_name = LINK_CPU_NAME,
+               .cpu_dai_name = CPU_DAI_NAME(7),
+               .codec_dai_name = CODEC_DAI_NAME,
+               .platform_name = PLATFORM_NAME,
+       },
+       {
+               /* 8 */
+               .name = SLAVE_NAME(8),
+               .stream_name = STREAM_NAME,
+               .codec_name = CODEC_NAME,
+               .cpu_name = LINK_CPU_NAME,
+               .cpu_dai_name = CPU_DAI_NAME(8),
+               .codec_dai_name = CODEC_DAI_NAME,
+               .platform_name = PLATFORM_NAME,
+       },
+       {
+               /* 9 */
+               .name = SLAVE_NAME(9),
+               .stream_name = STREAM_NAME,
+               .codec_name = CODEC_NAME,
+               .cpu_name = LINK_CPU_NAME,
+               .cpu_dai_name = CPU_DAI_NAME(9),
+               .codec_dai_name = CODEC_DAI_NAME,
+               .platform_name = PLATFORM_NAME,
+       },
+};
+
+static const struct of_device_id tegra124_virt_snd_slave_of_match[] = {
+       { .compatible = "nvidia,tegra124-virt-machine-slave", },
+       {},
+};
+
+static struct snd_soc_card snd_soc_tegra_vcm30t124_slave = {
+       .name = "tegra-virt-pcm",
+       .owner = THIS_MODULE,
+       .dai_link = tegra_vcm30t124_slave_links,
+       .num_links = ARRAY_SIZE(tegra_vcm30t124_slave_links),
+       .fully_routed = true,
+};
+
+static void tegra_vcm30t124_set_dai_params(
+               struct snd_soc_dai_link *tegra_vcm_dai_link,
+               struct snd_soc_pcm_stream *user_params,
+               unsigned int dai_id)
+{
+       tegra_vcm_dai_link[dai_id].params = user_params;
+}
+
+static int tegra_vcm30t124_slave_driver_probe(struct platform_device *pdev)
+{
+       struct snd_soc_card *card = &snd_soc_tegra_vcm30t124_slave;
+       int ret = 0;
+       int i;
+       int apbif_group_id = 0;
+       unsigned int num_apbif_masked = 6;
+       unsigned int start_offset_masked_apbif = 0;
+
+       card->dev = &pdev->dev;
+
+       if (of_property_read_u32(pdev->dev.of_node,
+                               "apbif_group_id", &apbif_group_id)) {
+               dev_err(&pdev->dev, "property apbif_group_id is not present\n");
+               return -ENODEV;
+       }
+
+       if (1 == apbif_group_id) {
+               num_apbif_masked = 6;
+               start_offset_masked_apbif = 4;
+       } else if (2 == apbif_group_id) {
+               num_apbif_masked = 4;
+               start_offset_masked_apbif = 0;
+       } else {
+               dev_err(&pdev->dev, "Invalid apbif_group_id\n");
+               return -ENODEV;
+       }
+
+       if (of_property_read_string(pdev->dev.of_node,
+               "cardname", &card->name))
+                       dev_warn(&pdev->dev, "Use default card name\n");
+
+       for (i = 0; i < num_apbif_masked; i++)
+               tegra_vcm30t124_set_dai_params(tegra_vcm30t124_slave_links,
+                                               &default_params,
+                                               i + start_offset_masked_apbif);
+
+       ret = snd_soc_register_card(card);
+       if (ret) {
+               dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n",
+                       ret);
+       }
+       return ret;
+}
+
+static int tegra_vcm30t124_slave_driver_remove(struct platform_device *pdev)
+{
+       struct snd_soc_card *card = platform_get_drvdata(pdev);
+
+       snd_soc_unregister_card(card);
+
+       return 0;
+}
+
+static struct platform_driver tegra_vcm30t124_slave_driver = {
+       .driver = {
+               .name = DRV_NAME,
+               .owner = THIS_MODULE,
+               .pm = &snd_soc_pm_ops,
+               .of_match_table =
+                       of_match_ptr(tegra124_virt_snd_slave_of_match),
+       },
+       .probe = tegra_vcm30t124_slave_driver_probe,
+       .remove = tegra_vcm30t124_slave_driver_remove,
+};
+module_platform_driver(tegra_vcm30t124_slave_driver);
+
+MODULE_AUTHOR("Aniket Bahadarpurkar <aniketb@nvidia.com>");
+MODULE_DESCRIPTION("Tegra+VCM30T124 slave machine ASoC driver");
+MODULE_LICENSE("GPL");
+MODULE_DEVICE_TABLE(of, tegra124_virt_snd_slave_of_match);
+MODULE_ALIAS("platform:" DRV_NAME);