]> rtime.felk.cvut.cz Git - sojka/nv-tegra/linux-3.10.git/commitdiff
Merge branch 'android-3.10' into 'dev-kernel-3.10'
authorSumit Singh <sumsingh@nvidia.com>
Mon, 24 Nov 2014 04:55:24 +0000 (10:25 +0530)
committerSumit Singh <sumsingh@nvidia.com>
Mon, 1 Dec 2014 05:35:24 +0000 (11:05 +0530)
As kernel build was giving error as below:
- missing braces around initializer [-Werror=missing-braces]

So, made modifications in sound/soc/tegra-alt/tegra210_adsp_alt.c
and sound/soc/tegra/tegra30_avp.c to fix above error.

Bug 200058653

Change-Id: I5812d4b4b058f509bcb559df427c4ed78e5cd16e
Signed-off-by: Sumit Singh <sumsingh@nvidia.com>
17 files changed:
1  2 
Documentation/networking/ip-sysctl.txt
drivers/base/power/main.c
drivers/base/power/wakeup.c
drivers/base/syscore.c
include/linux/cgroup.h
include/linux/ipv6.h
include/linux/suspend.h
kernel/cgroup.c
kernel/irq/pm.c
kernel/power/process.c
kernel/power/suspend.c
kernel/sched/core.c
mm/memcontrol.c
net/ipv6/addrconf.c
sound/core/compress_offload.c
sound/soc/tegra-alt/tegra210_adsp_alt.c
sound/soc/tegra/tegra30_avp.c

Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
diff --cc kernel/cgroup.c
Simple merge
diff --cc kernel/irq/pm.c
index 1e78f577df3d91e5a15a200ea3dd939f91a1ea3b,08d0916150d596e4d587f8210434618614a3e3de..1a82e33144da7dcc622e2bdd29775402f3c3950a
  #include <linux/module.h>
  #include <linux/interrupt.h>
  #include <linux/syscore_ops.h>
