]> rtime.felk.cvut.cz Git - sojka/nv-tegra/linux-3.10.git/commitdiff
media: video: nvavp: Add audio channel support
authorVandana Salve <vsalve@nvidia.com>
Thu, 3 May 2012 10:37:48 +0000 (16:07 +0530)
committerDan Willemsen <dwillemsen@nvidia.com>
Sat, 14 Sep 2013 08:04:21 +0000 (01:04 -0700)
Added support for audio channel.
Push buffers can be submitted on audio channel.

bug 964514

Change-Id: Ib13b6ed41678e9dcc170fddc64ab4646f960838b
Signed-off-by: Vandana Salve <vsalve@nvidia.com>
Reviewed-on: http://git-master/r/97534
Reviewed-by: Automatic_Commit_Validation_User
Reviewed-by: Bharat Nihalani <bnihalani@nvidia.com>
Tested-by: Bharat Nihalani <bnihalani@nvidia.com>
GVS: Gerrit_Virtual_Submit
Reviewed-by: Gajanan Bhat <gbhat@nvidia.com>
Rebase-Id: Rd284ca52352756ed242f1c5173797dc03d6e8260

drivers/media/video/tegra/nvavp/Kconfig
drivers/media/video/tegra/nvavp/nvavp_dev.c

index 2d3af3f79fb30f7cf2d2753ac7ea8442ecd73f12..294253a0de494c0390c1dd2836a31d1e396f52a9 100644 (file)
@@ -8,3 +8,14 @@ config TEGRA_NVAVP
          /dev/tegra_avpchannel.
 
          If unsure, say N
+
+config TEGRA_NVAVP_AUDIO
+       bool "Enable Audio Channel support for Tegra NVAVP driver"
+       depends on TEGRA_NVAVP
+       default n
+       help
+         Enables support for the push-buffer mechanism based driver for the Tegra
+         audio multimedia framework. Exports the Tegra nvavp interface on device node
+         /dev/tegra_audio_avpchannel.
+
+         If unsure, say N
index 03949d68df999f485c66424e8d360c16fcbdbae2..9e4f5c669d435eb951d5d1f3cf2de590a50e3c65 100644 (file)
 /* AVP behavior params */
 #define NVAVP_OS_IDLE_TIMEOUT          100 /* milli-seconds */
 
