diff mbox series

[28/30] IMG DEC V4L2 Interface function implementations

Message ID 20210818141037.19990-29-sidraya.bj@pathpartnertech.com
State New
Headers show
Series TI Video Decoder driver upstreaming to v5.14-rc6 kernel | expand

Commit Message

Sidraya Jayagond Aug. 18, 2021, 2:10 p.m. UTC
From: Sidraya <sidraya.bj@pathpartnertech.com>

Add Low-level VXD interface component and v4l2 wrapper Interface
for VXD driver.

Signed-off-by: Sunita Nadampalli <sunitan@ti.com>
Signed-off-by: Sidraya <sidraya.bj@pathpartnertech.com>
---
 MAINTAINERS                                  |    2 +
 drivers/staging/media/vxd/common/vid_buf.h   |   42 +
 drivers/staging/media/vxd/decoder/vxd_v4l2.c | 2129 ++++++++++++++++++
 3 files changed, 2173 insertions(+)
 create mode 100644 drivers/staging/media/vxd/common/vid_buf.h
 create mode 100644 drivers/staging/media/vxd/decoder/vxd_v4l2.c

Comments

kernel test robot Aug. 18, 2021, 7:22 p.m. UTC | #1
Hi,

I love your patch! Yet something to improve:

[auto build test ERROR on linuxtv-media/master]
[also build test ERROR on staging/staging-testing driver-core/driver-core-testing linus/master v5.14-rc6 next-20210818]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]

url:    https://github.com/0day-ci/linux/commits/sidraya-bj-pathpartnertech-com/TI-Video-Decoder-driver-upstreaming-to-v5-14-rc6-kernel/20210818-221811
base:   git://linuxtv.org/media_tree.git master
config: m68k-allmodconfig (attached as .config)
compiler: m68k-linux-gcc (GCC) 11.2.0
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # https://github.com/0day-ci/linux/commit/ed83bf9b395e58893b5d92675196aee8f619efc9
        git remote add linux-review https://github.com/0day-ci/linux
        git fetch --no-tags linux-review sidraya-bj-pathpartnertech-com/TI-Video-Decoder-driver-upstreaming-to-v5-14-rc6-kernel/20210818-221811
        git checkout ed83bf9b395e58893b5d92675196aee8f619efc9
        # save the attached .config to linux build tree
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-11.2.0 make.cross ARCH=m68k 

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@intel.com>

All error/warnings (new ones prefixed by >>):

   drivers/staging/media/vxd/decoder/vxd_core.c: In function 'stream_worker':
>> drivers/staging/media/vxd/decoder/vxd_core.c:549:25: warning: variable 'vxd' set but not used [-Wunused-but-set-variable]
     549 |         struct vxd_dev *vxd = NULL;
         |                         ^~~
--
   drivers/staging/media/vxd/decoder/vxd_v4l2.c: In function 'vxd_dec_buf_prepare':
>> drivers/staging/media/vxd/decoder/vxd_v4l2.c:712:36: error: implicit declaration of function 'phys_to_page'; did you mean 'pfn_to_page'? [-Werror=implicit-function-declaration]
     712 |                         new_page = phys_to_page(vb2_dma_contig_plane_dma_addr(vb, plane));
         |                                    ^~~~~~~~~~~~
         |                                    pfn_to_page
>> drivers/staging/media/vxd/decoder/vxd_v4l2.c:712:34: warning: assignment to 'struct page *' from 'int' makes pointer from integer without a cast [-Wint-conversion]
     712 |                         new_page = phys_to_page(vb2_dma_contig_plane_dma_addr(vb, plane));
         |                                  ^
>> drivers/staging/media/vxd/decoder/vxd_v4l2.c:618:13: warning: variable 'pages' set but not used [-Wunused-but-set-variable]
     618 |         int pages;
         |             ^~~~~
   In file included from drivers/staging/media/vxd/decoder/vxd_props.h:19,
                    from drivers/staging/media/vxd/decoder/decoder.h:29,
                    from drivers/staging/media/vxd/decoder/core.h:20,
                    from drivers/staging/media/vxd/decoder/vxd_v4l2.c:52:
   At top level:
   drivers/staging/media/vxd/common/imgmmu.h:65:28: warning: 'VIRT_DIR_IDX_MASK' defined but not used [-Wunused-const-variable=]
      65 | static const unsigned long VIRT_DIR_IDX_MASK = (~((1 << MMU_DIR_SHIFT) - 1));
         |                            ^~~~~~~~~~~~~~~~~
   drivers/staging/media/vxd/common/imgmmu.h:62:28: warning: 'VIRT_PAGE_TBL_MASK' defined but not used [-Wunused-const-variable=]
      62 | static const unsigned long VIRT_PAGE_TBL_MASK =
         |                            ^~~~~~~~~~~~~~~~~~
   drivers/staging/media/vxd/common/imgmmu.h:60:28: warning: 'VIRT_PAGE_OFF_MASK' defined but not used [-Wunused-const-variable=]
      60 | static const unsigned long VIRT_PAGE_OFF_MASK = ((1 << MMU_PAGE_SHIFT) - 1);
         |                            ^~~~~~~~~~~~~~~~~~
   cc1: some warnings being treated as errors
--
   drivers/staging/media/vxd/decoder/hevc_secure_parser.c: In function 'bspp_hevc_parse_vps':
>> drivers/staging/media/vxd/decoder/hevc_secure_parser.c:594:1: warning: the frame size of 1168 bytes is larger than 1024 bytes [-Wframe-larger-than=]
     594 | }
         | ^


vim +712 drivers/staging/media/vxd/decoder/vxd_v4l2.c

   604	
   605	static int vxd_dec_buf_prepare(struct vb2_buffer *vb)
   606	{
   607		struct vxd_dec_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
   608		struct device *dev = ctx->dev->dev;
   609		struct vxd_dec_q_data *q_data;
   610		void *sgt;
   611	#ifdef CAPTURE_CONTIG_ALLOC
   612		struct page *new_page;
   613	#else
   614		void *sgl;
   615	#endif
   616		struct sg_table *sgt_new;
   617		void *sgl_new;
 > 618		int pages;
   619		int nents = 0;
   620		int size = 0;
   621		int plane, num_planes, ret = 0;
   622		struct vxd_buffer *buf =
   623			container_of(vb, struct vxd_buffer, buffer.vb.vb2_buf);
   624	
   625		q_data = get_q_data(ctx, vb->vb2_queue->type);
   626		if (!q_data)
   627			return -EINVAL;
   628	
   629		num_planes = q_data->fmt->num_planes;
   630	
   631		for (plane = 0; plane < num_planes; plane++) {
   632			if (vb2_plane_size(vb, plane) < q_data->size_image[plane]) {
   633				dev_err(dev, "data will not fit into plane (%lu < %lu)\n",
   634					vb2_plane_size(vb, plane),
   635					(long)q_data->size_image[plane]);
   636				return -EINVAL;
   637			}
   638		}
   639	
   640		if (buf->mapped)
   641			return 0;
   642	
   643		buf->buf_info.cpu_linear_addr = vb2_plane_vaddr(vb, 0);
   644		buf->buf_info.buf_size = vb2_plane_size(vb, 0);
   645		buf->buf_info.fd = -1;
   646		sgt = vb2_dma_sg_plane_desc(vb, 0);
   647		if (!sgt) {
   648			dev_err(dev, "Could not get sg_table from plane 0\n");
   649			return -EINVAL;
   650		}
   651	
   652		if (V4L2_TYPE_IS_OUTPUT(vb->type)) {
   653			ret = core_stream_map_buf_sg(ctx->res_str_id,
   654						     VDEC_BUFTYPE_BITSTREAM,
   655						     &buf->buf_info, sgt,
   656						     &buf->buf_map_id);
   657			if (ret) {
   658				dev_err(dev, "OUTPUT core_stream_map_buf_sg failed\n");
   659				return ret;
   660			}
   661	
   662			buf->bstr_info.buf_size = q_data->size_image[0];
   663			buf->bstr_info.cpu_virt_addr = buf->buf_info.cpu_linear_addr;
   664			buf->bstr_info.mem_attrib =
   665				SYS_MEMATTRIB_UNCACHED | SYS_MEMATTRIB_WRITECOMBINE |
   666				SYS_MEMATTRIB_INPUT | SYS_MEMATTRIB_CPU_WRITE;
   667			buf->bstr_info.bufmap_id = buf->buf_map_id;
   668			lst_init(&buf->seq_unit.bstr_seg_list);
   669			lst_init(&buf->pic_unit.bstr_seg_list);
   670			lst_init(&buf->end_unit.bstr_seg_list);
   671	
   672			list_add_tail(&buf->list, &ctx->out_buffers);
   673		} else {
   674			/* Create a single sgt from the plane(s) */
   675			sgt_new = kmalloc(sizeof(*sgt_new), GFP_KERNEL);
   676			if (!sgt_new)
   677				return -EINVAL;
   678	
   679			for (plane = 0; plane < num_planes; plane++) {
   680				size += ALIGN(vb2_plane_size(vb, plane), PAGE_SIZE);
   681				sgt = vb2_dma_sg_plane_desc(vb, plane);
   682				if (!sgt) {
   683					dev_err(dev, "Could not get sg_table from plane %d\n", plane);
   684					kfree(sgt_new);
   685					return -EINVAL;
   686				}
   687	#ifdef CAPTURE_CONTIG_ALLOC
   688				nents += 1;
   689	#else
   690				nents += sg_nents(img_mmu_get_sgl(sgt));
   691	#endif
   692			}
   693			buf->buf_info.buf_size = size;
   694	
   695			pages = (size + PAGE_SIZE - 1) / PAGE_SIZE;
   696			ret = sg_alloc_table(sgt_new, nents, GFP_KERNEL);
   697			if (ret) {
   698				kfree(sgt_new);
   699				return -EINVAL;
   700			}
   701			sgl_new = img_mmu_get_sgl(sgt_new);
   702	
   703			for (plane = 0; plane < num_planes; plane++) {
   704				sgt = vb2_dma_sg_plane_desc(vb, plane);
   705				if (!sgt) {
   706					dev_err(dev, "Could not get sg_table from plane %d\n", plane);
   707					sg_free_table(sgt_new);
   708					kfree(sgt_new);
   709					return -EINVAL;
   710				}
   711	#ifdef CAPTURE_CONTIG_ALLOC
 > 712				new_page = phys_to_page(vb2_dma_contig_plane_dma_addr(vb, plane));
   713				sg_set_page(sgl_new, new_page, ALIGN(vb2_plane_size(vb, plane),
   714								     PAGE_SIZE), 0);
   715				sgl_new = sg_next(sgl_new);
   716	#else
   717				sgl = img_mmu_get_sgl(sgt);
   718	
   719				while (sgl) {
   720					sg_set_page(sgl_new, sg_page(sgl), img_mmu_get_sgl_length(sgl), 0);
   721					sgl = sg_next(sgl);
   722					sgl_new = sg_next(sgl_new);
   723				}
   724	#endif
   725			}
   726	
   727			buf->buf_info.pictbuf_cfg = ctx->pict_bufcfg;
   728			ret = core_stream_map_buf_sg(ctx->res_str_id,
   729						     VDEC_BUFTYPE_PICTURE,
   730						     &buf->buf_info, sgt_new,
   731						     &buf->buf_map_id);
   732			sg_free_table(sgt_new);
   733			kfree(sgt_new);
   734			if (ret) {
   735				dev_err(dev, "CAPTURE core_stream_map_buf_sg failed\n");
   736				return ret;
   737			}
   738			list_add_tail(&buf->list, &ctx->cap_buffers);
   739		}
   740		buf->mapped = TRUE;
   741		buf->reuse = TRUE;
   742	
   743		return 0;
   744	}
   745	

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org
diff mbox series

Patch

diff --git a/MAINTAINERS b/MAINTAINERS
index 0616ab620135..c7b4c860f8a7 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -19566,6 +19566,7 @@  F:	drivers/staging/media/vxd/common/rman_api.c
 F:	drivers/staging/media/vxd/common/rman_api.h
 F:	drivers/staging/media/vxd/common/talmmu_api.c
 F:	drivers/staging/media/vxd/common/talmmu_api.h