+ #include <linux/wakeup_reason.h>
  #include "internals.h"
  
 +static bool early_resume_irq_suspended;
 +
  /**
   * suspend_device_irqs - disable all currently enabled interrupt lines
   *
Simple merge
Simple merge
Simple merge
diff --cc mm/memcontrol.c
Simple merge
Simple merge
Simple merge
index bede74ce448a05693ca21912f7f100cb1dd9a86b,0000000000000000000000000000000000000000..feef0bd83473dcd59e98377c53d96dc8829c470c
mode 100644,000000..100644
--- /dev/null
@@@ -1,2045 -1,0 +1,2049 @@@
-                               .sample_rates = SNDRV_PCM_RATE_8000_48000,
 +/*
 + * tegra210_adsp_alt.c - Tegra ADSP audio driver
 + *
 + * Author: Sumit Bhattacharya <sumitb@nvidia.com>
 + * Copyright (c) 2014, NVIDIA CORPORATION.  All rights reserved.
 + *
 + * 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/device.h>
 +#include <linux/fs.h>
 +#include <linux/io.h>
 +#include <linux/of.h>
 +#include <../arch/arm/mach-tegra/iomap.h>
 +#include <linux/completion.h>
 +#include <linux/uaccess.h>
 +#include <linux/delay.h>
 +#include <linux/dma-mapping.h>
 +#include <linux/pm_runtime.h>
 +#include <linux/tegra_pm_domains.h>
 +#include <linux/slab.h>
 +#include <linux/platform_device.h>
 +#include <linux/firmware.h>
 +#include <linux/kthread.h>
 +#include <linux/tegra_nvadsp.h>
 +#include <linux/irqchip/tegra-agic.h>
 +
 +#include <sound/pcm.h>
 +#include <sound/core.h>
 +#include <sound/pcm_params.h>
 +#include <sound/soc.h>
 +#include <sound/compress_driver.h>
 +#include <sound/dmaengine_pcm.h>
 +#include <sound/tegra_nvfx.h>
 +#include <sound/tegra_nvfx_apm.h>
 +#include <sound/tegra_nvfx_plugin.h>
 +
 +#include "tegra210_adsp_alt.h"
 +
 +#define DRV_NAME "tegra210-adsp"
 +
 +/* Flag to enable/disable loading of ADSP firmware */
 +#define ENABLE_ADSP 1
 +
 +static struct tegra210_adsp_app_desc {
 +      const char name[NVADSP_NAME_SZ];
 +      const char fw_name[NVADSP_NAME_SZ];
 +      const uint32_t reg_start;
 +      const uint32_t reg_end;
 +      nvadsp_app_handle_t handle;
 +} adsp_app_desc[] = {
 +      {"apm", "nvapm.elf",
 +              TEGRA210_ADSP_APM_IN1, TEGRA210_ADSP_APM_IN8},
 +      {"mp3dec", "nvmp3dec.elf",
 +              TEGRA210_ADSP_PLUGIN_MP3_DEC1, TEGRA210_ADSP_PLUGIN_MP3_DEC2},
 +      {"adma", "nvadma.elf",
 +              TEGRA210_ADSP_PLUGIN_ADMA1, TEGRA210_ADSP_PLUGIN_ADMA4},
 +      {"spkprot", "nvspkprot.elf",
 +              TEGRA210_ADSP_PLUGIN_SPKPROT, TEGRA210_ADSP_PLUGIN_SPKPROT},
 +};
 +
 +/* ADSP APP specific structure */
 +struct tegra210_adsp_app {
 +      const struct tegra210_adsp_app_desc *desc;
 +      nvadsp_app_info_t *info;
 +      plugin_shared_mem_t *plugin;
 +      apm_shared_state_t *apm; /* For a plugin it stores parent apm data */
 +      struct nvadsp_mbox rx_mbox;
 +      uint32_t reg;
 +      uint32_t adma_chan; /* Valid for only ADMA app */
 +      uint32_t fe:1; /* Whether the app is used as a FE APM */
 +      uint32_t connect:1; /* if app is connected to a source */
 +      void *private_data;
 +      int (*msg_handler)(struct tegra210_adsp_app *, apm_msg_t *);
 +};
 +
 +struct tegra210_adsp_pcm_rtd {
 +      struct device *dev;
 +      struct snd_pcm_substream *substream;
 +      struct tegra210_adsp_app *fe_apm;
 +};
 +
 +struct tegra210_adsp_compr_rtd {
 +      struct device *dev;
 +      struct snd_dma_buffer buf;
 +      struct snd_compr_stream *cstream;
 +      struct snd_codec codec;
 +      struct tegra210_adsp_app *fe_apm;
 +      int is_draining;
 +};
 +
 +struct tegra210_adsp {
 +      struct device *dev;
 +      struct tegra210_adsp_app apps[TEGRA210_ADSP_VIRT_REG_MAX];
 +      atomic_t reg_val[TEGRA210_ADSP_VIRT_REG_MAX];
 +      DECLARE_BITMAP(adma_usage, TEGRA210_ADSP_ADMA_CHANNEL_COUNT);
 +      struct mutex mutex;
 +      int init_done;
 +};
 +
 +static const struct snd_pcm_hardware adsp_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           = 1,
 +      .channels_max           = 2,
 +      .period_bytes_min       = 128,
 +      .period_bytes_max       = PAGE_SIZE * 2,
 +      .periods_min            = 1,
 +      .periods_max            = 8,
 +      .buffer_bytes_max       = PAGE_SIZE * 8,
 +      .fifo_size              = 4,
 +};
 +
 +/* Following structure is ALSA-Compress specific */
 +static struct snd_compr_caps
 +      tegra210_adsp_compr_caps[SND_COMPRESS_CAPTURE + 1] = {
 +      [SND_COMPRESS_PLAYBACK] = {
 +              .num_codecs = 2,
 +              .direction = SND_COMPRESS_PLAYBACK,
 +              .min_fragment_size = 1024,
 +              .max_fragment_size = 1024 * 1024,        /* 1 MB */
 +              .min_fragments = 2,
 +              .max_fragments = 1024,
 +              .codecs = {
 +                      [0] = SND_AUDIOCODEC_MP3,
 +                      [1] = SND_AUDIOCODEC_AAC,
 +              },
 +      },
 +      [SND_COMPRESS_CAPTURE] = {
 +              .num_codecs = 0,
 +              .direction = SND_COMPRESS_CAPTURE,
 +      },
 +};
 +
 +/* Following structure is ALSA-Compress specific */
 +static struct snd_compr_codec_caps adsp_compr_codec_caps[] = {
 +      [SND_AUDIOCODEC_MP3] = {
 +              .codec = SND_AUDIOCODEC_MP3,
 +              .num_descriptors = 1,
 +              .descriptor = {
 +                      [0] = {
 +                              .max_ch = 2,
-                               .sample_rates = SNDRV_PCM_RATE_8000_48000,
++                              .sample_rates = {
++                                      [0] = SNDRV_PCM_RATE_8000_48000,
++                              },
 +                              .bit_rate = {
 +                                      [0] = 32000,
 +                                      [1] = 64000,
 +                                      [2] = 128000,
 +                                      [3] = 256000,
 +                                      [4] = 320000,
 +                              },
 +                              .num_bitrates = 5,
 +                              .rate_control =
 +                                      SND_RATECONTROLMODE_CONSTANTBITRATE |
 +                                      SND_RATECONTROLMODE_VARIABLEBITRATE,
 +                              .profiles = 0,
 +                              .modes = SND_AUDIOCHANMODE_MP3_STEREO,
 +                              .formats = SND_AUDIOSTREAMFORMAT_UNDEFINED,
 +                              .min_buffer = 1024,
 +                      },
 +              },
 +      },
 +      [SND_AUDIOCODEC_AAC] = {
 +              .codec = SND_AUDIOCODEC_AAC,
 +              .num_descriptors = 1,
 +              .descriptor = {
 +                      [0] = {
 +                              .max_ch = 2,
++                              .sample_rates = {
++                                      [0] = SNDRV_PCM_RATE_8000_48000,
++                              },
 +                              .bit_rate = {
 +                                      [0] = 32000,
 +                                      [1] = 64000,
 +                                      [2] = 128000,
 +                                      [3] = 256000,
 +                                      [4] = 320000,
 +                              },
 +                              .num_bitrates = 5,
 +                              .rate_control =
 +                                      SND_RATECONTROLMODE_CONSTANTBITRATE |
 +                                      SND_RATECONTROLMODE_VARIABLEBITRATE,
 +                              .profiles = SND_AUDIOPROFILE_AAC,
 +                              .modes = SND_AUDIOMODE_AAC_LC,
 +                              .formats = SND_AUDIOSTREAMFORMAT_MP4ADTS,
 +                              .min_buffer = 1024,
 +                      },
 +              },
 +      },
 +};
 +
 +static status_t tegra210_adsp_msg_handler(uint32_t msg, void *data);
 +
 +/*
 + * Utility functions
 + */
 +/* ADSP virtual register read/write functions */
 +static uint32_t tegra210_adsp_reg_read(struct tegra210_adsp *adsp, uint32_t reg)
 +{
 +      return atomic_read(&adsp->reg_val[reg]);
 +}
 +
 +static void tegra210_adsp_reg_write(struct tegra210_adsp *adsp,
 +                              uint32_t reg, uint32_t val)
 +{
 +      atomic_set(&adsp->reg_val[reg], val);
 +      dev_vdbg(adsp->dev, "%s : 0x%x -> 0x%x\n", __func__, reg, val);
 +}
 +
 +static void tegra210_adsp_reg_update_bits(struct tegra210_adsp *adsp,
 +                                      uint32_t reg, uint32_t mask,
 +                                      uint32_t val)
 +{
 +      uint32_t temp;
 +
 +      temp = tegra210_adsp_reg_read(adsp, reg);
 +      val = (val & mask) | (temp & ~mask);
 +      tegra210_adsp_reg_write(adsp, reg, val);
 +
 +      dev_vdbg(adsp->dev, "%s : 0x%x -> 0x%x\n", __func__, reg, val);
 +}
 +
 +/* API to get source widget id connected to a widget */
 +static uint32_t tegra210_adsp_get_source(struct tegra210_adsp *adsp,
 +                                       uint32_t reg)
 +{
 +      uint32_t source;
 +
 +      source = tegra210_adsp_reg_read(adsp, reg);
 +      source &= TEGRA210_ADSP_WIDGET_SOURCE_MASK;
 +      source >>= TEGRA210_ADSP_WIDGET_SOURCE_SHIFT;
 +
 +      return source;
 +}
 +/* ADSP shared memory allocate/free functions */
 +static int tegra210_adsp_preallocate_dma_buffer(struct device *dev, size_t size,
 +                              struct snd_dma_buffer *buf)
 +{
 +      dev_vdbg(dev, "%s : size %d.", __func__, (uint32_t)size);
 +
 +      buf->area = nvadsp_alloc_coherent(size, &buf->addr, GFP_KERNEL);
 +      if (!buf->area) {
 +              dev_err(dev, "Failed to pre-allocated DMA buffer.");
 +              return -ENOMEM;
 +      }
 +
 +      buf->bytes = size;
 +      buf->private_data = NULL;
 +      buf->dev.type = SNDRV_DMA_TYPE_DEV;
 +      buf->dev.dev = dev;
 +
 +      return 0;
 +}
 +
 +static void tegra210_adsp_deallocate_dma_buffer(struct snd_dma_buffer *buf)
 +{
 +      dev_vdbg(buf->dev.dev, "%s : size %d.", __func__, (uint32_t)buf->bytes);
 +
 +      if (!buf->area)
 +              return;
 +
 +      nvadsp_free_coherent(buf->bytes, buf->area, buf->addr);
 +      buf->area = NULL;
 +      buf->addr = 0;
 +}
 +
 +/* ADSP OS boot and init API */
 +static int tegra210_adsp_init(struct tegra210_adsp *adsp)
 +{
 +      int i, ret = 0;
 +
 +      mutex_lock(&adsp->mutex);
 +      ret = nvadsp_os_load();
 +      if (ret < 0) {
 +              dev_err(adsp->dev, "Failed to load OS.");
 +              goto exit;
 +      }
 +
 +      if (nvadsp_os_start()) {
 +              dev_err(adsp->dev, "Failed to start OS");
 +              goto exit;
 +      }
 +
 +      /* Load ADSP audio apps */
 +      for (i = 0; i < ARRAY_SIZE(adsp_app_desc); i++) {
 +               adsp_app_desc[i].handle = nvadsp_app_load(
 +                              adsp_app_desc[i].name,
 +                              adsp_app_desc[i].fw_name);
 +              if (ret < 0) {
 +                      dev_err(adsp->dev, "Failed to load app %s",
 +                                              adsp_app_desc[i].name);
 +                      goto exit;
 +              }
 +      }
 +      adsp->init_done = 1;
 +
 +exit:
 +      mutex_unlock(&adsp->mutex);
 +      return ret;
 +}
 +
 +static void tegra210_adsp_deinit(struct tegra210_adsp *adsp)
 +{
 +      mutex_lock(&adsp->mutex);
 +      if (adsp->init_done) {
 +              /* TODO : Stop ADSP OS if possible */
 +              adsp->init_done = 0;
 +      }
 +      mutex_unlock(&adsp->mutex);
 +}
 +
 +/* ADSP-CPU message send-receive utility functions */
 +static int tegra210_adsp_get_msg(apm_shared_state_t *apm, apm_msg_t *apm_msg)
 +{
 +      apm_msg->msgq_msg.size = MSGQ_MSG_WSIZE(apm_msg_t) -
 +              MSGQ_MESSAGE_HEADER_WSIZE;
 +      return msgq_dequeue_message(&apm->msgq_send.msgq,
 +              &apm_msg->msgq_msg);
 +}
 +
 +static int tegra210_adsp_send_msg(apm_shared_state_t *apm,
 +                                apm_msg_t *apm_msg, uint32_t flags)
 +{
 +      int ret = 0;
 +
 +      ret = msgq_queue_message(&apm->msgq_recv.msgq, &apm_msg->msgq_msg);
 +      if (ret < 0)
 +              return ret;
 +
 +      if (flags & TEGRA210_ADSP_MSG_FLAG_HOLD)
 +              return 0;
 +
 +      return nvadsp_hwmbox_send_data(apm->mbox_id,
 +              apm_cmd_msg_ready,
 +              NVADSP_MBOX_SMSG);
 +}
 +
 +static int tegra210_adsp_send_connect_msg(struct tegra210_adsp_app *src,
 +                                      struct tegra210_adsp_app *dst,
 +                                      uint32_t flags)
 +{
 +      apm_msg_t apm_msg;
 +
 +      apm_msg.msgq_msg.size = MSGQ_MSG_WSIZE(apm_fx_connect_params_t);
 +      apm_msg.msg.call_params.size = sizeof(apm_fx_connect_params_t);
 +      apm_msg.msg.call_params.method = nvfx_apm_method_fx_connect;
 +      apm_msg.msg.fx_connect_params.plugin_src.pvoid = IS_APM_IN(src->reg) ?
 +              NULL : src->plugin->plugin.pvoid;
 +      apm_msg.msg.fx_connect_params.pin_src = 0;
 +      apm_msg.msg.fx_connect_params.plugin_dst.pvoid = IS_APM_OUT(dst->reg) ?
 +              NULL : dst->plugin->plugin.pvoid;
 +      apm_msg.msg.fx_connect_params.pin_dst = 0;
 +
 +      return tegra210_adsp_send_msg(src->apm, &apm_msg, flags);
 +}
 +
 +static int tegra210_adsp_send_io_buffer_msg(struct tegra210_adsp_app *app,
 +                                      dma_addr_t addr, size_t size,
 +                                      uint32_t flags)
 +{
 +      apm_msg_t apm_msg;
 +
 +      apm_msg.msgq_msg.size = MSGQ_MSG_WSIZE(apm_io_buffer_params_t);
 +      apm_msg.msg.call_params.size = sizeof(apm_io_buffer_params_t);
 +      apm_msg.msg.call_params.method = nvfx_apm_method_set_io_buffer;
 +      apm_msg.msg.io_buffer_params.pin_type = IS_APM_IN(app->reg) ?
 +              NVFX_PIN_TYPE_INPUT : NVFX_PIN_TYPE_OUTPUT;
 +      apm_msg.msg.io_buffer_params.pin_id = 0;
 +      apm_msg.msg.io_buffer_params.addr.ptr = (uint64_t)addr;
 +      apm_msg.msg.io_buffer_params.size = size;
 +
 +      return tegra210_adsp_send_msg(app->apm, &apm_msg, flags);
 +}
 +
 +static int tegra210_adsp_send_period_size_msg(struct tegra210_adsp_app *app,
 +                                      size_t size, uint32_t flags)
 +{
 +      apm_msg_t apm_msg;
 +
 +      apm_msg.msgq_msg.size =
 +              MSGQ_MSG_WSIZE(apm_notification_params_t);
 +      apm_msg.msg.call_params.size =
 +              sizeof(apm_notification_params_t);
 +      apm_msg.msg.call_params.method = nvfx_apm_method_set_notification_size;
 +      apm_msg.msg.notification_params.pin_type = IS_APM_IN(app->reg) ?
 +              NVFX_PIN_TYPE_INPUT : NVFX_PIN_TYPE_OUTPUT;
 +      apm_msg.msg.notification_params.pin_id = 0;
 +      apm_msg.msg.notification_params.size = size;
 +
 +      return tegra210_adsp_send_msg(app->apm, &apm_msg, flags);
 +}
 +
 +static int tegra210_adsp_adma_params_msg(struct tegra210_adsp_app *app,
 +                                      nvfx_adma_init_params_t *params,
 +                                      uint32_t flags)
 +{
 +      apm_msg_t apm_msg;
 +
 +      apm_msg.msgq_msg.size = MSGQ_MSG_WSIZE(apm_fx_set_param_params_t);
 +      apm_msg.msg.call_params.size = sizeof(apm_fx_set_param_params_t);
 +      apm_msg.msg.call_params.method = nvfx_apm_method_fx_set_param;
 +      apm_msg.msg.fx_set_param_params.plugin.pvoid =
 +              app->plugin->plugin.pvoid;
 +
 +      params->call_params.size = sizeof(nvfx_adma_init_params_t);
 +      params->call_params.method = nvfx_adma_method_init;
 +      memcpy(&apm_msg.msg.fx_set_param_params.params, params,
 +              sizeof(*params));
 +
 +      return tegra210_adsp_send_msg(app->apm, &apm_msg, flags);
 +}
 +
 +static int tegra210_adsp_send_state_msg(struct tegra210_adsp_app *app,
 +                                      int32_t state, uint32_t flags)
 +{
 +      apm_msg_t apm_msg;
 +
 +      apm_msg.msgq_msg.size = MSGQ_MSG_WSIZE(nvfx_set_state_params_t);
 +      apm_msg.msg.call_params.size = sizeof(nvfx_set_state_params_t);
 +      apm_msg.msg.call_params.method = nvfx_method_set_state;
 +      apm_msg.msg.state_params.state = state;
 +
 +      return tegra210_adsp_send_msg(app->apm, &apm_msg, flags);
 +}
 +
 +static int tegra210_adsp_send_reset_msg(struct tegra210_adsp_app *app,
 +                                      uint32_t flags)
 +{
 +      apm_msg_t apm_msg;
 +
 +      apm_msg.msgq_msg.size = MSGQ_MSG_WSIZE(nvfx_reset_params_t);
 +      apm_msg.msg.call_params.size = sizeof(nvfx_reset_params_t);
 +      apm_msg.msg.call_params.method = nvfx_method_reset;
 +
 +      return tegra210_adsp_send_msg(app->apm, &apm_msg, flags);
 +}
 +
 +static int tegra210_adsp_send_eos_msg(struct tegra210_adsp_app *app,
 +                                      uint32_t flags)
 +{
 +      apm_msg_t apm_msg;
 +
 +      apm_msg.msgq_msg.size = MSGQ_MSG_WSIZE(apm_eos_params_t);
 +      apm_msg.msg.call_params.size = sizeof(apm_eos_params_t);
 +      apm_msg.msg.call_params.method = nvfx_apm_method_set_eos;
 +
 +      return tegra210_adsp_send_msg(app->apm, &apm_msg, flags);
 +}
 +
 +static int tegra210_adsp_send_pos_msg(struct tegra210_adsp_app *app,
 +                                      uint32_t pos, uint32_t flags)
 +{
 +      apm_msg_t apm_msg;
 +
 +      apm_msg.msgq_msg.size = MSGQ_MSG_WSIZE(apm_position_params_t);
 +      apm_msg.msg.call_params.size = sizeof(apm_position_params_t);
 +      apm_msg.msg.call_params.method = nvfx_apm_method_set_position;
 +      apm_msg.msg.position_params.pin_type = IS_APM_IN(app->reg) ?
 +              NVFX_PIN_TYPE_INPUT : NVFX_PIN_TYPE_OUTPUT;
 +      apm_msg.msg.position_params.pin_id = 0;
 +      apm_msg.msg.position_params.offset = pos;
 +
 +      return tegra210_adsp_send_msg(app->apm, &apm_msg, flags);
 +}
 +
 +/* ADSP app init/de-init APIs */
 +static int tegra210_adsp_app_init(struct tegra210_adsp *adsp,
 +                              struct tegra210_adsp_app *app)
 +{
 +      int ret = 0;
 +
 +      /* If app is already open or it is APM output pin don't open app */
 +      if (app->info || IS_APM_OUT(app->reg))
 +              return 0;
 +
 +      app->info = nvadsp_app_init(app->desc->handle, NULL);
 +      if (!app->info) {
 +              dev_err(adsp->dev, "Failed to init app %s(%s).",
 +                      app->desc->name, app->desc->fw_name);
 +              return -ENODEV;
 +      }
 +
 +      app->plugin = PLUGIN_SHARED_MEM(app->info->mem.shared);
 +      if (IS_APM_IN(app->reg)) {
 +              uint32_t apm_out_reg = APM_OUT_START +
 +                                      (app->reg - APM_IN_START);
 +              struct tegra210_adsp_app *apm_out = &adsp->apps[apm_out_reg];
 +
 +              app->apm = APM_SHARED_STATE(app->info->mem.shared);
 +
 +              ret = nvadsp_mbox_open(&app->rx_mbox,
 +                      &app->apm->mbox_id,
 +                      app->desc->name, tegra210_adsp_msg_handler, app);
 +              if (ret < 0) {
 +                      dev_err(adsp->dev, "Failed to init app %s(%s).",
 +                              app->desc->name, app->desc->fw_name);
 +                      goto err_app_exit;
 +              }
 +
 +              ret = nvadsp_app_start(app->info);
 +              if (ret < 0) {
 +                      dev_err(adsp->dev, "Failed to start adsp app");
 +                      goto err_mbox_close;
 +              }
 +              /* Copy APM IN app data to APM OUT app */
 +              apm_out->info = app->info;
 +              apm_out->plugin = app->plugin;
 +              apm_out->apm = app->apm;
 +      }
 +      return 0;
 +
 +err_mbox_close:
 +      nvadsp_mbox_close(&app->rx_mbox);
 +err_app_exit:
 +      app->info = NULL;
 +      return 0;
 +}
 +
 +static void tegra210_adsp_app_deinit(struct tegra210_adsp *adsp,
 +                              struct tegra210_adsp_app *app)
 +{
 +      if (!app || !app->desc)
 +              return;
 +
 +      if (app->info && !IS_APM_OUT(app->reg)) {
 +              nvadsp_mbox_close(&app->rx_mbox);
 +              nvadsp_app_stop(app->info);
 +              app->info = NULL;
 +              app->plugin = NULL;
 +              app->apm = NULL;
 +
 +              if (IS_APM_IN(app->reg)) {
 +                      uint32_t apm_out_reg = APM_OUT_START +
 +                                      (app->reg - APM_IN_START);
 +                      struct tegra210_adsp_app *apm_out =
 +                                      &adsp->apps[apm_out_reg];
 +
 +                      apm_out->info = NULL;
 +                      apm_out->plugin = NULL;
 +                      apm_out->apm = NULL;
 +              }
 +      }
 +}
 +
 +/* Recursive function to connect plugins under a APM */
 +static int tegra210_adsp_connect_plugin(struct tegra210_adsp *adsp,
 +                                      struct tegra210_adsp_app *app)
 +{
 +      struct tegra210_adsp_app *src;
 +      uint32_t source;
 +      int ret = 0;
 +
 +      source = tegra210_adsp_get_source(adsp, app->reg);
 +      if (!IS_ADSP_APP(source))
 +              return -ENODEV;
 +
 +      src = &adsp->apps[source];
 +      if (!IS_APM_IN(src->reg)) {
 +              ret = tegra210_adsp_connect_plugin(adsp, src);
 +              if (ret < 0)
 +                      return ret;
 +      }
 +      app->apm = src->apm;
 +
 +      /* If App is already connected and source connections have not changed
 +         no need to again send connect message */
 +      if (!ret && app->connect)
 +              return 0;
 +
 +      dev_vdbg(adsp->dev, "Connecting plugin 0x%x -> 0x%x",
 +              src->reg, app->reg);
 +
 +      ret = tegra210_adsp_send_connect_msg(src, app,
 +              TEGRA210_ADSP_MSG_FLAG_HOLD);
 +      if (ret < 0) {
 +              dev_err(adsp->dev, "Connect msg failed. err %d.", ret);
 +              return ret;
 +      }
 +      app->connect = 1;
 +
 +      /* return 1 if new connection is established */
 +      return 1;
 +}
 +
 +/* API to connect two APMs */
 +static int tegra210_adsp_connect_apm(struct tegra210_adsp *adsp,
 +                              struct tegra210_adsp_app *app)
 +{
 +      struct tegra210_adsp_app *src;
 +      uint32_t source;
 +      int ret = 0;
 +
 +      source = tegra210_adsp_get_source(adsp, app->reg);
 +      if (!IS_ADSP_APP(source))
 +              return -EPIPE;
 +
 +      src = &adsp->apps[source];
 +
 +      if (IS_APM_OUT(src->reg)) {
 +              /* If both APMs are in connected state no need to
 +                 send connect message */
 +              if (app->connect && src->connect)
 +                      return 0;
 +
 +              dev_vdbg(adsp->dev, "Connecting APM 0x%x -> 0x%x",
 +                      src->reg, app->reg);
 +
 +              ret = tegra210_adsp_send_connect_msg(src, app,
 +                      TEGRA210_ADSP_MSG_FLAG_HOLD);
 +              if (ret < 0) {
 +                      dev_err(adsp->dev, "Connect msg failed. err %d.", ret);
 +                      return ret;
 +              }
 +              return 1;
 +      }
 +      return -EPIPE;
 +}
 +
 +/* Iterate over all APMs and establish pending connections */
 +static int tegra210_adsp_update_connection(struct tegra210_adsp *adsp)
 +{
 +      int i;
 +
 +      for (i = APM_OUT_START; i <= APM_OUT_END; i++)
 +              tegra210_adsp_connect_plugin(adsp, &adsp->apps[i]);
 +
 +      for (i = APM_IN_START; i <= APM_IN_END; i++)
 +              tegra210_adsp_connect_apm(adsp, &adsp->apps[i]);
 +
 +      return 0;
 +}
 +
 +/* ADSP mailbox message handler */
 +static status_t tegra210_adsp_msg_handler(uint32_t msg, void *data)
 +{
 +      struct tegra210_adsp_app *app = data;
 +      apm_msg_t apm_msg;
 +      int ret = 0;
 +
 +      switch (msg) {
 +      case apm_cmd_msg_ready: {
 +              ret = tegra210_adsp_get_msg(app->apm, &apm_msg);
 +              if (ret < 0) {
 +                      pr_err("Dequeue failed %d.", ret);
 +                      break;
 +              }
 +
 +              if (app->msg_handler)
 +                      return app->msg_handler(app, &apm_msg);
 +      }
 +      break;
 +      default:
 +              pr_err("Unsupported mailbox msg %d.", msg);
 +      }
 +      return 0;
 +}
 +
 +static int tegra210_adsp_pcm_msg_handler(struct tegra210_adsp_app *app,
 +                                      apm_msg_t *apm_msg)
 +{
 +      struct tegra210_adsp_pcm_rtd *prtd = app->private_data;
 +
 +      switch (apm_msg->msg.call_params.method) {
 +      case nvfx_apm_method_set_position:
 +              snd_pcm_period_elapsed(prtd->substream);
 +              break;
 +      default:
 +              dev_err(prtd->dev, "Unsupported cmd %d.",
 +                      apm_msg->msg.call_params.method);
 +      }
 +      return 0;
 +}
 +
 +static int tegra210_adsp_compr_msg_handler(struct tegra210_adsp_app *app,
 +                                         apm_msg_t *apm_msg)
 +{
 +      struct tegra210_adsp_compr_rtd *prtd = app->private_data;
 +
 +      switch (apm_msg->msg.call_params.method) {
 +      case nvfx_apm_method_set_position:
 +              snd_compr_fragment_elapsed(prtd->cstream);
 +              break;
 +      case nvfx_apm_method_set_eos: {
 +              if (!prtd->is_draining) {
 +                      dev_warn(prtd->dev, "EOS reached before drain");
 +                      break;
 +              }
 +              tegra210_adsp_send_state_msg(prtd->fe_apm,
 +                      nvfx_state_inactive, 0);
 +              tegra210_adsp_send_reset_msg(prtd->fe_apm, 0);
 +              snd_compr_drain_notify(prtd->cstream);
 +              prtd->is_draining = 0;
 +      }
 +      break;
 +      default:
 +              dev_err(prtd->dev, "Unsupported cmd %d.",
 +                      apm_msg->msg.call_params.method);
 +      }
 +      return 0;
 +}
 +
 +/* Compress call-back APIs */
 +static int tegra210_adsp_compr_open(struct snd_compr_stream *cstream)
 +{
 +      struct snd_soc_pcm_runtime *rtd = cstream->device->private_data;
 +      struct tegra210_adsp *adsp =
 +              snd_soc_platform_get_drvdata(rtd->platform);
 +      struct tegra210_adsp_compr_rtd *prtd;
 +      uint32_t fe_reg = rtd->codec_dai->id;
 +      int i;
 +
 +      dev_vdbg(adsp->dev, "%s : DAI ID %d", __func__, rtd->codec_dai->id);
 +
 +      if (!adsp->init_done)
 +              return -ENODEV;
 +
 +      prtd = devm_kzalloc(adsp->dev, sizeof(struct tegra210_adsp_compr_rtd),
 +              GFP_KERNEL);
 +      if (!prtd) {
 +              dev_err(adsp->dev, "Failed to allocate adsp rtd.");
 +              return -ENOMEM;
 +      }
 +
 +      /* Find out the APM connected with ADSP-FE DAI */
 +      for (i = APM_IN_START; i <= APM_IN_END; i++) {
 +              struct tegra210_adsp_app *app = &adsp->apps[i];
 +              uint32_t source = tegra210_adsp_get_source(adsp, app->reg);
 +
 +              if (source == fe_reg) {
 +                      app->msg_handler = tegra210_adsp_compr_msg_handler;
 +                      app->private_data = prtd;
 +                      app->fe = 1;
 +                      prtd->fe_apm = app;
 +                      break;
 +              }
 +      }
 +
 +      if (!prtd->fe_apm) {
 +              dev_err(adsp->dev, "No FE APM found\n");
 +              devm_kfree(adsp->dev, prtd);
 +              return -ENODEV;
 +      }
 +
 +      prtd->cstream = cstream;
 +      prtd->dev = adsp->dev;
 +      cstream->runtime->private_data = prtd;
 +      return 0;
 +}
 +
 +static int tegra210_adsp_compr_free(struct snd_compr_stream *cstream)
 +{
 +      struct tegra210_adsp_compr_rtd *prtd = cstream->runtime->private_data;
 +
 +      if (prtd) {
 +              prtd->fe_apm->fe = 0;
 +              devm_kfree(prtd->dev, prtd);
 +      }
 +
 +      return 0;
 +}
 +
 +static int tegra210_adsp_compr_set_params(struct snd_compr_stream *cstream,
 +                      struct snd_compr_params *params)
 +{
 +      struct tegra210_adsp_compr_rtd *prtd = cstream->runtime->private_data;
 +      int ret = 0;
 +
 +      if (!prtd)
 +              return -ENODEV;
 +
 +      dev_vdbg(prtd->dev, "%s codec %d rate %d chan %d frag size %d frag %d",
 +               __func__, params->codec.id,
 +               snd_pcm_rate_bit_to_rate(params->codec.sample_rate),
 +               params->codec.ch_in, params->buffer.fragment_size,
 +               params->buffer.fragments);
 +
 +      ret = tegra210_adsp_preallocate_dma_buffer(prtd->dev,
 +              params->buffer.fragment_size * params->buffer.fragments,
 +              &prtd->buf);
 +      if (ret < 0)
 +              return ret;
 +
 +      ret = tegra210_adsp_send_io_buffer_msg(prtd->fe_apm, prtd->buf.addr,
 +                                      prtd->buf.bytes,
 +                                      TEGRA210_ADSP_MSG_FLAG_HOLD);
 +      if (ret < 0) {
 +              dev_err(prtd->dev, "IO buffer send msg failed. err %d.", ret);
 +              return ret;
 +      }
 +
 +      ret = tegra210_adsp_send_period_size_msg(prtd->fe_apm,
 +                                      params->buffer.fragment_size,
 +                                      TEGRA210_ADSP_MSG_FLAG_HOLD);
 +      if (ret < 0) {
 +              dev_err(prtd->dev, "Period size send msg failed. err %d.", ret);
 +              return ret;
 +      }
 +
 +      memcpy(&prtd->codec, &params->codec, sizeof(struct snd_codec));
 +      return 0;
 +}
 +
 +static int tegra210_adsp_compr_get_params(struct snd_compr_stream *cstream,
 +                      struct snd_codec *codec)
 +{
 +      struct tegra210_adsp_compr_rtd *prtd = cstream->runtime->private_data;
 +
 +      memcpy(codec, &prtd->codec, sizeof(struct snd_codec));
 +      return 0;
 +}
 +
 +static int tegra210_adsp_compr_trigger(struct snd_compr_stream *cstream,
 +                                      int cmd)
 +{
 +      struct tegra210_adsp_compr_rtd *prtd = cstream->runtime->private_data;
 +      int ret = 0;
 +
 +      dev_vdbg(prtd->dev, "%s : cmd %d", __func__, cmd);
 +
 +      switch (cmd) {
 +      case SNDRV_PCM_TRIGGER_START:
 +              ret = tegra210_adsp_send_state_msg(prtd->fe_apm,
 +                      nvfx_state_active, 0);
 +              if (ret < 0) {
 +                      dev_err(prtd->dev, "Failed to set state start.");
 +                      return ret;
 +              }
 +              break;
 +      case SNDRV_PCM_TRIGGER_RESUME:
 +      case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
 +              ret = tegra210_adsp_send_state_msg(prtd->fe_apm,
 +                              nvfx_state_active, 0);
 +              if (ret < 0) {
 +                      dev_err(prtd->dev, "Failed to set state resume");
 +                      return ret;
 +              }
 +              break;
 +      case SNDRV_PCM_TRIGGER_STOP:
 +              ret = tegra210_adsp_send_state_msg(prtd->fe_apm,
 +                      nvfx_state_inactive, 0);
 +              if (ret < 0) {
 +                      dev_err(prtd->dev, "Failed to set state stop");
 +                      return ret;
 +              }
 +
 +              ret = tegra210_adsp_send_reset_msg(prtd->fe_apm, 0);
 +              if (ret < 0) {
 +                      dev_err(prtd->dev, "Failed to reset");
 +                      return ret;
 +              }
 +              break;
 +      case SNDRV_PCM_TRIGGER_SUSPEND:
 +      case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
 +              ret = tegra210_adsp_send_state_msg(prtd->fe_apm,
 +                      nvfx_state_inactive, 0);
 +              if (ret < 0) {
 +                      dev_err(prtd->dev, "Failed to set state pause");
 +                      return ret;
 +              }
 +              break;
 +      case SND_COMPR_TRIGGER_DRAIN:
 +              prtd->is_draining = 1;
 +              ret = tegra210_adsp_send_eos_msg(prtd->fe_apm, 0);
 +              if (ret < 0) {
 +                      dev_err(prtd->dev, "Failed to set state drain");
 +                      return ret;
 +              }
 +              break;
 +
 +      default:
 +              dev_err(prtd->dev, "Unsupported state.");
 +              return -EINVAL;
 +      }
 +      return 0;
 +}
 +
 +static int tegra210_adsp_compr_copy(struct snd_compr_stream *cstream,
 +                      char __user *buf, size_t count)
 +{
 +      struct tegra210_adsp_compr_rtd *prtd = cstream->runtime->private_data;
 +      struct snd_compr_runtime *runtime = cstream->runtime;
 +      void *dstn;
 +      size_t copy;
 +      u64 app_pointer;
 +
 +      dev_vdbg(prtd->dev, "%s : size %d", __func__, (uint32_t)count);
 +
 +      if (!count)
 +              return 0;
 +
 +      app_pointer = div64_u64(runtime->total_bytes_available,
 +                              runtime->buffer_size);
 +      app_pointer = runtime->total_bytes_available -
 +                      (app_pointer * runtime->buffer_size);
 +      dstn = prtd->buf.area + app_pointer;
 +
 +      if (count < runtime->buffer_size - app_pointer) {
 +              if (copy_from_user(dstn, buf, count))
 +                      return -EFAULT;
 +      } else {
 +              copy = runtime->buffer_size - app_pointer;
 +              if (copy_from_user(dstn, buf, copy))
 +                      return -EFAULT;
 +              if (copy_from_user(prtd->buf.area, buf + copy, count - copy))
 +                      return -EFAULT;
 +      }
 +      tegra210_adsp_send_pos_msg(prtd->fe_apm,
 +          (runtime->total_bytes_available + count) % runtime->buffer_size, 0);
 +
 +      return count;
 +}
 +
 +static int tegra210_adsp_compr_pointer(struct snd_compr_stream *cstream,
 +                      struct snd_compr_tstamp *tstamp)
 +{
 +      struct tegra210_adsp_compr_rtd *prtd = cstream->runtime->private_data;
 +      struct tegra210_adsp_app *app = prtd->fe_apm;
 +      nvfx_shared_state_t *shared = &app->apm->nvfx_shared_state;
 +
 +      tstamp->byte_offset = shared->input[0].bytes %
 +              cstream->runtime->buffer_size;
 +      tstamp->copied_total = shared->input[0].bytes;
 +      tstamp->pcm_frames = shared->output[0].bytes / 4;
 +      /* TODO : calculate IO frames correctly */
 +      tstamp->pcm_io_frames = shared->output[0].bytes / 4;
 +      tstamp->sampling_rate = prtd->codec.sample_rate;
 +
 +      /* TODO : If SRC in path do size conversion */
 +
 +      dev_vdbg(prtd->dev, "%s off %d copied %d pcm %d pcm io %d",
 +               __func__, (int)tstamp->byte_offset, (int)tstamp->copied_total,
 +               (int)tstamp->pcm_frames, (int)tstamp->pcm_io_frames);
 +      return 0;
 +}
 +
 +static int tegra210_adsp_compr_get_caps(struct snd_compr_stream *cstream,
 +                      struct snd_compr_caps *caps)
 +{
 +      if (cstream->direction == SND_COMPRESS_PLAYBACK)
 +              memcpy(caps, &tegra210_adsp_compr_caps[SND_COMPRESS_PLAYBACK],
 +                      sizeof(struct snd_compr_caps));
 +      else
 +              memcpy(caps, &tegra210_adsp_compr_caps[SND_COMPRESS_CAPTURE],
 +                      sizeof(struct snd_compr_caps));
 +
 +      return 0;
 +}
 +
 +static int tegra210_adsp_compr_codec_caps(struct snd_compr_stream *cstream,
 +                      struct snd_compr_codec_caps *codec_caps)
 +{
 +      struct tegra210_adsp_compr_rtd *prtd = cstream->runtime->private_data;
 +
 +      dev_vdbg(prtd->dev, "%s : codec %d", __func__, codec_caps->codec);
 +
 +      if (!codec_caps->codec)
 +              codec_caps->codec = prtd->codec.id;
 +
 +      switch (codec_caps->codec) {
 +      case SND_AUDIOCODEC_MP3:
 +              memcpy(codec_caps, &adsp_compr_codec_caps[SND_AUDIOCODEC_MP3],
 +                      sizeof(struct snd_compr_codec_caps));
 +              return 0;
 +      case SND_AUDIOCODEC_AAC:
 +              memcpy(codec_caps, &adsp_compr_codec_caps[SND_AUDIOCODEC_AAC],
 +                      sizeof(struct snd_compr_codec_caps));
 +              return 0;
 +      default:
 +              dev_err(prtd->dev, "Unsupported codec %d", codec_caps->codec);
 +              return -EINVAL;
 +      }
 +      return 0;
 +}
 +
 +static struct snd_compr_ops tegra210_adsp_compr_ops = {
 +
 +      .open = tegra210_adsp_compr_open,
 +      .free = tegra210_adsp_compr_free,
 +      .set_params = tegra210_adsp_compr_set_params,
 +      .get_params = tegra210_adsp_compr_get_params,
 +      .trigger = tegra210_adsp_compr_trigger,
 +      .pointer = tegra210_adsp_compr_pointer,
 +      .copy = tegra210_adsp_compr_copy,
 +      .get_caps = tegra210_adsp_compr_get_caps,
 +      .get_codec_caps = tegra210_adsp_compr_codec_caps,
 +};
 +
 +/* PCM APIs */
 +static int tegra210_adsp_pcm_open(struct snd_pcm_substream *substream)
 +{
 +      struct snd_soc_pcm_runtime *rtd = substream->private_data;
 +      struct tegra210_adsp *adsp =
 +              snd_soc_platform_get_drvdata(rtd->platform);
 +      struct tegra210_adsp_pcm_rtd *prtd;
 +      uint32_t fe_reg = rtd->codec_dai->id;
 +      uint32_t source;
 +      int i, ret = 0;
 +
 +      dev_vdbg(adsp->dev, "%s", __func__);
 +
 +      prtd = devm_kzalloc(adsp->dev, sizeof(struct tegra210_adsp_pcm_rtd),
 +              GFP_KERNEL);
 +      if (!prtd) {
 +              dev_err(adsp->dev, "Failed to allocate adsp rtd.");
 +              return -ENOMEM;
 +      }
 +
 +      /* Find out the APM connected with ADSP-FE DAI */
 +      if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
 +              for (i = APM_IN_START; i < APM_IN_END; i++) {
 +                      struct tegra210_adsp_app *app = &adsp->apps[i];
 +
 +                      source = tegra210_adsp_get_source(adsp, app->reg);
 +                      if (source == fe_reg) {
 +                              prtd->fe_apm = app;
 +                              break;
 +                      }
 +              }
 +      } else {
 +              source = tegra210_adsp_get_source(adsp, fe_reg);
 +              if (IS_APM_OUT(source)) {
 +                      uint32_t apm_in_reg =
 +                              APM_IN_START + (source - APM_OUT_START);
 +                      adsp->apps[apm_in_reg].msg_handler =
 +                              tegra210_adsp_pcm_msg_handler;
 +                      adsp->apps[apm_in_reg].private_data = prtd;
 +                      prtd->fe_apm = &adsp->apps[source];
 +              }
 +      }
 +
 +      if (!prtd->fe_apm) {
 +              dev_err(adsp->dev, "No FE APM found\n");
 +              devm_kfree(adsp->dev, prtd);
 +              return -ENODEV;
 +      }
 +      prtd->fe_apm->msg_handler = tegra210_adsp_pcm_msg_handler;
 +      prtd->fe_apm->private_data = prtd;
 +      prtd->fe_apm->fe = 1;
 +
 +      /* Set HW params now that initialization is complete */
 +      snd_soc_set_runtime_hwparams(substream, &adsp_pcm_hardware);
 +
 +      /* Ensure period size is multiple of 4 */
 +      ret = snd_pcm_hw_constraint_step(substream->runtime, 0,
 +              SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 0x4);
 +      if (ret) {
 +              dev_err(adsp->dev, "failed to set constraint %d\n", ret);
 +              return ret;
 +      }
 +      substream->runtime->private_data = prtd;
 +      prtd->substream = substream;
 +      prtd->dev = adsp->dev;
 +
 +      return 0;
 +}
 +
 +static int tegra210_adsp_pcm_close(struct snd_pcm_substream *substream)
 +{
 +      struct tegra210_adsp_pcm_rtd *prtd = substream->runtime->private_data;
 +
 +      dev_vdbg(prtd->dev, "%s", __func__);
 +
 +      if (prtd) {
 +              prtd->fe_apm->fe = 1;
 +              devm_kfree(prtd->dev, prtd);
 +      }
 +
 +      return 0;
 +}
 +
 +static int tegra210_adsp_pcm_hw_params(struct snd_pcm_substream *substream,
 +                              struct snd_pcm_hw_params *params)
 +{
 +      struct tegra210_adsp_pcm_rtd *prtd = substream->runtime->private_data;
 +      struct snd_dma_buffer *buf = &substream->dma_buffer;
 +      int ret = 0;
 +
 +      dev_vdbg(prtd->dev, "%s rate %d chan %d bps %d"
 +              "period size %d buffer size %d",
 +               __func__, params_rate(params), params_channels(params),
 +               snd_pcm_format_width(params_format(params)),
 +               params_period_size(params),
 +               params_buffer_bytes(params));
 +
 +      ret = tegra210_adsp_send_io_buffer_msg(prtd->fe_apm, buf->addr,
 +                                      params_buffer_bytes(params),
 +                                      TEGRA210_ADSP_MSG_FLAG_HOLD);
 +      if (ret < 0)
 +              return ret;
 +
 +      ret = tegra210_adsp_send_period_size_msg(prtd->fe_apm,
 +                                      params_period_size(params),
 +                                      TEGRA210_ADSP_MSG_FLAG_HOLD);
 +      if (ret < 0)
 +              return ret;
 +
 +      snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
 +      return 0;
 +}
 +
 +static int tegra210_adsp_pcm_hw_free(struct snd_pcm_substream *substream)
 +{
 +      snd_pcm_set_runtime_buffer(substream, NULL);
 +      return 0;
 +}
 +
 +static int tegra210_adsp_pcm_trigger(struct snd_pcm_substream *substream,
 +                                   int cmd)
 +{
 +      struct tegra210_adsp_pcm_rtd *prtd = substream->runtime->private_data;
 +      int ret = 0;
 +
 +      dev_vdbg(prtd->dev, "%s : state %d", __func__, cmd);
 +
 +      switch (cmd) {
 +      case SNDRV_PCM_TRIGGER_START:
 +      case SNDRV_PCM_TRIGGER_RESUME:
 +      case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
 +              ret = tegra210_adsp_send_state_msg(prtd->fe_apm,
 +                      nvfx_state_active, 0);
 +              if (ret < 0) {
 +                      dev_err(prtd->dev, "Failed to set state");
 +                      return ret;
 +              }
 +              break;
 +      case SNDRV_PCM_TRIGGER_STOP:
 +      case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
 +      case SNDRV_PCM_TRIGGER_SUSPEND:
 +              ret = tegra210_adsp_send_state_msg(prtd->fe_apm,
 +                      nvfx_state_inactive, 0);
 +              if (ret < 0) {
 +                      dev_err(prtd->dev, "Failed to set state");
 +                      return ret;
 +              }
 +
 +              ret = tegra210_adsp_send_reset_msg(prtd->fe_apm, 0);
 +              if (ret < 0) {
 +                      dev_err(prtd->dev, "Failed to reset");
 +                      return ret;
 +              }
 +              break;
 +      default:
 +              dev_err(prtd->dev, "Unsupported state.");
 +              return -EINVAL;
 +      }
 +
 +      return 0;
 +}
 +
 +static int tegra210_adsp_pcm_ack(struct snd_pcm_substream *substream)
 +{
 +      struct tegra210_adsp_pcm_rtd *prtd = substream->runtime->private_data;
 +      struct snd_pcm_runtime *runtime = substream->runtime;
 +      size_t pos;
 +      int ret = 0;
 +
 +      dev_vdbg(prtd->dev, "%s %d", __func__, (int)runtime->control->appl_ptr);
 +
 +      pos = frames_to_bytes(runtime,
 +              runtime->control->appl_ptr % runtime->buffer_size);
 +      ret = tegra210_adsp_send_pos_msg(prtd->fe_apm, pos, 0);
 +      if (ret < 0) {
 +              dev_err(prtd->dev, "Failed to send write position.");
 +              return ret;
 +      }
 +
 +      return ret;
 +}
 +
 +static snd_pcm_uframes_t tegra210_adsp_pcm_pointer(
 +              struct snd_pcm_substream *substream)
 +{
 +      struct tegra210_adsp_pcm_rtd *prtd = substream->runtime->private_data;
 +      struct tegra210_adsp_app *app = prtd->fe_apm;
 +      size_t bytes, pos;
 +
 +      if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
 +              bytes = app->apm->nvfx_shared_state.output[0].bytes;
 +      else
 +              bytes = app->apm->nvfx_shared_state.input[0].bytes;
 +
 +      pos = bytes % frames_to_bytes(substream->runtime,
 +              substream->runtime->buffer_size);
 +
 +      /* TODO : If SRC in path do size conversion */
 +
 +      dev_vdbg(prtd->dev, "%s bytes %zu position %zu", __func__, bytes, pos);
 +      return bytes_to_frames(substream->runtime, pos);
 +}
 +
 +static struct snd_pcm_ops tegra210_adsp_pcm_ops = {
 +      .open           = tegra210_adsp_pcm_open,
 +      .close          = tegra210_adsp_pcm_close,
 +      .ioctl          = snd_pcm_lib_ioctl,
 +      .hw_params      = tegra210_adsp_pcm_hw_params,
 +      .hw_free        = tegra210_adsp_pcm_hw_free,
 +      .trigger        = tegra210_adsp_pcm_trigger,
 +      .pointer        = tegra210_adsp_pcm_pointer,
 +      .ack            = tegra210_adsp_pcm_ack,
 +};
 +
 +static int tegra210_adsp_pcm_new(struct snd_soc_pcm_runtime *rtd)
 +{
 +#if ENABLE_ADSP
 +      struct snd_card *card = rtd->card->snd_card;
 +      struct snd_pcm *pcm = rtd->pcm;
 +      int ret = 0;
 +
 +      if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) {
 +              struct snd_pcm_substream *substream =
 +                      pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
 +
 +              ret = tegra210_adsp_preallocate_dma_buffer(card->dev,
 +                                      adsp_pcm_hardware.buffer_bytes_max,
 +                                      &substream->dma_buffer);
 +
 +              if (ret)
 +                      return ret;
 +      }
 +
 +      if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) {
 +              struct snd_pcm_substream *substream =
 +                      pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream;
 +
 +              ret = tegra210_adsp_preallocate_dma_buffer(card->dev,
 +                                      adsp_pcm_hardware.buffer_bytes_max,
 +                                      &substream->dma_buffer);
 +              if (ret)
 +                      goto err;
 +      }
 +
 +      return 0;
 +
 +err:
 +      tegra210_adsp_deallocate_dma_buffer(
 +              &pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream->dma_buffer);
 +      return ret;
 +#else
 +      return 0;
 +#endif
 +}
 +
 +static void tegra210_adsp_pcm_free(struct snd_pcm *pcm)
 +{
 +      if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) {
 +              int stream = SNDRV_PCM_STREAM_PLAYBACK;
 +
 +              tegra210_adsp_deallocate_dma_buffer(
 +                      &pcm->streams[stream].substream->dma_buffer);
 +      }
 +      if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) {
 +              int stream = SNDRV_PCM_STREAM_CAPTURE;
 +
 +              tegra210_adsp_deallocate_dma_buffer(
 +                      &pcm->streams[stream].substream->dma_buffer);
 +      }
 +}
 +
 +static int tegra210_adsp_pcm_probe(struct snd_soc_platform *platform)
 +{
 +      platform->dapm.idle_bias_off = 1;
 +      return 0;
 +}
 +
 +/* ADSP-ADMAIF codec driver HW-params. Used for configuring ADMA */
 +static int tegra210_adsp_admaif_hw_params(struct snd_pcm_substream *substream,
 +                               struct snd_pcm_hw_params *params,
 +                               struct snd_soc_dai *dai)
 +{
 +      struct tegra210_adsp *adsp = snd_soc_dai_get_drvdata(dai);
 +      struct tegra210_adsp_app *app;
 +      nvfx_adma_init_params_t adma_params;
 +      uint32_t be_reg = dai->id;
 +      uint32_t admaif_id = be_reg - ADSP_ADMAIF_START + 1;
 +      uint32_t source;
 +      int i, ret;
 +
 +      dev_vdbg(adsp->dev, "%s : stream %d admaif %d\n",
 +              __func__, substream->stream, admaif_id);
 +
 +      adma_params.adma_channel = find_first_zero_bit(adsp->adma_usage,
 +                                      TEGRA210_ADSP_ADMA_CHANNEL_COUNT);
 +      if (adma_params.adma_channel >= TEGRA210_ADSP_ADMA_CHANNEL_COUNT) {
 +              dev_err(adsp->dev, "All ADMA channels are busy");
 +              return -EBUSY;
 +      }
 +      __set_bit(adma_params.adma_channel, adsp->adma_usage);
 +
 +      adma_params.adma_channel += TEGRA210_ADSP_ADMA_CHANNEL_START;
 +      adma_params.mode = ADMA_MODE_CONTINUOUS;
 +      adma_params.ahub_channel = admaif_id;
 +      adma_params.periods = 2; /* We need ping-pong buffers for ADMA */
 +
 +      /* Set DMA params connected with ADSP-BE */
 +      /* As a COCEC DAI, CAPTURE is transmit */
 +      if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
 +              app = &adsp->apps[be_reg];
 +              source = tegra210_adsp_get_source(adsp, app->reg);
 +
 +              app = &adsp->apps[source];
 +              if (!IS_APM_OUT(app->reg))
 +                      return 0;
 +
 +              source = tegra210_adsp_get_source(adsp, app->reg);
 +              app = &adsp->apps[source];
 +              if (!IS_ADMA(app->reg))
 +                      return 0;
 +
 +              app->adma_chan = adma_params.adma_channel;
 +              adma_params.direction = ADMA_MEMORY_TO_AHUB;
 +              adma_params.event.pvoid = app->apm->output_event.pvoid;
 +
 +              ret = tegra210_adsp_adma_params_msg(app, &adma_params, 0);
 +              if (ret < 0) {
 +                      dev_err(adsp->dev, "ADMA params msg failed. %d.", ret);
 +                      return ret;
 +              }
 +      } else {
 +              for (i = ADMA_START; i < ADMA_END; i++) {
 +                      app = &adsp->apps[i];
 +                      source = tegra210_adsp_get_source(adsp, app->reg);
 +                      if (!IS_APM_IN(source))
 +                              continue;
 +
 +                      app = &adsp->apps[source];
 +                      source = tegra210_adsp_get_source(adsp, app->reg);
 +                      if (source != be_reg)
 +                              continue;
 +
 +                      app = &adsp->apps[i];
 +                      app->adma_chan = adma_params.adma_channel;
 +                      adma_params.direction = ADMA_AHUB_TO_MEMORY;
 +                      adma_params.event.pvoid = app->apm->input_event.pvoid;
 +
 +                      ret = tegra210_adsp_adma_params_msg(app,
 +                                      &adma_params, 0);
 +                      if (ret < 0) {
 +                              dev_err(adsp->dev, "ADMA params msg failed");
 +                              return ret;
 +                      }
 +              }
 +      }
 +      return 0;
 +}
 +
 +/* ADSP platform driver read/write call-back */
 +static unsigned int tegra210_adsp_read(struct snd_soc_platform *platform,
 +              unsigned int reg)
 +{
 +      struct tegra210_adsp *adsp = snd_soc_platform_get_drvdata(platform);
 +
 +      dev_vdbg(adsp->dev, "%s [0x%x] -> 0x%x\n", __func__, reg,
 +              tegra210_adsp_reg_read(adsp, reg));
 +
 +      return tegra210_adsp_reg_read(adsp, reg);
 +}
 +
 +static int tegra210_adsp_write(struct snd_soc_platform *platform,
 +              unsigned int reg,
 +              unsigned int val)
 +{
 +      struct tegra210_adsp *adsp = snd_soc_platform_get_drvdata(platform);
 +
 +      dev_vdbg(adsp->dev, "%s [0x%x] -> 0x%x\n", __func__, reg, val);
 +
 +      tegra210_adsp_reg_write(adsp, reg, val);
 +      return 0;
 +}
 +
 +/* DAPM ENUM MUX get/put callbacks */
 +static int tegra210_adsp_mux_get(struct snd_kcontrol *kcontrol,
 +      struct snd_ctl_elem_value *ucontrol)
 +{
 +      struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
 +      struct snd_soc_dapm_widget *widget = wlist->widgets[0];
 +      struct snd_soc_platform *platform = widget->platform;
 +      struct soc_enum *e =
 +              (struct soc_enum *)kcontrol->private_value;
 +      struct tegra210_adsp *adsp = snd_soc_platform_get_drvdata(platform);
 +      uint32_t val = tegra210_adsp_reg_read(adsp, e->reg);
 +
 +      ucontrol->value.integer.value[0] =
 +              (val & TEGRA210_ADSP_WIDGET_SOURCE_MASK) >> e->shift_l;
 +      return 0;
 +}
 +
 +static int tegra210_adsp_mux_put(struct snd_kcontrol *kcontrol,
 +      struct snd_ctl_elem_value *ucontrol)
 +{
 +      struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
 +      struct snd_soc_dapm_widget *w = wlist->widgets[0];
 +      struct snd_soc_platform *platform = w->platform;
 +      uint32_t val = ucontrol->value.enumerated.item[0];
 +      struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
 +      struct tegra210_adsp *adsp = snd_soc_platform_get_drvdata(platform);
 +      struct tegra210_adsp_app *app;
 +      uint32_t cur_val = 0;
 +      int ret = 0;
 +
 +      if (!adsp->init_done)
 +              return -ENODEV;
 +
 +      /* Init or de-init app based on connection */
 +      if (IS_ADSP_APP(e->reg)) {
 +              app = &adsp->apps[e->reg];
 +              cur_val = tegra210_adsp_get_source(adsp, e->reg);
 +              if (cur_val != val)
 +                      app->connect = 0;
 +
 +              if (val == TEGRA210_ADSP_NONE) {
 +                      tegra210_adsp_app_deinit(adsp, app);
 +              } else {
 +                      ret = tegra210_adsp_app_init(adsp, app);
 +                      if (ret < 0) {
 +                              dev_err(adsp->dev, "Failed to init app.");
 +                              return -ENODEV;
 +                      }
 +              }
 +      }
 +      tegra210_adsp_reg_update_bits(adsp, e->reg,
 +              TEGRA210_ADSP_WIDGET_SOURCE_MASK, val << e->shift_l);
 +      tegra210_adsp_update_connection(adsp);
 +
 +      snd_soc_dapm_mux_update_power(w, kcontrol, val, e);
 +      return 1;
 +}
 +
 +/* ALSA control get/put call-back implementation */
 +static int tegra210_adsp_init_get(struct snd_kcontrol *kcontrol,
 +      struct snd_ctl_elem_value *ucontrol)
 +{
 +      struct snd_soc_platform *platform = snd_kcontrol_chip(kcontrol);
 +      struct tegra210_adsp *adsp = snd_soc_platform_get_drvdata(platform);
 +
 +      ucontrol->value.enumerated.item[0] = adsp->init_done;
 +      return 0;
 +}
 +
 +static int tegra210_adsp_init_put(struct snd_kcontrol *kcontrol,
 +      struct snd_ctl_elem_value *ucontrol)
 +{
 +      struct snd_soc_platform *platform = snd_kcontrol_chip(kcontrol);
 +      struct tegra210_adsp *adsp = snd_soc_platform_get_drvdata(platform);
 +      int init = ucontrol->value.enumerated.item[0];
 +      int ret = 0;
 +
 +      if (init == adsp->init_done)
 +              return 0;
 +
 +      if (init) {
 +              ret = tegra210_adsp_init(adsp);
 +              if (ret < 0) {
 +                      dev_err(adsp->dev, "Failed to init ADSP.");
 +                      return ret;
 +              }
 +      } else {
 +              tegra210_adsp_deinit(adsp);
 +      }
 +
 +      return 1;
 +}
 +
 +/* DAPM widget event */
 +static int tegra210_adsp_widget_event(struct snd_soc_dapm_widget *w,
 +                              struct snd_kcontrol *kcontrol, int event)
 +{
 +      struct snd_soc_platform *platform = w->platform;
 +      struct tegra210_adsp *adsp = snd_soc_platform_get_drvdata(platform);
 +      struct tegra210_adsp_app *app;
 +
 +      if (!IS_ADSP_APP(w->reg))
 +              return 0;
 +
 +      app = &adsp->apps[w->reg];
 +      /* For FE apm state change will be handled from trigger call back */
 +      if (app->fe)
 +              return 0;
 +
 +      if (SND_SOC_DAPM_EVENT_ON(event)) {
 +              if (IS_APM_IN(w->reg))
 +                      tegra210_adsp_send_state_msg(app, nvfx_state_active, 0);
 +      } else {
 +              if (IS_APM_IN(w->reg)) {
 +                      tegra210_adsp_send_state_msg(app, nvfx_state_inactive,
 +                              TEGRA210_ADSP_MSG_FLAG_HOLD);
 +                      tegra210_adsp_send_reset_msg(app, 0);
 +              } else if (IS_ADMA(w->reg)) {
 +                      __clear_bit(app->adma_chan -
 +                              TEGRA210_ADSP_ADMA_CHANNEL_START,
 +                              adsp->adma_usage);
 +              }
 +      }
 +
 +      return 0;
 +}
 +
 +static struct snd_soc_dai_ops tegra210_adsp_admaif_dai_ops = {
 +      .hw_params      = tegra210_adsp_admaif_hw_params,
 +};
 +
 +static struct snd_soc_dai_driver tegra210_adsp_dai[] = {
 +      {
 +              .name = "ADSP PCM",
 +              .playback = {
 +                      .stream_name = "ADSP PCM Receive",
 +                      .channels_min = 1,
 +                      .channels_max = 2,
 +                      .rates = SNDRV_PCM_RATE_8000_48000,
 +                      .formats = SNDRV_PCM_FMTBIT_S16_LE,
 +              },
 +              .capture = {
 +                      .stream_name = "ADSP PCM Transmit",
 +                      .channels_min = 1,
 +                      .channels_max = 2,
 +                      .rates = SNDRV_PCM_RATE_8000_48000,
 +                      .formats = SNDRV_PCM_FMTBIT_S16_LE,
 +              },
 +      },
 +      {
 +              .name = "ADSP COMPR1",
 +              .compress_dai = 1,
 +              .playback = {
 +                      .stream_name = "ADSP COMPR1 Receive",
 +                      .channels_min = 1,
 +                      .channels_max = 2,
 +                      .rates = SNDRV_PCM_RATE_8000_48000,
 +                      .formats = SNDRV_PCM_FMTBIT_S16_LE,
 +              },
 +      },
 +      {
 +              .name = "ADSP COMPR2",
 +              .compress_dai = 1,
 +              .playback = {
 +                      .stream_name = "ADSP COMPR2 Receive",
 +                      .channels_min = 1,
 +                      .channels_max = 2,
 +                      .rates = SNDRV_PCM_RATE_8000_48000,
 +                      .formats = SNDRV_PCM_FMTBIT_S16_LE,
 +              },
 +      },
 +};
 +
 +#define ADSP_FE_CODEC_DAI(idx)                                        \
 +      {                                                       \
 +              .name = "ADSP-FE" #idx,                         \
 +              .id = ADSP_FE_START + (idx - 1),                        \
 +              .playback = {                                   \
 +                      .stream_name = "ADSP-FE" #idx " Receive",\
 +                      .channels_min = 1,                      \
 +                      .channels_max = 2,                      \
 +                      .rates = SNDRV_PCM_RATE_8000_48000,     \
 +                      .formats = SNDRV_PCM_FMTBIT_S16_LE,     \
 +              },                                              \
 +              .capture = {                                    \
 +                      .stream_name = "ADSP-FE" #idx " Transmit",\
 +                      .channels_min = 1,                      \
 +                      .channels_max = 2,                      \
 +                      .rates = SNDRV_PCM_RATE_8000_48000,     \
 +                      .formats = SNDRV_PCM_FMTBIT_S16_LE,     \
 +              },                                              \
 +      }
 +
 +#define ADSP_ADMAIF_CODEC_DAI(idx)                            \
 +      {                                                       \
 +              .name = "ADSP-ADMAIF" #idx,                     \
 +              .id = ADSP_ADMAIF_START + (idx - 1),            \
 +              .playback = {                                   \
 +              .stream_name = "ADSP-ADMAIF" #idx " Receive",   \
 +                      .channels_min = 1,                      \
 +                      .channels_max = 2,                      \
 +                      .rates = SNDRV_PCM_RATE_8000_48000,     \
 +                      .formats = SNDRV_PCM_FMTBIT_S16_LE,     \
 +              },                                              \
 +              .capture = {                                    \
 +                      .stream_name = "ADSP-ADMAIF" #idx " Transmit",\
 +                      .channels_min = 1,                      \
 +                      .channels_max = 2,                      \
 +                      .rates = SNDRV_PCM_RATE_8000_48000,     \
 +                      .formats = SNDRV_PCM_FMTBIT_S16_LE,     \
 +              },                                              \
 +              .ops = &tegra210_adsp_admaif_dai_ops,           \
 +      }
 +
 +static struct snd_soc_dai_driver tegra210_adsp_codec_dai[] = {
 +      ADSP_FE_CODEC_DAI(1),
 +      ADSP_FE_CODEC_DAI(2),
 +      ADSP_FE_CODEC_DAI(3),
 +      ADSP_FE_CODEC_DAI(4),
 +      ADSP_FE_CODEC_DAI(5),
 +      ADSP_ADMAIF_CODEC_DAI(1),
 +      ADSP_ADMAIF_CODEC_DAI(2),
 +      ADSP_ADMAIF_CODEC_DAI(3),
 +      ADSP_ADMAIF_CODEC_DAI(4),
 +      ADSP_ADMAIF_CODEC_DAI(5),
 +      ADSP_ADMAIF_CODEC_DAI(6),
 +      ADSP_ADMAIF_CODEC_DAI(7),
 +      ADSP_ADMAIF_CODEC_DAI(8),
 +      ADSP_ADMAIF_CODEC_DAI(9),
 +      ADSP_ADMAIF_CODEC_DAI(10),
 +};
 +
 +/* This array is linked with tegra210_adsp_virt_widgets enum defines. Any thing
 +   changed in enum define should be also reflected here and vice-versa */
 +static const char * const tegra210_adsp_mux_texts[] = {
 +      "None",
 +      "ADSP-FE1",
 +      "ADSP-FE2",
 +      "ADSP-FE3",
 +      "ADSP-FE4",
 +      "ADSP-FE5",
 +      "ADSP-ADMAIF1",
 +      "ADSP-ADMAIF2",
 +      "ADSP-ADMAIF3",
 +      "ADSP-ADMAIF4",
 +      "ADSP-ADMAIF5",
 +      "ADSP-ADMAIF6",
 +      "ADSP-ADMAIF7",
 +      "ADSP-ADMAIF8",
 +      "ADSP-ADMAIF9",
 +      "ADSP-ADMAIF10",
 +      "APM-IN1",
 +      "APM-IN2",
 +      "APM-IN3",
 +      "APM-IN4",
 +      "APM-IN5",
 +      "APM-IN6",
 +      "APM-IN7",
 +      "APM-IN8",
 +      "APM-OUT1",
 +      "APM-OUT2",
 +      "APM-OUT3",
 +      "APM-OUT4",
 +      "APM-OUT5",
 +      "APM-OUT6",
 +      "APM-OUT7",
 +      "APM-OUT8",
 +      "ADMA1",
 +      "ADMA2",
 +      "ADMA3",
 +      "ADMA4",
 +      "MP3-DEC1",
 +      "MP3-DEC2",
 +      "AAC-DEC1",
 +      "AAC-DEC2",
 +      "SPKPROT-SW",
 +};
 +
 +#define ADSP_MUX_ENUM_CTRL_DECL(ename, reg)           \
 +      SOC_ENUM_SINGLE_DECL(ename##_enum, reg,         \
 +              TEGRA210_ADSP_WIDGET_SOURCE_SHIFT,              \
 +              tegra210_adsp_mux_texts);                               \
 +      static const struct snd_kcontrol_new ename##_ctrl =             \
 +              SOC_DAPM_ENUM_EXT("ADSP Route", ename##_enum,           \
 +                      tegra210_adsp_mux_get, tegra210_adsp_mux_put)
 +
 +static ADSP_MUX_ENUM_CTRL_DECL(adsp_fe1, TEGRA210_ADSP_FRONT_END1);
 +static ADSP_MUX_ENUM_CTRL_DECL(adsp_fe2, TEGRA210_ADSP_FRONT_END2);
 +static ADSP_MUX_ENUM_CTRL_DECL(adsp_fe3, TEGRA210_ADSP_FRONT_END3);
 +static ADSP_MUX_ENUM_CTRL_DECL(adsp_fe4, TEGRA210_ADSP_FRONT_END4);
 +static ADSP_MUX_ENUM_CTRL_DECL(adsp_fe5, TEGRA210_ADSP_FRONT_END5);
 +static ADSP_MUX_ENUM_CTRL_DECL(adsp_admaif1, TEGRA210_ADSP_ADMAIF1);
 +static ADSP_MUX_ENUM_CTRL_DECL(adsp_admaif2, TEGRA210_ADSP_ADMAIF2);
 +static ADSP_MUX_ENUM_CTRL_DECL(adsp_admaif3, TEGRA210_ADSP_ADMAIF3);
 +static ADSP_MUX_ENUM_CTRL_DECL(adsp_admaif4, TEGRA210_ADSP_ADMAIF4);
 +static ADSP_MUX_ENUM_CTRL_DECL(adsp_admaif5, TEGRA210_ADSP_ADMAIF5);
 +static ADSP_MUX_ENUM_CTRL_DECL(adsp_admaif6, TEGRA210_ADSP_ADMAIF6);
 +static ADSP_MUX_ENUM_CTRL_DECL(adsp_admaif7, TEGRA210_ADSP_ADMAIF7);
 +static ADSP_MUX_ENUM_CTRL_DECL(adsp_admaif8, TEGRA210_ADSP_ADMAIF8);
 +static ADSP_MUX_ENUM_CTRL_DECL(adsp_admaif9, TEGRA210_ADSP_ADMAIF9);
 +static ADSP_MUX_ENUM_CTRL_DECL(adsp_admaif10, TEGRA210_ADSP_ADMAIF10);
 +static ADSP_MUX_ENUM_CTRL_DECL(apm_in1, TEGRA210_ADSP_APM_IN1);
 +static ADSP_MUX_ENUM_CTRL_DECL(apm_in2, TEGRA210_ADSP_APM_IN2);
 +static ADSP_MUX_ENUM_CTRL_DECL(apm_in3, TEGRA210_ADSP_APM_IN3);
 +static ADSP_MUX_ENUM_CTRL_DECL(apm_in4, TEGRA210_ADSP_APM_IN4);
 +static ADSP_MUX_ENUM_CTRL_DECL(apm_in5, TEGRA210_ADSP_APM_IN5);
 +static ADSP_MUX_ENUM_CTRL_DECL(apm_in6, TEGRA210_ADSP_APM_IN6);
 +static ADSP_MUX_ENUM_CTRL_DECL(apm_in7, TEGRA210_ADSP_APM_IN7);
 +static ADSP_MUX_ENUM_CTRL_DECL(apm_in8, TEGRA210_ADSP_APM_IN8);
 +static ADSP_MUX_ENUM_CTRL_DECL(apm_out1, TEGRA210_ADSP_APM_OUT1);
 +static ADSP_MUX_ENUM_CTRL_DECL(apm_out2, TEGRA210_ADSP_APM_OUT2);
 +static ADSP_MUX_ENUM_CTRL_DECL(apm_out3, TEGRA210_ADSP_APM_OUT3);
 +static ADSP_MUX_ENUM_CTRL_DECL(apm_out4, TEGRA210_ADSP_APM_OUT4);
 +static ADSP_MUX_ENUM_CTRL_DECL(apm_out5, TEGRA210_ADSP_APM_OUT5);
 +static ADSP_MUX_ENUM_CTRL_DECL(apm_out6, TEGRA210_ADSP_APM_OUT6);
 +static ADSP_MUX_ENUM_CTRL_DECL(apm_out7, TEGRA210_ADSP_APM_OUT7);
 +static ADSP_MUX_ENUM_CTRL_DECL(apm_out8, TEGRA210_ADSP_APM_OUT8);
 +static ADSP_MUX_ENUM_CTRL_DECL(adma1, TEGRA210_ADSP_PLUGIN_ADMA1);
 +static ADSP_MUX_ENUM_CTRL_DECL(adma2, TEGRA210_ADSP_PLUGIN_ADMA2);
 +static ADSP_MUX_ENUM_CTRL_DECL(adma3, TEGRA210_ADSP_PLUGIN_ADMA3);
 +static ADSP_MUX_ENUM_CTRL_DECL(adma4, TEGRA210_ADSP_PLUGIN_ADMA4);
 +static ADSP_MUX_ENUM_CTRL_DECL(mp3_dec1, TEGRA210_ADSP_PLUGIN_MP3_DEC1);
 +static ADSP_MUX_ENUM_CTRL_DECL(mp3_dec2, TEGRA210_ADSP_PLUGIN_MP3_DEC2);
 +static ADSP_MUX_ENUM_CTRL_DECL(aac_dec1, TEGRA210_ADSP_PLUGIN_AAC_DEC1);
 +static ADSP_MUX_ENUM_CTRL_DECL(aac_dec2, TEGRA210_ADSP_PLUGIN_AAC_DEC2);
 +static ADSP_MUX_ENUM_CTRL_DECL(spkprot, TEGRA210_ADSP_PLUGIN_SPKPROT);
 +
 +#define ADSP_EP_WIDGETS(sname, ename)                                 \
 +      SND_SOC_DAPM_AIF_IN(sname " RX", NULL, 0, SND_SOC_NOPM, 0, 0),  \
 +      SND_SOC_DAPM_AIF_OUT(sname " TX", NULL, 0, SND_SOC_NOPM, 0, 0), \
 +      SND_SOC_DAPM_MUX(sname " MUX", SND_SOC_NOPM, 0, 0, &ename##_ctrl)
 +
 +#define ADSP_WIDGETS(sname, ename, reg)                                       \
 +      SND_SOC_DAPM_AIF_OUT_E(sname " TX", NULL, 0, reg,               \
 +              TEGRA210_ADSP_WIDGET_EN_SHIFT, 0, tegra210_adsp_widget_event, \
 +              SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),          \
 +      SND_SOC_DAPM_MUX(sname " MUX", SND_SOC_NOPM, 0, 0, &ename##_ctrl)
 +
 +static const struct snd_soc_dapm_widget tegra210_adsp_widgets[] = {
 +      ADSP_EP_WIDGETS("ADSP-FE1", adsp_fe1),
 +      ADSP_EP_WIDGETS("ADSP-FE2", adsp_fe2),
 +      ADSP_EP_WIDGETS("ADSP-FE3", adsp_fe3),
 +      ADSP_EP_WIDGETS("ADSP-FE4", adsp_fe4),
 +      ADSP_EP_WIDGETS("ADSP-FE5", adsp_fe5),
 +      ADSP_EP_WIDGETS("ADSP-ADMAIF1", adsp_admaif1),
 +      ADSP_EP_WIDGETS("ADSP-ADMAIF2", adsp_admaif2),
 +      ADSP_EP_WIDGETS("ADSP-ADMAIF3", adsp_admaif3),
 +      ADSP_EP_WIDGETS("ADSP-ADMAIF4", adsp_admaif4),
 +      ADSP_EP_WIDGETS("ADSP-ADMAIF5", adsp_admaif5),
 +      ADSP_EP_WIDGETS("ADSP-ADMAIF6", adsp_admaif6),
 +      ADSP_EP_WIDGETS("ADSP-ADMAIF7", adsp_admaif7),
 +      ADSP_EP_WIDGETS("ADSP-ADMAIF8", adsp_admaif8),
 +      ADSP_EP_WIDGETS("ADSP-ADMAIF9", adsp_admaif9),
 +      ADSP_EP_WIDGETS("ADSP-ADMAIF10", adsp_admaif10),
 +      ADSP_WIDGETS("APM-IN1", apm_in1, TEGRA210_ADSP_APM_IN1),
 +      ADSP_WIDGETS("APM-IN2", apm_in2, TEGRA210_ADSP_APM_IN2),
 +      ADSP_WIDGETS("APM-IN3", apm_in3, TEGRA210_ADSP_APM_IN3),
 +      ADSP_WIDGETS("APM-IN4", apm_in4, TEGRA210_ADSP_APM_IN4),
 +      ADSP_WIDGETS("APM-IN5", apm_in5, TEGRA210_ADSP_APM_IN5),
 +      ADSP_WIDGETS("APM-IN6", apm_in6, TEGRA210_ADSP_APM_IN6),
 +      ADSP_WIDGETS("APM-IN7", apm_in7, TEGRA210_ADSP_APM_IN7),
 +      ADSP_WIDGETS("APM-IN8", apm_in8, TEGRA210_ADSP_APM_IN8),
 +      ADSP_WIDGETS("APM-OUT1", apm_out1, TEGRA210_ADSP_APM_OUT1),
 +      ADSP_WIDGETS("APM-OUT2", apm_out2, TEGRA210_ADSP_APM_OUT2),
 +      ADSP_WIDGETS("APM-OUT3", apm_out3, TEGRA210_ADSP_APM_OUT3),
 +      ADSP_WIDGETS("APM-OUT4", apm_out4, TEGRA210_ADSP_APM_OUT4),
 +      ADSP_WIDGETS("APM-OUT5", apm_out5, TEGRA210_ADSP_APM_OUT5),
 +      ADSP_WIDGETS("APM-OUT6", apm_out6, TEGRA210_ADSP_APM_OUT6),
 +      ADSP_WIDGETS("APM-OUT7", apm_out7, TEGRA210_ADSP_APM_OUT7),
 +      ADSP_WIDGETS("APM-OUT8", apm_out8, TEGRA210_ADSP_APM_OUT8),
 +      ADSP_WIDGETS("ADMA1", adma1, TEGRA210_ADSP_PLUGIN_ADMA1),
 +      ADSP_WIDGETS("ADMA2", adma2, TEGRA210_ADSP_PLUGIN_ADMA2),
 +      ADSP_WIDGETS("ADMA3", adma3, TEGRA210_ADSP_PLUGIN_ADMA3),
 +      ADSP_WIDGETS("ADMA4", adma4, TEGRA210_ADSP_PLUGIN_ADMA4),
 +      ADSP_WIDGETS("MP3-DEC1", mp3_dec1, TEGRA210_ADSP_PLUGIN_MP3_DEC1),
 +      ADSP_WIDGETS("MP3-DEC2", mp3_dec2, TEGRA210_ADSP_PLUGIN_MP3_DEC2),
 +      ADSP_WIDGETS("AAC-DEC1", aac_dec1, TEGRA210_ADSP_PLUGIN_AAC_DEC1),
 +      ADSP_WIDGETS("AAC-DEC2", aac_dec2, TEGRA210_ADSP_PLUGIN_AAC_DEC2),
 +      ADSP_WIDGETS("SPKPROT-SW", spkprot, TEGRA210_ADSP_PLUGIN_SPKPROT),
 +};
 +
 +#define ADSP_EP_ROUTES(name)                                  \
 +      { name " MUX",          "ADSP-FE1", "ADSP-FE1 RX"},     \
 +      { name " MUX",          "ADSP-FE2", "ADSP-FE2 RX"},     \
 +      { name " MUX",          "ADSP-FE3", "ADSP-FE3 RX"},     \
 +      { name " MUX",          "ADSP-FE4", "ADSP-FE4 RX"},     \
 +      { name " MUX",          "ADSP-FE5", "ADSP-FE5 RX"},     \
 +      { name " MUX",          "ADSP-ADMAIF1", "ADSP-ADMAIF1 RX"}, \
 +      { name " MUX",          "ADSP-ADMAIF2", "ADSP-ADMAIF2 RX"}, \
 +      { name " MUX",          "ADSP-ADMAIF3", "ADSP-ADMAIF3 RX"}, \
 +      { name " MUX",          "ADSP-ADMAIF4", "ADSP-ADMAIF4 RX"}, \
 +      { name " MUX",          "ADSP-ADMAIF5", "ADSP-ADMAIF5 RX"}, \
 +      { name " MUX",          "ADSP-ADMAIF6", "ADSP-ADMAIF6 RX"}, \
 +      { name " MUX",          "ADSP-ADMAIF7", "ADSP-ADMAIF7 RX"}, \
 +      { name " MUX",          "ADSP-ADMAIF8", "ADSP-ADMAIF8 RX"}, \
 +      { name " MUX",          "ADSP-ADMAIF9", "ADSP-ADMAIF9 RX"}, \
 +      { name " MUX",          "ADSP-ADMAIF10", "ADSP-ADMAIF10 RX"}
 +
 +#define ADSP_APM_IN_ROUTES(name)                              \
 +      { name " MUX",  "APM-IN1",      "APM-IN1 TX"},          \
 +      { name " MUX",  "APM-IN2",      "APM-IN2 TX"},          \
 +      { name " MUX",  "APM-IN3",      "APM-IN3 TX"},          \
 +      { name " MUX",  "APM-IN4",      "APM-IN4 TX"},          \
 +      { name " MUX",  "APM-IN5",      "APM-IN5 TX"},          \
 +      { name " MUX",  "APM-IN6",      "APM-IN6 TX"},          \
 +      { name " MUX",  "APM-IN7",      "APM-IN7 TX"},          \
 +      { name " MUX",  "APM-IN8",      "APM-IN8 TX"}
 +
 +#define ADSP_APM_OUT_ROUTES(name)                             \
 +      { name " MUX",          "APM-OUT1", "APM-OUT1 TX"},     \
 +      { name " MUX",          "APM-OUT2", "APM-OUT2 TX"},     \
 +      { name " MUX",          "APM-OUT3", "APM-OUT3 TX"},     \
 +      { name " MUX",          "APM-OUT4", "APM-OUT4 TX"},     \
 +      { name " MUX",          "APM-OUT5", "APM-OUT5 TX"},     \
 +      { name " MUX",          "APM-OUT6", "APM-OUT6 TX"},     \
 +      { name " MUX",          "APM-OUT7", "APM-OUT7 TX"},     \
 +      { name " MUX",          "APM-OUT8", "APM-OUT8 TX"}
 +
 +#define ADSP_ADMA_ROUTES(name)                                        \
 +      { name " MUX",  "ADMA1",        "ADMA1 TX"},            \
 +      { name " MUX",  "ADMA2",        "ADMA2 TX"},            \
 +      { name " MUX",  "ADMA3",        "ADMA3 TX"},            \
 +      { name " MUX",  "ADMA4",        "ADMA4 TX"}
 +
 +#define ADSP_DEC_ROUTES(name)                                 \
 +      { name " MUX",  "MP3-DEC1",     "MP3-DEC1 TX"},         \
 +      { name " MUX",  "MP3-DEC2",     "MP3-DEC2 TX"},         \
 +      { name " MUX",  "AAC-DEC1",     "AAC-DEC1 TX"},         \
 +      { name " MUX",  "AAC-DEC2",     "AAC-DEC2 TX"}
 +
 +#define ADSP_SPKPROT_ROUTES(name)                             \
 +      { name " MUX",  "SPKPROT-SW",   "SPKPROT-SW TX"}
 +
 +#define ADSP_EP_MUX_ROUTES(name)                              \
 +      { name " RX",           NULL, name " Receive"},         \
 +      { name " Transmit",     NULL, name " TX"},              \
 +      { name " TX",           NULL, name " MUX"},             \
 +      ADSP_APM_OUT_ROUTES(name)
 +
 +#define ADSP_APM_IN_MUX_ROUTES(name)                          \
 +      { name " TX",           NULL, name " MUX"},             \
 +      ADSP_EP_ROUTES(name),                                   \
 +      ADSP_APM_OUT_ROUTES(name)
 +
 +#define ADSP_APM_OUT_MUX_ROUTES(name)                         \
 +      { name " TX",           NULL, name " MUX"},             \
 +      ADSP_ADMA_ROUTES(name),                                 \
 +      ADSP_DEC_ROUTES(name)
 +
 +#define ADSP_DEC_MUX_ROUTES(name)                             \
 +      { name " TX",           NULL, name " MUX"},             \
 +      ADSP_APM_IN_ROUTES(name)
 +
 +#define ADSP_ADMA_MUX_ROUTES(name)                            \
 +      { name " TX",           NULL, name " MUX"},             \
 +      ADSP_APM_IN_ROUTES(name),                               \
 +      ADSP_DEC_ROUTES(name),                                  \
 +      ADSP_SPKPROT_ROUTES(name)
 +
 +#define ADSP_SPKPROT_MUX_ROUTES(name)                         \
 +      { name " TX",           NULL, name " MUX"},             \
 +      ADSP_APM_IN_ROUTES(name),                               \
 +      ADSP_DEC_ROUTES(name),                                  \
 +      ADSP_ADMA_ROUTES(name)
 +
 +static const struct snd_soc_dapm_route tegra210_adsp_routes[] = {
 +      ADSP_EP_MUX_ROUTES("ADSP-FE1"),
 +      ADSP_EP_MUX_ROUTES("ADSP-FE2"),
 +      ADSP_EP_MUX_ROUTES("ADSP-FE3"),
 +      ADSP_EP_MUX_ROUTES("ADSP-FE4"),
 +      ADSP_EP_MUX_ROUTES("ADSP-FE5"),
 +      ADSP_EP_MUX_ROUTES("ADSP-ADMAIF1"),
 +      ADSP_EP_MUX_ROUTES("ADSP-ADMAIF2"),
 +      ADSP_EP_MUX_ROUTES("ADSP-ADMAIF3"),
 +      ADSP_EP_MUX_ROUTES("ADSP-ADMAIF4"),
 +      ADSP_EP_MUX_ROUTES("ADSP-ADMAIF5"),
 +      ADSP_EP_MUX_ROUTES("ADSP-ADMAIF6"),
 +      ADSP_EP_MUX_ROUTES("ADSP-ADMAIF7"),
 +      ADSP_EP_MUX_ROUTES("ADSP-ADMAIF8"),
 +      ADSP_EP_MUX_ROUTES("ADSP-ADMAIF9"),
 +      ADSP_EP_MUX_ROUTES("ADSP-ADMAIF10"),
 +
 +      ADSP_APM_IN_MUX_ROUTES("APM-IN1"),
 +      ADSP_APM_IN_MUX_ROUTES("APM-IN2"),
 +      ADSP_APM_IN_MUX_ROUTES("APM-IN3"),
 +      ADSP_APM_IN_MUX_ROUTES("APM-IN4"),
 +      ADSP_APM_IN_MUX_ROUTES("APM-IN5"),
 +      ADSP_APM_IN_MUX_ROUTES("APM-IN6"),
 +      ADSP_APM_IN_MUX_ROUTES("APM-IN7"),
 +      ADSP_APM_IN_MUX_ROUTES("APM-IN8"),
 +
 +      ADSP_APM_OUT_MUX_ROUTES("APM-OUT1"),
 +      ADSP_APM_OUT_MUX_ROUTES("APM-OUT2"),
 +      ADSP_APM_OUT_MUX_ROUTES("APM-OUT3"),
 +      ADSP_APM_OUT_MUX_ROUTES("APM-OUT4"),
 +      ADSP_APM_OUT_MUX_ROUTES("APM-OUT5"),
 +      ADSP_APM_OUT_MUX_ROUTES("APM-OUT6"),
 +      ADSP_APM_OUT_MUX_ROUTES("APM-OUT7"),
 +      ADSP_APM_OUT_MUX_ROUTES("APM-OUT8"),
 +
 +      ADSP_ADMA_MUX_ROUTES("ADMA1"),
 +      ADSP_ADMA_MUX_ROUTES("ADMA2"),
 +      ADSP_ADMA_MUX_ROUTES("ADMA3"),
 +      ADSP_ADMA_MUX_ROUTES("ADMA4"),
 +
 +      ADSP_DEC_MUX_ROUTES("MP3-DEC1"),
 +      ADSP_DEC_MUX_ROUTES("MP3-DEC2"),
 +      ADSP_DEC_MUX_ROUTES("AAC-DEC1"),
 +      ADSP_DEC_MUX_ROUTES("AAC-DEC2"),
 +
 +      ADSP_SPKPROT_MUX_ROUTES("SPKPROT-SW"),
 +};
 +
 +static const struct snd_kcontrol_new tegra210_adsp_controls[] = {
 +      SOC_SINGLE_BOOL_EXT("ADSP init", 0,
 +              tegra210_adsp_init_get, tegra210_adsp_init_put),
 +};
 +
 +static const struct snd_soc_component_driver tegra210_adsp_component = {
 +      .name           = "tegra210-adsp",
 +};
 +
 +static int tegra210_adsp_codec_probe(struct snd_soc_codec *codec)
 +{
 +      return 0;
 +}
 +
 +static struct snd_soc_codec_driver tegra210_adsp_codec = {
 +      .probe = tegra210_adsp_codec_probe,
 +      .idle_bias_off = 1,
 +};
 +
 +static struct snd_soc_platform_driver tegra210_adsp_platform = {
 +      .ops                    = &tegra210_adsp_pcm_ops,
 +      .compr_ops              = &tegra210_adsp_compr_ops,
 +      .pcm_new                = tegra210_adsp_pcm_new,
 +      .pcm_free               = tegra210_adsp_pcm_free,
 +      .probe                  = tegra210_adsp_pcm_probe,
 +      .read                   = tegra210_adsp_read,
 +      .write                  = tegra210_adsp_write,
 +      .dapm_widgets           = tegra210_adsp_widgets,
 +      .num_dapm_widgets       = ARRAY_SIZE(tegra210_adsp_widgets),
 +      .dapm_routes            = tegra210_adsp_routes,
 +      .num_dapm_routes        = ARRAY_SIZE(tegra210_adsp_routes),
 +      .controls               = tegra210_adsp_controls,
 +      .num_controls           = ARRAY_SIZE(tegra210_adsp_controls),
 +};
 +
 +static u64 tegra_dma_mask = DMA_BIT_MASK(32);
 +static int tegra210_adsp_audio_platform_probe(struct platform_device *pdev)
 +{
 +      struct tegra210_adsp *adsp;
 +      int i, j, ret = 0;
 +
 +      pr_info("tegra210_adsp_audio_platform_probe: platform probe started\n");
 +
 +      adsp = devm_kzalloc(&pdev->dev, sizeof(*adsp), GFP_KERNEL);
 +      if (!adsp) {
 +              dev_err(&pdev->dev, "Can't allocate tegra30_adsp_ctx\n");
 +              return -ENOMEM;
 +      }
 +      dev_set_drvdata(&pdev->dev, adsp);
 +      adsp->dev = &pdev->dev;
 +
 +      mutex_init(&adsp->mutex);
 +      pdev->dev.dma_mask = &tegra_dma_mask;
 +      pdev->dev.coherent_dma_mask = tegra_dma_mask;
 +
 +      tegra_ape_pd_add_device(&pdev->dev);
 +      pm_genpd_dev_need_save(&pdev->dev, true);
 +      pm_genpd_dev_need_restore(&pdev->dev, true);
 +
 +      pm_runtime_enable(&pdev->dev);
 +      if (!pm_runtime_enabled(&pdev->dev))
 +              goto err_pm_disable;
 +
 +      pm_runtime_get_sync(&pdev->dev);
 +      /* HACK : Should be handled through dma-engine */
 +      for (i = 0; i < TEGRA210_ADSP_ADMA_CHANNEL_COUNT; i++) {
 +              ret = tegra_agic_route_interrupt(
 +                      INT_ADMA_EOT0 + TEGRA210_ADSP_ADMA_CHANNEL_START + i,
 +                      TEGRA_AGIC_ADSP);
 +              if (ret < 0) {
 +                      dev_err(&pdev->dev, "Failed to route INT to ADSP");
 +                      goto err_pm_disable;
 +              }
 +      }
 +      /* HACK end */
 +      pm_runtime_put(&pdev->dev);
 +
 +      for (i = 0; i < TEGRA210_ADSP_VIRT_REG_MAX; i++)
 +              adsp->apps[i].reg = i;
 +
 +      for (i = 0; i < ARRAY_SIZE(adsp_app_desc); i++) {
 +              for (j = adsp_app_desc[i].reg_start;
 +                      j <= adsp_app_desc[i].reg_end; j++)
 +                      adsp->apps[j].desc = &adsp_app_desc[i];
 +      }
 +
 +      ret = snd_soc_register_platform(&pdev->dev, &tegra210_adsp_platform);
 +      if (ret) {
 +              dev_err(&pdev->dev, "Could not register platform: %d\n", ret);
 +              goto err_pm_disable;
 +      }
 +
 +      ret = snd_soc_register_component(&pdev->dev, &tegra210_adsp_component,
 +                      tegra210_adsp_dai, ARRAY_SIZE(tegra210_adsp_dai));
 +      if (ret) {
 +              dev_err(&pdev->dev, "Could not register component: %d\n", ret);
 +              goto err_unregister_platform;
 +      }
 +
 +      ret = snd_soc_register_codec(&pdev->dev, &tegra210_adsp_codec,
 +                                   tegra210_adsp_codec_dai,
 +                                   ARRAY_SIZE(tegra210_adsp_codec_dai));
 +      if (ret != 0) {
 +              dev_err(&pdev->dev, "Could not register CODEC: %d\n", ret);
 +              goto err_unregister_platform;
 +      }
 +
 +      pr_info("tegra210_adsp_audio_platform_probe probe successfull.");
 +      return 0;
 +
 +err_unregister_platform:
 +      snd_soc_unregister_platform(&pdev->dev);
 +err_pm_disable:
 +      pm_runtime_disable(&pdev->dev);
 +      tegra_ape_pd_remove_device(&pdev->dev);
 +      return ret;
 +}
 +
 +static int tegra210_adsp_audio_platform_remove(struct platform_device *pdev)
 +{
 +      pm_runtime_disable(&pdev->dev);
 +      tegra_ape_pd_remove_device(&pdev->dev);
 +      snd_soc_unregister_platform(&pdev->dev);
 +      return 0;
 +}
 +
 +static const struct of_device_id tegra210_adsp_audio_of_match[] = {
 +      { .compatible = "nvidia,tegra210-adsp-audio", },
 +      {},
 +};
 +
 +static struct platform_driver tegra210_adsp_audio_driver = {
 +      .driver = {
 +              .name = DRV_NAME,
 +              .owner = THIS_MODULE,
 +              .of_match_table = tegra210_adsp_audio_of_match,
 +      },
 +      .probe = tegra210_adsp_audio_platform_probe,
 +      .remove = tegra210_adsp_audio_platform_remove,
 +};
 +module_platform_driver(tegra210_adsp_audio_driver);
 +
 +MODULE_AUTHOR("Sumit Bhattacharya <sumitb@nvidia.com>");
 +MODULE_DESCRIPTION("Tegra210 ADSP Audio driver");
 +MODULE_LICENSE("GPL");
 +MODULE_ALIAS("platform:" DRV_NAME);
 +MODULE_DEVICE_TABLE(of, tegra210_adsp_audio_of_match);
 +
index e81a813ab6a8ffac5a9f811ab3cb4cbea0a2c441,0000000000000000000000000000000000000000..66cc45503956d2263a150ec2d6c0c3510fe73926
mode 100644,000000..100644
--- /dev/null
@@@ -1,1480 -1,0 +1,1484 @@@
-                               .sample_rates = SNDRV_PCM_RATE_44100 |
 +/*
 + * tegra30_avp.c - Tegra AVP audio driver
 + *
 + * Author: Sumit Bhattacharya <sumitb@nvidia.com>
 + * Copyright (c) 2014, NVIDIA CORPORATION.  All rights reserved.
 + *
 + * 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.
 + */
 +
 +/*
 +#define VERBOSE_DEBUG 1
 +#define DEBUG 1
 +#define DEBUG_AVP
 +*/
 +
 +#include <linux/module.h>
 +#include <linux/fs.h>
 +#include <linux/completion.h>
 +#include <linux/uaccess.h>
 +#include <linux/delay.h>
 +#include <linux/dma-mapping.h>
 +#include <linux/platform_device.h>
 +#include <linux/firmware.h>
 +#include <linux/kthread.h>
 +
 +#include <linux/dmaengine.h>
 +
 +#include <sound/compress_offload.h>
 +#include <sound/compress_params.h>
 +#include <sound/pcm.h>
 +
 +#include <linux/tegra_avp_audio.h>
 +#include "tegra_offload.h"
 +
 +#include <linux/tegra_nvavp.h>
 +#include "../../drivers/media/platform/tegra/nvavp/nvavp_os.h"
 +
 +#define DRV_NAME "tegra30-avp-audio"
 +
 +/******************************************************************************
 + * DEFINES, ENUMS & GLOBALS
 + *****************************************************************************/
 +#define AVP_UCODE_HEADER              "NVAVPAPP"
 +#define AVP_UCODE_HEADER_SIZE         (sizeof(AVP_UCODE_HEADER) - 1)
 +#define AVP_INIT_SAMPLE_RATE          48000
 +
 +#define AVP_COMPR_THRESHOLD           (4 * 1024)
 +#define AVP_UNITY_STREAM_VOLUME               0x10000
 +
 +#define AVP_CMD_BUFFER_SIZE           256
 +
 +enum avp_compr_formats {
 +      avp_compr_mp3,
 +      avp_compr_aac,
 +      avp_compr_max,
 +};
 +
 +#ifdef DEBUG_AVP
 +#define DUMP_AVP_STATUS(s) \
 +      pr_info("%s %d : id %d:: SS %d Halt %d RP %d %d PP %d LP %d FD %d "\
 +              "WP %d WC %d DS %d DBWP %d SR %d DBRP %d\n",\
 +              __func__, __LINE__,\
 +              s->id,\
 +              (int)s->stream->stream_state_current,\
 +              s->stream->halted,\
 +              s->stream->source_buffer_read_position,\
 +              s->stream->source_buffer_read_position_fraction,\
 +              s->stream->source_buffer_presentation_position,\
 +              s->stream->source_buffer_linear_position,\
 +              s->stream->source_buffer_frames_decoded,\
 +              s->stream->source_buffer_write_position,\
 +              s->stream->source_buffer_write_count,\
 +              (int)s->audio_avp->audio_engine->device_state_current,\
 +              s->audio_avp->audio_engine->device_buffer_write_position,\
 +              s->stream->stream_notification_request, \
 +              s->audio_avp->audio_engine->device_buffer_read_position);
 +#else
 +#define DUMP_AVP_STATUS(s)
 +#endif
 +
 +/******************************************************************************
 + * STRUCTURES
 + *****************************************************************************/
 +
 +static const struct tegra30_avp_ucode_desc {
 +      int max_mem_size;
 +      const char *bin_name;
 +} avp_ucode_desc[] = {
 +      [CODEC_PCM] = {25000, "nvavp_aud_ucode.bin" },
 +      [CODEC_MP3] = {45000, "nvavp_mp3dec_ucode.bin" },
 +      [CODEC_AAC] = {80000, "nvavp_aacdec_ucode.bin" },
 +};
 +
 +struct tegra30_avp_audio_dma {
 +      struct tegra_offload_dma_params params;
 +      struct dma_chan                 *chan;
 +      struct dma_async_tx_descriptor  *chan_desc;
 +      struct dma_slave_config         chan_slave_config;
 +      dma_cookie_t                    chan_cookie;
 +
 +      atomic_t                        is_dma_allocated;
 +      atomic_t                        active_count;
 +};
 +
 +struct tegra30_avp_stream {
 +      struct tegra30_avp_audio        *audio_avp;
 +      struct tegra_offload_mem        source_buf;
 +      struct stream_data              *stream;
 +      enum avp_audio_stream_id        id;
 +      int                             period_size;
 +
 +      /* TODO : Use spinlock in appropriate places */
 +      spinlock_t                      lock;
 +
 +      unsigned int                    last_notification_offset;
 +      unsigned int                    notification_received;
 +      unsigned int                    source_buffer_offset;
 +
 +      void            (*notify_cb)(void *args, unsigned int is_eos);
 +      void            *notify_args;
 +      unsigned int    is_drain_called;
 +};
 +
 +struct tegra30_avp_audio {
 +      struct device                   *dev;
 +
 +      nvavp_clientctx_t               nvavp_client;
 +      struct audio_engine_data        *audio_engine;
 +      struct tegra30_avp_stream       avp_stream[RENDERSW_MAX_STREAMS];
 +
 +      struct tegra_offload_mem        ucode_mem;
 +      struct tegra_offload_mem        cmd_buf_mem;
 +      struct tegra_offload_mem        param_mem;
 +
 +      unsigned int                    *cmd_buf;
 +      int                             cmd_buf_idx;
 +      atomic_t                stream_active_count;
 +      struct tegra30_avp_audio_dma    audio_dma;
 +      spinlock_t                      lock;
 +};
 +
 +struct snd_compr_caps tegra30_avp_compr_caps[SND_COMPRESS_CAPTURE + 1] = {
 +      [SND_COMPRESS_PLAYBACK] = {
 +              .num_codecs = avp_compr_max,
 +              .direction = SND_COMPRESS_PLAYBACK,
 +              .min_fragment_size = 1024,
 +              .max_fragment_size = 1024 * 1024,       /* 1 MB */
 +              .min_fragments = 2,
 +              .max_fragments = 1024,
 +              .codecs = {
 +                      [0] = SND_AUDIOCODEC_MP3,
 +                      [1] = SND_AUDIOCODEC_AAC,
 +              },
 +      },
 +      [SND_COMPRESS_CAPTURE] = {
 +              .num_codecs = 0,
 +              .direction = SND_COMPRESS_CAPTURE,
 +      },
 +};
 +
 +struct snd_compr_codec_caps tegra30_avp_compr_codec_caps[] = {
 +      [avp_compr_mp3] = {
 +              .codec = SND_AUDIOCODEC_MP3,
 +              .num_descriptors = 1,
 +              .descriptor = {
 +                      [0] = {
 +                              .max_ch = 2,
-                               .sample_rates = SNDRV_PCM_RATE_44100 |
++                              .sample_rates = {
++                                      [0] = SNDRV_PCM_RATE_44100 |
 +                                              SNDRV_PCM_RATE_48000,
++                              },
 +                              .bit_rate = {
 +                                      [0] = 32000,
 +                                      [1] = 64000,
 +                                      [2] = 128000,
 +                                      [3] = 256000,
 +                                      [4] = 320000,
 +                              },
 +                              .num_bitrates = 5,
 +                              .rate_control =
 +                                      SND_RATECONTROLMODE_CONSTANTBITRATE |
 +                                      SND_RATECONTROLMODE_VARIABLEBITRATE,
 +                              .profiles = 0,
 +                              .modes = SND_AUDIOCHANMODE_MP3_STEREO,
 +                              .formats = SND_AUDIOSTREAMFORMAT_UNDEFINED,
 +                              .min_buffer = 1024,
 +                      },
 +              },
 +      },
 +      [avp_compr_aac] = {
 +              .codec = SND_AUDIOCODEC_AAC,
 +              .num_descriptors = 1,
 +              .descriptor = {
 +                      [0] = {
 +                              .max_ch = 2,
++                              .sample_rates = {
++                                      [0] = SNDRV_PCM_RATE_44100 |
 +                                              SNDRV_PCM_RATE_48000,
++                              },
 +                              .bit_rate = {
 +                                      [0] = 32000,
 +                                      [1] = 64000,
 +                                      [2] = 128000,
 +                                      [3] = 256000,
 +                                      [4] = 320000,
 +                              },
 +                              .num_bitrates = 5,
 +                              .rate_control =
 +                                      SND_RATECONTROLMODE_CONSTANTBITRATE |
 +                                      SND_RATECONTROLMODE_VARIABLEBITRATE,
 +                              .profiles = SND_AUDIOPROFILE_AAC,
 +                              .modes = SND_AUDIOMODE_AAC_LC,
 +                              .formats = SND_AUDIOSTREAMFORMAT_RAW,
 +                              .min_buffer = 1024,
 +                      },
 +              },
 +      },
 +};
 +
 +static struct tegra30_avp_audio *avp_audio_ctx;
 +
 +/******************************************************************************
 + * PRIVATE FUNCTIONS
 + *****************************************************************************/
 +
 +static int tegra30_avp_mem_alloc(struct tegra_offload_mem *mem, size_t size)
 +{
 +      mem->virt_addr = dma_alloc_coherent(avp_audio_ctx->dev, size,
 +                                              &mem->phys_addr, GFP_KERNEL);
 +      if (!mem->virt_addr) {
 +              dev_err(avp_audio_ctx->dev, "Failed to allocate memory");
 +              return -ENOMEM;
 +      }
 +      mem->bytes = size;
 +      mem->dev = avp_audio_ctx->dev;
 +      memset(mem->virt_addr, 0, mem->bytes);
 +
 +      dev_vdbg(mem->dev, "%s::virt %p phys %llx size %d\n", __func__,
 +              mem->virt_addr, (u64)mem->phys_addr, (int)size);
 +      return 0;
 +}
 +
 +static void tegra30_avp_mem_free(struct tegra_offload_mem *mem)
 +{
 +      if (mem->virt_addr)
 +              dma_free_coherent(mem->dev, mem->bytes,
 +                      mem->virt_addr, mem->phys_addr);
 +}
 +
 +static int tegra30_avp_load_ucode(void)
 +{
 +      struct tegra30_avp_audio *audio_avp = avp_audio_ctx;
 +      struct audio_engine_data *audio_engine;
 +      const struct firmware *ucode_fw;
 +      const struct tegra30_avp_ucode_desc *ucode_desc;
 +      int ucode_size = 0, ucode_offset = 0, total_ucode_size = 0;
 +      int i, ret = 0;
 +
 +      dev_vdbg(audio_avp->dev, "%s", __func__);
 +
 +      for (i = 0; i < ARRAY_SIZE(avp_ucode_desc); i++)
 +              total_ucode_size += avp_ucode_desc[i].max_mem_size;
 +
 +      /* allocate memory for Ucode */
 +      ret = tegra30_avp_mem_alloc(&audio_avp->ucode_mem, total_ucode_size);
 +      if (ret < 0) {
 +              dev_err(audio_avp->dev, "Failed to allocate ucode memory");
 +              return ret;
 +      }
 +
 +      /* allocate memory for parameters */
 +      ret = tegra30_avp_mem_alloc(&audio_avp->param_mem,
 +              sizeof(struct audio_engine_data));
 +      if (ret < 0) {
 +              dev_err(audio_avp->dev, "Failed to allocate param memory");
 +              goto err_ucode_mem_free;
 +      }
 +      audio_engine =
 +              (struct audio_engine_data *)audio_avp->param_mem.virt_addr;
 +      audio_avp->audio_engine = audio_engine;
 +      audio_engine->codec_ucode_base_address =
 +              audio_avp->ucode_mem.phys_addr;
 +
 +      for (i = 0; i < ARRAY_SIZE(avp_ucode_desc); i++) {
 +              ucode_desc = &avp_ucode_desc[i];
 +
 +              ret = request_firmware(&ucode_fw, ucode_desc->bin_name,
 +                      audio_avp->dev);
 +              if (ret < 0) {
 +                      dev_err(audio_avp->dev, "cannot read audio firmware %s",
 +                              ucode_desc->bin_name);
 +                      goto err_param_mem_free;
 +              }
 +
 +              ucode_size = ucode_fw->size;
 +              if (ucode_size <= 0) {
 +                      dev_err(audio_avp->dev, "Invalid ucode size.");
 +                      ret = -EINVAL;
 +                      release_firmware(ucode_fw);
 +                      goto err_param_mem_free;
 +              }
 +              dev_vdbg(audio_avp->dev, "%s ucode size = %d bytes",
 +                      ucode_desc->bin_name, ucode_size);
 +
 +              /* Read ucode */
 +              if (strncmp((const char *)ucode_fw->data, AVP_UCODE_HEADER,
 +                      AVP_UCODE_HEADER_SIZE)) {
 +                      dev_err(audio_avp->dev, "ucode Header mismatch");
 +                      ret = -EINVAL;
 +                      release_firmware(ucode_fw);
 +                      goto err_param_mem_free;
 +              }
 +
 +              memcpy((char *)audio_avp->ucode_mem.virt_addr + ucode_offset,
 +                      ucode_fw->data + AVP_UCODE_HEADER_SIZE,
 +                      ucode_size - AVP_UCODE_HEADER_SIZE);
 +
 +              audio_engine->codec_ucode_desc[i][UCODE_DESC_OFFSET] =
 +                                                              ucode_offset;
 +              audio_engine->codec_ucode_desc[i][UCODE_DESC_SIZE] =
 +                                              ucode_desc->max_mem_size;
 +              ucode_offset += ucode_desc->max_mem_size;
 +
 +              release_firmware(ucode_fw);
 +      }
 +
 +      /* allocate memory for command buffer */
 +      ret = tegra30_avp_mem_alloc(&audio_avp->cmd_buf_mem,
 +                      AVP_CMD_BUFFER_SIZE);
 +      if (ret < 0) {
 +              dev_err(audio_avp->dev, "Failed to allocate cmd buffer memory");
 +              goto err_param_mem_free;
 +      }
 +      audio_avp->cmd_buf = (unsigned int *)audio_avp->cmd_buf_mem.virt_addr;
 +
 +      /* Set command */
 +      audio_avp->cmd_buf_idx = 0;
 +      audio_avp->cmd_buf[audio_avp->cmd_buf_idx++] =
 +              NVE26E_CH_OPCODE_INCR(NVE276_SET_MICROCODE_A, 3);
 +      audio_avp->cmd_buf[audio_avp->cmd_buf_idx++] = 0;
 +      audio_avp->cmd_buf[audio_avp->cmd_buf_idx++] =
 +              audio_avp->ucode_mem.phys_addr;
 +      audio_avp->cmd_buf[audio_avp->cmd_buf_idx++] =
 +              avp_ucode_desc[CODEC_PCM].max_mem_size - 8;
 +      audio_avp->cmd_buf[audio_avp->cmd_buf_idx++] =
 +              NVE26E_CH_OPCODE_INCR(NVE276_PARAMETER_METHOD(0), 1);
 +      audio_avp->cmd_buf[audio_avp->cmd_buf_idx++] =
 +              audio_avp->param_mem.phys_addr;
 +
 +      ret = nvavp_pushbuffer_submit_audio(audio_avp->nvavp_client,
 +                                          audio_avp->cmd_buf_mem.phys_addr,
 +                                          audio_avp->cmd_buf_idx);
 +      if (ret < 0) {
 +              dev_err(audio_avp->dev, "pushbuffer_submit failed %d", ret);
 +              ret = -EINVAL;
 +              goto err_cmd_buf_mem_free;
 +      }
 +      dev_vdbg(audio_avp->dev, "Successfully loaded audio ucode");
 +      return 0;
 +
 +err_cmd_buf_mem_free:
 +      tegra30_avp_mem_free(&audio_avp->cmd_buf_mem);
 +err_param_mem_free:
 +      tegra30_avp_mem_free(&audio_avp->param_mem);
 +err_ucode_mem_free:
 +      tegra30_avp_mem_free(&audio_avp->ucode_mem);
 +      return ret;
 +}
 +
 +static void tegra30_avp_audio_engine_init(void)
 +{
 +      struct tegra30_avp_audio *audio_avp = avp_audio_ctx;
 +      struct audio_engine_data *audio_engine = audio_avp->audio_engine;
 +      int i = 0;
 +
 +      /* Initialize audio_engine_data */
 +#if defined(CONFIG_ARCH_TEGRA_14x_SOC) || defined(CONFIG_ARCH_TEGRA_12x_SOC)
 +      audio_engine->chip_id = NV_TEGRA_T148;
 +#else
 +      audio_engine->chip_id = NV_TEGRA_T114;
 +#endif
 +      audio_engine->device_state_target = KSSTATE_STOP;
 +      audio_engine->device_state_current = KSSTATE_STOP;
 +
 +#ifdef DEBUG_AVP
 +      audio_engine->profile_state = PROFILE_RENDER_OVERALL_PROCESSING;
 +#endif
 +      audio_engine->device_format.rate = AVP_INIT_SAMPLE_RATE;
 +      audio_engine->device_format.bits_per_sample = 16;
 +      audio_engine->device_format.channels = 2;
 +
 +      /* Initialize stream memory */
 +      for (i = 0; i < max_stream_id; i++) {
 +              struct tegra30_avp_stream *avp_stream;
 +              struct stream_data *stream;
 +              int j = 0;
 +
 +              avp_stream = &audio_avp->avp_stream[i];
 +              memset(avp_stream, 0, sizeof(struct tegra30_avp_stream));
 +
 +              avp_stream->id = i;
 +
 +              stream = &audio_engine->stream[avp_stream->id];
 +              avp_stream->stream = stream;
 +              spin_lock_init(&avp_stream->lock);
 +
 +              stream->stream_state_target = KSSTATE_STOP;
 +              stream->source_buffer_write_position = 0;
 +              stream->source_buffer_write_count = 0;
 +              stream->stream_params.rate = AVP_INIT_SAMPLE_RATE;
 +
 +              for (j = 0; j < RENDERSW_MAX_CHANNELS; j++)
 +                      stream->stream_volume[j] = AVP_UNITY_STREAM_VOLUME;
 +
 +              stream->source_buffer_read_position = 0;
 +              stream->source_buffer_presentation_position = 0;
 +              stream->source_buffer_linear_position = 0;
 +              stream->source_buffer_presentation_position = 0;
 +              stream->source_buffer_frames_decoded = 0;
 +              stream->stream_state_current = KSSTATE_STOP;
 +
 +              stream->stream_params.rate = AVP_INIT_SAMPLE_RATE;
 +              stream->stream_params.bits_per_sample = 16;
 +              stream->stream_params.channels = 2;
 +
 +              avp_stream->audio_avp = audio_avp;
 +      }
 +}
 +
 +static int tegra30_avp_audio_alloc_dma(struct tegra_offload_dma_params *params)
 +{
 +      struct tegra30_avp_audio *audio_avp = avp_audio_ctx;
 +      struct tegra30_avp_audio_dma *dma = &audio_avp->audio_dma;
 +      struct audio_engine_data *audio_engine = audio_avp->audio_engine;
 +      dma_cap_mask_t mask;
 +      int ret = 0;
 +
 +      dev_vdbg(audio_avp->dev, "%s: is_dma_allocated %d",
 +                      __func__, atomic_read(&dma->is_dma_allocated));
 +
 +      if (atomic_read(&dma->is_dma_allocated) == 1)
 +              return 0;
 +
 +      memcpy(&dma->params, params, sizeof(struct tegra_offload_dma_params));
 +
 +      dma_cap_zero(mask);
 +      dma_cap_set(DMA_SLAVE, mask);
 +      dma_cap_set(DMA_CYCLIC, mask);
 +      dma->chan = dma_request_channel(mask, NULL, NULL);
 +      if (dma->chan == NULL) {
 +              dev_err(audio_avp->dev, "Failed to allocate DMA chan.");
 +              return -ENOMEM;
 +      }
 +
 +      /* Only playback is supported */
 +      dma->chan_slave_config.direction = DMA_MEM_TO_DEV;
 +      dma->chan_slave_config.dst_addr_width = dma->params.width;
 +      dma->chan_slave_config.dst_addr = dma->params.addr;
 +      dma->chan_slave_config.dst_maxburst = dma->params.max_burst;
 +      dma->chan_slave_config.slave_id = dma->params.req_sel;
 +
 +      ret = dmaengine_slave_config(dma->chan, &dma->chan_slave_config);
 +      if (ret < 0) {
 +              dev_err(audio_avp->dev, "dma slave config failed.err %d.", ret);
 +              return ret;
 +      }
 +      audio_engine->apb_channel_handle = dma->chan->chan_id;
 +      atomic_set(&dma->is_dma_allocated, 1);
 +
 +      return 0;
 +}
 +
 +static void tegra30_avp_audio_free_dma(void)
 +{
 +      struct tegra30_avp_audio *audio_avp = avp_audio_ctx;
 +      struct tegra30_avp_audio_dma *dma = &audio_avp->audio_dma;
 +
 +      dev_vdbg(audio_avp->dev, "%s: is_dma_allocated %d",
 +                      __func__, atomic_read(&dma->is_dma_allocated));
 +
 +      if (atomic_read(&dma->is_dma_allocated) == 1) {
 +              dma_release_channel(dma->chan);
 +              atomic_set(&dma->is_dma_allocated, 0);
 +      }
 +
 +      return;
 +}
 +
 +static int tegra30_avp_audio_start_dma(void)
 +{
 +      struct tegra30_avp_audio *audio_avp = avp_audio_ctx;
 +      struct tegra30_avp_audio_dma *dma = &audio_avp->audio_dma;
 +      struct audio_engine_data *audio_engine = audio_avp->audio_engine;
 +
 +      dev_vdbg(audio_avp->dev, "%s: active %d", __func__,
 +                      atomic_read(&dma->active_count));
 +
 +      if (atomic_inc_return(&dma->active_count) > 1)
 +              return 0;
 +
 +      dma->chan_desc = dmaengine_prep_dma_cyclic(dma->chan,
 +                              (dma_addr_t)audio_engine->device_buffer_avp,
 +                              DEVICE_BUFFER_SIZE,
 +                              DEVICE_BUFFER_SIZE,
 +                              dma->chan_slave_config.direction,
 +                              DMA_CTRL_ACK);
 +      if (!dma->chan_desc) {
 +              dev_err(audio_avp->dev, "Failed to prep cyclic dma");
 +              return -ENODEV;
 +      }
 +      dma->chan_cookie = dmaengine_submit(dma->chan_desc);
 +      dma_async_issue_pending(dma->chan);
 +      return 0;
 +}
 +
 +static int tegra30_avp_audio_stop_dma(void)
 +{
 +      struct tegra30_avp_audio *audio_avp = avp_audio_ctx;
 +      struct tegra30_avp_audio_dma *dma = &audio_avp->audio_dma;
 +
 +      dev_vdbg(audio_avp->dev, "%s: active %d.", __func__,
 +                      atomic_read(&dma->active_count));
 +
 +      if (atomic_dec_and_test(&dma->active_count))
 +              dmaengine_terminate_all(dma->chan);
 +
 +      return 0;
 +}
 +
 +static int tegra30_avp_audio_execute(void)
 +{
 +      struct tegra30_avp_audio *audio_avp = avp_audio_ctx;
 +      uint32_t cmd_idx = audio_avp->cmd_buf_idx;
 +      int ret = 0;
 +
 +      dev_vdbg(audio_avp->dev, "%s", __func__);
 +
 +      /* Set command */
 +      audio_avp->cmd_buf[cmd_idx++] =
 +              NVE26E_CH_OPCODE_INCR(NVE276_EXECUTE, 1);
 +      audio_avp->cmd_buf[cmd_idx++] = 0; /* NVE276_EXECUTE_APPID */
 +
 +      ret = nvavp_pushbuffer_submit_audio(audio_avp->nvavp_client,
 +              audio_avp->cmd_buf_mem.phys_addr,
 +              cmd_idx);
 +      if (ret < 0) {
 +              dev_err(audio_avp->dev, "nvavp_pushbuffer_submit_audio failed");
 +              return -EINVAL;
 +      }
 +      return 0;
 +}
 +
 +static int tegra30_avp_audio_set_state(enum KSSTATE new_state)
 +{
 +      struct tegra30_avp_audio *audio_avp = avp_audio_ctx;
 +      struct audio_engine_data *audio_engine = audio_avp->audio_engine;
 +      enum KSSTATE old_state;
 +
 +      old_state = audio_engine->device_state_target;
 +
 +      dev_vdbg(audio_avp->dev, "%s : state %d -> %d",
 +              __func__, old_state, new_state);
 +
 +      if (old_state == new_state)
 +              return 0;
 +
 +      audio_engine->device_state_target = new_state;
 +      /* TODO : Need a way to wait till AVP device state changes */
 +      if (new_state == KSSTATE_RUN)
 +              tegra30_avp_audio_execute();
 +
 +      return 0;
 +}
 +
 +
 +/* Call this function with stream lock held */
 +static int tegra30_avp_stream_set_state(int id, enum KSSTATE new_state)
 +{
 +      struct tegra30_avp_audio *audio_avp = avp_audio_ctx;
 +      struct tegra30_avp_stream *avp_stream = &audio_avp->avp_stream[id];
 +      struct stream_data *stream = avp_stream->stream;
 +      enum KSSTATE old_state = stream->stream_state_target;
 +      int ret = 0;
 +
 +      dev_vdbg(audio_avp->dev, "%s : id %d state %d -> %d", __func__, id,
 +               old_state, new_state);
 +
 +      if (old_state == new_state)
 +              return 0;
 +
 +      if (old_state == KSSTATE_STOP) {
 +              switch (stream->stream_format) {
 +              case FORMAT_MP3:
 +                      nvavp_enable_audio_clocks(audio_avp->nvavp_client,
 +                              NVAVP_MODULE_ID_VCP);
 +                      break;
 +              case FORMAT_AAC:
 +                      nvavp_enable_audio_clocks(audio_avp->nvavp_client,
 +                              NVAVP_MODULE_ID_VCP);
 +                      break;
 +              default:
 +                      break;
 +              }
 +      }
 +
 +      stream->stream_state_target = new_state;
 +      /* TODO : Need a way to wait till AVP stream state changes */
 +
 +      if (new_state == KSSTATE_STOP) {
 +              switch (stream->stream_format) {
 +              case FORMAT_MP3:
 +                      nvavp_disable_audio_clocks(audio_avp->nvavp_client,
 +                              NVAVP_MODULE_ID_VCP);
 +                      break;
 +              case FORMAT_AAC:
 +                      nvavp_disable_audio_clocks(audio_avp->nvavp_client,
 +                              NVAVP_MODULE_ID_VCP);
 +                      break;
 +              default:
 +                      break;
 +              }
 +      }
 +
 +      if (new_state == KSSTATE_STOP) {
 +              stream->source_buffer_write_position = 0;
 +              stream->source_buffer_write_count = 0;
 +              avp_stream->last_notification_offset = 0;
 +              avp_stream->notification_received = 0;
 +              avp_stream->source_buffer_offset = 0;
 +      }
 +
 +      if (new_state == KSSTATE_RUN)
 +              tegra30_avp_audio_start_dma();
 +      else if (old_state == KSSTATE_RUN)
 +              tegra30_avp_audio_stop_dma();
 +
 +      return ret;
 +}
 +
 +/* TODO : review ISR to make it more optimized if possible */
 +static void tegra30_avp_stream_notify(void)
 +{
 +      struct tegra30_avp_audio *audio_avp = avp_audio_ctx;
 +      struct tegra30_avp_stream *avp_stream;
 +      struct stream_data *stream;
 +      int i;
 +
 +      /* dev_vdbg(audio_avp->dev, "tegra30_avp_stream_notify"); */
 +
 +      for (i = 0; i < max_stream_id; i++) {
 +              avp_stream = &audio_avp->avp_stream[i];
 +              stream = avp_stream->stream;
 +
 +              if (!stream->stream_allocated)
 +                      continue;
 +
 +              if (avp_stream->is_drain_called &&
 +                 (stream->source_buffer_read_position ==
 +                      stream->source_buffer_write_position) &&
 +                 (avp_stream->notification_received >=
 +                      stream->stream_notification_request)) {
 +                      /* End of stream occured and noitfy same with value 1 */
 +                      avp_stream->notify_cb(avp_stream->notify_args, 1);
 +                      tegra30_avp_stream_set_state(i, KSSTATE_STOP);
 +              } else if (stream->stream_notification_request >
 +                      avp_stream->notification_received) {
 +                      avp_stream->notification_received++;
 +
 +                      avp_stream->notify_cb(avp_stream->notify_args, 0);
 +              }
 +      }
 +}
 +
 +/******************************************************************************
 + * EXPORTED FUNCTIONS
 + *****************************************************************************/
 +/* Device APIs */
 +static int tegra30_avp_set_hw_rate(int rate)
 +{
 +      struct tegra30_avp_audio *audio_avp = avp_audio_ctx;
 +      struct audio_engine_data *audio_engine = audio_avp->audio_engine;
 +
 +      dev_vdbg(audio_avp->dev, "%s rate %d", __func__, rate);
 +
 +      if (!audio_engine) {
 +              dev_err(audio_avp->dev, "AVP platform not initialized.");
 +              return -ENODEV;
 +      }
 +
 +      audio_engine->device_format.rate = rate;
 +      return 0;
 +}
 +
 +static int tegra30_avp_alloc_shared_mem(struct tegra_offload_mem *mem,
 +                                      int bytes)
 +{
 +      struct tegra30_avp_audio *audio_avp = avp_audio_ctx;
 +      int ret = 0;
 +
 +      ret = tegra30_avp_mem_alloc(mem, bytes);
 +      if (ret < 0) {
 +              dev_err(audio_avp->dev, "%s: Failed. ret %d", __func__, ret);
 +              return ret;
 +      }
 +      return 0;
 +}
 +
 +static void tegra30_avp_free_shared_mem(struct tegra_offload_mem *mem)
 +{
 +      tegra30_avp_mem_free(mem);
 +}
 +
 +/* Loopback APIs */
 +static int tegra30_avp_loopback_set_params(int id,
 +              struct tegra_offload_pcm_params *params)
 +{
 +      struct tegra30_avp_audio *audio_avp = avp_audio_ctx;
 +      struct tegra30_avp_stream *avp_stream = &audio_avp->avp_stream[id];
 +      struct stream_data *stream = avp_stream->stream;
 +      int ret = 0;
 +
 +      dev_vdbg(audio_avp->dev, "%s:entry\n", __func__);
 +
 +      /* TODO : check validity of parameters */
 +      if (!stream) {
 +              dev_err(audio_avp->dev, "AVP platform not initialized.");
 +              return -ENODEV;
 +      }
 +
 +
 +      stream->stream_notification_interval = params->period_size;
 +      stream->stream_notification_enable = 1;
 +      stream->stream_params.rate = params->rate;
 +      stream->stream_params.channels = params->channels;
 +      stream->stream_params.bits_per_sample = params->bits_per_sample;
 +
 +
 +      avp_stream->period_size = params->period_size;
 +      avp_stream->notify_cb = params->period_elapsed_cb;
 +      avp_stream->notify_args = params->period_elapsed_args;
 +
 +      stream->source_buffer_system =
 +              (uintptr_t)(params->source_buf.virt_addr);
 +      stream->source_buffer_avp = params->source_buf.phys_addr;
 +      stream->source_buffer_size = params->buffer_size;
 +      return ret;
 +}
 +
 +static int tegra30_avp_loopback_set_state(int id, int state)
 +{
 +      struct tegra30_avp_audio *audio_avp = avp_audio_ctx;
 +      struct tegra30_avp_stream *avp_stream = &audio_avp->avp_stream[id];
 +      struct stream_data *stream = avp_stream->stream;
 +
 +      dev_vdbg(audio_avp->dev, "%s : id %d state %d", __func__, id, state);
 +
 +      if (!stream) {
 +              dev_err(audio_avp->dev, "AVP platform not initialized.");
 +              return -ENODEV;
 +      }
 +
 +      switch (state) {
 +      case SNDRV_PCM_TRIGGER_START:
 +              stream->stream_state_target = KSSTATE_RUN;
 +              return 0;
 +      case SNDRV_PCM_TRIGGER_STOP:
 +              stream->stream_state_target = KSSTATE_STOP;
 +              stream->source_buffer_write_position = 0;
 +              stream->source_buffer_write_count = 0;
 +              avp_stream->last_notification_offset = 0;
 +              avp_stream->notification_received = 0;
 +              avp_stream->source_buffer_offset = 0;
 +              return 0;
 +      default:
 +              dev_err(audio_avp->dev, "Unsupported state.");
 +              return -EINVAL;
 +      }
 +}
 +
 +static size_t tegra30_avp_loopback_get_position(int id)
 +{
 +      struct tegra30_avp_audio *audio_avp = avp_audio_ctx;
 +      struct tegra30_avp_stream *avp_stream = &audio_avp->avp_stream[id];
 +      struct stream_data *stream = avp_stream->stream;
 +      size_t pos = 0;
 +
 +      pos = (size_t)stream->source_buffer_read_position;
 +
 +      dev_vdbg(audio_avp->dev, "%s id %d pos %d", __func__, id, (u32)pos);
 +
 +      return pos;
 +}
 +
 +static void tegra30_avp_loopback_data_ready(int id, int bytes)
 +{
 +      struct tegra30_avp_audio *audio_avp = avp_audio_ctx;
 +      struct tegra30_avp_stream *avp_stream = &audio_avp->avp_stream[id];
 +      struct stream_data *stream = avp_stream->stream;
 +
 +      dev_vdbg(audio_avp->dev, "%s :id %d size %d", __func__, id, bytes);
 +
 +      stream->source_buffer_write_position += bytes;
 +      stream->source_buffer_write_position %= stream->source_buffer_size;
 +
 +      avp_stream->source_buffer_offset += bytes;
 +      while (avp_stream->source_buffer_offset >=
 +              stream->stream_notification_interval) {
 +              stream->source_buffer_write_count++;
 +              avp_stream->source_buffer_offset -=
 +              stream->stream_notification_interval;
 +      }
 +      return;
 +}
 +
 +/* PCM APIs */
 +static int tegra30_avp_pcm_open(int *id, char *stream)
 +{
 +      struct tegra30_avp_audio *audio_avp = avp_audio_ctx;
 +      struct audio_engine_data *audio_engine = audio_avp->audio_engine;
 +      struct tegra30_avp_stream *avp_stream;
 +      int ret = 0;
 +
 +      dev_vdbg(audio_avp->dev, "%s", __func__);
 +
 +      if (!audio_avp->nvavp_client) {
 +              ret = tegra_nvavp_audio_client_open(&audio_avp->nvavp_client);
 +              if (ret < 0) {
 +                      dev_err(audio_avp->dev, "Failed to open nvavp.");
 +                      return ret;
 +              }
 +      }
 +      if (!audio_engine) {
 +              ret = tegra30_avp_load_ucode();
 +              if (ret < 0) {
 +                      dev_err(audio_avp->dev, "Failed to load ucode.");
 +                      return ret;
 +              }
 +              tegra30_avp_audio_engine_init();
 +              nvavp_register_audio_cb(audio_avp->nvavp_client,
 +                      tegra30_avp_stream_notify);
 +              audio_engine = audio_avp->audio_engine;
 +      }
 +
 +      if (strcmp(stream, "pcm") == 0) {
 +              avp_stream = &audio_avp->avp_stream[1];
 +              if (!audio_engine->stream[pcm_stream_id].stream_allocated) {
 +                      *id = pcm_stream_id;
 +                      audio_engine->stream[*id].stream_allocated = 1;
 +                      atomic_inc(&audio_avp->stream_active_count);
 +              } else if (
 +              !audio_engine->stream[pcm2_stream_id].stream_allocated) {
 +                      *id = pcm2_stream_id;
 +                      audio_engine->stream[*id].stream_allocated = 1;
 +                      atomic_inc(&audio_avp->stream_active_count);
 +              } else {
 +                      dev_err(audio_avp->dev, "All AVP PCM streams are busy");
 +                      return -EBUSY;
 +              }
 +      } else if (strcmp(stream, "loopback") == 0) {
 +              avp_stream = &audio_avp->avp_stream[0];
 +              if
 +              (!audio_engine->stream[loopback_stream_id].stream_allocated) {
 +                      dev_vdbg(audio_avp->dev,
 +                      "Assigning loopback id:%d\n", loopback_stream_id);
 +                      audio_engine->stream[*id].stream_allocated = 1;
 +                      *id = loopback_stream_id;
 +              } else {
 +                      dev_err(audio_avp->dev, "AVP loopback streams is busy");
 +                      return -EBUSY;
 +              }
 +      }
 +
 +      tegra30_avp_audio_set_state(KSSTATE_RUN);
 +      return 0;
 +}
 +
 +static int tegra30_avp_pcm_set_params(int id,
 +              struct tegra_offload_pcm_params *params)
 +{
 +      struct tegra30_avp_audio *audio_avp = avp_audio_ctx;
 +      struct tegra30_avp_stream *avp_stream = &audio_avp->avp_stream[id];
 +      struct stream_data *stream = avp_stream->stream;
 +      int ret = 0;
 +
 +      dev_vdbg(audio_avp->dev,
 +              "%s id %d rate %d chan %d bps %d period size %d buf size %d",
 +               __func__, id, params->rate, params->channels,
 +               params->bits_per_sample, params->period_size,
 +               params->buffer_size);
 +
 +      /* TODO : check validity of parameters */
 +      if (!stream) {
 +              dev_err(audio_avp->dev, "AVP platform not initialized.");
 +              return -ENODEV;
 +      }
 +
 +      stream->stream_notification_interval = params->period_size;
 +      stream->stream_notification_enable = 1;
 +      stream->stream_params.rate = params->rate;
 +      stream->stream_params.channels = params->channels;
 +      stream->stream_params.bits_per_sample = params->bits_per_sample;
 +      avp_stream->period_size = params->period_size;
 +
 +      avp_stream->notify_cb = params->period_elapsed_cb;
 +      avp_stream->notify_args = params->period_elapsed_args;
 +
 +      stream->source_buffer_system =
 +                      (uintptr_t) (params->source_buf.virt_addr);
 +      stream->source_buffer_avp = params->source_buf.phys_addr;
 +      stream->source_buffer_size = params->buffer_size;
 +
 +      /* Set DMA params */
 +      ret = tegra30_avp_audio_alloc_dma(&params->dma_params);
 +      if (ret < 0) {
 +              dev_err(audio_avp->dev, "Failed to allocate DMA. ret %d", ret);
 +              return ret;
 +      }
 +      return 0;
 +}
 +
 +static int tegra30_avp_pcm_set_state(int id, int state)
 +{
 +      struct tegra30_avp_audio *audio_avp = avp_audio_ctx;
 +      struct tegra30_avp_stream *avp_stream = &audio_avp->avp_stream[id];
 +      struct stream_data *stream = avp_stream->stream;
 +
 +      dev_vdbg(audio_avp->dev, "%s : id %d state %d", __func__, id, state);
 +
 +      if (!stream) {
 +              dev_err(audio_avp->dev, "AVP platform not initialized.");
 +              return -ENODEV;
 +      }
 +
 +      switch (state) {
 +      case SNDRV_PCM_TRIGGER_START:
 +      case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
 +      case SNDRV_PCM_TRIGGER_RESUME:
 +              tegra30_avp_stream_set_state(id, KSSTATE_RUN);
 +              return 0;
 +      case SNDRV_PCM_TRIGGER_STOP:
 +      case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
 +      case SNDRV_PCM_TRIGGER_SUSPEND:
 +              tegra30_avp_stream_set_state(id, KSSTATE_STOP);
 +              return 0;
 +      default:
 +              dev_err(audio_avp->dev, "Unsupported state.");
 +              return -EINVAL;
 +      }
 +}
 +
 +static void tegra30_avp_pcm_data_ready(int id, int bytes)
 +{
 +      struct tegra30_avp_audio *audio_avp = avp_audio_ctx;
 +      struct tegra30_avp_stream *avp_stream = &audio_avp->avp_stream[id];
 +      struct stream_data *stream = avp_stream->stream;
 +
 +      dev_vdbg(audio_avp->dev, "%s :id %d size %d", __func__, id, bytes);
 +
 +      stream->source_buffer_write_position += bytes;
 +      stream->source_buffer_write_position %= stream->source_buffer_size;
 +
 +      avp_stream->source_buffer_offset += bytes;
 +      while (avp_stream->source_buffer_offset >=
 +              stream->stream_notification_interval) {
 +              stream->source_buffer_write_count++;
 +              avp_stream->source_buffer_offset -=
 +                      stream->stream_notification_interval;
 +      }
 +      return;
 +}
 +
 +static size_t tegra30_avp_pcm_get_position(int id)
 +{
 +      struct tegra30_avp_audio *audio_avp = avp_audio_ctx;
 +      struct tegra30_avp_stream *avp_stream = &audio_avp->avp_stream[id];
 +      struct stream_data *stream = avp_stream->stream;
 +      size_t pos = 0;
 +
 +      pos = (size_t)stream->source_buffer_read_position;
 +
 +      dev_vdbg(audio_avp->dev, "%s id %d pos %d", __func__, id, (u32)pos);
 +
 +      return pos;
 +}
 +
 +/* Compress APIs */
 +static int tegra30_avp_compr_open(int *id)
 +{
 +      struct tegra30_avp_audio *audio_avp = avp_audio_ctx;
 +      struct audio_engine_data *audio_engine = audio_avp->audio_engine;
 +      int ret = 0;
 +
 +      dev_vdbg(audio_avp->dev, "%s", __func__);
 +
 +      if (!audio_avp->nvavp_client) {
 +              ret = tegra_nvavp_audio_client_open(&audio_avp->nvavp_client);
 +              if (ret < 0) {
 +                      dev_err(audio_avp->dev, "Failed to open nvavp.");
 +                      return ret;
 +              }
 +      }
 +      if (!audio_engine) {
 +              ret = tegra30_avp_load_ucode();
 +              if (ret < 0) {
 +                      dev_err(audio_avp->dev, "Failed to load ucode.");
 +                      return ret;
 +              }
 +              tegra30_avp_audio_engine_init();
 +              nvavp_register_audio_cb(audio_avp->nvavp_client,
 +                      tegra30_avp_stream_notify);
 +              audio_engine = audio_avp->audio_engine;
 +      }
 +
 +      if (!audio_engine->stream[decode_stream_id].stream_allocated)
 +              *id = decode_stream_id;
 +      else if (!audio_engine->stream[decode2_stream_id].stream_allocated)
 +              *id = decode2_stream_id;
 +      else {
 +              dev_err(audio_avp->dev, "All AVP COMPR streams are busy");
 +              return -EBUSY;
 +      }
 +      audio_avp->avp_stream[*id].is_drain_called = 0;
 +      audio_engine->stream[*id].stream_allocated = 1;
 +
 +      atomic_inc(&audio_avp->stream_active_count);
 +      tegra30_avp_audio_set_state(KSSTATE_RUN);
 +
 +      return 0;
 +}
 +
 +static int tegra30_avp_compr_set_params(int id,
 +              struct tegra_offload_compr_params *params)
 +{
 +      struct tegra30_avp_audio *audio_avp = avp_audio_ctx;
 +      struct tegra30_avp_stream *avp_stream = &audio_avp->avp_stream[id];
 +      struct stream_data *stream = avp_stream->stream;
 +      int ret = 0;
 +
 +      dev_vdbg(audio_avp->dev,
 +              "%s: id %d codec %d rate %d ch %d bps %d buf %d x %d",
 +               __func__, id, params->codec_type, params->rate,
 +               params->channels, params->bits_per_sample,
 +               params->fragment_size, params->fragments);
 +
 +      /* TODO : check validity of parameters */
 +      if (!stream) {
 +              dev_err(audio_avp->dev, "AVP platform not initialized.");
 +              return -ENODEV;
 +      }
 +
 +      if (params->codec_type == SND_AUDIOCODEC_MP3) {
 +              stream->stream_format = FORMAT_MP3;
 +      } else if (params->codec_type == SND_AUDIOCODEC_AAC) {
 +              stream->stream_format = FORMAT_AAC;
 +
 +              /* AAC-LC is only supported profile*/
 +              stream->u.aac.audio_profile = AAC_PROFILE_LC;
 +              stream->u.aac.sampling_freq = params->rate;
 +              stream->u.aac.payload_type  = AAC_PAYLOAD_RAW;
 +              switch (params->rate) {
 +              case 8000:
 +                      stream->u.aac.sampling_freq_index = 0xb;
 +                      break;
 +              case 11025:
 +                      stream->u.aac.sampling_freq_index = 0xa;
 +                      break;
 +              case 12000:
 +                      stream->u.aac.sampling_freq_index = 0x9;
 +                      break;
 +              case 16000:
 +                      stream->u.aac.sampling_freq_index = 0x8;
 +                      break;
 +              case 22050:
 +                      stream->u.aac.sampling_freq_index = 0x7;
 +                      break;
 +              case 24000:
 +                      stream->u.aac.sampling_freq_index = 0x6;
 +                      break;
 +              case 32000:
 +                      stream->u.aac.sampling_freq_index = 0x5;
 +                      break;
 +              case 44100:
 +                      stream->u.aac.sampling_freq_index = 0x4;
 +                      break;
 +              case 48000:
 +                      stream->u.aac.sampling_freq_index = 0x3;
 +                      break;
 +              case 64000:
 +                      stream->u.aac.sampling_freq_index = 0x2;
 +                      break;
 +              case 88200:
 +                      stream->u.aac.sampling_freq_index = 0x1;
 +                      break;
 +              case 96000:
 +                      stream->u.aac.sampling_freq_index = 0x0;
 +                      break;
 +              default:
 +                      dev_err(audio_avp->dev, "Unsupported rate");
 +                      return -EINVAL;
 +              }
 +              /* Only Stereo data is supported */
 +              stream->u.aac.channel_configuration = params->channels;
 +      } else {
 +              dev_err(audio_avp->dev, "Invalid stream/codec type.");
 +              return -EINVAL;
 +      }
 +
 +      stream->stream_params.rate = params->rate;
 +      stream->stream_params.channels = params->channels;
 +      stream->stream_params.bits_per_sample = params->bits_per_sample;
 +      avp_stream->period_size = params->fragment_size;
 +
 +      avp_stream->notify_cb = params->fragments_elapsed_cb;
 +      avp_stream->notify_args = params->fragments_elapsed_args;
 +
 +      stream->source_buffer_size = (params->fragments *
 +                      params->fragment_size);
 +      ret = tegra30_avp_mem_alloc(&avp_stream->source_buf,
 +                            stream->source_buffer_size);
 +      if (ret < 0) {
 +              dev_err(audio_avp->dev, "Failed to allocate source buf memory");
 +              return ret;
 +      }
 +
 +      stream->source_buffer_system =
 +                      (uintptr_t) avp_stream->source_buf.virt_addr;
 +      stream->source_buffer_avp = avp_stream->source_buf.phys_addr;
 +
 +      if (stream->source_buffer_size > AVP_COMPR_THRESHOLD) {
 +              stream->stream_notification_interval =
 +                      stream->source_buffer_size - AVP_COMPR_THRESHOLD;
 +      } else {
 +              stream->stream_notification_interval = avp_stream->period_size;
 +      }
 +      stream->stream_notification_enable = 1;
 +
 +      /* Set DMA params */
 +      ret = tegra30_avp_audio_alloc_dma(&params->dma_params);
 +      if (ret < 0) {
 +              dev_err(audio_avp->dev, "Failed to allocate DMA. ret %d", ret);
 +              return ret;
 +      }
 +
 +      if ((params->codec_type == SND_AUDIOCODEC_MP3) ||
 +          (params->codec_type == SND_AUDIOCODEC_AAC)) {
 +              dev_info(audio_avp->dev, "\n*** STARTING %s Offload PLAYBACK ***\n",
 +                 (params->codec_type == SND_AUDIOCODEC_MP3) ? "MP3" : "AAC");
 +      }
 +      return 0;
 +}
 +
 +static int tegra30_avp_compr_set_state(int id, int state)
 +{
 +      struct tegra30_avp_audio *audio_avp = avp_audio_ctx;
 +      struct tegra30_avp_stream *avp_stream = &audio_avp->avp_stream[id];
 +
 +      dev_vdbg(audio_avp->dev, "%s : id %d state %d",
 +               __func__, id, state);
 +
 +      switch (state) {
 +      case SNDRV_PCM_TRIGGER_START:
 +      case SNDRV_PCM_TRIGGER_RESUME:
 +              tegra30_avp_stream_set_state(id, KSSTATE_RUN);
 +              return 0;
 +      case SNDRV_PCM_TRIGGER_STOP:
 +      case SNDRV_PCM_TRIGGER_SUSPEND:
 +              tegra30_avp_stream_set_state(id, KSSTATE_STOP);
 +              return 0;
 +      case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
 +              tegra30_avp_stream_set_state(id, KSSTATE_PAUSE);
 +              return 0;
 +      case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
 +              tegra30_avp_stream_set_state(id, KSSTATE_RUN);
 +              return 0;
 +      case SND_COMPR_TRIGGER_DRAIN:
 +      case SND_COMPR_TRIGGER_PARTIAL_DRAIN:
 +              avp_stream->is_drain_called = 1;
 +              return 0;
 +      default:
 +              dev_err(audio_avp->dev, "Unsupported state.");
 +              return -EINVAL;
 +      }
 +}
 +
 +static void tegra30_avp_compr_data_ready(int id, int bytes)
 +{
 +      struct tegra30_avp_audio *audio_avp = avp_audio_ctx;
 +      struct tegra30_avp_stream *avp_stream = &audio_avp->avp_stream[id];
 +      struct stream_data *stream = avp_stream->stream;
 +
 +      dev_vdbg(audio_avp->dev, "%s : id %d size %d", __func__, id, bytes);
 +
 +      stream->source_buffer_write_position += bytes;
 +      stream->source_buffer_write_position %= stream->source_buffer_size;
 +
 +      avp_stream->source_buffer_offset += bytes;
 +      while (avp_stream->source_buffer_offset >=
 +              stream->stream_notification_interval) {
 +              stream->source_buffer_write_count++;
 +              avp_stream->source_buffer_offset -=
 +                      stream->stream_notification_interval;
 +      }
 +      return;
 +}
 +
 +static int tegra30_avp_compr_write(int id, char __user *buf, int bytes)
 +{
 +      struct tegra30_avp_audio *audio_avp = avp_audio_ctx;
 +      struct tegra30_avp_stream *avp_stream = &audio_avp->avp_stream[id];
 +      struct stream_data *stream = avp_stream->stream;
 +      void *dst = (char *)(uintptr_t)stream->source_buffer_system +
 +              stream->source_buffer_write_position;
 +      int avail = 0;
 +      int write = 0;
 +      int ret = 0;
 +
 +      avail = stream->source_buffer_read_position -
 +              stream->source_buffer_write_position;
 +      if ((avail < 0) || (!avail &&
 +              (stream->source_buffer_write_count ==
 +              stream->stream_notification_request)))
 +              avail += stream->source_buffer_size;
 +
 +      dev_vdbg(audio_avp->dev, "%s : id %d size %d", __func__, id, bytes);
 +
 +      /* TODO: check enough free space is available before writing */
 +      bytes = (bytes > avail) ? avail : bytes;
 +      if (!bytes) {
 +              dev_dbg(audio_avp->dev, "No free space in ring buffer.");
 +              DUMP_AVP_STATUS(avp_stream);
 +              return bytes;
 +      }
 +
 +      write = stream->source_buffer_size -
 +              stream->source_buffer_write_position;
 +      if (write > bytes) {
 +              ret = copy_from_user(dst, buf, bytes);
 +              if (ret < 0) {
 +                      dev_err(audio_avp->dev, "Failed to copy user data.");
 +                      return -EFAULT;
 +              }
 +      } else {
 +              ret = copy_from_user(dst, buf, write);
 +              if (ret < 0) {
 +                      dev_err(audio_avp->dev, "Failed to copy user data.");
 +                      return -EFAULT;
 +              }
 +
 +              ret = copy_from_user((void *)(uintptr_t)stream->source_buffer_system,
 +                              buf + write, bytes - write);
 +              if (ret < 0) {
 +                      dev_err(audio_avp->dev, "Failed to copy user data.");
 +                      return -EFAULT;
 +              }
 +      }
 +
 +      stream->source_buffer_write_position += bytes;
 +      stream->source_buffer_write_position %= stream->source_buffer_size;
 +
 +      avp_stream->source_buffer_offset += bytes;
 +      while (avp_stream->source_buffer_offset >=
 +              stream->stream_notification_interval) {
 +              stream->source_buffer_write_count++;
 +              avp_stream->source_buffer_offset -=
 +                      stream->stream_notification_interval;
 +      }
 +      DUMP_AVP_STATUS(avp_stream);
 +      return bytes;
 +}
 +
 +static int tegra30_avp_compr_get_position(int id,
 +                      struct snd_compr_tstamp *tstamp)
 +{
 +      struct tegra30_avp_audio *audio_avp = avp_audio_ctx;
 +      struct tegra30_avp_stream *avp_stream = &audio_avp->avp_stream[id];
 +      struct stream_data *stream = avp_stream->stream;
 +
 +      tstamp->byte_offset = stream->source_buffer_write_position;
 +      tstamp->copied_total = stream->source_buffer_write_position +
 +              (stream->source_buffer_write_count *
 +               stream->stream_notification_interval);
 +      tstamp->pcm_frames = stream->source_buffer_presentation_position;
 +      tstamp->pcm_io_frames = stream->source_buffer_presentation_position;
 +      tstamp->sampling_rate = stream->stream_params.rate;
 +
 +      dev_vdbg(audio_avp->dev, "%s id %d off %d copied %d pcm %d pcm io %d",
 +               __func__, id, (int)tstamp->byte_offset,
 +               (int)tstamp->copied_total, (int)tstamp->pcm_frames,
 +               (int)tstamp->pcm_io_frames);
 +
 +      return 0;
 +}
 +
 +static int tegra30_avp_compr_get_caps(struct snd_compr_caps *caps)
 +{
 +      struct tegra30_avp_audio *audio_avp = avp_audio_ctx;
 +
 +      dev_vdbg(audio_avp->dev, "%s : dir %d", __func__, caps->direction);
 +
 +      if (caps->direction == SND_COMPRESS_PLAYBACK)
 +              memcpy(caps, &tegra30_avp_compr_caps[SND_COMPRESS_PLAYBACK],
 +                      sizeof(struct snd_compr_caps));
 +      else
 +              memcpy(caps, &tegra30_avp_compr_caps[SND_COMPRESS_CAPTURE],
 +                      sizeof(struct snd_compr_caps));
 +      return 0;
 +}
 +
 +static int tegra30_avp_compr_get_codec_caps(struct snd_compr_codec_caps *codec)
 +{
 +      struct tegra30_avp_audio *audio_avp = avp_audio_ctx;
 +
 +      dev_vdbg(audio_avp->dev, "%s : codec %d", __func__, codec->codec);
 +
 +      switch (codec->codec) {
 +      case SND_AUDIOCODEC_MP3:
 +              memcpy(codec, &tegra30_avp_compr_codec_caps[avp_compr_mp3],
 +                      sizeof(struct snd_compr_codec_caps));
 +              return 0;
 +      case SND_AUDIOCODEC_AAC:
 +              memcpy(codec, &tegra30_avp_compr_codec_caps[avp_compr_aac],
 +                      sizeof(struct snd_compr_codec_caps));
 +              return 0;
 +      default:
 +              dev_err(audio_avp->dev, "Unsupported codec %d", codec->codec);
 +              return -EINVAL;
 +      }
 +}
 +
 +static int tegra30_avp_compr_set_volume(int id, int left, int right)
 +{
 +      struct tegra30_avp_audio *audio_avp = avp_audio_ctx;
 +      struct tegra30_avp_stream *avp_stream = &audio_avp->avp_stream[id];
 +      struct stream_data *stream = avp_stream->stream;
 +
 +      dev_vdbg(audio_avp->dev, "%s id %d left vol %d right vol %d",
 +               __func__, id, left, right);
 +
 +      if (!stream) {
 +              dev_err(audio_avp->dev, "AVP platform not initialized.");
 +              return -ENODEV;
 +      }
 +
 +      stream->stream_volume[0] = left;
 +      stream->stream_volume[1] = right;
 +
 +      return 0;
 +}
 +
 +/* Common APIs for pcm and compress */
 +static void tegra30_avp_stream_close(int id)
 +{
 +      struct tegra30_avp_audio *audio_avp = avp_audio_ctx;
 +      struct tegra30_avp_stream *avp_stream = &audio_avp->avp_stream[id];
 +      struct stream_data *stream = avp_stream->stream;
 +
 +      dev_vdbg(audio_avp->dev, "%s id %d", __func__, id);
 +
 +      if (!stream) {
 +              dev_err(audio_avp->dev, "AVP platform not initialized.");
 +              return;
 +      }
 +      tegra30_avp_mem_free(&avp_stream->source_buf);
 +      stream->stream_allocated = 0;
 +      tegra30_avp_stream_set_state(id, KSSTATE_STOP);
 +
 +      if (id == loopback_stream_id)
 +              return;
 +
 +      if (atomic_dec_and_test(&audio_avp->stream_active_count)) {
 +              tegra30_avp_audio_free_dma();
 +              tegra30_avp_audio_set_state(KSSTATE_STOP);
 +      }
 +}
 +
 +static struct tegra_offload_ops avp_audio_platform = {
 +      .device_ops = {
 +              .set_hw_rate = tegra30_avp_set_hw_rate,
 +              .alloc_shared_mem = tegra30_avp_alloc_shared_mem,
 +              .free_shared_mem = tegra30_avp_free_shared_mem,
 +      },
 +      .pcm_ops = {
 +              .stream_open = tegra30_avp_pcm_open,
 +              .stream_close = tegra30_avp_stream_close,
 +              .set_stream_params = tegra30_avp_pcm_set_params,
 +              .set_stream_state = tegra30_avp_pcm_set_state,
 +              .get_stream_position = tegra30_avp_pcm_get_position,
 +              .data_ready = tegra30_avp_pcm_data_ready,
 +      },
 +      .loopback_ops = {
 +              .stream_open = tegra30_avp_pcm_open,
 +              .stream_close = tegra30_avp_stream_close,
 +              .set_stream_params = tegra30_avp_loopback_set_params,
 +              .set_stream_state = tegra30_avp_loopback_set_state,
 +              .get_stream_position = tegra30_avp_loopback_get_position,
 +              .data_ready = tegra30_avp_loopback_data_ready,
 +      },
 +      .compr_ops = {
 +              .stream_open = tegra30_avp_compr_open,
 +              .stream_close = tegra30_avp_stream_close,
 +              .set_stream_params = tegra30_avp_compr_set_params,
 +              .set_stream_state = tegra30_avp_compr_set_state,
 +              .get_stream_position = tegra30_avp_compr_get_position,
 +              .data_ready = tegra30_avp_compr_data_ready,
 +              .write = tegra30_avp_compr_write,
 +              .get_caps = tegra30_avp_compr_get_caps,
 +              .get_codec_caps = tegra30_avp_compr_get_codec_caps,
 +              .set_stream_volume = tegra30_avp_compr_set_volume,
 +      },
 +};
 +
 +static u64 tegra_dma_mask = DMA_BIT_MASK(32);
 +static int tegra30_avp_audio_probe(struct platform_device *pdev)
 +{
 +      struct tegra30_avp_audio *audio_avp;
 +
 +      pr_debug("tegra30_avp_audio_platform_probe platform probe started\n");
 +
 +      audio_avp = devm_kzalloc(&pdev->dev, sizeof(*audio_avp), GFP_KERNEL);
 +      if (!audio_avp) {
 +              dev_err(&pdev->dev, "Can't allocate tegra30_avp_audio\n");
 +              return -ENOMEM;
 +      }
 +      dev_set_drvdata(&pdev->dev, audio_avp);
 +      audio_avp->dev = &pdev->dev;
 +
 +      /* set the ops */
 +      if (tegra_register_offload_ops(&avp_audio_platform)) {
 +              dev_err(&pdev->dev, "Failed to register avp audio device.");
 +              return -EPROBE_DEFER;
 +      }
 +
 +      spin_lock_init(&audio_avp->lock);
 +      pdev->dev.dma_mask = &tegra_dma_mask;
 +      pdev->dev.coherent_dma_mask = tegra_dma_mask;
 +      avp_audio_ctx = audio_avp;
 +      pr_info("tegra30_avp_audio_platform_probe successful.");
 +      return 0;
 +}
 +
 +static int tegra30_avp_audio_remove(struct platform_device *pdev)
 +{
 +      struct tegra30_avp_audio *audio_avp = dev_get_drvdata(&pdev->dev);
 +
 +      dev_vdbg(&pdev->dev, "%s", __func__);
 +
 +      tegra_deregister_offload_ops();
 +      if (audio_avp->nvavp_client)
 +              tegra_nvavp_audio_client_release(audio_avp->nvavp_client);
 +      tegra30_avp_mem_free(&audio_avp->cmd_buf_mem);
 +      tegra30_avp_mem_free(&audio_avp->param_mem);
 +      tegra30_avp_mem_free(&audio_avp->ucode_mem);
 +
 +      return 0;
 +}
 +
 +static const struct of_device_id tegra30_avp_audio_of_match[] = {
 +      { .compatible = "nvidia,tegra30-avp-audio", },
 +      {},
 +};
 +
 +static struct platform_driver tegra30_avp_audio_driver = {
 +      .driver = {
 +              .name = DRV_NAME,
 +              .owner = THIS_MODULE,
 +              .of_match_table = tegra30_avp_audio_of_match,
 +      },
 +      .probe = tegra30_avp_audio_probe,
 +      .remove = tegra30_avp_audio_remove,
 +};
 +module_platform_driver(tegra30_avp_audio_driver);
 +
 +MODULE_AUTHOR("Sumit Bhattacharya <sumitb@nvidia.com>");
 +MODULE_DESCRIPTION("Tegra30 AVP Audio driver");
 +MODULE_LICENSE("GPL");
 +MODULE_ALIAS("platform:" DRV_NAME);
 +MODULE_DEVICE_TABLE(of, tegra30_avp_audio_of_match);