+#if defined(CONFIG_TEGRA_NVAVP_AUDIO)
+/* Two control channels: Audio and Video channels */
+#define NVAVP_NUM_CHANNELS             2
+
+#define NVAVP_AUDIO_CHANNEL            1
+
+#define IS_AUDIO_CHANNEL_ID(channel_id)        (channel_id == NVAVP_AUDIO_CHANNEL ? 1: 0)
+#else
+#define NVAVP_NUM_CHANNELS             1
+#endif
+
+/* Channel ID 0 represents the Video channel control area */
+#define NVAVP_VIDEO_CHANNEL            0
+/* Channel ID 1 represents the Audio channel control area */
+
+#define IS_VIDEO_CHANNEL_ID(channel_id)        (channel_id == NVAVP_VIDEO_CHANNEL ? 1: 0)
+
+
+struct nvavp_channel {
+       struct mutex                    pushbuffer_lock;
+       struct nvmap_handle_ref         *pushbuf_handle;
+       unsigned long                   pushbuf_phys;
+       u8                              *pushbuf_data;
+       u32                             pushbuf_index;
+       u32                             pushbuf_fence;
+       struct nv_e276_control          *os_control;
+};
+
 struct nvavp_info {
        u32                             clk_enabled;
        struct clk                      *bsev_clk;
@@ -89,8 +117,10 @@ struct nvavp_info {
 
        struct mutex                    open_lock;
        int                             refcount;
-       int                             initialized;
-
+       int                             video_initialized;
+#if defined(CONFIG_TEGRA_NVAVP_AUDIO)
+       int                             audio_initialized;
+#endif
        struct work_struct              clock_disable_work;
 
        /* os information */
@@ -102,22 +132,18 @@ struct nvavp_info {
        /* client for driver allocations, persistent */
        struct nvmap_client             *nvmap;
 
-       struct mutex                    pushbuffer_lock;
-       struct nvmap_handle_ref         *pushbuf_handle;
-       unsigned long                   pushbuf_phys;
-       u8                              *pushbuf_data;
-       u32                             pushbuf_index;
-       u32                             pushbuf_fence;
-
-       struct nv_e276_control          *os_control;
+       struct nvavp_channel            channel_info[NVAVP_NUM_CHANNELS];
 
        struct nvhost_syncpt            *nvhost_syncpt;
        u32                             syncpt_id;
        u32                             syncpt_value;
 
        struct nvhost_device            *nvhost_dev;
-       struct miscdevice               misc_dev;
-       atomic_t                                clock_stay_on_refcount;
+       struct miscdevice               video_misc_dev;
+#if defined(CONFIG_TEGRA_NVAVP_AUDIO)
+       struct miscdevice               audio_misc_dev;
+#endif
+       atomic_t                        clock_stay_on_refcount;
 };
 
 struct nvavp_clientctx {
@@ -128,8 +154,77 @@ struct nvavp_clientctx {
        int num_relocs;
        struct nvavp_info *nvavp;
        int clock_stay_on;
+       int channel_id;
 };
 
+#if defined(CONFIG_TEGRA_NVAVP_AUDIO)
+static int nvavp_get_audio_init_status(struct nvavp_info *nvavp)
+{
+       return nvavp->audio_initialized;
+}
+
+static void nvavp_set_audio_init_status(struct nvavp_info *nvavp, int status)
+{
+       nvavp->audio_initialized = status;
+}
+#endif
+
+static void nvavp_set_video_init_status(struct nvavp_info *nvavp, int status)
+{
+       nvavp->video_initialized = status;
+}
+
+static int nvavp_get_video_init_status(struct nvavp_info *nvavp)
+{
+       return nvavp->video_initialized;
+}
+
+static struct nvavp_channel *nvavp_get_channel_info(struct nvavp_info *nvavp, int channel_id)
+{
+       return &nvavp->channel_info[channel_id];
+}
+
+static void nvavp_set_channel_control_area(struct nvavp_info *nvavp, int channel_id)
+{
+       struct nv_e276_control *control;
+       struct nvavp_os_info *os = &nvavp->os_info;
+       u32 temp;
+       void *ptr;
+       struct nvavp_channel *channel_info;
+
+       ptr = os->data + os->control_offset + (sizeof(struct nv_e276_control) * channel_id);
+
+       channel_info = nvavp_get_channel_info(nvavp, channel_id);
+       channel_info->os_control = (struct nv_e276_control *)ptr;
+
+       control = channel_info->os_control;
+
+       /* init get and put pointers */
+       writel(0x0, &control->put);
+       writel(0x0, &control->get);
+
+       pr_debug("nvavp_set_channel_control_area for channel_id (%d):\
+               control->put (0x%x) control->get (0x%x)\n",
+               channel_id, &control->put, &control->get);
+
+       /* enable avp VDE clock control and disable iram clock gating */
+       writel(0x0, &control->idle_clk_enable);
+       writel(0x0, &control->iram_clk_gating);
+
+       /* enable avp idle timeout interrupt */
+       writel(0x1, &control->idle_notify_enable);
+       writel(NVAVP_OS_IDLE_TIMEOUT, &control->idle_notify_delay);
+
+       /* init dma start and end pointers */
+       writel(channel_info->pushbuf_phys, &control->dma_start);
+       writel((channel_info->pushbuf_phys + NVAVP_PUSHBUFFER_SIZE),
+                                               &control->dma_end);
+
+       writel(0x00, &channel_info->pushbuf_index);
+       temp = NVAVP_PUSHBUFFER_SIZE - NVAVP_PUSHBUFFER_MIN_UPDATE_SPACE;
+       writel(temp, &channel_info->pushbuf_fence);
+}
+
 static struct clk *nvavp_clk_get(struct nvavp_info *nvavp, int id)
 {
        if (!nvavp)
@@ -172,7 +267,8 @@ static void nvavp_clk_ctrl(struct nvavp_info *nvavp, u32 clk_en)
 
 static u32 nvavp_check_idle(struct nvavp_info *nvavp)
 {
-       struct nv_e276_control *control = nvavp->os_control;
+       struct nvavp_channel *channel_info = nvavp_get_channel_info(nvavp, NVAVP_VIDEO_CHANNEL);
+       struct nv_e276_control *control = channel_info->os_control;
        return ((control->put == control->get)
                && (!atomic_read(&nvavp->clock_stay_on_refcount))) ? 1 : 0;
 }
@@ -180,13 +276,15 @@ static u32 nvavp_check_idle(struct nvavp_info *nvavp)
 static void clock_disable_handler(struct work_struct *work)
 {
        struct nvavp_info *nvavp;
+       struct nvavp_channel *channel_info;
 
        nvavp = container_of(work, struct nvavp_info,
                            clock_disable_work);
 
-       mutex_lock(&nvavp->pushbuffer_lock);
+       channel_info = nvavp_get_channel_info(nvavp, NVAVP_VIDEO_CHANNEL);
+       mutex_lock(&channel_info->pushbuffer_lock);
        nvavp_clk_ctrl(nvavp, !nvavp_check_idle(nvavp));
-       mutex_unlock(&nvavp->pushbuffer_lock);
+       mutex_unlock(&channel_info->pushbuffer_lock);
 }
 
 static int nvavp_service(struct nvavp_info *nvavp)
@@ -204,6 +302,10 @@ static int nvavp_service(struct nvavp_info *nvavp)
        if (inbox & NVE276_OS_INTERRUPT_VIDEO_IDLE)
                schedule_work(&nvavp->clock_disable_work);
 
+#if defined(CONFIG_TEGRA_NVAVP_AUDIO)
+       if (inbox & NVE276_OS_INTERRUPT_AUDIO_IDLE)
+               pr_debug("nvavp_service NVE276_OS_INTERRUPT_AUDIO_IDLE\n");
+#endif
        if (inbox & NVE276_OS_INTERRUPT_DEBUG_STRING) {
                /* Should only occur with debug AVP OS builds */
                debug_print = os->data;
@@ -326,99 +428,91 @@ static int nvavp_reset_vde(struct nvavp_info *nvavp)
        return 0;
 }
 
-static int nvavp_pushbuffer_alloc(struct nvavp_info *nvavp)
+static int nvavp_pushbuffer_alloc(struct nvavp_info *nvavp, int channel_id)
 {
        int ret = 0;
 
-       nvavp->pushbuf_handle = nvmap_alloc(nvavp->nvmap, NVAVP_PUSHBUFFER_SIZE,
-                               SZ_1M, NVMAP_HANDLE_UNCACHEABLE, 0);
-       if (IS_ERR(nvavp->pushbuf_handle)) {
+       struct nvavp_channel *channel_info = nvavp_get_channel_info(
+                                                       nvavp, channel_id);
+
+       channel_info->pushbuf_handle = nvmap_alloc(nvavp->nvmap,
+                                               NVAVP_PUSHBUFFER_SIZE,
+                                               SZ_1M, NVMAP_HANDLE_UNCACHEABLE,
+                                               0);
+       if (IS_ERR(channel_info->pushbuf_handle)) {
                dev_err(&nvavp->nvhost_dev->dev,
                        "cannot create pushbuffer handle\n");
-               ret = PTR_ERR(nvavp->pushbuf_handle);
+               ret = PTR_ERR(channel_info->pushbuf_handle);
                goto err_pushbuf_alloc;
        }
-       nvavp->pushbuf_data = (u8 *)nvmap_mmap(nvavp->pushbuf_handle);
-       if (!nvavp->pushbuf_data) {
+       channel_info->pushbuf_data = (u8 *)nvmap_mmap(
+                                               channel_info->pushbuf_handle);
+
+       if (!channel_info->pushbuf_data) {
                dev_err(&nvavp->nvhost_dev->dev,
                        "cannot map pushbuffer handle\n");
                ret = -ENOMEM;
                goto err_pushbuf_mmap;
        }
-       nvavp->pushbuf_phys = nvmap_pin(nvavp->nvmap, nvavp->pushbuf_handle);
-       if (IS_ERR((void *)nvavp->pushbuf_phys)) {
+       channel_info->pushbuf_phys = nvmap_pin(nvavp->nvmap,
+                                       channel_info->pushbuf_handle);
+       if (IS_ERR((void *)channel_info->pushbuf_phys)) {
                dev_err(&nvavp->nvhost_dev->dev,
                        "cannot pin pushbuffer handle\n");
-               ret = PTR_ERR((void *)nvavp->pushbuf_phys);
+               ret = PTR_ERR((void *)channel_info->pushbuf_phys);
                goto err_pushbuf_pin;
        }
 
-       memset(nvavp->pushbuf_data, 0, NVAVP_PUSHBUFFER_SIZE);
+       memset(channel_info->pushbuf_data, 0, NVAVP_PUSHBUFFER_SIZE);
 
        return 0;
 
 err_pushbuf_pin:
-       nvmap_munmap(nvavp->pushbuf_handle, nvavp->pushbuf_data);
+       nvmap_munmap(channel_info->pushbuf_handle, channel_info->pushbuf_data);
 err_pushbuf_mmap:
-       nvmap_free(nvavp->nvmap, nvavp->pushbuf_handle);
+       nvmap_free(nvavp->nvmap, channel_info->pushbuf_handle);
 err_pushbuf_alloc:
        return ret;
 }
 
 static void nvavp_pushbuffer_free(struct nvavp_info *nvavp)
 {
-       nvmap_unpin(nvavp->nvmap, nvavp->pushbuf_handle);
-       nvmap_munmap(nvavp->pushbuf_handle, nvavp->pushbuf_data);
-       nvmap_free(nvavp->nvmap, nvavp->pushbuf_handle);
+       int channel_id;
+
+       for (channel_id = 0; channel_id < NVAVP_NUM_CHANNELS; channel_id++) {
+               if (nvavp->channel_info[channel_id].pushbuf_data) {
+                       nvmap_unpin(nvavp->nvmap,
+                               nvavp->channel_info[channel_id].pushbuf_handle);
+                       nvmap_munmap(
+                               nvavp->channel_info[channel_id].pushbuf_handle,
+                               nvavp->channel_info[channel_id].pushbuf_data);
+                       nvmap_free(nvavp->nvmap,
+                               nvavp->channel_info[channel_id].pushbuf_handle);
+               }
+       }
 }
 
+
 static int nvavp_pushbuffer_init(struct nvavp_info *nvavp)
 {
-       void *ptr;
-       struct nvavp_os_info *os = &nvavp->os_info;
-       struct nv_e276_control *control;
-       u32 temp;
-       int ret;
-
-       ret = nvavp_pushbuffer_alloc(nvavp);
-       if (ret) {
-               dev_err(&nvavp->nvhost_dev->dev,
-                       "unable to alloc pushbuffer\n");
-               return ret;
-       }
-
-       ptr = os->data;
-       ptr += os->control_offset;
-       nvavp->os_control = (struct nv_e276_control *)ptr;
+       int ret, channel_id;
 
-       control = nvavp->os_control;
-       memset(control, 0, sizeof(struct nvavp_os_info));
-
-       /* init get and put pointers */
-       writel(0x0, &control->put);
-       writel(0x0, &control->get);
-
-       /* enable avp VDE clock control and disable iram clock gating */
-       writel(0x0, &control->idle_clk_enable);
-       writel(0x0, &control->iram_clk_gating);
-
-       /* enable avp idle timeout interrupt */
-       writel(0x1, &control->idle_notify_enable);
-       writel(NVAVP_OS_IDLE_TIMEOUT, &control->idle_notify_delay);
-
-       /* init dma start and end pointers */
-       writel(nvavp->pushbuf_phys, &control->dma_start);
-       writel((nvavp->pushbuf_phys + NVAVP_PUSHBUFFER_SIZE),
-                                       &control->dma_end);
-
-       writel(0x00, &nvavp->pushbuf_index);
-       temp = NVAVP_PUSHBUFFER_SIZE - NVAVP_PUSHBUFFER_MIN_UPDATE_SPACE;
-       writel(temp, &nvavp->pushbuf_fence);
-
-       nvavp->syncpt_id = NVSYNCPT_AVP_0;
-       nvavp->syncpt_value = nvhost_syncpt_read(nvavp->nvhost_syncpt,
-                                                nvavp->syncpt_id);
+       for (channel_id = 0; channel_id < NVAVP_NUM_CHANNELS; channel_id++) {
+               ret = nvavp_pushbuffer_alloc(nvavp, channel_id);
+               if (ret) {
+                       dev_err(&nvavp->nvhost_dev->dev,
+                               "unable to alloc pushbuffer\n");
+                       return ret;
+               }
+               nvavp_set_channel_control_area(nvavp, channel_id);
+               if (IS_VIDEO_CHANNEL_ID(channel_id)) {
+                       nvavp->syncpt_id = NVSYNCPT_AVP_0;
+                       nvavp->syncpt_value = nvhost_syncpt_read(
+                                                       nvavp->nvhost_syncpt,
+                                                       nvavp->syncpt_id);
+               }
 
+       }
        return 0;
 }
 
@@ -429,37 +523,47 @@ static void nvavp_pushbuffer_deinit(struct nvavp_info *nvavp)
 
 static int nvavp_pushbuffer_update(struct nvavp_info *nvavp, u32 phys_addr,
                        u32 gather_count, struct nvavp_syncpt *syncpt,
-                       u32 ext_ucode_flag)
+                       u32 ext_ucode_flag, int channel_id)
 {
-       struct nv_e276_control *control = nvavp->os_control;
+       struct nvavp_channel  *channel_info;
+       struct nv_e276_control *control;
        u32 gather_cmd, setucode_cmd, sync = 0;
        u32 wordcount = 0;
        u32 index, value = -1;
 
-       mutex_lock(&nvavp->pushbuffer_lock);
+       channel_info = nvavp_get_channel_info(nvavp, channel_id);
+
+       control = channel_info->os_control;
+       pr_debug("nvavp_pushbuffer_update for channel_id (%d):\
+               control->put (0x%x) control->get (0x%x)\n",
+               channel_id, &control->put, &control->get);
+
+       mutex_lock(&channel_info->pushbuffer_lock);
 
        /* check for pushbuffer wrapping */
-       if (nvavp->pushbuf_index >= nvavp->pushbuf_fence)
-               nvavp->pushbuf_index = 0;
+       if (channel_info->pushbuf_index >= channel_info->pushbuf_fence)
+               channel_info->pushbuf_index = 0;
 
        if (!ext_ucode_flag) {
                setucode_cmd =
                        NVE26E_CH_OPCODE_INCR(NVE276_SET_MICROCODE_A, 3);
 
-               index = wordcount + nvavp->pushbuf_index;
-               writel(setucode_cmd, (nvavp->pushbuf_data + index));
+               index = wordcount + channel_info->pushbuf_index;
+               writel(setucode_cmd, (channel_info->pushbuf_data + index));
                wordcount += sizeof(u32);
 
-               index = wordcount + nvavp->pushbuf_index;
-               writel(0, (nvavp->pushbuf_data + index));
+               index = wordcount + channel_info->pushbuf_index;
+               writel(0, (channel_info->pushbuf_data + index));
                wordcount += sizeof(u32);
 
-               index = wordcount + nvavp->pushbuf_index;
-               writel(nvavp->ucode_info.phys, (nvavp->pushbuf_data + index));
+               index = wordcount + channel_info->pushbuf_index;
+               writel(nvavp->ucode_info.phys,
+                       (channel_info->pushbuf_data + index));
                wordcount += sizeof(u32);
 
-               index = wordcount + nvavp->pushbuf_index;
-               writel(nvavp->ucode_info.size, (nvavp->pushbuf_data + index));
+               index = wordcount + channel_info->pushbuf_index;
+               writel(nvavp->ucode_info.size,
+                       (channel_info->pushbuf_data + index));
                wordcount += sizeof(u32);
        }
 
@@ -474,39 +578,52 @@ static int nvavp_pushbuffer_update(struct nvavp_info *nvavp, u32 phys_addr,
        }
 
        /* write commands out */
-       index = wordcount + nvavp->pushbuf_index;
-       writel(gather_cmd, (nvavp->pushbuf_data + index));
+       index = wordcount + channel_info->pushbuf_index;
+       writel(gather_cmd, (channel_info->pushbuf_data + index));
        wordcount += sizeof(u32);
 
-       index = wordcount + nvavp->pushbuf_index;
-       writel(phys_addr, (nvavp->pushbuf_data + index));
+       index = wordcount + channel_info->pushbuf_index;
+       writel(phys_addr, (channel_info->pushbuf_data + index));
        wordcount += sizeof(u32);
 
        if (syncpt) {
-               index = wordcount + nvavp->pushbuf_index;
-               writel(sync, (nvavp->pushbuf_data + index));
+               index = wordcount + channel_info->pushbuf_index;
+               writel(sync, (channel_info->pushbuf_data + index));
                wordcount += sizeof(u32);
        }
 
        /* enable clocks to VDE/BSEV */
-       nvavp_clk_ctrl(nvavp, 1);
+       if (IS_VIDEO_CHANNEL_ID(channel_id))
+               nvavp_clk_ctrl(nvavp, 1);
 
        /* update put pointer */
-       nvavp->pushbuf_index = (nvavp->pushbuf_index + wordcount) &
+       channel_info->pushbuf_index = (channel_info->pushbuf_index + wordcount)&
                                        (NVAVP_PUSHBUFFER_SIZE - 1);
-       writel(nvavp->pushbuf_index, &control->put);
+
+       writel(channel_info->pushbuf_index, &control->put);
        wmb();
 
        /* wake up avp */
-       writel(0xA0000001, NVAVP_OS_OUTBOX);
 
+       if (IS_VIDEO_CHANNEL_ID(channel_id)) {
+               pr_debug("Wake up Video Channel\n");
+               writel(0xA0000001, NVAVP_OS_OUTBOX);
+       }
+       else {
+#if defined(CONFIG_TEGRA_NVAVP_AUDIO)
+               if (IS_AUDIO_CHANNEL_ID(channel_id)) {
+                       pr_debug("Wake up Audio Channel\n");
+                       writel(0xA0000002, NVAVP_OS_OUTBOX);
+               }
+#endif
+       }
        /* Fill out fence struct */
        if (syncpt) {
                syncpt->id = nvavp->syncpt_id;
                syncpt->value = value;
        }
 
-       mutex_unlock(&nvavp->pushbuffer_lock);
+       mutex_unlock(&channel_info->pushbuffer_lock);
 
        return 0;
 }
@@ -531,14 +648,14 @@ static int nvavp_load_ucode(struct nvavp_info *nvavp)
                sprintf(fw_ucode_file, "nvavp_vid_ucode.bin");
 
                ret = request_firmware(&nvavp_ucode_fw, fw_ucode_file,
-                                       nvavp->misc_dev.this_device);
+                                       nvavp->video_misc_dev.this_device);
                if (ret) {
                        /* Try alternative version */
                        sprintf(fw_ucode_file, "nvavp_vid_ucode_alt.bin");
 
                        ret = request_firmware(&nvavp_ucode_fw,
                                                fw_ucode_file,
-                                               nvavp->misc_dev.this_device);
+                                               nvavp->video_misc_dev.this_device);
 
                        if (ret) {
                                dev_err(&nvavp->nvhost_dev->dev,
@@ -636,7 +753,7 @@ static int nvavp_load_os(struct nvavp_info *nvavp, char *fw_os_file)
 
        if (!os_info->os_bin) {
                ret = request_firmware(&nvavp_os_fw, fw_os_file,
-                                       nvavp->misc_dev.this_device);
+                                       nvavp->video_misc_dev.this_device);
                if (ret) {
                        dev_err(&nvavp->nvhost_dev->dev,
                                "cannot read os firmware '%s'\n", fw_os_file);
@@ -701,14 +818,28 @@ err_req_fw:
        return ret;
 }
 
-static int nvavp_init(struct nvavp_info *nvavp)
+
+static int nvavp_os_init(struct nvavp_info *nvavp)
 {
        char fw_os_file[32];
        int ret = 0;
+       int video_initialized, audio_initialized = 0;
+
+       video_initialized = nvavp_get_video_init_status(nvavp);
 
-       if (nvavp->initialized)
+#if defined(CONFIG_TEGRA_NVAVP_AUDIO)
+       audio_initialized = nvavp_get_audio_init_status(nvavp);
+#endif
+       pr_debug("video_initialized(%d) audio_initialized(%d)\n",
+               video_initialized, audio_initialized);
+
+       /* Video and Audio both are initialized */
+       if (video_initialized || audio_initialized)
                return ret;
 
+       /* Video or Audio both are uninitialized */
+       pr_debug("video_initialized == audio_initialized (%d)\n",
+               nvavp->video_initialized);
 #if defined(CONFIG_TEGRA_AVP_KERNEL_ON_MMU) /* Tegra2 with AVP MMU */
        /* paddr is any address returned from nvmap_pin */
        /* vaddr is AVP_KERNEL_VIRT_BASE */
@@ -752,7 +883,6 @@ static int nvavp_init(struct nvavp_info *nvavp)
        nvavp->os_info.reset_addr = nvavp->os_info.phys;
        nvavp->os_info.data = ioremap(nvavp->os_info.phys, SZ_1M);
 #endif
-
        ret = nvavp_load_os(nvavp, fw_os_file);
        if (ret) {
                dev_err(&nvavp->nvhost_dev->dev,
@@ -766,21 +896,45 @@ static int nvavp_init(struct nvavp_info *nvavp)
                        "unable to init pushbuffer\n");
                goto err_exit;
        }
+       tegra_init_legacy_irq_cop();
+       enable_irq(nvavp->mbox_from_avp_pend_irq);
+err_exit:
+       return ret;
+}
+
+static int nvavp_init(struct nvavp_info *nvavp, int channel_id)
+{
+       int ret = 0;
 
-       ret = nvavp_load_ucode(nvavp);
+       ret = nvavp_os_init(nvavp);
        if (ret) {
                dev_err(&nvavp->nvhost_dev->dev,
-                       "unable to load ucode\n");
-               goto err_exit;
+                       "unable to load os firmware and allocate buffers\n");
        }
 
-       tegra_init_legacy_irq_cop();
+       if (IS_VIDEO_CHANNEL_ID(channel_id) &&
+               (!nvavp_get_video_init_status(nvavp)) ) {
+               pr_debug("nvavp_init : channel_ID (%d)\n", channel_id);
+               ret = nvavp_load_ucode(nvavp);
+               if (ret) {
+                       dev_err(&nvavp->nvhost_dev->dev,
+                               "unable to load ucode\n");
+                       goto err_exit;
+               }
 
-       nvavp_reset_vde(nvavp);
-       nvavp_reset_avp(nvavp, nvavp->os_info.reset_addr);
-       enable_irq(nvavp->mbox_from_avp_pend_irq);
+               nvavp_reset_vde(nvavp);
+               nvavp_reset_avp(nvavp, nvavp->os_info.reset_addr);
 
-       nvavp->initialized = 1;
+               nvavp_set_video_init_status(nvavp, 1);
+       }
+#if defined(CONFIG_TEGRA_NVAVP_AUDIO)
+       if (IS_AUDIO_CHANNEL_ID(channel_id) &&
+               (!nvavp_get_audio_init_status(nvavp))) {
+               pr_debug("nvavp_init : channel_ID (%d)\n", channel_id);
+               nvavp_reset_avp(nvavp, nvavp->os_info.reset_addr);
+               nvavp_set_audio_init_status(nvavp, 1);
+       }
+#endif
 
 err_exit:
        return ret;
@@ -788,22 +942,48 @@ err_exit:
 
 static void nvavp_uninit(struct nvavp_info *nvavp)
 {
-       if (!nvavp->initialized)
+       int video_initialized, audio_initialized = 0;
+
+       video_initialized = nvavp_get_video_init_status(nvavp);
+
+#if defined(CONFIG_TEGRA_NVAVP_AUDIO)
+       audio_initialized = nvavp_get_audio_init_status(nvavp);
+#endif
+
+       pr_debug("nvavp_uninit video_initialized(%d) audio_initialized(%d)\n",
+               video_initialized, audio_initialized);
+
+       /* Video and Audio both are uninitialized */
+       if (!video_initialized && !audio_initialized)
                return;
 
-       disable_irq(nvavp->mbox_from_avp_pend_irq);
+       if (video_initialized) {
+               pr_debug("nvavp_uninit nvavp->video_initialized\n");
+               cancel_work_sync(&nvavp->clock_disable_work);
 
-       cancel_work_sync(&nvavp->clock_disable_work);
+               nvavp_halt_vde(nvavp);
 
-       nvavp_pushbuffer_deinit(nvavp);
+               clk_disable(nvavp->sclk);
+               clk_disable(nvavp->emc_clk);
 
-       nvavp_halt_vde(nvavp);
-       nvavp_halt_avp(nvavp);
+               nvavp_set_video_init_status(nvavp, 0);
+               video_initialized = 0;
+       }
 
-       clk_disable(nvavp->sclk);
-       clk_disable(nvavp->emc_clk);
+#if defined(CONFIG_TEGRA_NVAVP_AUDIO)
+       if (audio_initialized) {
+               nvavp_set_audio_init_status(nvavp, 0);
+               audio_initialized = 0;
+       }
+#endif
 
-       nvavp->initialized = 0;
+       /* Video and Audio both becomes uninitialized */
+       if (video_initialized == audio_initialized) {
+               pr_debug("nvavp_uninit both channels unitialized\n");
+               disable_irq(nvavp->mbox_from_avp_pend_irq);
+               nvavp_pushbuffer_deinit(nvavp);
+               nvavp_halt_avp(nvavp);
+       }
 }
 
 static int nvavp_set_clock_ioctl(struct file *filp, unsigned int cmd,
@@ -991,7 +1171,8 @@ static int nvavp_pushbuffer_submit_ioctl(struct file *filp, unsigned int cmd,
                ret = nvavp_pushbuffer_update(nvavp,
                                             (phys_addr + hdr.cmdbuf.offset),
                                              hdr.cmdbuf.words, &syncpt,
-                                             (hdr.flags & NVAVP_UCODE_EXT));
+                                             (hdr.flags & NVAVP_UCODE_EXT),
+                                               clientctx->channel_id);
 
                if (copy_to_user((void __user *)user_hdr->syncpt, &syncpt,
                                sizeof(struct nvavp_syncpt))) {
@@ -1002,7 +1183,8 @@ static int nvavp_pushbuffer_submit_ioctl(struct file *filp, unsigned int cmd,
                ret = nvavp_pushbuffer_update(nvavp,
                                             (phys_addr + hdr.cmdbuf.offset),
                                              hdr.cmdbuf.words, NULL,
-                                             (hdr.flags & NVAVP_UCODE_EXT));
+                                             (hdr.flags & NVAVP_UCODE_EXT),
+                                               clientctx->channel_id);
        }
 
 err_reloc_info:
@@ -1056,7 +1238,7 @@ static int nvavp_force_clock_stay_on_ioctl(struct file *filp, unsigned int cmd,
        return 0;
 }
 
-static int tegra_nvavp_open(struct inode *inode, struct file *filp)
+static int tegra_nvavp_open(struct inode *inode, struct file *filp, int channel_id)
 {
        struct miscdevice *miscdev = filp->private_data;
        struct nvavp_info *nvavp = dev_get_drvdata(miscdev->parent);
@@ -1073,8 +1255,11 @@ static int tegra_nvavp_open(struct inode *inode, struct file *filp)
 
        mutex_lock(&nvavp->open_lock);
 
-       if (!nvavp->refcount)
-               ret = nvavp_init(nvavp);
+       pr_debug("tegra_nvavp_open channel_id (%d)\n", channel_id);
+
+       clientctx->channel_id = channel_id;
+
+       ret = nvavp_init(nvavp, channel_id);
 
        if (!ret)
                nvavp->refcount++;
@@ -1090,6 +1275,20 @@ static int tegra_nvavp_open(struct inode *inode, struct file *filp)
        return ret;
 }
 
+static int tegra_nvavp_video_open(struct inode *inode, struct file *filp)
+{
+       pr_debug("tegra_nvavp_video_open NVAVP_VIDEO_CHANNEL\n");
+       return tegra_nvavp_open(inode, filp, NVAVP_VIDEO_CHANNEL);
+}
+
+#if defined(CONFIG_TEGRA_NVAVP_AUDIO)
+static int tegra_nvavp_audio_open(struct inode *inode, struct file *filp)
+{
+       pr_debug("tegra_nvavp_audio_open NVAVP_AUDIO_CHANNEL\n");
+       return tegra_nvavp_open(inode, filp, NVAVP_AUDIO_CHANNEL);
+}
+#endif
+
 static int tegra_nvavp_release(struct inode *inode, struct file *filp)
 {
        struct nvavp_clientctx *clientctx = filp->private_data;
@@ -1162,13 +1361,22 @@ static long tegra_nvavp_ioctl(struct file *filp, unsigned int cmd,
        return ret;
 }
 
-static const struct file_operations tegra_nvavp_fops = {
+static const struct file_operations tegra_video_nvavp_fops = {
        .owner          = THIS_MODULE,
-       .open           = tegra_nvavp_open,
+       .open           = tegra_nvavp_video_open,
        .release        = tegra_nvavp_release,
        .unlocked_ioctl = tegra_nvavp_ioctl,
 };
 
+#if defined(CONFIG_TEGRA_NVAVP_AUDIO)
+static const struct file_operations tegra_audio_nvavp_fops = {
+       .owner          = THIS_MODULE,
+       .open           = tegra_nvavp_audio_open,
+       .release        = tegra_nvavp_release,
+       .unlocked_ioctl = tegra_nvavp_ioctl,
+};
+#endif
+
 static int tegra_nvavp_probe(struct nvhost_device *ndev,
        struct nvhost_device_id *id_table)
 {
@@ -1176,7 +1384,7 @@ static int tegra_nvavp_probe(struct nvhost_device *ndev,
        int irq;
        unsigned int heap_mask;
        u32 iovmm_addr;
-       int ret = 0;
+       int ret = 0, channel_id;
 
        irq = nvhost_get_irq_byname(ndev, "mbox_from_nvavp_pending");
        if (irq < 0) {
@@ -1287,7 +1495,9 @@ static int tegra_nvavp_probe(struct nvhost_device *ndev,
 
        nvavp->mbox_from_avp_pend_irq = irq;
        mutex_init(&nvavp->open_lock);
-       mutex_init(&nvavp->pushbuffer_lock);
+
+       for (channel_id = 0; channel_id < NVAVP_NUM_CHANNELS; channel_id++)
+               mutex_init(&nvavp->channel_info[channel_id].pushbuffer_lock);
 
        /* TODO DO NOT USE NVAVP DEVICE */
        nvavp->cop_clk = clk_get(&ndev->dev, "cop");
@@ -1330,18 +1540,32 @@ static int tegra_nvavp_probe(struct nvhost_device *ndev,
 
        INIT_WORK(&nvavp->clock_disable_work, clock_disable_handler);
 
-       nvavp->misc_dev.minor = MISC_DYNAMIC_MINOR;
-       nvavp->misc_dev.name = "tegra_avpchannel";
-       nvavp->misc_dev.fops = &tegra_nvavp_fops;
-       nvavp->misc_dev.mode = S_IRWXUGO;
-       nvavp->misc_dev.parent = &ndev->dev;
+       nvavp->video_misc_dev.minor = MISC_DYNAMIC_MINOR;
+       nvavp->video_misc_dev.name = "tegra_avpchannel";
+       nvavp->video_misc_dev.fops = &tegra_video_nvavp_fops;
+       nvavp->video_misc_dev.mode = S_IRWXUGO;
+       nvavp->video_misc_dev.parent = &ndev->dev;
 
-       ret = misc_register(&nvavp->misc_dev);
+       ret = misc_register(&nvavp->video_misc_dev);
        if (ret) {
                dev_err(&ndev->dev, "unable to register misc device!\n");
                goto err_misc_reg;
        }
 
+#if defined(CONFIG_TEGRA_NVAVP_AUDIO)
+       nvavp->audio_misc_dev.minor = MISC_DYNAMIC_MINOR;
+       nvavp->audio_misc_dev.name = "tegra_audio_avpchannel";
+       nvavp->audio_misc_dev.fops = &tegra_audio_nvavp_fops;
+       nvavp->audio_misc_dev.mode = S_IRWXUGO;
+       nvavp->audio_misc_dev.parent = &ndev->dev;
+
+       ret = misc_register(&nvavp->audio_misc_dev);
+       if (ret) {
+       dev_err(&ndev->dev, "unable to register misc device!\n");
+               goto err_audio_misc_reg;
+       }
+#endif
+
        ret = request_irq(irq, nvavp_mbox_pending_isr, 0,
                          TEGRA_NVAVP_NAME, nvavp);
        if (ret) {
@@ -1356,7 +1580,11 @@ static int tegra_nvavp_probe(struct nvhost_device *ndev,
        return 0;
 
 err_req_irq_pend:
-       misc_deregister(&nvavp->misc_dev);
+#if defined(CONFIG_TEGRA_NVAVP_AUDIO)
+       misc_deregister(&nvavp->audio_misc_dev);
+err_audio_misc_reg:
+#endif
+       misc_deregister(&nvavp->video_misc_dev);
 err_misc_reg:
        clk_put(nvavp->emc_clk);
 err_get_emc_clk:
@@ -1402,8 +1630,11 @@ static int tegra_nvavp_remove(struct nvhost_device *ndev)
        nvavp_unload_ucode(nvavp);
        nvavp_unload_os(nvavp);
 
-       misc_deregister(&nvavp->misc_dev);
+       misc_deregister(&nvavp->video_misc_dev);
 
+#if defined(CONFIG_TEGRA_NVAVP_AUDIO)
+       misc_deregister(&nvavp->audio_misc_dev);
+#endif
        clk_put(nvavp->bsev_clk);
        clk_put(nvavp->vde_clk);
        clk_put(nvavp->cop_clk);
@@ -1444,7 +1675,7 @@ static int tegra_nvavp_resume(struct nvhost_device *ndev)
        mutex_lock(&nvavp->open_lock);
 
        if (nvavp->refcount)
-               nvavp_init(nvavp);
+               nvavp_init(nvavp, NVAVP_VIDEO_CHANNEL);
 
        mutex_unlock(&nvavp->open_lock);