+F:	drivers/staging/media/vxd/common/vid_buf.h
 F:	drivers/staging/media/vxd/common/work_queue.c
 F:	drivers/staging/media/vxd/common/work_queue.h
 F:	drivers/staging/media/vxd/decoder/Kconfig
@@ -19641,6 +19642,7 @@  F;	drivers/staging/media/vxd/decoder/vxd_props.h
 F:	drivers/staging/media/vxd/decoder/vxd_pvdec.c
 F:	drivers/staging/media/vxd/decoder/vxd_pvdec_priv.h
 F:	drivers/staging/media/vxd/decoder/vxd_pvdec_regs.h
+F:	drivers/staging/media/vxd/decoder/vxd_v4l2.c
 
 VIDEO I2C POLLING DRIVER
 M:	Matt Ranostay <matt.ranostay@konsulko.com>
diff --git a/drivers/staging/media/vxd/common/vid_buf.h b/drivers/staging/media/vxd/common/vid_buf.h
new file mode 100644
index 000000000000..ac0e4f9b4894
--- /dev/null
+++ b/drivers/staging/media/vxd/common/vid_buf.h
@@ -0,0 +1,42 @@ 
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Low-level VXD interface component
+ *
+ * Copyright (c) Imagination Technologies Ltd.
+ * Copyright (c) 2021 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ * Authors:
+ *	Angela Stegmaier <angelabaker@ti.com>
+ *
+ * Re-written for upstream
+ *	Sidraya Jayagond <sidraya.bj@pathpartnertech.com>
+ */
+
+#ifndef _VID_BUF_H
+#define _VID_BUF_H
+
+/*
+ * struct vidio_ddbufinfo - contains information about virtual address
+ * @buf_size: the size of the buffer (in bytes).
+ * @cpu_virt: the cpu virtual address  (mapped into the local cpu mmu)
+ * @dev_virt: device virtual address (pages mapped into IMG H/W mmu)
+ * @hndl_memory: handle to device mmu mapping
+ * @buff_id: buffer id used in communication with interface
+ * @is_internal: true, if the buffer is allocated internally
+ * @ref_count: reference count (number of users)
+ * @kmstr_id: stream id
+ * @core_id: core id
+ */
+struct vidio_ddbufinfo {
+	unsigned int buf_size;
+	void *cpu_virt;
+	unsigned int dev_virt;
+	void *hndl_memory;
+	unsigned int buff_id;
+	unsigned int is_internal;
+	unsigned int ref_count;
+	unsigned int kmstr_id;
+	unsigned int core_id;
+};
+
+#endif /* _VID_BUF_H */
diff --git a/drivers/staging/media/vxd/decoder/vxd_v4l2.c b/drivers/staging/media/vxd/decoder/vxd_v4l2.c
new file mode 100644
index 000000000000..292e2de0c5e0
--- /dev/null
+++ b/drivers/staging/media/vxd/decoder/vxd_v4l2.c
@@ -0,0 +1,2129 @@ 
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * IMG DEC V4L2 Interface function implementations
+ *
+ * Copyright (c) Imagination Technologies Ltd.
+ * Copyright (c) 2021 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ * Authors:
+ *	Angela Stegmaier <angelabaker@ti.com>
+ *
+ * Re-written for upstreming
+ *	Prashanth Kumar Amai <prashanth.ka@pathpartnertech.com>
+ *	Sidraya Jayagond <sidraya.bj@pathpartnertech.com>
+ */
+
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/of_irq.h>
+#include <linux/platform_device.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
+#include <linux/videodev2.h>
+#include <linux/idr.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/dma-mapping.h>
+#include <linux/spinlock.h>
+#include <linux/printk.h>
+#include <linux/mutex.h>
+
+#ifdef ERROR_RECOVERY_SIMULATION
+#include <linux/sysfs.h>
+#include <linux/kobject.h>
+#include <linux/types.h>
+#endif
+
+#include <media/v4l2-common.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-mem2mem.h>
+#include <media/videobuf2-core.h>
+#include <media/videobuf2-dma-sg.h>
+#ifdef CAPTURE_CONTIG_ALLOC
+#include <media/videobuf2-dma-contig.h>
+#endif
+
+#include "core.h"
+#include "h264fw_data.h"
+#include "hevcfw_data.h"
+#include "img_dec_common.h"
+#include "vxd_pvdec_priv.h"
+#include "vxd_dec.h"
+
+#define VXD_DEC_SPIN_LOCK_NAME  "vxd-dec"
+#define IMG_VXD_DEC_MODULE_NAME "vxd-dec"
+
+#ifdef ERROR_RECOVERY_SIMULATION
+/* This code should be execute only in debug flag */
+/*
+ * vxd decoder kernel object to create sysfs to debug error recovery and firmware
+ * watchdog timer. This kernel object will create a directory under /sys/kernel,
+ * containing two files fw_error_value and disable_fw_irq.
+ */
+struct kobject *vxd_dec_kobject;
+
+/* fw_error_value is the variable used to handle fw_error_attr */
+int fw_error_value = VDEC_ERROR_MAX;
+
+/* irq for the module, stored globally so can be accessed from sysfs */
+int g_module_irq;
+
+/*
+ * fw_error_attr. Application can set the value of this attribute, based on the
+ * firmware error that needs to be reproduced.
+ */
+struct kobj_attribute fw_error_attr =
+	__ATTR(fw_error_value, 0660, vxd_sysfs_show, vxd_sysfs_store);
+
+/* disable_fw_irq_value is variable to handle disable_fw_irq_attr */
+int disable_fw_irq_value;
+
+/*
+ * disable_fw_irq_attr. Application can set the value of this attribute. 1 to
+ * disable irq. 0 to enable irq.
+ */
+struct kobj_attribute disable_fw_irq_attr =
+	__ATTR(disable_fw_irq_value, 0660, vxd_sysfs_show, vxd_sysfs_store);
+
+/*
+ * Group attribute so that we can create and destroy all of them at once.
+ */
+struct attribute *attrs[] = {
+	&fw_error_attr.attr,
+	&disable_fw_irq_attr.attr,
+	NULL,         /* Terminate list of attributes with NULL */
+};
+
+/*
+ * An unnamed attribute group will put all of the attributes directly in
+ * the kobject directory.  If we specify a name, a sub directory will be
+ * created for the attributes with the directory being the name of the
+ * attribute group
+ */
+struct attribute_group attr_group = {
+	.attrs = attrs,
+};
+
+#endif
+
+static struct heap_config vxd_dec_heap_configs[] = {
+	{
+		.type = MEM_HEAP_TYPE_UNIFIED,
+		.options.unified = {
+			.gfp_type = __GFP_DMA32 | __GFP_ZERO,
+		},
+		.to_dev_addr = NULL,
+	},
+};
+
+static struct vxd_dec_fmt vxd_dec_formats[] = {
+	{
+		.fourcc = V4L2_PIX_FMT_NV12,
+		.num_planes = 1,
+		.type = IMG_DEC_FMT_TYPE_CAPTURE,
+		.std = VDEC_STD_UNDEFINED,
+		.pixfmt = IMG_PIXFMT_420PL12YUV8,
+		.interleave = PIXEL_UV_ORDER,
+		.idc = PIXEL_FORMAT_420,
+		.size_num = 3,
+		.size_den = 2,
+		.bytes_pp = 1,
+	},
+	{
+		.fourcc = V4L2_PIX_FMT_NV16,
+		.num_planes = 1,
+		.type = IMG_DEC_FMT_TYPE_CAPTURE,
+		.std = VDEC_STD_UNDEFINED,
+		.pixfmt = IMG_PIXFMT_422PL12YUV8,
+		.interleave = PIXEL_UV_ORDER,
+		.idc = PIXEL_FORMAT_422,
+		.size_num = 2,
+		.size_den = 1,
+		.bytes_pp = 1,
+	},
+	{
+		.fourcc = V4L2_PIX_FMT_TI1210,
+		.num_planes = 1,
+		.type = IMG_DEC_FMT_TYPE_CAPTURE,
+		.std = VDEC_STD_UNDEFINED,
+		.pixfmt = IMG_PIXFMT_420PL12YUV10_MSB,
+		.interleave = PIXEL_UV_ORDER,
+		.idc = PIXEL_FORMAT_420,
+		.size_num = 3,
+		.size_den = 2,
+		.bytes_pp = 2,
+	},
+	{
+		.fourcc = V4L2_PIX_FMT_TI1610,
+		.num_planes = 1,
+		.type = IMG_DEC_FMT_TYPE_CAPTURE,
+		.std = VDEC_STD_UNDEFINED,
+		.pixfmt = IMG_PIXFMT_422PL12YUV10_MSB,
+		.interleave = PIXEL_UV_ORDER,
+		.idc = PIXEL_FORMAT_422,
+		.size_num = 2,
+		.size_den = 1,
+		.bytes_pp = 2,
+	},
+	{
+		.fourcc = V4L2_PIX_FMT_H264,
+		.num_planes = 1,
+		.type = IMG_DEC_FMT_TYPE_OUTPUT,
+		.std = VDEC_STD_H264,
+		.pixfmt = IMG_PIXFMT_UNDEFINED,
+		.interleave = PIXEL_INVALID_CI,
+		.idc = PIXEL_FORMAT_INVALID,
+		.size_num = 1,
+		.size_den = 1,
+		.bytes_pp = 1,
+	},
+	{
+		.fourcc = V4L2_PIX_FMT_HEVC,
+		.num_planes = 1,
+		.type = IMG_DEC_FMT_TYPE_OUTPUT,
+		.std = VDEC_STD_HEVC,
+		.pixfmt = IMG_PIXFMT_UNDEFINED,
+		.interleave = PIXEL_INVALID_CI,
+		.idc = PIXEL_FORMAT_INVALID,
+		.size_num = 1,
+		.size_den = 1,
+		.bytes_pp = 1,
+	},
+	{
+		.fourcc = V4L2_PIX_FMT_MJPEG,
+		.num_planes = 1,
+		.type = IMG_DEC_FMT_TYPE_OUTPUT,
+		.std = VDEC_STD_JPEG,
+		.pixfmt = IMG_PIXFMT_UNDEFINED,
+		.interleave = PIXEL_INVALID_CI,
+		.idc = PIXEL_FORMAT_INVALID,
+		.size_num = 1,
+		.size_den = 1,
+		.bytes_pp = 1,
+	},
+	{
+		.fourcc = V4L2_PIX_FMT_YUV420M,
+		.num_planes = 3,
+		.type = IMG_DEC_FMT_TYPE_CAPTURE,
+		.std = VDEC_STD_UNDEFINED,
+		.pixfmt = 86031,
+		.interleave = PIXEL_UV_ORDER,
+		.idc = PIXEL_FORMAT_420,
+		.size_num = 2,
+		.size_den = 1,
+		.bytes_pp = 1,
+	},
+	{
+		.fourcc = V4L2_PIX_FMT_YUV422M,
+		.num_planes = 3,
+		.type = IMG_DEC_FMT_TYPE_CAPTURE,
+		.std = VDEC_STD_UNDEFINED,
+		.pixfmt = 81935,
+		.interleave = PIXEL_UV_ORDER,
+		.idc = PIXEL_FORMAT_422,
+		.size_num = 3,
+		.size_den = 1,
+		.bytes_pp = 1,
+	},
+};
+
+#ifdef ERROR_RECOVERY_SIMULATION
+ssize_t vxd_sysfs_show(struct kobject *vxd_dec_kobject,
+		       struct kobj_attribute *attr, char *buf)
+
+{
+	int var = 0;
+
+	if (strcmp(attr->attr.name, "fw_error_value") == 0)
+		var = fw_error_value;
+
+	else
+		var = disable_fw_irq_value;
+
+	return sprintf(buf, "%d\n", var);
+}
+
+ssize_t vxd_sysfs_store(struct kobject *vxd_dec_kobject,
+			struct kobj_attribute *attr,
+			const char *buf, unsigned long count)
+{
+	int var = 0, rv = 0;
+
+	rv = sscanf(buf, "%du", &var);
+
+	if (strcmp(attr->attr.name, "fw_error_value") == 0) {
+		fw_error_value = var;
+	} else {
+		disable_fw_irq_value = var;
+		/*
+		 * if disable_fw_irq_value is not zero, disable the irq to reproduce
+		 * firmware non responsiveness in vxd_worker.
+		 */
+		if (disable_fw_irq_value != 0) {
+			/* just ignore the irq */
+			disable_irq(g_module_irq);
+		}
+	}
+	return sprintf((char *)buf, "%d\n", var);
+}
+#endif
+
+static struct vxd_dec_ctx *file2ctx(struct file *file)
+{
+	return container_of(file->private_data, struct vxd_dec_ctx, fh);
+}
+
+static irqreturn_t soft_thread_irq(int irq, void *dev_id)
+{
+	struct platform_device *pdev = (struct platform_device *)dev_id;
+
+	if (!pdev)
+		return IRQ_NONE;
+
+	return vxd_handle_thread_irq(&pdev->dev);
+}
+
+static irqreturn_t hard_isrcb(int irq, void *dev_id)
+{
+	struct platform_device *pdev = (struct platform_device *)dev_id;
+
+	if (!pdev)
+		return IRQ_NONE;
+
+	return vxd_handle_irq(&pdev->dev);
+}
+
+static struct vxd_buffer *find_buffer(unsigned int buf_map_id, struct list_head *head)
+{
+	struct list_head *list;
+	struct vxd_buffer *buf = NULL;
+
+	list_for_each(list, head) {
+		buf = list_entry(list, struct vxd_buffer, list);
+		if (buf->buf_map_id == buf_map_id)
+			break;
+		buf = NULL;
+	}
+	return buf;
+}
+
+static void return_worker(void *work)
+{
+	struct vxd_dec_ctx *ctx;
+	struct vxd_return *res;
+	struct device *dev;
+	struct timespec64 time;
+	int loop;
+
+	work = get_work_buff(work, TRUE);
+
+	res = container_of(work, struct vxd_return, work);
+	ctx = res->ctx;
+	dev = ctx->dev->dev;
+	switch (res->type) {
+	case VXD_CB_PICT_DECODED:
+		v4l2_m2m_job_finish(ctx->dev->m2m_dev, ctx->fh.m2m_ctx);
+		ktime_get_real_ts64(&time);
+		for (loop = 0; loop < ARRAY_SIZE(ctx->dev->time_drv); loop++) {
+			if (ctx->dev->time_drv[loop].id == res->buf_map_id) {
+				ctx->dev->time_drv[loop].end_time =
+				timespec64_to_ns(&time);
+#ifdef DEBUG_DECODER_DRIVER
+				dev_info(dev, "picture buf decode time is %llu us for buf_map_id 0x%x\n",
+					 div_s64(ctx->dev->time_drv[loop].end_time -
+						 ctx->dev->time_drv[loop].start_time, 1000),
+					 res->buf_map_id);
+#endif
+				break;
+				}
+			}
+
+		if (loop == ARRAY_SIZE(ctx->dev->time_drv))
+			dev_err(dev, "picture buf decode for buf_map_id x%0x is not measured\n",
+				res->buf_map_id);
+		break;
+
+	default:
+		break;
+	}
+	kfree(res->work);
+	kfree(res);
+}
+
+static void vxd_error_recovery(struct vxd_dec_ctx *ctx)
+{
+	int ret = -1;
+
+	/*
+	 * In the previous frame decoding fatal error has been detected
+	 * so we need to reload the firmware to make it alive.
+	 */
+	pr_debug("Reloading the firmware because of previous error\n");
+	vxd_clean_fw_resources(ctx->dev);
+	ret = vxd_prepare_fw(ctx->dev);
+	if (ret)
+		pr_err("Reloading the firmware failed!!");
+}
+
+static struct vxd_dec_q_data *get_q_data(struct vxd_dec_ctx *ctx,
+					 enum v4l2_buf_type type)
+{
+	switch (type) {
+	case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+	case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+		return &ctx->q_data[Q_DATA_SRC];
+	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+	case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+		return &ctx->q_data[Q_DATA_DST];
+	default:
+		return NULL;
+	}
+	return NULL;
+}
+
+static void vxd_return_resource(void *ctx_handle, enum vxd_cb_type type,
+				unsigned int buf_map_id)
+{
+	struct vxd_return *res;
+	struct vxd_buffer *buf = NULL;
+	struct vb2_v4l2_buffer *vb;
+	struct vxd_dec_ctx *ctx = (struct vxd_dec_ctx *)ctx_handle;
+	struct v4l2_event event = {};
+	struct device *dev = ctx->dev->dev;
+	int i;
+	struct vxd_dec_q_data *q_data;
+
+	if (ctx->aborting) {
+		v4l2_m2m_job_finish(ctx->dev->m2m_dev, ctx->fh.m2m_ctx);
+		ctx->aborting = 0;
+		return;
+	}
+
+	switch (type) {
+	case VXD_CB_STRUNIT_PROCESSED:
+
+		buf = find_buffer(buf_map_id, &ctx->out_buffers);
+		if (!buf) {
+			dev_err(dev, "Could not locate buf_map_id=0x%x in OUTPUT buffers list\n",
+				buf_map_id);
+			break;
+		}
+		buf->buffer.vb.field = V4L2_FIELD_NONE;
+		q_data = get_q_data(ctx, buf->buffer.vb.vb2_buf.vb2_queue->type);
+		if (!q_data)
+			return;
+
+		for (i = 0; i < q_data->fmt->num_planes; i++)
+			vb2_set_plane_payload(&buf->buffer.vb.vb2_buf, i,
+					      ctx->pict_bufcfg.plane_size[i]);
+
+		v4l2_m2m_buf_done(&buf->buffer.vb, VB2_BUF_STATE_DONE);
+		break;
+	case VXD_CB_SPS_RELEASE:
+		break;
+	case VXD_CB_PPS_RELEASE:
+		break;
+	case VXD_CB_PICT_DECODED:
+		res = kzalloc(sizeof(*res), GFP_KERNEL);
+		if (!res)
+			return;
+		res->ctx = ctx;
+		res->type = type;
+		res->buf_map_id = buf_map_id;
+
+		init_work(&res->work, return_worker, HWA_DECODER);
+		if (!res->work)
+			return;
+
+		schedule_work(res->work);
+
+		break;
+	case VXD_CB_PICT_DISPLAY:
+		buf = find_buffer(buf_map_id, &ctx->cap_buffers);
+		if (!buf) {
+			dev_err(dev, "Could not locate buf_map_id=0x%x in CAPTURE buffers list\n",
+				buf_map_id);
+			break;
+		}
+		buf->reuse = FALSE;
+		buf->buffer.vb.field = V4L2_FIELD_NONE;
+		q_data = get_q_data(ctx, buf->buffer.vb.vb2_buf.vb2_queue->type);
+		if (!q_data)
+			return;
+
+		for (i = 0; i < q_data->fmt->num_planes; i++)
+			vb2_set_plane_payload(&buf->buffer.vb.vb2_buf, i,
+					      ctx->pict_bufcfg.plane_size[i]);
+
+		v4l2_m2m_buf_done(&buf->buffer.vb, VB2_BUF_STATE_DONE);
+		break;
+	case VXD_CB_PICT_RELEASE:
+		buf = find_buffer(buf_map_id, &ctx->reuse_queue);
+		if (buf) {
+			buf->reuse = TRUE;
+			list_move_tail(&buf->list, &ctx->cap_buffers);
+
+			v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, &buf->buffer.vb);
+			break;
+		}
+		buf = find_buffer(buf_map_id, &ctx->cap_buffers);
+		if (!buf) {
+			dev_err(dev, "Could not locate buf_map_id=0x%x in CAPTURE buffers list\n",
+				buf_map_id);
+
+			break;
+		}
+		buf->reuse = TRUE;
+
+		break;
+	case VXD_CB_PICT_END:
+		break;
+	case VXD_CB_STR_END:
+		event.type = V4L2_EVENT_EOS;
+		v4l2_event_queue_fh(&ctx->fh, &event);
+		if (v4l2_m2m_num_dst_bufs_ready(ctx->fh.m2m_ctx) > 0) {
+			vb = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
+			vb->flags |= V4L2_BUF_FLAG_LAST;
+
+			q_data = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
+			if (!q_data)
+				break;
+
+			for (i = 0; i < q_data->fmt->num_planes; i++)
+				vb2_set_plane_payload(&vb->vb2_buf, i, 0);
+
+			v4l2_m2m_buf_done(vb, VB2_BUF_STATE_DONE);
+		} else {
+			ctx->flag_last = TRUE;
+		}
+		break;
+	case VXD_CB_ERROR_FATAL:
+		/*
+		 * There has been FW error, so we need to reload the firmware.
+		 */
+		vxd_error_recovery(ctx);
+
+		/*
+		 * Just send zero size buffer to v4l2 application,
+		 * informing the error condition.
+		 */
+		if (v4l2_m2m_num_dst_bufs_ready(ctx->fh.m2m_ctx) > 0) {
+			vb = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
+
+			q_data = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
+			if (!q_data)
+				break;
+
+			for (i = 0; i < q_data->fmt->num_planes; i++)
+				vb2_set_plane_payload(&vb->vb2_buf, i, 0);
+
+			v4l2_m2m_buf_done(vb, VB2_BUF_STATE_DONE);
+		} else {
+			ctx->flag_last = TRUE;
+		}
+		break;
+	default:
+		break;
+	}
+}
+
+static int vxd_dec_submit_opconfig(struct vxd_dec_ctx *ctx)
+{
+	int ret = 0;
+
+	if (ctx->stream_created) {
+		ret = core_stream_set_output_config(ctx->res_str_id,
+						    &ctx->str_opcfg,
+						    &ctx->pict_bufcfg);
+		if (ret) {
+			dev_err(ctx->dev->dev, "core_stream_set_output_config failed\n");
+			ctx->opconfig_pending = TRUE;
+			return ret;
+		}
+		ctx->opconfig_pending = FALSE;
+		ctx->stream_configured = TRUE;
+	} else {
+		ctx->opconfig_pending = TRUE;
+	}
+	return ret;
+}
+
+static int vxd_dec_queue_setup(struct vb2_queue *vq,
+			       unsigned int *nbuffers,
+			       unsigned int *nplanes,
+			       unsigned int sizes[],
+			       struct device *alloc_devs[])
+{
+	struct vxd_dec_ctx *ctx = vb2_get_drv_priv(vq);
+	struct vxd_dec_q_data *q_data;
+	struct vxd_dec_q_data *src_q_data;
+	int i;
+	unsigned int hw_nbuffers = 0;
+
+	q_data = get_q_data(ctx, vq->type);
+	if (!q_data)
+		return -EINVAL;
+
+	if (*nplanes) {
+		/* This is being called from CREATEBUFS, perform validation */
+		if (*nplanes != q_data->fmt->num_planes)
+			return -EINVAL;
+
+		for (i = 0; i < *nplanes; i++) {
+			if (sizes[i] != q_data->size_image[i])
+				return -EINVAL;
+		}
+
+		return 0;
+	}
+
+	*nplanes = q_data->fmt->num_planes;
+
+	if (!V4L2_TYPE_IS_OUTPUT(vq->type)) {
+		src_q_data = &ctx->q_data[Q_DATA_SRC];
+		if (src_q_data)
+			hw_nbuffers = get_nbuffers(src_q_data->fmt->std,
+						   q_data->width,
+						   q_data->height,
+						   ctx->max_num_ref_frames);
+	}
+
+	*nbuffers = max(*nbuffers, hw_nbuffers);
+
+	for (i = 0; i < *nplanes; i++)
+		sizes[i] = q_data->size_image[i];
+
+	return 0;
+}
+
+static int vxd_dec_buf_prepare(struct vb2_buffer *vb)
+{
+	struct vxd_dec_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
+	struct device *dev = ctx->dev->dev;
+	struct vxd_dec_q_data *q_data;
+	void *sgt;
+#ifdef CAPTURE_CONTIG_ALLOC
+	struct page *new_page;
+#else
+	void *sgl;
+#endif
+	struct sg_table *sgt_new;
+	void *sgl_new;
+	int pages;
+	int nents = 0;
+	int size = 0;
+	int plane, num_planes, ret = 0;
+	struct vxd_buffer *buf =
+		container_of(vb, struct vxd_buffer, buffer.vb.vb2_buf);
+
+	q_data = get_q_data(ctx, vb->vb2_queue->type);
+	if (!q_data)
+		return -EINVAL;
+
+	num_planes = q_data->fmt->num_planes;
+
+	for (plane = 0; plane < num_planes; plane++) {
+		if (vb2_plane_size(vb, plane) < q_data->size_image[plane]) {
+			dev_err(dev, "data will not fit into plane (%lu < %lu)\n",
+				vb2_plane_size(vb, plane),
+				(long)q_data->size_image[plane]);
+			return -EINVAL;
+		}
+	}
+
+	if (buf->mapped)
+		return 0;
+
+	buf->buf_info.cpu_linear_addr = vb2_plane_vaddr(vb, 0);
+	buf->buf_info.buf_size = vb2_plane_size(vb, 0);
+	buf->buf_info.fd = -1;
+	sgt = vb2_dma_sg_plane_desc(vb, 0);
+	if (!sgt) {
+		dev_err(dev, "Could not get sg_table from plane 0\n");
+		return -EINVAL;
+	}
+
+	if (V4L2_TYPE_IS_OUTPUT(vb->type)) {
+		ret = core_stream_map_buf_sg(ctx->res_str_id,
+					     VDEC_BUFTYPE_BITSTREAM,
+					     &buf->buf_info, sgt,
+					     &buf->buf_map_id);
+		if (ret) {
+			dev_err(dev, "OUTPUT core_stream_map_buf_sg failed\n");
+			return ret;
+		}
+
+		buf->bstr_info.buf_size = q_data->size_image[0];
+		buf->bstr_info.cpu_virt_addr = buf->buf_info.cpu_linear_addr;
+		buf->bstr_info.mem_attrib =
+			SYS_MEMATTRIB_UNCACHED | SYS_MEMATTRIB_WRITECOMBINE |
+			SYS_MEMATTRIB_INPUT | SYS_MEMATTRIB_CPU_WRITE;
+		buf->bstr_info.bufmap_id = buf->buf_map_id;
+		lst_init(&buf->seq_unit.bstr_seg_list);
+		lst_init(&buf->pic_unit.bstr_seg_list);
+		lst_init(&buf->end_unit.bstr_seg_list);
+
+		list_add_tail(&buf->list, &ctx->out_buffers);
+	} else {
+		/* Create a single sgt from the plane(s) */
+		sgt_new = kmalloc(sizeof(*sgt_new), GFP_KERNEL);
+		if (!sgt_new)
+			return -EINVAL;
+
+		for (plane = 0; plane < num_planes; plane++) {
+			size += ALIGN(vb2_plane_size(vb, plane), PAGE_SIZE);
+			sgt = vb2_dma_sg_plane_desc(vb, plane);
+			if (!sgt) {
+				dev_err(dev, "Could not get sg_table from plane %d\n", plane);
+				kfree(sgt_new);
+				return -EINVAL;
+			}
+#ifdef CAPTURE_CONTIG_ALLOC
+			nents += 1;
+#else
+			nents += sg_nents(img_mmu_get_sgl(sgt));
+#endif
+		}
+		buf->buf_info.buf_size = size;
+
+		pages = (size + PAGE_SIZE - 1) / PAGE_SIZE;
+		ret = sg_alloc_table(sgt_new, nents, GFP_KERNEL);
+		if (ret) {
+			kfree(sgt_new);
+			return -EINVAL;
+		}
+		sgl_new = img_mmu_get_sgl(sgt_new);
+
+		for (plane = 0; plane < num_planes; plane++) {
+			sgt = vb2_dma_sg_plane_desc(vb, plane);
+			if (!sgt) {
+				dev_err(dev, "Could not get sg_table from plane %d\n", plane);
+				sg_free_table(sgt_new);
+				kfree(sgt_new);
+				return -EINVAL;
+			}
+#ifdef CAPTURE_CONTIG_ALLOC
+			new_page = phys_to_page(vb2_dma_contig_plane_dma_addr(vb, plane));
+			sg_set_page(sgl_new, new_page, ALIGN(vb2_plane_size(vb, plane),
+							     PAGE_SIZE), 0);
+			sgl_new = sg_next(sgl_new);
+#else
+			sgl = img_mmu_get_sgl(sgt);
+
+			while (sgl) {
+				sg_set_page(sgl_new, sg_page(sgl), img_mmu_get_sgl_length(sgl), 0);
+				sgl = sg_next(sgl);
+				sgl_new = sg_next(sgl_new);
+			}
+#endif
+		}
+
+		buf->buf_info.pictbuf_cfg = ctx->pict_bufcfg;
+		ret = core_stream_map_buf_sg(ctx->res_str_id,
+					     VDEC_BUFTYPE_PICTURE,
+					     &buf->buf_info, sgt_new,
+					     &buf->buf_map_id);
+		sg_free_table(sgt_new);
+		kfree(sgt_new);
+		if (ret) {
+			dev_err(dev, "CAPTURE core_stream_map_buf_sg failed\n");
+			return ret;
+		}
+		list_add_tail(&buf->list, &ctx->cap_buffers);
+	}
+	buf->mapped = TRUE;
+	buf->reuse = TRUE;
+
+	return 0;
+}
+
+static void vxd_dec_buf_queue(struct vb2_buffer *vb)
+{
+	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+	struct vxd_dec_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
+	struct vxd_buffer *buf =
+		container_of(vb, struct vxd_buffer, buffer.vb.vb2_buf);
+	struct vxd_dec_q_data *q_data;
+	int i;
+
+	if (V4L2_TYPE_IS_OUTPUT(vb->type)) {
+		v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf);
+	} else {
+		mutex_lock_nested(ctx->mutex, SUBCLASS_VXD_V4L2);
+		if (buf->reuse) {
+			mutex_unlock(ctx->mutex);
+			if (ctx->flag_last) {
+				q_data = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
+				vbuf->flags |= V4L2_BUF_FLAG_LAST;
+
+				for (i = 0; i < q_data->fmt->num_planes; i++)
+					vb2_set_plane_payload(&vbuf->vb2_buf, i, 0);
+
+				v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_DONE);
+			} else {
+				v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf);
+			}
+		} else {
+			list_move_tail(&buf->list, &ctx->reuse_queue);
+			mutex_unlock(ctx->mutex);
+		}
+	}
+}
+
+static void vxd_dec_return_all_buffers(struct vxd_dec_ctx *ctx,
+				       struct vb2_queue *q,
+				       enum vb2_buffer_state state)
+{
+	struct vb2_v4l2_buffer *vb;
+	unsigned long flags;
+
+	for (;;) {
+		if (V4L2_TYPE_IS_OUTPUT(q->type))
+			vb = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
+		else
+			vb = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
+
+		if (!vb)
+			break;
+
+		spin_lock_irqsave(ctx->dev->lock, flags);
+		v4l2_m2m_buf_done(vb, state);
+		spin_unlock_irqrestore(ctx->dev->lock, (unsigned long)flags);
+	}
+}
+
+static int vxd_dec_start_streaming(struct vb2_queue *vq, unsigned int count)
+{
+	int ret = 0;
+	struct vxd_dec_ctx *ctx = vb2_get_drv_priv(vq);
+
+	if (V4L2_TYPE_IS_OUTPUT(vq->type))
+		ctx->dst_streaming = TRUE;
+	else
+		ctx->src_streaming = TRUE;
+
+	if (ctx->dst_streaming && ctx->src_streaming && !ctx->core_streaming) {
+		if (!ctx->stream_configured) {
+			vxd_dec_return_all_buffers(ctx, vq, VB2_BUF_STATE_ERROR);
+			return -EINVAL;
+		}
+		ctx->eos = FALSE;
+		ctx->stop_initiated = FALSE;
+		ctx->flag_last = FALSE;
+		ret = core_stream_play(ctx->res_str_id);
+		if (ret) {
+			vxd_dec_return_all_buffers(ctx, vq, VB2_BUF_STATE_ERROR);
+			return ret;
+		}
+		ctx->core_streaming = TRUE;
+	}
+
+	return 0;
+}
+
+static void vxd_dec_stop_streaming(struct vb2_queue *vq)
+{
+	struct vxd_dec_ctx *ctx = vb2_get_drv_priv(vq);
+	struct list_head *list;
+	struct list_head *temp;
+	struct vxd_buffer *buf = NULL;
+
+	if (V4L2_TYPE_IS_OUTPUT(vq->type))
+		ctx->dst_streaming = FALSE;
+	else
+		ctx->src_streaming = FALSE;
+
+	if (ctx->core_streaming) {
+		core_stream_stop(ctx->res_str_id);
+		ctx->core_streaming = FALSE;
+
+		core_stream_flush(ctx->res_str_id, TRUE);
+	}
+
+	/* unmap all the output and capture plane buffers */
+	if (V4L2_TYPE_IS_OUTPUT(vq->type)) {
+		list_for_each(list, &ctx->out_buffers) {
+			buf = list_entry(list, struct vxd_buffer, list);
+			core_stream_unmap_buf_sg(buf->buf_map_id);
+			buf->mapped = FALSE;
+			__list_del_entry(&buf->list);
+		}
+	} else {
+		list_for_each_safe(list, temp, &ctx->reuse_queue) {
+			buf = list_entry(list, struct vxd_buffer, list);
+			list_move_tail(&buf->list, &ctx->cap_buffers);
+			v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, &buf->buffer.vb);
+		}
+
+		list_for_each(list, &ctx->cap_buffers) {
+			buf = list_entry(list, struct vxd_buffer, list);
+			core_stream_unmap_buf_sg(buf->buf_map_id);
+			buf->mapped = FALSE;
+			__list_del_entry(&buf->list);
+		}
+	}
+
+	vxd_dec_return_all_buffers(ctx, vq, VB2_BUF_STATE_ERROR);
+}
+
+static struct vb2_ops vxd_dec_video_ops = {
+	.queue_setup = vxd_dec_queue_setup,
+	.buf_prepare = vxd_dec_buf_prepare,
+	.buf_queue = vxd_dec_buf_queue,
+	.wait_prepare = vb2_ops_wait_prepare,
+	.wait_finish = vb2_ops_wait_finish,
+	.start_streaming = vxd_dec_start_streaming,
+	.stop_streaming = vxd_dec_stop_streaming,
+};
+
+static int queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq)
+{
+	struct vxd_dec_ctx *ctx = priv;
+	struct vxd_dev *vxd = ctx->dev;
+	int ret = 0;
+
+	/* src_vq */
+	memset(src_vq, 0, sizeof(*src_vq));
+	src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
+	src_vq->io_modes = VB2_MMAP | VB2_DMABUF;
+	src_vq->drv_priv = ctx;
+	src_vq->buf_struct_size = sizeof(struct vxd_buffer);
+	src_vq->ops = &vxd_dec_video_ops;
+	src_vq->mem_ops = &vb2_dma_sg_memops;
+	src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+	src_vq->lock = vxd->mutex;
+	src_vq->dev = vxd->v4l2_dev.dev;
+	ret = vb2_queue_init(src_vq);
+	if (ret)
+		return ret;
+
+	/* dst_vq */
+	memset(dst_vq, 0, sizeof(*dst_vq));
+	dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+	dst_vq->io_modes = VB2_MMAP | VB2_DMABUF;
+	dst_vq->drv_priv = ctx;
+	dst_vq->buf_struct_size = sizeof(struct vxd_buffer);
+	dst_vq->ops = &vxd_dec_video_ops;
+#ifdef CAPTURE_CONTIG_ALLOC
+	dst_vq->mem_ops = &vb2_dma_contig_memops;
+#else
+	dst_vq->mem_ops = &vb2_dma_sg_memops;
+#endif
+	dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+	dst_vq->lock = vxd->mutex;
+	dst_vq->dev = vxd->v4l2_dev.dev;
+	ret = vb2_queue_init(dst_vq);
+	if (ret) {
+		vb2_queue_release(src_vq);
+		return ret;
+	}
+
+	return ret;
+}
+
+static int vxd_dec_open(struct file *file)
+{
+	struct vxd_dev *vxd = video_drvdata(file);
+	struct vxd_dec_ctx *ctx;
+	struct vxd_dec_q_data *s_q_data;
+	int i, ret = 0;
+
+	dev_dbg(vxd->dev, "%s:%d vxd %p\n", __func__, __LINE__, vxd);
+
+	if (vxd->no_fw) {
+		dev_err(vxd->dev, "Error!! fw binary is not present");
+		return -1;
+	}
+
+	mutex_lock_nested(vxd->mutex, SUBCLASS_BASE);
+
+	ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
+	if (!ctx) {
+		mutex_unlock(vxd->mutex);
+		return -ENOMEM;
+	}
+	ctx->dev = vxd;
+
+	v4l2_fh_init(&ctx->fh, video_devdata(file));
+	file->private_data = &ctx->fh;
+
+	s_q_data = &ctx->q_data[Q_DATA_SRC];
+	s_q_data->fmt = &vxd_dec_formats[0];
+	s_q_data->width = 1920;
+	s_q_data->height = 1080;
+	for (i = 0; i < s_q_data->fmt->num_planes; i++) {
+		s_q_data->bytesperline[i] = s_q_data->width;
+		s_q_data->size_image[i] = s_q_data->bytesperline[i] * s_q_data->height;
+	}
+
+	ctx->q_data[Q_DATA_DST] = *s_q_data;
+
+	ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(vxd->m2m_dev, ctx, &queue_init);
+	if (IS_ERR_VALUE((unsigned long)ctx->fh.m2m_ctx)) {
+		ret = (long)(ctx->fh.m2m_ctx);
+		goto exit;
+	}
+
+	v4l2_fh_add(&ctx->fh);
+
+	ret = idr_alloc_cyclic(vxd->streams, &ctx->stream, VXD_MIN_STREAM_ID, VXD_MAX_STREAM_ID,
+			       GFP_KERNEL);
+	if (ret < VXD_MIN_STREAM_ID || ret > VXD_MAX_STREAM_ID) {
+		dev_err(vxd->dev, "%s: stream id creation failed!\n",
+			__func__);
+		ret = -EFAULT;
+		goto exit;
+	}
+
+	ctx->stream.id = ret;
+	ctx->stream.ctx = ctx;
+
+	ctx->stream_created = FALSE;
+	ctx->stream_configured = FALSE;
+	ctx->src_streaming = FALSE;
+	ctx->dst_streaming = FALSE;
+	ctx->core_streaming = FALSE;
+	ctx->eos = FALSE;
+	ctx->stop_initiated = FALSE;
+	ctx->flag_last = FALSE;
+
+	lst_init(&ctx->seg_list);
+	for (i = 0; i < MAX_SEGMENTS; i++)
+		lst_add(&ctx->seg_list, &ctx->bstr_segments[i]);
+
+	if (vxd_create_ctx(vxd, ctx))
+		goto out_idr_remove;
+
+	ctx->stream.mmu_ctx = ctx->mmu_ctx;
+	ctx->stream.ptd = ctx->ptd;
+
+	ctx->mutex = kzalloc(sizeof(*ctx->mutex), GFP_KERNEL);
+	if (!ctx->mutex) {
+		ret = -ENOMEM;
+		goto out_idr_remove;
+	}
+	mutex_init(ctx->mutex);
+
+	INIT_LIST_HEAD(&ctx->items_done);
+	INIT_LIST_HEAD(&ctx->reuse_queue);
+	INIT_LIST_HEAD(&ctx->return_queue);
+	INIT_LIST_HEAD(&ctx->out_buffers);
+	INIT_LIST_HEAD(&ctx->cap_buffers);
+
+	mutex_unlock(vxd->mutex);
+
+	return 0;
+
+out_idr_remove:
+	idr_remove(vxd->streams, ctx->stream.id);
+
+exit:
+	v4l2_fh_exit(&ctx->fh);
+	get_work_buff(ctx->work, TRUE);
+	kfree(ctx->work);
+	kfree(ctx);
+	mutex_unlock(vxd->mutex);
+	return ret;
+}
+
+static int vxd_dec_release(struct file *file)
+{
+	struct vxd_dev *vxd = video_drvdata(file);
+	struct vxd_dec_ctx *ctx = file2ctx(file);
+	struct bspp_ddbuf_array_info *fw_sequ = ctx->fw_sequ;
+	struct bspp_ddbuf_array_info *fw_pps = ctx->fw_pps;
+	int i, ret = 0;
+	struct vxd_dec_q_data *s_q_data;
+
+	s_q_data = &ctx->q_data[Q_DATA_SRC];
+
+	if (ctx->stream_created) {
+		bspp_stream_destroy(ctx->bspp_context);
+
+		for (i = 0; i < MAX_SEQUENCES; i++) {
+			core_stream_unmap_buf(fw_sequ[i].ddbuf_info.bufmap_id);
+			img_mem_free(ctx->mem_ctx, fw_sequ[i].ddbuf_info.buf_id);
+		}
+
+		if (s_q_data->fmt->std != VDEC_STD_JPEG) {
+			for (i = 0; i < MAX_PPSS; i++) {
+				core_stream_unmap_buf(fw_pps[i].ddbuf_info.bufmap_id);
+				img_mem_free(ctx->mem_ctx, fw_pps[i].ddbuf_info.buf_id);
+			}
+		}
+		core_stream_destroy(ctx->res_str_id);
+		ctx->stream_created = FALSE;
+	}
+
+	mutex_lock_nested(vxd->mutex, SUBCLASS_BASE);
+
+	vxd_destroy_ctx(vxd, ctx);
+
+	idr_remove(vxd->streams, ctx->stream.id);
+
+	v4l2_fh_del(&ctx->fh);
+
+	v4l2_fh_exit(&ctx->fh);
+
+	v4l2_m2m_ctx_release(ctx->fh.m2m_ctx);
+
+	mutex_destroy(ctx->mutex);
+	kfree(ctx->mutex);
+	ctx->mutex = NULL;
+
+	get_work_buff(ctx->work, TRUE);
+	kfree(ctx->work);
+	kfree(ctx);
+
+	mutex_unlock(vxd->mutex);
+
+	return ret;
+}
+
+static int vxd_dec_querycap(struct file *file, void *priv, struct v4l2_capability *cap)
+{
+	strncpy(cap->driver, IMG_VXD_DEC_MODULE_NAME, sizeof(cap->driver) - 1);
+	strncpy(cap->card, IMG_VXD_DEC_MODULE_NAME, sizeof(cap->card) - 1);
+	snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s", IMG_VXD_DEC_MODULE_NAME);
+	cap->device_caps = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING;
+	cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
+	return 0;
+}
+
+static int __enum_fmt(struct v4l2_fmtdesc *f, unsigned int type)
+{
+	int i, index;
+	struct vxd_dec_fmt *fmt = NULL;
+
+	index = 0;
+	for (i = 0; i < ARRAY_SIZE(vxd_dec_formats); ++i) {
+		if (vxd_dec_formats[i].type & type) {
+			if (index == f->index) {
+				fmt = &vxd_dec_formats[i];
+				break;
+			}
+			index++;
+		}
+	}
+
+	if (!fmt)
+		return -EINVAL;
+
+	f->pixelformat = fmt->fourcc;
+	return 0;
+}
+
+static int vxd_dec_enum_fmt(struct file *file, void *priv, struct v4l2_fmtdesc *f)
+{
+	if (V4L2_TYPE_IS_OUTPUT(f->type))
+		return __enum_fmt(f, IMG_DEC_FMT_TYPE_OUTPUT);
+
+	return __enum_fmt(f, IMG_DEC_FMT_TYPE_CAPTURE);
+}
+
+static struct vxd_dec_fmt *find_format(struct v4l2_format *f, unsigned int type)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(vxd_dec_formats); ++i) {
+		if (vxd_dec_formats[i].fourcc == f->fmt.pix_mp.pixelformat &&
+		    vxd_dec_formats[i].type == type)
+			return &vxd_dec_formats[i];
+	}
+	return NULL;
+}
+
+static unsigned int get_sizeimage(int w, int h, struct vxd_dec_fmt *fmt, int plane)
+{
+	switch (fmt->fourcc) {
+	case V4L2_PIX_FMT_YUV420M:
+		return ((plane == 0) ? (w * h) : (w * h / 2));
+	case V4L2_PIX_FMT_YUV422M:
+		return (w * h);
+	default:
+		return (w * h * fmt->size_num / fmt->size_den);
+	}
+
+	return 0;
+}
+
+static unsigned int get_stride(int w, struct vxd_dec_fmt *fmt)
+{
+	return (ALIGN(w, HW_ALIGN) * fmt->bytes_pp);
+}
+
+/*
+ * @ Function vxd_get_header_info
+ * Run bspp stream submit and preparse once before device_run
+ * To retrieve header information
+ */
+static int vxd_get_header_info(void *priv)
+{
+	struct vxd_dec_ctx *ctx = priv;
+	struct vxd_dev *vxd_dev = ctx->dev;
+	struct device *dev = vxd_dev->v4l2_dev.dev;
+	struct vb2_v4l2_buffer  *src_vb;
+	struct vxd_buffer *src_vxdb;
+	struct vxd_buffer *dst_vxdb;
+	struct bspp_preparsed_data *preparsed_data;
+	unsigned int data_size;
+	int ret;
+
+	/*
+	 * Checking for queued buffer.
+	 * If no next buffer present, do not get information from header.
+	 * Else, get header information and store for later use.
+	 */
+	src_vb =  v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx);
+	if (!src_vb) {
+		dev_warn(dev, "get_header_info Next src buffer is null\n");
+		return IMG_ERROR_INVALID_PARAMETERS;
+	}
+	mutex_lock_nested(ctx->mutex, SUBCLASS_VXD_V4L2);
+
+	src_vxdb = container_of(src_vb, struct vxd_buffer, buffer.vb);
+	/* Setting dst_vxdb to arbitrary value (using src_vb) for now */
+	dst_vxdb = container_of(src_vb, struct vxd_buffer, buffer.vb);
+
+	preparsed_data = &dst_vxdb->preparsed_data;
+
+	data_size = vb2_get_plane_payload(&src_vxdb->buffer.vb.vb2_buf, 0);
+
+	ret = bspp_stream_submit_buffer(ctx->bspp_context,
+					&src_vxdb->bstr_info,
+					src_vxdb->buf_map_id,
+					data_size, NULL,
+					VDEC_BSTRELEMENT_UNSPECIFIED);
+	if (ret) {
+		dev_err(dev, "get_header_info bspp_stream_submit_buffer failed %d\n", ret);
+		return ret;
+	}
+	mutex_unlock(ctx->mutex);
+
+	ret = bspp_stream_preparse_buffers(ctx->bspp_context, NULL, 0,
+					   &ctx->seg_list,
+					   preparsed_data, ctx->eos);
+	if (ret) {
+		dev_err(dev, "get_header_info bspp_stream_preparse_buffers failed %d\n", ret);
+		return ret;
+	}
+
+	if (preparsed_data->sequ_hdr_info.com_sequ_hdr_info.max_frame_size.height &&
+	    preparsed_data->sequ_hdr_info.com_sequ_hdr_info.max_ref_frame_num) {
+		ctx->height = preparsed_data->sequ_hdr_info.com_sequ_hdr_info.max_frame_size.height;
+		ctx->max_num_ref_frames =
+			preparsed_data->sequ_hdr_info.com_sequ_hdr_info.max_ref_frame_num;
+	} else {
+		dev_err(dev, "get_header_info preparsed data is null %d\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int vxd_dec_g_fmt(struct file *file, void *priv, struct v4l2_format *f)
+{
+	struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp;
+	struct vxd_dec_ctx *ctx = file2ctx(file);
+	struct vxd_dec_q_data *q_data;
+	struct vxd_dev *vxd_dev = ctx->dev;
+	unsigned int i = 0;
+	int ret = 0;
+
+	q_data = get_q_data(ctx, f->type);
+	if (!q_data)
+		return -EINVAL;
+
+	pix_mp->field = V4L2_FIELD_NONE;
+	pix_mp->pixelformat = q_data->fmt->fourcc;
+	pix_mp->num_planes = q_data->fmt->num_planes;
+
+	if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+		/* The buffer contains compressed image. */
+		pix_mp->width = ctx->width;
+		pix_mp->height = ctx->height;
+		pix_mp->plane_fmt[0].bytesperline = 0;
+		pix_mp->plane_fmt[0].sizeimage = q_data->size_image[0];
+	} else if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+		/* The buffer contains decoded YUV image. */
+		pix_mp->width = ctx->width;
+		pix_mp->height = ctx->height;
+		for (i = 0; i < q_data->fmt->num_planes; i++) {
+			pix_mp->plane_fmt[i].bytesperline = get_stride(pix_mp->width, q_data->fmt);
+			pix_mp->plane_fmt[i].sizeimage = get_sizeimage
+							(pix_mp->plane_fmt[i].bytesperline,
+							 ctx->height, q_data->fmt, i);
+		}
+	} else {
+		dev_err(vxd_dev->v4l2_dev.dev, "Wrong V4L2_format type\n");
+		return -EINVAL;
+	}
+
+	return ret;
+}
+
+static int vxd_dec_try_fmt(struct file *file, void *priv, struct v4l2_format *f)
+{
+	struct vxd_dec_ctx *ctx = file2ctx(file);
+	struct vxd_dev *vxd_dev = ctx->dev;
+	struct vxd_dec_fmt *fmt;
+	struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp;
+	struct v4l2_plane_pix_format *plane_fmt = pix_mp->plane_fmt;
+	unsigned int i = 0;
+	int ret = 0;
+
+	if (V4L2_TYPE_IS_OUTPUT(f->type)) {
+		fmt = find_format(f, IMG_DEC_FMT_TYPE_OUTPUT);
+		if (!fmt) {
+			dev_err(vxd_dev->v4l2_dev.dev, "Unsupported format for source.\n");
+			return -EINVAL;
+		}
+		/*
+		 * Allocation for worst case input frame size:
+		 * I frame with full YUV size (YUV422)
+		 */
+		plane_fmt[0].sizeimage = ALIGN(pix_mp->width, HW_ALIGN) *
+			ALIGN(pix_mp->height, HW_ALIGN) * 2;
+	} else {
+		fmt = find_format(f, IMG_DEC_FMT_TYPE_CAPTURE);
+		if (!fmt) {
+			dev_err(vxd_dev->v4l2_dev.dev, "Unsupported format for dest.\n");
+			return -EINVAL;
+		}
+		for (i = 0; i < fmt->num_planes; i++) {
+			plane_fmt[i].bytesperline = get_stride(pix_mp->width, fmt);
+			plane_fmt[i].sizeimage = get_sizeimage(plane_fmt[i].bytesperline,
+							       pix_mp->height, fmt, i);
+		}
+		pix_mp->num_planes = fmt->num_planes;
+		pix_mp->flags = 0;
+	}
+
+	if (pix_mp->field == V4L2_FIELD_ANY)
+		pix_mp->field = V4L2_FIELD_NONE;
+
+	return ret;
+}
+
+static int vxd_dec_s_fmt(struct file *file, void *priv, struct v4l2_format *f)
+{
+	struct v4l2_pix_format_mplane *pix_mp;
+	struct vxd_dec_ctx *ctx = file2ctx(file);
+	struct vxd_dev *vxd_dev = ctx->dev;
+	struct device *dev = vxd_dev->v4l2_dev.dev;
+	struct vxd_dec_q_data *q_data;
+	struct vb2_queue *vq;
+	struct vdec_str_configdata strcfgdata;
+	int ret = 0;
+	unsigned char i = 0, j = 0;
+
+	pix_mp = &f->fmt.pix_mp;
+
+	if (!V4L2_TYPE_IS_OUTPUT(f->type)) {
+		int res = vxd_get_header_info(ctx);
+
+		if (res == 0)
+			pix_mp->height = ctx->height;
+	}
+
+	ret = vxd_dec_try_fmt(file, priv, f);
+	if (ret)
+		return ret;
+
+	vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type);
+	if (!vq)
+		return -EINVAL;
+
+	if (vb2_is_busy(vq)) {
+		dev_err(dev, "Queue is busy\n");
+		return -EBUSY;
+	}
+
+	q_data = get_q_data(ctx, f->type);
+
+	if (!q_data)
+		return -EINVAL;
+
+	/*
+	 * saving the original dimensions to pass to gstreamer (to remove the green
+	 * padding on kmsink)
+	 */
+	ctx->width_orig = pix_mp->width;
+	ctx->height_orig = pix_mp->height;
+
+	ctx->width = pix_mp->width;
+	ctx->height = pix_mp->height;
+
+	q_data->width = pix_mp->width;
+	q_data->height = pix_mp->height;
+
+	if (V4L2_TYPE_IS_OUTPUT(f->type)) {
+		q_data->fmt = find_format(f, IMG_DEC_FMT_TYPE_OUTPUT);
+		q_data->size_image[0] = pix_mp->plane_fmt[0].sizeimage;
+
+		if (!ctx->stream_created) {
+			strcfgdata.vid_std = q_data->fmt->std;
+
+			if (strcfgdata.vid_std == VDEC_STD_UNDEFINED) {
+				dev_err(dev, "Invalid input format\n");
+				return -EINVAL;
+			}
+			strcfgdata.bstr_format = VDEC_BSTRFORMAT_ELEMENTARY;
+			strcfgdata.user_str_id = ctx->stream.id;
+			strcfgdata.update_yuv = FALSE;
+			strcfgdata.bandwidth_efficient = FALSE;
+			strcfgdata.disable_mvc = FALSE;
+			strcfgdata.full_scan = FALSE;
+			strcfgdata.immediate_decode = TRUE;
+			strcfgdata.intra_frame_closed_gop = TRUE;
+
+			ret = core_stream_create(ctx, &strcfgdata, &ctx->res_str_id);
+			if (ret) {
+				dev_err(dev, "Core stream create failed\n");
+				return -EINVAL;
+			}
+			ctx->stream_created = TRUE;
+			if (ctx->opconfig_pending) {
+				ret = vxd_dec_submit_opconfig(ctx);
+				if (ret) {
+					dev_err(dev, "Output config failed\n");
+					return -EINVAL;
+				}
+			}
+
+			vxd_dec_alloc_bspp_resource(ctx, strcfgdata.vid_std);
+			ret = bspp_stream_create(&strcfgdata,
+						 &ctx->bspp_context,
+						 ctx->fw_sequ,
+						 ctx->fw_pps);
+			if (ret) {
+				dev_err(dev, "BSPP stream create failed %d\n", ret);
+				return ret;
+			}
+		} else if (q_data->fmt !=
+			find_format(f, IMG_DEC_FMT_TYPE_OUTPUT)) {
+			dev_err(dev, "Input format already set\n");
+			return -EBUSY;
+		}
+	} else {
+		q_data->fmt = find_format(f, IMG_DEC_FMT_TYPE_CAPTURE);
+		for (i = 0; i < q_data->fmt->num_planes; i++) {
+			q_data->size_image[i] =
+				get_sizeimage(get_stride(pix_mp->width, q_data->fmt),
+					      ctx->height, q_data->fmt, i);
+		}
+
+		ctx->str_opcfg.pixel_info.pixfmt = q_data->fmt->pixfmt;
+		ctx->str_opcfg.pixel_info.chroma_interleave = q_data->fmt->interleave;
+		ctx->str_opcfg.pixel_info.chroma_fmt = TRUE;
+		ctx->str_opcfg.pixel_info.chroma_fmt_idc = q_data->fmt->idc;
+
+		if (q_data->fmt->pixfmt == IMG_PIXFMT_420PL12YUV10_MSB ||
+		    q_data->fmt->pixfmt == IMG_PIXFMT_422PL12YUV10_MSB) {
+			ctx->str_opcfg.pixel_info.mem_pkg = PIXEL_BIT10_MSB_MP;
+			ctx->str_opcfg.pixel_info.bitdepth_y = 10;
+			ctx->str_opcfg.pixel_info.bitdepth_c = 10;
+		} else {
+			ctx->str_opcfg.pixel_info.mem_pkg = PIXEL_BIT8_MP;
+			ctx->str_opcfg.pixel_info.bitdepth_y = 8;
+			ctx->str_opcfg.pixel_info.bitdepth_c = 8;
+		}
+
+		ctx->str_opcfg.force_oold = FALSE;
+
+		ctx->pict_bufcfg.coded_width = pix_mp->width;
+		ctx->pict_bufcfg.coded_height = pix_mp->height;
+		ctx->pict_bufcfg.pixel_fmt = q_data->fmt->pixfmt;
+		for (i = 0; i < pix_mp->num_planes; i++) {
+			q_data->bytesperline[i] = get_stride(q_data->width, q_data->fmt);
+			if (q_data->bytesperline[i] <
+				pix_mp->plane_fmt[0].bytesperline)
+				q_data->bytesperline[i] =
+					ALIGN(pix_mp->plane_fmt[0].bytesperline, HW_ALIGN);
+			pix_mp->plane_fmt[0].bytesperline =
+				q_data->bytesperline[i];
+			ctx->pict_bufcfg.stride[i] = q_data->bytesperline[i];
+		}
+		for (j = i; j < IMG_MAX_NUM_PLANES; j++) {
+			if ((i - 1) < 0)
+				i++;
+			ctx->pict_bufcfg.stride[j] =
+				q_data->bytesperline[i - 1];
+		}
+		ctx->pict_bufcfg.stride_alignment = HW_ALIGN;
+		ctx->pict_bufcfg.byte_interleave = FALSE;
+		for (i = 0; i < pix_mp->num_planes; i++) {
+			unsigned int plane_size =
+				get_sizeimage(ctx->pict_bufcfg.stride[i],
+					      ctx->pict_bufcfg.coded_height,
+					      q_data->fmt, i);
+			ctx->pict_bufcfg.buf_size += ALIGN(plane_size, PAGE_SIZE);
+			ctx->pict_bufcfg.plane_size[i] = plane_size;
+			pix_mp->plane_fmt[i].sizeimage = plane_size;
+		}
+		if (q_data->fmt->pixfmt == 86031 ||
+		    q_data->fmt->pixfmt == 81935) {
+			/* Handle the v4l2 multi-planar formats */
+			ctx->str_opcfg.pixel_info.num_planes = 3;
+			ctx->pict_bufcfg.packed = FALSE;
+			for (i = 0; i < pix_mp->num_planes; i++) {
+				ctx->pict_bufcfg.chroma_offset[i] =
+					ALIGN(pix_mp->plane_fmt[i].sizeimage, PAGE_SIZE);
+				ctx->pict_bufcfg.chroma_offset[i] +=
+					(i ? ctx->pict_bufcfg.chroma_offset[i - 1] : 0);
+			}
+		} else {
+			/* IMG Decoders support only multi-planar formats */
+			ctx->str_opcfg.pixel_info.num_planes = 2;
+			ctx->pict_bufcfg.packed = TRUE;
+			ctx->pict_bufcfg.chroma_offset[0] = 0;
+			ctx->pict_bufcfg.chroma_offset[1] = 0;
+		}
+
+		vxd_dec_submit_opconfig(ctx);
+	}
+
+	return ret;
+}
+
+static int vxd_dec_subscribe_event(struct v4l2_fh *fh, const struct v4l2_event_subscription *sub)
+{
+	if (sub->type != V4L2_EVENT_EOS)
+		return -EINVAL;
+
+	v4l2_event_subscribe(fh, sub, 0, NULL);
+	return 0;
+}
+
+static int vxd_dec_try_cmd(struct file *file, void *fh, struct v4l2_decoder_cmd *cmd)
+{
+	if (cmd->cmd != V4L2_DEC_CMD_STOP)
+		return -EINVAL;
+
+	return 0;
+}
+
+static int vxd_dec_cmd(struct file *file, void *fh, struct v4l2_decoder_cmd *cmd)
+{
+	struct vxd_dec_ctx *ctx = file2ctx(file);
+
+	if (cmd->cmd != V4L2_DEC_CMD_STOP)
+		return -EINVAL;
+
+#ifdef DEBUG_DECODER_DRIVER
+	pr_info("%s CMD_STOP\n", __func__);
+#endif
+	/*
+	 * When stop command is received, notify device_run if it is
+	 * scheduled to run, or tell the decoder that eos has
+	 * happened.
+	 */
+	mutex_lock_nested(ctx->mutex, SUBCLASS_VXD_V4L2);
+	if (v4l2_m2m_num_src_bufs_ready(ctx->fh.m2m_ctx) > 0) {
+#ifdef DEBUG_DECODER_DRIVER
+		pr_info("V4L2 src bufs not empty, set a flag to notify device_run\n");
+#endif
+		ctx->stop_initiated = TRUE;
+		mutex_unlock(ctx->mutex);
+	} else {
+		if (ctx->num_decoding) {
+#ifdef DEBUG_DECODER_DRIVER
+			pr_info("buffers are still being decoded, so just set eos flag\n");
+#endif
+			ctx->eos = TRUE;
+			mutex_unlock(ctx->mutex);
+		} else {
+			mutex_unlock(ctx->mutex);
+#ifdef DEBUG_DECODER_DRIVER
+			pr_info("All buffers are decoded, so issue dummy stream end\n");
+#endif
+			vxd_return_resource((void *)ctx, VXD_CB_STR_END, 0);
+		}
+	}
+
+	return 0;
+}
+
+static int vxd_g_selection(struct file *file, void *fh, struct v4l2_selection *s)
+{
+	struct vxd_dec_ctx *ctx = file2ctx(file);
+	bool def_bounds = true;
+
+	if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
+	    s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
+		return -EINVAL;
+
+	switch (s->target) {
+	case V4L2_SEL_TGT_COMPOSE_DEFAULT:
+	case V4L2_SEL_TGT_COMPOSE_BOUNDS:
+		if (s->type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
+			return -EINVAL;
+		break;
+	case V4L2_SEL_TGT_CROP_BOUNDS:
+	case V4L2_SEL_TGT_CROP_DEFAULT:
+		if (s->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+			return -EINVAL;
+		break;
+	case V4L2_SEL_TGT_COMPOSE:
+		if (s->type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
+			return -EINVAL;
+		def_bounds = false;
+		break;
+	case V4L2_SEL_TGT_CROP:
+		if (s->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+			return -EINVAL;
+		def_bounds = false;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	if (def_bounds) {
+		s->r.left = 0;
+		s->r.top = 0;
+		s->r.width = ctx->width_orig;
+		s->r.height = ctx->height_orig;
+	}
+
+	return 0;
+}
+
+static const struct v4l2_ioctl_ops vxd_dec_ioctl_ops = {
+	.vidioc_querycap = vxd_dec_querycap,
+
+	.vidioc_enum_fmt_vid_cap = vxd_dec_enum_fmt,
+	.vidioc_g_fmt_vid_cap_mplane = vxd_dec_g_fmt,
+	.vidioc_try_fmt_vid_cap_mplane = vxd_dec_try_fmt,
+	.vidioc_s_fmt_vid_cap_mplane = vxd_dec_s_fmt,
+
+	.vidioc_enum_fmt_vid_out = vxd_dec_enum_fmt,
+	.vidioc_g_fmt_vid_out_mplane = vxd_dec_g_fmt,
+	.vidioc_try_fmt_vid_out_mplane = vxd_dec_try_fmt,
+	.vidioc_s_fmt_vid_out_mplane = vxd_dec_s_fmt,
+
+	.vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs,
+	.vidioc_querybuf = v4l2_m2m_ioctl_querybuf,
+	.vidioc_qbuf = v4l2_m2m_ioctl_qbuf,
+	.vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf,
+	.vidioc_expbuf = v4l2_m2m_ioctl_expbuf,
+
+	.vidioc_streamon = v4l2_m2m_ioctl_streamon,
+	.vidioc_streamoff = v4l2_m2m_ioctl_streamoff,
+	.vidioc_log_status = v4l2_ctrl_log_status,
+	.vidioc_subscribe_event = vxd_dec_subscribe_event,
+	.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+	.vidioc_try_decoder_cmd = vxd_dec_try_cmd,
+	.vidioc_decoder_cmd = vxd_dec_cmd,
+
+	.vidioc_g_selection = vxd_g_selection,
+};
+
+static const struct v4l2_file_operations vxd_dec_fops = {
+	.owner = THIS_MODULE,
+	.open = vxd_dec_open,
+	.release = vxd_dec_release,
+	.poll = v4l2_m2m_fop_poll,
+	.unlocked_ioctl = video_ioctl2,
+	.mmap = v4l2_m2m_fop_mmap,
+};
+
+static struct video_device vxd_dec_videodev = {
+	.name = IMG_VXD_DEC_MODULE_NAME,
+	.fops = &vxd_dec_fops,
+	.ioctl_ops = &vxd_dec_ioctl_ops,
+	.minor = -1,
+	.release = video_device_release,
+	.vfl_dir = VFL_DIR_M2M,
+};
+
+static void device_run(void *priv)
+{
+	struct vxd_dec_ctx *ctx = priv;
+	struct vxd_dev *vxd_dev = ctx->dev;
+	struct device *dev = vxd_dev->v4l2_dev.dev;
+	struct vb2_v4l2_buffer  *src_vb;
+	struct vb2_v4l2_buffer  *dst_vb;
+	struct vxd_buffer *src_vxdb;
+	struct vxd_buffer *dst_vxdb;
+	struct bspp_bitstr_seg *item = NULL, *next = NULL;
+	struct bspp_preparsed_data *preparsed_data;
+	unsigned int data_size;
+	int ret;
+	struct timespec64 time;
+	static int cnt;
+	int i;
+
+	mutex_lock_nested(ctx->mutex, SUBCLASS_VXD_V4L2);
+	ctx->num_decoding++;
+
+	src_vb = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
+	if (!src_vb)
+		dev_err(dev, "Next src buffer is null\n");
+
+	dst_vb = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
+	if (!dst_vb)
+		dev_err(dev, "Next dst buffer is null\n");
+
+	src_vxdb = container_of(src_vb, struct vxd_buffer, buffer.vb);
+	dst_vxdb = container_of(dst_vb, struct vxd_buffer, buffer.vb);
+
+	preparsed_data = &dst_vxdb->preparsed_data;
+
+	data_size = vb2_get_plane_payload(&src_vxdb->buffer.vb.vb2_buf, 0);
+
+	ret = bspp_stream_submit_buffer(ctx->bspp_context,
+					&src_vxdb->bstr_info,
+					src_vxdb->buf_map_id,
+					data_size, NULL,
+					VDEC_BSTRELEMENT_UNSPECIFIED);
+	if (ret)
+		dev_err(dev, "bspp_stream_submit_buffer failed %d\n", ret);
+
+	if (ctx->stop_initiated &&
+	    (v4l2_m2m_num_src_bufs_ready(ctx->fh.m2m_ctx) == 0))
+		ctx->eos = TRUE;
+
+	mutex_unlock(ctx->mutex);
+
+	ret = bspp_stream_preparse_buffers(ctx->bspp_context, NULL, 0, &ctx->seg_list,
+					   preparsed_data, ctx->eos);
+	if (ret)
+		dev_err(dev, "bspp_stream_preparse_buffers failed %d\n", ret);
+
+	ktime_get_real_ts64(&time);
+	vxd_dev->time_drv[cnt].start_time = timespec64_to_ns(&time);
+	vxd_dev->time_drv[cnt].id = dst_vxdb->buf_map_id;
+	cnt++;
+
+	if (cnt >= ARRAY_SIZE(vxd_dev->time_drv))
+		cnt = 0;
+
+	core_stream_fill_pictbuf(dst_vxdb->buf_map_id);
+
+	if (preparsed_data->new_sequence) {
+		src_vxdb->seq_unit.str_unit_type =
+			VDECDD_STRUNIT_SEQUENCE_START;
+		src_vxdb->seq_unit.str_unit_handle = ctx;
+		src_vxdb->seq_unit.err_flags = 0;
+		src_vxdb->seq_unit.dd_data = NULL;
+		src_vxdb->seq_unit.seq_hdr_info =
+			&preparsed_data->sequ_hdr_info;
+		src_vxdb->seq_unit.seq_hdr_id = 0;
+		src_vxdb->seq_unit.closed_gop = TRUE;
+		src_vxdb->seq_unit.eop = FALSE;
+		src_vxdb->seq_unit.pict_hdr_info = NULL;
+		src_vxdb->seq_unit.dd_pict_data = NULL;
+		src_vxdb->seq_unit.last_pict_in_seq = FALSE;
+		src_vxdb->seq_unit.str_unit_tag = NULL;
+		src_vxdb->seq_unit.decode = FALSE;
+		src_vxdb->seq_unit.features = 0;
+		core_stream_submit_unit(ctx->res_str_id, &src_vxdb->seq_unit);
+	}
+
+	src_vxdb->pic_unit.str_unit_type = VDECDD_STRUNIT_PICTURE_START;
+	src_vxdb->pic_unit.str_unit_handle = ctx;
+	src_vxdb->pic_unit.err_flags = 0;
+	/* Move the processed segments to the submission buffer */
+	for (i = 0; i < BSPP_MAX_PICTURES_PER_BUFFER; i++) {
+		item = lst_first(&preparsed_data->picture_data.pre_pict_seg_list[i]);
+		while (item) {
+			next = lst_next(item);
+			lst_remove(&preparsed_data->picture_data.pre_pict_seg_list[i], item);
+			lst_add(&src_vxdb->pic_unit.bstr_seg_list, item);
+			item = next;
+		}
+		/* Move the processed segments to the submission buffer */
+		item = lst_first(&preparsed_data->picture_data.pict_seg_list[i]);
+		while (item) {
+			next = lst_next(item);
+			lst_remove(&preparsed_data->picture_data.pict_seg_list[i], item);
+			lst_add(&src_vxdb->pic_unit.bstr_seg_list, item);
+			item = next;
+		}
+	}
+
+	src_vxdb->pic_unit.dd_data = NULL;
+	src_vxdb->pic_unit.seq_hdr_info = NULL;
+	src_vxdb->pic_unit.seq_hdr_id = 0;
+	if (preparsed_data->new_sequence)
+		src_vxdb->pic_unit.closed_gop = TRUE;
+	else
+		src_vxdb->pic_unit.closed_gop = FALSE;
+	src_vxdb->pic_unit.eop = TRUE;
+	src_vxdb->pic_unit.eos = ctx->eos;
+	src_vxdb->pic_unit.pict_hdr_info =
+		&preparsed_data->picture_data.pict_hdr_info;
+	src_vxdb->pic_unit.dd_pict_data = NULL;
+	src_vxdb->pic_unit.last_pict_in_seq = FALSE;
+	src_vxdb->pic_unit.str_unit_tag = NULL;
+	src_vxdb->pic_unit.decode = FALSE;
+	src_vxdb->pic_unit.features = 0;
+	core_stream_submit_unit(ctx->res_str_id, &src_vxdb->pic_unit);
+
+	src_vxdb->end_unit.str_unit_type = VDECDD_STRUNIT_PICTURE_END;
+	src_vxdb->end_unit.str_unit_handle = ctx;
+	src_vxdb->end_unit.err_flags = 0;
+	src_vxdb->end_unit.dd_data = NULL;
+	src_vxdb->end_unit.seq_hdr_info = NULL;
+	src_vxdb->end_unit.seq_hdr_id = 0;
+	src_vxdb->end_unit.closed_gop = FALSE;
+	src_vxdb->end_unit.eop = FALSE;
+	src_vxdb->end_unit.eos = ctx->eos;
+	src_vxdb->end_unit.pict_hdr_info = NULL;
+	src_vxdb->end_unit.dd_pict_data = NULL;
+	src_vxdb->end_unit.last_pict_in_seq = FALSE;
+	src_vxdb->end_unit.str_unit_tag = NULL;
+	src_vxdb->end_unit.decode = FALSE;
+	src_vxdb->end_unit.features = 0;
+	core_stream_submit_unit(ctx->res_str_id, &src_vxdb->end_unit);
+}
+
+static int job_ready(void *priv)
+{
+	struct vxd_dec_ctx *ctx = priv;
+
+	if (v4l2_m2m_num_src_bufs_ready(ctx->fh.m2m_ctx) < 1 ||
+	    v4l2_m2m_num_dst_bufs_ready(ctx->fh.m2m_ctx) < 1 ||
+	    !ctx->core_streaming)
+		return 0;
+
+	return 1;
+}
+
+static void job_abort(void *priv)
+{
+	struct vxd_dec_ctx *ctx = priv;
+
+	/* Cancel the transaction at next callback */
+	ctx->aborting = 1;
+}
+
+static const struct v4l2_m2m_ops m2m_ops = {
+	.device_run = device_run,
+	.job_ready = job_ready,
+	.job_abort = job_abort,
+};
+
+static const struct of_device_id vxd_dec_of_match[] = {
+	{.compatible = "img,d5500-vxd"},
+	{ /* end */},
+};
+MODULE_DEVICE_TABLE(of, vxd_dec_of_match);
+
+static int vxd_dec_probe(struct platform_device *pdev)
+{
+	struct vxd_dev *vxd;
+	struct resource *res;
+	const struct of_device_id *of_dev_id;
+	int ret;
+	int module_irq;
+	struct video_device *vfd;
+
+	struct heap_config *heap_configs;
+	int num_heaps;
+	unsigned int i_heap_id;
+	/* Protect structure fields */
+	spinlock_t **lock;
+
+	of_dev_id = of_match_device(vxd_dec_of_match, &pdev->dev);
+	if (!of_dev_id) {
+		dev_err(&pdev->dev, "%s: Unable to match device\n", __func__);
+		return -ENODEV;
+	}
+
+	dma_set_mask(&pdev->dev, DMA_BIT_MASK(40));
+
+	vxd = devm_kzalloc(&pdev->dev, sizeof(*vxd), GFP_KERNEL);
+	if (!vxd)
+		return -ENOMEM;
+
+	vxd->dev = &pdev->dev;
+	vxd->plat_dev = pdev;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	vxd->reg_base = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR_VALUE((unsigned long)vxd->reg_base))
+		return (long)(vxd->reg_base);
+
+	module_irq = platform_get_irq(pdev, 0);
+	if (module_irq < 0)
+		return -ENXIO;
+	vxd->module_irq = module_irq;
+#ifdef ERROR_RECOVERY_SIMULATION
+	g_module_irq = module_irq;
+#endif
+
+	heap_configs = vxd_dec_heap_configs;
+	num_heaps = ARRAY_SIZE(vxd_dec_heap_configs);
+
+	vxd->mutex = kzalloc(sizeof(*vxd->mutex), GFP_KERNEL);
+	if (!vxd->mutex)
+		return -ENOMEM;
+
+	mutex_init(vxd->mutex);
+	platform_set_drvdata(pdev, vxd);
+
+	pm_runtime_enable(&pdev->dev);
+	ret = pm_runtime_get_sync(&pdev->dev);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "%s: failed to enable clock, status = %d\n", __func__, ret);
+		goto exit;
+	}
+
+	/* Read HW properties */
+	ret = vxd_pvdec_get_props(vxd->dev, vxd->reg_base, &vxd->props);
+	if (ret) {
+		dev_err(&pdev->dev, "%s: failed to fetch core properties!\n", __func__);
+		ret = -ENXIO;
+		goto out_put_sync;
+	}
+	vxd->mmu_config_addr_width = VXD_EXTRN_ADDR_WIDTH(vxd->props);
+#ifdef DEBUG_DECODER_DRIVER
+	dev_info(&pdev->dev, "hw:%u.%u.%u, num_pix: %d, num_ent: %d, mmu: %d, MTX RAM: %d\n",
+		 VXD_MAJ_REV(vxd->props),
+		 VXD_MIN_REV(vxd->props),
+		 VXD_MAINT_REV(vxd->props),
+		 VXD_NUM_PIX_PIPES(vxd->props),
+		 VXD_NUM_ENT_PIPES(vxd->props),
+		 VXD_EXTRN_ADDR_WIDTH(vxd->props),
+		 vxd->props.mtx_ram_size);
+#endif
+
+	INIT_LIST_HEAD(&vxd->msgs);
+	INIT_LIST_HEAD(&vxd->pend);
+
+	/* initialize memory manager */
+	ret = img_mem_init(&pdev->dev);
+	if (ret) {
+		dev_err(&pdev->dev, "Failed to initialize memory\n");
+		ret = -ENOMEM;
+		goto out_put_sync;
+	}
+	vxd->streams = kzalloc(sizeof(*vxd->streams), GFP_KERNEL);
+	if (!vxd->streams) {
+		ret = -ENOMEM;
+		goto out_init;
+	}
+
+	idr_init(vxd->streams);
+
+	ret = vxd_init(&pdev->dev, vxd, heap_configs, num_heaps);
+	if (ret) {
+		dev_err(&pdev->dev, "%s: main component initialisation failed!\n", __func__);
+		goto out_idr_init;
+	}
+
+	/* initialize core */
+	i_heap_id = vxd_g_internal_heap_id();
+	if (i_heap_id < 0) {
+		dev_err(&pdev->dev, "%s: Invalid internal heap id", __func__);
+		goto out_vxd_init;
+	}
+	ret = core_initialise(vxd, i_heap_id, vxd_return_resource);
+	if (ret) {
+		dev_err(&pdev->dev, "%s: core initialization failed!", __func__);
+		goto out_vxd_init;
+	}
+
+	vxd->fw_refcnt = 0;
+	vxd->hw_on = 0;
+
+#ifdef DEBUG_DECODER_DRIVER
+	vxd->hw_pm_delay = 10000;
+	vxd->hw_dwr_period = 10000;
+#else
+	vxd->hw_pm_delay = 1000;
+	vxd->hw_dwr_period = 1000;
+#endif
+	ret = vxd_prepare_fw(vxd);
+	if (ret) {
+		dev_err(&pdev->dev, "%s fw acquire failed!", __func__);
+		goto out_core_init;
+	}
+
+	if (vxd->no_fw) {
+		dev_err(&pdev->dev, "%s fw acquire failed!", __func__);
+		goto out_core_init;
+	}
+
+	 lock = (spinlock_t **)&vxd->lock;
+	*lock = kzalloc(sizeof(spinlock_t), GFP_KERNEL);
+
+	if (!(*lock)) {
+		pr_err("Memory allocation failed for spin-lock\n");
+		ret = ENOMEM;
+		goto out_core_init;
+	}
+	spin_lock_init(*lock);
+
+	ret = v4l2_device_register(&pdev->dev, &vxd->v4l2_dev);
+	if (ret)
+		goto out_clean_fw;
+
+#ifdef ERROR_RECOVERY_SIMULATION
+	/*
+	 * create a sysfs entry here, to debug firmware error recovery.
+	 */
+	vxd_dec_kobject = kobject_create_and_add("vxd_decoder", kernel_kobj);
+	if (!vxd_dec_kobject) {
+		dev_err(&pdev->dev, "Failed to create kernel object\n");
+		goto out_clean_fw;
+	}
+
+	ret = sysfs_create_group(vxd_dec_kobject, &attr_group);
+	if (ret) {
+		dev_err(&pdev->dev, "Failed to create sysfs files\n");
+		kobject_put(vxd_dec_kobject);
+	}
+#endif
+
+	vfd = video_device_alloc();
+	if (!vfd) {
+		dev_err(&pdev->dev, "Failed to allocate video device\n");
+		ret = -ENOMEM;
+		goto out_v4l2_device;
+	}
+
+	vxd->vfd_dec = vfd;
+	*vfd = vxd_dec_videodev;
+	vfd->v4l2_dev = &vxd->v4l2_dev;
+	vfd->device_caps = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING;
+	vfd->lock = vxd->mutex;
+
+	video_set_drvdata(vfd, vxd);
+
+	snprintf(vfd->name, sizeof(vfd->name), "%s", vxd_dec_videodev.name);
+	ret = devm_request_threaded_irq(&pdev->dev, module_irq, (irq_handler_t)hard_isrcb,
+					(irq_handler_t)soft_thread_irq, IRQF_SHARED,
+					IMG_VXD_DEC_MODULE_NAME, pdev);
+	if (ret) {
+		dev_err(&pdev->dev, "failed to request irq\n");
+		goto out_vid_dev;
+	}
+
+	vxd->m2m_dev = v4l2_m2m_init(&m2m_ops);
+	if (IS_ERR_VALUE((unsigned long)vxd->m2m_dev)) {
+		dev_err(&pdev->dev, "Failed to init mem2mem device\n");
+		ret = -EINVAL;
+		goto out_vid_dev;
+	}
+
+	ret = video_register_device(vfd, VFL_TYPE_VIDEO, 0);
+	if (ret) {
+		dev_err(&pdev->dev, "Failed to register video device\n");
+		goto out_vid_reg;
+	}
+	v4l2_info(&vxd->v4l2_dev, "decoder registered as /dev/video%d\n", vfd->num);
+
+	return 0;
+
+out_vid_reg:
+	v4l2_m2m_release(vxd->m2m_dev);
+
+out_vid_dev:
+	video_device_release(vfd);
+
+out_v4l2_device:
+	v4l2_device_unregister(&vxd->v4l2_dev);
+
+out_clean_fw:
+	vxd_clean_fw_resources(vxd);
+
+out_core_init:
+	core_deinitialise();
+
+out_vxd_init:
+	vxd_deinit(vxd);
+
+out_idr_init:
+	idr_destroy(vxd->streams);
+	kfree(vxd->streams);
+
+out_init:
+	img_mem_exit();
+
+out_put_sync:
+	pm_runtime_put_sync(&pdev->dev);
+
+exit:
+	pm_runtime_disable(&pdev->dev);
+	mutex_destroy(vxd->mutex);
+	kfree(vxd->mutex);
+	vxd->mutex = NULL;
+
+	return ret;
+}
+
+static int vxd_dec_remove(struct platform_device *pdev)
+{
+	struct vxd_dev *vxd = platform_get_drvdata(pdev);
+
+	core_deinitialise();
+
+	vxd_clean_fw_resources(vxd);
+	vxd_deinit(vxd);
+	idr_destroy(vxd->streams);
+	kfree(vxd->streams);
+	get_delayed_work_buff(&vxd->dwork, TRUE);
+	kfree(&vxd->lock);
+	img_mem_exit();
+
+	pm_runtime_put_sync(&pdev->dev);
+	pm_runtime_disable(&pdev->dev);
+	kfree(vxd->dwork);
+	mutex_destroy(vxd->mutex);
+	kfree(vxd->mutex);
+	vxd->mutex = NULL;
+
+	video_unregister_device(vxd->vfd_dec);
+	v4l2_m2m_release(vxd->m2m_dev);
+	v4l2_device_unregister(&vxd->v4l2_dev);
+
+	return 0;
+}
+
+static int __maybe_unused vxd_dec_suspend(struct device *dev)
+{
+	int ret = 0;
+
+	ret = vxd_suspend_dev(dev);
+	if (ret)
+		dev_err(dev, "failed to suspend core hw!\n");
+
+	return ret;
+}
+
+static int __maybe_unused vxd_dec_resume(struct device *dev)
+{
+	int ret = 0;
+
+	ret = vxd_resume_dev(dev);
+	if (ret)
+		dev_err(dev, "failed to resume core hw!\n");
+
+	return ret;
+}
+
+static UNIVERSAL_DEV_PM_OPS(vxd_dec_pm_ops,
+	vxd_dec_suspend, vxd_dec_resume, NULL);
+
+static struct platform_driver vxd_dec_driver = {
+	.probe = vxd_dec_probe,
+	.remove = vxd_dec_remove,
+	.driver = {
+		.name = "img_dec",
+		.pm = &vxd_dec_pm_ops,
+		.of_match_table = vxd_dec_of_match,
+	},
+};
+module_platform_driver(vxd_dec_driver);
+
+MODULE_AUTHOR("Prashanth Kumar Amai <prashanth.ka@pathpartnertech.com> Sidraya Jayagond <sidraya.bj@pathpartnertech.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("IMG D5520 video decoder driver");