Project import generated by Copybara.
GitOrigin-RevId: df5bf87eca1b9de24d3afd9ee49b125f95dc1351
diff --git a/Makefile b/Makefile
new file mode 100755
index 0000000..7481eee
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,51 @@
+mkfile_path := $(abspath $(lastword $(MAKEFILE_LIST)))
+MEDIA_MODULE_PATH := $(dir $(mkfile_path))
+VERSION_CONTROL_CFLAGS := $(shell ${MEDIA_MODULE_PATH}/version_control.sh)
+
+CONFIGS := CONFIG_AMLOGIC_MEDIA_VDEC_MPEG12=m \
+ CONFIG_AMLOGIC_MEDIA_VDEC_MPEG2_MULTI=m \
+ CONFIG_AMLOGIC_MEDIA_VDEC_MPEG4=m \
+ CONFIG_AMLOGIC_MEDIA_VDEC_MPEG4_MULTI=m \
+ CONFIG_AMLOGIC_MEDIA_VDEC_VC1=m \
+ CONFIG_AMLOGIC_MEDIA_VDEC_H264=m \
+ CONFIG_AMLOGIC_MEDIA_VDEC_H264_MULTI=m \
+ CONFIG_AMLOGIC_MEDIA_VDEC_H264_MVC=m \
+ CONFIG_AMLOGIC_MEDIA_VDEC_H265=m \
+ CONFIG_AMLOGIC_MEDIA_VDEC_VP9=m \
+ CONFIG_AMLOGIC_MEDIA_VDEC_MJPEG=m \
+ CONFIG_AMLOGIC_MEDIA_VDEC_MJPEG_MULTI=m \
+ CONFIG_AMLOGIC_MEDIA_VDEC_REAL=m \
+ CONFIG_AMLOGIC_MEDIA_VDEC_AVS=m \
+ CONFIG_AMLOGIC_MEDIA_VDEC_AVS_MULTI=m \
+ CONFIG_AMLOGIC_MEDIA_VDEC_AVS2=m \
+ CONFIG_AMLOGIC_MEDIA_VENC_H264=m \
+ CONFIG_AMLOGIC_MEDIA_VENC_H265=m \
+ CONFIG_AMLOGIC_MEDIA_VDEC_AV1=m \
+ CONFIG_AMLOGIC_MEDIA_ENHANCEMENT_DOLBYVISION=y \
+ CONFIG_AMLOGIC_MEDIA_GE2D=y \
+ CONFIG_AMLOGIC_MEDIA_VENC_MULTI=m \
+ CONFIG_AMLOGIC_MEDIA_VENC_JPEG=m
+
+EXTRA_INCLUDE := -I$(KERNEL_SRC)/$(M)/drivers/include
+
+CONFIGS_BUILD := -Wno-parentheses-equality -Wno-pointer-bool-conversion \
+ -Wno-unused-const-variable -Wno-typedef-redefinition \
+ -Wno-logical-not-parentheses -Wno-sometimes-uninitialized \
+ -Wno-frame-larger-than=
+
+KBUILD_CFLAGS_MODULE += $(GKI_EXT_MODULE_PREDEFINE)
+
+modules:
+ $(MAKE) -C $(KERNEL_SRC) M=$(M)/drivers modules "EXTRA_CFLAGS+=-I$(INCLUDE) -Wno-error $(CONFIGS_BUILD) $(EXTRA_INCLUDE) $(KBUILD_CFLAGS_MODULE) ${VERSION_CONTROL_CFLAGS}" $(CONFIGS)
+
+all: modules
+
+modules_install:
+ $(MAKE) INSTALL_MOD_STRIP=1 M=$(M)/drivers -C $(KERNEL_SRC) modules_install
+ mkdir -p ${OUT_DIR}/../vendor_lib/modules
+ cd ${OUT_DIR}/$(M)/; find -name "*.ko" -exec cp {} ${OUT_DIR}/../vendor_lib/modules/ \;
+ mkdir -p ${OUT_DIR}/../vendor_lib/firmware/video
+ cp $(KERNEL_SRC)/$(M)/firmware/* ${OUT_DIR}/../vendor_lib/firmware/video/
+
+clean:
+ $(MAKE) -C $(KERNEL_SRC) M=$(M) clean
diff --git a/Media.mk b/Media.mk
new file mode 100644
index 0000000..58db63b
--- /dev/null
+++ b/Media.mk
@@ -0,0 +1,110 @@
+ifeq ($(KERNEL_A32_SUPPORT), true)
+KERNEL_ARCH := arm
+else
+KERNEL_ARCH := arm64
+endif
+
+CONFIGS := CONFIG_AMLOGIC_MEDIA_VDEC_MPEG12=m \
+ CONFIG_AMLOGIC_MEDIA_VDEC_MPEG2_MULTI=m \
+ CONFIG_AMLOGIC_MEDIA_VDEC_MPEG4=m \
+ CONFIG_AMLOGIC_MEDIA_VDEC_MPEG4_MULTI=m \
+ CONFIG_AMLOGIC_MEDIA_VDEC_VC1=m \
+ CONFIG_AMLOGIC_MEDIA_VDEC_H264=m \
+ CONFIG_AMLOGIC_MEDIA_VDEC_H264_MULTI=m \
+ CONFIG_AMLOGIC_MEDIA_VDEC_H264_MVC=m \
+ CONFIG_AMLOGIC_MEDIA_VDEC_H265=m \
+ CONFIG_AMLOGIC_MEDIA_VDEC_VP9=m \
+ CONFIG_AMLOGIC_MEDIA_VDEC_MJPEG=m \
+ CONFIG_AMLOGIC_MEDIA_VDEC_MJPEG_MULTI=m \
+ CONFIG_AMLOGIC_MEDIA_VDEC_REAL=m \
+ CONFIG_AMLOGIC_MEDIA_VDEC_AVS=m \
+ CONFIG_AMLOGIC_MEDIA_VDEC_AVS2=m \
+ CONFIG_AMLOGIC_MEDIA_VENC_H264=m \
+ CONFIG_AMLOGIC_MEDIA_VENC_H265=m \
+ CONFIG_AMLOGIC_MEDIA_ENHANCEMENT_DOLBYVISION=y \
+ CONFIG_AMLOGIC_MEDIA_GE2D=y \
+ CONFIG_AMLOGIC_MEDIA_VENC_MULTI=m \
+ CONFIG_AMLOGIC_MEDIA_VENC_JPEG=m
+
+define copy-media-modules
+$(foreach m, $(shell find $(strip $(1)) -name "*.ko"),\
+ $(shell cp $(m) $(strip $(2)) -rfa))
+endef
+
+ifneq (,$(TOP))
+KDIR := $(shell pwd)/$(PRODUCT_OUT)/obj/KERNEL_OBJ/
+
+MEDIA_DRIVERS := $(TOP)/hardware/amlogic/media_modules/drivers
+ifeq (,$(wildcard $(MEDIA_DRIVERS)))
+$(error No find the dir of drivers.)
+endif
+
+INCLUDE := $(MEDIA_DRIVERS)/include
+ifeq (,$(wildcard $(INCLUDE)))
+$(error No find the dir of include.)
+endif
+
+MEDIA_MODULES := $(shell pwd)/$(PRODUCT_OUT)/obj/media_modules
+ifeq (,$(wildcard $(MEDIA_MODULES)))
+$(shell mkdir $(MEDIA_MODULES) -p)
+endif
+
+MODS_OUT := $(shell pwd)/$(PRODUCT_OUT)/obj/lib_vendor
+ifeq (,$(wildcard $(MODS_OUT)))
+$(shell mkdir $(MODS_OUT) -p)
+endif
+
+UCODE_OUT := $(shell pwd)/$(PRODUCT_OUT)/$(TARGET_COPY_OUT_VENDOR)/lib/firmware/video
+ifeq (,$(wildcard $(UCODE_OUT)))
+$(shell mkdir $(UCODE_OUT) -p)
+endif
+
+$(shell cp $(MEDIA_DRIVERS)/../firmware/* $(UCODE_OUT) -rfa)
+$(shell cp $(MEDIA_DRIVERS)/* $(MEDIA_MODULES) -rfa)
+
+define media-modules
+ PATH=$(KERNEL_TOOLPATHS):$$PATH \
+ $(MAKE) -C $(KDIR) M=$(MEDIA_MODULES) $(KERNEL_ARGS) $(CONFIGS) \
+ "EXTRA_CFLAGS+=-I$(INCLUDE) -Wno-error" modules; \
+ find $(MEDIA_MODULES) -name "*.ko" | PATH=$$(cd ./$(TARGET_HOST_TOOL_PATH); pwd):$$PATH xargs -i cp {} $(MODS_OUT)
+endef
+
+else
+KDIR := $(PWD)/kernel
+ifeq (,$(wildcard $(KDIR)))
+$(error No find the dir of kernel.)
+endif
+
+MEDIA_DRIVERS := $(PWD)/media_modules/drivers
+ifeq (,$(wildcard $(MEDIA_DRIVERS)))
+$(error No find the dir of drivers.)
+endif
+
+INCLUDE := $(MEDIA_DRIVERS)/include
+ifeq (,$(wildcard $(INCLUDE)))
+$(error No find the dir of include.)
+endif
+
+MODS_OUT ?= $(MEDIA_DRIVERS)/../modules
+ifeq (,$(wildcard $(MODS_OUT)))
+$(shell mkdir $(MODS_OUT) -p)
+endif
+
+modules:
+ CCACHE_NODIRECT="true" PATH=$(KERNEL_TOOLPATHS):$$PATH \
+ $(MAKE) -C $(KDIR) M=$(MEDIA_DRIVERS) ARCH=$(KERNEL_ARCH) $(KERNEL_ARGS) $(CONFIGS) \
+ EXTRA_CFLAGS+=-I$(INCLUDE) -j64
+
+copy-modules:
+ @echo "start copying media modules."
+ mkdir -p $(MODS_OUT)
+ $(call copy-media-modules, $(MEDIA_DRIVERS), $(MODS_OUT))
+
+all: modules copy-modules
+
+
+clean:
+ PATH=$(KERNEL_TOOLPATHS):$$PATH \
+ $(MAKE) -C $(KDIR) M=$(MEDIA_DRIVERS) $(KERNEL_ARGS) clean
+
+endif
diff --git a/VERSION b/VERSION
new file mode 100644
index 0000000..4b1504d
--- /dev/null
+++ b/VERSION
@@ -0,0 +1,76 @@
+Major_V=5
+Minor_V=3
+BaseChangeId=Ifc17e92ef9c9e211b8f01ebae1e97c5855fea084
+
+
+#history version
+#V5.2.104-g795aee00.003025 Release Notes
+#Release_ChangeId_V5_2=I34801cba60d54bfd9f459f7445d0362989c23496
+#V5.1.77-g0cdebf27.003009 Release Notes
+#Release_ChangeId_V5_1=Id7e315bbf379d166ca4b335cef13c983a8ca5078
+#Feature develop total count: 6
+# critical feature develop count: 0
+# important feature develop count: 4
+# normal feature develop count: 2
+#Feature develop detail information:
+# improve pip channel play
+# support decoder fence
+# fixed can't get hdr information correctly.
+# add multi-vdec info for new format av1 after revert
+# change ptsadjust and threshold from s64 to s32.
+# support dw 0x100
+#Fixed BUG total count: 30
+# fixed critical BUG count: 3
+# fixed important feature develop count: 15
+# fixed normal feature develop count: 12
+#Fixed BUG detail information:
+# add VIDTYPE_COMPRESS in vf for dw mode with afbc buffer
+# fix some the crash caused by null pointer
+# fixed vpp wrapper memory leak.
+# metadata lose the last byte every frame in frame mode
+# fix avs color abnormal.
+# fix gst secure v4l2 decoder not work
+# fix the playback stuck when resolution changed
+# h265 ucode send 2 interrupts cause playback stuck
+# fix av1 freeze when burn-in test.
+# fixed playback stuck after seek.
+# fix 8k display abnormal.
+# fixed irq-vdec-0 takes more cpu slice.
+# fixed AV1 seek freezing.
+# fixed failed to allocate tvp memory
+# fixed issue of reports resolution change.
+#V5.0 Release Notes
+#Release_ChangeId_V5_0=I6053e02900215d9006469c38ca375ace498b849f
+#upgrade Kernel version to 5.4
+#Android R + v4l2dec (no vpp) xts clean
+#v4ldec driver seperate from amports drivers
+#
+#V4.0.0 Release Notes
+#upgrade Kernel version to 4.19
+#v4l2 support for h264/h265/vp9
+#add fra support in decoder driver
+#
+#V3.0.0 Release Notes
+#upgrade Kernel version to 4.9
+#media_module remove from kernel
+#new firmware management
+#mjpeg/mpeg12/mpeg2 multi-instance decoder support
+#h264 4k afbc support
+#AVS2 decoder support
+#vdec double write support
+#add av1 decoder support
+#add decoder QOS info report
+#upgrade TA ucode to 0.2 version
+#
+#V2.0.0 Release Notes
+#upgrade Kernel version to 3.14
+#Introduce codec_mm memory managment
+#add afbc scatter memory support
+#add vp9 decoder support
+#add 264/265/vp9 multi-instance decoder support
+#
+#V1.0.0 Release Notes
+#based kernel to 3.10
+#add H264 4K decoder support
+#add H265 video decoder support
+#H265 decoder support afbc output
diff --git a/drivers/Makefile b/drivers/Makefile
new file mode 100644
index 0000000..e96ba44
--- /dev/null
+++ b/drivers/Makefile
@@ -0,0 +1,8 @@
+obj-y += common/
+obj-y += frame_provider/
+obj-y += frame_sink/
+obj-y += stream_input/
+obj-y += amvdec_ports/
+obj-y += framerate_adapter/
+obj-y += media_sync/
+obj-$(CONFIG_AMLOGIC_MEDIA_V4L_DEC) += amvdec_ports/
diff --git a/drivers/amvdec_ports/Makefile b/drivers/amvdec_ports/Makefile
new file mode 100644
index 0000000..2e7cff4
--- /dev/null
+++ b/drivers/amvdec_ports/Makefile
@@ -0,0 +1,26 @@
+obj-m += amvdec_ports.o
+amvdec_ports-objs += aml_vcodec_dec_drv.o
+amvdec_ports-objs += aml_vcodec_dec.o
+amvdec_ports-objs += aml_vcodec_util.o
+amvdec_ports-objs += aml_vcodec_adapt.o
+amvdec_ports-objs += aml_vcodec_vpp.o
+amvdec_ports-objs += aml_vcodec_ge2d.o
+amvdec_ports-objs += vdec_drv_if.o
+amvdec_ports-objs += aml_task_chain.o
+amvdec_ports-objs += decoder/vdec_h264_if.o
+amvdec_ports-objs += decoder/vdec_hevc_if.o
+amvdec_ports-objs += decoder/vdec_vp9_if.o
+amvdec_ports-objs += decoder/vdec_mpeg12_if.o
+amvdec_ports-objs += decoder/vdec_mpeg4_if.o
+amvdec_ports-objs += decoder/vdec_mjpeg_if.o
+amvdec_ports-objs += decoder/vdec_av1_if.o
+ifdef CONFIG_AMLOGIC_MEDIA_V4L_SOFTWARE_PARSER
+amvdec_ports-objs += decoder/aml_h264_parser.o
+amvdec_ports-objs += decoder/aml_hevc_parser.o
+amvdec_ports-objs += decoder/aml_vp9_parser.o
+amvdec_ports-objs += decoder/aml_mpeg12_parser.o
+amvdec_ports-objs += decoder/aml_mpeg4_parser.o
+amvdec_ports-objs += decoder/aml_mjpeg_parser.o
+amvdec_ports-objs += utils/golomb.o
+endif
+amvdec_ports-objs += utils/common.o
diff --git a/drivers/amvdec_ports/aml_task_chain.c b/drivers/amvdec_ports/aml_task_chain.c
new file mode 100644
index 0000000..8dfe014
--- /dev/null
+++ b/drivers/amvdec_ports/aml_task_chain.c
@@ -0,0 +1,365 @@
+/*
+* Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2 of the License, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful, but WITHOUT
+* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+* more details.
+*
+* You should have received a copy of the GNU General Public License along
+* with this program; if not, write to the Free Software Foundation, Inc.,
+* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*
+* Description:
+*/
+
+#include <linux/list.h>
+#include <linux/spinlock.h>
+
+#include "aml_vcodec_drv.h"
+#include "aml_task_chain.h"
+
+#define KERNEL_ATRACE_TAG KERNEL_ATRACE_TAG_V4L2
+#include <trace/events/meson_atrace.h>
+
+struct task_item_name_s {
+ enum task_type_e type;
+ const u8 *name;
+};
+
+static const struct task_item_name_s iname[] = {
+ {TASK_TYPE_DEC, "dec"},
+ {TASK_TYPE_VPP, "vpp"},
+ {TASK_TYPE_V4L_SINK, "v4l-sink"},
+ {TASK_TYPE_GE2D, "ge2d"},
+ {TASK_TYPE_MAX, "unknown"},
+};
+
+static const u8 *type_to_name(enum task_type_e type)
+{
+ const u8 *name = "unknown";
+ int i, size = ARRAY_SIZE(iname);
+
+ for (i = 0; i < size; i++) {
+ if (type == iname[i].type)
+ name = iname[i].name;
+ }
+
+ return name;
+}
+
+static struct task_item_s *find_task_item(struct task_chain_s *task,
+ enum task_type_e type)
+{
+ struct task_item_s *item = NULL;
+ ulong flags;
+
+ spin_lock_irqsave(&task->slock, flags);
+
+ if (!list_empty(&task->list_item)) {
+ struct task_item_s *p;
+
+ list_for_each_entry(p, &task->list_item, node) {
+ if (p->ops->type == type) {
+ item = p;
+ break;
+ }
+ }
+ }
+
+ if (item)
+ kref_get(&item->ref);
+
+ spin_unlock_irqrestore(&task->slock, flags);
+
+ return item;
+}
+
+static void task_item_release(struct kref *kref);
+
+static void task_item_vframe_push(struct task_item_s *item, struct vframe_s *vframe)
+{
+ int i = 0;
+
+ for (i = 0 ; i < 3; i++) {
+ if (item->vframe[i] == NULL) {
+ item->vframe[i] = vframe;
+ break;
+ }
+ }
+}
+
+static struct vframe_s *task_item_vframe_pop(struct task_item_s *item)
+{
+ struct vframe_s *vframe = NULL;
+ int i = 0;
+
+ for (i = 0 ; i < 3; i++) {
+ if (item->vframe[i] != NULL) {
+ vframe = item->vframe[i];
+ item->vframe[i] = NULL;
+ break;
+ }
+ }
+
+ return vframe;
+}
+
+static struct task_item_s *task_item_get(struct task_chain_s *task,
+ enum task_type_e type)
+{
+ struct task_item_s *item = NULL;
+
+ item = find_task_item(task, type);
+ if (!item) {
+ v4l_dbg(task->ctx, V4L_DEBUG_CODEC_ERROR,
+ "TSK(%px):%d get item:%d fail.\n", task, task->id, type);
+ }
+
+ return item;
+}
+
+static int task_item_put(struct task_item_s *item)
+{
+ return kref_put(&item->ref, task_item_release);
+}
+
+static void task_buffer_submit(struct task_chain_s *task,
+ enum task_type_e type)
+{
+ struct vdec_v4l2_buffer *fb =
+ (struct vdec_v4l2_buffer *)task->obj;
+ struct task_item_s *item = NULL;
+ struct task_item_s *item2 = NULL;
+ struct vframe_s *vf = NULL;
+
+ item = task_item_get(task, type);
+ if (item) {
+ item->ops->get_vframe(item->caller, &vf);
+ fb->vframe = (void *)vf;
+ task_item_vframe_push(item, vf);
+ item->is_active = false;
+
+ item2 = task_item_get(task, task->map[0][type]);
+ if (item2) {
+ item2->is_active = true;
+ item2->ops->fill_buffer(task->ctx, fb);
+
+ v4l_dbg(task->ctx, V4L_DEBUG_TASK_CHAIN,
+ "TSK(%px):%d, vf:%px, phy:%lx, submit %d => %d.\n",
+ task, task->id, vf, fb->m.mem[0].addr,
+ type, task->map[0][type]);
+
+ task->direction = TASK_DIR_SUBMIT;
+ task_item_put(item2);
+ }
+ task_item_put(item);
+ }
+}
+
+static void task_buffer_recycle(struct task_chain_s *task,
+ enum task_type_e type)
+{
+ struct vdec_v4l2_buffer *fb =
+ (struct vdec_v4l2_buffer *)task->obj;
+ struct task_item_s *item = NULL;
+ struct task_item_s *item2 = NULL;
+
+ item = task_item_get(task, type);
+ if (item) {
+ item->is_active = false;
+
+ item2 = task_item_get(task, task->map[1][type]);
+ if (item2) {
+ struct vframe_s *vf = NULL;
+
+ item2->is_active = true;
+
+ vf = task_item_vframe_pop(item2);
+ item2->ops->put_vframe(item2->caller, vf);
+
+ v4l_dbg(task->ctx, V4L_DEBUG_TASK_CHAIN,
+ "TSK(%px):%d, vf:%px, phy:%lx, recycle %d => %d.\n",
+ task, task->id, vf, fb->m.mem[0].addr,
+ type, task->map[1][type]);
+
+ task->direction = TASK_DIR_RECYCLE;
+ task_item_put(item2);
+ }
+ task_item_put(item);
+ }
+}
+
+void task_chain_show(struct task_chain_s *task)
+{
+ struct task_item_s *item = NULL;
+ char buf[128] = {0};
+ char *pbuf = buf;
+ ulong flags;
+
+ if (!task || !task->ctx)
+ return;
+
+ spin_lock_irqsave(&task->slock, flags);
+
+ if (!list_empty(&task->list_item)) {
+ struct vdec_v4l2_buffer *fb =
+ (struct vdec_v4l2_buffer *)task->obj;
+
+ list_for_each_entry(item, &task->list_item, node) {
+ pbuf += sprintf(pbuf, "%s(%d)",
+ item->name, item->is_active);
+ if (item->node.next != &task->list_item) {
+ if (task->direction == TASK_DIR_SUBMIT)
+ pbuf += sprintf(pbuf, " ==> ");
+ else
+ pbuf += sprintf(pbuf, " <== ");
+ }
+ }
+ v4l_dbg(task->ctx, V4L_DEBUG_CODEC_PRINFO,
+ "vb:%2d, phy:%lx %s\n",
+ task->id, fb->m.mem[0].addr, buf);
+ }
+
+ spin_unlock_irqrestore(&task->slock, flags);
+}
+EXPORT_SYMBOL(task_chain_show);
+
+static void task_chain_destroy(struct kref *kref)
+{
+ struct task_chain_s *task;
+
+ task = container_of(kref, struct task_chain_s, ref);
+
+ task->cur_type = TASK_TYPE_MAX;
+ memset(task->map, 0, sizeof(task->map));
+
+ v4l_dbg(task->ctx, V4L_DEBUG_TASK_CHAIN,
+ "TSK(%px):%d task chain destroyed.\n", task, task->id);
+
+ kfree(task);
+}
+
+static void task_item_release(struct kref *kref)
+{
+ struct task_item_s *item;
+
+ item = container_of(kref, struct task_item_s, ref);
+ list_del(&item->node);
+
+ v4l_dbg(item->task->ctx, V4L_DEBUG_TASK_CHAIN,
+ "TSK(%px):%d task item:(%px,%d) released.\n",
+ item->task, item->task->id, item, item->ops->type);
+
+ kref_put(&item->task->ref, task_chain_destroy);
+
+ kfree(item);
+}
+
+void task_chain_clean(struct task_chain_s *task)
+{
+ struct task_item_s *item, *tmp;
+
+ v4l_dbg(task->ctx, V4L_DEBUG_TASK_CHAIN,
+ "TSK(%px):%d task chain clean.\n", task, task->id);
+
+ if (!list_empty(&task->list_item)) {
+ list_for_each_entry_safe(item, tmp, &task->list_item, node)
+ kref_put(&item->ref, task_item_release);
+ }
+}
+EXPORT_SYMBOL(task_chain_clean);
+
+void task_chain_release(struct task_chain_s *task)
+{
+ v4l_dbg(task->ctx, V4L_DEBUG_TASK_CHAIN,
+ "TSK(%px):%d task chain release.\n", task, task->id);
+
+ kref_put(&task->ref, task_chain_destroy);
+}
+EXPORT_SYMBOL(task_chain_release);
+
+void task_order_attach(struct task_chain_s *task,
+ struct task_ops_s *ops,
+ void *caller)
+{
+ struct task_item_s *item;
+
+ item = kzalloc(sizeof(struct task_item_s), GFP_ATOMIC);
+ if (!item) {
+ v4l_dbg(task->ctx, V4L_DEBUG_CODEC_ERROR,
+ "TSK(%px):%d alloc item fail.\n", task, task->id);
+ return;
+ }
+
+ item->task = task;
+ item->ops = ops;
+ item->caller = caller;
+ item->name = type_to_name(ops->type);
+ kref_init(&item->ref);
+
+ task->map[0][ops->type] = task->cur_type;
+ task->map[1][task->cur_type] = ops->type;
+ task->cur_type = ops->type;
+ kref_get(&task->ref);
+
+ list_add(&item->node, &task->list_item);
+
+ v4l_dbg(task->ctx, V4L_DEBUG_TASK_CHAIN,
+ "TSK(%px):%d attach item:(%px,%d).\n",
+ task, task->id, item, ops->type);
+}
+EXPORT_SYMBOL(task_order_attach);
+
+void task_chain_update_object(struct task_chain_s *task, void *obj)
+{
+ /*
+ * Note: have to invoke this funtion
+ * if the task object has been changed.
+ */
+ task->obj = obj;
+
+ v4l_dbg(task->ctx, V4L_DEBUG_TASK_CHAIN,
+ "TSK(%px):%d update task obj:%px.\n",
+ task, task->id, obj);
+}
+EXPORT_SYMBOL(task_chain_update_object);
+
+int task_chain_init(struct task_chain_s **task_out,
+ void *v4l_ctx,
+ void *obj,
+ int vb_idx)
+{
+ struct task_chain_s *task;
+
+ task = kzalloc(sizeof(struct task_chain_s), GFP_ATOMIC);
+ if (!task) {
+ v4l_dbg(task->ctx, V4L_DEBUG_CODEC_ERROR,
+ "TSK(%px):%d alloc task fail.\n", task, task->id);
+ return -ENOMEM;
+ }
+
+ task->id = vb_idx;
+ task->obj = obj;
+ task->ctx = v4l_ctx;
+ kref_init(&task->ref);
+ spin_lock_init(&task->slock);
+ INIT_LIST_HEAD(&task->list_item);
+
+ task->attach = task_order_attach;
+ task->submit = task_buffer_submit;
+ task->recycle = task_buffer_recycle;
+
+ *task_out = task;
+
+ v4l_dbg(task->ctx, V4L_DEBUG_TASK_CHAIN,
+ "TSK(%px):%d task chain creat success.\n", task, task->id);
+ return 0;
+}
+EXPORT_SYMBOL(task_chain_init);
+
diff --git a/drivers/amvdec_ports/aml_task_chain.h b/drivers/amvdec_ports/aml_task_chain.h
new file mode 100644
index 0000000..fdbe2fb
--- /dev/null
+++ b/drivers/amvdec_ports/aml_task_chain.h
@@ -0,0 +1,125 @@
+/*
+* Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2 of the License, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful, but WITHOUT
+* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+* more details.
+*
+* You should have received a copy of the GNU General Public License along
+* with this program; if not, write to the Free Software Foundation, Inc.,
+* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*
+* Description:
+*/
+
+#ifndef AML_TASK_CHAIN_H
+#define AML_TASK_CHAIN_H
+
+#include <linux/amlogic/media/vfm/vframe.h>
+
+enum task_type_e {
+ TASK_TYPE_DEC,
+ TASK_TYPE_VPP,
+ TASK_TYPE_V4L_SINK,
+ TASK_TYPE_GE2D,
+ TASK_TYPE_MAX
+};
+
+enum task_dir_e {
+ TASK_DIR_SUBMIT,
+ TASK_DIR_RECYCLE,
+ TASK_DIR_MAX
+};
+
+struct task_chain_s;
+
+/*
+ * struct task_ops_s - interface of the task item.
+ * @type : type of task ops involves dec, vpp, v4l sink etc.
+ * @get_vframe : get the video frame from caller's fifo.
+ * @put_vframe : put the video frame to caller's fifo.
+ * @fill_buffer : submit the buffer into next process module.
+ */
+struct task_ops_s {
+ enum task_type_e type;
+ void (*get_vframe) (void *caller, struct vframe_s **vf);
+ void (*put_vframe) (void *caller, struct vframe_s *vf);
+ void (*fill_buffer) (void *v4l_ctx, void *fb_ctx);
+};
+
+/*
+ * struct task_item_s - items of the task chain.
+ * @node : list node of the specific task item.
+ * @ref : reference count of item be used by others.
+ * @name : name of task item, map with task type.
+ * @is_active : indicate this item whether is active.
+ * @vframe[3] : store the vframes that get from caller.
+ * @task : the context of the task chain.
+ * @caller : it's the handle, meght it's dec, vpp or v4l-sink etc.
+ * @ops : sets of interface which attach from task item.
+ */
+struct task_item_s {
+ struct list_head node;
+ struct kref ref;
+ const u8 *name;
+ bool is_active;
+ void *vframe[3];
+ struct task_chain_s *task;
+ void *caller;
+ struct task_ops_s *ops;
+};
+
+/*
+ * struct task_chain_s - the manager struct of the task chain.
+ * @list_item : all task items be attached are store in the list.
+ * @node : will register to the task chain pool.
+ * @ref : reference count of task chain be used by others.
+ * @slock : used for list item write and read safely.
+ * @id : it's vb index to be a mark used for task chain.
+ * @ctx : the context of the v4l driver.
+ * @obj : the object managed by task chain.
+ * @direction : direction incluse 2 flows submit & recycle.
+ * @cur_type : the latest item type before a new item be attached.
+ * @map : the map store the pipeline information.
+ * @attach : attach a new item to task chain.
+ * @submit : submit the finish item to next item module.
+ * @recycle : if item's date was consumed will be recycled to item.
+ */
+struct task_chain_s {
+ struct list_head list_item;
+ struct list_head node;
+ struct kref ref;
+ spinlock_t slock;
+ int id;
+ void *ctx;
+ void *obj;
+ enum task_dir_e direction;
+ enum task_type_e cur_type;
+ u8 map[2][8];
+
+ void (*attach) (struct task_chain_s *, struct task_ops_s *, void *);
+ void (*submit) (struct task_chain_s *, enum task_type_e);
+ void (*recycle) (struct task_chain_s *, enum task_type_e);
+};
+
+
+int task_chain_init(struct task_chain_s **task_out,
+ void *v4l_ctx,
+ void *obj,
+ int vb_idx);
+void task_order_attach(struct task_chain_s *task,
+ struct task_ops_s *ops,
+ void *caller);
+void task_chain_clean(struct task_chain_s *task);
+void task_chain_release(struct task_chain_s *task);
+void task_chain_show(struct task_chain_s *task);
+void task_chain_update_object(struct task_chain_s *task, void *obj);
+
+#endif //AML_TASK_CHAIN_H
+
diff --git a/drivers/amvdec_ports/aml_vcodec_adapt.c b/drivers/amvdec_ports/aml_vcodec_adapt.c
new file mode 100644
index 0000000..7e08371
--- /dev/null
+++ b/drivers/amvdec_ports/aml_vcodec_adapt.c
@@ -0,0 +1,591 @@
+/*
+* Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2 of the License, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful, but WITHOUT
+* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+* more details.
+*
+* You should have received a copy of the GNU General Public License along
+* with this program; if not, write to the Free Software Foundation, Inc.,
+* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*
+* Description:
+*/
+#include <linux/types.h>
+#include <linux/amlogic/media/utils/amstream.h>
+#include <linux/amlogic/media/utils/vformat.h>
+#include <linux/amlogic/media/utils/aformat.h>
+#include <linux/amlogic/media/frame_sync/tsync.h>
+#include <linux/amlogic/media/frame_sync/ptsserv.h>
+#include <linux/amlogic/media/frame_sync/timestamp.h>
+#include <linux/amlogic/media/utils/amports_config.h>
+#include <linux/amlogic/media/frame_sync/tsync_pcr.h>
+#include <linux/amlogic/media/codec_mm/codec_mm.h>
+#include <linux/amlogic/media/codec_mm/configs.h>
+#include <linux/amlogic/media/utils/vformat.h>
+#include <linux/amlogic/media/utils/aformat.h>
+#include <linux/amlogic/media/registers/register.h>
+#include "../stream_input/amports/adec.h"
+#include "../stream_input/amports/streambuf.h"
+#include "../stream_input/amports/streambuf_reg.h"
+#include "../stream_input/parser/tsdemux.h"
+#include "../stream_input/parser/psparser.h"
+#include "../stream_input/parser/esparser.h"
+#include "../frame_provider/decoder/utils/vdec.h"
+#include "../common/media_clock/switch/amports_gate.h"
+#include <linux/delay.h>
+#include "aml_vcodec_adapt.h"
+#include <linux/crc32.h>
+
+#define DEFAULT_VIDEO_BUFFER_SIZE (1024 * 1024 * 3)
+#define DEFAULT_VIDEO_BUFFER_SIZE_4K (1024 * 1024 * 6)
+#define DEFAULT_VIDEO_BUFFER_SIZE_TVP (1024 * 1024 * 10)
+#define DEFAULT_VIDEO_BUFFER_SIZE_4K_TVP (1024 * 1024 * 15)
+#define DEFAULT_AUDIO_BUFFER_SIZE (1024*768*2)
+#define DEFAULT_SUBTITLE_BUFFER_SIZE (1024*256)
+
+#define PTS_OUTSIDE (1)
+#define SYNC_OUTSIDE (2)
+
+//#define DATA_DEBUG
+
+extern int dump_output_frame;
+extern u32 dump_output_start_position;
+extern void aml_recycle_dma_buffers(struct aml_vcodec_ctx *ctx, u32 handle);
+
+static int slow_input = 0;
+
+static struct stream_buf_s bufs[BUF_MAX_NUM] = {
+ {
+ .reg_base = VLD_MEM_VIFIFO_REG_BASE,
+ .type = BUF_TYPE_VIDEO,
+ .buf_start = 0,
+ .buf_size = DEFAULT_VIDEO_BUFFER_SIZE,
+ .default_buf_size = DEFAULT_VIDEO_BUFFER_SIZE,
+ .first_tstamp = INVALID_PTS
+ },
+ {
+ .reg_base = AIU_MEM_AIFIFO_REG_BASE,
+ .type = BUF_TYPE_AUDIO,
+ .buf_start = 0,
+ .buf_size = DEFAULT_AUDIO_BUFFER_SIZE,
+ .default_buf_size = DEFAULT_AUDIO_BUFFER_SIZE,
+ .first_tstamp = INVALID_PTS
+ },
+ {
+ .reg_base = 0,
+ .type = BUF_TYPE_SUBTITLE,
+ .buf_start = 0,
+ .buf_size = DEFAULT_SUBTITLE_BUFFER_SIZE,
+ .default_buf_size = DEFAULT_SUBTITLE_BUFFER_SIZE,
+ .first_tstamp = INVALID_PTS
+ },
+ {
+ .reg_base = 0,
+ .type = BUF_TYPE_USERDATA,
+ .buf_start = 0,
+ .buf_size = 0,
+ .first_tstamp = INVALID_PTS
+ },
+ {
+ .reg_base = HEVC_STREAM_REG_BASE,
+ .type = BUF_TYPE_HEVC,
+ .buf_start = 0,
+ .buf_size = DEFAULT_VIDEO_BUFFER_SIZE_4K,
+ .default_buf_size = DEFAULT_VIDEO_BUFFER_SIZE_4K,
+ .first_tstamp = INVALID_PTS
+ },
+};
+
+extern int aml_set_vfm_path, aml_set_vdec_type;
+extern bool aml_set_vfm_enable, aml_set_vdec_type_enable;
+
+static void set_default_params(struct aml_vdec_adapt *vdec)
+{
+ ulong sync_mode = (PTS_OUTSIDE | SYNC_OUTSIDE);
+
+ vdec->dec_prop.param = (void *)sync_mode;
+ vdec->dec_prop.format = vdec->format;
+ vdec->dec_prop.width = 1920;
+ vdec->dec_prop.height = 1088;
+ vdec->dec_prop.rate = 3200;
+}
+
+static int enable_hardware(struct stream_port_s *port)
+{
+ if (get_cpu_type() < MESON_CPU_MAJOR_ID_M6)
+ return -1;
+
+ amports_switch_gate("demux", 1);
+ if (get_cpu_type() >= MESON_CPU_MAJOR_ID_M8)
+ amports_switch_gate("parser_top", 1);
+
+ if (port->type & PORT_TYPE_VIDEO) {
+ amports_switch_gate("vdec", 1);
+
+ if (has_hevc_vdec()) {
+ if (port->type & PORT_TYPE_HEVC)
+ vdec_poweron(VDEC_HEVC);
+ else
+ vdec_poweron(VDEC_1);
+ } else {
+ if (get_cpu_type() >= MESON_CPU_MAJOR_ID_M8)
+ vdec_poweron(VDEC_1);
+ }
+ }
+
+ return 0;
+}
+
+static int disable_hardware(struct stream_port_s *port)
+{
+ if (get_cpu_type() < MESON_CPU_MAJOR_ID_M6)
+ return -1;
+
+ if (port->type & PORT_TYPE_VIDEO) {
+ if (has_hevc_vdec()) {
+ if (port->type & PORT_TYPE_HEVC)
+ vdec_poweroff(VDEC_HEVC);
+ else
+ vdec_poweroff(VDEC_1);
+ }
+
+ amports_switch_gate("vdec", 0);
+ }
+
+ if (get_cpu_type() >= MESON_CPU_MAJOR_ID_M8)
+ amports_switch_gate("parser_top", 0);
+
+ amports_switch_gate("demux", 0);
+
+ return 0;
+}
+
+static void user_buffer_init(void)
+{
+ struct stream_buf_s *pubuf = &bufs[BUF_TYPE_USERDATA];
+
+ pubuf->buf_size = 0;
+ pubuf->buf_start = 0;
+ pubuf->buf_wp = 0;
+ pubuf->buf_rp = 0;
+}
+
+static void video_component_release(struct stream_port_s *port)
+{
+ struct aml_vdec_adapt *ada_ctx
+ = container_of(port, struct aml_vdec_adapt, port);
+ struct vdec_s *vdec = ada_ctx->vdec;
+
+ vdec_release(vdec);
+
+}
+
+static int video_component_init(struct stream_port_s *port,
+ struct stream_buf_s *pbuf)
+{
+ int ret = -1;
+ struct aml_vdec_adapt *ada_ctx
+ = container_of(port, struct aml_vdec_adapt, port);
+ struct vdec_s *vdec = ada_ctx->vdec;
+
+ if ((vdec->port_flag & PORT_FLAG_VFORMAT) == 0) {
+ v4l_dbg(ada_ctx->ctx, V4L_DEBUG_CODEC_ERROR, "vformat not set\n");
+ return -EPERM;
+ }
+
+ if ((vdec->sys_info->height * vdec->sys_info->width) > 1920 * 1088
+ || port->vformat == VFORMAT_H264_4K2K) {
+ port->is_4k = true;
+ if (get_cpu_type() >= MESON_CPU_MAJOR_ID_TXLX
+ && (port->vformat == VFORMAT_H264))
+ vdec_poweron(VDEC_HEVC);
+ } else
+ port->is_4k = false;
+
+ if (port->type & PORT_TYPE_FRAME ||
+ (port->type & PORT_TYPE_ES)) {
+ ret = vdec_init(vdec, port->is_4k, true);
+ if (ret < 0) {
+ v4l_dbg(ada_ctx->ctx, V4L_DEBUG_CODEC_ERROR, "failed\n");
+ video_component_release(port);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int vdec_ports_release(struct stream_port_s *port)
+{
+ struct stream_buf_s *pvbuf = &bufs[BUF_TYPE_VIDEO];
+
+ if (has_hevc_vdec()) {
+ if (port->vformat == VFORMAT_HEVC ||
+ port->vformat == VFORMAT_VP9)
+ pvbuf = &bufs[BUF_TYPE_HEVC];
+ }
+
+ if (port->type & PORT_TYPE_MPTS) {
+ tsync_pcr_stop();
+ tsdemux_release();
+ }
+
+ if (port->type & PORT_TYPE_MPPS)
+ psparser_release();
+
+ if (port->type & PORT_TYPE_VIDEO)
+ video_component_release(port);
+
+ port->pcr_inited = 0;
+ port->flag = 0;
+
+ return 0;
+}
+
+static void set_vdec_properity(struct vdec_s *vdec,
+ struct aml_vdec_adapt *ada_ctx)
+{
+ vdec->sys_info = &ada_ctx->dec_prop;
+ vdec->port = &ada_ctx->port;
+ vdec->format = ada_ctx->video_type;
+ vdec->sys_info_store = ada_ctx->dec_prop;
+
+ /* binding v4l2 ctx to vdec. */
+ vdec->private = ada_ctx->ctx;
+
+ /* set video format, sys info and vfm map.*/
+ vdec->port->vformat = vdec->format;
+ vdec->port->type |= PORT_TYPE_VIDEO;
+ vdec->port_flag |= (vdec->port->flag | PORT_FLAG_VFORMAT);
+ if (vdec->slave) {
+ vdec->slave->format = ada_ctx->dec_prop.format;
+ vdec->slave->port_flag |= PORT_FLAG_VFORMAT;
+ }
+
+ vdec->type = VDEC_TYPE_FRAME_BLOCK;
+ vdec->port->type |= PORT_TYPE_FRAME;
+ vdec->frame_base_video_path = FRAME_BASE_PATH_V4L_OSD;
+
+ if (aml_set_vdec_type_enable) {
+ if (aml_set_vdec_type == VDEC_TYPE_STREAM_PARSER) {
+ vdec->type = VDEC_TYPE_STREAM_PARSER;
+ vdec->port->type &= ~PORT_TYPE_FRAME;
+ vdec->port->type |= PORT_TYPE_ES;
+ } else if (aml_set_vdec_type == VDEC_TYPE_FRAME_BLOCK) {
+ vdec->type = VDEC_TYPE_FRAME_BLOCK;
+ vdec->port->type &= ~PORT_TYPE_ES;
+ vdec->port->type |= PORT_TYPE_FRAME;
+ }
+ }
+
+ if (aml_set_vfm_enable)
+ vdec->frame_base_video_path = aml_set_vfm_path;
+
+ vdec->port->flag = vdec->port_flag;
+
+ vdec->config_len = ada_ctx->config.length >
+ PAGE_SIZE ? PAGE_SIZE : ada_ctx->config.length;
+ memcpy(vdec->config, ada_ctx->config.buf, vdec->config_len);
+
+ ada_ctx->vdec = vdec;
+}
+
+static int vdec_ports_init(struct aml_vdec_adapt *ada_ctx)
+{
+ int ret = -1;
+ struct stream_buf_s *pvbuf = &bufs[BUF_TYPE_VIDEO];
+ struct vdec_s *vdec = NULL;
+
+ /* create the vdec instance.*/
+ vdec = vdec_create(&ada_ctx->port, NULL);
+ if (IS_ERR_OR_NULL(vdec))
+ return -1;
+
+ vdec->disable_vfm = true;
+ set_vdec_properity(vdec, ada_ctx);
+
+ /* init hw and gate*/
+ ret = enable_hardware(vdec->port);
+ if (ret < 0) {
+ v4l_dbg(ada_ctx->ctx, V4L_DEBUG_CODEC_ERROR, "enable hw fail.\n");
+ return ret;
+ }
+
+ stbuf_fetch_init();
+ user_buffer_init();
+
+ if ((vdec->port->type & PORT_TYPE_VIDEO)
+ && (vdec->port_flag & PORT_FLAG_VFORMAT)) {
+ vdec->port->is_4k = false;
+ if (has_hevc_vdec()) {
+ if (vdec->port->vformat == VFORMAT_HEVC ||
+ vdec->port->vformat == VFORMAT_VP9)
+ pvbuf = &bufs[BUF_TYPE_HEVC];
+ }
+
+ ret = video_component_init(vdec->port, pvbuf);
+ if (ret < 0) {
+ v4l_dbg(ada_ctx->ctx, V4L_DEBUG_CODEC_ERROR, "video_component_init failed\n");
+ return ret;
+ }
+
+ /* connect vdec at the end after all HW initialization */
+ vdec_connect(vdec);
+ }
+
+ return 0;
+}
+
+int video_decoder_init(struct aml_vdec_adapt *vdec)
+{
+ int ret = -1;
+
+ /* sets configure data */
+ set_default_params(vdec);
+
+ /* init the buffer work space and connect vdec.*/
+ ret = vdec_ports_init(vdec);
+ if (ret < 0) {
+ v4l_dbg(vdec->ctx, V4L_DEBUG_CODEC_ERROR, "vdec ports init fail.\n");
+ goto out;
+ }
+out:
+ return ret;
+}
+
+int video_decoder_release(struct aml_vdec_adapt *vdec)
+{
+ int ret = -1;
+ struct stream_port_s *port = &vdec->port;
+
+ ret = vdec_ports_release(port);
+ if (ret < 0) {
+ v4l_dbg(vdec->ctx, V4L_DEBUG_CODEC_ERROR, "vdec ports release fail.\n");
+ goto out;
+ }
+
+ /* disable gates */
+ ret = disable_hardware(port);
+ if (ret < 0) {
+ v4l_dbg(vdec->ctx, V4L_DEBUG_CODEC_ERROR, "disable hw fail.\n");
+ goto out;
+ }
+out:
+ return ret;
+}
+
+void dump(const char* path, const char *data, unsigned int size)
+{
+ struct file *fp;
+
+ fp = filp_open(path,
+ O_CREAT | O_RDWR | O_LARGEFILE | O_APPEND, 0600);
+ if (!IS_ERR(fp)) {
+ kernel_write(fp, data, size, 0);
+ filp_close(fp, NULL);
+ } else {
+ pr_info("Dump ES fail, should check RW permission, size:%x\n", size);
+ }
+}
+
+int vdec_vbuf_write(struct aml_vdec_adapt *ada_ctx,
+ const char *buf, unsigned int count)
+{
+ int ret = -1;
+ int try_cnt = 100;
+ struct stream_port_s *port = &ada_ctx->port;
+ struct vdec_s *vdec = ada_ctx->vdec;
+ struct stream_buf_s *pbuf = NULL;
+
+ if (has_hevc_vdec()) {
+ pbuf = (port->type & PORT_TYPE_HEVC) ? &bufs[BUF_TYPE_HEVC] :
+ &bufs[BUF_TYPE_VIDEO];
+ } else
+ pbuf = &bufs[BUF_TYPE_VIDEO];
+
+ /*if (!(port_get_inited(priv))) {
+ r = video_decoder_init(priv);
+ if (r < 0)
+ return r;
+ }*/
+
+ do {
+ if (vdec->port_flag & PORT_FLAG_DRM)
+ ret = drm_write(ada_ctx->filp, pbuf, buf, count);
+ else
+ ret = esparser_write(ada_ctx->filp, pbuf, buf, count);
+
+ if (ret == -EAGAIN)
+ msleep(30);
+ } while (ret == -EAGAIN && try_cnt--);
+
+ if (slow_input) {
+ v4l_dbg(ada_ctx->ctx, V4L_DEBUG_CODEC_PRINFO,
+ "slow_input: es codec write size %x\n", ret);
+ msleep(10);
+ }
+
+#ifdef DATA_DEBUG
+ /* dump to file */
+ //dump_write(vbuf, size);
+ //v4l_dbg(ada_ctx->ctx, V4L_DEBUG_CODEC_PRINFO, "vbuf: %p, size: %u, ret: %d\n", vbuf, size, ret);
+#endif
+
+ return ret;
+}
+
+bool vdec_input_full(struct aml_vdec_adapt *ada_ctx)
+{
+ struct vdec_s *vdec = ada_ctx->vdec;
+
+ return (vdec->input.have_frame_num > 60) ? true : false;
+}
+
+int vdec_vframe_write(struct aml_vdec_adapt *ada_ctx,
+ const char *buf, unsigned int count, u64 timestamp, ulong meta_ptr)
+{
+ int ret = -1;
+ struct vdec_s *vdec = ada_ctx->vdec;
+
+ /* set timestamp */
+ vdec_set_timestamp(vdec, timestamp);
+
+ /* set metadata */
+ vdec_set_metadata(vdec, meta_ptr);
+
+ ret = vdec_write_vframe(vdec, buf, count);
+
+ if (slow_input) {
+ v4l_dbg(ada_ctx->ctx, V4L_DEBUG_CODEC_PRINFO,
+ "slow_input: frame codec write size %d\n", ret);
+ msleep(30);
+ }
+
+ if (dump_output_frame > 0 &&
+ (!dump_output_start_position ||
+ (dump_output_start_position == crc32_le(0, buf, count)))) {
+ dump("/data/es.data", buf, count);
+ dump_output_frame--;
+ dump_output_start_position = 0;
+ }
+
+ v4l_dbg(ada_ctx->ctx, V4L_DEBUG_CODEC_INPUT,
+ "write frames, vbuf: %p, size: %u, ret: %d, crc: %x, ts: %llu\n",
+ buf, count, ret, crc32_le(0, buf, count), timestamp);
+
+ return ret;
+}
+
+void vdec_vframe_input_free(void *priv, u32 handle)
+{
+ struct aml_vcodec_ctx *ctx = priv;
+
+ aml_recycle_dma_buffers(ctx, handle);
+}
+
+int vdec_vframe_write_with_dma(struct aml_vdec_adapt *ada_ctx,
+ ulong addr, u32 count, u64 timestamp, u32 handle,
+ chunk_free free, void* priv)
+{
+ int ret = -1;
+ struct vdec_s *vdec = ada_ctx->vdec;
+
+ /* set timestamp */
+ vdec_set_timestamp(vdec, timestamp);
+
+ ret = vdec_write_vframe_with_dma(vdec, addr, count,
+ handle, free, priv);
+
+ if (slow_input) {
+ v4l_dbg(ada_ctx->ctx, V4L_DEBUG_CODEC_PRINFO,
+ "slow_input: frame codec write size %d\n", ret);
+ msleep(30);
+ }
+
+ v4l_dbg(ada_ctx->ctx, V4L_DEBUG_CODEC_INPUT,
+ "write frames, vbuf: %lx, size: %u, ret: %d, ts: %llu\n",
+ addr, count, ret, timestamp);
+
+ return ret;
+}
+
+void aml_decoder_flush(struct aml_vdec_adapt *ada_ctx)
+{
+ struct vdec_s *vdec = ada_ctx->vdec;
+
+ if (vdec)
+ vdec_set_eos(vdec, true);
+}
+
+int aml_codec_reset(struct aml_vdec_adapt *ada_ctx, int *mode)
+{
+ struct vdec_s *vdec = ada_ctx->vdec;
+ int ret = 0;
+
+ if (vdec) {
+ if (ada_ctx->ctx->v4l_resolution_change)
+ *mode = V4L_RESET_MODE_LIGHT;
+ else
+ vdec_set_eos(vdec, false);
+
+ v4l_dbg(ada_ctx->ctx, V4L_DEBUG_CODEC_PRINFO,
+ "reset mode: %d, es frames buffering: %d\n",
+ *mode, vdec_frame_number(ada_ctx));
+
+ ret = vdec_v4l2_reset(vdec, *mode);
+ *mode = V4L_RESET_MODE_NORMAL;
+ }
+
+ return ret;
+}
+
+bool is_input_ready(struct aml_vdec_adapt *ada_ctx)
+{
+ struct vdec_s *vdec = ada_ctx->vdec;
+ int state = VDEC_STATUS_UNINITIALIZED;
+
+ if (vdec) {
+ state = vdec_get_status(vdec);
+
+ if (state == VDEC_STATUS_CONNECTED
+ || state == VDEC_STATUS_ACTIVE)
+ return true;
+ }
+
+ return false;
+}
+
+int vdec_frame_number(struct aml_vdec_adapt *ada_ctx)
+{
+ struct vdec_s *vdec = ada_ctx->vdec;
+
+ if (vdec)
+ return vdec_get_frame_num(vdec);
+ else
+ return -1;
+}
+
+int vdec_get_instance_num(void)
+{
+ return vdec_get_core_nr();
+}
+
+void v4l2_config_vdec_parm(struct aml_vdec_adapt *ada_ctx, u8 *data, u32 len)
+{
+ struct vdec_s *vdec = ada_ctx->vdec;
+
+ vdec->config_len = len > PAGE_SIZE ? PAGE_SIZE : len;
+ memcpy(vdec->config, data, vdec->config_len);
+}
+
+void vdec_set_duration(s32 duration)
+{
+ vdec_frame_rate_uevent(duration);
+}
diff --git a/drivers/amvdec_ports/aml_vcodec_adapt.h b/drivers/amvdec_ports/aml_vcodec_adapt.h
new file mode 100644
index 0000000..c8641ce
--- /dev/null
+++ b/drivers/amvdec_ports/aml_vcodec_adapt.h
@@ -0,0 +1,79 @@
+/*
+* Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2 of the License, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful, but WITHOUT
+* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+* more details.
+*
+* You should have received a copy of the GNU General Public License along
+* with this program; if not, write to the Free Software Foundation, Inc.,
+* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*
+* Description:
+*/
+#ifndef VDEC_ADAPT_H
+#define VDEC_ADAPT_H
+
+#include <linux/amlogic/media/utils/vformat.h>
+#include <linux/amlogic/media/utils/amstream.h>
+#include "../stream_input/amports/streambuf.h"
+#include "../frame_provider/decoder/utils/vdec_input.h"
+#include "aml_vcodec_drv.h"
+
+struct aml_vdec_adapt {
+ int format;
+ void *vsi;
+ int32_t failure;
+ uint32_t inst_addr;
+ unsigned int signaled;
+ struct aml_vcodec_ctx *ctx;
+ wait_queue_head_t wq;
+ struct file *filp;
+ struct vdec_s *vdec;
+ struct stream_port_s port;
+ struct dec_sysinfo dec_prop;
+ struct v4l2_config_parm config;
+ int video_type;
+ char *frm_name;
+};
+
+int video_decoder_init(struct aml_vdec_adapt *ada_ctx);
+
+int video_decoder_release(struct aml_vdec_adapt *ada_ctx);
+
+int vdec_vbuf_write(struct aml_vdec_adapt *ada_ctx,
+ const char *buf, unsigned int count);
+
+int vdec_vframe_write(struct aml_vdec_adapt *ada_ctx,
+ const char *buf, unsigned int count, u64 timestamp, ulong meta_ptr);
+
+void vdec_vframe_input_free(void *priv, u32 handle);
+
+int vdec_vframe_write_with_dma(struct aml_vdec_adapt *ada_ctx,
+ ulong addr, u32 count, u64 timestamp, u32 handle,
+ chunk_free free, void *priv);
+
+bool vdec_input_full(struct aml_vdec_adapt *ada_ctx);
+
+void aml_decoder_flush(struct aml_vdec_adapt *ada_ctx);
+
+int aml_codec_reset(struct aml_vdec_adapt *ada_ctx, int *flag);
+
+extern void dump_write(const char __user *buf, size_t count);
+
+bool is_input_ready(struct aml_vdec_adapt *ada_ctx);
+
+int vdec_frame_number(struct aml_vdec_adapt *ada_ctx);
+
+int vdec_get_instance_num(void);
+
+void vdec_set_duration(s32 duration);
+
+#endif /* VDEC_ADAPT_H */
+
diff --git a/drivers/amvdec_ports/aml_vcodec_dec.c b/drivers/amvdec_ports/aml_vcodec_dec.c
new file mode 100644
index 0000000..2bbfb6e
--- /dev/null
+++ b/drivers/amvdec_ports/aml_vcodec_dec.c
@@ -0,0 +1,4593 @@
+/*
+* Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2 of the License, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful, but WITHOUT
+* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+* more details.
+*
+* You should have received a copy of the GNU General Public License along
+* with this program; if not, write to the Free Software Foundation, Inc.,
+* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*
+* Description:
+*/
+#include <media/v4l2-event.h>
+#include <media/v4l2-mem2mem.h>
+#include <media/videobuf2-dma-contig.h>
+#include <media/videobuf2-dma-sg.h>
+
+#include <linux/delay.h>
+#include <linux/atomic.h>
+#include <linux/crc32.h>
+#include <linux/sched.h>
+#include <linux/spinlock.h>
+#include <linux/amlogic/meson_uvm_core.h>
+#include <linux/scatterlist.h>
+#include <linux/sched/clock.h>
+#include <linux/highmem.h>
+#include <uapi/linux/sched/types.h>
+
+#include "aml_vcodec_drv.h"
+#include "aml_vcodec_dec.h"
+#include "aml_vcodec_util.h"
+#include "vdec_drv_if.h"
+#include "aml_vcodec_adapt.h"
+#include "aml_vcodec_vpp.h"
+#include "aml_vcodec_ge2d.h"
+
+#include "../frame_provider/decoder/utils/decoder_bmmu_box.h"
+#include "../frame_provider/decoder/utils/decoder_mmu_box.h"
+#include "../common/chips/decoder_cpu_ver_info.h"
+#include "utils/common.h"
+#include "../frame_provider/decoder/utils/vdec_sync.h"
+
+
+#define KERNEL_ATRACE_TAG KERNEL_ATRACE_TAG_V4L2
+#include <trace/events/meson_atrace.h>
+
+
+#define OUT_FMT_IDX (0) //default h264
+#define CAP_FMT_IDX (9) //capture nv21
+#define CAP_FMT_I420_IDX (12) //use for mjpeg
+
+#define AML_VDEC_MIN_W 64U
+#define AML_VDEC_MIN_H 64U
+#define DFT_CFG_WIDTH AML_VDEC_MIN_W
+#define DFT_CFG_HEIGHT AML_VDEC_MIN_H
+
+#define V4L2_CID_USER_AMLOGIC_BASE (V4L2_CID_USER_BASE + 0x1100)
+#define AML_V4L2_SET_DRMMODE (V4L2_CID_USER_AMLOGIC_BASE + 0)
+#define AML_V4L2_GET_INPUT_BUFFER_NUM (V4L2_CID_USER_AMLOGIC_BASE + 1)
+#define AML_V4L2_SET_DURATION (V4L2_CID_USER_AMLOGIC_BASE + 2)
+#define AML_V4L2_GET_FILMGRAIN_INFO (V4L2_CID_USER_AMLOGIC_BASE + 3)
+
+#define WORK_ITEMS_MAX (32)
+#define MAX_DI_INSTANCE (2)
+
+//#define USEC_PER_SEC 1000000
+
+#define call_void_memop(vb, op, args...) \
+ do { \
+ if ((vb)->vb2_queue->mem_ops->op) \
+ (vb)->vb2_queue->mem_ops->op(args); \
+ } while (0)
+
+static struct aml_video_fmt aml_video_formats[] = {
+ {
+ .name = "H.264",
+ .fourcc = V4L2_PIX_FMT_H264,
+ .type = AML_FMT_DEC,
+ .num_planes = 1,
+ },
+ {
+ .name = "H.265",
+ .fourcc = V4L2_PIX_FMT_HEVC,
+ .type = AML_FMT_DEC,
+ .num_planes = 1,
+ },
+ {
+ .name = "VP9",
+ .fourcc = V4L2_PIX_FMT_VP9,
+ .type = AML_FMT_DEC,
+ .num_planes = 1,
+ },
+ {
+ .name = "MPEG1",
+ .fourcc = V4L2_PIX_FMT_MPEG1,
+ .type = AML_FMT_DEC,
+ .num_planes = 1,
+ },
+ {
+ .name = "MPEG2",
+ .fourcc = V4L2_PIX_FMT_MPEG2,
+ .type = AML_FMT_DEC,
+ .num_planes = 1,
+ },
+ {
+ .name = "MPEG4",
+ .fourcc = V4L2_PIX_FMT_MPEG4,
+ .type = AML_FMT_DEC,
+ .num_planes = 1,
+ },
+ {
+ .name = "MJPEG",
+ .fourcc = V4L2_PIX_FMT_MJPEG,
+ .type = AML_FMT_DEC,
+ .num_planes = 1,
+ },
+ {
+ .name = "AV1",
+ .fourcc = V4L2_PIX_FMT_AV1,
+ .type = AML_FMT_DEC,
+ .num_planes = 1,
+ },
+ {
+ .name = "NV21",
+ .fourcc = V4L2_PIX_FMT_NV21,
+ .type = AML_FMT_FRAME,
+ .num_planes = 1,
+ },
+ {
+ .name = "NV21M",
+ .fourcc = V4L2_PIX_FMT_NV21M,
+ .type = AML_FMT_FRAME,
+ .num_planes = 2,
+ },
+ {
+ .name = "NV12",
+ .fourcc = V4L2_PIX_FMT_NV12,
+ .type = AML_FMT_FRAME,
+ .num_planes = 1,
+ },
+ {
+ .name = "NV12M",
+ .fourcc = V4L2_PIX_FMT_NV12M,
+ .type = AML_FMT_FRAME,
+ .num_planes = 2,
+ },
+ {
+ .name = "YUV420",
+ .fourcc = V4L2_PIX_FMT_YUV420,
+ .type = AML_FMT_FRAME,
+ .num_planes = 1,
+ },
+ {
+ .name = "YUV420M",
+ .fourcc = V4L2_PIX_FMT_YUV420M,
+ .type = AML_FMT_FRAME,
+ .num_planes = 2,
+ },
+};
+
+static const struct aml_codec_framesizes aml_vdec_framesizes[] = {
+ {
+ .fourcc = V4L2_PIX_FMT_H264,
+ .stepwise = { AML_VDEC_MIN_W, AML_VDEC_MAX_W, 2,
+ AML_VDEC_MIN_H, AML_VDEC_MAX_H, 2},
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_HEVC,
+ .stepwise = { AML_VDEC_MIN_W, AML_VDEC_MAX_W, 2,
+ AML_VDEC_MIN_H, AML_VDEC_MAX_H, 2},
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_VP9,
+ .stepwise = { AML_VDEC_MIN_W, AML_VDEC_MAX_W, 2,
+ AML_VDEC_MIN_H, AML_VDEC_MAX_H, 2},
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_MPEG1,
+ .stepwise = { AML_VDEC_MIN_W, AML_VDEC_MAX_W, 2,
+ AML_VDEC_MIN_H, AML_VDEC_MAX_H, 2},
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_MPEG2,
+ .stepwise = { AML_VDEC_MIN_W, AML_VDEC_MAX_W, 2,
+ AML_VDEC_MIN_H, AML_VDEC_MAX_H, 2},
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_MPEG4,
+ .stepwise = { AML_VDEC_MIN_W, AML_VDEC_MAX_W, 2,
+ AML_VDEC_MIN_H, AML_VDEC_MAX_H, 2},
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_MJPEG,
+ .stepwise = { AML_VDEC_MIN_W, AML_VDEC_MAX_W, 2,
+ AML_VDEC_MIN_H, AML_VDEC_MAX_H, 2},
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_AV1,
+ .stepwise = { AML_VDEC_MIN_W, AML_VDEC_MAX_W, 2,
+ AML_VDEC_MIN_H, AML_VDEC_MAX_H, 2},
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_NV21,
+ .stepwise = { AML_VDEC_MIN_W, AML_VDEC_MAX_W, 2,
+ AML_VDEC_MIN_H, AML_VDEC_MAX_H, 2},
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_NV21M,
+ .stepwise = { AML_VDEC_MIN_W, AML_VDEC_MAX_W, 2,
+ AML_VDEC_MIN_H, AML_VDEC_MAX_H, 2},
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_NV12,
+ .stepwise = { AML_VDEC_MIN_W, AML_VDEC_MAX_W, 2,
+ AML_VDEC_MIN_H, AML_VDEC_MAX_H, 2},
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_NV12M,
+ .stepwise = { AML_VDEC_MIN_W, AML_VDEC_MAX_W, 2,
+ AML_VDEC_MIN_H, AML_VDEC_MAX_H, 2},
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_YUV420,
+ .stepwise = { AML_VDEC_MIN_W, AML_VDEC_MAX_W, 2,
+ AML_VDEC_MIN_H, AML_VDEC_MAX_H, 2},
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_YUV420M,
+ .stepwise = { AML_VDEC_MIN_W, AML_VDEC_MAX_W, 2,
+ AML_VDEC_MIN_H, AML_VDEC_MAX_H, 2},
+ },
+};
+
+#define NUM_SUPPORTED_FRAMESIZE ARRAY_SIZE(aml_vdec_framesizes)
+#define NUM_FORMATS ARRAY_SIZE(aml_video_formats)
+
+extern bool multiplanar;
+extern int dump_capture_frame;
+extern int bypass_vpp;
+extern int bypass_ge2d;
+extern bool support_format_I420;
+extern bool support_mjpeg;
+extern int bypass_progressive;
+extern int force_enable_nr;
+extern int force_enable_di_local_buffer;
+extern int max_di_instance;
+extern int bypass_nr_flag;
+
+extern int dmabuf_fd_install_data(int fd, void* data, u32 size);
+extern bool is_v4l2_buf_file(struct file *file);
+extern int get_double_write_ratio(int dw_mode);
+static void box_release(struct kref *kref);
+static struct internal_comp_buf* vb_to_comp(struct aml_vcodec_ctx *ctx,
+ struct vb2_buffer *vb);
+static void update_ctx_dimension(struct aml_vcodec_ctx *ctx, u32 type);
+static void copy_v4l2_format_dimention(struct v4l2_pix_format_mplane *pix_mp,
+ struct v4l2_pix_format *pix,
+ struct aml_q_data *q_data,
+ u32 type);
+
+static ulong aml_vcodec_ctx_lock(struct aml_vcodec_ctx *ctx)
+{
+ ulong flags;
+
+ spin_lock_irqsave(&ctx->slock, flags);
+
+ return flags;
+}
+
+static void aml_vcodec_ctx_unlock(struct aml_vcodec_ctx *ctx, ulong flags)
+{
+ spin_unlock_irqrestore(&ctx->slock, flags);
+}
+
+static ulong dmabuf_contiguous_size(struct sg_table *sgt)
+{
+ struct scatterlist *s;
+ dma_addr_t expected = sg_dma_address(sgt->sgl);
+ ulong size = 0;
+ u32 i;
+
+ for_each_sg(sgt->sgl, s, sgt->nents, i) {
+ if (sg_dma_address(s) != expected)
+ break;
+ expected = sg_dma_address(s) + sg_dma_len(s);
+ size += sg_dma_len(s);
+ }
+
+ return size;
+}
+
+static struct aml_video_fmt *aml_vdec_find_format(struct v4l2_format *f)
+{
+ struct aml_video_fmt *fmt;
+ unsigned int k;
+
+ for (k = 0; k < NUM_FORMATS; k++) {
+ fmt = &aml_video_formats[k];
+ if (fmt->fourcc == f->fmt.pix_mp.pixelformat)
+ return fmt;
+ }
+
+ return NULL;
+}
+
+static struct aml_q_data *aml_vdec_get_q_data(struct aml_vcodec_ctx *ctx,
+ enum v4l2_buf_type type)
+{
+ if (V4L2_TYPE_IS_OUTPUT(type))
+ return &ctx->q_data[AML_Q_DATA_SRC];
+
+ return &ctx->q_data[AML_Q_DATA_DST];
+}
+
+void aml_vdec_dispatch_event(struct aml_vcodec_ctx *ctx, u32 changes)
+{
+ struct v4l2_event event = {0};
+
+ switch (changes) {
+ case V4L2_EVENT_SRC_CH_RESOLUTION:
+ case V4L2_EVENT_SRC_CH_HDRINFO:
+ case V4L2_EVENT_REQUEST_RESET:
+ case V4L2_EVENT_REQUEST_EXIT:
+ event.type = V4L2_EVENT_SOURCE_CHANGE;
+ event.u.src_change.changes = changes;
+ break;
+ case V4L2_EVENT_SEND_EOS:
+ event.type = V4L2_EVENT_EOS;
+ break;
+ default:
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_ERROR,
+ "unsupport dispatch event %x\n", changes);
+ return;
+ }
+
+ v4l2_event_queue_fh(&ctx->fh, &event);
+ if (changes != V4L2_EVENT_SRC_CH_HDRINFO)
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_PRINFO, "changes: %x\n", changes);
+ else
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_EXINFO, "changes: %x\n", changes);
+}
+
+static void aml_vdec_flush_decoder(struct aml_vcodec_ctx *ctx)
+{
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_EXINFO, "%s\n", __func__);
+
+ aml_decoder_flush(ctx->ada_ctx);
+}
+
+/* Conditions:
+ * Always connect VPP for mpeg2 and h264 when the stream size is under 2K.
+ * Always connect VPP for hevc/av1/vp9 when color space is not SDR and
+ * stream size is under 2K.
+ * For DV, need application to notify V4L2 driver to enforce the color space
+ * conversion. Plan to do it through a system node.
+ * Do not connect VPP in other cases.
+ */
+static bool vpp_needed(struct aml_vcodec_ctx *ctx, u32* mode)
+{
+ int width = ctx->picinfo.coded_width;
+ int height = ctx->picinfo.coded_height;
+ int size = 1920 * 1088;
+
+ if (bypass_vpp)
+ return false;
+
+ if (!ctx->vpp_cfg.enable_nr &&
+ (ctx->picinfo.field == V4L2_FIELD_NONE)) {
+ return false;
+ }
+
+ if (!ctx->vpp_cfg.enable_nr &&
+ (ctx->output_pix_fmt == V4L2_PIX_FMT_HEVC)) {
+ if (is_over_size(width, height, size)) {
+ return false;
+ }
+ }
+
+ if ((ctx->output_pix_fmt == V4L2_PIX_FMT_H264) &&
+ (ctx->picinfo.field != V4L2_FIELD_NONE)) {
+ if (is_over_size(width, height, size)) {
+ return false;
+ }
+ }
+
+ if (ctx->vpp_cfg.enable_nr) {
+ if (ctx->vpp_cfg.enable_local_buf)
+ *mode = VPP_MODE_NOISE_REDUC_LOCAL;
+ else
+ *mode = VPP_MODE_NOISE_REDUC;
+ } else {
+ if (ctx->vpp_cfg.enable_local_buf)
+ *mode = VPP_MODE_DI_LOCAL;
+ else
+ *mode = VPP_MODE_DI;
+ }
+
+#if 0//enable later
+ if (ctx->colorspace != V4L2_COLORSPACE_DEFAULT &&
+ !is_over_size(width, height, size)) {
+ if (ctx->vpp_cfg.enable_local_buf)
+ *mode = VPP_MODE_COLOR_CONV_LOCAL;
+ else
+ *mode = VPP_MODE_COLOR_CONV;
+ }
+#endif
+
+ return true;
+}
+
+static bool ge2d_needed(struct aml_vcodec_ctx *ctx, u32* mode)
+{
+ bool enable_fence = (ctx->config.parm.dec.cfg.low_latency_mode & 2) ? 1 : 0;
+
+ if ((get_cpu_major_id() == AM_MESON_CPU_MAJOR_ID_T7) && enable_fence) {
+ return false;
+ }
+
+ if (bypass_ge2d)
+ return false;
+
+ if (get_cpu_major_id() == AM_MESON_CPU_MAJOR_ID_T7) {
+ if ((ctx->output_pix_fmt != V4L2_PIX_FMT_H264) &&
+ (ctx->output_pix_fmt != V4L2_PIX_FMT_MPEG1) &&
+ (ctx->output_pix_fmt != V4L2_PIX_FMT_MPEG2) &&
+ (ctx->output_pix_fmt != V4L2_PIX_FMT_MPEG4) &&
+ (ctx->output_pix_fmt != V4L2_PIX_FMT_MJPEG)) {
+ return false;
+ }
+ } else if (ctx->output_pix_fmt != V4L2_PIX_FMT_MJPEG) {
+ return false;
+ }
+
+ if (ctx->picinfo.field != V4L2_FIELD_NONE) {
+ return false;
+ }
+
+ if ((ctx->cap_pix_fmt == V4L2_PIX_FMT_NV12) ||
+ (ctx->cap_pix_fmt == V4L2_PIX_FMT_NV12M))
+ *mode = GE2D_MODE_CONVERT_NV12;
+ else if ((ctx->cap_pix_fmt == V4L2_PIX_FMT_NV21) ||
+ (ctx->cap_pix_fmt == V4L2_PIX_FMT_NV21M))
+ *mode = GE2D_MODE_CONVERT_NV21;
+ else
+ *mode = GE2D_MODE_CONVERT_NV21;
+
+ *mode |= GE2D_MODE_CONVERT_LE;
+
+ return true;
+}
+
+static u32 v4l_buf_size_decision(struct aml_vcodec_ctx *ctx)
+{
+ u32 mode, total_size;
+ struct vdec_pic_info *picinfo = &ctx->picinfo;
+ struct aml_vpp_cfg_infos *vpp = &ctx->vpp_cfg;
+ struct aml_ge2d_cfg_infos *ge2d = &ctx->ge2d_cfg;
+
+ if (vpp_needed(ctx, &mode)) {
+ vpp->mode = mode;
+ vpp->fmt = ctx->cap_pix_fmt;
+ vpp->is_drm = ctx->is_drm_mode;
+ vpp->buf_size = aml_v4l2_vpp_get_buf_num(vpp->mode)
+ + picinfo->vpp_margin;
+
+ if (picinfo->field == V4L2_FIELD_NONE) {
+ vpp->is_prog = true;
+ vpp->buf_size = 0;
+ } else {
+ vpp->is_prog = false;
+ /* for between with dec & vpp. */
+ picinfo->dpb_margin = 2;
+ }
+
+ if (vpp->is_prog &&
+ !vpp->enable_local_buf &&
+ bypass_progressive) {
+ vpp->is_bypass_p = true;
+ }
+ ctx->vpp_is_need = true;
+ } else {
+ vpp->buf_size = 0;
+ ctx->vpp_is_need = false;
+ }
+
+ if (ge2d_needed(ctx, &mode)) {
+ ge2d->mode = mode;
+ ge2d->buf_size = 4 + picinfo->dpb_margin;
+ ctx->ge2d_is_need = true;
+ picinfo->dpb_margin = 2;
+ } else {
+ ge2d->buf_size = 0;
+ ctx->ge2d_is_need = false;
+ }
+
+ ctx->dpb_size = picinfo->dpb_frames + picinfo->dpb_margin;
+ ctx->vpp_size = vpp->buf_size;
+ ctx->ge2d_size = ge2d->buf_size;
+
+ total_size = ctx->dpb_size + ctx->vpp_size + ctx->ge2d_size;
+
+ if (total_size > V4L_CAP_BUFF_MAX) {
+ if (ctx->ge2d_size) {
+ ctx->dpb_size = V4L_CAP_BUFF_MAX - ctx->ge2d_size - ctx->vpp_size;
+ } else if (ctx->vpp_size) {
+ ctx->dpb_size = V4L_CAP_BUFF_MAX - ctx->vpp_size;
+ } else {
+ ctx->dpb_size = V4L_CAP_BUFF_MAX;
+ }
+ picinfo->dpb_margin = ctx->dpb_size - picinfo->dpb_frames;
+ total_size = V4L_CAP_BUFF_MAX;
+ }
+ vdec_if_set_param(ctx, SET_PARAM_PIC_INFO, picinfo);
+
+ return total_size;
+}
+
+void aml_vdec_pic_info_update(struct aml_vcodec_ctx *ctx)
+{
+ if (vdec_if_get_param(ctx, GET_PARAM_PIC_INFO, &ctx->last_decoded_picinfo)) {
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_ERROR,
+ "Cannot get param : GET_PARAM_PICTURE_INFO ERR\n");
+ return;
+ }
+
+ if (ctx->last_decoded_picinfo.visible_width == 0 ||
+ ctx->last_decoded_picinfo.visible_height == 0 ||
+ ctx->last_decoded_picinfo.coded_width == 0 ||
+ ctx->last_decoded_picinfo.coded_height == 0) {
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_ERROR,
+ "Cannot get correct pic info\n");
+ return;
+ }
+
+ /*if ((ctx->last_decoded_picinfo.visible_width == ctx->picinfo.visible_width) ||
+ (ctx->last_decoded_picinfo.visible_height == ctx->picinfo.visible_height))
+ return;*/
+
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_EXINFO,
+ "new(%d,%d), old(%d,%d), real(%d,%d)\n",
+ ctx->last_decoded_picinfo.visible_width,
+ ctx->last_decoded_picinfo.visible_height,
+ ctx->picinfo.visible_width, ctx->picinfo.visible_height,
+ ctx->last_decoded_picinfo.coded_width,
+ ctx->last_decoded_picinfo.coded_width);
+
+ ctx->picinfo = ctx->last_decoded_picinfo;
+
+ if (ctx->vpp_is_need)
+ ctx->vpp_cfg.is_vpp_reset = true;
+
+ v4l_buf_size_decision(ctx);
+
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_PRINFO,
+ "Update picture buffer count: dec:%u, vpp:%u, ge2d:%u, margin:%u, total:%u\n",
+ ctx->picinfo.dpb_frames, ctx->vpp_size, ctx->ge2d_size,
+ ctx->picinfo.dpb_margin,
+ CTX_BUF_TOTAL(ctx));
+}
+
+void vdec_frame_buffer_release(void *data)
+{
+ struct file_private_data *priv_data =
+ (struct file_private_data *) data;
+ struct aml_vcodec_ctx *ctx = (struct aml_vcodec_ctx *)
+ priv_data->v4l_dec_ctx;
+ struct aml_video_dec_buf *vb = (struct aml_video_dec_buf *)
+ priv_data->vb_handle;
+ struct uvm_hook_mod_info *uvm = NULL;
+
+ if (ctx && ctx->uvm_proxy) {
+ uvm = &ctx->uvm_proxy[vb->internal_index];
+ uvm->free(uvm->arg);
+ }
+
+ memset(data, 0, sizeof(struct file_private_data));
+ kfree(data);
+}
+
+static void v4l2_buff_done(struct vb2_v4l2_buffer *buf, enum vb2_buffer_state state)
+{
+ struct aml_vcodec_ctx *ctx = vb2_get_drv_priv(buf->vb2_buf.vb2_queue);
+
+ mutex_lock(&ctx->buff_done_lock);
+ if (buf->vb2_buf.state != VB2_BUF_STATE_ACTIVE) {
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_PROT, "vb is not active state = %d!\n",
+ buf->vb2_buf.state);
+ mutex_unlock(&ctx->buff_done_lock);
+ return;
+ }
+ v4l2_m2m_buf_done(buf, state);
+ mutex_unlock(&ctx->buff_done_lock);
+}
+
+static void comp_buf_set_vframe(struct aml_vcodec_ctx *ctx,
+ struct vb2_buffer *vb,
+ struct vframe_s *vf)
+{
+ dmabuf_set_vframe(vb->planes[0].dbuf, vf, VF_SRC_DECODER);
+}
+
+static void fb_map_table_clean(struct aml_vcodec_ctx *ctx)
+{
+ int i;
+ ulong flags;
+
+ flags = aml_vcodec_ctx_lock(ctx);
+
+ for (i = 0; i < ARRAY_SIZE(ctx->fb_map); i++) {
+ ctx->fb_map[i].addr = 0;
+ ctx->fb_map[i].vframe = NULL;
+ ctx->fb_map[i].task = NULL;
+ ctx->fb_map[i].icomp = 0;
+ }
+
+ aml_vcodec_ctx_unlock(ctx, flags);
+
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_PRINFO, "%s done\n", __func__);
+}
+
+static void fb_map_table_hold(struct aml_vcodec_ctx *ctx,
+ struct vb2_buffer *vb,
+ struct vframe_s *vf,
+ struct task_chain_s *task,
+ u32 icomp)
+{
+ int i;
+ ulong addr, flags;
+
+ flags = aml_vcodec_ctx_lock(ctx);
+
+ addr = vb2_dma_contig_plane_dma_addr(vb, 0);
+
+ for (i = 0; i < ARRAY_SIZE(ctx->fb_map); i++) {
+ if (!ctx->fb_map[i].addr ||
+ (addr == ctx->fb_map[i].addr)) {
+ ctx->fb_map[i].task = task;
+ ctx->fb_map[i].addr = addr;
+ ctx->fb_map[i].vframe = vf;
+ ctx->fb_map[i].icomp = icomp;
+
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_EXINFO,
+ "%s, task:%px, vf:%px, addr:%lx, icomp:%u\n",
+ __func__, task, vf, addr, icomp);
+ break;
+ }
+ }
+
+ aml_vcodec_ctx_unlock(ctx, flags);
+
+ if (i >= ARRAY_SIZE(ctx->fb_map)) {
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_ERROR,
+ "%s, table is full. addr:%lx, vf:%px\n",
+ __func__, addr, vf);
+ }
+}
+
+static void fb_map_table_fetch(struct aml_vcodec_ctx *ctx,
+ struct vb2_buffer *vb,
+ struct vframe_s **vf,
+ struct task_chain_s **task,
+ u32 *icomp)
+{
+ int i;
+ ulong addr, flags;
+
+ flags = aml_vcodec_ctx_lock(ctx);
+
+ addr = vb2_dma_contig_plane_dma_addr(vb, 0);
+
+ for (i = 0; i < ARRAY_SIZE(ctx->fb_map); i++) {
+ if (addr == ctx->fb_map[i].addr) {
+ *task = ctx->fb_map[i].task;
+ *vf = ctx->fb_map[i].vframe;
+ *icomp = ctx->fb_map[i].icomp;
+
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_EXINFO,
+ "%s, task:%px, vf:%px, addr:%lx, icomp:%u\n",
+ __func__, task, vf, addr, *icomp);
+
+ ctx->fb_map[i].task = NULL;
+ ctx->fb_map[i].vframe = NULL;
+ ctx->fb_map[i].addr = 0;
+ ctx->fb_map[i].icomp = 0;
+ break;
+ }
+ }
+
+ aml_vcodec_ctx_unlock(ctx, flags);
+
+ if (i >= ARRAY_SIZE(ctx->fb_map)) {
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_BUFMGR,
+ "%s, there is new addr:%lx.\n",
+ __func__, addr);
+ }
+}
+
+static bool is_fb_mapped(struct aml_vcodec_ctx *ctx, ulong addr)
+{
+ int i;
+ ulong flags;
+
+ flags = aml_vcodec_ctx_lock(ctx);
+
+ for (i = 0; i < ARRAY_SIZE(ctx->fb_map); i++) {
+ if (addr == ctx->fb_map[i].addr)
+ break;
+ }
+
+ aml_vcodec_ctx_unlock(ctx, flags);
+
+ if (i >= ARRAY_SIZE(ctx->fb_map)) {
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_EXINFO,
+ "%s, addr:%lx isn't output.\n",
+ __func__, addr);
+ return false;
+ }
+
+ return true;
+}
+
+ static void post_frame_to_upper(struct aml_vcodec_ctx *ctx,
+ struct vdec_v4l2_buffer *fb)
+{
+ struct aml_video_dec_buf *dstbuf =
+ container_of(fb, struct aml_video_dec_buf, frame_buffer);
+ struct vb2_buffer *vb2_buf = &dstbuf->vb.vb2_buf;
+ struct vframe_s *vf = fb->vframe;
+ struct vb2_v4l2_buffer *vb2_v4l2 = NULL;
+
+ vf->index_disp = ctx->index_disp;
+ ctx->index_disp++;
+ ctx->post_to_upper_done = false;
+
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_OUTPUT,
+ "OUT_BUFF (%s, st:%d, seq:%d) vb:(%d, %px), vf:(%d, %px), ts:%lld, "
+ "Y:(%lx, %u) C/U:(%lx, %u) V:(%lx, %u)\n",
+ ctx->ada_ctx->frm_name, fb->status, vf->index_disp,
+ vb2_buf->index, vb2_buf,
+ vf->index & 0xff, vf,
+ vf->timestamp,
+ fb->m.mem[0].addr, fb->m.mem[0].size,
+ fb->m.mem[1].addr, fb->m.mem[1].size,
+ fb->m.mem[2].addr, fb->m.mem[2].size);
+
+ vb2_v4l2 = container_of(vb2_buf, struct vb2_v4l2_buffer, vb2_buf);
+
+ if (dstbuf->frame_buffer.num_planes == 1) {
+ vb2_set_plane_payload(vb2_buf, 0, fb->m.mem[0].bytes_used);
+ } else if (dstbuf->frame_buffer.num_planes == 2) {
+ vb2_set_plane_payload(vb2_buf, 0, fb->m.mem[0].bytes_used);
+ vb2_set_plane_payload(vb2_buf, 1, fb->m.mem[1].bytes_used);
+ }
+ vb2_buf->timestamp = vf->timestamp;
+ dstbuf->vb.flags |= vf->frame_type;
+
+ if ((ctx->picinfo.field == V4L2_FIELD_INTERLACED) && (!ctx->vpp_is_need)) {
+ vb2_v4l2->field = V4L2_FIELD_INTERLACED;
+ }
+
+ do {
+ unsigned int dw_mode = VDEC_DW_NO_AFBC;
+ struct file *fp;
+ char file_name[32] = {0};
+
+ if (!dump_capture_frame || ctx->is_drm_mode)
+ break;
+ if (vdec_if_get_param(ctx, GET_PARAM_DW_MODE, &dw_mode))
+ break;
+ if (dw_mode == VDEC_DW_AFBC_ONLY)
+ break;
+
+ snprintf(file_name, 32, "/data/dec_dump_%ux%u.raw", vf->width, vf->height);
+
+ fp = filp_open(file_name,
+ O_CREAT | O_RDWR | O_LARGEFILE | O_APPEND, 0600);
+
+ if (!IS_ERR(fp)) {
+ struct vb2_buffer *vb = vb2_buf;
+
+ kernel_write(fp,vb2_plane_vaddr(vb, 0),vb->planes[0].length, 0);
+ if (dstbuf->frame_buffer.num_planes == 2)
+ kernel_write(fp,vb2_plane_vaddr(vb, 1),
+ vb->planes[1].length, 0);
+ pr_info("dump idx: %d %dx%d\n", dump_capture_frame, vf->width, vf->height);
+ dump_capture_frame--;
+ filp_close(fp, NULL);
+ }
+ } while(0);
+
+ ATRACE_COUNTER("VC_OUT_VSINK-1.submit", vb2_buf->index);
+ ATRACE_COUNTER("V_ST_VSINK-input_buffering", vdec_frame_number(ctx->ada_ctx));
+
+ if (vf->flag & VFRAME_FLAG_EMPTY_FRAME_V4L) {
+ dstbuf->vb.flags = V4L2_BUF_FLAG_LAST;
+ if (dstbuf->frame_buffer.num_planes == 1) {
+ vb2_set_plane_payload(vb2_buf, 0, 0);
+ } else if (dstbuf->frame_buffer.num_planes == 2) {
+ vb2_set_plane_payload(vb2_buf, 0, 0);
+ vb2_set_plane_payload(vb2_buf, 1, 0);
+ }
+ ctx->has_receive_eos = true;
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_BUFMGR,
+ "recevie a empty frame. idx: %d, state: %d\n",
+ vb2_buf->index, vb2_buf->state);
+ }
+
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_EXINFO,
+ "receive vbuf idx: %d, state: %d\n",
+ vb2_buf->index, vb2_buf->state);
+
+ if (vf->flag & VFRAME_FLAG_EMPTY_FRAME_V4L) {
+ if (ctx->v4l_resolution_change) {
+ /* make the run to stanby until new buffs to enque. */
+ ctx->v4l_codec_dpb_ready = false;
+ ctx->reset_flag = V4L_RESET_MODE_LIGHT;
+ ctx->vpp_cfg.res_chg = true;
+
+ /*
+ * After all buffers containing decoded frames from
+ * before the resolution change point ready to be
+ * dequeued on the CAPTURE queue, the driver sends a
+ * V4L2_EVENT_SOURCE_CHANGE event for source change
+ * type V4L2_EVENT_SRC_CH_RESOLUTION, also the upper
+ * layer will get new information from cts->picinfo.
+ */
+ aml_vdec_dispatch_event(ctx, V4L2_EVENT_SRC_CH_RESOLUTION);
+ } else
+ aml_vdec_dispatch_event(ctx, V4L2_EVENT_SEND_EOS);
+ }
+
+ if (dstbuf->vb.vb2_buf.state == VB2_BUF_STATE_ACTIVE) {
+ /* binding vframe handle. */
+ if (get_cpu_major_id() == AM_MESON_CPU_MAJOR_ID_T7) {
+ if (vf->canvas0_config[0].block_mode == CANVAS_BLKMODE_LINEAR) {
+ if ((ctx->output_pix_fmt != V4L2_PIX_FMT_H264) &&
+ (ctx->output_pix_fmt != V4L2_PIX_FMT_MPEG1) &&
+ (ctx->output_pix_fmt != V4L2_PIX_FMT_MPEG2) &&
+ (ctx->output_pix_fmt != V4L2_PIX_FMT_MPEG4) &&
+ (ctx->output_pix_fmt != V4L2_PIX_FMT_MJPEG)) {
+ vf->flag |= VFRAME_FLAG_VIDEO_LINEAR;
+ }
+ else {
+ if (fb->status == FB_ST_GE2D)
+ vf->flag |= VFRAME_FLAG_VIDEO_LINEAR;
+ }
+ }
+ } else {
+ if (vf->canvas0_config[0].block_mode == CANVAS_BLKMODE_LINEAR)
+ vf->flag |= VFRAME_FLAG_VIDEO_LINEAR;
+ }
+
+ vf->omx_index = vf->index_disp;
+ dstbuf->privdata.vf = *vf;
+
+ if (vb2_buf->memory == VB2_MEMORY_DMABUF) {
+ struct dma_buf * dma;
+
+ dma = dstbuf->vb.vb2_buf.planes[0].dbuf;
+ if (dmabuf_is_uvm(dma)) {
+ /* only Y will contain vframe */
+ comp_buf_set_vframe(ctx, vb2_buf, vf);
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_EXINFO,
+ "set vf(%px) into %dth buf\n",
+ vf, vb2_buf->index);
+ }
+ }
+
+ fb_map_table_hold(ctx, vb2_buf, vf, fb->task, dstbuf->internal_index);
+
+ v4l2_buff_done(&dstbuf->vb, VB2_BUF_STATE_DONE);
+
+ fb->status = FB_ST_DISPLAY;
+ }
+
+ mutex_lock(&ctx->state_lock);
+ if (ctx->state == AML_STATE_FLUSHING &&
+ ctx->has_receive_eos) {
+ ctx->state = AML_STATE_FLUSHED;
+ ATRACE_COUNTER("V_ST_VSINK-state", ctx->state);
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_STATE,
+ "vcodec state (AML_STATE_FLUSHED)\n");
+ }
+ mutex_unlock(&ctx->state_lock);
+
+ if (ctx->post_to_upper_done == false) {
+ ctx->post_to_upper_done = true;
+ wake_up_interruptible(&ctx->post_done_wq);
+ }
+
+ ctx->decoded_frame_cnt++;
+}
+
+static void fill_capture_done_cb(void *v4l_ctx, void *fb_ctx)
+{
+ struct aml_vcodec_ctx *ctx =
+ (struct aml_vcodec_ctx *)v4l_ctx;
+ struct vdec_v4l2_buffer *fb =
+ (struct vdec_v4l2_buffer *)fb_ctx;
+ struct aml_video_dec_buf *aml_buff =
+ container_of(fb, struct aml_video_dec_buf, frame_buffer);
+ struct vb2_v4l2_buffer *vb = &aml_buff->vb;
+
+ if (ctx->is_stream_off) {
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_INPUT,
+ "ignore buff idx: %d streamoff\n", fb->buf_idx);
+ return;
+ }
+
+ ATRACE_COUNTER("VC_OUT_VSINK-0.receive", vb->vb2_buf.index);
+
+ mutex_lock(&ctx->capture_buffer_lock);
+ kfifo_put(&ctx->capture_buffer, vb);
+ mutex_unlock(&ctx->capture_buffer_lock);
+
+ aml_thread_post_task(ctx, AML_THREAD_CAPTURE);
+}
+
+static void update_vdec_buf_plane(struct aml_vcodec_ctx *ctx,
+ struct vdec_v4l2_buffer *fb,
+ struct vb2_buffer *vb)
+{
+ int i;
+ char plane_n[3] = {'Y','U','V'};
+
+ fb->num_planes = vb->num_planes;
+ fb->buf_idx = vb->index;
+
+ for (i = 0 ; i < vb->num_planes ; i++) {
+ fb->m.mem[i].addr = vb2_dma_contig_plane_dma_addr(vb, i);
+ fb->m.mem[i].dbuf = vb->planes[i].dbuf;
+ if (i == 0) {
+ //Y
+ if (vb->num_planes == 1) {
+ fb->m.mem[0].size = ctx->picinfo.y_len_sz +
+ ctx->picinfo.c_len_sz;
+ fb->m.mem[0].offset = ctx->picinfo.y_len_sz;
+ } else {
+ fb->m.mem[0].size = ctx->picinfo.y_len_sz;
+ fb->m.mem[0].offset = 0;
+ }
+ } else {
+ if (vb->num_planes == 2) {
+ //UV
+ fb->m.mem[1].size = ctx->picinfo.c_len_sz;
+ fb->m.mem[1].offset = ctx->picinfo.c_len_sz >> 1;
+ } else {
+ fb->m.mem[i].size = ctx->picinfo.c_len_sz >> 1;
+ fb->m.mem[i].offset = 0;
+ }
+ }
+
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_BUFMGR,
+ "idx: %u, %c:(0x%lx, %d)\n", vb->index,
+ plane_n[i], fb->m.mem[i].addr, fb->m.mem[i].size);
+ }
+}
+
+static bool fb_token_insert(struct aml_vcodec_ctx *ctx,
+ ulong *token)
+{
+ ulong vb_handle;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(ctx->token_table); i++) {
+ if (ctx->token_table[i] &&
+ (ctx->token_table[i] == *token)) {
+ return true;
+ }
+ }
+
+ if (!v4l2_m2m_num_dst_bufs_ready(ctx->m2m_ctx))
+ return false;
+
+ vb_handle = (ulong)v4l2_m2m_dst_buf_remove(ctx->m2m_ctx);
+
+ for (i = 0; i < ARRAY_SIZE(ctx->token_table); i++) {
+ if (!ctx->token_table[i]) {
+ ctx->token_table[i] = vb_handle;
+ break;
+ }
+ }
+
+ if (i >= ARRAY_SIZE(ctx->token_table)) {
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_ERROR,
+ "%s, table is full. token:%lx\n",
+ __func__, vb_handle);
+ return false;
+ }
+
+ *token = vb_handle;
+
+ return true;
+}
+
+static void fb_token_remove(struct aml_vcodec_ctx *ctx,
+ ulong token)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(ctx->token_table); i++) {
+ if (token == ctx->token_table[i]) {
+ ctx->token_table[i] = 0;
+ break;
+ }
+ }
+
+ if (i >= ARRAY_SIZE(ctx->token_table)) {
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_ERROR,
+ "%s, remove token err, token:%lx.\n",
+ __func__, token);
+ }
+}
+
+static void fb_token_clean(struct aml_vcodec_ctx *ctx)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(ctx->token_table); i++) {
+ ctx->token_table[i] = 0;
+ }
+
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_PRINFO, "%s done\n", __func__);
+}
+
+static bool fb_buff_query(struct aml_fb_ops *fb, ulong *token)
+{
+ struct aml_vcodec_ctx *ctx =
+ container_of(fb, struct aml_vcodec_ctx, fb_ops);
+ struct vb2_queue * que = v4l2_m2m_get_dst_vq(ctx->m2m_ctx);
+ bool ret = false;
+ ulong flags;
+
+ if (!que->streaming)
+ return false;
+
+ flags = aml_vcodec_ctx_lock(ctx);
+
+ ret = fb_token_insert(ctx, token);
+
+ aml_vcodec_ctx_unlock(ctx, flags);
+
+ return ret;
+}
+
+static void aml_task_chain_remove(struct aml_vcodec_ctx *ctx)
+{
+ struct task_chain_s *task, *tmp;
+
+ list_for_each_entry_safe(task, tmp, &ctx->task_chain_pool, node) {
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_PRINFO,
+ "remove task chain:%d, %px\n", task->id, task);
+ list_del(&task->node);
+ task_chain_clean(task);
+ task_chain_release(task);
+ }
+}
+
+static struct task_ops_s *get_v4l_sink_ops(void);
+
+static void aml_creat_pipeline(struct aml_vcodec_ctx *ctx,
+ struct vdec_v4l2_buffer *fb,
+ u32 requester)
+{
+ struct task_chain_s *task = fb->task;
+ /*
+ * line 1: dec <==> vpp <==> v4l-sink, for P / P + DI.NR.
+ * line 2: dec <==> vpp, vpp <==> v4l-sink, for I / I + DI.NR.
+ * line 3: dec <==> v4l-sink, only for P.
+ * line 4: dec <==> ge2d, ge2d <==> v4l-sink, used for fmt convert.
+ * line 5: dec <==> ge2d, ge2d <==>vpp, vpp <==> v4l-sink.
+ * line 6: dec <==> ge2d, ge2d <==> vpp <==> v4l-sink.
+ */
+
+ switch (requester) {
+ case AML_FB_REQ_DEC:
+ if (ctx->ge2d) {
+ /* dec <==> ge2d. */
+ task->attach(task, get_ge2d_ops(), ctx->ge2d);
+ } else if (ctx->vpp) {
+ if (ctx->vpp->is_prog) {
+ /* dec <==> vpp <==> v4l-sink. */
+ task->attach(task, get_v4l_sink_ops(), ctx);
+ task->attach(task, get_vpp_ops(), ctx->vpp);
+ } else {
+ /* dec <==> vpp. */
+ task->attach(task, get_vpp_ops(), ctx->vpp);
+ }
+ } else {
+ /* dec <==> v4l-sink. */
+ task->attach(task, get_v4l_sink_ops(), ctx);
+ }
+ break;
+
+ case AML_FB_REQ_GE2D:
+ if (ctx->vpp) {
+ if (ctx->vpp->is_prog) {
+ /* ge2d <==> vpp <==> v4l-sink. */
+ task->attach(task, get_v4l_sink_ops(), ctx);
+ task->attach(task, get_vpp_ops(), ctx->vpp);
+ task->attach(task, get_ge2d_ops(), ctx->ge2d);
+ } else {
+ /* ge2d <==> vpp. */
+ task->attach(task, get_vpp_ops(), ctx->vpp);
+ task->attach(task, get_ge2d_ops(), ctx->ge2d);
+ }
+ } else {
+ /* ge2d <==> v4l-sink. */
+ task->attach(task, get_v4l_sink_ops(), ctx);
+ task->attach(task, get_ge2d_ops(), ctx->ge2d);
+ }
+ break;
+
+ case AML_FB_REQ_VPP:
+ /* vpp <==> v4l-sink. */
+ task->attach(task, get_v4l_sink_ops(), ctx);
+ task->attach(task, get_vpp_ops(), ctx->vpp);
+ break;
+
+ default:
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_ERROR,
+ "unsupport requester %x\n", requester);
+ }
+}
+
+static int fb_buff_from_queue(struct aml_fb_ops *fb_ops,
+ ulong token, struct vdec_v4l2_buffer **out_fb,
+ u32 requester)
+{
+ struct aml_vcodec_ctx *ctx =
+ container_of(fb_ops, struct aml_vcodec_ctx, fb_ops);
+ struct aml_video_dec_buf *aml_buf = NULL;
+ struct vb2_v4l2_buffer *v4l_buf = NULL;
+ struct vdec_v4l2_buffer *fb = NULL;
+ u32 buf_status = 0;
+ ulong flags;
+
+ flags = aml_vcodec_ctx_lock(ctx);
+
+ if (ctx->is_stream_off) {
+ aml_vcodec_ctx_unlock(ctx, flags);
+ return -1;
+ }
+
+ v4l_buf = (struct vb2_v4l2_buffer *) token;
+ if (!v4l_buf) {
+ aml_vcodec_ctx_unlock(ctx, flags);
+ return -1;
+ }
+
+ aml_buf = container_of(v4l_buf, struct aml_video_dec_buf, vb);
+
+ fb = &aml_buf->frame_buffer;
+ fb->buf_idx = v4l_buf->vb2_buf.index;
+ aml_buf->used = true;
+ ctx->buf_used_count++;
+
+ if (requester == AML_FB_REQ_VPP) {
+ buf_status = V4L_CAP_BUFF_IN_VPP;
+ ctx->cap_pool.vpp++;
+ } else if (requester == AML_FB_REQ_DEC) {
+ buf_status = V4L_CAP_BUFF_IN_DEC;
+ ctx->cap_pool.dec++;
+ } else if (requester == AML_FB_REQ_GE2D) {
+ buf_status = V4L_CAP_BUFF_IN_GE2D;
+ ctx->cap_pool.ge2d++;
+ }
+
+ ctx->cap_pool.seq[ctx->cap_pool.out++] =
+ (buf_status << 16 | fb->buf_idx);
+
+ update_vdec_buf_plane(ctx, fb, &v4l_buf->vb2_buf);
+
+ aml_creat_pipeline(ctx, fb, requester);
+
+ fb_token_remove(ctx, token);
+
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_BUFMGR,
+ "vid:%d, task:%px, phy:%lx, state:%d, ready:%d, requester:%d\n",
+ fb->buf_idx, fb->task, fb->m.mem[0].addr, v4l_buf->vb2_buf.state,
+ v4l2_m2m_num_dst_bufs_ready(ctx->m2m_ctx), requester);
+
+ ATRACE_COUNTER("VC_IN_VSINK-3.require", v4l_buf->vb2_buf.index);
+
+ *out_fb = fb;
+
+ aml_vcodec_ctx_unlock(ctx, flags);
+
+ return 0;
+}
+
+static struct task_ops_s v4l_sink_ops = {
+ .type = TASK_TYPE_V4L_SINK,
+ .fill_buffer = fill_capture_done_cb,
+};
+
+static struct task_ops_s *get_v4l_sink_ops(void)
+{
+ return &v4l_sink_ops;
+}
+
+void aml_vdec_basic_information(struct aml_vcodec_ctx *ctx)
+{
+ struct aml_q_data *outq = NULL;
+ struct aml_q_data *capq = NULL;
+ struct vdec_pic_info pic;
+
+ if (vdec_if_get_param(ctx, GET_PARAM_PIC_INFO, &pic)) {
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_ERROR,
+ "get pic info err\n");
+ return;
+ }
+
+ outq = aml_vdec_get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
+ capq = aml_vdec_get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
+
+ pr_info("\n==== Show Basic Information ==== \n");
+
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_PRINFO,
+ "Format : %s\n",
+ outq->fmt->name);
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_PRINFO,
+ "Color space: %s\n",
+ capq->fmt->name);
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_PRINFO,
+ "Scan type : %s\n",
+ (pic.field == V4L2_FIELD_NONE) ?
+ "Progressive" : "Interlaced");
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_PRINFO,
+ "Resolution : visible(%dx%d), coded(%dx%d)\n",
+ pic.visible_width, pic.visible_height,
+ pic.coded_width, pic.coded_height);
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_PRINFO,
+ "Buffer num : dec:%d, vpp:%d, ge2d:%d, margin:%d, total:%d\n",
+ ctx->picinfo.dpb_frames, ctx->vpp_size, ctx->ge2d_size,
+ ctx->picinfo.dpb_margin, CTX_BUF_TOTAL(ctx));
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_PRINFO,
+ "Config : dw:%d, drm:%d, byp:%d, lc:%d, nr:%d, ge2d:%x\n",
+ ctx->config.parm.dec.cfg.double_write_mode,
+ ctx->is_drm_mode,
+ ctx->vpp_cfg.is_bypass_p,
+ ctx->vpp_cfg.enable_local_buf,
+ ctx->vpp_cfg.enable_nr,
+ ctx->ge2d_cfg.mode);
+}
+
+void aml_buffer_status(struct aml_vcodec_ctx *ctx)
+{
+ struct vb2_v4l2_buffer *vb = NULL;
+ struct aml_video_dec_buf *aml_buff = NULL;
+ struct vdec_v4l2_buffer *fb = NULL;
+ struct vb2_queue *q = NULL;
+ ulong flags;
+ int i;
+
+ flags = aml_vcodec_ctx_lock(ctx);
+
+ q = v4l2_m2m_get_vq(ctx->m2m_ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
+ if (!q->streaming) {
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_ERROR,
+ "can't achieve buffers status before start streaming.\n");
+ }
+
+ pr_info("\n==== Show Buffer Status ======== \n");
+ for (i = 0; i < q->num_buffers; ++i) {
+ vb = to_vb2_v4l2_buffer(q->bufs[i]);
+ aml_buff = container_of(vb, struct aml_video_dec_buf, vb);
+ fb = &aml_buff->frame_buffer;
+
+ /* print out task chain status. */
+ task_chain_show(fb->task);
+ }
+
+ aml_vcodec_ctx_unlock(ctx, flags);
+}
+
+static void aml_check_dpb_ready(struct aml_vcodec_ctx *ctx)
+{
+ if (!ctx->v4l_codec_dpb_ready) {
+ /*
+ * make sure enough dst bufs for decoding.
+ */
+ if ((ctx->dpb_size) && (ctx->cap_pool.in >= ctx->dpb_size))
+ ctx->v4l_codec_dpb_ready = true;
+
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_BUFMGR,
+ "dpb: %d, vpp: %d, ready: %d, used: %d, dpb is ready: %s\n",
+ ctx->dpb_size, ctx->vpp_size,
+ v4l2_m2m_num_dst_bufs_ready(ctx->m2m_ctx),
+ ctx->cap_pool.out, ctx->v4l_codec_dpb_ready ? "yes" : "no");
+ }
+}
+
+static void reconfig_vpp_status(struct aml_vcodec_ctx *ctx)
+{
+ if (bypass_nr_flag &&
+ !ctx->vpp_cfg.is_prog &&
+ ((ctx->vpp_cfg.mode == VPP_MODE_NOISE_REDUC_LOCAL) ||
+ (ctx->vpp_cfg.mode == VPP_MODE_NOISE_REDUC))) {
+ ctx->vpp_cfg.enable_nr = 0;
+ ctx->vpp_cfg.enable_local_buf = 0;
+
+ ctx->vpp_cfg.mode = VPP_MODE_DI;
+ }
+}
+
+static int is_vdec_ready(struct aml_vcodec_ctx *ctx)
+{
+ struct aml_vcodec_dev *dev = ctx->dev;
+
+ if (!is_input_ready(ctx->ada_ctx)) {
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_ERROR,
+ "the decoder input has not ready.\n");
+ v4l2_m2m_job_finish(dev->m2m_dev_dec, ctx->m2m_ctx);
+ return 0;
+ }
+
+ if (ctx->state == AML_STATE_PROBE) {
+ mutex_lock(&ctx->state_lock);
+ if (ctx->state == AML_STATE_PROBE) {
+ ctx->state = AML_STATE_READY;
+ ATRACE_COUNTER("V_ST_VSINK-state", ctx->state);
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_STATE,
+ "vcodec state (AML_STATE_READY)\n");
+ }
+ mutex_unlock(&ctx->state_lock);
+ }
+
+ mutex_lock(&ctx->state_lock);
+ if (ctx->state == AML_STATE_READY) {
+ if (ctx->m2m_ctx->out_q_ctx.q.streaming &&
+ ctx->m2m_ctx->cap_q_ctx.q.streaming) {
+ ctx->state = AML_STATE_ACTIVE;
+ ATRACE_COUNTER("V_ST_VSINK-state", ctx->state);
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_STATE,
+ "vcodec state (AML_STATE_ACTIVE)\n");
+ }
+ }
+ mutex_unlock(&ctx->state_lock);
+
+ /* check dpb ready */
+ //aml_check_dpb_ready(ctx);
+
+ return 1;
+}
+
+static bool is_enough_work_items(struct aml_vcodec_ctx *ctx)
+{
+ struct aml_vcodec_dev *dev = ctx->dev;
+
+ if (vdec_frame_number(ctx->ada_ctx) >= WORK_ITEMS_MAX) {
+ v4l2_m2m_job_finish(dev->m2m_dev_dec, ctx->m2m_ctx);
+ return false;
+ }
+
+ return true;
+}
+
+static void aml_wait_buf_ready(struct aml_vcodec_ctx *ctx)
+{
+ ulong expires;
+
+ expires = jiffies + msecs_to_jiffies(1000);
+ while (!ctx->v4l_codec_dpb_ready) {
+ u32 ready_num = 0;
+
+ if (time_after(jiffies, expires)) {
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_ERROR,
+ "the DPB state has not ready.\n");
+ break;
+ }
+
+ ready_num = v4l2_m2m_num_dst_bufs_ready(ctx->m2m_ctx);
+ if ((ready_num + ctx->buf_used_count) >= CTX_BUF_TOTAL(ctx))
+ ctx->v4l_codec_dpb_ready = true;
+ }
+}
+
+void dmabuff_recycle_worker(struct work_struct *work)
+{
+ struct aml_vcodec_ctx *ctx =
+ container_of(work, struct aml_vcodec_ctx, dmabuff_recycle_work);
+ struct vb2_v4l2_buffer *vb = NULL;
+ struct aml_video_dec_buf *buf = NULL;
+ unsigned long flags;
+
+ for (;;) {
+ spin_lock_irqsave(&ctx->dmabuff_recycle_lock, flags);
+ if (!kfifo_get(&ctx->dmabuff_recycle, &vb)) {
+ spin_unlock_irqrestore(&ctx->dmabuff_recycle_lock, flags);
+ break;
+ }
+ spin_unlock_irqrestore(&ctx->dmabuff_recycle_lock, flags);
+
+ buf = container_of(vb, struct aml_video_dec_buf, vb);
+
+ if (ctx->is_out_stream_off)
+ continue;
+
+ if (wait_event_interruptible_timeout
+ (ctx->wq, buf->used == false,
+ msecs_to_jiffies(200)) == 0) {
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_ERROR,
+ "wait recycle dma buff timeout.\n");
+ }
+
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_INPUT,
+ "recycle buff idx: %d, vbuf: %lx\n", vb->vb2_buf.index,
+ (ulong)sg_dma_address(buf->out_sgt->sgl));
+
+ ATRACE_COUNTER("VO_OUT_VSINK-2.write_secure_end", vb->vb2_buf.index);
+
+ if (vb->vb2_buf.state != VB2_BUF_STATE_ERROR)
+ v4l2_buff_done(vb, buf->error ? VB2_BUF_STATE_ERROR :
+ VB2_BUF_STATE_DONE);
+ }
+}
+
+void aml_recycle_dma_buffers(struct aml_vcodec_ctx *ctx, u32 handle)
+{
+ struct aml_vcodec_dev *dev = ctx->dev;
+ struct vb2_v4l2_buffer *vb = NULL;
+ struct vb2_queue *q = NULL;
+ int index = handle & 0xf;
+ unsigned long flags;
+
+ if (ctx->is_out_stream_off) {
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_INPUT,
+ "ignore buff idx: %d streamoff\n", index);
+ return;
+ }
+
+ q = v4l2_m2m_get_vq(ctx->m2m_ctx,
+ V4L2_BUF_TYPE_VIDEO_OUTPUT);
+
+ vb = to_vb2_v4l2_buffer(q->bufs[index]);
+
+ spin_lock_irqsave(&ctx->dmabuff_recycle_lock, flags);
+ kfifo_put(&ctx->dmabuff_recycle, vb);
+ spin_unlock_irqrestore(&ctx->dmabuff_recycle_lock, flags);
+
+ queue_work(dev->decode_workqueue, &ctx->dmabuff_recycle_work);
+}
+
+static void aml_vdec_worker(struct work_struct *work)
+{
+ struct aml_vcodec_ctx *ctx =
+ container_of(work, struct aml_vcodec_ctx, decode_work);
+ struct aml_vcodec_dev *dev = ctx->dev;
+ struct aml_video_dec_buf *aml_buf;
+ struct vb2_v4l2_buffer *vb2_v4l2;
+ struct vb2_buffer *vb;
+ struct aml_vcodec_mem buf;
+ bool res_chg = false;
+ int ret;
+
+ if (ctx->state < AML_STATE_INIT ||
+ ctx->state > AML_STATE_FLUSHED) {
+ v4l2_m2m_job_finish(dev->m2m_dev_dec, ctx->m2m_ctx);
+ goto out;
+ }
+
+ if (!is_vdec_ready(ctx)) {
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_ERROR,
+ "the decoder has not ready.\n");
+ goto out;
+ }
+
+ vb2_v4l2 = v4l2_m2m_next_src_buf(ctx->m2m_ctx);
+ if (vb2_v4l2 == NULL) {
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_ERROR,
+ "src_buf empty.\n");
+ goto out;
+ }
+
+ vb = (struct vb2_buffer *)vb2_v4l2;
+
+ /*this case for google, but some frames are droped on ffmpeg, so disabled temp.*/
+ if (0 && !is_enough_work_items(ctx))
+ goto out;
+
+ aml_buf = container_of(vb2_v4l2, struct aml_video_dec_buf, vb);
+ if (aml_buf->lastframe) {
+ ulong expires;
+
+ /*the empty data use to flushed the decoder.*/
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_BUFMGR,
+ "Got empty flush input buffer.\n");
+
+ /*
+ * when inputs a small amount of src buff, then soon to
+ * switch state FLUSHING, must to wait the DBP to be ready.
+ * (!ctx->v4l_codec_dpb_ready) change to only need one buf
+ * for run ready in new version.
+ */
+ expires = jiffies + msecs_to_jiffies(5000);
+ while ((vdec_frame_number(ctx->ada_ctx) > 0) &&
+ (ctx->cap_pool.in < 1)) {
+ if (time_after(jiffies, expires)) {
+ aml_vdec_flush_decoder(ctx);
+ v4l2_m2m_src_buf_remove(ctx->m2m_ctx);
+ v4l2_m2m_job_finish(dev->m2m_dev_dec, ctx->m2m_ctx);
+ aml_vdec_dispatch_event(ctx, V4L2_EVENT_REQUEST_EXIT);
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_ERROR,
+ "capture buffer waiting timeout.\n");
+ goto out;
+ }
+ usleep_range(5000, 5500);
+ }
+
+ mutex_lock(&ctx->state_lock);
+ if (ctx->state == AML_STATE_ACTIVE) {
+ ctx->state = AML_STATE_FLUSHING;// prepare flushing
+ ATRACE_COUNTER("V_ST_VSINK-state", ctx->state);
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_STATE,
+ "vcodec state (AML_STATE_FLUSHING-LASTFRM)\n");
+ }
+ mutex_unlock(&ctx->state_lock);
+
+ v4l2_m2m_src_buf_remove(ctx->m2m_ctx);
+ v4l2_m2m_job_finish(dev->m2m_dev_dec, ctx->m2m_ctx);
+
+ /* sets eos data for vdec input. */
+ aml_vdec_flush_decoder(ctx);
+
+ goto out;
+ }
+
+ buf.index = vb->index;
+ buf.vaddr = vb2_plane_vaddr(vb, 0);
+ buf.addr = sg_dma_address(aml_buf->out_sgt->sgl);
+ buf.size = vb->planes[0].bytesused;
+ buf.model = vb->memory;
+ buf.timestamp = vb->timestamp;
+ buf.meta_ptr = (ulong)aml_buf->meta_data;
+
+ if (!buf.vaddr && !buf.addr) {
+ v4l2_m2m_job_finish(dev->m2m_dev_dec, ctx->m2m_ctx);
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_ERROR,
+ "id=%d src_addr is NULL.\n", vb->index);
+ goto out;
+ }
+
+ aml_buf->used = true;
+
+ /* v4l_dbg(ctx, V4L_DEBUG_CODEC_EXINFO,
+ "size: 0x%zx, crc: 0x%x\n",
+ buf.size, crc32(0, buf.va, buf.size));*/
+
+ /* pts = (time / 10e6) * (90k / fps) */
+ /*v4l_dbg(ctx, V4L_DEBUG_CODEC_EXINFO,
+ "timestamp: 0x%llx\n", src_buf->timestamp);*/
+
+ if (ctx->is_drm_mode &&
+ (buf.model == VB2_MEMORY_DMABUF)) {
+ ATRACE_COUNTER("VO_IN_VSINK-2.write_secure", buf.size);
+ } else {
+ ATRACE_COUNTER("VO_IN_VSINK-2.write", buf.size);
+ }
+
+ ATRACE_COUNTER("V_ST_VSINK-input_buffering", vdec_frame_number(ctx->ada_ctx));
+
+ ret = vdec_if_decode(ctx, &buf, &res_chg);
+ if (ret > 0) {
+ /*
+ * we only return src buffer with VB2_BUF_STATE_DONE
+ * when decode success without resolution change.
+ */
+ aml_buf->used = false;
+ v4l2_m2m_src_buf_remove(ctx->m2m_ctx);
+
+ if (ctx->is_drm_mode &&
+ (buf.model == VB2_MEMORY_DMABUF)) {
+ wake_up_interruptible(&ctx->wq);
+ } else {
+ ATRACE_COUNTER("VO_OUT_VSINK-0.wrtie_end", buf.size);
+ v4l2_buff_done(&aml_buf->vb,
+ VB2_BUF_STATE_DONE);
+ }
+ } else if (ret && ret != -EAGAIN) {
+ aml_buf->used = false;
+ v4l2_m2m_src_buf_remove(ctx->m2m_ctx);
+
+ if (ctx->is_drm_mode &&
+ (buf.model == VB2_MEMORY_DMABUF)) {
+ wake_up_interruptible(&ctx->wq);
+ } else {
+ ATRACE_COUNTER("VO_OUT_VSINK-3.write_error", buf.size);
+ v4l2_buff_done(&aml_buf->vb,
+ VB2_BUF_STATE_ERROR);
+ }
+
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_ERROR,
+ "error processing src data. %d.\n", ret);
+ } else if (res_chg) {
+ /* wait the DPB state to be ready. */
+ aml_wait_buf_ready(ctx);
+
+ aml_buf->used = false;
+ aml_vdec_pic_info_update(ctx);
+ /*
+ * On encountering a resolution change in the stream.
+ * The driver must first process and decode all
+ * remaining buffers from before the resolution change
+ * point, so call flush decode here
+ */
+ mutex_lock(&ctx->state_lock);
+ if (ctx->state == AML_STATE_ACTIVE) {
+ ctx->state = AML_STATE_FLUSHING;// prepare flushing
+ ATRACE_COUNTER("V_ST_VSINK-state", ctx->state);
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_STATE,
+ "vcodec state (AML_STATE_FLUSHING-RESCHG)\n");
+ }
+ mutex_unlock(&ctx->state_lock);
+
+ ctx->v4l_resolution_change = true;
+ while (ctx->m2m_ctx->job_flags & TRANS_RUNNING) {
+ v4l2_m2m_job_pause(dev->m2m_dev_dec, ctx->m2m_ctx);
+ }
+
+ aml_vdec_flush_decoder(ctx);
+
+ goto out;
+ } else {
+ ATRACE_COUNTER("VO_OUT_VSINK-1.write_again", buf.size);
+ /* decoder is lack of resource, retry after short delay */
+ if (vdec_get_instance_num() < 2)
+ usleep_range(2000, 4000);
+ }
+
+ v4l2_m2m_job_finish(dev->m2m_dev_dec, ctx->m2m_ctx);
+out:
+ return;
+}
+
+static void aml_vdec_reset(struct aml_vcodec_ctx *ctx)
+{
+ if (ctx->state == AML_STATE_ABORT) {
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_ERROR,
+ "the decoder will be exited.\n");
+ goto out;
+ }
+
+ if (aml_codec_reset(ctx->ada_ctx, &ctx->reset_flag)) {
+ ctx->state = AML_STATE_ABORT;
+ ATRACE_COUNTER("V_ST_VSINK-state", ctx->state);
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_STATE,
+ "vcodec state (AML_STATE_ABORT).\n");
+ }
+out:
+ complete(&ctx->comp);
+ return;
+}
+
+void wait_vcodec_ending(struct aml_vcodec_ctx *ctx)
+{
+ /* disable queue output item to worker. */
+ ctx->output_thread_ready = false;
+ ctx->is_stream_off = true;
+
+ /* flush output buffer worker. */
+ cancel_work_sync(&ctx->decode_work);
+ cancel_work_sync(&ctx->dmabuff_recycle_work);
+
+ /* clean output cache and decoder status . */
+ if (ctx->state > AML_STATE_INIT)
+ aml_vdec_reset(ctx);
+
+ /* pause the job and clean trans status. */
+ while (ctx->m2m_ctx->job_flags & TRANS_RUNNING) {
+ v4l2_m2m_job_pause(ctx->dev->m2m_dev_dec, ctx->m2m_ctx);
+ }
+
+ ctx->v4l_codec_dpb_ready = false;
+}
+
+void aml_thread_capture_worker(struct aml_vcodec_ctx *ctx)
+{
+ struct vb2_v4l2_buffer *vb = NULL;
+ struct aml_video_dec_buf *aml_buff = NULL;
+ struct vdec_v4l2_buffer *fb = NULL;
+
+ for (;;) {
+ mutex_lock(&ctx->capture_buffer_lock);
+ if (!kfifo_get(&ctx->capture_buffer, &vb)) {
+ mutex_unlock(&ctx->capture_buffer_lock);
+ break;
+ }
+ mutex_unlock(&ctx->capture_buffer_lock);
+
+ aml_buff = container_of(vb, struct aml_video_dec_buf, vb);
+ fb = &aml_buff->frame_buffer;
+
+ if (ctx->is_stream_off)
+ continue;
+
+ post_frame_to_upper(ctx, fb);
+ }
+}
+EXPORT_SYMBOL_GPL(aml_thread_capture_worker);
+
+static int vdec_capture_thread(void *data)
+{
+ struct aml_vdec_thread *thread =
+ (struct aml_vdec_thread *) data;
+ struct aml_vcodec_ctx *ctx =
+ (struct aml_vcodec_ctx *) thread->priv;
+
+ for (;;) {
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_EXINFO,
+ "%s, state: %d\n", __func__, ctx->state);
+
+ if (down_interruptible(&thread->sem))
+ break;
+
+ if (thread->stop)
+ break;
+
+ /* handle event. */
+ thread->func(ctx);
+ }
+
+ while (!kthread_should_stop()) {
+ usleep_range(1000, 2000);
+ }
+
+ return 0;
+}
+
+void aml_thread_post_task(struct aml_vcodec_ctx *ctx,
+ enum aml_thread_type type)
+{
+ struct aml_vdec_thread *thread = NULL;
+ ulong flags;
+
+ spin_lock_irqsave(&ctx->tsplock, flags);
+ list_for_each_entry(thread, &ctx->vdec_thread_list, node) {
+ if (thread->task == NULL)
+ continue;
+
+ if (thread->type == type)
+ up(&thread->sem);
+ }
+ spin_unlock_irqrestore(&ctx->tsplock, flags);
+}
+EXPORT_SYMBOL_GPL(aml_thread_post_task);
+
+int aml_thread_start(struct aml_vcodec_ctx *ctx, aml_thread_func func,
+ enum aml_thread_type type, const char *thread_name)
+{
+ struct aml_vdec_thread *thread;
+ struct sched_param param = { .sched_priority = MAX_RT_PRIO - 1 };
+ int ret = 0;
+
+ thread = kzalloc(sizeof(*thread), GFP_KERNEL);
+ if (thread == NULL)
+ return -ENOMEM;
+
+ thread->type = type;
+ thread->func = func;
+ thread->priv = ctx;
+ sema_init(&thread->sem, 0);
+
+ thread->task = kthread_run(vdec_capture_thread, thread, "aml-%s-%d", thread_name, ctx->id);
+ if (IS_ERR(thread->task)) {
+ ret = PTR_ERR(thread->task);
+ thread->task = NULL;
+ goto err;
+ }
+ sched_setscheduler_nocheck(thread->task, SCHED_FIFO, ¶m);
+
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_EXINFO,
+ "%s, policy is:%d priority is:%d\n",
+ __func__, thread->task->policy, thread->task->rt_priority);
+
+ list_add(&thread->node, &ctx->vdec_thread_list);
+
+ return 0;
+
+err:
+ kfree(thread);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(aml_thread_start);
+
+void aml_thread_stop(struct aml_vcodec_ctx *ctx)
+{
+ struct aml_vdec_thread *thread = NULL;
+ ulong flags;
+
+ while (!list_empty(&ctx->vdec_thread_list)) {
+ thread = list_entry(ctx->vdec_thread_list.next,
+ struct aml_vdec_thread, node);
+ spin_lock_irqsave(&ctx->tsplock, flags);
+ list_del(&thread->node);
+ spin_unlock_irqrestore(&ctx->tsplock, flags);
+
+ thread->stop = true;
+ up(&thread->sem);
+ kthread_stop(thread->task);
+ thread->task = NULL;
+ kfree(thread);
+ }
+}
+EXPORT_SYMBOL_GPL(aml_thread_stop);
+
+static int vidioc_try_decoder_cmd(struct file *file, void *priv,
+ struct v4l2_decoder_cmd *cmd)
+{
+ struct aml_vcodec_ctx *ctx = fh_to_ctx(priv);
+
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_PROT,
+ "%s, cmd: %u\n", __func__, cmd->cmd);
+
+ switch (cmd->cmd) {
+ case V4L2_DEC_CMD_STOP:
+ case V4L2_DEC_CMD_START:
+ if (cmd->cmd == V4L2_DEC_CMD_START) {
+ if (cmd->start.speed == ~0)
+ cmd->start.speed = 0;
+ if (cmd->start.format == ~0)
+ cmd->start.format = 0;
+ }
+
+ if (cmd->flags == ~0)
+ cmd->flags = 0;
+
+ if ((cmd->flags != 0) && (cmd->flags != ~0)) {
+ v4l_dbg(0, V4L_DEBUG_CODEC_ERROR,
+ "cmd->flags=%u\n", cmd->flags);
+ return -EINVAL;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int vidioc_decoder_cmd(struct file *file, void *priv,
+ struct v4l2_decoder_cmd *cmd)
+{
+ struct aml_vcodec_ctx *ctx = fh_to_ctx(priv);
+ struct vb2_queue *src_vq, *dst_vq;
+ int ret;
+
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_PROT,
+ "%s, cmd: %u\n", __func__, cmd->cmd);
+
+ ret = vidioc_try_decoder_cmd(file, priv, cmd);
+ if (ret)
+ return ret;
+
+ switch (cmd->cmd) {
+ case V4L2_DEC_CMD_STOP:
+ src_vq = v4l2_m2m_get_vq(ctx->m2m_ctx,
+ V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
+ if (!vb2_is_streaming(src_vq)) {
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_ERROR,
+ "Output stream is off. No need to flush.\n");
+ return 0;
+ }
+
+ dst_vq = v4l2_m2m_get_vq(ctx->m2m_ctx,
+ multiplanar ? V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE :
+ V4L2_BUF_TYPE_VIDEO_CAPTURE);
+ if (!vb2_is_streaming(dst_vq)) {
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_ERROR,
+ "Capture stream is off. No need to flush.\n");
+ return 0;
+ }
+
+ /* flush pipeline */
+ v4l2_m2m_buf_queue(ctx->m2m_ctx, &ctx->empty_flush_buf->vb);
+ v4l2_m2m_try_schedule(ctx->m2m_ctx);//pay attention
+ ctx->receive_cmd_stop = true;
+
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_PRINFO,
+ "%s, receive cmd stop and prepare flush pipeline.\n", __func__);
+ break;
+
+ case V4L2_DEC_CMD_START:
+ dst_vq = v4l2_m2m_get_vq(ctx->m2m_ctx,
+ multiplanar ? V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE :
+ V4L2_BUF_TYPE_VIDEO_CAPTURE);
+ vb2_clear_last_buffer_dequeued(dst_vq);//pay attention
+
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_PRINFO,
+ "%s, receive cmd start.\n", __func__);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void aml_wait_resource(struct aml_vcodec_ctx *ctx)
+{
+ ulong expires = jiffies + msecs_to_jiffies(1000);
+
+ while (atomic_read(&ctx->dev->vpp_count) >= max_di_instance) {
+ if (time_after(jiffies, expires)) {
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_PRINFO,
+ "wait resource timeout.\n");
+ break;
+ }
+ usleep_range(2000, 4000);
+ }
+}
+
+static int vidioc_decoder_streamon(struct file *file, void *priv,
+ enum v4l2_buf_type i)
+{
+ struct v4l2_fh *fh = file->private_data;
+ struct aml_vcodec_ctx *ctx = fh_to_ctx(fh);
+ struct vb2_queue *q;
+
+ q = v4l2_m2m_get_vq(fh->m2m_ctx, i);
+ if (!V4L2_TYPE_IS_OUTPUT(q->type) &&
+ ctx->is_stream_off) {
+ if (ctx->vpp_is_need) {
+ int ret;
+
+ if (ctx->vpp_cfg.fmt == 0)
+ ctx->vpp_cfg.fmt = ctx->cap_pix_fmt;
+
+ if (ctx->vpp == NULL)
+ aml_wait_resource(ctx);
+
+ if ((atomic_read(&ctx->dev->vpp_count) < max_di_instance) ||
+ (ctx->vpp != NULL)) {
+ if (ctx->vpp && ctx->vpp_cfg.is_vpp_reset &&
+ (ctx->vpp->is_prog == ctx->vpp_cfg.is_prog) &&
+ (ctx->vpp->is_bypass_p == ctx->vpp_cfg.is_bypass_p) &&
+ (ctx->vpp->work_mode == ctx->vpp_cfg.mode)) {
+ aml_v4l2_vpp_reset(ctx->vpp);
+ } else {
+ if (ctx->vpp) {
+ atomic_dec(&ctx->dev->vpp_count);
+ aml_v4l2_vpp_destroy(ctx->vpp);
+ ctx->vpp = NULL;
+ }
+
+ ret = aml_v4l2_vpp_init(ctx, &ctx->vpp_cfg, &ctx->vpp);
+ if (ret) {
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_ERROR,
+ "vpp_wrapper init err:%d vpp_cfg.fmt: %d\n",
+ ret, ctx->vpp_cfg.fmt);
+ return ret;
+ }
+
+ atomic_inc(&ctx->dev->vpp_count);
+
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_PRINFO,
+ "vpp_wrapper instance count: %d\n",
+ atomic_read(&ctx->dev->vpp_count));
+ }
+ } else {
+ ctx->vpp_cfg.enable_local_buf = 0;
+ ctx->vpp_cfg.enable_nr = 0;
+ ctx->picinfo.dpb_margin += ctx->vpp_size;
+ ctx->dpb_size = ctx->picinfo.dpb_margin + ctx->picinfo.dpb_frames;
+ ctx->vpp_size = 0;
+ vdec_if_set_param(ctx, SET_PARAM_PIC_INFO, &ctx->picinfo);
+ ctx->vpp_is_need = false;
+ }
+ ctx->vpp_cfg.is_vpp_reset = false;
+ } else {
+ if (ctx->vpp) {
+ atomic_dec(&ctx->dev->vpp_count);
+ aml_v4l2_vpp_destroy(ctx->vpp);
+ ctx->vpp = NULL;
+ }
+ }
+
+ if (ctx->ge2d_is_need) {
+ int ret;
+
+ if (ctx->ge2d) {
+ aml_v4l2_ge2d_destroy(ctx->ge2d);
+ ctx->ge2d = NULL;
+ }
+
+ ret = aml_v4l2_ge2d_init(ctx, &ctx->ge2d_cfg, &ctx->ge2d);
+ if (ret) {
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_ERROR,
+ "ge2d_wrapper init err:%d\n", ret);
+ return ret;
+ }
+ }
+
+ ctx->is_stream_off = false;
+ } else
+ ctx->is_out_stream_off = false;
+
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_PROT,
+ "%s, type: %d\n", __func__, q->type);
+
+ return v4l2_m2m_ioctl_streamon(file, priv, i);
+}
+
+static int vidioc_decoder_streamoff(struct file *file, void *priv,
+ enum v4l2_buf_type i)
+{
+ struct v4l2_fh *fh = file->private_data;
+ struct aml_vcodec_ctx *ctx = fh_to_ctx(fh);
+ struct vb2_queue *q;
+ ulong flags;
+
+ q = v4l2_m2m_get_vq(fh->m2m_ctx, i);
+
+ flags = aml_vcodec_ctx_lock(ctx);
+
+ if (V4L2_TYPE_IS_OUTPUT(q->type))
+ ctx->is_out_stream_off = true;
+ else
+ ctx->is_stream_off = true;
+
+ aml_vcodec_ctx_unlock(ctx, flags);
+
+ if (!V4L2_TYPE_IS_OUTPUT(q->type)) {
+ if (ctx->vpp) {
+ reconfig_vpp_status(ctx);
+ }
+ } else {
+ ctx->index_disp = 0;
+ }
+
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_PROT,
+ "%s, type: %d\n", __func__, q->type);
+
+ return v4l2_m2m_ioctl_streamoff(file, priv, i);
+}
+
+static int vidioc_decoder_reqbufs(struct file *file, void *priv,
+ struct v4l2_requestbuffers *rb)
+{
+ struct aml_vcodec_ctx *ctx = fh_to_ctx(priv);
+ struct v4l2_fh *fh = file->private_data;
+ struct vb2_queue *q;
+
+ q = v4l2_m2m_get_vq(fh->m2m_ctx, rb->type);
+
+ if (!rb->count) {
+ if (!V4L2_TYPE_IS_OUTPUT(rb->type)) {
+ if (wait_event_interruptible_timeout
+ (ctx->post_done_wq, ctx->post_to_upper_done == true,
+ msecs_to_jiffies(200)) == 0) {
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_ERROR,
+ "wait post frame to upper finish timeout.\n");
+ }
+ }
+ vb2_queue_release(q);
+ }
+
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_PROT,
+ "%s, type: %d, count: %d\n",
+ __func__, q->type, rb->count);
+
+ if (!V4L2_TYPE_IS_OUTPUT(rb->type)) {
+ /* driver needs match v4l buffer number with total size*/
+ if (rb->count > CTX_BUF_TOTAL(ctx)) {
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_PROT,
+ "reqbufs (st:%d) %d -> %d\n",
+ ctx->state, rb->count, CTX_BUF_TOTAL(ctx));
+ ctx->picinfo.dpb_margin += (rb->count - CTX_BUF_TOTAL(ctx));
+ ctx->dpb_size = ctx->picinfo.dpb_frames + ctx->picinfo.dpb_margin;
+ vdec_if_set_param(ctx, SET_PARAM_PIC_INFO, &ctx->picinfo);
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_PROT,
+ "%s buf updated, dec: %d (%d + %d), vpp %d\n",
+ __func__,
+ ctx->dpb_size,
+ ctx->picinfo.dpb_frames,
+ ctx->picinfo.dpb_margin,
+ ctx->vpp_size);
+ //rb->count = ctx->dpb_size;
+ }
+ } else {
+ ctx->output_dma_mode =
+ (rb->memory == VB2_MEMORY_DMABUF) ? 1 : 0;
+
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_INPUT,
+ "output buffer memory mode is %d\n", rb->memory);
+ }
+
+ return v4l2_m2m_ioctl_reqbufs(file, priv, rb);
+}
+
+static int vidioc_vdec_querybuf(struct file *file, void *priv,
+ struct v4l2_buffer *buf)
+{
+ struct aml_vcodec_ctx *ctx = fh_to_ctx(priv);
+
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_PROT,
+ "%s, type: %d\n", __func__, buf->type);
+
+ return v4l2_m2m_ioctl_querybuf(file, priv, buf);
+}
+
+static int vidioc_vdec_expbuf(struct file *file, void *priv,
+ struct v4l2_exportbuffer *eb)
+{
+ struct aml_vcodec_ctx *ctx = fh_to_ctx(priv);
+
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_PROT,
+ "%s, type: %d\n", __func__, eb->type);
+
+ return v4l2_m2m_ioctl_expbuf(file, priv, eb);
+}
+
+void aml_vcodec_dec_release(struct aml_vcodec_ctx *ctx)
+{
+ ulong flags;
+
+ if (kref_read(&ctx->box_ref))
+ kref_put(&ctx->box_ref, box_release);
+
+ flags = aml_vcodec_ctx_lock(ctx);
+ ctx->state = AML_STATE_ABORT;
+ ATRACE_COUNTER("V_ST_VSINK-state", ctx->state);
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_STATE,
+ "vcodec state (AML_STATE_ABORT)\n");
+ aml_vcodec_ctx_unlock(ctx, flags);
+
+ vdec_if_deinit(ctx);
+}
+
+void aml_vcodec_dec_set_default_params(struct aml_vcodec_ctx *ctx)
+{
+ struct aml_q_data *q_data;
+
+ ctx->m2m_ctx->q_lock = &ctx->dev->dev_mutex;
+ ctx->fh.m2m_ctx = ctx->m2m_ctx;
+ ctx->fh.ctrl_handler = &ctx->ctrl_hdl;
+ INIT_WORK(&ctx->decode_work, aml_vdec_worker);
+ ctx->colorspace = V4L2_COLORSPACE_REC709;
+ ctx->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
+ ctx->quantization = V4L2_QUANTIZATION_DEFAULT;
+ ctx->xfer_func = V4L2_XFER_FUNC_DEFAULT;
+ ctx->dev->dec_capability = 0;//VCODEC_CAPABILITY_4K_DISABLED;//disable 4k
+
+ q_data = &ctx->q_data[AML_Q_DATA_SRC];
+ memset(q_data, 0, sizeof(struct aml_q_data));
+ q_data->visible_width = DFT_CFG_WIDTH;
+ q_data->visible_height = DFT_CFG_HEIGHT;
+ q_data->coded_width = DFT_CFG_WIDTH;
+ q_data->coded_height = DFT_CFG_HEIGHT;
+ q_data->fmt = &aml_video_formats[OUT_FMT_IDX];
+ q_data->field = V4L2_FIELD_NONE;
+
+ q_data->sizeimage[0] = (1024 * 1024);//DFT_CFG_WIDTH * DFT_CFG_HEIGHT; //1m
+ q_data->bytesperline[0] = 0;
+
+ q_data = &ctx->q_data[AML_Q_DATA_DST];
+ memset(q_data, 0, sizeof(struct aml_q_data));
+ q_data->visible_width = DFT_CFG_WIDTH;
+ q_data->visible_height = DFT_CFG_HEIGHT;
+ q_data->coded_width = DFT_CFG_WIDTH;
+ q_data->coded_height = DFT_CFG_HEIGHT;
+ q_data->fmt = &aml_video_formats[CAP_FMT_IDX];
+ if (support_format_I420)
+ q_data->fmt = &aml_video_formats[CAP_FMT_I420_IDX];
+
+ q_data->field = V4L2_FIELD_NONE;
+
+ v4l_bound_align_image(&q_data->coded_width,
+ AML_VDEC_MIN_W,
+ AML_VDEC_MAX_W, 4,
+ &q_data->coded_height,
+ AML_VDEC_MIN_H,
+ AML_VDEC_MAX_H, 5, 6);
+
+ q_data->sizeimage[0] = q_data->coded_width * q_data->coded_height;
+ q_data->bytesperline[0] = q_data->coded_width;
+ q_data->sizeimage[1] = q_data->sizeimage[0] / 2;
+ q_data->bytesperline[1] = q_data->coded_width;
+ ctx->reset_flag = V4L_RESET_MODE_NORMAL;
+
+ ctx->fb_ops.query = fb_buff_query;
+ ctx->fb_ops.alloc = fb_buff_from_queue;
+
+ ctx->state = AML_STATE_IDLE;
+ ATRACE_COUNTER("V_ST_VSINK-state", ctx->state);
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_STATE,
+ "vcodec state (AML_STATE_IDLE)\n");
+}
+
+static int vidioc_vdec_qbuf(struct file *file, void *priv,
+ struct v4l2_buffer *buf)
+{
+ struct aml_vcodec_ctx *ctx = fh_to_ctx(priv);
+ int ret;
+
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_PROT,
+ "%s, type: %d\n", __func__, buf->type);
+
+ if (ctx->state == AML_STATE_ABORT) {
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_ERROR,
+ "Call on QBUF after unrecoverable error, type = %s\n",
+ V4L2_TYPE_IS_OUTPUT(buf->type) ? "OUT" : "IN");
+ return -EIO;
+ }
+
+ ret = v4l2_m2m_qbuf(file, ctx->m2m_ctx, buf);
+
+ if (V4L2_TYPE_IS_OUTPUT(buf->type)) {
+ if (V4L2_TYPE_IS_MULTIPLANAR(buf->type)) {
+ if (ret == -EAGAIN)
+ ATRACE_COUNTER("VO_IN_VSINK-1.que_again", buf->m.planes[0].bytesused);
+ else
+ ATRACE_COUNTER("VO_IN_VSINK-0.que", buf->m.planes[0].bytesused);
+ } else {
+ if (ret == -EAGAIN)
+ ATRACE_COUNTER("VO_IN_VSINK-1.que_again", buf->length);
+ else
+ ATRACE_COUNTER("VO_IN_VSINK-0.que", buf->length);
+ }
+ } else {
+ if (ret == -EAGAIN)
+ ATRACE_COUNTER("VC_IN_VSINK-1.que_again", buf->index);
+ else
+ ATRACE_COUNTER("VC_IN_VSINK-0.que", buf->index);
+ }
+
+ return ret;
+}
+
+static int vidioc_vdec_dqbuf(struct file *file, void *priv,
+ struct v4l2_buffer *buf)
+{
+ struct aml_vcodec_ctx *ctx = fh_to_ctx(priv);
+ int ret;
+
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_PROT,
+ "%s, type: %d\n", __func__, buf->type);
+
+ if (ctx->state == AML_STATE_ABORT) {
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_ERROR,
+ "Call on DQBUF after unrecoverable error, type = %s\n",
+ V4L2_TYPE_IS_OUTPUT(buf->type) ? "OUT" : "IN");
+ if (!V4L2_TYPE_IS_OUTPUT(buf->type))
+ return -EIO;
+ }
+
+ ret = v4l2_m2m_dqbuf(file, ctx->m2m_ctx, buf);
+ if (!ret && !V4L2_TYPE_IS_OUTPUT(buf->type)) {
+ struct vb2_queue *vq;
+ struct vb2_v4l2_buffer *vb2_v4l2 = NULL;
+ struct aml_video_dec_buf *aml_buf = NULL;
+ struct file *file = NULL;
+
+ vq = v4l2_m2m_get_vq(ctx->m2m_ctx, buf->type);
+ vb2_v4l2 = to_vb2_v4l2_buffer(vq->bufs[buf->index]);
+ aml_buf = container_of(vb2_v4l2, struct aml_video_dec_buf, vb);
+ aml_buf->privdata.vb_handle = (ulong) aml_buf;
+ aml_buf->privdata.v4l_dec_ctx = (ulong) ctx;
+
+ file = fget(vb2_v4l2->private);
+ if (file && is_v4l2_buf_file(file)) {
+ dmabuf_fd_install_data(vb2_v4l2->private,
+ (void*)&aml_buf->privdata,
+ sizeof(struct file_private_data));
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_EXINFO, "disp: %d, vf: %lx\n",
+ aml_buf->privdata.vf.index_disp,
+ (ulong) v4l_get_vf_handle(vb2_v4l2->private));
+ fput(file);
+ }
+ }
+
+ if (V4L2_TYPE_IS_OUTPUT(buf->type)) {
+ if (V4L2_TYPE_IS_MULTIPLANAR(buf->type)) {
+ if (ret == -EAGAIN)
+ ATRACE_COUNTER("VO_OUT_VSINK-5.deque_again", buf->m.planes[0].bytesused);
+ else
+ ATRACE_COUNTER("VO_OUT_VSINK-4.deque", buf->m.planes[0].bytesused);
+ } else {
+ if (ret == -EAGAIN)
+ ATRACE_COUNTER("VO_OUT_VSINK-5.deque_again", buf->length);
+ else
+ ATRACE_COUNTER("VO_OUT_VSINK-4.deque", buf->length);
+ }
+ } else {
+ if (ret == -EAGAIN)
+ ATRACE_COUNTER("VC_OUT_VSINK-3.deque_again", buf->index);
+ else
+ ATRACE_COUNTER("VC_OUT_VSINK-2.deque", buf->index);
+ }
+
+ return ret;
+}
+
+static int vidioc_vdec_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct aml_vcodec_ctx *ctx = fh_to_ctx(priv);
+ struct video_device *vfd_dec = video_devdata(file);
+
+ strlcpy(cap->driver, AML_VCODEC_DEC_NAME, sizeof(cap->driver));
+ strlcpy(cap->bus_info, AML_PLATFORM_STR, sizeof(cap->bus_info));
+ strlcpy(cap->card, AML_PLATFORM_STR, sizeof(cap->card));
+ cap->device_caps = vfd_dec->device_caps;
+
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_PROT, "%s, %s\n", __func__, cap->card);
+
+ return 0;
+}
+
+static int vidioc_vdec_subscribe_evt(struct v4l2_fh *fh,
+ const struct v4l2_event_subscription *sub)
+{
+ struct aml_vcodec_ctx *ctx = fh_to_ctx(fh);
+
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_PROT,
+ "%s, type: %d\n", __func__, sub->type);
+
+ switch (sub->type) {
+ case V4L2_EVENT_EOS:
+ return v4l2_event_subscribe(fh, sub, 2, NULL);
+ case V4L2_EVENT_SOURCE_CHANGE:
+ return v4l2_src_change_event_subscribe(fh, sub);
+ default:
+ return v4l2_ctrl_subscribe_event(fh, sub);
+ }
+}
+
+static int vidioc_vdec_event_unsubscribe(struct v4l2_fh *fh,
+ const struct v4l2_event_subscription *sub)
+{
+ struct aml_vcodec_ctx *ctx = fh_to_ctx(fh);
+
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_PROT, "%s, type: %d\n",
+ __func__, sub->type);
+
+ return v4l2_event_unsubscribe(fh, sub);
+}
+
+static int vidioc_try_fmt(struct v4l2_format *f, struct aml_video_fmt *fmt)
+{
+ int i;
+ struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp;
+ struct v4l2_pix_format *pix = &f->fmt.pix;
+
+ if (V4L2_TYPE_IS_MULTIPLANAR(f->type)) {
+ if (V4L2_TYPE_IS_OUTPUT(f->type)) {
+ pix_mp->num_planes = 1;
+ pix_mp->plane_fmt[0].bytesperline = 0;
+
+ if ((pix_mp->pixelformat != V4L2_PIX_FMT_MPEG2) &&
+ (pix_mp->pixelformat != V4L2_PIX_FMT_H264) &&
+ (pix_mp->pixelformat != V4L2_PIX_FMT_MPEG1)) {
+ pix_mp->field = V4L2_FIELD_NONE;
+ } else if (pix_mp->field != V4L2_FIELD_NONE) {
+ if (pix_mp->field == V4L2_FIELD_ANY)
+ pix_mp->field = V4L2_FIELD_NONE;
+
+ pr_info("%s, field: %u, fmt: %x\n",
+ __func__, pix_mp->field,
+ pix_mp->pixelformat);
+ }
+ } else {
+ if (pix_mp->field != V4L2_FIELD_INTERLACED)
+ pix_mp->field = V4L2_FIELD_NONE;
+ pix_mp->height = clamp(pix_mp->height,
+ AML_VDEC_MIN_H,
+ AML_VDEC_MAX_H);
+ pix_mp->width = clamp(pix_mp->width,
+ AML_VDEC_MIN_W,
+ AML_VDEC_MAX_W);
+
+ pix_mp->num_planes = fmt->num_planes;
+
+ pix_mp->plane_fmt[0].bytesperline = pix_mp->width;
+ pix_mp->plane_fmt[0].sizeimage =
+ pix_mp->width * pix_mp->height;
+
+ pix_mp->plane_fmt[1].bytesperline = pix_mp->width;
+ pix_mp->plane_fmt[1].sizeimage =
+ pix_mp->width * pix_mp->height / 2;
+ }
+
+ for (i = 0; i < pix_mp->num_planes; i++) {
+ memset(&(pix_mp->plane_fmt[i].reserved[0]), 0x0,
+ sizeof(pix_mp->plane_fmt[0].reserved));
+ }
+ memset(&pix_mp->reserved, 0x0, sizeof(pix_mp->reserved));
+
+ pix_mp->flags = 0;
+ } else {
+ if (V4L2_TYPE_IS_OUTPUT(f->type)) {
+ pix->bytesperline = 0;
+ if ((pix->pixelformat != V4L2_PIX_FMT_MPEG2) &&
+ (pix->pixelformat != V4L2_PIX_FMT_H264) &&
+ (pix->pixelformat != V4L2_PIX_FMT_MPEG1)) {
+ pix->field = V4L2_FIELD_NONE;
+ } else if (pix->field != V4L2_FIELD_NONE) {
+ if (pix->field == V4L2_FIELD_ANY)
+ pix->field = V4L2_FIELD_NONE;
+
+ pr_info("%s, field: %u, fmt: %x\n",
+ __func__, pix->field,
+ pix->pixelformat);
+ }
+ } else {
+ if (pix->field != V4L2_FIELD_INTERLACED)
+ pix->field = V4L2_FIELD_NONE;
+
+ pix->height = clamp(pix->height,
+ AML_VDEC_MIN_H,
+ AML_VDEC_MAX_H);
+ pix->width = clamp(pix->width,
+ AML_VDEC_MIN_W,
+ AML_VDEC_MAX_W);
+
+ pix->bytesperline = pix->width;
+ pix->sizeimage = pix->width * pix->height;
+ }
+ pix->flags = 0;
+ }
+
+ return 0;
+}
+
+static int vidioc_try_fmt_vid_cap_out(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp;
+ struct v4l2_pix_format *pix = &f->fmt.pix;
+ struct aml_q_data *q_data = NULL;
+ struct aml_video_fmt *fmt = NULL;
+ struct aml_vcodec_ctx *ctx = fh_to_ctx(priv);
+ struct vb2_queue *dst_vq;
+
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_PROT,
+ "%s, type: %u, planes: %u, fmt: %x\n",
+ __func__, f->type,
+ V4L2_TYPE_IS_MULTIPLANAR(f->type) ?
+ f->fmt.pix_mp.num_planes : 1,
+ f->fmt.pix_mp.pixelformat);
+
+ dst_vq = v4l2_m2m_get_vq(ctx->m2m_ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
+ if (!dst_vq) {
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_ERROR,
+ "no vb2 queue for type=%d\n", V4L2_BUF_TYPE_VIDEO_CAPTURE);
+ return -EINVAL;
+ }
+
+ if (!V4L2_TYPE_IS_MULTIPLANAR(f->type) && dst_vq->is_multiplanar)
+ return -EINVAL;
+
+ fmt = aml_vdec_find_format(f);
+ if (!fmt) {
+ if (V4L2_TYPE_IS_OUTPUT(f->type))
+ f->fmt.pix.pixelformat = aml_video_formats[OUT_FMT_IDX].fourcc;
+ else
+ f->fmt.pix.pixelformat = aml_video_formats[CAP_FMT_IDX].fourcc;
+ fmt = aml_vdec_find_format(f);
+ }
+
+ vidioc_try_fmt(f, fmt);
+
+ q_data = aml_vdec_get_q_data(ctx, f->type);
+ if (!q_data)
+ return -EINVAL;
+
+ if (ctx->state >= AML_STATE_PROBE)
+ update_ctx_dimension(ctx, f->type);
+ copy_v4l2_format_dimention(pix_mp, pix, q_data, f->type);
+
+ if (!V4L2_TYPE_IS_OUTPUT(f->type))
+ return 0;
+
+ if (V4L2_TYPE_IS_MULTIPLANAR(f->type)) {
+ if (pix_mp->plane_fmt[0].sizeimage == 0) {
+ v4l_dbg(0, V4L_DEBUG_CODEC_ERROR,
+ "sizeimage of output format must be given\n");
+ return -EINVAL;
+ }
+ } else {
+ if (pix->sizeimage == 0) {
+ v4l_dbg(0, V4L_DEBUG_CODEC_ERROR,
+ "sizeimage of output format must be given\n");
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+static int vidioc_vdec_g_selection(struct file *file, void *priv,
+ struct v4l2_selection *s)
+{
+ struct aml_vcodec_ctx *ctx = fh_to_ctx(priv);
+ struct aml_q_data *q_data;
+ int ratio = 1;
+
+ if ((s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) &&
+ (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE))
+ return -EINVAL;
+
+ if (ctx->internal_dw_scale) {
+ if (ctx->state >= AML_STATE_PROBE) {
+ unsigned int dw_mode = VDEC_DW_NO_AFBC;
+ if (vdec_if_get_param(ctx, GET_PARAM_DW_MODE, &dw_mode))
+ return -EBUSY;
+ ratio = get_double_write_ratio(dw_mode);
+ }
+ }
+
+ q_data = &ctx->q_data[AML_Q_DATA_DST];
+
+ switch (s->target) {
+ case V4L2_SEL_TGT_COMPOSE_DEFAULT:
+ case V4L2_SEL_TGT_COMPOSE:
+ s->r.left = 0;
+ s->r.top = 0;
+ s->r.width = ctx->picinfo.visible_width / ratio;
+ s->r.height = ctx->picinfo.visible_height / ratio;
+ break;
+ case V4L2_SEL_TGT_COMPOSE_BOUNDS:
+ s->r.left = 0;
+ s->r.top = 0;
+ s->r.width = ctx->picinfo.coded_width / ratio;
+ s->r.height = ctx->picinfo.coded_height / ratio;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (ctx->state < AML_STATE_PROBE) {
+ /* set to default value if header info not ready yet*/
+ s->r.left = 0;
+ s->r.top = 0;
+ s->r.width = q_data->visible_width;
+ s->r.height = q_data->visible_height;
+ }
+
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_PROT, "%s, type: %d\n",
+ __func__, s->type);
+
+ return 0;
+}
+
+static int vidioc_vdec_s_selection(struct file *file, void *priv,
+ struct v4l2_selection *s)
+{
+ struct aml_vcodec_ctx *ctx = fh_to_ctx(priv);
+ int ratio = 1;
+
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_PROT, "%s, type: %d\n",
+ __func__, s->type);
+
+ if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ if (ctx->internal_dw_scale) {
+ if (ctx->state >= AML_STATE_PROBE) {
+ unsigned int dw_mode = VDEC_DW_NO_AFBC;
+ if (vdec_if_get_param(ctx, GET_PARAM_DW_MODE, &dw_mode))
+ return -EBUSY;
+ ratio = get_double_write_ratio(dw_mode);
+ }
+ }
+
+ switch (s->target) {
+ case V4L2_SEL_TGT_COMPOSE:
+ s->r.left = 0;
+ s->r.top = 0;
+ s->r.width = ctx->picinfo.visible_width / ratio;
+ s->r.height = ctx->picinfo.visible_height / ratio;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/* called when it is beyong AML_STATE_PROBE */
+static void update_ctx_dimension(struct aml_vcodec_ctx *ctx, u32 type)
+{
+ struct aml_q_data *q_data;
+ unsigned int dw_mode = VDEC_DW_NO_AFBC;
+ int ratio = 1;
+
+ q_data = aml_vdec_get_q_data(ctx, type);
+
+ if (ctx->internal_dw_scale) {
+ if (vdec_if_get_param(ctx, GET_PARAM_DW_MODE, &dw_mode))
+ return;
+ ratio = get_double_write_ratio(dw_mode);
+ }
+
+ if (V4L2_TYPE_IS_MULTIPLANAR(type)) {
+ q_data->sizeimage[0] = ctx->picinfo.y_len_sz;
+ q_data->sizeimage[1] = ctx->picinfo.c_len_sz;
+
+ q_data->coded_width = ALIGN(ctx->picinfo.coded_width / ratio, 64);
+ q_data->coded_height = ALIGN(ctx->picinfo.coded_height / ratio, 64);
+
+ q_data->bytesperline[0] = ALIGN(ctx->picinfo.coded_width / ratio, 64);
+ q_data->bytesperline[1] = ALIGN(ctx->picinfo.coded_width / ratio, 64);
+ } else {
+ q_data->coded_width = ALIGN(ctx->picinfo.coded_width / ratio, 64);
+ q_data->coded_height = ALIGN(ctx->picinfo.coded_height / ratio, 64);
+ q_data->sizeimage[0] = ctx->picinfo.y_len_sz;
+ q_data->sizeimage[0] += ctx->picinfo.c_len_sz;
+ q_data->bytesperline[0] = ALIGN(ctx->picinfo.coded_width / ratio, 64);
+ }
+}
+
+static void copy_v4l2_format_dimention(struct v4l2_pix_format_mplane *pix_mp,
+ struct v4l2_pix_format *pix,
+ struct aml_q_data *q_data,
+ u32 type)
+{
+ int i;
+
+ if (!pix || !pix_mp || !q_data)
+ return;
+
+ if (V4L2_TYPE_IS_MULTIPLANAR(type)) {
+ pix_mp->width = q_data->coded_width;
+ pix_mp->height = q_data->coded_height;
+ pix_mp->num_planes = q_data->fmt->num_planes;
+ pix_mp->pixelformat = q_data->fmt->fourcc;
+
+ for (i = 0; i < q_data->fmt->num_planes; i++) {
+ pix_mp->plane_fmt[i].bytesperline = q_data->bytesperline[i];
+ pix_mp->plane_fmt[i].sizeimage = q_data->sizeimage[i];
+ }
+ } else {
+ pix->width = q_data->coded_width;
+ pix->height = q_data->coded_height;
+ pix->pixelformat = q_data->fmt->fourcc;
+ pix->bytesperline = q_data->bytesperline[0];
+ pix->sizeimage = q_data->sizeimage[0];
+ }
+}
+
+static int vidioc_vdec_s_fmt(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ int ret = 0;
+ struct aml_vcodec_ctx *ctx = fh_to_ctx(priv);
+ struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp;
+ struct v4l2_pix_format *pix = &f->fmt.pix;
+ struct aml_q_data *q_data = NULL;
+ struct aml_video_fmt *fmt;
+ struct vb2_queue *dst_vq;
+
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_PROT,
+ "%s, type: %u, planes: %u, fmt: %x\n",
+ __func__, f->type,
+ V4L2_TYPE_IS_MULTIPLANAR(f->type) ?
+ f->fmt.pix_mp.num_planes : 1,
+ f->fmt.pix_mp.pixelformat);
+
+ dst_vq = v4l2_m2m_get_vq(ctx->m2m_ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
+ if (!dst_vq) {
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_ERROR,
+ "no vb2 queue for type=%d\n", V4L2_BUF_TYPE_VIDEO_CAPTURE);
+ return -EINVAL;
+ }
+
+ if (!V4L2_TYPE_IS_MULTIPLANAR(f->type) && dst_vq->is_multiplanar)
+ return -EINVAL;
+
+ q_data = aml_vdec_get_q_data(ctx, f->type);
+ if (!q_data)
+ return -EINVAL;
+
+ if ((f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) &&
+ vb2_is_busy(&ctx->m2m_ctx->out_q_ctx.q)) {
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_ERROR,
+ "out_q_ctx buffers already requested\n");
+ ret = -EBUSY;
+ }
+
+ if ((!V4L2_TYPE_IS_OUTPUT(f->type)) &&
+ vb2_is_busy(&ctx->m2m_ctx->cap_q_ctx.q)) {
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_ERROR,
+ "cap_q_ctx buffers already requested\n");
+ ret = -EBUSY;
+ }
+
+ fmt = aml_vdec_find_format(f);
+ if (fmt == NULL) {
+ if (V4L2_TYPE_IS_OUTPUT(f->type))
+ fmt = &aml_video_formats[OUT_FMT_IDX];
+ else
+ fmt = &aml_video_formats[CAP_FMT_IDX];
+ f->fmt.pix.pixelformat = fmt->fourcc;
+ }
+
+ q_data->fmt = fmt;
+ vidioc_try_fmt(f, q_data->fmt);
+
+ if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+ q_data->sizeimage[0] = pix_mp->plane_fmt[0].sizeimage;
+ q_data->coded_width = pix_mp->width;
+ q_data->coded_height = pix_mp->height;
+
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_EXINFO,
+ "w: %d, h: %d, size: %d\n",
+ pix_mp->width, pix_mp->height,
+ pix_mp->plane_fmt[0].sizeimage);
+
+ ctx->output_pix_fmt = pix_mp->pixelformat;
+ ctx->colorspace = f->fmt.pix_mp.colorspace;
+ ctx->ycbcr_enc = f->fmt.pix_mp.ycbcr_enc;
+ ctx->quantization = f->fmt.pix_mp.quantization;
+ ctx->xfer_func = f->fmt.pix_mp.xfer_func;
+
+ mutex_lock(&ctx->state_lock);
+ if (ctx->state == AML_STATE_IDLE) {
+ ret = vdec_if_init(ctx, q_data->fmt->fourcc);
+ if (ret) {
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_ERROR,
+ "vdec_if_init() fail ret=%d\n", ret);
+ mutex_unlock(&ctx->state_lock);
+ return -EINVAL;
+ }
+ ctx->state = AML_STATE_INIT;
+ ATRACE_COUNTER("V_ST_VSINK-state", ctx->state);
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_STATE,
+ "vcodec state (AML_STATE_INIT)\n");
+ }
+ mutex_unlock(&ctx->state_lock);
+ } else if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
+ q_data->sizeimage[0] = pix->sizeimage;
+ q_data->coded_width = pix->width;
+ q_data->coded_height = pix->height;
+
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_EXINFO,
+ "w: %d, h: %d, size: %d\n",
+ pix->width, pix->height,
+ pix->sizeimage);
+
+ ctx->output_pix_fmt = pix->pixelformat;
+ ctx->colorspace = f->fmt.pix.colorspace;
+ ctx->ycbcr_enc = f->fmt.pix.ycbcr_enc;
+ ctx->quantization = f->fmt.pix.quantization;
+ ctx->xfer_func = f->fmt.pix.xfer_func;
+
+ mutex_lock(&ctx->state_lock);
+ if (ctx->state == AML_STATE_IDLE) {
+ ret = vdec_if_init(ctx, q_data->fmt->fourcc);
+ if (ret) {
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_ERROR,
+ "vdec_if_init() fail ret=%d\n", ret);
+ mutex_unlock(&ctx->state_lock);
+ return -EINVAL;
+ }
+ ctx->state = AML_STATE_INIT;
+ ATRACE_COUNTER("V_ST_VSINK-state", ctx->state);
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_STATE,
+ "vcodec state (AML_STATE_INIT)\n");
+ }
+ mutex_unlock(&ctx->state_lock);
+ }
+
+ if (!V4L2_TYPE_IS_OUTPUT(f->type)) {
+ ctx->cap_pix_fmt = V4L2_TYPE_IS_MULTIPLANAR(f->type) ?
+ pix_mp->pixelformat : pix->pixelformat;
+ if (ctx->state >= AML_STATE_PROBE) {
+ update_ctx_dimension(ctx, f->type);
+ copy_v4l2_format_dimention(pix_mp, pix, q_data, f->type);
+ v4l_buf_size_decision(ctx);
+ }
+ }
+
+ return 0;
+}
+
+static int vidioc_enum_framesizes(struct file *file, void *priv,
+ struct v4l2_frmsizeenum *fsize)
+{
+ int i = 0;
+ struct aml_vcodec_ctx *ctx = fh_to_ctx(priv);
+
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_PROT, "%s, idx: %d, pix fmt: %x\n",
+ __func__, fsize->index, fsize->pixel_format);
+
+ if (fsize->index != 0)
+ return -EINVAL;
+
+ for (i = 0; i < NUM_SUPPORTED_FRAMESIZE; ++i) {
+ if (fsize->pixel_format != aml_vdec_framesizes[i].fourcc)
+ continue;
+
+ fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE;
+ fsize->stepwise = aml_vdec_framesizes[i].stepwise;
+ if (!(ctx->dev->dec_capability &
+ VCODEC_CAPABILITY_4K_DISABLED)) {
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_EXINFO, "4K is enabled\n");
+ fsize->stepwise.max_width =
+ VCODEC_DEC_4K_CODED_WIDTH;
+ fsize->stepwise.max_height =
+ VCODEC_DEC_4K_CODED_HEIGHT;
+ }
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_EXINFO,
+ "%x, %d %d %d %d %d %d\n",
+ ctx->dev->dec_capability,
+ fsize->stepwise.min_width,
+ fsize->stepwise.max_width,
+ fsize->stepwise.step_width,
+ fsize->stepwise.min_height,
+ fsize->stepwise.max_height,
+ fsize->stepwise.step_height);
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static int vidioc_enum_fmt(struct v4l2_fmtdesc *f, bool output_queue)
+{
+ struct aml_video_fmt *fmt;
+ int i = 0, j = 0;
+
+ /* I420 only used for mjpeg. */
+ if (!output_queue && support_mjpeg && support_format_I420) {
+ for (i = 0; i < NUM_FORMATS; i++) {
+ fmt = &aml_video_formats[i];
+ if ((fmt->fourcc == V4L2_PIX_FMT_YUV420) ||
+ (fmt->fourcc == V4L2_PIX_FMT_YUV420M)) {
+ break;
+ }
+ }
+ }
+
+ for (; i < NUM_FORMATS; i++) {
+ fmt = &aml_video_formats[i];
+ if (output_queue && (fmt->type != AML_FMT_DEC))
+ continue;
+ if (!output_queue && (fmt->type != AML_FMT_FRAME))
+ continue;
+ if (support_mjpeg && !support_format_I420 &&
+ ((fmt->fourcc == V4L2_PIX_FMT_YUV420) ||
+ (fmt->fourcc == V4L2_PIX_FMT_YUV420M)))
+ continue;
+
+ if (j == f->index) {
+ f->pixelformat = fmt->fourcc;
+ return 0;
+ }
+ ++j;
+ }
+
+ return -EINVAL;
+}
+
+static int vidioc_vdec_enum_fmt_vid_cap_mplane(struct file *file,
+ void *priv, struct v4l2_fmtdesc *f)
+{
+ struct aml_vcodec_ctx *ctx = fh_to_ctx(priv);
+
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_PROT, "%s\n", __func__);
+
+ return vidioc_enum_fmt(f, false);
+}
+
+static int vidioc_vdec_enum_fmt_vid_out_mplane(struct file *file,
+ void *priv, struct v4l2_fmtdesc *f)
+{
+ struct aml_vcodec_ctx *ctx = fh_to_ctx(priv);
+
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_PROT, "%s\n", __func__);
+
+ return vidioc_enum_fmt(f, true);
+}
+
+static int vidioc_vdec_g_fmt(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct aml_vcodec_ctx *ctx = fh_to_ctx(priv);
+ struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp;
+ struct v4l2_pix_format *pix = &f->fmt.pix;
+ struct vb2_queue *vq;
+ struct vb2_queue *dst_vq;
+ struct aml_q_data *q_data;
+ int ret = 0;
+
+ vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type);
+ if (!vq) {
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_ERROR,
+ "no vb2 queue for type=%d\n", f->type);
+ return -EINVAL;
+ }
+
+ dst_vq = v4l2_m2m_get_vq(ctx->m2m_ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
+ if (!dst_vq) {
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_ERROR,
+ "no vb2 queue for type=%d\n", V4L2_BUF_TYPE_VIDEO_CAPTURE);
+ return -EINVAL;
+ }
+
+ if (!V4L2_TYPE_IS_MULTIPLANAR(f->type) && dst_vq->is_multiplanar)
+ return -EINVAL;
+
+ q_data = aml_vdec_get_q_data(ctx, f->type);
+
+ ret = vdec_if_get_param(ctx, GET_PARAM_PIC_INFO, &ctx->picinfo);
+ if (ret) {
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_ERROR,
+ "GET_PARAM_PICTURE_INFO err\n");
+ } else {
+ if ((ctx->picinfo.visible_height < 16 && ctx->picinfo.visible_height > 0) ||
+ (ctx->picinfo.visible_width < 16 && ctx->picinfo.visible_width > 0)) {
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_ERROR,
+ "The width or height of the stream is less than 16\n");
+ return -EPERM;
+ }
+ }
+
+ if (V4L2_TYPE_IS_MULTIPLANAR(f->type)) {
+ pix_mp->field = ret ? V4L2_FIELD_NONE : ctx->picinfo.field;
+ pix_mp->colorspace = ctx->colorspace;
+ pix_mp->ycbcr_enc = ctx->ycbcr_enc;
+ pix_mp->quantization = ctx->quantization;
+ pix_mp->xfer_func = ctx->xfer_func;
+ } else {
+ pix->field = ret ? V4L2_FIELD_NONE : ctx->picinfo.field;
+ pix->colorspace = ctx->colorspace;
+ pix->ycbcr_enc = ctx->ycbcr_enc;
+ pix->quantization = ctx->quantization;
+ pix->xfer_func = ctx->xfer_func;
+ }
+
+ if ((!V4L2_TYPE_IS_OUTPUT(f->type)) &&
+ (ctx->state >= AML_STATE_PROBE)) {
+ update_ctx_dimension(ctx, f->type);
+ copy_v4l2_format_dimention(pix_mp, pix, q_data, f->type);
+ } else if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+ /*
+ * This is run on OUTPUT
+ * The buffer contains compressed image
+ * so width and height have no meaning.
+ * Assign value here to pass v4l2-compliance test
+ */
+ copy_v4l2_format_dimention(pix_mp, pix, q_data, f->type);
+ } else {
+ copy_v4l2_format_dimention(pix_mp, pix, q_data, f->type);
+
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_EXINFO,
+ "type=%d state=%d Format information could not be read, not ready yet!\n",
+ f->type, ctx->state);
+ }
+
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_PROT,
+ "%s, type: %u, planes: %u, fmt: %x\n",
+ __func__, f->type,
+ V4L2_TYPE_IS_MULTIPLANAR(f->type) ?
+ q_data->fmt->num_planes : 1,
+ q_data->fmt->fourcc);
+
+ return 0;
+}
+
+static int vidioc_vdec_create_bufs(struct file *file, void *priv,
+ struct v4l2_create_buffers *create)
+{
+ struct aml_vcodec_ctx *ctx = fh_to_ctx(priv);
+
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_PROT,
+ "%s, type: %u, count: %u\n",
+ __func__, create->format.type, create->count);
+
+ return v4l2_m2m_ioctl_create_bufs(file, priv, create);
+}
+
+/*int vidioc_vdec_g_ctrl(struct file *file, void *fh,
+ struct v4l2_control *a)
+{
+ struct aml_vcodec_ctx *ctx = fh_to_ctx(fh);
+
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_PROT,
+ "%s, id: %d\n", __func__, a->id);
+
+ if (a->id == V4L2_CID_MIN_BUFFERS_FOR_CAPTURE)
+ a->value = 4;
+ else if (a->id == V4L2_CID_MIN_BUFFERS_FOR_OUTPUT)
+ a->value = 8;
+
+ return 0;
+}*/
+
+static int vb2ops_vdec_queue_setup(struct vb2_queue *vq,
+ unsigned int *nbuffers,
+ unsigned int *nplanes,
+ unsigned int sizes[], struct device *alloc_devs[])
+{
+ struct aml_vcodec_ctx *ctx = vb2_get_drv_priv(vq);
+ struct aml_q_data *q_data;
+ unsigned int i;
+
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_PROT, "%s, type: %d\n",
+ __func__, vq->type);
+
+ q_data = aml_vdec_get_q_data(ctx, vq->type);
+ if (q_data == NULL) {
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_ERROR,
+ "vq->type=%d err\n", vq->type);
+ return -EINVAL;
+ }
+
+ if (*nplanes) {
+ for (i = 0; i < *nplanes; i++) {
+ if (sizes[i] < q_data->sizeimage[i])
+ return -EINVAL;
+ alloc_devs[i] = &ctx->dev->plat_dev->dev;
+
+ if (!V4L2_TYPE_IS_OUTPUT(vq->type))
+ alloc_devs[i] = v4l_get_dev_from_codec_mm();
+ }
+ } else {
+ int dw_mode = VDEC_DW_NO_AFBC;
+
+ if (vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+ *nplanes = 2;
+ else
+ *nplanes = 1;
+
+ if (!vdec_if_get_param(ctx, GET_PARAM_DW_MODE, &dw_mode)) {
+ if (dw_mode == VDEC_DW_AFBC_ONLY)
+ *nplanes = 1;
+ }
+
+ for (i = 0; i < *nplanes; i++) {
+ sizes[i] = q_data->sizeimage[i];
+ if (V4L2_TYPE_IS_OUTPUT(vq->type) && ctx->output_dma_mode)
+ sizes[i] = 1;
+ alloc_devs[i] = &ctx->dev->plat_dev->dev;
+
+ if (!V4L2_TYPE_IS_OUTPUT(vq->type))
+ alloc_devs[i] = v4l_get_dev_from_codec_mm();
+ }
+ }
+
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_BUFMGR,
+ "type: %d, plane: %d, buf cnt: %d, size: [Y: %u, C: %u]\n",
+ vq->type, *nplanes, *nbuffers, sizes[0], sizes[1]);
+
+ return 0;
+}
+
+static int vb2ops_vdec_buf_prepare(struct vb2_buffer *vb)
+{
+ struct aml_vcodec_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
+ struct aml_q_data *q_data;
+ struct vb2_v4l2_buffer *vb2_v4l2 = NULL;
+ struct aml_video_dec_buf *buf = NULL;
+ int i;
+
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_PROT,
+ "%s, type: %d, idx: %d\n",
+ __func__, vb->vb2_queue->type, vb->index);
+
+ if (vb->memory == VB2_MEMORY_DMABUF
+ && V4L2_TYPE_IS_OUTPUT(vb->vb2_queue->type))
+ return 0;
+
+ q_data = aml_vdec_get_q_data(ctx, vb->vb2_queue->type);
+
+ for (i = 0; i < q_data->fmt->num_planes; i++) {
+ if (vb2_plane_size(vb, i) < q_data->sizeimage[i]) {
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_ERROR,
+ "data will not fit into plane %d (%lu < %d)\n",
+ i, vb2_plane_size(vb, i),
+ q_data->sizeimage[i]);
+ }
+ }
+
+ vb2_v4l2 = to_vb2_v4l2_buffer(vb);
+ buf = container_of(vb2_v4l2, struct aml_video_dec_buf, vb);
+
+ if (vb2_v4l2->meta_ptr && (copy_from_user(buf->meta_data,
+ (void *)vb2_v4l2->meta_ptr, META_DATA_SIZE + 4))) {
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_ERROR,
+ "%s:copy meta data error. ptr: %lx\n", __func__, vb2_v4l2->meta_ptr);
+ }
+
+ return 0;
+}
+
+static int init_mmu_bmmu_box(struct aml_vcodec_ctx *ctx)
+{
+ int i;
+ int mmu_flag = ctx->is_drm_mode? CODEC_MM_FLAGS_TVP:0;
+ int bmmu_flag = mmu_flag;
+ u32 dw_mode = VDEC_DW_NO_AFBC;
+
+ ctx->comp_bufs = vzalloc(sizeof(*ctx->comp_bufs) * V4L_CAP_BUFF_MAX);
+ if (!ctx->comp_bufs)
+ return -ENOMEM;
+
+ if (vdec_if_get_param(ctx, GET_PARAM_DW_MODE, &dw_mode)) {
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_ERROR, "invalid dw_mode\n");
+ goto free_comp_bufs;
+ }
+
+ /* init mmu box */
+ ctx->mmu_box = decoder_mmu_box_alloc_box("v4l2_dec",
+ ctx->id, V4L_CAP_BUFF_MAX,
+ ctx->comp_info.max_size * SZ_1M, mmu_flag);
+ if (!ctx->mmu_box) {
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_ERROR, "fail to create mmu box\n");
+ goto free_comp_bufs;
+ }
+
+ /* init bmmu box */
+ bmmu_flag |= CODEC_MM_FLAGS_CMA_CLEAR | CODEC_MM_FLAGS_FOR_VDECODER;
+ ctx->bmmu_box = decoder_bmmu_box_alloc_box("v4l2_dec",
+ ctx->id, V4L_CAP_BUFF_MAX,
+ 4 + PAGE_SHIFT, bmmu_flag);
+ if (!ctx->bmmu_box) {
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_ERROR, "fail to create bmmu box\n");
+ goto free_mmubox;
+ }
+
+ if (dw_mode & 0x20) {
+ /* init mmu box dw*/
+ ctx->mmu_box_dw = decoder_mmu_box_alloc_box("v4l2_dec",
+ ctx->id, V4L_CAP_BUFF_MAX,
+ ctx->comp_info.max_size * SZ_1M, mmu_flag);
+ if (!ctx->mmu_box_dw) {
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_ERROR, "fail to create mmu box dw\n");
+ goto free_bmmubox;
+ }
+
+ /* init bmmu box dw*/
+ bmmu_flag |= CODEC_MM_FLAGS_CMA_CLEAR | CODEC_MM_FLAGS_FOR_VDECODER;
+ ctx->bmmu_box_dw = decoder_bmmu_box_alloc_box("v4l2_dec",
+ ctx->id, V4L_CAP_BUFF_MAX,
+ 4 + PAGE_SHIFT, bmmu_flag);
+ if (!ctx->bmmu_box_dw) {
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_ERROR, "fail to create bmmu box dw\n");
+ goto free_mmubox_dw;
+ }
+ }
+
+ kref_init(&ctx->box_ref);
+ for (i = 0; i < V4L_CAP_BUFF_MAX; i++) {
+ struct internal_comp_buf *buf;
+ buf = &ctx->comp_bufs[i];
+ buf->index = i;
+ buf->ref = 0;
+ buf->box_ref = &ctx->box_ref;
+ buf->mmu_box = ctx->mmu_box;
+ buf->bmmu_box = ctx->bmmu_box;
+ buf->mmu_box_dw = ctx->mmu_box_dw;
+ buf->bmmu_box_dw = ctx->bmmu_box_dw;
+ }
+ kref_get(&ctx->ctx_ref);
+
+ ctx->uvm_proxy = vzalloc(sizeof(*ctx->uvm_proxy) * V4L_CAP_BUFF_MAX);
+ if (!ctx->uvm_proxy)
+ goto free_mmubox;
+
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_PRINFO,
+ "box init, bmmu: %px, mmu: %px, mmu_dw: %px bmmu_dw: %px\n",
+ ctx->bmmu_box, ctx->mmu_box, ctx->mmu_box_dw, ctx->bmmu_box_dw);
+
+ return 0;
+free_mmubox_dw:
+ decoder_mmu_box_free(ctx->mmu_box_dw);
+ ctx->mmu_box_dw = NULL;
+
+free_bmmubox:
+ decoder_bmmu_box_free(ctx->bmmu_box);
+ ctx->bmmu_box = NULL;
+
+free_mmubox:
+ decoder_mmu_box_free(ctx->mmu_box);
+ ctx->mmu_box = NULL;
+
+free_comp_bufs:
+ vfree(ctx->comp_bufs);
+ ctx->comp_bufs = NULL;
+
+ return -1;
+}
+
+void aml_alloc_buffer(struct aml_vcodec_ctx *ctx, int flag)
+{
+ int i = 0;
+
+ if (flag & DV_TYPE) {
+ for (i = 0; i < V4L_CAP_BUFF_MAX; i++) {
+ ctx->aux_infos.bufs[i].md_buf = vzalloc(MD_BUF_SIZE);
+ if (ctx->aux_infos.bufs[i].md_buf == NULL) {
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_ERROR,
+ "v4l2 alloc %dth dv md buffer fail\n", i);
+ }
+
+ ctx->aux_infos.bufs[i].comp_buf = vzalloc(COMP_BUF_SIZE);
+ if (ctx->aux_infos.bufs[i].comp_buf == NULL) {
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_ERROR,
+ "v4l2 alloc %dth dv comp buffer fail\n", i);
+ }
+ }
+ }
+
+ if (flag & SEI_TYPE) {
+ for (i = 0; i < V4L_CAP_BUFF_MAX; i++) {
+ ctx->aux_infos.bufs[i].sei_buf = vzalloc(SEI_BUF_SIZE);
+ if (ctx->aux_infos.bufs[i].sei_buf) {
+ ctx->aux_infos.bufs[i].sei_size = 0;
+ ctx->aux_infos.bufs[i].sei_state = 1;
+ ctx->aux_infos.sei_need_free = false;
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_EXINFO,
+ "v4l2 alloc %dth aux buffer:%px\n",
+ i, ctx->aux_infos.bufs[i].sei_buf);
+ } else {
+ ctx->aux_infos.bufs[i].sei_buf = NULL;
+ ctx->aux_infos.bufs[i].sei_state = 0;
+ ctx->aux_infos.bufs[i].sei_size = 0;
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_ERROR,
+ "v4l2 alloc %dth aux buffer fail\n", i);
+ }
+ }
+ }
+}
+
+void aml_free_buffer(struct aml_vcodec_ctx *ctx, int flag)
+{
+ int i = 0;
+
+ if (flag & DV_TYPE) {
+ for (i = 0; i < V4L_CAP_BUFF_MAX; i++) {
+ if (ctx->aux_infos.bufs[i].md_buf != NULL) {
+ vfree(ctx->aux_infos.bufs[i].md_buf);
+ ctx->aux_infos.bufs[i].md_buf = NULL;
+ }
+
+ if (ctx->aux_infos.bufs[i].comp_buf != NULL) {
+ vfree(ctx->aux_infos.bufs[i].comp_buf);
+ ctx->aux_infos.bufs[i].comp_buf = NULL;
+ }
+ }
+ }
+
+ if (flag & SEI_TYPE) {
+ for (i = 0; i < V4L_CAP_BUFF_MAX; i++) {
+ if (ctx->aux_infos.bufs[i].sei_buf != NULL) {
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_EXINFO,
+ "v4l2 free %dth aux buffer:%px\n",
+ i, ctx->aux_infos.bufs[i].sei_buf);
+ vfree(ctx->aux_infos.bufs[i].sei_buf);
+ ctx->aux_infos.bufs[i].sei_state = 0;
+ ctx->aux_infos.bufs[i].sei_size = 0;
+ ctx->aux_infos.bufs[i].sei_buf = NULL;
+ }
+ }
+ }
+}
+
+void aml_free_one_sei_buffer(struct aml_vcodec_ctx *ctx, char **addr, int *size, int idx)
+{
+ if (ctx->aux_infos.bufs[idx].sei_buf != NULL) {
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_EXINFO,
+ "v4l2 free %dth aux buffer:%px\n",
+ idx, ctx->aux_infos.bufs[idx].sei_buf);
+
+ vfree(ctx->aux_infos.bufs[idx].sei_buf);
+ ctx->aux_infos.bufs[idx].sei_state = 0;
+ ctx->aux_infos.bufs[idx].sei_size = 0;
+ ctx->aux_infos.bufs[idx].sei_buf = NULL;
+ *addr = NULL;
+ *size = 0;
+ ctx->aux_infos.sei_need_free = true;
+ }
+}
+
+void aml_bind_sei_buffer(struct aml_vcodec_ctx *ctx, char **addr, int *size, int *idx)
+{
+ int index = ctx->aux_infos.sei_index;
+ int count = 0;
+
+ if (ctx->aux_infos.sei_need_free) {
+ for (count = 0; count < V4L_CAP_BUFF_MAX; count++) {
+ if ((ctx->aux_infos.bufs[index].sei_buf != NULL) &&
+ (ctx->aux_infos.bufs[index].sei_state == 1)) {
+ break;
+ }
+ index = (index + 1) % V4L_CAP_BUFF_MAX;
+ }
+ } else {
+ for (count = 0; count < V4L_CAP_BUFF_MAX; count++) {
+ if ((ctx->aux_infos.bufs[index].sei_buf != NULL) &&
+ ((ctx->aux_infos.bufs[index].sei_state == 1) ||
+ (ctx->aux_infos.bufs[index].sei_state == 2))) {
+ memset(ctx->aux_infos.bufs[index].sei_buf, 0, SEI_BUF_SIZE);
+ ctx->aux_infos.bufs[index].sei_size = 0;
+ break;
+ }
+ index = (index + 1) % V4L_CAP_BUFF_MAX;
+ }
+ }
+
+ if (count == V4L_CAP_BUFF_MAX) {
+ *addr = NULL;
+ *size = 0;
+ } else {
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_EXINFO,
+ "v4l2 bind %dth aux buffer:%px, count = %d\n",
+ index, ctx->aux_infos.bufs[index].sei_buf, count);
+ *addr = ctx->aux_infos.bufs[index].sei_buf;
+ *size = ctx->aux_infos.bufs[index].sei_size;
+ *idx = index;
+ ctx->aux_infos.bufs[index].sei_state = 2;
+ ctx->aux_infos.sei_index = (index + 1) % V4L_CAP_BUFF_MAX;
+ }
+}
+
+void aml_bind_dv_buffer(struct aml_vcodec_ctx *ctx, char **comp_buf, char **md_buf)
+{
+ int index = ctx->aux_infos.dv_index;
+
+ if ((ctx->aux_infos.bufs[index].comp_buf != NULL) &&
+ (ctx->aux_infos.bufs[index].md_buf != NULL)) {
+ *comp_buf = ctx->aux_infos.bufs[index].comp_buf;
+ *md_buf = ctx->aux_infos.bufs[index].md_buf;
+ ctx->aux_infos.dv_index = (index + 1) % V4L_CAP_BUFF_MAX;
+ }
+}
+
+void aml_v4l_ctx_release(struct kref *kref)
+{
+ struct aml_vcodec_ctx * ctx;
+
+ ctx = container_of(kref, struct aml_vcodec_ctx, ctx_ref);
+
+ if (ctx->vpp) {
+ atomic_dec(&ctx->dev->vpp_count);
+ aml_v4l2_vpp_destroy(ctx->vpp);
+
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_PRINFO,
+ "vpp destory inst count:%d.\n",
+ atomic_read(&ctx->dev->vpp_count));
+ }
+
+ if (ctx->ge2d) {
+ aml_v4l2_ge2d_destroy(ctx->ge2d);
+
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_PRINFO,
+ "ge2d destory.\n");
+ }
+
+ v4l2_m2m_ctx_release(ctx->m2m_ctx);
+ aml_task_chain_remove(ctx);
+
+ vfree(ctx->meta_infos.meta_bufs);
+ ctx->aux_infos.free_buffer(ctx, SEI_TYPE | DV_TYPE);
+ ctx->aux_infos.free_buffer(ctx, 1);
+
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_PRINFO,
+ "v4ldec has been destroyed.\n");
+
+ if (ctx->sync) {
+ vdec_clean_all_fence(ctx->sync);
+ }
+
+ kfree(ctx);
+}
+
+static void box_release(struct kref *kref)
+{
+ struct aml_vcodec_ctx * ctx
+ = container_of(kref, struct aml_vcodec_ctx, box_ref);
+
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_BUFMGR,
+ "%s, bmmu: %px, mmu: %px mmu_dw: %pu\n",
+ __func__, ctx->bmmu_box, ctx->mmu_box,ctx->mmu_box_dw);
+
+ decoder_bmmu_box_free(ctx->bmmu_box);
+ decoder_mmu_box_free(ctx->mmu_box);
+
+ if (ctx->config.parm.dec.cfg.double_write_mode & 0x20) {
+ decoder_mmu_box_free(ctx->mmu_box_dw);
+ decoder_bmmu_box_free(ctx->bmmu_box_dw);
+ }
+ vfree(ctx->comp_bufs);
+ vfree(ctx->uvm_proxy);
+ kref_put(&ctx->ctx_ref, aml_v4l_ctx_release);
+}
+
+static void internal_buf_free(void *arg)
+{
+ struct internal_comp_buf* ibuf =
+ (struct internal_comp_buf*)arg;
+ struct aml_vcodec_ctx * ctx
+ = container_of(ibuf->box_ref,struct aml_vcodec_ctx, box_ref);
+
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_BUFMGR,
+ "%s, idx:%d\n", __func__, ibuf->index);
+
+ mutex_lock(&ctx->comp_lock);
+
+ if (!(ibuf->ref & 0xff00)) {
+ decoder_mmu_box_free_idx(ibuf->mmu_box, ibuf->index);
+ decoder_bmmu_box_free_idx(ibuf->bmmu_box, ibuf->index);
+
+ if (ctx->config.parm.dec.cfg.double_write_mode & 0x20) {
+ decoder_mmu_box_free_idx(ibuf->mmu_box_dw, ibuf->index);
+ decoder_bmmu_box_free_idx(ibuf->bmmu_box_dw, ibuf->index);
+ }
+ }
+ ibuf->ref = 0;
+
+ mutex_unlock(&ctx->comp_lock);
+
+ kref_put(ibuf->box_ref, box_release);
+}
+
+static void internal_buf_free2(void *arg)
+{
+ struct internal_comp_buf *ibuf =
+ container_of(arg, struct internal_comp_buf, priv_data);
+ struct aml_vcodec_ctx * ctx
+ = container_of(ibuf->box_ref, struct aml_vcodec_ctx, box_ref);
+
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_BUFMGR,
+ "%s, idx: %d\n", __func__, ibuf->index);
+
+ mutex_lock(&ctx->comp_lock);
+
+ if (!(ibuf->ref & 0xff00)) {
+ decoder_mmu_box_free_idx(ibuf->mmu_box, ibuf->index);
+ decoder_bmmu_box_free_idx(ibuf->bmmu_box, ibuf->index);
+
+ if (ctx->config.parm.dec.cfg.double_write_mode & 0x20) {
+ decoder_mmu_box_free_idx(ibuf->mmu_box_dw, ibuf->index);
+ decoder_bmmu_box_free_idx(ibuf->bmmu_box_dw, ibuf->index);
+ }
+ }
+ ibuf->ref = 0;
+
+ mutex_unlock(&ctx->comp_lock);
+
+ kref_put(ibuf->box_ref, box_release);
+}
+
+static void aml_uvm_buf_free(void *arg)
+{
+ struct aml_uvm_buff_ref * ubuf =
+ (struct aml_uvm_buff_ref*)arg;
+ struct aml_vcodec_ctx * ctx
+ = container_of(ubuf->ref, struct aml_vcodec_ctx, ctx_ref);
+
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_BUFMGR,
+ "%s, vb:%d, dbuf:%px, ino:%lu\n",
+ __func__, ubuf->index, ubuf->dbuf,
+ file_inode(ubuf->dbuf->file)->i_ino);
+
+ kref_put(ubuf->ref, aml_v4l_ctx_release);
+ vfree(ubuf);
+}
+
+static int uvm_attach_hook_mod_local(struct aml_vcodec_ctx *ctx,
+ struct uvm_hook_mod_info *uvm)
+{
+ struct internal_comp_buf* ibuf = uvm->arg;
+
+ ctx->uvm_proxy[ibuf->index] = *uvm;
+
+ return 0;
+}
+
+static int update_comp_buffer_to_reuse(struct aml_vcodec_ctx *ctx,
+ struct aml_video_dec_buf *buf)
+{
+ struct internal_comp_buf* ibuf = NULL;
+
+ mutex_lock(&ctx->comp_lock);
+
+ ibuf = vb_to_comp(ctx, &buf->vb.vb2_buf);
+ if (!ibuf) {
+ mutex_unlock(&ctx->comp_lock);
+ return 0;
+ }
+
+ if (ibuf->ref & 0xff) {
+ buf->internal_index = ibuf->index;
+ ibuf->frame_buffer_size = ctx->comp_info.frame_buffer_size;
+
+ if (ctx->comp_info.header_size != ibuf->header_size) {
+ decoder_bmmu_box_free_idx(ctx->bmmu_box, ibuf->index);
+ if (decoder_bmmu_box_alloc_buf_phy(ctx->bmmu_box,
+ ibuf->index, ctx->comp_info.header_size,
+ "v4l2_dec", &ibuf->header_addr) < 0) {
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_ERROR,
+ "fail to alloc %dth bmmu\n",
+ ibuf->index);
+ mutex_unlock(&ctx->comp_lock);
+ return -ENOMEM;
+ }
+ ibuf->header_size = ctx->comp_info.header_size;
+ }
+
+ ibuf->ref |= (1 << 8);
+
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_BUFMGR,
+ "%s, reuse comp buffer vb2:%d <--> internal: %d, header_addr 0x%lx, size: %u\n",
+ __func__, buf->vb.vb2_buf.index,
+ buf->internal_index,
+ ibuf->header_addr,
+ ibuf->header_size);
+ }
+
+ mutex_unlock(&ctx->comp_lock);
+
+ return (ibuf->ref & 0xff00) ? 1 : 0;
+}
+
+static int bind_comp_buffer_to_uvm(struct aml_vcodec_ctx *ctx,
+ struct aml_video_dec_buf *buf)
+{
+ int ret, i;
+ struct dma_buf * dma = buf->vb.vb2_buf.planes[0].dbuf;
+ struct aml_dec_params *parms = &ctx->config.parm.dec;
+ struct uvm_hook_mod_info u_info;
+ struct internal_comp_buf* ibuf;
+ u32 dw_mode = VDEC_DW_NO_AFBC;
+
+ /* get header and page size */
+ if (vdec_if_get_param(ctx, GET_PARAM_COMP_BUF_INFO, &ctx->comp_info)) {
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_ERROR, "fail to get comp info\n");
+ return -EINVAL;
+ }
+
+ if (!ctx->bmmu_box || !ctx->mmu_box)
+ if (init_mmu_bmmu_box(ctx))
+ return -EINVAL;
+
+ ret = update_comp_buffer_to_reuse(ctx, buf);
+ if (ret < 0)
+ return ret;
+
+ if (ret == 1 /*reused*/)
+ return 0;
+
+ for (i = 0; i < V4L_CAP_BUFF_MAX; i++) {
+ if (!ctx->comp_bufs[i].ref)
+ break;
+ }
+
+ if (i == V4L_CAP_BUFF_MAX) {
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_ERROR, "out of internal buf\n");
+ return -EINVAL;
+ }
+
+ if (vdec_if_get_param(ctx, GET_PARAM_DW_MODE, &dw_mode)) {
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_ERROR, "invalid dw_mode\n");
+ return -EINVAL;
+ }
+
+ buf->internal_index = i;
+ ibuf = &ctx->comp_bufs[i];
+ ibuf->frame_buffer_size = ctx->comp_info.frame_buffer_size;
+ ibuf->header_size = ctx->comp_info.header_size;
+
+ /* allocate header */
+ ret = decoder_bmmu_box_alloc_buf_phy(ctx->bmmu_box,
+ ibuf->index, ctx->comp_info.header_size,
+ "v4l2_dec", &ibuf->header_addr);
+ if (ret < 0) {
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_ERROR, "fail to alloc %dth bmmu\n", i);
+ return -ENOMEM;
+ }
+
+ if (dw_mode & 0x20) {
+ ret = decoder_bmmu_box_alloc_buf_phy(ctx->bmmu_box_dw,
+ ibuf->index, ctx->comp_info.header_size,
+ "v4l2_dec", &ibuf->header_dw_addr);
+ if (ret < 0) {
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_ERROR, "fail to alloc %dth bmmu dw\n", i);
+ return -ENOMEM;
+ }
+ }
+
+ kref_get(&ctx->box_ref);
+ ibuf->ref = 1;
+
+ /* frame SG buffer need to be realloc inside decoder,
+ * just before slice decoding to save memory
+ */
+ u_info.type = VF_SRC_DECODER;
+ u_info.arg = ibuf;
+ u_info.free = internal_buf_free;
+
+ if (parms->cfg.uvm_hook_type == VF_PROCESS_V4LVIDEO) {
+ /* adapted video composer to use for hwc. */
+ ibuf->priv_data.v4l_inst_id = ctx->id;
+ u_info.type = VF_PROCESS_V4LVIDEO;
+ u_info.arg = &ibuf->priv_data;
+ u_info.free = internal_buf_free2;
+ }
+
+ ret = dmabuf_is_uvm(dma) ?
+ uvm_attach_hook_mod(dma, &u_info) :
+ uvm_attach_hook_mod_local(ctx, &u_info);
+ if (ret < 0) {
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_ERROR, "fail to set dmabuf priv buf\n");
+ goto bmmu_box_free;
+ }
+
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_BUFMGR,
+ "%s, bind vb2:(%d, %px) <--> internal: (%d, %px) header_addr 0x%lx, size: %u\n",
+ __func__, buf->vb.vb2_buf.index,
+ dma, i, ibuf, ibuf->header_addr,
+ ctx->comp_info.header_size);
+
+ return 0;
+
+bmmu_box_free:
+ if (dw_mode & 0x20) {
+ decoder_bmmu_box_free_idx(ibuf->bmmu_box_dw, ibuf->index);
+ }
+ decoder_bmmu_box_free_idx(ibuf->bmmu_box, ibuf->index);
+ kref_put(&ctx->box_ref, box_release);
+ ibuf->ref = 0;
+ return -EINVAL;
+}
+
+static int aml_uvm_buff_attach(struct vb2_buffer * vb)
+{
+ int ret = 0;
+ struct dma_buf *dbuf = vb->planes[0].dbuf;
+ struct uvm_hook_mod_info u_info;
+ struct aml_vcodec_ctx *ctx =
+ vb2_get_drv_priv(vb->vb2_queue);
+ struct aml_uvm_buff_ref *ubuf = NULL;
+
+ if (vb->memory != VB2_MEMORY_DMABUF || !dmabuf_is_uvm(dbuf))
+ return 0;
+
+ ubuf = vzalloc(sizeof(struct aml_uvm_buff_ref));
+ if (ubuf == NULL)
+ return -ENOMEM;
+
+ ubuf->index = vb->index;
+ ubuf->addr = vb2_dma_contig_plane_dma_addr(vb, 0);
+ ubuf->dbuf = dbuf;
+ ubuf->ref = &ctx->ctx_ref;
+
+ u_info.type = VF_PROCESS_DECODER;
+ u_info.arg = (void *)ubuf;
+ u_info.free = aml_uvm_buf_free;
+ ret = uvm_attach_hook_mod(dbuf, &u_info);
+ if (ret < 0) {
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_ERROR,
+ "aml uvm buffer %d attach fail.\n",
+ ubuf->index);
+ return ret;
+ }
+
+ kref_get(ubuf->ref);
+
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_BUFMGR,
+ "%s, vb:%d, dbuf:%px, ino:%lu\n",
+ __func__, ubuf->index, ubuf->dbuf,
+ file_inode(ubuf->dbuf->file)->i_ino);
+
+ return ret;
+}
+
+static struct internal_comp_buf* vb_to_comp(struct aml_vcodec_ctx *ctx,
+ struct vb2_buffer *vb)
+{
+ struct aml_dec_params *parms = &ctx->config.parm.dec;
+ bool is_v4lvideo = (parms->cfg.uvm_hook_type == VF_PROCESS_V4LVIDEO);
+ enum uvm_hook_mod_type u_type =
+ is_v4lvideo ? VF_PROCESS_V4LVIDEO : VF_SRC_DECODER;
+ struct dma_buf *dbuf = vb->planes[0].dbuf;
+ struct internal_comp_buf *ibuf = NULL;
+ struct uvm_hook_mod *uhmod = NULL;
+
+ uhmod = uvm_get_hook_mod(dbuf, u_type);
+ if (IS_ERR_OR_NULL(uhmod))
+ return NULL;
+
+ ibuf = !is_v4lvideo ? (struct internal_comp_buf *) uhmod->arg :
+ container_of(uhmod->arg, struct internal_comp_buf, priv_data);
+
+ uvm_put_hook_mod(dbuf, u_type);
+
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_EXINFO,
+ "%s, vb2: (%d, %px) --> comp: (%d, %px)\n",
+ __func__, vb->index, dbuf, ibuf->index, ibuf);
+
+ return ibuf;
+}
+
+static void vb2ops_vdec_buf_queue(struct vb2_buffer *vb)
+{
+ struct aml_vcodec_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
+ struct vb2_v4l2_buffer *vb2_v4l2 = to_vb2_v4l2_buffer(vb);
+ struct aml_video_dec_buf *buf =
+ container_of(vb2_v4l2, struct aml_video_dec_buf, vb);
+ struct vdec_v4l2_buffer *fb = &buf->frame_buffer;
+ struct aml_vcodec_mem src_mem;
+
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_PROT,
+ "%s, vb: %lx, type: %d, idx: %d, state: %d, used: %d, ts: %llu\n",
+ __func__, (ulong) vb, vb->vb2_queue->type,
+ vb->index, vb->state, buf->used, vb->timestamp);
+
+ /*
+ * check if this buffer is ready to be used after decode
+ */
+ if (!V4L2_TYPE_IS_OUTPUT(vb->vb2_queue->type)) {
+ u32 dw_mode = VDEC_DW_NO_AFBC;
+
+ if (vdec_if_get_param(ctx, GET_PARAM_DW_MODE, &dw_mode)) {
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_ERROR, "invalid dw_mode\n");
+ return;
+
+ }
+
+ if (!buf->que_in_m2m) {
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_BUFMGR,
+ "enque capture buf idx %d, vf: %lx\n",
+ vb->index, (ulong) v4l_get_vf_handle(vb2_v4l2->private));
+
+ /* bind compressed buffer to uvm */
+ if ((dw_mode != VDEC_DW_NO_AFBC) &&
+ vb->memory == VB2_MEMORY_DMABUF &&
+ bind_comp_buffer_to_uvm(ctx, buf)) {
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_ERROR, "fail to bind comp buffer\n");
+ return;
+ }
+
+ /* DI hook must be detached if the dmabuff be reused. */
+ if (ctx->vpp_cfg.enable_local_buf) {
+ struct dma_buf *dma = vb->planes[0].dbuf;
+
+ if (dmabuf_is_uvm(dma) &&
+ uvm_detach_hook_mod(dma, VF_PROCESS_DI) < 0) {
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_EXINFO,
+ "dmabuf without attach DI hook.\n");
+ }
+ }
+
+ task_chain_clean(fb->task);
+
+ ctx->cap_pool.seq[ctx->cap_pool.in++] =
+ (V4L_CAP_BUFF_IN_M2M << 16 | vb->index);
+ v4l2_m2m_buf_queue(ctx->m2m_ctx, vb2_v4l2);
+ buf->que_in_m2m = true;
+
+ fb->status = FB_ST_INIT;
+ ATRACE_COUNTER("VC_IN_VSINK-2.storage", vb->index);
+
+ /* check dpb ready */
+ aml_check_dpb_ready(ctx);
+ } else {
+ struct vframe_s *vf = fb->vframe;
+ struct task_chain_s *task = fb->task;
+
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_OUTPUT,
+ "IN__BUFF (%s, st:%d, seq:%d) vb:(%d, %px), vf:(%d, %px), ts:%lld, "
+ "Y:(%lx, %u) C/U:(%lx, %u) V:(%lx, %u)\n",
+ ctx->ada_ctx->frm_name, fb->status, vf ? vf->index_disp : -1,
+ vb->index, vb,
+ vf ? vf->index & 0xff : -1, vf,
+ vf ? vf->timestamp : 0,
+ fb->m.mem[0].addr, fb->m.mem[0].size,
+ fb->m.mem[1].addr, fb->m.mem[1].size,
+ fb->m.mem[2].addr, fb->m.mem[2].size);
+
+ ATRACE_COUNTER("VC_IN_VSINK-4.recycle", vb->index);
+
+ task->recycle(task, TASK_TYPE_V4L_SINK);
+ }
+
+ wake_up_interruptible(&ctx->cap_wq);
+ return;
+ }
+
+ v4l2_m2m_buf_queue(ctx->m2m_ctx, to_vb2_v4l2_buffer(vb));
+
+ if (ctx->state != AML_STATE_INIT) {
+ return;
+ }
+
+ buf->used = true;
+ vb2_v4l2 = to_vb2_v4l2_buffer(vb);
+ buf = container_of(vb2_v4l2, struct aml_video_dec_buf, vb);
+ if (buf->lastframe) {
+ /* This shouldn't happen. Just in case. */
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_ERROR,
+ "Invalid flush buffer.\n");
+ buf->used = false;
+ v4l2_m2m_src_buf_remove(ctx->m2m_ctx);
+ if (ctx->is_drm_mode && (vb->memory == VB2_MEMORY_DMABUF))
+ wake_up_interruptible(&ctx->wq);
+
+ return;
+ }
+
+ src_mem.index = vb->index;
+ src_mem.vaddr = vb2_plane_vaddr(vb, 0);
+ src_mem.addr = sg_dma_address(buf->out_sgt->sgl);
+ src_mem.size = vb->planes[0].bytesused;
+ src_mem.model = vb->memory;
+ src_mem.timestamp = vb->timestamp;
+ src_mem.meta_ptr = (ulong)buf->meta_data;
+
+ if (vdec_if_probe(ctx, &src_mem, NULL)) {
+ buf->used = false;
+ v4l2_m2m_src_buf_remove(ctx->m2m_ctx);
+
+ if (ctx->is_drm_mode &&
+ (src_mem.model == VB2_MEMORY_DMABUF)) {
+ wake_up_interruptible(&ctx->wq);
+ } else {
+ v4l2_buff_done(to_vb2_v4l2_buffer(vb),
+ VB2_BUF_STATE_DONE);
+ }
+
+ return;
+ }
+
+ /*
+ * If on model dmabuf must remove the buffer
+ * because this data has been consumed by hw.
+ */
+ buf->used = false;
+ v4l2_m2m_src_buf_remove(ctx->m2m_ctx);
+
+ if (ctx->is_drm_mode &&
+ (src_mem.model == VB2_MEMORY_DMABUF)) {
+ wake_up_interruptible(&ctx->wq);
+ } else if (ctx->param_sets_from_ucode) {
+ v4l2_buff_done(to_vb2_v4l2_buffer(vb),
+ VB2_BUF_STATE_DONE);
+ }
+
+ if (vdec_if_get_param(ctx, GET_PARAM_PIC_INFO, &ctx->picinfo)) {
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_ERROR,
+ "GET_PARAM_PICTURE_INFO err\n");
+ return;
+ }
+
+ if (!ctx->picinfo.dpb_frames)
+ return;
+
+ v4l_buf_size_decision(ctx);
+ ctx->last_decoded_picinfo = ctx->picinfo;
+
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_PRINFO,
+ "Picture buffer count: dec:%u, vpp:%u, ge2d:%u, margin:%u, total:%u\n",
+ ctx->picinfo.dpb_frames, ctx->vpp_size, ctx->ge2d_size,
+ ctx->picinfo.dpb_margin,
+ CTX_BUF_TOTAL(ctx));
+
+ aml_vdec_dispatch_event(ctx, V4L2_EVENT_SRC_CH_RESOLUTION);
+
+ mutex_lock(&ctx->state_lock);
+ if (ctx->state == AML_STATE_INIT) {
+ ctx->state = AML_STATE_PROBE;
+ ATRACE_COUNTER("V_ST_VSINK-state", ctx->state);
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_STATE,
+ "vcodec state (AML_STATE_PROBE)\n");
+ }
+ mutex_unlock(&ctx->state_lock);
+}
+
+static void vb2ops_vdec_buf_finish(struct vb2_buffer *vb)
+{
+ struct aml_vcodec_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
+ struct vb2_v4l2_buffer *vb2_v4l2 = NULL;
+ struct aml_video_dec_buf *buf = NULL;
+
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_PROT,
+ "%s, type: %d, idx: %d\n",
+ __func__, vb->vb2_queue->type, vb->index);
+
+ vb2_v4l2 = to_vb2_v4l2_buffer(vb);
+ buf = container_of(vb2_v4l2, struct aml_video_dec_buf, vb);
+
+ if (buf->error) {
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_ERROR,
+ "Unrecoverable error on buffer.\n");
+ ctx->state = AML_STATE_ABORT;
+ ATRACE_COUNTER("V_ST_VSINK-state", ctx->state);
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_STATE,
+ "vcodec state (AML_STATE_ABORT)\n");
+ }
+}
+
+static int vb2ops_vdec_buf_init(struct vb2_buffer *vb)
+{
+ struct aml_vcodec_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
+ struct vb2_v4l2_buffer *vb2_v4l2 = container_of(vb,
+ struct vb2_v4l2_buffer, vb2_buf);
+ struct aml_video_dec_buf *buf = container_of(vb2_v4l2,
+ struct aml_video_dec_buf, vb);
+ struct vdec_v4l2_buffer *fb = &buf->frame_buffer;
+ u32 size, phy_addr = 0;
+ int i;
+
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_PROT, "%s, type: %d, idx: %d\n",
+ __func__, vb->vb2_queue->type, vb->index);
+
+ if (V4L2_TYPE_IS_OUTPUT(vb->vb2_queue->type)) {
+ buf->lastframe = false;
+ }
+
+ /* codec_mm buffers count */
+ if (!V4L2_TYPE_IS_OUTPUT(vb->type)) {
+ if (vb->memory == VB2_MEMORY_MMAP) {
+ char *owner = __getname();
+
+ snprintf(owner, PATH_MAX, "%s-%d", "v4l-output", ctx->id);
+ strncpy(buf->mem_onwer, owner, sizeof(buf->mem_onwer));
+ buf->mem_onwer[sizeof(buf->mem_onwer) - 1] = '\0';
+ __putname(owner);
+
+ for (i = 0; i < vb->num_planes; i++) {
+ size = vb->planes[i].length;
+ phy_addr = vb2_dma_contig_plane_dma_addr(vb, i);
+ buf->mem[i] = v4l_reqbufs_from_codec_mm(buf->mem_onwer,
+ phy_addr, size, vb->index);
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_BUFMGR,
+ "OUT %c alloc, addr: %x, size: %u, idx: %u\n",
+ (i == 0? 'Y':'C'), phy_addr, size, vb->index);
+ }
+ } else if (vb->memory == VB2_MEMORY_DMABUF) {
+ unsigned int dw_mode = VDEC_DW_NO_AFBC;
+
+ for (i = 0; i < vb->num_planes; i++) {
+ struct dma_buf * dma;
+
+ if (vdec_if_get_param(ctx, GET_PARAM_DW_MODE, &dw_mode)) {
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_ERROR, "invalid dw_mode\n");
+ return -EINVAL;
+ }
+ /* None-DW mode means single layer */
+ if (dw_mode == VDEC_DW_AFBC_ONLY && i > 0) {
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_ERROR,
+ "only support single plane in dw mode 0\n");
+ return -EINVAL;
+ }
+ size = vb->planes[i].length;
+ dma = vb->planes[i].dbuf;
+
+ if (!dmabuf_is_uvm(dma))
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_PRINFO, "non-uvm dmabuf\n");
+ }
+ }
+ }
+
+ if (!V4L2_TYPE_IS_OUTPUT(vb->type)) {
+ struct vframe_s *vf = NULL;
+ struct task_chain_s *task = NULL;
+ struct task_chain_s *task_pre = fb->task;
+ u32 icomp = -1;
+
+ fb_map_table_fetch(ctx, vb, &vf, &task, &icomp);
+ if (vf) {
+ fb->task = task;
+ fb->vframe = vf;
+ vf->v4l_mem_handle = (ulong)fb;
+ buf->internal_index = icomp;
+ task_chain_update_object(task, fb);
+ } else {
+ buf->que_in_m2m = false;
+
+ if (aml_uvm_buff_attach(vb))
+ return -EFAULT;
+
+ if (task_chain_init(&fb->task, ctx, fb, vb->index))
+ return -EFAULT;
+
+ list_add(&fb->task->node, &ctx->task_chain_pool);
+ }
+
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_BUFMGR,
+ "init buffer(%s), vb idx:%d, task:(%px -> %px), addr:(%lx -> %lx), icomp:%d\n",
+ vf ? "update" : "idel",
+ vb->index, task_pre, fb->task,
+ fb->m.mem[0].addr,
+ (ulong) vb2_dma_contig_plane_dma_addr(vb, 0),
+ (int)icomp);
+
+ update_vdec_buf_plane(ctx, fb, vb);
+ }
+
+ if (V4L2_TYPE_IS_OUTPUT(vb->type)) {
+ ulong contig_size;
+
+ buf->out_sgt = vb2_dma_sg_plane_desc(vb, 0);
+
+ contig_size = dmabuf_contiguous_size(buf->out_sgt);
+ if (contig_size < vb->planes[0].bytesused) {
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_ERROR,
+ "contiguous mapping is too small %lu/%u\n",
+ contig_size, size);
+ return -EFAULT;
+ }
+ }
+
+ return 0;
+}
+
+static void vb2ops_vdec_buf_cleanup(struct vb2_buffer *vb)
+{
+ struct aml_vcodec_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
+ struct vb2_v4l2_buffer *vb2_v4l2 = container_of(vb,
+ struct vb2_v4l2_buffer, vb2_buf);
+ struct aml_video_dec_buf *buf = container_of(vb2_v4l2,
+ struct aml_video_dec_buf, vb);
+ struct vdec_v4l2_buffer *fb = &buf->frame_buffer;;
+
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_PROT, "%s, type: %d, idx: %d\n",
+ __func__, vb->vb2_queue->type, vb->index);
+
+ if (!V4L2_TYPE_IS_OUTPUT(vb->type)) {
+ if (vb->memory == VB2_MEMORY_MMAP) {
+ int i;
+
+ for (i = 0; i < vb->num_planes ; i++) {
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_BUFMGR,
+ "OUT %c clean, addr: %lx, size: %u, idx: %u\n",
+ (i == 0)? 'Y':'C',
+ buf->mem[i]->phy_addr, buf->mem[i]->buffer_size, vb->index);
+ v4l_freebufs_back_to_codec_mm(buf->mem_onwer, buf->mem[i]);
+ buf->mem[i] = NULL;
+ }
+ }
+ if (ctx->output_thread_ready) {
+ if (!is_fb_mapped(ctx, fb->m.mem[0].addr)) {
+ list_del(&fb->task->node);
+ task_chain_clean(fb->task);
+ task_chain_release(fb->task);
+ }
+ }
+ }
+}
+
+static int vb2ops_vdec_start_streaming(struct vb2_queue *q, unsigned int count)
+{
+ struct aml_vcodec_ctx *ctx = vb2_get_drv_priv(q);
+
+ ctx->has_receive_eos = false;
+ ctx->v4l_resolution_change = false;
+
+ /* vdec has ready to decode subsequence data of new resolution. */
+ v4l2_m2m_job_resume(ctx->dev->m2m_dev_dec, ctx->m2m_ctx);
+
+ v4l2_m2m_set_dst_buffered(ctx->fh.m2m_ctx, true);
+
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_PROT,
+ "%s, type: %d\n", __func__, q->type);
+
+ return 0;
+}
+
+static void vb2ops_vdec_stop_streaming(struct vb2_queue *q)
+{
+ struct aml_video_dec_buf *buf = NULL;
+ struct vb2_v4l2_buffer *vb2_v4l2 = NULL;
+ struct aml_vcodec_ctx *ctx = vb2_get_drv_priv(q);
+ int i;
+
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_PROT,
+ "%s, type: %d, state: %x, frame_cnt: %d\n",
+ __func__, q->type, ctx->state, ctx->decoded_frame_cnt);
+
+ if (V4L2_TYPE_IS_OUTPUT(q->type))
+ ctx->is_out_stream_off = true;
+ else
+ ctx->is_stream_off = true;
+
+ if (V4L2_TYPE_IS_OUTPUT(q->type)) {
+ struct vb2_queue * que = v4l2_m2m_get_dst_vq(ctx->m2m_ctx);
+ unsigned long flags;
+
+ cancel_work_sync(&ctx->dmabuff_recycle_work);
+ spin_lock_irqsave(&ctx->dmabuff_recycle_lock, flags);
+ INIT_KFIFO(ctx->dmabuff_recycle);
+ spin_unlock_irqrestore(&ctx->dmabuff_recycle_lock, flags);
+
+ while ((vb2_v4l2 = v4l2_m2m_src_buf_remove(ctx->m2m_ctx)))
+ v4l2_buff_done(vb2_v4l2, VB2_BUF_STATE_ERROR);
+
+ for (i = 0; i < q->num_buffers; ++i) {
+ vb2_v4l2 = to_vb2_v4l2_buffer(q->bufs[i]);
+ if (vb2_v4l2->vb2_buf.state == VB2_BUF_STATE_ACTIVE)
+ v4l2_buff_done(vb2_v4l2, VB2_BUF_STATE_ERROR);
+ }
+
+ /*
+ * drop es frame was stored in the vdec_input
+ * if the capture queue have not start streaming.
+ */
+ if (!que->streaming &&
+ (vdec_frame_number(ctx->ada_ctx) > 0) &&
+ (ctx->state < AML_STATE_ACTIVE)) {
+ ctx->state = AML_STATE_INIT;
+ ATRACE_COUNTER("V_ST_VSINK-state", ctx->state);
+ ctx->v4l_resolution_change = false;
+ ctx->reset_flag = V4L_RESET_MODE_NORMAL;
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_PRINFO,
+ "force reset to drop es frames.\n");
+ wake_up_interruptible(&ctx->cap_wq);
+ aml_vdec_reset(ctx);
+ }
+ } else {
+ /* clean output cache and decoder status . */
+ if (ctx->state > AML_STATE_INIT) {
+ wake_up_interruptible(&ctx->cap_wq);
+ aml_vdec_reset(ctx);
+ }
+
+ cancel_work_sync(&ctx->decode_work);
+ mutex_lock(&ctx->capture_buffer_lock);
+ INIT_KFIFO(ctx->capture_buffer);
+ mutex_unlock(&ctx->capture_buffer_lock);
+
+ while ((vb2_v4l2 = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx)))
+ v4l2_buff_done(vb2_v4l2, VB2_BUF_STATE_ERROR);
+
+ for (i = 0; i < q->num_buffers; ++i) {
+ vb2_v4l2 = to_vb2_v4l2_buffer(q->bufs[i]);
+ buf = container_of(vb2_v4l2, struct aml_video_dec_buf, vb);
+ buf->frame_buffer.status = FB_ST_FREE;
+ buf->frame_buffer.vframe = NULL;
+ buf->que_in_m2m = false;
+ buf->used = false;
+ buf->vb.flags = 0;
+ ctx->cap_pool.seq[i] = 0;
+
+ if (vb2_v4l2->vb2_buf.state == VB2_BUF_STATE_ACTIVE)
+ v4l2_buff_done(vb2_v4l2, VB2_BUF_STATE_ERROR);
+
+ /*v4l_dbg(ctx, V4L_DEBUG_CODEC_EXINFO, "idx: %d, state: %d\n",
+ q->bufs[i]->index, q->bufs[i]->state);*/
+ }
+
+ fb_map_table_clean(ctx);
+
+ fb_token_clean(ctx);
+
+ ctx->buf_used_count = 0;
+ ctx->cap_pool.in = 0;
+ ctx->cap_pool.out = 0;
+ ctx->cap_pool.dec = 0;
+ ctx->cap_pool.vpp = 0;
+ }
+}
+
+static void m2mops_vdec_device_run(void *priv)
+{
+ struct aml_vcodec_ctx *ctx = priv;
+ struct aml_vcodec_dev *dev = ctx->dev;
+
+ if (ctx->output_thread_ready)
+ queue_work(dev->decode_workqueue, &ctx->decode_work);
+}
+
+static int m2mops_vdec_job_ready(void *m2m_priv)
+{
+ struct aml_vcodec_ctx *ctx = m2m_priv;
+
+ if (ctx->state < AML_STATE_PROBE ||
+ ctx->state > AML_STATE_FLUSHED)
+ return 0;
+
+ return 1;
+}
+
+static void m2mops_vdec_job_abort(void *priv)
+{
+ struct aml_vcodec_ctx *ctx = priv;
+
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_EXINFO, "%s\n", __func__);
+}
+
+static int aml_vdec_g_v_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct aml_vcodec_ctx *ctx = ctrl_to_ctx(ctrl);
+ int ret = 0;
+
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_PROT,
+ "%s, id: %d\n", __func__, ctrl->id);
+
+ switch (ctrl->id) {
+ case V4L2_CID_MIN_BUFFERS_FOR_CAPTURE:
+ if (ctx->state >= AML_STATE_PROBE) {
+ ctrl->val = CTX_BUF_TOTAL(ctx);
+ } else {
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_ERROR,
+ "Seqinfo not ready.\n");
+ ctrl->val = 0;
+ }
+ break;
+ case V4L2_CID_MIN_BUFFERS_FOR_OUTPUT:
+ ctrl->val = 4;
+ break;
+ case AML_V4L2_GET_INPUT_BUFFER_NUM:
+ if (ctx->ada_ctx != NULL)
+ ctrl->val = vdec_frame_number(ctx->ada_ctx);
+ break;
+ case AML_V4L2_GET_FILMGRAIN_INFO:
+ ctrl->val = ctx->film_grain_present;
+ break;
+ default:
+ ret = -EINVAL;
+ }
+ return ret;
+}
+
+static int aml_vdec_try_s_v_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct aml_vcodec_ctx *ctx = ctrl_to_ctx(ctrl);
+
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_PROT, "%s\n", __func__);
+
+ if (ctrl->id == AML_V4L2_SET_DRMMODE) {
+ ctx->is_drm_mode = ctrl->val;
+ ctx->param_sets_from_ucode = true;
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_PRINFO,
+ "set stream mode: %x\n", ctrl->val);
+ } else if (ctrl->id == AML_V4L2_SET_DURATION) {
+ vdec_set_duration(ctrl->val);
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_PRINFO,
+ "set duration: %x\n", ctrl->val);
+ }
+
+ return 0;
+}
+
+static const struct v4l2_ctrl_ops aml_vcodec_dec_ctrl_ops = {
+ .g_volatile_ctrl = aml_vdec_g_v_ctrl,
+ .try_ctrl = aml_vdec_try_s_v_ctrl,
+};
+
+static const struct v4l2_ctrl_config ctrl_st_mode = {
+ .name = "drm mode",
+ .id = AML_V4L2_SET_DRMMODE,
+ .ops = &aml_vcodec_dec_ctrl_ops,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .flags = V4L2_CTRL_FLAG_WRITE_ONLY,
+ .min = 0,
+ .max = 1,
+ .step = 1,
+ .def = 0,
+};
+
+static const struct v4l2_ctrl_config ctrl_gt_input_buffer_number = {
+ .name = "input buffer number",
+ .id = AML_V4L2_GET_INPUT_BUFFER_NUM,
+ .ops = &aml_vcodec_dec_ctrl_ops,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .flags = V4L2_CTRL_FLAG_VOLATILE,
+ .min = 0,
+ .max = 128,
+ .step = 1,
+ .def = 0,
+};
+
+static const struct v4l2_ctrl_config ctrl_st_duration = {
+ .name = "duration",
+ .id = AML_V4L2_SET_DURATION,
+ .ops = &aml_vcodec_dec_ctrl_ops,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .flags = V4L2_CTRL_FLAG_WRITE_ONLY,
+ .min = 0,
+ .max = 96000,
+ .step = 1,
+ .def = 0,
+};
+
+static const struct v4l2_ctrl_config ctrl_gt_filmgrain_info = {
+ .name = "filmgrain info",
+ .id = AML_V4L2_GET_FILMGRAIN_INFO,
+ .ops = &aml_vcodec_dec_ctrl_ops,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .flags = V4L2_CTRL_FLAG_VOLATILE,
+ .min = 0,
+ .max = 1,
+ .step = 1,
+ .def = 0,
+};
+
+int aml_vcodec_dec_ctrls_setup(struct aml_vcodec_ctx *ctx)
+{
+ int ret;
+ struct v4l2_ctrl *ctrl;
+
+ v4l2_ctrl_handler_init(&ctx->ctrl_hdl, 3);
+ ctrl = v4l2_ctrl_new_std(&ctx->ctrl_hdl,
+ &aml_vcodec_dec_ctrl_ops,
+ V4L2_CID_MIN_BUFFERS_FOR_CAPTURE,
+ 0, 32, 1, 2);
+ if ((ctrl == NULL) || (ctx->ctrl_hdl.error)) {
+ ret = ctx->ctrl_hdl.error;
+ goto err;
+ }
+ ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE;
+
+ ctrl = v4l2_ctrl_new_std(&ctx->ctrl_hdl,
+ &aml_vcodec_dec_ctrl_ops,
+ V4L2_CID_MIN_BUFFERS_FOR_OUTPUT,
+ 0, 32, 1, 8);
+ if ((ctrl == NULL) || (ctx->ctrl_hdl.error)) {
+ ret = ctx->ctrl_hdl.error;
+ goto err;
+ }
+ ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE;
+
+ ctrl = v4l2_ctrl_new_custom(&ctx->ctrl_hdl, &ctrl_st_mode, NULL);
+ if ((ctrl == NULL) || (ctx->ctrl_hdl.error)) {
+ ret = ctx->ctrl_hdl.error;
+ goto err;
+ }
+
+ ctrl = v4l2_ctrl_new_custom(&ctx->ctrl_hdl, &ctrl_gt_input_buffer_number, NULL);
+ if ((ctrl == NULL) || (ctx->ctrl_hdl.error)) {
+ ret = ctx->ctrl_hdl.error;
+ goto err;
+ }
+
+ ctrl = v4l2_ctrl_new_custom(&ctx->ctrl_hdl, &ctrl_st_duration, NULL);
+ if ((ctrl == NULL) || (ctx->ctrl_hdl.error)) {
+ ret = ctx->ctrl_hdl.error;
+ goto err;
+ }
+
+ ctrl = v4l2_ctrl_new_custom(&ctx->ctrl_hdl, &ctrl_gt_filmgrain_info, NULL);
+ if ((ctrl == NULL) || (ctx->ctrl_hdl.error)) {
+ ret = ctx->ctrl_hdl.error;
+ goto err;
+ }
+
+ v4l2_ctrl_handler_setup(&ctx->ctrl_hdl);
+
+ return 0;
+err:
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_ERROR,
+ "Adding control failed %d\n",
+ ctx->ctrl_hdl.error);
+ v4l2_ctrl_handler_free(&ctx->ctrl_hdl);
+ return ret;
+}
+
+static int vidioc_vdec_g_parm(struct file *file, void *fh,
+ struct v4l2_streamparm *a)
+{
+ struct aml_vcodec_ctx *ctx = fh_to_ctx(fh);
+ struct vb2_queue *dst_vq;
+
+ dst_vq = v4l2_m2m_get_vq(ctx->m2m_ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
+ if (!dst_vq) {
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_ERROR,
+ "no vb2 queue for type=%d\n", V4L2_BUF_TYPE_VIDEO_CAPTURE);
+ return -EINVAL;
+ }
+
+ if (!V4L2_TYPE_IS_MULTIPLANAR(a->type) && dst_vq->is_multiplanar)
+ return -EINVAL;
+
+ if ((a->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) || (a->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)) {
+ if (vdec_if_get_param(ctx, GET_PARAM_CONFIG_INFO, &ctx->config.parm.dec))
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_ERROR,
+ "GET_PARAM_CONFIG_INFO err\n");
+ else
+ memcpy(a->parm.raw_data, ctx->config.parm.data,
+ sizeof(a->parm.raw_data));
+ }
+
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_PROT, "%s\n", __func__);
+
+ return 0;
+}
+
+static int check_dec_cfginfo(struct aml_vdec_cfg_infos *cfg)
+{
+ if (cfg->double_write_mode != 0 &&
+ cfg->double_write_mode != 1 &&
+ cfg->double_write_mode != 2 &&
+ cfg->double_write_mode != 3 &&
+ cfg->double_write_mode != 4 &&
+ cfg->double_write_mode != 16 &&
+ cfg->double_write_mode != 0x21 &&
+ cfg->double_write_mode != 0x100 &&
+ cfg->double_write_mode != 0x200) {
+ pr_err("invalid double write mode %d\n", cfg->double_write_mode);
+ return -1;
+ }
+ if (cfg->ref_buf_margin > 20) {
+ pr_err("invalid margin %d\n", cfg->ref_buf_margin);
+ return -1;
+ }
+
+ if (mandatory_dw_mmu) {
+ cfg->double_write_mode = 0x21;
+ }
+
+ pr_info("double write mode %d margin %d\n",
+ cfg->double_write_mode, cfg->ref_buf_margin);
+ return 0;
+}
+
+static int vidioc_vdec_s_parm(struct file *file, void *fh,
+ struct v4l2_streamparm *a)
+{
+ struct aml_vcodec_ctx *ctx = fh_to_ctx(fh);
+ struct vb2_queue *dst_vq;
+
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_PROT, "%s\n", __func__);
+
+ dst_vq = v4l2_m2m_get_vq(ctx->m2m_ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
+ if (!dst_vq) {
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_ERROR,
+ "no vb2 queue for type=%d\n", V4L2_BUF_TYPE_VIDEO_CAPTURE);
+ return -EINVAL;
+ }
+
+ if (!V4L2_TYPE_IS_MULTIPLANAR(a->type) && dst_vq->is_multiplanar)
+ return -EINVAL;
+
+ if (a->type == V4L2_BUF_TYPE_VIDEO_OUTPUT ||
+ a->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+ struct aml_dec_params *in =
+ (struct aml_dec_params *) a->parm.raw_data;
+ struct aml_dec_params *dec = &ctx->config.parm.dec;
+
+ ctx->config.type = V4L2_CONFIG_PARM_DECODE;
+
+ if (in->parms_status & V4L2_CONFIG_PARM_DECODE_CFGINFO) {
+ if (check_dec_cfginfo(&in->cfg))
+ return -EINVAL;
+ dec->cfg = in->cfg;
+ }
+ if (in->parms_status & V4L2_CONFIG_PARM_DECODE_PSINFO)
+ dec->ps = in->ps;
+ if (in->parms_status & V4L2_CONFIG_PARM_DECODE_HDRINFO)
+ dec->hdr = in->hdr;
+ if (in->parms_status & V4L2_CONFIG_PARM_DECODE_CNTINFO)
+ dec->cnt = in->cnt;
+
+ dec->parms_status |= in->parms_status;
+
+ /* aml v4l driver parms config. */
+ ctx->vpp_cfg.enable_nr =
+ (dec->cfg.metadata_config_flag & (1 << 15));
+ if (force_enable_nr)
+ ctx->vpp_cfg.enable_nr = true;
+
+ ctx->vpp_cfg.enable_local_buf =
+ (dec->cfg.metadata_config_flag & (1 << 14));
+ if (force_enable_di_local_buffer)
+ ctx->vpp_cfg.enable_local_buf = true;
+
+ ctx->internal_dw_scale = dec->cfg.metadata_config_flag & (1 << 13);
+ ctx->second_field_pts_mode = dec->cfg.metadata_config_flag & (1 << 12);
+
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_PROT, "%s parms:%x metadata_config_flag: 0x%x\n",
+ __func__, in->parms_status, dec->cfg.metadata_config_flag);
+
+ memset(a->parm.output.reserved, 0, sizeof(a->parm.output.reserved));
+ } else {
+ memset(a->parm.capture.reserved, 0, sizeof(a->parm.capture.reserved));
+ }
+
+ return 0;
+}
+
+
+const struct v4l2_m2m_ops aml_vdec_m2m_ops = {
+ .device_run = m2mops_vdec_device_run,
+ .job_ready = m2mops_vdec_job_ready,
+ .job_abort = m2mops_vdec_job_abort,
+};
+
+static const struct vb2_ops aml_vdec_vb2_ops = {
+ .queue_setup = vb2ops_vdec_queue_setup,
+ .buf_prepare = vb2ops_vdec_buf_prepare,
+ .buf_queue = vb2ops_vdec_buf_queue,
+ .wait_prepare = vb2_ops_wait_prepare,
+ .wait_finish = vb2_ops_wait_finish,
+ .buf_init = vb2ops_vdec_buf_init,
+ .buf_cleanup = vb2ops_vdec_buf_cleanup,
+ .buf_finish = vb2ops_vdec_buf_finish,
+ .start_streaming = vb2ops_vdec_start_streaming,
+ .stop_streaming = vb2ops_vdec_stop_streaming,
+};
+
+const struct v4l2_ioctl_ops aml_vdec_ioctl_ops = {
+ .vidioc_streamon = vidioc_decoder_streamon,
+ .vidioc_streamoff = vidioc_decoder_streamoff,
+ .vidioc_reqbufs = vidioc_decoder_reqbufs,
+ .vidioc_querybuf = vidioc_vdec_querybuf,
+ .vidioc_expbuf = vidioc_vdec_expbuf,
+ //.vidioc_g_ctrl = vidioc_vdec_g_ctrl,
+
+ .vidioc_qbuf = vidioc_vdec_qbuf,
+ .vidioc_dqbuf = vidioc_vdec_dqbuf,
+
+ .vidioc_try_fmt_vid_cap_mplane = vidioc_try_fmt_vid_cap_out,
+ .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap_out,
+ .vidioc_try_fmt_vid_out_mplane = vidioc_try_fmt_vid_cap_out,
+ .vidioc_try_fmt_vid_out = vidioc_try_fmt_vid_cap_out,
+
+ .vidioc_s_fmt_vid_cap_mplane = vidioc_vdec_s_fmt,
+ .vidioc_s_fmt_vid_cap = vidioc_vdec_s_fmt,
+ .vidioc_s_fmt_vid_out_mplane = vidioc_vdec_s_fmt,
+ .vidioc_s_fmt_vid_out = vidioc_vdec_s_fmt,
+ .vidioc_g_fmt_vid_cap_mplane = vidioc_vdec_g_fmt,
+ .vidioc_g_fmt_vid_cap = vidioc_vdec_g_fmt,
+ .vidioc_g_fmt_vid_out_mplane = vidioc_vdec_g_fmt,
+ .vidioc_g_fmt_vid_out = vidioc_vdec_g_fmt,
+
+ .vidioc_create_bufs = vidioc_vdec_create_bufs,
+
+ //fixme
+ //.vidioc_enum_fmt_vid_cap_mplane = vidioc_vdec_enum_fmt_vid_cap_mplane,
+ //.vidioc_enum_fmt_vid_out_mplane = vidioc_vdec_enum_fmt_vid_out_mplane,
+ .vidioc_enum_fmt_vid_cap = vidioc_vdec_enum_fmt_vid_cap_mplane,
+ .vidioc_enum_fmt_vid_out = vidioc_vdec_enum_fmt_vid_out_mplane,
+ .vidioc_enum_framesizes = vidioc_enum_framesizes,
+
+ .vidioc_querycap = vidioc_vdec_querycap,
+ .vidioc_subscribe_event = vidioc_vdec_subscribe_evt,
+ .vidioc_unsubscribe_event = vidioc_vdec_event_unsubscribe,
+ .vidioc_g_selection = vidioc_vdec_g_selection,
+ .vidioc_s_selection = vidioc_vdec_s_selection,
+
+ .vidioc_decoder_cmd = vidioc_decoder_cmd,
+ .vidioc_try_decoder_cmd = vidioc_try_decoder_cmd,
+
+ .vidioc_g_parm = vidioc_vdec_g_parm,
+ .vidioc_s_parm = vidioc_vdec_s_parm,
+};
+
+int aml_vcodec_dec_queue_init(void *priv, struct vb2_queue *src_vq,
+ struct vb2_queue *dst_vq)
+{
+ struct aml_vcodec_ctx *ctx = priv;
+ int ret = 0;
+
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_EXINFO, "%s\n", __func__);
+
+ src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
+ src_vq->io_modes = VB2_DMABUF | VB2_MMAP | VB2_USERPTR;
+ src_vq->drv_priv = ctx;
+ src_vq->buf_struct_size = sizeof(struct aml_video_dec_buf);
+ src_vq->ops = &aml_vdec_vb2_ops;
+ src_vq->mem_ops = &vb2_dma_sg_memops;
+ src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+ src_vq->lock = &ctx->dev->dev_mutex;
+ ret = vb2_queue_init(src_vq);
+ if (ret) {
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_ERROR,
+ "Failed to initialize videobuf2 queue(output)\n");
+ return ret;
+ }
+
+ dst_vq->type = multiplanar ? V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE :
+ V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ dst_vq->io_modes = VB2_DMABUF | VB2_MMAP | VB2_USERPTR;
+ dst_vq->drv_priv = ctx;
+ dst_vq->buf_struct_size = sizeof(struct aml_video_dec_buf);
+ dst_vq->ops = &aml_vdec_vb2_ops;
+ dst_vq->mem_ops = &vb2_dma_contig_memops;
+ dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+ dst_vq->lock = &ctx->dev->dev_mutex;
+ dst_vq->min_buffers_needed = 1;
+ ret = vb2_queue_init(dst_vq);
+ if (ret) {
+ vb2_queue_release(src_vq);
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_ERROR,
+ "Failed to initialize videobuf2 queue(capture)\n");
+ }
+
+ return ret;
+}
+
diff --git a/drivers/amvdec_ports/aml_vcodec_dec.h b/drivers/amvdec_ports/aml_vcodec_dec.h
new file mode 100644
index 0000000..a23522c
--- /dev/null
+++ b/drivers/amvdec_ports/aml_vcodec_dec.h
@@ -0,0 +1,148 @@
+/*
+* Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2 of the License, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful, but WITHOUT
+* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+* more details.
+*
+* You should have received a copy of the GNU General Public License along
+* with this program; if not, write to the Free Software Foundation, Inc.,
+* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*
+* Description:
+*/
+#ifndef _AML_VCODEC_DEC_H_
+#define _AML_VCODEC_DEC_H_
+
+#include <linux/kref.h>
+#include <linux/scatterlist.h>
+#include <media/videobuf2-core.h>
+#include <media/videobuf2-v4l2.h>
+#include <linux/amlogic/media/codec_mm/codec_mm.h>
+#include <linux/amlogic/media/video_sink/v4lvideo_ext.h>
+#include "aml_vcodec_util.h"
+#include "aml_task_chain.h"
+
+#define VCODEC_CAPABILITY_4K_DISABLED 0x10
+#define VCODEC_DEC_4K_CODED_WIDTH 4096U
+#define VCODEC_DEC_4K_CODED_HEIGHT 2304U
+#define AML_VDEC_MAX_W 2048U
+#define AML_VDEC_MAX_H 1088U
+
+#define AML_VDEC_IRQ_STATUS_DEC_SUCCESS 0x10000
+#define V4L2_BUF_FLAG_LAST 0x00100000
+
+#define VDEC_GATHER_MEMORY_TYPE 0
+#define VDEC_SCATTER_MEMORY_TYPE 1
+
+#define META_DATA_SIZE (256)
+#define MD_BUF_SIZE (1024)
+#define COMP_BUF_SIZE (8196)
+#define SEI_BUF_SIZE (2 * 12 * 1024)
+#define SEI_TYPE (1)
+#define DV_TYPE (2)
+
+
+/*
+ * struct vdec_v4l2_buffer - decoder frame buffer
+ * @mem_type : gather or scatter memory.
+ * @num_planes : used number of the plane
+ * @mem[4] : array mem for used planes,
+ * mem[0]: Y, mem[1]: C/U, mem[2]: V
+ * @vf_fd : the file handle of video frame
+ * @status : frame buffer status (vdec_fb_status)
+ * @buf_idx : the index from vb2 index.
+ * @vframe : store the vframe that get from caller.
+ * @task : the context of task chain manager.
+ */
+
+struct vdec_v4l2_buffer {
+ int mem_type;
+ int num_planes;
+ union {
+ struct aml_vcodec_mem mem[4];
+ u32 vf_fd;
+ } m;
+ u32 status;
+ u32 buf_idx;
+ void *vframe;
+
+ struct task_chain_s *task;
+};
+
+/**
+ * struct aml_video_dec_buf - Private data related to each VB2 buffer.
+ * @b: VB2 buffer
+ * @list: link list
+ * @used: Capture buffer contain decoded frame data and keep in
+ * codec data structure
+ * @lastframe: Intput buffer is last buffer - EOS
+ * @error: An unrecoverable error occurs on this buffer.
+ * @frame_buffer: Decode status, and buffer information of Capture buffer
+ *
+ * Note : These status information help us track and debug buffer state
+ */
+struct aml_video_dec_buf {
+ struct vb2_v4l2_buffer vb;
+ struct list_head list;
+
+ struct vdec_v4l2_buffer frame_buffer;
+ struct file_private_data privdata;
+ struct codec_mm_s *mem[2];
+ char mem_onwer[32];
+ bool used;
+ bool que_in_m2m;
+ bool lastframe;
+ bool error;
+
+ /* internal compressed buffer */
+ unsigned int internal_index;
+
+ ulong vpp_buf_handle;
+ ulong ge2d_buf_handle;
+
+ /*4 bytes data for data len*/
+ char meta_data[META_DATA_SIZE + 4];
+
+ struct sg_table *out_sgt;
+ struct sg_table *cap_sgt;
+};
+
+extern const struct v4l2_ioctl_ops aml_vdec_ioctl_ops;
+extern const struct v4l2_m2m_ops aml_vdec_m2m_ops;
+
+/*
+ * aml_vdec_lock/aml_vdec_unlock are for ctx instance to
+ * get/release lock before/after access decoder hw.
+ * aml_vdec_lock get decoder hw lock and set curr_ctx
+ * to ctx instance that get lock
+ */
+void aml_vdec_unlock(struct aml_vcodec_ctx *ctx);
+void aml_vdec_lock(struct aml_vcodec_ctx *ctx);
+int aml_vcodec_dec_queue_init(void *priv, struct vb2_queue *src_vq,
+ struct vb2_queue *dst_vq);
+void aml_vcodec_dec_set_default_params(struct aml_vcodec_ctx *ctx);
+void aml_vcodec_dec_release(struct aml_vcodec_ctx *ctx);
+int aml_vcodec_dec_ctrls_setup(struct aml_vcodec_ctx *ctx);
+void wait_vcodec_ending(struct aml_vcodec_ctx *ctx);
+void vdec_frame_buffer_release(void *data);
+void aml_vdec_dispatch_event(struct aml_vcodec_ctx *ctx, u32 changes);
+void* v4l_get_vf_handle(int fd);
+void aml_v4l_ctx_release(struct kref *kref);
+void dmabuff_recycle_worker(struct work_struct *work);
+void aml_buffer_status(struct aml_vcodec_ctx *ctx);
+void aml_vdec_basic_information(struct aml_vcodec_ctx *ctx);
+
+void aml_alloc_buffer(struct aml_vcodec_ctx *ctx, int flag);
+void aml_free_buffer(struct aml_vcodec_ctx *ctx, int flag);
+void aml_free_one_sei_buffer(struct aml_vcodec_ctx *ctx, char **addr, int *size, int idx);
+void aml_bind_sei_buffer(struct aml_vcodec_ctx *v4l, char **addr, int *size, int *idx);
+void aml_bind_dv_buffer(struct aml_vcodec_ctx *v4l, char **comp_buf, char **md_buf);
+
+#endif /* _AML_VCODEC_DEC_H_ */
diff --git a/drivers/amvdec_ports/aml_vcodec_dec_drv.c b/drivers/amvdec_ports/aml_vcodec_dec_drv.c
new file mode 100644
index 0000000..18f0254
--- /dev/null
+++ b/drivers/amvdec_ports/aml_vcodec_dec_drv.c
@@ -0,0 +1,754 @@
+/*
+* Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2 of the License, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful, but WITHOUT
+* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+* more details.
+*
+* You should have received a copy of the GNU General Public License along
+* with this program; if not, write to the Free Software Foundation, Inc.,
+* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*
+* Description:
+*/
+
+#define DEBUG
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/of.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-mem2mem.h>
+#include <media/videobuf2-dma-contig.h>
+#include <linux/kthread.h>
+#include <linux/compat.h>
+#include "aml_vcodec_drv.h"
+#include "aml_vcodec_dec.h"
+#include "aml_vcodec_util.h"
+#include "aml_vcodec_vpp.h"
+#include <linux/file.h>
+#include <linux/anon_inodes.h>
+
+#define VDEC_HW_ACTIVE 0x10
+#define VDEC_IRQ_CFG 0x11
+#define VDEC_IRQ_CLR 0x10
+#define VDEC_IRQ_CFG_REG 0xa4
+
+#define V4LVIDEO_IOC_MAGIC 'I'
+#define V4LVIDEO_IOCTL_ALLOC_FD _IOW(V4LVIDEO_IOC_MAGIC, 0x02, int)
+#define V4LVIDEO_IOCTL_CHECK_FD _IOW(V4LVIDEO_IOC_MAGIC, 0x03, int)
+#define V4LVIDEO_IOCTL_SET_CONFIG_PARAMS _IOWR(V4LVIDEO_IOC_MAGIC, 0x04, struct v4l2_config_parm)
+#define V4LVIDEO_IOCTL_GET_CONFIG_PARAMS _IOWR(V4LVIDEO_IOC_MAGIC, 0x05, struct v4l2_config_parm)
+
+bool param_sets_from_ucode = 1;
+bool enable_drm_mode;
+extern void aml_vdec_pic_info_update(struct aml_vcodec_ctx *ctx);
+
+static int fops_vcodec_open(struct file *file)
+{
+ struct aml_vcodec_dev *dev = video_drvdata(file);
+ struct aml_vcodec_ctx *ctx = NULL;
+ struct aml_video_dec_buf *aml_buf = NULL;
+ int ret = 0;
+ struct vb2_queue *src_vq;
+
+ ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+ kref_init(&ctx->ctx_ref);
+
+ aml_buf = kzalloc(sizeof(*aml_buf), GFP_KERNEL);
+ if (!aml_buf) {
+ kfree(ctx);
+ return -ENOMEM;
+ }
+
+ ctx->meta_infos.meta_bufs = vzalloc(sizeof(struct meta_data) * V4L_CAP_BUFF_MAX);
+ if (ctx->meta_infos.meta_bufs == NULL) {
+ kfree(aml_buf);
+ kfree(ctx);
+ return -ENOMEM;
+ }
+
+ mutex_lock(&dev->dev_mutex);
+ ctx->empty_flush_buf = aml_buf;
+ ctx->id = dev->id_counter++;
+ v4l2_fh_init(&ctx->fh, video_devdata(file));
+ file->private_data = &ctx->fh;
+ v4l2_fh_add(&ctx->fh);
+ INIT_LIST_HEAD(&ctx->list);
+ INIT_LIST_HEAD(&ctx->vdec_thread_list);
+ INIT_LIST_HEAD(&ctx->task_chain_pool);
+ dev->filp = file;
+ ctx->dev = dev;
+ init_waitqueue_head(&ctx->queue);
+ mutex_init(&ctx->capture_buffer_lock);
+ mutex_init(&ctx->buff_done_lock);
+ mutex_init(&ctx->state_lock);
+ mutex_init(&ctx->comp_lock);
+ spin_lock_init(&ctx->slock);
+ spin_lock_init(&ctx->tsplock);
+ spin_lock_init(&ctx->dmabuff_recycle_lock);
+ init_completion(&ctx->comp);
+ init_waitqueue_head(&ctx->wq);
+ init_waitqueue_head(&ctx->cap_wq);
+ init_waitqueue_head(&ctx->post_done_wq);
+ INIT_WORK(&ctx->dmabuff_recycle_work, dmabuff_recycle_worker);
+ INIT_KFIFO(ctx->dmabuff_recycle);
+ INIT_KFIFO(ctx->capture_buffer);
+
+ ctx->post_to_upper_done = true;
+ ctx->param_sets_from_ucode = param_sets_from_ucode ? 1 : 0;
+
+ if (enable_drm_mode) {
+ ctx->is_drm_mode = true;
+ ctx->param_sets_from_ucode = true;
+ }
+
+ ctx->type = AML_INST_DECODER;
+ ret = aml_vcodec_dec_ctrls_setup(ctx);
+ if (ret) {
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_ERROR,
+ "Failed to setup vcodec controls\n");
+ goto err_ctrls_setup;
+ }
+ ctx->m2m_ctx = v4l2_m2m_ctx_init(dev->m2m_dev_dec, ctx,
+ &aml_vcodec_dec_queue_init);
+ if (IS_ERR((__force void *)ctx->m2m_ctx)) {
+ ret = PTR_ERR((__force void *)ctx->m2m_ctx);
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_ERROR,
+ "Failed to v4l2_m2m_ctx_init() (%d)\n", ret);
+ goto err_m2m_ctx_init;
+ }
+ src_vq = v4l2_m2m_get_vq(ctx->m2m_ctx,
+ V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
+ ctx->output_thread_ready = true;
+ ctx->empty_flush_buf->vb.vb2_buf.vb2_queue = src_vq;
+ ctx->empty_flush_buf->lastframe = true;
+ ctx->vdec_pic_info_update = aml_vdec_pic_info_update;
+ aml_vcodec_dec_set_default_params(ctx);
+ ctx->is_stream_off = true;
+
+ ctx->aux_infos.dv_index = 0;
+ ctx->aux_infos.sei_index = 0;
+ ctx->aux_infos.alloc_buffer = aml_alloc_buffer;
+ ctx->aux_infos.free_buffer = aml_free_buffer;
+ ctx->aux_infos.bind_sei_buffer = aml_bind_sei_buffer;
+ ctx->aux_infos.bind_dv_buffer = aml_bind_dv_buffer;
+ ctx->aux_infos.free_one_sei_buffer = aml_free_one_sei_buffer;
+
+ ret = aml_thread_start(ctx, aml_thread_capture_worker, AML_THREAD_CAPTURE, "cap");
+ if (ret) {
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_ERROR,
+ "Failed to creat capture thread.\n");
+ goto err_creat_thread;
+ }
+
+ list_add(&ctx->list, &dev->ctx_list);
+
+ mutex_unlock(&dev->dev_mutex);
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_PRINFO, "%s decoder %lx\n",
+ dev_name(&dev->plat_dev->dev), (ulong)ctx);
+
+ return 0;
+
+ /* Deinit when failure occurred */
+err_creat_thread:
+ v4l2_m2m_ctx_release(ctx->m2m_ctx);
+err_m2m_ctx_init:
+ v4l2_ctrl_handler_free(&ctx->ctrl_hdl);
+err_ctrls_setup:
+ v4l2_fh_del(&ctx->fh);
+ v4l2_fh_exit(&ctx->fh);
+ vfree(ctx->meta_infos.meta_bufs);
+ kfree(ctx->empty_flush_buf);
+ kfree(ctx);
+ mutex_unlock(&dev->dev_mutex);
+
+ return ret;
+}
+
+static int fops_vcodec_release(struct file *file)
+{
+ struct aml_vcodec_dev *dev = video_drvdata(file);
+ struct aml_vcodec_ctx *ctx = fh_to_ctx(file->private_data);
+
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_PRINFO, "release decoder %lx\n", (ulong) ctx);
+ mutex_lock(&dev->dev_mutex);
+
+ aml_thread_stop(ctx);
+ wait_vcodec_ending(ctx);
+ vb2_queue_release(&ctx->m2m_ctx->cap_q_ctx.q);
+ vb2_queue_release(&ctx->m2m_ctx->out_q_ctx.q);
+
+ aml_vcodec_dec_release(ctx);
+ v4l2_fh_del(&ctx->fh);
+ v4l2_fh_exit(&ctx->fh);
+ v4l2_ctrl_handler_free(&ctx->ctrl_hdl);
+
+ list_del_init(&ctx->list);
+
+ kfree(ctx->empty_flush_buf);
+ kref_put(&ctx->ctx_ref, aml_v4l_ctx_release);
+ mutex_unlock(&dev->dev_mutex);
+ return 0;
+}
+
+static int v4l2video_file_release(struct inode *inode, struct file *file)
+{
+ v4l_dbg(0, V4L_DEBUG_CODEC_BUFMGR, "file: %lx, data: %lx\n",
+ (ulong) file, (ulong) file->private_data);
+
+ if (file->private_data)
+ vdec_frame_buffer_release(file->private_data);
+
+ return 0;
+}
+
+const struct file_operations v4l2_file_fops = {
+ .release = v4l2video_file_release,
+};
+
+int v4l2_alloc_fd(int *fd)
+{
+ struct file *file = NULL;
+ int file_fd = get_unused_fd_flags(O_CLOEXEC);
+
+ if (file_fd < 0) {
+ v4l_dbg(0, V4L_DEBUG_CODEC_ERROR,
+ "get unused fd fail\n");
+ return -ENODEV;
+ }
+
+ file = anon_inode_getfile("v4l2_meta_file", &v4l2_file_fops, NULL, 0);
+ if (IS_ERR(file)) {
+ put_unused_fd(file_fd);
+ v4l_dbg(0, V4L_DEBUG_CODEC_ERROR,
+ "anon_inode_getfile fail\n");
+ return -ENODEV;
+ }
+
+ file->private_data =
+ kzalloc(sizeof(struct file_private_data), GFP_KERNEL);
+ if (!file->private_data) {
+ v4l_dbg(0, V4L_DEBUG_CODEC_ERROR,
+ "alloc priv data faild.\n");
+ return -ENOMEM;
+ }
+
+ v4l_dbg(0, V4L_DEBUG_CODEC_BUFMGR, "fd %d, file %lx, data: %lx\n",
+ file_fd, (ulong) file, (ulong) file->private_data);
+
+ fd_install(file_fd, file);
+ *fd = file_fd;
+
+ return 0;
+}
+
+extern const struct file_operations v4l2_file_fops;
+bool is_v4l2_buf_file(struct file *file)
+{
+ return file->f_op == &v4l2_file_fops;
+}
+
+int v4l2_check_fd(int fd)
+{
+ struct file *file;
+
+ file = fget(fd);
+
+ if (!file) {
+ v4l_dbg(0, V4L_DEBUG_CODEC_ERROR,
+ "fget fd %d fail!\n", fd);
+ return -EBADF;
+ }
+
+ if (!is_v4l2_buf_file(file)) {
+ fput(file);
+ v4l_dbg(0, V4L_DEBUG_CODEC_ERROR,
+ "is_v4l2_buf_file fail!\n");
+ return -1;
+ }
+
+ fput(file);
+
+ v4l_dbg(0, V4L_DEBUG_CODEC_EXINFO,
+ "ioctl ok, comm %s, pid %d\n",
+ current->comm, current->pid);
+
+ return 0;
+}
+
+int dmabuf_fd_install_data(int fd, void* data, u32 size)
+{
+ struct file *file;
+
+ file = fget(fd);
+
+ if (!file) {
+ v4l_dbg(0, V4L_DEBUG_CODEC_ERROR,
+ "fget fd %d fail!, comm %s, pid %d\n",
+ fd, current->comm, current->pid);
+ return -EBADF;
+ }
+
+ if (!is_v4l2_buf_file(file)) {
+ fput(file);
+ v4l_dbg(0, V4L_DEBUG_CODEC_ERROR,
+ "the buf file checked fail!\n");
+ return -EBADF;
+ }
+
+ memcpy(file->private_data, data, size);
+
+ fput(file);
+
+ return 0;
+}
+
+void* v4l_get_vf_handle(int fd)
+{
+ struct file *file;
+ struct file_private_data *data = NULL;
+ void *vf_handle = 0;
+
+ file = fget(fd);
+
+ if (!file) {
+ v4l_dbg(0, V4L_DEBUG_CODEC_ERROR,
+ "fget fd %d fail!, comm %s, pid %d\n",
+ fd, current->comm, current->pid);
+ return NULL;
+ }
+
+ if (!is_v4l2_buf_file(file)) {
+ fput(file);
+#if 0
+ v4l_dbg(0, V4L_DEBUG_CODEC_ERROR,
+ "the buf file checked fail!\n");
+#endif
+ return NULL;
+ }
+
+ data = (struct file_private_data*) file->private_data;
+ if (data) {
+ vf_handle = &data->vf;
+ v4l_dbg(0, V4L_DEBUG_CODEC_BUFMGR, "file: %lx, data: %lx\n",
+ (ulong) file, (ulong) data);
+ }
+
+ fput(file);
+
+ return vf_handle;
+}
+
+
+static long v4l2_vcodec_ioctl(struct file *file,
+ unsigned int cmd,
+ ulong arg)
+{
+ long ret = 0;
+ void __user *argp = (void __user *)arg;
+
+ switch (cmd) {
+ case V4LVIDEO_IOCTL_ALLOC_FD:
+ {
+ u32 v4lvideo_fd = 0;
+
+ ret = v4l2_alloc_fd(&v4lvideo_fd);
+ if (ret != 0)
+ break;
+ put_user(v4lvideo_fd, (u32 __user *)argp);
+ v4l_dbg(0, V4L_DEBUG_CODEC_EXINFO,
+ "V4LVIDEO_IOCTL_ALLOC_FD fd %d\n",
+ v4lvideo_fd);
+ break;
+ }
+ case V4LVIDEO_IOCTL_CHECK_FD:
+ {
+ u32 v4lvideo_fd = 0;
+
+ get_user(v4lvideo_fd, (u32 __user *)argp);
+ ret = v4l2_check_fd(v4lvideo_fd);
+ if (ret != 0)
+ break;
+ v4l_dbg(0, V4L_DEBUG_CODEC_EXINFO,
+ "V4LVIDEO_IOCTL_CHECK_FD fd %d\n",
+ v4lvideo_fd);
+ break;
+ }
+ case V4LVIDEO_IOCTL_SET_CONFIG_PARAMS:
+ {
+ struct aml_vcodec_ctx *ctx = NULL;
+
+ if (is_v4l2_buf_file(file))
+ break;
+
+ ctx = fh_to_ctx(file->private_data);
+ if (copy_from_user((void *)&ctx->config,
+ (void *)argp, sizeof(ctx->config))) {
+ v4l_dbg(0, V4L_DEBUG_CODEC_ERROR,
+ "set config parm err\n");
+ return -EFAULT;
+ }
+ break;
+ }
+ case V4LVIDEO_IOCTL_GET_CONFIG_PARAMS:
+ {
+ struct aml_vcodec_ctx *ctx = NULL;
+
+ if (is_v4l2_buf_file(file))
+ break;
+
+ ctx = fh_to_ctx(file->private_data);
+ if (copy_to_user((void *)argp,
+ (void *)&ctx->config, sizeof(ctx->config))) {
+ v4l_dbg(0, V4L_DEBUG_CODEC_ERROR,
+ "get config parm err\n");
+ return -EFAULT;
+ }
+ break;
+ }
+ default:
+ return video_ioctl2(file, cmd, arg);
+ }
+ return ret;
+}
+
+#ifdef CONFIG_COMPAT
+static long v4l2_compat_ioctl(struct file *file,
+ unsigned int cmd, ulong arg)
+{
+ long ret = 0;
+
+ ret = v4l2_vcodec_ioctl(file, cmd, (ulong)compat_ptr(arg));
+ return ret;
+}
+#endif
+
+static const struct v4l2_file_operations aml_vcodec_fops = {
+ .owner = THIS_MODULE,
+ .open = fops_vcodec_open,
+ .release = fops_vcodec_release,
+ .poll = v4l2_m2m_fop_poll,
+ .unlocked_ioctl = v4l2_vcodec_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl32 = v4l2_compat_ioctl,
+#endif
+ .mmap = v4l2_m2m_fop_mmap,
+};
+
+static ssize_t status_show(struct class *cls,
+ struct class_attribute *attr, char *buf)
+{
+ struct aml_vcodec_dev *dev = container_of(cls,
+ struct aml_vcodec_dev, v4ldec_class);
+ struct aml_vcodec_ctx *ctx = NULL;
+ char *pbuf = buf;
+
+ mutex_lock(&dev->dev_mutex);
+
+ if (list_empty(&dev->ctx_list)) {
+ pbuf += sprintf(pbuf, "No v4ldec context.\n");
+ goto out;
+ }
+
+ list_for_each_entry(ctx, &dev->ctx_list, list) {
+ /* basic information. */
+ aml_vdec_basic_information(ctx);
+
+ /* buffers status. */
+ aml_buffer_status(ctx);
+ }
+out:
+ mutex_unlock(&dev->dev_mutex);
+
+ return pbuf - buf;
+}
+
+static CLASS_ATTR_RO(status);
+
+static struct attribute *v4ldec_class_attrs[] = {
+ &class_attr_status.attr,
+ NULL
+};
+
+ATTRIBUTE_GROUPS(v4ldec_class);
+
+static int aml_vcodec_probe(struct platform_device *pdev)
+{
+ struct aml_vcodec_dev *dev;
+ struct video_device *vfd_dec;
+ int ret = 0;
+
+ dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
+ if (!dev)
+ return -ENOMEM;
+
+ INIT_LIST_HEAD(&dev->ctx_list);
+ dev->plat_dev = pdev;
+ atomic_set(&dev->vpp_count, 0);
+
+ mutex_init(&dev->dec_mutex);
+ mutex_init(&dev->dev_mutex);
+ spin_lock_init(&dev->irqlock);
+
+ snprintf(dev->v4l2_dev.name, sizeof(dev->v4l2_dev.name), "%s",
+ "[/AML_V4L2_VDEC]");
+
+ ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev);
+ if (ret) {
+ dev_err(&pdev->dev, "v4l2_device_register err=%d\n", ret);
+ goto err_res;
+ }
+
+ init_waitqueue_head(&dev->queue);
+
+ vfd_dec = video_device_alloc();
+ if (!vfd_dec) {
+ dev_err(&pdev->dev, "Failed to allocate video device\n");
+ ret = -ENOMEM;
+ goto err_dec_alloc;
+ }
+
+ vfd_dec->fops = &aml_vcodec_fops;
+ vfd_dec->ioctl_ops = &aml_vdec_ioctl_ops;
+ vfd_dec->release = video_device_release;
+ vfd_dec->lock = &dev->dev_mutex;
+ vfd_dec->v4l2_dev = &dev->v4l2_dev;
+ vfd_dec->vfl_dir = VFL_DIR_M2M;
+ vfd_dec->device_caps = V4L2_CAP_VIDEO_M2M_MPLANE |
+ V4L2_CAP_STREAMING;
+
+ snprintf(vfd_dec->name, sizeof(vfd_dec->name), "%s",
+ AML_VCODEC_DEC_NAME);
+ video_set_drvdata(vfd_dec, dev);
+ dev->vfd_dec = vfd_dec;
+ platform_set_drvdata(pdev, dev);
+
+ dev->m2m_dev_dec = v4l2_m2m_init(&aml_vdec_m2m_ops);
+ if (IS_ERR((__force void *)dev->m2m_dev_dec)) {
+ dev_err(&pdev->dev, "Failed to init mem2mem dec device\n");
+ ret = PTR_ERR((__force void *)dev->m2m_dev_dec);
+ goto err_dec_mem_init;
+ }
+
+ dev->decode_workqueue =
+ alloc_ordered_workqueue("output-worker",
+ __WQ_LEGACY | WQ_MEM_RECLAIM | WQ_HIGHPRI);
+ if (!dev->decode_workqueue) {
+ dev_err(&pdev->dev, "Failed to create decode workqueue\n");
+ ret = -EINVAL;
+ goto err_event_workq;
+ }
+
+ //dev_set_name(&vdev->dev, "%s%d", name_base, vdev->num);
+
+ ret = video_register_device(vfd_dec, VFL_TYPE_GRABBER, 26);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to register video device\n");
+ goto err_dec_reg;
+ }
+
+ /*init class*/
+ dev->v4ldec_class.name = "v4ldec";
+ dev->v4ldec_class.owner = THIS_MODULE;
+ dev->v4ldec_class.class_groups = v4ldec_class_groups;
+ ret = class_register(&dev->v4ldec_class);
+ if (ret) {
+ dev_err(&pdev->dev, "v4l dec class create fail.\n");
+ goto err_reg_class;
+ }
+
+ dev_info(&pdev->dev, "v4ldec registered as /dev/video%d\n", vfd_dec->num);
+
+ return 0;
+
+err_reg_class:
+ class_unregister(&dev->v4ldec_class);
+err_dec_reg:
+ destroy_workqueue(dev->decode_workqueue);
+err_event_workq:
+ v4l2_m2m_release(dev->m2m_dev_dec);
+err_dec_mem_init:
+ video_unregister_device(vfd_dec);
+err_dec_alloc:
+ v4l2_device_unregister(&dev->v4l2_dev);
+err_res:
+
+ return ret;
+}
+
+static int aml_vcodec_dec_remove(struct platform_device *pdev)
+{
+ struct aml_vcodec_dev *dev = platform_get_drvdata(pdev);
+
+ flush_workqueue(dev->decode_workqueue);
+ destroy_workqueue(dev->decode_workqueue);
+
+ class_unregister(&dev->v4ldec_class);
+
+ if (dev->m2m_dev_dec)
+ v4l2_m2m_release(dev->m2m_dev_dec);
+
+ if (dev->vfd_dec)
+ video_unregister_device(dev->vfd_dec);
+
+ v4l2_device_unregister(&dev->v4l2_dev);
+
+ dev_info(&pdev->dev, "v4ldec removed.\n");
+
+ return 0;
+}
+
+static const struct of_device_id aml_vcodec_match[] = {
+ {.compatible = "amlogic, vcodec-dec",},
+ {},
+};
+
+MODULE_DEVICE_TABLE(of, aml_vcodec_match);
+
+static struct platform_driver aml_vcodec_dec_driver = {
+ .probe = aml_vcodec_probe,
+ .remove = aml_vcodec_dec_remove,
+ .driver = {
+ .name = AML_VCODEC_DEC_NAME,
+ .of_match_table = aml_vcodec_match,
+ },
+};
+
+static int __init amvdec_ports_init(void)
+{
+ pr_info("v4l dec module init\n");
+
+ if (platform_driver_register(&aml_vcodec_dec_driver)) {
+ pr_err("failed to register v4l dec driver\n");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static void __exit amvdec_ports_exit(void)
+{
+ pr_info("v4l dec module exit\n");
+
+ platform_driver_unregister(&aml_vcodec_dec_driver);
+}
+
+module_init(amvdec_ports_init);
+module_exit(amvdec_ports_exit);
+
+u32 debug_mode;
+EXPORT_SYMBOL(debug_mode);
+module_param(debug_mode, uint, 0644);
+
+u32 mandatory_dw_mmu;
+EXPORT_SYMBOL(mandatory_dw_mmu);
+module_param(mandatory_dw_mmu, uint, 0644);
+
+bool aml_set_vfm_enable;
+EXPORT_SYMBOL(aml_set_vfm_enable);
+module_param(aml_set_vfm_enable, bool, 0644);
+
+int aml_set_vfm_path;
+EXPORT_SYMBOL(aml_set_vfm_path);
+module_param(aml_set_vfm_path, int, 0644);
+
+bool aml_set_vdec_type_enable;
+EXPORT_SYMBOL(aml_set_vdec_type_enable);
+module_param(aml_set_vdec_type_enable, bool, 0644);
+
+int aml_set_vdec_type;
+EXPORT_SYMBOL(aml_set_vdec_type);
+module_param(aml_set_vdec_type, int, 0644);
+
+int vp9_need_prefix;
+EXPORT_SYMBOL(vp9_need_prefix);
+module_param(vp9_need_prefix, int, 0644);
+
+int av1_need_prefix;
+EXPORT_SYMBOL(av1_need_prefix);
+module_param(av1_need_prefix, int, 0644);
+
+bool multiplanar;
+EXPORT_SYMBOL(multiplanar);
+module_param(multiplanar, bool, 0644);
+
+int dump_capture_frame;
+EXPORT_SYMBOL(dump_capture_frame);
+module_param(dump_capture_frame, int, 0644);
+
+int dump_vpp_input;
+EXPORT_SYMBOL(dump_vpp_input);
+module_param(dump_vpp_input, int, 0644);
+
+int dump_ge2d_input;
+EXPORT_SYMBOL(dump_ge2d_input);
+module_param(dump_ge2d_input, int, 0644);
+
+int dump_output_frame;
+EXPORT_SYMBOL(dump_output_frame);
+module_param(dump_output_frame, int, 0644);
+
+u32 dump_output_start_position;
+EXPORT_SYMBOL(dump_output_start_position);
+module_param(dump_output_start_position, uint, 0644);
+
+EXPORT_SYMBOL(param_sets_from_ucode);
+module_param(param_sets_from_ucode, bool, 0644);
+
+EXPORT_SYMBOL(enable_drm_mode);
+module_param(enable_drm_mode, bool, 0644);
+
+int bypass_vpp;
+EXPORT_SYMBOL(bypass_vpp);
+module_param(bypass_vpp, int, 0644);
+
+int bypass_ge2d;
+EXPORT_SYMBOL(bypass_ge2d);
+module_param(bypass_ge2d, int, 0644);
+
+int max_di_instance = 2;
+EXPORT_SYMBOL(max_di_instance);
+module_param(max_di_instance, int, 0644);
+
+int bypass_progressive = 1;
+EXPORT_SYMBOL(bypass_progressive);
+module_param(bypass_progressive, int, 0644);
+
+bool support_mjpeg;
+EXPORT_SYMBOL(support_mjpeg);
+module_param(support_mjpeg, bool, 0644);
+
+bool support_format_I420;
+EXPORT_SYMBOL(support_format_I420);
+module_param(support_format_I420, bool, 0644);
+
+int force_enable_nr;
+EXPORT_SYMBOL(force_enable_nr);
+module_param(force_enable_nr, int, 0644);
+
+int force_enable_di_local_buffer;
+EXPORT_SYMBOL(force_enable_di_local_buffer);
+module_param(force_enable_di_local_buffer, int, 0644);
+
+int vpp_bypass_frames;
+EXPORT_SYMBOL(vpp_bypass_frames);
+module_param(vpp_bypass_frames, int, 0644);
+
+int bypass_nr_flag;
+EXPORT_SYMBOL(bypass_nr_flag);
+module_param(bypass_nr_flag, int, 0644);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("AML video codec V4L2 decoder driver");
+
diff --git a/drivers/amvdec_ports/aml_vcodec_drv.h b/drivers/amvdec_ports/aml_vcodec_drv.h
new file mode 100644
index 0000000..522fe67
--- /dev/null
+++ b/drivers/amvdec_ports/aml_vcodec_drv.h
@@ -0,0 +1,788 @@
+/*
+* Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2 of the License, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful, but WITHOUT
+* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+* more details.
+*
+* You should have received a copy of the GNU General Public License along
+* with this program; if not, write to the Free Software Foundation, Inc.,
+* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*
+* Description:
+*/
+#ifndef _AML_VCODEC_DRV_H_
+#define _AML_VCODEC_DRV_H_
+
+#include <linux/kref.h>
+#include <linux/platform_device.h>
+#include <linux/videodev2.h>
+#include <linux/kfifo.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <media/videobuf2-core.h>
+#include <media/videobuf2-v4l2.h>
+#include <linux/amlogic/media/vfm/vframe.h>
+#include <linux/amlogic/media/video_sink/v4lvideo_ext.h>
+#include "aml_vcodec_util.h"
+#include "aml_vcodec_dec.h"
+
+#define AML_VCODEC_DRV_NAME "aml_vcodec_drv"
+#define AML_VCODEC_DEC_NAME "aml-vcodec-dec"
+#define AML_VCODEC_ENC_NAME "aml-vcodec-enc"
+#define AML_PLATFORM_STR "platform:amlogic"
+
+#define AML_VCODEC_MAX_PLANES 3
+#define AML_V4L2_BENCHMARK 0
+#define WAIT_INTR_TIMEOUT_MS 1000
+
+/* codec types of get/set parms. */
+#define V4L2_CONFIG_PARM_ENCODE (0)
+#define V4L2_CONFIG_PARM_DECODE (1)
+
+/* types of decode parms. */
+#define V4L2_CONFIG_PARM_DECODE_CFGINFO (1 << 0)
+#define V4L2_CONFIG_PARM_DECODE_PSINFO (1 << 1)
+#define V4L2_CONFIG_PARM_DECODE_HDRINFO (1 << 2)
+#define V4L2_CONFIG_PARM_DECODE_CNTINFO (1 << 3)
+
+/* amlogic event define. */
+/* #define V4L2_EVENT_SRC_CH_RESOLUTION (1 << 0) */
+#define V4L2_EVENT_SRC_CH_HDRINFO (1 << 1)
+#define V4L2_EVENT_SRC_CH_PSINFO (1 << 2)
+#define V4L2_EVENT_SRC_CH_CNTINFO (1 << 3)
+
+/* exception handing */
+#define V4L2_EVENT_REQUEST_RESET (1 << 8)
+#define V4L2_EVENT_REQUEST_EXIT (1 << 9)
+
+/* eos event */
+#define V4L2_EVENT_SEND_EOS (1 << 16)
+
+/* v4l buffer pool */
+#define V4L_CAP_BUFF_MAX (32)
+#define V4L_CAP_BUFF_INVALID (0)
+#define V4L_CAP_BUFF_IN_M2M (1)
+#define V4L_CAP_BUFF_IN_DEC (2)
+#define V4L_CAP_BUFF_IN_VPP (3)
+#define V4L_CAP_BUFF_IN_GE2D (4)
+
+/* v4l reset mode */
+#define V4L_RESET_MODE_NORMAL (1 << 0) /* reset vdec_input and decoder. */
+#define V4L_RESET_MODE_LIGHT (1 << 1) /* just only reset decoder. */
+
+/* m2m job queue's status */
+/* Instance is already queued on the job_queue */
+#define TRANS_QUEUED (1 << 0)
+/* Instance is currently running in hardware */
+#define TRANS_RUNNING (1 << 1)
+/* Instance is currently aborting */
+#define TRANS_ABORT (1 << 2)
+
+#define CTX_BUF_TOTAL(ctx) (ctx->dpb_size + ctx->vpp_size + ctx->ge2d_size)
+/**
+ * enum aml_hw_reg_idx - AML hw register base index
+ */
+enum aml_hw_reg_idx {
+ VDEC_SYS,
+ VDEC_MISC,
+ VDEC_LD,
+ VDEC_TOP,
+ VDEC_CM,
+ VDEC_AD,
+ VDEC_AV,
+ VDEC_PP,
+ VDEC_HWD,
+ VDEC_HWQ,
+ VDEC_HWB,
+ VDEC_HWG,
+ NUM_MAX_VDEC_REG_BASE,
+ /* h264 encoder */
+ VENC_SYS = NUM_MAX_VDEC_REG_BASE,
+ /* vp8 encoder */
+ VENC_LT_SYS,
+ NUM_MAX_VCODEC_REG_BASE
+};
+
+/**
+ * enum aml_instance_type - The type of an AML Vcodec instance.
+ */
+enum aml_instance_type {
+ AML_INST_DECODER = 0,
+ AML_INST_ENCODER = 1,
+};
+
+/**
+ * enum aml_instance_state - The state of an AML Vcodec instance.
+ * @AML_STATE_IDLE - default state when instance is created
+ * @AML_STATE_INIT - vcodec instance is initialized
+ * @AML_STATE_PROBE - vdec/venc had sps/pps header parsed/encoded
+ * @AML_STATE_ACTIVE - vdec is ready for work.
+ * @AML_STATE_FLUSHING - vdec is flushing. Only used by decoder
+ * @AML_STATE_FLUSHED - decoder has transacted the last frame.
+ * @AML_STATE_ABORT - vcodec should be aborted
+ */
+enum aml_instance_state {
+ AML_STATE_IDLE,
+ AML_STATE_INIT,
+ AML_STATE_PROBE,
+ AML_STATE_READY,
+ AML_STATE_ACTIVE,
+ AML_STATE_FLUSHING,
+ AML_STATE_FLUSHED,
+ AML_STATE_ABORT,
+};
+
+/**
+ * struct aml_encode_param - General encoding parameters type
+ */
+enum aml_encode_param {
+ AML_ENCODE_PARAM_NONE = 0,
+ AML_ENCODE_PARAM_BITRATE = (1 << 0),
+ AML_ENCODE_PARAM_FRAMERATE = (1 << 1),
+ AML_ENCODE_PARAM_INTRA_PERIOD = (1 << 2),
+ AML_ENCODE_PARAM_FORCE_INTRA = (1 << 3),
+ AML_ENCODE_PARAM_GOP_SIZE = (1 << 4),
+};
+
+enum aml_fmt_type {
+ AML_FMT_DEC = 0,
+ AML_FMT_ENC = 1,
+ AML_FMT_FRAME = 2,
+};
+
+/**
+ * struct aml_video_fmt - Structure used to store information about pixelformats
+ */
+struct aml_video_fmt {
+ u32 fourcc;
+ enum aml_fmt_type type;
+ u32 num_planes;
+ const u8 *name;
+};
+
+/**
+ * struct aml_codec_framesizes - Structure used to store information about
+ * framesizes
+ */
+struct aml_codec_framesizes {
+ u32 fourcc;
+ struct v4l2_frmsize_stepwise stepwise;
+};
+
+/**
+ * struct aml_q_type - Type of queue
+ */
+enum aml_q_type {
+ AML_Q_DATA_SRC = 0,
+ AML_Q_DATA_DST = 1,
+};
+
+
+/**
+ * struct aml_q_data - Structure used to store information about queue
+ */
+struct aml_q_data {
+ u32 visible_width;
+ u32 visible_height;
+ u32 coded_width;
+ u32 coded_height;
+ enum v4l2_field field;
+ u32 bytesperline[AML_VCODEC_MAX_PLANES];
+ u32 sizeimage[AML_VCODEC_MAX_PLANES];
+ struct aml_video_fmt *fmt;
+ bool resolution_changed;
+};
+
+/**
+ * struct aml_enc_params - General encoding parameters
+ * @bitrate: target bitrate in bits per second
+ * @num_b_frame: number of b frames between p-frame
+ * @rc_frame: frame based rate control
+ * @rc_mb: macroblock based rate control
+ * @seq_hdr_mode: H.264 sequence header is encoded separately or joined
+ * with the first frame
+ * @intra_period: I frame period
+ * @gop_size: group of picture size, it's used as the intra frame period
+ * @framerate_num: frame rate numerator. ex: framerate_num=30 and
+ * framerate_denom=1 menas FPS is 30
+ * @framerate_denom: frame rate denominator. ex: framerate_num=30 and
+ * framerate_denom=1 menas FPS is 30
+ * @h264_max_qp: Max value for H.264 quantization parameter
+ * @h264_profile: V4L2 defined H.264 profile
+ * @h264_level: V4L2 defined H.264 level
+ * @force_intra: force/insert intra frame
+ */
+struct aml_enc_params {
+ u32 bitrate;
+ u32 num_b_frame;
+ u32 rc_frame;
+ u32 rc_mb;
+ u32 seq_hdr_mode;
+ u32 intra_period;
+ u32 gop_size;
+ u32 framerate_num;
+ u32 framerate_denom;
+ u32 h264_max_qp;
+ u32 h264_profile;
+ u32 h264_level;
+ u32 force_intra;
+};
+
+/**
+ * struct vdec_pic_info - picture size information
+ * @visible_width: picture width
+ * @visible_height: picture height
+ * @coded_width: picture buffer width (64 aligned up from pic_w)
+ * @coded_height: picture buffer heiht (64 aligned up from pic_h)
+ * @y_bs_sz: Y bitstream size
+ * @c_bs_sz: CbCr bitstream size
+ * @y_len_sz: additional size required to store decompress information for y
+ * plane
+ * @c_len_sz: additional size required to store decompress information for cbcr
+ * plane
+ * E.g. suppose picture size is 176x144,
+ * buffer size will be aligned to 176x160.
+ * @profile_idc: source profile level
+ * @field: frame/field information.
+ * @dpb_frames: used for DPB size of calculation.
+ * @dpb_margin: extra buffers for decoder.
+ * @vpp_margin: extra buffers for vpp.
+ */
+struct vdec_pic_info {
+ u32 visible_width;
+ u32 visible_height;
+ u32 coded_width;
+ u32 coded_height;
+ u32 y_bs_sz;
+ u32 c_bs_sz;
+ u32 y_len_sz;
+ u32 c_len_sz;
+ int profile_idc;
+ enum v4l2_field field;
+ u32 dpb_frames;
+ u32 dpb_margin;
+ u32 vpp_margin;
+};
+
+/**
+ * struct vdec_comp_buf_info - compressed buffer info
+ * @max_size: max size needed for MMU Box in MB
+ * @header_size: contineous size for the compressed header
+ * @frame_buffer_size: SG page number to store the frame
+ */
+struct vdec_comp_buf_info {
+ u32 max_size;
+ u32 header_size;
+ u32 frame_buffer_size;
+};
+
+struct aml_vdec_cfg_infos {
+ u32 double_write_mode;
+ u32 init_width;
+ u32 init_height;
+ u32 ref_buf_margin;
+ u32 canvas_mem_mode;
+ u32 canvas_mem_endian;
+ u32 low_latency_mode;
+ u32 uvm_hook_type;
+ /*
+ * bit 16 : force progressive output flag.
+ * bit 15 : enable nr.
+ * bit 14 : enable di local buff.
+ * bit 13 : report downscale yuv buffer size flag.
+ * bit 12 : for second field pts mode.
+ * bit 11 : disable error policy
+ * bit 1 : Non-standard dv flag.
+ * bit 0 : dv two layer flag.
+ */
+ u32 metadata_config_flag; // for metadata config flag
+ u32 duration;
+ u32 data[4];
+};
+
+struct aml_vdec_hdr_infos {
+ /*
+ * bit 29 : present_flag
+ * bit 28-26: video_format "component", "PAL", "NTSC", "SECAM", "MAC", "unspecified"
+ * bit 25 : range "limited", "full_range"
+ * bit 24 : color_description_present_flag
+ * bit 23-16: color_primaries "unknown", "bt709", "undef", "bt601",
+ * "bt470m", "bt470bg", "smpte170m", "smpte240m", "film", "bt2020"
+ * bit 15-8 : transfer_characteristic unknown", "bt709", "undef", "bt601",
+ * "bt470m", "bt470bg", "smpte170m", "smpte240m",
+ * "linear", "log100", "log316", "iec61966-2-4",
+ * "bt1361e", "iec61966-2-1", "bt2020-10", "bt2020-12",
+ * "smpte-st-2084", "smpte-st-428"
+ * bit 7-0 : matrix_coefficient "GBR", "bt709", "undef", "bt601",
+ * "fcc", "bt470bg", "smpte170m", "smpte240m",
+ * "YCgCo", "bt2020nc", "bt2020c"
+ */
+ u32 signal_type;
+ struct vframe_master_display_colour_s color_parms;
+};
+
+struct aml_vdec_ps_infos {
+ u32 visible_width;
+ u32 visible_height;
+ u32 coded_width;
+ u32 coded_height;
+ u32 profile;
+ u32 mb_width;
+ u32 mb_height;
+ u32 dpb_size;
+ u32 ref_frames;
+ u32 dpb_frames;
+ u32 dpb_margin;
+ u32 field;
+ u32 data[3];
+};
+
+struct aml_vdec_cnt_infos {
+ u32 bit_rate;
+ u32 frame_count;
+ u32 error_frame_count;
+ u32 drop_frame_count;
+ u32 total_data;
+};
+
+struct aml_dec_params {
+ u32 parms_status;
+ struct aml_vdec_cfg_infos cfg;
+ struct aml_vdec_ps_infos ps;
+ struct aml_vdec_hdr_infos hdr;
+ struct aml_vdec_cnt_infos cnt;
+};
+
+struct v4l2_config_parm {
+ u32 type;
+ u32 length;
+ union {
+ struct aml_dec_params dec;
+ struct aml_enc_params enc;
+ u8 data[200];
+ } parm;
+ u8 buf[4096];
+};
+
+struct v4l_buff_pool {
+ /*
+ * bit 31-16: buffer state
+ * bit 15- 0: buffer index
+ */
+ u32 seq[V4L_CAP_BUFF_MAX];
+ u32 in, out;
+ u32 dec, vpp, ge2d;
+};
+
+enum aml_thread_type {
+ AML_THREAD_OUTPUT,
+ AML_THREAD_CAPTURE,
+};
+
+typedef void (*aml_thread_func)(struct aml_vcodec_ctx *ctx);
+
+struct aml_vdec_thread {
+ struct list_head node;
+ spinlock_t lock;
+ struct semaphore sem;
+ struct task_struct *task;
+ enum aml_thread_type type;
+ void *priv;
+ int stop;
+
+ aml_thread_func func;
+};
+
+/* struct internal_comp_buf - compressed buffer
+ * @index: index of this buf within (B)MMU BOX
+ * @ref: [0-7]:reference number of this buf
+ * [8-15]: use for reuse.
+ * @mmu_box: mmu_box of context
+ * @bmmu_box: bmmu_box of context
+ * @box_ref: box_ref of context
+ * @header_addr: header for compressed buffer
+ * @frame_buffer_size: SG buffer page number from
+ * @priv_data use for video composer
+ * struct vdec_comp_buf_info
+ */
+struct internal_comp_buf {
+ u32 index;
+ u32 ref;
+ void *mmu_box;
+ void *bmmu_box;
+ struct kref *box_ref;
+
+ ulong header_addr;
+ u32 header_size;
+ u32 frame_buffer_size;
+ struct file_private_data priv_data;
+ ulong header_dw_addr;
+ void *mmu_box_dw;
+ void *bmmu_box_dw;
+};
+
+/*
+ * struct aml_uvm_buff_ref - uvm buff is used reseve ctx ref count.
+ * @index : index of video buffer.
+ * @addr : physic address of video buffer.
+ * @ref : reference of v4ldec context.
+ * @dma : dma buf of associated with vb.
+ */
+struct aml_uvm_buff_ref {
+ int index;
+ ulong addr;
+ struct kref *ref;
+ struct dma_buf *dbuf;
+};
+
+/*
+ * enum aml_fb_requester - indicate which module request fb buffers.
+ */
+enum aml_fb_requester {
+ AML_FB_REQ_DEC,
+ AML_FB_REQ_VPP,
+ AML_FB_REQ_GE2D,
+ AML_FB_REQ_MAX
+};
+
+/*
+ * @query: try to achieved fb token.
+ * @alloc: used for allocte fb buffer.
+ */
+struct aml_fb_ops {
+ bool (*query)(struct aml_fb_ops *, ulong *);
+ int (*alloc)(struct aml_fb_ops *, ulong, struct vdec_v4l2_buffer **, u32);
+};
+
+/*
+ * struct aml_fb_map_table - record some buffer map infos
+ * @addr : yuv linear buffer address.
+ * @header_addr : used for compress buffer.
+ * @vframe : which is from decoder or vpp vf pool.
+ * @task : context of task chain.
+ * @icomp : compress buffer index.
+ */
+struct aml_fb_map_table {
+ ulong addr;
+ ulong header_addr;
+ struct vframe_s *vframe;
+ struct task_chain_s *task;
+ u32 icomp;
+};
+
+/*
+ * struct aux_data - record sei data and dv data
+ * @sei_size: sei data size.
+ * @sei_buf: sei data addr.
+ * @sei_state: sei buffer state. (0 free, 1 not used, 2 used)
+ * @comp_buf: stores comp data parsed from sei data.
+ * @md_buf: stores md data parsed from sei data.
+ */
+struct aux_data {
+ int sei_size;
+ char* sei_buf;
+ int sei_state;
+ char* comp_buf;
+ char* md_buf;
+};
+
+/*
+ * struct aux_info - record aux data infos
+ * @sei_index: sei data index.
+ * @dv_index: dv data index.
+ * @sei_need_free: sei buffer need to free.
+ * @bufs: stores aux data.
+ * @alloc_buffer: alloc aux buffer functions.
+ * @free_buffer: free aux buffer functions.
+ * @free_one_sei_buffer:free sei buffer with index functions.
+ * @bind_sei_buffer: bind sei buffer functions.
+ * @bind_dv_buffer: bind dv buffer functions.
+ */
+struct aux_info {
+ int sei_index;
+ int dv_index;
+ bool sei_need_free;
+ struct aux_data bufs[V4L_CAP_BUFF_MAX];
+ void (*alloc_buffer)(struct aml_vcodec_ctx *ctx, int flag);
+ void (*free_buffer)(struct aml_vcodec_ctx *ctx, int flag);
+ void (*free_one_sei_buffer)(struct aml_vcodec_ctx *ctx, char **addr, int *size, int idx);
+ void (*bind_sei_buffer)(struct aml_vcodec_ctx *ctx, char **addr, int *size, int *idx);
+ void (*bind_dv_buffer)(struct aml_vcodec_ctx *ctx, char **comp_buf, char **md_buf);
+};
+
+/*
+ * struct meta_data - record meta data.
+ * @buf[META_DATA_SIZE]: meta data information.
+ */
+struct meta_data {
+ char buf[META_DATA_SIZE];
+};
+
+/*
+ * struct meta_info - record some meta data infos
+ * @index: meta data index.
+ * @meta_bufs: record meta data.
+ */
+struct meta_info {
+ int index;
+ struct meta_data *meta_bufs;
+};
+
+/*
+ * struct aml_vpp_cfg_infos - config vpp init param
+ * @mode : vpp work mode
+ * @fmt : picture format used to switch nv21 or nv12.
+ * @buf_size: config buffer size for vpp
+ * @is_drm : is drm mode
+ * @is_prog : is a progressive source.
+ * @is_bypass_p : to set progressive bypass in vpp
+ * @enable_nr : enable nosie reduce.
+ * @enable_local_buf: DI used buff alloc by itself.
+ * @res_chg : indicate resolution changed.
+ * @is_vpp_reset: vpp reset just used to res chg.
+ */
+struct aml_vpp_cfg_infos {
+ u32 mode;
+ u32 fmt;
+ u32 buf_size;
+ bool is_drm;
+ bool is_prog;
+ bool is_bypass_p;
+ bool enable_nr;
+ bool enable_local_buf;
+ bool res_chg;
+ bool is_vpp_reset;
+};
+
+struct aml_ge2d_cfg_infos {
+ u32 mode;
+ u32 buf_size;
+ bool is_drm;
+};
+
+/*
+ * struct aml_vcodec_ctx - Context (instance) private data.
+ * @id: index of the context that this structure describes.
+ * @ctx_ref: for deferred free of this context.
+ * @type: type of the instance - decoder or encoder.
+ * @dev: pointer to the aml_vcodec_dev of the device.
+ * @m2m_ctx: pointer to the v4l2_m2m_ctx of the context.
+ * @ada_ctx: pointer to the aml_vdec_adapt of the context.
+ * @vpp: pointer to video post processor
+ * @dec_if: hooked decoder driver interface.
+ * @drv_handle: driver handle for specific decode instance
+ * @fh: struct v4l2_fh.
+ * @ctrl_hdl: handler for v4l2 framework.
+ * @slock: protect v4l2 codec context.
+ * @tsplock: protect the vdec thread context.
+ * @empty_flush_buf: a fake size-0 capture buffer that indicates flush.
+ * @list: link to ctx_list of aml_vcodec_dev.
+ * @q_data: store information of input and output queue of the context.
+ * @queue: waitqueue that can be used to wait for this context to finish.
+ * @state_lock: protect the codec status.
+ * @state: state of the context.
+ * @decode_work: decoder work be used to output buffer.
+ * @output_thread_ready: indicate the output thread ready.
+ * @cap_pool: capture buffers are remark in the pool.
+ * @vdec_thread_list: vdec thread be used to capture.
+ * @dpb_size: store dpb count after header parsing
+ * @vpp_size: store vpp buffer count after header parsing
+ * @param_change: indicate encode parameter type
+ * @param_sets_from_ucode: if true indicate ps from ucode.
+ * @v4l_codec_dpb_ready: queue buffer number greater than dpb.
+ # @v4l_resolution_change: indicate resolution change happend.
+ * @comp: comp be used for sync picture information with decoder.
+ * @config: used to set or get parms for application.
+ * @picinfo: store picture info after header parsing.
+ * @last_decoded_picinfo: pic information get from latest decode.
+ * @colorspace: enum v4l2_colorspace; supplemental to pixelformat.
+ * @ycbcr_enc: enum v4l2_ycbcr_encoding, Y'CbCr encoding.
+ * @quantization: enum v4l2_quantization, colorspace quantization.
+ * @xfer_func: enum v4l2_xfer_func, colorspace transfer function.
+ * @cap_pix_fmt: the picture format used to switch nv21 or nv12.
+ * @has_receive_eos: if receive last frame of capture that be set.
+ * @is_drm_mode: decoding work on drm mode if that set.
+ * @is_stream_mode: vdec input used to stream mode, default frame mode.
+ * @is_stream_off: the value used to handle reset active.
+ * @is_out_stream_off: streamoff called for output port.
+ * @receive_cmd_stop: if receive the cmd flush decoder.
+ * @reset_flag: reset mode includes lightly and normal mode.
+ * @decoded_frame_cnt: the capture buffer deque number to be count.
+ * @buf_used_count: means that decode allocate how many buffs from v4l.
+ * @wq: wait recycle dma buffer finish.
+ * @cap_wq: the wq used for wait capture buffer.
+ * @dmabuff_recycle_lock: protect the lock dmabuff free.
+ * @dmabuff_recycle_work: used for recycle dmabuff.
+ * @dmabuff_recycle: kfifo used for store vb buff.
+ * @capture_buffer: kfifo used for store capture vb buff.
+ * @mmu_box: mmu_box of context.
+ * @bmmu_box: bmmu_box of context.
+ * @box_ref: box_ref of context.
+ * @comp_info: compress buffer information.
+ * @comp_bufs: compress buffer describe.
+ * @comp_lock: used for lock ibuf free cb.
+ * @fb_ops: frame buffer ops interface.
+ * @dv_infos: dv data information.
+ * @vpp_cfg: vpp init parms of configuration.
+ * @vdec_pic_info_update: update pic info cb.
+ * @vpp_is_need: the instance is need vpp.
+ * @task_chain_pool: used to store task chain inst.
+ * @index_disp: the number of frames output.
+ */
+struct aml_vcodec_ctx {
+ int id;
+ struct kref ctx_ref;
+ enum aml_instance_type type;
+ struct aml_vcodec_dev *dev;
+ struct v4l2_m2m_ctx *m2m_ctx;
+ struct aml_vdec_adapt *ada_ctx;
+ struct aml_v4l2_vpp *vpp;
+ const struct vdec_common_if *dec_if;
+ ulong drv_handle;
+ struct v4l2_fh fh;
+ struct v4l2_ctrl_handler ctrl_hdl;
+ spinlock_t slock;
+ spinlock_t tsplock;
+ struct aml_video_dec_buf *empty_flush_buf;
+ struct list_head list;
+
+ struct aml_q_data q_data[2];
+ wait_queue_head_t queue;
+ struct mutex state_lock;
+ enum aml_instance_state state;
+ struct work_struct decode_work;
+ bool output_thread_ready;
+ struct v4l_buff_pool cap_pool;
+ struct list_head vdec_thread_list;
+
+ int dpb_size;
+ int vpp_size;
+ int ge2d_size;
+ bool param_sets_from_ucode;
+ bool v4l_codec_dpb_ready;
+ bool v4l_resolution_change;
+ struct completion comp;
+ struct v4l2_config_parm config;
+ struct vdec_pic_info picinfo;
+ struct vdec_pic_info last_decoded_picinfo;
+ enum v4l2_colorspace colorspace;
+ enum v4l2_ycbcr_encoding ycbcr_enc;
+ enum v4l2_quantization quantization;
+ enum v4l2_xfer_func xfer_func;
+ u32 cap_pix_fmt;
+ u32 output_pix_fmt;
+
+ bool has_receive_eos;
+ bool is_drm_mode;
+ bool output_dma_mode;
+ bool is_stream_off;
+ bool is_out_stream_off;
+ bool receive_cmd_stop;
+ int reset_flag;
+ int decoded_frame_cnt;
+ int buf_used_count;
+ wait_queue_head_t wq, cap_wq, post_done_wq;
+ struct mutex capture_buffer_lock;
+ spinlock_t dmabuff_recycle_lock;
+ struct mutex buff_done_lock;
+ struct work_struct dmabuff_recycle_work;
+ DECLARE_KFIFO(dmabuff_recycle, struct vb2_v4l2_buffer *, 32);
+ DECLARE_KFIFO(capture_buffer, struct vb2_v4l2_buffer *, 32);
+
+ /* compressed buffer support */
+ void *bmmu_box;
+ void *mmu_box;
+ struct kref box_ref;
+ struct vdec_comp_buf_info comp_info;
+ struct internal_comp_buf *comp_bufs;
+ struct uvm_hook_mod_info *uvm_proxy;
+ struct mutex comp_lock;
+
+ struct aml_fb_ops fb_ops;
+ ulong token_table[32];
+
+ struct aml_fb_map_table fb_map[32];
+ struct aml_vpp_cfg_infos vpp_cfg;
+ void (*vdec_pic_info_update)(struct aml_vcodec_ctx *ctx);
+ bool vpp_is_need;
+ struct list_head task_chain_pool;
+ struct meta_info meta_infos;
+ struct vdec_sync *sync;
+ u32 internal_dw_scale;
+
+ /* ge2d field. */
+ struct aml_v4l2_ge2d *ge2d;
+ struct aml_ge2d_cfg_infos ge2d_cfg;
+ bool ge2d_is_need;
+
+ bool second_field_pts_mode;
+ struct aux_info aux_infos;
+ u32 index_disp;
+ bool post_to_upper_done;
+ bool film_grain_present;
+ void *bmmu_box_dw;
+ void *mmu_box_dw;
+};
+
+/**
+ * struct aml_vcodec_dev - driver data.
+ * @v4l2_dev : V4L2 device to register video devices for.
+ * @vfd_dec : Video device for decoder.
+ * @plat_dev : platform device.
+ * @m2m_dev_dec : m2m device for decoder.
+ * @curr_ctx : The context that is waiting for codec hardware.
+ * @id_counter : used to identify current opened instance.
+ * @dec_capability : used to identify decode capability, ex: 4k
+ * @decode_workqueue : the worker used to output buffer schedule.
+ * @ctx_list : list of struct aml_vcodec_ctx.
+ * @irqlock : protect data access by irq handler and work thread.
+ * @dev_mutex : video_device lock.
+ * @dec_mutex : decoder hardware lock.
+ * @queue : waitqueue for waiting for completion of device commands.
+ * @vpp_count : count the number of open vpp.
+ * @v4ldec_class : creat class sysfs uesd to show some information.
+ */
+struct aml_vcodec_dev {
+ struct v4l2_device v4l2_dev;
+ struct video_device *vfd_dec;
+ struct platform_device *plat_dev;
+ struct v4l2_m2m_dev *m2m_dev_dec;
+ struct aml_vcodec_ctx *curr_ctx;
+ ulong id_counter;
+ u32 dec_capability;
+ struct workqueue_struct *decode_workqueue;
+ struct list_head ctx_list;
+ struct file *filp;
+ spinlock_t irqlock;
+ struct mutex dev_mutex;
+ struct mutex dec_mutex;
+ wait_queue_head_t queue;
+ atomic_t vpp_count;
+ struct class v4ldec_class;
+};
+
+static inline struct aml_vcodec_ctx *fh_to_ctx(struct v4l2_fh *fh)
+{
+ return container_of(fh, struct aml_vcodec_ctx, fh);
+}
+
+static inline struct aml_vcodec_ctx *ctrl_to_ctx(struct v4l2_ctrl *ctrl)
+{
+ return container_of(ctrl->handler, struct aml_vcodec_ctx, ctrl_hdl);
+}
+
+void aml_thread_capture_worker(struct aml_vcodec_ctx *ctx);
+void aml_thread_post_task(struct aml_vcodec_ctx *ctx, enum aml_thread_type type);
+int aml_thread_start(struct aml_vcodec_ctx *ctx, aml_thread_func func,
+ enum aml_thread_type type, const char *thread_name);
+void aml_thread_stop(struct aml_vcodec_ctx *ctx);
+
+#endif /* _AML_VCODEC_DRV_H_ */
diff --git a/drivers/amvdec_ports/aml_vcodec_ge2d.c b/drivers/amvdec_ports/aml_vcodec_ge2d.c
new file mode 100644
index 0000000..2d331ed
--- /dev/null
+++ b/drivers/amvdec_ports/aml_vcodec_ge2d.c
@@ -0,0 +1,968 @@
+/*
+* Copyright (C) 2020 Amlogic, Inc. All rights reserved.
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2 of the License, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful, but WITHOUT
+* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+* more details.
+*
+* You should have received a copy of the GNU General Public License along
+* with this program; if not, write to the Free Software Foundation, Inc.,
+* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*
+* Description:
+*/
+
+#include <linux/types.h>
+#include <linux/delay.h>
+#include <linux/videodev2.h>
+#include <linux/sched/clock.h>
+#include <uapi/linux/sched/types.h>
+#include <linux/amlogic/meson_uvm_core.h>
+#include <linux/amlogic/media/ge2d/ge2d.h>
+#include <linux/amlogic/media/canvas/canvas_mgr.h>
+
+#include "../common/chips/decoder_cpu_ver_info.h"
+#include "aml_vcodec_ge2d.h"
+#include "aml_vcodec_adapt.h"
+#include "vdec_drv_if.h"
+
+#define KERNEL_ATRACE_TAG KERNEL_ATRACE_TAG_V4L2
+#include <trace/events/meson_atrace.h>
+
+#define GE2D_BUF_GET_IDX(ge2d_buf) (ge2d_buf->aml_buf->vb.vb2_buf.index)
+#define INPUT_PORT 0
+#define OUTPUT_PORT 1
+
+extern int dump_ge2d_input;
+extern int ge2d_bypass_frames;
+
+enum GE2D_FLAG {
+ GE2D_FLAG_P = 0x1,
+ GE2D_FLAG_I = 0x2,
+ GE2D_FLAG_EOS = 0x4,
+ GE2D_FLAG_BUF_BY_PASS = 0x8,
+ GE2D_FLAG_MAX = 0x7FFFFFFF,
+};
+
+enum videocom_source_type {
+ DECODER_8BIT_NORMAL = 0,
+ DECODER_8BIT_BOTTOM,
+ DECODER_8BIT_TOP,
+ DECODER_10BIT_NORMAL,
+ DECODER_10BIT_BOTTOM,
+ DECODER_10BIT_TOP
+};
+
+#ifndef CONFIG_AMLOGIC_MEDIA_GE2D
+inline void stretchblt_noalpha(struct ge2d_context_s *wq,
+ int src_x, int src_y, int src_w, int src_h,
+ int dst_x, int dst_y, int dst_w, int dst_h) { return; }
+inline int ge2d_context_config_ex(struct ge2d_context_s *context,
+ struct config_para_ex_s *ge2d_config) { return -1; }
+inline struct ge2d_context_s *create_ge2d_work_queue(void) { return NULL; }
+inline int destroy_ge2d_work_queue(struct ge2d_context_s *ge2d_work_queue) { return -1; }
+#endif
+
+static int get_source_type(struct vframe_s *vf)
+{
+ enum videocom_source_type ret;
+ int interlace_mode;
+
+ interlace_mode = vf->type & VIDTYPE_TYPEMASK;
+
+ if ((vf->bitdepth & BITDEPTH_Y10) &&
+ (!(vf->type & VIDTYPE_COMPRESS)) &&
+ (get_cpu_type() >= MESON_CPU_MAJOR_ID_TXL)) {
+ if (interlace_mode == VIDTYPE_INTERLACE_TOP)
+ ret = DECODER_10BIT_TOP;
+ else if (interlace_mode == VIDTYPE_INTERLACE_BOTTOM)
+ ret = DECODER_10BIT_BOTTOM;
+ else
+ ret = DECODER_10BIT_NORMAL;
+ } else {
+ if (interlace_mode == VIDTYPE_INTERLACE_TOP)
+ ret = DECODER_8BIT_TOP;
+ else if (interlace_mode == VIDTYPE_INTERLACE_BOTTOM)
+ ret = DECODER_8BIT_BOTTOM;
+ else
+ ret = DECODER_8BIT_NORMAL;
+ }
+
+ return ret;
+}
+
+static int get_input_format(struct vframe_s *vf)
+{
+ int format = GE2D_FORMAT_M24_YUV420;
+ enum videocom_source_type soure_type;
+
+ soure_type = get_source_type(vf);
+
+ switch (soure_type) {
+ case DECODER_8BIT_NORMAL:
+ if (vf->type & VIDTYPE_VIU_422)
+ format = GE2D_FORMAT_S16_YUV422;
+ else if (vf->type & VIDTYPE_VIU_NV21)
+ format = GE2D_FORMAT_M24_NV21;
+ else if (vf->type & VIDTYPE_VIU_NV12)
+ format = GE2D_FORMAT_M24_NV12;
+ else if (vf->type & VIDTYPE_VIU_444)
+ format = GE2D_FORMAT_S24_YUV444;
+ else
+ format = GE2D_FORMAT_M24_YUV420;
+ break;
+ case DECODER_8BIT_BOTTOM:
+ if (vf->type & VIDTYPE_VIU_422)
+ format = GE2D_FORMAT_S16_YUV422
+ | (GE2D_FORMAT_S16_YUV422B & (3 << 3));
+ else if (vf->type & VIDTYPE_VIU_NV21)
+ format = GE2D_FORMAT_M24_NV21
+ | (GE2D_FORMAT_M24_NV21B & (3 << 3));
+ else if (vf->type & VIDTYPE_VIU_NV12)
+ format = GE2D_FORMAT_M24_NV12
+ | (GE2D_FORMAT_M24_NV12B & (3 << 3));
+ else if (vf->type & VIDTYPE_VIU_444)
+ format = GE2D_FORMAT_S24_YUV444
+ | (GE2D_FORMAT_S24_YUV444B & (3 << 3));
+ else
+ format = GE2D_FORMAT_M24_YUV420
+ | (GE2D_FMT_M24_YUV420B & (3 << 3));
+ break;
+ case DECODER_8BIT_TOP:
+ if (vf->type & VIDTYPE_VIU_422)
+ format = GE2D_FORMAT_S16_YUV422
+ | (GE2D_FORMAT_S16_YUV422T & (3 << 3));
+ else if (vf->type & VIDTYPE_VIU_NV21)
+ format = GE2D_FORMAT_M24_NV21
+ | (GE2D_FORMAT_M24_NV21T & (3 << 3));
+ else if (vf->type & VIDTYPE_VIU_NV12)
+ format = GE2D_FORMAT_M24_NV12
+ | (GE2D_FORMAT_M24_NV12T & (3 << 3));
+ else if (vf->type & VIDTYPE_VIU_444)
+ format = GE2D_FORMAT_S24_YUV444
+ | (GE2D_FORMAT_S24_YUV444T & (3 << 3));
+ else
+ format = GE2D_FORMAT_M24_YUV420
+ | (GE2D_FMT_M24_YUV420T & (3 << 3));
+ break;
+ case DECODER_10BIT_NORMAL:
+ if (vf->type & VIDTYPE_VIU_422) {
+ if (vf->bitdepth & FULL_PACK_422_MODE)
+ format = GE2D_FORMAT_S16_10BIT_YUV422;
+ else
+ format = GE2D_FORMAT_S16_12BIT_YUV422;
+ }
+ break;
+ case DECODER_10BIT_BOTTOM:
+ if (vf->type & VIDTYPE_VIU_422) {
+ if (vf->bitdepth & FULL_PACK_422_MODE)
+ format = GE2D_FORMAT_S16_10BIT_YUV422
+ | (GE2D_FORMAT_S16_10BIT_YUV422B
+ & (3 << 3));
+ else
+ format = GE2D_FORMAT_S16_12BIT_YUV422
+ | (GE2D_FORMAT_S16_12BIT_YUV422B
+ & (3 << 3));
+ }
+ break;
+ case DECODER_10BIT_TOP:
+ if (vf->type & VIDTYPE_VIU_422) {
+ if (vf->bitdepth & FULL_PACK_422_MODE)
+ format = GE2D_FORMAT_S16_10BIT_YUV422
+ | (GE2D_FORMAT_S16_10BIT_YUV422T
+ & (3 << 3));
+ else
+ format = GE2D_FORMAT_S16_12BIT_YUV422
+ | (GE2D_FORMAT_S16_12BIT_YUV422T
+ & (3 << 3));
+ }
+ break;
+ default:
+ format = GE2D_FORMAT_M24_YUV420;
+ }
+ return format;
+}
+
+static int v4l_ge2d_empty_input_done(struct aml_v4l2_ge2d_buf *buf)
+{
+ struct aml_v4l2_ge2d *ge2d = buf->caller_data;
+ struct vdec_v4l2_buffer *fb = NULL;
+ bool eos = false;
+
+ if (!ge2d || !ge2d->ctx) {
+ v4l_dbg(0, V4L_DEBUG_CODEC_ERROR,
+ "fatal %s %d ge2d:%px\n",
+ __func__, __LINE__, ge2d);
+ return -1;
+ }
+
+ fb = &buf->aml_buf->frame_buffer;
+ eos = (buf->flag & GE2D_FLAG_EOS);
+
+ v4l_dbg(ge2d->ctx, V4L_DEBUG_GE2D_BUFMGR,
+ "ge2d_input done: vf:%px, idx: %d, flag(vf:%x ge2d:%x) %s, ts:%lld, "
+ "in:%d, out:%d, vf:%d, in done:%d, out done:%d\n",
+ buf->vf,
+ buf->vf->index,
+ buf->vf->flag,
+ buf->flag,
+ eos ? "eos" : "",
+ buf->vf->timestamp,
+ kfifo_len(&ge2d->input),
+ kfifo_len(&ge2d->output),
+ kfifo_len(&ge2d->frame),
+ kfifo_len(&ge2d->in_done_q),
+ kfifo_len(&ge2d->out_done_q));
+
+ fb->task->recycle(fb->task, TASK_TYPE_GE2D);
+
+ kfifo_put(&ge2d->input, buf);
+
+ ATRACE_COUNTER("VC_IN_GE2D-1.recycle", fb->buf_idx);
+
+ return 0;
+}
+
+static int v4l_ge2d_fill_output_done(struct aml_v4l2_ge2d_buf *buf)
+{
+ struct aml_v4l2_ge2d *ge2d = buf->caller_data;
+ struct vdec_v4l2_buffer *fb = NULL;
+ bool bypass = false;
+ bool eos = false;
+
+ if (!ge2d || !ge2d->ctx) {
+ v4l_dbg(0, V4L_DEBUG_CODEC_ERROR,
+ "fatal %s %d ge2d:%px\n",
+ __func__, __LINE__, ge2d);
+ return -1;
+ }
+
+ fb = &buf->aml_buf->frame_buffer;
+ eos = (buf->flag & GE2D_FLAG_EOS);
+ bypass = (buf->flag & GE2D_FLAG_BUF_BY_PASS);
+
+ /* recovery fb handle. */
+ buf->vf->v4l_mem_handle = (ulong)fb;
+
+ kfifo_put(&ge2d->out_done_q, buf);
+
+ v4l_dbg(ge2d->ctx, V4L_DEBUG_GE2D_BUFMGR,
+ "ge2d_output done: vf:%px, idx:%d, flag(vf:%x ge2d:%x) %s, ts:%lld, "
+ "in:%d, out:%d, vf:%d, in done:%d, out done:%d, wxh:%ux%u\n",
+ buf->vf,
+ buf->vf->index,
+ buf->vf->flag,
+ buf->flag,
+ eos ? "eos" : "",
+ buf->vf->timestamp,
+ kfifo_len(&ge2d->input),
+ kfifo_len(&ge2d->output),
+ kfifo_len(&ge2d->frame),
+ kfifo_len(&ge2d->in_done_q),
+ kfifo_len(&ge2d->out_done_q),
+ buf->vf->width, buf->vf->height);
+
+ ATRACE_COUNTER("VC_OUT_GE2D-2.submit", fb->buf_idx);
+
+ fb->task->submit(fb->task, TASK_TYPE_GE2D);
+
+ ge2d->out_num[OUTPUT_PORT]++;
+
+ return 0;
+}
+
+static void ge2d_vf_get(void *caller, struct vframe_s **vf_out)
+{
+ struct aml_v4l2_ge2d *ge2d = (struct aml_v4l2_ge2d *)caller;
+ struct aml_v4l2_ge2d_buf *buf = NULL;
+ struct vdec_v4l2_buffer *fb = NULL;
+ struct vframe_s *vf = NULL;
+ bool bypass = false;
+ bool eos = false;
+
+ if (!ge2d || !ge2d->ctx) {
+ v4l_dbg(0, V4L_DEBUG_CODEC_ERROR,
+ "fatal %s %d ge2d:%px\n",
+ __func__, __LINE__, ge2d);
+ return;
+ }
+
+ if (kfifo_get(&ge2d->out_done_q, &buf)) {
+ fb = &buf->aml_buf->frame_buffer;
+ eos = (buf->flag & GE2D_FLAG_EOS);
+ bypass = (buf->flag & GE2D_FLAG_BUF_BY_PASS);
+ vf = buf->vf;
+
+ if (eos) {
+ v4l_dbg(ge2d->ctx, V4L_DEBUG_GE2D_DETAIL,
+ "%s %d got eos\n",
+ __func__, __LINE__);
+ vf->type |= VIDTYPE_V4L_EOS;
+ vf->flag = VFRAME_FLAG_EMPTY_FRAME_V4L;
+ }
+
+ *vf_out = vf;
+
+ ATRACE_COUNTER("VC_OUT_GE2D-3.vf_get", fb->buf_idx);
+
+ v4l_dbg(ge2d->ctx, V4L_DEBUG_GE2D_BUFMGR,
+ "%s: vf:%px, index:%d, flag(vf:%x ge2d:%x), ts:%lld, type:%x, wxh:%ux%u\n",
+ __func__, vf,
+ vf->index,
+ vf->flag,
+ buf->flag,
+ vf->timestamp, vf->type, vf->width, vf->height);
+ }
+}
+
+static void ge2d_vf_put(void *caller, struct vframe_s *vf)
+{
+ struct aml_v4l2_ge2d *ge2d = (struct aml_v4l2_ge2d *)caller;
+ struct vdec_v4l2_buffer *fb = NULL;
+ struct aml_video_dec_buf *aml_buf = NULL;
+ struct aml_v4l2_ge2d_buf *buf = NULL;
+ bool bypass = false;
+ bool eos = false;
+
+ fb = (struct vdec_v4l2_buffer *) vf->v4l_mem_handle;
+ aml_buf = container_of(fb, struct aml_video_dec_buf, frame_buffer);
+ buf = (struct aml_v4l2_ge2d_buf *) aml_buf->ge2d_buf_handle;
+ eos = (buf->flag & GE2D_FLAG_EOS);
+ bypass = (buf->flag & GE2D_FLAG_BUF_BY_PASS);
+
+ v4l_dbg(ge2d->ctx, V4L_DEBUG_GE2D_BUFMGR,
+ "%s: vf:%px, index:%d, flag(vf:%x ge2d:%x), ts:%lld\n",
+ __func__, vf,
+ vf->index,
+ vf->flag,
+ buf->flag,
+ vf->timestamp);
+
+ ATRACE_COUNTER("VC_IN_GE2D-0.vf_put", fb->buf_idx);
+
+ mutex_lock(&ge2d->output_lock);
+ kfifo_put(&ge2d->frame, vf);
+ kfifo_put(&ge2d->output, buf);
+ mutex_unlock(&ge2d->output_lock);
+ up(&ge2d->sem_out);
+}
+
+static int aml_v4l2_ge2d_thread(void* param)
+{
+ struct aml_v4l2_ge2d* ge2d = param;
+ struct aml_vcodec_ctx *ctx = ge2d->ctx;
+ struct config_para_ex_s ge2d_config;
+ u32 src_fmt = 0, dst_fmt = 0;
+ struct canvas_s cd;
+ ulong start_time;
+
+ v4l_dbg(ctx, V4L_DEBUG_GE2D_DETAIL, "enter ge2d thread\n");
+ while (ge2d->running) {
+ struct aml_v4l2_ge2d_buf *in_buf;
+ struct aml_v4l2_ge2d_buf *out_buf = NULL;
+ struct vframe_s *vf_out = NULL;
+ struct vdec_v4l2_buffer *fb;
+
+ if (down_interruptible(&ge2d->sem_in))
+ goto exit;
+retry:
+ if (!ge2d->running)
+ break;
+
+ if (kfifo_is_empty(&ge2d->output)) {
+ if (down_interruptible(&ge2d->sem_out))
+ goto exit;
+ goto retry;
+ }
+
+ mutex_lock(&ge2d->output_lock);
+ if (!kfifo_get(&ge2d->output, &out_buf)) {
+ mutex_unlock(&ge2d->output_lock);
+ v4l_dbg(ctx, 0, "ge2d can not get output\n");
+ goto exit;
+ }
+ mutex_unlock(&ge2d->output_lock);
+
+ /* bind v4l2 buffers */
+ if (!out_buf->aml_buf) {
+ struct vdec_v4l2_buffer *out;
+
+ if (!ctx->fb_ops.query(&ctx->fb_ops, &ge2d->fb_token)) {
+ usleep_range(500, 550);
+ mutex_lock(&ge2d->output_lock);
+ kfifo_put(&ge2d->output, out_buf);
+ mutex_unlock(&ge2d->output_lock);
+ goto retry;
+ }
+
+ if (ctx->fb_ops.alloc(&ctx->fb_ops, ge2d->fb_token, &out, AML_FB_REQ_GE2D)) {
+ usleep_range(5000, 5500);
+ mutex_lock(&ge2d->output_lock);
+ kfifo_put(&ge2d->output, out_buf);
+ mutex_unlock(&ge2d->output_lock);
+ goto retry;
+ }
+
+ out_buf->aml_buf = container_of(out,
+ struct aml_video_dec_buf, frame_buffer);
+ out_buf->aml_buf->ge2d_buf_handle = (ulong) out_buf;
+ v4l_dbg(ctx, V4L_DEBUG_GE2D_BUFMGR,
+ "ge2d bind buf:%d to ge2d_buf:%px\n",
+ GE2D_BUF_GET_IDX(out_buf), out_buf);
+
+ out->m.mem[0].bytes_used = out->m.mem[0].size;
+ out->m.mem[1].bytes_used = out->m.mem[1].size;
+ }
+
+ /* safe to pop in_buf */
+ if (!kfifo_get(&ge2d->in_done_q, &in_buf)) {
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_ERROR,
+ "ge2d can not get input\n");
+ goto exit;
+ }
+
+ mutex_lock(&ge2d->output_lock);
+ if (!kfifo_get(&ge2d->frame, &vf_out)) {
+ mutex_unlock(&ge2d->output_lock);
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_ERROR,
+ "ge2d can not get frame\n");
+ goto exit;
+ }
+ mutex_unlock(&ge2d->output_lock);
+
+ fb = &out_buf->aml_buf->frame_buffer;
+ fb->status = FB_ST_GE2D;
+
+ /* fill output vframe information. */
+ memcpy(vf_out, in_buf->vf, sizeof(*vf_out));
+ memcpy(vf_out->canvas0_config,
+ in_buf->vf->canvas0_config,
+ 2 * sizeof(struct canvas_config_s));
+
+ vf_out->canvas0_config[0].phy_addr = fb->m.mem[0].addr;
+ if (fb->num_planes == 1) {
+ vf_out->canvas0_config[1].phy_addr =
+ fb->m.mem[0].addr + fb->m.mem[0].offset;
+ vf_out->canvas0_config[2].phy_addr =
+ fb->m.mem[0].addr + fb->m.mem[0].offset
+ + (fb->m.mem[0].offset >> 2);
+ } else {
+ vf_out->canvas0_config[1].phy_addr =
+ fb->m.mem[1].addr;
+ vf_out->canvas0_config[2].phy_addr =
+ fb->m.mem[2].addr;
+ }
+
+ /* fill outbuf parms. */
+ out_buf->vf = vf_out;
+ out_buf->flag = 0;
+ out_buf->caller_data = ge2d;
+
+ /* fill inbuf parms. */
+ in_buf->caller_data = ge2d;
+
+ memset(&ge2d_config, 0, sizeof(ge2d_config));
+
+ src_fmt = get_input_format(in_buf->vf);
+ if (in_buf->vf->canvas0_config[0].endian == 7)
+ src_fmt |= GE2D_BIG_ENDIAN;
+ else
+ src_fmt |= GE2D_LITTLE_ENDIAN;
+
+ /* negotiate format of destination */
+ dst_fmt = get_input_format(in_buf->vf);
+ if (ge2d->work_mode & GE2D_MODE_CONVERT_NV12)
+ dst_fmt |= GE2D_FORMAT_M24_NV12;
+ else if (ge2d->work_mode & GE2D_MODE_CONVERT_NV21)
+ dst_fmt |= GE2D_FORMAT_M24_NV21;
+
+ if (ge2d->work_mode & GE2D_MODE_CONVERT_LE)
+ dst_fmt |= GE2D_LITTLE_ENDIAN;
+ else
+ dst_fmt |= GE2D_BIG_ENDIAN;
+
+ if ((dst_fmt & GE2D_COLOR_MAP_MASK) == GE2D_COLOR_MAP_NV12) {
+ vf_out->type |= VIDTYPE_VIU_NV12;
+ vf_out->type &= ~VIDTYPE_VIU_NV21;
+ } else if ((dst_fmt & GE2D_COLOR_MAP_MASK) == GE2D_COLOR_MAP_NV21) {
+ vf_out->type |= VIDTYPE_VIU_NV21;
+ vf_out->type &= ~VIDTYPE_VIU_NV12;
+ }
+ if ((dst_fmt & GE2D_ENDIAN_MASK) == GE2D_LITTLE_ENDIAN) {
+ vf_out->canvas0_config[0].endian = 0;
+ vf_out->canvas0_config[1].endian = 0;
+ vf_out->canvas0_config[2].endian = 0;
+ } else if ((dst_fmt & GE2D_ENDIAN_MASK) == GE2D_BIG_ENDIAN){
+ vf_out->canvas0_config[0].endian = 7;
+ vf_out->canvas0_config[1].endian = 7;
+ vf_out->canvas0_config[2].endian = 7;
+ }
+
+ start_time = local_clock();
+ /* src canvas configure. */
+ if ((in_buf->vf->canvas0Addr == 0) ||
+ (in_buf->vf->canvas0Addr == (u32)-1)) {
+ canvas_config_config(ge2d->src_canvas_id[0], &in_buf->vf->canvas0_config[0]);
+ canvas_config_config(ge2d->src_canvas_id[1], &in_buf->vf->canvas0_config[1]);
+ canvas_config_config(ge2d->src_canvas_id[2], &in_buf->vf->canvas0_config[2]);
+ ge2d_config.src_para.canvas_index =
+ ge2d->src_canvas_id[0] |
+ ge2d->src_canvas_id[1] << 8 |
+ ge2d->src_canvas_id[2] << 16;
+
+ ge2d_config.src_planes[0].addr =
+ in_buf->vf->canvas0_config[0].phy_addr;
+ ge2d_config.src_planes[0].w =
+ in_buf->vf->canvas0_config[0].width;
+ ge2d_config.src_planes[0].h =
+ in_buf->vf->canvas0_config[0].height;
+ ge2d_config.src_planes[1].addr =
+ in_buf->vf->canvas0_config[1].phy_addr;
+ ge2d_config.src_planes[1].w =
+ in_buf->vf->canvas0_config[1].width;
+ ge2d_config.src_planes[1].h =
+ in_buf->vf->canvas0_config[1].height;
+ ge2d_config.src_planes[2].addr =
+ in_buf->vf->canvas0_config[2].phy_addr;
+ ge2d_config.src_planes[2].w =
+ in_buf->vf->canvas0_config[2].width;
+ ge2d_config.src_planes[2].h =
+ in_buf->vf->canvas0_config[2].height;
+ } else {
+ ge2d_config.src_para.canvas_index = in_buf->vf->canvas0Addr;
+ }
+ ge2d_config.src_para.mem_type = CANVAS_TYPE_INVALID;
+ ge2d_config.src_para.format = src_fmt;
+ ge2d_config.src_para.fill_color_en = 0;
+ ge2d_config.src_para.fill_mode = 0;
+ ge2d_config.src_para.x_rev = 0;
+ ge2d_config.src_para.y_rev = 0;
+ ge2d_config.src_para.color = 0xffffffff;
+ ge2d_config.src_para.top = 0;
+ ge2d_config.src_para.left = 0;
+ ge2d_config.src_para.width = in_buf->vf->width;
+ if (in_buf->vf->type & VIDTYPE_INTERLACE)
+ ge2d_config.src_para.height = in_buf->vf->height >> 1;
+ else
+ ge2d_config.src_para.height = in_buf->vf->height;
+
+ /* dst canvas configure. */
+ canvas_config_config(ge2d->dst_canvas_id[0], &vf_out->canvas0_config[0]);
+ if ((ge2d_config.src_para.format & 0xfffff) == GE2D_FORMAT_M24_YUV420) {
+ vf_out->canvas0_config[1].width <<= 1;
+ }
+ canvas_config_config(ge2d->dst_canvas_id[1], &vf_out->canvas0_config[1]);
+ canvas_config_config(ge2d->dst_canvas_id[2], &vf_out->canvas0_config[2]);
+ ge2d_config.dst_para.canvas_index =
+ ge2d->dst_canvas_id[0] |
+ ge2d->dst_canvas_id[1] << 8;
+ canvas_read(ge2d->dst_canvas_id[0], &cd);
+ ge2d_config.dst_planes[0].addr = cd.addr;
+ ge2d_config.dst_planes[0].w = cd.width;
+ ge2d_config.dst_planes[0].h = cd.height;
+ canvas_read(ge2d->dst_canvas_id[1], &cd);
+ ge2d_config.dst_planes[1].addr = cd.addr;
+ ge2d_config.dst_planes[1].w = cd.width;
+ ge2d_config.dst_planes[1].h = cd.height;
+
+ ge2d_config.dst_para.format = dst_fmt;
+ ge2d_config.dst_para.width = in_buf->vf->width;
+ ge2d_config.dst_para.height = in_buf->vf->height;
+ ge2d_config.dst_para.mem_type = CANVAS_TYPE_INVALID;
+ ge2d_config.dst_para.fill_color_en = 0;
+ ge2d_config.dst_para.fill_mode = 0;
+ ge2d_config.dst_para.x_rev = 0;
+ ge2d_config.dst_para.y_rev = 0;
+ ge2d_config.dst_para.color = 0;
+ ge2d_config.dst_para.top = 0;
+ ge2d_config.dst_para.left = 0;
+
+ /* other ge2d parameters configure. */
+ ge2d_config.src_key.key_enable = 0;
+ ge2d_config.src_key.key_mask = 0;
+ ge2d_config.src_key.key_mode = 0;
+ ge2d_config.alu_const_color = 0;
+ ge2d_config.bitmask_en = 0;
+ ge2d_config.src1_gb_alpha = 0;
+ ge2d_config.dst_xy_swap = 0;
+ ge2d_config.src2_para.mem_type = CANVAS_TYPE_INVALID;
+
+ ATRACE_COUNTER("VC_OUT_GE2D-1.handle_start",
+ in_buf->aml_buf->frame_buffer.buf_idx);
+
+ v4l_dbg(ctx, V4L_DEBUG_GE2D_BUFMGR,
+ "ge2d_handle start: dec vf:%px/%d, ge2d vf:%px/%d, iphy:%lx/%lx %dx%d ophy:%lx/%lx %dx%d, vf:%ux%u, fmt(src:%x, dst:%x), "
+ "in:%d, out:%d, vf:%d, in done:%d, out done:%d\n",
+ in_buf->vf, in_buf->vf->index,
+ out_buf->vf, GE2D_BUF_GET_IDX(out_buf),
+ in_buf->vf->canvas0_config[0].phy_addr,
+ in_buf->vf->canvas0_config[1].phy_addr,
+ in_buf->vf->canvas0_config[0].width,
+ in_buf->vf->canvas0_config[0].height,
+ vf_out->canvas0_config[0].phy_addr,
+ vf_out->canvas0_config[1].phy_addr,
+ vf_out->canvas0_config[0].width,
+ vf_out->canvas0_config[0].height,
+ vf_out->width, vf_out->height,
+ src_fmt, dst_fmt,
+ kfifo_len(&ge2d->input),
+ kfifo_len(&ge2d->output),
+ kfifo_len(&ge2d->frame),
+ kfifo_len(&ge2d->in_done_q),
+ kfifo_len(&ge2d->out_done_q));
+
+ if (ge2d_context_config_ex(ge2d->ge2d_context, &ge2d_config) < 0) {
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_ERROR,
+ "ge2d_context_config_ex error.\n");
+ goto exit;
+ }
+
+ if (!(in_buf->flag & GE2D_FLAG_EOS)) {
+ if (in_buf->vf->type & VIDTYPE_INTERLACE) {
+ stretchblt_noalpha(ge2d->ge2d_context,
+ 0, 0, in_buf->vf->width, in_buf->vf->height / 2,
+ 0, 0, in_buf->vf->width, in_buf->vf->height);
+ } else {
+ stretchblt_noalpha(ge2d->ge2d_context,
+ 0, 0, in_buf->vf->width, in_buf->vf->height,
+ 0, 0, in_buf->vf->width, in_buf->vf->height);
+ }
+ }
+
+ //pr_info("consume time %d us\n", div64_u64(local_clock() - start_time, 1000));
+
+ v4l_ge2d_fill_output_done(out_buf);
+ v4l_ge2d_empty_input_done(in_buf);
+
+ ge2d->in_num[INPUT_PORT]++;
+ ge2d->out_num[INPUT_PORT]++;
+ }
+exit:
+ while (!kthread_should_stop()) {
+ usleep_range(1000, 2000);
+ }
+
+ v4l_dbg(ctx, V4L_DEBUG_GE2D_DETAIL, "exit ge2d thread\n");
+
+ return 0;
+}
+
+int aml_v4l2_ge2d_get_buf_num(u32 mode)
+{
+ return 4;
+}
+
+int aml_v4l2_ge2d_init(
+ struct aml_vcodec_ctx *ctx,
+ struct aml_ge2d_cfg_infos *cfg,
+ struct aml_v4l2_ge2d** ge2d_handle)
+{
+ struct sched_param param = { .sched_priority = MAX_RT_PRIO - 1 };
+ struct aml_v4l2_ge2d *ge2d;
+ u32 work_mode = cfg->mode;
+ u32 buf_size;
+ int i, ret;
+
+ if (!cfg || !ge2d_handle)
+ return -EINVAL;
+
+ ge2d = kzalloc(sizeof(*ge2d), GFP_KERNEL);
+ if (!ge2d)
+ return -ENOMEM;
+
+ ge2d->work_mode = work_mode;
+
+ /* default convert little endian. */
+ if (!ge2d->work_mode) {
+ ge2d->work_mode = GE2D_MODE_CONVERT_LE;
+ }
+
+ ge2d->ge2d_context = create_ge2d_work_queue();
+ if (!ge2d->ge2d_context) {
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_ERROR,
+ "ge2d_create_instance fail\n");
+ ret = -EINVAL;
+ goto error;
+ }
+
+ INIT_KFIFO(ge2d->input);
+ INIT_KFIFO(ge2d->output);
+ INIT_KFIFO(ge2d->frame);
+ INIT_KFIFO(ge2d->out_done_q);
+ INIT_KFIFO(ge2d->in_done_q);
+
+ ge2d->ctx = ctx;
+ buf_size = cfg->buf_size;
+ ge2d->buf_size = buf_size;
+
+ /* setup output fifo */
+ ret = kfifo_alloc(&ge2d->output, buf_size, GFP_KERNEL);
+ if (ret) {
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_ERROR,
+ "alloc output fifo fail.\n");
+ ret = -ENOMEM;
+ goto error2;
+ }
+
+ ge2d->ovbpool = vzalloc(buf_size * sizeof(*ge2d->ovbpool));
+ if (!ge2d->ovbpool) {
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_ERROR,
+ "alloc output vb pool fail.\n");
+ ret = -ENOMEM;
+ goto error3;
+ }
+
+ /* setup vframe fifo */
+ ret = kfifo_alloc(&ge2d->frame, buf_size, GFP_KERNEL);
+ if (ret) {
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_ERROR,
+ "alloc ge2d vframe fifo fail.\n");
+ ret = -ENOMEM;
+ goto error4;
+ }
+
+ ge2d->vfpool = vzalloc(buf_size * sizeof(*ge2d->vfpool));
+ if (!ge2d->vfpool) {
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_ERROR,
+ "alloc vf pool fail.\n");
+ ret = -ENOMEM;
+ goto error5;
+ }
+
+ ret = kfifo_alloc(&ge2d->input, GE2D_FRAME_SIZE, GFP_KERNEL);
+ if (ret) {
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_ERROR,
+ "alloc input fifo fail.\n");
+ ret = -ENOMEM;
+ goto error6;
+ }
+
+ ge2d->ivbpool = vzalloc(GE2D_FRAME_SIZE * sizeof(*ge2d->ivbpool));
+ if (!ge2d->ivbpool) {
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_ERROR,
+ "alloc input vb pool fail.\n");
+ ret = -ENOMEM;
+ goto error7;
+ }
+
+ for (i = 0 ; i < GE2D_FRAME_SIZE ; i++) {
+ kfifo_put(&ge2d->input, &ge2d->ivbpool[i]);
+ }
+
+ for (i = 0 ; i < buf_size ; i++) {
+ kfifo_put(&ge2d->output, &ge2d->ovbpool[i]);
+ kfifo_put(&ge2d->frame, &ge2d->vfpool[i]);
+ }
+
+ ge2d->src_canvas_id[0] = canvas_pool_map_alloc_canvas("v4ldec-ge2d");
+ ge2d->src_canvas_id[1] = canvas_pool_map_alloc_canvas("v4ldec-ge2d");
+ ge2d->src_canvas_id[2] = canvas_pool_map_alloc_canvas("v4ldec-ge2d");
+ ge2d->dst_canvas_id[0] = canvas_pool_map_alloc_canvas("v4ldec-ge2d");
+ ge2d->dst_canvas_id[1] = canvas_pool_map_alloc_canvas("v4ldec-ge2d");
+ ge2d->dst_canvas_id[2] = canvas_pool_map_alloc_canvas("v4ldec-ge2d");
+ if ((ge2d->src_canvas_id[0] <= 0) ||
+ (ge2d->src_canvas_id[1] <= 0) ||
+ (ge2d->src_canvas_id[2] <= 0) ||
+ (ge2d->dst_canvas_id[0] <= 0) ||
+ (ge2d->dst_canvas_id[1] <= 0) ||
+ (ge2d->dst_canvas_id[2] <= 0)) {
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_ERROR,
+ "canvas pool alloc fail. src(%d, %d, %d) dst(%d, %d, %d).\n",
+ ge2d->src_canvas_id[0],
+ ge2d->src_canvas_id[1],
+ ge2d->src_canvas_id[2],
+ ge2d->dst_canvas_id[0],
+ ge2d->dst_canvas_id[1],
+ ge2d->dst_canvas_id[2]);
+ goto error8;
+ }
+
+ mutex_init(&ge2d->output_lock);
+ sema_init(&ge2d->sem_in, 0);
+ sema_init(&ge2d->sem_out, 0);
+
+ ge2d->running = true;
+ ge2d->task = kthread_run(aml_v4l2_ge2d_thread, ge2d,
+ "%s", "aml-v4l2-ge2d");
+ if (IS_ERR(ge2d->task)) {
+ ret = PTR_ERR(ge2d->task);
+ goto error9;
+ }
+ sched_setscheduler_nocheck(ge2d->task, SCHED_FIFO, ¶m);
+
+ *ge2d_handle = ge2d;
+
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_PRINFO,
+ "GE2D_CFG bsize:%d, wkm:%x, bm:%x, drm:%d\n",
+ ge2d->buf_size,
+ ge2d->work_mode,
+ ge2d->buffer_mode,
+ cfg->is_drm);
+
+ return 0;
+
+error9:
+ if (ge2d->src_canvas_id[0] > 0)
+ canvas_pool_map_free_canvas(ge2d->src_canvas_id[0]);
+ if (ge2d->src_canvas_id[1] > 0)
+ canvas_pool_map_free_canvas(ge2d->src_canvas_id[1]);
+ if (ge2d->src_canvas_id[2] > 0)
+ canvas_pool_map_free_canvas(ge2d->src_canvas_id[2]);
+ if (ge2d->dst_canvas_id[0] > 0)
+ canvas_pool_map_free_canvas(ge2d->dst_canvas_id[0]);
+ if (ge2d->dst_canvas_id[1] > 0)
+ canvas_pool_map_free_canvas(ge2d->dst_canvas_id[1]);
+ if (ge2d->dst_canvas_id[2] > 0)
+ canvas_pool_map_free_canvas(ge2d->dst_canvas_id[2]);
+error8:
+ vfree(ge2d->ivbpool);
+error7:
+ kfifo_free(&ge2d->input);
+error6:
+ vfree(ge2d->vfpool);
+error5:
+ kfifo_free(&ge2d->frame);
+error4:
+ vfree(ge2d->ovbpool);
+error3:
+ kfifo_free(&ge2d->output);
+error2:
+ destroy_ge2d_work_queue(ge2d->ge2d_context);
+error:
+ kfree(ge2d);
+
+ return ret;
+}
+EXPORT_SYMBOL(aml_v4l2_ge2d_init);
+
+int aml_v4l2_ge2d_destroy(struct aml_v4l2_ge2d* ge2d)
+{
+ v4l_dbg(ge2d->ctx, V4L_DEBUG_GE2D_DETAIL,
+ "ge2d destroy begin\n");
+
+ ge2d->running = false;
+ up(&ge2d->sem_in);
+ up(&ge2d->sem_out);
+ kthread_stop(ge2d->task);
+
+ destroy_ge2d_work_queue(ge2d->ge2d_context);
+ /* no more ge2d callback below this line */
+
+ kfifo_free(&ge2d->frame);
+ vfree(ge2d->vfpool);
+ kfifo_free(&ge2d->output);
+ vfree(ge2d->ovbpool);
+ kfifo_free(&ge2d->input);
+ vfree(ge2d->ivbpool);
+ mutex_destroy(&ge2d->output_lock);
+
+ canvas_pool_map_free_canvas(ge2d->src_canvas_id[0]);
+ canvas_pool_map_free_canvas(ge2d->src_canvas_id[1]);
+ canvas_pool_map_free_canvas(ge2d->src_canvas_id[2]);
+ canvas_pool_map_free_canvas(ge2d->dst_canvas_id[0]);
+ canvas_pool_map_free_canvas(ge2d->dst_canvas_id[1]);
+ canvas_pool_map_free_canvas(ge2d->dst_canvas_id[2]);
+
+ v4l_dbg(ge2d->ctx, V4L_DEBUG_GE2D_DETAIL,
+ "ge2d destroy done\n");
+
+ kfree(ge2d);
+
+ return 0;
+}
+EXPORT_SYMBOL(aml_v4l2_ge2d_destroy);
+
+static int aml_v4l2_ge2d_push_vframe(struct aml_v4l2_ge2d* ge2d, struct vframe_s *vf)
+{
+ struct aml_v4l2_ge2d_buf* in_buf;
+ struct vdec_v4l2_buffer *fb = NULL;
+
+ if (!ge2d)
+ return -EINVAL;
+
+ if (!kfifo_get(&ge2d->input, &in_buf)) {
+ v4l_dbg(ge2d->ctx, V4L_DEBUG_CODEC_ERROR,
+ "cat not get free input buffer.\n");
+ return -1;
+ }
+
+ if (vf->type & VIDTYPE_V4L_EOS)
+ in_buf->flag |= GE2D_FLAG_EOS;
+
+ v4l_dbg(ge2d->ctx, V4L_DEBUG_GE2D_BUFMGR,
+ "ge2d_push_vframe: vf:%px, idx:%d, type:%x, ts:%lld\n",
+ vf, vf->index, vf->type, vf->timestamp);
+
+ fb = (struct vdec_v4l2_buffer *)vf->v4l_mem_handle;
+ in_buf->aml_buf = container_of(fb, struct aml_video_dec_buf, frame_buffer);
+ in_buf->vf = vf;
+
+ do {
+ unsigned int dw_mode = VDEC_DW_NO_AFBC;
+ struct file *fp;
+
+ if (!dump_ge2d_input || ge2d->ctx->is_drm_mode)
+ break;
+
+ if (vdec_if_get_param(ge2d->ctx, GET_PARAM_DW_MODE, &dw_mode))
+ break;
+
+ if (dw_mode == VDEC_DW_AFBC_ONLY)
+ break;
+
+ fp = filp_open("/data/dec_dump_before.raw",
+ O_CREAT | O_RDWR | O_LARGEFILE | O_APPEND, 0600);
+ if (!IS_ERR(fp)) {
+ struct vb2_buffer *vb = &in_buf->aml_buf->vb.vb2_buf;
+
+ kernel_write(fp,vb2_plane_vaddr(vb, 0),vb->planes[0].length, 0);
+ if (in_buf->aml_buf->frame_buffer.num_planes == 2)
+ kernel_write(fp,vb2_plane_vaddr(vb, 1),
+ vb->planes[1].length, 0);
+ dump_ge2d_input--;
+ filp_close(fp, NULL);
+ }
+ } while(0);
+
+ ATRACE_COUNTER("VC_OUT_GE2D-0.receive", fb->buf_idx);
+
+ kfifo_put(&ge2d->in_done_q, in_buf);
+ up(&ge2d->sem_in);
+
+ return 0;
+}
+
+static void fill_ge2d_buf_cb(void *v4l_ctx, void *fb_ctx)
+{
+ struct aml_vcodec_ctx *ctx =
+ (struct aml_vcodec_ctx *)v4l_ctx;
+ struct vdec_v4l2_buffer *fb =
+ (struct vdec_v4l2_buffer *)fb_ctx;
+ int ret = -1;
+
+ ret = aml_v4l2_ge2d_push_vframe(ctx->ge2d, fb->vframe);
+ if (ret < 0) {
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_ERROR,
+ "ge2d push vframe err, ret: %d\n", ret);
+ }
+}
+
+static struct task_ops_s ge2d_ops = {
+ .type = TASK_TYPE_GE2D,
+ .get_vframe = ge2d_vf_get,
+ .put_vframe = ge2d_vf_put,
+ .fill_buffer = fill_ge2d_buf_cb,
+};
+
+struct task_ops_s *get_ge2d_ops(void)
+{
+ return &ge2d_ops;
+}
+EXPORT_SYMBOL(get_ge2d_ops);
+
diff --git a/drivers/amvdec_ports/aml_vcodec_ge2d.h b/drivers/amvdec_ports/aml_vcodec_ge2d.h
new file mode 100644
index 0000000..a12931d
--- /dev/null
+++ b/drivers/amvdec_ports/aml_vcodec_ge2d.h
@@ -0,0 +1,89 @@
+/*
+* Copyright (C) 2020 Amlogic, Inc. All rights reserved.
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2 of the License, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful, but WITHOUT
+* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+* more details.
+*
+* You should have received a copy of the GNU General Public License along
+* with this program; if not, write to the Free Software Foundation, Inc.,
+* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*
+* Description:
+*/
+#ifndef _AML_VCODEC_GE2D_H_
+#define _AML_VCODEC_GE2D_H_
+
+#include <linux/kfifo.h>
+#include "aml_vcodec_drv.h"
+#include "aml_vcodec_dec.h"
+
+/* define ge2d work mode. */
+#define GE2D_MODE_CONVERT_NV12 (1 << 0)
+#define GE2D_MODE_CONVERT_NV21 (1 << 1)
+#define GE2D_MODE_CONVERT_LE (1 << 2)
+#define GE2D_MODE_CONVERT_BE (1 << 3)
+#define GE2D_MODE_SEPARATE_FIELD (1 << 4)
+#define GE2D_MODE_422_TO_420 (1 << 5)
+
+#define GE2D_FRAME_SIZE 64
+
+struct aml_v4l2_ge2d_buf {
+ u32 flag;
+ struct vframe_s *vf;
+ void *caller_data;
+ struct aml_video_dec_buf *aml_buf;
+};
+
+struct aml_v4l2_ge2d {
+ struct ge2d_context_s *ge2d_context; /* handle of GE2D */
+ u32 buf_size; /* buffer size for ge2d */
+ u32 work_mode; /* enum ge2d_work_mode */
+ u32 buffer_mode;
+ struct aml_vcodec_ctx *ctx;
+
+ DECLARE_KFIFO_PTR(input, typeof(struct aml_v4l2_ge2d_buf*));
+ DECLARE_KFIFO_PTR(output, typeof(struct aml_v4l2_ge2d_buf*));
+ DECLARE_KFIFO_PTR(frame, typeof(struct vframe_s *));
+ DECLARE_KFIFO(out_done_q, struct aml_v4l2_ge2d_buf *, GE2D_FRAME_SIZE);
+ DECLARE_KFIFO(in_done_q, struct aml_v4l2_ge2d_buf *, GE2D_FRAME_SIZE);
+
+ struct vframe_s *vfpool;
+ struct aml_v4l2_ge2d_buf *ovbpool;
+ struct aml_v4l2_ge2d_buf *ivbpool;
+ struct task_struct *task;
+ bool running;
+ struct semaphore sem_in, sem_out;
+
+ /* In p to i transition, output/frame can be multi writer */
+ struct mutex output_lock;
+
+ /* for debugging */
+ /*
+ * in[0] --> ge2d <-- in[1]
+ * out[0]<-- ge2d --> out[1]
+ */
+ int in_num[2];
+ int out_num[2];
+ ulong fb_token;
+
+ int src_canvas_id[3];
+ int dst_canvas_id[3];
+};
+
+struct task_ops_s *get_ge2d_ops(void);
+
+int aml_v4l2_ge2d_init(
+ struct aml_vcodec_ctx *ctx,
+ struct aml_ge2d_cfg_infos *cfg,
+ struct aml_v4l2_ge2d** ge2d_handle);
+
+int aml_v4l2_ge2d_destroy(struct aml_v4l2_ge2d* ge2d);
+
+#endif
diff --git a/drivers/amvdec_ports/aml_vcodec_util.c b/drivers/amvdec_ports/aml_vcodec_util.c
new file mode 100644
index 0000000..54b0d06
--- /dev/null
+++ b/drivers/amvdec_ports/aml_vcodec_util.c
@@ -0,0 +1,46 @@
+/*
+* Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2 of the License, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful, but WITHOUT
+* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+* more details.
+*
+* You should have received a copy of the GNU General Public License along
+* with this program; if not, write to the Free Software Foundation, Inc.,
+* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*
+* Description:
+*/
+#include <linux/module.h>
+
+#include "aml_vcodec_drv.h"
+#include "aml_vcodec_util.h"
+
+void aml_vcodec_set_curr_ctx(struct aml_vcodec_dev *dev,
+ struct aml_vcodec_ctx *ctx)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev->irqlock, flags);
+ dev->curr_ctx = ctx;
+ spin_unlock_irqrestore(&dev->irqlock, flags);
+}
+EXPORT_SYMBOL(aml_vcodec_set_curr_ctx);
+
+struct aml_vcodec_ctx *aml_vcodec_get_curr_ctx(struct aml_vcodec_dev *dev)
+{
+ unsigned long flags;
+ struct aml_vcodec_ctx *ctx;
+
+ spin_lock_irqsave(&dev->irqlock, flags);
+ ctx = dev->curr_ctx;
+ spin_unlock_irqrestore(&dev->irqlock, flags);
+ return ctx;
+}
+EXPORT_SYMBOL(aml_vcodec_get_curr_ctx);
diff --git a/drivers/amvdec_ports/aml_vcodec_util.h b/drivers/amvdec_ports/aml_vcodec_util.h
new file mode 100644
index 0000000..96c5453
--- /dev/null
+++ b/drivers/amvdec_ports/aml_vcodec_util.h
@@ -0,0 +1,106 @@
+/*
+* Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2 of the License, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful, but WITHOUT
+* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+* more details.
+*
+* You should have received a copy of the GNU General Public License along
+* with this program; if not, write to the Free Software Foundation, Inc.,
+* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*
+* Description:
+*/
+#ifndef _AML_VCODEC_UTIL_H_
+#define _AML_VCODEC_UTIL_H_
+
+#include <linux/types.h>
+#include <linux/dma-direction.h>
+#include <linux/amlogic/media/codec_mm/codec_mm.h>
+/*
+typedef unsigned long long u64;
+typedef signed long long s64;
+typedef unsigned int u32;
+typedef unsigned short int u16;
+typedef short int s16;
+typedef unsigned char u8;
+*/
+#define CODEC_MODE(a, b, c, d)\
+ (((u8)(a) << 24) | ((u8)(b) << 16) | ((u8)(c) << 8) | (u8)(d))
+
+#define BUFF_IDX(h, i)\
+ (((ulong)(h) << 8) | (u8)(i))
+
+struct aml_vcodec_mem {
+ int index;
+ ulong addr;
+ u32 size;
+ void *vaddr;
+ u32 bytes_used;
+ u32 offset;
+ u64 timestamp;
+ u32 model;
+ ulong meta_ptr;
+ struct dma_buf *dbuf;
+};
+
+struct aml_vcodec_ctx;
+struct aml_vcodec_dev;
+
+extern u32 debug_mode;
+extern u32 mandatory_dw_mmu;
+
+#ifdef v4l_dbg
+#undef v4l_dbg
+#endif
+
+/* v4l debug define. */
+#define V4L_DEBUG_CODEC_ERROR (0)
+#define V4L_DEBUG_CODEC_PRINFO (1 << 0)
+#define V4L_DEBUG_CODEC_STATE (1 << 1)
+#define V4L_DEBUG_CODEC_BUFMGR (1 << 2)
+#define V4L_DEBUG_CODEC_INPUT (1 << 3)
+#define V4L_DEBUG_CODEC_OUTPUT (1 << 4)
+#define V4L_DEBUG_CODEC_COUNT (1 << 5)
+#define V4L_DEBUG_CODEC_PARSER (1 << 6)
+#define V4L_DEBUG_CODEC_PROT (1 << 7)
+#define V4L_DEBUG_CODEC_EXINFO (1 << 8)
+#define V4L_DEBUG_VPP_BUFMGR (1 << 9)
+#define V4L_DEBUG_VPP_DETAIL (1 << 10)
+#define V4L_DEBUG_TASK_CHAIN (1 << 11)
+#define V4L_DEBUG_GE2D_BUFMGR (1 << 12)
+#define V4L_DEBUG_GE2D_DETAIL (1 << 13)
+
+#define __v4l_dbg(h, id, fmt, args...) \
+ do { \
+ if (h) \
+ pr_info("[%d]: " fmt, id, ##args); \
+ else \
+ pr_info(fmt, ##args); \
+ } while (0)
+
+#define v4l_dbg(h, flags, fmt, args...) \
+ do { \
+ struct aml_vcodec_ctx *__ctx = (struct aml_vcodec_ctx *) h; \
+ if ((flags == V4L_DEBUG_CODEC_ERROR) || \
+ (flags == V4L_DEBUG_CODEC_PRINFO) || \
+ (debug_mode & flags)) { \
+ if (flags == V4L_DEBUG_CODEC_ERROR) { \
+ __v4l_dbg(h, __ctx->id, "[ERR]: " fmt, ##args); \
+ } else { \
+ __v4l_dbg(h, __ctx->id, fmt, ##args); \
+ } \
+ } \
+ } while (0)
+
+void aml_vcodec_set_curr_ctx(struct aml_vcodec_dev *dev,
+ struct aml_vcodec_ctx *ctx);
+struct aml_vcodec_ctx *aml_vcodec_get_curr_ctx(struct aml_vcodec_dev *dev);
+
+#endif /* _AML_VCODEC_UTIL_H_ */
diff --git a/drivers/amvdec_ports/aml_vcodec_vpp.c b/drivers/amvdec_ports/aml_vcodec_vpp.c
new file mode 100644
index 0000000..9f4e960
--- /dev/null
+++ b/drivers/amvdec_ports/aml_vcodec_vpp.c
@@ -0,0 +1,1102 @@
+/*
+* Copyright (C) 2020 Amlogic, Inc. All rights reserved.
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2 of the License, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful, but WITHOUT
+* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+* more details.
+*
+* You should have received a copy of the GNU General Public License along
+* with this program; if not, write to the Free Software Foundation, Inc.,
+* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*
+* Description:
+*/
+
+#include <linux/types.h>
+#include <linux/delay.h>
+#include <linux/videodev2.h>
+#include <uapi/linux/sched/types.h>
+#include <linux/amlogic/meson_uvm_core.h>
+
+#include "aml_vcodec_vpp.h"
+#include "aml_vcodec_adapt.h"
+#include "vdec_drv_if.h"
+#include "../common/chips/decoder_cpu_ver_info.h"
+
+#define KERNEL_ATRACE_TAG KERNEL_ATRACE_TAG_V4L2
+#include <trace/events/meson_atrace.h>
+
+#define VPP_BUF_GET_IDX(vpp_buf) (vpp_buf->aml_buf->vb.vb2_buf.index)
+#define INPUT_PORT 0
+#define OUTPUT_PORT 1
+
+extern int dump_vpp_input;
+extern int vpp_bypass_frames;
+
+static void di_release_keep_buf_wrap(void *arg)
+{
+ struct di_buffer *buf = (struct di_buffer *)arg;
+
+ v4l_dbg(0, V4L_DEBUG_VPP_BUFMGR,
+ "%s release di local buffer %px, vf:%px, comm:%s, pid:%d\n",
+ __func__ , buf, buf->vf,
+ current->comm, current->pid);
+
+ di_release_keep_buf(buf);
+
+ ATRACE_COUNTER("VC_OUT_VPP_LC-2.lc_release", buf->mng.index);
+}
+
+static int attach_DI_buffer(struct aml_v4l2_vpp_buf *vpp_buf)
+{
+ struct aml_v4l2_vpp *vpp = vpp_buf->di_buf.caller_data;
+ struct dma_buf *dma = NULL;
+ struct aml_video_dec_buf *aml_buf = NULL;
+ struct uvm_hook_mod_info u_info;
+ int ret;
+
+ aml_buf = vpp_buf->aml_buf;
+ if (!aml_buf)
+ return -EINVAL;
+
+ dma = aml_buf->vb.vb2_buf.planes[0].dbuf;
+ if (!dma || !dmabuf_is_uvm(dma)) {
+ v4l_dbg(vpp->ctx, V4L_DEBUG_CODEC_ERROR,
+ "attach_DI_buffer err\n");
+ return -EINVAL;
+ }
+
+ if (!vpp_buf->di_local_buf) {
+ v4l_dbg(vpp->ctx, V4L_DEBUG_VPP_BUFMGR,
+ "attach_DI_buffer nothing\n");
+ return 0;
+ }
+
+ if (uvm_get_hook_mod(dma, VF_PROCESS_DI)) {
+ uvm_put_hook_mod(dma, VF_PROCESS_DI);
+ v4l_dbg(vpp->ctx, V4L_DEBUG_CODEC_ERROR,
+ "attach_DI_buffer exist hook\n");
+ return -EINVAL;
+ }
+ u_info.type = VF_PROCESS_DI;
+ u_info.arg = (void *)vpp_buf->di_local_buf;
+ u_info.free = di_release_keep_buf_wrap;
+
+ ret = uvm_attach_hook_mod(dma, &u_info);
+ if (ret < 0) {
+ v4l_dbg(vpp->ctx, V4L_DEBUG_CODEC_ERROR,
+ "fail to set dmabuf DI hook\n");
+ }
+
+ ATRACE_COUNTER("VC_OUT_VPP_LC-0.lc_attach", vpp_buf->di_local_buf->mng.index);
+
+ v4l_dbg(vpp->ctx, V4L_DEBUG_VPP_BUFMGR,
+ "%s attach di local buffer %px, dbuf:%px\n",
+ __func__ , vpp_buf->di_local_buf, dma);
+
+ return ret;
+}
+
+static int detach_DI_buffer(struct aml_v4l2_vpp_buf *vpp_buf)
+{
+ struct aml_v4l2_vpp *vpp = vpp_buf->di_buf.caller_data;
+ struct dma_buf *dma = NULL;
+ struct aml_video_dec_buf *aml_buf = NULL;
+ int ret;
+
+ aml_buf = vpp_buf->aml_buf;
+ if (!aml_buf)
+ return -EINVAL;
+
+ dma = aml_buf->vb.vb2_buf.planes[0].dbuf;
+ if (!dma || !dmabuf_is_uvm(dma)) {
+ v4l_dbg(vpp->ctx, V4L_DEBUG_CODEC_ERROR,
+ "detach_DI_buffer err\n");
+ return -EINVAL;
+ }
+
+ if (!vpp_buf->di_local_buf) {
+ v4l_dbg(vpp->ctx, V4L_DEBUG_VPP_BUFMGR,
+ "detach_DI_buffer nothing\n");
+ return 0;
+ }
+
+ ATRACE_COUNTER("VC_OUT_VPP_LC-1.lc_detach", vpp_buf->di_local_buf->mng.index);
+
+ ret = uvm_detach_hook_mod(dma, VF_PROCESS_DI);
+ if (ret < 0) {
+ v4l_dbg(vpp->ctx, V4L_DEBUG_VPP_BUFMGR,
+ "fail to remove dmabuf DI hook\n");
+ }
+
+ v4l_dbg(vpp->ctx, V4L_DEBUG_VPP_BUFMGR,
+ "%s detach di local buffer %px, dbuf:%px\n",
+ __func__ , vpp_buf->di_local_buf, dma);
+
+ return ret;
+}
+
+static void release_DI_buff(struct aml_v4l2_vpp* vpp)
+{
+ struct aml_v4l2_vpp_buf *vpp_buf = NULL;
+
+ while (kfifo_get(&vpp->out_done_q, &vpp_buf)) {
+ if (vpp_buf->di_buf.private_data) {
+ di_release_keep_buf(vpp_buf->di_local_buf);
+ v4l_dbg(vpp->ctx, V4L_DEBUG_VPP_BUFMGR,
+ "%s release di local buffer %px\n",
+ __func__ , vpp_buf->di_local_buf);
+ }
+ }
+}
+
+static int is_di_input_buff_full(struct aml_v4l2_vpp *vpp)
+{
+ return ((vpp->in_num[INPUT_PORT] - vpp->in_num[OUTPUT_PORT])
+ > vpp->di_ibuf_num) ? true : false;
+}
+
+static int is_di_output_buff_full(struct aml_v4l2_vpp *vpp)
+{
+ return ((vpp->out_num[INPUT_PORT] - vpp->out_num[OUTPUT_PORT])
+ > vpp->di_obuf_num) ? true : false;
+}
+
+static enum DI_ERRORTYPE
+ v4l_vpp_fill_output_done_alloc_buffer(struct di_buffer *buf)
+{
+ struct aml_v4l2_vpp *vpp = buf->caller_data;
+ struct aml_v4l2_vpp_buf *vpp_buf = NULL;
+ struct vdec_v4l2_buffer *fb = NULL;
+ bool bypass = false;
+ bool eos = false;
+
+ if (!vpp || !vpp->ctx) {
+ pr_err("fatal %s %d vpp:%p\n",
+ __func__, __LINE__, vpp);
+ di_release_keep_buf_wrap(buf);
+ return DI_ERR_UNDEFINED;
+ }
+
+ if (vpp->ctx->is_stream_off) {
+ v4l_dbg(vpp->ctx, V4L_DEBUG_CODEC_EXINFO,
+ "vpp discard submit frame %s %d vpp:%p\n",
+ __func__, __LINE__, vpp);
+ di_release_keep_buf_wrap(buf);
+ return DI_ERR_UNDEFINED;
+ }
+
+ if (!kfifo_get(&vpp->processing, &vpp_buf)) {
+ v4l_dbg(vpp->ctx, V4L_DEBUG_CODEC_EXINFO,
+ "vpp doesn't get output %s %d vpp:%p\n",
+ __func__, __LINE__, vpp);
+ di_release_keep_buf_wrap(buf);
+ return DI_ERR_UNDEFINED;
+ }
+
+ fb = &vpp_buf->aml_buf->frame_buffer;
+ eos = (buf->flag & DI_FLAG_EOS);
+ bypass = (buf->flag & DI_FLAG_BUF_BY_PASS);
+
+ vpp_buf->di_buf.vf->timestamp = buf->vf->timestamp;
+ vpp_buf->di_buf.private_data = buf->private_data;
+ vpp_buf->di_buf.vf->vf_ext = buf->vf;
+ vpp_buf->di_buf.flag = buf->flag;
+ vpp_buf->di_buf.vf->v4l_mem_handle = (ulong)fb;
+
+ if (!eos && !bypass) {
+ vpp_buf->di_local_buf = buf;
+ vpp_buf->di_buf.vf->vf_ext = buf->vf;
+ vpp_buf->di_buf.vf->flag |= VFRAME_FLAG_CONTAIN_POST_FRAME;
+ }
+
+ kfifo_put(&vpp->out_done_q, vpp_buf);
+
+ if (vpp->is_prog)
+ kfifo_put(&vpp->input, vpp_buf->inbuf);
+
+ v4l_dbg(vpp->ctx, V4L_DEBUG_VPP_BUFMGR,
+ "vpp_output local done: idx:%d, vf:%px, ext vf:%px, idx:%d, flag(vf:%x di:%x) %s %s, ts:%lld, "
+ "in:%d, out:%d, vf:%d, in done:%d, out done:%d\n",
+ fb->buf_idx,
+ vpp_buf->di_buf.vf,
+ vpp_buf->di_buf.vf->vf_ext,
+ vpp_buf->di_buf.vf->index,
+ vpp_buf->di_buf.vf->flag,
+ buf->flag,
+ vpp->is_prog ? "P" : "I",
+ eos ? "eos" : "",
+ vpp_buf->di_buf.vf->timestamp,
+ kfifo_len(&vpp->input),
+ kfifo_len(&vpp->output),
+ kfifo_len(&vpp->frame),
+ kfifo_len(&vpp->in_done_q),
+ kfifo_len(&vpp->out_done_q));
+
+ ATRACE_COUNTER("VC_OUT_VPP-2.lc_submit", fb->buf_idx);
+
+ fb->task->submit(fb->task, TASK_TYPE_VPP);
+
+ vpp->out_num[OUTPUT_PORT]++;
+ vpp->in_num[OUTPUT_PORT]++;
+
+ return DI_ERR_NONE;
+}
+
+static enum DI_ERRORTYPE
+ v4l_vpp_empty_input_done(struct di_buffer *buf)
+{
+ struct aml_v4l2_vpp *vpp = buf->caller_data;
+ struct aml_v4l2_vpp_buf *vpp_buf;
+ struct vdec_v4l2_buffer *fb = NULL;
+ bool eos = false;
+
+ if (!vpp || !vpp->ctx) {
+ v4l_dbg(0, V4L_DEBUG_CODEC_ERROR,
+ "fatal %s %d vpp:%px\n",
+ __func__, __LINE__, vpp);
+ return DI_ERR_UNDEFINED;
+ }
+
+ if (vpp->ctx->is_stream_off) {
+ v4l_dbg(vpp->ctx, V4L_DEBUG_CODEC_EXINFO,
+ "vpp discard recycle frame %s %d vpp:%p\n",
+ __func__, __LINE__, vpp);
+ return DI_ERR_UNDEFINED;
+ }
+
+ vpp_buf = container_of(buf, struct aml_v4l2_vpp_buf, di_buf);
+ fb = &vpp_buf->aml_buf->frame_buffer;
+ eos = (buf->flag & DI_FLAG_EOS);
+
+ v4l_dbg(vpp->ctx, V4L_DEBUG_VPP_BUFMGR,
+ "vpp_input done: idx:%d, vf:%px, idx: %d, flag(vf:%x di:%x) %s %s, ts:%lld, "
+ "in:%d, out:%d, vf:%d, in done:%d, out done:%d\n",
+ fb->buf_idx,
+ buf->vf,
+ buf->vf->index,
+ buf->vf->flag,
+ buf->flag,
+ vpp->is_prog ? "P" : "I",
+ eos ? "eos" : "",
+ buf->vf->timestamp,
+ kfifo_len(&vpp->input),
+ kfifo_len(&vpp->output),
+ kfifo_len(&vpp->frame),
+ kfifo_len(&vpp->in_done_q),
+ kfifo_len(&vpp->out_done_q));
+
+ if (!vpp->is_prog) {
+ /* recycle vf only in non-bypass mode */
+ fb->task->recycle(fb->task, TASK_TYPE_VPP);
+
+ kfifo_put(&vpp->input, vpp_buf);
+ }
+
+ if (vpp->buffer_mode != BUFFER_MODE_ALLOC_BUF)
+ vpp->in_num[OUTPUT_PORT]++;
+
+ ATRACE_COUNTER("VC_IN_VPP-1.recycle", fb->buf_idx);
+
+ return DI_ERR_NONE;
+}
+
+static enum DI_ERRORTYPE
+ v4l_vpp_fill_output_done(struct di_buffer *buf)
+{
+ struct aml_v4l2_vpp *vpp = buf->caller_data;
+ struct aml_v4l2_vpp_buf *vpp_buf = NULL;
+ struct vdec_v4l2_buffer *fb = NULL;
+ bool bypass = false;
+ bool eos = false;
+
+ if (!vpp || !vpp->ctx) {
+ v4l_dbg(0, V4L_DEBUG_CODEC_ERROR,
+ "fatal %s %d vpp:%px\n",
+ __func__, __LINE__, vpp);
+ return DI_ERR_UNDEFINED;
+ }
+
+ if (vpp->ctx->is_stream_off) {
+ v4l_dbg(vpp->ctx, V4L_DEBUG_CODEC_EXINFO,
+ "vpp discard submit frame %s %d vpp:%p\n",
+ __func__, __LINE__, vpp);
+ return DI_ERR_UNDEFINED;
+ }
+
+ vpp_buf = container_of(buf, struct aml_v4l2_vpp_buf, di_buf);
+ fb = &vpp_buf->aml_buf->frame_buffer;
+ eos = (buf->flag & DI_FLAG_EOS);
+ bypass = (buf->flag & DI_FLAG_BUF_BY_PASS);
+
+ /* recovery fb handle. */
+ buf->vf->v4l_mem_handle = (ulong)fb;
+
+ kfifo_put(&vpp->out_done_q, vpp_buf);
+
+ v4l_dbg(vpp->ctx, V4L_DEBUG_VPP_BUFMGR,
+ "vpp_output done: idx:%d, vf:%px, idx:%d, flag(vf:%x di:%x) %s %s, ts:%lld, "
+ "in:%d, out:%d, vf:%d, in done:%d, out done:%d\n",
+ fb->buf_idx,
+ buf->vf,
+ buf->vf->index,
+ buf->vf->flag,
+ buf->flag,
+ vpp->is_prog ? "P" : "I",
+ eos ? "eos" : "",
+ buf->vf->timestamp,
+ kfifo_len(&vpp->input),
+ kfifo_len(&vpp->output),
+ kfifo_len(&vpp->frame),
+ kfifo_len(&vpp->in_done_q),
+ kfifo_len(&vpp->out_done_q));
+
+ ATRACE_COUNTER("VC_OUT_VPP-2.submit", fb->buf_idx);
+
+ fb->task->submit(fb->task, TASK_TYPE_VPP);
+
+ vpp->out_num[OUTPUT_PORT]++;
+
+ /* count for bypass nr */
+ if (vpp->buffer_mode == BUFFER_MODE_ALLOC_BUF)
+ vpp->in_num[OUTPUT_PORT]++;
+
+ return DI_ERR_NONE;
+}
+
+static void vpp_vf_get(void *caller, struct vframe_s **vf_out)
+{
+ struct aml_v4l2_vpp *vpp = (struct aml_v4l2_vpp *)caller;
+ struct aml_v4l2_vpp_buf *vpp_buf = NULL;
+ struct vdec_v4l2_buffer *fb = NULL;
+ struct di_buffer *buf = NULL;
+ struct vframe_s *vf = NULL;
+ bool bypass = false;
+ bool eos = false;
+
+ if (!vpp || !vpp->ctx) {
+ v4l_dbg(0, V4L_DEBUG_CODEC_ERROR,
+ "fatal %s %d vpp:%px\n",
+ __func__, __LINE__, vpp);
+ return;
+ }
+
+ if (kfifo_get(&vpp->out_done_q, &vpp_buf)) {
+ fb = &vpp_buf->aml_buf->frame_buffer;
+ buf = &vpp_buf->di_buf;
+ eos = (buf->flag & DI_FLAG_EOS);
+ bypass = (buf->flag & DI_FLAG_BUF_BY_PASS);
+ vf = buf->vf;
+
+ if (eos) {
+ v4l_dbg(vpp->ctx, V4L_DEBUG_VPP_DETAIL,
+ "%s %d got eos\n",
+ __func__, __LINE__);
+ vf->type |= VIDTYPE_V4L_EOS;
+ vf->flag = VFRAME_FLAG_EMPTY_FRAME_V4L;
+ }
+
+ if (!eos && !bypass) {
+ if (vpp->buffer_mode == BUFFER_MODE_ALLOC_BUF) {
+ attach_DI_buffer(vpp_buf);
+ }
+ }
+
+ *vf_out = vf;
+
+ ATRACE_COUNTER("VC_OUT_VPP-3.vf_get", fb->buf_idx);
+
+ v4l_dbg(vpp->ctx, V4L_DEBUG_VPP_BUFMGR,
+ "%s: vf:%px, index:%d, flag(vf:%x di:%x), ts:%lld\n",
+ __func__, vf,
+ vf->index,
+ vf->flag,
+ buf->flag,
+ vf->timestamp);
+ }
+}
+
+static void vpp_vf_put(void *caller, struct vframe_s *vf)
+{
+ struct aml_v4l2_vpp *vpp = (struct aml_v4l2_vpp *)caller;
+ struct vdec_v4l2_buffer *fb = NULL;
+ struct aml_video_dec_buf *aml_buf = NULL;
+ struct aml_v4l2_vpp_buf *vpp_buf = NULL;
+ struct di_buffer *buf = NULL;
+ bool bypass = false;
+ bool eos = false;
+
+ fb = (struct vdec_v4l2_buffer *) vf->v4l_mem_handle;
+ aml_buf = container_of(fb, struct aml_video_dec_buf, frame_buffer);
+
+
+ vpp_buf = (struct aml_v4l2_vpp_buf *) aml_buf->vpp_buf_handle;
+ buf = &vpp_buf->di_buf;
+ eos = (buf->flag & DI_FLAG_EOS);
+ bypass = (buf->flag & DI_FLAG_BUF_BY_PASS);
+
+ v4l_dbg(vpp->ctx, V4L_DEBUG_VPP_BUFMGR,
+ "%s: vf:%px, index:%d, flag(vf:%x di:%x), ts:%lld\n",
+ __func__, vf,
+ vf->index,
+ vf->flag,
+ buf->flag,
+ vf->timestamp);
+
+ ATRACE_COUNTER("VC_IN_VPP-0.vf_put", fb->buf_idx);
+
+ if (vpp->is_prog) {
+ ATRACE_COUNTER("VC_IN_VPP-1.recycle", fb->buf_idx);
+ fb->task->recycle(fb->task, TASK_TYPE_VPP);
+ }
+
+ if (!eos && !bypass) {
+ if (vpp->buffer_mode == BUFFER_MODE_ALLOC_BUF) {
+ detach_DI_buffer(vpp_buf);
+ }
+ }
+
+ mutex_lock(&vpp->output_lock);
+ kfifo_put(&vpp->frame, vf);
+ kfifo_put(&vpp->output, vpp_buf);
+ mutex_unlock(&vpp->output_lock);
+ up(&vpp->sem_out);
+}
+
+static int aml_v4l2_vpp_thread(void* param)
+{
+ struct aml_v4l2_vpp* vpp = param;
+ struct aml_vcodec_ctx *ctx = vpp->ctx;
+
+ v4l_dbg(ctx, V4L_DEBUG_VPP_DETAIL, "enter vpp thread\n");
+ while (vpp->running) {
+ struct aml_v4l2_vpp_buf *in_buf;
+ struct aml_v4l2_vpp_buf *out_buf = NULL;
+ struct vframe_s *vf_out = NULL;
+ struct vdec_v4l2_buffer *fb;
+
+ if (down_interruptible(&vpp->sem_in))
+ goto exit;
+retry:
+ if (!vpp->running)
+ break;
+
+ if (kfifo_is_empty(&vpp->output)) {
+ if (down_interruptible(&vpp->sem_out))
+ goto exit;
+ goto retry;
+ }
+
+ if ((vpp->buffer_mode == BUFFER_MODE_ALLOC_BUF) &&
+ (is_di_input_buff_full(vpp) || is_di_output_buff_full(vpp))) {
+ usleep_range(500, 550);
+ goto retry;
+ }
+
+ mutex_lock(&vpp->output_lock);
+ if (!kfifo_get(&vpp->output, &out_buf)) {
+ mutex_unlock(&vpp->output_lock);
+ v4l_dbg(ctx, 0, "vpp can not get output\n");
+ goto exit;
+ }
+ mutex_unlock(&vpp->output_lock);
+
+ /* bind v4l2 buffers */
+ if (!vpp->is_prog && !out_buf->aml_buf) {
+ struct vdec_v4l2_buffer *out;
+
+ if (!ctx->fb_ops.query(&ctx->fb_ops, &vpp->fb_token)) {
+ usleep_range(500, 550);
+ mutex_lock(&vpp->output_lock);
+ kfifo_put(&vpp->output, out_buf);
+ mutex_unlock(&vpp->output_lock);
+ goto retry;
+ }
+
+ if (ctx->fb_ops.alloc(&ctx->fb_ops, vpp->fb_token, &out, AML_FB_REQ_VPP)) {
+ usleep_range(5000, 5500);
+ mutex_lock(&vpp->output_lock);
+ kfifo_put(&vpp->output, out_buf);
+ mutex_unlock(&vpp->output_lock);
+ goto retry;
+ }
+
+ out_buf->aml_buf = container_of(out,
+ struct aml_video_dec_buf, frame_buffer);
+ out_buf->aml_buf->vpp_buf_handle = (ulong) out_buf;
+ v4l_dbg(ctx, V4L_DEBUG_VPP_BUFMGR,
+ "vpp bind buf:%d to vpp_buf:%px\n",
+ VPP_BUF_GET_IDX(out_buf), out_buf);
+
+ out->m.mem[0].bytes_used = out->m.mem[0].size;
+ out->m.mem[1].bytes_used = out->m.mem[1].size;
+ }
+
+ /* safe to pop in_buf */
+ if (!kfifo_get(&vpp->in_done_q, &in_buf)) {
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_ERROR,
+ "vpp can not get input\n");
+ goto exit;
+ }
+
+ mutex_lock(&vpp->output_lock);
+ if (!kfifo_get(&vpp->frame, &vf_out)) {
+ mutex_unlock(&vpp->output_lock);
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_ERROR,
+ "vpp can not get frame\n");
+ goto exit;
+ }
+ mutex_unlock(&vpp->output_lock);
+
+ if (!vpp->is_prog) {
+ /* submit I to DI. */
+ fb = &out_buf->aml_buf->frame_buffer;
+ fb->status = FB_ST_VPP;
+
+ memcpy(vf_out, in_buf->di_buf.vf, sizeof(*vf_out));
+ memcpy(vf_out->canvas0_config,
+ in_buf->di_buf.vf->canvas0_config,
+ 2 * sizeof(struct canvas_config_s));
+
+ vf_out->canvas0_config[0].phy_addr = fb->m.mem[0].addr;
+ if (fb->num_planes == 1)
+ vf_out->canvas0_config[1].phy_addr =
+ fb->m.mem[0].addr + fb->m.mem[0].offset;
+ else
+ vf_out->canvas0_config[1].phy_addr =
+ fb->m.mem[1].addr;
+
+ if (in_buf->di_buf.flag & DI_FLAG_EOS)
+ memset(vf_out, 0, sizeof(*vf_out));
+
+ vf_out->meta_data_size = in_buf->di_buf.vf->meta_data_size;
+ vf_out->meta_data_buf = in_buf->di_buf.vf->meta_data_buf;
+ } else {
+ /* submit P to DI. */
+ out_buf->aml_buf = in_buf->aml_buf;
+ out_buf->aml_buf->vpp_buf_handle = (ulong) out_buf;
+
+ memcpy(vf_out, in_buf->di_buf.vf, sizeof(*vf_out));
+ }
+
+ /* fill outbuf parms. */
+ out_buf->di_buf.vf = vf_out;
+ out_buf->di_buf.flag = 0;
+ out_buf->di_local_buf = NULL;
+ out_buf->di_buf.caller_data = vpp;
+
+ /* fill inbuf parms. */
+ in_buf->di_buf.caller_data = vpp;
+
+ /*
+ * HWC or SF should hold di buffres refcnt after resolution changed
+ * that might cause stuck, thus sumbit 10 frames from dec to display directly.
+ * then frames will be pushed out from these buffer queuen and
+ * recycle local buffers to DI module.
+ */
+ if (/*(ctx->vpp_cfg.res_chg) && */(vpp->is_prog) &&
+ (vpp->buffer_mode == BUFFER_MODE_ALLOC_BUF)) {
+ if (vpp->in_num[INPUT_PORT] < vpp_bypass_frames) {
+ vpp->is_bypass_p = true;
+ } else {
+ vpp->is_bypass_p = false;
+ ctx->vpp_cfg.res_chg = false;
+ }
+ }
+
+ v4l_dbg(ctx, V4L_DEBUG_VPP_BUFMGR,
+ "vpp_handle start: idx:(%d, %d), dec vf:%px/%d, vpp vf:%px/%d, iphy:%lx/%lx %dx%d ophy:%lx/%lx %dx%d, %s %s "
+ "in:%d, out:%d, vf:%d, in done:%d, out done:%d",
+ in_buf->aml_buf->frame_buffer.buf_idx,
+ out_buf->aml_buf->frame_buffer.buf_idx,
+ in_buf->di_buf.vf, in_buf->di_buf.vf->index,
+ out_buf->di_buf.vf, VPP_BUF_GET_IDX(out_buf),
+ in_buf->di_buf.vf->canvas0_config[0].phy_addr,
+ in_buf->di_buf.vf->canvas0_config[1].phy_addr,
+ in_buf->di_buf.vf->canvas0_config[0].width,
+ in_buf->di_buf.vf->canvas0_config[0].height,
+ vf_out->canvas0_config[0].phy_addr,
+ vf_out->canvas0_config[1].phy_addr,
+ vf_out->canvas0_config[0].width,
+ vf_out->canvas0_config[0].height,
+ vpp->is_prog ? "P" : "",
+ vpp->is_bypass_p ? "bypass-prog" : "",
+ kfifo_len(&vpp->input),
+ kfifo_len(&vpp->output),
+ kfifo_len(&vpp->frame),
+ kfifo_len(&vpp->in_done_q),
+ kfifo_len(&vpp->out_done_q));
+
+ if (vpp->is_bypass_p) {
+ ATRACE_COUNTER("V4L_OUT_VPP-1.direct_handle_start",
+ in_buf->aml_buf->frame_buffer.buf_idx);
+ out_buf->di_buf.flag = in_buf->di_buf.flag;
+ out_buf->di_buf.vf->vf_ext = in_buf->di_buf.vf;
+
+ v4l_vpp_fill_output_done(&out_buf->di_buf);
+ v4l_vpp_empty_input_done(&in_buf->di_buf);
+ } else {
+ if (vpp->buffer_mode == BUFFER_MODE_ALLOC_BUF) {
+ /*
+ * the flow of DI local buffer:
+ * empty input -> output done cb -> fetch processing fifo.
+ */
+ ATRACE_COUNTER("VC_OUT_VPP-1.lc_handle_start",
+ in_buf->aml_buf->frame_buffer.buf_idx);
+ out_buf->inbuf = in_buf;
+ kfifo_put(&vpp->processing, out_buf);
+
+ di_empty_input_buffer(vpp->di_handle, &in_buf->di_buf);
+ } else {
+ ATRACE_COUNTER("VC_OUT_VPP-1.fill_output_start",
+ out_buf->aml_buf->frame_buffer.buf_idx);
+ di_fill_output_buffer(vpp->di_handle, &out_buf->di_buf);
+
+ ATRACE_COUNTER("VC_OUT_VPP-1.empty_input_start",
+ in_buf->aml_buf->frame_buffer.buf_idx);
+ di_empty_input_buffer(vpp->di_handle, &in_buf->di_buf);
+ }
+ }
+ vpp->in_num[INPUT_PORT]++;
+ vpp->out_num[INPUT_PORT]++;
+ }
+exit:
+ while (!kthread_should_stop()) {
+ usleep_range(1000, 2000);
+ }
+
+ v4l_dbg(ctx, V4L_DEBUG_VPP_DETAIL, "exit vpp thread\n");
+
+ return 0;
+}
+
+int aml_v4l2_vpp_get_buf_num(u32 mode)
+{
+ if ((mode == VPP_MODE_DI) ||
+ (mode == VPP_MODE_COLOR_CONV) ||
+ (mode == VPP_MODE_NOISE_REDUC)) {
+ return 4;
+ }
+ //TODO: support more modes
+ return 2;
+}
+
+int aml_v4l2_vpp_reset(struct aml_v4l2_vpp *vpp)
+{
+ int i;
+ struct sched_param param =
+ { .sched_priority = MAX_RT_PRIO - 1 };
+
+ vpp->running = false;
+ up(&vpp->sem_in);
+ up(&vpp->sem_out);
+ kthread_stop(vpp->task);
+
+ kfifo_reset(&vpp->input);
+ kfifo_reset(&vpp->output);
+ kfifo_reset(&vpp->frame);
+ kfifo_reset(&vpp->out_done_q);
+ kfifo_reset(&vpp->in_done_q);
+ kfifo_reset(&vpp->processing);
+
+ for (i = 0 ; i < VPP_FRAME_SIZE ; i++) {
+ memset(&vpp->ivbpool[i], 0, sizeof(struct aml_v4l2_vpp_buf));
+
+ kfifo_put(&vpp->input, &vpp->ivbpool[i]);
+ }
+
+ for (i = 0 ; i < vpp->buf_size ; i++) {
+ memset(&vpp->ovbpool[i], 0, sizeof(struct aml_v4l2_vpp_buf));
+ memset(&vpp->vfpool[i], 0, sizeof(struct vframe_s));
+
+ kfifo_put(&vpp->output, &vpp->ovbpool[i]);
+ kfifo_put(&vpp->frame, &vpp->vfpool[i]);
+ }
+
+ vpp->in_num[0] = 0;
+ vpp->in_num[1] = 0;
+ vpp->out_num[0] = 0;
+ vpp->out_num[1] = 0;
+ vpp->fb_token = 0;
+ sema_init(&vpp->sem_in, 0);
+ sema_init(&vpp->sem_out, 0);
+
+ vpp->running = true;
+ vpp->task = kthread_run(aml_v4l2_vpp_thread, vpp,
+ "%s", "aml-v4l2-vpp");
+ if (IS_ERR(vpp->task)) {
+ return PTR_ERR(vpp->task);
+ }
+
+ sched_setscheduler_nocheck(vpp->task, SCHED_FIFO, ¶m);
+
+ v4l_dbg(vpp->ctx, V4L_DEBUG_CODEC_PRINFO, "vpp wrapper reset.\n");
+
+ return 0;
+
+}
+EXPORT_SYMBOL(aml_v4l2_vpp_reset);
+
+int aml_v4l2_vpp_init(
+ struct aml_vcodec_ctx *ctx,
+ struct aml_vpp_cfg_infos *cfg,
+ struct aml_v4l2_vpp** vpp_handle)
+{
+ struct di_init_parm init;
+ u32 buf_size;
+ int i, ret;
+ struct sched_param param = { .sched_priority = MAX_RT_PRIO - 1 };
+ struct aml_v4l2_vpp *vpp;
+ u32 work_mode = cfg->mode;
+
+ if (!cfg || work_mode > VPP_MODE_MAX || !ctx || !vpp_handle)
+ return -EINVAL;
+ if (cfg->fmt != V4L2_PIX_FMT_NV12 && cfg->fmt != V4L2_PIX_FMT_NV12M &&
+ cfg->fmt != V4L2_PIX_FMT_NV21 && cfg->fmt != V4L2_PIX_FMT_NV21M)
+ return -EINVAL;
+
+ vpp = kzalloc(sizeof(*vpp), GFP_KERNEL);
+ if (!vpp)
+ return -ENOMEM;
+
+ vpp->work_mode = work_mode;
+ if (vpp->work_mode >= VPP_MODE_DI_LOCAL &&
+ vpp->work_mode <= VPP_MODE_NOISE_REDUC_LOCAL)
+ vpp->buffer_mode = BUFFER_MODE_ALLOC_BUF;
+ else
+ vpp->buffer_mode = BUFFER_MODE_USE_BUF;
+
+ init.work_mode = WORK_MODE_PRE_POST;
+ init.buffer_mode = vpp->buffer_mode;
+ init.ops.fill_output_done = v4l_vpp_fill_output_done;
+ init.ops.empty_input_done = v4l_vpp_empty_input_done;
+ init.caller_data = (void *)vpp;
+
+ if (vpp->buffer_mode == BUFFER_MODE_ALLOC_BUF) {
+ init.ops.fill_output_done =
+ v4l_vpp_fill_output_done_alloc_buffer;
+ }
+
+ if (vpp->buffer_mode == BUFFER_MODE_ALLOC_BUF)
+ init.output_format = DI_OUTPUT_BY_DI_DEFINE;
+ else if ((vpp->buffer_mode == BUFFER_MODE_USE_BUF) &&
+ ((cfg->fmt == V4L2_PIX_FMT_NV21M) || (cfg->fmt == V4L2_PIX_FMT_NV21)))
+ init.output_format = DI_OUTPUT_NV21 | DI_OUTPUT_LINEAR;
+ else if ((vpp->buffer_mode == BUFFER_MODE_USE_BUF) &&
+ ((cfg->fmt == V4L2_PIX_FMT_NV12M) || (cfg->fmt == V4L2_PIX_FMT_NV12)))
+ init.output_format = DI_OUTPUT_NV12 | DI_OUTPUT_LINEAR;
+ else /* AFBC deocde case, NV12 as default */
+ init.output_format = DI_OUTPUT_NV12 | DI_OUTPUT_LINEAR;
+
+ if (cfg->is_drm)
+ init.output_format |= DI_OUTPUT_TVP;
+
+ vpp->di_handle = di_create_instance(init);
+ if (vpp->di_handle < 0) {
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_ERROR,
+ "di_create_instance fail\n");
+ ret = -EINVAL;
+ goto error;
+ }
+
+ INIT_KFIFO(vpp->input);
+ INIT_KFIFO(vpp->output);
+ INIT_KFIFO(vpp->frame);
+ INIT_KFIFO(vpp->out_done_q);
+ INIT_KFIFO(vpp->in_done_q);
+ INIT_KFIFO(vpp->processing);
+
+ vpp->ctx = ctx;
+ vpp->is_prog = cfg->is_prog;
+ vpp->is_bypass_p = cfg->is_bypass_p;
+
+ buf_size = vpp->is_prog ? 16 : cfg->buf_size;
+ vpp->buf_size = buf_size;
+
+ /* setup output fifo */
+ ret = kfifo_alloc(&vpp->output, buf_size, GFP_KERNEL);
+ if (ret) {
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_ERROR,
+ "alloc output fifo fail.\n");
+ ret = -ENOMEM;
+ goto error2;
+ }
+
+ vpp->ovbpool = vzalloc(buf_size * sizeof(*vpp->ovbpool));
+ if (!vpp->ovbpool) {
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_ERROR,
+ "alloc output vb pool fail.\n");
+ ret = -ENOMEM;
+ goto error3;
+ }
+
+ /* setup vframe fifo */
+ ret = kfifo_alloc(&vpp->frame, buf_size, GFP_KERNEL);
+ if (ret) {
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_ERROR,
+ "alloc vpp vframe fifo fail.\n");
+ ret = -ENOMEM;
+ goto error4;
+ }
+
+ vpp->vfpool = vzalloc(buf_size * sizeof(*vpp->vfpool));
+ if (!vpp->vfpool) {
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_ERROR,
+ "alloc vf pool fail.\n");
+ ret = -ENOMEM;
+ goto error5;
+ }
+
+ /* setup processing fifo */
+ ret = kfifo_alloc(&vpp->processing, buf_size, GFP_KERNEL);
+ if (ret) {
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_ERROR,
+ "alloc processing fifo fail.\n");
+ ret = -ENOMEM;
+ goto error6;
+ }
+
+ ret = kfifo_alloc(&vpp->input, VPP_FRAME_SIZE, GFP_KERNEL);
+ if (ret) {
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_ERROR,
+ "alloc input fifo fail.\n");
+ ret = -ENOMEM;
+ goto error7;
+ }
+
+ vpp->ivbpool = vzalloc(VPP_FRAME_SIZE * sizeof(*vpp->ivbpool));
+ if (!vpp->ivbpool) {
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_ERROR,
+ "alloc input vb pool fail.\n");
+ ret = -ENOMEM;
+ goto error8;
+ }
+
+ for (i = 0 ; i < VPP_FRAME_SIZE ; i++) {
+ kfifo_put(&vpp->input, &vpp->ivbpool[i]);
+ }
+
+ for (i = 0 ; i < buf_size ; i++) {
+ kfifo_put(&vpp->output, &vpp->ovbpool[i]);
+ kfifo_put(&vpp->frame, &vpp->vfpool[i]);
+ }
+
+ mutex_init(&vpp->output_lock);
+ sema_init(&vpp->sem_in, 0);
+ sema_init(&vpp->sem_out, 0);
+
+ vpp->running = true;
+ vpp->task = kthread_run(aml_v4l2_vpp_thread, vpp,
+ "aml-%s", "aml-v4l2-vpp");
+ if (IS_ERR(vpp->task)) {
+ ret = PTR_ERR(vpp->task);
+ goto error9;
+ }
+ sched_setscheduler_nocheck(vpp->task, SCHED_FIFO, ¶m);
+
+ vpp->di_ibuf_num = di_get_input_buffer_num(vpp->di_handle);
+ vpp->di_obuf_num = di_get_output_buffer_num(vpp->di_handle);
+
+ *vpp_handle = vpp;
+
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_PRINFO,
+ "vpp_wrapper init bsize:%d, di(i:%d, o:%d), wkm:%x, bm:%x, fmt:%x, drm:%d, prog:%d, byp:%d, local:%d, NR:%d\n",
+ vpp->buf_size,
+ vpp->di_ibuf_num,
+ vpp->di_obuf_num,
+ vpp->work_mode,
+ vpp->buffer_mode,
+ init.output_format,
+ cfg->is_drm,
+ cfg->is_prog,
+ cfg->is_bypass_p,
+ cfg->enable_local_buf,
+ cfg->enable_nr);
+
+ return 0;
+
+error9:
+ vfree(vpp->ivbpool);
+error8:
+ kfifo_free(&vpp->input);
+error7:
+ kfifo_free(&vpp->processing);
+error6:
+ vfree(vpp->vfpool);
+error5:
+ kfifo_free(&vpp->frame);
+error4:
+ vfree(vpp->ovbpool);
+error3:
+ kfifo_free(&vpp->output);
+error2:
+ di_destroy_instance(vpp->di_handle);
+error:
+ kfree(vpp);
+ return ret;
+}
+EXPORT_SYMBOL(aml_v4l2_vpp_init);
+
+int aml_v4l2_vpp_destroy(struct aml_v4l2_vpp* vpp)
+{
+ v4l_dbg(vpp->ctx, V4L_DEBUG_VPP_DETAIL,
+ "vpp destroy begin\n");
+ vpp->running = false;
+ up(&vpp->sem_in);
+ up(&vpp->sem_out);
+ kthread_stop(vpp->task);
+
+ di_destroy_instance(vpp->di_handle);
+ /* no more vpp callback below this line */
+
+ if (vpp->buffer_mode == BUFFER_MODE_ALLOC_BUF)
+ release_DI_buff(vpp);
+
+ kfifo_free(&vpp->processing);
+ kfifo_free(&vpp->frame);
+ vfree(vpp->vfpool);
+ kfifo_free(&vpp->output);
+ vfree(vpp->ovbpool);
+ kfifo_free(&vpp->input);
+ vfree(vpp->ivbpool);
+ mutex_destroy(&vpp->output_lock);
+ v4l_dbg(vpp->ctx, V4L_DEBUG_VPP_DETAIL,
+ "vpp_wrapper destroy done\n");
+ kfree(vpp);
+
+ return 0;
+}
+EXPORT_SYMBOL(aml_v4l2_vpp_destroy);
+
+static int aml_v4l2_vpp_push_vframe(struct aml_v4l2_vpp* vpp, struct vframe_s *vf)
+{
+ struct aml_v4l2_vpp_buf *in_buf;
+ struct vdec_v4l2_buffer *fb = NULL;
+
+ if (!vpp)
+ return -EINVAL;
+
+ if (!kfifo_get(&vpp->input, &in_buf)) {
+ v4l_dbg(vpp->ctx, V4L_DEBUG_CODEC_ERROR,
+ "cat not get free input buffer.\n");
+ return -1;
+ }
+
+#if 0 //to debug di by frame
+ if (vpp->in_num[INPUT_PORT] > 2)
+ return 0;
+ if (vpp->in_num[INPUT_PORT] == 2)
+ vf->type |= VIDTYPE_V4L_EOS;
+#endif
+
+ in_buf->di_buf.vf = vf;
+ in_buf->di_buf.flag = 0;
+ if (vf->type & VIDTYPE_V4L_EOS) {
+ u32 dw_mode = VDEC_DW_NO_AFBC;
+
+ in_buf->di_buf.flag |= DI_FLAG_EOS;
+
+ if (vdec_if_get_param(vpp->ctx, GET_PARAM_DW_MODE, &dw_mode))
+ return -1;
+
+ vf->type |= vpp->ctx->vpp_cfg.is_prog ?
+ VIDTYPE_PROGRESSIVE :
+ VIDTYPE_INTERLACE;
+
+ if (dw_mode != VDEC_DW_NO_AFBC)
+ vf->type |= VIDTYPE_COMPRESS;
+ }
+
+ fb = (struct vdec_v4l2_buffer *)vf->v4l_mem_handle;
+ in_buf->aml_buf = container_of(fb, struct aml_video_dec_buf, frame_buffer);
+
+ if (get_cpu_major_id() == AM_MESON_CPU_MAJOR_ID_T7) {
+ if (vf->canvas0_config[0].block_mode == CANVAS_BLKMODE_LINEAR) {
+ if ((vpp->ctx->output_pix_fmt != V4L2_PIX_FMT_H264) &&
+ (vpp->ctx->output_pix_fmt != V4L2_PIX_FMT_MPEG1) &&
+ (vpp->ctx->output_pix_fmt != V4L2_PIX_FMT_MPEG2) &&
+ (vpp->ctx->output_pix_fmt != V4L2_PIX_FMT_MPEG4) &&
+ (vpp->ctx->output_pix_fmt != V4L2_PIX_FMT_MJPEG)) {
+ vf->flag |= VFRAME_FLAG_VIDEO_LINEAR;
+ }
+ else {
+ if (fb->status == FB_ST_GE2D)
+ vf->flag |= VFRAME_FLAG_VIDEO_LINEAR;
+ }
+ }
+ } else {
+ if (vf->canvas0_config[0].block_mode == CANVAS_BLKMODE_LINEAR)
+ vf->flag |= VFRAME_FLAG_VIDEO_LINEAR;
+ }
+
+ v4l_dbg(vpp->ctx, V4L_DEBUG_VPP_BUFMGR,
+ "vpp_push_vframe: idx:%d, vf:%px, idx:%d, type:%x, ts:%lld\n",
+ fb->buf_idx, vf, vf->index, vf->type, vf->timestamp);
+
+ do {
+ unsigned int dw_mode = VDEC_DW_NO_AFBC;
+ struct file *fp;
+
+ if (!dump_vpp_input || vpp->ctx->is_drm_mode)
+ break;
+ if (vdec_if_get_param(vpp->ctx, GET_PARAM_DW_MODE, &dw_mode))
+ break;
+ if (dw_mode == VDEC_DW_AFBC_ONLY)
+ break;
+
+ fp = filp_open("/data/dec_dump_before.raw",
+ O_CREAT | O_RDWR | O_LARGEFILE | O_APPEND, 0600);
+ if (!IS_ERR(fp)) {
+ struct vb2_buffer *vb = &in_buf->aml_buf->vb.vb2_buf;
+
+ kernel_write(fp,vb2_plane_vaddr(vb, 0),vb->planes[0].length, 0);
+ if (in_buf->aml_buf->frame_buffer.num_planes == 2)
+ kernel_write(fp,vb2_plane_vaddr(vb, 1),
+ vb->planes[1].length, 0);
+ dump_vpp_input--;
+ filp_close(fp, NULL);
+ }
+ } while(0);
+
+ ATRACE_COUNTER("VC_OUT_VPP-0.receive", fb->buf_idx);
+
+ kfifo_put(&vpp->in_done_q, in_buf);
+ up(&vpp->sem_in);
+
+ return 0;
+}
+
+static void fill_vpp_buf_cb(void *v4l_ctx, void *fb_ctx)
+{
+ struct aml_vcodec_ctx *ctx =
+ (struct aml_vcodec_ctx *)v4l_ctx;
+ struct vdec_v4l2_buffer *fb =
+ (struct vdec_v4l2_buffer *)fb_ctx;
+ int ret = -1;
+
+ ret = aml_v4l2_vpp_push_vframe(ctx->vpp, fb->vframe);
+ if (ret < 0) {
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_ERROR,
+ "vpp push vframe err, ret: %d\n", ret);
+ }
+}
+
+static struct task_ops_s vpp_ops = {
+ .type = TASK_TYPE_VPP,
+ .get_vframe = vpp_vf_get,
+ .put_vframe = vpp_vf_put,
+ .fill_buffer = fill_vpp_buf_cb,
+};
+
+struct task_ops_s *get_vpp_ops(void)
+{
+ return &vpp_ops;
+}
+EXPORT_SYMBOL(get_vpp_ops);
+
diff --git a/drivers/amvdec_ports/aml_vcodec_vpp.h b/drivers/amvdec_ports/aml_vcodec_vpp.h
new file mode 100644
index 0000000..7a5771d
--- /dev/null
+++ b/drivers/amvdec_ports/aml_vcodec_vpp.h
@@ -0,0 +1,112 @@
+/*
+* Copyright (C) 2020 Amlogic, Inc. All rights reserved.
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2 of the License, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful, but WITHOUT
+* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+* more details.
+*
+* You should have received a copy of the GNU General Public License along
+* with this program; if not, write to the Free Software Foundation, Inc.,
+* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*
+* Description:
+*/
+#ifndef _AML_VCODEC_VPP_H_
+#define _AML_VCODEC_VPP_H_
+
+#define SUPPORT_V4L_VPP
+
+#include <linux/kfifo.h>
+#ifdef SUPPORT_V4L_VPP
+#include <linux/amlogic/media/di/di_interface.h>
+#endif
+#include "aml_vcodec_drv.h"
+#include "aml_vcodec_dec.h"
+
+enum vpp_work_mode {
+ VPP_MODE_DI,
+ VPP_MODE_COLOR_CONV,
+ VPP_MODE_NOISE_REDUC,
+ VPP_MODE_DI_LOCAL = 0x81,
+ VPP_MODE_COLOR_CONV_LOCAL = 0x82,
+ VPP_MODE_NOISE_REDUC_LOCAL = 0x83,
+ VPP_MODE_MAX = 0xff
+};
+
+#define VPP_FRAME_SIZE 64
+
+struct aml_v4l2_vpp_buf {
+#ifdef SUPPORT_V4L_VPP
+ struct di_buffer di_buf;
+ struct di_buffer *di_local_buf;
+#endif
+ struct aml_video_dec_buf *aml_buf;
+ struct aml_v4l2_vpp_buf *inbuf;
+};
+
+struct aml_v4l2_vpp {
+ int di_handle; /* handle of DI */
+ struct aml_vcodec_ctx *ctx;
+ u32 buf_size; /* buffer size for vpp */
+ u32 work_mode; /* enum vpp_work_mode */
+ u32 buffer_mode;
+
+ DECLARE_KFIFO_PTR(input, typeof(struct aml_v4l2_vpp_buf*));
+ DECLARE_KFIFO_PTR(output, typeof(struct aml_v4l2_vpp_buf*));
+ DECLARE_KFIFO_PTR(processing, typeof(struct aml_v4l2_vpp_buf*));
+ DECLARE_KFIFO_PTR(frame, typeof(struct vframe_s *));
+ DECLARE_KFIFO(out_done_q, struct aml_v4l2_vpp_buf *, VPP_FRAME_SIZE);
+ DECLARE_KFIFO(in_done_q, struct aml_v4l2_vpp_buf *, VPP_FRAME_SIZE);
+
+ struct vframe_s *vfpool;
+ struct aml_v4l2_vpp_buf *ovbpool;
+ struct aml_v4l2_vpp_buf *ivbpool;
+ struct task_struct *task;
+ bool running;
+ struct semaphore sem_in, sem_out;
+
+ /* In p to i transition, output/frame can be multi writer */
+ struct mutex output_lock;
+
+ /* for debugging */
+ /*
+ * in[0] --> vpp <-- in[1]
+ * out[0]<-- vpp --> out[1]
+ */
+ int in_num[2];
+ int out_num[2];
+ ulong fb_token;
+
+ bool is_prog;
+ bool is_bypass_p;
+ int di_ibuf_num;
+ int di_obuf_num;
+};
+
+struct task_ops_s *get_vpp_ops(void);
+
+#ifdef SUPPORT_V4L_VPP
+/* get number of buffer needed for a working mode */
+int aml_v4l2_vpp_get_buf_num(u32 mode);
+int aml_v4l2_vpp_init(
+ struct aml_vcodec_ctx *ctx,
+ struct aml_vpp_cfg_infos *cfg,
+ struct aml_v4l2_vpp** vpp_handle);
+int aml_v4l2_vpp_destroy(struct aml_v4l2_vpp* vpp);
+int aml_v4l2_vpp_reset(struct aml_v4l2_vpp *vpp);
+#else
+static inline int aml_v4l2_vpp_get_buf_num(u32 mode) { return -1; }
+static inline int aml_v4l2_vpp_init(
+ struct aml_vcodec_ctx *ctx,
+ struct aml_vpp_cfg_infos *cfg,
+ struct aml_v4l2_vpp** vpp_handle) { return -1; }
+static inline int aml_v4l2_vpp_destroy(struct aml_v4l2_vpp* vpp) { return -1; }
+#endif
+
+#endif
diff --git a/drivers/amvdec_ports/decoder/aml_h264_parser.c b/drivers/amvdec_ports/decoder/aml_h264_parser.c
new file mode 100644
index 0000000..d0d0198
--- /dev/null
+++ b/drivers/amvdec_ports/decoder/aml_h264_parser.c
@@ -0,0 +1,713 @@
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/vmalloc.h>
+#include <linux/mm.h>
+#include <linux/string.h>
+
+#include "aml_h264_parser.h"
+#include "../utils/get_bits.h"
+#include "../utils/put_bits.h"
+#include "../utils/golomb.h"
+#include "../utils/common.h"
+#include "utils.h"
+
+#define MAX_DELAYED_PIC_COUNT (16)
+#define MAX_LOG2_MAX_FRAME_NUM (12 + 4)
+#define MIN_LOG2_MAX_FRAME_NUM (4)
+#define MAX_SPS_COUNT (32)
+#define EXTENDED_SAR (255)
+
+static const struct rational h264_pixel_aspect[17] = {
+ { 0, 1 },
+ { 1, 1 },
+ { 12, 11 },
+ { 10, 11 },
+ { 16, 11 },
+ { 40, 33 },
+ { 24, 11 },
+ { 20, 11 },
+ { 32, 11 },
+ { 80, 33 },
+ { 18, 11 },
+ { 15, 11 },
+ { 64, 33 },
+ { 160, 99 },
+ { 4, 3 },
+ { 3, 2 },
+ { 2, 1 },
+};
+
+/* maximum number of MBs in the DPB for a given level */
+static const int level_max_dpb_mbs[][2] = {
+ { 10, 396 },
+ { 11, 900 },
+ { 12, 2376 },
+ { 13, 2376 },
+ { 20, 2376 },
+ { 21, 4752 },
+ { 22, 8100 },
+ { 30, 8100 },
+ { 31, 18000 },
+ { 32, 20480 },
+ { 40, 32768 },
+ { 41, 32768 },
+ { 42, 34816 },
+ { 50, 110400 },
+ { 51, 184320 },
+ { 52, 184320 },
+};
+
+static const u8 default_scaling4[2][16] = {
+ { 6, 13, 20, 28, 13, 20, 28, 32,
+ 20, 28, 32, 37, 28, 32, 37, 42},
+ { 10, 14, 20, 24, 14, 20, 24, 27,
+ 20, 24, 27, 30, 24, 27, 30, 34 }
+};
+
+static const u8 default_scaling8[2][64] = {
+ { 6, 10, 13, 16, 18, 23, 25, 27,
+ 10, 11, 16, 18, 23, 25, 27, 29,
+ 13, 16, 18, 23, 25, 27, 29, 31,
+ 16, 18, 23, 25, 27, 29, 31, 33,
+ 18, 23, 25, 27, 29, 31, 33, 36,
+ 23, 25, 27, 29, 31, 33, 36, 38,
+ 25, 27, 29, 31, 33, 36, 38, 40,
+ 27, 29, 31, 33, 36, 38, 40, 42 },
+ { 9, 13, 15, 17, 19, 21, 22, 24,
+ 13, 13, 17, 19, 21, 22, 24, 25,
+ 15, 17, 19, 21, 22, 24, 25, 27,
+ 17, 19, 21, 22, 24, 25, 27, 28,
+ 19, 21, 22, 24, 25, 27, 28, 30,
+ 21, 22, 24, 25, 27, 28, 30, 32,
+ 22, 24, 25, 27, 28, 30, 32, 33,
+ 24, 25, 27, 28, 30, 32, 33, 35 }
+};
+
+extern const u8 ff_zigzag_scan[16 + 1];
+extern const u8 ff_zigzag_direct[64];
+
+static int decode_scaling_list(struct get_bits_context *gb,
+ u8 *factors, int size,
+ const u8 *jvt_list,
+ const u8 *fallback_list)
+{
+ int i, last = 8, next = 8;
+ const u8 *scan = size == 16 ? ff_zigzag_scan : ff_zigzag_direct;
+
+ if (!get_bits1(gb)) /* matrix not written, we use the predicted one */
+ memcpy(factors, fallback_list, size * sizeof(u8));
+ else
+ for (i = 0; i < size; i++) {
+ if (next) {
+ int v = get_se_golomb(gb);
+ /*if (v < -128 || v > 127) { //JM19 has not check.
+ pr_err( "delta scale %d is invalid\n", v);
+ return -1;
+ }*/
+ next = (last + v) & 0xff;
+ }
+ if (!i && !next) { /* matrix not written, we use the preset one */
+ memcpy(factors, jvt_list, size * sizeof(u8));
+ break;
+ }
+ last = factors[scan[i]] = next ? next : last;
+ }
+ return 0;
+}
+
+/* returns non zero if the provided SPS scaling matrix has been filled */
+static int decode_scaling_matrices(struct get_bits_context *gb,
+ const struct h264_SPS_t *sps,
+ const struct h264_PPS_t *pps, int is_sps,
+ u8(*scaling_matrix4)[16],
+ u8(*scaling_matrix8)[64])
+{
+ int ret = 0;
+ int fallback_sps = !is_sps && sps->scaling_matrix_present;
+ const u8 *fallback[4] = {
+ fallback_sps ? sps->scaling_matrix4[0] : default_scaling4[0],
+ fallback_sps ? sps->scaling_matrix4[3] : default_scaling4[1],
+ fallback_sps ? sps->scaling_matrix8[0] : default_scaling8[0],
+ fallback_sps ? sps->scaling_matrix8[3] : default_scaling8[1]
+ };
+
+ if (get_bits1(gb)) {
+ ret |= decode_scaling_list(gb, scaling_matrix4[0], 16, default_scaling4[0], fallback[0]); // Intra, Y
+ ret |= decode_scaling_list(gb, scaling_matrix4[1], 16, default_scaling4[0], scaling_matrix4[0]); // Intra, Cr
+ ret |= decode_scaling_list(gb, scaling_matrix4[2], 16, default_scaling4[0], scaling_matrix4[1]); // Intra, Cb
+ ret |= decode_scaling_list(gb, scaling_matrix4[3], 16, default_scaling4[1], fallback[1]); // Inter, Y
+ ret |= decode_scaling_list(gb, scaling_matrix4[4], 16, default_scaling4[1], scaling_matrix4[3]); // Inter, Cr
+ ret |= decode_scaling_list(gb, scaling_matrix4[5], 16, default_scaling4[1], scaling_matrix4[4]); // Inter, Cb
+ if (is_sps || pps->transform_8x8_mode) {
+ ret |= decode_scaling_list(gb, scaling_matrix8[0], 64, default_scaling8[0], fallback[2]); // Intra, Y
+ ret |= decode_scaling_list(gb, scaling_matrix8[3], 64, default_scaling8[1], fallback[3]); // Inter, Y
+ if (sps->chroma_format_idc == 3) {
+ ret |= decode_scaling_list(gb, scaling_matrix8[1], 64, default_scaling8[0], scaling_matrix8[0]); // Intra, Cr
+ ret |= decode_scaling_list(gb, scaling_matrix8[4], 64, default_scaling8[1], scaling_matrix8[3]); // Inter, Cr
+ ret |= decode_scaling_list(gb, scaling_matrix8[2], 64, default_scaling8[0], scaling_matrix8[1]); // Intra, Cb
+ ret |= decode_scaling_list(gb, scaling_matrix8[5], 64, default_scaling8[1], scaling_matrix8[4]); // Inter, Cb
+ }
+ }
+ if (!ret)
+ ret = is_sps;
+ }
+
+ return ret;
+}
+
+static int decode_hrd_parameters(struct get_bits_context *gb,
+ struct h264_SPS_t *sps)
+{
+ int cpb_count, i;
+
+ cpb_count = get_ue_golomb_31(gb) + 1;
+ if (cpb_count > 32U) {
+ v4l_dbg(0, V4L_DEBUG_CODEC_ERROR,
+ "cpb_count %d invalid\n", cpb_count);
+ return -1;
+ }
+
+ get_bits(gb, 4); /* bit_rate_scale */
+ get_bits(gb, 4); /* cpb_size_scale */
+ for (i = 0; i < cpb_count; i++) {
+ get_ue_golomb_long(gb); /* bit_rate_value_minus1 */
+ get_ue_golomb_long(gb); /* cpb_size_value_minus1 */
+ get_bits1(gb); /* cbr_flag */
+ }
+
+ sps->initial_cpb_removal_delay_length = get_bits(gb, 5) + 1;
+ sps->cpb_removal_delay_length = get_bits(gb, 5) + 1;
+ sps->dpb_output_delay_length = get_bits(gb, 5) + 1;
+ sps->time_offset_length = get_bits(gb, 5);
+ sps->cpb_cnt = cpb_count;
+
+ return 0;
+}
+
+static int decode_vui_parameters(struct get_bits_context *gb, struct h264_SPS_t *sps)
+{
+ int aspect_ratio_info_present_flag;
+ u32 aspect_ratio_idc;
+
+ aspect_ratio_info_present_flag = get_bits1(gb);
+
+ if (aspect_ratio_info_present_flag) {
+ aspect_ratio_idc = get_bits(gb, 8);
+ if (aspect_ratio_idc == EXTENDED_SAR) {
+ sps->sar.num = get_bits(gb, 16);
+ sps->sar.den = get_bits(gb, 16);
+ } else if (aspect_ratio_idc < ARRAY_SIZE(h264_pixel_aspect)) {
+ sps->sar = h264_pixel_aspect[aspect_ratio_idc];
+ } else {
+ return -1;
+ }
+ } else {
+ sps->sar.num =
+ sps->sar.den = 0;
+ }
+
+ if (get_bits1(gb)) /* overscan_info_present_flag */
+ get_bits1(gb); /* overscan_appropriate_flag */
+
+ sps->video_signal_type_present_flag = get_bits1(gb);
+ if (sps->video_signal_type_present_flag) {
+ get_bits(gb, 3); /* video_format */
+ sps->full_range = get_bits1(gb); /* video_full_range_flag */
+
+ sps->colour_description_present_flag = get_bits1(gb);
+ if (sps->colour_description_present_flag) {
+ sps->color_primaries = get_bits(gb, 8); /* colour_primaries */
+ sps->color_trc = get_bits(gb, 8); /* transfer_characteristics */
+ sps->colorspace = get_bits(gb, 8); /* matrix_coefficients */
+
+ // Set invalid values to "unspecified"
+ if (!av_color_primaries_name(sps->color_primaries))
+ sps->color_primaries = AVCOL_PRI_UNSPECIFIED;
+ if (!av_color_transfer_name(sps->color_trc))
+ sps->color_trc = AVCOL_TRC_UNSPECIFIED;
+ if (!av_color_space_name(sps->colorspace))
+ sps->colorspace = AVCOL_SPC_UNSPECIFIED;
+ }
+ }
+
+ /* chroma_location_info_present_flag */
+ if (get_bits1(gb)) {
+ /* chroma_sample_location_type_top_field */
+ //avctx->chroma_sample_location = get_ue_golomb(gb) + 1;
+ get_ue_golomb(gb); /* chroma_sample_location_type_bottom_field */
+ }
+
+ if (show_bits1(gb) && get_bits_left(gb) < 10) {
+ v4l_dbg(0, V4L_DEBUG_CODEC_PARSER, "Truncated VUI\n");
+ return 0;
+ }
+
+ sps->timing_info_present_flag = get_bits1(gb);
+ if (sps->timing_info_present_flag) {
+ unsigned num_units_in_tick = get_bits_long(gb, 32);
+ unsigned time_scale = get_bits_long(gb, 32);
+ if (!num_units_in_tick || !time_scale) {
+ v4l_dbg(0, V4L_DEBUG_CODEC_PARSER,
+ "time_scale/num_units_in_tick invalid or unsupported (%u/%u)\n",
+ time_scale, num_units_in_tick);
+ sps->timing_info_present_flag = 0;
+ } else {
+ sps->num_units_in_tick = num_units_in_tick;
+ sps->time_scale = time_scale;
+ }
+ sps->fixed_frame_rate_flag = get_bits1(gb);
+ }
+
+ sps->nal_hrd_parameters_present_flag = get_bits1(gb);
+ if (sps->nal_hrd_parameters_present_flag)
+ if (decode_hrd_parameters(gb, sps) < 0)
+ return -1;
+ sps->vcl_hrd_parameters_present_flag = get_bits1(gb);
+ if (sps->vcl_hrd_parameters_present_flag)
+ if (decode_hrd_parameters(gb, sps) < 0)
+ return -1;
+ if (sps->nal_hrd_parameters_present_flag ||
+ sps->vcl_hrd_parameters_present_flag)
+ get_bits1(gb); /* low_delay_hrd_flag */
+ sps->pic_struct_present_flag = get_bits1(gb);
+ if (!get_bits_left(gb))
+ return 0;
+ sps->bitstream_restriction_flag = get_bits1(gb);
+ if (sps->bitstream_restriction_flag) {
+ get_bits1(gb); /* motion_vectors_over_pic_boundaries_flag */
+ get_ue_golomb(gb); /* max_bytes_per_pic_denom */
+ get_ue_golomb(gb); /* max_bits_per_mb_denom */
+ get_ue_golomb(gb); /* log2_max_mv_length_horizontal */
+ get_ue_golomb(gb); /* log2_max_mv_length_vertical */
+ sps->num_reorder_frames = get_ue_golomb(gb);
+ sps->max_dec_frame_buffering = get_ue_golomb(gb); /*max_dec_frame_buffering*/
+
+ if (get_bits_left(gb) < 0) {
+ sps->num_reorder_frames = 0;
+ sps->bitstream_restriction_flag = 0;
+ }
+
+ if (sps->num_reorder_frames > 16U
+ /* max_dec_frame_buffering || max_dec_frame_buffering > 16 */) {
+ v4l_dbg(0, V4L_DEBUG_CODEC_ERROR,
+ "Clipping illegal num_reorder_frames %d\n",
+ sps->num_reorder_frames);
+ sps->num_reorder_frames = 16;
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static int aml_h264_parser_sps(struct get_bits_context *gb, struct h264_SPS_t *sps)
+{
+ int ret;
+ u32 sps_id;
+ int profile_idc, level_idc, constraint_set_flags = 0;
+ int i, log2_max_frame_num_minus4;
+
+ profile_idc = get_bits(gb, 8);
+ constraint_set_flags |= get_bits1(gb) << 0; // constraint_set0_flag
+ constraint_set_flags |= get_bits1(gb) << 1; // constraint_set1_flag
+ constraint_set_flags |= get_bits1(gb) << 2; // constraint_set2_flag
+ constraint_set_flags |= get_bits1(gb) << 3; // constraint_set3_flag
+ constraint_set_flags |= get_bits1(gb) << 4; // constraint_set4_flag
+ constraint_set_flags |= get_bits1(gb) << 5; // constraint_set5_flag
+ skip_bits(gb, 2); // reserved_zero_2bits
+ level_idc = get_bits(gb, 8);
+ sps_id = get_ue_golomb_31(gb);
+
+ if (sps_id >= MAX_SPS_COUNT) {
+ v4l_dbg(0, V4L_DEBUG_CODEC_ERROR,
+ "sps_id %u out of range\n", sps_id);
+ goto fail;
+ }
+
+ sps->sps_id = sps_id;
+ sps->time_offset_length = 24;
+ sps->profile_idc = profile_idc;
+ sps->constraint_set_flags = constraint_set_flags;
+ sps->level_idc = level_idc;
+ sps->full_range = -1;
+
+ memset(sps->scaling_matrix4, 16, sizeof(sps->scaling_matrix4));
+ memset(sps->scaling_matrix8, 16, sizeof(sps->scaling_matrix8));
+ sps->scaling_matrix_present = 0;
+ sps->colorspace = 2; //AVCOL_SPC_UNSPECIFIED
+
+ if (sps->profile_idc == 100 || // High profile
+ sps->profile_idc == 110 || // High10 profile
+ sps->profile_idc == 122 || // High422 profile
+ sps->profile_idc == 244 || // High444 Predictive profile
+ sps->profile_idc == 44 || // Cavlc444 profile
+ sps->profile_idc == 83 || // Scalable Constrained High profile (SVC)
+ sps->profile_idc == 86 || // Scalable High Intra profile (SVC)
+ sps->profile_idc == 118 || // Stereo High profile (MVC)
+ sps->profile_idc == 128 || // Multiview High profile (MVC)
+ sps->profile_idc == 138 || // Multiview Depth High profile (MVCD)
+ sps->profile_idc == 144) { // old High444 profile
+ sps->chroma_format_idc = get_ue_golomb_31(gb);
+
+ if (sps->chroma_format_idc > 3U) {
+ v4l_dbg(0, V4L_DEBUG_CODEC_ERROR,
+ "chroma_format_idc %u\n", sps->chroma_format_idc);
+ goto fail;
+ } else if (sps->chroma_format_idc == 3) {
+ sps->residual_color_transform_flag = get_bits1(gb);
+ if (sps->residual_color_transform_flag) {
+ v4l_dbg(0, V4L_DEBUG_CODEC_ERROR,
+ "separate color planes are not supported\n");
+ goto fail;
+ }
+ }
+
+ sps->bit_depth_luma = get_ue_golomb(gb) + 8;
+ sps->bit_depth_chroma = get_ue_golomb(gb) + 8;
+ if (sps->bit_depth_chroma != sps->bit_depth_luma) {
+ v4l_dbg(0, V4L_DEBUG_CODEC_ERROR,
+ "Different chroma and luma bit depth\n");
+ goto fail;
+ }
+
+ if (sps->bit_depth_luma < 8 || sps->bit_depth_luma > 14 ||
+ sps->bit_depth_chroma < 8 || sps->bit_depth_chroma > 14) {
+ v4l_dbg(0, V4L_DEBUG_CODEC_ERROR,
+ "illegal bit depth value (%d, %d)\n",
+ sps->bit_depth_luma, sps->bit_depth_chroma);
+ goto fail;
+ }
+
+ sps->transform_bypass = get_bits1(gb);
+ ret = decode_scaling_matrices(gb, sps, NULL, 1,
+ sps->scaling_matrix4, sps->scaling_matrix8);
+ if (ret < 0)
+ goto fail;
+ sps->scaling_matrix_present |= ret;
+ } else {
+ sps->chroma_format_idc = 1;
+ sps->bit_depth_luma = 8;
+ sps->bit_depth_chroma = 8;
+ }
+
+ log2_max_frame_num_minus4 = get_ue_golomb(gb);
+ if (log2_max_frame_num_minus4 < MIN_LOG2_MAX_FRAME_NUM - 4 ||
+ log2_max_frame_num_minus4 > MAX_LOG2_MAX_FRAME_NUM - 4) {
+ v4l_dbg(0, V4L_DEBUG_CODEC_ERROR,
+ "log2_max_frame_num_minus4 out of range (0-12): %d\n",
+ log2_max_frame_num_minus4);
+ goto fail;
+ }
+ sps->log2_max_frame_num = log2_max_frame_num_minus4 + 4;
+
+ sps->poc_type = get_ue_golomb_31(gb);
+ if (sps->poc_type == 0) { // FIXME #define
+ u32 t = get_ue_golomb(gb);
+ if (t > 12) {
+ v4l_dbg(0, V4L_DEBUG_CODEC_ERROR,
+ "log2_max_poc_lsb (%d) is out of range\n", t);
+ goto fail;
+ }
+ sps->log2_max_poc_lsb = t + 4;
+ } else if (sps->poc_type == 1) { // FIXME #define
+ sps->delta_pic_order_always_zero_flag = get_bits1(gb);
+ sps->offset_for_non_ref_pic = get_se_golomb_long(gb);
+ sps->offset_for_top_to_bottom_field = get_se_golomb_long(gb);
+
+ sps->poc_cycle_length = get_ue_golomb(gb);
+ if ((u32)sps->poc_cycle_length >= ARRAY_SIZE(sps->offset_for_ref_frame)) {
+ v4l_dbg(0, V4L_DEBUG_CODEC_ERROR,
+ "poc_cycle_length overflow %d\n", sps->poc_cycle_length);
+ goto fail;
+ }
+
+ for (i = 0; i < sps->poc_cycle_length; i++)
+ sps->offset_for_ref_frame[i] = get_se_golomb_long(gb);
+ } else if (sps->poc_type != 2) {
+ v4l_dbg(0, V4L_DEBUG_CODEC_ERROR,
+ "illegal POC type %d\n", sps->poc_type);
+ goto fail;
+ }
+
+ sps->ref_frame_count = get_ue_golomb_31(gb);
+ if (sps->ref_frame_count > MAX_DELAYED_PIC_COUNT) {
+ v4l_dbg(0, V4L_DEBUG_CODEC_ERROR,
+ "too many reference frames %d\n", sps->ref_frame_count);
+ goto fail;
+ }
+ sps->gaps_in_frame_num_allowed_flag = get_bits1(gb);
+ sps->mb_width = get_ue_golomb(gb) + 1;
+ sps->mb_height = get_ue_golomb(gb) + 1;
+
+ sps->frame_mbs_only_flag = get_bits1(gb);
+
+ if (sps->mb_height >= INT_MAX / 2U) {
+ v4l_dbg(0, V4L_DEBUG_CODEC_ERROR, "height overflow\n");
+ goto fail;
+ }
+ sps->mb_height *= 2 - sps->frame_mbs_only_flag;
+
+ if (!sps->frame_mbs_only_flag)
+ sps->mb_aff = get_bits1(gb);
+ else
+ sps->mb_aff = 0;
+
+ if ((u32)sps->mb_width >= INT_MAX / 16 ||
+ (u32)sps->mb_height >= INT_MAX / 16) {
+ v4l_dbg(0, V4L_DEBUG_CODEC_ERROR,
+ "mb_width/height overflow\n");
+ goto fail;
+ }
+
+ sps->direct_8x8_inference_flag = get_bits1(gb);
+
+ sps->crop = get_bits1(gb);
+ if (sps->crop) {
+ u32 crop_left = get_ue_golomb(gb);
+ u32 crop_right = get_ue_golomb(gb);
+ u32 crop_top = get_ue_golomb(gb);
+ u32 crop_bottom = get_ue_golomb(gb);
+ int width = 16 * sps->mb_width;
+ int height = 16 * sps->mb_height;
+ int vsub = (sps->chroma_format_idc == 1) ? 1 : 0;
+ int hsub = (sps->chroma_format_idc == 1 || sps->chroma_format_idc == 2) ? 1 : 0;
+ int step_x = 1 << hsub;
+ int step_y = (2 - sps->frame_mbs_only_flag) << vsub;
+
+ if (crop_left > (u32)INT_MAX / 4 / step_x ||
+ crop_right > (u32)INT_MAX / 4 / step_x ||
+ crop_top > (u32)INT_MAX / 4 / step_y ||
+ crop_bottom > (u32)INT_MAX / 4 / step_y ||
+ (crop_left + crop_right ) * step_x >= width ||
+ (crop_top + crop_bottom) * step_y >= height) {
+ v4l_dbg(0, V4L_DEBUG_CODEC_ERROR,
+ "crop values invalid %u %u %u %u / %d %d\n",
+ crop_left, crop_right, crop_top, crop_bottom, width, height);
+ goto fail;
+ }
+
+ sps->crop_left = crop_left * step_x;
+ sps->crop_right = crop_right * step_x;
+ sps->crop_top = crop_top * step_y;
+ sps->crop_bottom = crop_bottom * step_y;
+ } else {
+ sps->crop_left =
+ sps->crop_right =
+ sps->crop_top =
+ sps->crop_bottom =
+ sps->crop = 0;
+ }
+
+ sps->vui_parameters_present_flag = get_bits1(gb);
+ if (sps->vui_parameters_present_flag) {
+ int ret = decode_vui_parameters(gb, sps);
+ if (ret < 0)
+ goto fail;
+ }
+
+ if (get_bits_left(gb) < 0) {
+ v4l_dbg(0, V4L_DEBUG_CODEC_ERROR,
+ "Overread %s by %d bits\n",
+ sps->vui_parameters_present_flag ? "VUI" : "SPS", -get_bits_left(gb));
+ /*goto out;*/
+ }
+
+#if 0
+ /* if the maximum delay is not stored in the SPS, derive it based on the level */
+ if (!sps->bitstream_restriction_flag && sps->ref_frame_count) {
+ sps->num_reorder_frames = MAX_DELAYED_PIC_COUNT - 1;
+ for (i = 0; i < ARRAY_SIZE(level_max_dpb_mbs); i++) {
+ if (level_max_dpb_mbs[i][0] == sps->level_idc) {
+ sps->num_reorder_frames =
+ MIN(level_max_dpb_mbs[i][1] / (sps->mb_width * sps->mb_height),
+ sps->num_reorder_frames);
+ break;
+ }
+ }
+ }
+#endif
+
+ sps->num_reorder_frames = MAX_DELAYED_PIC_COUNT - 1;
+ for (i = 0; i < ARRAY_SIZE(level_max_dpb_mbs); i++) {
+ if (level_max_dpb_mbs[i][0] == sps->level_idc) {
+ sps->num_reorder_frames =
+ MIN(level_max_dpb_mbs[i][1] / (sps->mb_width * sps->mb_height),
+ sps->num_reorder_frames);
+ sps->num_reorder_frames += 1;
+ if (sps->max_dec_frame_buffering > sps->num_reorder_frames)
+ sps->num_reorder_frames = sps->max_dec_frame_buffering;
+ break;
+ }
+ }
+
+ if ((sps->bitstream_restriction_flag) &&
+ (sps->max_dec_frame_buffering <
+ sps->num_reorder_frames)) {
+ sps->num_reorder_frames = sps->max_dec_frame_buffering;
+ v4l_dbg(0, V4L_DEBUG_CODEC_PARSER,
+ "set reorder_pic_num to %d\n",
+ sps->num_reorder_frames);
+ }
+
+ if (!sps->sar.den)
+ sps->sar.den = 1;
+/*out:*/
+ if (1) {
+ static const char csp[4][5] = { "Gray", "420", "422", "444" };
+ v4l_dbg(0, V4L_DEBUG_CODEC_PARSER,
+ "sps:%u profile:%d/%d poc:%d ref:%d %dx%d %s %s crop:%u/%u/%u/%u %s %s %d/%d b%d reo:%d\n",
+ sps_id, sps->profile_idc, sps->level_idc,
+ sps->poc_type,
+ sps->ref_frame_count,
+ sps->mb_width, sps->mb_height,
+ sps->frame_mbs_only_flag ? "FRM" : (sps->mb_aff ? "MB-AFF" : "PIC-AFF"),
+ sps->direct_8x8_inference_flag ? "8B8" : "",
+ sps->crop_left, sps->crop_right,
+ sps->crop_top, sps->crop_bottom,
+ sps->vui_parameters_present_flag ? "VUI" : "",
+ csp[sps->chroma_format_idc],
+ sps->timing_info_present_flag ? sps->num_units_in_tick : 0,
+ sps->timing_info_present_flag ? sps->time_scale : 0,
+ sps->bit_depth_luma,
+ sps->bitstream_restriction_flag ? sps->num_reorder_frames : -1);
+ }
+
+ return 0;
+
+fail:
+ return -1;
+}
+
+static const char *h264_nal_type_name[32] = {
+ "Unspecified 0", //H264_NAL_UNSPECIFIED
+ "Coded slice of a non-IDR picture", // H264_NAL_SLICE
+ "Coded slice data partition A", // H264_NAL_DPA
+ "Coded slice data partition B", // H264_NAL_DPB
+ "Coded slice data partition C", // H264_NAL_DPC
+ "IDR", // H264_NAL_IDR_SLICE
+ "SEI", // H264_NAL_SEI
+ "SPS", // H264_NAL_SPS
+ "PPS", // H264_NAL_PPS
+ "AUD", // H264_NAL_AUD
+ "End of sequence", // H264_NAL_END_SEQUENCE
+ "End of stream", // H264_NAL_END_STREAM
+ "Filler data", // H264_NAL_FILLER_DATA
+ "SPS extension", // H264_NAL_SPS_EXT
+ "Prefix", // H264_NAL_PREFIX
+ "Subset SPS", // H264_NAL_SUB_SPS
+ "Depth parameter set", // H264_NAL_DPS
+ "Reserved 17", // H264_NAL_RESERVED17
+ "Reserved 18", // H264_NAL_RESERVED18
+ "Auxiliary coded picture without partitioning", // H264_NAL_AUXILIARY_SLICE
+ "Slice extension", // H264_NAL_EXTEN_SLICE
+ "Slice extension for a depth view or a 3D-AVC texture view", // H264_NAL_DEPTH_EXTEN_SLICE
+ "Reserved 22", // H264_NAL_RESERVED22
+ "Reserved 23", // H264_NAL_RESERVED23
+ "Unspecified 24", // H264_NAL_UNSPECIFIED24
+ "Unspecified 25", // H264_NAL_UNSPECIFIED25
+ "Unspecified 26", // H264_NAL_UNSPECIFIED26
+ "Unspecified 27", // H264_NAL_UNSPECIFIED27
+ "Unspecified 28", // H264_NAL_UNSPECIFIED28
+ "Unspecified 29", // H264_NAL_UNSPECIFIED29
+ "Unspecified 30", // H264_NAL_UNSPECIFIED30
+ "Unspecified 31", // H264_NAL_UNSPECIFIED31
+};
+
+static const char *h264_nal_unit_name(int nal_type)
+{
+ return h264_nal_type_name[nal_type];
+}
+
+static int decode_extradata_ps(u8 *data, int size, struct h264_param_sets *ps)
+{
+ int ret = 0;
+ struct get_bits_context gb;
+ u32 src_len, rbsp_size = 0;
+ u8 *rbsp_buf = NULL;
+ int ref_idc, nalu_pos;
+ u32 nal_type;
+ u8 *p = data;
+ u32 len = size;
+
+ nalu_pos = find_start_code(p, len);
+ if (nalu_pos < 0)
+ return -1;
+
+ src_len = calc_nal_len(p + nalu_pos, size - nalu_pos);
+ rbsp_buf = nal_unit_extract_rbsp(p + nalu_pos, src_len, &rbsp_size);
+ if (rbsp_buf == NULL)
+ return -ENOMEM;
+
+ ret = init_get_bits8(&gb, rbsp_buf, rbsp_size);
+ if (ret < 0)
+ goto out;
+
+ if (get_bits1(&gb) != 0) {
+ ret = -1;
+ v4l_dbg(0, V4L_DEBUG_CODEC_ERROR,
+ "invalid h264 data,return!\n");
+ goto out;
+ }
+
+ ref_idc = get_bits(&gb, 2);
+ nal_type = get_bits(&gb, 5);
+
+ v4l_dbg(0, V4L_DEBUG_CODEC_PARSER,
+ "nal_unit_type: %d(%s), nal_ref_idc: %d\n",
+ nal_type, h264_nal_unit_name(nal_type), ref_idc);
+
+ switch (nal_type) {
+ case H264_NAL_SPS:
+ ret = aml_h264_parser_sps(&gb, &ps->sps);
+ if (ret < 0)
+ goto out;
+ ps->sps_parsed = true;
+ break;
+ /*case H264_NAL_PPS:
+ ret = ff_h264_decode_picture_parameter_set(&gb, &ps->pps, rbsp_size);
+ if (ret < 0)
+ goto fail;
+ ps->pps_parsed = true;
+ break;*/
+ default:
+ v4l_dbg(0, V4L_DEBUG_CODEC_ERROR,
+ "Unsupport parser nal type (%s).\n",
+ h264_nal_unit_name(nal_type));
+ break;
+ }
+
+out:
+ vfree(rbsp_buf);
+
+ return ret;
+}
+
+int h264_decode_extradata_ps(u8 *buf, int size, struct h264_param_sets *ps)
+{
+ int ret = 0, i = 0, j = 0;
+ u8 *p = buf;
+ int len = size;
+
+ for (i = 4; i < size; i++) {
+ j = find_start_code(p, len);
+ if (j > 0) {
+ len = size - (p - buf);
+ ret = decode_extradata_ps(p, len, ps);
+ if (ret) {
+ v4l_dbg(0, V4L_DEBUG_CODEC_ERROR,
+ "parse extra data failed. err: %d\n", ret);
+ return ret;
+ }
+
+ if (ps->sps_parsed)
+ break;
+
+ p += j;
+ }
+ p++;
+ }
+
+ return ret;
+}
+
+
diff --git a/drivers/amvdec_ports/decoder/aml_h264_parser.h b/drivers/amvdec_ports/decoder/aml_h264_parser.h
new file mode 100644
index 0000000..def00dd
--- /dev/null
+++ b/drivers/amvdec_ports/decoder/aml_h264_parser.h
@@ -0,0 +1,210 @@
+/*
+ * drivers/amvdec_ports/decoder/aml_h264_parser.h
+ *
+ * Copyright (C) 2015 Amlogic, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#ifndef AML_H264_PARSER_H
+#define AML_H264_PARSER_H
+
+#include "../aml_vcodec_drv.h"
+#ifdef CONFIG_AMLOGIC_MEDIA_V4L_SOFTWARE_PARSER
+#include "../utils/pixfmt.h"
+#endif
+
+#define QP_MAX_NUM (51 + 6 * 6) // The maximum supported qp
+
+/* NAL unit types */
+enum {
+ H264_NAL_SLICE = 1,
+ H264_NAL_DPA = 2,
+ H264_NAL_DPB = 3,
+ H264_NAL_DPC = 4,
+ H264_NAL_IDR_SLICE = 5,
+ H264_NAL_SEI = 6,
+ H264_NAL_SPS = 7,
+ H264_NAL_PPS = 8,
+ H264_NAL_AUD = 9,
+ H264_NAL_END_SEQUENCE = 10,
+ H264_NAL_END_STREAM = 11,
+ H264_NAL_FILLER_DATA = 12,
+ H264_NAL_SPS_EXT = 13,
+ H264_NAL_AUXILIARY_SLICE = 19,
+};
+
+enum {
+ // 7.4.2.1.1: seq_parameter_set_id is in [0, 31].
+ H264_MAX_SPS_COUNT = 32,
+ // 7.4.2.2: pic_parameter_set_id is in [0, 255].
+ H264_MAX_PPS_COUNT = 256,
+
+ // A.3: MaxDpbFrames is bounded above by 16.
+ H264_MAX_DPB_FRAMES = 16,
+ // 7.4.2.1.1: max_num_ref_frames is in [0, MaxDpbFrames], and
+ // each reference frame can have two fields.
+ H264_MAX_REFS = 2 * H264_MAX_DPB_FRAMES,
+
+ // 7.4.3.1: modification_of_pic_nums_idc is not equal to 3 at most
+ // num_ref_idx_lN_active_minus1 + 1 times (that is, once for each
+ // possible reference), then equal to 3 once.
+ H264_MAX_RPLM_COUNT = H264_MAX_REFS + 1,
+
+ // 7.4.3.3: in the worst case, we begin with a full short-term
+ // reference picture list. Each picture in turn is moved to the
+ // long-term list (type 3) and then discarded from there (type 2).
+ // Then, we set the length of the long-term list (type 4), mark
+ // the current picture as long-term (type 6) and terminate the
+ // process (type 0).
+ H264_MAX_MMCO_COUNT = H264_MAX_REFS * 2 + 3,
+
+ // A.2.1, A.2.3: profiles supporting FMO constrain
+ // num_slice_groups_minus1 to be in [0, 7].
+ H264_MAX_SLICE_GROUPS = 8,
+
+ // E.2.2: cpb_cnt_minus1 is in [0, 31].
+ H264_MAX_CPB_CNT = 32,
+
+ // A.3: in table A-1 the highest level allows a MaxFS of 139264.
+ H264_MAX_MB_PIC_SIZE = 139264,
+ // A.3.1, A.3.2: PicWidthInMbs and PicHeightInMbs are constrained
+ // to be not greater than sqrt(MaxFS * 8). Hence height/width are
+ // bounded above by sqrt(139264 * 8) = 1055.5 macroblocks.
+ H264_MAX_MB_WIDTH = 1055,
+ H264_MAX_MB_HEIGHT = 1055,
+ H264_MAX_WIDTH = H264_MAX_MB_WIDTH * 16,
+ H264_MAX_HEIGHT = H264_MAX_MB_HEIGHT * 16,
+};
+
+/**
+ * Rational number (pair of numerator and denominator).
+ */
+struct rational{
+ int num; ///< Numerator
+ int den; ///< Denominator
+};
+
+/**
+ * Sequence parameter set
+ */
+struct h264_SPS_t {
+ u32 sps_id;
+ int profile_idc;
+ int level_idc;
+ int chroma_format_idc;
+ int transform_bypass; ///< qpprime_y_zero_transform_bypass_flag
+ int log2_max_frame_num; ///< log2_max_frame_num_minus4 + 4
+ int poc_type; ///< pic_order_cnt_type
+ int log2_max_poc_lsb; ///< log2_max_pic_order_cnt_lsb_minus4
+ int delta_pic_order_always_zero_flag;
+ int offset_for_non_ref_pic;
+ int offset_for_top_to_bottom_field;
+ int poc_cycle_length; ///< num_ref_frames_in_pic_order_cnt_cycle
+ int ref_frame_count; ///< num_ref_frames
+ int gaps_in_frame_num_allowed_flag;
+ int mb_width; ///< pic_width_in_mbs_minus1 + 1
+ ///< (pic_height_in_map_units_minus1 + 1) * (2 - frame_mbs_only_flag)
+ int mb_height;
+ int frame_mbs_only_flag;
+ int mb_aff; ///< mb_adaptive_frame_field_flag
+ int direct_8x8_inference_flag;
+ int crop; ///< frame_cropping_flag
+
+ /* those 4 are already in luma samples */
+ u32 crop_left; ///< frame_cropping_rect_left_offset
+ u32 crop_right; ///< frame_cropping_rect_right_offset
+ u32 crop_top; ///< frame_cropping_rect_top_offset
+ u32 crop_bottom; ///< frame_cropping_rect_bottom_offset
+ int vui_parameters_present_flag;
+ struct rational sar;
+ int video_signal_type_present_flag;
+ int full_range;
+ int colour_description_present_flag;
+#ifdef CONFIG_AMLOGIC_MEDIA_V4L_SOFTWARE_PARSER
+ enum AVColorPrimaries color_primaries;
+ enum AVColorTransferCharacteristic color_trc;
+ enum AVColorSpace colorspace;
+#endif
+ int timing_info_present_flag;
+ u32 num_units_in_tick;
+ u32 time_scale;
+ int fixed_frame_rate_flag;
+ int32_t offset_for_ref_frame[256];
+ int bitstream_restriction_flag;
+ int num_reorder_frames;
+ int max_dec_frame_buffering;
+ int scaling_matrix_present;
+ u8 scaling_matrix4[6][16];
+ u8 scaling_matrix8[6][64];
+ int nal_hrd_parameters_present_flag;
+ int vcl_hrd_parameters_present_flag;
+ int pic_struct_present_flag;
+ int time_offset_length;
+ int cpb_cnt; ///< See H.264 E.1.2
+ int initial_cpb_removal_delay_length; ///< initial_cpb_removal_delay_length_minus1 + 1
+ int cpb_removal_delay_length; ///< cpb_removal_delay_length_minus1 + 1
+ int dpb_output_delay_length; ///< dpb_output_delay_length_minus1 + 1
+ int bit_depth_luma; ///< bit_depth_luma_minus8 + 8
+ int bit_depth_chroma; ///< bit_depth_chroma_minus8 + 8
+ int residual_color_transform_flag; ///< residual_colour_transform_flag
+ int constraint_set_flags; ///< constraint_set[0-3]_flag
+} ;
+
+/**
+ * Picture parameter set
+ */
+struct h264_PPS_t {
+ u32 sps_id;
+ int cabac; ///< entropy_coding_mode_flag
+ int pic_order_present; ///< pic_order_present_flag
+ int slice_group_count; ///< num_slice_groups_minus1 + 1
+ int mb_slice_group_map_type;
+ u32 ref_count[2]; ///< num_ref_idx_l0/1_active_minus1 + 1
+ int weighted_pred; ///< weighted_pred_flag
+ int weighted_bipred_idc;
+ int init_qp; ///< pic_init_qp_minus26 + 26
+ int init_qs; ///< pic_init_qs_minus26 + 26
+ int chroma_qp_index_offset[2];
+ int deblocking_filter_parameters_present; ///< deblocking_filter_parameters_present_flag
+ int constrained_intra_pred; ///< constrained_intra_pred_flag
+ int redundant_pic_cnt_present; ///< redundant_pic_cnt_present_flag
+ int transform_8x8_mode; ///< transform_8x8_mode_flag
+ u8 scaling_matrix4[6][16];
+ u8 scaling_matrix8[6][64];
+ u8 chroma_qp_table[2][87+1]; ///< pre-scaled (with chroma_qp_index_offset) version of qp_table
+ int chroma_qp_diff;
+ u8 data[4096];
+ int data_size;
+
+ u32 dequant4_buffer[6][87 + 1][16];
+ u32 dequant8_buffer[6][87 + 1][64];
+ u32(*dequant4_coeff[6])[16];
+ u32(*dequant8_coeff[6])[64];
+} ;
+
+struct h264_param_sets {
+ bool sps_parsed;
+ bool pps_parsed;
+ struct h264_SPS_t sps;
+ struct h264_PPS_t pps;
+};
+
+
+#ifdef CONFIG_AMLOGIC_MEDIA_V4L_SOFTWARE_PARSER
+int h264_decode_extradata_ps(u8 *data, int size, struct h264_param_sets *ps);
+#else
+inline int h264_decode_extradata_ps(u8 *data, int size, struct h264_param_sets *ps) { return -1; }
+#endif
+
+#endif /* AML_H264_PARSER_H */
+
diff --git a/drivers/amvdec_ports/decoder/aml_hevc_parser.c b/drivers/amvdec_ports/decoder/aml_hevc_parser.c
new file mode 100644
index 0000000..24977a8
--- /dev/null
+++ b/drivers/amvdec_ports/decoder/aml_hevc_parser.c
@@ -0,0 +1,1282 @@
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/vmalloc.h>
+#include <linux/mm.h>
+#include <linux/string.h>
+
+#include "aml_hevc_parser.h"
+#include "../utils/get_bits.h"
+#include "../utils/put_bits.h"
+#include "../utils/golomb.h"
+#include "../utils/common.h"
+#include "utils.h"
+
+const u8 ff_hevc_diag_scan4x4_x[16] = {
+ 0, 0, 1, 0,
+ 1, 2, 0, 1,
+ 2, 3, 1, 2,
+ 3, 2, 3, 3,
+};
+
+const u8 ff_hevc_diag_scan4x4_y[16] = {
+ 0, 1, 0, 2,
+ 1, 0, 3, 2,
+ 1, 0, 3, 2,
+ 1, 3, 2, 3,
+};
+
+const u8 ff_hevc_diag_scan8x8_x[64] = {
+ 0, 0, 1, 0,
+ 1, 2, 0, 1,
+ 2, 3, 0, 1,
+ 2, 3, 4, 0,
+ 1, 2, 3, 4,
+ 5, 0, 1, 2,
+ 3, 4, 5, 6,
+ 0, 1, 2, 3,
+ 4, 5, 6, 7,
+ 1, 2, 3, 4,
+ 5, 6, 7, 2,
+ 3, 4, 5, 6,
+ 7, 3, 4, 5,
+ 6, 7, 4, 5,
+ 6, 7, 5, 6,
+ 7, 6, 7, 7,
+};
+
+const u8 ff_hevc_diag_scan8x8_y[64] = {
+ 0, 1, 0, 2,
+ 1, 0, 3, 2,
+ 1, 0, 4, 3,
+ 2, 1, 0, 5,
+ 4, 3, 2, 1,
+ 0, 6, 5, 4,
+ 3, 2, 1, 0,
+ 7, 6, 5, 4,
+ 3, 2, 1, 0,
+ 7, 6, 5, 4,
+ 3, 2, 1, 7,
+ 6, 5, 4, 3,
+ 2, 7, 6, 5,
+ 4, 3, 7, 6,
+ 5, 4, 7, 6,
+ 5, 7, 6, 7,
+};
+
+static const u8 default_scaling_list_intra[] = {
+ 16, 16, 16, 16, 17, 18, 21, 24,
+ 16, 16, 16, 16, 17, 19, 22, 25,
+ 16, 16, 17, 18, 20, 22, 25, 29,
+ 16, 16, 18, 21, 24, 27, 31, 36,
+ 17, 17, 20, 24, 30, 35, 41, 47,
+ 18, 19, 22, 27, 35, 44, 54, 65,
+ 21, 22, 25, 31, 41, 54, 70, 88,
+ 24, 25, 29, 36, 47, 65, 88, 115
+};
+
+static const u8 default_scaling_list_inter[] = {
+ 16, 16, 16, 16, 17, 18, 20, 24,
+ 16, 16, 16, 17, 18, 20, 24, 25,
+ 16, 16, 17, 18, 20, 24, 25, 28,
+ 16, 17, 18, 20, 24, 25, 28, 33,
+ 17, 18, 20, 24, 25, 28, 33, 41,
+ 18, 20, 24, 25, 28, 33, 41, 54,
+ 20, 24, 25, 28, 33, 41, 54, 71,
+ 24, 25, 28, 33, 41, 54, 71, 91
+};
+
+static const struct AVRational vui_sar[] = {
+ { 0, 1 },
+ { 1, 1 },
+ { 12, 11 },
+ { 10, 11 },
+ { 16, 11 },
+ { 40, 33 },
+ { 24, 11 },
+ { 20, 11 },
+ { 32, 11 },
+ { 80, 33 },
+ { 18, 11 },
+ { 15, 11 },
+ { 64, 33 },
+ { 160, 99 },
+ { 4, 3 },
+ { 3, 2 },
+ { 2, 1 },
+};
+
+static const u8 hevc_sub_width_c[] = {
+ 1, 2, 2, 1
+};
+
+static const u8 hevc_sub_height_c[] = {
+ 1, 2, 1, 1
+};
+
+static int decode_profile_tier_level(struct get_bits_context *gb, struct PTLCommon *ptl)
+{
+ int i;
+
+ if (get_bits_left(gb) < 2+1+5 + 32 + 4 + 16 + 16 + 12)
+ return -1;
+
+ ptl->profile_space = get_bits(gb, 2);
+ ptl->tier_flag = get_bits1(gb);
+ ptl->profile_idc = get_bits(gb, 5);
+ if (ptl->profile_idc == FF_PROFILE_HEVC_MAIN)
+ v4l_dbg(0, V4L_DEBUG_CODEC_PARSER, "Main profile bitstream\n");
+ else if (ptl->profile_idc == FF_PROFILE_HEVC_MAIN_10)
+ v4l_dbg(0, V4L_DEBUG_CODEC_PARSER, "Main 10 profile bitstream\n");
+ else if (ptl->profile_idc == FF_PROFILE_HEVC_MAIN_STILL_PICTURE)
+ v4l_dbg(0, V4L_DEBUG_CODEC_PARSER, "Main Still Picture profile bitstream\n");
+ else if (ptl->profile_idc == FF_PROFILE_HEVC_REXT)
+ v4l_dbg(0, V4L_DEBUG_CODEC_PARSER, "Range Extension profile bitstream\n");
+ else
+ v4l_dbg(0, V4L_DEBUG_CODEC_PARSER, "Unknown HEVC profile: %d\n", ptl->profile_idc);
+
+ for (i = 0; i < 32; i++) {
+ ptl->profile_compatibility_flag[i] = get_bits1(gb);
+
+ if (ptl->profile_idc == 0 && i > 0 && ptl->profile_compatibility_flag[i])
+ ptl->profile_idc = i;
+ }
+ ptl->progressive_source_flag = get_bits1(gb);
+ ptl->interlaced_source_flag = get_bits1(gb);
+ ptl->non_packed_constraint_flag = get_bits1(gb);
+ ptl->frame_only_constraint_flag = get_bits1(gb);
+
+ skip_bits(gb, 16); // XXX_reserved_zero_44bits[0..15]
+ skip_bits(gb, 16); // XXX_reserved_zero_44bits[16..31]
+ skip_bits(gb, 12); // XXX_reserved_zero_44bits[32..43]
+
+ return 0;
+}
+
+static int parse_ptl(struct get_bits_context *gb, struct PTL *ptl, int max_num_sub_layers)
+{
+ int i;
+ if (decode_profile_tier_level(gb, &ptl->general_ptl) < 0 ||
+ get_bits_left(gb) < 8 + (8*2 * (max_num_sub_layers - 1 > 0))) {
+ v4l_dbg(0, V4L_DEBUG_CODEC_ERROR, "PTL information too short\n");
+ return -1;
+ }
+
+ ptl->general_ptl.level_idc = get_bits(gb, 8);
+
+ for (i = 0; i < max_num_sub_layers - 1; i++) {
+ ptl->sub_layer_profile_present_flag[i] = get_bits1(gb);
+ ptl->sub_layer_level_present_flag[i] = get_bits1(gb);
+ }
+
+ if (max_num_sub_layers - 1> 0)
+ for (i = max_num_sub_layers - 1; i < 8; i++)
+ skip_bits(gb, 2); // reserved_zero_2bits[i]
+ for (i = 0; i < max_num_sub_layers - 1; i++) {
+ if (ptl->sub_layer_profile_present_flag[i] &&
+ decode_profile_tier_level(gb, &ptl->sub_layer_ptl[i]) < 0) {
+ v4l_dbg(0, V4L_DEBUG_CODEC_ERROR, "PTL information for sublayer %i too short\n", i);
+ return -1;
+ }
+ if (ptl->sub_layer_level_present_flag[i]) {
+ if (get_bits_left(gb) < 8) {
+ v4l_dbg(0, V4L_DEBUG_CODEC_ERROR, "Not enough data for sublayer %i level_idc\n", i);
+ return -1;
+ } else
+ ptl->sub_layer_ptl[i].level_idc = get_bits(gb, 8);
+ }
+ }
+
+ return 0;
+}
+
+static void decode_sublayer_hrd(struct get_bits_context *gb,
+ u32 nb_cpb, int subpic_params_present)
+{
+ int i;
+
+ for (i = 0; i < nb_cpb; i++) {
+ get_ue_golomb_long(gb); // bit_rate_value_minus1
+ get_ue_golomb_long(gb); // cpb_size_value_minus1
+
+ if (subpic_params_present) {
+ get_ue_golomb_long(gb); // cpb_size_du_value_minus1
+ get_ue_golomb_long(gb); // bit_rate_du_value_minus1
+ }
+ skip_bits1(gb); // cbr_flag
+ }
+}
+
+static int decode_hrd(struct get_bits_context *gb,
+ int common_inf_present, int max_sublayers)
+{
+ int nal_params_present = 0, vcl_params_present = 0;
+ int subpic_params_present = 0;
+ int i;
+
+ if (common_inf_present) {
+ nal_params_present = get_bits1(gb);
+ vcl_params_present = get_bits1(gb);
+
+ if (nal_params_present || vcl_params_present) {
+ subpic_params_present = get_bits1(gb);
+
+ if (subpic_params_present) {
+ skip_bits(gb, 8); // tick_divisor_minus2
+ skip_bits(gb, 5); // du_cpb_removal_delay_increment_length_minus1
+ skip_bits(gb, 1); // sub_pic_cpb_params_in_pic_timing_sei_flag
+ skip_bits(gb, 5); // dpb_output_delay_du_length_minus1
+ }
+
+ skip_bits(gb, 4); // bit_rate_scale
+ skip_bits(gb, 4); // cpb_size_scale
+
+ if (subpic_params_present)
+ skip_bits(gb, 4); // cpb_size_du_scale
+
+ skip_bits(gb, 5); // initial_cpb_removal_delay_length_minus1
+ skip_bits(gb, 5); // au_cpb_removal_delay_length_minus1
+ skip_bits(gb, 5); // dpb_output_delay_length_minus1
+ }
+ }
+
+ for (i = 0; i < max_sublayers; i++) {
+ int low_delay = 0;
+ u32 nb_cpb = 1;
+ int fixed_rate = get_bits1(gb);
+
+ if (!fixed_rate)
+ fixed_rate = get_bits1(gb);
+
+ if (fixed_rate)
+ get_ue_golomb_long(gb); // elemental_duration_in_tc_minus1
+ else
+ low_delay = get_bits1(gb);
+
+ if (!low_delay) {
+ nb_cpb = get_ue_golomb_long(gb) + 1;
+ if (nb_cpb < 1 || nb_cpb > 32) {
+ v4l_dbg(0, V4L_DEBUG_CODEC_ERROR, "nb_cpb %d invalid\n", nb_cpb);
+ return -1;
+ }
+ }
+
+ if (nal_params_present)
+ decode_sublayer_hrd(gb, nb_cpb, subpic_params_present);
+ if (vcl_params_present)
+ decode_sublayer_hrd(gb, nb_cpb, subpic_params_present);
+ }
+ return 0;
+}
+
+int ff_hevc_parse_vps(struct get_bits_context *gb, struct h265_VPS_t *vps)
+{
+ int i,j;
+ int vps_id = 0;
+
+ v4l_dbg(0, V4L_DEBUG_CODEC_PARSER, "Decoding VPS\n");
+
+ vps_id = get_bits(gb, 4);
+ if (vps_id >= HEVC_MAX_VPS_COUNT) {
+ v4l_dbg(0, V4L_DEBUG_CODEC_ERROR, "VPS id out of range: %d\n", vps_id);
+ goto err;
+ }
+
+ if (get_bits(gb, 2) != 3) { // vps_reserved_three_2bits
+ v4l_dbg(0, V4L_DEBUG_CODEC_ERROR, "vps_reserved_three_2bits is not three\n");
+ goto err;
+ }
+
+ vps->vps_max_layers = get_bits(gb, 6) + 1;
+ vps->vps_max_sub_layers = get_bits(gb, 3) + 1;
+ vps->vps_temporal_id_nesting_flag = get_bits1(gb);
+
+ if (get_bits(gb, 16) != 0xffff) { // vps_reserved_ffff_16bits
+ v4l_dbg(0, V4L_DEBUG_CODEC_ERROR, "vps_reserved_ffff_16bits is not 0xffff\n");
+ goto err;
+ }
+
+ if (vps->vps_max_sub_layers > HEVC_MAX_SUB_LAYERS) {
+ v4l_dbg(0, V4L_DEBUG_CODEC_ERROR, "vps_max_sub_layers out of range: %d\n",
+ vps->vps_max_sub_layers);
+ goto err;
+ }
+
+ if (parse_ptl(gb, &vps->ptl, vps->vps_max_sub_layers) < 0)
+ goto err;
+
+ vps->vps_sub_layer_ordering_info_present_flag = get_bits1(gb);
+
+ i = vps->vps_sub_layer_ordering_info_present_flag ? 0 : vps->vps_max_sub_layers - 1;
+ for (; i < vps->vps_max_sub_layers; i++) {
+ vps->vps_max_dec_pic_buffering[i] = get_ue_golomb_long(gb) + 1;
+ vps->vps_num_reorder_pics[i] = get_ue_golomb_long(gb);
+ vps->vps_max_latency_increase[i] = get_ue_golomb_long(gb) - 1;
+
+ if (vps->vps_max_dec_pic_buffering[i] > HEVC_MAX_DPB_SIZE || !vps->vps_max_dec_pic_buffering[i]) {
+ v4l_dbg(0, V4L_DEBUG_CODEC_ERROR, "vps_max_dec_pic_buffering_minus1 out of range: %d\n",
+ vps->vps_max_dec_pic_buffering[i] - 1);
+ goto err;
+ }
+ if (vps->vps_num_reorder_pics[i] > vps->vps_max_dec_pic_buffering[i] - 1) {
+ v4l_dbg(0, V4L_DEBUG_CODEC_ERROR, "vps_max_num_reorder_pics out of range: %d\n",
+ vps->vps_num_reorder_pics[i]);
+ goto err;
+ }
+ }
+
+ vps->vps_max_layer_id = get_bits(gb, 6);
+ vps->vps_num_layer_sets = get_ue_golomb_long(gb) + 1;
+ if (vps->vps_num_layer_sets < 1 || vps->vps_num_layer_sets > 1024 ||
+ (vps->vps_num_layer_sets - 1LL) * (vps->vps_max_layer_id + 1LL) > get_bits_left(gb)) {
+ v4l_dbg(0, V4L_DEBUG_CODEC_ERROR, "too many layer_id_included_flags\n");
+ goto err;
+ }
+
+ for (i = 1; i < vps->vps_num_layer_sets; i++)
+ for (j = 0; j <= vps->vps_max_layer_id; j++)
+ skip_bits(gb, 1); // layer_id_included_flag[i][j]
+
+ vps->vps_timing_info_present_flag = get_bits1(gb);
+ if (vps->vps_timing_info_present_flag) {
+ vps->vps_num_units_in_tick = get_bits_long(gb, 32);
+ vps->vps_time_scale = get_bits_long(gb, 32);
+ vps->vps_poc_proportional_to_timing_flag = get_bits1(gb);
+ if (vps->vps_poc_proportional_to_timing_flag)
+ vps->vps_num_ticks_poc_diff_one = get_ue_golomb_long(gb) + 1;
+ vps->vps_num_hrd_parameters = get_ue_golomb_long(gb);
+ if (vps->vps_num_hrd_parameters > (u32)vps->vps_num_layer_sets) {
+ v4l_dbg(0, V4L_DEBUG_CODEC_ERROR, "vps_num_hrd_parameters %d is invalid\n", vps->vps_num_hrd_parameters);
+ goto err;
+ }
+ for (i = 0; i < vps->vps_num_hrd_parameters; i++) {
+ int common_inf_present = 1;
+
+ get_ue_golomb_long(gb); // hrd_layer_set_idx
+ if (i)
+ common_inf_present = get_bits1(gb);
+ decode_hrd(gb, common_inf_present, vps->vps_max_sub_layers);
+ }
+ }
+ get_bits1(gb); /* vps_extension_flag */
+
+ if (get_bits_left(gb) < 0) {
+ v4l_dbg(0, V4L_DEBUG_CODEC_ERROR, "Overread VPS by %d bits\n", -get_bits_left(gb));
+ goto err;
+ }
+
+ return 0;
+err:
+ return -1;
+}
+
+static int map_pixel_format(struct h265_SPS_t *sps)
+{
+ /*const AVPixFmtDescriptor *desc;*/
+ switch (sps->bit_depth) {
+ case 8:
+ if (sps->chroma_format_idc == 0) sps->pix_fmt = AV_PIX_FMT_GRAY8;
+ if (sps->chroma_format_idc == 1) sps->pix_fmt = AV_PIX_FMT_YUV420P;
+ if (sps->chroma_format_idc == 2) sps->pix_fmt = AV_PIX_FMT_YUV422P;
+ if (sps->chroma_format_idc == 3) sps->pix_fmt = AV_PIX_FMT_YUV444P;
+ break;
+ case 9:
+ if (sps->chroma_format_idc == 0) sps->pix_fmt = AV_PIX_FMT_GRAY9;
+ if (sps->chroma_format_idc == 1) sps->pix_fmt = AV_PIX_FMT_YUV420P9;
+ if (sps->chroma_format_idc == 2) sps->pix_fmt = AV_PIX_FMT_YUV422P9;
+ if (sps->chroma_format_idc == 3) sps->pix_fmt = AV_PIX_FMT_YUV444P9;
+ break;
+ case 10:
+ if (sps->chroma_format_idc == 0) sps->pix_fmt = AV_PIX_FMT_GRAY10;
+ if (sps->chroma_format_idc == 1) sps->pix_fmt = AV_PIX_FMT_YUV420P10;
+ if (sps->chroma_format_idc == 2) sps->pix_fmt = AV_PIX_FMT_YUV422P10;
+ if (sps->chroma_format_idc == 3) sps->pix_fmt = AV_PIX_FMT_YUV444P10;
+ break;
+ case 12:
+ if (sps->chroma_format_idc == 0) sps->pix_fmt = AV_PIX_FMT_GRAY12;
+ if (sps->chroma_format_idc == 1) sps->pix_fmt = AV_PIX_FMT_YUV420P12;
+ if (sps->chroma_format_idc == 2) sps->pix_fmt = AV_PIX_FMT_YUV422P12;
+ if (sps->chroma_format_idc == 3) sps->pix_fmt = AV_PIX_FMT_YUV444P12;
+ break;
+ default:
+ v4l_dbg(0, V4L_DEBUG_CODEC_PARSER, "The following bit-depths are currently specified: 8, 9, 10 and 12 bits, "
+ "chroma_format_idc is %d, depth is %d\n",
+ sps->chroma_format_idc, sps->bit_depth);
+ return -1;
+ }
+
+ /*desc = av_pix_fmt_desc_get(sps->pix_fmt);
+ if (!desc)
+ return AVERROR(EINVAL);
+
+ sps->hshift[0] = sps->vshift[0] = 0;
+ sps->hshift[2] = sps->hshift[1] = desc->log2_chroma_w;
+ sps->vshift[2] = sps->vshift[1] = desc->log2_chroma_h;*/
+
+ sps->pixel_shift = sps->bit_depth > 8;
+
+ return 0;
+}
+
+static void set_default_scaling_list_data(struct ScalingList *sl)
+{
+ int matrixId;
+
+ for (matrixId = 0; matrixId < 6; matrixId++) {
+ // 4x4 default is 16
+ memset(sl->sl[0][matrixId], 16, 16);
+ sl->sl_dc[0][matrixId] = 16; // default for 16x16
+ sl->sl_dc[1][matrixId] = 16; // default for 32x32
+ }
+ memcpy(sl->sl[1][0], default_scaling_list_intra, 64);
+ memcpy(sl->sl[1][1], default_scaling_list_intra, 64);
+ memcpy(sl->sl[1][2], default_scaling_list_intra, 64);
+ memcpy(sl->sl[1][3], default_scaling_list_inter, 64);
+ memcpy(sl->sl[1][4], default_scaling_list_inter, 64);
+ memcpy(sl->sl[1][5], default_scaling_list_inter, 64);
+ memcpy(sl->sl[2][0], default_scaling_list_intra, 64);
+ memcpy(sl->sl[2][1], default_scaling_list_intra, 64);
+ memcpy(sl->sl[2][2], default_scaling_list_intra, 64);
+ memcpy(sl->sl[2][3], default_scaling_list_inter, 64);
+ memcpy(sl->sl[2][4], default_scaling_list_inter, 64);
+ memcpy(sl->sl[2][5], default_scaling_list_inter, 64);
+ memcpy(sl->sl[3][0], default_scaling_list_intra, 64);
+ memcpy(sl->sl[3][1], default_scaling_list_intra, 64);
+ memcpy(sl->sl[3][2], default_scaling_list_intra, 64);
+ memcpy(sl->sl[3][3], default_scaling_list_inter, 64);
+ memcpy(sl->sl[3][4], default_scaling_list_inter, 64);
+ memcpy(sl->sl[3][5], default_scaling_list_inter, 64);
+}
+
+static int scaling_list_data(struct get_bits_context *gb,
+ struct ScalingList *sl, struct h265_SPS_t *sps)
+{
+ u8 scaling_list_pred_mode_flag;
+ int scaling_list_dc_coef[2][6];
+ int size_id, matrix_id, pos;
+ int i;
+
+ for (size_id = 0; size_id < 4; size_id++)
+ for (matrix_id = 0; matrix_id < 6; matrix_id += ((size_id == 3) ? 3 : 1)) {
+ scaling_list_pred_mode_flag = get_bits1(gb);
+ if (!scaling_list_pred_mode_flag) {
+ u32 delta = get_ue_golomb_long(gb);
+ /* Only need to handle non-zero delta. Zero means default,
+ * which should already be in the arrays. */
+ if (delta) {
+ // Copy from previous array.
+ delta *= (size_id == 3) ? 3 : 1;
+ if (matrix_id < delta) {
+ v4l_dbg(0, V4L_DEBUG_CODEC_ERROR, "Invalid delta in scaling list data: %d.\n", delta);
+ return -1;
+ }
+
+ memcpy(sl->sl[size_id][matrix_id],
+ sl->sl[size_id][matrix_id - delta],
+ size_id > 0 ? 64 : 16);
+ if (size_id > 1)
+ sl->sl_dc[size_id - 2][matrix_id] = sl->sl_dc[size_id - 2][matrix_id - delta];
+ }
+ } else {
+ int next_coef, coef_num;
+ int scaling_list_delta_coef;
+
+ next_coef = 8;
+ coef_num = FFMIN(64, 1 << (4 + (size_id << 1)));
+ if (size_id > 1) {
+ scaling_list_dc_coef[size_id - 2][matrix_id] = get_se_golomb(gb) + 8;
+ next_coef = scaling_list_dc_coef[size_id - 2][matrix_id];
+ sl->sl_dc[size_id - 2][matrix_id] = next_coef;
+ }
+ for (i = 0; i < coef_num; i++) {
+ if (size_id == 0)
+ pos = 4 * ff_hevc_diag_scan4x4_y[i] +
+ ff_hevc_diag_scan4x4_x[i];
+ else
+ pos = 8 * ff_hevc_diag_scan8x8_y[i] +
+ ff_hevc_diag_scan8x8_x[i];
+
+ scaling_list_delta_coef = get_se_golomb(gb);
+ next_coef = (next_coef + 256U + scaling_list_delta_coef) % 256;
+ sl->sl[size_id][matrix_id][pos] = next_coef;
+ }
+ }
+ }
+
+ if (sps->chroma_format_idc == 3) {
+ for (i = 0; i < 64; i++) {
+ sl->sl[3][1][i] = sl->sl[2][1][i];
+ sl->sl[3][2][i] = sl->sl[2][2][i];
+ sl->sl[3][4][i] = sl->sl[2][4][i];
+ sl->sl[3][5][i] = sl->sl[2][5][i];
+ }
+ sl->sl_dc[1][1] = sl->sl_dc[0][1];
+ sl->sl_dc[1][2] = sl->sl_dc[0][2];
+ sl->sl_dc[1][4] = sl->sl_dc[0][4];
+ sl->sl_dc[1][5] = sl->sl_dc[0][5];
+ }
+
+ return 0;
+}
+
+int ff_hevc_decode_short_term_rps(struct get_bits_context *gb,
+ struct ShortTermRPS *rps, const struct h265_SPS_t *sps, int is_slice_header)
+{
+ u8 rps_predict = 0;
+ int delta_poc;
+ int k0 = 0;
+ int k1 = 0;
+ int k = 0;
+ int i;
+
+ if (rps != sps->st_rps && sps->nb_st_rps)
+ rps_predict = get_bits1(gb);
+
+ if (rps_predict) {
+ const struct ShortTermRPS *rps_ridx;
+ int delta_rps;
+ u32 abs_delta_rps;
+ u8 use_delta_flag = 0;
+ u8 delta_rps_sign;
+
+ if (is_slice_header) {
+ u32 delta_idx = get_ue_golomb_long(gb) + 1;
+ if (delta_idx > sps->nb_st_rps) {
+ v4l_dbg(0, V4L_DEBUG_CODEC_ERROR, "Invalid value of delta_idx in slice header RPS: %d > %d.\n",
+ delta_idx, sps->nb_st_rps);
+ return -1;
+ }
+ rps_ridx = &sps->st_rps[sps->nb_st_rps - delta_idx];
+ rps->rps_idx_num_delta_pocs = rps_ridx->num_delta_pocs;
+ } else
+ rps_ridx = &sps->st_rps[rps - sps->st_rps - 1];
+
+ delta_rps_sign = get_bits1(gb);
+ abs_delta_rps = get_ue_golomb_long(gb) + 1;
+ if (abs_delta_rps < 1 || abs_delta_rps > 32768) {
+ v4l_dbg(0, V4L_DEBUG_CODEC_ERROR, "Invalid value of abs_delta_rps: %d\n",
+ abs_delta_rps);
+ return -1;
+ }
+ delta_rps = (1 - (delta_rps_sign << 1)) * abs_delta_rps;
+ for (i = 0; i <= rps_ridx->num_delta_pocs; i++) {
+ int used = rps->used[k] = get_bits1(gb);
+
+ if (!used)
+ use_delta_flag = get_bits1(gb);
+
+ if (used || use_delta_flag) {
+ if (i < rps_ridx->num_delta_pocs)
+ delta_poc = delta_rps + rps_ridx->delta_poc[i];
+ else
+ delta_poc = delta_rps;
+ rps->delta_poc[k] = delta_poc;
+ if (delta_poc < 0)
+ k0++;
+ else
+ k1++;
+ k++;
+ }
+ }
+
+ if (k >= ARRAY_SIZE(rps->used)) {
+ v4l_dbg(0, V4L_DEBUG_CODEC_ERROR, "Invalid num_delta_pocs: %d\n", k);
+ return -1;
+ }
+
+ rps->num_delta_pocs = k;
+ rps->num_negative_pics = k0;
+ // sort in increasing order (smallest first)
+ if (rps->num_delta_pocs != 0) {
+ int used, tmp;
+ for (i = 1; i < rps->num_delta_pocs; i++) {
+ delta_poc = rps->delta_poc[i];
+ used = rps->used[i];
+ for (k = i - 1; k >= 0; k--) {
+ tmp = rps->delta_poc[k];
+ if (delta_poc < tmp) {
+ rps->delta_poc[k + 1] = tmp;
+ rps->used[k + 1] = rps->used[k];
+ rps->delta_poc[k] = delta_poc;
+ rps->used[k] = used;
+ }
+ }
+ }
+ }
+ if ((rps->num_negative_pics >> 1) != 0) {
+ int used;
+ k = rps->num_negative_pics - 1;
+ // flip the negative values to largest first
+ for (i = 0; i < rps->num_negative_pics >> 1; i++) {
+ delta_poc = rps->delta_poc[i];
+ used = rps->used[i];
+ rps->delta_poc[i] = rps->delta_poc[k];
+ rps->used[i] = rps->used[k];
+ rps->delta_poc[k] = delta_poc;
+ rps->used[k] = used;
+ k--;
+ }
+ }
+ } else {
+ u32 prev, nb_positive_pics;
+ rps->num_negative_pics = get_ue_golomb_long(gb);
+ nb_positive_pics = get_ue_golomb_long(gb);
+
+ if (rps->num_negative_pics >= HEVC_MAX_REFS ||
+ nb_positive_pics >= HEVC_MAX_REFS) {
+ v4l_dbg(0, V4L_DEBUG_CODEC_ERROR, "Too many refs in a short term RPS.\n");
+ return -1;
+ }
+
+ rps->num_delta_pocs = rps->num_negative_pics + nb_positive_pics;
+ if (rps->num_delta_pocs) {
+ prev = 0;
+ for (i = 0; i < rps->num_negative_pics; i++) {
+ delta_poc = get_ue_golomb_long(gb) + 1;
+ if (delta_poc < 1 || delta_poc > 32768) {
+ v4l_dbg(0, V4L_DEBUG_CODEC_ERROR, "Invalid value of delta_poc: %d\n",
+ delta_poc);
+ return -1;
+ }
+ prev -= delta_poc;
+ rps->delta_poc[i] = prev;
+ rps->used[i] = get_bits1(gb);
+ }
+ prev = 0;
+ for (i = 0; i < nb_positive_pics; i++) {
+ delta_poc = get_ue_golomb_long(gb) + 1;
+ if (delta_poc < 1 || delta_poc > 32768) {
+ v4l_dbg(0, V4L_DEBUG_CODEC_ERROR, "Invalid value of delta_poc: %d\n",
+ delta_poc);
+ return -1;
+ }
+ prev += delta_poc;
+ rps->delta_poc[rps->num_negative_pics + i] = prev;
+ rps->used[rps->num_negative_pics + i] = get_bits1(gb);
+ }
+ }
+ }
+ return 0;
+}
+
+static void decode_vui(struct get_bits_context *gb, struct h265_SPS_t *sps)
+{
+ struct VUI backup_vui, *vui = &sps->vui;
+ struct get_bits_context backup;
+ int sar_present, alt = 0;
+
+ v4l_dbg(0, V4L_DEBUG_CODEC_PARSER, "Decoding VUI\n");
+
+ sar_present = get_bits1(gb);
+ if (sar_present) {
+ u8 sar_idx = get_bits(gb, 8);
+ if (sar_idx < ARRAY_SIZE(vui_sar))
+ vui->sar = vui_sar[sar_idx];
+ else if (sar_idx == 255) {
+ vui->sar.num = get_bits(gb, 16);
+ vui->sar.den = get_bits(gb, 16);
+ } else
+ v4l_dbg(0, V4L_DEBUG_CODEC_PARSER,
+ "Unknown SAR index: %u.\n", sar_idx);
+ }
+
+ vui->overscan_info_present_flag = get_bits1(gb);
+ if (vui->overscan_info_present_flag)
+ vui->overscan_appropriate_flag = get_bits1(gb);
+
+ vui->video_signal_type_present_flag = get_bits1(gb);
+ if (vui->video_signal_type_present_flag) {
+ vui->video_format = get_bits(gb, 3);
+ vui->video_full_range_flag = get_bits1(gb);
+ vui->colour_description_present_flag = get_bits1(gb);
+ if (vui->video_full_range_flag && sps->pix_fmt == AV_PIX_FMT_YUV420P)
+ sps->pix_fmt = AV_PIX_FMT_YUVJ420P;
+ if (vui->colour_description_present_flag) {
+ vui->colour_primaries = get_bits(gb, 8);
+ vui->transfer_characteristic = get_bits(gb, 8);
+ vui->matrix_coeffs = get_bits(gb, 8);
+
+ // Set invalid values to "unspecified"
+ if (!av_color_primaries_name(vui->colour_primaries))
+ vui->colour_primaries = AVCOL_PRI_UNSPECIFIED;
+ if (!av_color_transfer_name(vui->transfer_characteristic))
+ vui->transfer_characteristic = AVCOL_TRC_UNSPECIFIED;
+ if (!av_color_space_name(vui->matrix_coeffs))
+ vui->matrix_coeffs = AVCOL_SPC_UNSPECIFIED;
+ if (vui->matrix_coeffs == AVCOL_SPC_RGB) {
+ switch (sps->pix_fmt) {
+ case AV_PIX_FMT_YUV444P:
+ sps->pix_fmt = AV_PIX_FMT_GBRP;
+ break;
+ case AV_PIX_FMT_YUV444P10:
+ sps->pix_fmt = AV_PIX_FMT_GBRP10;
+ break;
+ case AV_PIX_FMT_YUV444P12:
+ sps->pix_fmt = AV_PIX_FMT_GBRP12;
+ break;
+ }
+ }
+ }
+ }
+
+ vui->chroma_loc_info_present_flag = get_bits1(gb);
+ if (vui->chroma_loc_info_present_flag) {
+ vui->chroma_sample_loc_type_top_field = get_ue_golomb_long(gb);
+ vui->chroma_sample_loc_type_bottom_field = get_ue_golomb_long(gb);
+ }
+
+ vui->neutra_chroma_indication_flag = get_bits1(gb);
+ vui->field_seq_flag = get_bits1(gb);
+ vui->frame_field_info_present_flag = get_bits1(gb);
+
+ // Backup context in case an alternate header is detected
+ memcpy(&backup, gb, sizeof(backup));
+ memcpy(&backup_vui, vui, sizeof(backup_vui));
+ if (get_bits_left(gb) >= 68 && show_bits_long(gb, 21) == 0x100000) {
+ vui->default_display_window_flag = 0;
+ v4l_dbg(0, V4L_DEBUG_CODEC_PARSER, "Invalid default display window\n");
+ } else
+ vui->default_display_window_flag = get_bits1(gb);
+
+ if (vui->default_display_window_flag) {
+ int vert_mult = hevc_sub_height_c[sps->chroma_format_idc];
+ int horiz_mult = hevc_sub_width_c[sps->chroma_format_idc];
+ vui->def_disp_win.left_offset = get_ue_golomb_long(gb) * horiz_mult;
+ vui->def_disp_win.right_offset = get_ue_golomb_long(gb) * horiz_mult;
+ vui->def_disp_win.top_offset = get_ue_golomb_long(gb) * vert_mult;
+ vui->def_disp_win.bottom_offset = get_ue_golomb_long(gb) * vert_mult;
+ }
+
+timing_info:
+ vui->vui_timing_info_present_flag = get_bits1(gb);
+
+ if (vui->vui_timing_info_present_flag) {
+ if (get_bits_left(gb) < 66 && !alt) {
+ // The alternate syntax seem to have timing info located
+ // at where def_disp_win is normally located
+ v4l_dbg(0, V4L_DEBUG_CODEC_PARSER, "Strange VUI timing information, retrying...\n");
+ memcpy(vui, &backup_vui, sizeof(backup_vui));
+ memcpy(gb, &backup, sizeof(backup));
+ alt = 1;
+ goto timing_info;
+ }
+ vui->vui_num_units_in_tick = get_bits_long(gb, 32);
+ vui->vui_time_scale = get_bits_long(gb, 32);
+ if (alt) {
+ v4l_dbg(0, V4L_DEBUG_CODEC_PARSER, "Retry got %u/%u fps\n",
+ vui->vui_time_scale, vui->vui_num_units_in_tick);
+ }
+ vui->vui_poc_proportional_to_timing_flag = get_bits1(gb);
+ if (vui->vui_poc_proportional_to_timing_flag)
+ vui->vui_num_ticks_poc_diff_one_minus1 = get_ue_golomb_long(gb);
+ vui->vui_hrd_parameters_present_flag = get_bits1(gb);
+ if (vui->vui_hrd_parameters_present_flag)
+ decode_hrd(gb, 1, sps->max_sub_layers);
+ }
+
+ vui->bitstream_restriction_flag = get_bits1(gb);
+ if (vui->bitstream_restriction_flag) {
+ if (get_bits_left(gb) < 8 && !alt) {
+ v4l_dbg(0, V4L_DEBUG_CODEC_PARSER, "Strange VUI bitstream restriction information, retrying"
+ " from timing information...\n");
+ memcpy(vui, &backup_vui, sizeof(backup_vui));
+ memcpy(gb, &backup, sizeof(backup));
+ alt = 1;
+ goto timing_info;
+ }
+ vui->tiles_fixed_structure_flag = get_bits1(gb);
+ vui->motion_vectors_over_pic_boundaries_flag = get_bits1(gb);
+ vui->restricted_ref_pic_lists_flag = get_bits1(gb);
+ vui->min_spatial_segmentation_idc = get_ue_golomb_long(gb);
+ vui->max_bytes_per_pic_denom = get_ue_golomb_long(gb);
+ vui->max_bits_per_min_cu_denom = get_ue_golomb_long(gb);
+ vui->log2_max_mv_length_horizontal = get_ue_golomb_long(gb);
+ vui->log2_max_mv_length_vertical = get_ue_golomb_long(gb);
+ }
+
+ if (get_bits_left(gb) < 1 && !alt) {
+ // XXX: Alternate syntax when sps_range_extension_flag != 0?
+ v4l_dbg(0, V4L_DEBUG_CODEC_PARSER, "Overread in VUI, retrying from timing information...\n");
+ memcpy(vui, &backup_vui, sizeof(backup_vui));
+ memcpy(gb, &backup, sizeof(backup));
+ alt = 1;
+ goto timing_info;
+ }
+}
+
+int ff_hevc_parse_sps(struct get_bits_context *gb, struct h265_SPS_t *sps)
+{
+ int i, ret = 0;
+ int log2_diff_max_min_transform_block_size;
+ int bit_depth_chroma, start, vui_present, sublayer_ordering_info;
+ struct HEVCWindow *ow;
+
+ sps->vps_id = get_bits(gb, 4);
+ if (sps->vps_id >= HEVC_MAX_VPS_COUNT) {
+ v4l_dbg(0, V4L_DEBUG_CODEC_ERROR, "VPS id out of range: %d\n", sps->vps_id);
+ return -1;
+ }
+
+ sps->max_sub_layers = get_bits(gb, 3) + 1;
+ if (sps->max_sub_layers > HEVC_MAX_SUB_LAYERS) {
+ v4l_dbg(0, V4L_DEBUG_CODEC_ERROR, "sps_max_sub_layers out of range: %d\n",
+ sps->max_sub_layers);
+ return -1;
+ }
+
+ sps->temporal_id_nesting_flag = get_bits(gb, 1);
+
+ if ((ret = parse_ptl(gb, &sps->ptl, sps->max_sub_layers)) < 0)
+ return ret;
+
+ sps->sps_id = get_ue_golomb_long(gb);
+ if (sps->sps_id >= HEVC_MAX_SPS_COUNT) {
+ v4l_dbg(0, V4L_DEBUG_CODEC_ERROR, "SPS id out of range: %d\n", sps->sps_id);
+ return -1;
+ }
+
+ sps->chroma_format_idc = get_ue_golomb_long(gb);
+ if (sps->chroma_format_idc > 3U) {
+ v4l_dbg(0, V4L_DEBUG_CODEC_ERROR, "chroma_format_idc %d is invalid\n", sps->chroma_format_idc);
+ return -1;
+ }
+
+ if (sps->chroma_format_idc == 3)
+ sps->separate_colour_plane_flag = get_bits1(gb);
+
+ if (sps->separate_colour_plane_flag)
+ sps->chroma_format_idc = 0;
+
+ sps->width = get_ue_golomb_long(gb);
+ sps->height = get_ue_golomb_long(gb);
+ if (sps->width > 8192 || sps->height > 8192) {
+ v4l_dbg(0, V4L_DEBUG_CODEC_ERROR, "width or height oversize.\n");
+ return -1;
+ }
+
+ if (get_bits1(gb)) { // pic_conformance_flag
+ int vert_mult = hevc_sub_height_c[sps->chroma_format_idc];
+ int horiz_mult = hevc_sub_width_c[sps->chroma_format_idc];
+ sps->pic_conf_win.left_offset = get_ue_golomb_long(gb) * horiz_mult;
+ sps->pic_conf_win.right_offset = get_ue_golomb_long(gb) * horiz_mult;
+ sps->pic_conf_win.top_offset = get_ue_golomb_long(gb) * vert_mult;
+ sps->pic_conf_win.bottom_offset = get_ue_golomb_long(gb) * vert_mult;
+ sps->output_window = sps->pic_conf_win;
+ }
+
+ sps->bit_depth = get_ue_golomb_long(gb) + 8;
+ bit_depth_chroma = get_ue_golomb_long(gb) + 8;
+ if (sps->chroma_format_idc && bit_depth_chroma != sps->bit_depth) {
+ v4l_dbg(0, V4L_DEBUG_CODEC_ERROR, "Luma bit depth (%d) is different from chroma bit depth (%d), this is unsupported.\n",
+ sps->bit_depth, bit_depth_chroma);
+ return -1;
+ }
+ sps->bit_depth_chroma = bit_depth_chroma;
+
+ ret = map_pixel_format(sps);
+ if (ret < 0)
+ return ret;
+
+ sps->log2_max_poc_lsb = get_ue_golomb_long(gb) + 4;
+ if (sps->log2_max_poc_lsb > 16) {
+ v4l_dbg(0, V4L_DEBUG_CODEC_ERROR, "log2_max_pic_order_cnt_lsb_minus4 out range: %d\n",
+ sps->log2_max_poc_lsb - 4);
+ return -1;
+ }
+
+ sublayer_ordering_info = get_bits1(gb);
+ start = sublayer_ordering_info ? 0 : sps->max_sub_layers - 1;
+ for (i = start; i < sps->max_sub_layers; i++) {
+ sps->temporal_layer[i].max_dec_pic_buffering = get_ue_golomb_long(gb) + 1;
+ sps->temporal_layer[i].num_reorder_pics = get_ue_golomb_long(gb);
+ sps->temporal_layer[i].max_latency_increase = get_ue_golomb_long(gb) - 1;
+ if (sps->temporal_layer[i].max_dec_pic_buffering > (u32)HEVC_MAX_DPB_SIZE) {
+ v4l_dbg(0, V4L_DEBUG_CODEC_ERROR, "sps_max_dec_pic_buffering_minus1 out of range: %d\n",
+ sps->temporal_layer[i].max_dec_pic_buffering - 1U);
+ return -1;
+ }
+ if (sps->temporal_layer[i].num_reorder_pics > sps->temporal_layer[i].max_dec_pic_buffering - 1) {
+ v4l_dbg(0, V4L_DEBUG_CODEC_PARSER, "sps_max_num_reorder_pics out of range: %d\n",
+ sps->temporal_layer[i].num_reorder_pics);
+ if (sps->temporal_layer[i].num_reorder_pics > HEVC_MAX_DPB_SIZE - 1) {
+ return -1;
+ }
+ sps->temporal_layer[i].max_dec_pic_buffering = sps->temporal_layer[i].num_reorder_pics + 1;
+ }
+ }
+
+ if (!sublayer_ordering_info) {
+ for (i = 0; i < start; i++) {
+ sps->temporal_layer[i].max_dec_pic_buffering = sps->temporal_layer[start].max_dec_pic_buffering;
+ sps->temporal_layer[i].num_reorder_pics = sps->temporal_layer[start].num_reorder_pics;
+ sps->temporal_layer[i].max_latency_increase = sps->temporal_layer[start].max_latency_increase;
+ }
+ }
+
+ sps->log2_min_cb_size = get_ue_golomb_long(gb) + 3;
+ sps->log2_diff_max_min_coding_block_size = get_ue_golomb_long(gb);
+ sps->log2_min_tb_size = get_ue_golomb_long(gb) + 2;
+ log2_diff_max_min_transform_block_size = get_ue_golomb_long(gb);
+ sps->log2_max_trafo_size = log2_diff_max_min_transform_block_size + sps->log2_min_tb_size;
+
+ if (sps->log2_min_cb_size < 3 || sps->log2_min_cb_size > 30) {
+ v4l_dbg(0, V4L_DEBUG_CODEC_ERROR, "Invalid value %d for log2_min_cb_size", sps->log2_min_cb_size);
+ return -1;
+ }
+
+ if (sps->log2_diff_max_min_coding_block_size > 30) {
+ v4l_dbg(0, V4L_DEBUG_CODEC_ERROR, "Invalid value %d for log2_diff_max_min_coding_block_size", sps->log2_diff_max_min_coding_block_size);
+ return -1;
+ }
+
+ if (sps->log2_min_tb_size >= sps->log2_min_cb_size || sps->log2_min_tb_size < 2) {
+ v4l_dbg(0, V4L_DEBUG_CODEC_ERROR, "Invalid value for log2_min_tb_size");
+ return -1;
+ }
+
+ if (log2_diff_max_min_transform_block_size < 0 || log2_diff_max_min_transform_block_size > 30) {
+ v4l_dbg(0, V4L_DEBUG_CODEC_ERROR, "Invalid value %d for log2_diff_max_min_transform_block_size", log2_diff_max_min_transform_block_size);
+ return -1;
+ }
+
+ sps->max_transform_hierarchy_depth_inter = get_ue_golomb_long(gb);
+ sps->max_transform_hierarchy_depth_intra = get_ue_golomb_long(gb);
+
+ sps->scaling_list_enable_flag = get_bits1(gb);
+ if (sps->scaling_list_enable_flag) {
+ set_default_scaling_list_data(&sps->scaling_list);
+
+ if (get_bits1(gb)) {
+ ret = scaling_list_data(gb, &sps->scaling_list, sps);
+ if (ret < 0)
+ return ret;
+ }
+ }
+
+ sps->amp_enabled_flag = get_bits1(gb);
+ sps->sao_enabled = get_bits1(gb);
+
+ sps->pcm_enabled_flag = get_bits1(gb);
+ if (sps->pcm_enabled_flag) {
+ sps->pcm.bit_depth = get_bits(gb, 4) + 1;
+ sps->pcm.bit_depth_chroma = get_bits(gb, 4) + 1;
+ sps->pcm.log2_min_pcm_cb_size = get_ue_golomb_long(gb) + 3;
+ sps->pcm.log2_max_pcm_cb_size = sps->pcm.log2_min_pcm_cb_size +
+ get_ue_golomb_long(gb);
+ if (FFMAX(sps->pcm.bit_depth, sps->pcm.bit_depth_chroma) > sps->bit_depth) {
+ v4l_dbg(0, V4L_DEBUG_CODEC_ERROR, "PCM bit depth (%d, %d) is greater than normal bit depth (%d)\n",
+ sps->pcm.bit_depth, sps->pcm.bit_depth_chroma, sps->bit_depth);
+ return -1;
+ }
+
+ sps->pcm.loop_filter_disable_flag = get_bits1(gb);
+ }
+
+ sps->nb_st_rps = get_ue_golomb_long(gb);
+ if (sps->nb_st_rps > HEVC_MAX_SHORT_TERM_REF_PIC_SETS) {
+ v4l_dbg(0, V4L_DEBUG_CODEC_ERROR, "Too many short term RPS: %d.\n", sps->nb_st_rps);
+ return -1;
+ }
+ for (i = 0; i < sps->nb_st_rps; i++) {
+ if ((ret = ff_hevc_decode_short_term_rps(gb, &sps->st_rps[i], sps, 0)) < 0)
+ return ret;
+ }
+
+ sps->long_term_ref_pics_present_flag = get_bits1(gb);
+ if (sps->long_term_ref_pics_present_flag) {
+ sps->num_long_term_ref_pics_sps = get_ue_golomb_long(gb);
+ if (sps->num_long_term_ref_pics_sps > HEVC_MAX_LONG_TERM_REF_PICS) {
+ v4l_dbg(0, V4L_DEBUG_CODEC_ERROR, "Too many long term ref pics: %d.\n",
+ sps->num_long_term_ref_pics_sps);
+ return -1;
+ }
+ for (i = 0; i < sps->num_long_term_ref_pics_sps; i++) {
+ sps->lt_ref_pic_poc_lsb_sps[i] = get_bits(gb, sps->log2_max_poc_lsb);
+ sps->used_by_curr_pic_lt_sps_flag[i] = get_bits1(gb);
+ }
+ }
+
+ sps->sps_temporal_mvp_enabled_flag = get_bits1(gb);
+ sps->sps_strong_intra_smoothing_enable_flag = get_bits1(gb);
+ sps->vui.sar = (struct AVRational){0, 1};
+ vui_present = get_bits1(gb);
+ if (vui_present)
+ decode_vui(gb, sps);
+
+ if (get_bits1(gb)) { // sps_extension_flag
+ sps->sps_range_extension_flag = get_bits1(gb);
+ skip_bits(gb, 7); //sps_extension_7bits = get_bits(gb, 7);
+ if (sps->sps_range_extension_flag) {
+ sps->transform_skip_rotation_enabled_flag = get_bits1(gb);
+ sps->transform_skip_context_enabled_flag = get_bits1(gb);
+ sps->implicit_rdpcm_enabled_flag = get_bits1(gb);
+ sps->explicit_rdpcm_enabled_flag = get_bits1(gb);
+ sps->extended_precision_processing_flag = get_bits1(gb);
+ if (sps->extended_precision_processing_flag)
+ v4l_dbg(0, V4L_DEBUG_CODEC_PARSER, "extended_precision_processing_flag not yet implemented\n");
+
+ sps->intra_smoothing_disabled_flag = get_bits1(gb);
+ sps->high_precision_offsets_enabled_flag = get_bits1(gb);
+ if (sps->high_precision_offsets_enabled_flag)
+ v4l_dbg(0, V4L_DEBUG_CODEC_PARSER, "high_precision_offsets_enabled_flag not yet implemented\n");
+
+ sps->persistent_rice_adaptation_enabled_flag = get_bits1(gb);
+ sps->cabac_bypass_alignment_enabled_flag = get_bits1(gb);
+ if (sps->cabac_bypass_alignment_enabled_flag)
+ v4l_dbg(0, V4L_DEBUG_CODEC_PARSER, "cabac_bypass_alignment_enabled_flag not yet implemented\n");
+ }
+ }
+
+ ow = &sps->output_window;
+ if (ow->left_offset >= INT_MAX - ow->right_offset ||
+ ow->top_offset >= INT_MAX - ow->bottom_offset ||
+ ow->left_offset + ow->right_offset >= sps->width ||
+ ow->top_offset + ow->bottom_offset >= sps->height) {
+ v4l_dbg(0, V4L_DEBUG_CODEC_ERROR, "Invalid cropping offsets: %u/%u/%u/%u\n",
+ ow->left_offset, ow->right_offset, ow->top_offset, ow->bottom_offset);
+ return -1;
+ }
+
+ // Inferred parameters
+ sps->log2_ctb_size = sps->log2_min_cb_size +
+ sps->log2_diff_max_min_coding_block_size;
+ sps->log2_min_pu_size = sps->log2_min_cb_size - 1;
+
+ if (sps->log2_ctb_size > HEVC_MAX_LOG2_CTB_SIZE) {
+ v4l_dbg(0, V4L_DEBUG_CODEC_ERROR, "CTB size out of range: 2^%d\n", sps->log2_ctb_size);
+ return -1;
+ }
+ if (sps->log2_ctb_size < 4) {
+ v4l_dbg(0, V4L_DEBUG_CODEC_ERROR, "log2_ctb_size %d differs from the bounds of any known profile\n", sps->log2_ctb_size);
+ v4l_dbg(0, V4L_DEBUG_CODEC_ERROR, "log2_ctb_size %d", sps->log2_ctb_size);
+ return -1;
+ }
+
+ sps->ctb_width = (sps->width + (1 << sps->log2_ctb_size) - 1) >> sps->log2_ctb_size;
+ sps->ctb_height = (sps->height + (1 << sps->log2_ctb_size) - 1) >> sps->log2_ctb_size;
+ sps->ctb_size = sps->ctb_width * sps->ctb_height;
+
+ sps->min_cb_width = sps->width >> sps->log2_min_cb_size;
+ sps->min_cb_height = sps->height >> sps->log2_min_cb_size;
+ sps->min_tb_width = sps->width >> sps->log2_min_tb_size;
+ sps->min_tb_height = sps->height >> sps->log2_min_tb_size;
+ sps->min_pu_width = sps->width >> sps->log2_min_pu_size;
+ sps->min_pu_height = sps->height >> sps->log2_min_pu_size;
+ sps->tb_mask = (1 << (sps->log2_ctb_size - sps->log2_min_tb_size)) - 1;
+ sps->qp_bd_offset = 6 * (sps->bit_depth - 8);
+
+ if (av_mod_uintp2(sps->width, sps->log2_min_cb_size) ||
+ av_mod_uintp2(sps->height, sps->log2_min_cb_size)) {
+ v4l_dbg(0, V4L_DEBUG_CODEC_ERROR, "Invalid coded frame dimensions.\n");
+ return -1;
+ }
+
+ if (sps->max_transform_hierarchy_depth_inter > sps->log2_ctb_size - sps->log2_min_tb_size) {
+ v4l_dbg(0, V4L_DEBUG_CODEC_ERROR, "max_transform_hierarchy_depth_inter out of range: %d\n",
+ sps->max_transform_hierarchy_depth_inter);
+ return -1;
+ }
+ if (sps->max_transform_hierarchy_depth_intra > sps->log2_ctb_size - sps->log2_min_tb_size) {
+ v4l_dbg(0, V4L_DEBUG_CODEC_ERROR, "max_transform_hierarchy_depth_intra out of range: %d\n",
+ sps->max_transform_hierarchy_depth_intra);
+ return -1;
+ }
+ if (sps->log2_max_trafo_size > FFMIN(sps->log2_ctb_size, 5)) {
+ v4l_dbg(0, V4L_DEBUG_CODEC_ERROR, "max transform block size out of range: %d\n",
+ sps->log2_max_trafo_size);
+ return -1;
+ }
+
+ if (get_bits_left(gb) < 0) {
+ v4l_dbg(0, V4L_DEBUG_CODEC_ERROR, "Overread SPS by %d bits\n", -get_bits_left(gb));
+ return -1;
+ }
+
+ v4l_dbg(0, V4L_DEBUG_CODEC_PARSER, "Parsed SPS: id %d; ref: %d, coded wxh: %dx%d, cropped wxh: %dx%d; pix_fmt: %d.\n",
+ sps->sps_id, sps->temporal_layer[0].num_reorder_pics, sps->width, sps->height,
+ sps->width - (sps->output_window.left_offset + sps->output_window.right_offset),
+ sps->height - (sps->output_window.top_offset + sps->output_window.bottom_offset),
+ sps->pix_fmt);
+
+ return 0;
+}
+
+const char *hevc_nal_type_name[64] = {
+ "TRAIL_N", // HEVC_NAL_TRAIL_N
+ "TRAIL_R", // HEVC_NAL_TRAIL_R
+ "TSA_N", // HEVC_NAL_TSA_N
+ "TSA_R", // HEVC_NAL_TSA_R
+ "STSA_N", // HEVC_NAL_STSA_N
+ "STSA_R", // HEVC_NAL_STSA_R
+ "RADL_N", // HEVC_NAL_RADL_N
+ "RADL_R", // HEVC_NAL_RADL_R
+ "RASL_N", // HEVC_NAL_RASL_N
+ "RASL_R", // HEVC_NAL_RASL_R
+ "RSV_VCL_N10", // HEVC_NAL_VCL_N10
+ "RSV_VCL_R11", // HEVC_NAL_VCL_R11
+ "RSV_VCL_N12", // HEVC_NAL_VCL_N12
+ "RSV_VLC_R13", // HEVC_NAL_VCL_R13
+ "RSV_VCL_N14", // HEVC_NAL_VCL_N14
+ "RSV_VCL_R15", // HEVC_NAL_VCL_R15
+ "BLA_W_LP", // HEVC_NAL_BLA_W_LP
+ "BLA_W_RADL", // HEVC_NAL_BLA_W_RADL
+ "BLA_N_LP", // HEVC_NAL_BLA_N_LP
+ "IDR_W_RADL", // HEVC_NAL_IDR_W_RADL
+ "IDR_N_LP", // HEVC_NAL_IDR_N_LP
+ "CRA_NUT", // HEVC_NAL_CRA_NUT
+ "IRAP_IRAP_VCL22", // HEVC_NAL_IRAP_VCL22
+ "IRAP_IRAP_VCL23", // HEVC_NAL_IRAP_VCL23
+ "RSV_VCL24", // HEVC_NAL_RSV_VCL24
+ "RSV_VCL25", // HEVC_NAL_RSV_VCL25
+ "RSV_VCL26", // HEVC_NAL_RSV_VCL26
+ "RSV_VCL27", // HEVC_NAL_RSV_VCL27
+ "RSV_VCL28", // HEVC_NAL_RSV_VCL28
+ "RSV_VCL29", // HEVC_NAL_RSV_VCL29
+ "RSV_VCL30", // HEVC_NAL_RSV_VCL30
+ "RSV_VCL31", // HEVC_NAL_RSV_VCL31
+ "VPS", // HEVC_NAL_VPS
+ "SPS", // HEVC_NAL_SPS
+ "PPS", // HEVC_NAL_PPS
+ "AUD", // HEVC_NAL_AUD
+ "EOS_NUT", // HEVC_NAL_EOS_NUT
+ "EOB_NUT", // HEVC_NAL_EOB_NUT
+ "FD_NUT", // HEVC_NAL_FD_NUT
+ "SEI_PREFIX", // HEVC_NAL_SEI_PREFIX
+ "SEI_SUFFIX", // HEVC_NAL_SEI_SUFFIX
+ "RSV_NVCL41", // HEVC_NAL_RSV_NVCL41
+ "RSV_NVCL42", // HEVC_NAL_RSV_NVCL42
+ "RSV_NVCL43", // HEVC_NAL_RSV_NVCL43
+ "RSV_NVCL44", // HEVC_NAL_RSV_NVCL44
+ "RSV_NVCL45", // HEVC_NAL_RSV_NVCL45
+ "RSV_NVCL46", // HEVC_NAL_RSV_NVCL46
+ "RSV_NVCL47", // HEVC_NAL_RSV_NVCL47
+ "UNSPEC48", // HEVC_NAL_UNSPEC48
+ "UNSPEC49", // HEVC_NAL_UNSPEC49
+ "UNSPEC50", // HEVC_NAL_UNSPEC50
+ "UNSPEC51", // HEVC_NAL_UNSPEC51
+ "UNSPEC52", // HEVC_NAL_UNSPEC52
+ "UNSPEC53", // HEVC_NAL_UNSPEC53
+ "UNSPEC54", // HEVC_NAL_UNSPEC54
+ "UNSPEC55", // HEVC_NAL_UNSPEC55
+ "UNSPEC56", // HEVC_NAL_UNSPEC56
+ "UNSPEC57", // HEVC_NAL_UNSPEC57
+ "UNSPEC58", // HEVC_NAL_UNSPEC58
+ "UNSPEC59", // HEVC_NAL_UNSPEC59
+ "UNSPEC60", // HEVC_NAL_UNSPEC60
+ "UNSPEC61", // HEVC_NAL_UNSPEC61
+ "UNSPEC62", // HEVC_NAL_UNSPEC62
+ "UNSPEC63", // HEVC_NAL_UNSPEC63
+};
+
+static const char *hevc_nal_unit_name(int nal_type)
+{
+ return hevc_nal_type_name[nal_type];
+}
+
+/**
+* Parse NAL units of found picture and decode some basic information.
+*
+* @param s parser context.
+* @param avctx codec context.
+* @param buf buffer with field/frame data.
+* @param buf_size size of the buffer.
+*/
+static int decode_extradata_ps(u8 *data, int size, struct h265_param_sets *ps)
+{
+ int ret = 0;
+ struct get_bits_context gb;
+ u32 src_len, rbsp_size = 0;
+ u8 *rbsp_buf = NULL;
+ int nalu_pos, nuh_layer_id, temporal_id;
+ u32 nal_type;
+ u8 *p = data;
+ u32 len = size;
+
+ nalu_pos = find_start_code(p, len);
+ if (nalu_pos < 0)
+ return -1;
+
+ src_len = calc_nal_len(p + nalu_pos, size - nalu_pos);
+ rbsp_buf = nal_unit_extract_rbsp(p + nalu_pos, src_len, &rbsp_size);
+ if (rbsp_buf == NULL)
+ return -ENOMEM;
+
+ ret = init_get_bits8(&gb, rbsp_buf, rbsp_size);
+ if (ret < 0)
+ goto out;
+
+ if (get_bits1(&gb) != 0) {
+ ret = -1;
+ v4l_dbg(0, V4L_DEBUG_CODEC_ERROR, "invalid data, return!\n");
+ goto out;
+ }
+
+ nal_type = get_bits(&gb, 6);
+ nuh_layer_id = get_bits(&gb, 6);
+ temporal_id = get_bits(&gb, 3) - 1;
+ if (temporal_id < 0) {
+ ret = -1;
+ goto out;
+ }
+
+ /*pr_info("nal_unit_type: %d(%s), nuh_layer_id: %d, temporal_id: %d\n",
+ nal_type, hevc_nal_unit_name(nal_type),
+ nuh_layer_id, temporal_id);*/
+
+ switch (nal_type) {
+ case HEVC_NAL_VPS:
+ ret = ff_hevc_parse_vps(&gb, &ps->vps);
+ if (ret < 0)
+ goto out;
+ ps->vps_parsed = true;
+ break;
+ case HEVC_NAL_SPS:
+ ret = ff_hevc_parse_sps(&gb, &ps->sps);
+ if (ret < 0)
+ goto out;
+ ps->sps_parsed = true;
+ break;
+ /*case HEVC_NAL_PPS:
+ ret = ff_hevc_decode_nal_pps(&gb, NULL, ps);
+ if (ret < 0)
+ goto out;
+ ps->pps_parsed = true;
+ break;*/
+ default:
+ v4l_dbg(0, V4L_DEBUG_CODEC_ERROR, "Unsupport parser nal type (%s).\n",
+ hevc_nal_unit_name(nal_type));
+ break;
+ }
+
+out:
+ vfree(rbsp_buf);
+
+ return ret;
+}
+
+int h265_decode_extradata_ps(u8 *buf, int size, struct h265_param_sets *ps)
+{
+ int ret = 0, i = 0, j = 0;
+ u8 *p = buf;
+ int len = size;
+
+ for (i = 4; i < size; i++) {
+ j = find_start_code(p, len);
+ if (j > 0) {
+ len = size - (p - buf);
+ ret = decode_extradata_ps(p, len, ps);
+ if (ret) {
+ v4l_dbg(0, V4L_DEBUG_CODEC_ERROR, "parse extra data failed. err: %d\n", ret);
+ return ret;
+ }
+
+ if (ps->sps_parsed)
+ break;
+
+ p += j;
+ }
+ p++;
+ }
+
+ return ret;
+}
+
diff --git a/drivers/amvdec_ports/decoder/aml_hevc_parser.h b/drivers/amvdec_ports/decoder/aml_hevc_parser.h
new file mode 100644
index 0000000..9223639
--- /dev/null
+++ b/drivers/amvdec_ports/decoder/aml_hevc_parser.h
@@ -0,0 +1,562 @@
+/*
+ * drivers/amvdec_ports/decoder/aml_hevc_parser.h
+ *
+ * Copyright (C) 2015 Amlogic, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ */
+
+
+#ifndef AML_HEVC_PARSER_H
+#define AML_HEVC_PARSER_H
+
+#include "../aml_vcodec_drv.h"
+#include "../utils/common.h"
+
+#define MAX_DPB_SIZE 16 // A.4.1
+#define MAX_REFS 16
+
+#define MAX_NB_THREADS 16
+#define SHIFT_CTB_WPP 2
+
+/**
+ * 7.4.2.1
+ */
+#define MAX_SUB_LAYERS 7
+#define MAX_VPS_COUNT 16
+#define MAX_SPS_COUNT 32
+#define MAX_PPS_COUNT 256
+#define MAX_SHORT_TERM_RPS_COUNT 64
+#define MAX_CU_SIZE 128
+
+//TODO: check if this is really the maximum
+#define MAX_TRANSFORM_DEPTH 5
+
+#define MAX_TB_SIZE 32
+#define MAX_PB_SIZE 64
+#define MAX_LOG2_CTB_SIZE 6
+#define MAX_QP 51
+#define DEFAULT_INTRA_TC_OFFSET 2
+
+#define HEVC_CONTEXTS 183
+
+#define MRG_MAX_NUM_CANDS 5
+
+#define L0 0
+#define L1 1
+
+#define EPEL_EXTRA_BEFORE 1
+#define EPEL_EXTRA_AFTER 2
+#define EPEL_EXTRA 3
+
+#define FF_PROFILE_HEVC_MAIN 1
+#define FF_PROFILE_HEVC_MAIN_10 2
+#define FF_PROFILE_HEVC_MAIN_STILL_PICTURE 3
+#define FF_PROFILE_HEVC_REXT 4
+
+/**
+ * Value of the luma sample at position (x, y) in the 2D array tab.
+ */
+#define SAMPLE(tab, x, y) ((tab)[(y) * s->sps->width + (x)])
+#define SAMPLE_CTB(tab, x, y) ((tab)[(y) * min_cb_width + (x)])
+#define SAMPLE_CBF(tab, x, y) ((tab)[((y) & ((1<<log2_trafo_size)-1)) * MAX_CU_SIZE + ((x) & ((1<<log2_trafo_size)-1))])
+
+#define IS_IDR(s) (s->nal_unit_type == NAL_IDR_W_RADL || s->nal_unit_type == NAL_IDR_N_LP)
+#define IS_BLA(s) (s->nal_unit_type == NAL_BLA_W_RADL || s->nal_unit_type == NAL_BLA_W_LP || \
+ s->nal_unit_type == NAL_BLA_N_LP)
+#define IS_IRAP(s) (s->nal_unit_type >= 16 && s->nal_unit_type <= 23)
+
+/**
+ * Table 7-3: NAL unit type codes
+ */
+enum HEVCNALUnitType {
+ HEVC_NAL_TRAIL_N = 0,
+ HEVC_NAL_TRAIL_R = 1,
+ HEVC_NAL_TSA_N = 2,
+ HEVC_NAL_TSA_R = 3,
+ HEVC_NAL_STSA_N = 4,
+ HEVC_NAL_STSA_R = 5,
+ HEVC_NAL_RADL_N = 6,
+ HEVC_NAL_RADL_R = 7,
+ HEVC_NAL_RASL_N = 8,
+ HEVC_NAL_RASL_R = 9,
+ HEVC_NAL_VCL_N10 = 10,
+ HEVC_NAL_VCL_R11 = 11,
+ HEVC_NAL_VCL_N12 = 12,
+ HEVC_NAL_VCL_R13 = 13,
+ HEVC_NAL_VCL_N14 = 14,
+ HEVC_NAL_VCL_R15 = 15,
+ HEVC_NAL_BLA_W_LP = 16,
+ HEVC_NAL_BLA_W_RADL = 17,
+ HEVC_NAL_BLA_N_LP = 18,
+ HEVC_NAL_IDR_W_RADL = 19,
+ HEVC_NAL_IDR_N_LP = 20,
+ HEVC_NAL_CRA_NUT = 21,
+ HEVC_NAL_IRAP_VCL22 = 22,
+ HEVC_NAL_IRAP_VCL23 = 23,
+ HEVC_NAL_RSV_VCL24 = 24,
+ HEVC_NAL_RSV_VCL25 = 25,
+ HEVC_NAL_RSV_VCL26 = 26,
+ HEVC_NAL_RSV_VCL27 = 27,
+ HEVC_NAL_RSV_VCL28 = 28,
+ HEVC_NAL_RSV_VCL29 = 29,
+ HEVC_NAL_RSV_VCL30 = 30,
+ HEVC_NAL_RSV_VCL31 = 31,
+ HEVC_NAL_VPS = 32,
+ HEVC_NAL_SPS = 33,
+ HEVC_NAL_PPS = 34,
+ HEVC_NAL_AUD = 35,
+ HEVC_NAL_EOS_NUT = 36,
+ HEVC_NAL_EOB_NUT = 37,
+ HEVC_NAL_FD_NUT = 38,
+ HEVC_NAL_SEI_PREFIX = 39,
+ HEVC_NAL_SEI_SUFFIX = 40,
+};
+
+enum HEVCSliceType {
+ HEVC_SLICE_B = 0,
+ HEVC_SLICE_P = 1,
+ HEVC_SLICE_I = 2,
+};
+
+enum {
+ // 7.4.3.1: vps_max_layers_minus1 is in [0, 62].
+ HEVC_MAX_LAYERS = 63,
+ // 7.4.3.1: vps_max_sub_layers_minus1 is in [0, 6].
+ HEVC_MAX_SUB_LAYERS = 7,
+ // 7.4.3.1: vps_num_layer_sets_minus1 is in [0, 1023].
+ HEVC_MAX_LAYER_SETS = 1024,
+
+ // 7.4.2.1: vps_video_parameter_set_id is u(4).
+ HEVC_MAX_VPS_COUNT = 16,
+ // 7.4.3.2.1: sps_seq_parameter_set_id is in [0, 15].
+ HEVC_MAX_SPS_COUNT = 16,
+ // 7.4.3.3.1: pps_pic_parameter_set_id is in [0, 63].
+ HEVC_MAX_PPS_COUNT = 64,
+
+ // A.4.2: MaxDpbSize is bounded above by 16.
+ HEVC_MAX_DPB_SIZE = 16,
+ // 7.4.3.1: vps_max_dec_pic_buffering_minus1[i] is in [0, MaxDpbSize - 1].
+ HEVC_MAX_REFS = HEVC_MAX_DPB_SIZE,
+
+ // 7.4.3.2.1: num_short_term_ref_pic_sets is in [0, 64].
+ HEVC_MAX_SHORT_TERM_REF_PIC_SETS = 64,
+ // 7.4.3.2.1: num_long_term_ref_pics_sps is in [0, 32].
+ HEVC_MAX_LONG_TERM_REF_PICS = 32,
+
+ // A.3: all profiles require that CtbLog2SizeY is in [4, 6].
+ HEVC_MIN_LOG2_CTB_SIZE = 4,
+ HEVC_MAX_LOG2_CTB_SIZE = 6,
+
+ // E.3.2: cpb_cnt_minus1[i] is in [0, 31].
+ HEVC_MAX_CPB_CNT = 32,
+
+ // A.4.1: in table A.6 the highest level allows a MaxLumaPs of 35 651 584.
+ HEVC_MAX_LUMA_PS = 35651584,
+ // A.4.1: pic_width_in_luma_samples and pic_height_in_luma_samples are
+ // constrained to be not greater than sqrt(MaxLumaPs * 8). Hence height/
+ // width are bounded above by sqrt(8 * 35651584) = 16888.2 samples.
+ HEVC_MAX_WIDTH = 16888,
+ HEVC_MAX_HEIGHT = 16888,
+
+ // A.4.1: table A.6 allows at most 22 tile rows for any level.
+ HEVC_MAX_TILE_ROWS = 22,
+ // A.4.1: table A.6 allows at most 20 tile columns for any level.
+ HEVC_MAX_TILE_COLUMNS = 20,
+
+ // 7.4.7.1: in the worst case (tiles_enabled_flag and
+ // entropy_coding_sync_enabled_flag are both set), entry points can be
+ // placed at the beginning of every Ctb row in every tile, giving an
+ // upper bound of (num_tile_columns_minus1 + 1) * PicHeightInCtbsY - 1.
+ // Only a stream with very high resolution and perverse parameters could
+ // get near that, though, so set a lower limit here with the maximum
+ // possible value for 4K video (at most 135 16x16 Ctb rows).
+ HEVC_MAX_ENTRY_POINT_OFFSETS = HEVC_MAX_TILE_COLUMNS * 135,
+};
+
+struct ShortTermRPS {
+ u32 num_negative_pics;
+ int num_delta_pocs;
+ int rps_idx_num_delta_pocs;
+ int delta_poc[32];
+ u8 used[32];
+};
+
+struct LongTermRPS {
+ int poc[32];
+ u8 used[32];
+ u8 nb_refs;
+};
+
+struct SliceHeader {
+ u32 pps_id;
+
+ ///< address (in raster order) of the first block in the current slice segment
+ u32 slice_segment_addr;
+ ///< address (in raster order) of the first block in the current slice
+ u32 slice_addr;
+
+ enum HEVCSliceType slice_type;
+
+ int pic_order_cnt_lsb;
+
+ u8 first_slice_in_pic_flag;
+ u8 dependent_slice_segment_flag;
+ u8 pic_output_flag;
+ u8 colour_plane_id;
+
+ ///< RPS coded in the slice header itself is stored here
+ int short_term_ref_pic_set_sps_flag;
+ int short_term_ref_pic_set_size;
+ struct ShortTermRPS slice_rps;
+ const struct ShortTermRPS *short_term_rps;
+ int long_term_ref_pic_set_size;
+ struct LongTermRPS long_term_rps;
+ u32 list_entry_lx[2][32];
+
+ u8 rpl_modification_flag[2];
+ u8 no_output_of_prior_pics_flag;
+ u8 slice_temporal_mvp_enabled_flag;
+
+ u32 nb_refs[2];
+
+ u8 slice_sample_adaptive_offset_flag[3];
+ u8 mvd_l1_zero_flag;
+
+ u8 cabac_init_flag;
+ u8 disable_deblocking_filter_flag; ///< slice_header_disable_deblocking_filter_flag
+ u8 slice_loop_filter_across_slices_enabled_flag;
+ u8 collocated_list;
+
+ u32 collocated_ref_idx;
+
+ int slice_qp_delta;
+ int slice_cb_qp_offset;
+ int slice_cr_qp_offset;
+
+ u8 cu_chroma_qp_offset_enabled_flag;
+
+ int beta_offset; ///< beta_offset_div2 * 2
+ int tc_offset; ///< tc_offset_div2 * 2
+
+ u32 max_num_merge_cand; ///< 5 - 5_minus_max_num_merge_cand
+
+ u8 *entry_point_offset;
+ int * offset;
+ int * size;
+ int num_entry_point_offsets;
+
+ char slice_qp;
+
+ u8 luma_log2_weight_denom;
+ s16 chroma_log2_weight_denom;
+
+ s16 luma_weight_l0[16];
+ s16 chroma_weight_l0[16][2];
+ s16 chroma_weight_l1[16][2];
+ s16 luma_weight_l1[16];
+
+ s16 luma_offset_l0[16];
+ s16 chroma_offset_l0[16][2];
+
+ s16 luma_offset_l1[16];
+ s16 chroma_offset_l1[16][2];
+
+ int slice_ctb_addr_rs;
+};
+
+struct HEVCWindow {
+ u32 left_offset;
+ u32 right_offset;
+ u32 top_offset;
+ u32 bottom_offset;
+};
+
+struct VUI {
+#ifdef CONFIG_AMLOGIC_MEDIA_V4L_SOFTWARE_PARSER
+ struct AVRational sar;
+#endif
+ int overscan_info_present_flag;
+ int overscan_appropriate_flag;
+
+ int video_signal_type_present_flag;
+ int video_format;
+ int video_full_range_flag;
+ int colour_description_present_flag;
+ u8 colour_primaries;
+ u8 transfer_characteristic;
+ u8 matrix_coeffs;
+
+ int chroma_loc_info_present_flag;
+ int chroma_sample_loc_type_top_field;
+ int chroma_sample_loc_type_bottom_field;
+ int neutra_chroma_indication_flag;
+
+ int field_seq_flag;
+ int frame_field_info_present_flag;
+
+ int default_display_window_flag;
+ struct HEVCWindow def_disp_win;
+
+ int vui_timing_info_present_flag;
+ u32 vui_num_units_in_tick;
+ u32 vui_time_scale;
+ int vui_poc_proportional_to_timing_flag;
+ int vui_num_ticks_poc_diff_one_minus1;
+ int vui_hrd_parameters_present_flag;
+
+ int bitstream_restriction_flag;
+ int tiles_fixed_structure_flag;
+ int motion_vectors_over_pic_boundaries_flag;
+ int restricted_ref_pic_lists_flag;
+ int min_spatial_segmentation_idc;
+ int max_bytes_per_pic_denom;
+ int max_bits_per_min_cu_denom;
+ int log2_max_mv_length_horizontal;
+ int log2_max_mv_length_vertical;
+};
+
+struct PTLCommon {
+ u8 profile_space;
+ u8 tier_flag;
+ u8 profile_idc;
+ u8 profile_compatibility_flag[32];
+ u8 level_idc;
+ u8 progressive_source_flag;
+ u8 interlaced_source_flag;
+ u8 non_packed_constraint_flag;
+ u8 frame_only_constraint_flag;
+};
+
+struct PTL {
+ struct PTLCommon general_ptl;
+ struct PTLCommon sub_layer_ptl[HEVC_MAX_SUB_LAYERS];
+
+ u8 sub_layer_profile_present_flag[HEVC_MAX_SUB_LAYERS];
+ u8 sub_layer_level_present_flag[HEVC_MAX_SUB_LAYERS];
+};
+
+struct h265_VPS_t {
+ u8 vps_temporal_id_nesting_flag;
+ int vps_max_layers;
+ int vps_max_sub_layers; ///< vps_max_temporal_layers_minus1 + 1
+
+ struct PTL ptl;
+ int vps_sub_layer_ordering_info_present_flag;
+ u32 vps_max_dec_pic_buffering[HEVC_MAX_SUB_LAYERS];
+ u32 vps_num_reorder_pics[HEVC_MAX_SUB_LAYERS];
+ u32 vps_max_latency_increase[HEVC_MAX_SUB_LAYERS];
+ int vps_max_layer_id;
+ int vps_num_layer_sets; ///< vps_num_layer_sets_minus1 + 1
+ u8 vps_timing_info_present_flag;
+ u32 vps_num_units_in_tick;
+ u32 vps_time_scale;
+ u8 vps_poc_proportional_to_timing_flag;
+ int vps_num_ticks_poc_diff_one; ///< vps_num_ticks_poc_diff_one_minus1 + 1
+ int vps_num_hrd_parameters;
+};
+
+struct ScalingList {
+ /* This is a little wasteful, since sizeID 0 only needs 8 coeffs,
+ * and size ID 3 only has 2 arrays, not 6. */
+ u8 sl[4][6][64];
+ u8 sl_dc[2][6];
+};
+
+struct h265_SPS_t {
+ u8 vps_id;
+ u8 sps_id;
+ int chroma_format_idc;
+ u8 separate_colour_plane_flag;
+
+ struct HEVCWindow output_window;
+ struct HEVCWindow pic_conf_win;
+
+ int bit_depth;
+ int bit_depth_chroma;
+ int pixel_shift;
+ int pix_fmt;
+
+ u32 log2_max_poc_lsb;
+ int pcm_enabled_flag;
+
+ int max_sub_layers;
+ struct {
+ int max_dec_pic_buffering;
+ int num_reorder_pics;
+ int max_latency_increase;
+ } temporal_layer[HEVC_MAX_SUB_LAYERS];
+ u8 temporal_id_nesting_flag;
+
+ struct VUI vui;
+ struct PTL ptl;
+
+ u8 scaling_list_enable_flag;
+ struct ScalingList scaling_list;
+
+ u32 nb_st_rps;
+ struct ShortTermRPS st_rps[HEVC_MAX_SHORT_TERM_REF_PIC_SETS];
+
+ u8 amp_enabled_flag;
+ u8 sao_enabled;
+
+ u8 long_term_ref_pics_present_flag;
+ u16 lt_ref_pic_poc_lsb_sps[HEVC_MAX_LONG_TERM_REF_PICS];
+ u8 used_by_curr_pic_lt_sps_flag[HEVC_MAX_LONG_TERM_REF_PICS];
+ u8 num_long_term_ref_pics_sps;
+
+ struct {
+ u8 bit_depth;
+ u8 bit_depth_chroma;
+ u32 log2_min_pcm_cb_size;
+ u32 log2_max_pcm_cb_size;
+ u8 loop_filter_disable_flag;
+ } pcm;
+ u8 sps_temporal_mvp_enabled_flag;
+ u8 sps_strong_intra_smoothing_enable_flag;
+
+ u32 log2_min_cb_size;
+ u32 log2_diff_max_min_coding_block_size;
+ u32 log2_min_tb_size;
+ u32 log2_max_trafo_size;
+ u32 log2_ctb_size;
+ u32 log2_min_pu_size;
+
+ int max_transform_hierarchy_depth_inter;
+ int max_transform_hierarchy_depth_intra;
+
+ int sps_range_extension_flag;
+ int transform_skip_rotation_enabled_flag;
+ int transform_skip_context_enabled_flag;
+ int implicit_rdpcm_enabled_flag;
+ int explicit_rdpcm_enabled_flag;
+ int extended_precision_processing_flag;
+ int intra_smoothing_disabled_flag;
+ int high_precision_offsets_enabled_flag;
+ int persistent_rice_adaptation_enabled_flag;
+ int cabac_bypass_alignment_enabled_flag;
+
+ ///< coded frame dimension in various units
+ int width;
+ int height;
+ int ctb_width;
+ int ctb_height;
+ int ctb_size;
+ int min_cb_width;
+ int min_cb_height;
+ int min_tb_width;
+ int min_tb_height;
+ int min_pu_width;
+ int min_pu_height;
+ int tb_mask;
+
+ int hshift[3];
+ int vshift[3];
+
+ int qp_bd_offset;
+
+ u8 data[4096];
+ int data_size;
+};
+
+struct h265_PPS_t {
+ u32 sps_id; ///< seq_parameter_set_id
+
+ u8 sign_data_hiding_flag;
+
+ u8 cabac_init_present_flag;
+
+ int num_ref_idx_l0_default_active; ///< num_ref_idx_l0_default_active_minus1 + 1
+ int num_ref_idx_l1_default_active; ///< num_ref_idx_l1_default_active_minus1 + 1
+ int pic_init_qp_minus26;
+
+ u8 constrained_intra_pred_flag;
+ u8 transform_skip_enabled_flag;
+
+ u8 cu_qp_delta_enabled_flag;
+ int diff_cu_qp_delta_depth;
+
+ int cb_qp_offset;
+ int cr_qp_offset;
+ u8 pic_slice_level_chroma_qp_offsets_present_flag;
+ u8 weighted_pred_flag;
+ u8 weighted_bipred_flag;
+ u8 output_flag_present_flag;
+ u8 transquant_bypass_enable_flag;
+
+ u8 dependent_slice_segments_enabled_flag;
+ u8 tiles_enabled_flag;
+ u8 entropy_coding_sync_enabled_flag;
+
+ int num_tile_columns; ///< num_tile_columns_minus1 + 1
+ int num_tile_rows; ///< num_tile_rows_minus1 + 1
+ u8 uniform_spacing_flag;
+ u8 loop_filter_across_tiles_enabled_flag;
+
+ u8 seq_loop_filter_across_slices_enabled_flag;
+
+ u8 deblocking_filter_control_present_flag;
+ u8 deblocking_filter_override_enabled_flag;
+ u8 disable_dbf;
+ int beta_offset; ///< beta_offset_div2 * 2
+ int tc_offset; ///< tc_offset_div2 * 2
+
+ u8 scaling_list_data_present_flag;
+ struct ScalingList scaling_list;
+
+ u8 lists_modification_present_flag;
+ int log2_parallel_merge_level; ///< log2_parallel_merge_level_minus2 + 2
+ int num_extra_slice_header_bits;
+ u8 slice_header_extension_present_flag;
+ u8 log2_max_transform_skip_block_size;
+ u8 cross_component_prediction_enabled_flag;
+ u8 chroma_qp_offset_list_enabled_flag;
+ u8 diff_cu_chroma_qp_offset_depth;
+ u8 chroma_qp_offset_list_len_minus1;
+ char cb_qp_offset_list[6];
+ char cr_qp_offset_list[6];
+ u8 log2_sao_offset_scale_luma;
+ u8 log2_sao_offset_scale_chroma;
+
+ // Inferred parameters
+ u32 *column_width; ///< ColumnWidth
+ u32 *row_height; ///< RowHeight
+ u32 *col_bd; ///< ColBd
+ u32 *row_bd; ///< RowBd
+ int *col_idxX;
+
+ int *ctb_addr_rs_to_ts; ///< CtbAddrRSToTS
+ int *ctb_addr_ts_to_rs; ///< CtbAddrTSToRS
+ int *tile_id; ///< TileId
+ int *tile_pos_rs; ///< TilePosRS
+ int *min_tb_addr_zs; ///< MinTbAddrZS
+ int *min_tb_addr_zs_tab;///< MinTbAddrZS
+};
+
+struct h265_param_sets {
+ bool vps_parsed;
+ bool sps_parsed;
+ bool pps_parsed;
+ /* currently active parameter sets */
+ struct h265_VPS_t vps;
+ struct h265_SPS_t sps;
+ struct h265_PPS_t pps;
+};
+
+#ifdef CONFIG_AMLOGIC_MEDIA_V4L_SOFTWARE_PARSER
+int h265_decode_extradata_ps(u8 *data, int size, struct h265_param_sets *ps);
+#else
+inline int h265_decode_extradata_ps(u8 *data, int size, struct h265_param_sets *ps) { return -1; }
+#endif
+
+#endif /* AML_HEVC_PARSER_H */
+
diff --git a/drivers/amvdec_ports/decoder/aml_mjpeg_parser.c b/drivers/amvdec_ports/decoder/aml_mjpeg_parser.c
new file mode 100644
index 0000000..c582ab0
--- /dev/null
+++ b/drivers/amvdec_ports/decoder/aml_mjpeg_parser.c
@@ -0,0 +1,397 @@
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/vmalloc.h>
+#include <linux/mm.h>
+#include <linux/string.h>
+
+#include "aml_mjpeg_parser.h"
+#include "../utils/get_bits.h"
+#include "../utils/put_bits.h"
+#include "../utils/golomb.h"
+#include "../utils/common.h"
+#include "utils.h"
+
+/* return the 8 bit start code value and update the search
+state. Return -1 if no start code found */
+static int find_marker(const u8 **pbuf_ptr, const u8 *buf_end)
+{
+ const u8 *buf_ptr;
+ u32 v, v2;
+ int val;
+ int skipped = 0;
+
+ buf_ptr = *pbuf_ptr;
+ while (buf_end - buf_ptr > 1) {
+ v = *buf_ptr++;
+ v2 = *buf_ptr;
+ if ((v == 0xff) && (v2 >= 0xc0) && (v2 <= 0xfe) && buf_ptr < buf_end) {
+ val = *buf_ptr++;
+ goto found;
+ }
+ skipped++;
+ }
+ buf_ptr = buf_end;
+ val = -1;
+found:
+ v4l_dbg(0, V4L_DEBUG_CODEC_PARSER, "find_marker skipped %d bytes\n", skipped);
+ *pbuf_ptr = buf_ptr;
+
+ return val;
+}
+
+int ff_mjpeg_find_marker(struct MJpegDecodeContext *s,
+ const u8 **buf_ptr, const u8 *buf_end,
+ const u8 **unescaped_buf_ptr,
+ int *unescaped_buf_size)
+{
+ int start_code;
+
+ start_code = find_marker(buf_ptr, buf_end);
+
+ /* unescape buffer of SOS, use special treatment for JPEG-LS */
+ if (start_code == SOS && !s->ls) {
+ const u8 *src = *buf_ptr;
+ const u8 *ptr = src;
+ u8 *dst = s->buffer;
+
+ #define copy_data_segment(skip) do { \
+ int length = (ptr - src) - (skip); \
+ if (length > 0) { \
+ memcpy(dst, src, length); \
+ dst += length; \
+ src = ptr; \
+ } \
+ } while (0)
+
+
+ while (ptr < buf_end) {
+ u8 x = *(ptr++);
+
+ if (x == 0xff) {
+ int skip = 0;
+ while (ptr < buf_end && x == 0xff) {
+ x = *(ptr++);
+ skip++;
+ }
+
+ /* 0xFF, 0xFF, ... */
+ if (skip > 1) {
+ copy_data_segment(skip);
+
+ /* decrement src as it is equal to ptr after the
+ * copy_data_segment macro and we might want to
+ * copy the current value of x later on */
+ src--;
+ }
+
+ if (x < 0xd0 || x > 0xd7) {
+ copy_data_segment(1);
+ if (x)
+ break;
+ }
+ }
+ if (src < ptr)
+ copy_data_segment(0);
+ }
+ #undef copy_data_segment
+
+ *unescaped_buf_ptr = s->buffer;
+ *unescaped_buf_size = dst - s->buffer;
+ memset(s->buffer + *unescaped_buf_size, 0,
+ AV_INPUT_BUFFER_PADDING_SIZE);
+
+ v4l_dbg(0, V4L_DEBUG_CODEC_PARSER, "escaping removed %d bytes\n",
+ (int)((buf_end - *buf_ptr) - (dst - s->buffer)));
+ } else if (start_code == SOS && s->ls) {
+ const u8 *src = *buf_ptr;
+ u8 *dst = s->buffer;
+ int bit_count = 0;
+ int t = 0, b = 0;
+ struct put_bits_context pb;
+
+ /* find marker */
+ while (src + t < buf_end) {
+ u8 x = src[t++];
+ if (x == 0xff) {
+ while ((src + t < buf_end) && x == 0xff)
+ x = src[t++];
+ if (x & 0x80) {
+ t -= FFMIN(2, t);
+ break;
+ }
+ }
+ }
+ bit_count = t * 8;
+ init_put_bits(&pb, dst, t);
+
+ /* unescape bitstream */
+ while (b < t) {
+ u8 x = src[b++];
+ put_bits(&pb, 8, x);
+ if (x == 0xFF && b < t) {
+ x = src[b++];
+ if (x & 0x80) {
+ v4l_dbg(0, V4L_DEBUG_CODEC_ERROR, "Invalid escape sequence\n");
+ x &= 0x7f;
+ }
+ put_bits(&pb, 7, x);
+ bit_count--;
+ }
+ }
+ flush_put_bits(&pb);
+
+ *unescaped_buf_ptr = dst;
+ *unescaped_buf_size = (bit_count + 7) >> 3;
+ memset(s->buffer + *unescaped_buf_size, 0,
+ AV_INPUT_BUFFER_PADDING_SIZE);
+ } else {
+ *unescaped_buf_ptr = *buf_ptr;
+ *unescaped_buf_size = buf_end - *buf_ptr;
+ }
+
+ return start_code;
+}
+
+
+int ff_mjpeg_decode_sof(struct MJpegDecodeContext *s)
+{
+ int len, nb_components, i, width, height, bits, size_change;
+ int h_count[MAX_COMPONENTS] = { 0 };
+ int v_count[MAX_COMPONENTS] = { 0 };
+
+ s->cur_scan = 0;
+ memset(s->upscale_h, 0, sizeof(s->upscale_h));
+ memset(s->upscale_v, 0, sizeof(s->upscale_v));
+
+ /* XXX: verify len field validity */
+ len = get_bits(&s->gb, 16);
+ bits = get_bits(&s->gb, 8);
+
+ if (bits > 16 || bits < 1) {
+ v4l_dbg(0, V4L_DEBUG_CODEC_ERROR, "bits %d is invalid\n", bits);
+ return -1;
+ }
+
+ height = get_bits(&s->gb, 16);
+ width = get_bits(&s->gb, 16);
+
+ v4l_dbg(0, V4L_DEBUG_CODEC_PARSER, "sof0: picture: %dx%d\n", width, height);
+
+ nb_components = get_bits(&s->gb, 8);
+ if (nb_components <= 0 ||
+ nb_components > MAX_COMPONENTS)
+ return -1;
+
+ s->nb_components = nb_components;
+ s->h_max = 1;
+ s->v_max = 1;
+ for (i = 0; i < nb_components; i++) {
+ /* component id */
+ s->component_id[i] = get_bits(&s->gb, 8) - 1;
+ h_count[i] = get_bits(&s->gb, 4);
+ v_count[i] = get_bits(&s->gb, 4);
+ /* compute hmax and vmax (only used in interleaved case) */
+ if (h_count[i] > s->h_max)
+ s->h_max = h_count[i];
+ if (v_count[i] > s->v_max)
+ s->v_max = v_count[i];
+ s->quant_index[i] = get_bits(&s->gb, 8);
+ if (s->quant_index[i] >= 4) {
+ v4l_dbg(0, V4L_DEBUG_CODEC_ERROR, "quant_index is invalid\n");
+ return -1;
+ }
+ if (!h_count[i] || !v_count[i]) {
+ v4l_dbg(0, V4L_DEBUG_CODEC_ERROR, "Invalid sampling factor in component %d %d:%d\n",
+ i, h_count[i], v_count[i]);
+ return -1;
+ }
+
+ v4l_dbg(0, V4L_DEBUG_CODEC_PARSER, "component %d %d:%d id: %d quant:%d\n",
+ i, h_count[i], v_count[i],
+ s->component_id[i], s->quant_index[i]);
+ }
+ if (nb_components == 4
+ && s->component_id[0] == 'C' - 1
+ && s->component_id[1] == 'M' - 1
+ && s->component_id[2] == 'Y' - 1
+ && s->component_id[3] == 'K' - 1)
+ s->adobe_transform = 0;
+
+ /* if different size, realloc/alloc picture */
+ if (width != s->width || height != s->height || bits != s->bits ||
+ memcmp(s->h_count, h_count, sizeof(h_count)) ||
+ memcmp(s->v_count, v_count, sizeof(v_count))) {
+ size_change = 1;
+
+ s->width = width;
+ s->height = height;
+ s->bits = bits;
+ memcpy(s->h_count, h_count, sizeof(h_count));
+ memcpy(s->v_count, v_count, sizeof(v_count));
+ s->interlaced = 0;
+ s->got_picture = 0;
+ } else {
+ size_change = 0;
+ }
+
+ return 0;
+}
+
+static int ff_mjpeg_decode_frame(u8 *buf, int buf_size, struct MJpegDecodeContext *s)
+{
+ const u8 *buf_end, *buf_ptr;
+ const u8 *unescaped_buf_ptr;
+ int unescaped_buf_size;
+ int start_code;
+ int ret = 0;
+
+ buf_ptr = buf;
+ buf_end = buf + buf_size;
+ while (buf_ptr < buf_end) {
+ /* find start next marker */
+ start_code = ff_mjpeg_find_marker(s, &buf_ptr, buf_end,
+ &unescaped_buf_ptr,
+ &unescaped_buf_size);
+ /* EOF */
+ if (start_code < 0) {
+ break;
+ } else if (unescaped_buf_size > INT_MAX / 8) {
+ v4l_dbg(0, V4L_DEBUG_CODEC_ERROR, "MJPEG packet 0x%x too big (%d/%d), corrupt data?\n",
+ start_code, unescaped_buf_size, buf_size);
+ return -1;
+ }
+ v4l_dbg(0, V4L_DEBUG_CODEC_PARSER, "marker=%x avail_size_in_buf=%d\n",
+ start_code, (int)(buf_end - buf_ptr));
+
+ ret = init_get_bits8(&s->gb, unescaped_buf_ptr, unescaped_buf_size);
+ if (ret < 0) {
+ v4l_dbg(0, V4L_DEBUG_CODEC_ERROR, "invalid buffer\n");
+ goto fail;
+ }
+
+ s->start_code = start_code;
+ v4l_dbg(0, V4L_DEBUG_CODEC_PARSER, "startcode: %X\n", start_code);
+
+ switch (start_code) {
+ case SOF0:
+ case SOF1:
+ case SOF2:
+ case SOF3:
+ case SOF48:
+ case SOI:
+ case SOS:
+ case EOI:
+ break;
+ default:
+ goto skip;
+ }
+
+ switch (start_code) {
+ case SOI:
+ s->restart_interval = 0;
+ s->restart_count = 0;
+ s->raw_image_buffer = buf_ptr;
+ s->raw_image_buffer_size = buf_end - buf_ptr;
+ /* nothing to do on SOI */
+ break;
+ case SOF0:
+ case SOF1:
+ if (start_code == SOF0)
+ s->profile = FF_PROFILE_MJPEG_HUFFMAN_BASELINE_DCT;
+ else
+ s->profile = FF_PROFILE_MJPEG_HUFFMAN_EXTENDED_SEQUENTIAL_DCT;
+ s->lossless = 0;
+ s->ls = 0;
+ s->progressive = 0;
+ if ((ret = ff_mjpeg_decode_sof(s)) < 0)
+ goto fail;
+ break;
+ case SOF2:
+ s->profile = FF_PROFILE_MJPEG_HUFFMAN_PROGRESSIVE_DCT;
+ s->lossless = 0;
+ s->ls = 0;
+ s->progressive = 1;
+ if ((ret = ff_mjpeg_decode_sof(s)) < 0)
+ goto fail;
+ break;
+ case SOF3:
+ s->profile = FF_PROFILE_MJPEG_HUFFMAN_LOSSLESS;
+ s->properties |= FF_CODEC_PROPERTY_LOSSLESS;
+ s->lossless = 1;
+ s->ls = 0;
+ s->progressive = 0;
+ if ((ret = ff_mjpeg_decode_sof(s)) < 0)
+ goto fail;
+ break;
+ case SOF48:
+ s->profile = FF_PROFILE_MJPEG_JPEG_LS;
+ s->properties |= FF_CODEC_PROPERTY_LOSSLESS;
+ s->lossless = 1;
+ s->ls = 1;
+ s->progressive = 0;
+ if ((ret = ff_mjpeg_decode_sof(s)) < 0)
+ goto fail;
+ break;
+ case EOI:
+ goto the_end;
+ case DHT:
+ case LSE:
+ case SOS:
+ case DRI:
+ case SOF5:
+ case SOF6:
+ case SOF7:
+ case SOF9:
+ case SOF10:
+ case SOF11:
+ case SOF13:
+ case SOF14:
+ case SOF15:
+ case JPG:
+ v4l_dbg(0, V4L_DEBUG_CODEC_ERROR, "mjpeg: unsupported coding type (%x)\n", start_code);
+ break;
+ }
+skip:
+ /* eof process start code */
+ buf_ptr += (get_bits_count(&s->gb) + 7) / 8;
+ v4l_dbg(0, V4L_DEBUG_CODEC_PARSER, "marker parser used %d bytes (%d bits)\n",
+ (get_bits_count(&s->gb) + 7) / 8, get_bits_count(&s->gb));
+ }
+
+ v4l_dbg(0, V4L_DEBUG_CODEC_ERROR, "No JPEG data found in image\n");
+ return -1;
+fail:
+ s->got_picture = 0;
+ return ret;
+the_end:
+ v4l_dbg(0, V4L_DEBUG_CODEC_PARSER, "decode frame unused %d bytes\n", (int)(buf_end - buf_ptr));
+
+ return 0;
+}
+
+int mjpeg_decode_extradata_ps(u8 *buf, int size, struct mjpeg_param_sets *ps)
+{
+ int ret;
+
+ ps->head_parsed = false;
+
+ ps->dec_ps.buf_size = size;
+ ps->dec_ps.buffer = vzalloc(size + AV_INPUT_BUFFER_PADDING_SIZE);
+ if (!ps->dec_ps.buffer)
+ return -1;
+
+ ret = ff_mjpeg_decode_frame(buf, size, &ps->dec_ps);
+ if (ret) {
+ v4l_dbg(0, V4L_DEBUG_CODEC_ERROR, "parse extra data failed. err: %d\n", ret);
+ vfree(ps->dec_ps.buffer);
+ return ret;
+ }
+
+ if (ps->dec_ps.width && ps->dec_ps.height)
+ ps->head_parsed = true;
+
+ vfree(ps->dec_ps.buffer);
+
+ return 0;
+}
+
diff --git a/drivers/amvdec_ports/decoder/aml_mjpeg_parser.h b/drivers/amvdec_ports/decoder/aml_mjpeg_parser.h
new file mode 100644
index 0000000..4704ba0
--- /dev/null
+++ b/drivers/amvdec_ports/decoder/aml_mjpeg_parser.h
@@ -0,0 +1,170 @@
+#ifndef AML_MJPEG_PARSER_H
+#define AML_MJPEG_PARSER_H
+
+#include "../aml_vcodec_drv.h"
+#include "../utils/common.h"
+#ifdef CONFIG_AMLOGIC_MEDIA_V4L_SOFTWARE_PARSER
+#include "../utils/get_bits.h"
+#endif
+
+#define FF_PROFILE_MJPEG_HUFFMAN_BASELINE_DCT 0xc0
+#define FF_PROFILE_MJPEG_HUFFMAN_EXTENDED_SEQUENTIAL_DCT 0xc1
+#define FF_PROFILE_MJPEG_HUFFMAN_PROGRESSIVE_DCT 0xc2
+#define FF_PROFILE_MJPEG_HUFFMAN_LOSSLESS 0xc3
+#define FF_PROFILE_MJPEG_JPEG_LS 0xf7
+
+#define FF_CODEC_PROPERTY_LOSSLESS 0x00000001
+#define FF_CODEC_PROPERTY_CLOSED_CAPTIONS 0x00000002
+
+#define MAX_COMPONENTS 4
+
+/* JPEG marker codes */
+enum JpegMarker {
+ /* start of frame */
+ SOF0 = 0xc0, /* baseline */
+ SOF1 = 0xc1, /* extended sequential, huffman */
+ SOF2 = 0xc2, /* progressive, huffman */
+ SOF3 = 0xc3, /* lossless, huffman */
+
+ SOF5 = 0xc5, /* differential sequential, huffman */
+ SOF6 = 0xc6, /* differential progressive, huffman */
+ SOF7 = 0xc7, /* differential lossless, huffman */
+ JPG = 0xc8, /* reserved for JPEG extension */
+ SOF9 = 0xc9, /* extended sequential, arithmetic */
+ SOF10 = 0xca, /* progressive, arithmetic */
+ SOF11 = 0xcb, /* lossless, arithmetic */
+
+ SOF13 = 0xcd, /* differential sequential, arithmetic */
+ SOF14 = 0xce, /* differential progressive, arithmetic */
+ SOF15 = 0xcf, /* differential lossless, arithmetic */
+
+ DHT = 0xc4, /* define huffman tables */
+
+ DAC = 0xcc, /* define arithmetic-coding conditioning */
+
+ /* restart with modulo 8 count "m" */
+ RST0 = 0xd0,
+ RST1 = 0xd1,
+ RST2 = 0xd2,
+ RST3 = 0xd3,
+ RST4 = 0xd4,
+ RST5 = 0xd5,
+ RST6 = 0xd6,
+ RST7 = 0xd7,
+
+ SOI = 0xd8, /* start of image */
+ EOI = 0xd9, /* end of image */
+ SOS = 0xda, /* start of scan */
+ DQT = 0xdb, /* define quantization tables */
+ DNL = 0xdc, /* define number of lines */
+ DRI = 0xdd, /* define restart interval */
+ DHP = 0xde, /* define hierarchical progression */
+ EXP = 0xdf, /* expand reference components */
+
+ APP0 = 0xe0,
+ APP1 = 0xe1,
+ APP2 = 0xe2,
+ APP3 = 0xe3,
+ APP4 = 0xe4,
+ APP5 = 0xe5,
+ APP6 = 0xe6,
+ APP7 = 0xe7,
+ APP8 = 0xe8,
+ APP9 = 0xe9,
+ APP10 = 0xea,
+ APP11 = 0xeb,
+ APP12 = 0xec,
+ APP13 = 0xed,
+ APP14 = 0xee,
+ APP15 = 0xef,
+
+ JPG0 = 0xf0,
+ JPG1 = 0xf1,
+ JPG2 = 0xf2,
+ JPG3 = 0xf3,
+ JPG4 = 0xf4,
+ JPG5 = 0xf5,
+ JPG6 = 0xf6,
+ SOF48 = 0xf7, ///< JPEG-LS
+ LSE = 0xf8, ///< JPEG-LS extension parameters
+ JPG9 = 0xf9,
+ JPG10 = 0xfa,
+ JPG11 = 0xfb,
+ JPG12 = 0xfc,
+ JPG13 = 0xfd,
+
+ COM = 0xfe, /* comment */
+
+ TEM = 0x01, /* temporary private use for arithmetic coding */
+
+ /* 0x02 -> 0xbf reserved */
+};
+
+struct VLC {
+ int bits;
+ short (*table)[2]; ///< code, bits
+ int table_size, table_allocated;
+};
+
+struct MJpegDecodeContext {
+#ifdef CONFIG_AMLOGIC_MEDIA_V4L_SOFTWARE_PARSER
+ struct get_bits_context gb;
+#endif
+ int buf_size;
+
+ int start_code; /* current start code */
+ int buffer_size;
+ u8 *buffer;
+
+ u16 quant_matrixes[4][64];
+ struct VLC vlcs[3][4];
+ int qscale[4]; ///< quantizer scale calculated from quant_matrixes
+
+ int first_picture; /* true if decoding first picture */
+ int interlaced; /* true if interlaced */
+ int bottom_field; /* true if bottom field */
+ int lossless;
+ int ls;
+ int progressive;
+ u8 upscale_h[4];
+ u8 upscale_v[4];
+ int bits; /* bits per component */
+ int adobe_transform;
+
+ int width, height;
+ int mb_width, mb_height;
+ int nb_components;
+ int block_stride[MAX_COMPONENTS];
+ int component_id[MAX_COMPONENTS];
+ int h_count[MAX_COMPONENTS]; /* horizontal and vertical count for each component */
+ int v_count[MAX_COMPONENTS];
+ int h_scount[MAX_COMPONENTS];
+ int v_scount[MAX_COMPONENTS];
+ int h_max, v_max; /* maximum h and v counts */
+ int quant_index[4]; /* quant table index for each component */
+ int got_picture; ///< we found a SOF and picture is valid, too.
+ int restart_interval;
+ int restart_count;
+ int cur_scan; /* current scan, used by JPEG-LS */
+
+ // Raw stream data for hwaccel use.
+ const u8 *raw_image_buffer;
+ int raw_image_buffer_size;
+
+ int profile;
+ u32 properties;
+};
+
+struct mjpeg_param_sets {
+ bool head_parsed;
+ /* currently active parameter sets */
+ struct MJpegDecodeContext dec_ps;
+};
+
+#ifdef CONFIG_AMLOGIC_MEDIA_V4L_SOFTWARE_PARSER
+int mjpeg_decode_extradata_ps(u8 *buf, int size, struct mjpeg_param_sets *ps);
+#else
+inline int mjpeg_decode_extradata_ps(u8 *buf, int size, struct mjpeg_param_sets *ps) { return -1; }
+#endif
+
+#endif
diff --git a/drivers/amvdec_ports/decoder/aml_mpeg12_parser.c b/drivers/amvdec_ports/decoder/aml_mpeg12_parser.c
new file mode 100644
index 0000000..748a83f
--- /dev/null
+++ b/drivers/amvdec_ports/decoder/aml_mpeg12_parser.c
@@ -0,0 +1,198 @@
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/vmalloc.h>
+#include <linux/mm.h>
+#include <linux/string.h>
+
+#include "aml_mpeg12_parser.h"
+#include "../utils/get_bits.h"
+#include "../utils/put_bits.h"
+#include "../utils/golomb.h"
+#include "../utils/common.h"
+#include "utils.h"
+
+const struct AVRational ff_mpeg12_frame_rate_tab[16] = {
+ { 0, 0},
+ {24000, 1001},
+ { 24, 1},
+ { 25, 1},
+ {30000, 1001},
+ { 30, 1},
+ { 50, 1},
+ {60000, 1001},
+ { 60, 1},
+ // Xing's 15fps: (9)
+ { 15, 1},
+ // libmpeg3's "Unofficial economy rates": (10-13)
+ { 5, 1},
+ { 10, 1},
+ { 12, 1},
+ { 15, 1},
+ { 0, 0},
+};
+
+const u8 *avpriv_find_start_code(const u8 *p, const u8 *end, u32 *state)
+{
+ int i;
+
+ if (p >= end)
+ return end;
+
+ for (i = 0; i < 3; i++) {
+ u32 tmp = *state << 8;
+ *state = tmp + *(p++);
+ if (tmp == 0x100 || p == end)
+ return p;
+ }
+
+ while (p < end) {
+ if (p[-1] > 1 ) p += 3;
+ else if (p[-2] ) p += 2;
+ else if (p[-3]|(p[-1]-1)) p++;
+ else {
+ p++;
+ break;
+ }
+ }
+
+ p = FFMIN(p, end) - 4;
+ *state = AV_RB32(p);
+
+ return p + 4;
+}
+
+static void mpegvideo_extract_headers(const u8 *buf, int buf_size,
+ struct mpeg12_param_sets *ps)
+{
+ struct MpvParseContext *pc = &ps->dec_ps;
+ const u8 *buf_end = buf + buf_size;
+ u32 start_code;
+ int frame_rate_index, ext_type, bytes_left;
+ int frame_rate_ext_n, frame_rate_ext_d;
+ int top_field_first, repeat_first_field, progressive_frame;
+ int horiz_size_ext, vert_size_ext, bit_rate_ext;
+ int bit_rate = 0;
+ int vbv_delay = 0;
+ int chroma_format;
+ enum AVPixelFormat pix_fmt = AV_PIX_FMT_NONE;
+ //FIXME replace the crap with get_bits()
+ pc->repeat_pict = 0;
+
+ while (buf < buf_end) {
+ start_code= -1;
+ buf= avpriv_find_start_code(buf, buf_end, &start_code);
+ bytes_left = buf_end - buf;
+ switch (start_code) {
+ case PICTURE_START_CODE:
+ if (bytes_left >= 2) {
+ pc->pict_type = (buf[1] >> 3) & 7;
+ if (bytes_left >= 4)
+ vbv_delay = ((buf[1] & 0x07) << 13) | (buf[2] << 5) | (buf[3] >> 3);
+ }
+ break;
+ case SEQ_START_CODE:
+ if (bytes_left >= 7) {
+ pc->width = (buf[0] << 4) | (buf[1] >> 4);
+ pc->height = ((buf[1] & 0x0f) << 8) | buf[2];
+
+ pix_fmt = AV_PIX_FMT_YUV420P;
+ frame_rate_index = buf[3] & 0xf;
+ pc->frame_rate = ff_mpeg12_frame_rate_tab[frame_rate_index];
+ bit_rate = (buf[4]<<10) | (buf[5]<<2) | (buf[6]>>6);
+ pc->ticks_per_frame = 1;
+ }
+ break;
+ case EXT_START_CODE:
+ if (bytes_left >= 1) {
+ ext_type = (buf[0] >> 4);
+ switch (ext_type) {
+ case 0x1: /* sequence extension */
+ if (bytes_left >= 6) {
+ horiz_size_ext = ((buf[1] & 1) << 1) | (buf[2] >> 7);
+ vert_size_ext = (buf[2] >> 5) & 3;
+ bit_rate_ext = ((buf[2] & 0x1F)<<7) | (buf[3]>>1);
+ frame_rate_ext_n = (buf[5] >> 5) & 3;
+ frame_rate_ext_d = (buf[5] & 0x1f);
+ pc->progressive_sequence = buf[1] & (1 << 3);
+ pc->has_b_frames= !(buf[5] >> 7);
+
+ chroma_format = (buf[1] >> 1) & 3;
+ switch (chroma_format) {
+ case 1: pix_fmt = AV_PIX_FMT_YUV420P; break;
+ case 2: pix_fmt = AV_PIX_FMT_YUV422P; break;
+ case 3: pix_fmt = AV_PIX_FMT_YUV444P; break;
+ }
+
+ pc->width = (pc->width & 0xFFF) | (horiz_size_ext << 12);
+ pc->height = (pc->height& 0xFFF) | ( vert_size_ext << 12);
+ bit_rate = (bit_rate&0x3FFFF) | (bit_rate_ext << 18);
+ //if(did_set_size)
+ //set_dim_ret = ff_set_dimensions(avctx, pc->width, pc->height);
+ pc->framerate.num = pc->frame_rate.num * (frame_rate_ext_n + 1);
+ pc->framerate.den = pc->frame_rate.den * (frame_rate_ext_d + 1);
+ pc->ticks_per_frame = 2;
+ }
+ break;
+ case 0x8: /* picture coding extension */
+ if (bytes_left >= 5) {
+ top_field_first = buf[3] & (1 << 7);
+ repeat_first_field = buf[3] & (1 << 1);
+ progressive_frame = buf[4] & (1 << 7);
+
+ /* check if we must repeat the frame */
+ pc->repeat_pict = 1;
+ if (repeat_first_field) {
+ if (pc->progressive_sequence) {
+ if (top_field_first)
+ pc->repeat_pict = 5;
+ else
+ pc->repeat_pict = 3;
+ } else if (progressive_frame) {
+ pc->repeat_pict = 2;
+ }
+ }
+
+ if (!pc->progressive_sequence && !progressive_frame) {
+ if (top_field_first)
+ pc->field_order = AV_FIELD_TT;
+ else
+ pc->field_order = AV_FIELD_BB;
+ } else
+ pc->field_order = AV_FIELD_PROGRESSIVE;
+ }
+ break;
+ }
+ }
+ break;
+ case -1:
+ goto the_end;
+ default:
+ /* we stop parsing when we encounter a slice. It ensures
+ that this function takes a negligible amount of time */
+ if (start_code >= SLICE_MIN_START_CODE &&
+ start_code <= SLICE_MAX_START_CODE)
+ goto the_end;
+ break;
+ }
+ }
+the_end:
+
+ if (pix_fmt != AV_PIX_FMT_NONE) {
+ pc->format = pix_fmt;
+ pc->coded_width = ALIGN(pc->width, 16);
+ pc->coded_height = ALIGN(pc->height, 16);
+ }
+}
+
+int mpeg12_decode_extradata_ps(u8 *buf, int size, struct mpeg12_param_sets *ps)
+{
+ ps->head_parsed = false;
+
+ mpegvideo_extract_headers(buf, size, ps);
+
+ if (ps->dec_ps.width && ps->dec_ps.height)
+ ps->head_parsed = true;
+
+ return 0;
+}
+
diff --git a/drivers/amvdec_ports/decoder/aml_mpeg12_parser.h b/drivers/amvdec_ports/decoder/aml_mpeg12_parser.h
new file mode 100644
index 0000000..c06d632
--- /dev/null
+++ b/drivers/amvdec_ports/decoder/aml_mpeg12_parser.h
@@ -0,0 +1,81 @@
+#ifndef AML_MPEG12_PARSER_H
+#define AML_MPEG12_PARSER_H
+
+#include "../aml_vcodec_drv.h"
+#include "../utils/common.h"
+#ifdef CONFIG_AMLOGIC_MEDIA_V4L_SOFTWARE_PARSER
+#include "../utils/pixfmt.h"
+#endif
+
+/* Start codes. */
+#define SEQ_END_CODE 0x000001b7
+#define SEQ_START_CODE 0x000001b3
+#define GOP_START_CODE 0x000001b8
+#define PICTURE_START_CODE 0x00000100
+#define SLICE_MIN_START_CODE 0x00000101
+#define SLICE_MAX_START_CODE 0x000001af
+#define EXT_START_CODE 0x000001b5
+#define USER_START_CODE 0x000001b2
+#define SLICE_START_CODE 0x000001b7
+
+enum AVFieldOrder {
+ AV_FIELD_UNKNOWN,
+ AV_FIELD_PROGRESSIVE,
+ AV_FIELD_TT, //< Top coded_first, top displayed first
+ AV_FIELD_BB, //< Bottom coded first, bottom displayed first
+ AV_FIELD_TB, //< Top coded first, bottom displayed first
+ AV_FIELD_BT, //< Bottom coded first, top displayed first
+};
+
+struct MpvParseContext {
+ struct AVRational frame_rate;
+ int progressive_sequence;
+ int width, height;
+
+ int repeat_pict; /* XXX: Put it back in AVCodecContext. */
+ int pict_type; /* XXX: Put it back in AVCodecContext. */
+ enum AVFieldOrder field_order;
+ int format;
+ /**
+ * Dimensions of the coded video.
+ */
+ int coded_width;
+ int coded_height;
+ /**
+ * For some codecs, the time base is closer to the field rate than the frame rate.
+ * Most notably, H.264 and MPEG-2 specify time_base as half of frame duration
+ * if no telecine is used ...
+ *
+ * Set to time_base ticks per frame. Default 1, e.g., H.264/MPEG-2 set it to 2.
+ */
+ int ticks_per_frame;
+ /**
+ * Size of the frame reordering buffer in the decoder.
+ * For MPEG-2 it is 1 IPB or 0 low delay IP.
+ * - encoding: Set by libavcodec.
+ * - decoding: Set by libavcodec.
+ */
+ int has_b_frames;
+ /**
+ * - decoding: For codecs that store a framerate value in the compressed
+ * bitstream, the decoder may export it here. { 0, 1} when
+ * unknown.
+ * - encoding: May be used to signal the framerate of CFR content to an
+ * encoder.
+ */
+ struct AVRational framerate;
+};
+
+struct mpeg12_param_sets {
+ bool head_parsed;
+ /* currently active parameter sets */
+ struct MpvParseContext dec_ps;
+};
+
+#ifdef CONFIG_AMLOGIC_MEDIA_V4L_SOFTWARE_PARSER
+int mpeg12_decode_extradata_ps(u8 *buf, int size, struct mpeg12_param_sets *ps);
+#else
+inline int mpeg12_decode_extradata_ps(u8 *buf, int size, struct mpeg12_param_sets *ps) { return -1; }
+#endif
+
+#endif
diff --git a/drivers/amvdec_ports/decoder/aml_mpeg4_parser.c b/drivers/amvdec_ports/decoder/aml_mpeg4_parser.c
new file mode 100644
index 0000000..9c47c08
--- /dev/null
+++ b/drivers/amvdec_ports/decoder/aml_mpeg4_parser.c
@@ -0,0 +1,1231 @@
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/vmalloc.h>
+#include <linux/mm.h>
+#include <linux/string.h>
+
+#include "aml_mpeg4_parser.h"
+#include "../utils/get_bits.h"
+#include "../utils/put_bits.h"
+#include "../utils/golomb.h"
+#include "../utils/common.h"
+#include "utils.h"
+
+const u8 ff_mpeg4_dc_threshold[8]={
+ 99, 13, 15, 17, 19, 21, 23, 0
+};
+
+/* these matrixes will be permuted for the idct */
+const int16_t ff_mpeg4_default_intra_matrix[64] = {
+ 8, 17, 18, 19, 21, 23, 25, 27,
+ 17, 18, 19, 21, 23, 25, 27, 28,
+ 20, 21, 22, 23, 24, 26, 28, 30,
+ 21, 22, 23, 24, 26, 28, 30, 32,
+ 22, 23, 24, 26, 28, 30, 32, 35,
+ 23, 24, 26, 28, 30, 32, 35, 38,
+ 25, 26, 28, 30, 32, 35, 38, 41,
+ 27, 28, 30, 32, 35, 38, 41, 45,
+};
+
+const int16_t ff_mpeg4_default_non_intra_matrix[64] = {
+ 16, 17, 18, 19, 20, 21, 22, 23,
+ 17, 18, 19, 20, 21, 22, 23, 24,
+ 18, 19, 20, 21, 22, 23, 24, 25,
+ 19, 20, 21, 22, 23, 24, 26, 27,
+ 20, 21, 22, 23, 25, 26, 27, 28,
+ 21, 22, 23, 24, 26, 27, 28, 30,
+ 22, 23, 24, 26, 27, 28, 30, 31,
+ 23, 24, 25, 27, 28, 30, 31, 33,
+};
+
+const struct AVRational ff_h263_pixel_aspect[16] = {
+ { 0, 1 },
+ { 1, 1 },
+ { 12, 11 },
+ { 10, 11 },
+ { 16, 11 },
+ { 40, 33 },
+ { 0, 1 },
+ { 0, 1 },
+ { 0, 1 },
+ { 0, 1 },
+ { 0, 1 },
+ { 0, 1 },
+ { 0, 1 },
+ { 0, 1 },
+ { 0, 1 },
+ { 0, 1 },
+};
+
+/* As per spec, studio start code search isn't the same as the old type of start code */
+static void next_start_code_studio(struct get_bits_context *gb)
+{
+ align_get_bits(gb);
+
+ while (get_bits_left(gb) >= 24 && show_bits_long(gb, 24) != 0x1) {
+ get_bits(gb, 8);
+ }
+}
+
+static int read_quant_matrix_ext(struct MpegEncContext *s, struct get_bits_context *gb)
+{
+ int i, /*j,*/ v;
+
+ if (get_bits1(gb)) {
+ if (get_bits_left(gb) < 64*8)
+ return -1;
+ /* intra_quantiser_matrix */
+ for (i = 0; i < 64; i++) {
+ v = get_bits(gb, 8);
+ //j = s->idsp.idct_permutation[ff_zigzag_direct[i]];
+ //s->intra_matrix[j] = v;
+ //s->chroma_intra_matrix[j] = v;
+ }
+ }
+
+ if (get_bits1(gb)) {
+ if (get_bits_left(gb) < 64*8)
+ return -1;
+ /* non_intra_quantiser_matrix */
+ for (i = 0; i < 64; i++) {
+ get_bits(gb, 8);
+ }
+ }
+
+ if (get_bits1(gb)) {
+ if (get_bits_left(gb) < 64*8)
+ return -1;
+ /* chroma_intra_quantiser_matrix */
+ for (i = 0; i < 64; i++) {
+ v = get_bits(gb, 8);
+ //j = s->idsp.idct_permutation[ff_zigzag_direct[i]];
+ //s->chroma_intra_matrix[j] = v;
+ }
+ }
+
+ if (get_bits1(gb)) {
+ if (get_bits_left(gb) < 64*8)
+ return -1;
+ /* chroma_non_intra_quantiser_matrix */
+ for (i = 0; i < 64; i++) {
+ get_bits(gb, 8);
+ }
+ }
+
+ next_start_code_studio(gb);
+ return 0;
+}
+
+static void extension_and_user_data(struct MpegEncContext *s, struct get_bits_context *gb, int id)
+{
+ u32 startcode;
+ u8 extension_type;
+
+ startcode = show_bits_long(gb, 32);
+ if (startcode == USER_DATA_STARTCODE || startcode == EXT_STARTCODE) {
+ if ((id == 2 || id == 4) && startcode == EXT_STARTCODE) {
+ skip_bits_long(gb, 32);
+ extension_type = get_bits(gb, 4);
+ if (extension_type == QUANT_MATRIX_EXT_ID)
+ read_quant_matrix_ext(s, gb);
+ }
+ }
+}
+
+
+static int decode_studio_vol_header(struct mpeg4_dec_param *ctx, struct get_bits_context *gb)
+{
+ struct MpegEncContext *s = &ctx->m;
+ int width, height;
+ int bits_per_raw_sample;
+
+ // random_accessible_vol and video_object_type_indication have already
+ // been read by the caller decode_vol_header()
+ skip_bits(gb, 4); /* video_object_layer_verid */
+ ctx->shape = get_bits(gb, 2); /* video_object_layer_shape */
+ skip_bits(gb, 4); /* video_object_layer_shape_extension */
+ skip_bits1(gb); /* progressive_sequence */
+ if (ctx->shape != BIN_ONLY_SHAPE) {
+ ctx->rgb = get_bits1(gb); /* rgb_components */
+ s->chroma_format = get_bits(gb, 2); /* chroma_format */
+ if (!s->chroma_format) {
+ v4l_dbg(0, V4L_DEBUG_CODEC_ERROR, "illegal chroma format\n");
+ return -1;
+ }
+
+ bits_per_raw_sample = get_bits(gb, 4); /* bit_depth */
+ if (bits_per_raw_sample == 10) {
+ if (ctx->rgb) {
+ ctx->pix_fmt = AV_PIX_FMT_GBRP10;
+ } else {
+ ctx->pix_fmt = s->chroma_format == CHROMA_422 ? AV_PIX_FMT_YUV422P10 : AV_PIX_FMT_YUV444P10;
+ }
+ }
+ else {
+ v4l_dbg(0, V4L_DEBUG_CODEC_ERROR, "MPEG-4 Studio profile bit-depth %u", bits_per_raw_sample);
+ return -1;
+ }
+ ctx->bits_per_raw_sample = bits_per_raw_sample;
+ }
+ if (ctx->shape == RECT_SHAPE) {
+ check_marker(gb, "before video_object_layer_width");
+ width = get_bits(gb, 14); /* video_object_layer_width */
+ check_marker(gb, "before video_object_layer_height");
+ height = get_bits(gb, 14); /* video_object_layer_height */
+ check_marker(gb, "after video_object_layer_height");
+
+ /* Do the same check as non-studio profile */
+ if (width && height) {
+ if (s->width && s->height &&
+ (s->width != width || s->height != height))
+ s->context_reinit = 1;
+ s->width = width;
+ s->height = height;
+ }
+ }
+ s->aspect_ratio_info = get_bits(gb, 4);
+ if (s->aspect_ratio_info == FF_ASPECT_EXTENDED) {
+ ctx->sample_aspect_ratio.num = get_bits(gb, 8); // par_width
+ ctx->sample_aspect_ratio.den = get_bits(gb, 8); // par_height
+ } else {
+ ctx->sample_aspect_ratio = ff_h263_pixel_aspect[s->aspect_ratio_info];
+ }
+ skip_bits(gb, 4); /* frame_rate_code */
+ skip_bits(gb, 15); /* first_half_bit_rate */
+ check_marker(gb, "after first_half_bit_rate");
+ skip_bits(gb, 15); /* latter_half_bit_rate */
+ check_marker(gb, "after latter_half_bit_rate");
+ skip_bits(gb, 15); /* first_half_vbv_buffer_size */
+ check_marker(gb, "after first_half_vbv_buffer_size");
+ skip_bits(gb, 3); /* latter_half_vbv_buffer_size */
+ skip_bits(gb, 11); /* first_half_vbv_buffer_size */
+ check_marker(gb, "after first_half_vbv_buffer_size");
+ skip_bits(gb, 15); /* latter_half_vbv_occupancy */
+ check_marker(gb, "after latter_half_vbv_occupancy");
+ s->low_delay = get_bits1(gb);
+ s->mpeg_quant = get_bits1(gb); /* mpeg2_stream */
+
+ next_start_code_studio(gb);
+ extension_and_user_data(s, gb, 2);
+
+ return 0;
+}
+
+static int decode_vol_header(struct mpeg4_dec_param *ctx, struct get_bits_context *gb)
+{
+ struct MpegEncContext *s = &ctx->m;
+ int width, height, vo_ver_id;
+
+ /* vol header */
+ skip_bits(gb, 1); /* random access */
+ s->vo_type = get_bits(gb, 8);
+
+ /* If we are in studio profile (per vo_type), check if its all consistent
+ * and if so continue pass control to decode_studio_vol_header().
+ * elIf something is inconsistent, error out
+ * else continue with (non studio) vol header decpoding.
+ */
+ if (s->vo_type == CORE_STUDIO_VO_TYPE ||
+ s->vo_type == SIMPLE_STUDIO_VO_TYPE) {
+ if (ctx->profile != FF_PROFILE_UNKNOWN && ctx->profile != FF_PROFILE_MPEG4_SIMPLE_STUDIO)
+ return -1;
+ s->studio_profile = 1;
+ ctx->profile = FF_PROFILE_MPEG4_SIMPLE_STUDIO;
+ return decode_studio_vol_header(ctx, gb);
+ } else if (s->studio_profile) {
+ return -1;
+ }
+
+ if (get_bits1(gb) != 0) { /* is_ol_id */
+ vo_ver_id = get_bits(gb, 4); /* vo_ver_id */
+ skip_bits(gb, 3); /* vo_priority */
+ } else {
+ vo_ver_id = 1;
+ }
+ s->aspect_ratio_info = get_bits(gb, 4);
+ if (s->aspect_ratio_info == FF_ASPECT_EXTENDED) {
+ ctx->sample_aspect_ratio.num = get_bits(gb, 8); // par_width
+ ctx->sample_aspect_ratio.den = get_bits(gb, 8); // par_height
+ } else {
+ ctx->sample_aspect_ratio = ff_h263_pixel_aspect[s->aspect_ratio_info];
+ }
+
+ if ((ctx->vol_control_parameters = get_bits1(gb))) { /* vol control parameter */
+ int chroma_format = get_bits(gb, 2);
+ if (chroma_format != CHROMA_420)
+ v4l_dbg(0, V4L_DEBUG_CODEC_ERROR, "illegal chroma format\n");
+
+ s->low_delay = get_bits1(gb);
+ if (get_bits1(gb)) { /* vbv parameters */
+ get_bits(gb, 15); /* first_half_bitrate */
+ check_marker(gb, "after first_half_bitrate");
+ get_bits(gb, 15); /* latter_half_bitrate */
+ check_marker(gb, "after latter_half_bitrate");
+ get_bits(gb, 15); /* first_half_vbv_buffer_size */
+ check_marker(gb, "after first_half_vbv_buffer_size");
+ get_bits(gb, 3); /* latter_half_vbv_buffer_size */
+ get_bits(gb, 11); /* first_half_vbv_occupancy */
+ check_marker(gb, "after first_half_vbv_occupancy");
+ get_bits(gb, 15); /* latter_half_vbv_occupancy */
+ check_marker(gb, "after latter_half_vbv_occupancy");
+ }
+ } else {
+ /* is setting low delay flag only once the smartest thing to do?
+ * low delay detection will not be overridden. */
+ if (s->picture_number == 0) {
+ switch (s->vo_type) {
+ case SIMPLE_VO_TYPE:
+ case ADV_SIMPLE_VO_TYPE:
+ s->low_delay = 1;
+ break;
+ default:
+ s->low_delay = 0;
+ }
+ }
+ }
+
+ ctx->shape = get_bits(gb, 2); /* vol shape */
+ if (ctx->shape != RECT_SHAPE)
+ v4l_dbg(0, V4L_DEBUG_CODEC_ERROR, "only rectangular vol supported\n");
+ if (ctx->shape == GRAY_SHAPE && vo_ver_id != 1) {
+ v4l_dbg(0, V4L_DEBUG_CODEC_ERROR, "Gray shape not supported\n");
+ skip_bits(gb, 4); /* video_object_layer_shape_extension */
+ }
+
+ check_marker(gb, "before time_increment_resolution");
+
+ ctx->framerate.num = get_bits(gb, 16);
+ if (!ctx->framerate.num) {
+ v4l_dbg(0, V4L_DEBUG_CODEC_ERROR, "framerate==0\n");
+ return -1;
+ }
+
+ ctx->time_increment_bits = av_log2(ctx->framerate.num - 1) + 1;
+ if (ctx->time_increment_bits < 1)
+ ctx->time_increment_bits = 1;
+
+ check_marker(gb, "before fixed_vop_rate");
+
+ if (get_bits1(gb) != 0) /* fixed_vop_rate */
+ ctx->framerate.den = get_bits(gb, ctx->time_increment_bits);
+ else
+ ctx->framerate.den = 1;
+
+ //ctx->time_base = av_inv_q(av_mul_q(ctx->framerate, (AVRational){ctx->ticks_per_frame, 1}));
+
+ ctx->t_frame = 0;
+
+ if (ctx->shape != BIN_ONLY_SHAPE) {
+ if (ctx->shape == RECT_SHAPE) {
+ check_marker(gb, "before width");
+ width = get_bits(gb, 13);
+ check_marker(gb, "before height");
+ height = get_bits(gb, 13);
+ check_marker(gb, "after height");
+ if (width && height && /* they should be non zero but who knows */
+ !(s->width && s->codec_tag == AV_RL32("MP4S"))) {
+ if (s->width && s->height &&
+ (s->width != width || s->height != height))
+ s->context_reinit = 1;
+ s->width = width;
+ s->height = height;
+ }
+ }
+
+ s->progressive_sequence =
+ s->progressive_frame = get_bits1(gb) ^ 1;
+ s->interlaced_dct = 0;
+ if (!get_bits1(gb)) /* OBMC Disable */
+ v4l_dbg(0, V4L_DEBUG_CODEC_PARSER, "MPEG-4 OBMC not supported (very likely buggy encoder)\n");
+ if (vo_ver_id == 1)
+ ctx->vol_sprite_usage = get_bits1(gb); /* vol_sprite_usage */
+ else
+ ctx->vol_sprite_usage = get_bits(gb, 2); /* vol_sprite_usage */
+
+ if (ctx->vol_sprite_usage == STATIC_SPRITE)
+ v4l_dbg(0, V4L_DEBUG_CODEC_PARSER, "Static Sprites not supported\n");
+ if (ctx->vol_sprite_usage == STATIC_SPRITE ||
+ ctx->vol_sprite_usage == GMC_SPRITE) {
+ if (ctx->vol_sprite_usage == STATIC_SPRITE) {
+ skip_bits(gb, 13); // sprite_width
+ check_marker(gb, "after sprite_width");
+ skip_bits(gb, 13); // sprite_height
+ check_marker(gb, "after sprite_height");
+ skip_bits(gb, 13); // sprite_left
+ check_marker(gb, "after sprite_left");
+ skip_bits(gb, 13); // sprite_top
+ check_marker(gb, "after sprite_top");
+ }
+ ctx->num_sprite_warping_points = get_bits(gb, 6);
+ if (ctx->num_sprite_warping_points > 3) {
+ v4l_dbg(0, V4L_DEBUG_CODEC_ERROR, "%d sprite_warping_points\n",
+ ctx->num_sprite_warping_points);
+ ctx->num_sprite_warping_points = 0;
+ return -1;
+ }
+ s->sprite_warping_accuracy = get_bits(gb, 2);
+ ctx->sprite_brightness_change = get_bits1(gb);
+ if (ctx->vol_sprite_usage == STATIC_SPRITE)
+ skip_bits1(gb); // low_latency_sprite
+ }
+ // FIXME sadct disable bit if verid!=1 && shape not rect
+
+ if (get_bits1(gb) == 1) { /* not_8_bit */
+ s->quant_precision = get_bits(gb, 4); /* quant_precision */
+ if (get_bits(gb, 4) != 8) /* bits_per_pixel */
+ v4l_dbg(0, V4L_DEBUG_CODEC_ERROR, "N-bit not supported\n");
+ if (s->quant_precision != 5)
+ v4l_dbg(0, V4L_DEBUG_CODEC_ERROR, "quant precision %d\n", s->quant_precision);
+ if (s->quant_precision<3 || s->quant_precision>9) {
+ s->quant_precision = 5;
+ }
+ } else {
+ s->quant_precision = 5;
+ }
+
+ // FIXME a bunch of grayscale shape things
+
+ if ((s->mpeg_quant = get_bits1(gb))) { /* vol_quant_type */
+ int i, v;
+
+ //mpeg4_load_default_matrices(s);
+
+ /* load custom intra matrix */
+ if (get_bits1(gb)) {
+ int last = 0;
+ for (i = 0; i < 64; i++) {
+ //int j;
+ if (get_bits_left(gb) < 8) {
+ v4l_dbg(0, V4L_DEBUG_CODEC_ERROR, "insufficient data for custom matrix\n");
+ return -1;
+ }
+ v = get_bits(gb, 8);
+ if (v == 0)
+ break;
+
+ last = v;
+ //j = s->idsp.idct_permutation[ff_zigzag_direct[i]];
+ //s->intra_matrix[j] = last;
+ //s->chroma_intra_matrix[j] = last;
+ }
+
+ /* replicate last value */
+ //for (; i < 64; i++) {
+ //int j = s->idsp.idct_permutation[ff_zigzag_direct[i]];
+ //s->intra_matrix[j] = last;
+ //s->chroma_intra_matrix[j] = last;
+ //}
+ }
+
+ /* load custom non intra matrix */
+ if (get_bits1(gb)) {
+ int last = 0;
+ for (i = 0; i < 64; i++) {
+ //int j;
+ if (get_bits_left(gb) < 8) {
+ v4l_dbg(0, V4L_DEBUG_CODEC_ERROR, "insufficient data for custom matrix\n");
+ return -1;
+ }
+ v = get_bits(gb, 8);
+ if (v == 0)
+ break;
+
+ last = v;
+ //j = s->idsp.idct_permutation[ff_zigzag_direct[i]];
+ //s->inter_matrix[j] = v;
+ //s->chroma_inter_matrix[j] = v;
+ }
+
+ /* replicate last value */
+ //for (; i < 64; i++) {
+ //int j = s->idsp.idct_permutation[ff_zigzag_direct[i]];
+ //s->inter_matrix[j] = last;
+ //s->chroma_inter_matrix[j] = last;
+ //}
+ }
+
+ // FIXME a bunch of grayscale shape things
+ }
+
+ if (vo_ver_id != 1)
+ s->quarter_sample = get_bits1(gb);
+ else
+ s->quarter_sample = 0;
+
+ if (get_bits_left(gb) < 4) {
+ v4l_dbg(0, V4L_DEBUG_CODEC_ERROR, "VOL Header truncated\n");
+ return -1;
+ }
+
+ if (!get_bits1(gb)) {
+ int pos = get_bits_count(gb);
+ int estimation_method = get_bits(gb, 2);
+ if (estimation_method < 2) {
+ if (!get_bits1(gb)) {
+ ctx->cplx_estimation_trash_i += 8 * get_bits1(gb); /* opaque */
+ ctx->cplx_estimation_trash_i += 8 * get_bits1(gb); /* transparent */
+ ctx->cplx_estimation_trash_i += 8 * get_bits1(gb); /* intra_cae */
+ ctx->cplx_estimation_trash_i += 8 * get_bits1(gb); /* inter_cae */
+ ctx->cplx_estimation_trash_i += 8 * get_bits1(gb); /* no_update */
+ ctx->cplx_estimation_trash_i += 8 * get_bits1(gb); /* upsampling */
+ }
+ if (!get_bits1(gb)) {
+ ctx->cplx_estimation_trash_i += 8 * get_bits1(gb); /* intra_blocks */
+ ctx->cplx_estimation_trash_p += 8 * get_bits1(gb); /* inter_blocks */
+ ctx->cplx_estimation_trash_p += 8 * get_bits1(gb); /* inter4v_blocks */
+ ctx->cplx_estimation_trash_i += 8 * get_bits1(gb); /* not coded blocks */
+ }
+ if (!check_marker(gb, "in complexity estimation part 1")) {
+ skip_bits_long(gb, pos - get_bits_count(gb));
+ goto no_cplx_est;
+ }
+ if (!get_bits1(gb)) {
+ ctx->cplx_estimation_trash_i += 8 * get_bits1(gb); /* dct_coeffs */
+ ctx->cplx_estimation_trash_i += 8 * get_bits1(gb); /* dct_lines */
+ ctx->cplx_estimation_trash_i += 8 * get_bits1(gb); /* vlc_syms */
+ ctx->cplx_estimation_trash_i += 4 * get_bits1(gb); /* vlc_bits */
+ }
+ if (!get_bits1(gb)) {
+ ctx->cplx_estimation_trash_p += 8 * get_bits1(gb); /* apm */
+ ctx->cplx_estimation_trash_p += 8 * get_bits1(gb); /* npm */
+ ctx->cplx_estimation_trash_b += 8 * get_bits1(gb); /* interpolate_mc_q */
+ ctx->cplx_estimation_trash_p += 8 * get_bits1(gb); /* forwback_mc_q */
+ ctx->cplx_estimation_trash_p += 8 * get_bits1(gb); /* halfpel2 */
+ ctx->cplx_estimation_trash_p += 8 * get_bits1(gb); /* halfpel4 */
+ }
+ if (!check_marker(gb, "in complexity estimation part 2")) {
+ skip_bits_long(gb, pos - get_bits_count(gb));
+ goto no_cplx_est;
+ }
+ if (estimation_method == 1) {
+ ctx->cplx_estimation_trash_i += 8 * get_bits1(gb); /* sadct */
+ ctx->cplx_estimation_trash_p += 8 * get_bits1(gb); /* qpel */
+ }
+ } else
+ v4l_dbg(0, V4L_DEBUG_CODEC_ERROR, "Invalid Complexity estimation method %d\n",
+ estimation_method);
+ } else {
+
+no_cplx_est:
+ ctx->cplx_estimation_trash_i =
+ ctx->cplx_estimation_trash_p =
+ ctx->cplx_estimation_trash_b = 0;
+ }
+
+ ctx->resync_marker = !get_bits1(gb); /* resync_marker_disabled */
+
+ s->data_partitioning = get_bits1(gb);
+ if (s->data_partitioning)
+ ctx->rvlc = get_bits1(gb);
+
+ if (vo_ver_id != 1) {
+ ctx->new_pred = get_bits1(gb);
+ if (ctx->new_pred) {
+ v4l_dbg(0, V4L_DEBUG_CODEC_ERROR, "new pred not supported\n");
+ skip_bits(gb, 2); /* requested upstream message type */
+ skip_bits1(gb); /* newpred segment type */
+ }
+ if (get_bits1(gb)) // reduced_res_vop
+ v4l_dbg(0, V4L_DEBUG_CODEC_ERROR, "reduced resolution VOP not supported\n");
+ } else {
+ ctx->new_pred = 0;
+ }
+
+ ctx->scalability = get_bits1(gb);
+
+ if (ctx->scalability) {
+ struct get_bits_context bak = *gb;
+ int h_sampling_factor_n;
+ int h_sampling_factor_m;
+ int v_sampling_factor_n;
+ int v_sampling_factor_m;
+
+ skip_bits1(gb); // hierarchy_type
+ skip_bits(gb, 4); /* ref_layer_id */
+ skip_bits1(gb); /* ref_layer_sampling_dir */
+ h_sampling_factor_n = get_bits(gb, 5);
+ h_sampling_factor_m = get_bits(gb, 5);
+ v_sampling_factor_n = get_bits(gb, 5);
+ v_sampling_factor_m = get_bits(gb, 5);
+ ctx->enhancement_type = get_bits1(gb);
+
+ if (h_sampling_factor_n == 0 || h_sampling_factor_m == 0 ||
+ v_sampling_factor_n == 0 || v_sampling_factor_m == 0) {
+ /* illegal scalability header (VERY broken encoder),
+ * trying to workaround */
+ ctx->scalability = 0;
+ *gb = bak;
+ } else
+ v4l_dbg(0, V4L_DEBUG_CODEC_ERROR, "scalability not supported\n");
+
+ // bin shape stuff FIXME
+ }
+ }
+
+ if (1) {
+ v4l_dbg(0, V4L_DEBUG_CODEC_PARSER, "tb %d/%d, tincrbits:%d, qp_prec:%d, ps:%d, low_delay:%d %s%s%s%s\n",
+ ctx->framerate.den, ctx->framerate.num,
+ ctx->time_increment_bits,
+ s->quant_precision,
+ s->progressive_sequence,
+ s->low_delay,
+ ctx->scalability ? "scalability " :"" , s->quarter_sample ? "qpel " : "",
+ s->data_partitioning ? "partition " : "", ctx->rvlc ? "rvlc " : "");
+ }
+
+ return 0;
+}
+
+
+/**
+ * Decode the user data stuff in the header.
+ * Also initializes divx/xvid/lavc_version/build.
+ */
+static int decode_user_data(struct mpeg4_dec_param *ctx, struct get_bits_context *gb)
+{
+ struct MpegEncContext *s = &ctx->m;
+ char buf[256];
+ int i;
+ int e;
+ int ver = 0, build = 0, ver2 = 0, ver3 = 0;
+ char last;
+
+ for (i = 0; i < 255 && get_bits_count(gb) < gb->size_in_bits; i++) {
+ if (show_bits(gb, 23) == 0)
+ break;
+ buf[i] = get_bits(gb, 8);
+ }
+ buf[i] = 0;
+
+ /* divx detection */
+ e = sscanf(buf, "DivX%dBuild%d%c", &ver, &build, &last);
+ if (e < 2)
+ e = sscanf(buf, "DivX%db%d%c", &ver, &build, &last);
+ if (e >= 2) {
+ ctx->divx_version = ver;
+ ctx->divx_build = build;
+ s->divx_packed = e == 3 && last == 'p';
+ }
+
+ /* libavcodec detection */
+ e = sscanf(buf, "FFmpe%*[^b]b%d", &build) + 3;
+ if (e != 4)
+ e = sscanf(buf, "FFmpeg v%d.%d.%d / libavcodec build: %d", &ver, &ver2, &ver3, &build);
+ if (e != 4) {
+ e = sscanf(buf, "Lavc%d.%d.%d", &ver, &ver2, &ver3) + 1;
+ if (e > 1) {
+ if (ver > 0xFFU || ver2 > 0xFFU || ver3 > 0xFFU) {
+ v4l_dbg(0, V4L_DEBUG_CODEC_PARSER, "Unknown Lavc version string encountered, %d.%d.%d; "
+ "clamping sub-version values to 8-bits.\n",
+ ver, ver2, ver3);
+ }
+ build = ((ver & 0xFF) << 16) + ((ver2 & 0xFF) << 8) + (ver3 & 0xFF);
+ }
+ }
+ if (e != 4) {
+ if (strcmp(buf, "ffmpeg") == 0)
+ ctx->lavc_build = 4600;
+ }
+ if (e == 4)
+ ctx->lavc_build = build;
+
+ /* Xvid detection */
+ e = sscanf(buf, "XviD%d", &build);
+ if (e == 1)
+ ctx->xvid_build = build;
+
+ return 0;
+}
+
+
+static int mpeg4_decode_gop_header(struct MpegEncContext *s, struct get_bits_context *gb)
+{
+ int hours, minutes, seconds;
+
+ if (!show_bits(gb, 23)) {
+ v4l_dbg(0, V4L_DEBUG_CODEC_ERROR, "GOP header invalid\n");
+ return -1;
+ }
+
+ hours = get_bits(gb, 5);
+ minutes = get_bits(gb, 6);
+ check_marker(gb, "in gop_header");
+ seconds = get_bits(gb, 6);
+
+ s->time_base = seconds + 60*(minutes + 60*hours);
+
+ skip_bits1(gb);
+ skip_bits1(gb);
+
+ return 0;
+}
+
+
+static int mpeg4_decode_profile_level(struct MpegEncContext *s, struct get_bits_context *gb, int *profile, int *level)
+{
+
+ *profile = get_bits(gb, 4);
+ *level = get_bits(gb, 4);
+
+ // for Simple profile, level 0
+ if (*profile == 0 && *level == 8) {
+ *level = 0;
+ }
+
+ return 0;
+}
+
+
+static int decode_studiovisualobject(struct mpeg4_dec_param *ctx, struct get_bits_context *gb)
+{
+ struct MpegEncContext *s = &ctx->m;
+ int visual_object_type;
+
+ skip_bits(gb, 4); /* visual_object_verid */
+ visual_object_type = get_bits(gb, 4);
+ if (visual_object_type != VOT_VIDEO_ID) {
+ v4l_dbg(0, V4L_DEBUG_CODEC_ERROR, "VO type %u", visual_object_type);
+ return -1;
+ }
+
+ next_start_code_studio(gb);
+ extension_and_user_data(s, gb, 1);
+
+ return 0;
+}
+
+
+static int mpeg4_decode_visual_object(struct MpegEncContext *s, struct get_bits_context *gb)
+{
+ int visual_object_type;
+ int is_visual_object_identifier = get_bits1(gb);
+
+ if (is_visual_object_identifier) {
+ skip_bits(gb, 4+3);
+ }
+ visual_object_type = get_bits(gb, 4);
+
+ if (visual_object_type == VOT_VIDEO_ID ||
+ visual_object_type == VOT_STILL_TEXTURE_ID) {
+ int video_signal_type = get_bits1(gb);
+ if (video_signal_type) {
+ int video_range, color_description;
+ skip_bits(gb, 3); // video_format
+ video_range = get_bits1(gb);
+ color_description = get_bits1(gb);
+
+ s->ctx->color_range = video_range ? AVCOL_RANGE_JPEG : AVCOL_RANGE_MPEG;
+
+ if (color_description) {
+ s->ctx->color_primaries = get_bits(gb, 8);
+ s->ctx->color_trc = get_bits(gb, 8);
+ s->ctx->colorspace = get_bits(gb, 8);
+ }
+ }
+ }
+
+ return 0;
+}
+
+static void decode_smpte_tc(struct mpeg4_dec_param *ctx, struct get_bits_context *gb)
+{
+ skip_bits(gb, 16); /* Time_code[63..48] */
+ check_marker(gb, "after Time_code[63..48]");
+ skip_bits(gb, 16); /* Time_code[47..32] */
+ check_marker(gb, "after Time_code[47..32]");
+ skip_bits(gb, 16); /* Time_code[31..16] */
+ check_marker(gb, "after Time_code[31..16]");
+ skip_bits(gb, 16); /* Time_code[15..0] */
+ check_marker(gb, "after Time_code[15..0]");
+ skip_bits(gb, 4); /* reserved_bits */
+}
+
+static void reset_studio_dc_predictors(struct MpegEncContext *s)
+{
+ /* Reset DC Predictors */
+ s->last_dc[0] =
+ s->last_dc[1] =
+ s->last_dc[2] = 1 << (s->ctx->bits_per_raw_sample + s->dct_precision + s->intra_dc_precision - 1);
+}
+
+/**
+ * Decode the next studio vop header.
+ * @return <0 if something went wrong
+ */
+static int decode_studio_vop_header(struct mpeg4_dec_param *ctx, struct get_bits_context *gb)
+{
+ struct MpegEncContext *s = &ctx->m;
+
+ if (get_bits_left(gb) <= 32)
+ return 0;
+
+ //s->decode_mb = mpeg4_decode_studio_mb;
+
+ decode_smpte_tc(ctx, gb);
+
+ skip_bits(gb, 10); /* temporal_reference */
+ skip_bits(gb, 2); /* vop_structure */
+ s->pict_type = get_bits(gb, 2) + AV_PICTURE_TYPE_I; /* vop_coding_type */
+ if (get_bits1(gb)) { /* vop_coded */
+ skip_bits1(gb); /* top_field_first */
+ skip_bits1(gb); /* repeat_first_field */
+ s->progressive_frame = get_bits1(gb) ^ 1; /* progressive_frame */
+ }
+
+ if (s->pict_type == AV_PICTURE_TYPE_I) {
+ if (get_bits1(gb))
+ reset_studio_dc_predictors(s);
+ }
+
+ if (ctx->shape != BIN_ONLY_SHAPE) {
+ s->alternate_scan = get_bits1(gb);
+ s->frame_pred_frame_dct = get_bits1(gb);
+ s->dct_precision = get_bits(gb, 2);
+ s->intra_dc_precision = get_bits(gb, 2);
+ s->q_scale_type = get_bits1(gb);
+ }
+
+ //if (s->alternate_scan) { }
+
+ //mpeg4_load_default_matrices(s);
+
+ next_start_code_studio(gb);
+ extension_and_user_data(s, gb, 4);
+
+ return 0;
+}
+
+static int decode_new_pred(struct mpeg4_dec_param *ctx, struct get_bits_context *gb)
+{
+ int len = FFMIN(ctx->time_increment_bits + 3, 15);
+
+ get_bits(gb, len);
+ if (get_bits1(gb))
+ get_bits(gb, len);
+ check_marker(gb, "after new_pred");
+
+ return 0;
+}
+
+static int decode_vop_header(struct mpeg4_dec_param *ctx, struct get_bits_context *gb)
+{
+ struct MpegEncContext *s = &ctx->m;
+ int time_incr, time_increment;
+ int64_t pts;
+
+ s->mcsel = 0;
+ s->pict_type = get_bits(gb, 2) + AV_PICTURE_TYPE_I; /* pict type: I = 0 , P = 1 */
+ if (s->pict_type == AV_PICTURE_TYPE_B && s->low_delay &&
+ ctx->vol_control_parameters == 0) {
+ v4l_dbg(0, V4L_DEBUG_CODEC_ERROR, "low_delay flag set incorrectly, clearing it\n");
+ s->low_delay = 0;
+ }
+
+ s->partitioned_frame = s->data_partitioning && s->pict_type != AV_PICTURE_TYPE_B;
+ /*if (s->partitioned_frame)
+ s->decode_mb = mpeg4_decode_partitioned_mb;
+ else
+ s->decode_mb = mpeg4_decode_mb;*/
+
+ time_incr = 0;
+ while (get_bits1(gb) != 0)
+ time_incr++;
+
+ check_marker(gb, "before time_increment");
+
+ if (ctx->time_increment_bits == 0 ||
+ !(show_bits(gb, ctx->time_increment_bits + 1) & 1)) {
+ v4l_dbg(0, V4L_DEBUG_CODEC_PARSER, "time_increment_bits %d is invalid in relation to the current bitstream, this is likely caused by a missing VOL header\n", ctx->time_increment_bits);
+
+ for (ctx->time_increment_bits = 1;
+ ctx->time_increment_bits < 16;
+ ctx->time_increment_bits++) {
+ if (s->pict_type == AV_PICTURE_TYPE_P ||
+ (s->pict_type == AV_PICTURE_TYPE_S &&
+ ctx->vol_sprite_usage == GMC_SPRITE)) {
+ if ((show_bits(gb, ctx->time_increment_bits + 6) & 0x37) == 0x30)
+ break;
+ } else if ((show_bits(gb, ctx->time_increment_bits + 5) & 0x1F) == 0x18)
+ break;
+ }
+
+ v4l_dbg(0, V4L_DEBUG_CODEC_PARSER, "time_increment_bits set to %d bits, based on bitstream analysis\n", ctx->time_increment_bits);
+ if (ctx->framerate.num && 4*ctx->framerate.num < 1<<ctx->time_increment_bits) {
+ ctx->framerate.num = 1<<ctx->time_increment_bits;
+ //ctx->time_base = av_inv_q(av_mul_q(ctx->framerate, (AVRational){ctx->ticks_per_frame, 1}));
+ }
+ }
+
+ if (IS_3IV1)
+ time_increment = get_bits1(gb); // FIXME investigate further
+ else
+ time_increment = get_bits(gb, ctx->time_increment_bits);
+
+ if (s->pict_type != AV_PICTURE_TYPE_B) {
+ s->last_time_base = s->time_base;
+ s->time_base += time_incr;
+ s->time = s->time_base * (int64_t)ctx->framerate.num + time_increment;
+ //if (s->workaround_bugs & FF_BUG_UMP4) { }
+ s->pp_time = s->time - s->last_non_b_time;
+ s->last_non_b_time = s->time;
+ } else {
+ s->time = (s->last_time_base + time_incr) * (int64_t)ctx->framerate.num + time_increment;
+ s->pb_time = s->pp_time - (s->last_non_b_time - s->time);
+ if (s->pp_time <= s->pb_time ||
+ s->pp_time <= s->pp_time - s->pb_time ||
+ s->pp_time <= 0) {
+ /* messed up order, maybe after seeking? skipping current B-frame */
+ return FRAME_SKIPPED;
+ }
+ //ff_mpeg4_init_direct_mv(s);
+
+ if (ctx->t_frame == 0)
+ ctx->t_frame = s->pb_time;
+ if (ctx->t_frame == 0)
+ ctx->t_frame = 1; // 1/0 protection
+ s->pp_field_time = (ROUNDED_DIV(s->last_non_b_time, ctx->t_frame) -
+ ROUNDED_DIV(s->last_non_b_time - s->pp_time, ctx->t_frame)) * 2;
+ s->pb_field_time = (ROUNDED_DIV(s->time, ctx->t_frame) -
+ ROUNDED_DIV(s->last_non_b_time - s->pp_time, ctx->t_frame)) * 2;
+ if (s->pp_field_time <= s->pb_field_time || s->pb_field_time <= 1) {
+ s->pb_field_time = 2;
+ s->pp_field_time = 4;
+ if (!s->progressive_sequence)
+ return FRAME_SKIPPED;
+ }
+ }
+
+ if (ctx->framerate.den)
+ pts = ROUNDED_DIV(s->time, ctx->framerate.den);
+ else
+ pts = AV_NOPTS_VALUE;
+ v4l_dbg(0, V4L_DEBUG_CODEC_PARSER, "MPEG4 PTS: %lld\n", pts);
+
+ check_marker(gb, "before vop_coded");
+
+ /* vop coded */
+ if (get_bits1(gb) != 1) {
+ if (1)
+ v4l_dbg(0, V4L_DEBUG_CODEC_ERROR, "vop not coded\n");
+ return FRAME_SKIPPED;
+ }
+ if (ctx->new_pred)
+ decode_new_pred(ctx, gb);
+
+ if (ctx->shape != BIN_ONLY_SHAPE &&
+ (s->pict_type == AV_PICTURE_TYPE_P ||
+ (s->pict_type == AV_PICTURE_TYPE_S &&
+ ctx->vol_sprite_usage == GMC_SPRITE))) {
+ /* rounding type for motion estimation */
+ s->no_rounding = get_bits1(gb);
+ } else {
+ s->no_rounding = 0;
+ }
+ // FIXME reduced res stuff
+
+ if (ctx->shape != RECT_SHAPE) {
+ if (ctx->vol_sprite_usage != 1 || s->pict_type != AV_PICTURE_TYPE_I) {
+ skip_bits(gb, 13); /* width */
+ check_marker(gb, "after width");
+ skip_bits(gb, 13); /* height */
+ check_marker(gb, "after height");
+ skip_bits(gb, 13); /* hor_spat_ref */
+ check_marker(gb, "after hor_spat_ref");
+ skip_bits(gb, 13); /* ver_spat_ref */
+ }
+ skip_bits1(gb); /* change_CR_disable */
+
+ if (get_bits1(gb) != 0)
+ skip_bits(gb, 8); /* constant_alpha_value */
+ }
+
+ // FIXME complexity estimation stuff
+
+ if (ctx->shape != BIN_ONLY_SHAPE) {
+ skip_bits_long(gb, ctx->cplx_estimation_trash_i);
+ if (s->pict_type != AV_PICTURE_TYPE_I)
+ skip_bits_long(gb, ctx->cplx_estimation_trash_p);
+ if (s->pict_type == AV_PICTURE_TYPE_B)
+ skip_bits_long(gb, ctx->cplx_estimation_trash_b);
+
+ if (get_bits_left(gb) < 3) {
+ v4l_dbg(0, V4L_DEBUG_CODEC_ERROR, "Header truncated\n");
+ return -1;
+ }
+ ctx->intra_dc_threshold = ff_mpeg4_dc_threshold[get_bits(gb, 3)];
+ if (!s->progressive_sequence) {
+ s->top_field_first = get_bits1(gb);
+ s->alternate_scan = get_bits1(gb);
+ } else
+ s->alternate_scan = 0;
+ }
+
+ /*if (s->alternate_scan) { } */
+
+ if (s->pict_type == AV_PICTURE_TYPE_S) {
+ if((ctx->vol_sprite_usage == STATIC_SPRITE ||
+ ctx->vol_sprite_usage == GMC_SPRITE)) {
+ //if (mpeg4_decode_sprite_trajectory(ctx, gb) < 0)
+ //return -1;
+ if (ctx->sprite_brightness_change)
+ v4l_dbg(0, V4L_DEBUG_CODEC_ERROR, "sprite_brightness_change not supported\n");
+ if (ctx->vol_sprite_usage == STATIC_SPRITE)
+ v4l_dbg(0, V4L_DEBUG_CODEC_ERROR, "static sprite not supported\n");
+ } else {
+ memset(s->sprite_offset, 0, sizeof(s->sprite_offset));
+ memset(s->sprite_delta, 0, sizeof(s->sprite_delta));
+ }
+ }
+
+ if (ctx->shape != BIN_ONLY_SHAPE) {
+ s->chroma_qscale = s->qscale = get_bits(gb, s->quant_precision);
+ if (s->qscale == 0) {
+ v4l_dbg(0, V4L_DEBUG_CODEC_ERROR, "Error, header damaged or not MPEG-4 header (qscale=0)\n");
+ return -1; // makes no sense to continue, as there is nothing left from the image then
+ }
+
+ if (s->pict_type != AV_PICTURE_TYPE_I) {
+ s->f_code = get_bits(gb, 3); /* fcode_for */
+ if (s->f_code == 0) {
+ v4l_dbg(0, V4L_DEBUG_CODEC_ERROR, "Error, header damaged or not MPEG-4 header (f_code=0)\n");
+ s->f_code = 1;
+ return -1; // makes no sense to continue, as there is nothing left from the image then
+ }
+ } else
+ s->f_code = 1;
+
+ if (s->pict_type == AV_PICTURE_TYPE_B) {
+ s->b_code = get_bits(gb, 3);
+ if (s->b_code == 0) {
+ v4l_dbg(0, V4L_DEBUG_CODEC_ERROR, "Error, header damaged or not MPEG4 header (b_code=0)\n");
+ s->b_code=1;
+ return -1; // makes no sense to continue, as the MV decoding will break very quickly
+ }
+ } else
+ s->b_code = 1;
+
+ if (1) {
+ v4l_dbg(0, V4L_DEBUG_CODEC_PARSER, "qp:%d fc:%d,%d %s size:%d pro:%d alt:%d top:%d %spel part:%d resync:%d w:%d a:%d rnd:%d vot:%d%s dc:%d ce:%d/%d/%d time:%ld tincr:%d\n",
+ s->qscale, s->f_code, s->b_code,
+ s->pict_type == AV_PICTURE_TYPE_I ? "I" : (s->pict_type == AV_PICTURE_TYPE_P ? "P" : (s->pict_type == AV_PICTURE_TYPE_B ? "B" : "S")),
+ gb->size_in_bits,s->progressive_sequence, s->alternate_scan,
+ s->top_field_first, s->quarter_sample ? "q" : "h",
+ s->data_partitioning, ctx->resync_marker,
+ ctx->num_sprite_warping_points, s->sprite_warping_accuracy,
+ 1 - s->no_rounding, s->vo_type,
+ ctx->vol_control_parameters ? " VOLC" : " ", ctx->intra_dc_threshold,
+ ctx->cplx_estimation_trash_i, ctx->cplx_estimation_trash_p,
+ ctx->cplx_estimation_trash_b,
+ s->time,
+ time_increment);
+ }
+
+ if (!ctx->scalability) {
+ if (ctx->shape != RECT_SHAPE && s->pict_type != AV_PICTURE_TYPE_I)
+ skip_bits1(gb); // vop shape coding type
+ } else {
+ if (ctx->enhancement_type) {
+ int load_backward_shape = get_bits1(gb);
+ if (load_backward_shape)
+ v4l_dbg(0, V4L_DEBUG_CODEC_ERROR, "load backward shape isn't supported\n");
+ }
+ skip_bits(gb, 2); // ref_select_code
+ }
+ }
+ /* detect buggy encoders which don't set the low_delay flag
+ * (divx4/xvid/opendivx). Note we cannot detect divx5 without B-frames
+ * easily (although it's buggy too) */
+ if (s->vo_type == 0 && ctx->vol_control_parameters == 0 &&
+ ctx->divx_version == -1 && s->picture_number == 0) {
+ v4l_dbg(0, V4L_DEBUG_CODEC_PARSER, "looks like this file was encoded with (divx4/(old)xvid/opendivx) -> forcing low_delay flag\n");
+ s->low_delay = 1;
+ }
+
+ s->picture_number++; // better than pic number==0 always ;)
+
+ // FIXME add short header support
+ //s->y_dc_scale_table = ff_mpeg4_y_dc_scale_table;
+ //s->c_dc_scale_table = ff_mpeg4_c_dc_scale_table;
+
+ return 0;
+}
+
+/**
+ * Decode MPEG-4 headers.
+ * @return <0 if no VOP found (or a damaged one)
+ * FRAME_SKIPPED if a not coded VOP is found
+ * 0 if a VOP is found
+ */
+int ff_mpeg4_decode_picture_header(struct mpeg4_dec_param *ctx, struct get_bits_context *gb)
+{
+ struct MpegEncContext *s = &ctx->m;
+
+ unsigned startcode, v;
+ int ret;
+ int vol = 0;
+ int bits_per_raw_sample = 0;
+
+ s->ctx = ctx;
+
+ /* search next start code */
+ align_get_bits(gb);
+
+ // If we have not switched to studio profile than we also did not switch bps
+ // that means something else (like a previous instance) outside set bps which
+ // would be inconsistant with the currect state, thus reset it
+ if (!s->studio_profile && bits_per_raw_sample != 8)
+ bits_per_raw_sample = 0;
+
+ if (show_bits(gb, 24) == 0x575630) {
+ skip_bits(gb, 24);
+ if (get_bits(gb, 8) == 0xF0)
+ goto end;
+ }
+
+ startcode = 0xff;
+ for (;;) {
+ if (get_bits_count(gb) >= gb->size_in_bits) {
+ if (gb->size_in_bits == 8) {
+ v4l_dbg(0, V4L_DEBUG_CODEC_PARSER, "frame skip %d\n", gb->size_in_bits);
+ return FRAME_SKIPPED; // divx bug
+ } else
+ return -1; // end of stream
+ }
+
+ /* use the bits after the test */
+ v = get_bits(gb, 8);
+ startcode = ((startcode << 8) | v) & 0xffffffff;
+
+ if ((startcode & 0xFFFFFF00) != 0x100)
+ continue; // no startcode
+
+ if (1) { //debug
+ v4l_dbg(0, V4L_DEBUG_CODEC_PARSER, "startcode: %3X \n", startcode);
+ if (startcode <= 0x11F)
+ v4l_dbg(0, V4L_DEBUG_CODEC_PARSER, "Video Object Start\n");
+ else if (startcode <= 0x12F)
+ v4l_dbg(0, V4L_DEBUG_CODEC_PARSER, "Video Object Layer Start\n");
+ else if (startcode <= 0x13F)
+ v4l_dbg(0, V4L_DEBUG_CODEC_PARSER, "Reserved\n");
+ else if (startcode <= 0x15F)
+ v4l_dbg(0, V4L_DEBUG_CODEC_PARSER, "FGS bp start\n");
+ else if (startcode <= 0x1AF)
+ v4l_dbg(0, V4L_DEBUG_CODEC_PARSER, "Reserved\n");
+ else if (startcode == 0x1B0)
+ v4l_dbg(0, V4L_DEBUG_CODEC_PARSER, "Visual Object Seq Start\n");
+ else if (startcode == 0x1B1)
+ v4l_dbg(0, V4L_DEBUG_CODEC_PARSER, "Visual Object Seq End\n");
+ else if (startcode == 0x1B2)
+ v4l_dbg(0, V4L_DEBUG_CODEC_PARSER, "User Data\n");
+ else if (startcode == 0x1B3)
+ v4l_dbg(0, V4L_DEBUG_CODEC_PARSER, "Group of VOP start\n");
+ else if (startcode == 0x1B4)
+ v4l_dbg(0, V4L_DEBUG_CODEC_PARSER, "Video Session Error\n");
+ else if (startcode == 0x1B5)
+ v4l_dbg(0, V4L_DEBUG_CODEC_PARSER, "Visual Object Start\n");
+ else if (startcode == 0x1B6)
+ v4l_dbg(0, V4L_DEBUG_CODEC_PARSER, "Video Object Plane start\n");
+ else if (startcode == 0x1B7)
+ v4l_dbg(0, V4L_DEBUG_CODEC_PARSER, "slice start\n");
+ else if (startcode == 0x1B8)
+ v4l_dbg(0, V4L_DEBUG_CODEC_PARSER, "extension start\n");
+ else if (startcode == 0x1B9)
+ v4l_dbg(0, V4L_DEBUG_CODEC_PARSER, "fgs start\n");
+ else if (startcode == 0x1BA)
+ v4l_dbg(0, V4L_DEBUG_CODEC_PARSER, "FBA Object start\n");
+ else if (startcode == 0x1BB)
+ v4l_dbg(0, V4L_DEBUG_CODEC_PARSER, "FBA Object Plane start\n");
+ else if (startcode == 0x1BC)
+ v4l_dbg(0, V4L_DEBUG_CODEC_PARSER, "Mesh Object start\n");
+ else if (startcode == 0x1BD)
+ v4l_dbg(0, V4L_DEBUG_CODEC_PARSER, "Mesh Object Plane start\n");
+ else if (startcode == 0x1BE)
+ v4l_dbg(0, V4L_DEBUG_CODEC_PARSER, "Still Texture Object start\n");
+ else if (startcode == 0x1BF)
+ v4l_dbg(0, V4L_DEBUG_CODEC_PARSER, "Texture Spatial Layer start\n");
+ else if (startcode == 0x1C0)
+ v4l_dbg(0, V4L_DEBUG_CODEC_PARSER, "Texture SNR Layer start\n");
+ else if (startcode == 0x1C1)
+ v4l_dbg(0, V4L_DEBUG_CODEC_PARSER, "Texture Tile start\n");
+ else if (startcode == 0x1C2)
+ v4l_dbg(0, V4L_DEBUG_CODEC_PARSER, "Texture Shape Layer start\n");
+ else if (startcode == 0x1C3)
+ v4l_dbg(0, V4L_DEBUG_CODEC_PARSER, "stuffing start\n");
+ else if (startcode <= 0x1C5)
+ v4l_dbg(0, V4L_DEBUG_CODEC_PARSER, "reserved\n");
+ else if (startcode <= 0x1FF)
+ v4l_dbg(0, V4L_DEBUG_CODEC_PARSER, "System start\n");
+ }
+
+ if (startcode >= 0x120 && startcode <= 0x12F) {
+ if (vol) {
+ v4l_dbg(0, V4L_DEBUG_CODEC_ERROR, "Ignoring multiple VOL headers\n");
+ continue;
+ }
+ vol++;
+ if ((ret = decode_vol_header(ctx, gb)) < 0)
+ return ret;
+ } else if (startcode == USER_DATA_STARTCODE) {
+ decode_user_data(ctx, gb);
+ } else if (startcode == GOP_STARTCODE) {
+ mpeg4_decode_gop_header(s, gb);
+ } else if (startcode == VOS_STARTCODE) {
+ int profile, level;
+ mpeg4_decode_profile_level(s, gb, &profile, &level);
+ if (profile == FF_PROFILE_MPEG4_SIMPLE_STUDIO &&
+ (level > 0 && level < 9)) {
+ s->studio_profile = 1;
+ next_start_code_studio(gb);
+ extension_and_user_data(s, gb, 0);
+ } else if (s->studio_profile) {
+ v4l_dbg(0, V4L_DEBUG_CODEC_ERROR, "Mixes studio and non studio profile\n");
+ return -1;
+ }
+ ctx->profile = profile;
+ ctx->level = level;
+ } else if (startcode == VISUAL_OBJ_STARTCODE) {
+ if (s->studio_profile) {
+ if ((ret = decode_studiovisualobject(ctx, gb)) < 0)
+ return ret;
+ } else
+ mpeg4_decode_visual_object(s, gb);
+ } else if (startcode == VOP_STARTCODE) {
+ break;
+ }
+
+ align_get_bits(gb);
+ startcode = 0xff;
+ }
+
+end:
+ if (s->studio_profile) {
+ if (!bits_per_raw_sample) {
+ v4l_dbg(0, V4L_DEBUG_CODEC_ERROR, "Missing VOL header\n");
+ return -1;
+ }
+ return decode_studio_vop_header(ctx, gb);
+ } else
+ return decode_vop_header(ctx, gb);
+}
+
+int mpeg4_decode_extradata_ps(u8 *buf, int size, struct mpeg4_param_sets *ps)
+{
+ int ret = 0;
+ struct get_bits_context gb;
+
+ ps->head_parsed = false;
+
+ init_get_bits8(&gb, buf, size);
+
+ ret = ff_mpeg4_decode_picture_header(&ps->dec_ps, &gb);
+ if (ret < -1) {
+ v4l_dbg(0, V4L_DEBUG_CODEC_ERROR, "Failed to parse extradata\n");
+ return ret;
+ }
+
+ if (ps->dec_ps.m.width && ps->dec_ps.m.height)
+ ps->head_parsed = true;
+
+ return 0;
+}
+
diff --git a/drivers/amvdec_ports/decoder/aml_mpeg4_parser.h b/drivers/amvdec_ports/decoder/aml_mpeg4_parser.h
new file mode 100644
index 0000000..f9b71cf
--- /dev/null
+++ b/drivers/amvdec_ports/decoder/aml_mpeg4_parser.h
@@ -0,0 +1,259 @@
+#ifndef AVCODEC_MPEG4VIDEO_H
+#define AVCODEC_MPEG4VIDEO_H
+
+#include "../aml_vcodec_drv.h"
+#include "../utils/common.h"
+#ifdef CONFIG_AMLOGIC_MEDIA_V4L_SOFTWARE_PARSER
+#include "../utils/pixfmt.h"
+#endif
+
+//mpeg4 profile
+#define FF_PROFILE_MPEG4_SIMPLE 0
+#define FF_PROFILE_MPEG4_SIMPLE_SCALABLE 1
+#define FF_PROFILE_MPEG4_CORE 2
+#define FF_PROFILE_MPEG4_MAIN 3
+#define FF_PROFILE_MPEG4_N_BIT 4
+#define FF_PROFILE_MPEG4_SCALABLE_TEXTURE 5
+#define FF_PROFILE_MPEG4_SIMPLE_FACE_ANIMATION 6
+#define FF_PROFILE_MPEG4_BASIC_ANIMATED_TEXTURE 7
+#define FF_PROFILE_MPEG4_HYBRID 8
+#define FF_PROFILE_MPEG4_ADVANCED_REAL_TIME 9
+#define FF_PROFILE_MPEG4_CORE_SCALABLE 10
+#define FF_PROFILE_MPEG4_ADVANCED_CODING 11
+#define FF_PROFILE_MPEG4_ADVANCED_CORE 12
+#define FF_PROFILE_MPEG4_ADVANCED_SCALABLE_TEXTURE 13
+#define FF_PROFILE_MPEG4_SIMPLE_STUDIO 14
+#define FF_PROFILE_MPEG4_ADVANCED_SIMPLE 15
+
+// shapes
+#define RECT_SHAPE 0
+#define BIN_SHAPE 1
+#define BIN_ONLY_SHAPE 2
+#define GRAY_SHAPE 3
+
+#define SIMPLE_VO_TYPE 1
+#define CORE_VO_TYPE 3
+#define MAIN_VO_TYPE 4
+#define NBIT_VO_TYPE 5
+#define ARTS_VO_TYPE 10
+#define ACE_VO_TYPE 12
+#define SIMPLE_STUDIO_VO_TYPE 14
+#define CORE_STUDIO_VO_TYPE 15
+#define ADV_SIMPLE_VO_TYPE 17
+
+#define VOT_VIDEO_ID 1
+#define VOT_STILL_TEXTURE_ID 2
+
+#define FF_PROFILE_UNKNOWN -99
+#define FF_PROFILE_RESERVED -100
+
+// aspect_ratio_info
+#define EXTENDED_PAR 15
+
+//vol_sprite_usage / sprite_enable
+#define STATIC_SPRITE 1
+#define GMC_SPRITE 2
+
+#define MOTION_MARKER 0x1F001
+#define DC_MARKER 0x6B001
+
+#define VOS_STARTCODE 0x1B0
+#define USER_DATA_STARTCODE 0x1B2
+#define GOP_STARTCODE 0x1B3
+#define VISUAL_OBJ_STARTCODE 0x1B5
+#define VOP_STARTCODE 0x1B6
+#define SLICE_STARTCODE 0x1B7
+#define EXT_STARTCODE 0x1B8
+
+#define QUANT_MATRIX_EXT_ID 0x3
+
+/* smaller packets likely don't contain a real frame */
+#define MAX_NVOP_SIZE 19
+
+#define IS_3IV1 0
+
+#define CHROMA_420 1
+#define CHROMA_422 2
+#define CHROMA_444 3
+
+#define FF_ASPECT_EXTENDED 15
+
+#define AV_NOPTS_VALUE (LONG_MIN)
+
+/**
+ * Return value for header parsers if frame is not coded.
+ * */
+#define FRAME_SKIPPED 100
+
+enum AVPictureType {
+ AV_PICTURE_TYPE_NONE = 0, ///< Undefined
+ AV_PICTURE_TYPE_I, ///< Intra
+ AV_PICTURE_TYPE_P, ///< Predicted
+ AV_PICTURE_TYPE_B, ///< Bi-dir predicted
+ AV_PICTURE_TYPE_S, ///< S(GMC)-VOP MPEG-4
+ AV_PICTURE_TYPE_SI, ///< Switching Intra
+ AV_PICTURE_TYPE_SP, ///< Switching Predicted
+ AV_PICTURE_TYPE_BI, ///< BI type
+};
+
+struct VLC {
+ int bits;
+ short (*table)[2]; ///< code, bits
+ int table_size, table_allocated;
+};
+
+/**
+ * MpegEncContext.
+ */
+struct MpegEncContext {
+ struct mpeg4_dec_param *ctx;
+
+ /* the following parameters must be initialized before encoding */
+ int width, height;///< picture size. must be a multiple of 16
+ int codec_tag; ///< internal codec_tag upper case converted from avctx codec_tag
+ int picture_number; //FIXME remove, unclear definition
+
+ /** matrix transmitted in the bitstream */
+ u16 intra_matrix[64];
+ u16 chroma_intra_matrix[64];
+ u16 inter_matrix[64];
+ u16 chroma_inter_matrix[64];
+
+ /* MPEG-4 specific */
+ int studio_profile;
+ int time_base; ///< time in seconds of last I,P,S Frame
+ int quant_precision;
+ int quarter_sample; ///< 1->qpel, 0->half pel ME/MC
+ int aspect_ratio_info; //FIXME remove
+ int sprite_warping_accuracy;
+ int data_partitioning; ///< data partitioning flag from header
+ int low_delay; ///< no reordering needed / has no B-frames
+ int vo_type;
+ int mpeg_quant;
+
+ /* divx specific, used to workaround (many) bugs in divx5 */
+ int divx_packed;
+
+ /* MPEG-2-specific - I wished not to have to support this mess. */
+ int progressive_sequence;
+
+ int progressive_frame;
+ int interlaced_dct;
+
+ int h_edge_pos, v_edge_pos;///< horizontal / vertical position of the right/bottom edge (pixel replication)
+ const u8 *y_dc_scale_table; ///< qscale -> y_dc_scale table
+ const u8 *c_dc_scale_table; ///< qscale -> c_dc_scale table
+ int qscale; ///< QP
+ int chroma_qscale; ///< chroma QP
+ int pict_type; ///< AV_PICTURE_TYPE_I, AV_PICTURE_TYPE_P, AV_PICTURE_TYPE_B, ...
+ int f_code; ///< forward MV resolution
+ int b_code; ///< backward MV resolution for B-frames (MPEG-4)
+ int no_rounding; /**< apply no rounding to motion compensation (MPEG-4, msmpeg4, ...)
+ for B-frames rounding mode is always 0 */
+ int last_time_base;
+ long time; ///< time of current frame
+ long last_non_b_time;
+ u16 pp_time; ///< time distance between the last 2 p,s,i frames
+ u16 pb_time; ///< time distance between the last b and p,s,i frame
+ u16 pp_field_time;
+ u16 pb_field_time; ///< like above, just for interlaced
+ int real_sprite_warping_points;
+ int sprite_offset[2][2]; ///< sprite offset[isChroma][isMVY]
+ int sprite_delta[2][2]; ///< sprite_delta [isY][isMVY]
+ int mcsel;
+ int partitioned_frame; ///< is current frame partitioned
+ int top_field_first;
+ int alternate_scan;
+ int last_dc[3]; ///< last DC values for MPEG-1
+ int dct_precision;
+ int intra_dc_precision;
+ int frame_pred_frame_dct;
+ int q_scale_type;
+ int context_reinit;
+ int chroma_format;
+};
+
+struct mpeg4_dec_param {
+ struct MpegEncContext m;
+
+ /// number of bits to represent the fractional part of time
+ int time_increment_bits;
+ int shape;
+ int vol_sprite_usage;
+ int sprite_brightness_change;
+ int num_sprite_warping_points;
+ /// sprite trajectory points
+ u16 sprite_traj[4][2];
+ /// sprite shift [isChroma]
+ int sprite_shift[2];
+
+ // reversible vlc
+ int rvlc;
+ /// could this stream contain resync markers
+ int resync_marker;
+ /// time distance of first I -> B, used for interlaced B-frames
+ int t_frame;
+
+ int new_pred;
+ int enhancement_type;
+ int scalability;
+ int use_intra_dc_vlc;
+
+ /// QP above which the ac VLC should be used for intra dc
+ int intra_dc_threshold;
+
+ /* bug workarounds */
+ int divx_version;
+ int divx_build;
+ int xvid_build;
+ int lavc_build;
+
+ /// flag for having shown the warning about invalid Divx B-frames
+ int showed_packed_warning;
+ /** does the stream contain the low_delay flag,
+ * used to work around buggy encoders. */
+ int vol_control_parameters;
+ int cplx_estimation_trash_i;
+ int cplx_estimation_trash_p;
+ int cplx_estimation_trash_b;
+
+ struct VLC studio_intra_tab[12];
+ struct VLC studio_luma_dc;
+ struct VLC studio_chroma_dc;
+
+ int rgb;
+
+ struct AVRational time_base;
+ int ticks_per_frame;
+ struct AVRational sample_aspect_ratio;
+ enum AVColorPrimaries color_primaries;
+ enum AVColorTransferCharacteristic color_trc;
+ enum AVColorSpace colorspace;
+#ifdef CONFIG_AMLOGIC_MEDIA_V4L_SOFTWARE_PARSER
+ enum AVPixelFormat pix_fmt;
+ enum AVColorRange color_range;
+ enum AVChromaLocation chroma_sample_location;
+#endif
+ int err_recognition;
+ int idct_algo;
+ int bits_per_raw_sample;
+ int profile;
+ int level;
+ struct AVRational framerate;
+ int flags;
+};
+
+struct mpeg4_param_sets {
+ bool head_parsed;
+ /* currently active parameter sets */
+ struct mpeg4_dec_param dec_ps;
+};
+
+#ifdef CONFIG_AMLOGIC_MEDIA_V4L_SOFTWARE_PARSER
+int mpeg4_decode_extradata_ps(u8 *buf, int size, struct mpeg4_param_sets *ps);
+#else
+inline int mpeg4_decode_extradata_ps(u8 *buf, int size, struct mpeg4_param_sets *ps) { return -1; }
+#endif
+
+#endif
+
diff --git a/drivers/amvdec_ports/decoder/aml_vp9_parser.c b/drivers/amvdec_ports/decoder/aml_vp9_parser.c
new file mode 100644
index 0000000..21d5283
--- /dev/null
+++ b/drivers/amvdec_ports/decoder/aml_vp9_parser.c
@@ -0,0 +1,299 @@
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/vmalloc.h>
+#include <linux/mm.h>
+#include <linux/string.h>
+
+#include "aml_vp9_parser.h"
+#include "../utils/get_bits.h"
+#include "../utils/put_bits.h"
+#include "../utils/golomb.h"
+#include "../utils/common.h"
+#include "utils.h"
+
+#define VP9_SYNCCODE 0x498342
+
+static int read_colorspace_details(struct VP9Context *s, int profile)
+{
+ static const enum AVColorSpace colorspaces[8] = {
+ AVCOL_SPC_UNSPECIFIED, AVCOL_SPC_BT470BG, AVCOL_SPC_BT709, AVCOL_SPC_SMPTE170M,
+ AVCOL_SPC_SMPTE240M, AVCOL_SPC_BT2020_NCL, AVCOL_SPC_RESERVED, AVCOL_SPC_RGB,
+ };
+
+ enum AVColorSpace colorspace;
+ int color_range;
+ int bits = profile <= 1 ? 0 : 1 + get_bits1(&s->gb); // 0:8, 1:10, 2:12
+
+ s->bpp_index = bits;
+ s->s.h.bpp = 8 + bits * 2;
+ s->bytesperpixel = (7 + s->s.h.bpp) >> 3;
+ colorspace = colorspaces[get_bits(&s->gb, 3)];
+ if (colorspace == AVCOL_SPC_RGB) { // RGB = profile 1
+ if (profile & 1) {
+ if (get_bits1(&s->gb)) {
+ v4l_dbg(0, V4L_DEBUG_CODEC_ERROR, "Reserved bit set in RGB\n");
+ return -1;
+ }
+ } else {
+ v4l_dbg(0, V4L_DEBUG_CODEC_ERROR, "RGB not supported in profile %d\n", profile);
+ return -1;
+ }
+ } else {
+ static const enum AVPixelFormat pix_fmt_for_ss[3][2 /* v */][2 /* h */] = {
+ { { AV_PIX_FMT_YUV444P, AV_PIX_FMT_YUV422P },
+ { AV_PIX_FMT_YUV440P, AV_PIX_FMT_YUV420P } },
+ { { AV_PIX_FMT_YUV444P10, AV_PIX_FMT_YUV422P10 },
+ { AV_PIX_FMT_YUV440P10, AV_PIX_FMT_YUV420P10 } },
+ { { AV_PIX_FMT_YUV444P12, AV_PIX_FMT_YUV422P12 },
+ { AV_PIX_FMT_YUV440P12, AV_PIX_FMT_YUV420P12 } }};
+ color_range = get_bits1(&s->gb) ? 2 : 1;
+ if (profile & 1) {
+ s->ss_h = get_bits1(&s->gb);
+ s->ss_v = get_bits1(&s->gb);
+ s->pix_fmt = pix_fmt_for_ss[bits][s->ss_v][s->ss_h];
+ if (s->pix_fmt == AV_PIX_FMT_YUV420P) {
+ v4l_dbg(0, V4L_DEBUG_CODEC_ERROR, "YUV 4:2:0 not supported in profile %d\n", profile);
+ return -1;
+ } else if (get_bits1(&s->gb)) {
+ v4l_dbg(0, V4L_DEBUG_CODEC_ERROR, "Profile %d color details reserved bit set\n", profile);
+ return -1;
+ }
+ } else {
+ s->ss_h = s->ss_v = 1;
+ s->pix_fmt = pix_fmt_for_ss[bits][1][1];
+ }
+ }
+
+ return 0;
+}
+
+int decode_frame_header(const u8 *data, int size, struct VP9Context *s, int *ref)
+{
+ int ret, last_invisible, profile;
+
+ /* general header */
+ if ((ret = init_get_bits8(&s->gb, data, size)) < 0) {
+ v4l_dbg(0, V4L_DEBUG_CODEC_ERROR, "Failed to initialize bitstream reader\n");
+ return ret;
+ }
+
+ if (get_bits(&s->gb, 2) != 0x2) { // frame marker
+ v4l_dbg(0, V4L_DEBUG_CODEC_ERROR, "Invalid frame marker\n");
+ return -1;
+ }
+
+ profile = get_bits1(&s->gb);
+ profile |= get_bits1(&s->gb) << 1;
+ if (profile == 3)
+ profile += get_bits1(&s->gb);
+
+ if (profile > 3) {
+ v4l_dbg(0, V4L_DEBUG_CODEC_ERROR, "Profile %d is not yet supported\n", profile);
+ return -1;
+ }
+
+ s->s.h.profile = profile;
+ if (get_bits1(&s->gb)) {
+ *ref = get_bits(&s->gb, 3);
+ return 0;
+ }
+
+ s->last_keyframe = s->s.h.keyframe;
+ s->s.h.keyframe = !get_bits1(&s->gb);
+
+ last_invisible = s->s.h.invisible;
+ s->s.h.invisible = !get_bits1(&s->gb);
+ s->s.h.errorres = get_bits1(&s->gb);
+ s->s.h.use_last_frame_mvs = !s->s.h.errorres && !last_invisible;
+
+ if (s->s.h.keyframe) {
+ if (get_bits_long(&s->gb, 24) != VP9_SYNCCODE) { // synccode
+ v4l_dbg(0, V4L_DEBUG_CODEC_ERROR, "Invalid sync code\n");
+ return -1;
+ }
+ if ((ret = read_colorspace_details(s,profile)) < 0)
+ return ret;
+ // for profile 1, here follows the subsampling bits
+ s->s.h.refreshrefmask = 0xff;
+ s->width = get_bits(&s->gb, 16) + 1;
+ s->height = get_bits(&s->gb, 16) + 1;
+ if (get_bits1(&s->gb)) { // has scaling
+ s->render_width = get_bits(&s->gb, 16) + 1;
+ s->render_height = get_bits(&s->gb, 16) + 1;
+ } else {
+ s->render_width = s->width;
+ s->render_height = s->height;
+ }
+ /*pr_info("keyframe res: (%d x %d), render size: (%d x %d)\n",
+ s->width, s->height, s->render_width, s->render_height);*/
+ } else {
+ s->s.h.intraonly = s->s.h.invisible ? get_bits1(&s->gb) : 0;
+ s->s.h.resetctx = s->s.h.errorres ? 0 : get_bits(&s->gb, 2);
+ if (s->s.h.intraonly) {
+ if (get_bits_long(&s->gb, 24) != VP9_SYNCCODE) { // synccode
+ v4l_dbg(0, V4L_DEBUG_CODEC_ERROR, "Invalid sync code\n");
+ return -1;
+ }
+ if (profile >= 1) {
+ if ((ret = read_colorspace_details(s, profile)) < 0)
+ return ret;
+ } else {
+ s->ss_h = s->ss_v = 1;
+ s->s.h.bpp = 8;
+ s->bpp_index = 0;
+ s->bytesperpixel = 1;
+ s->pix_fmt = AV_PIX_FMT_YUV420P;
+ }
+ s->s.h.refreshrefmask = get_bits(&s->gb, 8);
+ s->width = get_bits(&s->gb, 16) + 1;
+ s->height = get_bits(&s->gb, 16) + 1;
+ if (get_bits1(&s->gb)) { // has scaling
+ s->render_width = get_bits(&s->gb, 16) + 1;
+ s->render_height = get_bits(&s->gb, 16) + 1;
+ } else {
+ s->render_width = s->width;
+ s->render_height = s->height;
+ }
+ v4l_dbg(0, V4L_DEBUG_CODEC_PARSER, "intra res: (%d x %d), render size: (%d x %d)\n",
+ s->width, s->height, s->render_width, s->render_height);
+ } else {
+ s->s.h.refreshrefmask = get_bits(&s->gb, 8);
+ s->s.h.refidx[0] = get_bits(&s->gb, 3);
+ s->s.h.signbias[0] = get_bits1(&s->gb) && !s->s.h.errorres;
+ s->s.h.refidx[1] = get_bits(&s->gb, 3);
+ s->s.h.signbias[1] = get_bits1(&s->gb) && !s->s.h.errorres;
+ s->s.h.refidx[2] = get_bits(&s->gb, 3);
+ s->s.h.signbias[2] = get_bits1(&s->gb) && !s->s.h.errorres;
+
+ /*refresh_frame_flags;
+ for (i = 0; i < REFS_PER_FRAME; ++i) {
+ frame_refs[i];
+ ref_frame_sign_biases[i];
+ }
+ frame_size_from_refs();
+ high_precision_mv;
+ interp_filter();*/
+
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+int vp9_superframe_split_filter(struct vp9_superframe_split *s)
+{
+ int i, j, ret, marker;
+ bool is_superframe = false;
+ int *prefix = (int *)s->data;
+
+ if (!s->data)
+ return -1;
+
+ #define AML_PREFIX ('V' << 24 | 'L' << 16 | 'M' << 8 | 'A')
+ if (prefix[3] == AML_PREFIX) {
+ s->prefix_size = 16;
+ /*pr_info("the frame data has beed added header\n");*/
+ }
+
+ marker = s->data[s->data_size - 1];
+ if ((marker & 0xe0) == 0xc0) {
+ int length_size = 1 + ((marker >> 3) & 0x3);
+ int nb_frames = 1 + (marker & 0x7);
+ int idx_size = 2 + nb_frames * length_size;
+
+ if (s->data_size >= idx_size &&
+ s->data[s->data_size - idx_size] == marker) {
+ s64 total_size = 0;
+ int idx = s->data_size + 1 - idx_size;
+
+ for (i = 0; i < nb_frames; i++) {
+ int frame_size = 0;
+ for (j = 0; j < length_size; j++)
+ frame_size |= s->data[idx++] << (j * 8);
+
+ total_size += frame_size;
+ if (frame_size < 0 ||
+ total_size > s->data_size - idx_size) {
+ v4l_dbg(0, V4L_DEBUG_CODEC_ERROR, "Invalid frame size in a sframe: %d\n",
+ frame_size);
+ ret = -EINVAL;
+ goto fail;
+ }
+ s->sizes[i] = frame_size;
+ }
+
+ s->nb_frames = nb_frames;
+ s->size = total_size;
+ s->next_frame = 0;
+ s->next_frame_offset = 0;
+ is_superframe = true;
+ }
+ }else {
+ s->nb_frames = 1;
+ s->sizes[0] = s->data_size;
+ s->size = s->data_size;
+ }
+
+ /*pr_info("sframe: %d, frames: %d, IN: %x, OUT: %x\n",
+ is_superframe, s->nb_frames,
+ s->data_size, s->size);*/
+
+ /* parse uncompressed header. */
+ if (is_superframe) {
+ /* bitstream profile. */
+ /* frame type. (intra or inter) */
+ /* colorspace descriptor */
+ /* ... */
+
+ v4l_dbg(0, V4L_DEBUG_CODEC_PARSER, "the frame is a superframe.\n");
+ }
+
+ /*pr_err("in: %x, %d, out: %x, sizes %d,%d,%d,%d,%d,%d,%d,%d\n",
+ s->data_size,
+ s->nb_frames,
+ s->size,
+ s->sizes[0],
+ s->sizes[1],
+ s->sizes[2],
+ s->sizes[3],
+ s->sizes[4],
+ s->sizes[5],
+ s->sizes[6],
+ s->sizes[7]);*/
+
+ return 0;
+fail:
+ return ret;
+}
+
+int vp9_decode_extradata_ps(u8 *data, int size, struct vp9_param_sets *ps)
+{
+ int i, ref = -1, ret = 0;
+ struct vp9_superframe_split s = {0};
+
+ /*parse superframe.*/
+ s.data = data;
+ s.data_size = size;
+ ret = vp9_superframe_split_filter(&s);
+ if (ret) {
+ v4l_dbg(0, V4L_DEBUG_CODEC_ERROR, "parse frames failed.\n");
+ return ret;
+ }
+
+ for (i = 0; i < s.nb_frames; i++) {
+ u32 len = s.sizes[i] - s.prefix_size;
+ u8 *buf = s.data + s.next_frame_offset + s.prefix_size;
+
+ ret = decode_frame_header(buf, len, &ps->ctx, &ref);
+ if (!ret) {
+ ps->head_parsed = ref < 0 ? true : false;
+ return 0;
+ }
+
+ s.next_frame_offset = len + s.prefix_size;
+ }
+
+ return ret;
+}
+
diff --git a/drivers/amvdec_ports/decoder/aml_vp9_parser.h b/drivers/amvdec_ports/decoder/aml_vp9_parser.h
new file mode 100644
index 0000000..ddeddec
--- /dev/null
+++ b/drivers/amvdec_ports/decoder/aml_vp9_parser.h
@@ -0,0 +1,184 @@
+/*
+ * drivers/amvdec_ports/decoder/aml_vp9_parser.h
+ *
+ * Copyright (C) 2015 Amlogic, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#ifndef AML_VP9_PARSER_H
+#define AML_VP9_PARSER_H
+
+#include "../aml_vcodec_drv.h"
+#ifdef CONFIG_AMLOGIC_MEDIA_V4L_SOFTWARE_PARSER
+#include "../utils/pixfmt.h"
+#include "../utils/get_bits.h"
+#endif
+
+#define MAX_SEGMENT 8
+
+struct VP9BitstreamHeader {
+ // bitstream header
+ u8 profile;
+ u8 bpp;
+ u8 keyframe;
+ u8 invisible;
+ u8 errorres;
+ u8 intraonly;
+ u8 resetctx;
+ u8 refreshrefmask;
+ u8 highprecisionmvs;
+ u8 allowcompinter;
+ u8 refreshctx;
+ u8 parallelmode;
+ u8 framectxid;
+ u8 use_last_frame_mvs;
+ u8 refidx[3];
+ u8 signbias[3];
+ u8 fixcompref;
+ u8 varcompref[2];
+ struct {
+ u8 level;
+ char sharpness;
+ } filter;
+ struct {
+ u8 enabled;
+ u8 updated;
+ char mode[2];
+ char ref[4];
+ } lf_delta;
+ u8 yac_qi;
+ char ydc_qdelta, uvdc_qdelta, uvac_qdelta;
+ u8 lossless;
+ struct {
+ u8 enabled;
+ u8 temporal;
+ u8 absolute_vals;
+ u8 update_map;
+ u8 prob[7];
+ u8 pred_prob[3];
+ struct {
+ u8 q_enabled;
+ u8 lf_enabled;
+ u8 ref_enabled;
+ u8 skip_enabled;
+ u8 ref_val;
+ int16_t q_val;
+ char lf_val;
+ int16_t qmul[2][2];
+ u8 lflvl[4][2];
+ } feat[MAX_SEGMENT];
+ } segmentation;
+ struct {
+ u32 log2_tile_cols, log2_tile_rows;
+ u32 tile_cols, tile_rows;
+ } tiling;
+
+ int uncompressed_header_size;
+ int compressed_header_size;
+};
+
+struct VP9SharedContext {
+ struct VP9BitstreamHeader h;
+
+ //struct ThreadFrame refs[8];
+#define CUR_FRAME 0
+#define REF_FRAME_MVPAIR 1
+#define REF_FRAME_SEGMAP 2
+ //struct VP9Frame frames[3];
+};
+
+struct VP9Context {
+ struct VP9SharedContext s;
+#ifdef CONFIG_AMLOGIC_MEDIA_V4L_SOFTWARE_PARSER
+ struct get_bits_context gb;
+#endif
+ int pass, active_tile_cols;
+
+ u8 ss_h, ss_v;
+ u8 last_bpp, bpp_index, bytesperpixel;
+ u8 last_keyframe;
+ // sb_cols/rows, rows/cols and last_fmt are used for allocating all internal
+ // arrays, and are thus per-thread. w/h and gf_fmt are synced between threads
+ // and are therefore per-stream. pix_fmt represents the value in the header
+ // of the currently processed frame.
+ int width;
+ int height;
+
+ int render_width;
+ int render_height;
+#ifdef CONFIG_AMLOGIC_MEDIA_V4L_SOFTWARE_PARSER
+ enum AVPixelFormat pix_fmt, last_fmt, gf_fmt;
+#endif
+ u32 sb_cols, sb_rows, rows, cols;
+
+ struct {
+ u8 lim_lut[64];
+ u8 mblim_lut[64];
+ } filter_lut;
+ struct {
+ u8 coef[4][2][2][6][6][3];
+ } prob_ctx[4];
+ struct {
+ u8 coef[4][2][2][6][6][11];
+ } prob;
+
+ // contextual (above) cache
+ u8 *above_partition_ctx;
+ u8 *above_mode_ctx;
+ // FIXME maybe merge some of the below in a flags field?
+ u8 *above_y_nnz_ctx;
+ u8 *above_uv_nnz_ctx[2];
+ u8 *above_skip_ctx; // 1bit
+ u8 *above_txfm_ctx; // 2bit
+ u8 *above_segpred_ctx; // 1bit
+ u8 *above_intra_ctx; // 1bit
+ u8 *above_comp_ctx; // 1bit
+ u8 *above_ref_ctx; // 2bit
+ u8 *above_filter_ctx;
+
+ // whole-frame cache
+ u8 *intra_pred_data[3];
+
+ // block reconstruction intermediates
+ int block_alloc_using_2pass;
+ uint16_t mvscale[3][2];
+ u8 mvstep[3][2];
+};
+
+struct vp9_superframe_split {
+ /*in data*/
+ u8 *data;
+ u32 data_size;
+
+ /*out data*/
+ int nb_frames;
+ int size;
+ int next_frame;
+ u32 next_frame_offset;
+ int prefix_size;
+ int sizes[8];
+};
+
+struct vp9_param_sets {
+ bool head_parsed;
+ struct VP9Context ctx;
+};
+
+#ifdef CONFIG_AMLOGIC_MEDIA_V4L_SOFTWARE_PARSER
+int vp9_superframe_split_filter(struct vp9_superframe_split *s);
+int vp9_decode_extradata_ps(u8 *data, int size, struct vp9_param_sets *ps);
+#else
+inline int vp9_decode_extradata_ps(u8 *data, int size, struct vp9_param_sets *ps) { return -1; }
+#endif
+
+#endif //AML_VP9_PARSER_H
diff --git a/drivers/amvdec_ports/decoder/utils.h b/drivers/amvdec_ports/decoder/utils.h
new file mode 100644
index 0000000..26b1552
--- /dev/null
+++ b/drivers/amvdec_ports/decoder/utils.h
@@ -0,0 +1,31 @@
+/*
+ * drivers/amlogic/media_modules/amvdec_ports/decoder/utils.h
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#ifndef _UTILS_H
+#define _UTILS_H
+
+#define MAX(a, b) (((a) > (b)) ? (a) : (b))
+#define MIN(a, b) (((a) < (b)) ? (a) : (b))
+#define CLAMP(x, low, high) \
+ (((x) > (high)) ? (high) : (((x) < (low)) ? (low) : (x)))
+#define BITAT(x, n) ((x & (1 << n)) == (1 << n))
+
+typedef unsigned char uint8_t;
+typedef int int32_t;
+typedef unsigned int uint32_t;
+
+#endif //_UTILS_H
diff --git a/drivers/amvdec_ports/decoder/vdec_av1_if.c b/drivers/amvdec_ports/decoder/vdec_av1_if.c
new file mode 100644
index 0000000..c28ca10
--- /dev/null
+++ b/drivers/amvdec_ports/decoder/vdec_av1_if.c
@@ -0,0 +1,1374 @@
+/*
+* Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2 of the License, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful, but WITHOUT
+* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+* more details.
+*
+* You should have received a copy of the GNU General Public License along
+* with this program; if not, write to the Free Software Foundation, Inc.,
+* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*
+* Description:
+*/
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/timer.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <uapi/linux/swab.h>
+#include "../vdec_drv_if.h"
+#include "../aml_vcodec_util.h"
+#include "../aml_vcodec_dec.h"
+#include "../aml_vcodec_drv.h"
+#include "../aml_vcodec_adapt.h"
+#include "../vdec_drv_base.h"
+#include "../utils/common.h"
+
+#define KERNEL_ATRACE_TAG KERNEL_ATRACE_TAG_V4L2
+#include <trace/events/meson_atrace.h>
+
+#define PREFIX_SIZE (16)
+
+#define HEADER_BUFFER_SIZE (32 * 1024)
+#define SYNC_CODE (0x498342)
+
+extern int av1_need_prefix;
+
+/**
+ * struct av1_fb - av1 decode frame buffer information
+ * @vdec_fb_va : virtual address of struct vdec_fb
+ * @y_fb_dma : dma address of Y frame buffer (luma)
+ * @c_fb_dma : dma address of C frame buffer (chroma)
+ * @poc : picture order count of frame buffer
+ * @reserved : for 8 bytes alignment
+ */
+struct av1_fb {
+ uint64_t vdec_fb_va;
+ uint64_t y_fb_dma;
+ uint64_t c_fb_dma;
+ int32_t poc;
+ uint32_t reserved;
+};
+
+/**
+ * struct vdec_av1_dec_info - decode information
+ * @dpb_sz : decoding picture buffer size
+ * @resolution_changed : resoltion change happen
+ * @reserved : for 8 bytes alignment
+ * @bs_dma : Input bit-stream buffer dma address
+ * @y_fb_dma : Y frame buffer dma address
+ * @c_fb_dma : C frame buffer dma address
+ * @vdec_fb_va : VDEC frame buffer struct virtual address
+ */
+struct vdec_av1_dec_info {
+ uint32_t dpb_sz;
+ uint32_t resolution_changed;
+ uint32_t reserved;
+ uint64_t bs_dma;
+ uint64_t y_fb_dma;
+ uint64_t c_fb_dma;
+ uint64_t vdec_fb_va;
+};
+
+/**
+ * struct vdec_av1_vsi - shared memory for decode information exchange
+ * between VPU and Host.
+ * The memory is allocated by VPU then mapping to Host
+ * in vpu_dec_init() and freed in vpu_dec_deinit()
+ * by VPU.
+ * AP-W/R : AP is writer/reader on this item
+ * VPU-W/R: VPU is write/reader on this item
+ * @hdr_buf : Header parsing buffer (AP-W, VPU-R)
+ * @list_free : free frame buffer ring list (AP-W/R, VPU-W)
+ * @list_disp : display frame buffer ring list (AP-R, VPU-W)
+ * @dec : decode information (AP-R, VPU-W)
+ * @pic : picture information (AP-R, VPU-W)
+ * @crop : crop information (AP-R, VPU-W)
+ */
+struct vdec_av1_vsi {
+ char *header_buf;
+ int sps_size;
+ int pps_size;
+ int sei_size;
+ int head_offset;
+ struct vdec_av1_dec_info dec;
+ struct vdec_pic_info pic;
+ struct vdec_pic_info cur_pic;
+ struct v4l2_rect crop;
+ bool is_combine;
+ int nalu_pos;
+};
+
+/**
+ * struct vdec_av1_inst - av1 decoder instance
+ * @num_nalu : how many nalus be decoded
+ * @ctx : point to aml_vcodec_ctx
+ * @vsi : VPU shared information
+ */
+struct vdec_av1_inst {
+ unsigned int num_nalu;
+ struct aml_vcodec_ctx *ctx;
+ struct aml_vdec_adapt vdec;
+ struct vdec_av1_vsi *vsi;
+ struct aml_dec_params parms;
+ struct completion comp;
+ struct vdec_comp_buf_info comp_info;
+};
+
+/*!\brief OBU types. */
+enum OBU_TYPE {
+ OBU_SEQUENCE_HEADER = 1,
+ OBU_TEMPORAL_DELIMITER = 2,
+ OBU_FRAME_HEADER = 3,
+ OBU_TILE_GROUP = 4,
+ OBU_METADATA = 5,
+ OBU_FRAME = 6,
+ OBU_REDUNDANT_FRAME_HEADER = 7,
+ OBU_TILE_LIST = 8,
+ OBU_PADDING = 15,
+};
+
+/*!\brief OBU metadata types. */
+enum OBU_METADATA_TYPE {
+ OBU_METADATA_TYPE_RESERVED_0 = 0,
+ OBU_METADATA_TYPE_HDR_CLL = 1,
+ OBU_METADATA_TYPE_HDR_MDCV = 2,
+ OBU_METADATA_TYPE_SCALABILITY = 3,
+ OBU_METADATA_TYPE_ITUT_T35 = 4,
+ OBU_METADATA_TYPE_TIMECODE = 5,
+};
+
+struct ObuHeader {
+ size_t size; // Size (1 or 2 bytes) of the OBU header (including the
+ // optional OBU extension header) in the bitstream.
+ enum OBU_TYPE type;
+ int has_size_field;
+ int has_extension;
+ // The following fields come from the OBU extension header and therefore are
+ // only used if has_extension is true.
+ int temporal_layer_id;
+ int spatial_layer_id;
+};
+
+static const size_t kMaximumLeb128Size = 8;
+static const u8 kLeb128ByteMask = 0x7f; // Binary: 01111111
+
+// Disallow values larger than 32-bits to ensure consistent behavior on 32 and
+// 64 bit targets: value is typically used to determine buffer allocation size
+// when decoded.
+static const u64 kMaximumLeb128Value = ULONG_MAX;
+
+char obu_type_name[16][32] = {
+ "UNKNOWN",
+ "OBU_SEQUENCE_HEADER",
+ "OBU_TEMPORAL_DELIMITER",
+ "OBU_FRAME_HEADER",
+ "OBU_TILE_GROUP",
+ "OBU_METADATA",
+ "OBU_FRAME",
+ "OBU_REDUNDANT_FRAME_HEADER",
+ "OBU_TILE_LIST",
+ "UNKNOWN",
+ "UNKNOWN",
+ "UNKNOWN",
+ "UNKNOWN",
+ "UNKNOWN",
+ "UNKNOWN",
+ "OBU_PADDING"
+};
+
+char meta_type_name[6][32] = {
+ "OBU_METADATA_TYPE_RESERVED_0",
+ "OBU_METADATA_TYPE_HDR_CLL",
+ "OBU_METADATA_TYPE_HDR_MDCV",
+ "OBU_METADATA_TYPE_SCALABILITY",
+ "OBU_METADATA_TYPE_ITUT_T35",
+ "OBU_METADATA_TYPE_TIMECODE"
+};
+
+struct read_bit_buffer {
+ const u8 *bit_buffer;
+ const u8 *bit_buffer_end;
+ u32 bit_offset;
+};
+
+struct DataBuffer {
+ const u8 *data;
+ size_t size;
+};
+
+static int vdec_write_nalu(struct vdec_av1_inst *inst,
+ u8 *buf, u32 size, u64 ts);
+static int vdec_get_dw_mode(struct vdec_av1_inst *inst, int dw_mode);
+
+static void get_pic_info(struct vdec_av1_inst *inst,
+ struct vdec_pic_info *pic)
+{
+ *pic = inst->vsi->pic;
+
+ v4l_dbg(inst->ctx, V4L_DEBUG_CODEC_EXINFO,
+ "pic(%d, %d), buf(%d, %d)\n",
+ pic->visible_width, pic->visible_height,
+ pic->coded_width, pic->coded_height);
+ v4l_dbg(inst->ctx, V4L_DEBUG_CODEC_EXINFO,
+ "Y(%d, %d), C(%d, %d)\n",
+ pic->y_bs_sz, pic->y_len_sz,
+ pic->c_bs_sz, pic->c_len_sz);
+}
+
+static void get_crop_info(struct vdec_av1_inst *inst, struct v4l2_rect *cr)
+{
+ cr->left = inst->vsi->crop.left;
+ cr->top = inst->vsi->crop.top;
+ cr->width = inst->vsi->crop.width;
+ cr->height = inst->vsi->crop.height;
+
+ v4l_dbg(inst->ctx, V4L_DEBUG_CODEC_EXINFO,
+ "l=%d, t=%d, w=%d, h=%d\n",
+ cr->left, cr->top, cr->width, cr->height);
+}
+
+static void get_dpb_size(struct vdec_av1_inst *inst, unsigned int *dpb_sz)
+{
+ *dpb_sz = inst->vsi->dec.dpb_sz;
+ v4l_dbg(inst->ctx, V4L_DEBUG_CODEC_EXINFO, "sz=%d\n", *dpb_sz);
+}
+
+static u32 vdec_config_default_parms(u8 *parm)
+{
+ u8 *pbuf = parm;
+
+ pbuf += sprintf(pbuf, "parm_v4l_codec_enable:1;");
+ pbuf += sprintf(pbuf, "parm_v4l_buffer_margin:7;");
+ pbuf += sprintf(pbuf, "av1_double_write_mode:1;");
+ pbuf += sprintf(pbuf, "av1_buf_width:1920;");
+ pbuf += sprintf(pbuf, "av1_buf_height:1088;");
+ pbuf += sprintf(pbuf, "av1_max_pic_w:4096;");
+ pbuf += sprintf(pbuf, "av1_max_pic_h:2304;");
+ pbuf += sprintf(pbuf, "save_buffer_mode:0;");
+ pbuf += sprintf(pbuf, "no_head:0;");
+ pbuf += sprintf(pbuf, "parm_v4l_canvas_mem_mode:0;");
+ pbuf += sprintf(pbuf, "parm_v4l_canvas_mem_endian:0;");
+
+ return parm - pbuf;
+}
+
+static void vdec_parser_parms(struct vdec_av1_inst *inst)
+{
+ struct aml_vcodec_ctx *ctx = inst->ctx;
+
+ v4l_dbg(ctx, V4L_DEBUG_CODEC_EXINFO,
+ "%s:parms_status = 0x%x, present_flag = %d\n",
+ __func__, ctx->config.parm.dec.parms_status,
+ ctx->config.parm.dec.hdr.color_parms.present_flag);
+ if (ctx->config.parm.dec.parms_status &
+ V4L2_CONFIG_PARM_DECODE_CFGINFO) {
+ u8 *pbuf = ctx->config.buf;
+
+ pbuf += sprintf(pbuf, "parm_v4l_codec_enable:1;");
+ pbuf += sprintf(pbuf, "parm_v4l_buffer_margin:%d;",
+ ctx->config.parm.dec.cfg.ref_buf_margin);
+ pbuf += sprintf(pbuf, "av1_double_write_mode:%d;",
+ ctx->config.parm.dec.cfg.double_write_mode);
+ pbuf += sprintf(pbuf, "av1_buf_width:1920;");
+ pbuf += sprintf(pbuf, "av1_buf_height:1088;");
+ pbuf += sprintf(pbuf, "save_buffer_mode:0;");
+ pbuf += sprintf(pbuf, "no_head:0;");
+ pbuf += sprintf(pbuf, "parm_v4l_canvas_mem_mode:%d;",
+ ctx->config.parm.dec.cfg.canvas_mem_mode);
+ pbuf += sprintf(pbuf, "parm_v4l_canvas_mem_endian:%d;",
+ ctx->config.parm.dec.cfg.canvas_mem_endian);
+ pbuf += sprintf(pbuf, "parm_v4l_low_latency_mode:%d;",
+ ctx->config.parm.dec.cfg.low_latency_mode);
+ pbuf += sprintf(pbuf, "parm_v4l_duration:%d;",
+ ctx->config.parm.dec.cfg.duration);
+ ctx->config.length = pbuf - ctx->config.buf;
+ } else {
+ ctx->config.parm.dec.cfg.double_write_mode = 1;
+ ctx->config.parm.dec.cfg.ref_buf_margin = 7;
+ ctx->config.length = vdec_config_default_parms(ctx->config.buf);
+ }
+
+ if ((ctx->config.parm.dec.parms_status &