summaryrefslogtreecommitdiff
authorNanxin Qin <nanxin.qin@amlogic.com>2017-01-08 13:04:11 (GMT)
committer Zhi Zhou <zhi.zhou@amlogic.com>2017-02-10 07:15:14 (GMT)
commit111584911714906d40678d158b9dc2947c59af1c (patch)
tree0795e40455aee9a9ff8841375becc4c742d859cc
parent1e7a5a7cc36c276eb90666765d760991ec8dc723 (diff)
downloadmedia-111584911714906d40678d158b9dc2947c59af1c.zip
media-111584911714906d40678d158b9dc2947c59af1c.tar.gz
media-111584911714906d40678d158b9dc2947c59af1c.tar.bz2
adds drivers of the multimedia.
vh264 decoder checkout out pts out. Change-Id: I86861da91beeff56c6e7c614799f0726e9e4a542
Diffstat
-rw-r--r--.gitignore101
-rw-r--r--Media.mk64
-rw-r--r--drivers/Makefile3
-rw-r--r--drivers/common/Makefile2
-rw-r--r--drivers/common/chips/chips.c158
-rw-r--r--drivers/common/chips/chips.h39
-rw-r--r--drivers/common/firmware/Makefile1
-rw-r--r--drivers/common/firmware/firmware.c753
-rw-r--r--drivers/common/firmware/firmware.h118
-rw-r--r--drivers/common/firmware/firmware_info.h23
-rw-r--r--drivers/common/media_clock/Makefile5
-rw-r--r--drivers/common/media_clock/clk/clk.c389
-rw-r--r--drivers/common/media_clock/clk/clk.h128
-rw-r--r--drivers/common/media_clock/clk/clk_priv.h38
-rw-r--r--drivers/common/media_clock/clk/clkgx.c668
-rw-r--r--drivers/common/media_clock/switch/amports_gate.c195
-rw-r--r--drivers/common/media_clock/switch/amports_gate.h25
-rw-r--r--drivers/frame_provider/Makefile1
-rw-r--r--drivers/frame_provider/decoder/Makefile13
-rw-r--r--drivers/frame_provider/decoder/h264/Makefile1
-rw-r--r--drivers/frame_provider/decoder/h264/vh264.c2925
-rw-r--r--drivers/frame_provider/decoder/h264/vh264.h27
-rw-r--r--drivers/frame_provider/decoder/h265/Makefile1
-rw-r--r--drivers/frame_provider/decoder/h265/vh265.c8155
-rw-r--r--drivers/frame_provider/decoder/h265/vh265.h27
-rw-r--r--drivers/frame_provider/decoder/utils/Makefile2
-rw-r--r--drivers/frame_provider/decoder/utils/amvdec.c936
-rw-r--r--drivers/frame_provider/decoder/utils/amvdec.h82
-rw-r--r--drivers/frame_provider/decoder/utils/decoder_mmu_box.c373
-rw-r--r--drivers/frame_provider/decoder/utils/decoder_mmu_box.h38
-rw-r--r--drivers/frame_provider/decoder/utils/utils.c61
-rw-r--r--drivers/frame_provider/decoder/utils/vdec.c2688
-rw-r--r--drivers/frame_provider/decoder/utils/vdec.h273
-rw-r--r--drivers/frame_provider/decoder/utils/vdec_input.c542
-rw-r--r--drivers/frame_provider/decoder/utils/vdec_input.h131
-rw-r--r--drivers/stream_input/Makefile9
-rw-r--r--drivers/stream_input/amports/Makefile2
-rw-r--r--drivers/stream_input/amports/adec.c295
-rw-r--r--drivers/stream_input/amports/adec.h32
-rw-r--r--drivers/stream_input/amports/amports_priv.h56
-rw-r--r--drivers/stream_input/amports/amstream.c3648
-rw-r--r--drivers/stream_input/amports/amstream_profile.c54
-rw-r--r--drivers/stream_input/parser/Makefile6
-rw-r--r--drivers/stream_input/parser/esparser.c911
-rw-r--r--drivers/stream_input/parser/esparser.h149
-rw-r--r--drivers/stream_input/parser/psparser.c1158
-rw-r--r--drivers/stream_input/parser/psparser.h141
-rw-r--r--drivers/stream_input/parser/streambuf.c426
-rw-r--r--drivers/stream_input/parser/streambuf.h136
-rw-r--r--drivers/stream_input/parser/streambuf_reg.h111
-rw-r--r--drivers/stream_input/parser/thread_rw.c505
-rw-r--r--drivers/stream_input/parser/thread_rw.h42
-rw-r--r--drivers/stream_input/parser/tsdemux.c1129
-rw-r--r--drivers/stream_input/parser/tsdemux.h93
54 files changed, 27889 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..a9a7f44
--- a/dev/null
+++ b/.gitignore
@@ -0,0 +1,101 @@
+#
+# NOTE! Don't add files that are generated in specific
+# subdirectories here. Add them in the ".gitignore" file
+# in that subdirectory instead.
+#
+# NOTE! Please use 'git ls-files -i --exclude-standard'
+# command after changing this file, to see if there are
+# any tracked files which get ignored after the change.
+#
+# Normal rules
+#
+.*
+*.o
+*.o.*
+*.a
+*.s
+*.ko
+*.so
+*.so.dbg
+*.mod.c
+*.i
+*.lst
+*.symtypes
+*.order
+modules.builtin
+*.elf
+*.bin
+*.gz
+*.bz2
+*.lzma
+*.xz
+*.lz4
+*.lzo
+*.patch
+*.gcno
+
+#
+# Top-level generic files
+#
+/tags
+/TAGS
+/linux
+/vmlinux
+/vmlinuz
+/System.map
+/Module.markers
+Module.symvers
+
+#
+# Debian directory (make deb-pkg)
+#
+/debian/
+
+#
+# git files that we don't want to ignore even it they are dot-files
+#
+!.gitignore
+!.mailmap
+
+#
+# Generated include files
+#
+include/config
+include/generated
+arch/*/include/generated
+
+# stgit generated dirs
+patches-*
+
+# quilt's files
+patches
+series
+
+# cscope files
+cscope.*
+ncscope.*
+
+# gnu global files
+GPATH
+GRTAGS
+GSYMS
+GTAGS
+
+*.orig
+*~
+\#*#
+
+#
+# Leavings from module signing
+#
+extra_certificates
+signing_key.priv
+signing_key.x509
+x509.genkey
+
+# Kconfig presets
+all.config
+
+# customer folder
+customer
+
diff --git a/Media.mk b/Media.mk
new file mode 100644
index 0000000..048c31a
--- a/dev/null
+++ b/Media.mk
@@ -0,0 +1,64 @@
+
+ARCH ?= arm64
+TOOLS := aarch64-linux-gnu-
+CONFIGS := CONFIG_AMLOGIC_MEDIA_VDEC_H264=m \
+ CONFIG_AMLOGIC_MEDIA_VDEC_H265=m
+
+define copy-media-modules
+$(foreach m, $(shell find $(strip $(1)) -name "*.ko"),\
+ $(shell cp $(m) $(strip $(2)) -rfa))
+endef
+
+ifneq (,$(ANDROID_BUILD_TOP))
+KDIR := $(OUT)/obj/KERNEL_OBJ/
+
+MEDIA_DRIVERS := $(ANDROID_BUILD_TOP)/hardware/amlogic/media/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
+
+define media-modules
+ $(MAKE) -C $(KDIR) M=$(MEDIA_DRIVERS) ARCH=$(ARCH) \
+ CROSS_COMPILE=$(TOOLS) $(CONFIGS) \
+ EXTRA_CFLAGS+=-I$(INCLUDE)
+endef
+
+else
+KDIR := $(PWD)/kernel
+ifeq (,$(wildcard $(KDIR)))
+$(error No find the dir of kernel.)
+endif
+
+MEDIA_DRIVERS := $(PWD)/media/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
+TOOLS := /opt/gcc-linaro-5.3-2016.02-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu-
+
+modules:
+ $(MAKE) -C $(KDIR) M=$(MEDIA_DRIVERS) ARCH=$(ARCH) \
+ CROSS_COMPILE=$(TOOLS) $(CONFIGS) \
+ EXTRA_CFLAGS+=-I$(INCLUDE)
+
+copy-modules:
+ @echo "start copying media modules."
+ $(call copy-media-modules, $(MEDIA_DRIVERS), $(MODS_OUT))
+
+all: modules copy-modules
+
+clean:
+ $(MAKE) -C $(KDIR) M=$(MEDIA_DRIVERS) ARCH=$(ARCH) clean
+
+endif
diff --git a/drivers/Makefile b/drivers/Makefile
new file mode 100644
index 0000000..5d0d782
--- a/dev/null
+++ b/drivers/Makefile
@@ -0,0 +1,3 @@
+obj-y += common/
+obj-y += frame_provider/
+obj-y += stream_input/
diff --git a/drivers/common/Makefile b/drivers/common/Makefile
new file mode 100644
index 0000000..77ce080
--- a/dev/null
+++ b/drivers/common/Makefile
@@ -0,0 +1,2 @@
+obj-y += media_clock/
+obj-y += firmware/
diff --git a/drivers/common/chips/chips.c b/drivers/common/chips/chips.c
new file mode 100644
index 0000000..f2e7fa6
--- a/dev/null
+++ b/drivers/common/chips/chips.c
@@ -0,0 +1,158 @@
+/*
+ * drivers/amlogic/media/common/arch/chips/chips.c
+ *
+ * Copyright (C) 2016 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.
+ *
+*/
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/vmalloc.h>
+#include <linux/mm.h>
+
+#include <linux/amlogic/media/utils/vformat.h>
+#include <linux/amlogic/media/old_cpu_version.h>
+#include "../../stream_input/amports/amports_priv.h"
+#include "../../frame_provider/decoder/utils/vdec.h"
+#include "chips.h"
+#include <linux/amlogic/media/utils/log.h>
+
+#define VIDEO_FIRMWARE_FATHER_NAME "video"
+
+/*
+*#define MESON_CPU_MAJOR_ID_M6 0x16
+*#define MESON_CPU_MAJOR_ID_M6TV 0x17
+*#define MESON_CPU_MAJOR_ID_M6TVL 0x18
+*#define MESON_CPU_MAJOR_ID_M8 0x19
+*#define MESON_CPU_MAJOR_ID_MTVD 0x1A
+*#define MESON_CPU_MAJOR_ID_M8B 0x1B
+*#define MESON_CPU_MAJOR_ID_MG9TV 0x1C
+*#define MESON_CPU_MAJOR_ID_M8M2 0x1D
+*#define MESON_CPU_MAJOR_ID_GXBB 0x1F
+*#define MESON_CPU_MAJOR_ID_GXTVBB 0x20
+*#define MESON_CPU_MAJOR_ID_GXL 0x21
+*#define MESON_CPU_MAJOR_ID_GXM 0x22
+*#define MESON_CPU_MAJOR_ID_TXL 0x23
+*/
+struct type_name {
+
+ int type;
+
+ const char *name;
+};
+static const struct type_name cpu_type_name[] = {
+ {MESON_CPU_MAJOR_ID_M6, "m6"},
+ {MESON_CPU_MAJOR_ID_M6TV, "m6tv"},
+ {MESON_CPU_MAJOR_ID_M6TVL, "m6tvl"},
+ {MESON_CPU_MAJOR_ID_M8, "m8"},
+ {MESON_CPU_MAJOR_ID_MTVD, "mtvd"},
+ {MESON_CPU_MAJOR_ID_M8B, "m8b"},
+ {MESON_CPU_MAJOR_ID_MG9TV, "mg9tv"},
+ {MESON_CPU_MAJOR_ID_M8M2, "m8"},
+ {MESON_CPU_MAJOR_ID_GXBB, "gxbb"},
+ {MESON_CPU_MAJOR_ID_GXTVBB, "gxtvbb"},
+ {MESON_CPU_MAJOR_ID_GXL, "gxl"},
+ {MESON_CPU_MAJOR_ID_GXM, "gxm"},
+ {MESON_CPU_MAJOR_ID_TXL, "txl"},
+ {0, NULL},
+};
+
+static const char *get_type_name(const struct type_name *typename, int size,
+ int type)
+{
+
+ const char *name = "unknown";
+
+ int i;
+
+ for (i = 0; i < size; i++) {
+
+ if (type == typename[i].type)
+
+ name = typename[i].name;
+
+ }
+
+ return name;
+}
+
+const char *get_cpu_type_name(void)
+{
+
+ return get_type_name(cpu_type_name,
+ sizeof(cpu_type_name) / sizeof(struct type_name),
+ get_cpu_type());
+}
+EXPORT_SYMBOL(get_cpu_type_name);
+
+/*
+*enum vformat_e {
+* VFORMAT_MPEG12 = 0,
+* VFORMAT_MPEG4,
+* VFORMAT_H264,
+* VFORMAT_MJPEG,
+* VFORMAT_REAL,
+* VFORMAT_JPEG,
+* VFORMAT_VC1,
+* VFORMAT_AVS,
+* VFORMAT_YUV,
+* VFORMAT_H264MVC,
+* VFORMAT_H264_4K2K,
+* VFORMAT_HEVC,
+* VFORMAT_H264_ENC,
+* VFORMAT_JPEG_ENC,
+* VFORMAT_VP9,
+* VFORMAT_MAX
+*};
+*/
+static const struct type_name vformat_type_name[] = {
+ {VFORMAT_MPEG12, "mpeg12"},
+ {VFORMAT_MPEG4, "mpeg4"},
+ {VFORMAT_H264, "h264"},
+ {VFORMAT_MJPEG, "mjpeg"},
+ {VFORMAT_REAL, "real"},
+ {VFORMAT_JPEG, "jpeg"},
+ {VFORMAT_VC1, "vc1"},
+ {VFORMAT_AVS, "avs"},
+ {VFORMAT_YUV, "yuv"},
+ {VFORMAT_H264MVC, "h264mvc"},
+ {VFORMAT_H264_4K2K, "h264_4k"},
+ {VFORMAT_HEVC, "hevc"},
+ {VFORMAT_H264_ENC, "h264_enc"},
+ {VFORMAT_JPEG_ENC, "jpeg_enc"},
+ {VFORMAT_VP9, "vp9"},
+ {VFORMAT_YUV, "yuv"},
+ {0, NULL},
+};
+
+const char *get_video_format_name(enum vformat_e type)
+{
+
+ return get_type_name(vformat_type_name,
+ sizeof(vformat_type_name) / sizeof(struct type_name), type);
+}
+EXPORT_SYMBOL(get_video_format_name);
+
+static struct chip_vdec_info_s current_chip_info;
+
+struct chip_vdec_info_s *get_current_vdec_chip(void)
+{
+
+ return &current_chip_info;
+}
+EXPORT_SYMBOL(get_current_vdec_chip);
+
diff --git a/drivers/common/chips/chips.h b/drivers/common/chips/chips.h
new file mode 100644
index 0000000..7a9faba
--- a/dev/null
+++ b/drivers/common/chips/chips.h
@@ -0,0 +1,39 @@
+/*
+ * drivers/amlogic/media/common/arch/chips/chips.h
+ *
+ * Copyright (C) 2016 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 UCODE_MANAGER_HEADER
+#define UCODE_MANAGER_HEADER
+#include "../firmware/firmware.h"
+#include "../media_clock/clk/clk_priv.h"
+
+struct chip_vdec_info_s {
+
+ int cpu_type;
+
+ struct video_firmware_s *firmware;
+
+ struct chip_vdec_clk_s *clk_mgr[VDEC_MAX];
+
+ struct clk_set_setting *clk_setting_array;
+};
+
+const char *get_cpu_type_name(void);
+const char *get_video_format_name(enum vformat_e type);
+
+struct chip_vdec_info_s *get_current_vdec_chip(void);
+
+#endif
diff --git a/drivers/common/firmware/Makefile b/drivers/common/firmware/Makefile
new file mode 100644
index 0000000..d2957a8
--- a/dev/null
+++ b/drivers/common/firmware/Makefile
@@ -0,0 +1 @@
+obj-m += firmware.o
diff --git a/drivers/common/firmware/firmware.c b/drivers/common/firmware/firmware.c
new file mode 100644
index 0000000..fc31c22
--- a/dev/null
+++ b/drivers/common/firmware/firmware.c
@@ -0,0 +1,753 @@
+/*
+ * drivers/amlogic/media/common/firmware/firmware.c
+ *
+ * Copyright (C) 2016 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.
+ *
+*/
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/vmalloc.h>
+#include <linux/mm.h>
+#include <linux/vmalloc.h>
+#include <linux/slab.h>
+
+#include <linux/amlogic/media/utils/vformat.h>
+#include <linux/amlogic/media/old_cpu_version.h>
+#include "../../stream_input/amports/amports_priv.h"
+#include "../../frame_provider/decoder/utils/vdec.h"
+#include "firmware.h"
+#include "../chips/chips.h"
+#include "linux/string.h"
+#include <linux/amlogic/media/utils/log.h>
+#include <linux/firmware.h>
+#include <linux/amlogic/major.h>
+#include <linux/cdev.h>
+#include <linux/crc32.h>
+
+#define CLASS_NAME "firmware_codec"
+#define DEV_NAME "firmware_vdec"
+#define DIR "video"
+#define FRIMWARE_SIZE (30*1024) /*30k*/
+#define PACK_SIZE (512*1024)
+#define BUFF_SIZE (512*1024)
+
+#define PACK ('P' << 24 | 'A' << 16 | 'C' << 8 | 'K')
+#define CODE ('C' << 24 | 'O' << 16 | 'D' << 8 | 'E')
+
+static DEFINE_MUTEX(mutex);
+
+struct firmware_mgr_s {
+ struct list_head head;
+ spinlock_t lock;
+};
+
+struct firmware_info_s {
+ struct list_head node;
+ char path[64];
+ char firmware_name[32];
+ struct firmware_s *data;
+};
+
+struct ucode_info_s {
+ int cpu_version;
+ const char *name;
+};
+
+struct firmware_header_s {
+ int magic;
+ int checksum;
+ char version[32];
+ char author[32];
+ char date[32];
+ char commit[16];
+ int data_size;
+ unsigned int time;
+ char reserved[32];
+};
+
+struct firmware_s {
+ union {
+ struct firmware_header_s header;
+ char buf[256];
+ };
+ char data[0];
+};
+
+struct package_header_s {
+ int magic;
+ int size;
+ int checksum;
+ char reserved[128];
+};
+
+struct package_s {
+ union {
+ struct package_header_s header;
+ char buf[256];
+ };
+ char data[0];
+};
+
+struct info_header_s {
+ char name[32];
+ char format[32];
+ char cpu[32];
+ int length;
+};
+
+struct package_info_s {
+ union {
+ struct info_header_s header;
+ char buf[256];
+ };
+ char data[0];
+};
+
+static struct ucode_info_s ucode_info[] = {
+#include "firmware_info.h"
+};
+
+struct firmware_dev_s {
+ struct cdev cdev;
+ struct device *dev;
+ dev_t dev_no;
+};
+
+static const struct file_operations firmware_fops = {
+ .owner = THIS_MODULE
+};
+
+struct firmware_mgr_s *g_mgr;
+struct firmware_dev_s *g_dev;
+
+static u32 debug = 0;
+
+static int request_firmware_from_sys(const char *file_name,
+ char *buf, int size)
+{
+ int ret = -1;
+ const struct firmware *firmware;
+
+ pr_info("Try load %s ...\n", file_name);
+
+ ret = request_firmware(&firmware, file_name, g_dev->dev);
+ if (ret < 0) {
+ pr_info("Error : %d can't load the %s.\n", ret, file_name);
+ goto err;
+ }
+
+ if (firmware->size > size) {
+ pr_info("Not enough memory size for ucode.\n");
+ ret = -ENOMEM;
+ goto release;
+ }
+
+ memcpy(buf, (char *)firmware->data, firmware->size);
+
+ pr_info("Load mcode size : %zd, Name : %s.\n",
+ firmware->size, file_name);
+ ret = firmware->size;
+release:
+ release_firmware(firmware);
+err:
+ return ret;
+}
+
+int request_decoder_firmware_on_sys(enum vformat_e type,
+ const char *file_name, char *buf, int size)
+{
+ int ret;
+
+ ret = get_firmware_data(file_name, buf);
+ if (ret < 0)
+ pr_info("Get firmware fail.\n");
+
+ if (ret > size) {
+ pr_info("Not enough memory.\n");
+ return -ENOMEM;
+ }
+
+ return ret;
+}
+int get_decoder_firmware_data(enum vformat_e type,
+ const char *file_name, char *buf, int size)
+{
+ int ret;
+
+ ret = request_decoder_firmware_on_sys(type, file_name, buf, size);
+ if (ret < 0)
+ pr_info("get_decoder_firmware_data %s for format %d failed!\n",
+ file_name, type);
+
+ return ret;
+}
+EXPORT_SYMBOL(get_decoder_firmware_data);
+
+static unsigned long firmware_mgr_lock(struct firmware_mgr_s *mgr)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&mgr->lock, flags);
+ return flags;
+}
+
+static void firmware_mgr_unlock(struct firmware_mgr_s *mgr, unsigned long flags)
+{
+ spin_unlock_irqrestore(&mgr->lock, flags);
+}
+
+static void add_info(struct firmware_info_s *info)
+{
+ unsigned long flags;
+ struct firmware_mgr_s *mgr = g_mgr;
+
+ flags = firmware_mgr_lock(mgr);
+ list_add(&info->node, &mgr->head);
+ firmware_mgr_unlock(mgr, flags);
+}
+
+static void del_info(struct firmware_info_s *info)
+{
+ unsigned long flags;
+ struct firmware_mgr_s *mgr = g_mgr;
+
+ flags = firmware_mgr_lock(mgr);
+ list_del(&info->node);
+ kfree(info);
+ firmware_mgr_unlock(mgr, flags);
+}
+
+static void walk_firmware_info(void)
+{
+ struct firmware_mgr_s *mgr = g_mgr;
+ struct firmware_info_s *info;
+
+ if (list_empty(&mgr->head)) {
+ pr_info("the info list is empty.\n");
+ return ;
+ }
+
+ list_for_each_entry(info, &mgr->head, node) {
+ if (IS_ERR_OR_NULL(info->data))
+ continue;
+
+ pr_info("path : %s.\n", info->path);
+ pr_info("name : %s.\n", info->firmware_name);
+ pr_info("version : %s.\n", info->data->header.version);
+ pr_info("checksum : 0x%x.\n", info->data->header.checksum);
+ pr_info("data size : %d.\n", info->data->header.data_size);
+ pr_info("author : %s.\n", info->data->header.author);
+ pr_info("date : %s.\n", info->data->header.date);
+ pr_info("commit : %s.\n\n", info->data->header.commit);
+ }
+}
+
+static ssize_t info_show(struct class *class,
+ struct class_attribute *attr, char *buf)
+{
+ char *pbuf = buf;
+ struct firmware_mgr_s *mgr = g_mgr;
+ struct firmware_info_s *info;
+
+ if (list_empty(&mgr->head)) {
+ pbuf += sprintf(pbuf, "No firmware.\n");
+ goto out;
+ }
+
+ list_for_each_entry(info, &mgr->head, node) {
+ if (IS_ERR_OR_NULL(info->data))
+ continue;
+
+ pr_info( "%10s : %s\n", "name",
+ info->firmware_name);
+ pr_info( "%10s : %d\n", "size",
+ info->data->header.data_size);
+ pr_info( "%10s : %s\n", "version",
+ info->data->header.version);
+ pr_info( "%10s : 0x%x\n", "checksum",
+ info->data->header.checksum);
+ pr_info( "%10s : %s\n", "commit",
+ info->data->header.commit);
+ pr_info( "%10s : %s\n", "author",
+ info->data->header.author);
+ pr_info( "%10s : %s\n\n", "date",
+ info->data->header.date);
+ }
+out:
+ return pbuf - buf;
+}
+
+static int set_firmware_info(void)
+{
+ int ret = 0, i, len;
+ struct firmware_info_s *info;
+ int info_size = ARRAY_SIZE(ucode_info);
+ int cpu_version = get_cpu_type();
+ const char *name;
+ char *path = __getname();
+
+ if (IS_ERR_OR_NULL(path))
+ return -ENOMEM;
+
+ for (i = 0; i < info_size; i++) {
+ if (cpu_version != ucode_info[i].cpu_version)
+ continue;
+
+ name = ucode_info[i].name;
+ if (IS_ERR_OR_NULL(name))
+ break;
+
+ len = snprintf(path, PATH_MAX, "%s/%s", DIR,
+ ucode_info[i].name);
+ if (len >= PATH_MAX)
+ continue;
+
+ info = kzalloc(sizeof(struct firmware_info_s), GFP_KERNEL);
+ if (IS_ERR_OR_NULL(info)) {
+ __putname(path);
+ return -ENOMEM;
+ }
+
+ strcpy(info->path, path);
+ strcpy(info->firmware_name, name);
+ info->data = NULL;
+
+ add_info(info);
+ }
+
+ __putname(path);
+
+ return ret;
+}
+
+static int firmware_probe(char *buf)
+{
+ int magic = 0;
+
+ memcpy(&magic, buf, sizeof(int));
+ return magic;
+}
+
+static int checksum(struct firmware_s *firmware)
+{
+ unsigned int crc;
+
+ crc = crc32_le(~0U, firmware->data, firmware->header.data_size);
+
+ if (debug)
+ pr_info("firmware crc result : 0x%x\n", crc ^ ~0U);
+
+ return firmware->header.checksum != (crc ^ ~0U) ? 0 : 1;
+}
+
+static int check_repeat(struct firmware_s *data, const char *name)
+{
+ struct firmware_mgr_s *mgr = g_mgr;
+ struct firmware_info_s *info;
+
+ if (list_empty(&mgr->head)) {
+ pr_info("the info list is empty.\n");
+ return 0;
+ }
+
+ list_for_each_entry(info, &mgr->head, node) {
+ struct firmware_s *tmp;
+
+ if (strcmp(info->firmware_name, name))
+ continue;
+
+ if (IS_ERR_OR_NULL(info->data)) {
+ pr_info("the data is null.\n");
+ info->data = data;
+
+ return 1;
+ }
+
+ if (info->data->header.time >= data->header.time) {
+ pr_info("the data is old.\n");
+ kfree(data);
+
+ return 1;
+ }
+
+ pr_info("the data is new.\n");
+ tmp = info->data;
+ info->data = data;
+ kfree(tmp);
+
+ return 1;
+ }
+
+ return 0;
+}
+
+static int firmware_parse_package(char *buf, int size)
+{
+ int ret = 0;
+ struct package_s *pack;
+ struct package_info_s *pack_info;
+ struct firmware_info_s *info;
+ struct firmware_s *data;
+ char *pack_data;
+ const char *cpu;
+ int info_len, len;
+ char *path = __getname();
+
+ if (IS_ERR_OR_NULL(path))
+ return -ENOMEM;
+
+ pack = vmalloc(PACK_SIZE);
+ if (IS_ERR_OR_NULL(pack)) {
+ __putname(path);
+ return -ENOMEM;
+ }
+
+ memset(pack, 0, PACK_SIZE);
+ memcpy(pack, buf, size);
+
+ pack_data = pack->data;
+ pack_info = (struct package_info_s *)pack_data;
+ info_len = sizeof(struct package_info_s);
+
+ for (;;) {
+ if (!pack_info->header.length)
+ break;
+
+ cpu = get_cpu_type_name();
+ if (strcmp(cpu, pack_info->header.cpu))
+ continue;
+
+ len = snprintf(path, PATH_MAX, "%s/%s", DIR,
+ pack_info->header.name);
+ if (len >= PATH_MAX)
+ continue;
+
+ info = kzalloc(sizeof(struct firmware_info_s), GFP_KERNEL);
+ if (IS_ERR_OR_NULL(info)) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ data = kzalloc(FRIMWARE_SIZE, GFP_KERNEL);
+ if (IS_ERR_OR_NULL(data)) {
+ kfree(info);
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ strcpy(info->path, path);
+ strcpy(info->firmware_name, pack_info->header.name);
+
+ len = pack_info->header.length;
+ memcpy(data, pack_info->data, len);
+
+ pack_data += (pack_info->header.length + info_len);
+ pack_info = (struct package_info_s *)pack_data;
+
+ if (!checksum(data)) {
+ pr_info("check sum fail !\n");
+ kfree(data);
+ kfree(info);
+ goto out;
+ }
+
+ if (check_repeat(data, info->firmware_name)) {
+ kfree(info);
+ continue;
+ }
+
+ info->data = data;
+ add_info(info);
+ }
+out:
+ __putname(path);
+ vfree(pack);
+
+ return ret;
+}
+
+static int firmware_parse_code(struct firmware_info_s *info,
+ char *buf, int size)
+{
+ info->data = kzalloc(FRIMWARE_SIZE, GFP_KERNEL);
+ if (IS_ERR_OR_NULL(info->data))
+ return -ENOMEM;
+
+ memcpy(info->data, buf, size);
+
+ if (!checksum(info->data)) {
+ pr_info("check sum fail !\n");
+ kfree(info->data);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int get_firmware_from_sys(const char *path,
+ char *buf, int size)
+{
+ int len = 0;
+
+ len = request_firmware_from_sys(path, buf, size);
+ if (len < 0)
+ pr_info("get data from fsys fail.\n");
+
+ return len;
+}
+
+static int set_firmware_data(void)
+{
+ int magic = 0;
+ struct firmware_mgr_s *mgr = g_mgr;
+ struct firmware_info_s *info, *temp;
+ char *buf;
+ int size;
+
+ if (list_empty(&mgr->head)) {
+ pr_info("the info list is empty.\n");
+ return 0;
+ }
+
+ buf = vmalloc(BUFF_SIZE);
+ if (IS_ERR_OR_NULL(buf))
+ return -ENOMEM;
+
+ list_for_each_entry_safe(info, temp, &mgr->head, node) {
+ size = get_firmware_from_sys(info->path, buf, BUFF_SIZE);
+ magic = firmware_probe(buf);
+
+ switch (magic) {
+ case PACK:
+ firmware_parse_package(buf, size);
+ del_info(info);
+ break;
+
+ case CODE:
+ firmware_parse_code(info, buf, size);
+ break;
+
+ default:
+ del_info(info);
+ pr_info("invaild type.\n");
+ }
+
+ memset(buf, 0, BUFF_SIZE);
+ }
+
+ if (debug)
+ walk_firmware_info();
+
+ vfree(buf);
+
+ return 0;
+}
+
+int get_firmware_data(const char *name, char *buf)
+{
+ int data_len, ret = -1;
+ struct firmware_mgr_s *mgr = g_mgr;
+ struct firmware_info_s *info;
+ char *firmware_name = __getname();
+
+ if (IS_ERR_OR_NULL(firmware_name))
+ return -ENOMEM;
+
+ strcat(firmware_name, name);
+ strcat(firmware_name, ".bin");
+
+ if (list_empty(&mgr->head)) {
+ pr_info("the info list is empty.\n");
+ return 0;
+ }
+
+ list_for_each_entry(info, &mgr->head, node) {
+ if (strcmp(firmware_name, info->firmware_name))
+ continue;
+
+ data_len = info->data->header.data_size;
+ memcpy(buf, info->data->data, data_len);
+ ret = data_len;
+
+ break;
+ }
+
+ __putname(firmware_name);
+
+ return ret;
+}
+EXPORT_SYMBOL(get_firmware_data);
+
+static int firmware_pre_load(void)
+{
+ int ret = -1;
+
+ ret = set_firmware_info();
+ if (ret < 0) {
+ pr_info("Get path fail.\n");
+ goto err;
+ }
+
+ ret = set_firmware_data();
+ if (ret < 0) {
+ pr_info("Set data fail.\n");
+ goto err;
+ }
+err:
+ return ret;
+}
+
+static int firmware_mgr_init(void)
+{
+ g_mgr = kzalloc(sizeof(struct firmware_mgr_s), GFP_KERNEL);
+ if (IS_ERR_OR_NULL(g_mgr))
+ return -ENOMEM;
+
+ INIT_LIST_HEAD(&g_mgr->head);
+ spin_lock_init(&g_mgr->lock);
+
+ return 0;
+}
+
+static struct class_attribute firmware_class_attrs[] = {
+ __ATTR_RO(info),
+ __ATTR_NULL
+};
+
+static struct class firmware_class = {
+ .name = CLASS_NAME,
+ .class_attrs = firmware_class_attrs,
+};
+
+static int firmware_driver_init(void)
+{
+ int ret = -1;
+
+ g_dev = kzalloc(sizeof(struct firmware_dev_s), GFP_KERNEL);
+ if (IS_ERR_OR_NULL(g_dev))
+ return -ENOMEM;
+
+ g_dev->dev_no = MKDEV(AMSTREAM_MAJOR, 100);
+
+ ret = register_chrdev_region(g_dev->dev_no, 1, DEV_NAME);
+ if (ret < 0) {
+ pr_info("Can't get major number %d.\n", AMSTREAM_MAJOR);
+ goto err;
+ }
+
+ cdev_init(&g_dev->cdev, &firmware_fops);
+ g_dev->cdev.owner = THIS_MODULE;
+
+ ret = cdev_add(&g_dev->cdev, g_dev->dev_no, 1);
+ if (ret) {
+ pr_info("Error %d adding cdev fail.\n", ret);
+ goto err;
+ }
+
+ ret = class_register(&firmware_class);
+ if (ret < 0) {
+ pr_info("Failed in creating class.\n");
+ goto err;
+ }
+
+ g_dev->dev = device_create(&firmware_class, NULL,
+ g_dev->dev_no, NULL, DEV_NAME);
+ if (IS_ERR_OR_NULL(g_dev->dev)) {
+ pr_info("Create device failed.\n");
+ ret = -ENODEV;
+ goto err;
+ }
+
+ pr_info("Registered firmware driver success.\n");
+err:
+ return ret;
+}
+
+static void firmware_info_clean(void)
+{
+ struct firmware_mgr_s *mgr = g_mgr;
+ struct firmware_info_s *info;
+ unsigned long flags;
+
+ flags = firmware_mgr_lock(mgr);
+ while (!list_empty(&mgr->head)) {
+ info = list_entry(mgr->head.next,
+ struct firmware_info_s, node);
+ list_del(&info->node);
+ kfree(info->data);
+ kfree(info);
+ }
+ firmware_mgr_unlock(mgr, flags);
+}
+
+static void firmware_mgr_clean(void)
+{
+ struct firmware_mgr_s *mgr = g_mgr;
+
+ kfree(mgr);
+}
+
+static void firmware_driver_exit(void)
+{
+ cdev_del(&g_dev->cdev);
+ device_destroy(&firmware_class, g_dev->dev_no);
+ class_unregister(&firmware_class);
+ unregister_chrdev_region(g_dev->dev_no, 1);
+ kfree(g_dev);
+
+ pr_info("Firmware driver cleaned up.\n");
+}
+
+static int __init firmware_module_init(void)
+{
+ int ret = -1;
+
+ ret = firmware_driver_init();
+ if (ret) {
+ pr_info("Error %d firmware driver init fail.\n", ret);
+ goto err;
+ }
+
+ ret = firmware_mgr_init();
+ if (ret) {
+ pr_info("Error %d firmware mgr init fail.\n", ret);
+ goto err;
+ }
+
+ ret = firmware_pre_load();
+ if (ret) {
+ pr_info("Error %d firmware pre load fail.\n", ret);
+ goto err;
+ }
+err:
+ return ret;
+}
+
+static void __exit firmware_module_exit(void)
+{
+ firmware_info_clean();
+ firmware_mgr_clean();
+ firmware_driver_exit();
+}
+
+module_param(debug, uint, 0664);
+
+module_init(firmware_module_init);
+module_exit(firmware_module_exit);
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Nanxin Qin <nanxin.qin@amlogic.com>");
diff --git a/drivers/common/firmware/firmware.h b/drivers/common/firmware/firmware.h
new file mode 100644
index 0000000..d7d34ea
--- a/dev/null
+++ b/drivers/common/firmware/firmware.h
@@ -0,0 +1,118 @@
+/*
+ * drivers/amlogic/media/common/firmware/firmware.h
+ *
+ * Copyright (C) 2016 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 __VIDEO_FIRMWARE_HEADER_
+#define __VIDEO_FIRMWARE_HEADER_
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/amlogic/media/old_cpu_version.h>
+#include <linux/amlogic/media/utils/vformat.h>
+
+struct video_firmware_s {
+
+ int cpu_type;
+
+ enum vformat_e type;
+
+ const char *name;
+
+ int size;
+
+ int ref_cnt;
+
+ const char *version;
+
+ struct video_firmware_s *next;
+
+ const char ucode[1]; /*malloced more for ucode. */
+};
+int register_video_firamre_per_cpu(int cputype, enum vformat_e type,
+ const char *name, const char *code, int size, const char *ver);
+int register_video_firamre(int cpus[], enum vformat_e type, const char *name,
+ const char *code, int size, const char *ver);
+int get_decoder_firmware_data(enum vformat_e type, const char *file_name,
+ char *buf, int size);
+
+int show_all_buildin_firmwares(void);
+int get_firmware_data(const char *name, char *buf);
+
+
+#define F_VERSION "0.0.0.0"
+
+#define REGISTER_FIRMARE_PER_CPU_VER(cpu, type, name, ver)\
+ register_video_firamre_per_cpu(cpu, type,\
+ #name, (const char *)name, sizeof(name), ver)
+
+#define REGISTER_FIRMARE_PER_CPU(cpu, type, name)\
+ REGISTER_FIRMARE_PER_CPU_VER(cpu, type, name, F_VERSION)
+
+#define REGISTER_FIRMARE_IN(cpus, type, name, ver)\
+ register_video_firamre(cpus, type,\
+ #name, (const char *)name, sizeof(name), ver)
+
+#define DEF_FIRMWARE_FOR_CPUS_TYPE_VER(cpus, type, name, ver)\
+ do {\
+ int t_cpus[] = cpus;\
+ REGISTER_FIRMARE_IN(t_cpus, type, name, ver);\
+ } while (0)
+
+#define DEF_FIRMWARE_FOR_CPUS_TYPE(cpus, type, name)\
+ DEF_FIRMWARE_FOR_CPUS_TYPE_VER(cpus, type, name, F_VERSION)
+
+#define DEF_FIRMWARE_FOR_CPUS_VER(cpus, name, ver)\
+ DEF_FIRMWARE_FOR_CPUS_TYPE_VER(cpus, FOR_VFORMAT, name, ver)
+
+#define DEF_FIRMWARE_FOR_CPUS(cpus, name)\
+ DEF_FIRMWARE_FOR_CPUS_TYPE(cpus, FOR_VFORMAT, name)
+
+#define DEF_FIRMWARE_VER(name, ver)\
+ DEF_FIRMWARE_FOR_CPUS_TYPE_VER(FOR_CPUS, FOR_VFORMAT, name, ver)
+
+#define DEF_FIRMWARE(name)\
+ DEF_FIRMWARE_FOR_CPUS_TYPE_VER(FOR_CPUS, \
+ FOR_VFORMAT, name, F_VERSION)
+
+/*
+*#define INIT_DEF_FIRMWARE()\
+*static int __init init_ucode_per_cpu(void)\
+* {\
+* REG_FIRMWARE_ALL();\
+* return 0;\
+* } \
+*module_init(init_ucode_per_cpu);
+*/
+
+
+/*
+*sample:
+*
+*#define PER_CPUS {MESON_CPU_MAJOR_ID_M8,MESON_CPU_MAJOR_ID_M8M2,0}
+*#define FOR_VFORMAT VFORMAT_H264
+*
+*#define REG_FIRMWARE_ALL()\
+* DEF_FIRMWARE(vh264_mc);\
+* DEF_FIRMWARE(vh264_header_mc);\
+* DEF_FIRMWARE(vh264_data_mc);\
+* DEF_FIRMWARE(vh264_mmco_mc);\
+* DEF_FIRMWARE(vh264_list_mc);\
+* DEF_FIRMWARE(vh264_slice_mc);\
+* DEF_FIRMWARE_VER(vh264_slice_mc,"1.0");\
+*
+*INIT_DEF_FIRMWARE();
+*/
+#endif
diff --git a/drivers/common/firmware/firmware_info.h b/drivers/common/firmware/firmware_info.h
new file mode 100644
index 0000000..3d2cdd9
--- a/dev/null
+++ b/drivers/common/firmware/firmware_info.h
@@ -0,0 +1,23 @@
+/*
+ * drivers/amlogic/media/common/firmware/firmware_info.h
+ *
+ * Copyright (C) 2016 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.
+ *
+*/
+
+{MESON_CPU_MAJOR_ID_GXL, "gxl_vh265.bin"},
+
+{MESON_CPU_MAJOR_ID_GXL, "gxl_ucode.bin"},
+
+{MESON_CPU_MAJOR_ID_GXL, "gxl_h264_all.bin"},
+
diff --git a/drivers/common/media_clock/Makefile b/drivers/common/media_clock/Makefile
new file mode 100644
index 0000000..25c9a10
--- a/dev/null
+++ b/drivers/common/media_clock/Makefile
@@ -0,0 +1,5 @@
+obj-m += media_clock.o
+media_clock-objs += ../chips/chips.o
+media_clock-objs += clk/clkgx.o
+media_clock-objs += clk/clk.o
+media_clock-objs += switch/amports_gate.o
diff --git a/drivers/common/media_clock/clk/clk.c b/drivers/common/media_clock/clk/clk.c
new file mode 100644
index 0000000..a32c8d0
--- a/dev/null
+++ b/drivers/common/media_clock/clk/clk.c
@@ -0,0 +1,389 @@
+/*
+ * drivers/amlogic/media/common/arch/clk/clk.c
+ *
+ * Copyright (C) 2016 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.
+ *
+*/
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/vmalloc.h>
+#include <linux/mm.h>
+#include <linux/vmalloc.h>
+#include <linux/slab.h>
+
+#include <linux/amlogic/media/utils/vformat.h>
+#include <linux/amlogic/media/old_cpu_version.h>
+#include "../../../stream_input/amports/amports_priv.h"
+#include "../../../frame_provider/decoder/utils/vdec.h"
+#include "../../chips/chips.h"
+#include "clk_priv.h"
+#include <linux/amlogic/media/utils/log.h>
+
+#define p_vdec() (get_current_vdec_chip()->clk_mgr[VDEC_1])
+#define p_vdec2() (get_current_vdec_chip()->clk_mgr[VDEC_2])
+#define p_vdec_hcodec() (get_current_vdec_chip()->clk_mgr[VDEC_HCODEC])
+#define p_vdec_hevc() (get_current_vdec_chip()->clk_mgr[VDEC_HEVC])
+
+static int clock_source_wxhxfps_saved[VDEC_MAX + 1];
+
+#define IF_HAVE_RUN(p, fn)\
+ do {\
+ if (p && p->fn)\
+ p->fn();\
+ } while (0)
+/*
+*#define IF_HAVE_RUN_P1_RET(p, fn, p1)\
+* do {\
+* pr_debug("%s-----%d\n", __func__, clk);\
+* if (p && p->fn)\
+* return p->fn(p1);\
+* else\
+* return -1;\
+* } while (0)
+*
+*#define IF_HAVE_RUN_RET(p, fn)\
+* do {\
+* if (p && p->fn)\
+* return p->fn();\
+* else\
+* return 0;\
+* } while (0)
+*/
+
+int vdec_clock_init(void)
+{
+ if (p_vdec() && p_vdec()->clock_init)
+ return p_vdec()->clock_init();
+ else
+ return 0;
+}
+EXPORT_SYMBOL(vdec_clock_init);
+
+/*
+*clk ==0 :
+* to be release.
+* released shared clk,
+*clk ==1 :default low clk
+*clk ==2 :default high clk
+*/
+int vdec_clock_set(int clk)
+{
+ pr_debug("%s-----%d\n", __func__, clk);
+ if (p_vdec() && p_vdec()->clock_set)
+ return p_vdec()->clock_set(clk);
+ else
+ return -1;
+}
+EXPORT_SYMBOL(vdec_clock_set);
+
+void vdec_clock_enable(void)
+{
+ vdec_clock_set(1);
+}
+EXPORT_SYMBOL(vdec_clock_enable);
+
+void vdec_clock_hi_enable(void)
+{
+ vdec_clock_set(2);
+}
+EXPORT_SYMBOL(vdec_clock_hi_enable);
+
+void vdec_clock_on(void)
+{
+ IF_HAVE_RUN(p_vdec(), clock_on);
+}
+EXPORT_SYMBOL(vdec_clock_on);
+
+void vdec_clock_off(void)
+{
+ IF_HAVE_RUN(p_vdec(), clock_off);
+ clock_source_wxhxfps_saved[VDEC_1] = 0;
+}
+EXPORT_SYMBOL(vdec_clock_off);
+
+int vdec2_clock_set(int clk)
+{
+ pr_debug("%s-----%d\n", __func__, clk);
+ if (p_vdec2() && p_vdec2()->clock_set)
+ return p_vdec2()->clock_set(clk);
+ else
+ return -1;
+}
+EXPORT_SYMBOL(vdec2_clock_set);
+
+void vdec2_clock_enable(void)
+{
+ vdec2_clock_set(1);
+}
+EXPORT_SYMBOL(vdec2_clock_enable);
+
+void vdec2_clock_hi_enable(void)
+{
+ vdec2_clock_set(2);
+}
+EXPORT_SYMBOL(vdec2_clock_hi_enable);
+
+void vdec2_clock_on(void)
+{
+ IF_HAVE_RUN(p_vdec2(), clock_on);
+}
+EXPORT_SYMBOL(vdec2_clock_on);
+
+void vdec2_clock_off(void)
+{
+ IF_HAVE_RUN(p_vdec2(), clock_off);
+ clock_source_wxhxfps_saved[VDEC_2] = 0;
+}
+EXPORT_SYMBOL(vdec2_clock_off);
+
+int hcodec_clock_set(int clk)
+{
+ pr_debug("%s-----%d\n", __func__, clk);
+ if (p_vdec_hcodec() && p_vdec_hcodec()->clock_set)
+ return p_vdec_hcodec()->clock_set(clk);
+ else
+ return -1;
+}
+EXPORT_SYMBOL(hcodec_clock_set);
+
+void hcodec_clock_enable(void)
+{
+ hcodec_clock_set(1);
+}
+EXPORT_SYMBOL(hcodec_clock_enable);
+
+void hcodec_clock_hi_enable(void)
+{
+ hcodec_clock_set(2);
+}
+EXPORT_SYMBOL(hcodec_clock_hi_enable);
+
+void hcodec_clock_on(void)
+{
+ IF_HAVE_RUN(p_vdec_hcodec(), clock_on);
+}
+EXPORT_SYMBOL(hcodec_clock_on);
+
+void hcodec_clock_off(void)
+{
+ IF_HAVE_RUN(p_vdec_hcodec(), clock_off);
+ clock_source_wxhxfps_saved[VDEC_HCODEC] = 0;
+}
+EXPORT_SYMBOL(hcodec_clock_off);
+
+int hevc_clock_init(void)
+{
+ if (p_vdec_hevc() && p_vdec_hevc()->clock_init)
+ return p_vdec_hevc()->clock_init();
+ else
+ return 0;
+}
+EXPORT_SYMBOL(hevc_clock_init);
+
+int hevc_clock_set(int clk)
+{
+ pr_debug("%s-----%d\n", __func__, clk);
+ if (p_vdec_hevc() && p_vdec_hevc()->clock_set)
+ return p_vdec_hevc()->clock_set(clk);
+ else
+ return -1;
+}
+EXPORT_SYMBOL(hevc_clock_set);
+
+void hevc_clock_enable(void)
+{
+ hevc_clock_set(1);
+}
+EXPORT_SYMBOL(hevc_clock_enable);
+
+void hevc_clock_hi_enable(void)
+{
+ hevc_clock_set(2);
+}
+EXPORT_SYMBOL(hevc_clock_hi_enable);
+
+void hevc_clock_on(void)
+{
+ IF_HAVE_RUN(p_vdec_hevc(), clock_on);
+}
+EXPORT_SYMBOL(hevc_clock_on);
+
+void hevc_clock_off(void)
+{
+ IF_HAVE_RUN(p_vdec_hevc(), clock_off);
+ clock_source_wxhxfps_saved[VDEC_HEVC] = 0;
+}
+EXPORT_SYMBOL(hevc_clock_off);
+
+int vdec_source_get(enum vdec_type_e core)
+{
+ return clock_source_wxhxfps_saved[core];
+}
+EXPORT_SYMBOL(vdec_source_get);
+
+int vdec_clk_get(enum vdec_type_e core)
+{
+ return get_current_vdec_chip()->clk_mgr[core]->clock_get(core);
+}
+EXPORT_SYMBOL(vdec_clk_get);
+
+int get_clk_with_source(int format, int w_x_h_fps)
+{
+ struct clk_set_setting *p_setting;
+ int i;
+ int clk = -2;
+
+ p_setting = get_current_vdec_chip()->clk_setting_array;
+ if (!p_setting || format < 0 || format > VFORMAT_MAX) {
+ pr_info("error on get_clk_with_source ,%p,%d\n",
+ p_setting, format);
+ return -1; /*no setting found. */
+ }
+ p_setting = &p_setting[format];
+ for (i = 0; i < MAX_CLK_SET; i++) {
+ if (p_setting->set[i].wh_X_fps > w_x_h_fps) {
+ clk = p_setting->set[i].clk_Mhz;
+ break;
+ }
+ }
+ return clk;
+}
+EXPORT_SYMBOL(get_clk_with_source);
+
+int vdec_source_changed_for_clk_set(int format, int width, int height, int fps)
+{
+ int clk = get_clk_with_source(format, width * height * fps);
+ int ret_clk;
+
+ if (clk < 0) {
+ pr_info("can't get valid clk for source ,%d,%d,%d\n",
+ width, height, fps);
+ if (format >= 1920 && width >= 1080 && fps >= 30)
+ clk = 2; /*default high clk */
+ else
+ clk = 0; /*default clk. */
+ }
+ if (width * height * fps == 0)
+ clk = 0;
+ /*
+ *clk == 0
+ *is used for set default clk;
+ *if used supper clk.
+ *changed to default min clk.
+ */
+
+ if (format == VFORMAT_HEVC || format == VFORMAT_VP9) {
+ ret_clk = hevc_clock_set(clk);
+ clock_source_wxhxfps_saved[VDEC_HEVC] = width * height * fps;
+ } else if (format == VFORMAT_H264_ENC && format == VFORMAT_JPEG_ENC) {
+ ret_clk = hcodec_clock_set(clk);
+ clock_source_wxhxfps_saved[VDEC_HCODEC] = width * height * fps;
+ } else if (format == VFORMAT_H264_4K2K &&
+ get_cpu_type() == MESON_CPU_MAJOR_ID_M8) {
+ ret_clk = vdec2_clock_set(clk);
+ clock_source_wxhxfps_saved[VDEC_2] = width * height * fps;
+ ret_clk = vdec_clock_set(clk);
+ clock_source_wxhxfps_saved[VDEC_1] = width * height * fps;
+ } else {
+ ret_clk = vdec_clock_set(clk);
+ clock_source_wxhxfps_saved[VDEC_1] = width * height * fps;
+ }
+ return ret_clk;
+}
+EXPORT_SYMBOL(vdec_source_changed_for_clk_set);
+
+static int register_vdec_clk_mgr_per_cpu(int cputype,
+ enum vdec_type_e vdec_type, struct chip_vdec_clk_s *t_mgr)
+{
+
+ struct chip_vdec_clk_s *mgr;
+
+ if (cputype != get_cpu_type() || vdec_type >= VDEC_MAX) {
+ /*
+ *pr_info("ignore vdec clk mgr for vdec[%d] cpu=%d\n",
+ *vdec_type, cputype);
+ */
+ return 0; /* ignore don't needed firmare. */
+ }
+ mgr = kmalloc(sizeof(struct chip_vdec_clk_s), GFP_KERNEL);
+ if (!mgr)
+ return -ENOMEM;
+ *mgr = *t_mgr;
+ /*
+ *pr_info("register vdec clk mgr for vdec[%d]\n", vdec_type);
+ */
+ if (mgr->clock_init) {
+ if (mgr->clock_init()) {
+ kfree(mgr);
+ return -ENOMEM;
+ }
+ }
+ get_current_vdec_chip()->clk_mgr[vdec_type] = mgr;
+ return 0;
+}
+
+int register_vdec_clk_mgr(int cputype[], enum vdec_type_e vdec_type,
+ struct chip_vdec_clk_s *t_mgr)
+{
+ int i = 0;
+
+ while (cputype[i] > 0) {
+ register_vdec_clk_mgr_per_cpu(cputype[i], vdec_type, t_mgr);
+ i++;
+ }
+ return 0;
+}
+EXPORT_SYMBOL(register_vdec_clk_mgr);
+
+static int register_vdec_clk_setting_per_cpu(int cputype,
+ struct clk_set_setting *setting, int size)
+{
+
+ struct clk_set_setting *p_setting;
+
+ if (cputype != get_cpu_type()) {
+ /*
+ *pr_info("ignore clk_set_setting for cpu=%d\n",
+ *cputype);
+ */
+ return 0; /* ignore don't needed this setting . */
+ }
+ p_setting = kmalloc(size, GFP_KERNEL);
+ if (!p_setting)
+ return -ENOMEM;
+ memcpy(p_setting, setting, size);
+
+ pr_info("register clk_set_setting cpu[%d]\n", cputype);
+
+ get_current_vdec_chip()->clk_setting_array = p_setting;
+ return 0;
+}
+
+int register_vdec_clk_setting(int cputype[],
+ struct clk_set_setting *p_seting, int size)
+{
+ int i = 0;
+
+ while (cputype[i] > 0) {
+ register_vdec_clk_setting_per_cpu(cputype[i], p_seting, size);
+ i++;
+ }
+ return 0;
+}
+EXPORT_SYMBOL(register_vdec_clk_setting);
+
diff --git a/drivers/common/media_clock/clk/clk.h b/drivers/common/media_clock/clk/clk.h
new file mode 100644
index 0000000..64cdd73
--- a/dev/null
+++ b/drivers/common/media_clock/clk/clk.h
@@ -0,0 +1,128 @@
+/*
+ * drivers/amlogic/media/common/arch/clk/clk.h
+ *
+ * Copyright (C) 2016 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 VDEC_CHIP_CLK_HEADER
+#define VDEC_CHIP_CLK_HEADER
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include "clk_priv.h"
+#include <linux/amlogic/media/clk/gp_pll.h>
+
+#ifndef INCLUDE_FROM_ARCH_CLK_MGR
+int vdec_clock_init(void);
+int vdec_clock_set(int clk);
+int vdec2_clock_set(int clk);
+
+int hcodec_clock_set(int clk);
+int hevc_clock_init(void);
+int hevc_clock_set(int clk);
+
+void vdec_clock_on(void);
+void vdec_clock_off(void);
+void vdec2_clock_on(void);
+
+void vdec2_clock_off(void);
+void hcodec_clock_on(void);
+void hcodec_clock_off(void);
+void hevc_clock_on(void);
+void hevc_clock_off(void);
+
+int vdec_source_get(enum vdec_type_e core);
+int vdec_clk_get(enum vdec_type_e core);
+
+int vdec_source_changed_for_clk_set(int format, int width, int height, int fps);
+int get_clk_with_source(int format, int w_x_h_fps);
+
+void vdec_clock_enable(void);
+void vdec_clock_hi_enable(void);
+void hcodec_clock_enable(void);
+void hcodec_clock_hi_enable(void);
+void hevc_clock_enable(void);
+void hevc_clock_hi_enable(void);
+void vdec2_clock_enable(void);
+void vdec2_clock_hi_enable(void);
+
+#endif
+int register_vdec_clk_mgr(int cputype[],
+ enum vdec_type_e vdec_type, struct chip_vdec_clk_s *t_mgr);
+
+int register_vdec_clk_setting(int cputype[],
+ struct clk_set_setting *p_seting, int size);
+
+#ifdef INCLUDE_FROM_ARCH_CLK_MGR
+static struct chip_vdec_clk_s vdec_clk_mgr __initdata = {
+ .clock_init = vdec_clock_init,
+ .clock_set = vdec_clock_set,
+ .clock_on = vdec_clock_on,
+ .clock_off = vdec_clock_off,
+ .clock_get = vdec_clock_get,
+};
+
+#ifdef VDEC_HAS_VDEC2
+static struct chip_vdec_clk_s vdec2_clk_mgr __initdata = {
+ .clock_set = vdec2_clock_set,
+ .clock_on = vdec2_clock_on,
+ .clock_off = vdec2_clock_off,
+ .clock_get = vdec_clock_get,
+};
+#endif
+
+#ifdef VDEC_HAS_HEVC
+static struct chip_vdec_clk_s vdec_hevc_clk_mgr __initdata = {
+ .clock_init = hevc_clock_init,
+ .clock_set = hevc_clock_set,
+ .clock_on = hevc_clock_on,
+ .clock_off = hevc_clock_off,
+ .clock_get = vdec_clock_get,
+};
+#endif
+
+#ifdef VDEC_HAS_VDEC_HCODEC
+static struct chip_vdec_clk_s vdec_hcodec_clk_mgr __initdata = {
+ .clock_set = hcodec_clock_set,
+ .clock_on = hcodec_clock_on,
+ .clock_off = hcodec_clock_off,
+ .clock_get = vdec_clock_get,
+};
+#endif
+
+static int __init vdec_init_clk(void)
+{
+ int cpus[] = CLK_FOR_CPU;
+ register_vdec_clk_mgr(cpus, VDEC_1, &vdec_clk_mgr);
+#ifdef VDEC_HAS_VDEC2
+ register_vdec_clk_mgr(cpus, VDEC_2, &vdec2_clk_mgr);
+#endif
+#ifdef VDEC_HAS_HEVC
+ register_vdec_clk_mgr(cpus, VDEC_HEVC, &vdec_hevc_clk_mgr);
+#endif
+#ifdef VDEC_HAS_VDEC_HCODEC
+ register_vdec_clk_mgr(cpus, VDEC_HCODEC, &vdec_hcodec_clk_mgr);
+#endif
+
+#ifdef VDEC_HAS_CLK_SETTINGS
+ register_vdec_clk_setting(cpus,
+ clks_for_formats, sizeof(clks_for_formats));
+#endif
+ return 0;
+}
+
+#define ARCH_VDEC_CLK_INIT()\
+ module_init(vdec_init_clk)
+#endif
+#endif
diff --git a/drivers/common/media_clock/clk/clk_priv.h b/drivers/common/media_clock/clk/clk_priv.h
new file mode 100644
index 0000000..1898e6d
--- a/dev/null
+++ b/drivers/common/media_clock/clk/clk_priv.h
@@ -0,0 +1,38 @@
+/*
+ * drivers/amlogic/media/common/arch/clk/clk_priv.h
+ *
+ * Copyright (C) 2016 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 AMPORTS_CLK_PRIV_HEADER
+#define AMPORTS_CLK_PRIV_HEADER
+
+struct clk_set {
+ u32 wh_X_fps; /* [x*y*fps */
+ u32 clk_Mhz; /*min MHZ */
+};
+#define MAX_CLK_SET 6
+struct clk_set_setting {
+ struct clk_set set[MAX_CLK_SET];
+};
+
+struct chip_vdec_clk_s {
+ int (*clock_get)(enum vdec_type_e core);
+ int (*clock_init)(void);
+ int (*clock_set)(int clk);
+ void (*clock_on)(void);
+ void (*clock_off)(void);
+ void (*clock_prepare_switch)(void);
+};
+#endif
diff --git a/drivers/common/media_clock/clk/clkgx.c b/drivers/common/media_clock/clk/clkgx.c
new file mode 100644
index 0000000..a32dceb
--- a/dev/null
+++ b/drivers/common/media_clock/clk/clkgx.c
@@ -0,0 +1,668 @@
+/*
+ * drivers/amlogic/media/common/arch/clk/clkgx.c
+ *
+ * Copyright (C) 2016 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.
+ *
+*/
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/amlogic/media/clk/gp_pll.h>
+#include <linux/amlogic/media/utils/vdec_reg.h>
+#include <linux/amlogic/media/utils/amports_config.h>
+#include "../../../frame_provider/decoder/utils/vdec.h"
+#include <linux/amlogic/media/registers/register.h>
+#include "clk_priv.h"
+#include <linux/amlogic/media/utils/log.h>
+
+#include <linux/amlogic/media/registers/register_ops.h>
+#define debug_print pr_info
+
+#define MHz (1000000)
+
+struct clk_mux_s {
+ struct clk *vdec_clk;
+ struct clk *hcodec_clk;
+ struct clk *hevc_clk;
+};
+
+struct clk_mux_s gclk;
+/*
+*HHI_VDEC_CLK_CNTL
+*0x1078[11:9] (fclk = 2000MHz)
+ *0: fclk_div4
+ *1: fclk_div3
+ *2: fclk_div5
+ *3: fclk_div7
+ *4: mpll_clk_out1
+ *5: mpll_clk_out2
+*0x1078[6:0]
+ *divider
+*0x1078[8]
+ *enable
+*/
+
+void vdec1_set_clk(int source, int div)
+{
+ debug_print("vdec1_set_clk %d, %d\n", source, div);
+ WRITE_HHI_REG_BITS(HHI_VDEC_CLK_CNTL, (source << 9) | (div - 1), 0, 16);
+}
+EXPORT_SYMBOL(vdec1_set_clk);
+
+void hcodec_set_clk(int source, int div)
+{
+ WRITE_HHI_REG_BITS(HHI_VDEC_CLK_CNTL,
+ (source << 9) | (div - 1), 16, 16);
+}
+EXPORT_SYMBOL(hcodec_set_clk);
+
+void vdec2_set_clk(int source, int div)
+{
+ WRITE_HHI_REG_BITS(HHI_VDEC2_CLK_CNTL,
+ (source << 9) | (div - 1), 0, 16);
+}
+EXPORT_SYMBOL(vdec2_set_clk);
+
+void hevc_set_clk(int source, int div)
+{
+ debug_print("hevc_set_clk %d, %d\n", source, div);
+ WRITE_HHI_REG_BITS(HHI_VDEC2_CLK_CNTL,
+ (source << 9) | (div - 1), 16, 16);
+}
+EXPORT_SYMBOL(hevc_set_clk);
+
+void clock_set_init(struct device *dev)
+{
+ gclk.vdec_clk = devm_clk_get(dev, "clk_vdec_mux");
+ if (IS_ERR(gclk.vdec_clk)) {
+ printk("get vdec clk err.\n");
+ }
+
+ gclk.hcodec_clk = devm_clk_get(dev, "clk_hcodec_mux");
+ if (IS_ERR(gclk.hcodec_clk)) {
+ printk("get hcodec clk err.\n");
+ }
+
+ gclk.hevc_clk = devm_clk_get(dev, "clk_hevc_mux");
+ if (IS_ERR(gclk.hevc_clk)) {
+ printk("get hevc clk err.\n");
+ }
+}
+EXPORT_SYMBOL(clock_set_init);
+
+void vdec_get_clk_source(int clk, int *source, int *div, int *rclk)
+{
+#define source_div4 (0)
+#define source_div3 (1)
+#define source_div5 (2)
+#define source_div7 (3)
+ if (clk > 500) {
+ *source = source_div3;
+ *div = 1;
+ *rclk = 667;
+ } else if (clk >= 500) {
+ *source = source_div4;
+ *div = 1;
+ *rclk = 500;
+ } else if (clk >= 400) {
+ *source = source_div5;
+ *div = 1;
+ *rclk = 400;
+ } else if (clk >= 333) {
+ *source = source_div3;
+ *div = 2;
+ *rclk = 333;
+ } else if (clk >= 200) {
+ *source = source_div5;
+ *div = 2;
+ *rclk = 200;
+ } else if (clk >= 166) {
+ *source = source_div4;
+ *div = 3;
+ *rclk = 166;
+ } else if (clk >= 133) {
+ *source = source_div5;
+ *div = 3;
+ *rclk = 133;
+ } else if (clk >= 100) {
+ *source = source_div5;
+ *div = 4;
+ *rclk = 100;
+ } else if (clk >= 50) {
+ *source = source_div5;
+ *div = 8;
+ *rclk = 50;
+ } else {
+ *source = source_div5;
+ *div = 20;
+ *rclk = 10;
+ }
+}
+EXPORT_SYMBOL(vdec_get_clk_source);
+
+static int vdec_set_clk(int dec, int rate)
+{
+ struct clk *clk;
+
+ switch (dec) {
+ case VDEC_1:
+ clk = gclk.vdec_clk;
+ break;
+
+ case VDEC_HCODEC:
+ clk = gclk.hcodec_clk;
+ break;
+
+ case VDEC_2:
+ clk = gclk.vdec_clk;
+ break;
+
+ case VDEC_HEVC:
+ clk = gclk.hevc_clk;
+ break;
+
+ case VDEC_MAX:
+ break;
+
+ default:
+ pr_info("invaild vdec type.");
+ }
+
+ clk_set_rate(clk, rate);
+ WRITE_VREG_BITS(DOS_GCLK_EN0, 0x3ff, 0, 10);
+
+ return 0;
+}
+
+static bool is_gp0_div2 = true;
+
+/* set gp0 648M vdec use gp0 clk*/
+#define VDEC1_648M() \
+ WRITE_HHI_REG_BITS(HHI_VDEC_CLK_CNTL, (6 << 9) | (0), 0, 16)
+
+#define HEVC_648M() \
+ WRITE_HHI_REG_BITS(HHI_VDEC2_CLK_CNTL, (6 << 9) | (0), 16, 16)
+
+/*set gp0 1296M vdec use gp0 clk div2*/
+#define VDEC1_648M_DIV() \
+ WRITE_HHI_REG_BITS(HHI_VDEC_CLK_CNTL, (6 << 9) | (1), 0, 16)
+
+#define HEVC_648M_DIV() \
+ WRITE_HHI_REG_BITS(HHI_VDEC2_CLK_CNTL, (6 << 9) | (1), 16, 16)
+
+#define VDEC1_WITH_GP_PLL() \
+ ((READ_HHI_REG(HHI_VDEC_CLK_CNTL) & 0xe00) == 0xc00)
+#define HEVC_WITH_GP_PLL() \
+ ((READ_HHI_REG(HHI_VDEC2_CLK_CNTL) & 0xe000000) == 0xc000000)
+
+#define VDEC1_CLOCK_ON() \
+ do { if (is_meson_m8_cpu()) { \
+ WRITE_HHI_REG_BITS(HHI_VDEC_CLK_CNTL, 1, 8, 1); \
+ WRITE_VREG_BITS(DOS_GCLK_EN0, 0x3ff, 0, 10); \
+ } else { \
+ WRITE_HHI_REG_BITS(HHI_VDEC_CLK_CNTL, 1, 8, 1); \
+ WRITE_HHI_REG_BITS(HHI_VDEC3_CLK_CNTL, 0, 15, 1); \
+ WRITE_HHI_REG_BITS(HHI_VDEC3_CLK_CNTL, 0, 8, 1); \
+ WRITE_VREG_BITS(DOS_GCLK_EN0, 0x3ff, 0, 10); \
+ } \
+ } while (0)
+
+#define VDEC2_CLOCK_ON() do {\
+ WRITE_HHI_REG_BITS(HHI_VDEC2_CLK_CNTL, 1, 8, 1); \
+ WRITE_VREG(DOS_GCLK_EN1, 0x3ff);\
+ } while (0)
+
+#define HCODEC_CLOCK_ON() do {\
+ WRITE_HHI_REG_BITS(HHI_VDEC_CLK_CNTL, 1, 24, 1); \
+ WRITE_VREG_BITS(DOS_GCLK_EN0, 0x7fff, 12, 15);\
+ } while (0)
+#define HEVC_CLOCK_ON() do {\
+ WRITE_HHI_REG_BITS(HHI_VDEC2_CLK_CNTL, 1, 24, 1); \
+ WRITE_HHI_REG_BITS(HHI_VDEC4_CLK_CNTL, 0, 31, 1); \
+ WRITE_HHI_REG_BITS(HHI_VDEC4_CLK_CNTL, 0, 24, 1); \
+ WRITE_VREG(DOS_GCLK_EN3, 0xffffffff);\
+ } while (0)
+#define VDEC1_SAFE_CLOCK() do {\
+ WRITE_HHI_REG_BITS(HHI_VDEC3_CLK_CNTL, \
+ READ_HHI_REG(HHI_VDEC_CLK_CNTL) & 0x7f, 0, 7); \
+ WRITE_HHI_REG_BITS(HHI_VDEC3_CLK_CNTL, 1, 8, 1); \
+ WRITE_HHI_REG_BITS(HHI_VDEC3_CLK_CNTL, 1, 15, 1);\
+ } while (0)
+
+#define VDEC1_CLOCK_OFF() \
+ WRITE_HHI_REG_BITS(HHI_VDEC_CLK_CNTL, 0, 8, 1)
+#define VDEC2_CLOCK_OFF() \
+ WRITE_HHI_REG_BITS(HHI_VDEC2_CLK_CNTL, 0, 8, 1)
+#define HCODEC_CLOCK_OFF() \
+ WRITE_HHI_REG_BITS(HHI_VDEC_CLK_CNTL, 0, 24, 1)
+#define HEVC_SAFE_CLOCK() do { \
+ WRITE_HHI_REG_BITS(HHI_VDEC4_CLK_CNTL, \
+ (READ_HHI_REG(HHI_VDEC2_CLK_CNTL) >> 16) & 0x7f, 16, 7);\
+ WRITE_HHI_REG_BITS(HHI_VDEC4_CLK_CNTL, \
+ (READ_HHI_REG(HHI_VDEC2_CLK_CNTL) >> 25) & 0x7f, 25, 7);\
+ WRITE_HHI_REG_BITS(HHI_VDEC4_CLK_CNTL, 1, 24, 1); \
+ WRITE_HHI_REG_BITS(HHI_VDEC4_CLK_CNTL, 1, 31, 1);\
+ } while (0)
+
+#define HEVC_CLOCK_OFF() WRITE_HHI_REG_BITS(HHI_VDEC2_CLK_CNTL, 0, 24, 1)
+
+static int clock_real_clk[VDEC_MAX + 1];
+static struct gp_pll_user_handle_s *gp_pll_user_vdec, *gp_pll_user_hevc;
+
+static int gp_pll_user_cb_vdec(struct gp_pll_user_handle_s *user, int event)
+{
+ pr_info("gp_pll_user_cb_vdec call\n");
+ if (event == GP_PLL_USER_EVENT_GRANT) {
+ if (!IS_ERR(gclk.vdec_clk)) {
+ vdec_set_clk(VDEC_1, 648 * MHz);
+ pr_info("get clock : %lu\n",
+ clk_get_rate(gclk.vdec_clk));
+ }
+ }
+ return 0;
+}
+
+/*
+*enum vformat_e {
+* VFORMAT_MPEG12 = 0,
+* VFORMAT_MPEG4,
+* VFORMAT_H264,
+* VFORMAT_MJPEG,
+* VFORMAT_REAL,
+* VFORMAT_JPEG,
+* VFORMAT_VC1,
+* VFORMAT_AVS,
+* VFORMAT_YUV,
+* VFORMAT_H264MVC,
+* VFORMAT_H264_4K2K,
+* VFORMAT_HEVC,
+* VFORMAT_H264_ENC,
+* VFORMAT_JPEG_ENC,
+* VFORMAT_VP9,
+* VFORMAT_MAX
+*};
+*sample:
+*{{1280*720*30, 100}, {1920*1080*30, 166}, {1920*1080*60, 333},
+* {4096*2048*30, 600}, {4096*2048*60, 600}, {INT_MAX, 600},}
+*mean:
+*width * height * fps
+*<720p30fps clk=100MHZ
+*>=720p30fps & < 1080p30fps clk=166MHZ
+*>=1080p 30fps & < 1080p60fps clk=333MHZ
+*/
+static struct clk_set_setting clks_for_formats[] = {
+ { /*[VFORMAT_MPEG12] */
+ {{1280 * 720 * 30, 100}, {1920 * 1080 * 30, 166},
+ {1920 * 1080 * 60, 333},
+ {4096 * 2048 * 30, 600}, {4096 * 2048 * 60,
+ 600}, {INT_MAX, 600},
+ }
+ },
+ { /*[VFORMAT_MPEG4] */
+ {{1280 * 720 * 30, 100}, {1920 * 1080 * 30, 166},
+ {1920 * 1080 * 60, 333},
+ {4096 * 2048 * 30, 600}, {4096 * 2048 * 60,
+ 600}, {INT_MAX, 600},
+ }
+ },
+ { /*[VFORMAT_H264] */
+ {{1280 * 720 * 30, 100}, {1920 * 1080 * 21, 166},
+ {1920 * 1080 * 30, 333},
+ {1920 * 1080 * 60, 600}, {4096 * 2048 * 60,
+ 600}, {INT_MAX, 600},
+ }
+ },
+ { /*[VFORMAT_MJPEG] */
+ {{1280 * 720 * 30, 200}, {1920 * 1080 * 30, 200},
+ {1920 * 1080 * 60, 333},
+ {4096 * 2048 * 30, 600}, {4096 * 2048 * 60,
+ 600}, {INT_MAX, 600},
+ }
+ },
+ { /*[VFORMAT_REAL] */
+ {{1280 * 720 * 20, 200}, {1920 * 1080 * 30, 500},
+ {1920 * 1080 * 60, 500},
+ {4096 * 2048 * 30, 600}, {4096 * 2048 * 60,
+ 600}, {INT_MAX, 600},
+ }
+ },
+ { /*[VFORMAT_JPEG] */
+ {{1280 * 720 * 30, 100}, {1920 * 1080 * 30, 166},
+ {1920 * 1080 * 60, 333},
+ {4096 * 2048 * 30, 600}, {4096 * 2048 * 60,
+ 600}, {INT_MAX, 600},
+ }
+ },
+ { /*[VFORMAT_VC1] */
+ {{1280 * 720 * 30, 100}, {1920 * 1080 * 30, 166},
+ {1920 * 1080 * 60, 333},
+ {4096 * 2048 * 30, 600}, {4096 * 2048 * 60,
+ 600}, {INT_MAX, 600},
+ }
+ },
+ { /*[VFORMAT_AVS] */
+ {{1280 * 720 * 30, 100}, {1920 * 1080 * 30, 166},
+ {1920 * 1080 * 60, 333},
+ {4096 * 2048 * 30, 600}, {4096 * 2048 * 60,
+ 600}, {INT_MAX, 600},
+ }
+ },
+ { /*[VFORMAT_YUV] */
+ {{1280 * 720 * 30, 100}, {INT_MAX, 100},
+ {0, 0}, {0, 0}, {0, 0}, {0, 0},
+ }
+ },
+ { /*VFORMAT_H264MVC */
+ {{1280 * 720 * 30, 333}, {1920 * 1080 * 30, 333},
+ {4096 * 2048 * 60, 600},
+ {INT_MAX, 630}, {0, 0}, {0, 0},
+ }
+ },
+ { /*VFORMAT_H264_4K2K */
+ {{1280 * 720 * 30, 600}, {4096 * 2048 * 60, 630},
+ {INT_MAX, 630},
+ {0, 0}, {0, 0}, {0, 0},
+ }
+ },
+ { /*VFORMAT_HEVC */
+ {{1280 * 720 * 30, 100}, {1920 * 1080 * 60, 600},
+ {4096 * 2048 * 25, 630},
+ {4096 * 2048 * 30, 630}, {4096 * 2048 * 60,
+ 630}, {INT_MAX, 630},
+ }
+ },
+ { /*VFORMAT_H264_ENC */
+ {{1280 * 720 * 30, 0}, {INT_MAX, 0},
+ {0, 0}, {0, 0}, {0, 0}, {0, 0},
+ }
+ },
+ { /*VFORMAT_JPEG_ENC */
+ {{1280 * 720 * 30, 0}, {INT_MAX, 0},
+ {0, 0}, {0, 0}, {0, 0}, {0, 0},
+ }
+ },
+ { /*VFORMAT_VP9 */
+ {{1280 * 720 * 30, 100}, {1920 * 1080 * 30, 100},
+ {1920 * 1080 * 60, 166},
+ {4096 * 2048 * 30, 333}, {4096 * 2048 * 60,
+ 630}, {INT_MAX, 630},
+ }
+ },
+
+};
+
+static int vdec_clock_init(void)
+{
+ gp_pll_user_vdec = gp_pll_user_register("vdec", 0, gp_pll_user_cb_vdec);
+ if (get_cpu_type() >= MESON_CPU_MAJOR_ID_GXL)
+ is_gp0_div2 = false;
+ else
+ is_gp0_div2 = true;
+
+ if (get_cpu_type() >= MESON_CPU_MAJOR_ID_GXL) {
+ pr_info("used fix clk for vdec clk source!\n");
+ /*update_vdec_clk_config_settings(1);*//*mask*/
+ }
+ return (gp_pll_user_vdec) ? 0 : -ENOMEM;
+}
+
+static void update_clk_with_clk_configs(int clk, int *source, int *div,
+ int *rclk)
+{
+ unsigned int config = 0;/*get_vdec_clk_config_settings();*//*mask*/
+
+ if (!config)
+ return;
+ if (config >= 10) {
+ int wantclk;
+
+ wantclk = config;
+ vdec_get_clk_source(wantclk, source, div, rclk);
+ }
+}
+
+#define NO_GP0_PLL 0/*(get_vdec_clk_config_settings() == 1)*//*mask*/
+#define ALWAYS_GP0_PLL 0/*(get_vdec_clk_config_settings() == 2)*//*mask*/
+
+static int vdec_clock_set(int clk)
+{
+ int use_gpll = 0;
+ int clk_seted = 0;
+ int pll_wait = 0;
+
+ if (clk == 1)
+ clk = 200;
+ else if (clk == 2) {
+ if (clock_real_clk[VDEC_1] != 648)
+ clk = 500;
+ else
+ clk = 648;
+ } else if (clk == 0) {
+ /*
+ *used for release gp pull.
+ *if used, release it.
+ *if not used gp pll
+ *do nothing.
+ */
+ if (clock_real_clk[VDEC_1] == 667 ||
+ (clock_real_clk[VDEC_1] == 648) ||
+ clock_real_clk[VDEC_1] <= 0)
+ clk = 200;
+ else
+ clk = clock_real_clk[VDEC_1];
+ }
+
+ if ((clk > 500 && clk != 667)) {
+ if (clock_real_clk[VDEC_1] == 648)
+ return 648;
+
+ gp_pll_request(gp_pll_user_vdec);
+
+ while (pll_wait++ < 1000000) {
+ if (clk_get_rate(gclk.vdec_clk) == 648) {
+ clk_seted = 1;
+ break;
+ }
+ udelay(1);
+ }
+
+ if (!clk_seted) {
+ use_gpll = 0;
+ clk = 667;
+ pr_info("get pll failed used fix pll\n");
+ }
+ }
+
+ if (!clk_seted) {/*if 648 not set, */
+ vdec_set_clk(VDEC_1, clk * MHz);
+ pr_info("get clock : %lu", clk_get_rate(gclk.vdec_clk));
+ }
+
+ if (!use_gpll)
+ gp_pll_release(gp_pll_user_vdec);
+
+ clock_real_clk[VDEC_1] = clk;
+ pr_info("vdec_clock_set to %d\n", clk);
+
+ return clk;
+}
+
+static void vdec_clock_on(void)
+{
+ VDEC1_CLOCK_ON();
+}
+
+static void vdec_clock_off(void)
+{
+ VDEC1_CLOCK_OFF();
+ clock_real_clk[VDEC_1] = 0;
+ gp_pll_release(gp_pll_user_vdec);
+}
+
+static int hcodec_clock_set(int clk)
+{
+ clk_set_rate(gclk.hcodec_clk, clk * MHz);
+ clock_real_clk[VDEC_HCODEC] = clk;
+ return clk;
+}
+
+static void hcodec_clock_on(void)
+{
+ HCODEC_CLOCK_ON();
+}
+
+static void hcodec_clock_off(void)
+{
+ HCODEC_CLOCK_OFF();
+}
+
+static int gp_pll_user_cb_hevc(struct gp_pll_user_handle_s *user, int event)
+{
+ debug_print("gp_pll_user_cb_hevc callback\n");
+ if (event == GP_PLL_USER_EVENT_GRANT) {
+ struct clk *clk = clk_get(NULL, "gp0_pll");
+
+ if (!IS_ERR(clk)) {
+ if (is_gp0_div2)
+ clk_set_rate(clk, 1296000000UL);
+ else
+ clk_set_rate(clk, 648000000UL);
+ HEVC_SAFE_CLOCK();
+ HEVC_CLOCK_OFF();
+ if (is_gp0_div2)
+ HEVC_648M_DIV();
+ else
+ HEVC_648M();
+ HEVC_CLOCK_ON();
+ debug_print("gp_pll_user_cb_hevc callback2\n");
+ }
+ }
+
+ return 0;
+}
+
+static int hevc_clock_init(void)
+{
+ gp_pll_user_hevc = gp_pll_user_register("hevc", 0, gp_pll_user_cb_hevc);
+
+ return (gp_pll_user_hevc) ? 0 : -ENOMEM;
+}
+
+static int hevc_clock_set(int clk)
+{
+ int use_gpll = 0;
+ int source, div, rclk;
+ int gp_pll_wait = 0;
+ int clk_seted = 0;
+
+ debug_print("hevc_clock_set 1 to clk %d\n", clk);
+ if (clk == 1)
+ clk = 200;
+ else if (clk == 2) {
+ if (clock_real_clk[VDEC_HEVC] != 648)
+ clk = 500;
+ else
+ clk = 648;
+ } else if (clk == 0) {
+ /*
+ *used for release gp pull.
+ *if used, release it.
+ *if not used gp pll
+ *do nothing.
+ */
+ if ((clock_real_clk[VDEC_HEVC] == 667) ||
+ (clock_real_clk[VDEC_HEVC] == 648) ||
+ (clock_real_clk[VDEC_HEVC] <= 0))
+ clk = 200;
+ else
+ clk = clock_real_clk[VDEC_HEVC];
+ }
+ vdec_get_clk_source(clk, &source, &div, &rclk);
+ update_clk_with_clk_configs(clk, &source, &div, &rclk);
+
+ if (rclk == clock_real_clk[VDEC_HEVC])
+ return rclk; /*clk not changed, */
+ if (NO_GP0_PLL) {
+ use_gpll = 0;
+ clk_seted = 0;
+ } else if ((rclk > 500 && clk != 667) || ALWAYS_GP0_PLL) {
+ if (clock_real_clk[VDEC_HEVC] == 648)
+ return 648;
+ use_gpll = 1;
+ gp_pll_request(gp_pll_user_hevc);
+ while (!HEVC_WITH_GP_PLL() && gp_pll_wait++ < 1000000)
+ udelay(1);
+ if (HEVC_WITH_GP_PLL()) {
+ clk_seted = 1;
+ rclk = 648;
+ } else {
+ rclk = 667;
+ /*gp_pull request failed,used default 500Mhz */
+ pr_info("get gp pll failed used fix pull\n");
+ }
+ }
+ if (!clk_seted) { /*if 648 not set, */
+ //HEVC_SAFE_CLOCK();
+ //HEVC_CLOCK_OFF();
+ //vdec_set_clk(VDEC_HEVC, source, div);
+ //HEVC_CLOCK_ON();
+ }
+ if (!use_gpll)
+ gp_pll_release(gp_pll_user_hevc);
+ clock_real_clk[VDEC_HEVC] = rclk;
+/*
+* debug_print("hevc_clock_set 2 to rclk=%d, configs=%d\n",
+* rclk, get_vdec_clk_config_settings());
+*/
+ return rclk;
+}
+
+static void hevc_clock_on(void)
+{
+ HEVC_CLOCK_ON();
+}
+
+static void hevc_clock_off(void)
+{
+ HEVC_CLOCK_OFF();
+ gp_pll_release(gp_pll_user_hevc);
+ clock_real_clk[VDEC_HEVC] = 0;
+}
+
+static int vdec_clock_get(enum vdec_type_e core)
+{
+ if (core >= VDEC_MAX)
+ return 0;
+
+ return clock_real_clk[core];
+}
+
+#define INCLUDE_FROM_ARCH_CLK_MGR
+
+/*#define VDEC_HAS_VDEC2*/
+#define VDEC_HAS_HEVC
+#define VDEC_HAS_VDEC_HCODEC
+#define VDEC_HAS_CLK_SETTINGS
+#define CLK_FOR_CPU {\
+ MESON_CPU_MAJOR_ID_GXBB,\
+ MESON_CPU_MAJOR_ID_GXTVBB,\
+ MESON_CPU_MAJOR_ID_GXL,\
+ MESON_CPU_MAJOR_ID_GXM,\
+ MESON_CPU_MAJOR_ID_TXL,\
+ 0}
+#include "clk.h"
+ARCH_VDEC_CLK_INIT();
+MODULE_LICENSE("GPL");
diff --git a/drivers/common/media_clock/switch/amports_gate.c b/drivers/common/media_clock/switch/amports_gate.c
new file mode 100644
index 0000000..51e606c
--- a/dev/null
+++ b/drivers/common/media_clock/switch/amports_gate.c
@@ -0,0 +1,195 @@
+/*
+ * drivers/amlogic/media/common/arch/switch/amports_gate.c
+ *
+ * Copyright (C) 2016 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.
+ *
+*/
+#include <linux/compiler.h>
+#include <linux/clk-provider.h>
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/spinlock.h>
+#include <linux/clk.h>
+#include "amports_gate.h"
+#include <linux/amlogic/media/utils/vdec_reg.h>
+#include "../../../stream_input/amports/amports_priv.h"
+
+#define DEBUG_REF 0
+#define GATE_RESET_OK
+
+struct gate_swtch_node {
+ struct clk *clk;
+ const char *name;
+ spinlock_t lock;
+ unsigned long flags;
+ int ref_count;
+};
+#ifdef GATE_RESET_OK
+
+struct gate_swtch_node gates[] = {
+ {
+ .name = "demux",
+ },
+ {
+ .name = "parser_top",
+ },
+ {
+ .name = "vpu_intr",
+ },
+ {
+ .name = "vdec",
+ },
+ {
+ .name = "clk_vdec_mux",
+ },
+ {
+ .name = "clk_hcodec_mux",
+ },
+ {
+ .name = "clk_hevc_mux",
+ },
+};
+
+
+/*
+mesonstream {
+ compatible = "amlogic, codec, streambuf";
+ dev_name = "mesonstream";
+ status = "okay";
+ clocks = <&clkc CLKID_DOS_PARSER
+ &clkc CLKID_VPU_INTR
+ &clkc CLKID_DEMUX
+ &clkc CLKID_DOS
+ &clkc CLKID_VDEC_MUX
+ &clkc CLKID_HCODEC_MUX
+ &clkc CLKID_HEVC_MUX>;
+ clock-names = "parser_top",
+ "vpu_intr",
+ "demux",
+ "vdec",
+ "clk_vdec_mux",
+ "clk_hcodec_mux",
+ "clk_hevc_mux";
+};
+*/
+
+int amports_clock_gate_init(struct device *dev)
+{
+ int i;
+
+ for (i = 0; i < sizeof(gates) / sizeof(struct gate_swtch_node); i++) {
+ gates[i].clk = devm_clk_get(dev, gates[i].name);
+ if (IS_ERR_OR_NULL(gates[i].clk)) {
+ gates[i].clk = NULL;
+ pr_info("get gate %s control failed %p\n",
+ gates[i].name,
+ gates[i].clk);
+ } else {
+ pr_info("get gate %s control ok %p\n",
+ gates[i].name,
+ gates[i].clk);
+ }
+ gates[i].ref_count = 0;
+ spin_lock_init(&gates[i].lock);
+ }
+ return 0;
+}
+EXPORT_SYMBOL(amports_clock_gate_init);
+
+static int amports_gate_clk(struct gate_swtch_node *gate_node, int enable)
+{
+ spin_lock_irqsave(&gate_node->lock, gate_node->flags);
+ if (enable) {
+ if (DEBUG_REF)
+ pr_info("amports_gate_reset,count: %d\n",
+ gate_node->ref_count);
+ if (gate_node->ref_count == 0)
+ clk_prepare_enable(gate_node->clk);
+
+ gate_node->ref_count++;
+ } else {
+ gate_node->ref_count--;
+ if (DEBUG_REF)
+ pr_info("amports_gate_reset,count: %d\n",
+ gate_node->ref_count);
+
+ if (gate_node->ref_count == 0)
+ clk_disable_unprepare(gate_node->clk);
+ }
+ spin_unlock_irqrestore(&gate_node->lock, gate_node->flags);
+ return 0;
+}
+
+int amports_switch_gate(const char *name, int enable)
+{
+ int i;
+
+ for (i = 0; i < sizeof(gates) / sizeof(struct gate_swtch_node); i++) {
+ if (!strcmp(name, gates[i].name)) {
+
+ /*pr_info("openclose:%d gate %s control\n", enable,
+ gates[i].name);*/
+
+ if (gates[i].clk)
+ amports_gate_clk(&gates[i], enable);
+ }
+ }
+ return 0;
+}
+EXPORT_SYMBOL(amports_switch_gate);
+
+#else
+/*
+*can used for debug.
+*on chip bringup.
+*/
+int amports_clock_gate_init(struct device *dev)
+{
+ static int gate_inited;
+
+ if (gate_inited)
+ return 0;
+/*
+*#define HHI_GCLK_MPEG0 0x1050
+*#define HHI_GCLK_MPEG1 0x1051
+*#define HHI_GCLK_MPEG2 0x1052
+*#define HHI_GCLK_OTHER 0x1054
+*#define HHI_GCLK_AO 0x1055
+*/
+ WRITE_HHI_REG_BITS(HHI_GCLK_MPEG0, 1, 1, 1);/*dos*/
+ WRITE_HHI_REG_BITS(HHI_GCLK_MPEG1, 1, 25, 1);/*U_parser_top()*/
+ WRITE_HHI_REG_BITS(HHI_GCLK_MPEG1, 0xff, 6, 8);/*aiu()*/
+ WRITE_HHI_REG_BITS(HHI_GCLK_MPEG1, 1, 4, 1);/*demux()*/
+ WRITE_HHI_REG_BITS(HHI_GCLK_MPEG1, 1, 2, 1);/*audio in()*/
+ WRITE_HHI_REG_BITS(HHI_GCLK_MPEG2, 1, 25, 1);/*VPU Interrupt*/
+ gate_inited++;
+
+
+
+ return 0;
+}
+EXPORT_SYMBOL(amports_clock_gate_init);
+
+static int amports_switch_gate(struct gate_swtch_node *gate_node, int enable)
+{
+ return 0;
+}
+
+int amports_switch_gate(const char *name, int enable)
+{
+ amports_switch_gate(0, 0);
+ return 0;
+}
+EXPORT_SYMBOL(amports_switch_gate);
+
+#endif
diff --git a/drivers/common/media_clock/switch/amports_gate.h b/drivers/common/media_clock/switch/amports_gate.h
new file mode 100644
index 0000000..53e1960
--- a/dev/null
+++ b/drivers/common/media_clock/switch/amports_gate.h
@@ -0,0 +1,25 @@
+/*
+ * drivers/amlogic/media/common/arch/switch/amports_gate.h
+ *
+ * Copyright (C) 2016 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 AMPORT_GATE_H
+#define AMPORT_GATE_H
+#include <linux/device.h>
+
+extern int amports_clock_gate_init(struct device *dev);
+extern int amports_switch_gate(const char *name, int enable);
+
+#endif
diff --git a/drivers/frame_provider/Makefile b/drivers/frame_provider/Makefile
new file mode 100644
index 0000000..371e088
--- a/dev/null
+++ b/drivers/frame_provider/Makefile
@@ -0,0 +1 @@
+obj-y += decoder/
diff --git a/drivers/frame_provider/decoder/Makefile b/drivers/frame_provider/decoder/Makefile
new file mode 100644
index 0000000..2010cbb
--- a/dev/null
+++ b/drivers/frame_provider/decoder/Makefile
@@ -0,0 +1,13 @@
+obj-y += utils/
+obj-$(CONFIG_AMLOGIC_MEDIA_VDEC_MPEG12) += mpeg12/
+obj-$(CONFIG_AMLOGIC_MEDIA_VDEC_MPEG4) += mpeg4/
+obj-$(CONFIG_AMLOGIC_MEDIA_VDEC_MMPEG4) += mmpeg4/
+obj-$(CONFIG_AMLOGIC_MEDIA_VDEC_VC1) += vc1/
+obj-$(CONFIG_AMLOGIC_MEDIA_VDEC_H264) += h264/
+obj-$(CONFIG_AMLOGIC_MEDIA_VDEC_MH264) += mh264/
+obj-$(CONFIG_AMLOGIC_MEDIA_VDEC_H264MVC) += h264_mvc/
+obj-$(CONFIG_AMLOGIC_MEDIA_VDEC_H265) += h265/
+obj-$(CONFIG_AMLOGIC_MEDIA_VDEC_VP9) += vp9/
+obj-$(CONFIG_AMLOGIC_MEDIA_VDEC_MJPEG) += mjpeg/
+obj-$(CONFIG_AMLOGIC_MEDIA_VDEC_REAL) += real/
+obj-$(CONFIG_AMLOGIC_MEDIA_VDEC_AVS) += avs/
diff --git a/drivers/frame_provider/decoder/h264/Makefile b/drivers/frame_provider/decoder/h264/Makefile
new file mode 100644
index 0000000..b7a7ce5
--- a/dev/null
+++ b/drivers/frame_provider/decoder/h264/Makefile
@@ -0,0 +1 @@
+obj-m += vh264.o
diff --git a/drivers/frame_provider/decoder/h264/vh264.c b/drivers/frame_provider/decoder/h264/vh264.c
new file mode 100644
index 0000000..08bb598
--- a/dev/null
+++ b/drivers/frame_provider/decoder/h264/vh264.c
@@ -0,0 +1,2925 @@
+/*
+ * drivers/amlogic/media/frame_provider/decoder/h264/vh264.c
+ *
+ * Copyright (C) 2016 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.
+ *
+*/
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/timer.h>
+#include <linux/kfifo.h>
+#include <linux/platform_device.h>
+
+#include <linux/amlogic/media/utils/amstream.h>
+#include <linux/amlogic/media/frame_sync/ptsserv.h>
+#include <linux/amlogic/media/vfm/vframe.h>
+#include <linux/amlogic/media/vfm/vframe_provider.h>
+#include <linux/amlogic/media/vfm/vframe_receiver.h>
+#include <linux/amlogic/media/utils/vformat.h>
+#include <linux/amlogic/media/frame_sync/tsync.h>
+#include <linux/workqueue.h>
+#include <linux/dma-mapping.h>
+#include <linux/atomic.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include "../../../stream_input/amports/amports_priv.h"
+#include <linux/amlogic/media/canvas/canvas.h>
+#include <linux/amlogic/media/codec_mm/codec_mm.h>
+
+#include "../utils/vdec.h"
+#include <linux/amlogic/media/utils/vdec_reg.h>
+#include "../utils/amvdec.h"
+#include "vh264.h"
+#include "../../../stream_input/parser/streambuf.h"
+#include <linux/delay.h>
+
+/*#include <linux/amlogic/ge2d/ge2d.h>*/
+
+#define DRIVER_NAME "amvdec_h264"
+#define MODULE_NAME "amvdec_h264"
+#define MEM_NAME "codec_264"
+#define HANDLE_H264_IRQ
+/* #define DEBUG_PTS */
+#if 0 /* MESON_CPU_TYPE <= MESON_CPU_TYPE_MESON6TV */
+#define DROP_B_FRAME_FOR_1080P_50_60FPS
+#endif
+#define RATE_MEASURE_NUM 8
+#define RATE_CORRECTION_THRESHOLD 5
+#define RATE_24_FPS 4004 /* 23.97 */
+#define RATE_25_FPS 3840 /* 25 */
+#define DUR2PTS(x) ((x)*90/96)
+#define PTS2DUR(x) ((x)*96/90)
+#define DUR2PTS_REM(x) (x*90 - DUR2PTS(x)*96)
+#define FIX_FRAME_RATE_CHECK_IDRFRAME_NUM 2
+#define VDEC_CLOCK_ADJUST_FRAME 50
+
+static inline bool close_to(int a, int b, int m)
+{
+ return (abs(a - b) < m) ? true : false;
+}
+
+static DEFINE_MUTEX(vh264_mutex);
+/* 12M for L41 */
+#define MAX_DPB_BUFF_SIZE (12*1024*1024)
+#define DEFAULT_MEM_SIZE (32*1024*1024)
+#define AVIL_DPB_BUFF_SIZE 0x01ec2000
+
+#define DEF_BUF_START_ADDR 0x1000000
+#define V_BUF_ADDR_OFFSET_NEW (0x1ee000)
+#define V_BUF_ADDR_OFFSET (0x13e000)
+
+#define PIC_SINGLE_FRAME 0
+#define PIC_TOP_BOT_TOP 1
+#define PIC_BOT_TOP_BOT 2
+#define PIC_DOUBLE_FRAME 3
+#define PIC_TRIPLE_FRAME 4
+#define PIC_TOP_BOT 5
+#define PIC_BOT_TOP 6
+#define PIC_INVALID 7
+
+#define EXTEND_SAR 0xff
+
+#define VF_POOL_SIZE 64
+#define VF_BUF_NUM 24
+#define PUT_INTERVAL (HZ/100)
+#define NO_DISP_WD_COUNT (3 * HZ / PUT_INTERVAL)
+
+#define SWITCHING_STATE_OFF 0
+#define SWITCHING_STATE_ON_CMD3 1
+#define SWITCHING_STATE_ON_CMD1 2
+#define SWITCHING_STATE_ON_CMD1_PENDING 3
+
+
+#define DEC_CONTROL_FLAG_FORCE_2997_1080P_INTERLACE 0x0001
+#define DEC_CONTROL_FLAG_FORCE_2500_576P_INTERLACE 0x0002
+#define DEC_CONTROL_FLAG_DISABLE_FAST_POC 0x0004
+
+#define INCPTR(p) ptr_atomic_wrap_inc(&p)
+
+#define SLICE_TYPE_I 2
+#define SLICE_TYPE_P 5
+#define SLICE_TYPE_B 6
+
+struct buffer_spec_s {
+ unsigned int y_addr;
+ unsigned int u_addr;
+ unsigned int v_addr;
+
+ int y_canvas_index;
+ int u_canvas_index;
+ int v_canvas_index;
+
+ unsigned int y_canvas_width;
+ unsigned int u_canvas_width;
+ unsigned int v_canvas_width;
+
+ unsigned int y_canvas_height;
+ unsigned int u_canvas_height;
+ unsigned int v_canvas_height;
+
+ unsigned long phy_addr;
+ int alloc_count;
+};
+
+#define spec2canvas(x) \
+ (((x)->v_canvas_index << 16) | \
+ ((x)->u_canvas_index << 8) | \
+ ((x)->y_canvas_index << 0))
+
+static struct vframe_s *vh264_vf_peek(void *);
+static struct vframe_s *vh264_vf_get(void *);
+static void vh264_vf_put(struct vframe_s *, void *);
+static int vh264_vf_states(struct vframe_states *states, void *);
+static int vh264_event_cb(int type, void *data, void *private_data);
+
+static void vh264_prot_init(void);
+static void vh264_local_init(void);
+static void vh264_put_timer_func(unsigned long arg);
+static void stream_switching_done(void);
+
+static const char vh264_dec_id[] = "vh264-dev";
+
+#define PROVIDER_NAME "decoder.h264"
+
+static const struct vframe_operations_s vh264_vf_provider_ops = {
+ .peek = vh264_vf_peek,
+ .get = vh264_vf_get,
+ .put = vh264_vf_put,
+ .event_cb = vh264_event_cb,
+ .vf_states = vh264_vf_states,
+};
+
+static struct vframe_provider_s vh264_vf_prov;
+/*TODO irq*/
+#if 1
+static u32 frame_buffer_size;
+static u32 frame_width, frame_height, frame_dur, frame_prog, frame_packing_type,
+ last_duration;
+static u32 saved_resolution;
+static u32 last_mb_width, last_mb_height;
+#else
+static u32 frame_buffer_size;
+static u32 frame_width, frame_height, frame_dur, frame_prog, last_duration;
+static u32 last_mb_width, last_mb_height;
+static u32 frame_packing_type;
+#endif
+static DECLARE_KFIFO(newframe_q, struct vframe_s *, VF_POOL_SIZE);
+static DECLARE_KFIFO(display_q, struct vframe_s *, VF_POOL_SIZE);
+static DECLARE_KFIFO(recycle_q, struct vframe_s *, VF_POOL_SIZE);
+static DECLARE_KFIFO(delay_display_q, struct vframe_s *, VF_POOL_SIZE);
+
+static struct vframe_s vfpool[VF_POOL_SIZE];
+static s32 vfbuf_use[VF_BUF_NUM];
+static struct buffer_spec_s buffer_spec[VF_BUF_NUM];
+static struct buffer_spec_s fense_buffer_spec[2];
+static struct vframe_s fense_vf[2];
+
+static struct timer_list recycle_timer;
+static u32 stat;
+static unsigned long buf_start, buf_end;
+static u32 buf_size;
+static s32 buf_offset;
+static u32 ucode_map_start;
+static u32 pts_outside;
+static u32 sync_outside;
+static u32 dec_control;
+static u32 vh264_ratio;
+static u32 vh264_rotation;
+static u32 use_idr_framerate;
+
+static u32 seq_info;
+static u32 timing_info_present_flag;
+static u32 fixed_frame_rate_flag;
+static u32 fixed_frame_rate_check_count;
+static u32 aspect_ratio_info;
+static u32 num_units_in_tick;
+static u32 time_scale;
+static u32 h264_ar;
+static u32 decoder_debug_flag;
+#ifdef DROP_B_FRAME_FOR_1080P_50_60FPS
+static u32 last_interlaced;
+#endif
+static bool is_4k;
+static unsigned char h264_first_pts_ready;
+static bool h264_first_valid_pts_ready;
+static u32 h264pts1, h264pts2;
+static u32 h264_pts_count, duration_from_pts_done, duration_on_correcting;
+static u32 vh264_error_count;
+static u32 vh264_no_disp_count;
+static u32 fatal_error_flag;
+static u32 fatal_error_reset;
+static u32 max_refer_buf = 1;
+static u32 decoder_force_reset;
+static unsigned int no_idr_error_count;
+static unsigned int no_idr_error_max = 60;
+
+#if 0
+static u32 vh264_no_disp_wd_count;
+#endif
+static u32 vh264_running;
+static s32 vh264_stream_switching_state;
+static s32 vh264_eos;
+static struct vframe_s *p_last_vf;
+static s32 iponly_early_mode;
+
+/*TODO irq*/
+#if 1
+static u32 last_pts, last_pts_remainder;
+#else
+static u32 last_pts;
+#endif
+static bool check_pts_discontinue;
+static u32 wait_buffer_counter;
+static u32 video_signal_from_vui;
+
+static uint error_recovery_mode;
+static uint error_recovery_mode_in = 3;
+static uint error_recovery_mode_use = 3;
+
+static uint mb_total = 0, mb_width = 0, mb_height;
+#define UCODE_IP_ONLY 2
+#define UCODE_IP_ONLY_PARAM 1
+static uint ucode_type;
+
+#ifdef DEBUG_PTS
+static unsigned long pts_missed, pts_hit;
+#endif
+static uint debugfirmware;
+
+static atomic_t vh264_active = ATOMIC_INIT(0);
+static int vh264_reset;
+static struct work_struct error_wd_work;
+static struct work_struct stream_switching_work;
+static struct work_struct set_parameter_work;
+
+static struct dec_sysinfo vh264_amstream_dec_info;
+static dma_addr_t mc_dma_handle;
+static void *mc_cpu_addr;
+static u32 first_offset;
+static u32 first_pts;
+static u64 first_pts64;
+static bool first_pts_cached;
+static void *sei_data_buffer;
+static dma_addr_t sei_data_buffer_phys;
+static int clk_adj_frame_count;
+
+#define MC_OFFSET_HEADER 0x0000
+#define MC_OFFSET_DATA 0x1000
+#define MC_OFFSET_MMCO 0x2000
+#define MC_OFFSET_LIST 0x3000
+#define MC_OFFSET_SLICE 0x4000
+
+#define MC_TOTAL_SIZE (20*SZ_1K)
+#define MC_SWAP_SIZE (4*SZ_1K)
+
+#define MODE_ERROR 0
+#define MODE_FULL 1
+
+static DEFINE_SPINLOCK(lock);
+static DEFINE_SPINLOCK(prepare_lock);
+static DEFINE_SPINLOCK(recycle_lock);
+
+static bool block_display_q;
+static int vh264_stop(int mode);
+static s32 vh264_init(void);
+
+#define DFS_HIGH_THEASHOLD 3
+
+static bool pts_discontinue;
+#if 0
+
+static struct ge2d_context_s *ge2d_videoh264_context;
+
+static int ge2d_videoh264task_init(void)
+{
+ if (ge2d_videoh264_context == NULL)
+ ge2d_videoh264_context = create_ge2d_work_queue();
+
+ if (ge2d_videoh264_context == NULL) {
+ pr_info("create_ge2d_work_queue video task failed\n");
+ return -1;
+ }
+ return 0;
+}
+
+static int ge2d_videoh264task_release(void)
+{
+ if (ge2d_videoh264_context) {
+ destroy_ge2d_work_queue(ge2d_videoh264_context);
+ ge2d_videoh264_context = NULL;
+ }
+ return 0;
+}
+
+static int ge2d_canvas_dup(struct canvas_s *srcy, struct canvas_s *srcu,
+ struct canvas_s *des, int format, u32 srcindex,
+ u32 desindex)
+{
+
+ struct config_para_ex_s ge2d_config;
+ /* pr_info("[%s]h264 ADDR srcy[0x%lx] srcu[0x%lx] des[0x%lx]\n",
+ * __func__, srcy->addr, srcu->addr, des->addr);
+ */
+ memset(&ge2d_config, 0, sizeof(struct config_para_ex_s));
+
+ ge2d_config.alu_const_color = 0;
+ ge2d_config.bitmask_en = 0;
+ ge2d_config.src1_gb_alpha = 0;
+
+ ge2d_config.src_planes[0].addr = srcy->addr;
+ ge2d_config.src_planes[0].w = srcy->width;
+ ge2d_config.src_planes[0].h = srcy->height;
+
+ ge2d_config.src_planes[1].addr = srcu->addr;
+ ge2d_config.src_planes[1].w = srcu->width;
+ ge2d_config.src_planes[1].h = srcu->height;
+
+ ge2d_config.dst_planes[0].addr = des->addr;
+ ge2d_config.dst_planes[0].w = des->width;
+ ge2d_config.dst_planes[0].h = des->height;
+
+ ge2d_config.src_para.canvas_index = srcindex;
+ ge2d_config.src_para.mem_type = CANVAS_TYPE_INVALID;
+ ge2d_config.src_para.format = format;
+ ge2d_config.src_para.fill_color_en = 0;
+ ge2d_config.src_para.fill_mode = 0;
+ ge2d_config.src_para.color = 0;
+ ge2d_config.src_para.top = 0;
+ ge2d_config.src_para.left = 0;
+ ge2d_config.src_para.width = srcy->width;
+ ge2d_config.src_para.height = srcy->height;
+
+ ge2d_config.dst_para.canvas_index = desindex;
+ ge2d_config.dst_para.mem_type = CANVAS_TYPE_INVALID;
+ ge2d_config.dst_para.format = format;
+ ge2d_config.dst_para.fill_color_en = 0;
+ ge2d_config.dst_para.fill_mode = 0;
+ ge2d_config.dst_para.color = 0;
+ ge2d_config.dst_para.top = 0;
+ ge2d_config.dst_para.left = 0;
+ ge2d_config.dst_para.width = srcy->width;
+ ge2d_config.dst_para.height = srcy->height;
+
+ if (ge2d_context_config_ex(ge2d_videoh264_context, &ge2d_config) < 0) {
+ pr_info("ge2d_context_config_ex failed\n");
+ return -1;
+ }
+
+ stretchblt_noalpha(ge2d_videoh264_context, 0, 0, srcy->width,
+ srcy->height, 0, 0, srcy->width, srcy->height);
+
+ return 0;
+}
+#endif/*mask*/
+
+static inline int fifo_level(void)
+{
+ return VF_POOL_SIZE - kfifo_len(&newframe_q);
+}
+
+
+void spec_set_canvas(struct buffer_spec_s *spec,
+ unsigned width, unsigned height)
+{
+ canvas_config(spec->y_canvas_index,
+ spec->y_addr,
+ width, height,
+ CANVAS_ADDR_NOWRAP, CANVAS_BLKMODE_32X32);
+
+ canvas_config(spec->u_canvas_index,
+ spec->u_addr,
+ width, height / 2,
+ CANVAS_ADDR_NOWRAP, CANVAS_BLKMODE_32X32);
+}
+
+static void prepare_display_q(void)
+{
+ unsigned long flags;
+ int count;
+
+ spin_lock_irqsave(&prepare_lock, flags);
+
+ if (block_display_q) {
+ spin_unlock_irqrestore(&prepare_lock, flags);
+ return;
+ }
+
+ spin_unlock_irqrestore(&prepare_lock, flags);
+
+ count = (int)VF_POOL_SIZE -
+ kfifo_len(&delay_display_q) -
+ kfifo_len(&display_q) -
+ kfifo_len(&recycle_q) -
+ kfifo_len(&newframe_q);
+
+ if ((vh264_stream_switching_state != SWITCHING_STATE_OFF)
+ || is_4k)
+ count = 0;
+ else
+ count = (count < 2) ? 0 : 2;
+
+ while (kfifo_len(&delay_display_q) > count) {
+ struct vframe_s *vf;
+
+ if (kfifo_get(&delay_display_q, &vf)) {
+ kfifo_put(&display_q,
+ (const struct vframe_s *)vf);
+ vf_notify_receiver(PROVIDER_NAME,
+ VFRAME_EVENT_PROVIDER_VFRAME_READY, NULL);
+ }
+ }
+}
+
+static struct vframe_s *vh264_vf_peek(void *op_arg)
+{
+ struct vframe_s *vf;
+
+ if (kfifo_peek(&display_q, &vf))
+ return vf;
+
+ return NULL;
+}
+
+static struct vframe_s *vh264_vf_get(void *op_arg)
+{
+ struct vframe_s *vf;
+
+ if (kfifo_get(&display_q, &vf))
+ return vf;
+
+ return NULL;
+}
+
+static void vh264_vf_put(struct vframe_s *vf, void *op_arg)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&recycle_lock, flags);
+
+ if ((vf != &fense_vf[0]) && (vf != &fense_vf[1]))
+ kfifo_put(&recycle_q, (const struct vframe_s *)vf);
+
+ spin_unlock_irqrestore(&recycle_lock, flags);
+}
+
+static int vh264_event_cb(int type, void *data, void *private_data)
+{
+ if (type & VFRAME_EVENT_RECEIVER_RESET) {
+ unsigned long flags;
+
+ amvdec_stop();
+#ifndef CONFIG_POST_PROCESS_MANAGER
+ vf_light_unreg_provider(&vh264_vf_prov);
+#endif
+ spin_lock_irqsave(&lock, flags);
+ vh264_local_init();
+ vh264_prot_init();
+ spin_unlock_irqrestore(&lock, flags);
+#ifndef CONFIG_POST_PROCESS_MANAGER
+ vf_reg_provider(&vh264_vf_prov);
+#endif
+ amvdec_start();
+ }
+ return 0;
+}
+
+static int vh264_vf_states(struct vframe_states *states, void *op_arg)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&lock, flags);
+
+ states->vf_pool_size = VF_POOL_SIZE;
+ states->buf_free_num = kfifo_len(&newframe_q);
+ states->buf_avail_num = kfifo_len(&display_q) +
+ kfifo_len(&delay_display_q);
+ states->buf_recycle_num = kfifo_len(&recycle_q);
+
+ spin_unlock_irqrestore(&lock, flags);
+
+ return 0;
+}
+
+#if 0
+static tvin_trans_fmt_t convert_3d_format(u32 type)
+{
+ const tvin_trans_fmt_t conv_tab[] = {
+ 0, /* checkerboard */
+ 0, /* column alternation */
+ TVIN_TFMT_3D_LA, /* row alternation */
+ TVIN_TFMT_3D_LRH_OLER, /* side by side */
+ TVIN_TFMT_3D_FA /* top bottom */
+ };
+
+ return (type <= 4) ? conv_tab[type] : 0;
+}
+#endif
+
+static void set_frame_info(struct vframe_s *vf)
+{
+ vf->width = frame_width;
+ vf->height = frame_height;
+ vf->duration = frame_dur;
+ vf->ratio_control =
+ (min(h264_ar, (u32) DISP_RATIO_ASPECT_RATIO_MAX)) <<
+ DISP_RATIO_ASPECT_RATIO_BIT;
+ vf->orientation = vh264_rotation;
+ vf->flag = 0;
+
+#ifdef CONFIG_POST_PROCESS_MANAGER_3D_PROCESS
+ vf->trans_fmt = 0;
+ if ((vf->trans_fmt == TVIN_TFMT_3D_LRF) ||
+ (vf->trans_fmt == TVIN_TFMT_3D_LA)) {
+ vf->left_eye.start_x = 0;
+ vf->left_eye.start_y = 0;
+ vf->left_eye.width = frame_width / 2;
+ vf->left_eye.height = frame_height;
+
+ vf->right_eye.start_x = 0;
+ vf->right_eye.start_y = 0;
+ vf->right_eye.width = frame_width / 2;
+ vf->right_eye.height = frame_height;
+ } else if ((vf->trans_fmt == TVIN_TFMT_3D_LRH_OLER) ||
+ (vf->trans_fmt == TVIN_TFMT_3D_TB)) {
+ vf->left_eye.start_x = 0;
+ vf->left_eye.start_y = 0;
+ vf->left_eye.width = frame_width / 2;
+ vf->left_eye.height = frame_height;
+
+ vf->right_eye.start_x = 0;
+ vf->right_eye.start_y = 0;
+ vf->right_eye.width = frame_width / 2;
+ vf->right_eye.height = frame_height;
+ }
+#endif
+
+}
+
+#ifdef CONFIG_POST_PROCESS_MANAGER
+static void vh264_ppmgr_reset(void)
+{
+ vf_notify_receiver(PROVIDER_NAME, VFRAME_EVENT_PROVIDER_RESET, NULL);
+
+ vh264_local_init();
+
+ pr_info("vh264dec: vf_ppmgr_reset\n");
+}
+#endif
+
+static int get_max_dpb_size(int level_idc, int mb_width, int mb_height)
+{
+ int size, r;
+
+ switch (level_idc) {
+ case 10:
+ r = 1485;
+ break;
+ case 11:
+ r = 3375;
+ break;
+ case 12:
+ case 13:
+ case 20:
+ r = 8910;
+ break;
+ case 21:
+ r = 17820;
+ break;
+ case 22:
+ case 30:
+ r = 30375;
+ break;
+ case 31:
+ r = 67500;
+ break;
+ case 32:
+ r = 76800;
+ break;
+ case 40:
+ case 41:
+ case 42:
+ r = 122880;
+ break;
+ case 50:
+ r = 414000;
+ break;
+ case 51:
+ case 52:
+ r = 691200;
+ break;
+ default:
+ return 0;
+ }
+ size = (mb_width * mb_height +
+ (mb_width * mb_height / 2)) * 256 * 10;
+ r = (r * 1024 + size-1) / size;
+ r = min(r, 16);
+ /*pr_info("max_dpb %d size:%d\n", r, size);*/
+ return r;
+}
+static void vh264_set_params(struct work_struct *work)
+{
+ int aspect_ratio_info_present_flag, aspect_ratio_idc;
+ int max_dpb_size, actual_dpb_size, max_reference_size;
+ int i, mb_mv_byte, start_addr;
+ unsigned long addr;
+ unsigned int post_canvas;
+ unsigned int frame_mbs_only_flag;
+ unsigned int chroma_format_idc, chroma444, video_signal;
+ unsigned int crop_infor, crop_bottom, crop_right, level_idc;
+ u32 disp_addr = 0xffffffff;
+ //struct canvas_s cur_canvas;
+
+ if (!atomic_read(&vh264_active))
+ return;
+ mutex_lock(&vh264_mutex);
+ if (vh264_stream_switching_state == SWITCHING_STATE_ON_CMD1)
+ vh264_stream_switching_state = SWITCHING_STATE_ON_CMD1_PENDING;
+ post_canvas = get_post_canvas();
+ clk_adj_frame_count = 0;
+ /* set to max decoder clock rate at the beginning */
+ vdec_source_changed(VFORMAT_H264, 3840, 2160, 60);
+ timing_info_present_flag = 0;
+ mb_width = READ_VREG(AV_SCRATCH_1);
+ seq_info = READ_VREG(AV_SCRATCH_2);
+ aspect_ratio_info = READ_VREG(AV_SCRATCH_3);
+ num_units_in_tick = READ_VREG(AV_SCRATCH_4);
+ time_scale = READ_VREG(AV_SCRATCH_5);
+ level_idc = READ_VREG(AV_SCRATCH_A);
+ video_signal = READ_VREG(AV_SCRATCH_H);
+ video_signal_from_vui =
+ ((video_signal & 0xffff) << 8) |
+ ((video_signal & 0xff0000) >> 16) |
+ ((video_signal & 0x3f000000));
+/*
+* pr_info("video_signal_type_present_flag 0x%x\n",
+* (video_signal_from_vui >> 29) & 1);
+* pr_info("video_format 0x%x\n",
+* (video_signal_from_vui >> 26) & 7);
+* pr_info("video_full_range_flag 0x%x\n",
+* (video_signal_from_vui >> 25) & 1);
+* pr_info("color_description_present_flag 0x%x\n",
+* (video_signal_from_vui >> 24) & 1);
+* pr_info("color_primaries 0x%x\n",
+* (video_signal_from_vui >> 16) & 0xff);
+* pr_info("transfer_characteristic 0x%x\n",
+* (video_signal_from_vui >> 8) & 0xff);
+* pr_info("matrix_coefficient 0x%x\n",
+* video_signal_from_vui & 0xff);
+*/
+
+ mb_total = (mb_width >> 8) & 0xffff;
+ max_reference_size = (mb_width >> 24) & 0x7f;
+ mb_mv_byte = (mb_width & 0x80000000) ? 24 : 96;
+ if (ucode_type == UCODE_IP_ONLY_PARAM)
+ mb_mv_byte = 96;
+ mb_width = mb_width & 0xff;
+ if (get_cpu_type() >= MESON_CPU_MAJOR_ID_GXTVBB) {
+ if (!mb_width && mb_total)
+ mb_width = 256;
+ }
+ mb_height = mb_total / mb_width;
+ last_duration = 0;
+ /* AV_SCRATCH_2
+ * bit 15: frame_mbs_only_flag
+ *bit 13-14: chroma_format_idc
+ */
+ frame_mbs_only_flag = (seq_info >> 15) & 0x01;
+ chroma_format_idc = (seq_info >> 13) & 0x03;
+ chroma444 = (chroma_format_idc == 3) ? 1 : 0;
+
+ /* @AV_SCRATCH_6.31-16 = (left << 8 | right ) << 1
+ * @AV_SCRATCH_6.15-0 = (top << 8 | bottom ) <<
+ * (2 - frame_mbs_only_flag)
+ */
+ crop_infor = READ_VREG(AV_SCRATCH_6);
+ crop_bottom = (crop_infor & 0xff) >> (2 - frame_mbs_only_flag);
+ crop_right = ((crop_infor >> 16) & 0xff) >> (2 - frame_mbs_only_flag);
+
+ /* if width or height from outside is not equal to mb, then use mb */
+ /* add: for seeking stream with other resolution */
+ if ((last_mb_width && (last_mb_width != mb_width))
+ || (mb_width != ((frame_width + 15) >> 4)))
+ frame_width = 0;
+ if ((last_mb_height && (last_mb_height != mb_height))
+ || (mb_height != ((frame_height + 15) >> 4)))
+ frame_height = 0;
+ last_mb_width = mb_width;
+ last_mb_height = mb_height;
+
+ if ((frame_width == 0) || (frame_height == 0) || crop_infor) {
+ frame_width = mb_width << 4;
+ frame_height = mb_height << 4;
+ if (frame_mbs_only_flag) {
+ frame_height =
+ frame_height - (2 >> chroma444) *
+ min(crop_bottom,
+ (unsigned int)((8 << chroma444) - 1));
+ frame_width =
+ frame_width - (2 >> chroma444) * min(crop_right,
+ (unsigned
+ int)((8 << chroma444) - 1));
+ } else {
+ frame_height =
+ frame_height - (4 >> chroma444) *
+ min(crop_bottom,
+ (unsigned int)((8 << chroma444)
+ - 1));
+ frame_width =
+ frame_width - (4 >> chroma444) * min(crop_right,
+ (unsigned
+ int)((8 <<
+ chroma444)
+ - 1));
+ }
+#if 0
+ pr_info
+ ("frame_mbs_only_flag %d, crop_bottom %d, frame_height %d, ",
+ frame_mbs_only_flag, crop_bottom, frame_height);
+ pr_info
+ ("mb_height %d,crop_right %d, frame_width %d, mb_width %d\n",
+ mb_height, crop_right, frame_width, mb_width);
+#endif
+ if (frame_height == 1088)
+ frame_height = 1080;
+ }
+
+ mb_width = (mb_width + 3) & 0xfffffffc;
+ mb_height = (mb_height + 3) & 0xfffffffc;
+ mb_total = mb_width * mb_height;
+
+ /*max_reference_size <= max_dpb_size <= actual_dpb_size*/
+ is_4k = (mb_total > 8160) ? true:false;
+ if (is_4k) {
+ /*4k2k*/
+ if (get_cpu_type() >= MESON_CPU_MAJOR_ID_GXTVBB) {
+ max_dpb_size = get_max_dpb_size(
+ level_idc, mb_width, mb_height);
+ actual_dpb_size = max_dpb_size + 4;
+ if (actual_dpb_size > VF_BUF_NUM)
+ actual_dpb_size = VF_BUF_NUM;
+ } else {
+ vh264_running = 0;
+ fatal_error_flag =
+ DECODER_FATAL_ERROR_SIZE_OVERFLOW;
+ mutex_unlock(&vh264_mutex);
+ pr_err("oversize ! mb_total %d,\n", mb_total);
+ return;
+ }
+ } else {
+ actual_dpb_size = (frame_buffer_size - mb_total * mb_mv_byte *
+ max_reference_size) / (mb_total * 384);
+ actual_dpb_size = min(actual_dpb_size, VF_BUF_NUM);
+ max_dpb_size = get_max_dpb_size(level_idc, mb_width, mb_height);
+ if (max_reference_size > 1)
+ max_dpb_size = max_reference_size - 1;
+ else
+ max_dpb_size = max_reference_size;
+ if (actual_dpb_size < (max_dpb_size + 4)) {
+ actual_dpb_size = max_dpb_size + 4;
+ if (actual_dpb_size > VF_BUF_NUM)
+ actual_dpb_size = VF_BUF_NUM;
+ }
+ pr_info("actual_dpb_size %d max_dpb_size %d\n",
+ actual_dpb_size, max_dpb_size);
+ }
+ if (max_dpb_size == 0)
+ max_dpb_size = actual_dpb_size;
+ else
+ max_dpb_size = min(max_dpb_size, actual_dpb_size);
+ max_reference_size = min(max_reference_size, actual_dpb_size-1);
+ max_dpb_size = max(max_reference_size, max_dpb_size);
+ max_reference_size++;
+
+ start_addr = addr = buf_start;
+ if (is_4k)
+ addr += ((mb_total << 8) + (mb_total << 7));/*keep last frame */
+ WRITE_VREG(AV_SCRATCH_1, addr);
+ WRITE_VREG(AV_SCRATCH_3, post_canvas); /* should be modified later */
+ /*canvas_read((READ_VCBUS_REG(VD1_IF0_CANVAS0) & 0xff), &cur_canvas);
+ disp_addr = (cur_canvas.addr + 7) >> 3;*//*mask*/
+ if ((addr + mb_total * mb_mv_byte * max_reference_size)
+ >= buf_end) {
+ fatal_error_flag =
+ DECODER_FATAL_ERROR_NO_MEM;
+ vh264_running = 0;
+ mutex_unlock(&vh264_mutex);
+ pr_err("mv buf not enough!\n");
+ return;
+ }
+ addr += mb_total * mb_mv_byte * max_reference_size;
+ WRITE_VREG(AV_SCRATCH_4, addr);
+ if (!(READ_VREG(AV_SCRATCH_F) & 0x1)) {
+ bool use_alloc = is_4k ? true:false;
+ int alloc_count = 0;
+
+ for (i = 0; i < actual_dpb_size; i++) {
+ if (((addr + (mb_total << 8) + (mb_total << 7))
+ >= buf_end) && (!use_alloc)) {
+ pr_info("start alloc for %d\n", i);
+ use_alloc = true;
+ }
+ if (use_alloc) {
+#ifdef DOUBLE_WRITE
+ int page_count =
+ PAGE_ALIGN((mb_total << 8) + (mb_total
+ << 7) + (mb_total << 6) +
+ (mb_total << 5)) / PAGE_SIZE;
+#else
+ int page_count =
+ PAGE_ALIGN((mb_total << 8) +
+ (mb_total << 7)) / PAGE_SIZE;
+#endif
+ if (buffer_spec[i].phy_addr) {
+ if (page_count !=
+ buffer_spec[i].alloc_count) {
+ pr_info("Delay release cma buf %d\n",
+ i);
+ codec_mm_free_for_dma(MEM_NAME,
+ buffer_spec[i].phy_addr);
+ buffer_spec[i].phy_addr = 0;
+ buffer_spec[i].alloc_count = 0;
+ } else
+ pr_info("Re-use CMA buffer %d\n", i);
+ }
+ if (!buffer_spec[i].phy_addr) {
+ if (!codec_mm_enough_for_size(
+ page_count * PAGE_SIZE, 1)) {
+ buffer_spec[i].alloc_count = 0;
+ fatal_error_flag =
+ DECODER_FATAL_ERROR_NO_MEM;
+ vh264_running = 0;
+ mutex_unlock(&vh264_mutex);
+ pr_err("CMA not enough mem! %d\n",
+ i);
+ return;
+ }
+ buffer_spec[i].alloc_count = page_count;
+ buffer_spec[i].phy_addr =
+ codec_mm_alloc_for_dma(MEM_NAME,
+ buffer_spec[i].alloc_count,
+ 4 + PAGE_SHIFT,
+ CODEC_MM_FLAGS_CMA_CLEAR |
+ CODEC_MM_FLAGS_FOR_VDECODER);
+ pr_info("CMA malloc ok %d\n", i);
+ }
+ alloc_count++;
+ if (!buffer_spec[i].phy_addr) {
+ buffer_spec[i].alloc_count = 0;
+ pr_err("264-4k mem alloc failed %d\n",
+ i);
+ vh264_running = 0;
+ mutex_unlock(&vh264_mutex);
+ return;
+ }
+ addr = buffer_spec[i].phy_addr;
+ } else {
+ if (buffer_spec[i].phy_addr) {
+ codec_mm_free_for_dma(MEM_NAME,
+ buffer_spec[i].phy_addr);
+ buffer_spec[i].phy_addr = 0;
+ buffer_spec[i].alloc_count = 0;
+ }
+ }
+ /*4k keep last frame */
+ if (is_4k && ((addr + 7) >> 3) == disp_addr)
+ addr = start_addr;
+ if (i <= 21) {
+ buffer_spec[i].y_addr = addr;
+ addr += mb_total << 8;
+ buffer_spec[i].u_addr = addr;
+ buffer_spec[i].v_addr = addr;
+ addr += mb_total << 7;
+ vfbuf_use[i] = 0;
+
+ buffer_spec[i].y_canvas_index = 128 + i * 2;
+ buffer_spec[i].u_canvas_index = 128 + i * 2 + 1;
+ buffer_spec[i].v_canvas_index = 128 + i * 2 + 1;
+
+ buffer_spec[i].y_canvas_width = mb_width << 4;
+ buffer_spec[i].y_canvas_height = mb_height << 4;
+ buffer_spec[i].u_canvas_width = mb_width << 4;
+ buffer_spec[i].u_canvas_height = mb_height << 4;
+ buffer_spec[i].v_canvas_width = mb_width << 4;
+ buffer_spec[i].v_canvas_height = mb_height << 4;
+
+ canvas_config(128 + i * 2,
+ buffer_spec[i].y_addr,
+ mb_width << 4, mb_height << 4,
+ CANVAS_ADDR_NOWRAP,
+ CANVAS_BLKMODE_32X32);
+ canvas_config(128 + i * 2 + 1,
+ buffer_spec[i].u_addr,
+ mb_width << 4, mb_height << 3,
+ CANVAS_ADDR_NOWRAP,
+ CANVAS_BLKMODE_32X32);
+ WRITE_VREG(ANC0_CANVAS_ADDR + i,
+ spec2canvas(&buffer_spec[i]));
+ } else {
+ buffer_spec[i].y_canvas_index =
+ 2 * (i - 21) + 4;
+ buffer_spec[i].y_addr = addr;
+ addr += mb_total << 8;
+ buffer_spec[i].u_canvas_index =
+ 2 * (i - 21) + 5;
+ buffer_spec[i].v_canvas_index =
+ 2 * (i - 21) + 5;
+ buffer_spec[i].u_addr = addr;
+ addr += mb_total << 7;
+ vfbuf_use[i] = 0;
+
+ buffer_spec[i].y_canvas_width = mb_width << 4;
+ buffer_spec[i].y_canvas_height = mb_height << 4;
+ buffer_spec[i].u_canvas_width = mb_width << 4;
+ buffer_spec[i].u_canvas_height = mb_height << 4;
+ buffer_spec[i].v_canvas_width = mb_width << 4;
+ buffer_spec[i].v_canvas_height = mb_height << 4;
+
+ spec_set_canvas(&buffer_spec[i]
+ , mb_width << 4, mb_height << 4);
+ WRITE_VREG(ANC0_CANVAS_ADDR + i
+ , spec2canvas(&buffer_spec[i]));
+ }
+ }
+ } else
+ addr = buf_start + mb_total * 384 * actual_dpb_size;
+
+ timing_info_present_flag = seq_info & 0x2;
+ fixed_frame_rate_flag = 0;
+ aspect_ratio_info_present_flag = seq_info & 0x1;
+ aspect_ratio_idc = (seq_info >> 16) & 0xff;
+
+ if (timing_info_present_flag) {
+ fixed_frame_rate_flag = seq_info & 0x40;
+
+ if (((num_units_in_tick * 120) >= time_scale
+ && ((!sync_outside) || (!frame_dur))) &&
+ num_units_in_tick
+ && time_scale) {
+ if (use_idr_framerate || !frame_dur
+ || !duration_from_pts_done || vh264_running) {
+ u32 frame_dur_es =
+ div_u64(96000ULL * 2 *
+ num_units_in_tick,
+ time_scale);
+
+ /* hack to avoid use ES frame duration
+ * when it's half of the rate from
+ * system info
+ */
+ /* sometimes the encoder is given a wrong
+ * frame rate but the system side information
+ *is more reliable
+ */
+ if ((frame_dur * 2) != frame_dur_es)
+ frame_dur = frame_dur_es;
+ }
+ }
+ } else
+ pr_info("H.264: timing_info not present\n");
+
+ if (aspect_ratio_info_present_flag) {
+ if (aspect_ratio_idc == EXTEND_SAR) {
+ h264_ar =
+ div_u64(256ULL * (aspect_ratio_info >> 16) *
+ frame_height,
+ (aspect_ratio_info & 0xffff) *
+ frame_width);
+ } else {
+ /* pr_info("v264dec: aspect_ratio_idc = %d\n",
+ * aspect_ratio_idc);
+ */
+
+ switch (aspect_ratio_idc) {
+ case 1:
+ h264_ar = 0x100 * frame_height / frame_width;
+ break;
+ case 2:
+ h264_ar = 0x100 * frame_height * 11 /
+ (frame_width * 12);
+ break;
+ case 3:
+ h264_ar = 0x100 * frame_height * 11 /
+ (frame_width * 10);
+ break;
+ case 4:
+ h264_ar = 0x100 * frame_height * 11 /
+ (frame_width * 16);
+ break;
+ case 5:
+ h264_ar = 0x100 * frame_height * 33 /
+ (frame_width * 40);
+ break;
+ case 6:
+ h264_ar = 0x100 * frame_height * 11 /
+ (frame_width * 24);
+ break;
+ case 7:
+ h264_ar = 0x100 * frame_height * 11 /
+ (frame_width * 20);
+ break;
+ case 8:
+ h264_ar = 0x100 * frame_height * 11 /
+ (frame_width * 32);
+ break;
+ case 9:
+ h264_ar = 0x100 * frame_height * 33 /
+ (frame_width * 80);
+ break;
+ case 10:
+ h264_ar = 0x100 * frame_height * 11 /
+ (frame_width * 18);
+ break;
+ case 11:
+ h264_ar = 0x100 * frame_height * 11 /
+ (frame_width * 15);
+ break;
+ case 12:
+ h264_ar = 0x100 * frame_height * 33 /
+ (frame_width * 64);
+ break;
+ case 13:
+ h264_ar = 0x100 * frame_height * 99 /
+ (frame_width * 160);
+ break;
+ case 14:
+ h264_ar = 0x100 * frame_height * 3 /
+ (frame_width * 4);
+ break;
+ case 15:
+ h264_ar = 0x100 * frame_height * 2 /
+ (frame_width * 3);
+ break;
+ case 16:
+ h264_ar = 0x100 * frame_height * 1 /
+ (frame_width * 2);
+ break;
+ default:
+ if (vh264_ratio >> 16) {
+ h264_ar = (frame_height *
+ (vh264_ratio & 0xffff) *
+ 0x100 +
+ ((vh264_ratio >> 16) *
+ frame_width / 2)) /
+ ((vh264_ratio >> 16) *
+ frame_width);
+ } else {
+ h264_ar = frame_height * 0x100 /
+ frame_width;
+ }
+ break;
+ }
+ }
+ } else {
+ pr_info("v264dec: aspect_ratio not available from source\n");
+ if (vh264_ratio >> 16) {
+ /* high 16 bit is width, low 16 bit is height */
+ h264_ar =
+ ((vh264_ratio & 0xffff) * frame_height * 0x100 +
+ (vh264_ratio >> 16) * frame_width / 2) /
+ ((vh264_ratio >> 16) * frame_width);
+ } else
+ h264_ar = frame_height * 0x100 / frame_width;
+ }
+
+ WRITE_VREG(AV_SCRATCH_0,
+ (max_reference_size << 24) | (actual_dpb_size << 16) |
+ (max_dpb_size << 8));
+ if (vh264_stream_switching_state != SWITCHING_STATE_OFF) {
+ vh264_stream_switching_state = SWITCHING_STATE_OFF;
+ pr_info("Leaving switching mode.\n");
+ }
+ mutex_unlock(&vh264_mutex);
+}
+
+static unsigned pts_inc_by_duration(unsigned *new_pts, unsigned *new_pts_rem)
+{
+ unsigned r, rem;
+
+ r = last_pts + DUR2PTS(frame_dur);
+ rem = last_pts_remainder + DUR2PTS_REM(frame_dur);
+
+ if (rem >= 96) {
+ r++;
+ rem -= 96;
+ }
+
+ if (new_pts)
+ *new_pts = r;
+ if (new_pts_rem)
+ *new_pts_rem = rem;
+
+ return r;
+}
+static inline bool vh264_isr_parser(struct vframe_s *vf,
+ unsigned int pts_valid, unsigned int buffer_index,
+ unsigned int pts)
+{
+ unsigned int pts_duration = 0;
+
+ if (h264_first_pts_ready == 0) {
+ if (pts_valid == 0) {
+ vfbuf_use[buffer_index]++;
+ vf->index = buffer_index;
+ kfifo_put(&recycle_q,
+ (const struct vframe_s *)vf);
+ return false;
+ }
+
+ h264pts1 = pts;
+ h264_pts_count = 0;
+ h264_first_pts_ready = 1;
+ } else {
+
+ if (pts_valid && (pts > h264pts1) && (h264_pts_count > 24)
+ && (duration_from_pts_done == 0)) {
+ unsigned int
+ old_duration = frame_dur;
+ h264pts2 = pts;
+
+ pts_duration = (h264pts2 - h264pts1) * 16 /
+ (h264_pts_count * 15);
+
+ if ((pts_duration != frame_dur)
+ && (!pts_outside)) {
+ if (use_idr_framerate) {
+ bool pts_c_24 = close_to(pts_duration,
+ RATE_24_FPS,
+ RATE_CORRECTION_THRESHOLD);
+ bool frm_c_25 = close_to(frame_dur,
+ RATE_25_FPS,
+ RATE_CORRECTION_THRESHOLD);
+ bool pts_c_25 = close_to(pts_duration,
+ RATE_25_FPS,
+ RATE_CORRECTION_THRESHOLD);
+ bool frm_c_24 = close_to(frame_dur,
+ RATE_24_FPS,
+ RATE_CORRECTION_THRESHOLD);
+ if ((pts_c_24 && frm_c_25)
+ || (pts_c_25 && frm_c_24)) {
+ pr_info
+ ("H.264:Correct frame dur ");
+ pr_info
+ (" from %d to duration based ",
+ frame_dur);
+ pr_info
+ ("on PTS %d ---\n",
+ pts_duration);
+ frame_dur = pts_duration;
+ duration_from_pts_done = 1;
+ } else if (((frame_dur < 96000 / 240)
+ && (pts_duration > 96000 / 240))
+ || (!duration_on_correcting &&
+ !frm_c_25 && !frm_c_24)) {
+ /* fft: if the frame rate is
+ * not regular, use the
+ * calculate rate insteadof.
+ */
+ pr_info
+ ("H.264:Correct frame dur ");
+ pr_info
+ (" from %d to duration based ",
+ frame_dur);
+ pr_info
+ ("on PTS %d ---\n",
+ pts_duration);
+ frame_dur = pts_duration;
+ duration_on_correcting = 1;
+ }
+ } else {
+ if (close_to(pts_duration,
+ frame_dur, 2000)) {
+ frame_dur = pts_duration;
+ pr_info
+ ("used calculate frame rate,");
+ pr_info("on duration =%d\n",
+ frame_dur);
+ } else {
+ pr_info
+ ("don't use calculate frame ");
+ pr_info
+ ("rate pts_duration =%d\n",
+ pts_duration);
+ }
+ }
+ }
+
+ if (duration_from_pts_done == 0) {
+ if (close_to
+ (pts_duration,
+ old_duration,
+ RATE_CORRECTION_THRESHOLD)) {
+ pr_info
+ ("finished correct frame dur");
+ pr_info
+ (" new=%d,old_duration=%d,cnt=%d\n",
+ pts_duration,
+ old_duration,
+ h264_pts_count);
+ duration_from_pts_done = 1;
+ } else { /*not the same,redo it. */
+ if (!close_to(pts_duration,
+ old_duration, 1000) &&
+ !close_to(pts_duration,
+ frame_dur, 1000) &&
+ close_to(pts_duration,
+ last_duration, 200)) {
+ /* yangle: frame_dur must
+ * wrong,recover it.
+ */
+ frame_dur = pts_duration;
+ }
+
+ pr_info
+ ("restart correct frame duration ");
+ pr_info
+ ("new=%d,old_duration=%d,cnt=%d\n",
+ pts_duration,
+ old_duration,
+ h264_pts_count);
+ h264pts1 = h264pts2;
+ h264_pts_count = 0;
+ duration_from_pts_done = 0;
+ }
+ }
+ last_duration = pts_duration;
+ }
+ }
+ return true;
+}
+#ifdef HANDLE_H264_IRQ
+static irqreturn_t vh264_isr(int irq, void *dev_id)
+#else
+static void vh264_isr(void)
+#endif
+{
+ unsigned int buffer_index;
+ struct vframe_s *vf;
+ unsigned int cpu_cmd;
+ unsigned int pts, pts_lookup_save, pts_valid_save, pts_valid = 0;
+ unsigned int pts_us64_valid = 0;
+ u64 pts_us64;
+ bool force_interlaced_frame = false;
+ unsigned int sei_itu35_flags;
+ static const unsigned int idr_num =
+ FIX_FRAME_RATE_CHECK_IDRFRAME_NUM;
+ static const unsigned int flg_1080_itl =
+ DEC_CONTROL_FLAG_FORCE_2997_1080P_INTERLACE;
+ static const unsigned int flg_576_itl =
+ DEC_CONTROL_FLAG_FORCE_2500_576P_INTERLACE;
+
+ WRITE_VREG(ASSIST_MBOX1_CLR_REG, 1);
+
+ if (0 == (stat & STAT_VDEC_RUN)) {
+ pr_info("decoder is not running\n");
+#ifdef HANDLE_H264_IRQ
+ return IRQ_HANDLED;
+#else
+ return;
+#endif
+ }
+
+ cpu_cmd = READ_VREG(AV_SCRATCH_0);
+
+#ifdef DROP_B_FRAME_FOR_1080P_50_60FPS
+ if ((frame_dur < 2004) &&
+ (frame_width >= 1400) &&
+ (frame_height >= 1000) && (last_interlaced == 0))
+ SET_VREG_MASK(AV_SCRATCH_F, 0x8);
+#endif
+ if ((decoder_force_reset == 1)
+ || ((error_recovery_mode != 1)
+ && (no_idr_error_count >= no_idr_error_max)
+ && (ucode_type != UCODE_IP_ONLY_PARAM))) {
+ vh264_running = 0;
+ pr_info("force reset decoder %d!!!\n", no_idr_error_count);
+ schedule_work(&error_wd_work);
+ decoder_force_reset = 0;
+ no_idr_error_count = 0;
+ } else if ((cpu_cmd & 0xff) == 1) {
+ if (unlikely
+ (vh264_running
+ && (kfifo_len(&newframe_q) != VF_POOL_SIZE))) {
+ /* a cmd 1 sent during decoding w/o getting a cmd 3. */
+ /* should not happen but the original code has such
+ * case, do the same process
+ */
+ if ((READ_VREG(AV_SCRATCH_1) & 0xff)
+ == 1) {/*invalid mb_width*/
+ vh264_running = 0;
+ fatal_error_flag = DECODER_FATAL_ERROR_UNKNOWN;
+ /* this is fatal error, need restart */
+ pr_info("cmd 1 fatal error happened\n");
+ schedule_work(&error_wd_work);
+ } else {
+ vh264_stream_switching_state = SWITCHING_STATE_ON_CMD1;
+ pr_info("Enter switching mode cmd1.\n");
+ schedule_work(&stream_switching_work);
+ }
+ return IRQ_HANDLED;
+ }
+ pr_info("Enter set parameter cmd1.\n");
+ schedule_work(&set_parameter_work);
+ return IRQ_HANDLED;
+ } else if ((cpu_cmd & 0xff) == 2) {
+ int frame_mb_only, pic_struct_present, pic_struct, prog_frame,
+ poc_sel, idr_flag, eos, error;
+ int i, status, num_frame, b_offset;
+ int current_error_count, slice_type;
+
+ vh264_running = 1;
+ vh264_no_disp_count = 0;
+ num_frame = (cpu_cmd >> 8) & 0xff;
+ frame_mb_only = seq_info & 0x8000;
+ pic_struct_present = seq_info & 0x10;
+
+ current_error_count = READ_VREG(AV_SCRATCH_D);
+ if (vh264_error_count != current_error_count) {
+ /* pr_info("decoder error happened, count %d\n",
+ * current_error_count);
+ */
+ vh264_error_count = current_error_count;
+ }
+
+ for (i = 0; (i < num_frame) && (!vh264_eos); i++) {
+ status = READ_VREG(AV_SCRATCH_1 + i);
+ buffer_index = status & 0x1f;
+ error = status & 0x200;
+ slice_type = (READ_VREG(AV_SCRATCH_H) >> (i * 4)) & 0xf;
+
+ if ((error_recovery_mode_use & 2) && error)
+ check_pts_discontinue = true;
+ if (ucode_type == UCODE_IP_ONLY_PARAM
+ && iponly_early_mode)
+ continue;
+ if ((p_last_vf != NULL)
+ && (p_last_vf->index == buffer_index))
+ continue;
+
+ if (buffer_index >= VF_BUF_NUM)
+ continue;
+
+ pic_struct = (status >> 5) & 0x7;
+ prog_frame = status & 0x100;
+ poc_sel = status & 0x200;
+ idr_flag = status & 0x400;
+ frame_packing_type = (status >> 12) & 0x7;
+ eos = (status >> 15) & 1;
+
+ if (eos)
+ vh264_eos = 1;
+
+ b_offset = (status >> 16) & 0xffff;
+
+ if (error)
+ no_idr_error_count++;
+ if (idr_flag ||
+ (!error && (slice_type != SLICE_TYPE_I)))
+ no_idr_error_count = 0;
+
+ if (decoder_debug_flag) {
+ pr_info
+ ("slice_type %x idr %x error %x count %d",
+ slice_type, idr_flag, error,
+ no_idr_error_count);
+ pr_info(" prog %x pic_struct %x offset %x\n",
+ prog_frame, pic_struct, b_offset);
+ }
+#ifdef DROP_B_FRAME_FOR_1080P_50_60FPS
+ last_interlaced = prog_frame ? 0 : 1;
+#endif
+ if (kfifo_get(&newframe_q, &vf) == 0) {
+ pr_info
+ ("fatal error, no available buffer slot.");
+ return IRQ_HANDLED;
+ }
+
+ if (clk_adj_frame_count < VDEC_CLOCK_ADJUST_FRAME)
+ clk_adj_frame_count++;
+
+ set_frame_info(vf);
+
+ switch (i) {
+ case 0:
+ b_offset |=
+ (READ_VREG(AV_SCRATCH_A) & 0xffff)
+ << 16;
+ break;
+ case 1:
+ b_offset |=
+ READ_VREG(AV_SCRATCH_A) & 0xffff0000;
+ break;
+ case 2:
+ b_offset |=
+ (READ_VREG(AV_SCRATCH_B) & 0xffff)
+ << 16;
+ break;
+ case 3:
+ b_offset |=
+ READ_VREG(AV_SCRATCH_B) & 0xffff0000;
+ break;
+ case 4:
+ b_offset |=
+ (READ_VREG(AV_SCRATCH_C) & 0xffff)
+ << 16;
+ break;
+ case 5:
+ b_offset |=
+ READ_VREG(AV_SCRATCH_C) & 0xffff0000;
+ break;
+ default:
+ break;
+ }
+
+ /* add 64bit pts us ; */
+ if (unlikely
+ ((b_offset == first_offset)
+ && (first_pts_cached))) {
+ pts = first_pts;
+ pts_us64 = first_pts64;
+ first_pts_cached = false;
+ pts_valid = 1;
+ pts_us64_valid = 1;
+#ifdef DEBUG_PTS
+ pts_hit++;
+#endif
+ } else if (pts_lookup_offset_us64
+ (PTS_TYPE_VIDEO, b_offset, &pts, 0,
+ &pts_us64) == 0) {
+ pts_valid = 1;
+ pts_us64_valid = 1;
+#ifdef DEBUG_PTS
+ pts_hit++;
+#endif
+ } else {
+ pts_valid = 0;
+ pts_us64_valid = 0;
+#ifdef DEBUG_PTS
+ pts_missed++;
+#endif
+ }
+
+ /* on second IDR frame,check the diff between pts
+ * compute from duration and pts from lookup ,
+ * if large than frame_dur,we think it is uncorrect.
+ */
+ pts_lookup_save = pts;
+ pts_valid_save = pts_valid;
+ if (fixed_frame_rate_flag
+ && (fixed_frame_rate_check_count <=
+ idr_num)) {
+ if (idr_flag && pts_valid) {
+ fixed_frame_rate_check_count++;
+ /* pr_info("diff:%d\n",
+ * last_pts - pts_lookup_save);
+ */
+ if ((fixed_frame_rate_check_count ==
+ idr_num) &&
+ (abs(pts - (last_pts +
+ DUR2PTS(frame_dur))) >
+ DUR2PTS(frame_dur))) {
+ fixed_frame_rate_flag = 0;
+ pr_info("pts sync mode play\n");
+ }
+
+ if (fixed_frame_rate_flag
+ && (fixed_frame_rate_check_count
+ > idr_num)) {
+ pr_info
+ ("fix_frame_rate mode play\n");
+ }
+ }
+ }
+
+ if (READ_VREG(AV_SCRATCH_F) & 2) {
+ /* for I only mode, ignore the PTS information
+ * and only uses frame duration for each I
+ * frame decoded
+ */
+ if (p_last_vf)
+ pts_valid = 0;
+ /* also skip frame duration calculation
+ * based on PTS
+ */
+ duration_from_pts_done = 1;
+ /* and add a default duration for 1/30 second
+ * if there is no valid frame
+ * duration available
+ */
+ if (frame_dur == 0)
+ frame_dur = 96000 / 30;
+ }
+
+ if (sync_outside == 0) {
+ if (!vh264_isr_parser(vf,
+ pts_valid, buffer_index, pts))
+ continue;
+
+ h264_pts_count++;
+ } else {
+ if (!idr_flag)
+ pts_valid = 0;
+ }
+
+ if (pts_valid && !pts_discontinue) {
+ pts_discontinue =
+ (abs(last_pts - pts) >=
+ tsync_vpts_discontinuity_margin());
+ }
+ /* if use_idr_framerate or fixed frame rate, only
+ * use PTS for IDR frames except for pts discontinue
+ */
+ if (timing_info_present_flag &&
+ frame_dur &&
+ (use_idr_framerate ||
+ (fixed_frame_rate_flag != 0))
+ && pts_valid && h264_first_valid_pts_ready
+ && (!pts_discontinue)) {
+ pts_valid =
+ (slice_type == SLICE_TYPE_I) ? 1 : 0;
+ }
+
+ if (!h264_first_valid_pts_ready && pts_valid) {
+ h264_first_valid_pts_ready = true;
+ last_pts = pts - DUR2PTS(frame_dur);
+ last_pts_remainder = 0;
+ }
+ /* calculate PTS of next frame and smooth
+ * PTS for fixed rate source
+ */
+ if (pts_valid) {
+ if ((fixed_frame_rate_flag) &&
+ (!pts_discontinue) &&
+ (abs(pts_inc_by_duration(NULL, NULL)
+ - pts)
+ < DUR2PTS(frame_dur))) {
+ pts = pts_inc_by_duration(&pts,
+ &last_pts_remainder);
+ } else
+ last_pts_remainder = 0;
+
+ } else {
+ if (fixed_frame_rate_flag && !pts_discontinue &&
+ (fixed_frame_rate_check_count > idr_num) &&
+ pts_valid_save && (sync_outside == 0) &&
+ (abs(pts_inc_by_duration(NULL, NULL) - pts)
+ > DUR2PTS(frame_dur))) {
+ duration_from_pts_done = 0;
+ pr_info("recalc frame_dur\n");
+ } else
+ pts = pts_inc_by_duration(&pts,
+ &last_pts_remainder);
+ pts_valid = 1;
+ }
+
+ if ((dec_control &
+ flg_1080_itl)
+ && (frame_width == 1920)
+ && (frame_height >= 1080)
+ && (vf->duration == 3203))
+ force_interlaced_frame = true;
+ else if ((dec_control &
+ flg_576_itl)
+ && (frame_width == 720)
+ && (frame_height == 576)
+ && (vf->duration == 3840))
+ force_interlaced_frame = true;
+
+ /* for frames with PTS, check if there is PTS
+ * discontinue based on previous frames
+ * (including error frames),
+ * force no VPTS discontinue reporting if we saw
+ *errors earlier but only once.
+ */
+ if ((pts_valid) && (check_pts_discontinue)
+ && (!error)) {
+ if (pts_discontinue) {
+ vf->flag = 0;
+ check_pts_discontinue = false;
+ } else if ((pts - last_pts) < 90000) {
+ vf->flag = VFRAME_FLAG_NO_DISCONTINUE;
+ check_pts_discontinue = false;
+ }
+ }
+
+ last_pts = pts;
+
+ if (fixed_frame_rate_flag
+ && (fixed_frame_rate_check_count <=
+ idr_num)
+ && (sync_outside == 0)
+ && pts_valid_save)
+ pts = pts_lookup_save;
+
+ if (pic_struct_present) {
+ if ((pic_struct == PIC_TOP_BOT)
+ || (pic_struct == PIC_BOT_TOP))
+ prog_frame = 0;
+ }
+
+ if ((!force_interlaced_frame)
+ && (prog_frame
+ || (pic_struct_present
+ && pic_struct
+ <= PIC_TRIPLE_FRAME))) {
+ if (pic_struct_present) {
+ if (pic_struct == PIC_TOP_BOT_TOP
+ || pic_struct
+ == PIC_BOT_TOP_BOT) {
+ vf->duration +=
+ vf->duration >> 1;
+ } else if (pic_struct ==
+ PIC_DOUBLE_FRAME)
+ vf->duration += vf->duration;
+ else if (pic_struct ==
+ PIC_TRIPLE_FRAME) {
+ vf->duration +=
+ vf->duration << 1;
+ }
+ }
+
+ last_pts =
+ last_pts + DUR2PTS(vf->duration -
+ frame_dur);
+
+ vf->index = buffer_index;
+ vf->type =
+ VIDTYPE_PROGRESSIVE |
+ VIDTYPE_VIU_FIELD |
+ VIDTYPE_VIU_NV21;
+ vf->duration_pulldown = 0;
+ vf->signal_type = video_signal_from_vui;
+ vf->index = buffer_index;
+ vf->pts = (pts_valid) ? pts : 0;
+ if (pts_us64_valid == 1)
+ vf->pts_us64 = pts_us64;
+ else
+ vf->pts_us64 = div64_u64(((u64)vf->pts)*100, 9);
+ vf->canvas0Addr = vf->canvas1Addr =
+ spec2canvas(&buffer_spec[buffer_index]);
+ vf->type_original = vf->type;
+ vfbuf_use[buffer_index]++;
+
+ if ((error_recovery_mode_use & 2) && error) {
+ kfifo_put(&recycle_q,
+ (const struct vframe_s *)vf);
+ } else {
+ p_last_vf = vf;
+ pts_discontinue = false;
+ kfifo_put(&delay_display_q,
+ (const struct vframe_s *)vf);
+ }
+ } else {
+ if (pic_struct_present
+ && pic_struct == PIC_TOP_BOT)
+ vf->type = VIDTYPE_INTERLACE_TOP;
+ else if (pic_struct_present
+ && pic_struct == PIC_BOT_TOP)
+ vf->type = VIDTYPE_INTERLACE_BOTTOM;
+ else {
+ vf->type =
+ poc_sel ?
+ VIDTYPE_INTERLACE_BOTTOM :
+ VIDTYPE_INTERLACE_TOP;
+ }
+ vf->type |= VIDTYPE_VIU_NV21;
+ vf->type |= VIDTYPE_INTERLACE_FIRST;
+
+ vf->duration >>= 1;
+ vf->duration_pulldown = 0;
+ vf->signal_type = video_signal_from_vui;
+ vf->index = buffer_index;
+ vf->pts = (pts_valid) ? pts : 0;
+ if (pts_us64_valid == 1)
+ vf->pts_us64 = pts_us64;
+ else
+ vf->pts_us64 = div64_u64(((u64)vf->pts)*100, 9);
+ vf->canvas0Addr = vf->canvas1Addr =
+ spec2canvas(&buffer_spec[buffer_index]);
+ vf->type_original = vf->type;
+ vfbuf_use[buffer_index]++;
+ vf->ready_jiffies64 = jiffies_64;
+
+ if ((error_recovery_mode_use & 2) && error) {
+ kfifo_put(&recycle_q,
+ (const struct vframe_s *)vf);
+ continue;
+ } else {
+ pts_discontinue = false;
+ kfifo_put(&delay_display_q,
+ (const struct vframe_s *)vf);
+ }
+
+ if (READ_VREG(AV_SCRATCH_F) & 2)
+ continue;
+
+ if (kfifo_get(&newframe_q, &vf) == 0) {
+ pr_info
+ ("fatal error, no avail buffer slot.");
+ return IRQ_HANDLED;
+ }
+
+ set_frame_info(vf);
+
+ if (pic_struct_present
+ && pic_struct == PIC_TOP_BOT)
+ vf->type = VIDTYPE_INTERLACE_BOTTOM;
+ else if (pic_struct_present
+ && pic_struct == PIC_BOT_TOP)
+ vf->type = VIDTYPE_INTERLACE_TOP;
+ else {
+ vf->type =
+ poc_sel ?
+ VIDTYPE_INTERLACE_TOP :
+ VIDTYPE_INTERLACE_BOTTOM;
+ }
+
+ vf->type |= VIDTYPE_VIU_NV21;
+ vf->duration >>= 1;
+ vf->duration_pulldown = 0;
+ vf->signal_type = video_signal_from_vui;
+ vf->index = buffer_index;
+ vf->pts = 0;
+ vf->pts_us64 = 0;
+ vf->canvas0Addr = vf->canvas1Addr =
+ spec2canvas(&buffer_spec[buffer_index]);
+ vf->type_original = vf->type;
+ vfbuf_use[buffer_index]++;
+
+ p_last_vf = vf;
+ vf->ready_jiffies64 = jiffies_64;
+
+ kfifo_put(&delay_display_q,
+ (const struct vframe_s *)vf);
+ }
+ }
+
+ WRITE_VREG(AV_SCRATCH_0, 0);
+ } else if ((cpu_cmd & 0xff) == 3) {
+ vh264_running = 1;
+ vh264_stream_switching_state = SWITCHING_STATE_ON_CMD3;
+
+ pr_info("Enter switching mode cmd3.\n");
+ schedule_work(&stream_switching_work);
+
+ } else if ((cpu_cmd & 0xff) == 4) {
+ vh264_running = 1;
+ /* reserved for slice group */
+ WRITE_VREG(AV_SCRATCH_0, 0);
+ } else if ((cpu_cmd & 0xff) == 5) {
+ vh264_running = 1;
+ /* reserved for slice group */
+ WRITE_VREG(AV_SCRATCH_0, 0);
+ } else if ((cpu_cmd & 0xff) == 6) {
+ vh264_running = 0;
+ fatal_error_flag = DECODER_FATAL_ERROR_UNKNOWN;
+ /* this is fatal error, need restart */
+ pr_info("fatal error happened\n");
+ if (!fatal_error_reset)
+ schedule_work(&error_wd_work);
+ } else if ((cpu_cmd & 0xff) == 7) {
+ vh264_running = 0;
+ frame_width = (READ_VREG(AV_SCRATCH_1) + 1) * 16;
+ pr_info("Over decoder supported size, width = %d\n",
+ frame_width);
+ fatal_error_flag = DECODER_FATAL_ERROR_SIZE_OVERFLOW;
+ } else if ((cpu_cmd & 0xff) == 8) {
+ vh264_running = 0;
+ frame_height = (READ_VREG(AV_SCRATCH_1) + 1) * 16;
+ pr_info("Over decoder supported size, height = %d\n",
+ frame_height);
+ fatal_error_flag = DECODER_FATAL_ERROR_SIZE_OVERFLOW;
+ } else if ((cpu_cmd & 0xff) == 9) {
+ first_offset = READ_VREG(AV_SCRATCH_1);
+ if (pts_lookup_offset_us64
+ (PTS_TYPE_VIDEO, first_offset, &first_pts, 0,
+ &first_pts64) == 0)
+ first_pts_cached = true;
+ WRITE_VREG(AV_SCRATCH_0, 0);
+
+ } else if ((cpu_cmd & 0xff) == 0xa) {
+ int b_offset = READ_VREG(AV_SCRATCH_2);
+
+ buffer_index = READ_VREG(AV_SCRATCH_1);
+ /*pr_info("iponly output %d b_offset %x\n",
+ * buffer_index,b_offset);
+ */
+ if (kfifo_get(&newframe_q, &vf) == 0) {
+ WRITE_VREG(AV_SCRATCH_0, 0);
+ pr_info
+ ("fatal error, no available buffer slot.");
+ return IRQ_HANDLED;
+ }
+ if (pts_lookup_offset_us64 (PTS_TYPE_VIDEO, b_offset,
+ &pts, 0, &pts_us64) != 0)
+ vf->pts_us64 = vf->pts = 0;
+ else {
+ vf->pts_us64 = pts_us64;
+ vf->pts = pts;
+ }
+
+ set_frame_info(vf);
+ vf->type = VIDTYPE_PROGRESSIVE |
+ VIDTYPE_VIU_FIELD |
+ VIDTYPE_VIU_NV21;
+ vf->duration_pulldown = 0;
+ vf->signal_type = video_signal_from_vui;
+ vf->index = buffer_index;
+ vf->canvas0Addr = vf->canvas1Addr =
+ spec2canvas(&buffer_spec[buffer_index]);
+ vf->type_original = vf->type;
+ vfbuf_use[buffer_index]++;
+ p_last_vf = vf;
+ pts_discontinue = false;
+ iponly_early_mode = 1;
+ kfifo_put(&delay_display_q,
+ (const struct vframe_s *)vf);
+ WRITE_VREG(AV_SCRATCH_0, 0);
+ }
+
+ sei_itu35_flags = READ_VREG(AV_SCRATCH_J);
+ if (sei_itu35_flags & (1 << 15)) { /* data ready */
+ /* int ltemp; */
+ /* unsigned char *daddr; */
+ unsigned int sei_itu35_wp = (sei_itu35_flags >> 16) & 0xffff;
+ unsigned int sei_itu35_data_length = sei_itu35_flags & 0x7fff;
+ struct userdata_poc_info_t user_data_poc;
+
+#if 0
+ /* dump lmem for debug */
+ WRITE_VREG(0x301, 0x8000);
+ WRITE_VREG(0x31d, 0x2);
+ for (ltemp = 0; ltemp < 64; ltemp++) {
+ laddr = 0x20 + ltemp;
+ WRITE_VREG(0x31b, laddr);
+ pr_info("mem 0x%x data 0x%x\n", laddr,
+ READ_VREG(0x31c) & 0xffff);
+ }
+#endif
+#if 0
+ for (ltemp = 0; ltemp < sei_itu35_wp; ltemp++) {
+ daddr =
+ (unsigned char *)phys_to_virt(
+ sei_data_buffer_phys +
+ ltemp);
+ /* daddr = (unsigned char *)(sei_data_buffer +
+ * ltemp);
+ */
+ pr_info("0x%x\n", *daddr);
+ }
+#endif
+ /* pr_info("pocinfo 0x%x, top poc %d, wp 0x%x, length %d\n",
+ * READ_VREG(AV_SCRATCH_L), READ_VREG(AV_SCRATCH_M),
+ * sei_itu35_wp, sei_itu35_data_length);
+ */
+ user_data_poc.poc_info = READ_VREG(AV_SCRATCH_L);
+ user_data_poc.poc_number = READ_VREG(AV_SCRATCH_M);
+ set_userdata_poc(user_data_poc);
+ WRITE_VREG(AV_SCRATCH_J, 0);
+ wakeup_userdata_poll(sei_itu35_wp,
+ (unsigned long)sei_data_buffer,
+ USER_DATA_SIZE, sei_itu35_data_length);
+ }
+#ifdef HANDLE_H264_IRQ
+ return IRQ_HANDLED;
+#else
+ return;
+#endif
+}
+
+static void vh264_put_timer_func(unsigned long arg)
+{
+ struct timer_list *timer = (struct timer_list *)arg;
+ unsigned int wait_buffer_status;
+ unsigned int wait_i_pass_frames;
+ unsigned int reg_val;
+
+ enum receviver_start_e state = RECEIVER_INACTIVE;
+
+ if (vh264_reset) {
+ pr_info("operation forbidden in timer !\n");
+ goto exit;
+ }
+
+ prepare_display_q();
+
+ if (vf_get_receiver(PROVIDER_NAME)) {
+ state =
+ vf_notify_receiver(PROVIDER_NAME,
+ VFRAME_EVENT_PROVIDER_QUREY_STATE,
+ NULL);
+ if ((state == RECEIVER_STATE_NULL)
+ || (state == RECEIVER_STATE_NONE)) {
+ /* receiver has no event_cb or receiver's
+ * event_cb does not process this event
+ */
+ state = RECEIVER_INACTIVE;
+ }
+ } else
+ state = RECEIVER_INACTIVE;
+#ifndef HANDLE_H264_IRQ
+ vh264_isr();
+#endif
+
+ if (vh264_stream_switching_state != SWITCHING_STATE_OFF)
+ wait_buffer_counter = 0;
+ else {
+ reg_val = READ_VREG(AV_SCRATCH_9);
+ wait_buffer_status = reg_val & (1 << 31);
+ wait_i_pass_frames = reg_val & 0xff;
+ if (wait_buffer_status) {
+ if (kfifo_is_empty(&display_q) &&
+ kfifo_is_empty(&delay_display_q) &&
+ kfifo_is_empty(&recycle_q) &&
+ (state == RECEIVER_INACTIVE)) {
+ pr_info("$$$$decoder is waiting for buffer\n");
+ if (++wait_buffer_counter > 4) {
+ amvdec_stop();
+
+#ifdef CONFIG_POST_PROCESS_MANAGER
+ vh264_ppmgr_reset();
+#else
+ vf_light_unreg_provider(&vh264_vf_prov);
+ vh264_local_init();
+ vf_reg_provider(&vh264_vf_prov);
+#endif
+ vh264_prot_init();
+ amvdec_start();
+ }
+ } else
+ wait_buffer_counter = 0;
+ } else if (wait_i_pass_frames > 1000) {
+ pr_info("i passed frames > 1000\n");
+ amvdec_stop();
+#ifdef CONFIG_POST_PROCESS_MANAGER
+ vh264_ppmgr_reset();
+#else
+ vf_light_unreg_provider(&vh264_vf_prov);
+ vh264_local_init();
+ vf_reg_provider(&vh264_vf_prov);
+#endif
+ vh264_prot_init();
+ amvdec_start();
+ }
+ }
+
+#if 0
+ if (!wait_buffer_status) {
+ if (vh264_no_disp_count++ > NO_DISP_WD_COUNT) {
+ pr_info("$$$decoder did not send frame out\n");
+ amvdec_stop();
+#ifdef CONFIG_POST_PROCESS_MANAGER
+ vh264_ppmgr_reset();
+#else
+ vf_light_unreg_provider(PROVIDER_NAME);
+ vh264_local_init();
+ vf_reg_provider(vh264_vf_prov);
+#endif
+ vh264_prot_init();
+ amvdec_start();
+
+ vh264_no_disp_count = 0;
+ vh264_no_disp_wd_count++;
+ }
+ }
+#endif
+
+ while (!kfifo_is_empty(&recycle_q) &&
+ ((READ_VREG(AV_SCRATCH_7) == 0)
+ || (READ_VREG(AV_SCRATCH_8) == 0))
+ && (vh264_stream_switching_state == SWITCHING_STATE_OFF)) {
+ struct vframe_s *vf;
+
+ if (kfifo_get(&recycle_q, &vf)) {
+ if ((vf->index >= 0) && (vf->index < VF_BUF_NUM)) {
+ if (--vfbuf_use[vf->index] == 0) {
+ if (READ_VREG(AV_SCRATCH_7) == 0) {
+ WRITE_VREG(AV_SCRATCH_7,
+ vf->index + 1);
+ } else {
+ WRITE_VREG(AV_SCRATCH_8,
+ vf->index + 1);
+ }
+ }
+
+ vf->index = VF_BUF_NUM;
+ kfifo_put(&newframe_q,
+ (const struct vframe_s *)vf);
+ }
+ }
+ }
+
+ if (vh264_stream_switching_state != SWITCHING_STATE_OFF) {
+ while (!kfifo_is_empty(&recycle_q)) {
+ struct vframe_s *vf;
+
+ if (kfifo_get(&recycle_q, &vf)) {
+ if ((vf->index >= 0 &&
+ (vf->index < VF_BUF_NUM))) {
+ vf->index = VF_BUF_NUM;
+ kfifo_put(&newframe_q,
+ (const struct vframe_s *)vf);
+ }
+ }
+ }
+
+ WRITE_VREG(AV_SCRATCH_7, 0);
+ WRITE_VREG(AV_SCRATCH_8, 0);
+
+ if (kfifo_len(&newframe_q) == VF_POOL_SIZE)
+ stream_switching_done();
+ }
+
+ if (ucode_type != UCODE_IP_ONLY_PARAM &&
+ (clk_adj_frame_count > VDEC_CLOCK_ADJUST_FRAME) &&
+ frame_dur > 0 && saved_resolution !=
+ frame_width * frame_height * (96000 / frame_dur)) {
+ int fps = 96000 / frame_dur;
+
+ if (frame_dur < 10) /*dur is too small ,think it errors fps*/
+ fps = 60;
+ saved_resolution = frame_width * frame_height * fps;
+ vdec_source_changed(VFORMAT_H264,
+ frame_width, frame_height, fps);
+ }
+exit:
+ timer->expires = jiffies + PUT_INTERVAL;
+
+ add_timer(timer);
+}
+
+int vh264_dec_status(struct vdec_s *vdec, struct vdec_status *vstatus)
+{
+ vstatus->width = frame_width;
+ vstatus->height = frame_height;
+ if (frame_dur != 0)
+ vstatus->fps = 96000 / frame_dur;
+ else
+ vstatus->fps = -1;
+ vstatus->error_count = READ_VREG(AV_SCRATCH_D);
+ vstatus->status = stat;
+ if (fatal_error_reset)
+ vstatus->status |= fatal_error_flag;
+ return 0;
+}
+
+int vh264_set_trickmode(struct vdec_s *vdec, unsigned long trickmode)
+{
+ if (trickmode == TRICKMODE_I) {
+ WRITE_VREG(AV_SCRATCH_F,
+ (READ_VREG(AV_SCRATCH_F) & 0xfffffffc) | 2);
+ trickmode_i = 1;
+ } else if (trickmode == TRICKMODE_NONE) {
+ WRITE_VREG(AV_SCRATCH_F, READ_VREG(AV_SCRATCH_F) & 0xfffffffc);
+ trickmode_i = 0;
+ }
+
+ return 0;
+}
+
+static void vh264_prot_init(void)
+{
+
+ while (READ_VREG(DCAC_DMA_CTRL) & 0x8000)
+ ;
+ while (READ_VREG(LMEM_DMA_CTRL) & 0x8000)
+ ; /* reg address is 0x350 */
+
+#if 1 /* MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON6 */
+ WRITE_VREG(DOS_SW_RESET0, (1 << 7) | (1 << 6) | (1 << 4));
+ WRITE_VREG(DOS_SW_RESET0, 0);
+
+ READ_VREG(DOS_SW_RESET0);
+ READ_VREG(DOS_SW_RESET0);
+ READ_VREG(DOS_SW_RESET0);
+
+ WRITE_VREG(DOS_SW_RESET0, (1 << 7) | (1 << 6) | (1 << 4));
+ WRITE_VREG(DOS_SW_RESET0, 0);
+
+ WRITE_VREG(DOS_SW_RESET0, (1 << 9) | (1 << 8));
+ WRITE_VREG(DOS_SW_RESET0, 0);
+
+ READ_VREG(DOS_SW_RESET0);
+ READ_VREG(DOS_SW_RESET0);
+ READ_VREG(DOS_SW_RESET0);
+
+#else
+ WRITE_MPEG_REG(RESET0_REGISTER,
+ RESET_IQIDCT | RESET_MC | RESET_VLD_PART);
+ READ_MPEG_REG(RESET0_REGISTER);
+ WRITE_MPEG_REG(RESET0_REGISTER,
+ RESET_IQIDCT | RESET_MC | RESET_VLD_PART);
+
+ WRITE_MPEG_REG(RESET2_REGISTER, RESET_PIC_DC | RESET_DBLK);
+#endif
+
+ WRITE_VREG(POWER_CTL_VLD,
+ READ_VREG(POWER_CTL_VLD) |
+ (0 << 10) | (1 << 9) | (1 << 6));
+
+ /* disable PSCALE for hardware sharing */
+ WRITE_VREG(PSCALE_CTRL, 0);
+
+ WRITE_VREG(AV_SCRATCH_0, 0);
+ WRITE_VREG(AV_SCRATCH_1, buf_offset);
+ WRITE_VREG(AV_SCRATCH_G, mc_dma_handle);
+ WRITE_VREG(AV_SCRATCH_7, 0);
+ WRITE_VREG(AV_SCRATCH_8, 0);
+ WRITE_VREG(AV_SCRATCH_9, 0);
+
+ error_recovery_mode_use =
+ (error_recovery_mode !=
+ 0) ? error_recovery_mode : error_recovery_mode_in;
+ WRITE_VREG(AV_SCRATCH_F,
+ (READ_VREG(AV_SCRATCH_F) & 0xffffffc3) |
+ (READ_VREG(AV_SCRATCH_F) & 0xffffff43) |
+ ((error_recovery_mode_use & 0x1) << 4));
+ if (dec_control & DEC_CONTROL_FLAG_DISABLE_FAST_POC)
+ SET_VREG_MASK(AV_SCRATCH_F, 1 << 7);
+ /* clear mailbox interrupt */
+ WRITE_VREG(ASSIST_MBOX1_CLR_REG, 1);
+
+ /* enable mailbox interrupt */
+ WRITE_VREG(ASSIST_MBOX1_MASK, 1);
+
+ SET_VREG_MASK(MDEC_PIC_DC_CTRL, 1 << 17);
+ if (ucode_type == UCODE_IP_ONLY_PARAM)
+ SET_VREG_MASK(AV_SCRATCH_F, 1 << 6);
+ else
+ CLEAR_VREG_MASK(AV_SCRATCH_F, 1 << 6);
+
+ WRITE_VREG(AV_SCRATCH_I, (u32)(sei_data_buffer_phys - buf_offset));
+ WRITE_VREG(AV_SCRATCH_J, 0);
+ /* #if MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON8 */
+ if ((get_cpu_type() >= MESON_CPU_MAJOR_ID_M8) && !is_meson_mtvd_cpu()) {
+ /* pr_info("vh264 meson8 prot init\n"); */
+ WRITE_VREG(MDEC_PIC_DC_THRESH, 0x404038aa);
+ }
+ /* #endif */
+}
+
+static void vh264_local_init(void)
+{
+ int i;
+
+ vh264_ratio = vh264_amstream_dec_info.ratio;
+ /* vh264_ratio = 0x100; */
+
+ vh264_rotation = (((unsigned long) vh264_amstream_dec_info.param)
+ >> 16) & 0xffff;
+
+ frame_buffer_size = AVIL_DPB_BUFF_SIZE + buf_size - DEFAULT_MEM_SIZE;
+ frame_prog = 0;
+ frame_width = vh264_amstream_dec_info.width;
+ frame_height = vh264_amstream_dec_info.height;
+ frame_dur = vh264_amstream_dec_info.rate;
+ pts_outside = ((unsigned long) vh264_amstream_dec_info.param) & 0x01;
+ sync_outside = ((unsigned long) vh264_amstream_dec_info.param & 0x02)
+ >> 1;
+ use_idr_framerate = ((unsigned long) vh264_amstream_dec_info.param
+ & 0x04) >> 2;
+ max_refer_buf = !(((unsigned long) vh264_amstream_dec_info.param
+ & 0x10) >> 4);
+
+ pr_info
+ ("H264 sysinfo: %dx%d duration=%d, pts_outside=%d, ",
+ frame_width, frame_height, frame_dur, pts_outside);
+ pr_debug("sync_outside=%d, use_idr_framerate=%d\n",
+ sync_outside, use_idr_framerate);
+
+ if ((unsigned long) vh264_amstream_dec_info.param & 0x08)
+ ucode_type = UCODE_IP_ONLY_PARAM;
+ else
+ ucode_type = 0;
+
+ if ((unsigned long) vh264_amstream_dec_info.param & 0x20)
+ error_recovery_mode_in = 1;
+ else
+ error_recovery_mode_in = 3;
+
+ if (!vh264_running) {
+ last_mb_width = 0;
+ last_mb_height = 0;
+ }
+
+ for (i = 0; i < VF_BUF_NUM; i++)
+ vfbuf_use[i] = 0;
+
+ INIT_KFIFO(display_q);
+ INIT_KFIFO(delay_display_q);
+ INIT_KFIFO(recycle_q);
+ INIT_KFIFO(newframe_q);
+
+ for (i = 0; i < VF_POOL_SIZE; i++) {
+ const struct vframe_s *vf = &vfpool[i];
+
+ vfpool[i].index = VF_BUF_NUM;
+ vfpool[i].bufWidth = 1920;
+ kfifo_put(&newframe_q, vf);
+ }
+
+#ifdef DROP_B_FRAME_FOR_1080P_50_60FPS
+ last_interlaced = 1;
+#endif
+ h264_first_pts_ready = 0;
+ h264_first_valid_pts_ready = false;
+ h264pts1 = 0;
+ h264pts2 = 0;
+ h264_pts_count = 0;
+ duration_from_pts_done = 0;
+ vh264_error_count = READ_VREG(AV_SCRATCH_D);
+
+ p_last_vf = NULL;
+ check_pts_discontinue = false;
+ last_pts = 0;
+ wait_buffer_counter = 0;
+ vh264_no_disp_count = 0;
+ fatal_error_flag = 0;
+ vh264_stream_switching_state = SWITCHING_STATE_OFF;
+#ifdef DEBUG_PTS
+ pts_missed = 0;
+ pts_hit = 0;
+#endif
+ pts_discontinue = false;
+ no_idr_error_count = 0;
+}
+
+static s32 vh264_init(void)
+{
+ int trickmode_fffb = 0;
+ int firmwareloaded = 0;
+ int i;
+
+ /* pr_info("\nvh264_init\n"); */
+ init_timer(&recycle_timer);
+
+ stat |= STAT_TIMER_INIT;
+
+ vh264_running = 0;/* init here to reset last_mb_width&last_mb_height */
+ vh264_eos = 0;
+ duration_on_correcting = 0;
+ first_pts = 0;
+ first_pts64 = 0;
+ first_offset = 0;
+ first_pts_cached = false;
+ fixed_frame_rate_check_count = 0;
+ saved_resolution = 0;
+ iponly_early_mode = 0;
+ vh264_local_init();
+
+ query_video_status(0, &trickmode_fffb);
+
+#if 0
+ if (!trickmode_fffb) {
+ void __iomem *p =
+ ioremap_nocache(ucode_map_start, V_BUF_ADDR_OFFSET);
+ if (p != NULL) {
+ memset(p, 0, V_BUF_ADDR_OFFSET);
+ iounmap(p);
+ }
+ }
+#endif
+
+ amvdec_enable();
+
+ /* -- ucode loading (amrisc and swap code) */
+ mc_cpu_addr =
+ dma_alloc_coherent(amports_get_dma_device(), MC_TOTAL_SIZE,
+ &mc_dma_handle, GFP_KERNEL);
+ if (!mc_cpu_addr) {
+ amvdec_disable();
+
+ pr_err("vh264_init: Can not allocate mc memory.\n");
+ return -ENOMEM;
+ }
+
+ pr_debug("264 ucode swap area: phyaddr %p, cpu vaddr %p\n",
+ (void *)mc_dma_handle, mc_cpu_addr);
+ if (debugfirmware) {
+ int r0, r1, r2, r3, r4, r5;
+ char firmwarename[32];
+
+ pr_debug("start load debug %d firmware ...\n", debugfirmware);
+
+ snprintf(firmwarename, 32, "%s%d", "vh264_mc", debugfirmware);
+ r0 = amvdec_loadmc_ex(VFORMAT_H264, firmwarename, NULL);
+
+#define DEBUGGET_FW(t, name, buf, size, ret)\
+ do {\
+ snprintf(firmwarename, 32, "%s%d", name,\
+ debugfirmware);\
+ ret = get_decoder_firmware_data(t,\
+ firmwarename, buf, size);\
+ } while (0)
+ /*memcpy((u8 *) mc_cpu_addr + MC_OFFSET_HEADER, vh264_header_mc,
+ *MC_SWAP_SIZE);
+ */
+ DEBUGGET_FW(VFORMAT_H264, "vh264_header_mc",
+ (u8 *) mc_cpu_addr + MC_OFFSET_HEADER,
+ MC_SWAP_SIZE, r1);
+
+ /*memcpy((u8 *) mc_cpu_addr + MC_OFFSET_DATA, vh264_data_mc,
+ *MC_SWAP_SIZE);
+ */
+ DEBUGGET_FW(VFORMAT_H264, "vh264_data_mc",
+ (u8 *) mc_cpu_addr + MC_OFFSET_DATA, MC_SWAP_SIZE, r2);
+ /*memcpy((u8 *) mc_cpu_addr + MC_OFFSET_MMCO, vh264_mmco_mc,
+ *MC_SWAP_SIZE);
+ */
+ DEBUGGET_FW(VFORMAT_H264, "vh264_mmco_mc",
+ (u8 *) mc_cpu_addr + MC_OFFSET_MMCO, MC_SWAP_SIZE, r3);
+ /*memcpy((u8 *) mc_cpu_addr + MC_OFFSET_LIST, vh264_list_mc,
+ *MC_SWAP_SIZE);
+ */
+ DEBUGGET_FW(VFORMAT_H264, "vh264_list_mc",
+ (u8 *) mc_cpu_addr + MC_OFFSET_LIST, MC_SWAP_SIZE, r4);
+ /*memcpy((u8 *) mc_cpu_addr + MC_OFFSET_SLICE, vh264_slice_mc,
+ *MC_SWAP_SIZE);
+ */
+ DEBUGGET_FW(VFORMAT_H264, "vh264_slice_mc",
+ (u8 *) mc_cpu_addr + MC_OFFSET_SLICE, MC_SWAP_SIZE, r5);
+
+ if (r0 < 0 || r1 < 0 || r2 < 0 || r3 < 0 || r4 < 0 || r5 < 0) {
+ pr_err("264 load debugfirmware err %d,%d,%d,%d,%d,%d\n",
+ r0, r1, r2, r3, r4, r5);
+ amvdec_disable();
+ if (mc_cpu_addr) {
+ dma_free_coherent(amports_get_dma_device(),
+ MC_TOTAL_SIZE, mc_cpu_addr,
+ mc_dma_handle);
+ mc_cpu_addr = NULL;
+ }
+ return -EBUSY;
+ }
+ firmwareloaded = 1;
+ } else {
+ int ret = -1, size = -1;
+ unsigned int cpu = get_cpu_type();
+ char *buf = vmalloc(0x1000 * 8);
+
+ switch (cpu) {
+ case MESON_CPU_MAJOR_ID_GXTVBB:
+ case MESON_CPU_MAJOR_ID_GXL:
+ size = get_firmware_data("gxl_h264_all", buf);
+ if (size < 0) {
+ pr_err("get firmware fail.");
+ vfree(buf);
+ return -1;
+ }
+
+ ret = amvdec_loadmc_ex(VFORMAT_H264,
+ "gxl_h264_all", buf);
+ memcpy((u8 *) mc_cpu_addr + MC_OFFSET_HEADER,
+ buf + 0x4000, MC_SWAP_SIZE);
+ memcpy((u8 *) mc_cpu_addr + MC_OFFSET_DATA,
+ buf + 0x2000, MC_SWAP_SIZE);
+ memcpy((u8 *) mc_cpu_addr + MC_OFFSET_MMCO,
+ buf + 0x6000, MC_SWAP_SIZE);
+ memcpy((u8 *) mc_cpu_addr + MC_OFFSET_LIST,
+ buf + 0x3000, MC_SWAP_SIZE);
+ memcpy((u8 *) mc_cpu_addr + MC_OFFSET_SLICE,
+ buf + 0x5000, MC_SWAP_SIZE);
+ break;
+
+ default:
+ pr_err("invalid cpu type 0x%x.\n", cpu);
+ vfree(buf);
+ return -1;
+ }
+
+ vfree(buf);
+
+ if (ret < 0) {
+ pr_err("h264 load orignal firmware error %d.\n", ret);
+ amvdec_disable();
+ if (mc_cpu_addr) {
+ dma_free_coherent(amports_get_dma_device(),
+ MC_TOTAL_SIZE, mc_cpu_addr,
+ mc_dma_handle);
+ mc_cpu_addr = NULL;
+ }
+ return -EBUSY;
+ }
+ }
+
+ stat |= STAT_MC_LOAD;
+
+ for (i = 0; i < ARRAY_SIZE(fense_buffer_spec); i++) {
+ struct buffer_spec_s *s = &fense_buffer_spec[i];
+
+ if (!codec_mm_enough_for_size(3 * SZ_1M, 1))
+ return -ENOMEM;
+
+ s->alloc_count = 3 * SZ_1M / PAGE_SIZE;
+ s->phy_addr = codec_mm_alloc_for_dma(MEM_NAME,
+ s->alloc_count,
+ 4 + PAGE_SHIFT,
+ CODEC_MM_FLAGS_CMA_CLEAR | CODEC_MM_FLAGS_FOR_VDECODER);
+ s->y_canvas_index = 2 * i;
+ s->u_canvas_index = 2 * i + 1;
+ s->v_canvas_index = 2 * i + 1;
+ }
+
+ /* enable AMRISC side protocol */
+ vh264_prot_init();
+
+#ifdef HANDLE_H264_IRQ
+ /*TODO irq */
+
+ if (vdec_request_irq(VDEC_IRQ_1, vh264_isr,
+ "vh264-irq", (void *)vh264_dec_id)) {
+ pr_err("vh264 irq register error.\n");
+ amvdec_disable();
+ return -ENOENT;
+ }
+#endif
+
+ stat |= STAT_ISR_REG;
+
+#ifdef CONFIG_POST_PROCESS_MANAGER
+ vf_provider_init(&vh264_vf_prov, PROVIDER_NAME, &vh264_vf_provider_ops,
+ NULL);
+ vf_reg_provider(&vh264_vf_prov);
+ vf_notify_receiver(PROVIDER_NAME, VFRAME_EVENT_PROVIDER_START, NULL);
+#else
+ vf_provider_init(&vh264_vf_prov, PROVIDER_NAME, &vh264_vf_provider_ops,
+ NULL);
+ vf_reg_provider(&vh264_vf_prov);
+#endif
+
+ vf_notify_receiver(PROVIDER_NAME, VFRAME_EVENT_PROVIDER_FR_HINT,
+ (void *)((unsigned long)frame_dur));
+
+ stat |= STAT_VF_HOOK;
+
+ recycle_timer.data = (ulong) &recycle_timer;
+ recycle_timer.function = vh264_put_timer_func;
+ recycle_timer.expires = jiffies + PUT_INTERVAL;
+
+ add_timer(&recycle_timer);
+
+ stat |= STAT_TIMER_ARM;
+
+ vh264_stream_switching_state = SWITCHING_STATE_OFF;
+
+ stat |= STAT_VDEC_RUN;
+ wmb(); /* Ensure fetchbuf contents visible */
+
+ /* -- start decoder */
+ amvdec_start();
+
+ init_userdata_fifo();
+
+ return 0;
+}
+
+static int vh264_stop(int mode)
+{
+ int i;
+
+ if (stat & STAT_VDEC_RUN) {
+ amvdec_stop();
+ stat &= ~STAT_VDEC_RUN;
+ }
+
+ if (stat & STAT_ISR_REG) {
+ WRITE_VREG(ASSIST_MBOX1_MASK, 0);
+ /*TODO irq */
+
+ vdec_free_irq(VDEC_IRQ_1, (void *)vh264_dec_id);
+
+ stat &= ~STAT_ISR_REG;
+ }
+
+ if (stat & STAT_TIMER_ARM) {
+ del_timer_sync(&recycle_timer);
+ stat &= ~STAT_TIMER_ARM;
+ }
+
+ if (stat & STAT_VF_HOOK) {
+ if (mode == MODE_FULL) {
+ vf_notify_receiver(PROVIDER_NAME,
+ VFRAME_EVENT_PROVIDER_FR_END_HINT,
+ NULL);
+ }
+
+ vf_unreg_provider(&vh264_vf_prov);
+ stat &= ~STAT_VF_HOOK;
+ }
+
+ if (stat & STAT_MC_LOAD) {
+ if (mc_cpu_addr != NULL) {
+ dma_free_coherent(amports_get_dma_device(),
+ MC_TOTAL_SIZE, mc_cpu_addr,
+ mc_dma_handle);
+ mc_cpu_addr = NULL;
+ }
+ }
+ if (sei_data_buffer != NULL) {
+ dma_free_coherent(
+ amports_get_dma_device(),
+ USER_DATA_SIZE,
+ sei_data_buffer,
+ sei_data_buffer_phys);
+ sei_data_buffer = NULL;
+ sei_data_buffer_phys = 0;
+ }
+ amvdec_disable();
+
+ for (i = 0; i < ARRAY_SIZE(fense_buffer_spec); i++) {
+ if (fense_buffer_spec[i].phy_addr) {
+ codec_mm_free_for_dma(MEM_NAME,
+ fense_buffer_spec[i].phy_addr);
+ fense_buffer_spec[i].phy_addr = 0;
+ fense_buffer_spec[i].alloc_count = 0;
+ }
+ }
+
+ for (i = 0; i < ARRAY_SIZE(buffer_spec); i++) {
+ if (buffer_spec[i].phy_addr) {
+ if (is_4k && !get_blackout_policy())
+ pr_info("Skip releasing CMA buffer %d\n", i);
+ else {
+ codec_mm_free_for_dma(MEM_NAME,
+ buffer_spec[i].phy_addr);
+ buffer_spec[i].phy_addr = 0;
+ buffer_spec[i].alloc_count = 0;
+ }
+ }
+ }
+ return 0;
+}
+
+static void error_do_work(struct work_struct *work)
+{
+ mutex_lock(&vh264_mutex);
+
+ /*
+ * we need to lock vh264_stop/vh264_init.
+ * because we will call amvdec_h264_remove on this step;
+ * then we may call more than once on
+ * free_irq/deltimer/..and some other.
+ */
+ if (atomic_read(&vh264_active)) {
+ amvdec_stop();
+ vh264_reset = 1;
+#ifdef CONFIG_POST_PROCESS_MANAGER
+ vh264_ppmgr_reset();
+#else
+ vf_light_unreg_provider(&vh264_vf_prov);
+
+ vh264_local_init();
+
+ vf_reg_provider(&vh264_vf_prov);
+#endif
+ msleep(30);
+ vh264_local_init();
+ vh264_prot_init();
+
+ amvdec_start();
+ vh264_reset = 0;
+ }
+
+ mutex_unlock(&vh264_mutex);
+}
+
+static void stream_switching_done(void)
+{
+ int state = vh264_stream_switching_state;
+
+ WRITE_VREG(AV_SCRATCH_7, 0);
+ WRITE_VREG(AV_SCRATCH_8, 0);
+ WRITE_VREG(AV_SCRATCH_9, 0);
+
+ if (state == SWITCHING_STATE_ON_CMD1) {
+ pr_info("Enter set parameter cmd1 switching_state %x.\n",
+ vh264_stream_switching_state);
+ schedule_work(&set_parameter_work);
+ return;
+ } else if (state == SWITCHING_STATE_ON_CMD1_PENDING)
+ return;
+
+ vh264_stream_switching_state = SWITCHING_STATE_OFF;
+
+ wmb(); /* Ensure fetchbuf contents visible */
+
+ if (state == SWITCHING_STATE_ON_CMD3)
+ WRITE_VREG(AV_SCRATCH_0, 0);
+
+ pr_info("Leaving switching mode.\n");
+}
+
+/* construt a new frame as a copy of last frame so frame receiver can
+ * release all buffer resources to decoder.
+ */
+static void stream_switching_do(struct work_struct *work)
+{
+ int mb_total_num, mb_width_num, mb_height_num, i = 0;
+ struct vframe_s *vf = NULL;
+ u32 y_index, u_index, src_index, des_index, y_desindex, u_desindex;
+ struct canvas_s csy, csu, cyd;
+ unsigned long flags;
+ bool delay = true;
+
+ if (!atomic_read(&vh264_active))
+ return;
+
+ if (vh264_stream_switching_state == SWITCHING_STATE_OFF)
+ return;
+
+ spin_lock_irqsave(&prepare_lock, flags);
+
+ block_display_q = true;
+
+ spin_unlock_irqrestore(&prepare_lock, flags);
+
+ mb_total_num = mb_total;
+ mb_width_num = mb_width;
+ mb_height_num = mb_height;
+
+ while (is_4k || kfifo_len(&delay_display_q) > 2) {
+ if (kfifo_get(&delay_display_q, &vf)) {
+ kfifo_put(&display_q,
+ (const struct vframe_s *)vf);
+ vf_notify_receiver(PROVIDER_NAME,
+ VFRAME_EVENT_PROVIDER_VFRAME_READY, NULL);
+ } else
+ break;
+ }
+
+ if (!kfifo_get(&delay_display_q, &vf)) {
+ vf = p_last_vf;
+ delay = false;
+ }
+
+ while (vf) {
+ int buffer_index;
+
+ buffer_index = vf->index & 0xff;
+
+ /* construct a clone of the frame from last frame */
+#if 0
+ pr_info("src yaddr[0x%x] index[%d] width[%d] heigth[%d]\n",
+ buffer_spec[buffer_index].y_addr,
+ buffer_spec[buffer_index].y_canvas_index,
+ buffer_spec[buffer_index].y_canvas_width,
+ buffer_spec[buffer_index].y_canvas_height);
+
+ pr_info("src uaddr[0x%x] index[%d] width[%d] heigth[%d]\n",
+ buffer_spec[buffer_index].u_addr,
+ buffer_spec[buffer_index].u_canvas_index,
+ buffer_spec[buffer_index].u_canvas_width,
+ buffer_spec[buffer_index].u_canvas_height);
+#endif
+ if (!is_4k) {
+ y_index = buffer_spec[buffer_index].y_canvas_index;
+ u_index = buffer_spec[buffer_index].u_canvas_index;
+
+ canvas_read(y_index, &csy);
+ canvas_read(u_index, &csu);
+
+ canvas_config(fense_buffer_spec[i].y_canvas_index,
+ fense_buffer_spec[i].phy_addr,
+ mb_width_num << 4, mb_height_num << 4,
+ CANVAS_ADDR_NOWRAP,
+ CANVAS_BLKMODE_LINEAR);
+ canvas_config(fense_buffer_spec[i].u_canvas_index,
+ fense_buffer_spec[i].phy_addr +
+ (mb_total_num << 8),
+ mb_width_num << 4, mb_height_num << 3,
+ CANVAS_ADDR_NOWRAP,
+ CANVAS_BLKMODE_LINEAR);
+
+ y_desindex = fense_buffer_spec[i].y_canvas_index;
+ u_desindex = fense_buffer_spec[i].u_canvas_index;
+
+ canvas_read(y_desindex, &cyd);
+
+ src_index = ((y_index & 0xff) |
+ ((u_index << 8) & 0x0000ff00));
+ des_index = ((y_desindex & 0xff) |
+ ((u_desindex << 8) & 0x0000ff00));
+#if 0
+ ge2d_canvas_dup(&csy, &csu, &cyd,
+ GE2D_FORMAT_M24_NV21,
+ src_index,
+ des_index);
+#endif/*mask*/
+ }
+ fense_vf[i] = *vf;
+ fense_vf[i].index = -1;
+
+ if (!is_4k)
+ fense_vf[i].canvas0Addr =
+ spec2canvas(&fense_buffer_spec[i]);
+ else
+ fense_vf[i].flag |= VFRAME_FLAG_SWITCHING_FENSE;
+
+ /* send clone to receiver */
+ kfifo_put(&display_q,
+ (const struct vframe_s *)&fense_vf[i]);
+
+ /* early recycle frames for last session */
+ if (delay)
+ vh264_vf_put(vf, NULL);
+
+ vf_notify_receiver(PROVIDER_NAME,
+ VFRAME_EVENT_PROVIDER_VFRAME_READY, NULL);
+
+ i++;
+
+ if (!kfifo_get(&delay_display_q, &vf))
+ break;
+ }
+
+ block_display_q = false;
+
+ pr_info("Switching fense frame post\n");
+}
+
+static int amvdec_h264_probe(struct platform_device *pdev)
+{
+ struct vdec_s *pdata = *(struct vdec_s **)pdev->dev.platform_data;
+
+ mutex_lock(&vh264_mutex);
+
+ if (pdata == NULL) {
+ pr_info("\namvdec_h264 memory resource undefined.\n");
+ mutex_unlock(&vh264_mutex);
+ return -EFAULT;
+ }
+
+ ucode_map_start = pdata->mem_start;
+ buf_size = pdata->mem_end - pdata->mem_start + 1;
+ if (buf_size < DEFAULT_MEM_SIZE) {
+ pr_info("\namvdec_h264 memory size not enough.\n");
+ mutex_unlock(&vh264_mutex);
+ return -ENOMEM;
+ }
+
+ buf_offset = pdata->mem_start - DEF_BUF_START_ADDR;
+ if (get_cpu_type() >= MESON_CPU_MAJOR_ID_GXTVBB)
+ buf_start = V_BUF_ADDR_OFFSET_NEW + pdata->mem_start;
+ else
+ buf_start = V_BUF_ADDR_OFFSET + pdata->mem_start;
+ buf_end = pdata->mem_end;
+ if (pdata->sys_info)
+ vh264_amstream_dec_info = *pdata->sys_info;
+ if (sei_data_buffer == NULL) {
+ sei_data_buffer =
+ dma_alloc_coherent(amports_get_dma_device(),
+ USER_DATA_SIZE,
+ &sei_data_buffer_phys, GFP_KERNEL);
+ if (!sei_data_buffer) {
+ pr_info("%s: Can not allocate sei_data_buffer\n",
+ __func__);
+ mutex_unlock(&vh264_mutex);
+ return -ENOMEM;
+ }
+ /* pr_info("buffer 0x%x, phys 0x%x, remap 0x%x\n",
+ * sei_data_buffer, sei_data_buffer_phys,
+ * (u32)sei_data_buffer_remap);
+ */
+ }
+ pr_debug("amvdec_h264 mem-addr=%lx,buff_offset=%x,buf_start=%lx buf_size %x\n",
+ pdata->mem_start, buf_offset, buf_start, buf_size);
+ pdata->dec_status = vh264_dec_status;
+ pdata->set_trickmode = vh264_set_trickmode;
+
+ if (vh264_init() < 0) {
+ pr_info("\namvdec_h264 init failed.\n");
+ mutex_unlock(&vh264_mutex);
+ return -ENODEV;
+ }
+
+ INIT_WORK(&error_wd_work, error_do_work);
+ INIT_WORK(&stream_switching_work, stream_switching_do);
+ INIT_WORK(&set_parameter_work, vh264_set_params);
+
+ atomic_set(&vh264_active, 1);
+
+ mutex_unlock(&vh264_mutex);
+
+ return 0;
+}
+
+static int amvdec_h264_remove(struct platform_device *pdev)
+{
+ atomic_set(&vh264_active, 0);
+ cancel_work_sync(&set_parameter_work);
+ cancel_work_sync(&error_wd_work);
+ cancel_work_sync(&stream_switching_work);
+ mutex_lock(&vh264_mutex);
+ vh264_stop(MODE_FULL);
+ vdec_source_changed(VFORMAT_H264, 0, 0, 0);
+ atomic_set(&vh264_active, 0);
+#ifdef DEBUG_PTS
+ pr_info
+ ("pts missed %ld, pts hit %ld, pts_outside %d, duration %d, ",
+ pts_missed, pts_hit, pts_outside, frame_dur);
+ pr_info("sync_outside %d, use_idr_framerate %d\n",
+ sync_outside, use_idr_framerate);
+#endif
+ mutex_unlock(&vh264_mutex);
+ return 0;
+}
+
+/****************************************/
+
+static struct platform_driver amvdec_h264_driver = {
+ .probe = amvdec_h264_probe,
+ .remove = amvdec_h264_remove,
+#ifdef CONFIG_PM
+ .suspend = amvdec_suspend,
+ .resume = amvdec_resume,
+#endif
+ .driver = {
+ .name = DRIVER_NAME,
+ }
+};
+
+static struct codec_profile_t amvdec_h264_profile = {
+ .name = "h264",
+ .profile = ""
+};
+
+static int __init amvdec_h264_driver_init_module(void)
+{
+ pr_debug("amvdec_h264 module init\n");
+
+ /*ge2d_videoh264task_init();mask*/
+
+ if (platform_driver_register(&amvdec_h264_driver)) {
+ pr_err("failed to register amvdec_h264 driver\n");
+ return -ENODEV;
+ }
+ if (get_cpu_type() >= MESON_CPU_MAJOR_ID_GXTVBB)
+ amvdec_h264_profile.profile = "4k";
+ vcodec_profile_register(&amvdec_h264_profile);
+ return 0;
+}
+
+static void __exit amvdec_h264_driver_remove_module(void)
+{
+ pr_debug("amvdec_h264 module remove.\n");
+
+ platform_driver_unregister(&amvdec_h264_driver);
+
+ /*ge2d_videoh264task_release();mask*/
+}
+
+/****************************************/
+
+module_param(stat, uint, 0664);
+MODULE_PARM_DESC(stat, "\n amvdec_h264 stat\n");
+module_param(error_recovery_mode, uint, 0664);
+MODULE_PARM_DESC(error_recovery_mode, "\n amvdec_h264 error_recovery_mode\n");
+module_param(sync_outside, uint, 0664);
+MODULE_PARM_DESC(sync_outside, "\n amvdec_h264 sync_outside\n");
+module_param(dec_control, uint, 0664);
+MODULE_PARM_DESC(dec_control, "\n amvdec_h264 decoder control\n");
+module_param(fatal_error_reset, uint, 0664);
+MODULE_PARM_DESC(fatal_error_reset,
+ "\n amvdec_h264 decoder reset when fatal error happens\n");
+module_param(max_refer_buf, uint, 0664);
+MODULE_PARM_DESC(max_refer_buf,
+ "\n amvdec_h264 dec buffering or not for reference frame\n");
+module_param(ucode_type, uint, 0664);
+MODULE_PARM_DESC(ucode_type,
+ "\n amvdec_h264 dec buffering or not for reference frame\n");
+module_param(debugfirmware, uint, 0664);
+MODULE_PARM_DESC(debugfirmware, "\n amvdec_h264 debug load firmware\n");
+module_param(fixed_frame_rate_flag, uint, 0664);
+MODULE_PARM_DESC(fixed_frame_rate_flag,
+ "\n amvdec_h264 fixed_frame_rate_flag\n");
+module_param(decoder_debug_flag, uint, 0664);
+MODULE_PARM_DESC(decoder_debug_flag,
+ "\n amvdec_h264 decoder_debug_flag\n");
+
+module_param(decoder_force_reset, uint, 0664);
+MODULE_PARM_DESC(decoder_force_reset,
+ "\n amvdec_h264 decoder force reset\n");
+module_param(no_idr_error_max, uint, 0664);
+MODULE_PARM_DESC(no_idr_error_max,
+ "\n print no_idr_error_max\n");
+
+
+module_init(amvdec_h264_driver_init_module);
+module_exit(amvdec_h264_driver_remove_module);
+
+MODULE_DESCRIPTION("AMLOGIC H264 Video Decoder Driver");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Chen Zhang <chen.zhang@amlogic.com>");
diff --git a/drivers/frame_provider/decoder/h264/vh264.h b/drivers/frame_provider/decoder/h264/vh264.h
new file mode 100644
index 0000000..a8b0c62
--- a/dev/null
+++ b/drivers/frame_provider/decoder/h264/vh264.h
@@ -0,0 +1,27 @@
+/*
+ * drivers/amlogic/media/frame_provider/decoder/h264/vh264.h
+ *
+ * Copyright (C) 2016 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 VH264_H
+#define VH264_H
+
+extern int query_video_status(int type, int *value);
+
+/* extern s32 vh264_init(void); */
+
+extern s32 vh264_release(void);
+
+#endif /* VMPEG4_H */
diff --git a/drivers/frame_provider/decoder/h265/Makefile b/drivers/frame_provider/decoder/h265/Makefile
new file mode 100644
index 0000000..cc2cdc1
--- a/dev/null
+++ b/drivers/frame_provider/decoder/h265/Makefile
@@ -0,0 +1 @@
+obj-m += vh265.o
diff --git a/drivers/frame_provider/decoder/h265/vh265.c b/drivers/frame_provider/decoder/h265/vh265.c
new file mode 100644
index 0000000..dea958e
--- a/dev/null
+++ b/drivers/frame_provider/decoder/h265/vh265.c
@@ -0,0 +1,8155 @@
+/*
+ * drivers/amlogic/amports/vh265.c
+ *
+ * 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.
+ *
+*/
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/semaphore.h>
+#include <linux/delay.h>
+#include <linux/timer.h>
+#include <linux/kfifo.h>
+#include <linux/kthread.h>
+#include <linux/platform_device.h>
+#include <linux/amlogic/media/vfm/vframe.h>
+#include <linux/amlogic/media/utils/amstream.h>
+#include <linux/amlogic/media/utils/vformat.h>
+#include <linux/amlogic/media/frame_sync/ptsserv.h>
+#include <linux/amlogic/media/canvas/canvas.h>
+#include <linux/amlogic/media/vfm/vframe.h>
+#include <linux/amlogic/media/vfm/vframe_provider.h>
+#include <linux/amlogic/media/vfm/vframe_receiver.h>
+#include <linux/dma-mapping.h>
+#include <linux/dma-contiguous.h>
+#include <linux/slab.h>
+#include "../../../stream_input/amports/amports_priv.h"
+#include <linux/amlogic/media/codec_mm/codec_mm.h>
+#include "../utils/decoder_mmu_box.h"
+
+/*#define HEVC_PIC_STRUCT_SUPPORT*/
+#define MULTI_INSTANCE_SUPPORT
+
+
+
+#define MMU_COMPRESS_HEADER_SIZE 0x48000
+#define MAX_FRAME_4K_NUM 0x1200
+#define FRAME_MMU_MAP_SIZE (MAX_FRAME_4K_NUM * 4)
+#define H265_MMU_MAP_BUFFER HEVC_ASSIST_SCRATCH_7
+#define HEVC_CM_HEADER_START_ADDR 0x3628
+#define HEVC_SAO_MMU_VH1_ADDR 0x363b
+#define HEVC_SAO_MMU_VH0_ADDR 0x363a
+#define HEVC_SAO_MMU_STATUS 0x3639
+
+
+
+#define MEM_NAME "codec_265"
+/* #include <mach/am_regs.h> */
+#include <linux/amlogic/media/utils/vdec_reg.h>
+
+#include "../utils/vdec.h"
+#include "../utils/amvdec.h"
+#include <linux/amlogic/media/video_sink/video/video.h>
+
+#define SUPPORT_10BIT
+/* #define ERROR_HANDLE_DEBUG */
+#if 0/*MESON_CPU_TYPE == MESON_CPU_TYPE_MESON8B*/
+#undef SUPPORT_4K2K
+#else
+#define SUPPORT_4K2K
+#endif
+
+#ifndef STAT_KTHREAD
+#define STAT_KTHREAD 0x40
+#endif
+
+#ifdef MULTI_INSTANCE_SUPPORT
+#define MULTI_DRIVER_NAME "ammvdec_h265"
+#endif
+#define DRIVER_NAME "amvdec_h265"
+#define MODULE_NAME "amvdec_h265"
+
+#define PUT_INTERVAL (HZ/100)
+#define ERROR_SYSTEM_RESET_COUNT 200
+
+#define PTS_NORMAL 0
+#define PTS_NONE_REF_USE_DURATION 1
+
+#define PTS_MODE_SWITCHING_THRESHOLD 3
+#define PTS_MODE_SWITCHING_RECOVERY_THREASHOLD 3
+
+#define DUR2PTS(x) ((x)*90/96)
+#define HEVC_SIZE (4096*2304)
+
+static atomic_t cma_allocate;
+
+struct hevc_state_s;
+static int hevc_print(struct hevc_state_s *hevc,
+ int debug_flag, const char *fmt, ...);
+static int vh265_vf_states(struct vframe_states *states, void *);
+static struct vframe_s *vh265_vf_peek(void *);
+static struct vframe_s *vh265_vf_get(void *);
+static void vh265_vf_put(struct vframe_s *, void *);
+static int vh265_event_cb(int type, void *data, void *private_data);
+
+static int vh265_stop(struct hevc_state_s *hevc);
+#ifdef MULTI_INSTANCE_SUPPORT
+static int vmh265_stop(struct hevc_state_s *hevc);
+static s32 vh265_init(struct vdec_s *vdec);
+#else
+static s32 vh265_init(struct hevc_state_s *hevc);
+#endif
+static void vh265_prot_init(struct hevc_state_s *hevc);
+static int vh265_local_init(struct hevc_state_s *hevc);
+static void vh265_put_timer_func(unsigned long arg);
+
+static const char vh265_dec_id[] = "vh265-dev";
+
+#define PROVIDER_NAME "decoder.h265"
+#define MULTI_INSTANCE_PROVIDER_NAME "vdec.h265"
+
+static const struct vframe_operations_s vh265_vf_provider = {
+ .peek = vh265_vf_peek,
+ .get = vh265_vf_get,
+ .put = vh265_vf_put,
+ .event_cb = vh265_event_cb,
+ .vf_states = vh265_vf_states,
+};
+
+static struct vframe_provider_s vh265_vf_prov;
+
+static u32 bit_depth_luma;
+static u32 bit_depth_chroma;
+static u32 video_signal_type;
+
+
+#define VIDEO_SIGNAL_TYPE_AVAILABLE_MASK 0x20000000
+
+static const char * const video_format_names[] = {
+ "component", "PAL", "NTSC", "SECAM",
+ "MAC", "unspecified", "unspecified", "unspecified"
+};
+
+static const char * const color_primaries_names[] = {
+ "unknown", "bt709", "undef", "unknown",
+ "bt470m", "bt470bg", "smpte170m", "smpte240m",
+ "film", "bt2020"
+};
+
+static const char * const transfer_characteristics_names[] = {
+ "unknown", "bt709", "undef", "unknown",
+ "bt470m", "bt470bg", "smpte170m", "smpte240m",
+ "linear", "log100", "log316", "iec61966-2-4",
+ "bt1361e", "iec61966-2-1", "bt2020-10", "bt2020-12",
+ "smpte-st-2084", "smpte-st-428"
+};
+
+static const char * const matrix_coeffs_names[] = {
+ "GBR", "bt709", "undef", "unknown",
+ "fcc", "bt470bg", "smpte170m", "smpte240m",
+ "YCgCo", "bt2020nc", "bt2020c"
+};
+
+#ifdef SUPPORT_10BIT
+#define HEVC_CM_BODY_START_ADDR 0x3626
+#define HEVC_CM_BODY_LENGTH 0x3627
+#define HEVC_CM_HEADER_LENGTH 0x3629
+#define HEVC_CM_HEADER_OFFSET 0x362b
+#define HEVC_SAO_CTRL9 0x362d
+#define LOSLESS_COMPRESS_MODE
+/* DOUBLE_WRITE_MODE is enabled only when NV21 8 bit output is needed */
+/*
+*double_write_mode: 0, no double write; 1, 1:1 ratio; 2, (1/4):(1/4) ratio;
+* 3, (1/4):(1/4) ratio, with both compressed frame included
+* 0x10, double write only
+*/
+static u32 double_write_mode;
+
+/*#define DECOMP_HEADR_SURGENT*/
+
+static u32 mem_map_mode; /* 0:linear 1:32x32 2:64x32 ; m8baby test1902 */
+static u32 enable_mem_saving = 1;
+static u32 workaround_enable;
+static u32 force_w_h;
+#endif
+static u32 force_fps;
+static u32 pts_unstable;
+#define H265_DEBUG_BUFMGR 0x01
+#define H265_DEBUG_BUFMGR_MORE 0x02
+#define H265_DEBUG_UCODE 0x04
+#define H265_DEBUG_REG 0x08
+#define H265_DEBUG_MAN_SEARCH_NAL 0x10
+#define H265_DEBUG_MAN_SKIP_NAL 0x20
+#define H265_DEBUG_DISPLAY_CUR_FRAME 0x40
+#define H265_DEBUG_FORCE_CLK 0x80
+#define H265_DEBUG_SEND_PARAM_WITH_REG 0x100
+#define H265_DEBUG_NO_DISPLAY 0x200
+#define H265_DEBUG_DISCARD_NAL 0x400
+#define H265_DEBUG_OUT_PTS 0x800
+#define H265_DEBUG_PRINT_DECODE_STATUS 0x1000
+#define H265_DEBUG_PRINT_SEI 0x2000
+#define H265_DEBUG_PIC_STRUCT 0x4000
+#define H265_DEBUG_DIS_LOC_ERROR_PROC 0x10000
+#define H265_DEBUG_DIS_SYS_ERROR_PROC 0x20000
+#define H265_DEBUG_DUMP_PIC_LIST 0x40000
+#define H265_DEBUG_TRIG_SLICE_SEGMENT_PROC 0x80000
+#define H265_DEBUG_HW_RESET 0x100000
+#define H265_DEBUG_LOAD_UCODE_FROM_FILE 0x200000
+#define H265_DEBUG_ERROR_TRIG 0x400000
+#define H265_DEBUG_NO_EOS_SEARCH_DONE 0x800000
+#define H265_DEBUG_NOT_USE_LAST_DISPBUF 0x1000000
+#define H265_DEBUG_IGNORE_CONFORMANCE_WINDOW 0x2000000
+#define H265_DEBUG_WAIT_DECODE_DONE_WHEN_STOP 0x4000000
+#ifdef MULTI_INSTANCE_SUPPORT
+#define USE_OLD_PROVIDER 0x10000000
+#define PRINT_FLAG_VDEC_STATUS 0x20000000
+#define PRINT_FLAG_VDEC_DETAIL 0x40000000
+#define ONLY_RESET_AT_START 0x80000000
+#endif
+#define MAX_BUF_NUM 24
+#define MAX_REF_PIC_NUM 24
+#define MAX_REF_ACTIVE 16
+
+const u32 h265_version = 201602101;
+static u32 debug;
+static u32 radr;
+static u32 rval;
+static u32 dbg_cmd;
+static u32 dbg_skip_decode_index;
+static u32 endian = 0xff0;
+#ifdef ERROR_HANDLE_DEBUG
+static u32 dbg_nal_skip_flag;
+ /* bit[0], skip vps; bit[1], skip sps; bit[2], skip pps */
+static u32 dbg_nal_skip_count;
+#endif
+/*for debug*/
+static u32 decode_stop_pos;
+static u32 decode_stop_pos_pre;
+static u32 decode_pic_begin;
+static uint slice_parse_begin;
+static u32 step;
+#ifdef MIX_STREAM_SUPPORT
+#ifdef SUPPORT_4K2K
+static u32 buf_alloc_width = 4096;
+static u32 buf_alloc_height = 2304;
+#else
+static u32 buf_alloc_width = 1920;
+static u32 buf_alloc_height = 1088;
+#endif
+static u32 dynamic_buf_num_margin;
+#else
+static u32 buf_alloc_width;
+static u32 buf_alloc_height;
+static u32 dynamic_buf_num_margin = 8;
+#endif
+static u32 buf_alloc_size;
+static u32 re_config_pic_flag;
+/*
+*bit[0]: 0,
+ *bit[1]: 0, always release cma buffer when stop
+ *bit[1]: 1, never release cma buffer when stop
+*bit[0]: 1, when stop, release cma buffer if blackout is 1;
+*do not release cma buffer is blackout is not 1
+*
+*bit[2]: 0, when start decoding, check current displayed buffer
+* (only for buffer decoded by h265) if blackout is 0
+* 1, do not check current displayed buffer
+*
+*bit[3]: 1, if blackout is not 1, do not release current
+* displayed cma buffer always.
+*/
+/* set to 1 for fast play;
+* set to 8 for other case of "keep last frame"
+*/
+static u32 buffer_mode = 1;
+/* buffer_mode_dbg: debug only*/
+static u32 buffer_mode_dbg = 0xffff0000;
+/**/
+/*
+*bit[1:0]PB_skip_mode: 0, start decoding at begin;
+*1, start decoding after first I;
+*2, only decode and display none error picture;
+*3, start decoding and display after IDR,etc
+*bit[31:16] PB_skip_count_after_decoding (decoding but not display),
+*only for mode 0 and 1.
+ */
+static u32 nal_skip_policy = 2;
+
+/*
+*bit 0, 1: only display I picture;
+*bit 1, 1: only decode I picture;
+*/
+static u32 i_only_flag;
+
+/*
+*use_cma: 1, use both reserver memory and cma for buffers
+*2, only use cma for buffers
+*/
+static u32 use_cma = 2;
+
+static u32 max_decoding_time;
+/*
+*error handling
+*/
+/*
+*error_handle_policy:
+*bit 0: 0, auto skip error_skip_nal_count nals before error recovery;
+*1, skip error_skip_nal_count nals before error recovery;
+*bit 1 (valid only when bit0 == 1):
+*1, wait vps/sps/pps after error recovery;
+*bit 2 (valid only when bit0 == 0):
+*0, auto search after error recovery (hevc_recover() called);
+*1, manual search after error recovery
+*(change to auto search after get IDR: WRITE_VREG(NAL_SEARCH_CTL, 0x2))
+*
+*bit 4: 0, set error_mark after reset/recover
+ *1, do not set error_mark after reset/recover
+*bit 5: 0, check total lcu for every picture
+ *1, do not check total lcu
+*
+*/
+
+static u32 error_handle_policy;
+static u32 error_skip_nal_count = 6;
+static u32 error_handle_threshold = 30;
+static u32 error_handle_nal_skip_threshold = 10;
+static u32 error_handle_system_threshold = 30;
+static u32 interlace_enable = 1;
+static u32 parser_sei_enable;
+/* this is only for h265 mmu enable */
+static u32 mmu_enable = 1;
+
+
+
+
+static u32 max_buf_num = 16;
+
+#ifdef MULTI_INSTANCE_SUPPORT
+static unsigned int m_hevc_count;
+#define MAX_DECODE_INSTANCE_NUM 4
+static unsigned int max_decode_instance_num
+ = MAX_DECODE_INSTANCE_NUM;
+static unsigned int decode_frame_count[MAX_DECODE_INSTANCE_NUM];
+static unsigned int max_process_time[MAX_DECODE_INSTANCE_NUM];
+static unsigned int max_get_frame_interval[MAX_DECODE_INSTANCE_NUM];
+static unsigned int debug_flag[MAX_DECODE_INSTANCE_NUM];
+#endif
+
+DEFINE_SPINLOCK(lock);
+struct task_struct *h265_task;
+#undef DEBUG_REG
+#ifdef DEBUG_REG
+void WRITE_VREG_DBG(unsigned adr, unsigned val)
+{
+ if (debug & H265_DEBUG_REG)
+ pr_info("%s(%x, %x)\n", __func__, adr, val);
+ WRITE_VREG(adr, val);
+}
+
+#undef WRITE_VREG
+#define WRITE_VREG WRITE_VREG_DBG
+#endif
+
+static DEFINE_MUTEX(vh265_mutex);
+
+
+/*
+*************************************************
+*
+h265 buffer management include
+*
+**************************************************
+*/
+enum NalUnitType {
+ NAL_UNIT_CODED_SLICE_TRAIL_N = 0, /* 0 */
+ NAL_UNIT_CODED_SLICE_TRAIL_R, /* 1 */
+
+ NAL_UNIT_CODED_SLICE_TSA_N, /* 2 */
+ /* Current name in the spec: TSA_R */
+ NAL_UNIT_CODED_SLICE_TLA, /* 3 */
+
+ NAL_UNIT_CODED_SLICE_STSA_N, /* 4 */
+ NAL_UNIT_CODED_SLICE_STSA_R, /* 5 */
+
+ NAL_UNIT_CODED_SLICE_RADL_N, /* 6 */
+ /* Current name in the spec: RADL_R */
+ NAL_UNIT_CODED_SLICE_DLP, /* 7 */
+
+ NAL_UNIT_CODED_SLICE_RASL_N, /* 8 */
+ /* Current name in the spec: RASL_R */
+ NAL_UNIT_CODED_SLICE_TFD, /* 9 */
+
+ NAL_UNIT_RESERVED_10,
+ NAL_UNIT_RESERVED_11,
+ NAL_UNIT_RESERVED_12,
+ NAL_UNIT_RESERVED_13,
+ NAL_UNIT_RESERVED_14,
+ NAL_UNIT_RESERVED_15,
+
+ /* Current name in the spec: BLA_W_LP */
+ NAL_UNIT_CODED_SLICE_BLA, /* 16 */
+ /* Current name in the spec: BLA_W_DLP */
+ NAL_UNIT_CODED_SLICE_BLANT, /* 17 */
+ NAL_UNIT_CODED_SLICE_BLA_N_LP, /* 18 */
+ /* Current name in the spec: IDR_W_DLP */
+ NAL_UNIT_CODED_SLICE_IDR, /* 19 */
+ NAL_UNIT_CODED_SLICE_IDR_N_LP, /* 20 */
+ NAL_UNIT_CODED_SLICE_CRA, /* 21 */
+ NAL_UNIT_RESERVED_22,
+ NAL_UNIT_RESERVED_23,
+
+ NAL_UNIT_RESERVED_24,
+ NAL_UNIT_RESERVED_25,
+ NAL_UNIT_RESERVED_26,
+ NAL_UNIT_RESERVED_27,
+ NAL_UNIT_RESERVED_28,
+ NAL_UNIT_RESERVED_29,
+ NAL_UNIT_RESERVED_30,
+ NAL_UNIT_RESERVED_31,
+
+ NAL_UNIT_VPS, /* 32 */
+ NAL_UNIT_SPS, /* 33 */
+ NAL_UNIT_PPS, /* 34 */
+ NAL_UNIT_ACCESS_UNIT_DELIMITER, /* 35 */
+ NAL_UNIT_EOS, /* 36 */
+ NAL_UNIT_EOB, /* 37 */
+ NAL_UNIT_FILLER_DATA, /* 38 */
+ NAL_UNIT_SEI, /* 39 Prefix SEI */
+ NAL_UNIT_SEI_SUFFIX, /* 40 Suffix SEI */
+ NAL_UNIT_RESERVED_41,
+ NAL_UNIT_RESERVED_42,
+ NAL_UNIT_RESERVED_43,
+ NAL_UNIT_RESERVED_44,
+ NAL_UNIT_RESERVED_45,
+ NAL_UNIT_RESERVED_46,
+ NAL_UNIT_RESERVED_47,
+ NAL_UNIT_UNSPECIFIED_48,
+ NAL_UNIT_UNSPECIFIED_49,
+ NAL_UNIT_UNSPECIFIED_50,
+ NAL_UNIT_UNSPECIFIED_51,
+ NAL_UNIT_UNSPECIFIED_52,
+ NAL_UNIT_UNSPECIFIED_53,
+ NAL_UNIT_UNSPECIFIED_54,
+ NAL_UNIT_UNSPECIFIED_55,
+ NAL_UNIT_UNSPECIFIED_56,
+ NAL_UNIT_UNSPECIFIED_57,
+ NAL_UNIT_UNSPECIFIED_58,
+ NAL_UNIT_UNSPECIFIED_59,
+ NAL_UNIT_UNSPECIFIED_60,
+ NAL_UNIT_UNSPECIFIED_61,
+ NAL_UNIT_UNSPECIFIED_62,
+ NAL_UNIT_UNSPECIFIED_63,
+ NAL_UNIT_INVALID,
+};
+
+/* --------------------------------------------------- */
+/* Amrisc Software Interrupt */
+/* --------------------------------------------------- */
+#define AMRISC_STREAM_EMPTY_REQ 0x01
+#define AMRISC_PARSER_REQ 0x02
+#define AMRISC_MAIN_REQ 0x04
+
+/* --------------------------------------------------- */
+/* HEVC_DEC_STATUS define */
+/* --------------------------------------------------- */
+#define HEVC_DEC_IDLE 0x0
+#define HEVC_NAL_UNIT_VPS 0x1
+#define HEVC_NAL_UNIT_SPS 0x2
+#define HEVC_NAL_UNIT_PPS 0x3
+#define HEVC_NAL_UNIT_CODED_SLICE_SEGMENT 0x4
+#define HEVC_CODED_SLICE_SEGMENT_DAT 0x5
+#define HEVC_SLICE_DECODING 0x6
+#define HEVC_NAL_UNIT_SEI 0x7
+#define HEVC_SLICE_SEGMENT_DONE 0x8
+#define HEVC_NAL_SEARCH_DONE 0x9
+#define HEVC_DECPIC_DATA_DONE 0xa
+#define HEVC_DECPIC_DATA_ERROR 0xb
+#define HEVC_SEI_DAT 0xc
+#define HEVC_SEI_DAT_DONE 0xd
+#define HEVC_NAL_DECODE_DONE 0xe
+
+#define HEVC_DUMP_LMEM 0x20
+
+#define HEVC_4k2k_60HZ_NOT_SUPPORT 0x80
+#define HEVC_DISCARD_NAL 0xf0
+#define HEVC_ACTION_ERROR 0xfe
+#define HEVC_ACTION_DONE 0xff
+
+/* --------------------------------------------------- */
+/* Include "parser_cmd.h" */
+/* --------------------------------------------------- */
+#define PARSER_CMD_SKIP_CFG_0 0x0000090b
+
+#define PARSER_CMD_SKIP_CFG_1 0x1b14140f
+
+#define PARSER_CMD_SKIP_CFG_2 0x001b1910
+
+#define PARSER_CMD_NUMBER 37
+
+static unsigned short parser_cmd[PARSER_CMD_NUMBER] = {
+ 0x0401,
+ 0x8401,
+ 0x0800,
+ 0x0402,
+ 0x9002,
+ 0x1423,
+ 0x8CC3,
+ 0x1423,
+ 0x8804,
+ 0x9825,
+ 0x0800,
+ 0x04FE,
+ 0x8406,
+ 0x8411,
+ 0x1800,
+ 0x8408,
+ 0x8409,
+ 0x8C2A,
+ 0x9C2B,
+ 0x1C00,
+ 0x840F,
+ 0x8407,
+ 0x8000,
+ 0x8408,
+ 0x2000,
+ 0xA800,
+ 0x8410,
+ 0x04DE,
+ 0x840C,
+ 0x840D,
+ 0xAC00,
+ 0xA000,
+ 0x08C0,
+ 0x08E0,
+ 0xA40E,
+ 0xFC00,
+ 0x7C00
+};
+
+/*
+*************************************************
+*
+*h265 buffer management
+*
+**************************************************
+*/
+/* #define BUFFER_MGR_ONLY */
+/* #define CONFIG_HEVC_CLK_FORCED_ON */
+/* #define ENABLE_SWAP_TEST */
+#define MCRCC_ENABLE
+#define INVALID_POC 0x80000000
+
+#define HEVC_DEC_STATUS_REG HEVC_ASSIST_SCRATCH_0
+#define HEVC_RPM_BUFFER HEVC_ASSIST_SCRATCH_1
+#define HEVC_SHORT_TERM_RPS HEVC_ASSIST_SCRATCH_2
+#define HEVC_VPS_BUFFER HEVC_ASSIST_SCRATCH_3
+#define HEVC_SPS_BUFFER HEVC_ASSIST_SCRATCH_4
+#define HEVC_PPS_BUFFER HEVC_ASSIST_SCRATCH_5
+#define HEVC_SAO_UP HEVC_ASSIST_SCRATCH_6
+#define HEVC_STREAM_SWAP_BUFFER HEVC_ASSIST_SCRATCH_7
+#define HEVC_STREAM_SWAP_BUFFER2 HEVC_ASSIST_SCRATCH_8
+#define HEVC_sao_mem_unit HEVC_ASSIST_SCRATCH_9
+#define HEVC_SAO_ABV HEVC_ASSIST_SCRATCH_A
+#define HEVC_sao_vb_size HEVC_ASSIST_SCRATCH_B
+#define HEVC_SAO_VB HEVC_ASSIST_SCRATCH_C
+#define HEVC_SCALELUT HEVC_ASSIST_SCRATCH_D
+#define HEVC_WAIT_FLAG HEVC_ASSIST_SCRATCH_E
+#define RPM_CMD_REG HEVC_ASSIST_SCRATCH_F
+#define LMEM_DUMP_ADR HEVC_ASSIST_SCRATCH_F
+#define HEVC_STREAM_SWAP_TEST HEVC_ASSIST_SCRATCH_L
+#define HEVC_DECODE_PIC_BEGIN_REG HEVC_ASSIST_SCRATCH_M
+/*#define HEVC_DECODE_PIC_NUM_REG HEVC_ASSIST_SCRATCH_N*/
+#define HEVC_DECODE_SIZE HEVC_ASSIST_SCRATCH_N
+
+#define DEBUG_REG1 HEVC_ASSIST_SCRATCH_G
+#define DEBUG_REG2 HEVC_ASSIST_SCRATCH_H
+/*
+*ucode parser/search control
+*bit 0: 0, header auto parse; 1, header manual parse
+*bit 1: 0, auto skip for noneseamless stream; 1, no skip
+*bit [3:2]: valid when bit1==0;
+*0, auto skip nal before first vps/sps/pps/idr;
+*1, auto skip nal before first vps/sps/pps
+*2, auto skip nal before first vps/sps/pps,
+* and not decode until the first I slice (with slice address of 0)
+*
+*3, auto skip before first I slice (nal_type >=16 && nal_type<=21)
+*bit [15:4] nal skip count (valid when bit0 == 1 (manual mode) )
+*bit [16]: for NAL_UNIT_EOS when bit0 is 0:
+* 0, send SEARCH_DONE to arm ; 1, do not send SEARCH_DONE to arm
+*bit [17]: for NAL_SEI when bit0 is 0:
+* 0, do not parse SEI in ucode; 1, parse SEI in ucode
+*
+*bit [31:20]: used by ucode for debug purpose
+*/
+#define NAL_SEARCH_CTL HEVC_ASSIST_SCRATCH_I
+#define CUR_NAL_UNIT_TYPE HEVC_ASSIST_SCRATCH_J
+#define DECODE_STOP_POS HEVC_ASSIST_SCRATCH_K
+
+#define MAX_INT 0x7FFFFFFF
+
+#define RPM_BEGIN 0x100
+#define modification_list_cur 0x140
+#define RPM_END 0x180
+
+#define RPS_USED_BIT 14
+/* MISC_FLAG0 */
+#define PCM_LOOP_FILTER_DISABLED_FLAG_BIT 0
+#define PCM_ENABLE_FLAG_BIT 1
+#define LOOP_FILER_ACROSS_TILES_ENABLED_FLAG_BIT 2
+#define PPS_LOOP_FILTER_ACROSS_SLICES_ENABLED_FLAG_BIT 3
+#define DEBLOCKING_FILTER_OVERRIDE_ENABLED_FLAG_BIT 4
+#define PPS_DEBLOCKING_FILTER_DISABLED_FLAG_BIT 5
+#define DEBLOCKING_FILTER_OVERRIDE_FLAG_BIT 6
+#define SLICE_DEBLOCKING_FILTER_DISABLED_FLAG_BIT 7
+#define SLICE_SAO_LUMA_FLAG_BIT 8
+#define SLICE_SAO_CHROMA_FLAG_BIT 9
+#define SLICE_LOOP_FILTER_ACROSS_SLICES_ENABLED_FLAG_BIT 10
+
+union param_u {
+ struct {
+ unsigned short data[RPM_END - RPM_BEGIN];
+ } l;
+ struct {
+ /* from ucode lmem, do not change this struct */
+ unsigned short CUR_RPS[0x10];
+ unsigned short num_ref_idx_l0_active;
+ unsigned short num_ref_idx_l1_active;
+ unsigned short slice_type;
+ unsigned short slice_temporal_mvp_enable_flag;
+ unsigned short dependent_slice_segment_flag;
+ unsigned short slice_segment_address;
+ unsigned short num_title_rows_minus1;
+ unsigned short pic_width_in_luma_samples;
+ unsigned short pic_height_in_luma_samples;
+ unsigned short log2_min_coding_block_size_minus3;
+ unsigned short log2_diff_max_min_coding_block_size;
+ unsigned short log2_max_pic_order_cnt_lsb_minus4;
+ unsigned short POClsb;
+ unsigned short collocated_from_l0_flag;
+ unsigned short collocated_ref_idx;
+ unsigned short log2_parallel_merge_level;
+ unsigned short five_minus_max_num_merge_cand;
+ unsigned short sps_num_reorder_pics_0;
+ unsigned short modification_flag;
+ unsigned short tiles_enabled_flag;
+ unsigned short num_tile_columns_minus1;
+ unsigned short num_tile_rows_minus1;
+ unsigned short tile_width[4];
+ unsigned short tile_height[4];
+ unsigned short misc_flag0;
+ unsigned short pps_beta_offset_div2;
+ unsigned short pps_tc_offset_div2;
+ unsigned short slice_beta_offset_div2;
+ unsigned short slice_tc_offset_div2;
+ unsigned short pps_cb_qp_offset;
+ unsigned short pps_cr_qp_offset;
+ unsigned short first_slice_segment_in_pic_flag;
+ unsigned short m_temporalId;
+ unsigned short m_nalUnitType;
+
+ unsigned short vui_num_units_in_tick_hi;
+ unsigned short vui_num_units_in_tick_lo;
+ unsigned short vui_time_scale_hi;
+ unsigned short vui_time_scale_lo;
+ unsigned short bit_depth;
+ unsigned short profile_etc;
+ unsigned short sei_frame_field_info;
+ unsigned short video_signal_type;
+ unsigned short modification_list[0x20];
+ unsigned short conformance_window_flag;
+ unsigned short conf_win_left_offset;
+ unsigned short conf_win_right_offset;
+ unsigned short conf_win_top_offset;
+ unsigned short conf_win_bottom_offset;
+ unsigned short chroma_format_idc;
+ unsigned short color_description;
+ } p;
+};
+
+#define RPM_BUF_SIZE (0x80*2)
+/* non mmu mode lmem size : 0x400, mmu mode : 0x500*/
+#define LMEM_BUF_SIZE (0x500 * 2)
+
+struct buff_s {
+ u32 buf_start;
+ u32 buf_size;
+ u32 buf_end;
+};
+
+struct BuffInfo_s {
+ u32 max_width;
+ u32 max_height;
+ unsigned int start_adr;
+ unsigned int end_adr;
+ struct buff_s ipp;
+ struct buff_s sao_abv;
+ struct buff_s sao_vb;
+ struct buff_s short_term_rps;
+ struct buff_s vps;
+ struct buff_s sps;
+ struct buff_s pps;
+ struct buff_s sao_up;
+ struct buff_s swap_buf;
+ struct buff_s swap_buf2;
+ struct buff_s scalelut;
+ struct buff_s dblk_para;
+ struct buff_s dblk_data;
+ struct buff_s mmu_vbh;
+ struct buff_s cm_header;
+ struct buff_s mpred_above;
+ struct buff_s mpred_mv;
+ struct buff_s rpm;
+ struct buff_s lmem;
+};
+#define WORK_BUF_SPEC_NUM 2
+static struct BuffInfo_s amvh265_workbuff_spec[WORK_BUF_SPEC_NUM] = {
+ {
+ /* 8M bytes */
+ .max_width = 1920,
+ .max_height = 1088,
+ .ipp = {
+ /*
+ *IPP work space calculation :
+ *4096 * (Y+CbCr+Flags) = 12k, round to 16k
+ */
+ .buf_size = 0x4000,
+ },
+ .sao_abv = {
+ .buf_size = 0x30000,
+ },
+ .sao_vb = {
+ .buf_size = 0x30000,
+ },
+ .short_term_rps = {
+ /*
+ *SHORT_TERM_RPS - Max 64 set, 16 entry every set,
+ *total 64x16x2 = 2048 bytes (0x800)
+ */
+ .buf_size = 0x800,
+ },
+ .vps = {
+ /*
+ *VPS STORE AREA - Max 16 VPS, each has 0x80 bytes,
+ *total 0x0800 bytes
+ */
+ .buf_size = 0x800,
+ },
+ .sps = {
+ /*
+ *SPS STORE AREA - Max 16 SPS, each has 0x80 bytes,
+ *total 0x0800 bytes
+ */
+ .buf_size = 0x800,
+ },
+ .pps = {
+ /*
+ *PPS STORE AREA - Max 64 PPS, each has 0x80 bytes,
+ *total 0x2000 bytes
+ */
+ .buf_size = 0x2000,
+ },
+ .sao_up = {
+ /*
+ *SAO UP STORE AREA - Max 640(10240/16) LCU,
+ *each has 16 bytes total 0x2800 bytes
+ */
+ .buf_size = 0x2800,
+ },
+ .swap_buf = {
+ /*
+ *256cyclex64bit = 2K bytes 0x800
+ *(only 144 cycles valid)
+ */
+ .buf_size = 0x800,
+ },
+ .swap_buf2 = {
+ .buf_size = 0x800,
+ },
+ .scalelut = {
+ /*
+ *support up to 32 SCALELUT 1024x32 =
+ *32Kbytes (0x8000)
+ */
+ .buf_size = 0x8000,
+ },
+ .dblk_para = {
+#ifdef SUPPORT_10BIT
+ .buf_size = 0x40000,
+#else
+ /*
+ *DBLK -> Max 256(4096/16) LCU, each para
+ *512bytes(total:0x20000), data 1024bytes(total:0x40000)
+ */
+ .buf_size = 0x20000,
+#endif
+ },
+ .dblk_data = {
+ .buf_size = 0x40000,
+ },
+ .mmu_vbh = {
+ .buf_size = 0x5000, /*2*16*2304/4, 4K*/
+ },
+ .cm_header = {/* 0x44000 = ((1088*2*1024*4)/32/4)*(32/8)*/
+ .buf_size = MMU_COMPRESS_HEADER_SIZE*(16+1),
+ },
+ .mpred_above = {
+ .buf_size = 0x8000,
+ },
+ .mpred_mv = {/* 1080p, 0x40000 per buffer */
+ .buf_size = 0x40000 * MAX_REF_PIC_NUM,
+ },
+ .rpm = {
+ .buf_size = RPM_BUF_SIZE,
+ },
+ .lmem = {
+ .buf_size = 0x500 * 2,
+ }
+ },
+ {
+ .max_width = 4096,
+ .max_height = 2048,
+ .ipp = {
+ /*
+ *IPP work space calculation :
+ *4096 * (Y+CbCr+Flags) = 12k, round to 16k
+ */
+ .buf_size = 0x4000,
+ },
+ .sao_abv = {
+ .buf_size = 0x30000,
+ },
+ .sao_vb = {
+ .buf_size = 0x30000,
+ },
+ .short_term_rps = {
+ /*
+ *SHORT_TERM_RPS - Max 64 set, 16 entry every set,
+ *total 64x16x2 = 2048 bytes (0x800)
+ */
+ .buf_size = 0x800,
+ },
+ .vps = {
+ /*
+ *VPS STORE AREA - Max 16 VPS, each has 0x80 bytes,
+ *total 0x0800 bytes
+ */
+ .buf_size = 0x800,
+ },
+ .sps = {
+ /*
+ *SPS STORE AREA - Max 16 SPS, each has 0x80 bytes,
+ *total 0x0800 bytes
+ */
+ .buf_size = 0x800,
+ },
+ .pps = {
+ /*
+ *PPS STORE AREA - Max 64 PPS, each has 0x80 bytes,
+ *total 0x2000 bytes
+ */
+ .buf_size = 0x2000,
+ },
+ .sao_up = {
+ /*
+ *SAO UP STORE AREA - Max 640(10240/16) LCU,
+ *each has 16 bytes total 0x2800 bytes
+ */
+ .buf_size = 0x2800,
+ },
+ .swap_buf = {
+ /*
+ *256cyclex64bit = 2K bytes 0x800
+ *(only 144 cycles valid)
+ */
+ .buf_size = 0x800,
+ },
+ .swap_buf2 = {
+ .buf_size = 0x800,
+ },
+ .scalelut = {
+ /*
+ *support up to 32 SCALELUT 1024x32 = 32Kbytes
+ *(0x8000)
+ */
+ .buf_size = 0x8000,
+ },
+ .dblk_para = {
+ /*
+ *DBLK -> Max 256(4096/16) LCU, each para
+ *512bytes(total:0x20000),
+ *data 1024bytes(total:0x40000)
+ */
+ .buf_size = 0x20000,
+ },
+ .dblk_data = {
+ .buf_size = 0x40000,
+ },
+ .mmu_vbh = {
+ .buf_size = 0x5000, /*2*16*2304/4, 4K*/
+ },
+ .cm_header = {/*0x44000 = ((1088*2*1024*4)/32/4)*(32/8)*/
+ .buf_size = MMU_COMPRESS_HEADER_SIZE * (16+1),
+ },
+ .mpred_above = {
+ .buf_size = 0x8000,
+ },
+ .mpred_mv = {
+ /*
+ *.buf_size = 0x100000*16,
+ *4k2k , 0x100000 per buffer
+ */
+ /* 4096x2304 , 0x120000 per buffer */
+ .buf_size = 0x120000 * MAX_REF_PIC_NUM,
+ },
+ .rpm = {
+ .buf_size = RPM_BUF_SIZE,
+ },
+ .lmem = {
+ .buf_size = 0x500 * 2,
+ }
+ }
+};
+
+unsigned int get_mmu_mode(void)
+{
+ return mmu_enable;
+}
+
+#ifdef SUPPORT_10BIT
+/* Losless compression body buffer size 4K per 64x32 (jt) */
+static int compute_losless_comp_body_size(int width, int height,
+ int mem_saving_mode)
+{
+ int width_x64;
+ int height_x32;
+ int bsize;
+
+ width_x64 = width + 63;
+ width_x64 >>= 6;
+
+ height_x32 = height + 31;
+ height_x32 >>= 5;
+ if (mem_saving_mode == 1 && mmu_enable)
+ bsize = 3200 * width_x64 * height_x32;
+ else if (mem_saving_mode == 1)
+ bsize = 3072 * width_x64 * height_x32;
+ else
+ bsize = 4096 * width_x64 * height_x32;
+
+ return bsize;
+}
+
+/* Losless compression header buffer size 32bytes per 128x64 (jt) */
+static int compute_losless_comp_header_size(int width, int height)
+{
+ int width_x128;
+ int height_x64;
+ int hsize;
+
+ width_x128 = width + 127;
+ width_x128 >>= 7;
+
+ height_x64 = height + 63;
+ height_x64 >>= 6;
+
+ hsize = 32*width_x128*height_x64;
+
+ return hsize;
+}
+
+#endif
+
+static void init_buff_spec(struct hevc_state_s *hevc,
+ struct BuffInfo_s *buf_spec)
+{
+ buf_spec->ipp.buf_start = buf_spec->start_adr;
+ buf_spec->sao_abv.buf_start =
+ buf_spec->ipp.buf_start + buf_spec->ipp.buf_size;
+
+ buf_spec->sao_vb.buf_start =
+ buf_spec->sao_abv.buf_start + buf_spec->sao_abv.buf_size;
+ buf_spec->short_term_rps.buf_start =
+ buf_spec->sao_vb.buf_start + buf_spec->sao_vb.buf_size;
+ buf_spec->vps.buf_start =
+ buf_spec->short_term_rps.buf_start +
+ buf_spec->short_term_rps.buf_size;
+ buf_spec->sps.buf_start =
+ buf_spec->vps.buf_start + buf_spec->vps.buf_size;
+ buf_spec->pps.buf_start =
+ buf_spec->sps.buf_start + buf_spec->sps.buf_size;
+ buf_spec->sao_up.buf_start =
+ buf_spec->pps.buf_start + buf_spec->pps.buf_size;
+ buf_spec->swap_buf.buf_start =
+ buf_spec->sao_up.buf_start + buf_spec->sao_up.buf_size;
+ buf_spec->swap_buf2.buf_start =
+ buf_spec->swap_buf.buf_start + buf_spec->swap_buf.buf_size;
+ buf_spec->scalelut.buf_start =
+ buf_spec->swap_buf2.buf_start + buf_spec->swap_buf2.buf_size;
+ buf_spec->dblk_para.buf_start =
+ buf_spec->scalelut.buf_start + buf_spec->scalelut.buf_size;
+ buf_spec->dblk_data.buf_start =
+ buf_spec->dblk_para.buf_start + buf_spec->dblk_para.buf_size;
+ buf_spec->mmu_vbh.buf_start =
+ buf_spec->dblk_data.buf_start + buf_spec->dblk_data.buf_size;
+ buf_spec->cm_header.buf_start =
+ buf_spec->mmu_vbh.buf_start + buf_spec->mmu_vbh.buf_size;
+ buf_spec->mpred_above.buf_start =
+ buf_spec->cm_header.buf_start + buf_spec->cm_header.buf_size;
+ buf_spec->mpred_mv.buf_start =
+ buf_spec->mpred_above.buf_start +
+ buf_spec->mpred_above.buf_size;
+ if (debug & H265_DEBUG_SEND_PARAM_WITH_REG) {
+ buf_spec->end_adr =
+ buf_spec->mpred_mv.buf_start +
+ buf_spec->mpred_mv.buf_size;
+ } else {
+ buf_spec->rpm.buf_start =
+ buf_spec->mpred_mv.buf_start +
+ buf_spec->mpred_mv.buf_size;
+ if (debug & H265_DEBUG_UCODE) {
+ buf_spec->lmem.buf_start =
+ buf_spec->rpm.buf_start +
+ buf_spec->rpm.buf_size;
+ buf_spec->end_adr =
+ buf_spec->lmem.buf_start +
+ buf_spec->lmem.buf_size;
+ } else {
+ buf_spec->end_adr =
+ buf_spec->rpm.buf_start +
+ buf_spec->rpm.buf_size;
+ }
+ }
+
+ if (debug) {
+ hevc_print(hevc, 0,
+ "%s workspace (%x %x) size = %x\n", __func__,
+ buf_spec->start_adr, buf_spec->end_adr,
+ buf_spec->end_adr - buf_spec->start_adr);
+ }
+ if (debug) {
+ hevc_print(hevc, 0,
+ "ipp.buf_start :%x\n",
+ buf_spec->ipp.buf_start);
+ hevc_print(hevc, 0,
+ "sao_abv.buf_start :%x\n",
+ buf_spec->sao_abv.buf_start);
+ hevc_print(hevc, 0,
+ "sao_vb.buf_start :%x\n",
+ buf_spec->sao_vb.buf_start);
+ hevc_print(hevc, 0,
+ "short_term_rps.buf_start :%x\n",
+ buf_spec->short_term_rps.buf_start);
+ hevc_print(hevc, 0,
+ "vps.buf_start :%x\n",
+ buf_spec->vps.buf_start);
+ hevc_print(hevc, 0,
+ "sps.buf_start :%x\n",
+ buf_spec->sps.buf_start);
+ hevc_print(hevc, 0,
+ "pps.buf_start :%x\n",
+ buf_spec->pps.buf_start);
+ hevc_print(hevc, 0,
+ "sao_up.buf_start :%x\n",
+ buf_spec->sao_up.buf_start);
+ hevc_print(hevc, 0,
+ "swap_buf.buf_start :%x\n",
+ buf_spec->swap_buf.buf_start);
+ hevc_print(hevc, 0,
+ "swap_buf2.buf_start :%x\n",
+ buf_spec->swap_buf2.buf_start);
+ hevc_print(hevc, 0,
+ "scalelut.buf_start :%x\n",
+ buf_spec->scalelut.buf_start);
+ hevc_print(hevc, 0,
+ "dblk_para.buf_start :%x\n",
+ buf_spec->dblk_para.buf_start);
+ hevc_print(hevc, 0,
+ "dblk_data.buf_start :%x\n",
+ buf_spec->dblk_data.buf_start);
+ hevc_print(hevc, 0,
+ "mpred_above.buf_start :%x\n",
+ buf_spec->mpred_above.buf_start);
+ hevc_print(hevc, 0,
+ "mpred_mv.buf_start :%x\n",
+ buf_spec->mpred_mv.buf_start);
+ if ((debug & H265_DEBUG_SEND_PARAM_WITH_REG) == 0) {
+ hevc_print(hevc, 0,
+ "rpm.buf_start :%x\n",
+ buf_spec->rpm.buf_start);
+ }
+ }
+
+}
+
+enum SliceType {
+ B_SLICE,
+ P_SLICE,
+ I_SLICE
+};
+
+/*USE_BUF_BLOCK*/
+struct BUF_s {
+ int index;
+ unsigned int alloc_flag;
+ /*buffer */
+ unsigned int cma_page_count;
+ unsigned long alloc_addr;
+ unsigned long start_adr;
+ unsigned int size;
+
+ unsigned int free_start_adr;
+} /*BUF_t */;
+
+/* level 6, 6.1 maximum slice number is 800; other is 200 */
+#define MAX_SLICE_NUM 800
+struct PIC_s {
+ int index;
+ int BUF_index;
+ int POC;
+ int decode_idx;
+ int slice_type;
+ int RefNum_L0;
+ int RefNum_L1;
+ int num_reorder_pic;
+ int stream_offset;
+ unsigned char referenced;
+ unsigned char output_mark;
+ unsigned char recon_mark;
+ unsigned char output_ready;
+ unsigned char error_mark;
+ unsigned char used_by_display;
+ /**/ int slice_idx;
+ int m_aiRefPOCList0[MAX_SLICE_NUM][16];
+ int m_aiRefPOCList1[MAX_SLICE_NUM][16];
+ /*buffer */
+ unsigned int header_adr;
+ unsigned long cma_alloc_addr;
+ unsigned int cma_page_count;
+ struct page *alloc_pages;
+ unsigned int mpred_mv_wr_start_addr;
+ unsigned int mc_y_adr;
+ unsigned int mc_u_v_adr;
+#ifdef SUPPORT_10BIT
+ unsigned int comp_body_size;
+ unsigned int dw_y_adr;
+ unsigned int dw_u_v_adr;
+#endif
+ unsigned int buf_size;
+ int mc_canvas_y;
+ int mc_canvas_u_v;
+ int width;
+ int height;
+
+ int y_canvas_index;
+ int uv_canvas_index;
+#ifdef MULTI_INSTANCE_SUPPORT
+ struct canvas_config_s canvas_config[2];
+#endif
+#ifdef LOSLESS_COMPRESS_MODE
+ unsigned int losless_comp_body_size;
+#endif
+ unsigned char pic_struct;
+ int vf_ref;
+} /*PIC_t */;
+
+#define MAX_TILE_COL_NUM 5
+#define MAX_TILE_ROW_NUM 5
+struct tile_s {
+ int width;
+ int height;
+ int start_cu_x;
+ int start_cu_y;
+
+ unsigned int sao_vb_start_addr;
+ unsigned int sao_abv_start_addr;
+};
+
+#define SEI_MASTER_DISPLAY_COLOR_MASK 0x00000001
+
+#define VF_POOL_SIZE 32
+
+#ifdef MULTI_INSTANCE_SUPPORT
+#define DEC_RESULT_NONE 0
+#define DEC_RESULT_DONE 1
+#define DEC_RESULT_AGAIN 2
+#define DEC_RESULT_CONFIG_PARAM 3
+#define DEC_RESULT_ERROR 4
+#define DEC_INIT_PICLIST 5
+#define DEC_UNINIT_PICLIST 6
+static void vh265_work(struct work_struct *work);
+#endif
+struct hevc_state_s {
+#ifdef MULTI_INSTANCE_SUPPORT
+ struct platform_device *platform_dev;
+ void (*vdec_cb)(struct vdec_s *, void *);
+ void *vdec_cb_arg;
+ struct vframe_chunk_s *chunk;
+ int dec_result;
+ struct work_struct work;
+#endif
+ char *provider_name;
+ unsigned char index;
+ struct device *cma_dev;
+ unsigned char m_ins_flag;
+ unsigned long buf_start;
+ u32 buf_size;
+
+ struct BuffInfo_s work_space_buf_store;
+ struct BuffInfo_s *work_space_buf;
+ struct buff_s *mc_buf;
+
+ void *rpm_addr;
+ void *lmem_addr;
+ dma_addr_t rpm_phy_addr;
+ dma_addr_t lmem_phy_addr;
+
+ unsigned int pic_list_init_flag;
+ unsigned int use_cma_flag;
+
+ unsigned short *rpm_ptr;
+ unsigned short *lmem_ptr;
+ unsigned short *debug_ptr;
+ int debug_ptr_size;
+ int pic_w;
+ int pic_h;
+ int lcu_x_num;
+ int lcu_y_num;
+ int lcu_total;
+ int lcu_size;
+ int lcu_size_log2;
+ int lcu_x_num_pre;
+ int lcu_y_num_pre;
+ int first_pic_after_recover;
+
+ int num_tile_col;
+ int num_tile_row;
+ int tile_enabled;
+ int tile_x;
+ int tile_y;
+ int tile_y_x;
+ int tile_start_lcu_x;
+ int tile_start_lcu_y;
+ int tile_width_lcu;
+ int tile_height_lcu;
+
+ int slice_type;
+ unsigned int slice_addr;
+ unsigned int slice_segment_addr;
+
+ unsigned char interlace_flag;
+ unsigned char curr_pic_struct;
+
+ unsigned short sps_num_reorder_pics_0;
+ unsigned short misc_flag0;
+ int m_temporalId;
+ int m_nalUnitType;
+ int TMVPFlag;
+ int isNextSliceSegment;
+ int LDCFlag;
+ int m_pocRandomAccess;
+ int plevel;
+ int MaxNumMergeCand;
+
+ int new_pic;
+ int new_tile;
+ int curr_POC;
+ int iPrevPOC;
+ int iPrevTid0POC;
+ int list_no;
+ int RefNum_L0;
+ int RefNum_L1;
+ int ColFromL0Flag;
+ int LongTerm_Curr;
+ int LongTerm_Col;
+ int Col_POC;
+ int LongTerm_Ref;
+
+ struct PIC_s *cur_pic;
+ struct PIC_s *col_pic;
+ int skip_flag;
+ int decode_idx;
+ int slice_idx;
+ unsigned char have_vps;
+ unsigned char have_sps;
+ unsigned char have_pps;
+ unsigned char have_valid_start_slice;
+ unsigned char wait_buf;
+ unsigned char error_flag;
+ unsigned int error_skip_nal_count;
+
+ unsigned char
+ ignore_bufmgr_error; /* bit 0, for decoding; bit 1, for displaying */
+ int PB_skip_mode;
+ int PB_skip_count_after_decoding;
+#ifdef SUPPORT_10BIT
+ int mem_saving_mode;
+#endif
+#ifdef LOSLESS_COMPRESS_MODE
+ unsigned int losless_comp_body_size;
+#endif
+ int pts_mode;
+ int last_lookup_pts;
+ int last_pts;
+ u64 last_lookup_pts_us64;
+ u64 last_pts_us64;
+ u32 shift_byte_count_lo;
+ u32 shift_byte_count_hi;
+ int pts_mode_switching_count;
+ int pts_mode_recovery_count;
+
+ int buf_num;
+ int pic_num;
+
+ /**/
+ struct buff_s mc_buf_spec;
+ union param_u param;
+
+ struct tile_s m_tile[MAX_TILE_ROW_NUM][MAX_TILE_COL_NUM];
+
+ struct timer_list timer;
+ struct BUF_s m_BUF[MAX_BUF_NUM];
+ u32 used_buf_num;
+ struct PIC_s m_PIC[MAX_REF_PIC_NUM];
+
+ DECLARE_KFIFO(newframe_q, struct vframe_s *, VF_POOL_SIZE);
+ DECLARE_KFIFO(display_q, struct vframe_s *, VF_POOL_SIZE);
+ DECLARE_KFIFO(pending_q, struct vframe_s *, VF_POOL_SIZE);
+ struct vframe_s vfpool[VF_POOL_SIZE];
+
+ u32 stat;
+ u32 frame_width;
+ u32 frame_height;
+ u32 frame_dur;
+ u32 frame_ar;
+ u32 bit_depth_luma;
+ u32 bit_depth_chroma;
+ u32 video_signal_type;
+ u32 saved_resolution;
+ bool get_frame_dur;
+ u32 error_watchdog_count;
+ u32 error_skip_nal_wt_cnt;
+ u32 error_system_watchdog_count;
+
+#ifdef DEBUG_PTS
+ unsigned long pts_missed;
+ unsigned long pts_hit;
+#endif
+ struct dec_sysinfo vh265_amstream_dec_info;
+ unsigned char init_flag;
+ unsigned char uninit_list;
+ u32 start_decoding_time;
+
+ int show_frame_num;
+ struct semaphore h265_sema;
+#ifdef USE_UNINIT_SEMA
+ struct semaphore h265_uninit_done_sema;
+#endif
+ int fatal_error;
+
+ unsigned long pre_last_frame_alloc_addr;
+ unsigned long pre_last_frame_alloc_size;
+ u32 predisp_addr;
+ u32 predisp_size;
+
+ u32 sei_present_flag;
+ void *frame_mmu_map_addr;
+ dma_addr_t frame_mmu_map_phy_addr;
+ unsigned int mmu_mc_buf_start;
+ unsigned int mmu_mc_buf_end;
+ unsigned int mmu_mc_start_4k_adr;
+ void *mmu_box;
+
+ unsigned int last_put_idx_a;
+ unsigned int last_put_idx_b;
+
+ unsigned int dec_status;
+
+
+ /* data for SEI_MASTER_DISPLAY_COLOR */
+ unsigned int primaries[3][2];
+ unsigned int white_point[2];
+ unsigned int luminance[2];
+
+ struct PIC_s *pre_top_pic;
+ struct PIC_s *pre_bot_pic;
+
+} /*hevc_stru_t */;
+
+static int hevc_print(struct hevc_state_s *hevc,
+ int flag, const char *fmt, ...)
+{
+#define HEVC_PRINT_BUF 128
+ unsigned char buf[HEVC_PRINT_BUF];
+ int len = 0;
+#ifdef MULTI_INSTANCE_SUPPORT
+ if (hevc == NULL ||
+ (debug_flag[hevc->index] && flag == 0) ||
+ (debug_flag[hevc->index] & flag)) {
+#endif
+ va_list args;
+
+ va_start(args, fmt);
+ if (hevc)
+ len = sprintf(buf, "%d: ", hevc->index);
+ vsnprintf(buf + len, HEVC_PRINT_BUF - len - 1, fmt, args);
+ pr_info("%s", buf);
+ va_end(args);
+#ifdef MULTI_INSTANCE_SUPPORT
+ }
+#endif
+ return 0;
+}
+
+#undef pr_info
+
+/*#define pr_info(format, ...) hevc_print(hevc, 0, format)*/
+#define pr_info printk
+
+static void set_canvas(struct PIC_s *pic);
+
+static void hevc_init_stru(struct hevc_state_s *hevc,
+ struct BuffInfo_s *buf_spec_i,
+ struct buff_s *mc_buf_i)
+{
+ int i;
+
+ hevc->work_space_buf = buf_spec_i;
+ hevc->mc_buf = mc_buf_i;
+ hevc->rpm_addr = NULL;
+ hevc->lmem_addr = NULL;
+
+ hevc->curr_POC = INVALID_POC;
+
+ hevc->pic_list_init_flag = 0;
+ hevc->use_cma_flag = 0;
+ hevc->decode_idx = 0;
+ hevc->slice_idx = 0;
+ hevc->new_pic = 0;
+ hevc->new_tile = 0;
+ hevc->iPrevPOC = 0;
+ hevc->list_no = 0;
+ /* int m_uiMaxCUWidth = 1<<7; */
+ /* int m_uiMaxCUHeight = 1<<7; */
+ hevc->m_pocRandomAccess = MAX_INT;
+ hevc->tile_enabled = 0;
+ hevc->tile_x = 0;
+ hevc->tile_y = 0;
+ hevc->iPrevTid0POC = 0;
+ hevc->slice_addr = 0;
+ hevc->slice_segment_addr = 0;
+ hevc->skip_flag = 0;
+ hevc->misc_flag0 = 0;
+
+ hevc->cur_pic = NULL;
+ hevc->col_pic = NULL;
+ hevc->wait_buf = 0;
+ hevc->error_flag = 0;
+ hevc->error_skip_nal_count = 0;
+ hevc->have_vps = 0;
+ hevc->have_sps = 0;
+ hevc->have_pps = 0;
+ hevc->have_valid_start_slice = 0;
+
+ hevc->pts_mode = PTS_NORMAL;
+ hevc->last_pts = 0;
+ hevc->last_lookup_pts = 0;
+ hevc->last_pts_us64 = 0;
+ hevc->last_lookup_pts_us64 = 0;
+ hevc->shift_byte_count_lo = 0;
+ hevc->shift_byte_count_hi = 0;
+ hevc->pts_mode_switching_count = 0;
+ hevc->pts_mode_recovery_count = 0;
+
+ hevc->PB_skip_mode = nal_skip_policy & 0x3;
+ hevc->PB_skip_count_after_decoding = (nal_skip_policy >> 16) & 0xffff;
+ if (hevc->PB_skip_mode == 0)
+ hevc->ignore_bufmgr_error = 0x1;
+ else
+ hevc->ignore_bufmgr_error = 0x0;
+
+ for (i = 0; i < MAX_REF_PIC_NUM; i++)
+ hevc->m_PIC[i].index = -1;
+ hevc->buf_num = 0;
+ hevc->pic_num = 0;
+ hevc->lcu_x_num_pre = 0;
+ hevc->lcu_y_num_pre = 0;
+ hevc->first_pic_after_recover = 0;
+
+ hevc->pre_top_pic = NULL;
+ hevc->pre_bot_pic = NULL;
+
+ hevc->sei_present_flag = 0;
+}
+
+static int prepare_display_buf(struct hevc_state_s *hevc, struct PIC_s *pic);
+static int H265_alloc_mmu(struct hevc_state_s *hevc,
+ struct PIC_s *new_pic, unsigned short bit_depth,
+ unsigned int *mmu_index_adr);
+
+static void get_rpm_param(union param_u *params)
+{
+ int i;
+ unsigned int data32;
+
+ for (i = 0; i < 128; i++) {
+ do {
+ data32 = READ_VREG(RPM_CMD_REG);
+ /* pr_info("%x\n", data32); */
+ } while ((data32 & 0x10000) == 0);
+ params->l.data[i] = data32 & 0xffff;
+ /* pr_info("%x\n", data32); */
+ WRITE_VREG(RPM_CMD_REG, 0);
+ }
+}
+
+static struct PIC_s *get_pic_by_POC(struct hevc_state_s *hevc, int POC)
+{
+ int i;
+ struct PIC_s *pic;
+ struct PIC_s *ret_pic = NULL;
+
+ for (i = 0; i < MAX_REF_PIC_NUM; i++) {
+ pic = &hevc->m_PIC[i];
+ if (pic->index == -1)
+ continue;
+ if (pic->POC == POC) {
+ if (ret_pic == NULL)
+ ret_pic = pic;
+ else {
+ if (pic->decode_idx > ret_pic->decode_idx)
+ ret_pic = pic;
+ }
+ }
+ }
+ return ret_pic;
+}
+
+static struct PIC_s *get_ref_pic_by_POC(struct hevc_state_s *hevc, int POC)
+{
+ int i;
+ struct PIC_s *pic;
+ struct PIC_s *ret_pic = NULL;
+
+ for (i = 0; i < MAX_REF_PIC_NUM; i++) {
+ pic = &hevc->m_PIC[i];
+ if (pic->index == -1)
+ continue;
+ if ((pic->POC == POC) && (pic->referenced)) {
+ if (ret_pic == NULL)
+ ret_pic = pic;
+ else {
+ if (pic->decode_idx > ret_pic->decode_idx)
+ ret_pic = pic;
+ }
+ }
+ }
+
+ if (ret_pic == NULL) {
+ if (debug) {
+ pr_info("Wrong, POC of %d is not in referenced list\n",
+ POC);
+ }
+ ret_pic = get_pic_by_POC(hevc, POC);
+ }
+ return ret_pic;
+}
+
+static unsigned int log2i(unsigned int val)
+{
+ unsigned int ret = -1;
+
+ while (val != 0) {
+ val >>= 1;
+ ret++;
+ }
+ return ret;
+}
+
+static int init_buf_spec(struct hevc_state_s *hevc);
+
+static void uninit_mmu_buffers(struct hevc_state_s *hevc)
+{
+ int i;
+
+ decoder_mmu_box_free(hevc->mmu_box);
+ hevc->mmu_box = NULL;
+
+ for (i = 0; i < MAX_REF_PIC_NUM; i++) {
+ struct PIC_s *pic = &hevc->m_PIC[i];
+
+ if (pic->cma_alloc_addr) {
+ codec_mm_free_for_dma(MEM_NAME,
+ pic->cma_alloc_addr);
+ pic->cma_alloc_addr = 0;
+ }
+ }
+}
+
+/*USE_BUF_BLOCK*/
+static void uninit_buf_list(struct hevc_state_s *hevc, bool force_free)
+{
+ int i;
+ unsigned char release_cma_flag = 0;
+ unsigned char blackout = get_blackout_policy();
+ u32 buffer_mode_real =
+ (buffer_mode & ((buffer_mode_dbg >> 16) & 0xfff)) |
+ (buffer_mode_dbg & 0xfff);
+
+ blackout &= ((buffer_mode_dbg >> 28) & 0xf);
+ blackout |= ((buffer_mode_dbg >> 12) & 0xf);
+
+ hevc->predisp_addr = 0;
+
+ if (force_free) {
+ blackout = 0;
+ buffer_mode_real = 0;
+ pr_info("maybe reuinit buf_list, free cma buffer\n");
+ }
+
+ if (buffer_mode_real & 1) {
+ if (blackout == 1)
+ release_cma_flag = 1;
+ } else {
+ if (buffer_mode_real & 2)
+ ;
+ else
+ release_cma_flag = 1;
+ }
+
+ if (blackout != 1) {
+ struct PIC_s *pic;
+
+ if ((release_cma_flag == 1) &&
+ (buffer_mode_real & 8)) {
+ release_cma_flag = 2;
+ }
+
+ msleep(50); /* ensure RDMA for display is done */
+ if (get_cpu_type() >= MESON_CPU_MAJOR_ID_GXBB &&
+ ((double_write_mode == 0) ||
+ (double_write_mode == 3))) {
+ hevc->predisp_addr =
+ READ_VCBUS_REG(AFBC_BODY_BADDR) << 4;
+ } else {
+ struct canvas_s cur_canvas;
+
+ canvas_read((READ_VCBUS_REG(VD1_IF0_CANVAS0) & 0xff),
+ &cur_canvas);
+ hevc->predisp_addr = cur_canvas.addr;
+ }
+
+ for (i = 0; i < MAX_REF_PIC_NUM; i++) {
+ pic = &hevc->m_PIC[i];
+ if (pic->index == -1)
+ continue;
+ if (hevc->predisp_addr == pic->mc_y_adr) {
+ hevc->predisp_size = pic->buf_size;
+ pr_info("%s, set hevc->predisp_size = %d\n",
+ __func__, pic->buf_size);
+ break;
+ }
+ }
+ }
+
+ if (hevc->pre_last_frame_alloc_addr) {
+ if (blackout == 1 || hevc->predisp_addr == 0
+ || hevc->predisp_addr < hevc->pre_last_frame_alloc_addr
+ || hevc->predisp_addr >=
+ (hevc->pre_last_frame_alloc_addr
+ + hevc->pre_last_frame_alloc_size)
+ ) {
+ codec_mm_free_for_dma(MEM_NAME,
+ hevc->pre_last_frame_alloc_addr);
+ pr_info("release pre_last_frame cma buffer %ld\n",
+ hevc->pre_last_frame_alloc_addr);
+ hevc->pre_last_frame_alloc_addr = 0;
+ hevc->pre_last_frame_alloc_size = 0;
+ }
+ }
+
+ if (release_cma_flag) {
+ pr_info("release cma begin\n");
+ for (i = 0; i < hevc->used_buf_num; i++) {
+ if (hevc->m_BUF[i].alloc_addr != 0
+ && hevc->m_BUF[i].cma_page_count > 0) {
+ if ((release_cma_flag == 2)
+ && (hevc->predisp_addr >=
+ hevc->m_BUF[i].start_adr)
+ && (hevc->predisp_addr <
+ (hevc->m_BUF[i].start_adr +
+ hevc->m_BUF[i].size))) {
+ if (hevc->pre_last_frame_alloc_addr)
+ pr_info("last buf not free\n");
+ else {
+ hevc->pre_last_frame_alloc_addr
+ =
+ hevc->m_BUF[i].alloc_addr;
+ hevc->pre_last_frame_alloc_size
+ = hevc->m_BUF[i].size;
+ hevc->m_BUF[i].alloc_addr = 0;
+ hevc->m_BUF[i].
+ cma_page_count = 0;
+ continue;
+ }
+ }
+
+ pr_debug("release cma buffer[%d] (%d %ld)\n", i,
+ hevc->m_BUF[i].cma_page_count,
+ hevc->m_BUF[i].alloc_addr);
+ codec_mm_free_for_dma(MEM_NAME,
+ hevc->m_BUF[i].alloc_addr);
+ hevc->m_BUF[i].alloc_addr = 0;
+ hevc->m_BUF[i].cma_page_count = 0;
+
+ }
+ }
+ pr_info("release cma end\n");
+ }
+ pr_info("%s, blackout %x r%x buf_mode %x r%x rel_cma_flag %x hevc->predisp_addr %d pre_alloc_addr(%ld, %ld)\n",
+ __func__, get_blackout_policy(), blackout,
+ buffer_mode, buffer_mode_real, release_cma_flag,
+ hevc->predisp_addr, hevc->pre_last_frame_alloc_addr,
+ hevc->pre_last_frame_alloc_size);
+ hevc->pic_list_init_flag = 0;
+ hevc->buf_num = 0;
+}
+
+static void init_buf_list(struct hevc_state_s *hevc)
+{
+ int i;
+ int buf_size;
+ int mc_buffer_end = hevc->mc_buf->buf_start + hevc->mc_buf->buf_size;
+
+ if (dynamic_buf_num_margin > 0)
+ hevc->used_buf_num = hevc->sps_num_reorder_pics_0
+ + dynamic_buf_num_margin;
+ else
+ hevc->used_buf_num = max_buf_num;
+
+ if (hevc->used_buf_num > MAX_BUF_NUM)
+ hevc->used_buf_num = MAX_BUF_NUM;
+ if (buf_alloc_size > 0) {
+ buf_size = buf_alloc_size;
+ if (debug)
+ pr_info("[Buffer Management] init_buf_list:\n");
+ } else {
+ int pic_width = buf_alloc_width ? buf_alloc_width : hevc->pic_w;
+ int pic_height =
+ buf_alloc_height ? buf_alloc_height : hevc->pic_h;
+#ifdef LOSLESS_COMPRESS_MODE
+/*SUPPORT_10BIT*/
+ int losless_comp_header_size = compute_losless_comp_header_size
+ (pic_width, pic_height);
+ int losless_comp_body_size = compute_losless_comp_body_size
+ (pic_width, pic_height, hevc->mem_saving_mode);
+ int mc_buffer_size = losless_comp_header_size
+ + losless_comp_body_size;
+ int mc_buffer_size_h = (mc_buffer_size + 0xffff)>>16;
+
+ if (double_write_mode) {
+ int pic_width_dw = ((double_write_mode == 2) ||
+ (double_write_mode == 3)) ?
+ pic_width / 2 : pic_width;
+ int pic_height_dw = ((double_write_mode == 2) ||
+ (double_write_mode == 3)) ?
+ pic_height / 2 : pic_height;
+ int lcu_size = hevc->lcu_size;
+ int pic_width_lcu = (pic_width_dw % lcu_size)
+ ? pic_width_dw / lcu_size
+ + 1 : pic_width_dw / lcu_size;
+ int pic_height_lcu = (pic_height_dw % lcu_size)
+ ? pic_height_dw / lcu_size
+ + 1 : pic_height_dw / lcu_size;
+ int lcu_total = pic_width_lcu * pic_height_lcu;
+ int mc_buffer_size_u_v = lcu_total * lcu_size * lcu_size / 2;
+ int mc_buffer_size_u_v_h = (mc_buffer_size_u_v + 0xffff) >> 16;
+ /*64k alignment*/
+ buf_size = ((mc_buffer_size_u_v_h << 16) * 3);
+ } else
+ buf_size = 0;
+
+ if (mc_buffer_size & 0xffff) { /*64k alignment*/
+ mc_buffer_size_h += 1;
+ }
+ if ((double_write_mode & 0x10) == 0)
+ buf_size += (mc_buffer_size_h << 16);
+#else
+ int lcu_size = hevc->lcu_size;
+ int pic_width_lcu =
+ (pic_width % lcu_size) ? pic_width / lcu_size
+ + 1 : pic_width / lcu_size;
+ int pic_height_lcu =
+ (pic_height % lcu_size) ? pic_height / lcu_size
+ + 1 : pic_height / lcu_size;
+ int lcu_total = pic_width_lcu * pic_height_lcu;
+ int mc_buffer_size_u_v = lcu_total * lcu_size * lcu_size / 2;
+ int mc_buffer_size_u_v_h =
+ (mc_buffer_size_u_v + 0xffff) >> 16;
+ /*64k alignment*/
+ buf_size = (mc_buffer_size_u_v_h << 16) * 3;
+#endif
+ if (debug) {
+ pr_info
+ ("init_buf_list num %d (width %d height %d):\n",
+ hevc->used_buf_num, pic_width, pic_height);
+ }
+ }
+
+ pr_info("allocate begin\n");
+ //get_cma_alloc_ref();
+ atomic_inc(&cma_allocate);
+
+ for (i = 0; i < hevc->used_buf_num; i++) {
+ if (((i + 1) * buf_size) > hevc->mc_buf->buf_size) {
+ if (use_cma)
+ hevc->use_cma_flag = 1;
+ else {
+ if (debug) {
+ pr_info("%s maximum buf size is used\n",
+ __func__);
+ }
+ break;
+ }
+ }
+
+
+ if (!mmu_enable) {
+ hevc->m_BUF[i].alloc_flag = 0;
+ hevc->m_BUF[i].index = i;
+
+ if (use_cma == 2)
+ hevc->use_cma_flag = 1;
+ if (hevc->use_cma_flag) {
+ if ((hevc->m_BUF[i].cma_page_count != 0)
+ && (hevc->m_BUF[i].alloc_addr != 0)
+ && (hevc->m_BUF[i].size != buf_size)) {
+ if ((hevc->predisp_addr >=
+ hevc->m_BUF[i].alloc_addr)
+ && (hevc->predisp_addr <
+ (hevc->m_BUF[i].alloc_addr +
+ hevc->m_BUF[i].size))) {
+ hevc->pre_last_frame_alloc_addr =
+ hevc->m_BUF[i].alloc_addr;
+ hevc->pre_last_frame_alloc_size =
+ hevc->m_BUF[i].size;
+ } else {
+ codec_mm_free_for_dma(MEM_NAME,
+ hevc->m_BUF[i].alloc_addr);
+ pr_info("release cma buffer[%d] (%d %ld)\n",
+ i, hevc->m_BUF[i].cma_page_count,
+ hevc->m_BUF[i].alloc_addr);
+ }
+ hevc->m_BUF[i].alloc_addr = 0;
+ hevc->m_BUF[i].cma_page_count = 0;
+ }
+ if (hevc->m_BUF[i].alloc_addr == 0) {
+ if (!codec_mm_enough_for_size(buf_size, 1)) {
+ /*
+ *not enough mem for buffer.
+ */
+ pr_info("not enought buffer for [%d],%d\n",
+ i, buf_size);
+ hevc->m_BUF[i].cma_page_count = 0;
+ if (i <= 8) {
+ /*
+ *if alloced (i+1)>=9
+ *don't send errors.
+ */
+ hevc->fatal_error |=
+ DECODER_FATAL_ERROR_NO_MEM;
+ }
+ break;
+ }
+ hevc->m_BUF[i].cma_page_count =
+ PAGE_ALIGN(buf_size) / PAGE_SIZE;
+ hevc->m_BUF[i].alloc_addr =
+ codec_mm_alloc_for_dma(
+ MEM_NAME, hevc->m_BUF[i].cma_page_count,
+ 4 + PAGE_SHIFT,
+ CODEC_MM_FLAGS_FOR_VDECODER);
+ if (hevc->m_BUF[i].alloc_addr == 0) {
+ pr_info("alloc cma buffer[%d] fail\n",
+ i);
+ hevc->m_BUF[i].cma_page_count = 0;
+ break;
+ }
+ pr_debug("allocate cma buffer[%d] (%d,%ld,%ld)\n",
+ i,
+ hevc->m_BUF[i].cma_page_count,
+ hevc->m_BUF[i].alloc_addr,
+ hevc->m_BUF[i].start_adr);
+ } else {
+ pr_info("reuse cma buffer[%d] (%d,%ld,%ld)\n",
+ i,
+ hevc->m_BUF[i].cma_page_count,
+ hevc->m_BUF[i].alloc_addr,
+ hevc->m_BUF[i].start_adr);
+ }
+ hevc->m_BUF[i].start_adr = hevc->m_BUF[i].alloc_addr;
+ } else {
+ hevc->m_BUF[i].cma_page_count = 0;
+ hevc->m_BUF[i].alloc_addr = 0;
+ hevc->m_BUF[i].start_adr =
+ hevc->mc_buf->buf_start + i * buf_size;
+ }
+ hevc->m_BUF[i].size = buf_size;
+ hevc->m_BUF[i].free_start_adr = hevc->m_BUF[i].start_adr;
+
+ if (((hevc->m_BUF[i].start_adr + buf_size) > mc_buffer_end)
+ && (hevc->m_BUF[i].alloc_addr == 0)) {
+ if (debug) {
+ pr_info
+ ("Max mc buffer or mpred_mv buffer is used\n");
+ }
+ break;
+ }
+
+ if (debug) {
+ pr_info("Buffer %d: start_adr %p size %x\n", i,
+ (void *)hevc->m_BUF[i].start_adr,
+ hevc->m_BUF[i].size);
+ }
+ }
+ }
+ //put_cma_alloc_ref();
+ atomic_dec(&cma_allocate);
+
+ pr_info("allocate end\n");
+ hevc->buf_num = i;
+
+}
+
+static int config_pic(struct hevc_state_s *hevc, struct PIC_s *pic,
+ unsigned int last_disp_addr)
+{
+ int ret = -1;
+ int i;
+ int pic_width = ((re_config_pic_flag == 0) && buf_alloc_width) ?
+ buf_alloc_width : hevc->pic_w;
+ int pic_height = ((re_config_pic_flag == 0) && buf_alloc_height) ?
+ buf_alloc_height : hevc->pic_h;
+ int lcu_size = hevc->lcu_size;
+ int pic_width_lcu = (pic_width % lcu_size) ? pic_width / lcu_size +
+ 1 : pic_width / lcu_size;
+ int pic_height_lcu = (pic_height % lcu_size) ? pic_height / lcu_size +
+ 1 : pic_height / lcu_size;
+ int lcu_total = pic_width_lcu * pic_height_lcu;
+ int lcu_size_log2 = hevc->lcu_size_log2;
+ /*
+ *int MV_MEM_UNIT=lcu_size_log2==
+ * 6 ? 0x100 : lcu_size_log2==5 ? 0x40 : 0x10;
+ */
+ int MV_MEM_UNIT = lcu_size_log2 == 6 ? 0x200 : lcu_size_log2 ==
+ 5 ? 0x80 : 0x20;
+ int mpred_mv_end = hevc->work_space_buf->mpred_mv.buf_start +
+ hevc->work_space_buf->mpred_mv.buf_size;
+ unsigned int y_adr = 0;
+ int buf_size = 0;
+#ifdef LOSLESS_COMPRESS_MODE
+/*SUPPORT_10BIT*/
+ int losless_comp_header_size =
+ compute_losless_comp_header_size(pic_width,
+ pic_height);
+ int losless_comp_body_size = compute_losless_comp_body_size(pic_width,
+ pic_height, hevc->mem_saving_mode);
+ int mc_buffer_size = losless_comp_header_size + losless_comp_body_size;
+ int mc_buffer_size_h = (mc_buffer_size + 0xffff)>>16;
+ int mc_buffer_size_u_v = 0;
+ int mc_buffer_size_u_v_h = 0;
+
+ if (double_write_mode) {
+ int pic_width_dw = ((double_write_mode == 2) ||
+ (double_write_mode == 3)) ?
+ pic_width / 2 : pic_width;
+ int pic_height_dw = ((double_write_mode == 2) ||
+ (double_write_mode == 3)) ?
+ pic_height / 2 : pic_height;
+ int pic_width_lcu_dw = (pic_width_dw % lcu_size) ?
+ pic_width_dw / lcu_size + 1 :
+ pic_width_dw / lcu_size;
+ int pic_height_lcu_dw = (pic_height_dw % lcu_size) ?
+ pic_height_dw / lcu_size + 1 :
+ pic_height_dw / lcu_size;
+ int lcu_total_dw = pic_width_lcu_dw * pic_height_lcu_dw;
+
+ mc_buffer_size_u_v = lcu_total_dw * lcu_size * lcu_size / 2;
+ mc_buffer_size_u_v_h = (mc_buffer_size_u_v + 0xffff) >> 16;
+ /*64k alignment*/
+ buf_size = ((mc_buffer_size_u_v_h << 16) * 3);
+ }
+ if (mc_buffer_size & 0xffff) { /*64k alignment*/
+ mc_buffer_size_h += 1;
+ }
+ if ((double_write_mode & 0x10) == 0)
+ buf_size += (mc_buffer_size_h << 16);
+#else
+ int mc_buffer_size_u_v = lcu_total * lcu_size * lcu_size / 2;
+ int mc_buffer_size_u_v_h = (mc_buffer_size_u_v + 0xffff) >> 16;
+ /*64k alignment*/
+ buf_size = ((mc_buffer_size_u_v_h << 16) * 3);
+#endif
+
+
+ if (mmu_enable) {
+ if ((hevc->work_space_buf->cm_header.buf_start +
+ ((pic->index + 1)
+ * MMU_COMPRESS_HEADER_SIZE))
+ > (hevc->work_space_buf->cm_header.buf_start +
+ hevc->work_space_buf->cm_header.buf_size)) {
+ pr_info("MMU header_adr allocate fail\n");
+ return -1;
+ }
+
+ pic->header_adr = hevc->work_space_buf->cm_header.buf_start +
+ (pic->index * MMU_COMPRESS_HEADER_SIZE);
+ if (last_disp_addr && pic->header_adr == last_disp_addr) {
+ /*if same as disp add used last one.*/
+ pr_info("same as disp %d: %d\n",
+ pic->index, pic->header_adr);
+ pic->header_adr =
+ hevc->work_space_buf->cm_header.buf_start +
+ (16 * MMU_COMPRESS_HEADER_SIZE);
+ }
+ if (debug&H265_DEBUG_BUFMGR) {
+ pr_info("MMU header_adr %d: %x\n",
+ pic->index, pic->header_adr);
+ }
+ }
+
+
+ if ((hevc->work_space_buf->mpred_mv.buf_start + (((pic->index + 1)
+ * lcu_total) * MV_MEM_UNIT))
+ <= mpred_mv_end) {
+
+ if (!mmu_enable) {
+ for (i = 0; i < hevc->buf_num; i++) {
+ y_adr = ((hevc->m_BUF[i].free_start_adr
+ + 0xffff) >> 16) << 16;
+ /*64k alignment*/
+ if ((y_adr+buf_size) <=
+ (hevc->m_BUF[i].start_adr+
+ hevc->m_BUF[i].size)) {
+ hevc->m_BUF[i].free_start_adr =
+ y_adr + buf_size;
+ break;
+ }
+ }
+ } else
+ i = pic->index;
+ if (i < hevc->buf_num) {
+ pic->POC = INVALID_POC;
+ /*
+ *ensure get_pic_by_POC()
+ *not get the buffer not decoded
+ */
+ pic->BUF_index = i;
+#ifdef LOSLESS_COMPRESS_MODE
+/*SUPPORT_10BIT*/
+ pic->comp_body_size = losless_comp_body_size;
+ pic->buf_size = buf_size;
+
+ if (!mmu_enable)
+ pic->mc_y_adr = y_adr;
+ else if (double_write_mode) {
+ if ((hevc->mc_buf->buf_start
+ + (i + 1) * buf_size)
+ < hevc->mc_buf->buf_end)
+ y_adr = hevc->mc_buf->buf_start
+ + i * buf_size;
+ else {
+ pic->cma_alloc_addr
+ = codec_mm_alloc_for_dma(
+ MEM_NAME, buf_size/PAGE_SIZE,
+ 4 + PAGE_SHIFT,
+ CODEC_MM_FLAGS_FOR_VDECODER);
+ if (pic->cma_alloc_addr)
+ y_adr = pic->cma_alloc_addr;
+ else
+ return -1;
+ }
+ }
+ pic->mc_canvas_y = pic->index;
+ pic->mc_canvas_u_v = pic->index;
+ if (!mmu_enable && double_write_mode & 0x10) {
+ pic->mc_u_v_adr = y_adr +
+ ((mc_buffer_size_u_v_h << 16) << 1);
+
+ pic->mc_canvas_y = (pic->index << 1);
+ pic->mc_canvas_u_v = (pic->index << 1) + 1;
+
+ pic->dw_y_adr = y_adr;
+ pic->dw_u_v_adr = pic->mc_u_v_adr;
+ } else if (double_write_mode) {
+ if (mmu_enable)
+ pic->dw_y_adr = y_adr;
+ else
+ pic->dw_y_adr = y_adr +
+ (mc_buffer_size_h << 16);
+ pic->dw_u_v_adr = pic->dw_y_adr +
+ ((mc_buffer_size_u_v_h << 16) << 1);
+ }
+#else
+ pic->buf_size = (mc_buffer_size_u_v_h << 16) * 3;
+ pic->mc_y_adr = y_adr;
+ pic->mc_u_v_adr = y_adr +
+ ((mc_buffer_size_u_v_h << 16) << 1);
+
+ pic->mc_canvas_y = (pic->index << 1);
+ pic->mc_canvas_u_v = (pic->index << 1) + 1;
+#endif
+ pic->mpred_mv_wr_start_addr =
+ hevc->work_space_buf->mpred_mv.buf_start +
+ ((pic->index * lcu_total)
+ * MV_MEM_UNIT);
+
+
+ if (!mmu_enable && (hevc->predisp_addr != 0) &&
+ (hevc->predisp_size != 0) &&
+ (buffer_mode & 0x4) == 0) {
+ if ((pic->mc_y_adr >=
+ (hevc->predisp_addr +
+ hevc->predisp_size)) ||
+ ((pic->mc_y_adr + pic->buf_size) <=
+ hevc->predisp_addr)) {
+ pic->used_by_display = 0;
+ } else {
+ pic->used_by_display = 1;
+ pr_info("%s, pic[%d] is displayed now, do not use it before it is not displayed\n",
+ __func__, i);
+ }
+ } else
+ pic->used_by_display = 0;
+
+ if (debug) {
+ pr_info
+ ("%s index %d BUF_index %d mc_y_adr %x ",
+ __func__, pic->index,
+ pic->BUF_index, pic->mc_y_adr);
+#ifdef LOSLESS_COMPRESS_MODE
+ pr_info
+ ("comp_body_size %x comp_buf_size %x ",
+ pic->comp_body_size, pic->buf_size);
+ pr_info
+ ("mpred_mv_wr_start_adr %x\n",
+ pic->mpred_mv_wr_start_addr);
+ if (mmu_enable && double_write_mode)
+ pr_info
+ ("mmu double write adr %ld\n",
+ pic->cma_alloc_addr);
+
+#else
+ pr_info
+ ("mc_u_v_adr %x mpred_mv_wr_start_adr %x\n",
+ pic->mc_u_v_adr, pic->mpred_mv_wr_start_addr);
+#endif
+ }
+ ret = 0;
+ }
+ }
+ return ret;
+}
+
+/*
+*free hevc->m_BUF[..] for all free hevc->m_PIC[..]
+* with the different size of hevc->pic_w,hevc->pic_h
+*/
+static int recycle_buf(struct hevc_state_s *hevc)
+{
+ int i, j;
+
+ for (i = 0; i < MAX_REF_PIC_NUM; i++) {
+ struct PIC_s *pic = &hevc->m_PIC[i];
+
+ if (pic->index == -1)
+ continue;
+ if (pic->width != hevc->pic_w || pic->height != hevc->pic_h) {
+ if (pic->output_mark == 0 && pic->referenced == 0
+ && pic->output_ready == 0) {
+ if (mmu_enable) {
+ decoder_mmu_box_free_idx(hevc->mmu_box,
+ pic->index);
+ }
+ pic->BUF_index = -1;
+ if (debug) {
+ pr_info("%s: %d\n", __func__,
+ pic->index);
+ }
+ }
+ }
+ }
+
+ for (i = 0; i < hevc->buf_num; i++) {
+ if (hevc->m_BUF[i].free_start_adr != hevc->m_BUF[i].start_adr) {
+ for (j = 0; j < MAX_REF_PIC_NUM; j++) {
+ struct PIC_s *pic = &hevc->m_PIC[j];
+
+ if (pic->index == -1)
+ continue;
+ if (pic->BUF_index == i)
+ break;
+ }
+ if (j == MAX_REF_PIC_NUM)
+ hevc->m_BUF[i].free_start_adr =
+ hevc->m_BUF[i].start_adr;
+ }
+ }
+ return 0;
+}
+
+//need export..
+extern int get_video0_frame_info(struct vframe_s *vf);
+
+static void init_pic_list(struct hevc_state_s *hevc)
+{
+ int i;
+ struct vframe_s vf;
+ unsigned long flags;
+ unsigned long disp_addr = 0;
+
+ if (!get_video0_frame_info(&vf)) {
+ spin_lock_irqsave(&lock, flags);
+ if (vf.type & VIDTYPE_SCATTER) {
+ /*sc only used header.*/
+ disp_addr = (VSYNC_RD_MPEG_REG(AFBC_HEAD_BADDR) << 4);
+ } else if (vf.type & VIDTYPE_COMPRESS) {
+ /*sc checked body.*/
+ disp_addr = (VSYNC_RD_MPEG_REG(AFBC_BODY_BADDR) << 4);
+ } else {
+ struct canvas_s cur_canvas;
+
+ canvas_read(vf.canvas0Addr & 0xff, &cur_canvas);
+ disp_addr = cur_canvas.addr;
+ }
+ spin_unlock_irqrestore(&lock, flags);
+ }
+ for (i = 0; i < MAX_REF_PIC_NUM; i++) {
+ struct PIC_s *pic = &hevc->m_PIC[i];
+
+ memset(pic, 0, sizeof(struct PIC_s));
+ pic->index = i;
+ pic->BUF_index = -1;
+ if (config_pic(hevc, pic, disp_addr) < 0) {
+ if (debug)
+ pr_info("Config_pic %d fail\n", pic->index);
+ pic->index = -1;
+ break;
+ }
+ pic->width = hevc->pic_w;
+ pic->height = hevc->pic_h;
+ if (double_write_mode)
+ set_canvas(pic);
+ }
+
+ for (; i < MAX_REF_PIC_NUM; i++) {
+ struct PIC_s *pic = &hevc->m_PIC[i];
+
+ memset(pic, 0, sizeof(struct PIC_s));
+ pic->index = -1;
+ pic->BUF_index = -1;
+ }
+
+}
+
+#ifdef LOSLESS_COMPRESS_MODE
+static void init_decode_head_hw(struct hevc_state_s *hevc)
+{
+
+ struct BuffInfo_s *buf_spec = hevc->work_space_buf;
+ unsigned int data32;
+
+ int losless_comp_header_size =
+ compute_losless_comp_header_size(hevc->pic_w,
+ hevc->pic_h);
+ int losless_comp_body_size = compute_losless_comp_body_size(hevc->pic_w,
+ hevc->pic_h, hevc->mem_saving_mode);
+
+ hevc->losless_comp_body_size = losless_comp_body_size;
+
+
+ if (mmu_enable) {
+ WRITE_VREG(HEVCD_MPP_DECOMP_CTL1, (0x1 << 4));
+ WRITE_VREG(HEVCD_MPP_DECOMP_CTL2, 0x0);
+ } else {
+ if (hevc->mem_saving_mode == 1)
+ WRITE_VREG(HEVCD_MPP_DECOMP_CTL1,
+ (1 << 3) | ((workaround_enable & 2) ? 1 : 0));
+ else
+ WRITE_VREG(HEVCD_MPP_DECOMP_CTL1,
+ ((workaround_enable & 2) ? 1 : 0));
+ WRITE_VREG(HEVCD_MPP_DECOMP_CTL2, (losless_comp_body_size >> 5));
+ /*
+ *WRITE_VREG(HEVCD_MPP_DECOMP_CTL3,(0xff<<20) | (0xff<<10) | 0xff);
+ * //8-bit mode
+ */
+ }
+ WRITE_VREG(HEVC_CM_BODY_LENGTH, losless_comp_body_size);
+ WRITE_VREG(HEVC_CM_HEADER_OFFSET, losless_comp_body_size);
+ WRITE_VREG(HEVC_CM_HEADER_LENGTH, losless_comp_header_size);
+
+ if (mmu_enable) {
+ WRITE_VREG(HEVC_SAO_MMU_VH0_ADDR, buf_spec->mmu_vbh.buf_start);
+ WRITE_VREG(HEVC_SAO_MMU_VH1_ADDR,
+ buf_spec->mmu_vbh.buf_start +
+ buf_spec->mmu_vbh.buf_size/2);
+ data32 = READ_VREG(HEVC_SAO_CTRL9);
+ data32 |= 0x1;
+ WRITE_VREG(HEVC_SAO_CTRL9, data32);
+
+ /* use HEVC_CM_HEADER_START_ADDR */
+ data32 = READ_VREG(HEVC_SAO_CTRL5);
+ data32 |= (1<<10);
+ WRITE_VREG(HEVC_SAO_CTRL5, data32);
+ }
+
+
+ pr_info("%s: (%d, %d) body_size 0x%x header_size 0x%x\n",
+ __func__, hevc->pic_w, hevc->pic_h,
+ losless_comp_body_size, losless_comp_header_size);
+
+}
+#endif
+#define HEVCD_MPP_ANC2AXI_TBL_DATA 0x3464
+
+static void init_pic_list_hw(struct hevc_state_s *hevc)
+{
+ int i;
+ int cur_pic_num = MAX_REF_PIC_NUM;
+
+ if (get_cpu_type() >= MESON_CPU_MAJOR_ID_GXL)
+ WRITE_VREG(HEVCD_MPP_ANC2AXI_TBL_CONF_ADDR,
+ (0x1 << 1) | (0x1 << 2));
+ else
+ WRITE_VREG(HEVCD_MPP_ANC2AXI_TBL_CONF_ADDR, 0x0);
+
+ for (i = 0; i < MAX_REF_PIC_NUM; i++) {
+ if (hevc->m_PIC[i].index == -1) {
+ cur_pic_num = i;
+ break;
+ }
+ if (get_cpu_type() >= MESON_CPU_MAJOR_ID_GXL) {
+ if (mmu_enable)
+ WRITE_VREG(HEVCD_MPP_ANC2AXI_TBL_DATA,
+ hevc->m_PIC[i].header_adr>>5);
+ else
+ WRITE_VREG(HEVCD_MPP_ANC2AXI_TBL_DATA,
+ hevc->m_PIC[i].mc_y_adr >> 5);
+ } else
+ WRITE_VREG(HEVCD_MPP_ANC2AXI_TBL_CMD_ADDR,
+ hevc->m_PIC[i].mc_y_adr |
+ (hevc->m_PIC[i].mc_canvas_y << 8) | 0x1);
+ if (double_write_mode & 0x10) {
+ if (get_cpu_type() >= MESON_CPU_MAJOR_ID_GXL) {
+ if (mmu_enable)
+ WRITE_VREG(HEVCD_MPP_ANC2AXI_TBL_DATA,
+ hevc->m_PIC[i].header_adr>>5);
+ else
+ WRITE_VREG(HEVCD_MPP_ANC2AXI_TBL_DATA,
+ hevc->m_PIC[i].mc_u_v_adr >> 5);
+ }
+ else
+ WRITE_VREG(HEVCD_MPP_ANC2AXI_TBL_CMD_ADDR,
+ hevc->m_PIC[i].mc_u_v_adr |
+ (hevc->m_PIC[i].mc_canvas_u_v << 8)
+ | 0x1);
+ }
+ }
+
+ for (; i < MAX_REF_PIC_NUM; i++) {
+ if (get_cpu_type() >= MESON_CPU_MAJOR_ID_GXL) {
+ if (mmu_enable)
+ WRITE_VREG(HEVCD_MPP_ANC2AXI_TBL_DATA,
+ hevc->m_PIC[cur_pic_num-1].header_adr>>5);
+ else
+ WRITE_VREG(HEVCD_MPP_ANC2AXI_TBL_DATA,
+ hevc->m_PIC[cur_pic_num-1].mc_y_adr >> 5);
+#ifndef LOSLESS_COMPRESS_MODE
+ WRITE_VREG(HEVCD_MPP_ANC2AXI_TBL_DATA,
+ hevc->m_PIC[cur_pic_num-1].mc_u_v_adr >> 5);
+#endif
+ } else {
+ WRITE_VREG(HEVCD_MPP_ANC2AXI_TBL_CMD_ADDR,
+ hevc->m_PIC[cur_pic_num-1].mc_y_adr|
+ (hevc->m_PIC[cur_pic_num-1].mc_canvas_y<<8)
+ | 0x1);
+#ifndef LOSLESS_COMPRESS_MODE
+ WRITE_VREG(HEVCD_MPP_ANC2AXI_TBL_CMD_ADDR,
+ hevc->m_PIC[cur_pic_num-1].mc_u_v_adr|
+ (hevc->m_PIC[cur_pic_num-1].mc_canvas_u_v<<8)
+ | 0x1);
+#endif
+ }
+ }
+
+ WRITE_VREG(HEVCD_MPP_ANC2AXI_TBL_CONF_ADDR, 0x1);
+
+ /* Zero out canvas registers in IPP -- avoid simulation X */
+ WRITE_VREG(HEVCD_MPP_ANC_CANVAS_ACCCONFIG_ADDR,
+ (0 << 8) | (0 << 1) | 1);
+ for (i = 0; i < 32; i++)
+ WRITE_VREG(HEVCD_MPP_ANC_CANVAS_DATA_ADDR, 0);
+
+#ifdef LOSLESS_COMPRESS_MODE
+ if ((double_write_mode & 0x10) == 0)
+ init_decode_head_hw(hevc);
+#endif
+
+}
+
+
+static void dump_pic_list(struct hevc_state_s *hevc)
+{
+ int i;
+ struct PIC_s *pic;
+
+ pr_info("pic_list_init_flag is %d\r\n", hevc->pic_list_init_flag);
+ for (i = 0; i < MAX_REF_PIC_NUM; i++) {
+ pic = &hevc->m_PIC[i];
+ if (pic->index == -1)
+ continue;
+ pr_info
+ ("index %d decode_idx:%d, POC:%d, referenced:%d, ",
+ pic->index, pic->decode_idx, pic->POC, pic->referenced);
+ pr_info("num_reorder_pic:%d, output_mark:%d, w/h %d,%d",
+ pic->num_reorder_pic, pic->output_mark,
+ pic->width, pic->height);
+ pr_info("output_ready:%d, mv_wr_start %x vf_ref %d\n",
+ pic->output_ready, pic->mpred_mv_wr_start_addr,
+ pic->vf_ref);
+ }
+}
+
+static struct PIC_s *output_pic(struct hevc_state_s *hevc,
+ unsigned char flush_flag)
+{
+ int num_pic_not_yet_display = 0;
+ int i;
+ struct PIC_s *pic;
+ struct PIC_s *pic_display = NULL;
+
+ if (i_only_flag & 0x4) {
+ for (i = 0; i < MAX_REF_PIC_NUM; i++) {
+ pic = &hevc->m_PIC[i];
+ if ((pic->index == -1) || (pic->POC == INVALID_POC))
+ continue;
+ if (pic->output_mark) {
+ if (pic_display) {
+ if (pic->decode_idx <
+ pic_display->decode_idx)
+ pic_display = pic;
+
+ } else
+ pic_display = pic;
+
+ }
+ }
+ if (pic_display) {
+ pic_display->output_mark = 0;
+ pic_display->recon_mark = 0;
+ pic_display->output_ready = 1;
+ pic_display->referenced = 0;
+ }
+ } else {
+ for (i = 0; i < MAX_REF_PIC_NUM; i++) {
+ pic = &hevc->m_PIC[i];
+ if ((pic->index == -1) || (pic->POC == INVALID_POC))
+ continue;
+ if (pic->output_mark)
+ num_pic_not_yet_display++;
+ }
+
+ for (i = 0; i < MAX_REF_PIC_NUM; i++) {
+ pic = &hevc->m_PIC[i];
+ if ((pic->index == -1) || (pic->POC == INVALID_POC))
+ continue;
+ if (pic->output_mark) {
+ if (pic_display) {
+ if (pic->POC < pic_display->POC)
+ pic_display = pic;
+ else if ((pic->POC == pic_display->POC)
+ && (pic->decode_idx <
+ pic_display->
+ decode_idx))
+ pic_display
+ = pic;
+ } else
+ pic_display = pic;
+ }
+ }
+ if (pic_display) {
+ if ((num_pic_not_yet_display >
+ pic_display->num_reorder_pic)
+ || flush_flag) {
+ pic_display->output_mark = 0;
+ pic_display->recon_mark = 0;
+ pic_display->output_ready = 1;
+ } else if (num_pic_not_yet_display >=
+ (MAX_REF_PIC_NUM - 1)) {
+ pic_display->output_mark = 0;
+ pic_display->recon_mark = 0;
+ pic_display->output_ready = 1;
+ pr_info("Warning, num_reorder_pic %d is byeond buf num\n",
+ pic_display->num_reorder_pic);
+ } else
+ pic_display = NULL;
+ }
+ }
+ return pic_display;
+}
+
+static int config_mc_buffer(struct hevc_state_s *hevc, struct PIC_s *cur_pic)
+{
+ int i;
+ struct PIC_s *pic;
+
+ if (debug & H265_DEBUG_BUFMGR)
+ pr_info("config_mc_buffer entered .....\n");
+ if (cur_pic->slice_type != 2) { /* P and B pic */
+ WRITE_VREG(HEVCD_MPP_ANC_CANVAS_ACCCONFIG_ADDR,
+ (0 << 8) | (0 << 1) | 1);
+ for (i = 0; i < cur_pic->RefNum_L0; i++) {
+ pic =
+ get_ref_pic_by_POC(hevc,
+ cur_pic->
+ m_aiRefPOCList0[cur_pic->
+ slice_idx][i]);
+ if (pic) {
+ if (pic->error_mark)
+ cur_pic->error_mark = 1;
+ WRITE_VREG(HEVCD_MPP_ANC_CANVAS_DATA_ADDR,
+ (pic->mc_canvas_u_v << 16)
+ | (pic->mc_canvas_u_v
+ << 8) |
+ pic->mc_canvas_y);
+ if (debug & H265_DEBUG_BUFMGR) {
+ pr_info
+ ("refid %x mc_canvas_u_v %x",
+ i, pic->mc_canvas_u_v);
+ pr_info(" mc_canvas_y %x\n",
+ pic->mc_canvas_y);
+ }
+ } else {
+ if (debug) {
+ pr_info
+ ("Error %s, %dth poc (%d)",
+ __func__, i,
+ cur_pic->m_aiRefPOCList0[cur_pic->
+ slice_idx][i]);
+ pr_info
+ (" of RPS is not in the pic list0\n");
+ }
+ cur_pic->error_mark = 1;
+ /* dump_lmem(); */
+ }
+ }
+ }
+ if (cur_pic->slice_type == 0) { /* B pic */
+ if (debug & H265_DEBUG_BUFMGR)
+ pr_info("config_mc_buffer RefNum_L1\n");
+ WRITE_VREG(HEVCD_MPP_ANC_CANVAS_ACCCONFIG_ADDR,
+ (16 << 8) | (0 << 1) | 1);
+ for (i = 0; i < cur_pic->RefNum_L1; i++) {
+ pic =
+ get_ref_pic_by_POC(hevc,
+ cur_pic->
+ m_aiRefPOCList1[cur_pic->
+ slice_idx][i]);
+ if (pic) {
+ if (pic->error_mark)
+ cur_pic->error_mark = 1;
+ WRITE_VREG(HEVCD_MPP_ANC_CANVAS_DATA_ADDR,
+ (pic->mc_canvas_u_v << 16)
+ | (pic->mc_canvas_u_v
+ << 8) |
+ pic->mc_canvas_y);
+ if (debug & H265_DEBUG_BUFMGR) {
+ pr_info
+ ("refid %x mc_canvas_u_v %x",
+ i, pic->mc_canvas_u_v);
+ pr_info(" mc_canvas_y %x\n",
+ pic->mc_canvas_y);
+ }
+ } else {
+ if (debug) {
+ pr_info
+ ("Error %s, %dth poc (%d)",
+ __func__, i,
+ cur_pic->m_aiRefPOCList1[cur_pic->
+ slice_idx][i]);
+ pr_info
+ (" of RPS is not in the pic list1\n");
+ }
+ cur_pic->error_mark = 1;
+ /* dump_lmem(); */
+ }
+ }
+ }
+ return 0;
+}
+
+static void apply_ref_pic_set(struct hevc_state_s *hevc, int cur_poc,
+ union param_u *params)
+{
+ int ii, i;
+ int poc_tmp;
+ struct PIC_s *pic;
+ unsigned char is_referenced;
+
+ /* pr_info("%s cur_poc %d\n", __func__, cur_poc); */
+ for (ii = 0; ii < MAX_REF_PIC_NUM; ii++) {
+ pic = &hevc->m_PIC[ii];
+ if (pic->index == -1)
+ continue;
+
+ if ((pic->referenced == 0 || pic->POC == cur_poc))
+ continue;
+ is_referenced = 0;
+ for (i = 0; i < 16; i++) {
+ int delt;
+
+ if (params->p.CUR_RPS[i] & 0x8000)
+ break;
+ delt =
+ params->p.CUR_RPS[i] &
+ ((1 << (RPS_USED_BIT - 1)) - 1);
+ if (params->p.CUR_RPS[i] & (1 << (RPS_USED_BIT - 1))) {
+ poc_tmp =
+ cur_poc - ((1 << (RPS_USED_BIT - 1)) -
+ delt);
+ } else
+ poc_tmp = cur_poc + delt;
+ if (poc_tmp == pic->POC) {
+ is_referenced = 1;
+ /* pr_info("i is %d\n", i); */
+ break;
+ }
+ }
+ if (is_referenced == 0) {
+ pic->referenced = 0;
+ /* pr_info("set poc %d reference to 0\n", pic->POC); */
+ }
+ }
+
+}
+
+static void set_ref_pic_list(struct hevc_state_s *hevc, union param_u *params)
+{
+ struct PIC_s *pic = hevc->cur_pic;
+ int i, rIdx;
+ int num_neg = 0;
+ int num_pos = 0;
+ int total_num;
+ int num_ref_idx_l0_active =
+ (params->p.num_ref_idx_l0_active >
+ MAX_REF_ACTIVE) ? MAX_REF_ACTIVE :
+ params->p.num_ref_idx_l0_active;
+ int num_ref_idx_l1_active =
+ (params->p.num_ref_idx_l1_active >
+ MAX_REF_ACTIVE) ? MAX_REF_ACTIVE :
+ params->p.num_ref_idx_l1_active;
+
+ int RefPicSetStCurr0[16];
+ int RefPicSetStCurr1[16];
+
+ for (i = 0; i < 16; i++) {
+ RefPicSetStCurr0[i] = 0;
+ RefPicSetStCurr1[i] = 0;
+ pic->m_aiRefPOCList0[pic->slice_idx][i] = 0;
+ pic->m_aiRefPOCList1[pic->slice_idx][i] = 0;
+ }
+ for (i = 0; i < 16; i++) {
+ if (params->p.CUR_RPS[i] & 0x8000)
+ break;
+ if ((params->p.CUR_RPS[i] >> RPS_USED_BIT) & 1) {
+ int delt =
+ params->p.CUR_RPS[i] &
+ ((1 << (RPS_USED_BIT - 1)) - 1);
+
+ if ((params->p.CUR_RPS[i] >> (RPS_USED_BIT - 1)) & 1) {
+ RefPicSetStCurr0[num_neg] =
+ pic->POC - ((1 << (RPS_USED_BIT - 1)) -
+ delt);
+ /*
+ *pr_info("RefPicSetStCurr0 %x %x %x\n",
+ *RefPicSetStCurr0[num_neg], pic->POC,
+ *(0x800-(params[i]&0x7ff)));
+ */
+ num_neg++;
+ } else {
+ RefPicSetStCurr1[num_pos] = pic->POC + delt;
+ /*
+ *pr_info("RefPicSetStCurr1 %d\n",
+ *RefPicSetStCurr1[num_pos]);
+ */
+ num_pos++;
+ }
+ }
+ }
+ total_num = num_neg + num_pos;
+ if (debug & H265_DEBUG_BUFMGR) {
+ pr_info
+ ("%s: curpoc %d slice_type %d, total %d ",
+ __func__, pic->POC, params->p.slice_type, total_num);
+ pr_info("num_neg %d num_list0 %d num_list1 %d\n",
+ num_neg, num_ref_idx_l0_active, num_ref_idx_l1_active);
+ }
+
+ if (debug & H265_DEBUG_BUFMGR) {
+ pr_info
+ ("HEVC Stream buf start ");
+ pr_info("%x end %x wr %x rd %x lev %x ctl %x intctl %x\n",
+ READ_VREG(HEVC_STREAM_START_ADDR),
+ READ_VREG(HEVC_STREAM_END_ADDR),
+ READ_VREG(HEVC_STREAM_WR_PTR),
+ READ_VREG(HEVC_STREAM_RD_PTR),
+ READ_VREG(HEVC_STREAM_LEVEL),
+ READ_VREG(HEVC_STREAM_FIFO_CTL),
+ READ_VREG(HEVC_PARSER_INT_CONTROL));
+ }
+
+ if (total_num > 0) {
+ if (params->p.modification_flag & 0x1) {
+ if (debug & H265_DEBUG_BUFMGR)
+ pr_info("ref0 POC (modification):");
+ for (rIdx = 0; rIdx < num_ref_idx_l0_active; rIdx++) {
+ int cIdx = params->p.modification_list[rIdx];
+
+ pic->m_aiRefPOCList0[pic->slice_idx][rIdx] =
+ cIdx >=
+ num_neg ? RefPicSetStCurr1[cIdx -
+ num_neg] :
+ RefPicSetStCurr0[cIdx];
+ if (debug & H265_DEBUG_BUFMGR) {
+ pr_info("%d ",
+ pic->m_aiRefPOCList0[pic->
+ slice_idx]
+ [rIdx]);
+ }
+ }
+ } else {
+ if (debug & H265_DEBUG_BUFMGR)
+ pr_info("ref0 POC:");
+ for (rIdx = 0; rIdx < num_ref_idx_l0_active; rIdx++) {
+ int cIdx = rIdx % total_num;
+
+ pic->m_aiRefPOCList0[pic->slice_idx][rIdx] =
+ cIdx >=
+ num_neg ? RefPicSetStCurr1[cIdx -
+ num_neg] :
+ RefPicSetStCurr0[cIdx];
+ if (debug & H265_DEBUG_BUFMGR) {
+ pr_info("%d ",
+ pic->m_aiRefPOCList0[pic->
+ slice_idx]
+ [rIdx]);
+ }
+ }
+ }
+ if (debug & H265_DEBUG_BUFMGR)
+ pr_info("\n");
+ if (params->p.slice_type == B_SLICE) {
+ if (params->p.modification_flag & 0x2) {
+ if (debug & H265_DEBUG_BUFMGR)
+ pr_info("ref1 POC (modification):");
+ for (rIdx = 0; rIdx < num_ref_idx_l1_active;
+ rIdx++) {
+ int cIdx;
+
+ if (params->p.modification_flag & 0x1) {
+ cIdx =
+ params->p.
+ modification_list
+ [num_ref_idx_l0_active +
+ rIdx];
+ } else {
+ cIdx =
+ params->p.
+ modification_list[rIdx];
+ }
+ pic->m_aiRefPOCList1[pic->
+ slice_idx][rIdx] =
+ cIdx >=
+ num_pos ?
+ RefPicSetStCurr0[cIdx - num_pos]
+ : RefPicSetStCurr1[cIdx];
+ if (debug & H265_DEBUG_BUFMGR) {
+ pr_info("%d ",
+ pic->
+ m_aiRefPOCList1[pic->
+ slice_idx]
+ [rIdx]);
+ }
+ }
+ } else {
+ if (debug & H265_DEBUG_BUFMGR)
+ pr_info("ref1 POC:");
+ for (rIdx = 0; rIdx < num_ref_idx_l1_active;
+ rIdx++) {
+ int cIdx = rIdx % total_num;
+
+ pic->m_aiRefPOCList1[pic->
+ slice_idx][rIdx] =
+ cIdx >=
+ num_pos ?
+ RefPicSetStCurr0[cIdx -
+ num_pos]
+ : RefPicSetStCurr1[cIdx];
+ if (debug & H265_DEBUG_BUFMGR) {
+ pr_info("%d ",
+ pic->
+ m_aiRefPOCList1[pic->
+ slice_idx]
+ [rIdx]);
+ }
+ }
+ }
+ if (debug & H265_DEBUG_BUFMGR)
+ pr_info("\n");
+ }
+ }
+ /*set m_PIC */
+ pic->slice_type = (params->p.slice_type == I_SLICE) ? 2 :
+ (params->p.slice_type == P_SLICE) ? 1 :
+ (params->p.slice_type == B_SLICE) ? 0 : 3;
+ pic->RefNum_L0 = num_ref_idx_l0_active;
+ pic->RefNum_L1 = num_ref_idx_l1_active;
+}
+
+static void update_tile_info(struct hevc_state_s *hevc, int pic_width_cu,
+ int pic_height_cu, int sao_mem_unit,
+ union param_u *params)
+{
+ int i, j;
+ int start_cu_x, start_cu_y;
+ int sao_vb_size = (sao_mem_unit + (2 << 4)) * pic_height_cu;
+ int sao_abv_size = sao_mem_unit * pic_width_cu;
+
+ hevc->tile_enabled = params->p.tiles_enabled_flag & 1;
+ if (params->p.tiles_enabled_flag & 1) {
+ hevc->num_tile_col = params->p.num_tile_columns_minus1 + 1;
+ hevc->num_tile_row = params->p.num_tile_rows_minus1 + 1;
+
+ if (hevc->num_tile_row > MAX_TILE_ROW_NUM
+ || hevc->num_tile_row <= 0) {
+ hevc->num_tile_row = 1;
+ pr_info("%s: num_tile_rows_minus1 (%d) error!!\n",
+ __func__, params->p.num_tile_rows_minus1);
+ }
+ if (hevc->num_tile_col > MAX_TILE_COL_NUM
+ || hevc->num_tile_col <= 0) {
+ hevc->num_tile_col = 1;
+ pr_info("%s: num_tile_columns_minus1 (%d) error!!\n",
+ __func__, params->p.num_tile_columns_minus1);
+ }
+ if (debug & H265_DEBUG_BUFMGR) {
+ pr_info
+ ("%s pic_w_cu %d pic_h_cu %d tile_enabled ",
+ __func__, pic_width_cu, pic_height_cu);
+ pr_info("num_tile_col %d num_tile_row %d:\n",
+ hevc->num_tile_col, hevc->num_tile_row);
+ }
+
+ if (params->p.tiles_enabled_flag & 2) { /* uniform flag */
+ int w = pic_width_cu / hevc->num_tile_col;
+ int h = pic_height_cu / hevc->num_tile_row;
+
+ start_cu_y = 0;
+ for (i = 0; i < hevc->num_tile_row; i++) {
+ start_cu_x = 0;
+ for (j = 0; j < hevc->num_tile_col; j++) {
+ if (j == (hevc->num_tile_col - 1)) {
+ hevc->m_tile[i][j].width =
+ pic_width_cu -
+ start_cu_x;
+ } else
+ hevc->m_tile[i][j].width = w;
+ if (i == (hevc->num_tile_row - 1)) {
+ hevc->m_tile[i][j].height =
+ pic_height_cu -
+ start_cu_y;
+ } else
+ hevc->m_tile[i][j].height = h;
+ hevc->m_tile[i][j].start_cu_x
+ = start_cu_x;
+ hevc->m_tile[i][j].start_cu_y
+ = start_cu_y;
+ hevc->m_tile[i][j].sao_vb_start_addr =
+ hevc->work_space_buf->sao_vb.
+ buf_start + j * sao_vb_size;
+ hevc->m_tile[i][j].sao_abv_start_addr =
+ hevc->work_space_buf->sao_abv.
+ buf_start + i * sao_abv_size;
+ if (debug & H265_DEBUG_BUFMGR) {
+ pr_info
+ ("{y=%d, x=%d w %d h %d ",
+ i, j, hevc->m_tile[i][j].width,
+ hevc->m_tile[i][j].height);
+ pr_info
+ ("start_x %d start_y %d ",
+ hevc->m_tile[i][j].start_cu_x,
+ hevc->m_tile[i][j].start_cu_y);
+ pr_info
+ ("sao_vb_start 0x%x ",
+ hevc->m_tile[i][j].
+ sao_vb_start_addr);
+ pr_info
+ ("sao_abv_start 0x%x}\n",
+ hevc->m_tile[i][j].
+ sao_abv_start_addr);
+ }
+ start_cu_x += hevc->m_tile[i][j].width;
+
+ }
+ start_cu_y += hevc->m_tile[i][0].height;
+ }
+ } else {
+ start_cu_y = 0;
+ for (i = 0; i < hevc->num_tile_row; i++) {
+ start_cu_x = 0;
+ for (j = 0; j < hevc->num_tile_col; j++) {
+ if (j == (hevc->num_tile_col - 1)) {
+ hevc->m_tile[i][j].width =
+ pic_width_cu -
+ start_cu_x;
+ } else {
+ hevc->m_tile[i][j].width =
+ params->p.tile_width[j];
+ }
+ if (i == (hevc->num_tile_row - 1)) {
+ hevc->m_tile[i][j].height =
+ pic_height_cu -
+ start_cu_y;
+ } else {
+ hevc->m_tile[i][j].height =
+ params->
+ p.tile_height[i];
+ }
+ hevc->m_tile[i][j].start_cu_x
+ = start_cu_x;
+ hevc->m_tile[i][j].start_cu_y
+ = start_cu_y;
+ hevc->m_tile[i][j].sao_vb_start_addr =
+ hevc->work_space_buf->sao_vb.
+ buf_start + j * sao_vb_size;
+ hevc->m_tile[i][j].sao_abv_start_addr =
+ hevc->work_space_buf->sao_abv.
+ buf_start + i * sao_abv_size;
+ if (debug & H265_DEBUG_BUFMGR) {
+ pr_info
+ ("{y=%d, x=%d w %d h %d ",
+ i, j, hevc->m_tile[i][j].width,
+ hevc->m_tile[i][j].height);
+ pr_info
+ ("start_x %d start_y %d ",
+ hevc->m_tile[i][j].start_cu_x,
+ hevc->m_tile[i][j].start_cu_y);
+ pr_info
+ ("sao_vb_start 0x%x ",
+ hevc->m_tile[i][j].
+ sao_vb_start_addr);
+ pr_info
+ ("sao_abv_start 0x%x}\n",
+ hevc->m_tile[i][j].
+ sao_abv_start_addr);
+
+ }
+ start_cu_x += hevc->m_tile[i][j].width;
+ }
+ start_cu_y += hevc->m_tile[i][0].height;
+ }
+ }
+ } else {
+ hevc->num_tile_col = 1;
+ hevc->num_tile_row = 1;
+ hevc->m_tile[0][0].width = pic_width_cu;
+ hevc->m_tile[0][0].height = pic_height_cu;
+ hevc->m_tile[0][0].start_cu_x = 0;
+ hevc->m_tile[0][0].start_cu_y = 0;
+ hevc->m_tile[0][0].sao_vb_start_addr =
+ hevc->work_space_buf->sao_vb.buf_start;
+ hevc->m_tile[0][0].sao_abv_start_addr =
+ hevc->work_space_buf->sao_abv.buf_start;
+ }
+}
+
+static int get_tile_index(struct hevc_state_s *hevc, int cu_adr,
+ int pic_width_lcu)
+{
+ int cu_x;
+ int cu_y;
+ int tile_x = 0;
+ int tile_y = 0;
+ int i;
+
+ if (pic_width_lcu == 0) {
+ if (debug) {
+ pr_info
+ ("%s Error, pic_width_lcu is 0, pic_w %d, pic_h %d\n",
+ __func__, hevc->pic_w, hevc->pic_h);
+ }
+ return -1;
+ }
+ cu_x = cu_adr % pic_width_lcu;
+ cu_y = cu_adr / pic_width_lcu;
+ if (hevc->tile_enabled) {
+ for (i = 0; i < hevc->num_tile_col; i++) {
+ if (cu_x >= hevc->m_tile[0][i].start_cu_x)
+ tile_x = i;
+ else
+ break;
+ }
+ for (i = 0; i < hevc->num_tile_row; i++) {
+ if (cu_y >= hevc->m_tile[i][0].start_cu_y)
+ tile_y = i;
+ else
+ break;
+ }
+ }
+ return (tile_x) | (tile_y << 8);
+}
+
+static void print_scratch_error(int error_num)
+{
+#if 0
+ if (debug) {
+ pr_info(" ERROR : HEVC_ASSIST_SCRATCH_TEST Error : %d\n",
+ error_num);
+ }
+#endif
+}
+
+static void hevc_config_work_space_hw(struct hevc_state_s *hevc)
+{
+ struct BuffInfo_s *buf_spec = hevc->work_space_buf;
+
+ if (debug)
+ pr_info("%s %x %x %x %x %x %x %x %x %x %x %x %x\n",
+ __func__,
+ buf_spec->ipp.buf_start,
+ buf_spec->start_adr,
+ buf_spec->short_term_rps.buf_start,
+ buf_spec->vps.buf_start,
+ buf_spec->sps.buf_start,
+ buf_spec->pps.buf_start,
+ buf_spec->sao_up.buf_start,
+ buf_spec->swap_buf.buf_start,
+ buf_spec->swap_buf2.buf_start,
+ buf_spec->scalelut.buf_start,
+ buf_spec->dblk_para.buf_start,
+ buf_spec->dblk_data.buf_start);
+ WRITE_VREG(HEVCD_IPP_LINEBUFF_BASE, buf_spec->ipp.buf_start);
+ if ((debug & H265_DEBUG_SEND_PARAM_WITH_REG) == 0)
+ WRITE_VREG(HEVC_RPM_BUFFER, (u32)hevc->rpm_phy_addr);
+ WRITE_VREG(HEVC_SHORT_TERM_RPS, buf_spec->short_term_rps.buf_start);
+ WRITE_VREG(HEVC_VPS_BUFFER, buf_spec->vps.buf_start);
+ WRITE_VREG(HEVC_SPS_BUFFER, buf_spec->sps.buf_start);
+ WRITE_VREG(HEVC_PPS_BUFFER, buf_spec->pps.buf_start);
+ WRITE_VREG(HEVC_SAO_UP, buf_spec->sao_up.buf_start);
+ if (mmu_enable)
+ WRITE_VREG(H265_MMU_MAP_BUFFER, hevc->frame_mmu_map_phy_addr);
+ else
+ WRITE_VREG(HEVC_STREAM_SWAP_BUFFER,
+ buf_spec->swap_buf.buf_start);
+ WRITE_VREG(HEVC_STREAM_SWAP_BUFFER2, buf_spec->swap_buf2.buf_start);
+ WRITE_VREG(HEVC_SCALELUT, buf_spec->scalelut.buf_start);
+ /* cfg_p_addr */
+ WRITE_VREG(HEVC_DBLK_CFG4, buf_spec->dblk_para.buf_start);
+ /* cfg_d_addr */
+ WRITE_VREG(HEVC_DBLK_CFG5, buf_spec->dblk_data.buf_start);
+
+ if (debug & H265_DEBUG_UCODE)
+ WRITE_VREG(LMEM_DUMP_ADR, (u32)hevc->lmem_phy_addr);
+
+}
+
+static void hevc_init_decoder_hw(struct hevc_state_s *hevc,
+ int decode_pic_begin, int decode_pic_num)
+{
+ unsigned int data32;
+ int i;
+
+#if 1
+ /* m8baby test1902 */
+ if (debug & H265_DEBUG_BUFMGR)
+ pr_info("[test.c] Test Parser Register Read/Write\n");
+ data32 = READ_VREG(HEVC_PARSER_VERSION);
+ if (data32 != 0x00010001) {
+ print_scratch_error(25);
+ return;
+ }
+ WRITE_VREG(HEVC_PARSER_VERSION, 0x5a5a55aa);
+ data32 = READ_VREG(HEVC_PARSER_VERSION);
+ if (data32 != 0x5a5a55aa) {
+ print_scratch_error(26);
+ return;
+ }
+#if 0
+ /* test Parser Reset */
+ /* reset iqit to start mem init again */
+ WRITE_VREG(DOS_SW_RESET3, (1 << 14) |
+ (1 << 3) /* reset_whole parser */
+ );
+ WRITE_VREG(DOS_SW_RESET3, 0); /* clear reset_whole parser */
+ data32 = READ_VREG(HEVC_PARSER_VERSION);
+ if (data32 != 0x00010001)
+ pr_info("Test Parser Fatal Error\n");
+#endif
+ /* reset iqit to start mem init again */
+ WRITE_VREG(DOS_SW_RESET3, (1 << 14)
+ );
+ CLEAR_VREG_MASK(HEVC_CABAC_CONTROL, 1);
+ CLEAR_VREG_MASK(HEVC_PARSER_CORE_CONTROL, 1);
+
+#endif
+
+ if (debug & H265_DEBUG_BUFMGR)
+ pr_info("[test.c] Enable BitStream Fetch\n");
+ data32 = READ_VREG(HEVC_STREAM_CONTROL);
+ data32 = data32 | (1 << 0) /* stream_fetch_enable */
+ ;
+ if (hevc->m_ins_flag)
+ data32 |= (7 << 4); /*endian control*/
+ WRITE_VREG(HEVC_STREAM_CONTROL, data32);
+
+ data32 = READ_VREG(HEVC_SHIFT_STARTCODE);
+ if (data32 != 0x00000100) {
+ print_scratch_error(29);
+ return;
+ }
+ data32 = READ_VREG(HEVC_SHIFT_EMULATECODE);
+ if (data32 != 0x00000300) {
+ print_scratch_error(30);
+ return;
+ }
+ WRITE_VREG(HEVC_SHIFT_STARTCODE, 0x12345678);
+ WRITE_VREG(HEVC_SHIFT_EMULATECODE, 0x9abcdef0);
+ data32 = READ_VREG(HEVC_SHIFT_STARTCODE);
+ if (data32 != 0x12345678) {
+ print_scratch_error(31);
+ return;
+ }
+ data32 = READ_VREG(HEVC_SHIFT_EMULATECODE);
+ if (data32 != 0x9abcdef0) {
+ print_scratch_error(32);
+ return;
+ }
+ WRITE_VREG(HEVC_SHIFT_STARTCODE, 0x00000100);
+ WRITE_VREG(HEVC_SHIFT_EMULATECODE, 0x00000300);
+
+ if (debug & H265_DEBUG_BUFMGR)
+ pr_info("[test.c] Enable HEVC Parser Interrupt\n");
+ data32 = READ_VREG(HEVC_PARSER_INT_CONTROL);
+ data32 &= 0x03ffffff;
+ data32 = data32 | (3 << 29) | (2 << 26) | (1 << 24)
+ | /* stream_buffer_empty_int_amrisc_enable */
+ (1 << 22) | /* stream_fifo_empty_int_amrisc_enable*/
+ (1 << 7) | /* dec_done_int_cpu_enable */
+ (1 << 4) | /* startcode_found_int_cpu_enable */
+ (0 << 3) | /* startcode_found_int_amrisc_enable */
+ (1 << 0) /* parser_int_enable */
+ ;
+ WRITE_VREG(HEVC_PARSER_INT_CONTROL, data32);
+
+ if (debug & H265_DEBUG_BUFMGR)
+ pr_info("[test.c] Enable HEVC Parser Shift\n");
+
+ data32 = READ_VREG(HEVC_SHIFT_STATUS);
+ data32 = data32 | (1 << 1) | /* emulation_check_on */
+ (1 << 0) /* startcode_check_on */
+ ;
+ WRITE_VREG(HEVC_SHIFT_STATUS, data32);
+
+ WRITE_VREG(HEVC_SHIFT_CONTROL, (3 << 6) |/* sft_valid_wr_position */
+ (2 << 4) | /* emulate_code_length_sub_1 */
+ (2 << 1) | /* start_code_length_sub_1 */
+ (1 << 0) /* stream_shift_enable */
+ );
+
+ WRITE_VREG(HEVC_CABAC_CONTROL, (1 << 0) /* cabac_enable */
+ );
+ /* hevc_parser_core_clk_en */
+ WRITE_VREG(HEVC_PARSER_CORE_CONTROL, (1 << 0)
+ );
+
+ WRITE_VREG(HEVC_DEC_STATUS_REG, 0);
+
+ /* Initial IQIT_SCALELUT memory -- just to avoid X in simulation */
+ if (debug & H265_DEBUG_BUFMGR) {
+ pr_info
+ ("[test.c] Initial IQIT_SCALELUT memory --");
+ pr_info
+ (" just to avoid X in simulation...\n");
+ }
+ WRITE_VREG(HEVC_IQIT_SCALELUT_WR_ADDR, 0); /* cfg_p_addr */
+ for (i = 0; i < 1024; i++)
+ WRITE_VREG(HEVC_IQIT_SCALELUT_DATA, 0);
+
+#ifdef ENABLE_SWAP_TEST
+ WRITE_VREG(HEVC_STREAM_SWAP_TEST, 100);
+#else
+ WRITE_VREG(HEVC_STREAM_SWAP_TEST, 0);
+#endif
+
+ WRITE_VREG(HEVC_DECODE_PIC_BEGIN_REG, 0);
+ /*WRITE_VREG(HEVC_DECODE_PIC_NUM_REG, 0xffffffff);*/
+ /*WRITE_VREG(HEVC_DECODE_SIZE, 0);*/
+ WRITE_VREG(HEVC_DECODE_SIZE, 0);
+ /* Send parser_cmd */
+ if (debug)
+ pr_info("[test.c] SEND Parser Command ...\n");
+ WRITE_VREG(HEVC_PARSER_CMD_WRITE, (1 << 16) | (0 << 0));
+ for (i = 0; i < PARSER_CMD_NUMBER; i++)
+ WRITE_VREG(HEVC_PARSER_CMD_WRITE, parser_cmd[i]);
+
+ WRITE_VREG(HEVC_PARSER_CMD_SKIP_0, PARSER_CMD_SKIP_CFG_0);
+ WRITE_VREG(HEVC_PARSER_CMD_SKIP_1, PARSER_CMD_SKIP_CFG_1);
+ WRITE_VREG(HEVC_PARSER_CMD_SKIP_2, PARSER_CMD_SKIP_CFG_2);
+
+ WRITE_VREG(HEVC_PARSER_IF_CONTROL,
+ /* (1 << 8) | // sao_sw_pred_enable */
+ (1 << 5) | /* parser_sao_if_en */
+ (1 << 2) | /* parser_mpred_if_en */
+ (1 << 0) /* parser_scaler_if_en */
+ );
+
+ /* Changed to Start MPRED in microcode */
+ /*
+ *pr_info("[test.c] Start MPRED\n");
+ *WRITE_VREG(HEVC_MPRED_INT_STATUS,
+ *(1<<31)
+ *);
+ */
+
+ if (debug)
+ pr_info("[test.c] Reset IPP\n");
+ WRITE_VREG(HEVCD_IPP_TOP_CNTL, (0 << 1) | /* enable ipp */
+ (1 << 0) /* software reset ipp and mpp */
+ );
+ WRITE_VREG(HEVCD_IPP_TOP_CNTL, (1 << 1) | /* enable ipp */
+ (0 << 0) /* software reset ipp and mpp */
+ );
+
+ if (double_write_mode & 0x10)
+ WRITE_VREG(HEVCD_MPP_DECOMP_CTL1,
+ 0x1 << 31 /*/Enable NV21 reference read mode for MC*/
+ );
+
+}
+
+static void decoder_hw_reset(void)
+{
+ int i;
+ unsigned int data32;
+ /* reset iqit to start mem init again */
+ WRITE_VREG(DOS_SW_RESET3, (1 << 14)
+ );
+ CLEAR_VREG_MASK(HEVC_CABAC_CONTROL, 1);
+ CLEAR_VREG_MASK(HEVC_PARSER_CORE_CONTROL, 1);
+
+ data32 = READ_VREG(HEVC_STREAM_CONTROL);
+ data32 = data32 | (1 << 0) /* stream_fetch_enable */
+ ;
+ WRITE_VREG(HEVC_STREAM_CONTROL, data32);
+
+ data32 = READ_VREG(HEVC_SHIFT_STARTCODE);
+ if (data32 != 0x00000100) {
+ print_scratch_error(29);
+ return;
+ }
+ data32 = READ_VREG(HEVC_SHIFT_EMULATECODE);
+ if (data32 != 0x00000300) {
+ print_scratch_error(30);
+ return;
+ }
+ WRITE_VREG(HEVC_SHIFT_STARTCODE, 0x12345678);
+ WRITE_VREG(HEVC_SHIFT_EMULATECODE, 0x9abcdef0);
+ data32 = READ_VREG(HEVC_SHIFT_STARTCODE);
+ if (data32 != 0x12345678) {
+ print_scratch_error(31);
+ return;
+ }
+ data32 = READ_VREG(HEVC_SHIFT_EMULATECODE);
+ if (data32 != 0x9abcdef0) {
+ print_scratch_error(32);
+ return;
+ }
+ WRITE_VREG(HEVC_SHIFT_STARTCODE, 0x00000100);
+ WRITE_VREG(HEVC_SHIFT_EMULATECODE, 0x00000300);
+
+ data32 = READ_VREG(HEVC_PARSER_INT_CONTROL);
+ data32 &= 0x03ffffff;
+ data32 = data32 | (3 << 29) | (2 << 26) | (1 << 24)
+ | /* stream_buffer_empty_int_amrisc_enable */
+ (1 << 22) | /*stream_fifo_empty_int_amrisc_enable */
+ (1 << 7) | /* dec_done_int_cpu_enable */
+ (1 << 4) | /* startcode_found_int_cpu_enable */
+ (0 << 3) | /* startcode_found_int_amrisc_enable */
+ (1 << 0) /* parser_int_enable */
+ ;
+ WRITE_VREG(HEVC_PARSER_INT_CONTROL, data32);
+
+ data32 = READ_VREG(HEVC_SHIFT_STATUS);
+ data32 = data32 | (1 << 1) | /* emulation_check_on */
+ (1 << 0) /* startcode_check_on */
+ ;
+ WRITE_VREG(HEVC_SHIFT_STATUS, data32);
+
+ WRITE_VREG(HEVC_SHIFT_CONTROL, (3 << 6) |/* sft_valid_wr_position */
+ (2 << 4) | /* emulate_code_length_sub_1 */
+ (2 << 1) | /* start_code_length_sub_1 */
+ (1 << 0) /* stream_shift_enable */
+ );
+
+ WRITE_VREG(HEVC_CABAC_CONTROL, (1 << 0) /* cabac_enable */
+ );
+ /* hevc_parser_core_clk_en */
+ WRITE_VREG(HEVC_PARSER_CORE_CONTROL, (1 << 0)
+ );
+
+ /* Initial IQIT_SCALELUT memory -- just to avoid X in simulation */
+ WRITE_VREG(HEVC_IQIT_SCALELUT_WR_ADDR, 0); /* cfg_p_addr */
+ for (i = 0; i < 1024; i++)
+ WRITE_VREG(HEVC_IQIT_SCALELUT_DATA, 0);
+
+ /* Send parser_cmd */
+ WRITE_VREG(HEVC_PARSER_CMD_WRITE, (1 << 16) | (0 << 0));
+ for (i = 0; i < PARSER_CMD_NUMBER; i++)
+ WRITE_VREG(HEVC_PARSER_CMD_WRITE, parser_cmd[i]);
+
+ WRITE_VREG(HEVC_PARSER_CMD_SKIP_0, PARSER_CMD_SKIP_CFG_0);
+ WRITE_VREG(HEVC_PARSER_CMD_SKIP_1, PARSER_CMD_SKIP_CFG_1);
+ WRITE_VREG(HEVC_PARSER_CMD_SKIP_2, PARSER_CMD_SKIP_CFG_2);
+
+ WRITE_VREG(HEVC_PARSER_IF_CONTROL,
+ /* (1 << 8) | // sao_sw_pred_enable */
+ (1 << 5) | /* parser_sao_if_en */
+ (1 << 2) | /* parser_mpred_if_en */
+ (1 << 0) /* parser_scaler_if_en */
+ );
+
+ WRITE_VREG(HEVCD_IPP_TOP_CNTL, (0 << 1) | /* enable ipp */
+ (1 << 0) /* software reset ipp and mpp */
+ );
+ WRITE_VREG(HEVCD_IPP_TOP_CNTL, (1 << 1) | /* enable ipp */
+ (0 << 0) /* software reset ipp and mpp */
+ );
+}
+
+#ifdef CONFIG_HEVC_CLK_FORCED_ON
+static void config_hevc_clk_forced_on(void)
+{
+ unsigned int rdata32;
+ /* IQIT */
+ rdata32 = READ_VREG(HEVC_IQIT_CLK_RST_CTRL);
+ WRITE_VREG(HEVC_IQIT_CLK_RST_CTRL, rdata32 | (0x1 << 2));
+
+ /* DBLK */
+ rdata32 = READ_VREG(HEVC_DBLK_CFG0);
+ WRITE_VREG(HEVC_DBLK_CFG0, rdata32 | (0x1 << 2));
+
+ /* SAO */
+ rdata32 = READ_VREG(HEVC_SAO_CTRL1);
+ WRITE_VREG(HEVC_SAO_CTRL1, rdata32 | (0x1 << 2));
+
+ /* MPRED */
+ rdata32 = READ_VREG(HEVC_MPRED_CTRL1);
+ WRITE_VREG(HEVC_MPRED_CTRL1, rdata32 | (0x1 << 24));
+
+ /* PARSER */
+ rdata32 = READ_VREG(HEVC_STREAM_CONTROL);
+ WRITE_VREG(HEVC_STREAM_CONTROL, rdata32 | (0x1 << 15));
+ rdata32 = READ_VREG(HEVC_SHIFT_CONTROL);
+ WRITE_VREG(HEVC_SHIFT_CONTROL, rdata32 | (0x1 << 15));
+ rdata32 = READ_VREG(HEVC_CABAC_CONTROL);
+ WRITE_VREG(HEVC_CABAC_CONTROL, rdata32 | (0x1 << 13));
+ rdata32 = READ_VREG(HEVC_PARSER_CORE_CONTROL);
+ WRITE_VREG(HEVC_PARSER_CORE_CONTROL, rdata32 | (0x1 << 15));
+ rdata32 = READ_VREG(HEVC_PARSER_INT_CONTROL);
+ WRITE_VREG(HEVC_PARSER_INT_CONTROL, rdata32 | (0x1 << 15));
+ rdata32 = READ_VREG(HEVC_PARSER_IF_CONTROL);
+ WRITE_VREG(HEVC_PARSER_IF_CONTROL,
+ rdata32 | (0x3 << 5) | (0x3 << 2) | (0x3 << 0));
+
+ /* IPP */
+ rdata32 = READ_VREG(HEVCD_IPP_DYNCLKGATE_CONFIG);
+ WRITE_VREG(HEVCD_IPP_DYNCLKGATE_CONFIG, rdata32 | 0xffffffff);
+
+ /* MCRCC */
+ rdata32 = READ_VREG(HEVCD_MCRCC_CTL1);
+ WRITE_VREG(HEVCD_MCRCC_CTL1, rdata32 | (0x1 << 3));
+}
+#endif
+
+#ifdef MCRCC_ENABLE
+static void config_mcrcc_axi_hw(struct hevc_state_s *hevc, int slice_type)
+{
+ unsigned int rdata32;
+ unsigned int rdata32_2;
+ int l0_cnt = 0;
+ int l1_cnt = 0x7fff;
+
+ if (double_write_mode & 0x10) {
+ l0_cnt = hevc->cur_pic->RefNum_L0;
+ l1_cnt = hevc->cur_pic->RefNum_L1;
+ }
+
+ WRITE_VREG(HEVCD_MCRCC_CTL1, 0x2); /* reset mcrcc */
+
+ if (slice_type == 2) { /* I-PIC */
+ /* remove reset -- disables clock */
+ WRITE_VREG(HEVCD_MCRCC_CTL1, 0x0);
+ return;
+ }
+
+ if (slice_type == 0) { /* B-PIC */
+ /* Programme canvas0 */
+ WRITE_VREG(HEVCD_MPP_ANC_CANVAS_ACCCONFIG_ADDR,
+ (0 << 8) | (0 << 1) | 0);
+ rdata32 = READ_VREG(HEVCD_MPP_ANC_CANVAS_DATA_ADDR);
+ rdata32 = rdata32 & 0xffff;
+ rdata32 = rdata32 | (rdata32 << 16);
+ WRITE_VREG(HEVCD_MCRCC_CTL2, rdata32);
+
+ /* Programme canvas1 */
+ WRITE_VREG(HEVCD_MPP_ANC_CANVAS_ACCCONFIG_ADDR,
+ (16 << 8) | (1 << 1) | 0);
+ rdata32_2 = READ_VREG(HEVCD_MPP_ANC_CANVAS_DATA_ADDR);
+ rdata32_2 = rdata32_2 & 0xffff;
+ rdata32_2 = rdata32_2 | (rdata32_2 << 16);
+ if (rdata32 == rdata32_2 && l1_cnt > 1) {
+ rdata32_2 = READ_VREG(HEVCD_MPP_ANC_CANVAS_DATA_ADDR);
+ rdata32_2 = rdata32_2 & 0xffff;
+ rdata32_2 = rdata32_2 | (rdata32_2 << 16);
+ }
+ WRITE_VREG(HEVCD_MCRCC_CTL3, rdata32_2);
+ } else { /* P-PIC */
+ WRITE_VREG(HEVCD_MPP_ANC_CANVAS_ACCCONFIG_ADDR,
+ (0 << 8) | (1 << 1) | 0);
+ rdata32 = READ_VREG(HEVCD_MPP_ANC_CANVAS_DATA_ADDR);
+ rdata32 = rdata32 & 0xffff;
+ rdata32 = rdata32 | (rdata32 << 16);
+ WRITE_VREG(HEVCD_MCRCC_CTL2, rdata32);
+
+ if (l0_cnt == 1) {
+ WRITE_VREG(HEVCD_MCRCC_CTL3, rdata32);
+ } else {
+ /* Programme canvas1 */
+ rdata32 = READ_VREG(HEVCD_MPP_ANC_CANVAS_DATA_ADDR);
+ rdata32 = rdata32 & 0xffff;
+ rdata32 = rdata32 | (rdata32 << 16);
+ WRITE_VREG(HEVCD_MCRCC_CTL3, rdata32);
+ }
+ }
+ /* enable mcrcc progressive-mode */
+ WRITE_VREG(HEVCD_MCRCC_CTL1, 0xff0);
+}
+#endif
+
+static void config_title_hw(struct hevc_state_s *hevc, int sao_vb_size,
+ int sao_mem_unit)
+{
+ WRITE_VREG(HEVC_sao_mem_unit, sao_mem_unit);
+ WRITE_VREG(HEVC_SAO_ABV, hevc->work_space_buf->sao_abv.buf_start);
+ WRITE_VREG(HEVC_sao_vb_size, sao_vb_size);
+ WRITE_VREG(HEVC_SAO_VB, hevc->work_space_buf->sao_vb.buf_start);
+}
+
+static void config_mpred_hw(struct hevc_state_s *hevc)
+{
+ int i;
+ unsigned int data32;
+ struct PIC_s *cur_pic = hevc->cur_pic;
+ struct PIC_s *col_pic = hevc->col_pic;
+ int AMVP_MAX_NUM_CANDS_MEM = 3;
+ int AMVP_MAX_NUM_CANDS = 2;
+ int NUM_CHROMA_MODE = 5;
+ int DM_CHROMA_IDX = 36;
+ int above_ptr_ctrl = 0;
+ int buffer_linear = 1;
+ int cu_size_log2 = 3;
+
+ int mpred_mv_rd_start_addr;
+ int mpred_curr_lcu_x;
+ int mpred_curr_lcu_y;
+ int mpred_above_buf_start;
+ int mpred_mv_rd_ptr;
+ int mpred_mv_rd_ptr_p1;
+ int mpred_mv_rd_end_addr;
+ int MV_MEM_UNIT;
+ int mpred_mv_wr_ptr;
+ int *ref_poc_L0, *ref_poc_L1;
+
+ int above_en;
+ int mv_wr_en;
+ int mv_rd_en;
+ int col_isIntra;
+
+ if (hevc->slice_type != 2) {
+ above_en = 1;
+ mv_wr_en = 1;
+ mv_rd_en = 1;
+ col_isIntra = 0;
+ } else {
+ above_en = 1;
+ mv_wr_en = 1;
+ mv_rd_en = 0;
+ col_isIntra = 0;
+ }
+
+ mpred_mv_rd_start_addr = col_pic->mpred_mv_wr_start_addr;
+ data32 = READ_VREG(HEVC_MPRED_CURR_LCU);
+ mpred_curr_lcu_x = data32 & 0xffff;
+ mpred_curr_lcu_y = (data32 >> 16) & 0xffff;
+
+ MV_MEM_UNIT =
+ hevc->lcu_size_log2 == 6 ? 0x200 : hevc->lcu_size_log2 ==
+ 5 ? 0x80 : 0x20;
+ mpred_mv_rd_ptr =
+ mpred_mv_rd_start_addr + (hevc->slice_addr * MV_MEM_UNIT);
+
+ mpred_mv_rd_ptr_p1 = mpred_mv_rd_ptr + MV_MEM_UNIT;
+ mpred_mv_rd_end_addr =
+ mpred_mv_rd_start_addr +
+ ((hevc->lcu_x_num * hevc->lcu_y_num) * MV_MEM_UNIT);
+
+ mpred_above_buf_start = hevc->work_space_buf->mpred_above.buf_start;
+
+ mpred_mv_wr_ptr =
+ cur_pic->mpred_mv_wr_start_addr +
+ (hevc->slice_addr * MV_MEM_UNIT);
+
+ if (debug & H265_DEBUG_BUFMGR) {
+ pr_info("cur pic index %d col pic index %d\n", cur_pic->index,
+ col_pic->index);
+ }
+
+ WRITE_VREG(HEVC_MPRED_MV_WR_START_ADDR,
+ cur_pic->mpred_mv_wr_start_addr);
+ WRITE_VREG(HEVC_MPRED_MV_RD_START_ADDR, mpred_mv_rd_start_addr);
+
+ data32 = ((hevc->lcu_x_num - hevc->tile_width_lcu) * MV_MEM_UNIT);
+ WRITE_VREG(HEVC_MPRED_MV_WR_ROW_JUMP, data32);
+ WRITE_VREG(HEVC_MPRED_MV_RD_ROW_JUMP, data32);
+
+ data32 = READ_VREG(HEVC_MPRED_CTRL0);
+ data32 = (hevc->slice_type |
+ hevc->new_pic << 2 |
+ hevc->new_tile << 3 |
+ hevc->isNextSliceSegment << 4 |
+ hevc->TMVPFlag << 5 |
+ hevc->LDCFlag << 6 |
+ hevc->ColFromL0Flag << 7 |
+ above_ptr_ctrl << 8 |
+ above_en << 9 |
+ mv_wr_en << 10 |
+ mv_rd_en << 11 |
+ col_isIntra << 12 |
+ buffer_linear << 13 |
+ hevc->LongTerm_Curr << 14 |
+ hevc->LongTerm_Col << 15 |
+ hevc->lcu_size_log2 << 16 |
+ cu_size_log2 << 20 | hevc->plevel << 24);
+ WRITE_VREG(HEVC_MPRED_CTRL0, data32);
+
+ data32 = READ_VREG(HEVC_MPRED_CTRL1);
+ data32 = (
+#if 0
+ /* no set in m8baby test1902 */
+ /* Don't override clk_forced_on , */
+ (data32 & (0x1 << 24)) |
+#endif
+ hevc->MaxNumMergeCand |
+ AMVP_MAX_NUM_CANDS << 4 |
+ AMVP_MAX_NUM_CANDS_MEM << 8 |
+ NUM_CHROMA_MODE << 12 | DM_CHROMA_IDX << 16);
+ WRITE_VREG(HEVC_MPRED_CTRL1, data32);
+
+ data32 = (hevc->pic_w | hevc->pic_h << 16);
+ WRITE_VREG(HEVC_MPRED_PIC_SIZE, data32);
+
+ data32 = ((hevc->lcu_x_num - 1) | (hevc->lcu_y_num - 1) << 16);
+ WRITE_VREG(HEVC_MPRED_PIC_SIZE_LCU, data32);
+
+ data32 = (hevc->tile_start_lcu_x | hevc->tile_start_lcu_y << 16);
+ WRITE_VREG(HEVC_MPRED_TILE_START, data32);
+
+ data32 = (hevc->tile_width_lcu | hevc->tile_height_lcu << 16);
+ WRITE_VREG(HEVC_MPRED_TILE_SIZE_LCU, data32);
+
+ data32 = (hevc->RefNum_L0 | hevc->RefNum_L1 << 8 | 0
+ /* col_RefNum_L0<<16| */
+ /* col_RefNum_L1<<24 */
+ );
+ WRITE_VREG(HEVC_MPRED_REF_NUM, data32);
+
+ data32 = (hevc->LongTerm_Ref);
+ WRITE_VREG(HEVC_MPRED_LT_REF, data32);
+
+ data32 = 0;
+ for (i = 0; i < hevc->RefNum_L0; i++)
+ data32 = data32 | (1 << i);
+ WRITE_VREG(HEVC_MPRED_REF_EN_L0, data32);
+
+ data32 = 0;
+ for (i = 0; i < hevc->RefNum_L1; i++)
+ data32 = data32 | (1 << i);
+ WRITE_VREG(HEVC_MPRED_REF_EN_L1, data32);
+
+ WRITE_VREG(HEVC_MPRED_CUR_POC, hevc->curr_POC);
+ WRITE_VREG(HEVC_MPRED_COL_POC, hevc->Col_POC);
+
+ /*
+ *below MPRED Ref_POC_xx_Lx registers must follow Ref_POC_xx_L0 ->
+ *Ref_POC_xx_L1 in pair write order!!!
+ */
+ ref_poc_L0 = &(cur_pic->m_aiRefPOCList0[cur_pic->slice_idx][0]);
+ ref_poc_L1 = &(cur_pic->m_aiRefPOCList1[cur_pic->slice_idx][0]);
+
+ WRITE_VREG(HEVC_MPRED_L0_REF00_POC, ref_poc_L0[0]);
+ WRITE_VREG(HEVC_MPRED_L1_REF00_POC, ref_poc_L1[0]);
+
+ WRITE_VREG(HEVC_MPRED_L0_REF01_POC, ref_poc_L0[1]);
+ WRITE_VREG(HEVC_MPRED_L1_REF01_POC, ref_poc_L1[1]);
+
+ WRITE_VREG(HEVC_MPRED_L0_REF02_POC, ref_poc_L0[2]);
+ WRITE_VREG(HEVC_MPRED_L1_REF02_POC, ref_poc_L1[2]);
+
+ WRITE_VREG(HEVC_MPRED_L0_REF03_POC, ref_poc_L0[3]);
+ WRITE_VREG(HEVC_MPRED_L1_REF03_POC, ref_poc_L1[3]);
+
+ WRITE_VREG(HEVC_MPRED_L0_REF04_POC, ref_poc_L0[4]);
+ WRITE_VREG(HEVC_MPRED_L1_REF04_POC, ref_poc_L1[4]);
+
+ WRITE_VREG(HEVC_MPRED_L0_REF05_POC, ref_poc_L0[5]);
+ WRITE_VREG(HEVC_MPRED_L1_REF05_POC, ref_poc_L1[5]);
+
+ WRITE_VREG(HEVC_MPRED_L0_REF06_POC, ref_poc_L0[6]);
+ WRITE_VREG(HEVC_MPRED_L1_REF06_POC, ref_poc_L1[6]);
+
+ WRITE_VREG(HEVC_MPRED_L0_REF07_POC, ref_poc_L0[7]);
+ WRITE_VREG(HEVC_MPRED_L1_REF07_POC, ref_poc_L1[7]);
+
+ WRITE_VREG(HEVC_MPRED_L0_REF08_POC, ref_poc_L0[8]);
+ WRITE_VREG(HEVC_MPRED_L1_REF08_POC, ref_poc_L1[8]);
+
+ WRITE_VREG(HEVC_MPRED_L0_REF09_POC, ref_poc_L0[9]);
+ WRITE_VREG(HEVC_MPRED_L1_REF09_POC, ref_poc_L1[9]);
+
+ WRITE_VREG(HEVC_MPRED_L0_REF10_POC, ref_poc_L0[10]);
+ WRITE_VREG(HEVC_MPRED_L1_REF10_POC, ref_poc_L1[10]);
+
+ WRITE_VREG(HEVC_MPRED_L0_REF11_POC, ref_poc_L0[11]);
+ WRITE_VREG(HEVC_MPRED_L1_REF11_POC, ref_poc_L1[11]);
+
+ WRITE_VREG(HEVC_MPRED_L0_REF12_POC, ref_poc_L0[12]);
+ WRITE_VREG(HEVC_MPRED_L1_REF12_POC, ref_poc_L1[12]);
+
+ WRITE_VREG(HEVC_MPRED_L0_REF13_POC, ref_poc_L0[13]);
+ WRITE_VREG(HEVC_MPRED_L1_REF13_POC, ref_poc_L1[13]);
+
+ WRITE_VREG(HEVC_MPRED_L0_REF14_POC, ref_poc_L0[14]);
+ WRITE_VREG(HEVC_MPRED_L1_REF14_POC, ref_poc_L1[14]);
+
+ WRITE_VREG(HEVC_MPRED_L0_REF15_POC, ref_poc_L0[15]);
+ WRITE_VREG(HEVC_MPRED_L1_REF15_POC, ref_poc_L1[15]);
+
+ if (hevc->new_pic) {
+ WRITE_VREG(HEVC_MPRED_ABV_START_ADDR, mpred_above_buf_start);
+ WRITE_VREG(HEVC_MPRED_MV_WPTR, mpred_mv_wr_ptr);
+ /* WRITE_VREG(HEVC_MPRED_MV_RPTR,mpred_mv_rd_ptr); */
+ WRITE_VREG(HEVC_MPRED_MV_RPTR, mpred_mv_rd_start_addr);
+ } else if (!hevc->isNextSliceSegment) {
+ /* WRITE_VREG(HEVC_MPRED_MV_RPTR,mpred_mv_rd_ptr_p1); */
+ WRITE_VREG(HEVC_MPRED_MV_RPTR, mpred_mv_rd_ptr);
+ }
+
+ WRITE_VREG(HEVC_MPRED_MV_RD_END_ADDR, mpred_mv_rd_end_addr);
+}
+
+static void config_sao_hw(struct hevc_state_s *hevc, union param_u *params)
+{
+ unsigned int data32, data32_2;
+ int misc_flag0 = hevc->misc_flag0;
+ int slice_deblocking_filter_disabled_flag = 0;
+
+ int mc_buffer_size_u_v =
+ hevc->lcu_total * hevc->lcu_size * hevc->lcu_size / 2;
+ int mc_buffer_size_u_v_h = (mc_buffer_size_u_v + 0xffff) >> 16;
+ struct PIC_s *cur_pic = hevc->cur_pic;
+
+ data32 = READ_VREG(HEVC_SAO_CTRL0);
+ data32 &= (~0xf);
+ data32 |= hevc->lcu_size_log2;
+ WRITE_VREG(HEVC_SAO_CTRL0, data32);
+
+ data32 = (hevc->pic_w | hevc->pic_h << 16);
+ WRITE_VREG(HEVC_SAO_PIC_SIZE, data32);
+
+ data32 = ((hevc->lcu_x_num - 1) | (hevc->lcu_y_num - 1) << 16);
+ WRITE_VREG(HEVC_SAO_PIC_SIZE_LCU, data32);
+
+ if (hevc->new_pic)
+ WRITE_VREG(HEVC_SAO_Y_START_ADDR, 0xffffffff);
+#ifdef LOSLESS_COMPRESS_MODE
+/*SUPPORT_10BIT*/
+ if ((double_write_mode & 0x10) == 0) {
+ data32 = READ_VREG(HEVC_SAO_CTRL5);
+ data32 &= (~(0xff << 16));
+ if (double_write_mode != 1)
+ data32 |= (0xff<<16);
+ if (hevc->mem_saving_mode == 1)
+ data32 |= (1 << 9);
+ else
+ data32 &= ~(1 << 9);
+ if (workaround_enable & 1)
+ data32 |= (1 << 7);
+ WRITE_VREG(HEVC_SAO_CTRL5, data32);
+ }
+ data32 = cur_pic->mc_y_adr;
+ if (double_write_mode)
+ WRITE_VREG(HEVC_SAO_Y_START_ADDR, cur_pic->dw_y_adr);
+
+ if ((double_write_mode & 0x10) == 0)
+ WRITE_VREG(HEVC_CM_BODY_START_ADDR, data32);
+
+ if (mmu_enable)
+ WRITE_VREG(HEVC_CM_HEADER_START_ADDR, cur_pic->header_adr);
+#else
+ data32 = cur_pic->mc_y_adr;
+ WRITE_VREG(HEVC_SAO_Y_START_ADDR, data32);
+#endif
+ data32 = (mc_buffer_size_u_v_h << 16) << 1;
+ WRITE_VREG(HEVC_SAO_Y_LENGTH, data32);
+
+#ifdef LOSLESS_COMPRESS_MODE
+/*SUPPORT_10BIT*/
+ if (double_write_mode)
+ WRITE_VREG(HEVC_SAO_C_START_ADDR, cur_pic->dw_u_v_adr);
+#else
+ data32 = cur_pic->mc_u_v_adr;
+ WRITE_VREG(HEVC_SAO_C_START_ADDR, data32);
+#endif
+ data32 = (mc_buffer_size_u_v_h << 16);
+ WRITE_VREG(HEVC_SAO_C_LENGTH, data32);
+
+#ifdef LOSLESS_COMPRESS_MODE
+/*SUPPORT_10BIT*/
+ if (double_write_mode) {
+ WRITE_VREG(HEVC_SAO_Y_WPTR, cur_pic->dw_y_adr);
+ WRITE_VREG(HEVC_SAO_C_WPTR, cur_pic->dw_u_v_adr);
+ }
+#else
+ /* multi tile to do... */
+ data32 = cur_pic->mc_y_adr;
+ WRITE_VREG(HEVC_SAO_Y_WPTR, data32);
+
+ data32 = cur_pic->mc_u_v_adr;
+ WRITE_VREG(HEVC_SAO_C_WPTR, data32);
+#endif
+ /* DBLK CONFIG HERE */
+ if (hevc->new_pic) {
+ data32 = (hevc->pic_w | hevc->pic_h << 16);
+ WRITE_VREG(HEVC_DBLK_CFG2, data32);
+
+ if ((misc_flag0 >> PCM_ENABLE_FLAG_BIT) & 0x1) {
+ data32 =
+ ((misc_flag0 >>
+ PCM_LOOP_FILTER_DISABLED_FLAG_BIT) &
+ 0x1) << 3;
+ } else
+ data32 = 0;
+ data32 |=
+ (((params->p.pps_cb_qp_offset & 0x1f) << 4) |
+ ((params->p.pps_cr_qp_offset
+ & 0x1f) <<
+ 9));
+ data32 |=
+ (hevc->lcu_size ==
+ 64) ? 0 : ((hevc->lcu_size == 32) ? 1 : 2);
+
+ WRITE_VREG(HEVC_DBLK_CFG1, data32);
+ }
+#if 0
+ data32 = READ_VREG(HEVC_SAO_CTRL1);
+ data32 &= (~0x3000);
+ data32 |= (mem_map_mode <<
+ 12);/*
+ *[13:12] axi_aformat,
+ *0-Linear, 1-32x32, 2-64x32
+ */
+ WRITE_VREG(HEVC_SAO_CTRL1, data32);
+
+ data32 = READ_VREG(HEVCD_IPP_AXIIF_CONFIG);
+ data32 &= (~0x30);
+ data32 |= (mem_map_mode <<
+ 4); /*
+ *[5:4] -- address_format
+ *00:linear 01:32x32 10:64x32
+ */
+ WRITE_VREG(HEVCD_IPP_AXIIF_CONFIG, data32);
+#else
+ /* m8baby test1902 */
+ data32 = READ_VREG(HEVC_SAO_CTRL1);
+ data32 &= (~0x3000);
+ data32 |= (mem_map_mode <<
+ 12); /*
+ *[13:12] axi_aformat, 0-Linear,
+ *1-32x32, 2-64x32
+ */
+ data32 &= (~0xff0);
+ /* data32 |= 0x670; // Big-Endian per 64-bit */
+ data32 |= endian; /* Big-Endian per 64-bit */
+ data32 &= (~0x3); /*[1]:dw_disable [0]:cm_disable*/
+ if (double_write_mode == 0)
+ data32 |= 0x2; /*disable double write*/
+ else if (!mmu_enable && (double_write_mode & 0x10))
+ data32 |= 0x1; /*disable cm*/
+ WRITE_VREG(HEVC_SAO_CTRL1, data32);
+
+ if (double_write_mode & 0x10) {
+ /*
+ *[23:22] dw_v1_ctrl
+ *[21:20] dw_v0_ctrl
+ *[19:18] dw_h1_ctrl
+ *[17:16] dw_h0_ctrl
+ */
+ data32 = READ_VREG(HEVC_SAO_CTRL5);
+ /*set them all 0 for H265_NV21 (no down-scale)*/
+ data32 &= ~(0xff << 16);
+ WRITE_VREG(HEVC_SAO_CTRL5, data32);
+ }
+
+ data32 = READ_VREG(HEVCD_IPP_AXIIF_CONFIG);
+ data32 &= (~0x30);
+ /* [5:4] -- address_format 00:linear 01:32x32 10:64x32 */
+ data32 |= (mem_map_mode <<
+ 4);
+ data32 &= (~0xF);
+ data32 |= 0xf; /* valid only when double write only */
+ /*data32 |= 0x8;*/ /* Big-Endian per 64-bit */
+ WRITE_VREG(HEVCD_IPP_AXIIF_CONFIG, data32);
+#endif
+ data32 = 0;
+ data32_2 = READ_VREG(HEVC_SAO_CTRL0);
+ data32_2 &= (~0x300);
+ /*
+ *slice_deblocking_filter_disabled_flag = 0;
+ * ucode has handle it , so read it from ucode directly
+ */
+ if (hevc->tile_enabled) {
+ data32 |=
+ ((misc_flag0 >>
+ LOOP_FILER_ACROSS_TILES_ENABLED_FLAG_BIT) &
+ 0x1) << 0;
+ data32_2 |=
+ ((misc_flag0 >>
+ LOOP_FILER_ACROSS_TILES_ENABLED_FLAG_BIT) &
+ 0x1) << 8;
+ }
+ slice_deblocking_filter_disabled_flag = (misc_flag0 >>
+ SLICE_DEBLOCKING_FILTER_DISABLED_FLAG_BIT) &
+ 0x1; /* ucode has handle it,so read it from ucode directly */
+ if ((misc_flag0 & (1 << DEBLOCKING_FILTER_OVERRIDE_ENABLED_FLAG_BIT))
+ && (misc_flag0 & (1 << DEBLOCKING_FILTER_OVERRIDE_FLAG_BIT))) {
+ /*
+ *slice_deblocking_filter_disabled_flag =
+ *(misc_flag0>>SLICE_DEBLOCKING_FILTER_DISABLED_FLAG_BIT)&0x1;
+ * //ucode has handle it , so read it from ucode directly
+ */
+ data32 |= slice_deblocking_filter_disabled_flag << 2;
+ if (debug & H265_DEBUG_BUFMGR)
+ pr_info("(1,%x)", data32);
+ if (!slice_deblocking_filter_disabled_flag) {
+ data32 |= (params->p.slice_beta_offset_div2 & 0xf) << 3;
+ data32 |= (params->p.slice_tc_offset_div2 & 0xf) << 7;
+ if (debug & H265_DEBUG_BUFMGR)
+ pr_info("(2,%x)", data32);
+ }
+ } else {
+ data32 |=
+ ((misc_flag0 >>
+ PPS_DEBLOCKING_FILTER_DISABLED_FLAG_BIT) &
+ 0x1) << 2;
+ if (debug & H265_DEBUG_BUFMGR)
+ pr_info("(3,%x)", data32);
+ if (((misc_flag0 >> PPS_DEBLOCKING_FILTER_DISABLED_FLAG_BIT) &
+ 0x1) == 0) {
+ data32 |= (params->p.pps_beta_offset_div2 & 0xf) << 3;
+ data32 |= (params->p.pps_tc_offset_div2 & 0xf) << 7;
+ if (debug & H265_DEBUG_BUFMGR)
+ pr_info("(4,%x)", data32);
+ }
+ }
+ if ((misc_flag0 & (1 << PPS_LOOP_FILTER_ACROSS_SLICES_ENABLED_FLAG_BIT))
+ && ((misc_flag0 & (1 << SLICE_SAO_LUMA_FLAG_BIT))
+ || (misc_flag0 & (1 << SLICE_SAO_CHROMA_FLAG_BIT))
+ || (!slice_deblocking_filter_disabled_flag))) {
+ data32 |=
+ ((misc_flag0 >>
+ SLICE_LOOP_FILTER_ACROSS_SLICES_ENABLED_FLAG_BIT)
+ & 0x1) << 1;
+ data32_2 |=
+ ((misc_flag0 >>
+ SLICE_LOOP_FILTER_ACROSS_SLICES_ENABLED_FLAG_BIT)
+ & 0x1) << 9;
+ if (debug & H265_DEBUG_BUFMGR)
+ pr_info("(5,%x)\n", data32);
+ } else {
+ data32 |=
+ ((misc_flag0 >>
+ PPS_LOOP_FILTER_ACROSS_SLICES_ENABLED_FLAG_BIT)
+ & 0x1) << 1;
+ data32_2 |=
+ ((misc_flag0 >>
+ PPS_LOOP_FILTER_ACROSS_SLICES_ENABLED_FLAG_BIT)
+ & 0x1) << 9;
+ if (debug & H265_DEBUG_BUFMGR)
+ pr_info("(6,%x)\n", data32);
+ }
+ WRITE_VREG(HEVC_DBLK_CFG9, data32);
+ WRITE_VREG(HEVC_SAO_CTRL0, data32_2);
+}
+
+static void clear_used_by_display_flag(struct hevc_state_s *hevc)
+{
+ struct PIC_s *pic;
+ int i;
+
+ if (debug & H265_DEBUG_NOT_USE_LAST_DISPBUF)
+ return;
+
+ for (i = 0; i < MAX_REF_PIC_NUM; i++) {
+ pic = &hevc->m_PIC[i];
+ pic->used_by_display = 0;
+ }
+}
+
+static struct PIC_s *get_new_pic(struct hevc_state_s *hevc,
+ union param_u *rpm_param)
+{
+ struct PIC_s *new_pic = NULL;
+ struct PIC_s *pic;
+ /* recycle un-used pic */
+ int i;
+ int ret;
+
+ for (i = 0; i < MAX_REF_PIC_NUM; i++) {
+ pic = &hevc->m_PIC[i];
+ if (pic->index == -1)
+ continue;
+ if ((pic->used_by_display) && !mmu_enable
+ && ((READ_VCBUS_REG(AFBC_BODY_BADDR) << 4) !=
+ pic->mc_y_adr))
+ pic->used_by_display = 0;
+ if (pic->output_mark == 0 && pic->referenced == 0
+ && pic->output_ready == 0
+ && pic->used_by_display == 0) {
+ if (new_pic) {
+ if (pic->POC < new_pic->POC)
+ new_pic = pic;
+ } else
+ new_pic = pic;
+ }
+ }
+
+ /*try to allocate more pic for new resolution*/
+ if (re_config_pic_flag && new_pic == NULL) {
+ int ii;
+
+ for (ii = 0; ii < MAX_REF_PIC_NUM; ii++) {
+ if (hevc->m_PIC[ii].index == -1)
+ break;
+ }
+ if (ii < MAX_REF_PIC_NUM) {
+ new_pic = &hevc->m_PIC[ii];
+ memset(new_pic, 0, sizeof(struct PIC_s));
+ new_pic->index = ii;
+ new_pic->BUF_index = -1;
+ }
+ }
+ /**/
+
+ if (new_pic == NULL) {
+ /* pr_info("Error: Buffer management, no free buffer\n"); */
+ return new_pic;
+ }
+
+ new_pic->referenced = 1;
+ if (new_pic->width != hevc->pic_w || new_pic->height != hevc->pic_h) {
+ if (re_config_pic_flag) {
+ /* re config pic for new resolution */
+ recycle_buf(hevc);
+ /* if(new_pic->BUF_index == -1){ */
+ if (config_pic(hevc, new_pic, 0) < 0) {
+ if (debug & H265_DEBUG_BUFMGR_MORE) {
+ pr_info("Config_pic %d fail\n",
+ new_pic->index);
+ dump_pic_list(hevc);
+ }
+ new_pic->index = -1;
+ new_pic = NULL;
+ } else
+ init_pic_list_hw(hevc);
+ }
+ if (new_pic) {
+ new_pic->width = hevc->pic_w;
+ new_pic->height = hevc->pic_h;
+ set_canvas(new_pic);
+ }
+ }
+ if (new_pic) {
+ new_pic->decode_idx = hevc->decode_idx;
+ new_pic->slice_idx = 0;
+ new_pic->referenced = 1;
+ new_pic->output_mark = 0;
+ new_pic->recon_mark = 0;
+ new_pic->error_mark = 0;
+ /* new_pic->output_ready = 0; */
+ new_pic->num_reorder_pic = rpm_param->p.sps_num_reorder_pics_0;
+ new_pic->losless_comp_body_size = hevc->losless_comp_body_size;
+ new_pic->POC = hevc->curr_POC;
+ new_pic->pic_struct = hevc->curr_pic_struct;
+ }
+
+ if (mmu_enable) {
+ ret = H265_alloc_mmu(hevc, new_pic, rpm_param->p.bit_depth,
+ hevc->frame_mmu_map_addr);
+ /*pr_info("get pic index %x\n",new_pic->index);*/
+ if (ret != 0) {
+ pr_err("can't alloc need mmu1,idx %d ret =%d\n",
+ new_pic->decode_idx,
+ ret);
+ return NULL;
+ }
+
+ }
+
+ return new_pic;
+}
+
+static int get_display_pic_num(struct hevc_state_s *hevc)
+{
+ int i;
+ struct PIC_s *pic;
+ int num = 0;
+
+ for (i = 0; i < MAX_REF_PIC_NUM; i++) {
+ pic = &hevc->m_PIC[i];
+ if (pic->index == -1)
+ continue;
+
+ if (pic->output_ready == 1)
+ num++;
+ }
+ return num;
+}
+
+static void flush_output(struct hevc_state_s *hevc, struct PIC_s *pic)
+{
+ struct PIC_s *pic_display;
+
+ if (pic) {
+ /*PB skip control */
+ if (pic->error_mark == 0 && hevc->PB_skip_mode == 1) {
+ /* start decoding after first I */
+ hevc->ignore_bufmgr_error |= 0x1;
+ }
+ if (hevc->ignore_bufmgr_error & 1) {
+ if (hevc->PB_skip_count_after_decoding > 0)
+ hevc->PB_skip_count_after_decoding--;
+ else {
+ /* start displaying */
+ hevc->ignore_bufmgr_error |= 0x2;
+ }
+ }
+ /**/
+ if (pic->POC != INVALID_POC) {
+ pic->output_mark = 1;
+ pic->recon_mark = 1;
+ }
+ pic->recon_mark = 1;
+ }
+ do {
+ pic_display = output_pic(hevc, 1);
+
+ if (pic_display) {
+ pic_display->referenced = 0;
+ if ((pic_display->error_mark
+ && ((hevc->ignore_bufmgr_error & 0x2) == 0))
+ || (debug & H265_DEBUG_DISPLAY_CUR_FRAME)
+ || (debug & H265_DEBUG_NO_DISPLAY)) {
+ pic_display->output_ready = 0;
+ if (debug & H265_DEBUG_BUFMGR) {
+ pr_info
+ ("[BM] Display: POC %d, ",
+ pic_display->POC);
+ pr_info
+ ("decoding index %d ==> ",
+ pic_display->decode_idx);
+ pr_info
+ ("Debug mode or error, recycle it\n");
+ }
+ } else {
+ if (i_only_flag & 0x1
+ && pic_display->slice_type != 2)
+ pic_display->output_ready = 0;
+ else {
+ prepare_display_buf(hevc, pic_display);
+ if (debug & H265_DEBUG_BUFMGR) {
+ pr_info
+ ("[BM] flush Display: POC %d, ",
+ pic_display->POC);
+ pr_info
+ ("decoding index %d\n",
+ pic_display->decode_idx);
+ }
+ }
+ }
+ }
+ } while (pic_display);
+}
+
+static inline void hevc_pre_pic(struct hevc_state_s *hevc,
+ struct PIC_s *pic)
+{
+
+ /* prev pic */
+ /*if (hevc->curr_POC != 0) {*/
+ if (hevc->m_nalUnitType != NAL_UNIT_CODED_SLICE_IDR
+ && hevc->m_nalUnitType !=
+ NAL_UNIT_CODED_SLICE_IDR_N_LP) {
+ struct PIC_s *pic_display;
+
+ pic = get_pic_by_POC(hevc, hevc->iPrevPOC);
+ if (pic && (pic->POC != INVALID_POC)) {
+ /*PB skip control */
+ if (pic->error_mark == 0
+ && hevc->PB_skip_mode == 1) {
+ /*
+ *start decoding after
+ *first I
+ */
+ hevc->ignore_bufmgr_error |= 0x1;
+ }
+ if (hevc->ignore_bufmgr_error & 1) {
+ if (hevc->PB_skip_count_after_decoding > 0) {
+ hevc->PB_skip_count_after_decoding--;
+ } else {
+ /* start displaying */
+ hevc->ignore_bufmgr_error |= 0x2;
+ }
+ }
+ pic->output_mark = 1;
+ pic->recon_mark = 1;
+ }
+ do {
+ pic_display = output_pic(hevc, 0);
+
+ if (pic_display) {
+ if ((pic_display->error_mark &&
+ ((hevc->ignore_bufmgr_error &
+ 0x2) == 0))
+ || (debug &
+ H265_DEBUG_DISPLAY_CUR_FRAME)
+ || (debug &
+ H265_DEBUG_NO_DISPLAY)) {
+ pic_display->output_ready = 0;
+ if (debug & H265_DEBUG_BUFMGR) {
+ pr_info
+ ("[BM] Display: POC %d, ",
+ pic_display->POC);
+ pr_info
+ ("decoding index %d ==> ",
+ pic_display->
+ decode_idx);
+ pr_info
+ ("Debug or err,recycle it\n");
+ }
+ } else {
+ if (i_only_flag & 0x1
+ && pic_display->
+ slice_type != 2) {
+ pic_display->output_ready = 0;
+ } else {
+ prepare_display_buf
+ (hevc,
+ pic_display);
+ if (debug & H265_DEBUG_BUFMGR) {
+ pr_info
+ ("[BM] Display: POC %d, ",
+ pic_display->POC);
+ pr_info
+ ("decoding index %d\n",
+ pic_display->
+ decode_idx);
+ }
+ }
+ }
+ }
+ } while (pic_display);
+ } else {
+ if (debug & H265_DEBUG_BUFMGR) {
+ pr_info
+ ("[BM] current pic is IDR, ");
+ pr_info
+ ("clear referenced flag of all buffers\n");
+ }
+ if (debug & H265_DEBUG_BUFMGR)
+ dump_pic_list(hevc);
+ pic = get_pic_by_POC(hevc, hevc->iPrevPOC);
+ flush_output(hevc, pic);
+ }
+
+}
+
+static void check_pic_decoded_lcu_count(struct hevc_state_s *hevc)
+{
+ int current_lcu_idx = READ_VREG(HEVC_PARSER_LCU_START)&0xffffff;
+
+ if (debug & H265_DEBUG_BUFMGR) {
+ pr_info("cur lcu idx = %d, (total %d)\n",
+ current_lcu_idx, hevc->lcu_total);
+ }
+ if ((error_handle_policy & 0x20) == 0 && hevc->cur_pic != NULL) {
+ if (hevc->first_pic_after_recover) {
+ if (current_lcu_idx !=
+ ((hevc->lcu_x_num_pre*hevc->lcu_y_num_pre) - 1))
+ hevc->cur_pic->error_mark = 1;
+ } else {
+ if (hevc->lcu_x_num_pre != 0
+ && hevc->lcu_y_num_pre != 0
+ && current_lcu_idx != 0
+ && current_lcu_idx <
+ ((hevc->lcu_x_num_pre*hevc->lcu_y_num_pre) - 1))
+ hevc->cur_pic->error_mark = 1;
+ }
+ if (hevc->cur_pic->error_mark)
+ pr_info("cur lcu idx = %d, (total %d), set error_mark\n",
+ current_lcu_idx,
+ hevc->lcu_x_num_pre*hevc->lcu_y_num_pre);
+
+ }
+ hevc->lcu_x_num_pre = hevc->lcu_x_num;
+ hevc->lcu_y_num_pre = hevc->lcu_y_num;
+}
+
+static int hevc_slice_segment_header_process(struct hevc_state_s *hevc,
+ union param_u *rpm_param,
+ int decode_pic_begin)
+{
+ int i;
+ int lcu_x_num_div;
+ int lcu_y_num_div;
+ int Col_ref;
+ int dbg_skip_flag = 0;
+
+ if (hevc->wait_buf == 0) {
+ hevc->sps_num_reorder_pics_0 =
+ rpm_param->p.sps_num_reorder_pics_0;
+ hevc->m_temporalId = rpm_param->p.m_temporalId;
+ hevc->m_nalUnitType = rpm_param->p.m_nalUnitType;
+ hevc->interlace_flag =
+ (rpm_param->p.profile_etc >> 2) & 0x1;
+ hevc->curr_pic_struct =
+ (rpm_param->p.sei_frame_field_info >> 3) & 0xf;
+
+ if (interlace_enable == 0)
+ hevc->interlace_flag = 0;
+ if (interlace_enable & 0x100)
+ hevc->interlace_flag = interlace_enable & 0x1;
+ if (hevc->interlace_flag == 0)
+ hevc->curr_pic_struct = 0;
+ /* if(hevc->m_nalUnitType == NAL_UNIT_EOS){ */
+ /*
+ *hevc->m_pocRandomAccess = MAX_INT;
+ * //add to fix RAP_B_Bossen_1
+ */
+ /* } */
+ hevc->misc_flag0 = rpm_param->p.misc_flag0;
+ if (rpm_param->p.first_slice_segment_in_pic_flag == 0) {
+ hevc->slice_segment_addr =
+ rpm_param->p.slice_segment_address;
+ if (!rpm_param->p.dependent_slice_segment_flag)
+ hevc->slice_addr = hevc->slice_segment_addr;
+ } else {
+ hevc->slice_segment_addr = 0;
+ hevc->slice_addr = 0;
+ }
+
+ hevc->iPrevPOC = hevc->curr_POC;
+ hevc->slice_type = (rpm_param->p.slice_type == I_SLICE) ? 2 :
+ (rpm_param->p.slice_type == P_SLICE) ? 1 :
+ (rpm_param->p.slice_type == B_SLICE) ? 0 : 3;
+ /* hevc->curr_predFlag_L0=(hevc->slice_type==2) ? 0:1; */
+ /* hevc->curr_predFlag_L1=(hevc->slice_type==0) ? 1:0; */
+ hevc->TMVPFlag = rpm_param->p.slice_temporal_mvp_enable_flag;
+ hevc->isNextSliceSegment =
+ rpm_param->p.dependent_slice_segment_flag ? 1 : 0;
+ if (hevc->pic_w != rpm_param->p.pic_width_in_luma_samples
+ || hevc->pic_h !=
+ rpm_param->p.pic_height_in_luma_samples) {
+ pr_info("Pic Width/Height Change (%d,%d)=>(%d,%d), interlace %d\n",
+ hevc->pic_w, hevc->pic_h,
+ rpm_param->p.pic_width_in_luma_samples,
+ rpm_param->p.pic_height_in_luma_samples,
+ hevc->interlace_flag);
+
+ hevc->pic_w = rpm_param->p.pic_width_in_luma_samples;
+ hevc->pic_h = rpm_param->p.pic_height_in_luma_samples;
+ hevc->frame_width = hevc->pic_w;
+ hevc->frame_height = hevc->pic_h;
+#ifdef LOSLESS_COMPRESS_MODE
+ if (re_config_pic_flag == 0 &&
+ (double_write_mode & 0x10) == 0)
+ init_decode_head_hw(hevc);
+#endif
+ }
+
+ if (hevc->pic_w * hevc->pic_h > HEVC_SIZE) {
+ pr_info("over size : %u x %u.\n",
+ hevc->pic_w, hevc->pic_h);
+ debug |= (H265_DEBUG_DIS_LOC_ERROR_PROC |
+ H265_DEBUG_DIS_SYS_ERROR_PROC);
+ hevc->fatal_error |= DECODER_FATAL_ERROR_SIZE_OVERFLOW;
+ return -1;
+ }
+
+ /* it will cause divide 0 error */
+ if (hevc->pic_w == 0 || hevc->pic_h == 0) {
+ if (debug) {
+ pr_info("Fatal Error, pic_w = %d, pic_h = %d\n",
+ hevc->pic_w, hevc->pic_h);
+ }
+ return 3;
+ }
+ hevc->lcu_size =
+ 1 << (rpm_param->p.log2_min_coding_block_size_minus3 +
+ 3 + rpm_param->
+ p.log2_diff_max_min_coding_block_size);
+ if (hevc->lcu_size == 0) {
+ pr_info("Error, lcu_size = 0 (%d,%d)\n",
+ rpm_param->p.
+ log2_min_coding_block_size_minus3,
+ rpm_param->p.
+ log2_diff_max_min_coding_block_size);
+ return 3;
+ }
+ hevc->lcu_size_log2 = log2i(hevc->lcu_size);
+ lcu_x_num_div = (hevc->pic_w / hevc->lcu_size);
+ lcu_y_num_div = (hevc->pic_h / hevc->lcu_size);
+ hevc->lcu_x_num =
+ ((hevc->pic_w % hevc->lcu_size) ==
+ 0) ? lcu_x_num_div : lcu_x_num_div + 1;
+ hevc->lcu_y_num =
+ ((hevc->pic_h % hevc->lcu_size) ==
+ 0) ? lcu_y_num_div : lcu_y_num_div + 1;
+ hevc->lcu_total = hevc->lcu_x_num * hevc->lcu_y_num;
+
+ if (hevc->m_nalUnitType == NAL_UNIT_CODED_SLICE_IDR
+ || hevc->m_nalUnitType ==
+ NAL_UNIT_CODED_SLICE_IDR_N_LP) {
+ hevc->curr_POC = 0;
+ if ((hevc->m_temporalId - 1) == 0)
+ hevc->iPrevTid0POC = hevc->curr_POC;
+ } else {
+ int iMaxPOClsb =
+ 1 << (rpm_param->p.
+ log2_max_pic_order_cnt_lsb_minus4 + 4);
+ int iPrevPOClsb;
+ int iPrevPOCmsb;
+ int iPOCmsb;
+ int iPOClsb = rpm_param->p.POClsb;
+
+ if (iMaxPOClsb == 0) {
+ pr_info("error iMaxPOClsb is 0\n");
+ return 3;
+ }
+
+ iPrevPOClsb = hevc->iPrevTid0POC % iMaxPOClsb;
+ iPrevPOCmsb = hevc->iPrevTid0POC - iPrevPOClsb;
+
+ if ((iPOClsb < iPrevPOClsb)
+ && ((iPrevPOClsb - iPOClsb) >=
+ (iMaxPOClsb / 2)))
+ iPOCmsb = iPrevPOCmsb + iMaxPOClsb;
+ else if ((iPOClsb > iPrevPOClsb)
+ && ((iPOClsb - iPrevPOClsb) >
+ (iMaxPOClsb / 2)))
+ iPOCmsb = iPrevPOCmsb - iMaxPOClsb;
+ else
+ iPOCmsb = iPrevPOCmsb;
+ if (debug & H265_DEBUG_BUFMGR) {
+ pr_info
+ ("iPrePOC%d iMaxPOClsb%d iPOCmsb%d iPOClsb%d\n",
+ hevc->iPrevTid0POC, iMaxPOClsb, iPOCmsb,
+ iPOClsb);
+ }
+ if (hevc->m_nalUnitType == NAL_UNIT_CODED_SLICE_BLA
+ || hevc->m_nalUnitType ==
+ NAL_UNIT_CODED_SLICE_BLANT
+ || hevc->m_nalUnitType ==
+ NAL_UNIT_CODED_SLICE_BLA_N_LP) {
+ /* For BLA picture types, POCmsb is set to 0. */
+ iPOCmsb = 0;
+ }
+ hevc->curr_POC = (iPOCmsb + iPOClsb);
+ if ((hevc->m_temporalId - 1) == 0)
+ hevc->iPrevTid0POC = hevc->curr_POC;
+ else {
+ if (debug & H265_DEBUG_BUFMGR) {
+ pr_info("m_temporalID is %d\n",
+ hevc->m_temporalId);
+ }
+ }
+ }
+ hevc->RefNum_L0 =
+ (rpm_param->p.num_ref_idx_l0_active >
+ MAX_REF_ACTIVE) ? MAX_REF_ACTIVE : rpm_param->p.
+ num_ref_idx_l0_active;
+ hevc->RefNum_L1 =
+ (rpm_param->p.num_ref_idx_l1_active >
+ MAX_REF_ACTIVE) ? MAX_REF_ACTIVE : rpm_param->p.
+ num_ref_idx_l1_active;
+
+ /* if(curr_POC==0x10) dump_lmem(); */
+
+ /* skip RASL pictures after CRA/BLA pictures */
+ if (hevc->m_pocRandomAccess == MAX_INT) {/* first picture */
+ if (hevc->m_nalUnitType == NAL_UNIT_CODED_SLICE_CRA ||
+ hevc->m_nalUnitType == NAL_UNIT_CODED_SLICE_BLA
+ || hevc->m_nalUnitType ==
+ NAL_UNIT_CODED_SLICE_BLANT
+ || hevc->m_nalUnitType ==
+ NAL_UNIT_CODED_SLICE_BLA_N_LP)
+ hevc->m_pocRandomAccess = hevc->curr_POC;
+ else
+ hevc->m_pocRandomAccess = -MAX_INT;
+ } else if (hevc->m_nalUnitType == NAL_UNIT_CODED_SLICE_BLA
+ || hevc->m_nalUnitType ==
+ NAL_UNIT_CODED_SLICE_BLANT
+ || hevc->m_nalUnitType ==
+ NAL_UNIT_CODED_SLICE_BLA_N_LP)
+ hevc->m_pocRandomAccess = hevc->curr_POC;
+ else if ((hevc->curr_POC < hevc->m_pocRandomAccess) &&
+ (nal_skip_policy >= 3) &&
+ (hevc->m_nalUnitType ==
+ NAL_UNIT_CODED_SLICE_RASL_N ||
+ hevc->m_nalUnitType ==
+ NAL_UNIT_CODED_SLICE_TFD)) { /* skip */
+ if (debug) {
+ pr_info
+ ("RASL picture with POC %d < %d ",
+ hevc->curr_POC, hevc->m_pocRandomAccess);
+ pr_info("RandomAccess point POC), skip it\n");
+ }
+ return 1;
+ }
+
+ WRITE_VREG(HEVC_WAIT_FLAG, READ_VREG(HEVC_WAIT_FLAG) | 0x2);
+ hevc->skip_flag = 0;
+ /**/
+ /* if((iPrevPOC != curr_POC)){ */
+ if (rpm_param->p.slice_segment_address == 0) {
+ struct PIC_s *pic;
+
+ hevc->new_pic = 1;
+ check_pic_decoded_lcu_count(hevc);
+ /**/ if (use_cma == 0) {
+ if (hevc->pic_list_init_flag == 0) {
+ /*USE_BUF_BLOCK*/
+ init_buf_list(hevc);
+ /**/
+ init_pic_list(hevc);
+ init_pic_list_hw(hevc);
+ init_buf_spec(hevc);
+ hevc->pic_list_init_flag = 3;
+ }
+ }
+ hevc->first_pic_after_recover = 0;
+ if (debug & H265_DEBUG_BUFMGR_MORE)
+ dump_pic_list(hevc);
+ /* prev pic */
+ hevc_pre_pic(hevc, pic);
+ /*
+ *update referenced of old pictures
+ *(cur_pic->referenced is 1 and not updated)
+ */
+ apply_ref_pic_set(hevc, hevc->curr_POC,
+ rpm_param);
+ if (mmu_enable && hevc->cur_pic != NULL) {
+ if (!(hevc->cur_pic->error_mark
+ && ((hevc->ignore_bufmgr_error & 0x1) == 0))) {
+ long used_4k_num =
+ (READ_VREG(HEVC_SAO_MMU_STATUS) >> 16);
+ decoder_mmu_box_free_idx_tail(hevc->mmu_box,
+ hevc->cur_pic->index, used_4k_num);
+ }
+
+ }
+
+ /* new pic */
+ hevc->cur_pic = get_new_pic(hevc, rpm_param);
+ if (hevc->cur_pic == NULL) {
+ if (debug & H265_DEBUG_BUFMGR)
+ dump_pic_list(hevc);
+ hevc->wait_buf = 1;
+ return -1;
+ }
+ if (debug & H265_DEBUG_DISPLAY_CUR_FRAME) {
+ hevc->cur_pic->output_ready = 1;
+ hevc->cur_pic->stream_offset =
+ READ_VREG(HEVC_SHIFT_BYTE_COUNT);
+ prepare_display_buf(hevc, hevc->cur_pic);
+ hevc->wait_buf = 2;
+ return -1;
+ }
+ } else {
+ if (hevc->pic_list_init_flag != 3
+ || hevc->cur_pic == NULL) {
+ /* make it dec from the first slice segment */
+ return 3;
+ }
+ hevc->cur_pic->slice_idx++;
+ hevc->new_pic = 0;
+ }
+ } else {
+ if (hevc->wait_buf == 1) {
+ /*
+ *if (mmu_enable && hevc->cur_pic != NULL) {
+ * long used_4k_num =
+ * (READ_VREG(HEVC_SAO_MMU_STATUS) >> 16);
+ * decoder_mmu_box_free_idx_tail(hevc->mmu_box,
+ * hevc->cur_pic->index, used_4k_num);
+ *
+ * }
+ */
+ hevc->cur_pic = get_new_pic(hevc, rpm_param);
+ if (hevc->cur_pic == NULL)
+ return -1;
+ hevc->wait_buf = 0;
+ } else if (hevc->wait_buf ==
+ 2) {
+ if (get_display_pic_num(hevc) >
+ 1)
+ return -1;
+ hevc->wait_buf = 0;
+ }
+ if (debug & H265_DEBUG_BUFMGR_MORE)
+ dump_pic_list(hevc);
+ }
+
+ if (hevc->new_pic) {
+#if 1
+ /*SUPPORT_10BIT*/
+ int sao_mem_unit =
+ (hevc->lcu_size == 16 ? 9 :
+ hevc->lcu_size ==
+ 32 ? 14 : 24) << 4;
+#else
+ int sao_mem_unit = ((hevc->lcu_size / 8) * 2 + 4) << 4;
+#endif
+ int pic_height_cu =
+ (hevc->pic_h + hevc->lcu_size - 1) / hevc->lcu_size;
+ int pic_width_cu =
+ (hevc->pic_w + hevc->lcu_size - 1) / hevc->lcu_size;
+ int sao_vb_size = (sao_mem_unit + (2 << 4)) * pic_height_cu;
+
+ /* int sao_abv_size = sao_mem_unit*pic_width_cu; */
+ if (debug & H265_DEBUG_BUFMGR) {
+ pr_info("==>%s dec idx %d, struct %d interlace %d\n",
+ __func__,
+ hevc->decode_idx,
+ hevc->curr_pic_struct,
+ hevc->interlace_flag);
+ }
+ if (dbg_skip_decode_index != 0 &&
+ hevc->decode_idx == dbg_skip_decode_index)
+ dbg_skip_flag = 1;
+
+ hevc->decode_idx++;
+ update_tile_info(hevc, pic_width_cu, pic_height_cu,
+ sao_mem_unit, rpm_param);
+
+ config_title_hw(hevc, sao_vb_size, sao_mem_unit);
+ }
+
+ if (hevc->iPrevPOC != hevc->curr_POC) {
+ hevc->new_tile = 1;
+ hevc->tile_x = 0;
+ hevc->tile_y = 0;
+ hevc->tile_y_x = 0;
+ if (debug & H265_DEBUG_BUFMGR) {
+ pr_info("new_tile (new_pic) tile_x=%d, tile_y=%d\n",
+ hevc->tile_x, hevc->tile_y);
+ }
+ } else if (hevc->tile_enabled) {
+ if (debug & H265_DEBUG_BUFMGR) {
+ pr_info("slice_segment_address is %d\n",
+ rpm_param->p.slice_segment_address);
+ }
+ hevc->tile_y_x =
+ get_tile_index(hevc, rpm_param->p.slice_segment_address,
+ (hevc->pic_w +
+ hevc->lcu_size -
+ 1) / hevc->lcu_size);
+ if (hevc->tile_y_x != (hevc->tile_x | (hevc->tile_y << 8))) {
+ hevc->new_tile = 1;
+ hevc->tile_x = hevc->tile_y_x & 0xff;
+ hevc->tile_y = (hevc->tile_y_x >> 8) & 0xff;
+ if (debug & H265_DEBUG_BUFMGR) {
+ pr_info
+ ("new_tile seg adr %d tile_x=%d, tile_y=%d\n",
+ rpm_param->p.slice_segment_address,
+ hevc->tile_x, hevc->tile_y);
+ }
+ } else
+ hevc->new_tile = 0;
+ } else
+ hevc->new_tile = 0;
+
+ if (hevc->new_tile) {
+ hevc->tile_start_lcu_x =
+ hevc->m_tile[hevc->tile_y][hevc->tile_x].start_cu_x;
+ hevc->tile_start_lcu_y =
+ hevc->m_tile[hevc->tile_y][hevc->tile_x].start_cu_y;
+ hevc->tile_width_lcu =
+ hevc->m_tile[hevc->tile_y][hevc->tile_x].width;
+ hevc->tile_height_lcu =
+ hevc->m_tile[hevc->tile_y][hevc->tile_x].height;
+ }
+
+ set_ref_pic_list(hevc, rpm_param);
+
+ Col_ref = rpm_param->p.collocated_ref_idx;
+
+ hevc->LDCFlag = 0;
+ if (rpm_param->p.slice_type != I_SLICE) {
+ hevc->LDCFlag = 1;
+ for (i = 0; (i < hevc->RefNum_L0) && hevc->LDCFlag; i++) {
+ if (hevc->cur_pic->
+ m_aiRefPOCList0[hevc->cur_pic->slice_idx][i] >
+ hevc->curr_POC)
+ hevc->LDCFlag = 0;
+ }
+ if (rpm_param->p.slice_type == B_SLICE) {
+ for (i = 0; (i < hevc->RefNum_L1)
+ && hevc->LDCFlag; i++) {
+ if (hevc->cur_pic->
+ m_aiRefPOCList1[hevc->cur_pic->
+ slice_idx][i] >
+ hevc->curr_POC)
+ hevc->LDCFlag = 0;
+ }
+ }
+ }
+
+ hevc->ColFromL0Flag = rpm_param->p.collocated_from_l0_flag;
+
+ hevc->plevel =
+ rpm_param->p.log2_parallel_merge_level;
+ hevc->MaxNumMergeCand = 5 - rpm_param->p.five_minus_max_num_merge_cand;
+
+ hevc->LongTerm_Curr = 0; /* to do ... */
+ hevc->LongTerm_Col = 0; /* to do ... */
+
+ hevc->list_no = 0;
+ if (rpm_param->p.slice_type == B_SLICE)
+ hevc->list_no = 1 - hevc->ColFromL0Flag;
+ if (hevc->list_no == 0) {
+ if (Col_ref < hevc->RefNum_L0) {
+ hevc->Col_POC =
+ hevc->cur_pic->m_aiRefPOCList0[hevc->cur_pic->
+ slice_idx][Col_ref];
+ } else
+ hevc->Col_POC = INVALID_POC;
+ } else {
+ if (Col_ref < hevc->RefNum_L1) {
+ hevc->Col_POC =
+ hevc->cur_pic->m_aiRefPOCList1[hevc->cur_pic->
+ slice_idx][Col_ref];
+ } else
+ hevc->Col_POC = INVALID_POC;
+ }
+
+ hevc->LongTerm_Ref = 0; /* to do ... */
+
+ if (hevc->slice_type != 2) {
+ /* if(i_only_flag==1){ */
+ /* return 0xf; */
+ /* } */
+
+ if (hevc->Col_POC != INVALID_POC) {
+ hevc->col_pic = get_ref_pic_by_POC(hevc, hevc->Col_POC);
+ if (hevc->col_pic == NULL) {
+ hevc->cur_pic->error_mark = 1;
+ if (debug) {
+ pr_info
+ ("WRONG,fail to get the pic Col_POC\n");
+ }
+ } else if (hevc->col_pic->error_mark) {
+ hevc->cur_pic->error_mark = 1;
+ if (debug) {
+ pr_info
+ ("WRONG, Col_POC error_mark is 1\n");
+ }
+ }
+
+ if (hevc->cur_pic->error_mark
+ && ((hevc->ignore_bufmgr_error & 0x1) == 0)) {
+ return 2;
+ }
+ } else
+ hevc->col_pic = hevc->cur_pic;
+ } /* */
+ if (hevc->col_pic == NULL)
+ hevc->col_pic = hevc->cur_pic;
+#ifdef BUFFER_MGR_ONLY
+ return 0xf;
+#else
+ if ((decode_pic_begin > 0 && hevc->decode_idx <= decode_pic_begin)
+ || (dbg_skip_flag))
+ return 0xf;
+#endif
+
+ config_mc_buffer(hevc, hevc->cur_pic);
+
+ if (hevc->cur_pic->error_mark
+ && ((hevc->ignore_bufmgr_error & 0x1) == 0)) {
+ if (debug)
+ pr_info("Discard this picture index %d\n",
+ hevc->cur_pic->index);
+ return 2;
+ }
+#ifdef MCRCC_ENABLE
+ config_mcrcc_axi_hw(hevc, hevc->cur_pic->slice_type);
+#endif
+ config_mpred_hw(hevc);
+
+ config_sao_hw(hevc, rpm_param);
+
+ if ((hevc->slice_type != 2) && (i_only_flag & 0x2))
+ return 0xf;
+
+ return 0;
+}
+
+
+
+static int H265_alloc_mmu(struct hevc_state_s *hevc, struct PIC_s *new_pic,
+ unsigned short bit_depth, unsigned int *mmu_index_adr) {
+ int cur_buf_idx = new_pic->index;
+ int bit_depth_10 = (bit_depth != 0x00);
+ int picture_size;
+ int cur_mmu_4k_number;
+
+ picture_size = compute_losless_comp_body_size(new_pic->width,
+ new_pic->height, !bit_depth_10);
+ cur_mmu_4k_number = ((picture_size+(1<<12)-1) >> 12);
+
+ /*
+ *pr_info("alloc_mmu cur_idx : %d picture_size : %d mmu_4k_number : %d\r\n",
+* cur_buf_idx, picture_size, cur_mmu_4k_number);
+*/
+ return decoder_mmu_box_alloc_idx(
+ hevc->mmu_box,
+ cur_buf_idx,
+ cur_mmu_4k_number,
+ mmu_index_adr);
+}
+
+
+
+
+
+
+/*
+*************************************************
+*
+*h265 buffer management end
+*
+**************************************************
+*/
+static struct hevc_state_s gHevc;
+
+static void hevc_local_uninit(struct hevc_state_s *hevc)
+{
+ hevc->rpm_ptr = NULL;
+ hevc->lmem_ptr = NULL;
+
+ if (hevc->rpm_addr) {
+ dma_unmap_single(amports_get_dma_device(),
+ hevc->rpm_phy_addr, RPM_BUF_SIZE, DMA_FROM_DEVICE);
+ kfree(hevc->rpm_addr);
+ hevc->rpm_addr = NULL;
+ }
+ if (hevc->lmem_addr) {
+ dma_unmap_single(amports_get_dma_device(),
+ hevc->lmem_phy_addr, LMEM_BUF_SIZE, DMA_FROM_DEVICE);
+ kfree(hevc->lmem_addr);
+ hevc->lmem_addr = NULL;
+ }
+
+ if (mmu_enable && hevc->frame_mmu_map_addr) {
+ if (hevc->frame_mmu_map_phy_addr)
+ dma_free_coherent(amports_get_dma_device(),
+ FRAME_MMU_MAP_SIZE, hevc->frame_mmu_map_addr,
+ hevc->frame_mmu_map_phy_addr);
+ hevc->frame_mmu_map_addr = NULL;
+ }
+
+
+}
+
+static int hevc_local_init(struct hevc_state_s *hevc)
+{
+ int ret = -1;
+ struct BuffInfo_s *cur_buf_info = NULL;
+
+ memset(&hevc->param, 0, sizeof(union param_u));
+
+ cur_buf_info = &hevc->work_space_buf_store;
+#ifdef SUPPORT_4K2K
+ memcpy(cur_buf_info, &amvh265_workbuff_spec[1], /* 4k2k work space */
+ sizeof(struct BuffInfo_s));
+#else
+ memcpy(cur_buf_info, &amvh265_workbuff_spec[0], /* 1080p work space */
+ sizeof(struct BuffInfo_s));
+#endif
+ cur_buf_info->start_adr = hevc->buf_start;
+ hevc->mc_buf_spec.buf_end = hevc->buf_start + hevc->buf_size;
+ init_buff_spec(hevc, cur_buf_info);
+
+ if (mmu_enable) {
+ hevc->mmu_box = decoder_mmu_box_alloc_box(DRIVER_NAME,
+ 0, MAX_REF_PIC_NUM);
+ if (!hevc->mmu_box) {
+ pr_err("h265 alloc mmu box failed!!\n");
+ return -1;
+ }
+ }
+
+
+ hevc->mc_buf_spec.buf_start = (cur_buf_info->end_adr + 0xffff)
+ & (~0xffff);
+ hevc->mc_buf_spec.buf_size = (hevc->mc_buf_spec.buf_end
+ - hevc->mc_buf_spec.buf_start);
+
+ hevc_init_stru(hevc, cur_buf_info, &hevc->mc_buf_spec);
+
+ hevc->bit_depth_luma = 8;
+ hevc->bit_depth_chroma = 8;
+ hevc->video_signal_type = 0;
+ bit_depth_luma = hevc->bit_depth_luma;
+ bit_depth_chroma = hevc->bit_depth_chroma;
+ video_signal_type = hevc->video_signal_type;
+
+ if ((debug & H265_DEBUG_SEND_PARAM_WITH_REG) == 0) {
+ hevc->rpm_addr = kmalloc(RPM_BUF_SIZE, GFP_KERNEL);
+ if (hevc->rpm_addr == NULL) {
+ pr_err("%s: failed to alloc rpm buffer\n", __func__);
+ return -1;
+ }
+
+ hevc->rpm_phy_addr = dma_map_single(amports_get_dma_device(),
+ hevc->rpm_addr, RPM_BUF_SIZE, DMA_FROM_DEVICE);
+ if (dma_mapping_error(amports_get_dma_device(),
+ hevc->rpm_phy_addr)) {
+ pr_err("%s: failed to map rpm buffer\n", __func__);
+ kfree(hevc->rpm_addr);
+ hevc->rpm_addr = NULL;
+ return -1;
+ }
+
+ hevc->rpm_ptr = hevc->rpm_addr;
+ }
+
+ if (debug & H265_DEBUG_UCODE) {
+ hevc->lmem_addr = kmalloc(LMEM_BUF_SIZE, GFP_KERNEL);
+ if (hevc->lmem_addr == NULL) {
+ pr_err("%s: failed to alloc lmem buffer\n", __func__);
+ return -1;
+ }
+
+ hevc->lmem_phy_addr = dma_map_single(amports_get_dma_device(),
+ hevc->lmem_addr, LMEM_BUF_SIZE, DMA_FROM_DEVICE);
+ if (dma_mapping_error(amports_get_dma_device(),
+ hevc->lmem_phy_addr)) {
+ pr_err("%s: failed to map lmem buffer\n", __func__);
+ kfree(hevc->lmem_addr);
+ hevc->lmem_addr = NULL;
+ return -1;
+ }
+
+ hevc->lmem_ptr = hevc->lmem_addr;
+ }
+
+ if (mmu_enable) {
+ hevc->frame_mmu_map_addr =
+ dma_alloc_coherent(amports_get_dma_device(),
+ FRAME_MMU_MAP_SIZE,
+ &hevc->frame_mmu_map_phy_addr, GFP_KERNEL);
+ if (hevc->frame_mmu_map_addr == NULL) {
+ pr_err("%s: failed to alloc count_buffer\n", __func__);
+ return -1;
+ }
+ memset(hevc->frame_mmu_map_addr, 0, FRAME_MMU_MAP_SIZE);
+ }
+ ret = 0;
+ return ret;
+}
+
+/*
+*******************************************
+ * Mailbox command
+ *******************************************
+ */
+#define CMD_FINISHED 0
+#define CMD_ALLOC_VIEW 1
+#define CMD_FRAME_DISPLAY 3
+#define CMD_DEBUG 10
+
+
+#define DECODE_BUFFER_NUM_MAX 32
+#define DISPLAY_BUFFER_NUM 6
+
+#define video_domain_addr(adr) (adr&0x7fffffff)
+#define DECODER_WORK_SPACE_SIZE 0x800000
+
+#define spec2canvas(x) \
+ (((x)->uv_canvas_index << 16) | \
+ ((x)->uv_canvas_index << 8) | \
+ ((x)->y_canvas_index << 0))
+
+
+static void set_canvas(struct PIC_s *pic)
+{
+ int canvas_w = ALIGN(pic->width, 64)/4;
+ int canvas_h = ALIGN(pic->height, 32)/4;
+ int blkmode = mem_map_mode;
+
+ /*CANVAS_BLKMODE_64X32*/
+#ifdef SUPPORT_10BIT
+ if (double_write_mode) {
+ canvas_w = pic->width;
+ canvas_h = pic->height;
+ if ((double_write_mode == 2) ||
+ (double_write_mode == 3)) {
+ canvas_w >>= 2;
+ canvas_h >>= 2;
+ }
+
+ if (mem_map_mode == 0)
+ canvas_w = ALIGN(canvas_w, 32);
+ else
+ canvas_w = ALIGN(canvas_w, 64);
+ canvas_h = ALIGN(canvas_h, 32);
+
+ pic->y_canvas_index = 128 + pic->index * 2;
+ pic->uv_canvas_index = 128 + pic->index * 2 + 1;
+
+ canvas_config_ex(pic->y_canvas_index,
+ pic->dw_y_adr, canvas_w, canvas_h,
+ CANVAS_ADDR_NOWRAP, blkmode, 0x7);
+ canvas_config_ex(pic->uv_canvas_index, pic->dw_u_v_adr,
+ canvas_w, canvas_h,
+ CANVAS_ADDR_NOWRAP, blkmode, 0x7);
+#ifdef MULTI_INSTANCE_SUPPORT
+ pic->canvas_config[0].phy_addr =
+ pic->dw_y_adr;
+ pic->canvas_config[0].width =
+ canvas_w;
+ pic->canvas_config[0].height =
+ canvas_h;
+ pic->canvas_config[0].block_mode =
+ blkmode;
+ pic->canvas_config[0].endian = 7;
+
+ pic->canvas_config[1].phy_addr =
+ pic->dw_u_v_adr;
+ pic->canvas_config[1].width =
+ canvas_w;
+ pic->canvas_config[1].height =
+ canvas_h;
+ pic->canvas_config[1].block_mode =
+ blkmode;
+ pic->canvas_config[1].endian = 7;
+#endif
+ } else {
+ if (!mmu_enable) {
+ /* to change after 10bit VPU is ready ... */
+ pic->y_canvas_index = 128 + pic->index;
+ pic->uv_canvas_index = 128 + pic->index;
+
+ canvas_config_ex(pic->y_canvas_index,
+ pic->mc_y_adr, canvas_w, canvas_h,
+ CANVAS_ADDR_NOWRAP, blkmode, 0x7);
+ canvas_config_ex(pic->uv_canvas_index, pic->mc_u_v_adr,
+ canvas_w, canvas_h,
+ CANVAS_ADDR_NOWRAP, blkmode, 0x7);
+ }
+ }
+#else
+ pic->y_canvas_index = 128 + pic->index * 2;
+ pic->uv_canvas_index = 128 + pic->index * 2 + 1;
+
+ canvas_config_ex(pic->y_canvas_index, pic->mc_y_adr, canvas_w, canvas_h,
+ CANVAS_ADDR_NOWRAP, blkmode, 0x7);
+ canvas_config_ex(pic->uv_canvas_index, pic->mc_u_v_adr,
+ canvas_w, canvas_h,
+ CANVAS_ADDR_NOWRAP, blkmode, 0x7);
+#endif
+}
+
+static int init_buf_spec(struct hevc_state_s *hevc)
+{
+ int pic_width = hevc->pic_w;
+ int pic_height = hevc->pic_h;
+
+ /* pr_info("%s1: %d %d\n", __func__, hevc->pic_w, hevc->pic_h); */
+ pr_info("%s2 %d %d\n", __func__, pic_width, pic_height);
+ /* pic_width = hevc->pic_w; */
+ /* pic_height = hevc->pic_h; */
+
+ if (hevc->frame_width == 0 || hevc->frame_height == 0) {
+ hevc->frame_width = pic_width;
+ hevc->frame_height = pic_height;
+
+ }
+
+ return 0;
+}
+
+static void set_frame_info(struct hevc_state_s *hevc, struct vframe_s *vf)
+{
+ unsigned int ar;
+ int i, j;
+
+ if ((double_write_mode == 2) ||
+ (double_write_mode == 3)) {
+ vf->width = hevc->frame_width/4;
+ vf->height = hevc->frame_height/4;
+ } else {
+ vf->width = hevc->frame_width;
+ vf->height = hevc->frame_height;
+ }
+ vf->duration = hevc->frame_dur;
+ vf->duration_pulldown = 0;
+ vf->flag = 0;
+
+ ar = min_t(u32, hevc->frame_ar, DISP_RATIO_ASPECT_RATIO_MAX);
+ vf->ratio_control = (ar << DISP_RATIO_ASPECT_RATIO_BIT);
+
+ /* signal_type */
+ if (hevc->video_signal_type & VIDEO_SIGNAL_TYPE_AVAILABLE_MASK)
+ vf->signal_type = hevc->video_signal_type;
+ else
+ vf->signal_type = 0;
+
+ /* master_display_colour */
+ if (hevc->sei_present_flag & SEI_MASTER_DISPLAY_COLOR_MASK) {
+ for (i = 0; i < 3; i++)
+ for (j = 0; j < 2; j++)
+ vf->prop.master_display_colour.primaries[i][j]
+ = hevc->primaries[i][j];
+ for (i = 0; i < 2; i++) {
+ vf->prop.master_display_colour.white_point[i]
+ = hevc->white_point[i];
+ vf->prop.master_display_colour.luminance[i]
+ = hevc->luminance[i];
+ }
+ vf->prop.master_display_colour.present_flag = 1;
+ } else
+ vf->prop.master_display_colour.present_flag = 0;
+
+}
+
+static int vh265_vf_states(struct vframe_states *states, void *op_arg)
+{
+ unsigned long flags;
+#ifdef MULTI_INSTANCE_SUPPORT
+ struct vdec_s *vdec = op_arg;
+ struct hevc_state_s *hevc = (struct hevc_state_s *)vdec->private;
+#else
+ struct hevc_state_s *hevc = (struct hevc_state_s *)op_arg;
+#endif
+
+ spin_lock_irqsave(&lock, flags);
+
+ states->vf_pool_size = VF_POOL_SIZE;
+ states->buf_free_num = kfifo_len(&hevc->newframe_q);
+ states->buf_avail_num = kfifo_len(&hevc->display_q);
+
+ if (step == 2)
+ states->buf_avail_num = 0;
+ spin_unlock_irqrestore(&lock, flags);
+ return 0;
+}
+
+static struct vframe_s *vh265_vf_peek(void *op_arg)
+{
+ struct vframe_s *vf;
+#ifdef MULTI_INSTANCE_SUPPORT
+ struct vdec_s *vdec = op_arg;
+ struct hevc_state_s *hevc = (struct hevc_state_s *)vdec->private;
+#else
+ struct hevc_state_s *hevc = (struct hevc_state_s *)op_arg;
+#endif
+
+ if (step == 2)
+ return NULL;
+
+ if (kfifo_peek(&hevc->display_q, &vf))
+ return vf;
+
+ return NULL;
+}
+
+static struct vframe_s *vh265_vf_get(void *op_arg)
+{
+ struct vframe_s *vf;
+#ifdef MULTI_INSTANCE_SUPPORT
+ struct vdec_s *vdec = op_arg;
+ struct hevc_state_s *hevc = (struct hevc_state_s *)vdec->private;
+#else
+ struct hevc_state_s *hevc = (struct hevc_state_s *)op_arg;
+#endif
+
+ if (step == 2)
+ return NULL;
+ else if (step == 1)
+ step = 2;
+
+ if (kfifo_get(&hevc->display_q, &vf)) {
+ if (debug & H265_DEBUG_PIC_STRUCT)
+ pr_info("%s(type %d index 0x%x)\n",
+ __func__, vf->type, vf->index);
+
+ hevc->show_frame_num++;
+ return vf;
+ }
+
+ return NULL;
+}
+
+static void vh265_vf_put(struct vframe_s *vf, void *op_arg)
+{
+ unsigned long flags;
+#ifdef MULTI_INSTANCE_SUPPORT
+ struct vdec_s *vdec = op_arg;
+ struct hevc_state_s *hevc = (struct hevc_state_s *)vdec->private;
+#else
+ struct hevc_state_s *hevc = (struct hevc_state_s *)op_arg;
+#endif
+ unsigned char index_top = vf->index & 0xff;
+ unsigned char index_bot = (vf->index >> 8) & 0xff;
+
+ if (debug & H265_DEBUG_PIC_STRUCT)
+ pr_info("%s(type %d index 0x%x)\n",
+ __func__, vf->type, vf->index);
+
+ kfifo_put(&hevc->newframe_q, (const struct vframe_s *)vf);
+ spin_lock_irqsave(&lock, flags);
+
+ if (index_top != 0xff && index_top >= 0
+ && index_top < MAX_REF_PIC_NUM) {
+ if (hevc->m_PIC[index_top].vf_ref > 0) {
+ hevc->m_PIC[index_top].vf_ref--;
+
+ if (hevc->m_PIC[index_top].vf_ref == 0) {
+ hevc->m_PIC[index_top].output_ready = 0;
+ if (mmu_enable)
+ hevc->m_PIC[index_top].
+ used_by_display = 0;
+ hevc->last_put_idx_a = index_top;
+ if (hevc->wait_buf != 0)
+ WRITE_VREG(HEVC_ASSIST_MBOX1_IRQ_REG,
+ 0x1);
+ }
+ }
+ }
+
+ if (index_bot != 0xff && index_bot >= 0
+ && index_bot < MAX_REF_PIC_NUM) {
+ if (hevc->m_PIC[index_bot].vf_ref > 0) {
+ hevc->m_PIC[index_bot].vf_ref--;
+
+ if (hevc->m_PIC[index_bot].vf_ref == 0) {
+ clear_used_by_display_flag(hevc);
+ hevc->m_PIC[index_bot].output_ready = 0;
+ hevc->last_put_idx_b = index_bot;
+ if (hevc->wait_buf != 0)
+ WRITE_VREG(HEVC_ASSIST_MBOX1_IRQ_REG,
+ 0x1);
+ }
+ }
+ }
+ spin_unlock_irqrestore(&lock, flags);
+}
+
+static int vh265_event_cb(int type, void *data, void *private_data)
+{
+ if (type & VFRAME_EVENT_RECEIVER_RESET) {
+#if 0
+ unsigned long flags;
+
+ amhevc_stop();
+#ifndef CONFIG_POST_PROCESS_MANAGER
+ vf_light_unreg_provider(&vh265_vf_prov);
+#endif
+ spin_lock_irqsave(&hevc->lock, flags);
+ vh265_local_init();
+ vh265_prot_init();
+ spin_unlock_irqrestore(&hevc->lock, flags);
+#ifndef CONFIG_POST_PROCESS_MANAGER
+ vf_reg_provider(&vh265_vf_prov);
+#endif
+ amhevc_start();
+#endif
+ }
+
+ return 0;
+}
+
+#ifdef HEVC_PIC_STRUCT_SUPPORT
+static int process_pending_vframe(struct hevc_state_s *hevc,
+ struct PIC_s *pair_pic, unsigned char pair_frame_top_flag)
+{
+ struct vframe_s *vf;
+
+ if (debug & H265_DEBUG_PIC_STRUCT)
+ pr_info("%s: pair_pic index 0x%x %s\n",
+ __func__, pair_pic->index,
+ pair_frame_top_flag ?
+ "top" : "bot");
+
+ if (kfifo_len(&hevc->pending_q) > 1) {
+ /* do not pending more than 1 frame */
+ if (kfifo_get(&hevc->pending_q, &vf) == 0) {
+ pr_info("fatal error, no available buffer slot.");
+ return -1;
+ }
+ if (debug & H265_DEBUG_PIC_STRUCT)
+ pr_info("%s warning(1), vf=>display_q: (index 0x%x)\n",
+ __func__, vf->index);
+ kfifo_put(&hevc->display_q, (const struct vframe_s *)vf);
+ }
+
+ if (kfifo_peek(&hevc->pending_q, &vf)) {
+ if (pair_pic == NULL || pair_pic->vf_ref <= 0) {
+ /*
+ *if pair_pic is recycled (pair_pic->vf_ref <= 0),
+ *do not use it
+ */
+ if (kfifo_get(&hevc->pending_q, &vf) == 0) {
+ pr_info("fatal error, no available buffer slot.");
+ return -1;
+ }
+ if (debug & H265_DEBUG_PIC_STRUCT)
+ pr_info("%s warning(2), vf=>display_q: (index 0x%x)\n",
+ __func__, vf->index);
+ if (vf)
+ kfifo_put(&hevc->display_q,
+ (const struct vframe_s *)vf);
+ } else if ((!pair_frame_top_flag) &&
+ (((vf->index >> 8) & 0xff) == 0xff)) {
+ if (kfifo_get(&hevc->pending_q, &vf) == 0) {
+ pr_info("fatal error, no available buffer slot.");
+ return -1;
+ }
+ if (vf) {
+ vf->type = VIDTYPE_PROGRESSIVE
+ | VIDTYPE_VIU_NV21;
+ vf->index &= 0xff;
+ vf->index |= (pair_pic->index << 8);
+ vf->canvas1Addr = spec2canvas(pair_pic);
+ pair_pic->vf_ref++;
+ kfifo_put(&hevc->display_q,
+ (const struct vframe_s *)vf);
+ if (debug & H265_DEBUG_PIC_STRUCT)
+ pr_info("%s vf => display_q: (index 0x%x)\n",
+ __func__, vf->index);
+ }
+ } else if (pair_frame_top_flag &&
+ ((vf->index & 0xff) == 0xff)) {
+ if (kfifo_get(&hevc->pending_q, &vf) == 0) {
+ pr_info("fatal error, no available buffer slot.");
+ return -1;
+ }
+ if (vf) {
+ vf->type = VIDTYPE_PROGRESSIVE
+ | VIDTYPE_VIU_NV21;
+ vf->index &= 0xff00;
+ vf->index |= pair_pic->index;
+ vf->canvas0Addr = spec2canvas(pair_pic);
+ pair_pic->vf_ref++;
+ kfifo_put(&hevc->display_q,
+ (const struct vframe_s *)vf);
+ if (debug & H265_DEBUG_PIC_STRUCT)
+ pr_info("%s vf => display_q: (index 0x%x)\n",
+ __func__, vf->index);
+ }
+ }
+ }
+ return 0;
+}
+#endif
+
+static int prepare_display_buf(struct hevc_state_s *hevc, struct PIC_s *pic)
+{
+ struct vframe_s *vf = NULL;
+ int stream_offset = pic->stream_offset;
+ unsigned short slice_type = pic->slice_type;
+
+ if (kfifo_get(&hevc->newframe_q, &vf) == 0) {
+ pr_info("fatal error, no available buffer slot.");
+ return -1;
+ }
+
+ if (vf) {
+ /*
+ *if (pts_lookup_offset(PTS_TYPE_VIDEO,
+ *stream_offset, &vf->pts, 0) != 0) {
+ */
+ if (pts_lookup_offset_us64
+ (PTS_TYPE_VIDEO, stream_offset, &vf->pts, 0,
+ &vf->pts_us64) != 0) {
+#ifdef DEBUG_PTS
+ hevc->pts_missed++;
+#endif
+ vf->pts = 0;
+ vf->pts_us64 = 0;
+ }
+#ifdef DEBUG_PTS
+ else
+ hevc->pts_hit++;
+#endif
+ if (pts_unstable && (hevc->frame_dur > 0))
+ hevc->pts_mode = PTS_NONE_REF_USE_DURATION;
+
+ if ((hevc->pts_mode == PTS_NORMAL) && (vf->pts != 0)
+ && hevc->get_frame_dur) {
+ int pts_diff = (int)vf->pts - hevc->last_lookup_pts;
+
+ if (pts_diff < 0) {
+ hevc->pts_mode_switching_count++;
+ hevc->pts_mode_recovery_count = 0;
+
+ if (hevc->pts_mode_switching_count >=
+ PTS_MODE_SWITCHING_THRESHOLD) {
+ hevc->pts_mode =
+ PTS_NONE_REF_USE_DURATION;
+ pr_info
+ ("HEVC: switch to n_d mode.\n");
+ }
+
+ } else {
+ int p = PTS_MODE_SWITCHING_RECOVERY_THREASHOLD;
+
+ hevc->pts_mode_recovery_count++;
+ if (hevc->pts_mode_recovery_count > p) {
+ hevc->pts_mode_switching_count = 0;
+ hevc->pts_mode_recovery_count = 0;
+ }
+ }
+ }
+
+ if (vf->pts != 0)
+ hevc->last_lookup_pts = vf->pts;
+
+ if ((hevc->pts_mode == PTS_NONE_REF_USE_DURATION)
+ && (slice_type != 2))
+ vf->pts = hevc->last_pts + DUR2PTS(hevc->frame_dur);
+ hevc->last_pts = vf->pts;
+
+ if (vf->pts_us64 != 0)
+ hevc->last_lookup_pts_us64 = vf->pts_us64;
+
+ if ((hevc->pts_mode == PTS_NONE_REF_USE_DURATION)
+ && (slice_type != 2)) {
+ vf->pts_us64 =
+ hevc->last_pts_us64 +
+ (DUR2PTS(hevc->frame_dur) * 100 / 9);
+ }
+ hevc->last_pts_us64 = vf->pts_us64;
+ if ((debug & H265_DEBUG_OUT_PTS) != 0) {
+ pr_info
+ ("H265 dec out pts: vf->pts=%d, vf->pts_us64 = %lld\n",
+ vf->pts, vf->pts_us64);
+ }
+
+ /*
+ *vf->index:
+ *(1) vf->type is VIDTYPE_PROGRESSIVE
+ * and vf->canvas0Addr != vf->canvas1Addr,
+ * vf->index[7:0] is the index of top pic
+ * vf->index[15:8] is the index of bot pic
+ *(2) other cases,
+ * only vf->index[7:0] is used
+ * vf->index[15:8] == 0xff
+ */
+ vf->index = 0xff00 | pic->index;
+#if 1
+/*SUPPORT_10BIT*/
+ if (double_write_mode & 0x10) {
+ /* double write only */
+ vf->compBodyAddr = 0;
+ vf->compHeadAddr = 0;
+ } else {
+
+ if (mmu_enable) {
+ vf->compBodyAddr = 0;
+ vf->compHeadAddr = pic->header_adr;
+ } else {
+ vf->compBodyAddr = pic->mc_y_adr; /*body adr*/
+ vf->compHeadAddr = pic->mc_y_adr +
+ pic->losless_comp_body_size;
+ }
+
+ /*head adr*/
+ vf->canvas0Addr = vf->canvas1Addr = 0;
+ }
+ if (double_write_mode) {
+ vf->type = VIDTYPE_PROGRESSIVE | VIDTYPE_VIU_FIELD;
+ vf->type |= VIDTYPE_VIU_NV21;
+ if (double_write_mode == 3)
+ vf->type |= VIDTYPE_COMPRESS;
+#ifdef MULTI_INSTANCE_SUPPORT
+ if (hevc->m_ins_flag &&
+ (debug & USE_OLD_PROVIDER) == 0) {
+ vf->canvas0Addr = vf->canvas1Addr = -1;
+ vf->canvas0_config[0] =
+ pic->canvas_config[0];
+ vf->canvas0_config[1] =
+ pic->canvas_config[1];
+
+ vf->canvas1_config[0] =
+ pic->canvas_config[0];
+ vf->canvas1_config[1] =
+ pic->canvas_config[1];
+
+ } else
+#endif
+ vf->canvas0Addr = vf->canvas1Addr
+ = spec2canvas(pic);
+ } else {
+ vf->canvas0Addr = vf->canvas1Addr = 0;
+ vf->type = VIDTYPE_COMPRESS | VIDTYPE_VIU_FIELD;
+ if (mmu_enable)
+ vf->type |= VIDTYPE_SCATTER;
+
+ }
+ vf->compWidth = pic->width;
+ vf->compHeight = pic->height;
+
+ switch (hevc->bit_depth_luma) {
+ case 9:
+ vf->bitdepth = BITDEPTH_Y9;
+ break;
+ case 10:
+ vf->bitdepth = BITDEPTH_Y10;
+ break;
+ default:
+ vf->bitdepth = BITDEPTH_Y8;
+ break;
+ }
+ switch (hevc->bit_depth_chroma) {
+ case 9:
+ vf->bitdepth |= (BITDEPTH_U9 | BITDEPTH_V9);
+ break;
+ case 10:
+ vf->bitdepth |= (BITDEPTH_U10 | BITDEPTH_V10);
+ break;
+ default:
+ vf->bitdepth |= (BITDEPTH_U8 | BITDEPTH_V8);
+ break;
+ }
+ if (hevc->mem_saving_mode == 1)
+ vf->bitdepth |= BITDEPTH_SAVING_MODE;
+#else
+ vf->type = VIDTYPE_PROGRESSIVE | VIDTYPE_VIU_FIELD;
+ vf->type |= VIDTYPE_VIU_NV21;
+ vf->canvas0Addr = vf->canvas1Addr = spec2canvas(pic);
+#endif
+ set_frame_info(hevc, vf);
+ /*
+ *if((vf->width!=pic->width)||(vf->height!=pic->height))
+ */
+ /*
+ *pr_info("aaa: %d/%d, %d/%d\n",
+ *vf->width,vf->height, pic->width, pic->height);
+ */
+ if ((double_write_mode == 2) ||
+ (double_write_mode == 3)) {
+ vf->width = pic->width/4;
+ vf->height = pic->height/4;
+ } else {
+ vf->width = pic->width;
+ vf->height = pic->height;
+ }
+ if (force_w_h != 0) {
+ vf->width = (force_w_h >> 16) & 0xffff;
+ vf->height = force_w_h & 0xffff;
+ }
+ if (force_fps & 0x100) {
+ u32 rate = force_fps & 0xff;
+
+ if (rate)
+ vf->duration = 96000/rate;
+ else
+ vf->duration = 0;
+ }
+
+ /*
+ * !!! to do ...
+ * need move below code to get_new_pic(),
+ * hevc->xxx can only be used by current decoded pic
+ */
+ if (hevc->param.p.conformance_window_flag &&
+ (debug & H265_DEBUG_IGNORE_CONFORMANCE_WINDOW) == 0) {
+ unsigned SubWidthC, SubHeightC;
+
+ switch (hevc->param.p.chroma_format_idc) {
+ case 1:
+ SubWidthC = 2;
+ SubHeightC = 2;
+ break;
+ case 2:
+ SubWidthC = 2;
+ SubHeightC = 1;
+ break;
+ default:
+ SubWidthC = 1;
+ SubHeightC = 1;
+ break;
+ }
+ vf->width -= SubWidthC *
+ (hevc->param.p.conf_win_left_offset +
+ hevc->param.p.conf_win_right_offset);
+ vf->height -= SubHeightC *
+ (hevc->param.p.conf_win_top_offset +
+ hevc->param.p.conf_win_bottom_offset);
+ if (debug & H265_DEBUG_BUFMGR)
+ pr_info("conformance_window %d, %d, %d, %d, %d => cropped width %d, height %d\n",
+ hevc->param.p.chroma_format_idc,
+ hevc->param.p.conf_win_left_offset,
+ hevc->param.p.conf_win_right_offset,
+ hevc->param.p.conf_win_top_offset,
+ hevc->param.p.conf_win_bottom_offset,
+ vf->width, vf->height);
+ }
+
+#ifdef HEVC_PIC_STRUCT_SUPPORT
+ if (pic->pic_struct == 3 || pic->pic_struct == 4) {
+ struct vframe_s *vf2;
+
+ if (debug & H265_DEBUG_PIC_STRUCT)
+ pr_info("pic_struct = %d index 0x%x\n",
+ pic->pic_struct,
+ pic->index);
+
+ if (kfifo_get(&hevc->newframe_q, &vf2) == 0) {
+ pr_info("fatal error, no available buffer slot.");
+ return -1;
+ }
+ pic->vf_ref = 2;
+ vf->duration = vf->duration>>1;
+ memcpy(vf2, vf, sizeof(struct vframe_s));
+
+ if (pic->pic_struct == 3) {
+ vf->type = VIDTYPE_INTERLACE_TOP
+ | VIDTYPE_VIU_NV21;
+ vf2->type = VIDTYPE_INTERLACE_BOTTOM
+ | VIDTYPE_VIU_NV21;
+ } else {
+ vf->type = VIDTYPE_INTERLACE_BOTTOM
+ | VIDTYPE_VIU_NV21;
+ vf2->type = VIDTYPE_INTERLACE_TOP
+ | VIDTYPE_VIU_NV21;
+ }
+ kfifo_put(&hevc->display_q,
+ (const struct vframe_s *)vf);
+ kfifo_put(&hevc->display_q,
+ (const struct vframe_s *)vf2);
+ } else if (pic->pic_struct == 5
+ || pic->pic_struct == 6) {
+ struct vframe_s *vf2, *vf3;
+
+ if (debug & H265_DEBUG_PIC_STRUCT)
+ pr_info("pic_struct = %d index 0x%x\n",
+ pic->pic_struct,
+ pic->index);
+
+ if (kfifo_get(&hevc->newframe_q, &vf2) == 0) {
+ pr_info("fatal error, no available buffer slot.");
+ return -1;
+ }
+ if (kfifo_get(&hevc->newframe_q, &vf3) == 0) {
+ pr_info("fatal error, no available buffer slot.");
+ return -1;
+ }
+ pic->vf_ref = 3;
+ vf->duration = vf->duration/3;
+ memcpy(vf2, vf, sizeof(struct vframe_s));
+ memcpy(vf3, vf, sizeof(struct vframe_s));
+
+ if (pic->pic_struct == 5) {
+ vf->type = VIDTYPE_INTERLACE_TOP
+ | VIDTYPE_VIU_NV21;
+ vf2->type = VIDTYPE_INTERLACE_BOTTOM
+ | VIDTYPE_VIU_NV21;
+ vf3->type = VIDTYPE_INTERLACE_TOP
+ | VIDTYPE_VIU_NV21;
+ } else {
+ vf->type = VIDTYPE_INTERLACE_BOTTOM
+ | VIDTYPE_VIU_NV21;
+ vf2->type = VIDTYPE_INTERLACE_TOP
+ | VIDTYPE_VIU_NV21;
+ vf3->type = VIDTYPE_INTERLACE_BOTTOM
+ | VIDTYPE_VIU_NV21;
+ }
+ kfifo_put(&hevc->display_q,
+ (const struct vframe_s *)vf);
+ kfifo_put(&hevc->display_q,
+ (const struct vframe_s *)vf2);
+ kfifo_put(&hevc->display_q,
+ (const struct vframe_s *)vf3);
+
+ } else if (pic->pic_struct == 9
+ || pic->pic_struct == 10) {
+ if (debug & H265_DEBUG_PIC_STRUCT)
+ pr_info("pic_struct = %d index 0x%x\n",
+ pic->pic_struct,
+ pic->index);
+
+ pic->vf_ref = 1;
+ /* process previous pending vf*/
+ process_pending_vframe(hevc,
+ pic, (pic->pic_struct == 9));
+
+ /* process current vf */
+ kfifo_put(&hevc->pending_q,
+ (const struct vframe_s *)vf);
+ vf->height <<= 1;
+ if (pic->pic_struct == 9) {
+ vf->type = VIDTYPE_INTERLACE_TOP
+ | VIDTYPE_VIU_NV21 | VIDTYPE_VIU_FIELD;
+ process_pending_vframe(hevc,
+ hevc->pre_bot_pic, 0);
+ } else {
+ vf->type = VIDTYPE_INTERLACE_BOTTOM |
+ VIDTYPE_VIU_NV21 | VIDTYPE_VIU_FIELD;
+ vf->index = (pic->index << 8) | 0xff;
+ process_pending_vframe(hevc,
+ hevc->pre_top_pic, 1);
+ }
+
+ /**/
+ if (pic->pic_struct == 9)
+ hevc->pre_top_pic = pic;
+ else
+ hevc->pre_bot_pic = pic;
+
+ } else if (pic->pic_struct == 11
+ || pic->pic_struct == 12) {
+ if (debug & H265_DEBUG_PIC_STRUCT)
+ pr_info("pic_struct = %d index 0x%x\n",
+ pic->pic_struct,
+ pic->index);
+ pic->vf_ref = 1;
+ /* process previous pending vf*/
+ process_pending_vframe(hevc, pic,
+ (pic->pic_struct == 11));
+
+ /* put current into pending q */
+ vf->height <<= 1;
+ if (pic->pic_struct == 11)
+ vf->type = VIDTYPE_INTERLACE_TOP |
+ VIDTYPE_VIU_NV21 | VIDTYPE_VIU_FIELD;
+ else {
+ vf->type = VIDTYPE_INTERLACE_BOTTOM |
+ VIDTYPE_VIU_NV21 | VIDTYPE_VIU_FIELD;
+ vf->index = (pic->index << 8) | 0xff;
+ }
+ kfifo_put(&hevc->pending_q,
+ (const struct vframe_s *)vf);
+
+ /**/
+ if (pic->pic_struct == 11)
+ hevc->pre_top_pic = pic;
+ else
+ hevc->pre_bot_pic = pic;
+
+ } else {
+ pic->vf_ref = 1;
+
+ if (debug & H265_DEBUG_PIC_STRUCT)
+ pr_info("pic_struct = %d index 0x%x\n",
+ pic->pic_struct,
+ pic->index);
+
+ switch (pic->pic_struct) {
+ case 7:
+ vf->duration <<= 1;
+ break;
+ case 8:
+ vf->duration = vf->duration * 3;
+ break;
+ case 1:
+ vf->height <<= 1;
+ vf->type = VIDTYPE_INTERLACE_TOP |
+ VIDTYPE_VIU_NV21 | VIDTYPE_VIU_FIELD;
+ process_pending_vframe(hevc, pic, 1);
+ hevc->pre_top_pic = pic;
+ break;
+ case 2:
+ vf->height <<= 1;
+ vf->type = VIDTYPE_INTERLACE_BOTTOM
+ | VIDTYPE_VIU_NV21
+ | VIDTYPE_VIU_FIELD;
+ process_pending_vframe(hevc, pic, 0);
+ hevc->pre_bot_pic = pic;
+ break;
+ }
+ kfifo_put(&hevc->display_q,
+ (const struct vframe_s *)vf);
+ }
+#else
+ vf->type_original = vf->type;
+
+ if (mmu_enable)
+ vf->mem_handle =
+ decoder_mmu_box_get_mem_handle(
+ hevc->mmu_box, pic->index);
+ pic->vf_ref = 1;
+ kfifo_put(&hevc->display_q, (const struct vframe_s *)vf);
+#endif
+
+ vf_notify_receiver(hevc->provider_name,
+ VFRAME_EVENT_PROVIDER_VFRAME_READY, NULL);
+ }
+
+ return 0;
+}
+
+static void process_nal_sei(struct hevc_state_s *hevc,
+ int payload_type, int payload_size)
+{
+ unsigned short data;
+
+ if (debug & H265_DEBUG_PRINT_SEI)
+ pr_info("\tsei message: payload_type = 0x%02x, payload_size = 0x%02x\n",
+ payload_type, payload_size);
+
+ if (payload_type == 137) {
+ int i, j;
+ /* MASTERING_DISPLAY_COLOUR_VOLUME */
+ if (payload_size >= 24) {
+ if (debug & H265_DEBUG_PRINT_SEI)
+ pr_info("\tsei MASTERING_DISPLAY_COLOUR_VOLUME available\n");
+ for (i = 0; i < 3; i++) {
+ for (j = 0; j < 2; j++) {
+ data =
+ (READ_HREG(HEVC_SHIFTED_DATA) >> 16);
+ hevc->primaries[i][j] = data;
+ WRITE_HREG(HEVC_SHIFT_COMMAND,
+ (1<<7)|16);
+ if (debug & H265_DEBUG_PRINT_SEI)
+ pr_info("\t\tprimaries[%1d][%1d] = %04x\n",
+ i, j, hevc->primaries[i][j]);
+ }
+ }
+ for (i = 0; i < 2; i++) {
+ data = (READ_HREG(HEVC_SHIFTED_DATA) >> 16);
+ hevc->white_point[i] = data;
+ WRITE_HREG(HEVC_SHIFT_COMMAND, (1<<7)|16);
+ if (debug & H265_DEBUG_PRINT_SEI)
+ pr_info("\t\twhite_point[%1d] = %04x\n",
+ i, hevc->white_point[i]);
+ }
+ for (i = 0; i < 2; i++) {
+ data =
+ (READ_HREG(HEVC_SHIFTED_DATA) >> 16);
+ hevc->luminance[i] = data << 16;
+ WRITE_HREG(HEVC_SHIFT_COMMAND,
+ (1<<7)|16);
+ data =
+ (READ_HREG(HEVC_SHIFTED_DATA) >> 16);
+ hevc->luminance[i] |= data;
+ WRITE_HREG(HEVC_SHIFT_COMMAND,
+ (1<<7)|16);
+ if (debug & H265_DEBUG_PRINT_SEI)
+ pr_info("\t\tluminance[%1d] = %08x\n",
+ i, hevc->luminance[i]);
+ }
+ hevc->sei_present_flag |= SEI_MASTER_DISPLAY_COLOR_MASK;
+ }
+ payload_size -= 24;
+ while (payload_size > 0) {
+ data = (READ_HREG(HEVC_SHIFTED_DATA) >> 24);
+ payload_size--;
+ WRITE_HREG(HEVC_SHIFT_COMMAND, (1<<7)|8);
+ pr_info("\t\tskip byte %02x\n", data);
+ }
+ }
+}
+
+static void hevc_recover(struct hevc_state_s *hevc)
+{
+ u32 rem;
+ u64 shift_byte_count64;
+ unsigned hevc_shift_byte_count;
+ unsigned hevc_stream_start_addr;
+ unsigned hevc_stream_end_addr;
+ unsigned hevc_stream_rd_ptr;
+ unsigned hevc_stream_wr_ptr;
+ unsigned hevc_stream_control;
+ unsigned hevc_stream_fifo_ctl;
+ unsigned hevc_stream_buf_size;
+#if 0
+ for (i = 0; i < (hevc->debug_ptr_size / 2); i += 4) {
+ int ii;
+
+ for (ii = 0; ii < 4; ii++)
+ pr_info("%04x ", hevc->debug_ptr[i + 3 - ii]);
+ if (((i + ii) & 0xf) == 0)
+ pr_info("\n");
+ }
+#endif
+#define ES_VID_MAN_RD_PTR (1<<0)
+
+ amhevc_stop();
+
+ /* reset */
+ WRITE_MPEG_REG(PARSER_VIDEO_RP, READ_VREG(HEVC_STREAM_RD_PTR));
+ SET_MPEG_REG_MASK(PARSER_ES_CONTROL, ES_VID_MAN_RD_PTR);
+
+ hevc_stream_start_addr = READ_VREG(HEVC_STREAM_START_ADDR);
+ hevc_stream_end_addr = READ_VREG(HEVC_STREAM_END_ADDR);
+ hevc_stream_rd_ptr = READ_VREG(HEVC_STREAM_RD_PTR);
+ hevc_stream_wr_ptr = READ_VREG(HEVC_STREAM_WR_PTR);
+ hevc_stream_control = READ_VREG(HEVC_STREAM_CONTROL);
+ hevc_stream_fifo_ctl = READ_VREG(HEVC_STREAM_FIFO_CTL);
+ hevc_stream_buf_size = hevc_stream_end_addr - hevc_stream_start_addr;
+
+ /*
+ *HEVC streaming buffer will reset and restart
+ *from current hevc_stream_rd_ptr position
+ */
+ /* calculate HEVC_SHIFT_BYTE_COUNT value with the new position. */
+ hevc_shift_byte_count = READ_VREG(HEVC_SHIFT_BYTE_COUNT);
+ if ((hevc->shift_byte_count_lo & (1 << 31))
+ && ((hevc_shift_byte_count & (1 << 31)) == 0))
+ hevc->shift_byte_count_hi++;
+
+ hevc->shift_byte_count_lo = hevc_shift_byte_count;
+ shift_byte_count64 = ((u64)(hevc->shift_byte_count_hi) << 32) |
+ hevc->shift_byte_count_lo;
+ div_u64_rem(shift_byte_count64, hevc_stream_buf_size, &rem);
+ shift_byte_count64 -= rem;
+ shift_byte_count64 += hevc_stream_rd_ptr - hevc_stream_start_addr;
+
+ if (rem > (hevc_stream_rd_ptr - hevc_stream_start_addr))
+ shift_byte_count64 += hevc_stream_buf_size;
+
+ hevc->shift_byte_count_lo = (u32)shift_byte_count64;
+ hevc->shift_byte_count_hi = (u32)(shift_byte_count64 >> 32);
+
+ WRITE_VREG(DOS_SW_RESET3,
+ /* (1<<2)| */
+ (1 << 3) | (1 << 4) | (1 << 8) |
+ (1 << 11) | (1 << 12) | (1 << 14)
+ | (1 << 15) | (1 << 17) | (1 << 18) | (1 << 19));
+ WRITE_VREG(DOS_SW_RESET3, 0);
+
+ WRITE_VREG(HEVC_STREAM_START_ADDR, hevc_stream_start_addr);
+ WRITE_VREG(HEVC_STREAM_END_ADDR, hevc_stream_end_addr);
+ WRITE_VREG(HEVC_STREAM_RD_PTR, hevc_stream_rd_ptr);
+ WRITE_VREG(HEVC_STREAM_WR_PTR, hevc_stream_wr_ptr);
+ WRITE_VREG(HEVC_STREAM_CONTROL, hevc_stream_control);
+ WRITE_VREG(HEVC_SHIFT_BYTE_COUNT, hevc->shift_byte_count_lo);
+ WRITE_VREG(HEVC_STREAM_FIFO_CTL, hevc_stream_fifo_ctl);
+
+ hevc_config_work_space_hw(hevc);
+ decoder_hw_reset();
+
+ hevc->have_vps = 0;
+ hevc->have_sps = 0;
+ hevc->have_pps = 0;
+
+ hevc->have_valid_start_slice = 0;
+
+ if (double_write_mode & 0x10)
+ WRITE_VREG(HEVCD_MPP_DECOMP_CTL1,
+ 0x1 << 31 /*/Enable NV21 reference read mode for MC*/
+ );
+
+ WRITE_VREG(HEVC_WAIT_FLAG, 1);
+ /* clear mailbox interrupt */
+ WRITE_VREG(HEVC_ASSIST_MBOX1_CLR_REG, 1);
+ /* enable mailbox interrupt */
+ WRITE_VREG(HEVC_ASSIST_MBOX1_MASK, 1);
+ /* disable PSCALE for hardware sharing */
+ WRITE_VREG(HEVC_PSCALE_CTRL, 0);
+
+ CLEAR_MPEG_REG_MASK(PARSER_ES_CONTROL, ES_VID_MAN_RD_PTR);
+
+ if (debug & H265_DEBUG_UCODE)
+ WRITE_VREG(DEBUG_REG1, 0x1);
+ else
+ WRITE_VREG(DEBUG_REG1, 0x0);
+
+ if ((error_handle_policy & 1) == 0) {
+ if ((error_handle_policy & 4) == 0) {
+ /* ucode auto mode, and do not check vps/sps/pps/idr */
+ WRITE_VREG(NAL_SEARCH_CTL,
+ 0xc);
+ } else {
+ WRITE_VREG(NAL_SEARCH_CTL, 0x1);/* manual parser NAL */
+ }
+ } else {
+ WRITE_VREG(NAL_SEARCH_CTL, 0x1);/* manual parser NAL */
+ }
+
+ if (debug & H265_DEBUG_NO_EOS_SEARCH_DONE)
+ WRITE_VREG(NAL_SEARCH_CTL, READ_VREG(NAL_SEARCH_CTL) | 0x10000);
+ if (parser_sei_enable & 0x1)
+ WRITE_VREG(NAL_SEARCH_CTL,
+ READ_VREG(NAL_SEARCH_CTL) | 0x20000);
+ WRITE_VREG(DECODE_STOP_POS, decode_stop_pos);
+
+ /* if (amhevc_loadmc(vh265_mc) < 0) { */
+ /* amhevc_disable(); */
+ /* return -EBUSY; */
+ /* } */
+#if 0
+ for (i = 0; i < (hevc->debug_ptr_size / 2); i += 4) {
+ int ii;
+
+ for (ii = 0; ii < 4; ii++) {
+ /* hevc->debug_ptr[i+3-ii]=ttt++; */
+ pr_info("%04x ", hevc->debug_ptr[i + 3 - ii]);
+ }
+ if (((i + ii) & 0xf) == 0)
+ pr_info("\n");
+ }
+#endif
+ init_pic_list_hw(hevc);
+
+ pr_info("%s HEVC_SHIFT_BYTE_COUNT=%x\n", __func__,
+ READ_VREG(HEVC_SHIFT_BYTE_COUNT));
+
+ amhevc_start();
+
+ /* skip, search next start code */
+ WRITE_VREG(HEVC_WAIT_FLAG, READ_VREG(HEVC_WAIT_FLAG) & (~0x2));
+ hevc->skip_flag = 1;
+#ifdef ERROR_HANDLE_DEBUG
+ if (dbg_nal_skip_count & 0x20000) {
+ dbg_nal_skip_count &= ~0x20000;
+ return;
+ }
+#endif
+ WRITE_VREG(HEVC_DEC_STATUS_REG, HEVC_ACTION_DONE);
+ /* Interrupt Amrisc to excute */
+ WRITE_VREG(HEVC_MCPU_INTR_REQ, AMRISC_MAIN_REQ);
+#ifdef MULTI_INSTANCE_SUPPORT
+ if (!hevc->m_ins_flag)
+#endif
+ hevc->first_pic_after_recover = 1;
+}
+
+
+static irqreturn_t vh265_isr_thread_fn(int irq, void *data)
+{
+ struct hevc_state_s *hevc = (struct hevc_state_s *) data;
+ unsigned int dec_status = hevc->dec_status;
+ int i, ret;
+
+
+
+ if (dec_status == HEVC_SEI_DAT) {
+ int payload_type = READ_HREG(CUR_NAL_UNIT_TYPE) & 0xffff;
+ int payload_size =
+ (READ_HREG(CUR_NAL_UNIT_TYPE) >> 16) & 0xffff;
+ process_nal_sei(hevc, payload_type, payload_size);
+
+ WRITE_VREG(HEVC_DEC_STATUS_REG, HEVC_SEI_DAT_DONE);
+ } else if (dec_status == HEVC_NAL_SEARCH_DONE) {
+ int naltype = READ_HREG(CUR_NAL_UNIT_TYPE);
+ int parse_type = HEVC_DISCARD_NAL;
+
+ hevc->error_watchdog_count = 0;
+ hevc->error_skip_nal_wt_cnt = 0;
+ if (slice_parse_begin > 0 && debug & H265_DEBUG_DISCARD_NAL) {
+ pr_info("nal type %d, discard %d\n", naltype,
+ slice_parse_begin);
+ if (naltype <= NAL_UNIT_CODED_SLICE_CRA)
+ slice_parse_begin--;
+ }
+ if (naltype == NAL_UNIT_EOS) {
+ struct PIC_s *pic;
+
+ pr_info("get NAL_UNIT_EOS, flush output");
+ pic = get_pic_by_POC(hevc, hevc->curr_POC);
+ hevc->curr_POC = INVALID_POC;
+ /* add to fix RAP_B_Bossen_1 */
+ hevc->m_pocRandomAccess = MAX_INT;
+ flush_output(hevc, pic);
+ WRITE_VREG(HEVC_DEC_STATUS_REG, HEVC_DISCARD_NAL);
+ /* Interrupt Amrisc to excute */
+ WRITE_VREG(HEVC_MCPU_INTR_REQ, AMRISC_MAIN_REQ);
+ return IRQ_HANDLED;
+ }
+
+ if (hevc->error_skip_nal_count > 0) {
+ pr_info("nal type %d, discard %d\n", naltype,
+ hevc->error_skip_nal_count);
+ hevc->error_skip_nal_count--;
+ if (hevc->error_skip_nal_count == 0) {
+ hevc_recover(hevc);
+ hevc->error_flag = 0;
+ if ((error_handle_policy & 0x2) == 0) {
+ hevc->have_vps = 1;
+ hevc->have_sps = 1;
+ hevc->have_pps = 1;
+ }
+ return IRQ_HANDLED;
+ }
+ } else if (naltype == NAL_UNIT_VPS) {
+ parse_type = HEVC_NAL_UNIT_VPS;
+ hevc->have_vps = 1;
+#ifdef ERROR_HANDLE_DEBUG
+ if (dbg_nal_skip_flag & 1)
+ parse_type = HEVC_DISCARD_NAL;
+#endif
+ } else if (hevc->have_vps) {
+ if (naltype == NAL_UNIT_SPS) {
+ parse_type = HEVC_NAL_UNIT_SPS;
+ hevc->have_sps = 1;
+#ifdef ERROR_HANDLE_DEBUG
+ if (dbg_nal_skip_flag & 2)
+ parse_type = HEVC_DISCARD_NAL;
+#endif
+ } else if (naltype == NAL_UNIT_PPS) {
+ parse_type = HEVC_NAL_UNIT_PPS;
+ hevc->have_pps = 1;
+#ifdef ERROR_HANDLE_DEBUG
+ if (dbg_nal_skip_flag & 4)
+ parse_type = HEVC_DISCARD_NAL;
+#endif
+ } else if (hevc->have_sps && hevc->have_pps) {
+ int seg = HEVC_NAL_UNIT_CODED_SLICE_SEGMENT;
+
+ if ((naltype == NAL_UNIT_CODED_SLICE_IDR) ||
+ (naltype ==
+ NAL_UNIT_CODED_SLICE_IDR_N_LP)
+ || (naltype ==
+ NAL_UNIT_CODED_SLICE_CRA)
+ || (naltype ==
+ NAL_UNIT_CODED_SLICE_BLA)
+ || (naltype ==
+ NAL_UNIT_CODED_SLICE_BLANT)
+ || (naltype ==
+ NAL_UNIT_CODED_SLICE_BLA_N_LP)
+ ) {
+ if (slice_parse_begin > 0) {
+ pr_info
+ ("discard %d, for debugging\n",
+ slice_parse_begin);
+ slice_parse_begin--;
+ } else {
+ parse_type = seg;
+ }
+ hevc->have_valid_start_slice = 1;
+ } else if (naltype <=
+ NAL_UNIT_CODED_SLICE_CRA
+ && (hevc->have_valid_start_slice
+ || (hevc->PB_skip_mode != 3))) {
+ if (slice_parse_begin > 0) {
+ pr_info
+ ("discard %d, dd\n",
+ slice_parse_begin);
+ slice_parse_begin--;
+ } else
+ parse_type = seg;
+
+ }
+ }
+ }
+ if (hevc->have_vps && hevc->have_sps && hevc->have_pps
+ && hevc->have_valid_start_slice &&
+ hevc->error_flag == 0) {
+ if ((debug & H265_DEBUG_MAN_SEARCH_NAL) == 0 &&
+ (!hevc->m_ins_flag)) {
+ /*
+ *auot parser NAL; do not check
+ *vps/sps/pps/idr
+ */
+ WRITE_VREG(NAL_SEARCH_CTL, 0x2);
+ }
+
+ if (debug & H265_DEBUG_NO_EOS_SEARCH_DONE) {
+ WRITE_VREG(NAL_SEARCH_CTL,
+ READ_VREG(NAL_SEARCH_CTL) |
+ 0x10000);
+ }
+ if (parser_sei_enable & 0x1)
+ WRITE_VREG(NAL_SEARCH_CTL,
+ READ_VREG(NAL_SEARCH_CTL) | 0x20000);
+ }
+
+ if (debug & H265_DEBUG_BUFMGR) {
+ pr_info("naltype = %d parse_type %d\n %d %d %d %d\n",
+ naltype, parse_type, hevc->have_vps,
+ hevc->have_sps, hevc->have_pps,
+ hevc->have_valid_start_slice);
+ }
+
+ WRITE_VREG(HEVC_DEC_STATUS_REG, parse_type);
+ /* Interrupt Amrisc to excute */
+ WRITE_VREG(HEVC_MCPU_INTR_REQ, AMRISC_MAIN_REQ);
+
+ } else if (dec_status == HEVC_SLICE_SEGMENT_DONE) {
+ if (hevc->start_decoding_time > 0) {
+ u32 process_time = 1000*
+ (jiffies - hevc->start_decoding_time)/HZ;
+ if (process_time > max_decoding_time)
+ max_decoding_time = process_time;
+ }
+
+ hevc->error_watchdog_count = 0;
+ if (hevc->pic_list_init_flag == 2) {
+ hevc->pic_list_init_flag = 3;
+ pr_info("set pic_list_init_flag to 3\n");
+ } else if (hevc->wait_buf == 0) {
+ u32 vui_time_scale;
+ u32 vui_num_units_in_tick;
+
+ if (debug & H265_DEBUG_SEND_PARAM_WITH_REG)
+ get_rpm_param(&hevc->param);
+ else {
+ dma_sync_single_for_cpu(
+ amports_get_dma_device(),
+ hevc->rpm_phy_addr,
+ RPM_BUF_SIZE,
+ DMA_FROM_DEVICE);
+
+ for (i = 0; i < (RPM_END - RPM_BEGIN); i += 4) {
+ int ii;
+
+ for (ii = 0; ii < 4; ii++) {
+ hevc->param.l.data[i + ii] =
+ hevc->rpm_ptr[i + 3
+ - ii];
+ }
+ }
+ }
+ if (debug & H265_DEBUG_BUFMGR_MORE) {
+ pr_info("rpm_param: (%d)\n", hevc->slice_idx);
+ hevc->slice_idx++;
+ for (i = 0; i < (RPM_END - RPM_BEGIN); i++) {
+ pr_info("%04x ", hevc->param.l.data[i]);
+ if (((i + 1) & 0xf) == 0)
+ pr_info("\n");
+ }
+
+ pr_info("vui_timing_info: %x, %x, %x, %x\n",
+ hevc->param.p.vui_num_units_in_tick_hi,
+ hevc->param.p.vui_num_units_in_tick_lo,
+ hevc->param.p.vui_time_scale_hi,
+ hevc->param.p.vui_time_scale_lo);
+ }
+
+ vui_time_scale =
+ (u32)(hevc->param.p.vui_time_scale_hi << 16) |
+ hevc->param.p.vui_time_scale_lo;
+ vui_num_units_in_tick =
+ (u32)(hevc->param.
+ p.vui_num_units_in_tick_hi << 16) |
+ hevc->param.
+ p.vui_num_units_in_tick_lo;
+ if (hevc->bit_depth_luma !=
+ ((hevc->param.p.bit_depth & 0xf) + 8)) {
+ pr_info("Bit depth luma = %d\n",
+ (hevc->param.p.bit_depth & 0xf) + 8);
+ }
+ if (hevc->bit_depth_chroma !=
+ (((hevc->param.p.bit_depth >> 4) & 0xf) + 8)) {
+ pr_info("Bit depth chroma = %d\n",
+ ((hevc->param.p.bit_depth >> 4) &
+ 0xf) + 8);
+ }
+ hevc->bit_depth_luma =
+ (hevc->param.p.bit_depth & 0xf) + 8;
+ hevc->bit_depth_chroma =
+ ((hevc->param.p.bit_depth >> 4) & 0xf) + 8;
+ bit_depth_luma = hevc->bit_depth_luma;
+ bit_depth_chroma = hevc->bit_depth_chroma;
+#ifdef SUPPORT_10BIT
+ if (hevc->bit_depth_luma == 8 &&
+ hevc->bit_depth_chroma == 8 &&
+ enable_mem_saving)
+ hevc->mem_saving_mode = 1;
+ else
+ hevc->mem_saving_mode = 0;
+#endif
+ if ((vui_time_scale != 0)
+ && (vui_num_units_in_tick != 0)) {
+ hevc->frame_dur =
+ div_u64(96000ULL *
+ vui_num_units_in_tick,
+ vui_time_scale);
+ hevc->get_frame_dur = true;
+ }
+
+ if (hevc->video_signal_type !=
+ ((hevc->param.p.video_signal_type << 16)
+ | hevc->param.p.color_description)) {
+ u32 v = hevc->param.p.video_signal_type;
+ u32 c = hevc->param.p.color_description;
+#if 0
+ if (v & 0x2000) {
+ pr_info("video_signal_type present:\n");
+ pr_info(" %s %s\n",
+ video_format_names[(v >> 10) & 7],
+ ((v >> 9) & 1) ?
+ "full_range" : "limited");
+ if (v & 0x100) {
+ pr_info(" color_description present:\n");
+ pr_info(" color_primarie = %s\n",
+ color_primaries_names
+ [v & 0xff]);
+ pr_info(" transfer_characteristic = %s\n",
+ transfer_characteristics_names
+ [(c >> 8) & 0xff]);
+ pr_info(" matrix_coefficient = %s\n",
+ matrix_coeffs_names[c & 0xff]);
+ }
+ }
+#endif
+ hevc->video_signal_type = (v << 16) | c;
+ video_signal_type = hevc->video_signal_type;
+ }
+
+ if (use_cma &&
+ (hevc->param.p.slice_segment_address == 0)
+ && (hevc->pic_list_init_flag == 0)) {
+ int log = hevc->param.p.log2_min_coding_block_size_minus3;
+ int log_s = hevc->param.p.log2_diff_max_min_coding_block_size;
+
+ hevc->pic_w = hevc->param.p.pic_width_in_luma_samples;
+ hevc->pic_h = hevc->param.p.pic_height_in_luma_samples;
+ hevc->lcu_size = 1 << (log + 3 + log_s);
+ hevc->lcu_size_log2 = log2i(hevc->lcu_size);
+ if (hevc->pic_w == 0 || hevc->pic_h == 0
+ || hevc->lcu_size == 0) {
+ /* skip search next start code */
+ WRITE_VREG(HEVC_WAIT_FLAG, READ_VREG(HEVC_WAIT_FLAG)
+ & (~0x2));
+ hevc->skip_flag = 1;
+ WRITE_VREG(HEVC_DEC_STATUS_REG, HEVC_ACTION_DONE);
+ /* Interrupt Amrisc to excute */
+ WRITE_VREG(HEVC_MCPU_INTR_REQ, AMRISC_MAIN_REQ);
+
+ } else {
+ hevc->sps_num_reorder_pics_0 =
+ hevc->param.p.sps_num_reorder_pics_0;
+ hevc->pic_list_init_flag = 1;
+#ifdef MULTI_INSTANCE_SUPPORT
+ if (hevc->m_ins_flag)
+ schedule_work(&hevc->work);
+ else
+#endif
+ up(&hevc->h265_sema);
+ pr_info("set pic_list_init_flag 1\n");
+ }
+ return IRQ_HANDLED;
+ }
+
+}
+ ret =
+ hevc_slice_segment_header_process(hevc,
+ &hevc->param, decode_pic_begin);
+ if (ret < 0)
+ ;
+ else if (ret == 0) {
+ if ((hevc->new_pic) && (hevc->cur_pic)) {
+ hevc->cur_pic->stream_offset =
+ READ_VREG(HEVC_SHIFT_BYTE_COUNT);
+ }
+
+ WRITE_VREG(HEVC_DEC_STATUS_REG,
+ HEVC_CODED_SLICE_SEGMENT_DAT);
+ /* Interrupt Amrisc to excute */
+ WRITE_VREG(HEVC_MCPU_INTR_REQ, AMRISC_MAIN_REQ);
+
+ hevc->start_decoding_time = jiffies;
+ } else {
+ /* skip, search next start code */
+ WRITE_VREG(HEVC_WAIT_FLAG, READ_VREG(HEVC_WAIT_FLAG) & (~0x2));
+ hevc->skip_flag = 1;
+ WRITE_VREG(HEVC_DEC_STATUS_REG, HEVC_ACTION_DONE);
+ /* Interrupt Amrisc to excute */
+ WRITE_VREG(HEVC_MCPU_INTR_REQ, AMRISC_MAIN_REQ);
+ }
+
+ }
+
+ if (mmu_enable) {
+ if (hevc->last_put_idx_a >= 0
+ && hevc->last_put_idx_a < MAX_REF_PIC_NUM) {
+ int i = hevc->last_put_idx_a;
+ struct PIC_s *pic = &hevc->m_PIC[i];
+
+ /*free not used buffers.*/
+ if (pic->output_mark == 0 && pic->referenced == 0
+ && pic->output_ready == 0
+ && pic->used_by_display == 0
+ && (pic[i].index != -1)) {
+ decoder_mmu_box_free_idx(hevc->mmu_box, i);
+ hevc->last_put_idx_a = -1;
+ /* pr_info("release pic buf %x\n",i);*/
+ }
+ }
+ if (hevc->last_put_idx_b >= 0
+ && hevc->last_put_idx_b < MAX_REF_PIC_NUM) {
+ int i = hevc->last_put_idx_b;
+ struct PIC_s *pic = &hevc->m_PIC[i];
+
+ /*free not used buffers.*/
+ if (pic->output_mark == 0 && pic->referenced == 0
+ && pic->output_ready == 0
+ && pic->used_by_display == 0
+ && (pic[i].index != -1)) {
+ decoder_mmu_box_free_idx(hevc->mmu_box, i);
+ hevc->last_put_idx_b = -1;
+ }
+ }
+ }
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t vh265_isr(int irq, void *data)
+{
+ int i, temp;
+ unsigned int dec_status;
+ struct hevc_state_s *hevc = (struct hevc_state_s *)data;
+
+ dec_status = READ_VREG(HEVC_DEC_STATUS_REG);
+ if (hevc->init_flag == 0)
+ return IRQ_HANDLED;
+ hevc->dec_status = dec_status;
+ if (debug & H265_DEBUG_BUFMGR)
+ pr_info("265 isr dec status = %d\n", dec_status);
+
+ if (debug & H265_DEBUG_UCODE) {
+ if (READ_HREG(DEBUG_REG1) & 0x10000) {
+ dma_sync_single_for_cpu(
+ amports_get_dma_device(),
+ hevc->lmem_phy_addr,
+ LMEM_BUF_SIZE,
+ DMA_FROM_DEVICE);
+
+ pr_info("LMEM<tag %x>:\n", READ_HREG(DEBUG_REG1));
+
+ if (mmu_enable)
+ temp = 0x500;
+ else
+ temp = 0x400;
+ for (i = 0; i < temp; i += 4) {
+ int ii;
+
+ if ((i & 0xf) == 0)
+ pr_info("%03x: ", i);
+ for (ii = 0; ii < 4; ii++) {
+ pr_info("%04x ",
+ hevc->lmem_ptr[i + 3 - ii]);
+ }
+ if (((i + ii) & 0xf) == 0)
+ pr_info("\n");
+ }
+ WRITE_HREG(DEBUG_REG1, 0);
+ } else if (READ_HREG(DEBUG_REG1) != 0) {
+ pr_info("dbg%x: %x\n", READ_HREG(DEBUG_REG1),
+ READ_HREG(DEBUG_REG2));
+ WRITE_HREG(DEBUG_REG1, 0);
+ return IRQ_HANDLED;
+ }
+
+ }
+
+ if (hevc->pic_list_init_flag == 1)
+ return IRQ_HANDLED;
+
+ if (hevc->error_flag == 1) {
+ if ((error_handle_policy & 0x10) == 0) {
+ if (hevc->cur_pic) {
+ int current_lcu_idx =
+ READ_VREG(HEVC_PARSER_LCU_START)
+ & 0xffffff;
+ if (current_lcu_idx <
+ ((hevc->lcu_x_num*hevc->lcu_y_num)-1))
+ hevc->cur_pic->error_mark = 1;
+
+ }
+ }
+ if ((error_handle_policy & 1) == 0) {
+ hevc->error_skip_nal_count = 1;
+ /*
+ *manual search nal, skip error_skip_nal_count
+ *of nal and trigger the HEVC_NAL_SEARCH_DONE irq
+ */
+ WRITE_VREG(NAL_SEARCH_CTL,
+ (error_skip_nal_count << 4) | 0x1);
+ } else {
+ hevc->error_skip_nal_count = error_skip_nal_count;
+ WRITE_VREG(NAL_SEARCH_CTL, 0x1);/* manual parser NAL */
+ }
+ if (debug & H265_DEBUG_NO_EOS_SEARCH_DONE) {
+ WRITE_VREG(NAL_SEARCH_CTL,
+ READ_VREG(NAL_SEARCH_CTL) | 0x10000);
+ }
+ if (parser_sei_enable & 0x1)
+ WRITE_VREG(NAL_SEARCH_CTL,
+ READ_VREG(NAL_SEARCH_CTL) | 0x20000);
+ /* search new nal */
+ WRITE_VREG(HEVC_DEC_STATUS_REG, HEVC_ACTION_DONE);
+ /* Interrupt Amrisc to excute */
+ WRITE_VREG(HEVC_MCPU_INTR_REQ, AMRISC_MAIN_REQ);
+
+ /* pr_info("%s: error handle\n", __func__); */
+ hevc->error_flag = 2;
+ return IRQ_HANDLED;
+ } else if (hevc->error_flag == 3) {
+ pr_info("error_flag=3, hevc_recover");
+ hevc_recover(hevc);
+ hevc->error_flag = 0;
+
+ if ((error_handle_policy & 0x10) == 0) {
+ if (hevc->cur_pic) {
+ int current_lcu_idx =
+ READ_VREG(HEVC_PARSER_LCU_START)
+ & 0xffffff;
+ if (current_lcu_idx <
+ ((hevc->lcu_x_num*hevc->lcu_y_num)-1))
+ hevc->cur_pic->error_mark = 1;
+
+ }
+ }
+ if ((error_handle_policy & 1) == 0) {
+ /*
+ *need skip some data when
+ *error_flag of 3 is triggered,
+ */
+ /*
+ *to avoid hevc_recover() being called
+ *for many times at the same bitstream position
+ */
+ hevc->error_skip_nal_count = 1;
+ /*
+ *manual search nal, skip error_skip_nal_count
+ *of nal and trigger the HEVC_NAL_SEARCH_DONE irq
+ */
+ WRITE_VREG(NAL_SEARCH_CTL,
+ (error_skip_nal_count << 4) | 0x1);
+ }
+
+ if ((error_handle_policy & 0x2) == 0) {
+ hevc->have_vps = 1;
+ hevc->have_sps = 1;
+ hevc->have_pps = 1;
+ }
+ return IRQ_HANDLED;
+ }
+
+ i = READ_VREG(HEVC_SHIFT_BYTE_COUNT);
+ if ((hevc->shift_byte_count_lo & (1 << 31)) && ((i & (1 << 31)) == 0))
+ hevc->shift_byte_count_hi++;
+ hevc->shift_byte_count_lo = i;
+
+#ifdef MULTI_INSTANCE_SUPPORT
+ if (dec_status == HEVC_NAL_DECODE_DONE ||
+ dec_status == HEVC_DECPIC_DATA_DONE) {
+ if (hevc->m_ins_flag) {
+ hevc->dec_result = DEC_RESULT_DONE;
+ schedule_work(&hevc->work);
+ }
+
+ return IRQ_HANDLED;
+ }
+#endif
+#if 0
+ if (dec_status == HEVC_SEI_DAT) {
+ int payload_type = READ_HREG(CUR_NAL_UNIT_TYPE) & 0xffff;
+ int payload_size =
+ (READ_HREG(CUR_NAL_UNIT_TYPE) >> 16) & 0xffff;
+ process_nal_sei(hevc, payload_type, payload_size);
+
+ WRITE_VREG(HEVC_DEC_STATUS_REG, HEVC_SEI_DAT_DONE);
+ } else if (dec_status == HEVC_NAL_SEARCH_DONE) {
+ int naltype = READ_HREG(CUR_NAL_UNIT_TYPE);
+ int parse_type = HEVC_DISCARD_NAL;
+
+ hevc->error_watchdog_count = 0;
+ hevc->error_skip_nal_wt_cnt = 0;
+ if (slice_parse_begin > 0 && debug & H265_DEBUG_DISCARD_NAL) {
+ pr_info("nal type %d, discard %d\n", naltype,
+ slice_parse_begin);
+ if (naltype <= NAL_UNIT_CODED_SLICE_CRA)
+ slice_parse_begin--;
+ }
+ if (naltype == NAL_UNIT_EOS) {
+ struct PIC_s *pic;
+
+ pr_info("get NAL_UNIT_EOS, flush output");
+ pic = get_pic_by_POC(hevc, hevc->curr_POC);
+ hevc->curr_POC = INVALID_POC;
+ /* add to fix RAP_B_Bossen_1 */
+ hevc->m_pocRandomAccess = MAX_INT;
+ flush_output(hevc, pic);
+ WRITE_VREG(HEVC_DEC_STATUS_REG, HEVC_DISCARD_NAL);
+ /* Interrupt Amrisc to excute */
+ WRITE_VREG(HEVC_MCPU_INTR_REQ, AMRISC_MAIN_REQ);
+ return IRQ_HANDLED;
+ }
+
+ if (hevc->error_skip_nal_count > 0) {
+ pr_info("nal type %d, discard %d\n", naltype,
+ hevc->error_skip_nal_count);
+ hevc->error_skip_nal_count--;
+ if (hevc->error_skip_nal_count == 0) {
+ hevc_recover(hevc);
+ hevc->error_flag = 0;
+ if ((error_handle_policy & 0x2) == 0) {
+ hevc->have_vps = 1;
+ hevc->have_sps = 1;
+ hevc->have_pps = 1;
+ }
+ return IRQ_HANDLED;
+ }
+ } else if (naltype == NAL_UNIT_VPS) {
+ parse_type = HEVC_NAL_UNIT_VPS;
+ hevc->have_vps = 1;
+#ifdef ERROR_HANDLE_DEBUG
+ if (dbg_nal_skip_flag & 1)
+ parse_type = HEVC_DISCARD_NAL;
+#endif
+ } else if (hevc->have_vps) {
+ if (naltype == NAL_UNIT_SPS) {
+ parse_type = HEVC_NAL_UNIT_SPS;
+ hevc->have_sps = 1;
+#ifdef ERROR_HANDLE_DEBUG
+ if (dbg_nal_skip_flag & 2)
+ parse_type = HEVC_DISCARD_NAL;
+#endif
+ } else if (naltype == NAL_UNIT_PPS) {
+ parse_type = HEVC_NAL_UNIT_PPS;
+ hevc->have_pps = 1;
+#ifdef ERROR_HANDLE_DEBUG
+ if (dbg_nal_skip_flag & 4)
+ parse_type = HEVC_DISCARD_NAL;
+#endif
+ } else if (hevc->have_sps && hevc->have_pps) {
+ int seg = HEVC_NAL_UNIT_CODED_SLICE_SEGMENT;
+
+ if ((naltype == NAL_UNIT_CODED_SLICE_IDR) ||
+ (naltype ==
+ NAL_UNIT_CODED_SLICE_IDR_N_LP)
+ || (naltype ==
+ NAL_UNIT_CODED_SLICE_CRA)
+ || (naltype ==
+ NAL_UNIT_CODED_SLICE_BLA)
+ || (naltype ==
+ NAL_UNIT_CODED_SLICE_BLANT)
+ || (naltype ==
+ NAL_UNIT_CODED_SLICE_BLA_N_LP)
+ ) {
+ if (slice_parse_begin > 0) {
+ pr_info
+ ("discard %d, for debugging\n",
+ slice_parse_begin);
+ slice_parse_begin--;
+ } else {
+ parse_type = seg;
+ }
+ hevc->have_valid_start_slice = 1;
+ } else if (naltype <=
+ NAL_UNIT_CODED_SLICE_CRA
+ && (hevc->have_valid_start_slice
+ || (hevc->PB_skip_mode != 3))) {
+ if (slice_parse_begin > 0) {
+ pr_info
+ ("discard %d, dd\n",
+ slice_parse_begin);
+ slice_parse_begin--;
+ } else
+ parse_type = seg;
+
+ }
+ }
+ }
+ if (hevc->have_vps && hevc->have_sps && hevc->have_pps
+ && hevc->have_valid_start_slice &&
+ hevc->error_flag == 0) {
+ if ((debug & H265_DEBUG_MAN_SEARCH_NAL) == 0 &&
+ (!hevc->m_ins_flag)) {
+ /*
+ *auot parser NAL; do not check
+ *vps/sps/pps/idr
+ */
+ WRITE_VREG(NAL_SEARCH_CTL, 0x2);
+ }
+
+ if (debug & H265_DEBUG_NO_EOS_SEARCH_DONE) {
+ WRITE_VREG(NAL_SEARCH_CTL,
+ READ_VREG(NAL_SEARCH_CTL) |
+ 0x10000);
+ }
+ if (parser_sei_enable & 0x1)
+ WRITE_VREG(NAL_SEARCH_CTL,
+ READ_VREG(NAL_SEARCH_CTL) | 0x20000);
+ }
+
+ if (debug & H265_DEBUG_BUFMGR) {
+ pr_info("naltype = %d parse_type %d\n %d %d %d %d\n",
+ naltype, parse_type, hevc->have_vps,
+ hevc->have_sps, hevc->have_pps,
+ hevc->have_valid_start_slice);
+ }
+
+ WRITE_VREG(HEVC_DEC_STATUS_REG, parse_type);
+ /* Interrupt Amrisc to excute */
+ WRITE_VREG(HEVC_MCPU_INTR_REQ, AMRISC_MAIN_REQ);
+
+ } else if (dec_status == HEVC_SLICE_SEGMENT_DONE) {
+ if (hevc->start_decoding_time > 0) {
+ u32 process_time = 1000*
+ (jiffies - hevc->start_decoding_time)/HZ;
+ if (process_time > max_decoding_time)
+ max_decoding_time = process_time;
+ }
+
+ hevc->error_watchdog_count = 0;
+ if (hevc->pic_list_init_flag == 2) {
+ hevc->pic_list_init_flag = 3;
+ pr_info("set pic_list_init_flag to 3\n");
+ } else if (hevc->wait_buf == 0) {
+ u32 vui_time_scale;
+ u32 vui_num_units_in_tick;
+
+ if (debug & H265_DEBUG_SEND_PARAM_WITH_REG)
+ get_rpm_param(&hevc->param);
+ else {
+ dma_sync_single_for_cpu(
+ amports_get_dma_device(),
+ hevc->rpm_phy_addr,
+ RPM_BUF_SIZE,
+ DMA_FROM_DEVICE);
+
+ for (i = 0; i < (RPM_END - RPM_BEGIN); i += 4) {
+ int ii;
+
+ for (ii = 0; ii < 4; ii++) {
+ hevc->param.l.data[i + ii] =
+ hevc->rpm_ptr[i + 3
+ - ii];
+ }
+ }
+ }
+ if (debug & H265_DEBUG_BUFMGR_MORE) {
+ pr_info("rpm_param: (%d)\n", hevc->slice_idx);
+ hevc->slice_idx++;
+ for (i = 0; i < (RPM_END - RPM_BEGIN); i++) {
+ pr_info("%04x ", hevc->param.l.data[i]);
+ if (((i + 1) & 0xf) == 0)
+ pr_info("\n");
+ }
+
+ pr_info("vui_timing_info: %x, %x, %x, %x\n",
+ hevc->
+ param.p.vui_num_units_in_tick_hi,
+ hevc->
+ param.p.vui_num_units_in_tick_lo,
+ hevc->param.p.vui_time_scale_hi,
+ hevc->param.p.vui_time_scale_lo);
+ }
+
+ vui_time_scale =
+ (u32)(hevc->param.p.
+ vui_time_scale_hi << 16) |
+ hevc->param.p.vui_time_scale_lo;
+ vui_num_units_in_tick =
+ (u32)(hevc->param.p.
+ vui_num_units_in_tick_hi << 16) |
+ hevc->param.
+ p.vui_num_units_in_tick_lo;
+ if (hevc->bit_depth_luma !=
+ ((hevc->param.p.bit_depth & 0xf) + 8)) {
+ pr_info("Bit depth luma = %d\n",
+ (hevc->param.p.bit_depth & 0xf) + 8);
+ }
+ if (hevc->bit_depth_chroma !=
+ (((hevc->param.p.bit_depth >> 4) & 0xf) + 8)) {
+ pr_info("Bit depth chroma = %d\n",
+ ((hevc->param.p.bit_depth >> 4) &
+ 0xf) + 8);
+ }
+ hevc->bit_depth_luma =
+ (hevc->param.p.bit_depth & 0xf) + 8;
+ hevc->bit_depth_chroma =
+ ((hevc->param.p.bit_depth >> 4) & 0xf) + 8;
+ bit_depth_luma = hevc->bit_depth_luma;
+ bit_depth_chroma = hevc->bit_depth_chroma;
+#ifdef SUPPORT_10BIT
+ if (hevc->bit_depth_luma == 8 &&
+ hevc->bit_depth_chroma == 8 &&
+ enable_mem_saving)
+ hevc->mem_saving_mode = 1;
+ else
+ hevc->mem_saving_mode = 0;
+#endif
+ if ((vui_time_scale != 0)
+ && (vui_num_units_in_tick != 0)) {
+ hevc->frame_dur =
+ div_u64(96000ULL *
+ vui_num_units_in_tick,
+ vui_time_scale);
+ hevc->get_frame_dur = true;
+ }
+
+ if (hevc->video_signal_type !=
+ ((hevc->param.p.video_signal_type << 16)
+ | hevc->param.p.color_description)) {
+ u32 v = hevc->param.p.video_signal_type;
+ u32 c = hevc->param.p.color_description;
+#if 0
+ if (v & 0x2000) {
+ pr_info("video_signal_type present:\n");
+ pr_info(" %s %s\n",
+ video_format_names[(v >> 10) & 7],
+ ((v >> 9) & 1) ?
+ "full_range" : "limited");
+ if (v & 0x100) {
+ pr_info(" color_description present:\n");
+ pr_info(" color_primarie = %s\n",
+ color_primaries_names
+ [v & 0xff]);
+ pr_info(" transfer_characteristic = %s\n",
+ transfer_characteristics_names
+ [(c >> 8) & 0xff]);
+ pr_info(" matrix_coefficient = %s\n",
+ matrix_coeffs_names[c & 0xff]);
+ }
+ }
+#endif
+ hevc->video_signal_type = (v << 16) | c;
+ video_signal_type = hevc->video_signal_type;
+ }
+
+ if (use_cma &&
+ (hevc->param.p.slice_segment_address == 0)
+ && (hevc->pic_list_init_flag == 0)) {
+ int log = hevc->param.p.
+ log2_min_coding_block_size_minus3;
+ int log_s = hevc->param.p.
+ log2_diff_max_min_coding_block_size;
+ hevc->pic_w =
+ hevc->param.p.pic_width_in_luma_samples;
+ hevc->pic_h =
+ hevc->param.p.pic_height_in_luma_samples;
+ hevc->lcu_size =
+ 1 << (log + 3 + log_s);
+ hevc->lcu_size_log2 = log2i(hevc->lcu_size);
+ if (hevc->pic_w == 0 || hevc->pic_h == 0
+ || hevc->lcu_size == 0) {
+ /* skip, search next start code */
+ WRITE_VREG(HEVC_WAIT_FLAG,
+ READ_VREG(HEVC_WAIT_FLAG) &
+ (~0x2));
+ hevc->skip_flag = 1;
+ WRITE_VREG(HEVC_DEC_STATUS_REG,
+ HEVC_ACTION_DONE);
+ /* Interrupt Amrisc to excute */
+ WRITE_VREG(HEVC_MCPU_INTR_REQ,
+ AMRISC_MAIN_REQ);
+
+ } else {
+ hevc->sps_num_reorder_pics_0 =
+ hevc->param.p.sps_num_reorder_pics_0;
+ hevc->pic_list_init_flag = 1;
+#ifdef MULTI_INSTANCE_SUPPORT
+ if (hevc->m_ins_flag)
+ schedule_work(&hevc->work);
+ else
+#endif
+ up(&hevc->h265_sema);
+ pr_info("set pic_list_init_flag 1\n");
+ }
+ return IRQ_HANDLED;
+ }
+
+ }
+ ret =
+ hevc_slice_segment_header_process(hevc, &hevc->param,
+ decode_pic_begin);
+ if (ret < 0)
+ ;
+ else if (ret == 0) {
+ if ((hevc->new_pic) && (hevc->cur_pic)) {
+ hevc->cur_pic->stream_offset =
+ READ_VREG(HEVC_SHIFT_BYTE_COUNT);
+ }
+
+ WRITE_VREG(HEVC_DEC_STATUS_REG,
+ HEVC_CODED_SLICE_SEGMENT_DAT);
+ /* Interrupt Amrisc to excute */
+ WRITE_VREG(HEVC_MCPU_INTR_REQ, AMRISC_MAIN_REQ);
+
+ hevc->start_decoding_time = jiffies;
+ } else {
+ /* skip, search next start code */
+ WRITE_VREG(HEVC_WAIT_FLAG,
+ READ_VREG(HEVC_WAIT_FLAG) & (~0x2));
+ hevc->skip_flag = 1;
+ WRITE_VREG(HEVC_DEC_STATUS_REG, HEVC_ACTION_DONE);
+ /* Interrupt Amrisc to excute */
+ WRITE_VREG(HEVC_MCPU_INTR_REQ, AMRISC_MAIN_REQ);
+ }
+
+ }
+ return IRQ_HANDLED;
+
+#else
+ return IRQ_WAKE_THREAD;
+#endif
+
+}
+
+static void vh265_put_timer_func(unsigned long arg)
+{
+ struct hevc_state_s *hevc = (struct hevc_state_s *)arg;
+ struct timer_list *timer = &hevc->timer;
+ unsigned char empty_flag;
+ unsigned int buf_level;
+
+ enum receviver_start_e state = RECEIVER_INACTIVE;
+
+ if (hevc->init_flag == 0) {
+ if (hevc->stat & STAT_TIMER_ARM) {
+ timer->expires = jiffies + PUT_INTERVAL;
+ add_timer(&hevc->timer);
+ }
+ return;
+ }
+
+ if (hevc->m_ins_flag == 0 &&
+ vf_get_receiver(hevc->provider_name)) {
+ state =
+ vf_notify_receiver(hevc->provider_name,
+ VFRAME_EVENT_PROVIDER_QUREY_STATE, NULL);
+ if ((state == RECEIVER_STATE_NULL)
+ || (state == RECEIVER_STATE_NONE))
+ state = RECEIVER_INACTIVE;
+ } else
+ state = RECEIVER_INACTIVE;
+
+ empty_flag = (READ_VREG(HEVC_PARSER_INT_STATUS) >> 6) & 0x1;
+ /* error watchdog */
+ if (hevc->m_ins_flag == 0 &&
+ (empty_flag == 0)
+ && (hevc->pic_list_init_flag == 0
+ || hevc->pic_list_init_flag == 3)) {
+ /* decoder has input */
+ if ((debug & H265_DEBUG_DIS_LOC_ERROR_PROC) == 0) {
+
+ buf_level = READ_VREG(HEVC_STREAM_LEVEL);
+ /* receiver has no buffer to recycle */
+ if ((state == RECEIVER_INACTIVE) &&
+ (kfifo_is_empty(&hevc->display_q) &&
+ buf_level > 0x200)
+ ) {
+ if (hevc->error_flag == 0) {
+ hevc->error_watchdog_count++;
+ if (hevc->error_watchdog_count ==
+ error_handle_threshold) {
+ pr_info
+ ("H265 dec err local reset.\n");
+ hevc->error_flag = 1;
+ hevc->error_watchdog_count = 0;
+ hevc->error_skip_nal_wt_cnt = 0;
+ hevc->
+ error_system_watchdog_count++;
+ WRITE_VREG
+ (HEVC_ASSIST_MBOX1_IRQ_REG,
+ 0x1);
+ }
+ } else if (hevc->error_flag == 2) {
+ int th =
+ error_handle_nal_skip_threshold;
+ hevc->error_skip_nal_wt_cnt++;
+ if (hevc->error_skip_nal_wt_cnt
+ == th) {
+ hevc->error_flag = 3;
+ hevc->error_watchdog_count = 0;
+ hevc->
+ error_skip_nal_wt_cnt = 0;
+ WRITE_VREG
+ (HEVC_ASSIST_MBOX1_IRQ_REG,
+ 0x1);
+ }
+ }
+ }
+ }
+
+ if ((debug & H265_DEBUG_DIS_SYS_ERROR_PROC) == 0)
+ /* receiver has no buffer to recycle */
+ if ((state == RECEIVER_INACTIVE) &&
+ (kfifo_is_empty(&hevc->display_q))
+ ) { /* no buffer to recycle */
+ if ((debug & H265_DEBUG_DIS_LOC_ERROR_PROC) !=
+ 0)
+ hevc->error_system_watchdog_count++;
+ if (hevc->error_system_watchdog_count ==
+ error_handle_system_threshold) {
+ /* and it lasts for a while */
+ pr_info
+ ("H265 dec fatal error watchdog.\n");
+ hevc->error_system_watchdog_count = 0;
+ hevc->fatal_error =
+ DECODER_FATAL_ERROR_UNKNOWN;
+ }
+ }
+ } else {
+ hevc->error_watchdog_count = 0;
+ hevc->error_system_watchdog_count = 0;
+ }
+
+ if (decode_stop_pos != decode_stop_pos_pre) {
+ WRITE_VREG(DECODE_STOP_POS, decode_stop_pos);
+ decode_stop_pos_pre = decode_stop_pos;
+ }
+
+ if (debug & H265_DEBUG_DUMP_PIC_LIST) {
+ dump_pic_list(hevc);
+ debug &= ~H265_DEBUG_DUMP_PIC_LIST;
+ }
+ if (debug & H265_DEBUG_TRIG_SLICE_SEGMENT_PROC) {
+ WRITE_VREG(HEVC_ASSIST_MBOX1_IRQ_REG, 0x1);
+ debug &= ~H265_DEBUG_TRIG_SLICE_SEGMENT_PROC;
+ }
+ if (debug & H265_DEBUG_HW_RESET) {
+ hevc->error_skip_nal_count = error_skip_nal_count;
+ WRITE_VREG(HEVC_DEC_STATUS_REG, HEVC_ACTION_DONE);
+
+ debug &= ~H265_DEBUG_HW_RESET;
+ }
+ if (debug & H265_DEBUG_ERROR_TRIG) {
+ WRITE_VREG(DECODE_STOP_POS, 1);
+ debug &= ~H265_DEBUG_ERROR_TRIG;
+ }
+#ifdef ERROR_HANDLE_DEBUG
+ if ((dbg_nal_skip_count > 0) && ((dbg_nal_skip_count & 0x10000) != 0)) {
+ hevc->error_skip_nal_count = dbg_nal_skip_count & 0xffff;
+ dbg_nal_skip_count &= ~0x10000;
+ WRITE_VREG(HEVC_DEC_STATUS_REG, HEVC_ACTION_DONE);
+ }
+#endif
+
+ if (radr != 0) {
+ if (rval != 0) {
+ WRITE_VREG(radr, rval);
+ pr_info("WRITE_VREG(%x,%x)\n", radr, rval);
+ } else
+ pr_info("READ_VREG(%x)=%x\n", radr, READ_VREG(radr));
+ rval = 0;
+ radr = 0;
+ }
+ if (dbg_cmd != 0) {
+ if (dbg_cmd == 1) {
+ u32 disp_laddr;
+
+ if (get_cpu_type() >= MESON_CPU_MAJOR_ID_GXBB &&
+ double_write_mode == 0) {
+ disp_laddr =
+ READ_VCBUS_REG(AFBC_BODY_BADDR) << 4;
+ } else {
+ struct canvas_s cur_canvas;
+
+ canvas_read((READ_VCBUS_REG(VD1_IF0_CANVAS0)
+ & 0xff), &cur_canvas);
+ disp_laddr = cur_canvas.addr;
+ }
+ pr_info("current displayed buffer address %x\r\n",
+ disp_laddr);
+ }
+ dbg_cmd = 0;
+ }
+ /*don't changed at start.*/
+ if (hevc->m_ins_flag == 0 &&
+ hevc->get_frame_dur && hevc->show_frame_num > 60 &&
+ hevc->frame_dur > 0 && hevc->saved_resolution !=
+ hevc->frame_width * hevc->frame_height *
+ (96000 / hevc->frame_dur)) {
+ int fps = 96000 / hevc->frame_dur;
+
+ if (hevc_source_changed(VFORMAT_HEVC,
+ hevc->frame_width, hevc->frame_height, fps) > 0)
+ hevc->saved_resolution = hevc->frame_width *
+ hevc->frame_height * fps;
+ }
+
+
+ timer->expires = jiffies + PUT_INTERVAL;
+ add_timer(timer);
+}
+
+static int h265_task_handle(void *data)
+{
+ int ret = 0;
+ struct hevc_state_s *hevc = (struct hevc_state_s *)data;
+
+ set_user_nice(current, -10);
+ while (1) {
+ if (use_cma == 0) {
+ pr_info
+ ("ERROR: use_cma can not be changed dynamically\n");
+ }
+ ret = down_interruptible(&hevc->h265_sema);
+ if ((hevc->init_flag != 0) && (hevc->pic_list_init_flag == 1)) {
+ /*USE_BUF_BLOCK*/
+ init_buf_list(hevc);
+ /**/
+ init_pic_list(hevc);
+ init_pic_list_hw(hevc);
+ init_buf_spec(hevc);
+ hevc->pic_list_init_flag = 2;
+ pr_info("set pic_list_init_flag to 2\n");
+
+ WRITE_VREG(HEVC_ASSIST_MBOX1_IRQ_REG, 0x1);
+
+ }
+
+ if (hevc->uninit_list) {
+ /*USE_BUF_BLOCK*/
+ uninit_buf_list(hevc, false);
+ pr_info("uninit list\n");
+ hevc->uninit_list = 0;
+#ifdef USE_UNINIT_SEMA
+ up(&hevc->h265_uninit_done_sema);
+#endif
+ }
+
+ }
+
+ return 0;
+
+}
+
+void vh265_free_cmabuf(void)
+{
+ struct hevc_state_s *hevc = &gHevc;
+
+ mutex_lock(&vh265_mutex);
+
+ if (hevc->init_flag) {
+ mutex_unlock(&vh265_mutex);
+ return;
+ }
+
+ if (use_cma) {
+ pr_info("force uninit_buf_list\n");
+ uninit_buf_list(hevc, true);
+ }
+
+ mutex_unlock(&vh265_mutex);
+}
+
+#ifdef MULTI_INSTANCE_SUPPORT
+int vh265_dec_status(struct vdec_s *vdec, struct vdec_status *vstatus)
+#else
+int vh265_dec_status(struct vdec_status *vstatus)
+#endif
+{
+ struct hevc_state_s *hevc = &gHevc;
+
+ vstatus->width = hevc->frame_width;
+ vstatus->height = hevc->frame_height;
+ if (hevc->frame_dur != 0)
+ vstatus->fps = 96000 / hevc->frame_dur;
+ else
+ vstatus->fps = -1;
+ vstatus->error_count = 0;
+ vstatus->status = hevc->stat | hevc->fatal_error;
+ return 0;
+}
+
+#if 0
+static void H265_DECODE_INIT(void)
+{
+ /* enable hevc clocks */
+ WRITE_VREG(DOS_GCLK_EN3, 0xffffffff);
+ /* *************************************************************** */
+ /* Power ON HEVC */
+ /* *************************************************************** */
+ /* Powerup HEVC */
+ WRITE_VREG(P_AO_RTI_GEN_PWR_SLEEP0,
+ READ_VREG(P_AO_RTI_GEN_PWR_SLEEP0) & (~(0x3 << 6)));
+ WRITE_VREG(DOS_MEM_PD_HEVC, 0x0);
+ WRITE_VREG(DOS_SW_RESET3, READ_VREG(DOS_SW_RESET3) | (0x3ffff << 2));
+ WRITE_VREG(DOS_SW_RESET3, READ_VREG(DOS_SW_RESET3) & (~(0x3ffff << 2)));
+ /* remove isolations */
+ WRITE_VREG(AO_RTI_GEN_PWR_ISO0,
+ READ_VREG(AO_RTI_GEN_PWR_ISO0) & (~(0x3 << 10)));
+
+}
+#endif
+
+static void vh265_prot_init(struct hevc_state_s *hevc)
+{
+ /* H265_DECODE_INIT(); */
+
+ hevc_config_work_space_hw(hevc);
+
+ hevc_init_decoder_hw(hevc, 0, 0xffffffff);
+
+ WRITE_VREG(HEVC_WAIT_FLAG, 1);
+
+ /* WRITE_VREG(P_HEVC_MPSR, 1); */
+
+ /* clear mailbox interrupt */
+ WRITE_VREG(HEVC_ASSIST_MBOX1_CLR_REG, 1);
+
+ /* enable mailbox interrupt */
+ WRITE_VREG(HEVC_ASSIST_MBOX1_MASK, 1);
+
+ /* disable PSCALE for hardware sharing */
+ WRITE_VREG(HEVC_PSCALE_CTRL, 0);
+
+ if (debug & H265_DEBUG_UCODE)
+ WRITE_VREG(DEBUG_REG1, 0x1);
+ else
+ WRITE_VREG(DEBUG_REG1, 0x0);
+
+ if ((debug & (H265_DEBUG_MAN_SKIP_NAL | H265_DEBUG_MAN_SEARCH_NAL)) ||
+ hevc->m_ins_flag) {
+ WRITE_VREG(NAL_SEARCH_CTL, 0x1); /* manual parser NAL */
+ } else {
+ unsigned ctl_val = 0x8; /* check vps/sps/pps/i-slice in ucode */
+
+ if (hevc->PB_skip_mode == 0)
+ ctl_val = 0x4; /* check vps/sps/pps only in ucode */
+ else if (hevc->PB_skip_mode == 3)
+ ctl_val = 0x0; /* check vps/sps/pps/idr in ucode */
+ WRITE_VREG(NAL_SEARCH_CTL, ctl_val);
+ }
+ if (debug & H265_DEBUG_NO_EOS_SEARCH_DONE)
+ WRITE_VREG(NAL_SEARCH_CTL, READ_VREG(NAL_SEARCH_CTL) | 0x10000);
+
+ if (parser_sei_enable & 0x1)
+ WRITE_VREG(NAL_SEARCH_CTL, READ_VREG(NAL_SEARCH_CTL) | 0x20000);
+ WRITE_VREG(DECODE_STOP_POS, decode_stop_pos);
+
+}
+
+static int vh265_local_init(struct hevc_state_s *hevc)
+{
+ int i;
+ int ret = -1;
+
+#ifdef DEBUG_PTS
+ hevc->pts_missed = 0;
+ hevc->pts_hit = 0;
+#endif
+
+ hevc->last_put_idx_a = -1;
+ hevc->last_put_idx_b = -1;
+ hevc->saved_resolution = 0;
+ hevc->get_frame_dur = false;
+ hevc->frame_width = hevc->vh265_amstream_dec_info.width;
+ hevc->frame_height = hevc->vh265_amstream_dec_info.height;
+ if (hevc->frame_width * hevc->frame_height > HEVC_SIZE) {
+ pr_info("over size : %u x %u.\n",
+ hevc->frame_width, hevc->frame_height);
+ hevc->fatal_error |= DECODER_FATAL_ERROR_SIZE_OVERFLOW;
+ return ret;
+ }
+ hevc->frame_dur =
+ (hevc->vh265_amstream_dec_info.rate ==
+ 0) ? 3600 : hevc->vh265_amstream_dec_info.rate;
+ if (hevc->frame_width && hevc->frame_height)
+ hevc->frame_ar = hevc->frame_height * 0x100 / hevc->frame_width;
+ hevc->error_watchdog_count = 0;
+ hevc->sei_present_flag = 0;
+ pts_unstable = ((unsigned long)hevc->vh265_amstream_dec_info.param
+ & 0x40) >> 6;
+ pr_info("h265:pts_unstable=%d\n", pts_unstable);
+/*
+*TODO:FOR VERSION
+*/
+ pr_info("h265: ver (%d,%d) decinfo: %dx%d rate=%d\n", h265_version,
+ 0, hevc->frame_width, hevc->frame_height, hevc->frame_dur);
+
+ if (hevc->frame_dur == 0)
+ hevc->frame_dur = 96000 / 24;
+
+ INIT_KFIFO(hevc->display_q);
+ INIT_KFIFO(hevc->newframe_q);
+
+
+ for (i = 0; i < VF_POOL_SIZE; i++) {
+ const struct vframe_s *vf = &hevc->vfpool[i];
+
+ hevc->vfpool[i].index = -1;
+ kfifo_put(&hevc->newframe_q, vf);
+ }
+
+
+ ret = hevc_local_init(hevc);
+
+ return ret;
+}
+#ifdef MULTI_INSTANCE_SUPPORT
+static s32 vh265_init(struct vdec_s *vdec)
+{
+ struct hevc_state_s *hevc = (struct hevc_state_s *)vdec->private;
+#else
+static s32 vh265_init(struct hevc_state_s *hevc)
+{
+#endif
+ init_timer(&hevc->timer);
+
+ hevc->stat |= STAT_TIMER_INIT;
+ if (vh265_local_init(hevc) < 0)
+ return -EBUSY;
+
+#ifdef MULTI_INSTANCE_SUPPORT
+ if (hevc->m_ins_flag) {
+ hevc->timer.data = (ulong) hevc;
+ hevc->timer.function = vh265_put_timer_func;
+ hevc->timer.expires = jiffies + PUT_INTERVAL;
+
+ add_timer(&hevc->timer);
+
+ hevc->stat |= STAT_TIMER_ARM;
+
+ INIT_WORK(&hevc->work, vh265_work);
+ return 0;
+ }
+#endif
+ amhevc_enable();
+ if (debug & H265_DEBUG_LOAD_UCODE_FROM_FILE) {
+ pr_info("load ucode from file of vh265_mc_debug\n");
+ if (amhevc_loadmc_ex(VFORMAT_HEVC,
+ "vh265_mc_debug", NULL) < 0) {
+ amhevc_disable();
+ return -EBUSY;
+ }
+#if 0
+ } else if (double_write_mode & 0x10) {
+ pr_info("load ucode from file of vh265_mc_dw\n");
+ if (amhevc_loadmc_ex(VFORMAT_HEVC,
+ "vh265_mc_dw", NULL) < 0) {
+ amhevc_disable();
+ return -EBUSY;
+ }
+#endif
+ } else if (mmu_enable && (get_cpu_type() >= MESON_CPU_MAJOR_ID_GXL)) {
+ if (amhevc_loadmc_ex(VFORMAT_HEVC, "vh265_mc_mmu", NULL)
+ < 0) {
+ amhevc_disable();
+ return -EBUSY;
+ }
+ pr_info("vh265 mmu ucode loaded!\n");
+ } else if (amhevc_loadmc_ex(VFORMAT_HEVC, "vh265_mc", NULL) < 0) {
+ amhevc_disable();
+ return -EBUSY;
+ }
+ hevc->stat |= STAT_MC_LOAD;
+
+ /* enable AMRISC side protocol */
+ vh265_prot_init(hevc);
+
+ if (vdec_request_threaded_irq(VDEC_IRQ_1, vh265_isr,
+ vh265_isr_thread_fn,
+ IRQF_ONESHOT,/*run thread on this irq disabled*/
+ "vh265-irq", (void *)hevc)) {
+ pr_info("vh265 irq register error.\n");
+ amhevc_disable();
+ return -ENOENT;
+ }
+
+ hevc->stat |= STAT_ISR_REG;
+ hevc->provider_name = PROVIDER_NAME;
+
+#ifdef MULTI_INSTANCE_SUPPORT
+ vf_provider_init(&vh265_vf_prov, hevc->provider_name,
+ &vh265_vf_provider, vdec);
+ vf_reg_provider(&vh265_vf_prov);
+ vf_notify_receiver(hevc->provider_name, VFRAME_EVENT_PROVIDER_START,
+ NULL);
+ vf_notify_receiver(hevc->provider_name, VFRAME_EVENT_PROVIDER_FR_HINT,
+ (void *)((unsigned long)hevc->frame_dur));
+#else
+ vf_provider_init(&vh265_vf_prov, PROVIDER_NAME, &vh265_vf_provider,
+ hevc);
+ vf_reg_provider(&vh265_vf_prov);
+ vf_notify_receiver(PROVIDER_NAME, VFRAME_EVENT_PROVIDER_START, NULL);
+ vf_notify_receiver(PROVIDER_NAME, VFRAME_EVENT_PROVIDER_FR_HINT,
+ (void *)((unsigned long)hevc->frame_dur));
+#endif
+ hevc->stat |= STAT_VF_HOOK;
+
+ hevc->timer.data = (ulong) hevc;
+ hevc->timer.function = vh265_put_timer_func;
+ hevc->timer.expires = jiffies + PUT_INTERVAL;
+
+ add_timer(&hevc->timer);
+
+ hevc->stat |= STAT_TIMER_ARM;
+
+ if (use_cma) {
+ if (h265_task == NULL) {
+ sema_init(&hevc->h265_sema, 1);
+#ifdef USE_UNINIT_SEMA
+ sema_init(
+ &hevc->h265_uninit_done_sema, 0);
+#endif
+ h265_task =
+ kthread_run(h265_task_handle, hevc,
+ "kthread_h265");
+ }
+ }
+ /* hevc->stat |= STAT_KTHREAD; */
+
+ if (debug & H265_DEBUG_FORCE_CLK) {
+ pr_info("%s force clk\n", __func__);
+ WRITE_VREG(HEVC_IQIT_CLK_RST_CTRL,
+ READ_VREG(HEVC_IQIT_CLK_RST_CTRL) |
+ ((1 << 2) | (1 << 1)));
+ WRITE_VREG(HEVC_DBLK_CFG0,
+ READ_VREG(HEVC_DBLK_CFG0) | ((1 << 2) |
+ (1 << 1) | 0x3fff0000));/* 2,29:16 */
+ WRITE_VREG(HEVC_SAO_CTRL1, READ_VREG(HEVC_SAO_CTRL1) |
+ (1 << 2)); /* 2 */
+ WRITE_VREG(HEVC_MPRED_CTRL1, READ_VREG(HEVC_MPRED_CTRL1) |
+ (1 << 24)); /* 24 */
+ WRITE_VREG(HEVC_STREAM_CONTROL,
+ READ_VREG(HEVC_STREAM_CONTROL) |
+ (1 << 15)); /* 15 */
+ WRITE_VREG(HEVC_CABAC_CONTROL, READ_VREG(HEVC_CABAC_CONTROL) |
+ (1 << 13)); /* 13 */
+ WRITE_VREG(HEVC_PARSER_CORE_CONTROL,
+ READ_VREG(HEVC_PARSER_CORE_CONTROL) |
+ (1 << 15)); /* 15 */
+ WRITE_VREG(HEVC_PARSER_INT_CONTROL,
+ READ_VREG(HEVC_PARSER_INT_CONTROL) |
+ (1 << 15)); /* 15 */
+ WRITE_VREG(HEVC_PARSER_IF_CONTROL,
+ READ_VREG(HEVC_PARSER_IF_CONTROL) | ((1 << 6) |
+ (1 << 3) | (1 << 1))); /* 6, 3, 1 */
+ WRITE_VREG(HEVCD_IPP_DYNCLKGATE_CONFIG,
+ READ_VREG(HEVCD_IPP_DYNCLKGATE_CONFIG) |
+ 0xffffffff); /* 31:0 */
+ WRITE_VREG(HEVCD_MCRCC_CTL1, READ_VREG(HEVCD_MCRCC_CTL1) |
+ (1 << 3)); /* 3 */
+ }
+
+ amhevc_start();
+
+ hevc->stat |= STAT_VDEC_RUN;
+#ifndef MULTI_INSTANCE_SUPPORT
+ set_vdec_func(&vh265_dec_status);
+#endif
+ hevc->init_flag = 1;
+ if (mmu_enable)
+ error_handle_threshold = 300;
+ else
+ error_handle_threshold = 30;
+ /*
+ *pr_info("%d, vh265_init, RP=0x%x\n",
+ *__LINE__, READ_VREG(HEVC_STREAM_RD_PTR));
+ */
+
+ return 0;
+}
+
+static int vh265_stop(struct hevc_state_s *hevc)
+{
+
+ hevc->init_flag = 0;
+
+ if (debug & H265_DEBUG_WAIT_DECODE_DONE_WHEN_STOP) {
+ int wait_timeout_count = 0;
+
+ while (READ_VREG(HEVC_DEC_STATUS_REG) ==
+ HEVC_CODED_SLICE_SEGMENT_DAT &&
+ wait_timeout_count < 10){
+ wait_timeout_count++;
+ msleep(20);
+ }
+ }
+ if (hevc->stat & STAT_VDEC_RUN) {
+ amhevc_stop();
+ hevc->stat &= ~STAT_VDEC_RUN;
+ }
+
+ if (hevc->stat & STAT_ISR_REG) {
+ WRITE_VREG(HEVC_ASSIST_MBOX1_MASK, 0);
+ vdec_free_irq(VDEC_IRQ_1, (void *)hevc);
+ hevc->stat &= ~STAT_ISR_REG;
+ }
+
+ hevc->stat &= ~STAT_TIMER_INIT;
+ if (hevc->stat & STAT_TIMER_ARM) {
+ del_timer_sync(&hevc->timer);
+ hevc->stat &= ~STAT_TIMER_ARM;
+ }
+
+ if (hevc->stat & STAT_VF_HOOK) {
+ vf_notify_receiver(hevc->provider_name,
+ VFRAME_EVENT_PROVIDER_FR_END_HINT, NULL);
+
+ vf_unreg_provider(&vh265_vf_prov);
+ hevc->stat &= ~STAT_VF_HOOK;
+ }
+
+ hevc_local_uninit(hevc);
+
+ if (use_cma) {
+#ifdef USE_UNINIT_SEMA
+ int ret;
+#endif
+ hevc->uninit_list = 1;
+ up(&hevc->h265_sema);
+#ifdef USE_UNINIT_SEMA
+ ret = down_interruptible(
+ &hevc->h265_uninit_done_sema);
+#else
+ while (hevc->uninit_list) /* wait uninit complete */
+ msleep(20);
+#endif
+ if (hevc->mmu_box)
+ uninit_mmu_buffers(hevc);
+ }
+
+ amhevc_disable();
+
+ return 0;
+}
+
+#ifdef MULTI_INSTANCE_SUPPORT
+static int vmh265_stop(struct hevc_state_s *hevc)
+{
+
+ hevc->init_flag = 0;
+
+ if (hevc->stat & STAT_TIMER_ARM) {
+ del_timer_sync(&hevc->timer);
+ hevc->stat &= ~STAT_TIMER_ARM;
+ }
+
+ if (hevc->stat & STAT_VF_HOOK) {
+ vf_notify_receiver(hevc->provider_name,
+ VFRAME_EVENT_PROVIDER_FR_END_HINT, NULL);
+
+ vf_unreg_provider(&vh265_vf_prov);
+ hevc->stat &= ~STAT_VF_HOOK;
+ }
+
+ hevc_local_uninit(hevc);
+
+ if (use_cma) {
+#ifdef USE_UNINIT_SEMA
+ int ret;
+#endif
+ hevc->uninit_list = 1;
+ schedule_work(&hevc->work);
+#ifdef USE_UNINIT_SEMA
+ ret = down_interruptible(
+ &hevc->h265_uninit_done_sema);
+#else
+ while (hevc->uninit_list) /* wait uninit complete */
+ msleep(20);
+#endif
+ }
+ cancel_work_sync(&hevc->work);
+
+ return 0;
+}
+
+static unsigned int start_decode_buf_level; /* = 0x80000;*/
+
+static void vh265_work(struct work_struct *work)
+{
+ struct hevc_state_s *hevc = container_of(work,
+ struct hevc_state_s, work);
+
+ /*
+ *finished decoding one frame or error,
+ * notify vdec core to switch context
+ */
+ if ((hevc->init_flag != 0) && (hevc->pic_list_init_flag == 1)) {
+ /*USE_BUF_BLOCK*/
+ init_buf_list(hevc);
+ /**/
+ init_pic_list(hevc);
+ init_pic_list_hw(hevc);
+ init_buf_spec(hevc);
+ hevc->pic_list_init_flag = 2;
+ pr_info("set pic_list_init_flag to 2\n");
+
+ WRITE_VREG(HEVC_ASSIST_MBOX1_IRQ_REG, 0x1);
+ return;
+ }
+
+ if (hevc->uninit_list) {
+ /*USE_BUF_BLOCK*/
+ uninit_buf_list(hevc, false);
+ pr_info("uninit list\n");
+ hevc->uninit_list = 0;
+#ifdef USE_UNINIT_SEMA
+ up(&hevc->h265_uninit_done_sema);
+#endif
+ return;
+ }
+
+ if (hevc->dec_result == DEC_RESULT_DONE) {
+ /*
+ *if (!hevc->ctx_valid)
+ * hevc->ctx_valid = 1;
+ */
+ hevc_print(hevc, PRINT_FLAG_VDEC_STATUS,
+ "%s dec_result %d %x %x %x\n",
+ __func__,
+ hevc->dec_result,
+ READ_VREG(HEVC_STREAM_LEVEL),
+ READ_VREG(HEVC_STREAM_WR_PTR),
+ READ_VREG(HEVC_STREAM_RD_PTR));
+ vdec_vframe_dirty(hw_to_vdec(hevc), hevc->chunk);
+ } else {
+ hevc_print(hevc, PRINT_FLAG_VDEC_DETAIL,
+ "%s dec_result %d %x %x %x\n",
+ __func__,
+ hevc->dec_result,
+ READ_VREG(HEVC_STREAM_LEVEL),
+ READ_VREG(HEVC_STREAM_WR_PTR),
+ READ_VREG(HEVC_STREAM_RD_PTR));
+ }
+
+ /* mark itself has all HW resource released and input released */
+ vdec_set_status(hw_to_vdec(hevc), VDEC_STATUS_CONNECTED);
+
+ if (hevc->vdec_cb)
+ hevc->vdec_cb(hw_to_vdec(hevc), hevc->vdec_cb_arg);
+}
+
+static int vh265_hw_ctx_restore(struct hevc_state_s *hevc)
+{
+ /* new to do ... */
+ vh265_prot_init(hevc);
+ return 0;
+}
+
+static bool is_buffer_available(struct vdec_s *vdec)
+{
+ /* new to do ... */
+ return 1;
+}
+
+static bool run_ready(struct vdec_s *vdec)
+{
+ struct hevc_state_s *hevc =
+ (struct hevc_state_s *)vdec->private;
+
+ hevc_print(hevc,
+ PRINT_FLAG_VDEC_DETAIL, "%s\r\n", __func__);
+
+ return is_buffer_available(vdec);
+}
+
+static void run(struct vdec_s *vdec,
+ void (*callback)(struct vdec_s *, void *), void *arg)
+{
+ struct hevc_state_s *hevc =
+ (struct hevc_state_s *)vdec->private;
+ int r;
+
+ hevc->vdec_cb_arg = arg;
+ hevc->vdec_cb = callback;
+ /* hw->chunk = vdec_prepare_input(vdec); */
+ r = vdec_prepare_input(vdec, &hevc->chunk);
+ if (r < 0) {
+ hevc->dec_result = DEC_RESULT_AGAIN;
+
+ hevc_print(hevc, PRINT_FLAG_VDEC_DETAIL,
+ "ammvdec_vh265: Insufficient data\n");
+
+ schedule_work(&hevc->work);
+ return;
+ }
+
+ hevc->dec_result = DEC_RESULT_NONE;
+
+ if (/*(!input_frame_based(vdec)) && */(start_decode_buf_level > 0)) {
+ if (READ_VREG(HEVC_STREAM_LEVEL) <
+ start_decode_buf_level) {
+ hevc_print(hevc,
+ PRINT_FLAG_VDEC_DETAIL,
+ "%s: VIFIFO_LEVEL %x is low (<%x)\n",
+ __func__,
+ READ_VREG(HEVC_STREAM_LEVEL),
+ start_decode_buf_level);
+
+ hevc->dec_result = DEC_RESULT_AGAIN;
+ schedule_work(&hevc->work);
+ return;
+ }
+ }
+
+ hevc_print(hevc, PRINT_FLAG_VDEC_STATUS,
+ "%s: %x %x %x\n",
+ __func__,
+ READ_VREG(HEVC_STREAM_LEVEL),
+ READ_VREG(HEVC_STREAM_WR_PTR),
+ READ_VREG(HEVC_STREAM_RD_PTR));
+
+ if (((debug & ONLY_RESET_AT_START) == 0) ||
+ (hevc->init_flag == 0)) {
+ if (amhevc_loadmc_ex(VFORMAT_HEVC, "vh265_mc", NULL) < 0) {
+ amhevc_disable();
+ pr_info("%s: Error amvdec_loadmc fail\n", __func__);
+ return;
+ }
+
+ if (vh265_hw_ctx_restore(hevc) < 0) {
+ schedule_work(&hevc->work);
+ return;
+ }
+
+ vdec_enable_input(vdec);
+
+ WRITE_VREG(HEVC_DEC_STATUS_REG, HEVC_ACTION_DONE);
+
+ WRITE_VREG(HEVC_SHIFT_BYTE_COUNT, 0);
+ WRITE_VREG(HEVC_DECODE_SIZE, hevc->chunk->size);
+ hevc->init_flag = 1;
+
+ if (hevc->pic_list_init_flag == 3)
+ init_pic_list_hw(hevc);
+ amhevc_start();
+ } else {
+ WRITE_VREG(HEVC_SHIFT_BYTE_COUNT, 0);
+ WRITE_VREG(HEVC_DECODE_SIZE, hevc->chunk->size);
+
+ hevc_recover(hevc);
+
+ vdec_enable_input(vdec);
+
+ WRITE_VREG(HEVC_DEC_STATUS_REG, HEVC_ACTION_DONE);
+ }
+
+}
+
+static void reset(struct vdec_s *vdec)
+{
+
+ struct hevc_state_s *hevc =
+ (struct hevc_state_s *)vdec->private;
+
+ hevc_print(hevc,
+ PRINT_FLAG_VDEC_DETAIL, "%s\r\n", __func__);
+
+}
+
+static irqreturn_t vh265_irq_cb(struct vdec_s *vdec)
+{
+ struct hevc_state_s *hevc =
+ (struct hevc_state_s *)vdec->private;
+
+ return vh265_isr(0, hevc);
+}
+
+static irqreturn_t vh265_threaded_irq_cb(struct vdec_s *vdec)
+{
+ struct hevc_state_s *hevc =
+ (struct hevc_state_s *)vdec->private;
+
+ return vh265_isr_thread_fn(0, hevc);
+}
+#endif
+
+static int amvdec_h265_probe(struct platform_device *pdev)
+{
+#ifdef MULTI_INSTANCE_SUPPORT
+ struct vdec_s *pdata = *(struct vdec_s **)pdev->dev.platform_data;
+#else
+ struct vdec_dev_reg_s *pdata =
+ (struct vdec_dev_reg_s *)pdev->dev.platform_data;
+#endif
+ struct hevc_state_s *hevc = &gHevc;
+
+ if (debug)
+ pr_info("%s\r\n", __func__);
+ mutex_lock(&vh265_mutex);
+
+ if ((get_cpu_type() >= MESON_CPU_MAJOR_ID_GXTVBB) &&
+ (parser_sei_enable & 0x100) == 0)
+ parser_sei_enable = 1;
+ hevc->m_ins_flag = 0;
+ hevc->init_flag = 0;
+ hevc->uninit_list = 0;
+ hevc->fatal_error = 0;
+ hevc->show_frame_num = 0;
+ if (pdata == NULL) {
+ pr_info("\namvdec_h265 memory resource undefined.\n");
+ mutex_unlock(&vh265_mutex);
+ return -EFAULT;
+ }
+
+ hevc->buf_start = pdata->mem_start;
+ hevc->buf_size = pdata->mem_end - pdata->mem_start + 1;
+ /*
+ *hevc->mc_buf_spec.buf_end = pdata->mem_end + 1;
+ *for (i = 0; i < WORK_BUF_SPEC_NUM; i++)
+ * amvh265_workbuff_spec[i].start_adr = pdata->mem_start;
+ */
+ if (debug) {
+ pr_info("===H.265 decoder mem resource 0x%lx -- 0x%lx\n",
+ pdata->mem_start, pdata->mem_end + 1);
+ }
+
+ if (pdata->sys_info)
+ hevc->vh265_amstream_dec_info = *pdata->sys_info;
+ else {
+ hevc->vh265_amstream_dec_info.width = 0;
+ hevc->vh265_amstream_dec_info.height = 0;
+ hevc->vh265_amstream_dec_info.rate = 30;
+ }
+#ifndef MULTI_INSTANCE_SUPPORT
+ if (pdata->flag & DEC_FLAG_HEVC_WORKAROUND) {
+ workaround_enable |= 3;
+ pr_info("amvdec_h265 HEVC_WORKAROUND flag set.\n");
+ } else
+ workaround_enable &= ~3;
+#endif
+ hevc->cma_dev = pdata->cma_dev;
+#ifdef MULTI_INSTANCE_SUPPORT
+ pdata->private = hevc;
+ pdata->dec_status = vh265_dec_status;
+ if (vh265_init(pdata) < 0) {
+#else
+ if (vh265_init(hevc) < 0) {
+#endif
+ pr_info("\namvdec_h265 init failed.\n");
+ hevc_local_uninit(hevc);
+ mutex_unlock(&vh265_mutex);
+ return -ENODEV;
+ }
+ /*set the max clk for smooth playing...*/
+ hevc_source_changed(VFORMAT_HEVC,
+ 4096, 2048, 30);
+ mutex_unlock(&vh265_mutex);
+
+ return 0;
+}
+
+static int amvdec_h265_remove(struct platform_device *pdev)
+{
+ struct hevc_state_s *hevc = &gHevc;
+
+ if (debug)
+ pr_info("%s\r\n", __func__);
+
+ mutex_lock(&vh265_mutex);
+
+ vh265_stop(hevc);
+
+ hevc_source_changed(VFORMAT_HEVC, 0, 0, 0);
+
+
+#ifdef DEBUG_PTS
+ pr_info("pts missed %ld, pts hit %ld, duration %d\n",
+ hevc->pts_missed, hevc->pts_hit, hevc->frame_dur);
+#endif
+
+ mutex_unlock(&vh265_mutex);
+
+ return 0;
+}
+/****************************************/
+
+static struct platform_driver amvdec_h265_driver = {
+ .probe = amvdec_h265_probe,
+ .remove = amvdec_h265_remove,
+#ifdef CONFIG_PM
+ .suspend = amhevc_suspend,
+ .resume = amhevc_resume,
+#endif
+ .driver = {
+ .name = DRIVER_NAME,
+ }
+};
+
+#ifdef MULTI_INSTANCE_SUPPORT
+static int ammvdec_h265_probe(struct platform_device *pdev)
+{
+
+ struct vdec_s *pdata = *(struct vdec_s **)pdev->dev.platform_data;
+ struct hevc_state_s *hevc = NULL;
+
+ if (debug)
+ pr_info("%s\r\n", __func__);
+ if (pdata == NULL) {
+ pr_info("\nammvdec_h265 memory resource undefined.\n");
+ return -EFAULT;
+ }
+
+ hevc = (struct hevc_state_s *)devm_kzalloc(&pdev->dev,
+ sizeof(struct hevc_state_s), GFP_KERNEL);
+ if (hevc == NULL) {
+ pr_info("\nammvdec_h265 device data allocation failed\n");
+ return -ENOMEM;
+ }
+ hevc->index = m_hevc_count % max_decode_instance_num;
+ m_hevc_count++;
+ pdata->private = hevc;
+ pdata->dec_status = vh265_dec_status;
+ /* pdata->set_trickmode = set_trickmode; */
+ pdata->run_ready = run_ready;
+ pdata->run = run;
+ pdata->reset = reset;
+ pdata->irq_handler = vh265_irq_cb;
+ pdata->threaded_irq_handler = vh265_threaded_irq_cb;
+
+ pdata->id = pdev->id;
+
+ if (pdata->use_vfm_path)
+ snprintf(pdata->vf_provider_name, VDEC_PROVIDER_NAME_SIZE,
+ VFM_DEC_PROVIDER_NAME);
+ else if (debug & USE_OLD_PROVIDER)
+ snprintf(pdata->vf_provider_name, VDEC_PROVIDER_NAME_SIZE,
+ PROVIDER_NAME);
+ else
+ snprintf(pdata->vf_provider_name, VDEC_PROVIDER_NAME_SIZE,
+ MULTI_INSTANCE_PROVIDER_NAME ".%02x", pdev->id & 0xff);
+
+ vf_provider_init(&pdata->vframe_provider, pdata->vf_provider_name,
+ &vh265_vf_provider, pdata);
+
+ hevc->provider_name = pdata->vf_provider_name;
+ platform_set_drvdata(pdev, pdata);
+
+ hevc->platform_dev = pdev;
+
+ hevc->buf_start = pdata->mem_start;
+ hevc->buf_size = pdata->mem_end - pdata->mem_start + 1;
+ if (get_cpu_type() < MESON_CPU_MAJOR_ID_GXL
+ || double_write_mode == 0x10)
+ mmu_enable = 0;
+ if ((get_cpu_type() >= MESON_CPU_MAJOR_ID_GXTVBB) &&
+ (parser_sei_enable & 0x100) == 0)
+ parser_sei_enable = 1;
+ hevc->m_ins_flag = 1;
+ hevc->init_flag = 0;
+ hevc->uninit_list = 0;
+ hevc->fatal_error = 0;
+ hevc->show_frame_num = 0;
+ if (pdata == NULL) {
+ pr_info("\namvdec_h265 memory resource undefined.\n");
+ return -EFAULT;
+ }
+ /*
+ *hevc->mc_buf_spec.buf_end = pdata->mem_end + 1;
+ *for (i = 0; i < WORK_BUF_SPEC_NUM; i++)
+ * amvh265_workbuff_spec[i].start_adr = pdata->mem_start;
+ */
+ if (debug) {
+ pr_info("===H.265 decoder mem resource 0x%lx -- 0x%lx\n",
+ pdata->mem_start, pdata->mem_end + 1);
+ }
+
+ if (pdata->sys_info)
+ hevc->vh265_amstream_dec_info = *pdata->sys_info;
+ else {
+ hevc->vh265_amstream_dec_info.width = 0;
+ hevc->vh265_amstream_dec_info.height = 0;
+ hevc->vh265_amstream_dec_info.rate = 30;
+ }
+
+ hevc->cma_dev = pdata->cma_dev;
+
+ if (vh265_init(pdata) < 0) {
+ pr_info("\namvdec_h265 init failed.\n");
+ hevc_local_uninit(hevc);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static int ammvdec_h265_remove(struct platform_device *pdev)
+{
+ struct hevc_state_s *hevc =
+ (struct hevc_state_s *)
+ (((struct vdec_s *)(platform_get_drvdata(pdev)))->private);
+
+ if (debug)
+ pr_info("%s\r\n", __func__);
+
+ vmh265_stop(hevc);
+
+ /* vdec_source_changed(VFORMAT_H264, 0, 0, 0); */
+
+ vdec_set_status(hw_to_vdec(hevc), VDEC_STATUS_DISCONNECTED);
+
+ return 0;
+}
+
+static struct platform_driver ammvdec_h265_driver = {
+ .probe = ammvdec_h265_probe,
+ .remove = ammvdec_h265_remove,
+#ifdef CONFIG_PM
+ .suspend = amvdec_suspend,
+ .resume = amvdec_resume,
+#endif
+ .driver = {
+ .name = MULTI_DRIVER_NAME,
+ }
+};
+#endif
+
+static struct codec_profile_t amvdec_h265_profile = {
+ .name = "hevc",
+ .profile = ""
+};
+
+static int __init amvdec_h265_driver_init_module(void)
+{
+ pr_debug("amvdec_h265 module init\n");
+ error_handle_policy = 0;
+
+#ifdef ERROR_HANDLE_DEBUG
+ dbg_nal_skip_flag = 0;
+ dbg_nal_skip_count = 0;
+#endif
+ decode_stop_pos = 0;
+ decode_stop_pos_pre = 0;
+ decode_pic_begin = 0;
+ slice_parse_begin = 0;
+ step = 0;
+ buf_alloc_size = 0;
+
+#ifdef MULTI_INSTANCE_SUPPORT
+ if (platform_driver_register(&ammvdec_h265_driver))
+ pr_err("failed to register ammvdec_h265 driver\n");
+
+#endif
+ if (platform_driver_register(&amvdec_h265_driver)) {
+ pr_err("failed to register amvdec_h265 driver\n");
+ return -ENODEV;
+ }
+#if 1/*MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON8*/
+ if (!has_hevc_vdec()) {
+ /* not support hevc */
+ amvdec_h265_profile.name = "hevc_unsupport";
+ } else if (is_meson_m8m2_cpu()) {
+ /* m8m2 support 4k */
+ amvdec_h265_profile.profile = "4k";
+ } else if (get_cpu_type() >= MESON_CPU_MAJOR_ID_GXBB) {
+ amvdec_h265_profile.profile =
+ "4k, 9bit, 10bit, dwrite, compressed";
+ } else if (get_cpu_type() >= MESON_CPU_MAJOR_ID_MG9TV)
+ amvdec_h265_profile.profile = "4k";
+#endif
+ if (codec_mm_get_total_size() < 80 * SZ_1M) {
+ pr_info("amvdec_h265 default mmu enabled.\n");
+ mmu_enable = 1;
+ }
+
+ vcodec_profile_register(&amvdec_h265_profile);
+
+ return 0;
+}
+
+static void __exit amvdec_h265_driver_remove_module(void)
+{
+ pr_debug("amvdec_h265 module remove.\n");
+
+#ifdef MULTI_INSTANCE_SUPPORT
+ platform_driver_unregister(&ammvdec_h265_driver);
+#endif
+ platform_driver_unregister(&amvdec_h265_driver);
+}
+
+/****************************************/
+/*
+*module_param(stat, uint, 0664);
+*MODULE_PARM_DESC(stat, "\n amvdec_h265 stat\n");
+*/
+module_param(use_cma, uint, 0664);
+MODULE_PARM_DESC(use_cma, "\n amvdec_h265 use_cma\n");
+
+module_param(bit_depth_luma, uint, 0664);
+MODULE_PARM_DESC(bit_depth_luma, "\n amvdec_h265 bit_depth_luma\n");
+
+module_param(bit_depth_chroma, uint, 0664);
+MODULE_PARM_DESC(bit_depth_chroma, "\n amvdec_h265 bit_depth_chroma\n");
+
+module_param(video_signal_type, uint, 0664);
+MODULE_PARM_DESC(video_signal_type, "\n amvdec_h265 video_signal_type\n");
+
+module_param(debug, uint, 0664);
+MODULE_PARM_DESC(debug, "\n amvdec_h265 debug\n");
+
+#ifdef ERROR_HANDLE_DEBUG
+module_param(dbg_nal_skip_flag, uint, 0664);
+MODULE_PARM_DESC(dbg_nal_skip_flag, "\n amvdec_h265 dbg_nal_skip_flag\n");
+
+module_param(dbg_nal_skip_count, uint, 0664);
+MODULE_PARM_DESC(dbg_nal_skip_count, "\n amvdec_h265 dbg_nal_skip_count\n");
+#endif
+
+module_param(radr, uint, 0664);
+MODULE_PARM_DESC(radr, "\nradr\n");
+
+module_param(rval, uint, 0664);
+MODULE_PARM_DESC(rval, "\nrval\n");
+
+module_param(dbg_cmd, uint, 0664);
+MODULE_PARM_DESC(dbg_cmd, "\ndbg_cmd\n");
+
+module_param(dbg_skip_decode_index, uint, 0664);
+MODULE_PARM_DESC(dbg_skip_decode_index, "\ndbg_skip_decode_index\n");
+
+module_param(endian, uint, 0664);
+MODULE_PARM_DESC(endian, "\nrval\n");
+
+module_param(step, uint, 0664);
+MODULE_PARM_DESC(step, "\n amvdec_h265 step\n");
+
+module_param(decode_stop_pos, uint, 0664);
+MODULE_PARM_DESC(decode_stop_pos, "\n amvdec_h265 decode_stop_pos\n");
+
+module_param(decode_pic_begin, uint, 0664);
+MODULE_PARM_DESC(decode_pic_begin, "\n amvdec_h265 decode_pic_begin\n");
+
+module_param(slice_parse_begin, uint, 0664);
+MODULE_PARM_DESC(slice_parse_begin, "\n amvdec_h265 slice_parse_begin\n");
+
+module_param(nal_skip_policy, uint, 0664);
+MODULE_PARM_DESC(nal_skip_policy, "\n amvdec_h265 nal_skip_policy\n");
+
+module_param(i_only_flag, uint, 0664);
+MODULE_PARM_DESC(i_only_flag, "\n amvdec_h265 i_only_flag\n");
+
+module_param(error_handle_policy, uint, 0664);
+MODULE_PARM_DESC(error_handle_policy, "\n amvdec_h265 error_handle_policy\n");
+
+module_param(error_handle_threshold, uint, 0664);
+MODULE_PARM_DESC(error_handle_threshold,
+ "\n amvdec_h265 error_handle_threshold\n");
+
+module_param(error_handle_nal_skip_threshold, uint, 0664);
+MODULE_PARM_DESC(error_handle_nal_skip_threshold,
+ "\n amvdec_h265 error_handle_nal_skip_threshold\n");
+
+module_param(error_handle_system_threshold, uint, 0664);
+MODULE_PARM_DESC(error_handle_system_threshold,
+ "\n amvdec_h265 error_handle_system_threshold\n");
+
+module_param(error_skip_nal_count, uint, 0664);
+MODULE_PARM_DESC(error_skip_nal_count,
+ "\n amvdec_h265 error_skip_nal_count\n");
+
+module_param(buf_alloc_width, uint, 0664);
+MODULE_PARM_DESC(buf_alloc_width, "\n buf_alloc_width\n");
+
+module_param(buf_alloc_height, uint, 0664);
+MODULE_PARM_DESC(buf_alloc_height, "\n buf_alloc_height\n");
+
+module_param(buf_alloc_size, uint, 0664);
+MODULE_PARM_DESC(buf_alloc_size, "\n buf_alloc_size\n");
+
+module_param(re_config_pic_flag, uint, 0664);
+MODULE_PARM_DESC(re_config_pic_flag, "\n re_config_pic_flag\n");
+
+module_param(buffer_mode, uint, 0664);
+MODULE_PARM_DESC(buffer_mode, "\n buffer_mode\n");
+
+module_param(buffer_mode_dbg, uint, 0664);
+MODULE_PARM_DESC(buffer_mode_dbg, "\n buffer_mode_dbg\n");
+/*USE_BUF_BLOCK*/
+module_param(max_buf_num, uint, 0664);
+MODULE_PARM_DESC(max_buf_num, "\n max_buf_num\n");
+
+module_param(dynamic_buf_num_margin, uint, 0664);
+MODULE_PARM_DESC(dynamic_buf_num_margin, "\n dynamic_buf_num_margin\n");
+/**/
+
+module_param(mem_map_mode, uint, 0664);
+MODULE_PARM_DESC(mem_map_mode, "\n mem_map_mode\n");
+
+#ifdef SUPPORT_10BIT
+module_param(double_write_mode, uint, 0664);
+MODULE_PARM_DESC(double_write_mode, "\n double_write_mode\n");
+
+module_param(enable_mem_saving, uint, 0664);
+MODULE_PARM_DESC(enable_mem_saving, "\n enable_mem_saving\n");
+
+module_param(force_w_h, uint, 0664);
+MODULE_PARM_DESC(force_w_h, "\n force_w_h\n");
+#endif
+
+module_param(force_fps, uint, 0664);
+MODULE_PARM_DESC(force_fps, "\n force_fps\n");
+
+module_param(max_decoding_time, uint, 0664);
+MODULE_PARM_DESC(max_decoding_time, "\n max_decoding_time\n");
+
+module_param(interlace_enable, uint, 0664);
+MODULE_PARM_DESC(interlace_enable, "\n interlace_enable\n");
+module_param(pts_unstable, uint, 0664);
+MODULE_PARM_DESC(pts_unstable, "\n amvdec_h265 pts_unstable\n");
+module_param(parser_sei_enable, uint, 0664);
+MODULE_PARM_DESC(parser_sei_enable, "\n parser_sei_enable\n");
+module_param(mmu_enable, uint, 0664);
+MODULE_PARM_DESC(mmu_enable, "\n mmu_enable\n");
+
+#ifdef MULTI_INSTANCE_SUPPORT
+module_param(start_decode_buf_level, uint, 0664);
+MODULE_PARM_DESC(start_decode_buf_level,
+ "\n ammvdec_h264 start_decode_buf_level\n");
+
+module_param_array(decode_frame_count, uint,
+ &max_decode_instance_num, 0664);
+
+module_param_array(max_process_time, uint,
+ &max_decode_instance_num, 0664);
+
+module_param_array(max_get_frame_interval,
+ uint, &max_decode_instance_num, 0664);
+
+module_param_array(debug_flag, uint, &max_decode_instance_num, 0664);
+#endif
+
+module_init(amvdec_h265_driver_init_module);
+module_exit(amvdec_h265_driver_remove_module);
+
+MODULE_DESCRIPTION("AMLOGIC h265 Video Decoder Driver");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Tim Yao <tim.yao@amlogic.com>");
diff --git a/drivers/frame_provider/decoder/h265/vh265.h b/drivers/frame_provider/decoder/h265/vh265.h
new file mode 100644
index 0000000..8b10541
--- a/dev/null
+++ b/drivers/frame_provider/decoder/h265/vh265.h
@@ -0,0 +1,27 @@
+/*
+ * drivers/amlogic/amports/vh265.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 VH265_H
+#define VH265_H
+
+extern u32 get_blackout_policy(void);
+
+extern s32 vh265_init(void);
+
+extern s32 vh265_release(void);
+
+#endif /* VMPEG4_H */
diff --git a/drivers/frame_provider/decoder/utils/Makefile b/drivers/frame_provider/decoder/utils/Makefile
new file mode 100644
index 0000000..97e47f4
--- a/dev/null
+++ b/drivers/frame_provider/decoder/utils/Makefile
@@ -0,0 +1,2 @@
+obj-m += decoder_common.o
+decoder_common-objs += utils.o vdec.o vdec_input.o amvdec.o decoder_mmu_box.o
diff --git a/drivers/frame_provider/decoder/utils/amvdec.c b/drivers/frame_provider/decoder/utils/amvdec.c
new file mode 100644
index 0000000..f9b8662
--- a/dev/null
+++ b/drivers/frame_provider/decoder/utils/amvdec.c
@@ -0,0 +1,936 @@
+/*
+ * drivers/amlogic/media/frame_provider/decoder/utils/amvdec.c
+ *
+ * Copyright (C) 2016 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.
+ *
+*/
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/platform_device.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/dma-mapping.h>
+#include <linux/amlogic/media/utils/vformat.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/vmalloc.h>
+#include "vdec.h"
+
+#ifdef CONFIG_PM
+#include <linux/pm.h>
+#endif
+
+#ifdef CONFIG_WAKELOCK
+#include <linux/wakelock.h>
+#endif
+#include "../../../stream_input/amports/amports_priv.h"
+
+/* #include <mach/am_regs.h> */
+/* #include <mach/power_gate.h> */
+#include <linux/amlogic/media/utils/vdec_reg.h>
+#include "amvdec.h"
+#include <linux/amlogic/media/utils/amports_config.h>
+#include "../../../common/firmware/firmware.h"
+
+#define MC_SIZE (4096 * 4)
+
+#ifdef CONFIG_WAKELOCK
+static struct wake_lock amvdec_lock;
+struct timer_list amvdevtimer;
+#define WAKE_CHECK_INTERVAL (100*HZ/100)
+#endif
+#define AMVDEC_USE_STATIC_MEMORY
+static void *mc_addr;
+static dma_addr_t mc_addr_map;
+
+#ifdef CONFIG_WAKELOCK
+static int video_running;
+static int video_stated_changed = 1;
+#endif
+
+static void amvdec_pg_enable(bool enable)
+{
+ ulong timeout;
+
+ if (enable) {
+ AMVDEC_CLK_GATE_ON(MDEC_CLK_PIC_DC);
+ AMVDEC_CLK_GATE_ON(MDEC_CLK_DBLK);
+ AMVDEC_CLK_GATE_ON(MC_CLK);
+ AMVDEC_CLK_GATE_ON(IQIDCT_CLK);
+ /* AMVDEC_CLK_GATE_ON(VLD_CLK); */
+ AMVDEC_CLK_GATE_ON(AMRISC);
+ /* #if MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON6TVD */
+ if (get_cpu_type() >= MESON_CPU_MAJOR_ID_M8)
+ WRITE_VREG(GCLK_EN, 0x3ff);
+ /* #endif */
+ CLEAR_VREG_MASK(MDEC_PIC_DC_CTRL, 1 << 31);
+ } else {
+
+ AMVDEC_CLK_GATE_OFF(AMRISC);
+ timeout = jiffies + HZ / 10;
+
+ while (READ_VREG(MDEC_PIC_DC_STATUS) != 0) {
+ if (time_after(jiffies, timeout)) {
+ WRITE_VREG_BITS(MDEC_PIC_DC_CTRL, 1, 0, 1);
+ WRITE_VREG_BITS(MDEC_PIC_DC_CTRL, 0, 0, 1);
+ READ_VREG(MDEC_PIC_DC_STATUS);
+ READ_VREG(MDEC_PIC_DC_STATUS);
+ READ_VREG(MDEC_PIC_DC_STATUS);
+ break;
+ }
+ }
+
+ AMVDEC_CLK_GATE_OFF(MDEC_CLK_PIC_DC);
+ timeout = jiffies + HZ / 10;
+
+ while (READ_VREG(DBLK_STATUS) & 1) {
+ if (time_after(jiffies, timeout)) {
+ WRITE_VREG(DBLK_CTRL, 3);
+ WRITE_VREG(DBLK_CTRL, 0);
+ READ_VREG(DBLK_STATUS);
+ READ_VREG(DBLK_STATUS);
+ READ_VREG(DBLK_STATUS);
+ break;
+ }
+ }
+ AMVDEC_CLK_GATE_OFF(MDEC_CLK_DBLK);
+ timeout = jiffies + HZ / 10;
+
+ while (READ_VREG(MC_STATUS0) & 1) {
+ if (time_after(jiffies, timeout)) {
+ SET_VREG_MASK(MC_CTRL1, 0x9);
+ CLEAR_VREG_MASK(MC_CTRL1, 0x9);
+ READ_VREG(MC_STATUS0);
+ READ_VREG(MC_STATUS0);
+ READ_VREG(MC_STATUS0);
+ break;
+ }
+ }
+ AMVDEC_CLK_GATE_OFF(MC_CLK);
+ timeout = jiffies + HZ / 10;
+ while (READ_VREG(DCAC_DMA_CTRL) & 0x8000) {
+ if (time_after(jiffies, timeout))
+ break;
+ }
+ AMVDEC_CLK_GATE_OFF(IQIDCT_CLK);
+ /* AMVDEC_CLK_GATE_OFF(VLD_CLK); */
+ }
+}
+
+static void amvdec2_pg_enable(bool enable)
+{
+ if (has_vdec2()) {
+ ulong timeout;
+
+ if (!vdec_on(VDEC_2))
+ return;
+ if (enable) {
+ /* WRITE_VREG(VDEC2_GCLK_EN, 0x3ff); */
+ } else {
+ timeout = jiffies + HZ / 10;
+
+ while (READ_VREG(VDEC2_MDEC_PIC_DC_STATUS) != 0) {
+ if (time_after(jiffies, timeout)) {
+ WRITE_VREG_BITS(VDEC2_MDEC_PIC_DC_CTRL,
+ 1, 0, 1);
+ WRITE_VREG_BITS(VDEC2_MDEC_PIC_DC_CTRL,
+ 0, 0, 1);
+ READ_VREG(VDEC2_MDEC_PIC_DC_STATUS);
+ READ_VREG(VDEC2_MDEC_PIC_DC_STATUS);
+ READ_VREG(VDEC2_MDEC_PIC_DC_STATUS);
+ break;
+ }
+ }
+
+ timeout = jiffies + HZ / 10;
+
+ while (READ_VREG(VDEC2_DBLK_STATUS) & 1) {
+ if (time_after(jiffies, timeout)) {
+ WRITE_VREG(VDEC2_DBLK_CTRL, 3);
+ WRITE_VREG(VDEC2_DBLK_CTRL, 0);
+ READ_VREG(VDEC2_DBLK_STATUS);
+ READ_VREG(VDEC2_DBLK_STATUS);
+ READ_VREG(VDEC2_DBLK_STATUS);
+ break;
+ }
+ }
+
+ timeout = jiffies + HZ / 10;
+
+ while (READ_VREG(VDEC2_DCAC_DMA_CTRL) & 0x8000) {
+ if (time_after(jiffies, timeout))
+ break;
+ }
+ }
+ }
+}
+
+static void amhevc_pg_enable(bool enable)
+{
+ if (has_hevc_vdec()) {
+ ulong timeout;
+
+ if (!vdec_on(VDEC_HEVC))
+ return;
+ if (enable) {
+ /* WRITE_VREG(VDEC2_GCLK_EN, 0x3ff); */
+ } else {
+ timeout = jiffies + HZ / 10;
+
+ while (READ_VREG(HEVC_MDEC_PIC_DC_STATUS) != 0) {
+ if (time_after(jiffies, timeout)) {
+ WRITE_VREG_BITS(HEVC_MDEC_PIC_DC_CTRL,
+ 1, 0, 1);
+ WRITE_VREG_BITS(HEVC_MDEC_PIC_DC_CTRL,
+ 0, 0, 1);
+ READ_VREG(HEVC_MDEC_PIC_DC_STATUS);
+ READ_VREG(HEVC_MDEC_PIC_DC_STATUS);
+ READ_VREG(HEVC_MDEC_PIC_DC_STATUS);
+ break;
+ }
+ }
+
+ timeout = jiffies + HZ / 10;
+
+ while (READ_VREG(HEVC_DBLK_STATUS) & 1) {
+ if (time_after(jiffies, timeout)) {
+ WRITE_VREG(HEVC_DBLK_CTRL, 3);
+ WRITE_VREG(HEVC_DBLK_CTRL, 0);
+ READ_VREG(HEVC_DBLK_STATUS);
+ READ_VREG(HEVC_DBLK_STATUS);
+ READ_VREG(HEVC_DBLK_STATUS);
+ break;
+ }
+ }
+
+ timeout = jiffies + HZ / 10;
+
+ while (READ_VREG(HEVC_DCAC_DMA_CTRL) & 0x8000) {
+ if (time_after(jiffies, timeout))
+ break;
+ }
+ }
+ }
+}
+
+#ifdef CONFIG_WAKELOCK
+int amvdec_wake_lock(void)
+{
+ wake_lock(&amvdec_lock);
+ return 0;
+}
+
+int amvdec_wake_unlock(void)
+{
+ wake_unlock(&amvdec_lock);
+ return 0;
+}
+#else
+#define amvdec_wake_lock()
+#define amvdec_wake_unlock()
+#endif
+
+static s32 am_loadmc_ex(enum vformat_e type,
+ const char *name, char *def, s32(*load)(const u32 *))
+{
+ char *mc_addr = vmalloc(4096 * 4);
+ char *pmc_addr = def;
+ int err;
+
+ if (!def && mc_addr) {
+ int loaded;
+
+ loaded = get_decoder_firmware_data(type,
+ name, mc_addr, (4096 * 4));
+ if (loaded > 0)
+ pmc_addr = mc_addr;
+ }
+ if (!pmc_addr) {
+ vfree(mc_addr);
+ return -1;
+ }
+ err = (*load)((u32 *) pmc_addr);
+ if (err < 0) {
+ pr_err("loading firmware %s to vdec ram failed!\n", name);
+ return err;
+ }
+ vfree(mc_addr);
+ pr_debug("loading firmware %s to vdec ram ok!\n", name);
+ return err;
+}
+
+static s32 amvdec_loadmc(const u32 *p)
+{
+ ulong timeout;
+ s32 ret = 0;
+
+#ifdef AMVDEC_USE_STATIC_MEMORY
+ if (mc_addr == NULL) {
+#else
+ {
+#endif
+ mc_addr = kmalloc(MC_SIZE, GFP_KERNEL);
+ }
+
+ if (!mc_addr)
+ return -ENOMEM;
+
+ memcpy(mc_addr, p, MC_SIZE);
+
+ mc_addr_map = dma_map_single(get_vdec_device(),
+ mc_addr, MC_SIZE, DMA_TO_DEVICE);
+
+ WRITE_VREG(MPSR, 0);
+ WRITE_VREG(CPSR, 0);
+
+ /* Read CBUS register for timing */
+ timeout = READ_VREG(MPSR);
+ timeout = READ_VREG(MPSR);
+
+ timeout = jiffies + HZ;
+
+ WRITE_VREG(IMEM_DMA_ADR, mc_addr_map);
+ WRITE_VREG(IMEM_DMA_COUNT, 0x1000);
+ WRITE_VREG(IMEM_DMA_CTRL, (0x8000 | (7 << 16)));
+
+ while (READ_VREG(IMEM_DMA_CTRL) & 0x8000) {
+ if (time_before(jiffies, timeout))
+ schedule();
+ else {
+ pr_err("vdec load mc error\n");
+ ret = -EBUSY;
+ break;
+ }
+ }
+
+ dma_unmap_single(get_vdec_device(),
+ mc_addr_map, MC_SIZE, DMA_TO_DEVICE);
+
+#ifndef AMVDEC_USE_STATIC_MEMORY
+ kfree(mc_addr);
+ mc_addr = NULL;
+#endif
+
+ return ret;
+}
+
+s32 amvdec_loadmc_ex(enum vformat_e type, const char *name, char *def)
+{
+ return am_loadmc_ex(type, name, def, &amvdec_loadmc);
+}
+EXPORT_SYMBOL(amvdec_loadmc_ex);
+
+static s32 amvdec2_loadmc(const u32 *p)
+{
+ if (has_vdec2()) {
+ ulong timeout;
+ s32 ret = 0;
+
+#ifdef AMVDEC_USE_STATIC_MEMORY
+ if (mc_addr == NULL) {
+#else
+ {
+#endif
+ mc_addr = kmalloc(MC_SIZE, GFP_KERNEL);
+ }
+
+ if (!mc_addr)
+ return -ENOMEM;
+
+ memcpy(mc_addr, p, MC_SIZE);
+
+ mc_addr_map = dma_map_single(get_vdec_device(),
+ mc_addr, MC_SIZE, DMA_TO_DEVICE);
+
+ WRITE_VREG(VDEC2_MPSR, 0);
+ WRITE_VREG(VDEC2_CPSR, 0);
+
+ /* Read CBUS register for timing */
+ timeout = READ_VREG(VDEC2_MPSR);
+ timeout = READ_VREG(VDEC2_MPSR);
+
+ timeout = jiffies + HZ;
+
+ WRITE_VREG(VDEC2_IMEM_DMA_ADR, mc_addr_map);
+ WRITE_VREG(VDEC2_IMEM_DMA_COUNT, 0x1000);
+ WRITE_VREG(VDEC2_IMEM_DMA_CTRL, (0x8000 | (7 << 16)));
+
+ while (READ_VREG(VDEC2_IMEM_DMA_CTRL) & 0x8000) {
+ if (time_before(jiffies, timeout))
+ schedule();
+ else {
+ pr_err("vdec2 load mc error\n");
+ ret = -EBUSY;
+ break;
+ }
+ }
+
+ dma_unmap_single(get_vdec_device(),
+ mc_addr_map, MC_SIZE, DMA_TO_DEVICE);
+
+#ifndef AMVDEC_USE_STATIC_MEMORY
+ kfree(mc_addr);
+ mc_addr = NULL;
+#endif
+
+ return ret;
+ } else
+ return 0;
+}
+
+s32 amvdec2_loadmc_ex(enum vformat_e type, const char *name, char *def)
+{
+ if (has_vdec2())
+ return am_loadmc_ex(type, name, def, &amvdec2_loadmc);
+ else
+ return 0;
+}
+EXPORT_SYMBOL(amvdec2_loadmc_ex);
+
+s32 amhcodec_loadmc(const u32 *p)
+{
+#ifdef AMVDEC_USE_STATIC_MEMORY
+ if (mc_addr == NULL) {
+#else
+ {
+#endif
+ mc_addr = kmalloc(MC_SIZE, GFP_KERNEL);
+ }
+
+ if (!mc_addr)
+ return -ENOMEM;
+
+ memcpy(mc_addr, p, MC_SIZE);
+
+ mc_addr_map = dma_map_single(get_vdec_device(),
+ mc_addr, MC_SIZE, DMA_TO_DEVICE);
+
+ WRITE_VREG(HCODEC_IMEM_DMA_ADR, mc_addr_map);
+ WRITE_VREG(HCODEC_IMEM_DMA_COUNT, 0x100);
+ WRITE_VREG(HCODEC_IMEM_DMA_CTRL, (0x8000 | (7 << 16)));
+
+ while (READ_VREG(HCODEC_IMEM_DMA_CTRL) & 0x8000)
+ udelay(1000);
+
+ dma_unmap_single(get_vdec_device(),
+ mc_addr_map, MC_SIZE, DMA_TO_DEVICE);
+
+#ifndef AMVDEC_USE_STATIC_MEMORY
+ kfree(mc_addr);
+#endif
+
+ return 0;
+}
+EXPORT_SYMBOL(amhcodec_loadmc);
+
+s32 amhcodec_loadmc_ex(enum vformat_e type, const char *name, char *def)
+{
+ return am_loadmc_ex(type, name, def, &amhcodec_loadmc);
+}
+EXPORT_SYMBOL(amhcodec_loadmc_ex);
+
+static s32 amhevc_loadmc(const u32 *p)
+{
+ ulong timeout;
+ s32 ret = 0;
+
+ if (has_hevc_vdec()) {
+#ifdef AMVDEC_USE_STATIC_MEMORY
+ if (mc_addr == NULL) {
+#else
+ {
+#endif
+ mc_addr = kmalloc(MC_SIZE, GFP_KERNEL);
+ }
+
+ if (!mc_addr)
+ return -ENOMEM;
+
+ memcpy(mc_addr, p, MC_SIZE);
+
+ mc_addr_map =
+ dma_map_single(get_vdec_device(),
+ mc_addr, MC_SIZE, DMA_TO_DEVICE);
+
+ WRITE_VREG(HEVC_MPSR, 0);
+ WRITE_VREG(HEVC_CPSR, 0);
+
+ /* Read CBUS register for timing */
+ timeout = READ_VREG(HEVC_MPSR);
+ timeout = READ_VREG(HEVC_MPSR);
+
+ timeout = jiffies + HZ;
+
+ WRITE_VREG(HEVC_IMEM_DMA_ADR, mc_addr_map);
+ WRITE_VREG(HEVC_IMEM_DMA_COUNT, 0x1000);
+ WRITE_VREG(HEVC_IMEM_DMA_CTRL, (0x8000 | (7 << 16)));
+
+ while (READ_VREG(HEVC_IMEM_DMA_CTRL) & 0x8000) {
+ if (time_before(jiffies, timeout))
+ schedule();
+ else {
+ pr_err("vdec2 load mc error\n");
+ ret = -EBUSY;
+ break;
+ }
+ }
+
+ dma_unmap_single(get_vdec_device(),
+ mc_addr_map, MC_SIZE, DMA_TO_DEVICE);
+
+#ifndef AMVDEC_USE_STATIC_MEMORY
+ kfree(mc_addr);
+ mc_addr = NULL;
+#endif
+ }
+
+ return ret;
+}
+
+s32 amhevc_loadmc_ex(enum vformat_e type, const char *name, char *def)
+{
+ if (has_hevc_vdec())
+ return am_loadmc_ex(type, name, def, &amhevc_loadmc);
+ else
+ return 0;
+}
+EXPORT_SYMBOL(amhevc_loadmc_ex);
+
+void amvdec_start(void)
+{
+#ifdef CONFIG_WAKELOCK
+ amvdec_wake_lock();
+#endif
+
+ /* #if MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON6 */
+ if (get_cpu_type() >= MESON_CPU_MAJOR_ID_M6) {
+ READ_VREG(DOS_SW_RESET0);
+ READ_VREG(DOS_SW_RESET0);
+ READ_VREG(DOS_SW_RESET0);
+
+ WRITE_VREG(DOS_SW_RESET0, (1 << 12) | (1 << 11));
+ WRITE_VREG(DOS_SW_RESET0, 0);
+
+ READ_VREG(DOS_SW_RESET0);
+ READ_VREG(DOS_SW_RESET0);
+ READ_VREG(DOS_SW_RESET0);
+ } else {
+ /* #else */
+ /* additional cbus dummy register reading for timing control */
+ READ_MPEG_REG(RESET0_REGISTER);
+ READ_MPEG_REG(RESET0_REGISTER);
+ READ_MPEG_REG(RESET0_REGISTER);
+ READ_MPEG_REG(RESET0_REGISTER);
+
+ WRITE_MPEG_REG(RESET0_REGISTER, RESET_VCPU | RESET_CCPU);
+
+ READ_MPEG_REG(RESET0_REGISTER);
+ READ_MPEG_REG(RESET0_REGISTER);
+ READ_MPEG_REG(RESET0_REGISTER);
+ }
+ /* #endif */
+
+ WRITE_VREG(MPSR, 0x0001);
+}
+EXPORT_SYMBOL(amvdec_start);
+
+void amvdec2_start(void)
+{
+ if (has_vdec2()) {
+#ifdef CONFIG_WAKELOCK
+ amvdec_wake_lock();
+#endif
+
+ READ_VREG(DOS_SW_RESET2);
+ READ_VREG(DOS_SW_RESET2);
+ READ_VREG(DOS_SW_RESET2);
+
+ WRITE_VREG(DOS_SW_RESET2, (1 << 12) | (1 << 11));
+ WRITE_VREG(DOS_SW_RESET2, 0);
+
+ READ_VREG(DOS_SW_RESET2);
+ READ_VREG(DOS_SW_RESET2);
+ READ_VREG(DOS_SW_RESET2);
+
+ WRITE_VREG(VDEC2_MPSR, 0x0001);
+ }
+}
+EXPORT_SYMBOL(amvdec2_start);
+
+void amhcodec_start(void)
+{
+ WRITE_VREG(HCODEC_MPSR, 0x0001);
+}
+EXPORT_SYMBOL(amhcodec_start);
+
+void amhevc_start(void)
+{
+
+ if (has_hevc_vdec()) {
+#ifdef CONFIG_WAKELOCK
+ amvdec_wake_lock();
+#endif
+
+ READ_VREG(DOS_SW_RESET3);
+ READ_VREG(DOS_SW_RESET3);
+ READ_VREG(DOS_SW_RESET3);
+
+ WRITE_VREG(DOS_SW_RESET3, (1 << 12) | (1 << 11));
+ WRITE_VREG(DOS_SW_RESET3, 0);
+
+ READ_VREG(DOS_SW_RESET3);
+ READ_VREG(DOS_SW_RESET3);
+ READ_VREG(DOS_SW_RESET3);
+
+ WRITE_VREG(HEVC_MPSR, 0x0001);
+ }
+}
+EXPORT_SYMBOL(amhevc_start);
+
+void amvdec_stop(void)
+{
+ ulong timeout = jiffies + HZ;
+
+ WRITE_VREG(MPSR, 0);
+ WRITE_VREG(CPSR, 0);
+
+ while (READ_VREG(IMEM_DMA_CTRL) & 0x8000) {
+ if (time_after(jiffies, timeout))
+ break;
+ }
+
+ /* #if MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON6 */
+ if (get_cpu_type() >= MESON_CPU_MAJOR_ID_M6) {
+ READ_VREG(DOS_SW_RESET0);
+ READ_VREG(DOS_SW_RESET0);
+ READ_VREG(DOS_SW_RESET0);
+
+ WRITE_VREG(DOS_SW_RESET0, (1 << 12) | (1 << 11));
+ WRITE_VREG(DOS_SW_RESET0, 0);
+
+ READ_VREG(DOS_SW_RESET0);
+ READ_VREG(DOS_SW_RESET0);
+ READ_VREG(DOS_SW_RESET0);
+ } else {
+ /* #else */
+ WRITE_MPEG_REG(RESET0_REGISTER, RESET_VCPU | RESET_CCPU);
+
+ /* additional cbus dummy register reading for timing control */
+ READ_MPEG_REG(RESET0_REGISTER);
+ READ_MPEG_REG(RESET0_REGISTER);
+ READ_MPEG_REG(RESET0_REGISTER);
+ READ_MPEG_REG(RESET0_REGISTER);
+ }
+ /* #endif */
+
+#ifdef CONFIG_WAKELOCK
+ amvdec_wake_unlock();
+#endif
+}
+EXPORT_SYMBOL(amvdec_stop);
+
+void amvdec2_stop(void)
+{
+ if (has_vdec2()) {
+ ulong timeout = jiffies + HZ;
+
+ WRITE_VREG(VDEC2_MPSR, 0);
+ WRITE_VREG(VDEC2_CPSR, 0);
+
+ while (READ_VREG(VDEC2_IMEM_DMA_CTRL) & 0x8000) {
+ if (time_after(jiffies, timeout))
+ break;
+ }
+
+ READ_VREG(DOS_SW_RESET2);
+ READ_VREG(DOS_SW_RESET2);
+ READ_VREG(DOS_SW_RESET2);
+
+#ifdef CONFIG_WAKELOCK
+ amvdec_wake_unlock();
+#endif
+ }
+}
+EXPORT_SYMBOL(amvdec2_stop);
+
+void amhcodec_stop(void)
+{
+ WRITE_VREG(HCODEC_MPSR, 0);
+}
+EXPORT_SYMBOL(amhcodec_stop);
+
+void amhevc_stop(void)
+{
+ if (has_hevc_vdec()) {
+ ulong timeout = jiffies + HZ;
+
+ WRITE_VREG(HEVC_MPSR, 0);
+ WRITE_VREG(HEVC_CPSR, 0);
+
+ while (READ_VREG(HEVC_IMEM_DMA_CTRL) & 0x8000) {
+ if (time_after(jiffies, timeout))
+ break;
+ }
+
+ READ_VREG(DOS_SW_RESET3);
+ READ_VREG(DOS_SW_RESET3);
+ READ_VREG(DOS_SW_RESET3);
+
+#ifdef CONFIG_WAKELOCK
+ amvdec_wake_unlock();
+#endif
+ }
+}
+EXPORT_SYMBOL(amhevc_stop);
+
+void amvdec_enable(void)
+{
+ amvdec_pg_enable(true);
+}
+EXPORT_SYMBOL(amvdec_enable);
+
+void amvdec_disable(void)
+{
+ amvdec_pg_enable(false);
+}
+EXPORT_SYMBOL(amvdec_disable);
+
+void amvdec2_enable(void)
+{
+ if (has_vdec2())
+ amvdec2_pg_enable(true);
+}
+EXPORT_SYMBOL(amvdec2_enable);
+
+void amvdec2_disable(void)
+{
+ if (has_vdec2())
+ amvdec2_pg_enable(false);
+}
+EXPORT_SYMBOL(amvdec2_disable);
+
+void amhevc_enable(void)
+{
+ if (has_hevc_vdec())
+ amhevc_pg_enable(true);
+}
+EXPORT_SYMBOL(amhevc_enable);
+
+void amhevc_disable(void)
+{
+ if (has_hevc_vdec())
+ amhevc_pg_enable(false);
+}
+EXPORT_SYMBOL(amhevc_disable);
+
+#ifdef CONFIG_PM
+int amvdec_suspend(struct platform_device *dev, pm_message_t event)
+{
+ amvdec_pg_enable(false);
+
+ /* #if MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON6TVD */
+ if (has_vdec2())
+ amvdec2_pg_enable(false);
+ /* #endif */
+
+ if (has_hevc_vdec())
+ amhevc_pg_enable(false);
+
+ return 0;
+}
+EXPORT_SYMBOL(amvdec_suspend);
+
+int amvdec_resume(struct platform_device *dev)
+{
+ amvdec_pg_enable(true);
+
+ /* #if MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON6TVD */
+ if (has_vdec2())
+ amvdec2_pg_enable(true);
+ /* #endif */
+
+ /* #if MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON8 */
+ if (has_hevc_vdec())
+ amhevc_pg_enable(true);
+ /* #endif */
+
+ return 0;
+}
+EXPORT_SYMBOL(amvdec_resume);
+
+int amhevc_suspend(struct platform_device *dev, pm_message_t event)
+{
+ if (has_hevc_vdec())
+ amhevc_pg_enable(false);
+ return 0;
+}
+EXPORT_SYMBOL(amhevc_suspend);
+
+int amhevc_resume(struct platform_device *dev)
+{
+ if (has_hevc_vdec())
+ amhevc_pg_enable(true);
+ return 0;
+}
+EXPORT_SYMBOL(amhevc_resume);
+
+
+#endif
+
+#ifdef CONFIG_WAKELOCK
+
+static int vdec_is_paused(void)
+{
+ static unsigned long old_wp = -1, old_rp = -1, old_level = -1;
+ unsigned long wp, rp, level;
+ static int paused_time;
+
+ /* #if MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON8 */
+ if (has_hevc_vdec()) {
+ if ((vdec_on(VDEC_HEVC))
+ && (READ_VREG(HEVC_STREAM_CONTROL) & 1)) {
+ wp = READ_VREG(HEVC_STREAM_WR_PTR);
+ rp = READ_VREG(HEVC_STREAM_RD_PTR);
+ level = READ_VREG(HEVC_STREAM_LEVEL);
+ } else {
+ wp = READ_VREG(VLD_MEM_VIFIFO_WP);
+ rp = READ_VREG(VLD_MEM_VIFIFO_RP);
+ level = READ_VREG(VLD_MEM_VIFIFO_LEVEL);
+ }
+ } else
+ /* #endif */
+ {
+ wp = READ_VREG(VLD_MEM_VIFIFO_WP);
+ rp = READ_VREG(VLD_MEM_VIFIFO_RP);
+ level = READ_VREG(VLD_MEM_VIFIFO_LEVEL);
+ }
+ /*have data,but output buffer is full */
+ if ((rp == old_rp && level > 1024) ||
+ (rp == old_rp && wp == old_wp && level == level)) {
+ /*no write && not read */
+ paused_time++;
+ } else {
+ paused_time = 0;
+ }
+ old_wp = wp; old_rp = rp; old_level = level;
+ if (paused_time > 10)
+ return 1;
+ return 0;
+}
+
+int amvdev_pause(void)
+{
+ video_running = 0;
+ video_stated_changed = 1;
+ return 0;
+}
+EXPORT_SYMBOL(amvdev_pause);
+
+int amvdev_resume(void)
+{
+ video_running = 1;
+ video_stated_changed = 1;
+ return 0;
+}
+EXPORT_SYMBOL(amvdev_resume);
+
+static void vdec_paused_check_timer(unsigned long arg)
+{
+ if (video_stated_changed) {
+ if (!video_running) {
+ if (vdec_is_paused()) {
+ pr_info("vdec paused and release wakelock now\n");
+ amvdec_wake_unlock();
+ video_stated_changed = 0;
+ }
+ } else {
+ amvdec_wake_lock();
+ video_stated_changed = 0;
+ }
+ }
+ mod_timer(&amvdevtimer, jiffies + WAKE_CHECK_INTERVAL);
+}
+#else
+int amvdev_pause(void)
+{
+ return 0;
+}
+
+int amvdev_resume(void)
+{
+ return 0;
+}
+#endif
+
+int amvdec_init(void)
+{
+#ifdef CONFIG_WAKELOCK
+ /*
+ *wake_lock_init(&amvdec_lock, WAKE_LOCK_IDLE, "amvdec_lock");
+ *tmp mark for compile, no "WAKE_LOCK_IDLE" definition in kernel 3.8
+ */
+ wake_lock_init(&amvdec_lock, /*WAKE_LOCK_IDLE */ WAKE_LOCK_SUSPEND,
+ "amvdec_lock");
+
+ init_timer(&amvdevtimer);
+
+ amvdevtimer.data = (ulong) &amvdevtimer;
+ amvdevtimer.function = vdec_paused_check_timer;
+#endif
+ return 0;
+}
+EXPORT_SYMBOL(amvdec_init);
+
+void amvdec_exit(void)
+{
+#ifdef CONFIG_WAKELOCK
+ del_timer_sync(&amvdevtimer);
+#endif
+}
+EXPORT_SYMBOL(amvdec_exit);
+
+#if 0
+int __init amvdec_init(void)
+{
+#ifdef CONFIG_WAKELOCK
+ /*
+ *wake_lock_init(&amvdec_lock, WAKE_LOCK_IDLE, "amvdec_lock");
+ *tmp mark for compile, no "WAKE_LOCK_IDLE" definition in kernel 3.8
+ */
+ wake_lock_init(&amvdec_lock, /*WAKE_LOCK_IDLE */ WAKE_LOCK_SUSPEND,
+ "amvdec_lock");
+
+ init_timer(&amvdevtimer);
+
+ amvdevtimer.data = (ulong) &amvdevtimer;
+ amvdevtimer.function = vdec_paused_check_timer;
+#endif
+ return 0;
+}
+
+static void __exit amvdec_exit(void)
+{
+#ifdef CONFIG_WAKELOCK
+ del_timer_sync(&amvdevtimer);
+#endif
+}
+
+module_init(amvdec_init);
+module_exit(amvdec_exit);
+#endif
+
+MODULE_DESCRIPTION("Amlogic Video Decoder Utility Driver");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Tim Yao <timyao@amlogic.com>");
diff --git a/drivers/frame_provider/decoder/utils/amvdec.h b/drivers/frame_provider/decoder/utils/amvdec.h
new file mode 100644
index 0000000..82aceef
--- a/dev/null
+++ b/drivers/frame_provider/decoder/utils/amvdec.h
@@ -0,0 +1,82 @@
+/*
+ * drivers/amlogic/media/frame_provider/decoder/utils/amvdec.h
+ *
+ * Copyright (C) 2016 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 AMVDEC_H
+#define AMVDEC_H
+#include <linux/amlogic/media/utils/amports_config.h>
+#include <linux/amlogic/media/utils/vformat.h>
+
+#define UCODE_ALIGN 8
+#define UCODE_ALIGN_MASK 7UL
+
+struct amvdec_dec_reg_s {
+ unsigned long mem_start;
+ unsigned long mem_end;
+ struct device *cma_dev;
+ struct dec_sysinfo *dec_sysinfo;
+}; /*amvdec_dec_reg_t */
+
+extern void amvdec_start(void);
+extern void amvdec_stop(void);
+extern void amvdec_enable(void);
+extern void amvdec_disable(void);
+s32 amvdec_loadmc_ex(enum vformat_e type, const char *name, char *def);
+
+extern void amvdec2_start(void);
+extern void amvdec2_stop(void);
+extern void amvdec2_enable(void);
+extern void amvdec2_disable(void);
+s32 amvdec2_loadmc_ex(enum vformat_e type, const char *name, char *def);
+
+extern void amhevc_start(void);
+extern void amhevc_stop(void);
+extern void amhevc_enable(void);
+extern void amhevc_disable(void);
+s32 amhevc_loadmc_ex(enum vformat_e type, const char *name, char *def);
+
+extern void amhcodec_start(void);
+extern void amhcodec_stop(void);
+s32 amhcodec_loadmc(const u32 *p);
+s32 amhcodec_loadmc_ex(enum vformat_e type, const char *name, char *def);
+
+extern int amvdev_pause(void);
+extern int amvdev_resume(void);
+
+#ifdef CONFIG_PM
+extern int amvdec_suspend(struct platform_device *dev, pm_message_t event);
+extern int amvdec_resume(struct platform_device *dec);
+extern int amhevc_suspend(struct platform_device *dev, pm_message_t event);
+extern int amhevc_resume(struct platform_device *dec);
+
+#endif
+
+int amvdec_init(void);
+void amvdec_exit(void);
+
+#if 1 /* MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON6 */
+#define AMVDEC_CLK_GATE_ON(a)
+#define AMVDEC_CLK_GATE_OFF(a)
+#else
+#define AMVDEC_CLK_GATE_ON(a) CLK_GATE_ON(a)
+#define AMVDEC_CLK_GATE_OFF(a) CLK_GATE_OFF(a)
+#endif
+
+/* TODO: move to register headers */
+#define RESET_VCPU (1<<7)
+#define RESET_CCPU (1<<8)
+
+#endif /* AMVDEC_H */
diff --git a/drivers/frame_provider/decoder/utils/decoder_mmu_box.c b/drivers/frame_provider/decoder/utils/decoder_mmu_box.c
new file mode 100644
index 0000000..941829a
--- a/dev/null
+++ b/drivers/frame_provider/decoder/utils/decoder_mmu_box.c
@@ -0,0 +1,373 @@
+/*
+ * drivers/amlogic/media/frame_provider/decoder/utils/decoder_mmu_box.c
+ *
+ * Copyright (C) 2016 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.
+ *
+*/
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/semaphore.h>
+#include <linux/delay.h>
+#include <linux/timer.h>
+#include <linux/kfifo.h>
+#include <linux/kthread.h>
+#include <linux/slab.h>
+#include <linux/amlogic/media/codec_mm/codec_mm_scatter.h>
+#include <linux/platform_device.h>
+struct decoder_mmu_box {
+ int max_sc_num;
+ const char *name;
+ int channel_id;
+ struct mutex mutex;
+ struct list_head list;
+ struct codec_mm_scatter *sc_list[1];
+};
+#define MAX_KEEP_FRAME 4
+#define START_KEEP_ID 0x9
+#define MAX_KEEP_ID (INT_MAX - 1)
+struct decoder_mmu_box_mgr {
+ int num;
+ struct mutex mutex;
+ struct codec_mm_scatter *keep_sc[MAX_KEEP_FRAME];
+ int keep_id[MAX_KEEP_FRAME];
+ int next_id;/*id for keep & free.*/
+ struct list_head box_list;
+};
+static struct decoder_mmu_box_mgr global_mgr;
+static struct decoder_mmu_box_mgr *get_decoder_mmu_box_mgr(void)
+{
+ return &global_mgr;
+}
+
+static int decoder_mmu_box_mgr_add_box(struct decoder_mmu_box *box)
+{
+ struct decoder_mmu_box_mgr *mgr = get_decoder_mmu_box_mgr();
+
+ mutex_lock(&mgr->mutex);
+ list_add_tail(&box->list, &mgr->box_list);
+ mutex_unlock(&mgr->mutex);
+ return 0;
+}
+
+static int decoder_mmu_box_mgr_del_box(struct decoder_mmu_box *box)
+{
+ struct decoder_mmu_box_mgr *mgr = get_decoder_mmu_box_mgr();
+
+ mutex_lock(&mgr->mutex);
+ list_del(&box->list);
+ mutex_unlock(&mgr->mutex);
+ return 0;
+}
+
+
+
+void *decoder_mmu_box_alloc_box(const char *name, int channel_id, int max_num)
+{
+ struct decoder_mmu_box *box;
+ int size;
+
+ size = sizeof(struct decoder_mmu_box) +
+ sizeof(struct codec_mm_scatter *) *
+ max_num;
+ box = kmalloc(size, GFP_KERNEL);
+ if (!box) {
+ pr_err("can't alloc decoder buffers box!!!\n");
+ return NULL;
+ }
+ memset(box, 0, size);
+ box->max_sc_num = max_num;
+ box->name = name;
+ box->channel_id = channel_id;
+ mutex_init(&box->mutex);
+ INIT_LIST_HEAD(&box->list);
+ decoder_mmu_box_mgr_add_box(box);
+ codec_mm_scatter_mgt_delay_free_swith(1, 0);
+ return (void *)box;
+}
+EXPORT_SYMBOL(decoder_mmu_box_alloc_box);
+
+int decoder_mmu_box_alloc_idx(
+ void *handle, int idx, int num_pages,
+ unsigned int *mmu_index_adr)
+{
+ struct decoder_mmu_box *box = handle;
+ struct codec_mm_scatter *sc;
+ int ret;
+ int i;
+
+ if (!box || idx < 0 || idx >= box->max_sc_num) {
+ pr_err("can't alloc mmu box(%p),idx:%d in (%d-%d)\n",
+ box, idx, 0,
+ box->max_sc_num - 1);
+ return -1;
+ }
+ mutex_lock(&box->mutex);
+ sc = box->sc_list[idx];
+ if (sc) {
+ if (sc->page_max_cnt >= num_pages)
+ ret = codec_mm_scatter_alloc_want_pages(sc,
+ num_pages);
+ else {
+ codec_mm_scatter_dec_owner_user(sc, 0);
+ box->sc_list[idx] = NULL;
+ sc = NULL;
+ }
+
+ }
+ if (!sc) {
+ sc = codec_mm_scatter_alloc(num_pages + 64, num_pages);
+ if (!sc) {
+ mutex_unlock(&box->mutex);
+ pr_err("alloc mmu failed, need pages=%d\n",
+ num_pages);
+ return -1;
+ }
+ box->sc_list[idx] = sc;
+ }
+
+ for (i = 0; i < num_pages; i++)
+ mmu_index_adr[i] = PAGE_INDEX(sc->pages_list[i]);
+ mmu_index_adr[num_pages] = 0;
+
+ mutex_unlock(&box->mutex);
+
+ return 0;
+}
+EXPORT_SYMBOL(decoder_mmu_box_alloc_idx);
+
+int decoder_mmu_box_free_idx_tail(
+ void *handle, int idx,
+ int start_release_index)
+{
+ struct decoder_mmu_box *box = handle;
+ struct codec_mm_scatter *sc;
+
+ if (!box || idx < 0 || idx >= box->max_sc_num) {
+ pr_err("can't free tail mmu box(%p),idx:%d in (%d-%d)\n",
+ box, idx, 0,
+ box->max_sc_num - 1);
+ return -1;
+ }
+ mutex_lock(&box->mutex);
+ sc = box->sc_list[idx];
+ if (sc && start_release_index < sc->page_cnt)
+ codec_mm_scatter_free_tail_pages_fast(sc,
+ start_release_index);
+ mutex_unlock(&box->mutex);
+ return 0;
+}
+EXPORT_SYMBOL(decoder_mmu_box_free_idx_tail);
+
+int decoder_mmu_box_free_idx(void *handle, int idx)
+{
+ struct decoder_mmu_box *box = handle;
+ struct codec_mm_scatter *sc;
+
+ if (!box || idx < 0 || idx >= box->max_sc_num) {
+ pr_err("can't free idx of box(%p),idx:%d in (%d-%d)\n",
+ box, idx, 0,
+ box->max_sc_num - 1);
+ return -1;
+ }
+ mutex_lock(&box->mutex);
+ sc = box->sc_list[idx];
+ if (sc && sc->page_cnt > 0) {
+ codec_mm_scatter_dec_owner_user(sc, 0);
+ box->sc_list[idx] = NULL;
+ } mutex_unlock(&box->mutex);
+ return 0;
+}
+EXPORT_SYMBOL(decoder_mmu_box_free_idx);
+
+int decoder_mmu_box_free(void *handle)
+{
+ struct decoder_mmu_box *box = handle;
+ struct codec_mm_scatter *sc;
+ int i;
+
+ if (!box) {
+ pr_err("can't free box of NULL box!\n");
+ return -1;
+ }
+ mutex_lock(&box->mutex);
+ for (i = 0; i < box->max_sc_num; i++) {
+ sc = box->sc_list[i];
+ if (sc) {
+ codec_mm_scatter_dec_owner_user(sc, 200);
+ box->sc_list[i] = NULL;
+ }
+ }
+ mutex_unlock(&box->mutex);
+ decoder_mmu_box_mgr_del_box(box);
+ kfree(box);
+ codec_mm_scatter_mgt_delay_free_swith(0, 2000);
+ return 0;
+}
+EXPORT_SYMBOL(decoder_mmu_box_free);
+
+void *decoder_mmu_box_get_mem_handle(void *box_handle, int idx)
+{
+ struct decoder_mmu_box *box = box_handle;
+
+ if (!box || idx < 0 || idx >= box->max_sc_num)
+ return NULL;
+ return box->sc_list[idx];
+}
+EXPORT_SYMBOL(decoder_mmu_box_get_mem_handle);
+
+static int decoder_mmu_box_dump(struct decoder_mmu_box *box,
+ void *buf, int size)
+{
+ char *pbuf = buf;
+ char sbuf[512];
+ int tsize = 0;
+ int s;
+ int i;
+
+ if (!pbuf)
+ pbuf = sbuf;
+
+ #define BUFPRINT(args...) \
+ do {\
+ s = sprintf(pbuf, args);\
+ tsize += s;\
+ pbuf += s; \
+ } while (0)
+
+ for (i = 0; i < box->max_sc_num; i++) {
+ struct codec_mm_scatter *sc = box->sc_list[i];
+
+ if (sc) {
+ BUFPRINT("sc mem[%d]:%p, size=%d\n",
+ i, sc,
+ sc->page_cnt << PAGE_SHIFT);
+ }
+ }
+#undef BUFPRINT
+ if (!buf)
+ pr_info("%s", sbuf);
+
+ return tsize;
+}
+
+static int decoder_mmu_box_dump_all(void *buf, int size)
+{
+ struct decoder_mmu_box_mgr *mgr = get_decoder_mmu_box_mgr();
+ char *pbuf = buf;
+ char sbuf[512];
+ int tsize = 0;
+ int s;
+ int i;
+ struct list_head *head, *list;
+
+ if (!pbuf)
+ pbuf = sbuf;
+
+ #define BUFPRINT(args...) \
+ do {\
+ s = sprintf(pbuf, args);\
+ tsize += s;\
+ pbuf += s; \
+ } while (0)
+
+ mutex_lock(&mgr->mutex);
+ head = &mgr->box_list;
+ list = head->next;
+ i = 0;
+ while (list != head) {
+ struct decoder_mmu_box *box;
+
+ box = list_entry(list, struct decoder_mmu_box,
+ list);
+ BUFPRINT("box[%d]: %s, player_id:%d, max_num:%d\n",
+ i,
+ box->name,
+ box->channel_id,
+ box->max_sc_num);
+ if (buf) {
+ tsize += decoder_mmu_box_dump(box, pbuf, size - tsize);
+ if (tsize > 0)
+ pbuf += tsize;
+ } else {
+ pr_info("%s", sbuf);
+ pbuf = sbuf;
+ tsize += decoder_mmu_box_dump(box, NULL, 0);
+ }
+ list = list->next;
+ i++;
+ }
+ mutex_unlock(&mgr->mutex);
+
+
+#undef BUFPRINT
+ if (!buf)
+ pr_info("%s", sbuf);
+ return tsize;
+}
+
+
+
+static ssize_t
+box_dump_show(struct class *class,
+ struct class_attribute *attr, char *buf)
+{
+ ssize_t ret = 0;
+
+ ret = decoder_mmu_box_dump_all(buf, PAGE_SIZE);
+ return ret;
+}
+
+
+
+static struct class_attribute decoder_mmu_box_class_attrs[] = {
+ __ATTR_RO(box_dump),
+ __ATTR_NULL
+};
+
+static struct class decoder_mmu_box_class = {
+ .name = "decoder_mmu_box",
+ .class_attrs = decoder_mmu_box_class_attrs,
+};
+
+int decoder_mmu_box_init(void)
+{
+ int r;
+
+ memset(&global_mgr, 0, sizeof(global_mgr));
+ INIT_LIST_HEAD(&global_mgr.box_list);
+ mutex_init(&global_mgr.mutex);
+ global_mgr.next_id = START_KEEP_ID;
+ r = class_register(&decoder_mmu_box_class);
+ return r;
+}
+EXPORT_SYMBOL(decoder_mmu_box_init);
+
+#if 0
+static int __init decoder_mmu_box_init(void)
+{
+ int r;
+
+ memset(&global_mgr, 0, sizeof(global_mgr));
+ INIT_LIST_HEAD(&global_mgr.box_list);
+ mutex_init(&global_mgr.mutex);
+ global_mgr.next_id = START_KEEP_ID;
+ r = class_register(&decoder_mmu_box_class);
+ return r;
+}
+
+module_init(decoder_mmu_box_init);
+#endif
diff --git a/drivers/frame_provider/decoder/utils/decoder_mmu_box.h b/drivers/frame_provider/decoder/utils/decoder_mmu_box.h
new file mode 100644
index 0000000..7b3fb43
--- a/dev/null
+++ b/drivers/frame_provider/decoder/utils/decoder_mmu_box.h
@@ -0,0 +1,38 @@
+/*
+ * drivers/amlogic/media/frame_provider/decoder/utils/decoder_mmu_box.h
+ *
+ * Copyright (C) 2016 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 DECODER_BUFFER_BOX
+#define DECODER_BUFFER_BOX
+void *decoder_mmu_box_alloc_box(const char *name, int player_id, int max_num);
+
+int decoder_mmu_box_alloc_idx(void *handle, int idx, int num_pages,
+ unsigned int *mmu_index_adr);
+
+int decoder_mmu_box_free_idx_tail(void *handle, int idx,
+ int start_release_index);
+
+int decoder_mmu_box_free_idx(void *handle, int idx);
+
+int decoder_mmu_box_free(void *handle);
+
+int decoder_mmu_box_move_keep_idx(void *box_handle, int keep_idx);
+int decoder_mmu_box_free_keep(int keep_id);
+int decoder_mmu_box_free_all_keep(void);
+void *decoder_mmu_box_get_mem_handle(void *box_handle, int idx);
+int decoder_mmu_box_init(void);
+
+#endif
diff --git a/drivers/frame_provider/decoder/utils/utils.c b/drivers/frame_provider/decoder/utils/utils.c
new file mode 100644
index 0000000..3455330
--- a/dev/null
+++ b/drivers/frame_provider/decoder/utils/utils.c
@@ -0,0 +1,61 @@
+/*
+ * drivers/amlogic/media/frame_provider/decoder/utils/utils.c
+ *
+ * Copyright (C) 2016 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.
+ *
+*/
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/kthread.h>
+#include <linux/platform_device.h>
+#include <linux/uaccess.h>
+#include <linux/semaphore.h>
+#include <linux/sched/rt.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+
+#include "vdec.h"
+#include "vdec_input.h"
+#include <linux/amlogic/media/utils/vdec_reg.h>
+#include "amvdec.h"
+#include "decoder_mmu_box.h"
+
+static int __init decoder_common_init(void)
+{
+ /*vdec init.*/
+ vdec_module_init();
+
+ /*amvdec init.*/
+ amvdec_init();
+
+ /*mmu box init.*/
+ decoder_mmu_box_init();/*exit?*/
+
+ return 0;
+}
+
+static void __exit decoder_common_exit(void)
+{
+ /*vdec exit.*/
+ vdec_module_exit();
+
+ /*amvdec exit.*/
+ amvdec_exit();
+}
+
+module_init(decoder_common_init);
+module_exit(decoder_common_exit);
diff --git a/drivers/frame_provider/decoder/utils/vdec.c b/drivers/frame_provider/decoder/utils/vdec.c
new file mode 100644
index 0000000..8088965
--- a/dev/null
+++ b/drivers/frame_provider/decoder/utils/vdec.c
@@ -0,0 +1,2688 @@
+/*
+ * drivers/amlogic/media/frame_provider/decoder/utils/vdec.c
+ *
+ * Copyright (C) 2016 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.
+ *
+*/
+
+#include <linux/kernel.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/kthread.h>
+#include <linux/platform_device.h>
+#include <linux/uaccess.h>
+#include <linux/semaphore.h>
+#include <linux/sched/rt.h>
+#include <linux/interrupt.h>
+#include <linux/amlogic/media/utils/vformat.h>
+#include <linux/amlogic/iomap.h>
+#include <linux/amlogic/media/canvas/canvas.h>
+#include <linux/amlogic/media/vfm/vframe.h>
+#include <linux/amlogic/media/vfm/vframe_provider.h>
+#include <linux/amlogic/media/vfm/vframe_receiver.h>
+#include <linux/amlogic/media/video_sink/video/ionvideo_ext.h>
+#include <linux/amlogic/media/vfm/vfm_ext.h>
+
+#include <linux/amlogic/media/utils/vdec_reg.h>
+#include "vdec.h"
+#include <linux/of.h>
+#include <linux/of_fdt.h>
+#include <linux/libfdt_env.h>
+#include <linux/of_reserved_mem.h>
+#include <linux/dma-contiguous.h>
+#include <linux/cma.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/dma-mapping.h>
+#include <linux/dma-contiguous.h>
+#include "../../../stream_input/amports/amports_priv.h"
+
+#include <linux/amlogic/media/utils/amports_config.h>
+#include "../utils/amvdec.h"
+/*#include "../vp9/vvp9.h"*//*mask*/
+#include "vdec_input.h"
+
+#include "../../../common/media_clock/clk/clk.h"
+#include <linux/reset.h>
+#include <linux/amlogic/media/old_cpu_version.h>
+#include <linux/amlogic/media/codec_mm/codec_mm.h>
+
+static DEFINE_MUTEX(vdec_mutex);
+
+#define MC_SIZE (4096 * 4)
+#define CMA_ALLOC_SIZE SZ_64M
+#define MEM_NAME "vdec_prealloc"
+static int inited_vcodec_num;
+static int poweron_clock_level;
+static int keep_vdec_mem;
+static unsigned int debug_trace_num = 16 * 20;
+static unsigned int clk_config;
+
+static struct page *vdec_cma_page;
+int vdec_mem_alloced_from_codec, delay_release;
+static unsigned long reserved_mem_start, reserved_mem_end;
+static int hevc_max_reset_count;
+static DEFINE_SPINLOCK(vdec_spin_lock);
+
+#define HEVC_TEST_LIMIT 100
+#define GXBB_REV_A_MINOR 0xA
+
+struct am_reg {
+ char *name;
+ int offset;
+};
+
+struct vdec_isr_context_s {
+ int index;
+ int irq;
+ irq_handler_t dev_isr;
+ irq_handler_t dev_threaded_isr;
+ void *dev_id;
+};
+
+struct vdec_core_s {
+ struct list_head connected_vdec_list;
+ spinlock_t lock;
+
+ atomic_t vdec_nr;
+ struct vdec_s *vfm_vdec;
+ struct vdec_s *active_vdec;
+ struct platform_device *vdec_core_platform_device;
+ struct device *cma_dev;
+ unsigned long mem_start;
+ unsigned long mem_end;
+
+ struct semaphore sem;
+ struct task_struct *thread;
+
+ struct vdec_isr_context_s isr_context[VDEC_IRQ_MAX];
+};
+
+static struct vdec_core_s *vdec_core;
+
+unsigned long vdec_core_lock(struct vdec_core_s *core)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&core->lock, flags);
+
+ return flags;
+}
+
+void vdec_core_unlock(struct vdec_core_s *core, unsigned long flags)
+{
+ spin_unlock_irqrestore(&core->lock, flags);
+}
+
+static int get_canvas(unsigned int index, unsigned int base)
+{
+ int start;
+ int canvas_index = index * base;
+
+ if ((base > 4) || (base == 0))
+ return -1;
+
+ if ((AMVDEC_CANVAS_START_INDEX + canvas_index + base - 1)
+ <= AMVDEC_CANVAS_MAX1) {
+ start = AMVDEC_CANVAS_START_INDEX + base * index;
+ } else {
+ canvas_index -= (AMVDEC_CANVAS_MAX1 -
+ AMVDEC_CANVAS_START_INDEX + 1) / base * base;
+ if (canvas_index <= AMVDEC_CANVAS_MAX2)
+ start = canvas_index / base;
+ else
+ return -1;
+ }
+
+ if (base == 1) {
+ return start;
+ } else if (base == 2) {
+ return ((start + 1) << 16) | ((start + 1) << 8) | start;
+ } else if (base == 3) {
+ return ((start + 2) << 16) | ((start + 1) << 8) | start;
+ } else if (base == 4) {
+ return (((start + 3) << 24) | (start + 2) << 16) |
+ ((start + 1) << 8) | start;
+ }
+
+ return -1;
+}
+
+
+int vdec_status(struct vdec_s *vdec, struct vdec_status *vstatus)
+{
+ if (vdec->dec_status)
+ return vdec->dec_status(vdec, vstatus);
+
+ return -1;
+}
+EXPORT_SYMBOL(vdec_status);
+
+int vdec_set_trickmode(struct vdec_s *vdec, unsigned long trickmode)
+{
+ if (vdec->set_trickmode)
+ return vdec->set_trickmode(vdec, trickmode);
+
+ return -1;
+}
+EXPORT_SYMBOL(vdec_set_trickmode);
+
+/*
+* clk_config:
+ *0:default
+ *1:no gp0_pll;
+ *2:always used gp0_pll;
+ *>=10:fixed n M clk;
+ *== 100 , 100M clks;
+*/
+unsigned int get_vdec_clk_config_settings(void)
+{
+ return clk_config;
+}
+void update_vdec_clk_config_settings(unsigned int config)
+{
+ clk_config = config;
+}
+EXPORT_SYMBOL(update_vdec_clk_config_settings);
+
+static bool hevc_workaround_needed(void)
+{
+ return (get_cpu_type() == MESON_CPU_MAJOR_ID_GXBB) &&
+ (get_meson_cpu_version(MESON_CPU_VERSION_LVL_MINOR)
+ == GXBB_REV_A_MINOR);
+}
+
+struct device *get_codec_cma_device(void)
+{
+ return vdec_core->cma_dev;
+}
+
+static unsigned int get_mmu_mode(void)
+{
+ return 1;/*mask temp*/
+}
+
+#ifdef CONFIG_MULTI_DEC
+static const char * const vdec_device_name[] = {
+ "amvdec_mpeg12", "ammvdec_mpeg12",
+ "amvdec_mpeg4", "ammvdec_mpeg4",
+ "amvdec_h264", "ammvdec_h264",
+ "amvdec_mjpeg", "ammvdec_mjpeg",
+ "amvdec_real", "ammvdec_real",
+ "amjpegdec", "ammjpegdec",
+ "amvdec_vc1", "ammvdec_vc1",
+ "amvdec_avs", "ammvdec_avs",
+ "amvdec_yuv", "ammvdec_yuv",
+ "amvdec_h264mvc", "ammvdec_h264mvc",
+ "amvdec_h264_4k2k", "ammvdec_h264_4k2k",
+ "amvdec_h265", "ammvdec_h265",
+ "amvenc_avc", "amvenc_avc",
+ "jpegenc", "jpegenc",
+ "amvdec_vp9", "amvdec_vp9"
+};
+
+static int vdec_default_buf_size[] = {
+ 32, 32, /*"amvdec_mpeg12",*/
+ 32, 32, /*"amvdec_mpeg4",*/
+ 48, 0, /*"amvdec_h264",*/
+ 32, 32, /*"amvdec_mjpeg",*/
+ 32, 32, /*"amvdec_real",*/
+ 32, 32, /*"amjpegdec",*/
+ 32, 32, /*"amvdec_vc1",*/
+ 32, 32, /*"amvdec_avs",*/
+ 32, 32, /*"amvdec_yuv",*/
+ 64, 64, /*"amvdec_h264mvc",*/
+ 64, 64, /*"amvdec_h264_4k2k", else alloc on decoder*/
+ 48, 48, /*"amvdec_h265", else alloc on decoder*/
+ 0, 0, /* avs encoder */
+ 0, 0, /* jpg encoder */
+#ifdef VP9_10B_MMU
+ 24, 24, /*"amvdec_vp9", else alloc on decoder*/
+#else
+ 32, 32,
+#endif
+ 0
+};
+
+#else
+
+static const char * const vdec_device_name[] = {
+ "amvdec_mpeg12",
+ "amvdec_mpeg4",
+ "amvdec_h264",
+ "amvdec_mjpeg",
+ "amvdec_real",
+ "amjpegdec",
+ "amvdec_vc1",
+ "amvdec_avs",
+ "amvdec_yuv",
+ "amvdec_h264mvc",
+ "amvdec_h264_4k2k",
+ "amvdec_h265",
+ "amvenc_avc",
+ "jpegenc",
+ "amvdec_vp9"
+};
+
+static int vdec_default_buf_size[] = {
+ 32, /*"amvdec_mpeg12",*/
+ 32, /*"amvdec_mpeg4",*/
+ 48, /*"amvdec_h264",*/
+ 32, /*"amvdec_mjpeg",*/
+ 32, /*"amvdec_real",*/
+ 32, /*"amjpegdec",*/
+ 32, /*"amvdec_vc1",*/
+ 32, /*"amvdec_avs",*/
+ 32, /*"amvdec_yuv",*/
+ 64, /*"amvdec_h264mvc",*/
+ 64, /*"amvdec_h264_4k2k", else alloc on decoder*/
+ 48, /*"amvdec_h265", else alloc on decoder*/
+ 0, /* avs encoder */
+ 0, /* jpg encoder */
+#ifdef VP9_10B_MMU
+ 24, /*"amvdec_vp9", else alloc on decoder*/
+#else
+ 32,
+#endif
+};
+#endif
+
+int vdec_set_decinfo(struct vdec_s *vdec, struct dec_sysinfo *p)
+{
+ if (copy_from_user((void *)&vdec->sys_info_store, (void *)p,
+ sizeof(struct dec_sysinfo)))
+ return -EFAULT;
+
+ return 0;
+}
+EXPORT_SYMBOL(vdec_set_decinfo);
+
+/* construct vdec strcture */
+struct vdec_s *vdec_create(int type)
+{
+ struct vdec_s *vdec;
+
+ vdec = kzalloc(sizeof(struct vdec_s), GFP_KERNEL);
+
+ /* TBD */
+ if (vdec) {
+ vdec->magic = 0x43454456;
+ vdec->id = 0;
+ vdec->type = type;
+ vdec->sys_info = &vdec->sys_info_store;
+
+ INIT_LIST_HEAD(&vdec->list);
+
+ vdec_input_init(&vdec->input, vdec);
+
+ atomic_inc(&vdec_core->vdec_nr);
+ }
+
+ pr_info("vdec_create instance %p, total %d\n", vdec,
+ atomic_read(&vdec_core->vdec_nr));
+
+ return vdec;
+}
+EXPORT_SYMBOL(vdec_create);
+
+int vdec_set_format(struct vdec_s *vdec, int format)
+{
+ vdec->format = format;
+ return 0;
+}
+EXPORT_SYMBOL(vdec_set_format);
+
+int vdec_set_pts(struct vdec_s *vdec, u32 pts)
+{
+ vdec->pts = pts;
+ vdec->pts_valid = true;
+ return 0;
+}
+EXPORT_SYMBOL(vdec_set_pts);
+
+int vdec_set_pts64(struct vdec_s *vdec, u64 pts64)
+{
+ vdec->pts64 = pts64;
+ vdec->pts_valid = true;
+ return 0;
+}
+EXPORT_SYMBOL(vdec_set_pts64);
+
+void vdec_set_status(struct vdec_s *vdec, int status)
+{
+ vdec->status = status;
+}
+EXPORT_SYMBOL(vdec_set_status);
+
+void vdec_set_next_status(struct vdec_s *vdec, int status)
+{
+ vdec->next_status = status;
+}
+EXPORT_SYMBOL(vdec_set_next_status);
+
+/* add frame data to input chain */
+int vdec_write_vframe(struct vdec_s *vdec, const char *buf, size_t count)
+{
+ return vdec_input_add_frame(&vdec->input, buf, count);
+}
+EXPORT_SYMBOL(vdec_write_vframe);
+
+/*
+*get next frame from input chain
+*/
+/*
+*THE VLD_FIFO is 512 bytes and Video buffer level
+ * empty interrupt is set to 0x80 bytes threshold
+ */
+#define VLD_PADDING_SIZE 1024
+#define HEVC_PADDING_SIZE (1024*16)
+#define FIFO_ALIGN 8
+int vdec_prepare_input(struct vdec_s *vdec, struct vframe_chunk_s **p)
+{
+ struct vdec_input_s *input = &vdec->input;
+ struct vframe_chunk_s *chunk = NULL;
+ struct vframe_block_list_s *block = NULL;
+ int dummy;
+
+ /* full reset to HW input */
+ if (input->target == VDEC_INPUT_TARGET_VLD) {
+ WRITE_VREG(VLD_MEM_VIFIFO_CONTROL, 0);
+
+ /* reset VLD fifo for all vdec */
+ WRITE_VREG(DOS_SW_RESET0, (1<<5) | (1<<4) | (1<<3));
+ WRITE_VREG(DOS_SW_RESET0, 0);
+
+ dummy = READ_MPEG_REG(RESET0_REGISTER);
+ WRITE_VREG(POWER_CTL_VLD, 1 << 4);
+ } else if (input->target == VDEC_INPUT_TARGET_HEVC) {
+ WRITE_VREG(HEVC_STREAM_CONTROL, 0);
+
+ WRITE_VREG(DOS_SW_RESET3,
+ (1<<3)|(1<<4)|(1<<8)|(1<<11)|(1<<12)|(1<<14)|(1<<15)|
+ (1<<17)|(1<<18)|(1<<19));
+ WRITE_VREG(DOS_SW_RESET3, 0);
+ }
+
+ /*
+ *setup HW decoder input buffer (VLD context)
+ * based on input->type and input->target
+ */
+ if (input_frame_based(input)) {
+ chunk = vdec_input_next_chunk(&vdec->input);
+
+ if (chunk == NULL) {
+ *p = NULL;
+ return -1;
+ }
+
+ block = chunk->block;
+
+ if (input->target == VDEC_INPUT_TARGET_VLD) {
+ WRITE_VREG(VLD_MEM_VIFIFO_START_PTR, block->start);
+ WRITE_VREG(VLD_MEM_VIFIFO_END_PTR, block->start +
+ block->size - 8);
+ WRITE_VREG(VLD_MEM_VIFIFO_CURR_PTR,
+ round_down(block->start + chunk->offset,
+ FIFO_ALIGN));
+
+ WRITE_VREG(VLD_MEM_VIFIFO_CONTROL, 1);
+ WRITE_VREG(VLD_MEM_VIFIFO_CONTROL, 0);
+
+ /* set to manual mode */
+ WRITE_VREG(VLD_MEM_VIFIFO_BUF_CNTL, 2);
+ WRITE_VREG(VLD_MEM_VIFIFO_RP,
+ round_down(block->start + chunk->offset,
+ FIFO_ALIGN));
+ dummy = chunk->offset + chunk->size +
+ VLD_PADDING_SIZE;
+ if (dummy >= block->size)
+ dummy -= block->size;
+ WRITE_VREG(VLD_MEM_VIFIFO_WP,
+ round_down(block->start + dummy, FIFO_ALIGN));
+
+ WRITE_VREG(VLD_MEM_VIFIFO_BUF_CNTL, 3);
+ WRITE_VREG(VLD_MEM_VIFIFO_BUF_CNTL, 2);
+
+ WRITE_VREG(VLD_MEM_VIFIFO_CONTROL,
+ (0x11 << 16) | (1<<10) | (7<<3));
+
+ } else if (input->target == VDEC_INPUT_TARGET_HEVC) {
+ WRITE_VREG(HEVC_STREAM_START_ADDR, block->start);
+ WRITE_VREG(HEVC_STREAM_END_ADDR, block->start +
+ block->size);
+ WRITE_VREG(HEVC_STREAM_RD_PTR, block->start +
+ chunk->offset);
+ dummy = chunk->offset + chunk->size +
+ HEVC_PADDING_SIZE;
+ if (dummy >= block->size)
+ dummy -= block->size;
+ WRITE_VREG(HEVC_STREAM_WR_PTR,
+ round_down(block->start + dummy, FIFO_ALIGN));
+ }
+
+ *p = chunk;
+ return chunk->size;
+
+ } else {
+ u32 rp = 0, wp = 0, fifo_len = 0;
+ int size;
+ /* stream based */
+ if (input->swap_valid) {
+ if (input->target == VDEC_INPUT_TARGET_VLD) {
+ WRITE_VREG(VLD_MEM_VIFIFO_CONTROL, 0);
+
+ /* restore read side */
+ WRITE_VREG(VLD_MEM_SWAP_ADDR,
+ page_to_phys(input->swap_page));
+ WRITE_VREG(VLD_MEM_SWAP_CTL, 1);
+
+ while (READ_VREG(VLD_MEM_SWAP_CTL) & (1<<7))
+ ;
+ WRITE_VREG(VLD_MEM_SWAP_CTL, 0);
+
+ /* restore wrap count */
+ WRITE_VREG(VLD_MEM_VIFIFO_WRAP_COUNT,
+ input->stream_cookie);
+
+ rp = READ_VREG(VLD_MEM_VIFIFO_RP);
+ fifo_len = READ_VREG(VLD_MEM_VIFIFO_LEVEL);
+
+ /* enable */
+ WRITE_VREG(VLD_MEM_VIFIFO_CONTROL,
+ (0x11 << 16) | (1<<10));
+
+ /* update write side */
+ WRITE_VREG(VLD_MEM_VIFIFO_WP,
+ READ_MPEG_REG(PARSER_VIDEO_WP));
+
+ wp = READ_VREG(VLD_MEM_VIFIFO_WP);
+ } else if (input->target == VDEC_INPUT_TARGET_HEVC) {
+ /* restore read side */
+ WRITE_VREG(HEVC_STREAM_SWAP_ADDR,
+ page_to_phys(input->swap_page));
+ WRITE_VREG(HEVC_STREAM_SWAP_CTRL, 1);
+
+ while (READ_VREG(HEVC_STREAM_SWAP_CTRL)
+ & (1<<7))
+ ;
+ WRITE_VREG(HEVC_STREAM_SWAP_CTRL, 0);
+
+ /* restore stream offset */
+ WRITE_VREG(HEVC_SHIFT_BYTE_COUNT,
+ input->stream_cookie);
+
+ rp = READ_VREG(HEVC_STREAM_RD_PTR);
+ fifo_len = (READ_VREG(HEVC_STREAM_FIFO_CTL)
+ >> 16) & 0x7f;
+
+
+ /* enable */
+
+ /* update write side */
+ WRITE_VREG(HEVC_STREAM_WR_PTR,
+ READ_MPEG_REG(PARSER_VIDEO_WP));
+
+ wp = READ_VREG(HEVC_STREAM_WR_PTR);
+ }
+
+ } else {
+ if (input->target == VDEC_INPUT_TARGET_VLD) {
+ WRITE_VREG(VLD_MEM_VIFIFO_START_PTR,
+ input->start);
+ WRITE_VREG(VLD_MEM_VIFIFO_END_PTR,
+ input->start + input->size - 8);
+ WRITE_VREG(VLD_MEM_VIFIFO_CURR_PTR,
+ input->start);
+
+ WRITE_VREG(VLD_MEM_VIFIFO_CONTROL, 1);
+ WRITE_VREG(VLD_MEM_VIFIFO_CONTROL, 0);
+
+ /* set to manual mode */
+ WRITE_VREG(VLD_MEM_VIFIFO_BUF_CNTL, 2);
+ WRITE_VREG(VLD_MEM_VIFIFO_RP, input->start);
+ WRITE_VREG(VLD_MEM_VIFIFO_WP,
+ READ_MPEG_REG(PARSER_VIDEO_WP));
+
+ rp = READ_VREG(VLD_MEM_VIFIFO_RP);
+
+ /* enable */
+ WRITE_VREG(VLD_MEM_VIFIFO_CONTROL,
+ (0x11 << 16) | (1<<10));
+
+ wp = READ_VREG(VLD_MEM_VIFIFO_WP);
+
+ } else if (input->target == VDEC_INPUT_TARGET_HEVC) {
+ WRITE_VREG(HEVC_STREAM_START_ADDR,
+ input->start);
+ WRITE_VREG(HEVC_STREAM_END_ADDR,
+ input->start + input->size);
+ WRITE_VREG(HEVC_STREAM_RD_PTR,
+ input->start);
+ WRITE_VREG(HEVC_STREAM_WR_PTR,
+ READ_MPEG_REG(PARSER_VIDEO_WP));
+
+ rp = READ_VREG(HEVC_STREAM_RD_PTR);
+ wp = READ_VREG(HEVC_STREAM_WR_PTR);
+ fifo_len = (READ_VREG(HEVC_STREAM_FIFO_CTL)
+ >> 16) & 0x7f;
+
+ /* enable */
+ }
+ }
+ *p = NULL;
+ if (wp >= rp)
+ size = wp - rp + fifo_len;
+ else
+ size = wp + input->size - rp + fifo_len;
+ if (size < 0) {
+ pr_info("%s error: input->size %x wp %x rp %x fifo_len %x => size %x\r\n",
+ __func__, input->size, wp, rp, fifo_len, size);
+ size = 0;
+ }
+ return size;
+ }
+}
+EXPORT_SYMBOL(vdec_prepare_input);
+
+void vdec_enable_input(struct vdec_s *vdec)
+{
+ struct vdec_input_s *input = &vdec->input;
+
+ if (vdec->status != VDEC_STATUS_ACTIVE)
+ return;
+
+ if (input->target == VDEC_INPUT_TARGET_VLD)
+ SET_VREG_MASK(VLD_MEM_VIFIFO_CONTROL, (1<<2) | (1<<1));
+ else if (input->target == VDEC_INPUT_TARGET_HEVC) {
+ SET_VREG_MASK(HEVC_STREAM_CONTROL, 1);
+ SET_VREG_MASK(HEVC_STREAM_FIFO_CTL, (1<<29));
+ }
+}
+EXPORT_SYMBOL(vdec_enable_input);
+
+void vdec_vframe_dirty(struct vdec_s *vdec, struct vframe_chunk_s *chunk)
+{
+ if (chunk)
+ chunk->flag |= VFRAME_CHUNK_FLAG_CONSUMED;
+
+ if (vdec_stream_based(vdec)) {
+ vdec->input.swap_needed = true;
+ if (vdec->input.target == VDEC_INPUT_TARGET_VLD) {
+ WRITE_MPEG_REG(PARSER_VIDEO_RP,
+ READ_VREG(VLD_MEM_VIFIFO_RP));
+ WRITE_VREG(VLD_MEM_VIFIFO_WP,
+ READ_MPEG_REG(PARSER_VIDEO_WP));
+ } else if (vdec->input.target == VDEC_INPUT_TARGET_HEVC) {
+ WRITE_MPEG_REG(PARSER_VIDEO_RP,
+ READ_VREG(HEVC_STREAM_RD_PTR));
+ WRITE_VREG(HEVC_STREAM_WR_PTR,
+ READ_MPEG_REG(PARSER_VIDEO_WP));
+ }
+ }
+}
+EXPORT_SYMBOL(vdec_vframe_dirty);
+
+void vdec_save_input_context(struct vdec_s *vdec)
+{
+ struct vdec_input_s *input = &vdec->input;
+
+ if (input->target == VDEC_INPUT_TARGET_VLD)
+ WRITE_VREG(VLD_MEM_VIFIFO_CONTROL, 1<<15);
+ else if (input->target == VDEC_INPUT_TARGET_HEVC)
+ WRITE_VREG(HEVC_STREAM_CONTROL, 0);
+
+ if (input_stream_based(input) && (input->swap_needed)) {
+ if (input->target == VDEC_INPUT_TARGET_VLD) {
+ WRITE_VREG(VLD_MEM_SWAP_ADDR,
+ page_to_phys(input->swap_page));
+ WRITE_VREG(VLD_MEM_SWAP_CTL, 3);
+ while (READ_VREG(VLD_MEM_SWAP_CTL) & (1<<7))
+ ;
+ WRITE_VREG(VLD_MEM_SWAP_CTL, 0);
+ vdec->input.stream_cookie =
+ READ_VREG(VLD_MEM_VIFIFO_WRAP_COUNT);
+ } else if (input->target == VDEC_INPUT_TARGET_HEVC) {
+ WRITE_VREG(HEVC_STREAM_SWAP_ADDR,
+ page_to_phys(input->swap_page));
+ WRITE_VREG(HEVC_STREAM_SWAP_CTRL, 3);
+
+ while (READ_VREG(HEVC_STREAM_SWAP_CTRL) & (1<<7))
+ ;
+ WRITE_VREG(HEVC_STREAM_SWAP_CTRL, 0);
+
+ vdec->input.stream_cookie =
+ READ_VREG(HEVC_SHIFT_BYTE_COUNT);
+ }
+
+ input->swap_valid = true;
+
+ WRITE_MPEG_REG(PARSER_VIDEO_RP, READ_VREG(VLD_MEM_VIFIFO_RP));
+ }
+}
+EXPORT_SYMBOL(vdec_save_input_context);
+
+void vdec_clean_input(struct vdec_s *vdec)
+{
+ struct vdec_input_s *input = &vdec->input;
+
+ while (!list_empty(&input->vframe_chunk_list)) {
+ struct vframe_chunk_s *chunk =
+ vdec_input_next_chunk(input);
+ if (chunk->flag & VFRAME_CHUNK_FLAG_CONSUMED)
+ vdec_input_release_chunk(input, chunk);
+ else
+ break;
+ }
+ vdec_save_input_context(vdec);
+}
+EXPORT_SYMBOL(vdec_clean_input);
+
+static const char *vdec_status_str(struct vdec_s *vdec)
+{
+ switch (vdec->status) {
+ case VDEC_STATUS_UNINITIALIZED:
+ return "VDEC_STATUS_UNINITIALIZED";
+ case VDEC_STATUS_DISCONNECTED:
+ return "VDEC_STATUS_DISCONNECTED";
+ case VDEC_STATUS_CONNECTED:
+ return "VDEC_STATUS_CONNECTED";
+ case VDEC_STATUS_ACTIVE:
+ return "VDEC_STATUS_ACTIVE";
+ default:
+ return "invalid status";
+ }
+}
+
+static const char *vdec_type_str(struct vdec_s *vdec)
+{
+ switch (vdec->type) {
+ case VDEC_TYPE_SINGLE:
+ return "VDEC_TYPE_SINGLE";
+ case VDEC_TYPE_STREAM_PARSER:
+ return "VDEC_TYPE_STREAM_PARSER";
+ case VDEC_TYPE_FRAME_BLOCK:
+ return "VDEC_TYPE_FRAME_BLOCK";
+ case VDEC_TYPE_FRAME_CIRCULAR:
+ return "VDEC_TYPE_FRAME_CIRCULAR";
+ default:
+ return "VDEC_TYPE_INVALID";
+ }
+}
+
+void walk_vdec_core_list(char *s)
+{
+ struct vdec_s *vdec;
+ struct vdec_core_s *core = vdec_core;
+ unsigned long flags;
+
+ pr_info("%s --->\n", s);
+
+ flags = vdec_core_lock(vdec_core);
+
+ if (list_empty(&core->connected_vdec_list)) {
+ pr_info("connected vdec list empty\n");
+ } else {
+ list_for_each_entry(vdec, &core->connected_vdec_list, list) {
+ pr_info("\tvdec (%p), status = %s\n", vdec,
+ vdec_status_str(vdec));
+ }
+ }
+
+ vdec_core_unlock(vdec_core, flags);
+}
+EXPORT_SYMBOL(walk_vdec_core_list);
+
+/* insert vdec to vdec_core for scheduling */
+int vdec_connect(struct vdec_s *vdec)
+{
+ unsigned long flags;
+
+ if (vdec->status != VDEC_STATUS_DISCONNECTED)
+ return 0;
+
+ vdec_set_status(vdec, VDEC_STATUS_CONNECTED);
+ vdec_set_next_status(vdec, VDEC_STATUS_CONNECTED);
+
+ init_completion(&vdec->inactive_done);
+
+ flags = vdec_core_lock(vdec_core);
+
+ list_add_tail(&vdec->list, &vdec_core->connected_vdec_list);
+
+ vdec_core_unlock(vdec_core, flags);
+
+ up(&vdec_core->sem);
+
+ return 0;
+}
+EXPORT_SYMBOL(vdec_connect);
+
+/* remove vdec from vdec_core scheduling */
+int vdec_disconnect(struct vdec_s *vdec)
+{
+ if ((vdec->status != VDEC_STATUS_CONNECTED) &&
+ (vdec->status != VDEC_STATUS_ACTIVE)) {
+ return 0;
+ }
+
+ /*
+ *when a vdec is under the management of scheduler
+ * the status change will only be from vdec_core_thread
+ */
+ vdec_set_next_status(vdec, VDEC_STATUS_DISCONNECTED);
+
+ up(&vdec_core->sem);
+
+ wait_for_completion(&vdec->inactive_done);
+
+ return 0;
+}
+EXPORT_SYMBOL(vdec_disconnect);
+
+/* release vdec structure */
+int vdec_destroy(struct vdec_s *vdec)
+{
+ vdec_input_release(&vdec->input);
+
+ kfree(vdec);
+
+ atomic_dec(&vdec_core->vdec_nr);
+
+ return 0;
+}
+EXPORT_SYMBOL(vdec_destroy);
+
+/*
+ * Only support time sliced decoding for frame based input,
+ * so legacy decoder can exist with time sliced decoder.
+ */
+static const char *get_dev_name(bool use_legacy_vdec, int format)
+{
+#ifdef CONFIG_MULTI_DEC
+ if (use_legacy_vdec)
+ return vdec_device_name[format * 2];
+ else
+ return vdec_device_name[format * 2 + 1];
+#else
+ return vdec_device_name[format];
+#endif
+}
+
+/*
+*register vdec_device
+ * create output, vfm or create ionvideo output
+ */
+s32 vdec_init(struct vdec_s *vdec, int is_4k)
+{
+ int r = 0;
+ struct vdec_s *p = vdec;
+ int retry_num = 0;
+ int more_buffers = 0;
+ const char *dev_name;
+
+ if (is_4k && vdec->format < VFORMAT_H264) {
+ /*
+ *old decoder don't support 4k
+ * but size is bigger;
+ * clear 4k flag, and used more buffers;
+ */
+ more_buffers = 1;
+ is_4k = 0;
+ }
+
+ dev_name = get_dev_name((vdec->type == VDEC_TYPE_SINGLE),
+ vdec->format);
+
+ if (dev_name == NULL)
+ return -ENODEV;
+
+ pr_info("vdec_init, dev_name:%s, vdec_type=%s\n",
+ dev_name, vdec_type_str(vdec));
+
+ /*
+ *todo: VFM patch control should be configurable,
+ * for now all stream based input uses default VFM path.
+ */
+ if (vdec_stream_based(vdec)) {
+ if (vdec_core->vfm_vdec == NULL) {
+ pr_info("vdec_init set vfm decoder %p\n", vdec);
+ vdec_core->vfm_vdec = vdec;
+ } else {
+ pr_info("vdec_init vfm path busy.\n");
+ return -EBUSY;
+ }
+ }
+
+ if (vdec->format == VFORMAT_H264_4K2K ||
+ (vdec->format == VFORMAT_HEVC && is_4k)) {
+ //try_free_keep_video(0);/*mask*/
+ }
+
+ /*
+ *when blackout_policy was set, vdec would not free cma buffer, if
+ * current vformat require larger buffer size than current
+ * buf size, reallocated it
+ */
+ if (vdec_core->mem_start != vdec_core->mem_end &&
+ vdec_core->mem_end - vdec_core->mem_start + 1 <
+ vdec_default_buf_size[vdec->format] * SZ_1M) {
+#ifdef CONFIG_MULTI_DEC
+ pr_info("current vdec size %ld, vformat %d need size %d\n",
+ vdec_core->mem_end - vdec_core->mem_start,
+ vdec->format,
+ vdec_default_buf_size[vdec->format * 2] * SZ_1M);
+#else
+ pr_info("current vdec size %ld, vformat %d need size %d\n",
+ vdec_core->mem_end - vdec_core->mem_start,
+ vdec->format,
+ vdec_default_buf_size[vdec->format] * SZ_1M);
+#endif
+ //try_free_keep_video(0);/*mask*/
+ vdec_free_cmabuf();
+ }
+
+ mutex_lock(&vdec_mutex);
+ inited_vcodec_num++;
+ mutex_unlock(&vdec_mutex);
+
+ vdec_input_set_type(&vdec->input, vdec->type,
+ (vdec->format == VFORMAT_HEVC ||
+ vdec->format == VFORMAT_VP9) ?
+ VDEC_INPUT_TARGET_HEVC :
+ VDEC_INPUT_TARGET_VLD);
+
+ p->cma_dev = vdec_core->cma_dev;
+ p->get_canvas = get_canvas;
+ /* todo */
+ p->use_vfm_path = vdec_stream_based(vdec);
+
+ if (p->use_vfm_path) {
+ pr_info("vdec_dev_reg.mem[0x%lx -- 0x%lx]\n",
+ vdec_core->mem_start,
+ vdec_core->mem_end);
+ p->mem_start = vdec_core->mem_start;
+ p->mem_end = vdec_core->mem_end;
+ }
+
+ /* allocate base memory for decoder instance */
+ while (p->mem_start == p->mem_end) {
+ int alloc_size;
+
+#ifdef CONFIG_MULTI_DEC
+ if (p->use_vfm_path)
+ alloc_size =
+ vdec_default_buf_size[vdec->format * 2]
+ * SZ_1M;
+ else
+ alloc_size =
+ vdec_default_buf_size[vdec->format * 2 + 1]
+ * SZ_1M;
+#else
+ alloc_size = vdec_default_buf_size[vdec->format] * SZ_1M;
+#endif
+ if (alloc_size == 0)
+ break;/*alloc end*/
+ if (is_4k) {
+ /*used 264 4k's setting for 265.*/
+#ifdef CONFIG_MULTI_DEC
+ int m4k_size =
+ vdec_default_buf_size[VFORMAT_H264_4K2K * 2] *
+ SZ_1M;
+#else
+ int m4k_size =
+ vdec_default_buf_size[VFORMAT_H264_4K2K] *
+ SZ_1M;
+#endif
+ if (get_cpu_type() >= MESON_CPU_MAJOR_ID_GXTVBB)
+ m4k_size = 32 * SZ_1M;
+ if ((m4k_size > 0) && (m4k_size < 200 * SZ_1M))
+ alloc_size = m4k_size;
+
+#ifdef VP9_10B_MMU
+ if ((vdec->format == VFORMAT_VP9) &&
+ (get_cpu_type() >= MESON_CPU_MAJOR_ID_GXL)) {
+#ifdef CONFIG_MULTI_DEC
+ if (p->use_vfm_path)
+ alloc_size =
+ vdec_default_buf_size[VFORMAT_VP9 * 2]
+ * SZ_1M;
+ else
+ alloc_size =
+ vdec_default_buf_size[VFORMAT_VP9
+ * 2 + 1] * SZ_1M;
+
+#else
+ alloc_size =
+ vdec_default_buf_size[VFORMAT_VP9] * SZ_1M;
+#endif
+ }
+#endif
+ } else if (more_buffers) {
+ alloc_size = alloc_size + 16 * SZ_1M;
+ }
+
+ if ((vdec->format == VFORMAT_HEVC)
+ && get_mmu_mode()
+ && (get_cpu_type() >= MESON_CPU_MAJOR_ID_GXL)) {
+#ifdef CONFIG_MULTI_DEC
+ if (p->use_vfm_path)
+ alloc_size = 33 * SZ_1M;
+ else
+ alloc_size = 33 * SZ_1M;
+#else
+ alloc_size = 33 * SZ_1M;
+#endif
+ }
+
+ p->mem_start = codec_mm_alloc_for_dma(MEM_NAME,
+ alloc_size / PAGE_SIZE, 4 + PAGE_SHIFT,
+ CODEC_MM_FLAGS_CMA_CLEAR | CODEC_MM_FLAGS_CPU |
+ CODEC_MM_FLAGS_FOR_VDECODER);
+ if (!p->mem_start) {
+ if (retry_num < 1) {
+ pr_err("vdec base CMA allocation failed,try again\\n");
+ retry_num++;
+ //try_free_keep_video(0);/*mask*/
+ continue;/*retry alloc*/
+ }
+ pr_err("vdec base CMA allocation failed.\n");
+
+ mutex_lock(&vdec_mutex);
+ inited_vcodec_num--;
+ mutex_unlock(&vdec_mutex);
+
+ return -ENOMEM;
+ }
+
+ p->mem_end = p->mem_start + alloc_size - 1;
+ pr_info("vdec base memory alloced [%p -- %p]\n",
+ (void *)p->mem_start,
+ (void *)p->mem_end);
+
+ break;/*alloc end*/
+ }
+
+ if (p->use_vfm_path) {
+ vdec_core->mem_start = p->mem_start;
+ vdec_core->mem_end = p->mem_end;
+ vdec_mem_alloced_from_codec = 1;
+ }
+
+/*alloc end:*/
+ /* vdec_dev_reg.flag = 0; */
+
+ p->dev =
+ platform_device_register_data(
+ &vdec_core->vdec_core_platform_device->dev,
+ dev_name,
+ PLATFORM_DEVID_AUTO,
+ &p, sizeof(struct vdec_s *));
+
+ if (IS_ERR(p->dev)) {
+ r = PTR_ERR(p->dev);
+ pr_err("vdec: Decoder device %s register failed (%d)\n",
+ dev_name, r);
+
+ mutex_lock(&vdec_mutex);
+ inited_vcodec_num--;
+ mutex_unlock(&vdec_mutex);
+
+ goto error;
+ }
+
+ if ((p->type == VDEC_TYPE_FRAME_BLOCK) && (p->run == NULL)) {
+ r = -ENODEV;
+ pr_err("vdec: Decoder device not handled (%s)\n", dev_name);
+
+ mutex_lock(&vdec_mutex);
+ inited_vcodec_num--;
+ mutex_unlock(&vdec_mutex);
+
+ goto error;
+ }
+#if 0
+ if (p->use_vfm_path) {
+ vdec->vf_receiver_inst = -1;
+ } else {
+ /*
+ *create IONVIDEO instance and connect decoder's
+ * vf_provider interface to it
+ */
+ if (p->type != VDEC_TYPE_FRAME_BLOCK) {
+ r = -ENODEV;
+ pr_err("vdec: Incorrect decoder type\n");
+
+ mutex_lock(&vdec_mutex);
+ inited_vcodec_num--;
+ mutex_unlock(&vdec_mutex);
+
+ goto error;
+ }
+#if 1
+ r = ionvideo_alloc_map(&vdec->vf_receiver_name,
+ &vdec->vf_receiver_inst);
+#else
+ /*
+ * temporarily just use decoder instance ID as iondriver ID
+ * to solve OMX iondriver instance number check time sequence
+ * only the limitation is we can NOT mix different video
+ * decoders since same ID will be used for different decoder
+ * formats.
+ */
+ vdec->vf_receiver_inst = p->dev->id;
+ r = ionvideo_assign_map(&vdec->vf_receiver_name,
+ &vdec->vf_receiver_inst);
+#endif
+ if (r < 0) {
+ pr_err("IonVideo frame receiver allocation failed.\n");
+
+ mutex_lock(&vdec_mutex);
+ inited_vcodec_num--;
+ mutex_unlock(&vdec_mutex);
+
+ goto error;
+ }
+
+ snprintf(vdec->vfm_map_chain, VDEC_MAP_NAME_SIZE,
+ "%s %s", vdec->vf_provider_name,
+ vdec->vf_receiver_name);
+ snprintf(vdec->vfm_map_id, VDEC_MAP_NAME_SIZE,
+ "%s-%s", vdec->vf_provider_name,
+ vdec->vf_receiver_name);
+
+ if (vfm_map_add(vdec->vfm_map_id, vdec->vfm_map_chain) < 0) {
+ r = -ENOMEM;
+ pr_err("Decoder pipeline map creation failed %s.\n",
+ vdec->vfm_map_id);
+ vdec->vfm_map_id[0] = 0;
+
+ mutex_lock(&vdec_mutex);
+ inited_vcodec_num--;
+ mutex_unlock(&vdec_mutex);
+
+ goto error;
+ }
+
+ pr_info("vfm map %s created\n", vdec->vfm_map_id);
+
+ /*
+ *assume IONVIDEO driver already have a few vframe_receiver
+ * registered.
+ * 1. Call iondriver function to allocate a IONVIDEO path and
+ * provide receiver's name and receiver op.
+ * 2. Get decoder driver's provider name from driver instance
+ * 3. vfm_map_add(name, "<decoder provider name>
+ * <iondriver receiver name>"), e.g.
+ * vfm_map_add("vdec_ion_map_0", "mpeg4_0 iondriver_1");
+ * 4. vf_reg_provider and vf_reg_receiver
+ * Note: the decoder provider's op uses vdec as op_arg
+ * the iondriver receiver's op uses iondev device as
+ * op_arg
+ */
+
+ }
+
+#endif/*mask*/
+ if (vdec->type != VDEC_TYPE_SINGLE) {
+ vf_reg_provider(&p->vframe_provider);
+
+ vf_notify_receiver(p->vf_provider_name,
+ VFRAME_EVENT_PROVIDER_START,
+ vdec);
+ }
+
+ pr_info("vdec_init, vf_provider_name = %s\n", p->vf_provider_name);
+
+ /* vdec is now ready to be active */
+ vdec_set_status(vdec, VDEC_STATUS_DISCONNECTED);
+
+ return 0;
+
+error:
+ return r;
+}
+EXPORT_SYMBOL(vdec_init);
+
+void vdec_release(struct vdec_s *vdec)
+{
+ vdec_disconnect(vdec);
+
+ if (vdec->vframe_provider.name)
+ vf_unreg_provider(&vdec->vframe_provider);
+
+ if (vdec_core->vfm_vdec == vdec)
+ vdec_core->vfm_vdec = NULL;
+#if 0
+ if (vdec->vf_receiver_inst >= 0) {
+ if (vdec->vfm_map_id[0]) {
+ vfm_map_remove(vdec->vfm_map_id);
+ vdec->vfm_map_id[0] = 0;
+ }
+
+ /*
+ *vf_receiver_inst should be > 0 since 0 is
+ * for either un-initialized vdec or a ionvideo
+ * instance reserved for legacy path.
+ */
+ ionvideo_release_map(vdec->vf_receiver_inst);
+ }
+#endif/*mask*/
+ platform_device_unregister(vdec->dev);
+
+ if (!vdec->use_vfm_path) {
+ if (vdec->mem_start) {
+ codec_mm_free_for_dma(MEM_NAME, vdec->mem_start);
+ vdec->mem_start = 0;
+ vdec->mem_end = 0;
+ }
+ } else if (delay_release-- <= 0 &&
+ !keep_vdec_mem &&
+ vdec_mem_alloced_from_codec &&
+ vdec_core->mem_start /*&&
+ get_blackout_policy()*//*mask*/) {
+ codec_mm_free_for_dma(MEM_NAME, vdec_core->mem_start);
+ vdec_cma_page = NULL;
+ vdec_core->mem_start = reserved_mem_start;
+ vdec_core->mem_end = reserved_mem_end;
+ }
+
+ vdec_destroy(vdec);
+
+ mutex_lock(&vdec_mutex);
+ inited_vcodec_num--;
+ mutex_unlock(&vdec_mutex);
+}
+EXPORT_SYMBOL(vdec_release);
+
+int vdec_reset(struct vdec_s *vdec)
+{
+ vdec_disconnect(vdec);
+
+ if (vdec->vframe_provider.name)
+ vf_unreg_provider(&vdec->vframe_provider);
+
+ if (vdec->reset)
+ vdec->reset(vdec);
+
+ vdec_input_release(&vdec->input);
+
+ vf_reg_provider(&vdec->vframe_provider);
+ vf_notify_receiver(vdec->vf_provider_name,
+ VFRAME_EVENT_PROVIDER_START, vdec);
+
+ vdec_connect(vdec);
+
+ return 0;
+}
+EXPORT_SYMBOL(vdec_reset);
+
+void vdec_free_cmabuf(void)
+{
+ mutex_lock(&vdec_mutex);
+
+ if (inited_vcodec_num > 0) {
+ mutex_unlock(&vdec_mutex);
+ return;
+ }
+
+ if (vdec_mem_alloced_from_codec && vdec_core->mem_start) {
+ codec_mm_free_for_dma(MEM_NAME, vdec_core->mem_start);
+ vdec_cma_page = NULL;
+ vdec_core->mem_start = reserved_mem_start;
+ vdec_core->mem_end = reserved_mem_end;
+ pr_info("force free vdec memory\n");
+ }
+
+ mutex_unlock(&vdec_mutex);
+}
+EXPORT_SYMBOL(vdec_free_cmabuf);
+
+static struct vdec_s *active_vdec(struct vdec_core_s *core)
+{
+ struct vdec_s *vdec;
+ struct list_head *p;
+
+ list_for_each(p, &core->connected_vdec_list) {
+ vdec = list_entry(p, struct vdec_s, list);
+ if (vdec->status == VDEC_STATUS_ACTIVE)
+ return vdec;
+ }
+
+ return NULL;
+}
+
+/*
+*Decoder callback
+ * Each decoder instance uses this callback to notify status change, e.g. when
+ * decoder finished using HW resource.
+ * a sample callback from decoder's driver is following:
+ *
+ * if (hw->vdec_cb) {
+ * vdec_set_next_status(vdec, VDEC_STATUS_CONNECTED);
+ * hw->vdec_cb(vdec, hw->vdec_cb_arg);
+ * }
+ */
+static void vdec_callback(struct vdec_s *vdec, void *data)
+{
+ struct vdec_core_s *core = (struct vdec_core_s *)data;
+
+ up(&core->sem);
+}
+
+static irqreturn_t vdec_isr(int irq, void *dev_id)
+{
+ struct vdec_isr_context_s *c =
+ (struct vdec_isr_context_s *)dev_id;
+ struct vdec_s *vdec = vdec_core->active_vdec;
+
+ if (c->dev_isr)
+ return c->dev_isr(irq, c->dev_id);
+
+ if (c != &vdec_core->isr_context[VDEC_IRQ_1]) {
+#if 0
+ pr_warn("vdec interrupt w/o a valid receiver\n");
+#endif
+ return IRQ_HANDLED;
+ }
+
+ if (!vdec) {
+#if 0
+ pr_warn("vdec interrupt w/o an active instance running. core = %p\n",
+ core);
+#endif
+ return IRQ_HANDLED;
+ }
+
+ if (!vdec->irq_handler) {
+#if 0
+ pr_warn("vdec instance has no irq handle.\n");
+#endif
+ return IRQ_HANDLED;
+ }
+
+ return vdec->irq_handler(vdec);
+}
+
+static irqreturn_t vdec_thread_isr(int irq, void *dev_id)
+{
+ struct vdec_isr_context_s *c =
+ (struct vdec_isr_context_s *)dev_id;
+ struct vdec_s *vdec = vdec_core->active_vdec;
+
+ if (c->dev_threaded_isr)
+ return c->dev_threaded_isr(irq, c->dev_id);
+
+ if (!vdec)
+ return IRQ_HANDLED;
+
+ if (!vdec->threaded_irq_handler)
+ return IRQ_HANDLED;
+
+ return vdec->threaded_irq_handler(vdec);
+}
+
+static inline bool vdec_ready_to_run(struct vdec_s *vdec)
+{
+ if (vdec->status != VDEC_STATUS_CONNECTED)
+ return false;
+
+ if (!vdec->run_ready)
+ return false;
+
+ return vdec->run_ready(vdec);
+}
+
+/*
+*struct vdec_core_shread manages all decoder instance in active list. When
+ * a vdec is added into the active list, it can onlt be in two status:
+ * VDEC_STATUS_CONNECTED(the decoder does not own HW resource and ready to run)
+ * VDEC_STATUS_ACTIVE(the decoder owns HW resources and is running).
+ * Removing a decoder from active list is only performed within core thread.
+ * Adding a decoder into active list is performed from user thread.
+ */
+static int vdec_core_thread(void *data)
+{
+ unsigned long flags;
+ struct vdec_core_s *core = (struct vdec_core_s *)data;
+
+ struct sched_param param = {.sched_priority = MAX_RT_PRIO - 1};
+
+ sched_setscheduler(current, SCHED_FIFO, &param);
+
+ allow_signal(SIGTERM);
+
+ while (down_interruptible(&core->sem) == 0) {
+ struct vdec_s *vdec, *tmp;
+ LIST_HEAD(disconnecting_list);
+
+ if (kthread_should_stop())
+ break;
+
+ /* clean up previous active vdec's input */
+ if ((core->active_vdec) &&
+ (core->active_vdec->status == VDEC_STATUS_CONNECTED)) {
+ struct vdec_input_s *input = &core->active_vdec->input;
+
+ while (!list_empty(&input->vframe_chunk_list)) {
+ struct vframe_chunk_s *chunk =
+ vdec_input_next_chunk(input);
+ if (chunk->flag & VFRAME_CHUNK_FLAG_CONSUMED)
+ vdec_input_release_chunk(input, chunk);
+ else
+ break;
+ }
+
+ vdec_save_input_context(core->active_vdec);
+ }
+
+ /*
+ *todo:
+ * this is the case when the decoder is in active mode and
+ * the system side wants to stop it. Currently we rely on
+ * the decoder instance to go back to VDEC_STATUS_CONNECTED
+ * from VDEC_STATUS_ACTIVE by its own. However, if for some
+ * reason the decoder can not exist by itself (dead decoding
+ * or whatever), then we may have to add another vdec API
+ * to kill the vdec and release its HW resource and make it
+ * become inactive again.
+ * if ((core->active_vdec) &&
+ * (core->active_vdec->status == VDEC_STATUS_DISCONNECTED)) {
+ * }
+ */
+
+ flags = vdec_core_lock(core);
+
+ /* check disconnected decoders */
+ list_for_each_entry_safe(vdec, tmp,
+ &core->connected_vdec_list, list) {
+ if ((vdec->status == VDEC_STATUS_CONNECTED) &&
+ (vdec->next_status == VDEC_STATUS_DISCONNECTED)) {
+ if (core->active_vdec == vdec)
+ core->active_vdec = NULL;
+ list_move(&vdec->list, &disconnecting_list);
+ }
+ }
+
+ /* activate next decoder instance if there is none */
+ vdec = active_vdec(core);
+
+ if (!vdec) {
+ /*
+ *round-robin decoder scheduling
+ * start from the decoder after previous active
+ * decoder instance, if not, then start from beginning
+ */
+ if (core->active_vdec)
+ vdec = list_entry(
+ core->active_vdec->list.next,
+ struct vdec_s, list);
+ else
+ vdec = list_entry(
+ core->connected_vdec_list.next,
+ struct vdec_s, list);
+
+ list_for_each_entry_from(vdec,
+ &core->connected_vdec_list, list) {
+ if (vdec_ready_to_run(vdec))
+ break;
+ }
+
+ if ((&vdec->list == &core->connected_vdec_list) &&
+ (core->active_vdec)) {
+ /* search from beginning */
+ list_for_each_entry(vdec,
+ &core->connected_vdec_list, list) {
+ if (vdec_ready_to_run(vdec))
+ break;
+
+ if (vdec == core->active_vdec) {
+ vdec = NULL;
+ break;
+ }
+ }
+ }
+
+ if (&vdec->list == &core->connected_vdec_list)
+ vdec = NULL;
+
+ core->active_vdec = NULL;
+ }
+
+ vdec_core_unlock(core, flags);
+
+ /* start the vdec instance */
+ if ((vdec) && (vdec->status != VDEC_STATUS_ACTIVE)) {
+ vdec_set_status(vdec, VDEC_STATUS_ACTIVE);
+
+ /* activatate the decoder instance to run */
+ core->active_vdec = vdec;
+ vdec->run(vdec, vdec_callback, core);
+ }
+
+ /* remove disconnected decoder from active list */
+ list_for_each_entry_safe(vdec, tmp, &disconnecting_list, list) {
+ list_del(&vdec->list);
+ vdec_set_status(vdec, VDEC_STATUS_DISCONNECTED);
+ complete(&vdec->inactive_done);
+ }
+
+ if (!core->active_vdec) {
+ msleep(20);
+ up(&core->sem);
+ }
+ }
+
+ return 0;
+}
+
+#if 1 /* MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON8 */
+static bool test_hevc(u32 decomp_addr, u32 us_delay)
+{
+ int i;
+
+ /* SW_RESET IPP */
+ WRITE_VREG(HEVCD_IPP_TOP_CNTL, 1);
+ WRITE_VREG(HEVCD_IPP_TOP_CNTL, 0);
+
+ /* initialize all canvas table */
+ WRITE_VREG(HEVCD_MPP_ANC2AXI_TBL_CONF_ADDR, 0);
+ for (i = 0; i < 32; i++)
+ WRITE_VREG(HEVCD_MPP_ANC2AXI_TBL_CMD_ADDR,
+ 0x1 | (i << 8) | decomp_addr);
+ WRITE_VREG(HEVCD_MPP_ANC2AXI_TBL_CONF_ADDR, 1);
+ WRITE_VREG(HEVCD_MPP_ANC_CANVAS_ACCCONFIG_ADDR, (0 << 8) | (0<<1) | 1);
+ for (i = 0; i < 32; i++)
+ WRITE_VREG(HEVCD_MPP_ANC_CANVAS_DATA_ADDR, 0);
+
+ /* Initialize mcrcc */
+ WRITE_VREG(HEVCD_MCRCC_CTL1, 0x2);
+ WRITE_VREG(HEVCD_MCRCC_CTL2, 0x0);
+ WRITE_VREG(HEVCD_MCRCC_CTL3, 0x0);
+ WRITE_VREG(HEVCD_MCRCC_CTL1, 0xff0);
+
+ /* Decomp initialize */
+ WRITE_VREG(HEVCD_MPP_DECOMP_CTL1, 0x0);
+ WRITE_VREG(HEVCD_MPP_DECOMP_CTL2, 0x0);
+
+ /* Frame level initialization */
+ WRITE_VREG(HEVCD_IPP_TOP_FRMCONFIG, 0x100 | (0x100 << 16));
+ WRITE_VREG(HEVCD_IPP_TOP_TILECONFIG3, 0x0);
+ WRITE_VREG(HEVCD_IPP_TOP_LCUCONFIG, 0x1 << 5);
+ WRITE_VREG(HEVCD_IPP_BITDEPTH_CONFIG, 0x2 | (0x2 << 2));
+
+ WRITE_VREG(HEVCD_IPP_CONFIG, 0x0);
+ WRITE_VREG(HEVCD_IPP_LINEBUFF_BASE, 0x0);
+
+ /* Enable SWIMP mode */
+ WRITE_VREG(HEVCD_IPP_SWMPREDIF_CONFIG, 0x1);
+
+ /* Enable frame */
+ WRITE_VREG(HEVCD_IPP_TOP_CNTL, 0x2);
+ WRITE_VREG(HEVCD_IPP_TOP_FRMCTL, 0x1);
+
+ /* Send SW-command CTB info */
+ WRITE_VREG(HEVCD_IPP_SWMPREDIF_CTBINFO, 0x1 << 31);
+
+ /* Send PU_command */
+ WRITE_VREG(HEVCD_IPP_SWMPREDIF_PUINFO0, (0x4 << 9) | (0x4 << 16));
+ WRITE_VREG(HEVCD_IPP_SWMPREDIF_PUINFO1, 0x1 << 3);
+ WRITE_VREG(HEVCD_IPP_SWMPREDIF_PUINFO2, 0x0);
+ WRITE_VREG(HEVCD_IPP_SWMPREDIF_PUINFO3, 0x0);
+
+ udelay(us_delay);
+
+ WRITE_VREG(HEVCD_IPP_DBG_SEL, 0x2 << 4);
+
+ return (READ_VREG(HEVCD_IPP_DBG_DATA) & 3) == 1;
+}
+
+void vdec_poweron(enum vdec_type_e core)
+{
+ void *decomp_addr = NULL;
+ dma_addr_t decomp_dma_addr;
+ u32 decomp_addr_aligned = 0;
+ int hevc_loop = 0;
+
+ mutex_lock(&vdec_mutex);
+
+ if (vdec_on(core)) {
+ mutex_unlock(&vdec_mutex);
+ return;
+ }
+
+ if (hevc_workaround_needed() &&
+ (core == VDEC_HEVC)) {
+ decomp_addr = codec_mm_dma_alloc_coherent(MEM_NAME,
+ SZ_64K + SZ_4K, &decomp_dma_addr, GFP_KERNEL, 0);
+
+ if (decomp_addr) {
+ decomp_addr_aligned = ALIGN(decomp_dma_addr, SZ_64K);
+ memset((u8 *)decomp_addr +
+ (decomp_addr_aligned - decomp_dma_addr),
+ 0xff, SZ_4K);
+ } else
+ pr_err("vdec: alloc HEVC gxbb decomp buffer failed.\n");
+ }
+
+ if (core == VDEC_1) {
+ /* vdec1 power on */
+ WRITE_AOREG(AO_RTI_GEN_PWR_SLEEP0,
+ READ_AOREG(AO_RTI_GEN_PWR_SLEEP0) & ~0xc);
+ /* wait 10uS */
+ udelay(10);
+ /* vdec1 soft reset */
+ WRITE_VREG(DOS_SW_RESET0, 0xfffffffc);
+ WRITE_VREG(DOS_SW_RESET0, 0);
+ /* enable vdec1 clock */
+ /*
+ *add power on vdec clock level setting,only for m8 chip,
+ * m8baby and m8m2 can dynamic adjust vdec clock,
+ * power on with default clock level
+ */
+ vdec_clock_hi_enable();
+ /* power up vdec memories */
+ WRITE_VREG(DOS_MEM_PD_VDEC, 0);
+ /* remove vdec1 isolation */
+ WRITE_AOREG(AO_RTI_GEN_PWR_ISO0,
+ READ_AOREG(AO_RTI_GEN_PWR_ISO0) & ~0xC0);
+ /* reset DOS top registers */
+ WRITE_VREG(DOS_VDEC_MCRCC_STALL_CTRL, 0);
+ if (get_cpu_type() >=
+ MESON_CPU_MAJOR_ID_GXBB) {
+ /*
+ *enable VDEC_1 DMC request
+ */
+ unsigned long flags;
+
+ spin_lock_irqsave(&vdec_spin_lock, flags);
+ codec_dmcbus_write(DMC_REQ_CTRL,
+ codec_dmcbus_read(DMC_REQ_CTRL) | (1 << 13));
+ spin_unlock_irqrestore(&vdec_spin_lock, flags);
+ }
+ } else if (core == VDEC_2) {
+ if (has_vdec2()) {
+ /* vdec2 power on */
+ WRITE_AOREG(AO_RTI_GEN_PWR_SLEEP0,
+ READ_AOREG(AO_RTI_GEN_PWR_SLEEP0) &
+ ~0x30);
+ /* wait 10uS */
+ udelay(10);
+ /* vdec2 soft reset */
+ WRITE_VREG(DOS_SW_RESET2, 0xffffffff);
+ WRITE_VREG(DOS_SW_RESET2, 0);
+ /* enable vdec1 clock */
+ vdec2_clock_hi_enable();
+ /* power up vdec memories */
+ WRITE_VREG(DOS_MEM_PD_VDEC2, 0);
+ /* remove vdec2 isolation */
+ WRITE_AOREG(AO_RTI_GEN_PWR_ISO0,
+ READ_AOREG(AO_RTI_GEN_PWR_ISO0) &
+ ~0x300);
+ /* reset DOS top registers */
+ WRITE_VREG(DOS_VDEC2_MCRCC_STALL_CTRL, 0);
+ }
+ } else if (core == VDEC_HCODEC) {
+ if (has_hdec()) {
+ /* hcodec power on */
+ WRITE_AOREG(AO_RTI_GEN_PWR_SLEEP0,
+ READ_AOREG(AO_RTI_GEN_PWR_SLEEP0) &
+ ~0x3);
+ /* wait 10uS */
+ udelay(10);
+ /* hcodec soft reset */
+ WRITE_VREG(DOS_SW_RESET1, 0xffffffff);
+ WRITE_VREG(DOS_SW_RESET1, 0);
+ /* enable hcodec clock */
+ hcodec_clock_enable();
+ /* power up hcodec memories */
+ WRITE_VREG(DOS_MEM_PD_HCODEC, 0);
+ /* remove hcodec isolation */
+ WRITE_AOREG(AO_RTI_GEN_PWR_ISO0,
+ READ_AOREG(AO_RTI_GEN_PWR_ISO0) &
+ ~0x30);
+ }
+ } else if (core == VDEC_HEVC) {
+ if (has_hevc_vdec()) {
+ bool hevc_fixed = false;
+
+ while (!hevc_fixed) {
+ /* hevc power on */
+ WRITE_AOREG(AO_RTI_GEN_PWR_SLEEP0,
+ READ_AOREG(AO_RTI_GEN_PWR_SLEEP0) &
+ ~0xc0);
+ /* wait 10uS */
+ udelay(10);
+ /* hevc soft reset */
+ WRITE_VREG(DOS_SW_RESET3, 0xffffffff);
+ WRITE_VREG(DOS_SW_RESET3, 0);
+ /* enable hevc clock */
+ hevc_clock_hi_enable();
+ /* power up hevc memories */
+ WRITE_VREG(DOS_MEM_PD_HEVC, 0);
+ /* remove hevc isolation */
+ WRITE_AOREG(AO_RTI_GEN_PWR_ISO0,
+ READ_AOREG(AO_RTI_GEN_PWR_ISO0) &
+ ~0xc00);
+
+ if (!hevc_workaround_needed())
+ break;
+
+ if (decomp_addr)
+ hevc_fixed = test_hevc(
+ decomp_addr_aligned, 20);
+
+ if (!hevc_fixed) {
+ hevc_loop++;
+
+ mutex_unlock(&vdec_mutex);
+
+ if (hevc_loop >= HEVC_TEST_LIMIT) {
+ pr_warn("hevc power sequence over limit\n");
+ pr_warn("=====================================================\n");
+ pr_warn(" This chip is identified to have HW failure.\n");
+ pr_warn(" Please contact sqa-platform to replace the platform.\n");
+ pr_warn("=====================================================\n");
+
+ panic("Force panic for chip detection !!!\n");
+
+ break;
+ }
+
+ vdec_poweroff(VDEC_HEVC);
+
+ mdelay(10);
+
+ mutex_lock(&vdec_mutex);
+ }
+ }
+
+ if (hevc_loop > hevc_max_reset_count)
+ hevc_max_reset_count = hevc_loop;
+
+ WRITE_VREG(DOS_SW_RESET3, 0xffffffff);
+ udelay(10);
+ WRITE_VREG(DOS_SW_RESET3, 0);
+ }
+ }
+
+ if (decomp_addr)
+ codec_mm_dma_free_coherent(MEM_NAME,
+ SZ_64K + SZ_4K, decomp_addr, decomp_dma_addr, 0);
+
+ mutex_unlock(&vdec_mutex);
+}
+EXPORT_SYMBOL(vdec_poweron);
+
+void vdec_poweroff(enum vdec_type_e core)
+{
+ mutex_lock(&vdec_mutex);
+ if (core == VDEC_1) {
+ if (get_cpu_type() >=
+ MESON_CPU_MAJOR_ID_GXBB) {
+ /* disable VDEC_1 DMC REQ*/
+ unsigned long flags;
+
+ spin_lock_irqsave(&vdec_spin_lock, flags);
+ codec_dmcbus_write(DMC_REQ_CTRL,
+ codec_dmcbus_read(DMC_REQ_CTRL) & (~(1 << 13)));
+ spin_unlock_irqrestore(&vdec_spin_lock, flags);
+ udelay(10);
+ }
+ /* enable vdec1 isolation */
+ WRITE_AOREG(AO_RTI_GEN_PWR_ISO0,
+ READ_AOREG(AO_RTI_GEN_PWR_ISO0) | 0xc0);
+ /* power off vdec1 memories */
+ WRITE_VREG(DOS_MEM_PD_VDEC, 0xffffffffUL);
+ /* disable vdec1 clock */
+ vdec_clock_off();
+ /* vdec1 power off */
+ WRITE_AOREG(AO_RTI_GEN_PWR_SLEEP0,
+ READ_AOREG(AO_RTI_GEN_PWR_SLEEP0) | 0xc);
+ } else if (core == VDEC_2) {
+ if (has_vdec2()) {
+ /* enable vdec2 isolation */
+ WRITE_AOREG(AO_RTI_GEN_PWR_ISO0,
+ READ_AOREG(AO_RTI_GEN_PWR_ISO0) |
+ 0x300);
+ /* power off vdec2 memories */
+ WRITE_VREG(DOS_MEM_PD_VDEC2, 0xffffffffUL);
+ /* disable vdec2 clock */
+ vdec2_clock_off();
+ /* vdec2 power off */
+ WRITE_AOREG(AO_RTI_GEN_PWR_SLEEP0,
+ READ_AOREG(AO_RTI_GEN_PWR_SLEEP0) |
+ 0x30);
+ }
+ } else if (core == VDEC_HCODEC) {
+ if (has_hdec()) {
+ /* enable hcodec isolation */
+ WRITE_AOREG(AO_RTI_GEN_PWR_ISO0,
+ READ_AOREG(AO_RTI_GEN_PWR_ISO0) |
+ 0x30);
+ /* power off hcodec memories */
+ WRITE_VREG(DOS_MEM_PD_HCODEC, 0xffffffffUL);
+ /* disable hcodec clock */
+ hcodec_clock_off();
+ /* hcodec power off */
+ WRITE_AOREG(AO_RTI_GEN_PWR_SLEEP0,
+ READ_AOREG(AO_RTI_GEN_PWR_SLEEP0) | 3);
+ }
+ } else if (core == VDEC_HEVC) {
+ if (has_hevc_vdec()) {
+ /* enable hevc isolation */
+ WRITE_AOREG(AO_RTI_GEN_PWR_ISO0,
+ READ_AOREG(AO_RTI_GEN_PWR_ISO0) |
+ 0xc00);
+ /* power off hevc memories */
+ WRITE_VREG(DOS_MEM_PD_HEVC, 0xffffffffUL);
+ /* disable hevc clock */
+ hevc_clock_off();
+ /* hevc power off */
+ WRITE_AOREG(AO_RTI_GEN_PWR_SLEEP0,
+ READ_AOREG(AO_RTI_GEN_PWR_SLEEP0) |
+ 0xc0);
+ }
+ }
+ mutex_unlock(&vdec_mutex);
+}
+EXPORT_SYMBOL(vdec_poweroff);
+
+bool vdec_on(enum vdec_type_e core)
+{
+ bool ret = false;
+
+ if (core == VDEC_1) {
+ if (((READ_AOREG(AO_RTI_GEN_PWR_SLEEP0) & 0xc) == 0) &&
+ (READ_HHI_REG(HHI_VDEC_CLK_CNTL) & 0x100))
+ ret = true;
+ } else if (core == VDEC_2) {
+ if (has_vdec2()) {
+ if (((READ_AOREG(AO_RTI_GEN_PWR_SLEEP0) & 0x30) == 0) &&
+ (READ_HHI_REG(HHI_VDEC2_CLK_CNTL) & 0x100))
+ ret = true;
+ }
+ } else if (core == VDEC_HCODEC) {
+ if (has_hdec()) {
+ if (((READ_AOREG(AO_RTI_GEN_PWR_SLEEP0) & 0x3) == 0) &&
+ (READ_HHI_REG(HHI_VDEC_CLK_CNTL) & 0x1000000))
+ ret = true;
+ }
+ } else if (core == VDEC_HEVC) {
+ if (has_hevc_vdec()) {
+ if (((READ_AOREG(AO_RTI_GEN_PWR_SLEEP0) & 0xc0) == 0) &&
+ (READ_HHI_REG(HHI_VDEC2_CLK_CNTL) & 0x1000000))
+ ret = true;
+ }
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL(vdec_on);
+
+#elif 0 /* MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON6TVD */
+void vdec_poweron(enum vdec_type_e core)
+{
+ ulong flags;
+
+ spin_lock_irqsave(&lock, flags);
+
+ if (core == VDEC_1) {
+ /* vdec1 soft reset */
+ WRITE_VREG(DOS_SW_RESET0, 0xfffffffc);
+ WRITE_VREG(DOS_SW_RESET0, 0);
+ /* enable vdec1 clock */
+ vdec_clock_enable();
+ /* reset DOS top registers */
+ WRITE_VREG(DOS_VDEC_MCRCC_STALL_CTRL, 0);
+ } else if (core == VDEC_2) {
+ /* vdec2 soft reset */
+ WRITE_VREG(DOS_SW_RESET2, 0xffffffff);
+ WRITE_VREG(DOS_SW_RESET2, 0);
+ /* enable vdec2 clock */
+ vdec2_clock_enable();
+ /* reset DOS top registers */
+ WRITE_VREG(DOS_VDEC2_MCRCC_STALL_CTRL, 0);
+ } else if (core == VDEC_HCODEC) {
+ /* hcodec soft reset */
+ WRITE_VREG(DOS_SW_RESET1, 0xffffffff);
+ WRITE_VREG(DOS_SW_RESET1, 0);
+ /* enable hcodec clock */
+ hcodec_clock_enable();
+ }
+
+ spin_unlock_irqrestore(&lock, flags);
+}
+
+void vdec_poweroff(enum vdec_type_e core)
+{
+ ulong flags;
+
+ spin_lock_irqsave(&lock, flags);
+
+ if (core == VDEC_1) {
+ /* disable vdec1 clock */
+ vdec_clock_off();
+ } else if (core == VDEC_2) {
+ /* disable vdec2 clock */
+ vdec2_clock_off();
+ } else if (core == VDEC_HCODEC) {
+ /* disable hcodec clock */
+ hcodec_clock_off();
+ }
+
+ spin_unlock_irqrestore(&lock, flags);
+}
+
+bool vdec_on(enum vdec_type_e core)
+{
+ bool ret = false;
+
+ if (core == VDEC_1) {
+ if (READ_HHI_REG(HHI_VDEC_CLK_CNTL) & 0x100)
+ ret = true;
+ } else if (core == VDEC_2) {
+ if (READ_HHI_REG(HHI_VDEC2_CLK_CNTL) & 0x100)
+ ret = true;
+ } else if (core == VDEC_HCODEC) {
+ if (READ_HHI_REG(HHI_VDEC_CLK_CNTL) & 0x1000000)
+ ret = true;
+ }
+
+ return ret;
+}
+#endif
+
+int vdec_source_changed(int format, int width, int height, int fps)
+{
+ /* todo: add level routines for clock adjustment per chips */
+ int ret = -1;
+ static int on_setting;
+
+ if (on_setting > 0)
+ return ret;/*on changing clk,ignore this change*/
+
+ if (vdec_source_get(VDEC_1) == width * height * fps)
+ return ret;
+
+
+ on_setting = 1;
+ ret = vdec_source_changed_for_clk_set(format, width, height, fps);
+ pr_info("vdec1 video changed to %d x %d %d fps clk->%dMHZ\n",
+ width, height, fps, vdec_clk_get(VDEC_1));
+ on_setting = 0;
+ return ret;
+
+}
+EXPORT_SYMBOL(vdec_source_changed);
+
+int vdec2_source_changed(int format, int width, int height, int fps)
+{
+ int ret = -1;
+ static int on_setting;
+
+ if (has_vdec2()) {
+ /* todo: add level routines for clock adjustment per chips */
+ if (on_setting != 0)
+ return ret;/*on changing clk,ignore this change*/
+
+ if (vdec_source_get(VDEC_2) == width * height * fps)
+ return ret;
+
+ on_setting = 1;
+ ret = vdec_source_changed_for_clk_set(format,
+ width, height, fps);
+ pr_info("vdec2 video changed to %d x %d %d fps clk->%dMHZ\n",
+ width, height, fps, vdec_clk_get(VDEC_2));
+ on_setting = 0;
+ return ret;
+ }
+ return 0;
+}
+EXPORT_SYMBOL(vdec2_source_changed);
+
+int hevc_source_changed(int format, int width, int height, int fps)
+{
+ /* todo: add level routines for clock adjustment per chips */
+ int ret = -1;
+ static int on_setting;
+
+ if (on_setting != 0)
+ return ret;/*on changing clk,ignore this change*/
+
+ if (vdec_source_get(VDEC_HEVC) == width * height * fps)
+ return ret;
+
+ on_setting = 1;
+ ret = vdec_source_changed_for_clk_set(format, width, height, fps);
+ pr_info("hevc video changed to %d x %d %d fps clk->%dMHZ\n",
+ width, height, fps, vdec_clk_get(VDEC_HEVC));
+ on_setting = 0;
+
+ return ret;
+}
+EXPORT_SYMBOL(hevc_source_changed);
+
+static enum vdec2_usage_e vdec2_usage = USAGE_NONE;
+void set_vdec2_usage(enum vdec2_usage_e usage)
+{
+ if (has_vdec2()) {
+ mutex_lock(&vdec_mutex);
+ vdec2_usage = usage;
+ mutex_unlock(&vdec_mutex);
+ }
+}
+EXPORT_SYMBOL(set_vdec2_usage);
+
+enum vdec2_usage_e get_vdec2_usage(void)
+{
+ if (has_vdec2())
+ return vdec2_usage;
+ else
+ return 0;
+}
+EXPORT_SYMBOL(get_vdec2_usage);
+
+static struct am_reg am_risc[] = {
+ {"MSP", 0x300},
+ {"MPSR", 0x301},
+ {"MCPU_INT_BASE", 0x302},
+ {"MCPU_INTR_GRP", 0x303},
+ {"MCPU_INTR_MSK", 0x304},
+ {"MCPU_INTR_REQ", 0x305},
+ {"MPC-P", 0x306},
+ {"MPC-D", 0x307},
+ {"MPC_E", 0x308},
+ {"MPC_W", 0x309},
+ {"CSP", 0x320},
+ {"CPSR", 0x321},
+ {"CCPU_INT_BASE", 0x322},
+ {"CCPU_INTR_GRP", 0x323},
+ {"CCPU_INTR_MSK", 0x324},
+ {"CCPU_INTR_REQ", 0x325},
+ {"CPC-P", 0x326},
+ {"CPC-D", 0x327},
+ {"CPC_E", 0x328},
+ {"CPC_W", 0x329},
+ {"AV_SCRATCH_0", 0x09c0},
+ {"AV_SCRATCH_1", 0x09c1},
+ {"AV_SCRATCH_2", 0x09c2},
+ {"AV_SCRATCH_3", 0x09c3},
+ {"AV_SCRATCH_4", 0x09c4},
+ {"AV_SCRATCH_5", 0x09c5},
+ {"AV_SCRATCH_6", 0x09c6},
+ {"AV_SCRATCH_7", 0x09c7},
+ {"AV_SCRATCH_8", 0x09c8},
+ {"AV_SCRATCH_9", 0x09c9},
+ {"AV_SCRATCH_A", 0x09ca},
+ {"AV_SCRATCH_B", 0x09cb},
+ {"AV_SCRATCH_C", 0x09cc},
+ {"AV_SCRATCH_D", 0x09cd},
+ {"AV_SCRATCH_E", 0x09ce},
+ {"AV_SCRATCH_F", 0x09cf},
+ {"AV_SCRATCH_G", 0x09d0},
+ {"AV_SCRATCH_H", 0x09d1},
+ {"AV_SCRATCH_I", 0x09d2},
+ {"AV_SCRATCH_J", 0x09d3},
+ {"AV_SCRATCH_K", 0x09d4},
+ {"AV_SCRATCH_L", 0x09d5},
+ {"AV_SCRATCH_M", 0x09d6},
+ {"AV_SCRATCH_N", 0x09d7},
+};
+
+static ssize_t amrisc_regs_show(struct class *class,
+ struct class_attribute *attr, char *buf)
+{
+ char *pbuf = buf;
+ struct am_reg *regs = am_risc;
+ int rsize = sizeof(am_risc) / sizeof(struct am_reg);
+ int i;
+ unsigned val;
+ ssize_t ret;
+
+ if (get_cpu_type() >= MESON_CPU_MAJOR_ID_M8) {
+ mutex_lock(&vdec_mutex);
+ if (!vdec_on(VDEC_1)) {
+ mutex_unlock(&vdec_mutex);
+ pbuf += sprintf(pbuf, "amrisc is power off\n");
+ ret = pbuf - buf;
+ return ret;
+ }
+ } else if (get_cpu_type() >= MESON_CPU_MAJOR_ID_M6) {
+ /*TODO:M6 define */
+ /*
+ * switch_mod_gate_by_type(MOD_VDEC, 1);
+ */
+ amports_switch_gate("vdec", 1);
+ }
+ pbuf += sprintf(pbuf, "amrisc registers show:\n");
+ for (i = 0; i < rsize; i++) {
+ val = READ_VREG(regs[i].offset);
+ pbuf += sprintf(pbuf, "%s(%#x)\t:%#x(%d)\n",
+ regs[i].name, regs[i].offset, val, val);
+ }
+ if (get_cpu_type() >= MESON_CPU_MAJOR_ID_M8)
+ mutex_unlock(&vdec_mutex);
+ else if (get_cpu_type() >= MESON_CPU_MAJOR_ID_M6) {
+ /*TODO:M6 define */
+ /*
+ * switch_mod_gate_by_type(MOD_VDEC, 0);
+ */
+ amports_switch_gate("vdec", 0);
+ }
+ ret = pbuf - buf;
+ return ret;
+}
+
+static ssize_t dump_trace_show(struct class *class,
+ struct class_attribute *attr, char *buf)
+{
+ int i;
+ char *pbuf = buf;
+ ssize_t ret;
+ u16 *trace_buf = kmalloc(debug_trace_num * 2, GFP_KERNEL);
+
+ if (!trace_buf) {
+ pbuf += sprintf(pbuf, "No Memory bug\n");
+ ret = pbuf - buf;
+ return ret;
+ }
+ if (get_cpu_type() >= MESON_CPU_MAJOR_ID_M8) {
+ mutex_lock(&vdec_mutex);
+ if (!vdec_on(VDEC_1)) {
+ mutex_unlock(&vdec_mutex);
+ kfree(trace_buf);
+ pbuf += sprintf(pbuf, "amrisc is power off\n");
+ ret = pbuf - buf;
+ return ret;
+ }
+ } else if (get_cpu_type() >= MESON_CPU_MAJOR_ID_M6) {
+ /*TODO:M6 define */
+ /*
+ * switch_mod_gate_by_type(MOD_VDEC, 1);
+ */
+ amports_switch_gate("vdec", 1);
+ }
+ pr_info("dump trace steps:%d start\n", debug_trace_num);
+ i = 0;
+ while (i <= debug_trace_num - 16) {
+ trace_buf[i] = READ_VREG(MPC_E);
+ trace_buf[i + 1] = READ_VREG(MPC_E);
+ trace_buf[i + 2] = READ_VREG(MPC_E);
+ trace_buf[i + 3] = READ_VREG(MPC_E);
+ trace_buf[i + 4] = READ_VREG(MPC_E);
+ trace_buf[i + 5] = READ_VREG(MPC_E);
+ trace_buf[i + 6] = READ_VREG(MPC_E);
+ trace_buf[i + 7] = READ_VREG(MPC_E);
+ trace_buf[i + 8] = READ_VREG(MPC_E);
+ trace_buf[i + 9] = READ_VREG(MPC_E);
+ trace_buf[i + 10] = READ_VREG(MPC_E);
+ trace_buf[i + 11] = READ_VREG(MPC_E);
+ trace_buf[i + 12] = READ_VREG(MPC_E);
+ trace_buf[i + 13] = READ_VREG(MPC_E);
+ trace_buf[i + 14] = READ_VREG(MPC_E);
+ trace_buf[i + 15] = READ_VREG(MPC_E);
+ i += 16;
+ };
+ pr_info("dump trace steps:%d finished\n", debug_trace_num);
+ if (get_cpu_type() >= MESON_CPU_MAJOR_ID_M8)
+ mutex_unlock(&vdec_mutex);
+ else if (get_cpu_type() >= MESON_CPU_MAJOR_ID_M6) {
+ /*TODO:M6 define */
+ /*
+ * switch_mod_gate_by_type(MOD_VDEC, 0);
+ */
+ amports_switch_gate("vdec", 0);
+ }
+ for (i = 0; i < debug_trace_num; i++) {
+ if (i % 4 == 0) {
+ if (i % 16 == 0)
+ pbuf += sprintf(pbuf, "\n");
+ else if (i % 8 == 0)
+ pbuf += sprintf(pbuf, " ");
+ else /* 4 */
+ pbuf += sprintf(pbuf, " ");
+ }
+ pbuf += sprintf(pbuf, "%04x:", trace_buf[i]);
+ }
+ while (i < debug_trace_num)
+ ;
+ kfree(trace_buf);
+ pbuf += sprintf(pbuf, "\n");
+ ret = pbuf - buf;
+ return ret;
+}
+
+static ssize_t clock_level_show(struct class *class,
+ struct class_attribute *attr, char *buf)
+{
+ char *pbuf = buf;
+ size_t ret;
+
+ pbuf += sprintf(pbuf, "%dMHZ\n", vdec_clk_get(VDEC_1));
+
+ if (has_vdec2())
+ pbuf += sprintf(pbuf, "%dMHZ\n", vdec_clk_get(VDEC_2));
+
+ if (has_hevc_vdec())
+ pbuf += sprintf(pbuf, "%dMHZ\n", vdec_clk_get(VDEC_HEVC));
+
+ ret = pbuf - buf;
+ return ret;
+}
+
+static ssize_t store_poweron_clock_level(struct class *class,
+ struct class_attribute *attr,
+ const char *buf, size_t size)
+{
+ unsigned val;
+ ssize_t ret;
+
+ /*ret = sscanf(buf, "%d", &val);*/
+ ret = kstrtoint(buf, 0, &val);
+
+ if (ret != 0)
+ return -EINVAL;
+ poweron_clock_level = val;
+ return size;
+}
+
+static ssize_t show_poweron_clock_level(struct class *class,
+ struct class_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%d\n", poweron_clock_level);
+}
+
+/*
+*if keep_vdec_mem == 1
+*always don't release
+*vdec 64 memory for fast play.
+*/
+static ssize_t store_keep_vdec_mem(struct class *class,
+ struct class_attribute *attr,
+ const char *buf, size_t size)
+{
+ unsigned val;
+ ssize_t ret;
+
+ /*ret = sscanf(buf, "%d", &val);*/
+ ret = kstrtoint(buf, 0, &val);
+ if (ret != 0)
+ return -EINVAL;
+ keep_vdec_mem = val;
+ return size;
+}
+
+static ssize_t show_keep_vdec_mem(struct class *class,
+ struct class_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%d\n", keep_vdec_mem);
+}
+
+
+/*irq num as same as .dts*/
+/*
+* interrupts = <0 3 1
+* 0 23 1
+* 0 32 1
+* 0 43 1
+* 0 44 1
+* 0 45 1>;
+* interrupt-names = "vsync",
+* "demux",
+* "parser",
+* "mailbox_0",
+* "mailbox_1",
+* "mailbox_2";
+*/
+s32 vdec_request_threaded_irq(enum vdec_irq_num num,
+ irq_handler_t handler,
+ irq_handler_t thread_fn,
+ unsigned long irqflags,
+ const char *devname, void *dev)
+{
+ s32 res_irq;
+ s32 ret = 0;
+
+ if (num >= VDEC_IRQ_MAX) {
+ pr_err("[%s] request irq error, irq num too big!", __func__);
+ return -EINVAL;
+ }
+
+ if (vdec_core->isr_context[num].irq < 0) {
+ res_irq = platform_get_irq(
+ vdec_core->vdec_core_platform_device, num);
+ if (res_irq < 0) {
+ pr_err("[%s] get irq error!", __func__);
+ return -EINVAL;
+ }
+
+ vdec_core->isr_context[num].irq = res_irq;
+ vdec_core->isr_context[num].dev_isr = handler;
+ vdec_core->isr_context[num].dev_threaded_isr = thread_fn;
+ vdec_core->isr_context[num].dev_id = dev;
+
+ ret = request_threaded_irq(res_irq,
+ vdec_isr,
+ vdec_thread_isr,
+ (thread_fn) ? IRQF_ONESHOT : irqflags,
+ devname,
+ &vdec_core->isr_context[num]);
+
+ if (ret) {
+ vdec_core->isr_context[num].irq = -1;
+ vdec_core->isr_context[num].dev_isr = NULL;
+ vdec_core->isr_context[num].dev_threaded_isr = NULL;
+ vdec_core->isr_context[num].dev_id = NULL;
+
+ pr_err("vdec irq register error for %s.\n", devname);
+ return -EIO;
+ }
+ } else {
+ vdec_core->isr_context[num].dev_isr = handler;
+ vdec_core->isr_context[num].dev_threaded_isr = thread_fn;
+ vdec_core->isr_context[num].dev_id = dev;
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL(vdec_request_threaded_irq);
+
+s32 vdec_request_irq(enum vdec_irq_num num, irq_handler_t handler,
+ const char *devname, void *dev)
+{
+ pr_info("vdec_request_irq %p, %s\n", handler, devname);
+
+ return vdec_request_threaded_irq(num,
+ handler,
+ NULL,/*no thread_fn*/
+ IRQF_SHARED,
+ devname,
+ dev);
+}
+EXPORT_SYMBOL(vdec_request_irq);
+
+void vdec_free_irq(enum vdec_irq_num num, void *dev)
+{
+ if (num >= VDEC_IRQ_MAX) {
+ pr_err("[%s] request irq error, irq num too big!", __func__);
+ return;
+ }
+
+ synchronize_irq(vdec_core->isr_context[num].irq);
+
+ /*
+ *assume amrisc is stopped already and there is no mailbox interrupt
+ * when we reset pointers here.
+ */
+ vdec_core->isr_context[num].dev_isr = NULL;
+ vdec_core->isr_context[num].dev_threaded_isr = NULL;
+ vdec_core->isr_context[num].dev_id = NULL;
+}
+EXPORT_SYMBOL(vdec_free_irq);
+
+static int dump_mode;
+static ssize_t dump_risc_mem_store(struct class *class,
+ struct class_attribute *attr,
+ const char *buf, size_t size)/*set*/
+{
+ unsigned val;
+ ssize_t ret;
+ char dump_mode_str[4] = "PRL";
+
+ /*ret = sscanf(buf, "%d", &val);*/
+ ret = kstrtoint(buf, 0, &val);
+
+ if (ret != 0)
+ return -EINVAL;
+ dump_mode = val & 0x3;
+ pr_info("set dump mode to %d,%c_mem\n",
+ dump_mode, dump_mode_str[dump_mode]);
+ return size;
+}
+static u32 read_amrisc_reg(int reg)
+{
+ WRITE_VREG(0x31b, reg);
+ return READ_VREG(0x31c);
+}
+
+static void dump_pmem(void)
+{
+ int i;
+
+ WRITE_VREG(0x301, 0x8000);
+ WRITE_VREG(0x31d, 0);
+ pr_info("start dump amrisc pmem of risc\n");
+ for (i = 0; i < 0xfff; i++) {
+ /*same as .o format*/
+ pr_info("%08x // 0x%04x:\n", read_amrisc_reg(i), i);
+ }
+}
+
+static void dump_lmem(void)
+{
+ int i;
+
+ WRITE_VREG(0x301, 0x8000);
+ WRITE_VREG(0x31d, 2);
+ pr_info("start dump amrisc lmem\n");
+ for (i = 0; i < 0x3ff; i++) {
+ /*same as */
+ pr_info("[%04x] = 0x%08x:\n", i, read_amrisc_reg(i));
+ }
+}
+
+static ssize_t dump_risc_mem_show(struct class *class,
+ struct class_attribute *attr, char *buf)
+{
+ char *pbuf = buf;
+ int ret;
+
+ if (get_cpu_type() >= MESON_CPU_MAJOR_ID_M8) {
+ mutex_lock(&vdec_mutex);
+ if (!vdec_on(VDEC_1)) {
+ mutex_unlock(&vdec_mutex);
+ pbuf += sprintf(pbuf, "amrisc is power off\n");
+ ret = pbuf - buf;
+ return ret;
+ }
+ } else if (get_cpu_type() >= MESON_CPU_MAJOR_ID_M6) {
+ /*TODO:M6 define */
+ /*
+ * switch_mod_gate_by_type(MOD_VDEC, 1);
+ */
+ amports_switch_gate("vdec", 1);
+ }
+ /*start do**/
+ switch (dump_mode) {
+ case 0:
+ dump_pmem();
+ break;
+ case 2:
+ dump_lmem();
+ break;
+ default:
+ break;
+ }
+
+ /*done*/
+ if (get_cpu_type() >= MESON_CPU_MAJOR_ID_M8)
+ mutex_unlock(&vdec_mutex);
+ else if (get_cpu_type() >= MESON_CPU_MAJOR_ID_M6) {
+ /*TODO:M6 define */
+ /*
+ * switch_mod_gate_by_type(MOD_VDEC, 0);
+ */
+ amports_switch_gate("vdec", 0);
+ }
+ return sprintf(buf, "done\n");
+}
+
+static ssize_t core_show(struct class *class, struct class_attribute *attr,
+ char *buf)
+{
+ struct vdec_core_s *core = vdec_core;
+ char *pbuf = buf;
+
+ if (list_empty(&core->connected_vdec_list))
+ pbuf += sprintf(pbuf, "connected vdec list empty\n");
+ else {
+ struct vdec_s *vdec;
+
+ list_for_each_entry(vdec, &core->connected_vdec_list, list) {
+ pbuf += sprintf(pbuf,
+ "\tvdec (%p (%s)), status = %s\n",
+ vdec, vdec_device_name[vdec->format * 2],
+ vdec_status_str(vdec));
+ }
+ }
+
+ return pbuf - buf;
+}
+
+static struct class_attribute vdec_class_attrs[] = {
+ __ATTR_RO(amrisc_regs),
+ __ATTR_RO(dump_trace),
+ __ATTR_RO(clock_level),
+ __ATTR(poweron_clock_level, S_IRUGO | S_IWUSR | S_IWGRP,
+ show_poweron_clock_level, store_poweron_clock_level),
+ __ATTR(dump_risc_mem, S_IRUGO | S_IWUSR | S_IWGRP,
+ dump_risc_mem_show, dump_risc_mem_store),
+ __ATTR(keep_vdec_mem, S_IRUGO | S_IWUSR | S_IWGRP,
+ show_keep_vdec_mem, store_keep_vdec_mem),
+ __ATTR_RO(core),
+ __ATTR_NULL
+};
+
+static struct class vdec_class = {
+ .name = "vdec",
+ .class_attrs = vdec_class_attrs,
+ };
+
+
+/*
+*pre alloced enough memory for decoder
+*fast start.
+*/
+void pre_alloc_vdec_memory(void)
+{
+ if (!keep_vdec_mem || vdec_core->mem_start)
+ return;
+
+ vdec_core->mem_start = codec_mm_alloc_for_dma(MEM_NAME,
+ CMA_ALLOC_SIZE / PAGE_SIZE, 4 + PAGE_SHIFT,
+ CODEC_MM_FLAGS_CMA_CLEAR |
+ CODEC_MM_FLAGS_FOR_VDECODER);
+ if (!vdec_core->mem_start)
+ return;
+ pr_debug("vdec base memory alloced %p\n",
+ (void *)vdec_core->mem_start);
+
+ vdec_core->mem_end = vdec_core->mem_start + CMA_ALLOC_SIZE - 1;
+ vdec_mem_alloced_from_codec = 1;
+ delay_release = 3;
+}
+EXPORT_SYMBOL(pre_alloc_vdec_memory);
+
+struct device *get_vdec_device(void)
+{
+ return &vdec_core->vdec_core_platform_device->dev;
+}
+EXPORT_SYMBOL(get_vdec_device);
+
+static int vdec_probe(struct platform_device *pdev)
+{
+ s32 i, r;
+
+ vdec_core = (struct vdec_core_s *)devm_kzalloc(&pdev->dev,
+ sizeof(struct vdec_core_s), GFP_KERNEL);
+ if (vdec_core == NULL) {
+ pr_err("vdec core allocation failed.\n");
+ return -ENOMEM;
+ }
+
+ atomic_set(&vdec_core->vdec_nr, 0);
+ sema_init(&vdec_core->sem, 1);
+
+ r = class_register(&vdec_class);
+ if (r) {
+ pr_info("vdec class create fail.\n");
+ return r;
+ }
+
+ vdec_core->vdec_core_platform_device = pdev;
+
+ platform_set_drvdata(pdev, vdec_core);
+
+ for (i = 0; i < VDEC_IRQ_MAX; i++) {
+ vdec_core->isr_context[i].index = i;
+ vdec_core->isr_context[i].irq = -1;
+ }
+
+ r = vdec_request_threaded_irq(VDEC_IRQ_1, NULL, NULL,
+ IRQF_ONESHOT, "vdec-1", NULL);
+ if (r < 0) {
+ pr_err("vdec interrupt request failed\n");
+ return r;
+ }
+
+ r = of_reserved_mem_device_init(&pdev->dev);
+ if (r == 0)
+ pr_info("vdec_probe done\n");
+
+ vdec_core->cma_dev = &pdev->dev;
+
+ if (get_cpu_type() < MESON_CPU_MAJOR_ID_M8) {
+ /* default to 250MHz */
+ vdec_clock_hi_enable();
+ }
+
+ if (get_cpu_type() == MESON_CPU_MAJOR_ID_GXBB) {
+ /* set vdec dmc request to urgent */
+ WRITE_DMCREG(DMC_AM5_CHAN_CTRL, 0x3f203cf);
+ }
+ if (codec_mm_get_reserved_size() >= 48 * SZ_1M
+ && codec_mm_get_reserved_size() <= 96 * SZ_1M) {
+#ifdef CONFIG_MULTI_DEC
+ vdec_default_buf_size[VFORMAT_H264_4K2K * 2] =
+ codec_mm_get_reserved_size() / SZ_1M;
+#else
+ vdec_default_buf_size[VFORMAT_H264_4K2K] =
+ codec_mm_get_reserved_size() / SZ_1M;
+#endif
+
+ /*all reserved size for prealloc*/
+ }
+ pre_alloc_vdec_memory();
+
+ INIT_LIST_HEAD(&vdec_core->connected_vdec_list);
+ spin_lock_init(&vdec_core->lock);
+
+ vdec_core->thread = kthread_run(vdec_core_thread, vdec_core,
+ "vdec-core");
+
+ return 0;
+}
+
+static int vdec_remove(struct platform_device *pdev)
+{
+ int i;
+
+ for (i = 0; i < VDEC_IRQ_MAX; i++) {
+ if (vdec_core->isr_context[i].irq >= 0) {
+ free_irq(vdec_core->isr_context[i].irq,
+ &vdec_core->isr_context[i]);
+ vdec_core->isr_context[i].irq = -1;
+ vdec_core->isr_context[i].dev_isr = NULL;
+ vdec_core->isr_context[i].dev_threaded_isr = NULL;
+ vdec_core->isr_context[i].dev_id = NULL;
+ }
+ }
+
+ kthread_stop(vdec_core->thread);
+
+ class_unregister(&vdec_class);
+
+ return 0;
+}
+
+static const struct of_device_id amlogic_vdec_dt_match[] = {
+ {
+ .compatible = "amlogic, vdec",
+ },
+ {},
+};
+
+static struct platform_driver vdec_driver = {
+ .probe = vdec_probe,
+ .remove = vdec_remove,
+ .driver = {
+ .name = "vdec",
+ .of_match_table = amlogic_vdec_dt_match,
+ }
+};
+
+int vdec_module_init(void)
+{
+ if (platform_driver_register(&vdec_driver)) {
+ pr_info("failed to register vdec module\n");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(vdec_module_init);
+
+void vdec_module_exit(void)
+{
+ platform_driver_unregister(&vdec_driver);
+}
+EXPORT_SYMBOL(vdec_module_exit);
+
+#if 0
+static int __init vdec_module_init(void)
+{
+ if (platform_driver_register(&vdec_driver)) {
+ pr_info("failed to register vdec module\n");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static void __exit vdec_module_exit(void)
+{
+ platform_driver_unregister(&vdec_driver);
+}
+#endif
+
+static int vdec_mem_device_init(struct reserved_mem *rmem, struct device *dev)
+{
+ unsigned long start, end;
+
+ start = rmem->base;
+ end = rmem->base + rmem->size - 1;
+ pr_info("init vdec memsource %lx->%lx\n", start, end);
+
+ vdec_core->mem_start = start;
+ vdec_core->mem_end = end;
+ vdec_core->cma_dev = dev;
+
+ return 0;
+}
+
+static const struct reserved_mem_ops rmem_vdec_ops = {
+ .device_init = vdec_mem_device_init,
+};
+
+static int __init vdec_mem_setup(struct reserved_mem *rmem)
+{
+ rmem->ops = &rmem_vdec_ops;
+ pr_info("vdec: reserved mem setup\n");
+
+ return 0;
+}
+
+RESERVEDMEM_OF_DECLARE(vdec, "amlogic, vdec-memory", vdec_mem_setup);
+
+module_param(debug_trace_num, uint, 0664);
+module_param(hevc_max_reset_count, int, 0664);
+module_param(clk_config, uint, 0664);
+/*
+*module_init(vdec_module_init);
+*module_exit(vdec_module_exit);
+*/
+MODULE_DESCRIPTION("AMLOGIC vdec driver");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Tim Yao <timyao@amlogic.com>");
diff --git a/drivers/frame_provider/decoder/utils/vdec.h b/drivers/frame_provider/decoder/utils/vdec.h
new file mode 100644
index 0000000..26c4ced
--- a/dev/null
+++ b/drivers/frame_provider/decoder/utils/vdec.h
@@ -0,0 +1,273 @@
+/*
+ * drivers/amlogic/media/frame_provider/decoder/utils/vdec.h
+ *
+ * Copyright (C) 2016 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 VDEC_H
+#define VDEC_H
+#include <linux/amlogic/media/utils/amports_config.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/list.h>
+#include <linux/completion.h>
+#include <linux/irqreturn.h>
+
+#include <linux/amlogic/media/utils/amstream.h>
+#include <linux/amlogic/media/vfm/vframe.h>
+#include <linux/amlogic/media/vfm/vframe_provider.h>
+#include <linux/amlogic/media/vfm/vframe_receiver.h>
+
+#include "vdec_input.h"
+
+s32 vdec_dev_register(void);
+s32 vdec_dev_unregister(void);
+
+int vdec_source_changed(int format, int width, int height, int fps);
+int vdec2_source_changed(int format, int width, int height, int fps);
+int hevc_source_changed(int format, int width, int height, int fps);
+struct device *get_vdec_device(void);
+int vdec_module_init(void);
+void vdec_module_exit(void);
+
+#define DEC_FLAG_HEVC_WORKAROUND 0x01
+
+enum vdec_type_e {
+ VDEC_1 = 0,
+ VDEC_HCODEC,
+ VDEC_2,
+ VDEC_HEVC,
+ VDEC_MAX
+};
+
+extern void vdec2_power_mode(int level);
+extern void vdec_poweron(enum vdec_type_e core);
+extern void vdec_poweroff(enum vdec_type_e core);
+extern bool vdec_on(enum vdec_type_e core);
+
+/*irq num as same as .dts*/
+
+/*
+* interrupts = <0 3 1
+* 0 23 1
+* 0 32 1
+* 0 43 1
+* 0 44 1
+* 0 45 1>;
+* interrupt-names = "vsync",
+* "demux",
+* "parser",
+* "mailbox_0",
+* "mailbox_1",
+* "mailbox_2";
+*/
+enum vdec_irq_num {
+ VSYNC_IRQ = 0,
+ DEMUX_IRQ,
+ PARSER_IRQ,
+ VDEC_IRQ_0,
+ VDEC_IRQ_1,
+ VDEC_IRQ_2,
+ VDEC_IRQ_MAX,
+};
+extern s32 vdec_request_threaded_irq(enum vdec_irq_num num,
+ irq_handler_t handler,
+ irq_handler_t thread_fn,
+ unsigned long irqflags, const char *devname, void *dev);
+extern s32 vdec_request_irq(enum vdec_irq_num num, irq_handler_t handler,
+ const char *devname, void *dev);
+extern void vdec_free_irq(enum vdec_irq_num num, void *dev);
+
+enum vdec2_usage_e {
+ USAGE_NONE,
+ USAGE_DEC_4K2K,
+ USAGE_ENCODE,
+};
+
+extern void set_vdec2_usage(enum vdec2_usage_e usage);
+extern enum vdec2_usage_e get_vdec2_usage(void);
+
+extern void dma_contiguous_early_fixup(phys_addr_t base, unsigned long size);
+unsigned int get_vdec_clk_config_settings(void);
+void update_vdec_clk_config_settings(unsigned int config);
+/*unsigned int get_mmu_mode(void);*//*mask*/
+
+struct vdec_s;
+enum vformat_t;
+
+/* stream based with single instance decoder driver */
+#define VDEC_TYPE_SINGLE 0
+
+/* stream based with multi-instance decoder with HW resouce sharing */
+#define VDEC_TYPE_STREAM_PARSER 1
+
+/* frame based with multi-instance decoder, input block list based */
+#define VDEC_TYPE_FRAME_BLOCK 2
+
+/* frame based with multi-instance decoder, single circular input block */
+#define VDEC_TYPE_FRAME_CIRCULAR 3
+
+/* decoder status: uninitialized */
+#define VDEC_STATUS_UNINITIALIZED 0
+
+/* decoder status: before the decoder can start consuming data */
+#define VDEC_STATUS_DISCONNECTED 1
+
+/* decoder status: decoder should become disconnected once it's not active */
+#define VDEC_STATUS_CONNECTED 2
+
+/* decoder status: decoder owns HW resource and is running */
+#define VDEC_STATUS_ACTIVE 3
+
+#define VDEC_PROVIDER_NAME_SIZE 16
+#define VDEC_RECEIVER_NAME_SIZE 16
+#define VDEC_MAP_NAME_SIZE 40
+
+struct vdec_s {
+ u32 magic;
+ struct list_head list;
+ int id;
+
+ int status;
+ int next_status;
+ int type;
+ int port_flag;
+ int format;
+ u32 pts;
+ u64 pts64;
+ bool pts_valid;
+
+ struct completion inactive_done;
+
+ /* config (temp) */
+ unsigned long mem_start;
+ unsigned long mem_end;
+
+ struct device *cma_dev;
+ struct platform_device *dev;
+ struct dec_sysinfo sys_info_store;
+ struct dec_sysinfo *sys_info;
+
+ /* input */
+ struct vdec_input_s input;
+
+ /* frame provider/receiver interface */
+ char vf_provider_name[VDEC_PROVIDER_NAME_SIZE];
+ struct vframe_provider_s vframe_provider;
+ char *vf_receiver_name;
+ char vfm_map_id[VDEC_MAP_NAME_SIZE];
+ char vfm_map_chain[VDEC_MAP_NAME_SIZE];
+ int vf_receiver_inst;
+ bool use_vfm_path;
+
+ /* canvas */
+ int (*get_canvas)(unsigned int index, unsigned int base);
+
+ int (*dec_status)(struct vdec_s *vdec, struct vdec_status *vstatus);
+ int (*set_trickmode)(struct vdec_s *vdec, unsigned long trickmode);
+
+ bool (*run_ready)(struct vdec_s *vdec);
+ void (*run)(struct vdec_s *vdec,
+ void (*callback)(struct vdec_s *, void *), void *);
+ void (*reset)(struct vdec_s *vdec);
+ irqreturn_t (*irq_handler)(struct vdec_s *);
+ irqreturn_t (*threaded_irq_handler)(struct vdec_s *);
+
+ /* private */
+ void *private; /* decoder per instance specific data */
+};
+
+/* common decoder vframe provider name to use default vfm path */
+#define VFM_DEC_PROVIDER_NAME "decoder"
+
+#define hw_to_vdec(hw) ((struct vdec_s *) \
+ (platform_get_drvdata(hw->platform_dev)))
+
+#define canvas_y(canvas) ((canvas) & 0xff)
+#define canvas_u(canvas) (((canvas) >> 8) & 0xff)
+#define canvas_v(canvas) (((canvas) >> 16) & 0xff)
+#define canvas_y2(canvas) (((canvas) >> 16) & 0xff)
+#define canvas_u2(canvas) (((canvas) >> 24) & 0xff)
+
+#define vdec_frame_based(vdec) \
+ (((vdec)->type == VDEC_TYPE_FRAME_BLOCK) || \
+ ((vdec)->type == VDEC_TYPE_FRAME_CIRCULAR))
+#define vdec_stream_based(vdec) \
+ (((vdec)->type == VDEC_TYPE_STREAM_PARSER) || \
+ ((vdec)->type == VDEC_TYPE_SINGLE))
+#define vdec_stream_auto(vdec) \
+ (vdec->type == VDEC_TYPE_SINGLE)
+
+/* construct vdec strcture */
+extern struct vdec_s *vdec_create(int type);
+
+/* set video format */
+extern int vdec_set_format(struct vdec_s *vdec, int format);
+
+/* set PTS */
+extern int vdec_set_pts(struct vdec_s *vdec, u32 pts);
+
+extern int vdec_set_pts64(struct vdec_s *vdec, u64 pts64);
+
+/* add frame data to input chain */
+extern int vdec_write_vframe(struct vdec_s *vdec, const char *buf,
+ size_t count);
+
+/* mark the vframe_chunk as consumed */
+extern void vdec_vframe_dirty(struct vdec_s *vdec,
+ struct vframe_chunk_s *chunk);
+
+/* prepare decoder input */
+extern int vdec_prepare_input(struct vdec_s *vdec, struct vframe_chunk_s **p);
+
+/* clean decoder input */
+extern void vdec_clean_input(struct vdec_s *vdec);
+
+/* enable decoder input */
+extern void vdec_enable_input(struct vdec_s *vdec);
+
+/* allocate input chain
+ * register vdec_device
+ * create output, vfm or create ionvideo output
+ * insert vdec to vdec_manager for scheduling
+ */
+extern int vdec_connect(struct vdec_s *vdec);
+
+/* remove vdec from vdec_manager scheduling
+ * release input chain
+ * disconnect video output from ionvideo
+ */
+extern int vdec_disconnect(struct vdec_s *vdec);
+
+/* release vdec structure */
+extern int vdec_destroy(struct vdec_s *vdec);
+
+/* reset vdec */
+extern int vdec_reset(struct vdec_s *vdec);
+
+extern void vdec_set_status(struct vdec_s *vdec, int status);
+
+extern void vdec_set_next_status(struct vdec_s *vdec, int status);
+
+extern int vdec_set_decinfo(struct vdec_s *vdec, struct dec_sysinfo *p);
+
+extern int vdec_init(struct vdec_s *vdec, int is_4k);
+
+extern void vdec_release(struct vdec_s *vdec);
+
+extern int vdec_status(struct vdec_s *vdec, struct vdec_status *vstatus);
+
+extern int vdec_set_trickmode(struct vdec_s *vdec, unsigned long trickmode);
+
+#endif /* VDEC_H */
diff --git a/drivers/frame_provider/decoder/utils/vdec_input.c b/drivers/frame_provider/decoder/utils/vdec_input.c
new file mode 100644
index 0000000..e0c3ce0
--- a/dev/null
+++ b/drivers/frame_provider/decoder/utils/vdec_input.c
@@ -0,0 +1,542 @@
+/*
+ * drivers/amlogic/media/frame_provider/decoder/utils/vdec_input.c
+ *
+ * Copyright (C) 2016 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.
+ *
+*/
+
+#include <linux/uaccess.h>
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <linux/dma-mapping.h>
+#include <linux/amlogic/media/codec_mm/codec_mm.h>
+
+#include "../../../stream_input/amports/amports_priv.h"
+#include "vdec.h"
+#include "vdec_input.h"
+
+#define VFRAME_BLOCK_SIZE (4*SZ_1M)
+#define VFRAME_BLOCK_PAGESIZE (PAGE_ALIGN(VFRAME_BLOCK_SIZE)/PAGE_SIZE)
+#define VFRAME_BLOCK_PAGEALIGN 4
+#define VFRAME_BLOCK_MAX_LEVEL (8*SZ_1M)
+#define VFRAME_BLOCK_HOLE (SZ_64K)
+
+#define FRAME_PADDING_SIZE 1024U
+#define MEM_NAME "VFRAME_INPUT"
+
+static int vframe_chunk_fill(struct vdec_input_s *input,
+ struct vframe_chunk_s *chunk, const char *buf,
+ size_t count, struct vframe_block_list_s *block)
+{
+ u8 *p = (u8 *)block->start_virt + block->wp;
+
+ if (block->type == VDEC_TYPE_FRAME_BLOCK) {
+ if (copy_from_user(p, buf, count))
+ return -EFAULT;
+
+ p += count;
+
+ memset(p, 0, FRAME_PADDING_SIZE);
+
+ dma_sync_single_for_device(get_vdec_device(),
+ block->start + block->wp,
+ count + FRAME_PADDING_SIZE, DMA_TO_DEVICE);
+
+ } else if (block->type == VDEC_TYPE_FRAME_CIRCULAR) {
+ size_t len = min((size_t)(block->size - block->wp), count);
+ u32 wp;
+
+ if (copy_from_user(p, buf, len))
+ return -EFAULT;
+
+ dma_sync_single_for_device(get_vdec_device(),
+ block->start + block->wp,
+ len, DMA_TO_DEVICE);
+
+ p += len;
+
+ if (count > len) {
+ p = (u8 *)block->start_virt;
+ if (copy_from_user(p, buf, count - len))
+ return -EFAULT;
+
+ dma_sync_single_for_device(get_vdec_device(),
+ block->start,
+ count-len, DMA_TO_DEVICE);
+
+ p += count - len;
+ }
+
+ wp = block->wp + count;
+ if (wp >= block->size)
+ wp -= block->size;
+
+ len = min(block->size - wp, FRAME_PADDING_SIZE);
+
+ memset(p, 0, len);
+
+ dma_sync_single_for_device(get_vdec_device(),
+ block->start + wp,
+ len, DMA_TO_DEVICE);
+
+ if (len < FRAME_PADDING_SIZE) {
+ p = (u8 *)block->start_virt;
+
+ memset(p, 0, count - len);
+
+ dma_sync_single_for_device(get_vdec_device(),
+ block->start,
+ count - len, DMA_TO_DEVICE);
+ }
+ }
+
+ return 0;
+}
+
+static inline u32 vframe_block_space(struct vframe_block_list_s *block)
+{
+ if (block->type == VDEC_TYPE_FRAME_BLOCK) {
+ return block->size - block->wp;
+ } else {
+ return (block->rp >= block->wp) ?
+ (block->rp - block->wp) :
+ (block->rp - block->wp + block->size);
+ }
+}
+
+static void vframe_block_add_chunk(struct vframe_block_list_s *block,
+ struct vframe_chunk_s *chunk)
+{
+ block->wp += chunk->size + FRAME_PADDING_SIZE;
+ if (block->wp >= block->size)
+ block->wp -= block->size;
+ block->chunk_count++;
+ chunk->block = block;
+ block->input->wr_block = block;
+ chunk->sequence = block->input->sequence;
+ block->input->sequence++;
+}
+
+static void vframe_block_free_storage(struct vframe_block_list_s *block)
+{
+ struct vdec_input_s *input = block->input;
+
+ if (block->addr) {
+ dma_unmap_single(
+ get_vdec_device(),
+ block->start,
+ VFRAME_BLOCK_PAGESIZE,
+ DMA_TO_DEVICE);
+
+ codec_mm_free_for_dma(MEM_NAME, block->addr);
+
+ block->addr = 0;
+ block->start_virt = NULL;
+ block->start = 0;
+ }
+
+ if (input->swap_page) {
+ __free_page(input->swap_page);
+ input->swap_page = NULL;
+ input->swap_valid = false;
+ }
+
+ block->size = 0;
+}
+
+static int vframe_block_init_alloc_storage(struct vdec_input_s *input,
+ struct vframe_block_list_s *block)
+{
+ block->magic = 0x4b434c42;
+ block->input = input;
+ block->type = input->type;
+
+ /*
+ * todo: for different type use different size
+ */
+ block->addr = codec_mm_alloc_for_dma(
+ MEM_NAME,
+ VFRAME_BLOCK_PAGESIZE,
+ VFRAME_BLOCK_PAGEALIGN,
+ CODEC_MM_FLAGS_DMA_CPU | CODEC_MM_FLAGS_FOR_VDECODER);
+
+ if (!block->addr) {
+ pr_err("Input block allocation failed\n");
+ return -ENOMEM;
+ }
+
+ block->start_virt = (void *)codec_mm_phys_to_virt(block->addr);
+ block->start = dma_map_single(
+ get_vdec_device(),
+ block->start_virt,
+ VFRAME_BLOCK_PAGESIZE,
+ DMA_TO_DEVICE);
+ block->size = VFRAME_BLOCK_PAGESIZE * PAGE_SIZE;
+
+ return 0;
+}
+
+void vdec_input_init(struct vdec_input_s *input, struct vdec_s *vdec)
+{
+ INIT_LIST_HEAD(&input->vframe_block_list);
+ INIT_LIST_HEAD(&input->vframe_chunk_list);
+ spin_lock_init(&input->lock);
+
+ input->vdec = vdec;
+}
+EXPORT_SYMBOL(vdec_input_init);
+
+int vdec_input_set_buffer(struct vdec_input_s *input, u32 start, u32 size)
+{
+ if (input_frame_based(input))
+ return -EINVAL;
+
+ input->start = start;
+ input->size = size;
+ input->swap_page = alloc_page(GFP_KERNEL);
+
+ if (input->swap_page == NULL)
+ return -ENOMEM;
+
+ return 0;
+}
+EXPORT_SYMBOL(vdec_input_set_buffer);
+
+void vdec_input_set_type(struct vdec_input_s *input, int type, int target)
+{
+ input->type = type;
+ input->target = target;
+}
+EXPORT_SYMBOL(vdec_input_set_type);
+
+int vdec_input_get_status(struct vdec_input_s *input,
+ struct vdec_input_status_s *status)
+{
+ unsigned long flags;
+
+ if (input->vdec == NULL)
+ return -EINVAL;
+
+ flags = vdec_input_lock(input);
+
+ if (list_empty(&input->vframe_block_list)) {
+ status->size = VFRAME_BLOCK_SIZE;
+ status->data_len = 0;
+ status->free_len = VFRAME_BLOCK_SIZE;
+ status->read_pointer = 0;
+ } else {
+ int r = VFRAME_BLOCK_MAX_LEVEL - vdec_input_level(input)
+ - VFRAME_BLOCK_HOLE;
+ status->size = input->size;
+ status->data_len = vdec_input_level(input);
+ status->free_len = (r > 0) ? r : 0;
+ status->read_pointer = input->total_rd_count;
+ }
+
+ vdec_input_unlock(input, flags);
+
+ return 0;
+}
+EXPORT_SYMBOL(vdec_input_get_status);
+
+static void vdec_input_add_block(struct vdec_input_s *input,
+ struct vframe_block_list_s *block)
+{
+ unsigned long flags;
+
+ flags = vdec_input_lock(input);
+
+ list_add_tail(&block->list, &input->vframe_block_list);
+ input->size += block->size;
+
+ vdec_input_unlock(input, flags);
+}
+
+static void vdec_input_remove_block(struct vdec_input_s *input,
+ struct vframe_block_list_s *block)
+{
+ unsigned long flags;
+
+ flags = vdec_input_lock(input);
+
+ list_del(&block->list);
+ input->size -= block->size;
+
+ vdec_input_unlock(input, flags);
+
+ vframe_block_free_storage(block);
+
+ kfree(block);
+
+ pr_info("block %p removed\n", block);
+}
+
+int vdec_input_level(struct vdec_input_s *input)
+{
+ return input->total_wr_count - input->total_rd_count;
+}
+EXPORT_SYMBOL(vdec_input_level);
+
+int vdec_input_add_frame(struct vdec_input_s *input, const char *buf,
+ size_t count)
+{
+ unsigned long flags;
+ struct vframe_chunk_s *chunk;
+ struct vdec_s *vdec = input->vdec;
+ struct vframe_block_list_s *block = input->wr_block;
+
+#if 0
+ if (add_count == 0) {
+ add_count++;
+ memcpy(sps, buf, 30);
+ return 30;
+ } else if (add_count == 1) {
+ add_count++;
+ memcpy(pps, buf, 8);
+ return 8;
+ }
+ add_count++;
+#endif
+
+#if 0
+ pr_info("vdec_input_add_frame add %p, count=%d\n", buf, (int)count);
+
+ if (count >= 8) {
+ pr_info("%02x %02x %02x %02x %02x %02x %02x %02x\n",
+ buf[0], buf[1], buf[2], buf[3],
+ buf[4], buf[5], buf[6], buf[7]);
+ }
+ if (count >= 16) {
+ pr_info("%02x %02x %02x %02x %02x %02x %02x %02x\n",
+ buf[8], buf[9], buf[10], buf[11],
+ buf[12], buf[13], buf[14], buf[15]);
+ }
+ if (count >= 24) {
+ pr_info("%02x %02x %02x %02x %02x %02x %02x %02x\n",
+ buf[16], buf[17], buf[18], buf[19],
+ buf[20], buf[21], buf[22], buf[23]);
+ }
+ if (count >= 32) {
+ pr_info("%02x %02x %02x %02x %02x %02x %02x %02x\n",
+ buf[24], buf[25], buf[26], buf[27],
+ buf[28], buf[29], buf[30], buf[31]);
+ }
+#endif
+ if (input_stream_based(input))
+ return -EINVAL;
+
+ /* prepare block to write */
+ if ((!block) || (block &&
+ (vframe_block_space(block) < (count + FRAME_PADDING_SIZE)))) {
+ /* switch to another block for the added chunk */
+ struct vframe_block_list_s *new_block;
+
+#if 0
+ pr_info("Adding new block, total level = %d, total_wr_count=%d, total_rd_count=%d\n",
+ vdec_input_level(input),
+ (int)input->total_wr_count,
+ (int)input->total_rd_count);
+#endif
+
+ if ((!list_empty(&input->vframe_block_list)) &&
+ (block->type == VDEC_TYPE_FRAME_CIRCULAR))
+ return -EAGAIN;
+ /*
+ *todo: add input limit check for
+ * VDEC_TYPE_FRAME_BLOCK
+ */
+ if (vdec_input_level(input) >
+ VFRAME_BLOCK_MAX_LEVEL) {
+ pr_info("vdec_input %p reaching max size\n",
+ input);
+ return -EAGAIN;
+ }
+
+ /*
+ *check next block of current wr_block, it should be an empty
+ * block to use
+ */
+ if ((block) && (!list_is_last(
+ &block->list, &input->vframe_block_list))) {
+ block = list_next_entry(block, list);
+
+ if (vframe_block_space(block) != VFRAME_BLOCK_SIZE)
+ /* should never happen */
+ pr_err("next writing block not empty.\n");
+ } else {
+ /* add a new block into input list */
+ new_block = kzalloc(sizeof(struct vframe_block_list_s),
+ GFP_KERNEL);
+ if (new_block == NULL)
+ return -EAGAIN;
+
+ if (vframe_block_init_alloc_storage(input,
+ new_block) != 0) {
+ kfree(new_block);
+ pr_err("vframe_block storage allocation failed\n");
+ return -EAGAIN;
+ }
+
+ INIT_LIST_HEAD(&new_block->list);
+
+ vdec_input_add_block(input, new_block);
+
+ /* pr_info("added new block %p\n", new_block); */
+
+ block = new_block;
+ }
+ }
+
+ chunk = kzalloc(sizeof(struct vframe_chunk_s), GFP_KERNEL);
+
+ if (!chunk)
+ /*pr_err("vframe_chunk structure allocation failed\n");*/
+ return -ENOMEM;
+
+ chunk->magic = 0x4b554843;
+ if (vdec->pts_valid) {
+ chunk->pts = vdec->pts;
+ chunk->pts64 = vdec->pts64;
+ }
+ chunk->pts_valid = vdec->pts_valid;
+ vdec->pts_valid = false;
+ chunk->offset = block->wp;
+ chunk->size = count;
+
+ INIT_LIST_HEAD(&chunk->list);
+
+ if (vframe_chunk_fill(input, chunk, buf, count, block)) {
+ pr_err("vframe_chunk_fill failed\n");
+ kfree(chunk);
+ return -EFAULT;
+ }
+
+ flags = vdec_input_lock(input);
+
+ vframe_block_add_chunk(block, chunk);
+
+ list_add_tail(&chunk->list, &input->vframe_chunk_list);
+
+ vdec_input_unlock(input, flags);
+
+ input->total_wr_count += count;
+
+#if 0
+ if (add_count == 2)
+ input->total_wr_count += 38;
+#endif
+
+ return count;
+}
+EXPORT_SYMBOL(vdec_input_add_frame);
+
+struct vframe_chunk_s *vdec_input_next_chunk(struct vdec_input_s *input)
+{
+ if (list_empty(&input->vframe_chunk_list))
+ return NULL;
+
+ return list_first_entry(&input->vframe_chunk_list,
+ struct vframe_chunk_s, list);
+}
+EXPORT_SYMBOL(vdec_input_next_chunk);
+
+struct vframe_chunk_s *vdec_input_next_input_chunk(
+ struct vdec_input_s *input)
+{
+ struct vframe_chunk_s *chunk = NULL;
+ struct list_head *p;
+
+ list_for_each(p, &input->vframe_chunk_list) {
+ struct vframe_chunk_s *c = list_entry(
+ p, struct vframe_chunk_s, list);
+ if ((c->flag & VFRAME_CHUNK_FLAG_CONSUMED) == 0) {
+ chunk = c;
+ break;
+ }
+ }
+
+ return chunk;
+}
+EXPORT_SYMBOL(vdec_input_next_input_chunk);
+
+void vdec_input_release_chunk(struct vdec_input_s *input,
+ struct vframe_chunk_s *chunk)
+{
+ unsigned long flags;
+ struct vframe_block_list_s *block = chunk->block;
+
+ flags = vdec_input_lock(input);
+
+ list_del(&chunk->list);
+
+ block->rp += chunk->size;
+ if (block->rp >= block->size)
+ block->rp -= block->size;
+ block->chunk_count--;
+ input->total_rd_count += chunk->size;
+
+ if (block->chunk_count == 0) {
+ /* reuse the block */
+ block->wp = 0;
+ block->rp = 0;
+
+ if ((input->wr_block == block) &&
+ (!list_is_last(&block->list,
+ &input->vframe_block_list)))
+ input->wr_block = list_next_entry(block, list);
+
+ list_move_tail(&block->list, &input->vframe_block_list);
+ }
+
+ vdec_input_unlock(input, flags);
+
+ kfree(chunk);
+}
+EXPORT_SYMBOL(vdec_input_release_chunk);
+
+unsigned long vdec_input_lock(struct vdec_input_s *input)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&input->lock, flags);
+
+ return flags;
+}
+EXPORT_SYMBOL(vdec_input_lock);
+
+void vdec_input_unlock(struct vdec_input_s *input, unsigned long flags)
+{
+ spin_unlock_irqrestore(&input->lock, flags);
+}
+EXPORT_SYMBOL(vdec_input_unlock);
+
+void vdec_input_release(struct vdec_input_s *input)
+{
+ struct list_head *p, *tmp;
+
+ /* release chunk data */
+ list_for_each_safe(p, tmp, &input->vframe_chunk_list) {
+ struct vframe_chunk_s *chunk = list_entry(
+ p, struct vframe_chunk_s, list);
+ vdec_input_release_chunk(input, chunk);
+ }
+
+ /* release input blocks */
+ list_for_each_safe(p, tmp, &input->vframe_block_list) {
+ struct vframe_block_list_s *block = list_entry(
+ p, struct vframe_block_list_s, list);
+ vdec_input_remove_block(input, block);
+ }
+}
+EXPORT_SYMBOL(vdec_input_release);
+
diff --git a/drivers/frame_provider/decoder/utils/vdec_input.h b/drivers/frame_provider/decoder/utils/vdec_input.h
new file mode 100644
index 0000000..2b952a7
--- a/dev/null
+++ b/drivers/frame_provider/decoder/utils/vdec_input.h
@@ -0,0 +1,131 @@
+/*
+ * drivers/amlogic/media/frame_provider/decoder/utils/vdec_input.h
+ *
+ * Copyright (C) 2016 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 VDEC_INPUT_H
+#define VDEC_INPUT_H
+
+struct vdec_s;
+struct vdec_input_s;
+
+struct vframe_block_list_s {
+ u32 magic;
+ struct list_head list;
+ ulong start;
+ void *start_virt;
+ ulong addr;
+ int type;
+ u32 size;
+ u32 wp;
+ u32 rp;
+ int chunk_count;
+ struct vdec_input_s *input;
+};
+
+#define VFRAME_CHUNK_FLAG_CONSUMED 0x0001
+
+struct vframe_chunk_s {
+ u32 magic;
+ struct list_head list;
+ int flag;
+ u32 offset;
+ u32 size;
+ u32 pts;
+ u64 pts64;
+ bool pts_valid;
+ u64 sequence;
+ struct vframe_block_list_s *block;
+};
+
+#define VDEC_INPUT_TARGET_VLD 0
+#define VDEC_INPUT_TARGET_HEVC 1
+
+struct vdec_input_s {
+ struct list_head vframe_block_list;
+ struct list_head vframe_chunk_list;
+ struct vframe_block_list_s *wr_block;
+ spinlock_t lock;
+ int type;
+ int target;
+ struct vdec_s *vdec;
+ bool swap_valid;
+ bool swap_needed;
+ struct page *swap_page;
+ int total_wr_count;
+ int total_rd_count;
+ u64 sequence;
+ unsigned start;
+ unsigned size;
+ int stream_cookie; /* wrap count for vld_mem and*/
+ /*HEVC_SHIFT_BYTE_COUNT for hevc */
+};
+
+struct vdec_input_status_s {
+ int size;
+ int data_len;
+ int free_len;
+ int read_pointer;
+};
+
+#define input_frame_based(input) \
+ (((input)->type == VDEC_TYPE_FRAME_BLOCK) || \
+ ((input)->type == VDEC_TYPE_FRAME_CIRCULAR))
+#define input_stream_based(input) \
+ (((input)->type == VDEC_TYPE_STREAM_PARSER) || \
+ ((input)->type == VDEC_TYPE_SINGLE))
+
+/* Initialize vdec_input structure */
+extern void vdec_input_init(struct vdec_input_s *input, struct vdec_s *vdec);
+
+/* Get available input data size */
+extern int vdec_input_level(struct vdec_input_s *input);
+
+/* Set input type and target */
+extern void vdec_input_set_type(struct vdec_input_s *input, int type,
+ int target);
+
+/* Set stream buffer information for stream based input */
+extern int vdec_input_set_buffer(struct vdec_input_s *input, u32 start,
+ u32 size);
+
+/* Add enqueue video data into decoder's input */
+extern int vdec_input_add_frame(struct vdec_input_s *input, const char *buf,
+ size_t count);
+
+/* Peek next frame data from decoder's input */
+extern struct vframe_chunk_s
+ *vdec_input_next_chunk(struct vdec_input_s *input);
+
+/* Peek next frame data from decoder's input, not marked as consumed */
+extern struct vframe_chunk_s *vdec_input_next_input_chunk(struct vdec_input_s
+ *input);
+
+/* Consume next frame data from decoder's input */
+extern void vdec_input_release_chunk(struct vdec_input_s *input,
+ struct vframe_chunk_s *chunk);
+
+/* Get decoder input buffer status */
+extern int vdec_input_get_status(struct vdec_input_s *input,
+ struct vdec_input_status_s *status);
+
+extern unsigned long vdec_input_lock(struct vdec_input_s *input);
+
+extern void vdec_input_unlock(struct vdec_input_s *input, unsigned long lock);
+
+/* release all resource for decoder's input */
+extern void vdec_input_release(struct vdec_input_s *input);
+
+#endif /* VDEC_INPUT_H */
diff --git a/drivers/stream_input/Makefile b/drivers/stream_input/Makefile
new file mode 100644
index 0000000..fa6ea32
--- a/dev/null
+++ b/drivers/stream_input/Makefile
@@ -0,0 +1,9 @@
+obj-m += stream_input.o
+stream_input-objs += amports/amstream.o
+stream_input-objs += amports/amstream_profile.o
+stream_input-objs += amports/adec.o
+stream_input-objs += parser/thread_rw.o
+stream_input-objs += parser/streambuf.o
+stream_input-objs += parser/esparser.o
+stream_input-objs += parser/tsdemux.o
+stream_input-objs += parser/psparser.o
diff --git a/drivers/stream_input/amports/Makefile b/drivers/stream_input/amports/Makefile
new file mode 100644
index 0000000..55fbdce
--- a/dev/null
+++ b/drivers/stream_input/amports/Makefile
@@ -0,0 +1,2 @@
+obj-y += amports.o
+amports-objs += amstream.o amstream_profile.o adec.o
diff --git a/drivers/stream_input/amports/adec.c b/drivers/stream_input/amports/adec.c
new file mode 100644
index 0000000..220c888
--- a/dev/null
+++ b/drivers/stream_input/amports/adec.c
@@ -0,0 +1,295 @@
+/*
+ * drivers/amlogic/media/stream_input/amports/adec.c
+ *
+ * Copyright (C) 2016 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.
+ *
+*/
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/uio_driver.h>
+
+#include <linux/amlogic/media/utils/aformat.h>
+#include <linux/amlogic/media/frame_sync/ptsserv.h>
+
+#include <linux/amlogic/media/registers/register.h>
+
+#include "../parser/streambuf.h"
+#include <linux/module.h>
+#include "amports_priv.h"
+
+#define INFO_VALID ((astream_dev) && (astream_dev->format))
+
+struct astream_device_s {
+ char *name;
+ char *format;
+ s32 channum;
+ s32 samplerate;
+ s32 datawidth;
+
+ struct device dev;
+};
+
+static char *astream_format[] = {
+ "amadec_mpeg",
+ "amadec_pcm_s16le",
+ "amadec_aac",
+ "amadec_ac3",
+ "amadec_alaw",
+ "amadec_mulaw",
+ "amadec_dts",
+ "amadec_pcm_s16be",
+ "amadec_flac",
+ "amadec_cook",
+ "amadec_pcm_u8",
+ "amadec_adpcm",
+ "amadec_amr",
+ "amadec_raac",
+ "amadec_wma",
+ "amadec_wmapro",
+ "amadec_pcm_bluray",
+ "amadec_alac",
+ "amadec_vorbis",
+ "amadec_aac_latm",
+ "amadec_ape",
+ "amadec_eac3",
+ "amadec_pcm_widi",
+ "amadec_wmavoi"
+};
+
+static const char *na_string = "NA";
+static struct astream_device_s *astream_dev;
+
+static ssize_t format_show(struct class *class, struct class_attribute *attr,
+ char *buf)
+{
+ if (INFO_VALID && astream_dev->format)
+ return sprintf(buf, "%s\n", astream_dev->format);
+ else
+ return sprintf(buf, "%s\n", na_string);
+}
+
+static ssize_t channum_show(struct class *class, struct class_attribute *attr,
+ char *buf)
+{
+ if (INFO_VALID)
+ return sprintf(buf, "%u\n", astream_dev->channum);
+ else
+ return sprintf(buf, "%s\n", na_string);
+}
+
+static ssize_t samplerate_show(struct class *class,
+ struct class_attribute *attr, char *buf)
+{
+ if (INFO_VALID)
+ return sprintf(buf, "%u\n", astream_dev->samplerate);
+ else
+ return sprintf(buf, "%s\n", na_string);
+}
+
+static ssize_t datawidth_show(struct class *class,
+ struct class_attribute *attr,
+ char *buf)
+{
+ if (INFO_VALID)
+ return sprintf(buf, "%u\n", astream_dev->datawidth);
+ else
+ return sprintf(buf, "%s\n", na_string);
+}
+
+static ssize_t pts_show(struct class *class, struct class_attribute *attr,
+ char *buf)
+{
+ u32 pts;
+ u32 pts_margin = 0;
+
+ if (astream_dev->samplerate <= 12000)
+ pts_margin = 512;
+
+ if (INFO_VALID && (pts_lookup(PTS_TYPE_AUDIO, &pts, pts_margin) >= 0))
+ return sprintf(buf, "0x%x\n", pts);
+ else
+ return sprintf(buf, "%s\n", na_string);
+}
+
+static struct class_attribute astream_class_attrs[] = {
+ __ATTR_RO(format),
+ __ATTR_RO(samplerate),
+ __ATTR_RO(channum),
+ __ATTR_RO(datawidth),
+ __ATTR_RO(pts),
+ __ATTR_NULL
+};
+
+static struct class astream_class = {
+ .name = "astream",
+ .class_attrs = astream_class_attrs,
+ };
+
+#if 1
+#define IO_CBUS_PHY_BASE 0xc1100000
+#define CBUS_REG_OFFSET(reg) ((reg) << 2)
+#define IO_SECBUS_PHY_BASE 0xda000000
+
+static struct uio_info astream_uio_info = {
+ .name = "astream_uio",
+ .version = "0.1",
+ .irq = UIO_IRQ_NONE,
+
+ .mem = {
+ [0] = {
+ .name = "AIFIFO",
+ .memtype = UIO_MEM_PHYS,
+ .addr =
+ (IO_CBUS_PHY_BASE + CBUS_REG_OFFSET(AIU_AIFIFO_CTRL))
+ &(PAGE_MASK),
+ .size = PAGE_SIZE,
+ },
+ [1] = {
+ .memtype = UIO_MEM_PHYS,
+ .addr =
+ (IO_CBUS_PHY_BASE + CBUS_REG_OFFSET(VCOP_CTRL_REG)),
+ .size = PAGE_SIZE,
+ },
+ [2] = {
+ .name = "SECBUS",
+ .memtype = UIO_MEM_PHYS,
+ .addr = (IO_SECBUS_PHY_BASE),
+ .size = PAGE_SIZE,
+ },
+ [3] = {
+ .name = "CBUS",
+ .memtype = UIO_MEM_PHYS,
+ .addr =
+ (IO_CBUS_PHY_BASE + CBUS_REG_OFFSET(ASSIST_HW_REV))
+ &(PAGE_MASK),
+ .size = PAGE_SIZE,
+ },
+ [4] = {
+ .name = "CBUS-START",
+ .memtype = UIO_MEM_PHYS,
+ .addr = (IO_CBUS_PHY_BASE + CBUS_REG_OFFSET(0x1000)),
+ .size = PAGE_SIZE * 4,
+ },
+ },
+};
+#endif
+
+static void astream_release(struct device *dev)
+{
+ kfree(astream_dev);
+
+ astream_dev = NULL;
+}
+
+s32 adec_init(struct stream_port_s *port)
+{
+ enum aformat_e af;
+
+ if (!astream_dev)
+ return -ENODEV;
+
+ af = port->aformat;
+
+ astream_dev->channum = port->achanl;
+ astream_dev->samplerate = port->asamprate;
+ astream_dev->datawidth = port->adatawidth;
+
+ /*wmb();don't need it...*/
+ if (af <= ARRAY_SIZE(astream_format))
+ astream_dev->format = astream_format[af];
+ else
+ astream_dev->format = NULL;
+ return 0;
+}
+
+s32 adec_release(enum aformat_e vf)
+{
+ pr_info("adec_release\n");
+
+ if (!astream_dev)
+ return -ENODEV;
+
+ astream_dev->format = NULL;
+
+ return 0;
+}
+
+s32 astream_dev_register(void)
+{
+ s32 r;
+
+ r = class_register(&astream_class);
+ if (r) {
+ pr_info("astream class create fail.\n");
+ return r;
+ }
+
+ astream_dev = kzalloc(sizeof(struct astream_device_s), GFP_KERNEL);
+
+ if (!astream_dev) {
+ r = -ENOMEM;
+ goto err_3;
+ }
+
+ astream_dev->dev.class = &astream_class;
+ astream_dev->dev.release = astream_release;
+
+ dev_set_name(&astream_dev->dev, "astream-dev");
+
+ dev_set_drvdata(&astream_dev->dev, astream_dev);
+
+ r = device_register(&astream_dev->dev);
+ if (r) {
+ pr_info("astream device register fail.\n");
+ goto err_2;
+ }
+
+#if 1
+ if (uio_register_device(&astream_dev->dev, &astream_uio_info)) {
+ pr_info("astream UIO device register fail.\n");
+ r = -ENODEV;
+ goto err_1;
+ }
+#endif
+
+ return 0;
+
+err_1:
+ device_unregister(&astream_dev->dev);
+
+err_2:
+ kfree(astream_dev);
+ astream_dev = NULL;
+
+err_3:
+ class_unregister(&astream_class);
+
+ return r;
+}
+
+void astream_dev_unregister(void)
+{
+ if (astream_dev) {
+#if 1
+ uio_unregister_device(&astream_uio_info);
+#endif
+
+ device_unregister(&astream_dev->dev);
+
+ class_unregister(&astream_class);
+ }
+}
diff --git a/drivers/stream_input/amports/adec.h b/drivers/stream_input/amports/adec.h
new file mode 100644
index 0000000..545ac76
--- a/dev/null
+++ b/drivers/stream_input/amports/adec.h
@@ -0,0 +1,32 @@
+/*
+ * drivers/amlogic/media/stream_input/amports/adec.h
+ *
+ * Copyright (C) 2016 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 ADEC_H
+#define ADEC_H
+
+#include "../parser/streambuf.h"
+#include <linux/amlogic/media/utils/aformat.h>
+
+extern s32 adec_init(struct stream_port_s *port);
+
+extern s32 adec_release(enum aformat_e af);
+
+extern s32 astream_dev_register(void);
+
+extern s32 astream_dev_unregister(void);
+
+#endif /* ADEC_H */
diff --git a/drivers/stream_input/amports/amports_priv.h b/drivers/stream_input/amports/amports_priv.h
new file mode 100644
index 0000000..4b6f96e
--- a/dev/null
+++ b/drivers/stream_input/amports/amports_priv.h
@@ -0,0 +1,56 @@
+/*
+ * drivers/amlogic/media/stream_input/amports/amports_priv.h
+ *
+ * Copyright (C) 2016 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 AMPORTS_PRIV_HEAD_HH
+#define AMPORTS_PRIV_HEAD_HH
+#include "../parser/streambuf.h"
+#include "../../common/media_clock/switch/amports_gate.h"
+#include <linux/amlogic/media/vfm/vframe.h>
+#include "../../common/firmware/firmware.h"
+#include <linux/amlogic/media/registers/register.h>
+#include <linux/amlogic/media/utils/log.h>
+
+struct port_priv_s {
+ struct vdec_s *vdec;
+ struct stream_port_s *port;
+};
+
+struct stream_buf_s *get_buf_by_type(u32 type);
+
+extern void amvenc_dos_top_reg_fix(void);
+
+/*video.c provide*/
+extern u32 trickmode_i;
+struct amvideocap_req;
+extern u32 set_blackout_policy(int policy);
+extern u32 get_blackout_policy(void);
+int calculation_stream_ext_delayed_ms(u8 type);
+int ext_get_cur_video_frame(struct vframe_s **vf, int *canvas_index);
+int ext_put_video_frame(struct vframe_s *vf);
+int ext_register_end_frame_callback(struct amvideocap_req *req);
+int amstream_request_firmware_from_sys(const char *file_name,
+ char *buf, int size);
+void set_vsync_pts_inc_mode(int inc);
+
+void set_real_audio_info(void *arg);
+#define dbg() pr_info("on %s,line %d\n", __func__, __LINE__)
+
+struct device *amports_get_dma_device(void);
+struct device *get_codec_cma_device(void);
+int amports_get_debug_flags(void);
+
+#endif
diff --git a/drivers/stream_input/amports/amstream.c b/drivers/stream_input/amports/amstream.c
new file mode 100644
index 0000000..3f4459d
--- a/dev/null
+++ b/drivers/stream_input/amports/amstream.c
@@ -0,0 +1,3648 @@
+/*
+ * drivers/amlogic/media/stream_input/amports/amstream.c
+ *
+ * Copyright (C) 2016 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.
+ *
+*/
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/mm.h>
+#include <linux/amlogic/major.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/kthread.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/types.h>
+#include <linux/uaccess.h>
+#include <linux/io.h>
+/* #include <mach/am_regs.h> */
+
+#include <linux/platform_device.h>
+#include <linux/mutex.h>
+#include <linux/poll.h>
+#include <linux/dma-mapping.h>
+#include <linux/dma-contiguous.h>
+#include <linux/uaccess.h>
+#include <linux/clk.h>
+#if 1 /* MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON6 */
+/* #include <mach/mod_gate.h> */
+/* #include <mach/power_gate.h> */
+#endif
+#include "../parser/streambuf.h"
+#include "../parser/streambuf_reg.h"
+#include "../parser/tsdemux.h"
+#include "../parser/psparser.h"
+#include "../parser/esparser.h"
+#include "../../frame_provider/decoder/utils/vdec.h"
+#include "adec.h"
+/*#include "../parser/rmparser.h"*//*mask*/
+#include "amports_priv.h"
+#include <linux/amlogic/media/utils/amports_config.h>
+#include <linux/amlogic/media/frame_sync/tsync_pcr.h>
+#include "../parser/thread_rw.h"
+
+
+#include <linux/firmware.h>
+
+#include <linux/of.h>
+#include <linux/of_fdt.h>
+#include <linux/libfdt_env.h>
+#include <linux/of_reserved_mem.h>
+#include <linux/reset.h>
+#include "../../common/firmware/firmware.h"
+#ifdef CONFIG_COMPAT
+#include <linux/compat.h>
+#endif
+#include <linux/amlogic/media/codec_mm/codec_mm.h>
+
+
+#define DEVICE_NAME "amstream-dev"
+#define DRIVER_NAME "amstream"
+#define MODULE_NAME "amstream"
+
+#define MAX_AMSTREAM_PORT_NUM ARRAY_SIZE(ports)
+u32 amstream_port_num;
+u32 amstream_buf_num;
+
+#if 0
+#if MESON_CPU_TYPE == MESON_CPU_TYPE_MESONG9TV
+#define NO_VDEC2_INIT 1
+#elif MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON6TVD
+#define NO_VDEC2_INIT IS_MESON_M8M2_CPU
+#endif
+#endif
+#define NO_VDEC2_INIT 1
+
+
+static int debugflags;
+
+/* #define DATA_DEBUG */
+static int use_bufferlevelx10000 = 10000;
+static int reset_canuse_buferlevel(int level);
+static struct platform_device *amstream_pdev;
+struct device *amports_get_dma_device(void)
+{
+ return &amstream_pdev->dev;
+}
+EXPORT_SYMBOL(amports_get_dma_device);
+
+/*
+*bit0:no threadrw
+*/
+int amports_get_debug_flags(void)
+{
+ return debugflags;
+}
+
+#ifdef DATA_DEBUG
+#include <linux/fs.h>
+#define DEBUG_FILE_NAME "/tmp/debug.tmp"
+static struct file *debug_filp;
+static loff_t debug_file_pos;
+
+void debug_file_write(const char __user *buf, size_t count)
+{
+ mm_segment_t old_fs;
+
+ if (!debug_filp)
+ return;
+
+ old_fs = get_fs();
+ set_fs(KERNEL_DS);
+
+ if (count != vfs_write(debug_filp, buf, count, &debug_file_pos))
+ pr_err("Failed to write debug file\n");
+
+ set_fs(old_fs);
+}
+#endif
+
+#define DEFAULT_VIDEO_BUFFER_SIZE (1024*1024*15)
+#define DEFAULT_VIDEO_BUFFER_SIZE_4K (1024*1024*15)
+
+#define DEFAULT_AUDIO_BUFFER_SIZE (1024*768*2)
+#define DEFAULT_SUBTITLE_BUFFER_SIZE (1024*256)
+
+static int amstream_open(struct inode *inode, struct file *file);
+static int amstream_release(struct inode *inode, struct file *file);
+static long amstream_ioctl(struct file *file, unsigned int cmd, ulong arg);
+#ifdef CONFIG_COMPAT
+static long amstream_compat_ioctl
+ (struct file *file, unsigned int cmd, ulong arg);
+#endif
+static ssize_t amstream_vbuf_write
+(struct file *file, const char *buf, size_t count, loff_t *ppos);
+static ssize_t amstream_vframe_write
+(struct file *file, const char *buf, size_t count, loff_t *ppos);
+static ssize_t amstream_abuf_write
+(struct file *file, const char *buf, size_t count, loff_t *ppos);
+static ssize_t amstream_mpts_write
+(struct file *file, const char *buf, size_t count, loff_t *ppos);
+static ssize_t amstream_mpps_write
+(struct file *file, const char *buf, size_t count, loff_t *ppos);
+static ssize_t amstream_sub_read
+(struct file *file, char *buf, size_t count, loff_t *ppos);
+static ssize_t amstream_sub_write
+(struct file *file, const char *buf, size_t count, loff_t *ppos);
+static unsigned int amstream_sub_poll
+(struct file *file, poll_table *wait_table);
+static unsigned int amstream_userdata_poll
+(struct file *file, poll_table *wait_table);
+static ssize_t amstream_userdata_read
+(struct file *file, char *buf, size_t count, loff_t *ppos);
+static int (*amstream_adec_status)
+(struct adec_status *astatus);
+#ifdef CONFIG_AM_VDEC_REAL
+static ssize_t amstream_mprm_write
+(struct file *file, const char *buf, size_t count, loff_t *ppos);
+#endif
+
+static const struct file_operations vbuf_fops = {
+ .owner = THIS_MODULE,
+ .open = amstream_open,
+ .release = amstream_release,
+ .write = amstream_vbuf_write,
+ .unlocked_ioctl = amstream_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = amstream_compat_ioctl,
+#endif
+};
+
+static const struct file_operations vframe_fops = {
+ .owner = THIS_MODULE,
+ .open = amstream_open,
+ .release = amstream_release,
+ .write = amstream_vframe_write,
+ .unlocked_ioctl = amstream_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = amstream_compat_ioctl,
+#endif
+};
+
+static const struct file_operations abuf_fops = {
+ .owner = THIS_MODULE,
+ .open = amstream_open,
+ .release = amstream_release,
+ .write = amstream_abuf_write,
+ .unlocked_ioctl = amstream_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = amstream_compat_ioctl,
+#endif
+};
+
+static const struct file_operations mpts_fops = {
+ .owner = THIS_MODULE,
+ .open = amstream_open,
+ .release = amstream_release,
+ .write = amstream_mpts_write,
+ .unlocked_ioctl = amstream_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = amstream_compat_ioctl,
+#endif
+};
+
+static const struct file_operations mpps_fops = {
+ .owner = THIS_MODULE,
+ .open = amstream_open,
+ .release = amstream_release,
+ .write = amstream_mpps_write,
+ .unlocked_ioctl = amstream_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = amstream_compat_ioctl,
+#endif
+};
+
+static const struct file_operations mprm_fops = {
+ .owner = THIS_MODULE,
+ .open = amstream_open,
+ .release = amstream_release,
+#ifdef CONFIG_AM_VDEC_REAL
+ .write = amstream_mprm_write,
+#endif
+ .unlocked_ioctl = amstream_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = amstream_compat_ioctl,
+#endif
+};
+
+static const struct file_operations sub_fops = {
+ .owner = THIS_MODULE,
+ .open = amstream_open,
+ .release = amstream_release,
+ .write = amstream_sub_write,
+ .unlocked_ioctl = amstream_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = amstream_compat_ioctl,
+#endif
+};
+
+static const struct file_operations sub_read_fops = {
+ .owner = THIS_MODULE,
+ .open = amstream_open,
+ .release = amstream_release,
+ .read = amstream_sub_read,
+ .poll = amstream_sub_poll,
+ .unlocked_ioctl = amstream_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = amstream_compat_ioctl,
+#endif
+};
+
+static const struct file_operations userdata_fops = {
+ .owner = THIS_MODULE,
+ .open = amstream_open,
+ .release = amstream_release,
+ .read = amstream_userdata_read,
+ .poll = amstream_userdata_poll,
+ .unlocked_ioctl = amstream_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = amstream_compat_ioctl,
+#endif
+};
+
+static const struct file_operations amstream_fops = {
+ .owner = THIS_MODULE,
+ .open = amstream_open,
+ .release = amstream_release,
+ .unlocked_ioctl = amstream_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = amstream_compat_ioctl,
+#endif
+};
+
+/**************************************************/
+static struct audio_info audio_dec_info;
+static struct class *amstream_dev_class;
+static DEFINE_MUTEX(amstream_mutex);
+
+atomic_t subdata_ready = ATOMIC_INIT(0);
+static int sub_type;
+static int sub_port_inited;
+/* wait queue for poll */
+static wait_queue_head_t amstream_sub_wait;
+atomic_t userdata_ready = ATOMIC_INIT(0);
+static int userdata_length;
+static wait_queue_head_t amstream_userdata_wait;
+#define USERDATA_FIFO_NUM 1024
+static struct userdata_poc_info_t userdata_poc_info[USERDATA_FIFO_NUM];
+static int userdata_poc_ri = 0, userdata_poc_wi;
+
+static struct stream_port_s ports[] = {
+ {
+ .name = "amstream_vbuf",
+ .type = PORT_TYPE_ES | PORT_TYPE_VIDEO,
+ .fops = &vbuf_fops,
+ },
+#ifdef CONFIG_MULTI_DEC
+ {
+ .name = "amstream_vbuf_sched",
+ .type = PORT_TYPE_ES | PORT_TYPE_VIDEO |
+ PORT_TYPE_DECODER_SCHED,
+ .fops = &vbuf_fops,
+ },
+ {
+ .name = "amstream_vframe",
+ .type = PORT_TYPE_ES | PORT_TYPE_VIDEO |
+ PORT_TYPE_FRAME | PORT_TYPE_DECODER_SCHED,
+ .fops = &vframe_fops,
+ },
+#endif
+ {
+ .name = "amstream_abuf",
+ .type = PORT_TYPE_ES | PORT_TYPE_AUDIO,
+ .fops = &abuf_fops,
+ },
+ {
+ .name = "amstream_mpts",
+ .type = PORT_TYPE_MPTS | PORT_TYPE_VIDEO |
+ PORT_TYPE_AUDIO | PORT_TYPE_SUB,
+ .fops = &mpts_fops,
+ },
+#ifdef CONFIG_MULTI_DEC
+ {
+ .name = "amstream_mpts_sched",
+ .type = PORT_TYPE_MPTS | PORT_TYPE_VIDEO |
+ PORT_TYPE_AUDIO | PORT_TYPE_SUB |
+ PORT_TYPE_DECODER_SCHED,
+ .fops = &mpts_fops,
+ },
+#endif
+ {
+ .name = "amstream_mpps",
+ .type = PORT_TYPE_MPPS | PORT_TYPE_VIDEO |
+ PORT_TYPE_AUDIO | PORT_TYPE_SUB,
+ .fops = &mpps_fops,
+ },
+ {
+ .name = "amstream_rm",
+ .type = PORT_TYPE_RM | PORT_TYPE_VIDEO | PORT_TYPE_AUDIO,
+ .fops = &mprm_fops,
+ },
+ {
+ .name = "amstream_sub",
+ .type = PORT_TYPE_SUB,
+ .fops = &sub_fops,
+ },
+ {
+ .name = "amstream_sub_read",
+ .type = PORT_TYPE_SUB_RD,
+ .fops = &sub_read_fops,
+ },
+ {
+ .name = "amstream_userdata",
+ .type = PORT_TYPE_USERDATA,
+ .fops = &userdata_fops,
+ },
+ {
+ .name = "amstream_hevc",
+ .type = PORT_TYPE_ES | PORT_TYPE_VIDEO | PORT_TYPE_HEVC,
+ .fops = &vbuf_fops,
+ .vformat = VFORMAT_HEVC,
+ }
+#ifdef CONFIG_MULTI_DEC
+ ,
+ {
+ .name = "amstream_hevc_frame",
+ .type = PORT_TYPE_ES | PORT_TYPE_VIDEO | PORT_TYPE_HEVC |
+ PORT_TYPE_FRAME | PORT_TYPE_DECODER_SCHED,
+ .fops = &vframe_fops,
+ .vformat = VFORMAT_HEVC,
+ },
+ {
+ .name = "amstream_hevc_sched",
+ .type = PORT_TYPE_ES | PORT_TYPE_VIDEO | PORT_TYPE_HEVC |
+ PORT_TYPE_DECODER_SCHED,
+ .fops = &vbuf_fops,
+ .vformat = VFORMAT_HEVC,
+ }
+#endif
+};
+
+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
+ },
+};
+
+struct stream_buf_s *get_buf_by_type(u32 type)
+{
+ if (type == PTS_TYPE_VIDEO)
+ return &bufs[BUF_TYPE_VIDEO];
+ if (type == PTS_TYPE_AUDIO)
+ return &bufs[BUF_TYPE_AUDIO];
+ if (has_hevc_vdec()) {
+ if (type == PTS_TYPE_HEVC)
+ return &bufs[BUF_TYPE_HEVC];
+ }
+
+ return NULL;
+}
+
+void set_sample_rate_info(int arg)
+{
+ audio_dec_info.sample_rate = arg;
+ audio_dec_info.valid = 1;
+}
+
+void set_ch_num_info(int arg)
+{
+ audio_dec_info.channels = arg;
+}
+
+struct audio_info *get_audio_info(void)
+{
+ return &audio_dec_info;
+}
+EXPORT_SYMBOL(get_audio_info);
+
+static void amstream_change_vbufsize(struct port_priv_s *priv,
+ struct stream_buf_s *pvbuf)
+{
+ if (pvbuf->buf_start != 0) {
+ pr_info("streambuf is alloced before\n");
+ return;
+ }
+ if (pvbuf->for_4k) {
+ pvbuf->buf_size = DEFAULT_VIDEO_BUFFER_SIZE_4K;
+ if ((pvbuf->buf_size > 30 * SZ_1M) &&
+ (codec_mm_get_total_size() < 220 * SZ_1M)) {
+ /*if less than 250M, used 20M for 4K & 265*/
+ pvbuf->buf_size = pvbuf->buf_size >> 1;
+ }
+ } else if (pvbuf->buf_size > DEFAULT_VIDEO_BUFFER_SIZE) {
+ pvbuf->buf_size = DEFAULT_VIDEO_BUFFER_SIZE;
+ } else {
+ pvbuf->buf_size = DEFAULT_VIDEO_BUFFER_SIZE;
+ }
+ reset_canuse_buferlevel(10000);
+}
+
+static bool port_get_inited(struct port_priv_s *priv)
+{
+ struct stream_port_s *port = priv->port;
+
+ if (port->type & PORT_TYPE_VIDEO) {
+ struct vdec_s *vdec = priv->vdec;
+
+ return vdec->port_flag & PORT_FLAG_INITED;
+ }
+
+ return port->flag & PORT_FLAG_INITED;
+}
+
+static void port_set_inited(struct port_priv_s *priv)
+{
+ struct stream_port_s *port = priv->port;
+
+ if (port->type & PORT_TYPE_VIDEO) {
+ struct vdec_s *vdec = priv->vdec;
+
+ vdec->port_flag |= PORT_FLAG_INITED;
+ } else
+ port->flag |= PORT_FLAG_INITED;
+}
+
+static void video_port_release(struct port_priv_s *priv,
+ struct stream_buf_s *pbuf, int release_num)
+{
+ struct stream_port_s *port = priv->port;
+ struct vdec_s *vdec = priv->vdec;
+
+ switch (release_num) {
+ default:
+ /*fallthrough*/
+ case 0: /*release all */
+ /*fallthrough*/
+ case 4:
+ if ((port->type & PORT_TYPE_FRAME) == 0)
+ esparser_release(pbuf);
+ /*fallthrough*/
+ case 3:
+ vdec_release(vdec);
+ priv->vdec = NULL;
+ /*fallthrough*/
+ case 2:
+ if ((port->type & PORT_TYPE_FRAME) == 0)
+ stbuf_release(pbuf);
+ /*fallthrough*/
+ case 1:
+ ;
+ }
+}
+
+static int video_port_init(struct port_priv_s *priv,
+ struct stream_buf_s *pbuf)
+{
+ int r;
+ struct stream_port_s *port = priv->port;
+ struct vdec_s *vdec = priv->vdec;
+
+ if ((port->flag & PORT_FLAG_VFORMAT) == 0) {
+ pr_err("vformat not set\n");
+ return -EPERM;
+ }
+
+ if (port->vformat == VFORMAT_H264_4K2K ||
+ (priv->vdec->sys_info->height *
+ priv->vdec->sys_info->width) > 1920*1088) {
+ pbuf->for_4k = 1;
+ }
+
+ if (port->type & PORT_TYPE_FRAME) {
+ r = vdec_init(vdec,
+ (priv->vdec->sys_info->height *
+ priv->vdec->sys_info->width) > 1920*1088);
+ if (r < 0) {
+ pr_err("video_port_init %d, vdec_init failed\n",
+ __LINE__);
+ video_port_release(priv, pbuf, 2);
+ return r;
+ }
+
+ return 0;
+ }
+
+ amstream_change_vbufsize(priv, pbuf);
+
+ if (has_hevc_vdec()) {
+ if (port->type & PORT_TYPE_MPTS) {
+ if (pbuf->type == BUF_TYPE_HEVC)
+ vdec_poweroff(VDEC_1);
+ else
+ vdec_poweroff(VDEC_HEVC);
+ }
+ }
+
+ r = stbuf_init(pbuf, vdec);
+ if (r < 0) {
+ pr_err("video_port_init %d, stbuf_init failed\n", __LINE__);
+ return r;
+ }
+
+ /* todo: set path based on port flag */
+ r = vdec_init(vdec,
+ (priv->vdec->sys_info->height *
+ priv->vdec->sys_info->width) > 1920*1088);
+
+ if (r < 0) {
+ pr_err("video_port_init %d, vdec_init failed\n", __LINE__);
+ video_port_release(priv, pbuf, 2);
+ return r;
+ }
+
+ if (port->type & PORT_TYPE_ES) {
+ r = esparser_init(pbuf, vdec);
+ if (r < 0) {
+ video_port_release(priv, pbuf, 3);
+ pr_err("esparser_init() failed\n");
+ return r;
+ }
+ }
+
+ pbuf->flag |= BUF_FLAG_IN_USE;
+
+ vdec_connect(priv->vdec);
+
+ return 0;
+}
+
+static void audio_port_release(struct stream_port_s *port,
+ struct stream_buf_s *pbuf, int release_num)
+{
+ switch (release_num) {
+ default:
+ /*fallthrough*/
+ case 0: /*release all */
+ /*fallthrough*/
+ case 4:
+ esparser_release(pbuf);
+ /*fallthrough*/
+ case 3:
+ adec_release(port->vformat);
+ /*fallthrough*/
+ case 2:
+ stbuf_release(pbuf);
+ /*fallthrough*/
+ case 1:
+ ;
+ }
+}
+
+static int audio_port_reset(struct stream_port_s *port,
+ struct stream_buf_s *pbuf)
+{
+ int r;
+
+ if ((port->flag & PORT_FLAG_AFORMAT) == 0) {
+ pr_err("aformat not set\n");
+ return 0;
+ }
+
+ pts_stop(PTS_TYPE_AUDIO);
+
+ stbuf_release(pbuf);
+
+ r = stbuf_init(pbuf, NULL);
+ if (r < 0)
+ return r;
+
+ r = adec_init(port);
+ if (r < 0) {
+ audio_port_release(port, pbuf, 2);
+ return r;
+ }
+
+ if (port->type & PORT_TYPE_ES)
+ esparser_audio_reset_s(pbuf);
+
+ if (port->type & PORT_TYPE_MPTS)
+ tsdemux_audio_reset();
+
+ if (port->type & PORT_TYPE_MPPS)
+ psparser_audio_reset();
+
+#ifdef CONFIG_AM_VDEC_REAL
+ if (port->type & PORT_TYPE_RM)
+ rm_audio_reset();
+#endif
+
+ pbuf->flag |= BUF_FLAG_IN_USE;
+
+ pts_start(PTS_TYPE_AUDIO);
+
+ return 0;
+}
+
+static int sub_port_reset(struct stream_port_s *port,
+ struct stream_buf_s *pbuf)
+{
+ int r;
+
+ port->flag &= (~PORT_FLAG_INITED);
+
+ stbuf_release(pbuf);
+
+ r = stbuf_init(pbuf, NULL);
+ if (r < 0)
+ return r;
+
+ if (port->type & PORT_TYPE_MPTS)
+ tsdemux_sub_reset();
+
+ if (port->type & PORT_TYPE_MPPS)
+ psparser_sub_reset();
+
+ if (port->sid == 0xffff) { /* es sub */
+ esparser_sub_reset();
+ pbuf->flag |= BUF_FLAG_PARSER;
+ }
+
+ pbuf->flag |= BUF_FLAG_IN_USE;
+
+ port->flag |= PORT_FLAG_INITED;
+
+ return 0;
+}
+
+static int audio_port_init(struct stream_port_s *port,
+ struct stream_buf_s *pbuf)
+{
+ int r;
+
+ if ((port->flag & PORT_FLAG_AFORMAT) == 0) {
+ pr_err("aformat not set\n");
+ return 0;
+ }
+
+ r = stbuf_init(pbuf, NULL);
+ if (r < 0)
+ return r;
+ r = adec_init(port);
+ if (r < 0) {
+ audio_port_release(port, pbuf, 2);
+ return r;
+ }
+ if (port->type & PORT_TYPE_ES) {
+ r = esparser_init(pbuf, NULL);
+ if (r < 0) {
+ audio_port_release(port, pbuf, 3);
+ return r;
+ }
+ }
+ pbuf->flag |= BUF_FLAG_IN_USE;
+ return 0;
+}
+
+static void sub_port_release(struct stream_port_s *port,
+ struct stream_buf_s *pbuf)
+{
+ if ((port->sid == 0xffff) &&
+ ((port->type & (PORT_TYPE_MPPS | PORT_TYPE_MPTS)) == 0)) {
+ /* this is es sub */
+ esparser_release(pbuf);
+ }
+ stbuf_release(pbuf);
+ sub_port_inited = 0;
+}
+
+static int sub_port_init(struct stream_port_s *port, struct stream_buf_s *pbuf)
+{
+ int r;
+
+ if ((port->flag & PORT_FLAG_SID) == 0) {
+ pr_err("subtitle id not set\n");
+ return 0;
+ }
+
+ r = stbuf_init(pbuf, NULL);
+ if (r < 0)
+ return r;
+
+ if ((port->sid == 0xffff) &&
+ ((port->type & (PORT_TYPE_MPPS | PORT_TYPE_MPTS)) == 0)) {
+ /* es sub */
+ r = esparser_init(pbuf, NULL);
+ if (r < 0) {
+ sub_port_release(port, pbuf);
+ return r;
+ }
+ }
+
+ sub_port_inited = 1;
+ return 0;
+}
+
+static int amstream_port_init(struct port_priv_s *priv)
+{
+ int r;
+ struct stream_buf_s *pvbuf = &bufs[BUF_TYPE_VIDEO];
+ struct stream_buf_s *pabuf = &bufs[BUF_TYPE_AUDIO];
+ struct stream_buf_s *psbuf = &bufs[BUF_TYPE_SUBTITLE];
+ struct stream_buf_s *pubuf = &bufs[BUF_TYPE_USERDATA];
+ struct stream_port_s *port = priv->port;
+ struct vdec_s *vdec = priv->vdec;
+
+ mutex_lock(&amstream_mutex);
+
+ stbuf_fetch_init();
+
+ if (port_get_inited(priv)) {
+ mutex_unlock(&amstream_mutex);
+ return 0;
+ }
+
+ if ((port->type & PORT_TYPE_AUDIO) &&
+ (port->flag & PORT_FLAG_AFORMAT)) {
+ r = audio_port_init(port, pabuf);
+ if (r < 0) {
+ pr_err("audio_port_init failed\n");
+ goto error1;
+ }
+ }
+
+ if ((port->type & PORT_TYPE_VIDEO) &&
+ (port->flag & PORT_FLAG_VFORMAT)) {
+ pubuf->buf_size = 0;
+ pubuf->buf_start = 0;
+ pubuf->buf_wp = 0;
+ pubuf->buf_rp = 0;
+ pubuf->for_4k = 0;
+ if (has_hevc_vdec()) {
+ if (port->vformat == VFORMAT_HEVC ||
+ port->vformat == VFORMAT_VP9)
+ pvbuf = &bufs[BUF_TYPE_HEVC];
+ }
+ r = video_port_init(priv, pvbuf);
+ if (r < 0) {
+ pr_err("video_port_init failed\n");
+ goto error2;
+ }
+ }
+
+ if ((port->type & PORT_TYPE_SUB) && (port->flag & PORT_FLAG_SID)) {
+ r = sub_port_init(port, psbuf);
+ if (r < 0) {
+ pr_err("sub_port_init failed\n");
+ goto error3;
+ }
+ }
+
+ if (port->type & PORT_TYPE_MPTS) {
+ if (has_hevc_vdec()) {
+ r = tsdemux_init(
+ (port->flag & PORT_FLAG_VID) ? port->vid : 0xffff,
+ (port->flag & PORT_FLAG_AID) ? port->aid : 0xffff,
+ (port->flag & PORT_FLAG_SID) ? port->sid : 0xffff,
+ (port->pcr_inited == 1) ? port->pcrid : 0xffff,
+ (port->vformat == VFORMAT_HEVC) ||
+ (port->vformat == VFORMAT_VP9),
+ vdec);
+ } else {
+ r = tsdemux_init(
+ (port->flag & PORT_FLAG_VID) ? port->vid : 0xffff,
+ (port->flag & PORT_FLAG_AID) ? port->aid : 0xffff,
+ (port->flag & PORT_FLAG_SID) ? port->sid : 0xffff,
+ (port->pcr_inited == 1) ? port->pcrid : 0xffff,
+ 0,
+ vdec);
+ }
+
+ if (r < 0) {
+ pr_err("tsdemux_init failed\n");
+ goto error4;
+ }
+ tsync_pcr_start();
+ }
+ if (port->type & PORT_TYPE_MPPS) {
+ r = psparser_init(
+ (port->flag & PORT_FLAG_VID) ? port->vid : 0xffff,
+ (port->flag & PORT_FLAG_AID) ? port->aid : 0xffff,
+ (port->flag & PORT_FLAG_SID) ? port->sid : 0xffff,
+ priv->vdec);
+ if (r < 0) {
+ pr_err("psparser_init failed\n");
+ goto error5;
+ }
+ }
+#ifdef CONFIG_AM_VDEC_REAL
+ if (port->type & PORT_TYPE_RM) {
+ rm_set_vasid(
+ (port->flag & PORT_FLAG_VID) ? port->vid : 0xffff,
+ (port->flag & PORT_FLAG_AID) ? port->aid : 0xffff);
+ }
+#endif
+#if 1 /* MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON6TVD */
+ if (!NO_VDEC2_INIT) {
+ if ((port->type & PORT_TYPE_VIDEO)
+ && (port->vformat == VFORMAT_H264_4K2K))
+ stbuf_vdec2_init(pvbuf);
+ }
+#endif
+
+ if ((port->type & PORT_TYPE_VIDEO) &&
+ (port->flag & PORT_FLAG_VFORMAT))
+ /* connect vdec at the end after all HW initialization */
+ vdec_connect(vdec);
+
+ tsync_audio_break(0); /* clear audio break */
+ set_vsync_pts_inc_mode(0); /* clear video inc */
+
+ port_set_inited(priv);
+
+ mutex_unlock(&amstream_mutex);
+ return 0;
+ /*errors follow here */
+error5:
+ tsdemux_release();
+error4:
+ sub_port_release(port, psbuf);
+error3:
+ video_port_release(priv, pvbuf, 0);
+error2:
+ audio_port_release(port, pabuf, 0);
+error1:
+ mutex_unlock(&amstream_mutex);
+ return r;
+}
+
+static int amstream_port_release(struct port_priv_s *priv)
+{
+ struct stream_port_s *port = priv->port;
+ struct stream_buf_s *pvbuf = &bufs[BUF_TYPE_VIDEO];
+ struct stream_buf_s *pabuf = &bufs[BUF_TYPE_AUDIO];
+ struct stream_buf_s *psbuf = &bufs[BUF_TYPE_SUBTITLE];
+
+ 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_port_release(priv, pvbuf, 0);
+
+ if (port->type & PORT_TYPE_AUDIO)
+ audio_port_release(port, pabuf, 0);
+
+ if (port->type & PORT_TYPE_SUB)
+ sub_port_release(port, psbuf);
+
+ port->pcr_inited = 0;
+ port->flag = 0;
+ return 0;
+}
+
+static void amstream_change_avid(struct stream_port_s *port)
+{
+ if (port->type & PORT_TYPE_MPTS) {
+ tsdemux_change_avid(
+ (port->flag & PORT_FLAG_VID) ? port->vid : 0xffff,
+ (port->flag & PORT_FLAG_AID) ? port->aid : 0xffff);
+ }
+
+ if (port->type & PORT_TYPE_MPPS) {
+ psparser_change_avid(
+ (port->flag & PORT_FLAG_VID) ? port->vid : 0xffff,
+ (port->flag & PORT_FLAG_AID) ? port->aid : 0xffff);
+ }
+
+#ifdef CONFIG_AM_VDEC_REAL
+ if (port->type & PORT_TYPE_RM) {
+ rm_set_vasid(
+ (port->flag & PORT_FLAG_VID) ? port->vid : 0xffff,
+ (port->flag & PORT_FLAG_AID) ? port->aid : 0xffff);
+ }
+#endif
+}
+
+static void amstream_change_sid(struct stream_port_s *port)
+{
+ if (port->type & PORT_TYPE_MPTS) {
+ tsdemux_change_sid(
+ (port->flag & PORT_FLAG_SID) ? port->sid : 0xffff);
+ }
+
+ if (port->type & PORT_TYPE_MPPS) {
+ psparser_change_sid(
+ (port->flag & PORT_FLAG_SID) ? port->sid : 0xffff);
+ }
+}
+
+/**************************************************/
+static ssize_t amstream_vbuf_write(struct file *file, const char *buf,
+ size_t count, loff_t *ppos)
+{
+ struct port_priv_s *priv = (struct port_priv_s *)file->private_data;
+ struct stream_port_s *port = priv->port;
+ struct stream_buf_s *pbuf = NULL;
+ int r;
+
+
+ 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 = amstream_port_init(priv);
+ if (r < 0)
+ return r;
+ }
+
+ if (port->flag & PORT_FLAG_DRM)
+ r = drm_write(file, pbuf, buf, count);
+ else
+ r = esparser_write(file, pbuf, buf, count);
+#ifdef DATA_DEBUG
+ debug_file_write(buf, r);
+#endif
+
+ return r;
+}
+
+static ssize_t amstream_vframe_write(struct file *file, const char *buf,
+ size_t count, loff_t *ppos)
+{
+ struct port_priv_s *priv = (struct port_priv_s *)file->private_data;
+
+ return vdec_write_vframe(priv->vdec, buf, count);
+}
+
+static ssize_t amstream_abuf_write(struct file *file, const char *buf,
+ size_t count, loff_t *ppos)
+{
+ struct port_priv_s *priv = (struct port_priv_s *)file->private_data;
+ struct stream_port_s *port = priv->port;
+ struct stream_buf_s *pbuf = &bufs[BUF_TYPE_AUDIO];
+ int r;
+
+ if (!(port_get_inited(priv))) {
+ r = amstream_port_init(priv);
+ if (r < 0)
+ return r;
+ }
+
+ if (port->flag & PORT_FLAG_DRM)
+ r = drm_write(file, pbuf, buf, count);
+ else
+ r = esparser_write(file, pbuf, buf, count);
+
+ return r;
+}
+
+static ssize_t amstream_mpts_write(struct file *file, const char *buf,
+ size_t count, loff_t *ppos)
+{
+ struct port_priv_s *priv = (struct port_priv_s *)file->private_data;
+ struct stream_port_s *port = priv->port;
+ struct stream_buf_s *pabuf = &bufs[BUF_TYPE_AUDIO];
+ struct stream_buf_s *pvbuf = NULL;
+ int r = 0;
+
+ if (has_hevc_vdec()) {
+ pvbuf = (port->vformat == VFORMAT_HEVC ||
+ port->vformat == VFORMAT_VP9) ?
+ &bufs[BUF_TYPE_HEVC] : &bufs[BUF_TYPE_VIDEO];
+ } else
+ pvbuf = &bufs[BUF_TYPE_VIDEO];
+
+ if (!(port_get_inited(priv))) {
+ r = amstream_port_init(priv);
+ if (r < 0)
+ return r;
+ }
+#ifdef DATA_DEBUG
+ debug_file_write(buf, count);
+#endif
+ if (port->flag & PORT_FLAG_DRM)
+ r = drm_tswrite(file, pvbuf, pabuf, buf, count);
+ else
+ r = tsdemux_write(file, pvbuf, pabuf, buf, count);
+ return r;
+}
+
+static ssize_t amstream_mpps_write(struct file *file, const char *buf,
+ size_t count, loff_t *ppos)
+{
+ struct port_priv_s *priv = (struct port_priv_s *)file->private_data;
+ struct stream_buf_s *pvbuf = &bufs[BUF_TYPE_VIDEO];
+ struct stream_buf_s *pabuf = &bufs[BUF_TYPE_AUDIO];
+ int r;
+
+ if (!(port_get_inited(priv))) {
+ r = amstream_port_init(priv);
+ if (r < 0)
+ return r;
+ }
+ return psparser_write(file, pvbuf, pabuf, buf, count);
+}
+
+#ifdef CONFIG_AM_VDEC_REAL
+static ssize_t amstream_mprm_write(struct file *file, const char *buf,
+ size_t count, loff_t *ppos)
+{
+ struct port_priv_s *priv = (struct port_priv_s *)file->private_data;
+ struct stream_buf_s *pvbuf = &bufs[BUF_TYPE_VIDEO];
+ struct stream_buf_s *pabuf = &bufs[BUF_TYPE_AUDIO];
+ int r;
+
+ if (!(port_get_inited(priv))) {
+ r = amstream_port_init(priv);
+ if (r < 0)
+ return r;
+ }
+ return rmparser_write(file, pvbuf, pabuf, buf, count);
+}
+#endif
+
+static ssize_t amstream_sub_read(struct file *file, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ u32 sub_rp, sub_wp, sub_start, data_size, res;
+ struct stream_buf_s *s_buf = &bufs[BUF_TYPE_SUBTITLE];
+
+ if (sub_port_inited == 0)
+ return 0;
+
+ sub_rp = stbuf_sub_rp_get();
+ sub_wp = stbuf_sub_wp_get();
+ sub_start = stbuf_sub_start_get();
+
+ if (sub_wp == sub_rp || sub_rp == 0)
+ return 0;
+
+ if (sub_wp > sub_rp)
+ data_size = sub_wp - sub_rp;
+ else
+ data_size = s_buf->buf_size - sub_rp + sub_wp;
+
+ if (data_size > count)
+ data_size = count;
+
+ if (sub_wp < sub_rp) {
+ int first_num = s_buf->buf_size - (sub_rp - sub_start);
+
+ if (data_size <= first_num) {
+ res = copy_to_user((void *)buf,
+ (void *)(codec_mm_phys_to_virt(sub_rp)),
+ data_size);
+ if (res >= 0)
+ stbuf_sub_rp_set(sub_rp + data_size - res);
+
+ return data_size - res;
+ }
+
+ if (first_num > 0) {
+ res = copy_to_user((void *)buf,
+ (void *)(codec_mm_phys_to_virt(sub_rp)),
+ first_num);
+ if (res >= 0) {
+ stbuf_sub_rp_set(sub_rp + first_num -
+ res);
+ }
+
+ return first_num - res;
+ }
+
+ res = copy_to_user((void *)buf,
+ (void *)(codec_mm_phys_to_virt(sub_start)),
+ data_size - first_num);
+
+ if (res >= 0) {
+ stbuf_sub_rp_set(sub_start + data_size -
+ first_num - res);
+ }
+
+ return data_size - first_num - res;
+ }
+ res =
+ copy_to_user((void *)buf,
+ (void *)(codec_mm_phys_to_virt(sub_rp)),
+ data_size);
+
+ if (res >= 0)
+ stbuf_sub_rp_set(sub_rp + data_size - res);
+
+ return data_size - res;
+}
+
+static ssize_t amstream_sub_write(struct file *file, const char *buf,
+ size_t count, loff_t *ppos)
+{
+ struct port_priv_s *priv = (struct port_priv_s *)file->private_data;
+ struct stream_buf_s *pbuf = &bufs[BUF_TYPE_SUBTITLE];
+ int r;
+
+ if (!(port_get_inited(priv))) {
+ r = amstream_port_init(priv);
+ if (r < 0)
+ return r;
+ }
+ r = esparser_write(file, pbuf, buf, count);
+ if (r < 0)
+ return r;
+
+ wakeup_sub_poll();
+
+ return r;
+}
+
+static unsigned int amstream_sub_poll(struct file *file,
+ poll_table *wait_table)
+{
+ poll_wait(file, &amstream_sub_wait, wait_table);
+
+ if (atomic_read(&subdata_ready)) {
+ atomic_dec(&subdata_ready);
+ return POLLOUT | POLLWRNORM;
+ }
+
+ return 0;
+}
+
+void set_userdata_poc(struct userdata_poc_info_t poc)
+{
+ /*
+ *pr_err("id %d, slicetype %d\n",
+ * userdata_slicetype_wi, slicetype);
+ */
+ userdata_poc_info[userdata_poc_wi] = poc;
+ userdata_poc_wi++;
+ if (userdata_poc_wi == USERDATA_FIFO_NUM)
+ userdata_poc_wi = 0;
+}
+EXPORT_SYMBOL(set_userdata_poc);
+
+void init_userdata_fifo(void)
+{
+ userdata_poc_ri = 0;
+ userdata_poc_wi = 0;
+}
+EXPORT_SYMBOL(init_userdata_fifo);
+
+int wakeup_userdata_poll(int wp, unsigned long start_phyaddr, int buf_size,
+ int data_length)
+{
+ struct stream_buf_s *userdata_buf = &bufs[BUF_TYPE_USERDATA];
+
+ userdata_buf->buf_start = start_phyaddr;
+ userdata_buf->buf_wp = wp;
+ userdata_buf->buf_size = buf_size;
+ atomic_set(&userdata_ready, 1);
+ userdata_length += data_length;
+ wake_up_interruptible(&amstream_userdata_wait);
+ return userdata_buf->buf_rp;
+}
+EXPORT_SYMBOL(wakeup_userdata_poll);
+
+static unsigned int amstream_userdata_poll(struct file *file,
+ poll_table *wait_table)
+{
+ poll_wait(file, &amstream_userdata_wait, wait_table);
+ if (atomic_read(&userdata_ready)) {
+ atomic_set(&userdata_ready, 0);
+ return POLLIN | POLLRDNORM;
+ }
+ return 0;
+}
+
+static ssize_t amstream_userdata_read(struct file *file, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ u32 data_size, res, retVal = 0, buf_wp;
+ struct stream_buf_s *userdata_buf = &bufs[BUF_TYPE_USERDATA];
+
+ buf_wp = userdata_buf->buf_wp;
+ if (userdata_buf->buf_start == 0 || userdata_buf->buf_size == 0)
+ return 0;
+ if (buf_wp == userdata_buf->buf_rp)
+ return 0;
+ if (buf_wp > userdata_buf->buf_rp)
+ data_size = buf_wp - userdata_buf->buf_rp;
+ else {
+ data_size =
+ userdata_buf->buf_size - userdata_buf->buf_rp + buf_wp;
+ }
+ if (data_size > count)
+ data_size = count;
+ if (buf_wp < userdata_buf->buf_rp) {
+ int first_num = userdata_buf->buf_size - userdata_buf->buf_rp;
+
+ if (data_size <= first_num) {
+ res = copy_to_user((void *)buf,
+ (void *)((userdata_buf->buf_rp +
+ userdata_buf->buf_start)), data_size);
+ userdata_buf->buf_rp += data_size - res;
+ retVal = data_size - res;
+ } else {
+ if (first_num > 0) {
+ res = copy_to_user((void *)buf,
+ (void *)((userdata_buf->buf_rp +
+ userdata_buf->buf_start)), first_num);
+ userdata_buf->buf_rp += first_num - res;
+ retVal = first_num - res;
+ } else {
+ res = copy_to_user((void *)buf,
+ (void *)((userdata_buf->buf_start)),
+ data_size - first_num);
+ userdata_buf->buf_rp =
+ data_size - first_num - res;
+ retVal = data_size - first_num - res;
+ }
+ }
+ } else {
+ res = copy_to_user((void *)buf,
+ (void *)((userdata_buf->buf_rp +
+ userdata_buf->buf_start)),
+ data_size);
+ userdata_buf->buf_rp += data_size - res;
+ retVal = data_size - res;
+ }
+ return retVal;
+}
+
+static int amstream_open(struct inode *inode, struct file *file)
+{
+ s32 i;
+ struct stream_port_s *s;
+ struct stream_port_s *port = &ports[iminor(inode)];
+ struct port_priv_s *priv;
+
+ if (iminor(inode) >= amstream_port_num)
+ return -ENODEV;
+
+ mutex_lock(&amstream_mutex);
+
+ if (port->type & PORT_TYPE_VIDEO) {
+ for (s = &ports[0], i = 0; i < amstream_port_num; i++, s++) {
+ if (((s->type & PORT_TYPE_VIDEO) == 0) ||
+ ((s->flag & PORT_FLAG_IN_USE) == 0))
+ continue;
+
+ if (((s->type & PORT_TYPE_DECODER_SCHED) == 0) ||
+ ((s->type & PORT_TYPE_FRAME) == 0)) {
+ mutex_unlock(&amstream_mutex);
+ return -EBUSY;
+ }
+ }
+ }
+
+ if ((port->flag & PORT_FLAG_IN_USE) &&
+ ((port->type & PORT_TYPE_FRAME) == 0)) {
+ mutex_unlock(&amstream_mutex);
+ return -EBUSY;
+ }
+
+ /* check other ports conflicts for audio */
+ for (s = &ports[0], i = 0; i < amstream_port_num; i++, s++) {
+ if ((s->flag & PORT_FLAG_IN_USE) &&
+ ((port->type) & (s->type) & PORT_TYPE_AUDIO)) {
+ mutex_unlock(&amstream_mutex);
+ return -EBUSY;
+ }
+ }
+
+ priv = kzalloc(sizeof(struct port_priv_s), GFP_KERNEL);
+ if (priv == NULL)
+ return -ENOMEM;
+
+ priv->port = port;
+
+ if (get_cpu_type() >= MESON_CPU_MAJOR_ID_M6) {
+
+ amports_switch_gate("clk_vdec_mux", 1);
+ amports_switch_gate("clk_hcodec_mux", 1);
+ amports_switch_gate("clk_hevc_mux", 1);
+
+ /* TODO: mod gate */
+ /* switch_mod_gate_by_name("demux", 1); */
+ amports_switch_gate("demux", 1);
+ if (get_cpu_type() >= MESON_CPU_MAJOR_ID_M8) {
+ /* TODO: clc gate */
+ /* CLK_GATE_ON(HIU_PARSER_TOP); */
+ amports_switch_gate("parser_top", 1);
+ }
+
+ if ((get_cpu_type() >= MESON_CPU_MAJOR_ID_M8)
+ && !is_meson_mtvd_cpu()) {
+ /* TODO: clc gate */
+ /* CLK_GATE_ON(VPU_INTR); */
+ amports_switch_gate("vpu_intr", 1);
+ }
+
+ if (port->type & PORT_TYPE_VIDEO) {
+ /* TODO: mod gate */
+ /* switch_mod_gate_by_name("vdec", 1); */
+ amports_switch_gate("vdec", 1);
+ if (has_hevc_vdec()) {
+ if (port->type &
+ (PORT_TYPE_MPTS | PORT_TYPE_HEVC))
+ vdec_poweron(VDEC_HEVC);
+
+ if ((port->type & PORT_TYPE_HEVC) == 0)
+ vdec_poweron(VDEC_1);
+ } else {
+ if (get_cpu_type() >= MESON_CPU_MAJOR_ID_M8)
+ vdec_poweron(VDEC_1);
+ }
+ }
+
+ if (port->type & PORT_TYPE_AUDIO) {
+ /* TODO: mod gate */
+ /* switch_mod_gate_by_name("audio", 1); */
+ amports_switch_gate("audio", 1);
+ }
+ }
+
+ port->vid = 0;
+ port->aid = 0;
+ port->sid = 0;
+ port->pcrid = 0xffff;
+ file->f_op = port->fops;
+ file->private_data = priv;
+
+ port->flag = PORT_FLAG_IN_USE;
+ port->pcr_inited = 0;
+#ifdef DATA_DEBUG
+ debug_filp = filp_open(DEBUG_FILE_NAME, O_WRONLY, 0);
+ if (IS_ERR(debug_filp)) {
+ pr_err("amstream: open debug file failed\n");
+ debug_filp = NULL;
+ }
+#endif
+ mutex_unlock(&amstream_mutex);
+
+ if (port->type & PORT_TYPE_VIDEO) {
+ int type = VDEC_TYPE_SINGLE;
+
+ if (port->type & PORT_TYPE_DECODER_SCHED)
+ type = (port->type & PORT_TYPE_FRAME) ?
+ VDEC_TYPE_FRAME_BLOCK :
+ VDEC_TYPE_STREAM_PARSER;
+
+ priv->vdec = vdec_create(type);
+
+ if (priv->vdec == NULL) {
+ kfree(priv);
+ pr_err("amstream: vdec creation failed\n");
+ return -ENOMEM;
+ }
+ }
+
+ return 0;
+}
+
+static int amstream_release(struct inode *inode, struct file *file)
+{
+ struct port_priv_s *priv = file->private_data;
+ struct stream_port_s *port = priv->port;
+
+ if (iminor(inode) >= amstream_port_num)
+ return -ENODEV;
+
+ mutex_lock(&amstream_mutex);
+
+ if (port_get_inited(priv))
+ amstream_port_release(priv);
+
+ if (priv->vdec) {
+ vdec_release(priv->vdec);
+ priv->vdec = NULL;
+ }
+
+ if ((port->type & (PORT_TYPE_AUDIO | PORT_TYPE_VIDEO)) ==
+ PORT_TYPE_AUDIO) {
+ s32 i;
+ struct stream_port_s *s;
+
+ for (s = &ports[0], i = 0; i < amstream_port_num; i++, s++) {
+ if ((s->flag & PORT_FLAG_IN_USE)
+ && (s->type & PORT_TYPE_VIDEO))
+ break;
+ }
+ if (i == amstream_port_num)
+ timestamp_firstvpts_set(0);
+ }
+ port->flag = 0;
+
+ /* /timestamp_pcrscr_set(0); */
+
+#ifdef DATA_DEBUG
+ if (debug_filp) {
+ filp_close(debug_filp, current->files);
+ debug_filp = NULL;
+ debug_file_pos = 0;
+ }
+#endif
+
+ if (get_cpu_type() >= MESON_CPU_MAJOR_ID_M6) {
+ if (port->type & PORT_TYPE_VIDEO) {
+ if (get_cpu_type() >= MESON_CPU_MAJOR_ID_M8) {
+#ifndef CONFIG_MULTI_DEC
+ if (has_hevc_vdec())
+ vdec_poweroff(VDEC_HEVC);
+
+ vdec_poweroff(VDEC_1);
+#else
+ /*
+ *todo: power control based on
+ * active decoder instances
+ */
+#endif
+ }
+ /* TODO: mod gate */
+ /* switch_mod_gate_by_name("vdec", 0); */
+ amports_switch_gate("vdec", 0);
+ }
+
+ if (port->type & PORT_TYPE_AUDIO) {
+ /* TODO: mod gate */
+ /* switch_mod_gate_by_name("audio", 0); */
+ /* amports_switch_gate("audio", 0); */
+ }
+
+ if (get_cpu_type() >= MESON_CPU_MAJOR_ID_M8
+ && !is_meson_mtvd_cpu()) {
+ /* TODO: clc gate */
+ /* /CLK_GATE_OFF(VPU_INTR); */
+ amports_switch_gate("vpu_intr", 0);
+ }
+
+ if (get_cpu_type() >= MESON_CPU_MAJOR_ID_M8) {
+ /* TODO: clc gate */
+ /* CLK_GATE_OFF(HIU_PARSER_TOP); */
+ amports_switch_gate("parser_top", 0);
+ }
+ /* TODO: mod gate */
+ /* switch_mod_gate_by_name("demux", 0); */
+ amports_switch_gate("demux", 0);
+ }
+
+ kfree(priv);
+
+ mutex_unlock(&amstream_mutex);
+ return 0;
+}
+
+static long amstream_ioctl_get_version(struct port_priv_s *priv,
+ ulong arg)
+{
+ int version = (AMSTREAM_IOC_VERSION_FIRST & 0xffff) << 16
+ | (AMSTREAM_IOC_VERSION_SECOND & 0xffff);
+ put_user(version, (u32 __user *)arg);
+
+ return 0;
+}
+static long amstream_ioctl_get(struct port_priv_s *priv, ulong arg)
+{
+ struct stream_port_s *this = priv->port;
+ long r = 0;
+
+ struct am_ioctl_parm parm;
+
+ if (copy_from_user
+ ((void *)&parm, (void *)arg,
+ sizeof(parm)))
+ r = -EFAULT;
+
+ switch (parm.cmd) {
+ case AMSTREAM_GET_SUB_LENGTH:
+ if ((this->type & PORT_TYPE_SUB) ||
+ (this->type & PORT_TYPE_SUB_RD)) {
+ u32 sub_wp, sub_rp;
+ struct stream_buf_s *psbuf = &bufs[BUF_TYPE_SUBTITLE];
+ int val;
+
+ sub_wp = stbuf_sub_wp_get();
+ sub_rp = stbuf_sub_rp_get();
+
+ if (sub_wp == sub_rp)
+ val = 0;
+ else if (sub_wp > sub_rp)
+ val = sub_wp - sub_rp;
+ else
+ val = psbuf->buf_size - (sub_rp - sub_wp);
+ parm.data_32 = val;
+ } else
+ r = -EINVAL;
+ break;
+ case AMSTREAM_GET_UD_LENGTH:
+ if (this->type & PORT_TYPE_USERDATA) {
+ parm.data_32 = userdata_length;
+ userdata_length = 0;
+ } else
+ r = -EINVAL;
+ break;
+ case AMSTREAM_GET_APTS_LOOKUP:
+ if (this->type & PORT_TYPE_AUDIO) {
+ u32 pts = 0, offset;
+
+ offset = parm.data_32;
+ pts_lookup_offset(PTS_TYPE_AUDIO, offset, &pts, 300);
+ parm.data_32 = pts;
+ }
+ break;
+ case AMSTREAM_GET_FIRST_APTS_FLAG:
+ if (this->type & PORT_TYPE_AUDIO) {
+ parm.data_32 = first_pts_checkin_complete(
+ PTS_TYPE_AUDIO);
+ }
+ break;
+ case AMSTREAM_GET_APTS:
+ parm.data_32 = timestamp_apts_get();
+ break;
+ case AMSTREAM_GET_VPTS:
+ parm.data_32 = timestamp_vpts_get();
+ break;
+ case AMSTREAM_GET_PCRSCR:
+ parm.data_32 = timestamp_pcrscr_get();
+ break;
+ case AMSTREAM_GET_LAST_CHECKIN_APTS:
+ parm.data_32 = get_last_checkin_pts(PTS_TYPE_AUDIO);
+ break;
+ case AMSTREAM_GET_LAST_CHECKIN_VPTS:
+ parm.data_32 = get_last_checkin_pts(PTS_TYPE_VIDEO);
+ break;
+ case AMSTREAM_GET_LAST_CHECKOUT_APTS:
+ parm.data_32 = get_last_checkout_pts(PTS_TYPE_AUDIO);
+ break;
+ case AMSTREAM_GET_LAST_CHECKOUT_VPTS:
+ parm.data_32 = get_last_checkout_pts(PTS_TYPE_VIDEO);
+ break;
+ case AMSTREAM_GET_SUB_NUM:
+ parm.data_32 = psparser_get_sub_found_num();
+ break;
+ case AMSTREAM_GET_VIDEO_DELAY_LIMIT_MS:
+ parm.data_32 = bufs[BUF_TYPE_VIDEO].max_buffer_delay_ms;
+ break;
+ case AMSTREAM_GET_AUDIO_DELAY_LIMIT_MS:
+ parm.data_32 = bufs[BUF_TYPE_AUDIO].max_buffer_delay_ms;
+ break;
+ case AMSTREAM_GET_VIDEO_CUR_DELAY_MS: {
+ int delay;
+
+ delay = calculation_stream_delayed_ms(
+ PTS_TYPE_VIDEO, NULL, NULL);
+ if (delay >= 0)
+ parm.data_32 = delay;
+ else
+ parm.data_32 = 0;
+ }
+ break;
+
+ case AMSTREAM_GET_AUDIO_CUR_DELAY_MS: {
+ int delay;
+
+ delay = calculation_stream_delayed_ms(
+ PTS_TYPE_AUDIO, NULL, NULL);
+ if (delay >= 0)
+ parm.data_32 = delay;
+ else
+ parm.data_32 = 0;
+ }
+ break;
+ case AMSTREAM_GET_AUDIO_AVG_BITRATE_BPS: {
+ int delay;
+ u32 avgbps;
+
+ delay = calculation_stream_delayed_ms(
+ PTS_TYPE_AUDIO, NULL, &avgbps);
+ if (delay >= 0)
+ parm.data_32 = avgbps;
+ else
+ parm.data_32 = 0;
+ }
+ break;
+ case AMSTREAM_GET_VIDEO_AVG_BITRATE_BPS: {
+ int delay;
+ u32 avgbps;
+
+ delay = calculation_stream_delayed_ms(
+ PTS_TYPE_VIDEO, NULL, &avgbps);
+ if (delay >= 0)
+ parm.data_32 = avgbps;
+ else
+ parm.data_32 = 0;
+ }
+ break;
+ case AMSTREAM_GET_ION_ID:
+ parm.data_32 = priv->vdec->vf_receiver_inst;
+ break;
+ default:
+ r = -ENOIOCTLCMD;
+ break;
+ }
+ /* pr_info("parm size:%d\n", sizeof(parm)); */
+ if (r == 0) {
+ if (copy_to_user((void *)arg, &parm, sizeof(parm)))
+ r = -EFAULT;
+ }
+
+ return r;
+
+}
+static long amstream_ioctl_set(struct port_priv_s *priv, ulong arg)
+{
+ struct stream_port_s *this = priv->port;
+ struct am_ioctl_parm parm;
+ long r = 0;
+
+ if (copy_from_user
+ ((void *)&parm, (void *)arg,
+ sizeof(parm)))
+ r = -EFAULT;
+
+
+ switch (parm.cmd) {
+ case AMSTREAM_SET_VB_START:
+ if ((this->type & PORT_TYPE_VIDEO) &&
+ ((bufs[BUF_TYPE_VIDEO].flag & BUF_FLAG_IN_USE) == 0)) {
+ if (has_hevc_vdec())
+ bufs[BUF_TYPE_HEVC].buf_start = parm.data_32;
+ bufs[BUF_TYPE_VIDEO].buf_start = parm.data_32;
+ } else
+ r = -EINVAL;
+ break;
+ case AMSTREAM_SET_VB_SIZE:
+ if ((this->type & PORT_TYPE_VIDEO) &&
+ ((bufs[BUF_TYPE_VIDEO].flag & BUF_FLAG_IN_USE) == 0)) {
+ if (bufs[BUF_TYPE_VIDEO].flag & BUF_FLAG_ALLOC) {
+ if (has_hevc_vdec()) {
+ r = stbuf_change_size(
+ &bufs[BUF_TYPE_HEVC],
+ parm.data_32);
+ }
+ r = stbuf_change_size(
+ &bufs[BUF_TYPE_VIDEO],
+ parm.data_32);
+ }
+ } else if (this->type & PORT_TYPE_FRAME) {
+ /* todo: frame based set max buffer size */
+ r = 0;
+ } else
+ r = -EINVAL;
+ break;
+ case AMSTREAM_SET_AB_START:
+ if ((this->type & PORT_TYPE_AUDIO) &&
+ ((bufs[BUF_TYPE_AUDIO].flag & BUF_FLAG_IN_USE) == 0))
+ bufs[BUF_TYPE_AUDIO].buf_start = parm.data_32;
+ else
+ r = -EINVAL;
+ break;
+ case AMSTREAM_SET_AB_SIZE:
+ if ((this->type & PORT_TYPE_AUDIO) &&
+ ((bufs[BUF_TYPE_AUDIO].flag & BUF_FLAG_IN_USE) == 0)) {
+ if (bufs[BUF_TYPE_AUDIO].flag & BUF_FLAG_ALLOC) {
+ r = stbuf_change_size(
+ &bufs[BUF_TYPE_AUDIO], parm.data_32);
+ }
+ } else
+ r = -EINVAL;
+ break;
+ case AMSTREAM_SET_VFORMAT:
+ if ((this->type & PORT_TYPE_VIDEO) &&
+ (parm.data_vformat < VFORMAT_MAX)) {
+ this->vformat = parm.data_vformat;
+ this->flag |= PORT_FLAG_VFORMAT;
+
+ vdec_set_format(priv->vdec, this->vformat);
+ } else
+ r = -EINVAL;
+ break;
+ case AMSTREAM_SET_AFORMAT:
+ if ((this->type & PORT_TYPE_AUDIO) &&
+ (parm.data_aformat < AFORMAT_MAX)) {
+ memset(&audio_dec_info, 0,
+ sizeof(struct audio_info));
+ /* for new format,reset the audio info. */
+ this->aformat = parm.data_aformat;
+ this->flag |= PORT_FLAG_AFORMAT;
+ } else
+ r = -EINVAL;
+ break;
+ case AMSTREAM_SET_VID:
+ if (this->type & PORT_TYPE_VIDEO) {
+ this->vid = parm.data_32;
+ this->flag |= PORT_FLAG_VID;
+ } else
+ r = -EINVAL;
+
+ break;
+ case AMSTREAM_SET_AID:
+ if (this->type & PORT_TYPE_AUDIO) {
+ this->aid = parm.data_32;
+ this->flag |= PORT_FLAG_AID;
+
+ if (port_get_inited(priv)) {
+ tsync_audio_break(1);
+ amstream_change_avid(this);
+ }
+ } else
+ r = -EINVAL;
+ break;
+ case AMSTREAM_SET_SID:
+ if (this->type & PORT_TYPE_SUB) {
+ this->sid = parm.data_32;
+ this->flag |= PORT_FLAG_SID;
+
+ if (port_get_inited(priv))
+ amstream_change_sid(this);
+ } else
+ r = -EINVAL;
+
+ break;
+ case AMSTREAM_IOC_PCRID:
+ this->pcrid = parm.data_32;
+ this->pcr_inited = 1;
+ pr_err("set pcrid = 0x%x\n", this->pcrid);
+ break;
+ case AMSTREAM_SET_ACHANNEL:
+ if (this->type & PORT_TYPE_AUDIO) {
+ this->achanl = parm.data_32;
+ set_ch_num_info(parm.data_32);
+ } else
+ r = -EINVAL;
+ break;
+ case AMSTREAM_SET_SAMPLERATE:
+ if (this->type & PORT_TYPE_AUDIO) {
+ this->asamprate = parm.data_32;
+ set_sample_rate_info(parm.data_32);
+ } else
+ r = -EINVAL;
+ break;
+ case AMSTREAM_SET_DATAWIDTH:
+ if (this->type & PORT_TYPE_AUDIO)
+ this->adatawidth = parm.data_32;
+ else
+ r = -EINVAL;
+ break;
+ case AMSTREAM_SET_TSTAMP:
+ if ((this->type & (PORT_TYPE_AUDIO | PORT_TYPE_VIDEO)) ==
+ ((PORT_TYPE_AUDIO | PORT_TYPE_VIDEO)))
+ r = -EINVAL;
+ else if (this->type & PORT_TYPE_FRAME)
+ r = vdec_set_pts(priv->vdec, parm.data_32);
+ else if (has_hevc_vdec() && this->type & PORT_TYPE_HEVC)
+ r = es_vpts_checkin(&bufs[BUF_TYPE_HEVC],
+ parm.data_32);
+ else if (this->type & PORT_TYPE_VIDEO)
+ r = es_vpts_checkin(&bufs[BUF_TYPE_VIDEO],
+ parm.data_32);
+ else if (this->type & PORT_TYPE_AUDIO)
+ r = es_apts_checkin(&bufs[BUF_TYPE_AUDIO],
+ parm.data_32);
+ break;
+ case AMSTREAM_SET_TSTAMP_US64:
+ if ((this->type & (PORT_TYPE_AUDIO | PORT_TYPE_VIDEO)) ==
+ ((PORT_TYPE_AUDIO | PORT_TYPE_VIDEO)))
+ r = -EINVAL;
+ else {
+ u64 pts = parm.data_64;
+
+ if (this->type & PORT_TYPE_FRAME) {
+ /*
+ *todo: check upper layer for decoder handler
+ * life sequence or multi-tasking management
+ */
+ r = vdec_set_pts64(priv->vdec, pts);
+ } else if (has_hevc_vdec()) {
+ if (this->type & PORT_TYPE_HEVC) {
+ r = es_vpts_checkin_us64(
+ &bufs[BUF_TYPE_HEVC], pts);
+ } else if (this->type & PORT_TYPE_VIDEO) {
+ r = es_vpts_checkin_us64(
+ &bufs[BUF_TYPE_VIDEO], pts);
+ } else if (this->type & PORT_TYPE_AUDIO) {
+ r = es_vpts_checkin_us64(
+ &bufs[BUF_TYPE_AUDIO], pts);
+ }
+ } else {
+ if (this->type & PORT_TYPE_VIDEO) {
+ r = es_vpts_checkin_us64(
+ &bufs[BUF_TYPE_VIDEO], pts);
+ } else if (this->type & PORT_TYPE_AUDIO) {
+ r = es_vpts_checkin_us64(
+ &bufs[BUF_TYPE_AUDIO], pts);
+ }
+ }
+ }
+ break;
+ case AMSTREAM_PORT_INIT:
+ r = amstream_port_init(priv);
+ break;
+ case AMSTREAM_SET_TRICKMODE:
+ if ((this->type & PORT_TYPE_VIDEO) == 0)
+ return -EINVAL;
+ r = vdec_set_trickmode(priv->vdec, parm.data_32);
+ if (r == -1)
+ return -ENODEV;
+ break;
+
+ case AMSTREAM_AUDIO_RESET:
+ if (this->type & PORT_TYPE_AUDIO) {
+ struct stream_buf_s *pabuf = &bufs[BUF_TYPE_AUDIO];
+
+ r = audio_port_reset(this, pabuf);
+ } else
+ r = -EINVAL;
+
+ break;
+ case AMSTREAM_SUB_RESET:
+ if (this->type & PORT_TYPE_SUB) {
+ struct stream_buf_s *psbuf = &bufs[BUF_TYPE_SUBTITLE];
+
+ r = sub_port_reset(this, psbuf);
+ } else
+ r = -EINVAL;
+ break;
+ case AMSTREAM_DEC_RESET:
+ tsync_set_dec_reset();
+ break;
+ case AMSTREAM_SET_TS_SKIPBYTE:
+ if (parm.data_32 >= 0)
+ tsdemux_set_skipbyte(parm.data_32);
+ else
+ r = -EINVAL;
+ break;
+ case AMSTREAM_SET_SUB_TYPE:
+ sub_type = parm.data_32;
+ break;
+ case AMSTREAM_SET_PCRSCR:
+ timestamp_pcrscr_set(parm.data_32);
+ break;
+ case AMSTREAM_SET_DEMUX:
+ tsdemux_set_demux(parm.data_32);
+ break;
+ case AMSTREAM_SET_VIDEO_DELAY_LIMIT_MS:
+ if (has_hevc_vdec())
+ bufs[BUF_TYPE_HEVC].max_buffer_delay_ms = parm.data_32;
+ bufs[BUF_TYPE_VIDEO].max_buffer_delay_ms = parm.data_32;
+ break;
+ case AMSTREAM_SET_AUDIO_DELAY_LIMIT_MS:
+ bufs[BUF_TYPE_AUDIO].max_buffer_delay_ms = parm.data_32;
+ break;
+ case AMSTREAM_SET_DRMMODE:
+ if (parm.data_32 == 1) {
+ pr_err("set drmmode\n");
+ this->flag |= PORT_FLAG_DRM;
+ } else {
+ this->flag &= (~PORT_FLAG_DRM);
+ pr_err("no drmmode\n");
+ }
+ break;
+ case AMSTREAM_SET_APTS: {
+ unsigned int pts;
+
+ pts = parm.data_32;
+ if (tsync_get_mode() == TSYNC_MODE_PCRMASTER)
+ tsync_pcr_set_apts(pts);
+ else
+ tsync_set_apts(pts);
+ break;
+ }
+ default:
+ r = -ENOIOCTLCMD;
+ break;
+ }
+ return r;
+}
+static long amstream_ioctl_get_ex(struct port_priv_s *priv, ulong arg)
+{
+ struct stream_port_s *this = priv->port;
+ long r = 0;
+ struct am_ioctl_parm_ex parm;
+
+ if (copy_from_user
+ ((void *)&parm, (void *)arg,
+ sizeof(parm)))
+ r = -EFAULT;
+
+ switch (parm.cmd) {
+ case AMSTREAM_GET_EX_VB_STATUS:
+ if (this->type & PORT_TYPE_VIDEO) {
+ struct am_ioctl_parm_ex *p = &parm;
+ struct stream_buf_s *buf = NULL;
+
+ buf = (this->vformat == VFORMAT_HEVC ||
+ this->vformat == VFORMAT_VP9) ?
+ &bufs[BUF_TYPE_HEVC] :
+ &bufs[BUF_TYPE_VIDEO];
+
+ if (p == NULL) {
+ r = -EINVAL;
+ break;
+ }
+
+ if (this->type & PORT_TYPE_FRAME) {
+ struct vdec_input_status_s status;
+
+ /*
+ *todo: check upper layer for decoder
+ * handler lifecycle
+ */
+ if (priv->vdec == NULL) {
+ r = -EINVAL;
+ break;
+ }
+
+ r = vdec_input_get_status(&priv->vdec->input,
+ &status);
+ if (r == 0) {
+ p->status.size = status.size;
+ p->status.data_len = status.data_len;
+ p->status.free_len = status.free_len;
+ p->status.read_pointer =
+ status.read_pointer;
+ }
+ break;
+ }
+
+ p->status.size = stbuf_canusesize(buf);
+ p->status.data_len = stbuf_level(buf);
+ p->status.free_len = stbuf_space(buf);
+ p->status.read_pointer = stbuf_rp(buf);
+ } else
+ r = -EINVAL;
+ break;
+ case AMSTREAM_GET_EX_AB_STATUS:
+ if (this->type & PORT_TYPE_AUDIO) {
+ struct am_ioctl_parm_ex *p = &parm;
+ struct stream_buf_s *buf = &bufs[BUF_TYPE_AUDIO];
+
+ if (p == NULL)
+ r = -EINVAL;
+
+ p->status.size = stbuf_canusesize(buf);
+ p->status.data_len = stbuf_level(buf);
+ p->status.free_len = stbuf_space(buf);
+ p->status.read_pointer = stbuf_rp(buf);
+
+ } else
+ r = -EINVAL;
+ break;
+ case AMSTREAM_GET_EX_VDECSTAT:
+ if ((this->type & PORT_TYPE_VIDEO) == 0) {
+ pr_err("no video\n");
+ return -EINVAL;
+ }
+ {
+ struct vdec_status vstatus;
+ struct am_ioctl_parm_ex *p = &parm;
+
+ if (p == NULL)
+ return -EINVAL;
+ if (vdec_status(priv->vdec, &vstatus) == -1)
+ return -ENODEV;
+ p->vstatus.width = vstatus.width;
+ p->vstatus.height = vstatus.height;
+ p->vstatus.fps = vstatus.fps;
+ p->vstatus.error_count = vstatus.error_count;
+ p->vstatus.status = vstatus.status;
+ }
+ break;
+ case AMSTREAM_GET_EX_ADECSTAT:
+ if ((this->type & PORT_TYPE_AUDIO) == 0) {
+ pr_err("no audio\n");
+ return -EINVAL;
+ }
+ if (amstream_adec_status == NULL) {
+ /*
+ *pr_err("no amstream_adec_status\n");
+ *return -ENODEV;
+ */
+ memset(&parm.astatus, 0, sizeof(parm.astatus));
+ } else {
+ struct adec_status astatus;
+ struct am_ioctl_parm_ex *p = &parm;
+
+ if (p == NULL)
+ return -EINVAL;
+ amstream_adec_status(&astatus);
+ p->astatus.channels = astatus.channels;
+ p->astatus.sample_rate = astatus.sample_rate;
+ p->astatus.resolution = astatus.resolution;
+ p->astatus.error_count = astatus.error_count;
+ p->astatus.status = astatus.status;
+ }
+ break;
+
+ case AMSTREAM_GET_EX_UD_POC:
+ if (this->type & PORT_TYPE_USERDATA) {
+ struct userdata_poc_info_t userdata_poc =
+ userdata_poc_info[userdata_poc_ri];
+ memcpy(&parm.data_userdata_info,
+ &userdata_poc,
+ sizeof(struct userdata_poc_info_t));
+
+ userdata_poc_ri++;
+ if (userdata_poc_ri == USERDATA_FIFO_NUM)
+ userdata_poc_ri = 0;
+ } else
+ r = -EINVAL;
+ break;
+ default:
+ r = -ENOIOCTLCMD;
+ break;
+ }
+ /* pr_info("parm size:%zx\n", sizeof(parm)); */
+ if (r == 0) {
+ if (copy_to_user((void *)arg, &parm, sizeof(parm)))
+ r = -EFAULT;
+ }
+ return r;
+
+}
+static long amstream_ioctl_set_ex(struct port_priv_s *priv, ulong arg)
+{
+ long r = 0;
+ return r;
+}
+static long amstream_ioctl_get_ptr(struct port_priv_s *priv, ulong arg)
+{
+ long r = 0;
+
+ struct am_ioctl_parm_ptr parm;
+
+ if (copy_from_user
+ ((void *)&parm, (void *)arg,
+ sizeof(parm)))
+ r = -EFAULT;
+
+ switch (parm.cmd) {
+ case AMSTREAM_GET_PTR_SUB_INFO:
+ {
+ struct subtitle_info msub_info[MAX_SUB_NUM];
+ struct subtitle_info *psub_info[MAX_SUB_NUM];
+ int i;
+
+ for (i = 0; i < MAX_SUB_NUM; i++)
+ psub_info[i] = &msub_info[i];
+
+ r = psparser_get_sub_info(psub_info);
+
+ if (r == 0) {
+ memcpy(parm.pdata_sub_info, msub_info,
+ sizeof(struct subtitle_info)
+ * MAX_SUB_NUM);
+ }
+ }
+ break;
+ default:
+ r = -ENOIOCTLCMD;
+ break;
+ }
+ /* pr_info("parm size:%d\n", sizeof(parm)); */
+ if (r == 0) {
+ if (copy_to_user((void *)arg, &parm, sizeof(parm)))
+ r = -EFAULT;
+ }
+
+ return r;
+
+}
+static long amstream_ioctl_set_ptr(struct port_priv_s *priv, ulong arg)
+{
+ struct stream_port_s *this = priv->port;
+ struct am_ioctl_parm_ptr parm;
+ long r = 0;
+
+ if (copy_from_user
+ ((void *)&parm, (void *)arg,
+ sizeof(parm))) {
+ pr_err("[%s]%d, arg err\n", __func__, __LINE__);
+ r = -EFAULT;
+ }
+ switch (parm.cmd) {
+ case AMSTREAM_SET_PTR_AUDIO_INFO:
+ if ((this->type & PORT_TYPE_VIDEO)
+ || (this->type & PORT_TYPE_AUDIO)) {
+ if (parm.pdata_audio_info != NULL)
+ memcpy((void *)&audio_dec_info,
+ (void *)parm.pdata_audio_info,
+ sizeof(audio_dec_info));
+ } else
+ r = -EINVAL;
+ break;
+ default:
+ r = -ENOIOCTLCMD;
+ break;
+ }
+ return r;
+}
+
+static long amstream_do_ioctl_new(struct port_priv_s *priv,
+ unsigned int cmd, ulong arg)
+{
+ long r = 0;
+ struct stream_port_s *this = priv->port;
+
+ switch (cmd) {
+ case AMSTREAM_IOC_GET_VERSION:
+ r = amstream_ioctl_get_version(priv, arg);
+ break;
+ case AMSTREAM_IOC_GET:
+ r = amstream_ioctl_get(priv, arg);
+ break;
+ case AMSTREAM_IOC_SET:
+ r = amstream_ioctl_set(priv, arg);
+ break;
+ case AMSTREAM_IOC_GET_EX:
+ r = amstream_ioctl_get_ex(priv, arg);
+ break;
+ case AMSTREAM_IOC_SET_EX:
+ r = amstream_ioctl_set_ex(priv, arg);
+ break;
+ case AMSTREAM_IOC_GET_PTR:
+ r = amstream_ioctl_get_ptr(priv, arg);
+ break;
+ case AMSTREAM_IOC_SET_PTR:
+ r = amstream_ioctl_set_ptr(priv, arg);
+ break;
+ case AMSTREAM_IOC_SYSINFO:
+ if (this->type & PORT_TYPE_VIDEO)
+ r = vdec_set_decinfo(priv->vdec, (void *)arg);
+ else
+ r = -EINVAL;
+ break;
+ default:
+ r = -ENOIOCTLCMD;
+ break;
+ }
+
+ return r;
+}
+
+static long amstream_do_ioctl_old(struct port_priv_s *priv,
+ unsigned int cmd, ulong arg)
+{
+ struct stream_port_s *this = priv->port;
+ long r = 0;
+
+ switch (cmd) {
+
+ case AMSTREAM_IOC_VB_START:
+ if ((this->type & PORT_TYPE_VIDEO) &&
+ ((bufs[BUF_TYPE_VIDEO].flag & BUF_FLAG_IN_USE) == 0)) {
+ if (has_hevc_vdec())
+ bufs[BUF_TYPE_HEVC].buf_start = arg;
+ bufs[BUF_TYPE_VIDEO].buf_start = arg;
+ } else
+ r = -EINVAL;
+ break;
+
+ case AMSTREAM_IOC_VB_SIZE:
+ if ((this->type & PORT_TYPE_VIDEO) &&
+ ((bufs[BUF_TYPE_VIDEO].flag & BUF_FLAG_IN_USE) == 0)) {
+ if (bufs[BUF_TYPE_VIDEO].flag & BUF_FLAG_ALLOC) {
+ if (has_hevc_vdec()) {
+ r = stbuf_change_size(
+ &bufs[BUF_TYPE_HEVC], arg);
+ }
+ r = stbuf_change_size(
+ &bufs[BUF_TYPE_VIDEO], arg);
+ }
+ } else
+ r = -EINVAL;
+ break;
+
+ case AMSTREAM_IOC_AB_START:
+ if ((this->type & PORT_TYPE_AUDIO) &&
+ ((bufs[BUF_TYPE_AUDIO].flag & BUF_FLAG_IN_USE) == 0))
+ bufs[BUF_TYPE_AUDIO].buf_start = arg;
+ else
+ r = -EINVAL;
+ break;
+
+ case AMSTREAM_IOC_AB_SIZE:
+ if ((this->type & PORT_TYPE_AUDIO) &&
+ ((bufs[BUF_TYPE_AUDIO].flag & BUF_FLAG_IN_USE) == 0)) {
+ if (bufs[BUF_TYPE_AUDIO].flag & BUF_FLAG_ALLOC) {
+ r = stbuf_change_size(
+ &bufs[BUF_TYPE_AUDIO], arg);
+ }
+ } else
+ r = -EINVAL;
+ break;
+
+ case AMSTREAM_IOC_VFORMAT:
+ if ((this->type & PORT_TYPE_VIDEO) && (arg < VFORMAT_MAX)) {
+ this->vformat = (enum vformat_e)arg;
+ this->flag |= PORT_FLAG_VFORMAT;
+
+ vdec_set_format(priv->vdec, this->vformat);
+ } else
+ r = -EINVAL;
+ break;
+
+ case AMSTREAM_IOC_AFORMAT:
+ if ((this->type & PORT_TYPE_AUDIO) && (arg < AFORMAT_MAX)) {
+ memset(&audio_dec_info, 0,
+ sizeof(struct audio_info));
+ /* for new format,reset the audio info. */
+ this->aformat = (enum aformat_e)arg;
+ this->flag |= PORT_FLAG_AFORMAT;
+ } else
+ r = -EINVAL;
+ break;
+
+ case AMSTREAM_IOC_VID:
+ if (this->type & PORT_TYPE_VIDEO) {
+ this->vid = (u32) arg;
+ this->flag |= PORT_FLAG_VID;
+ } else
+ r = -EINVAL;
+
+ break;
+
+ case AMSTREAM_IOC_AID:
+ if (this->type & PORT_TYPE_AUDIO) {
+ this->aid = (u32) arg;
+ this->flag |= PORT_FLAG_AID;
+
+ if (port_get_inited(priv)) {
+ tsync_audio_break(1);
+ amstream_change_avid(this);
+ }
+ } else
+ r = -EINVAL;
+ break;
+
+ case AMSTREAM_IOC_SID:
+ if (this->type & PORT_TYPE_SUB) {
+ this->sid = (u32) arg;
+ this->flag |= PORT_FLAG_SID;
+
+ if (port_get_inited(priv))
+ amstream_change_sid(this);
+ } else
+ r = -EINVAL;
+
+ break;
+
+ case AMSTREAM_IOC_PCRID:
+ this->pcrid = (u32) arg;
+ this->pcr_inited = 1;
+ pr_err("set pcrid = 0x%x\n", this->pcrid);
+ break;
+
+ case AMSTREAM_IOC_VB_STATUS:
+ if (this->type & PORT_TYPE_VIDEO) {
+ struct am_io_param para;
+ struct am_io_param *p = &para;
+ struct stream_buf_s *buf = NULL;
+
+ buf = (this->vformat == VFORMAT_HEVC ||
+ this->vformat == VFORMAT_VP9) ?
+ &bufs[BUF_TYPE_HEVC] :
+ &bufs[BUF_TYPE_VIDEO];
+
+ if (p == NULL) {
+ r = -EINVAL;
+ break;
+ }
+
+ if (this->type & PORT_TYPE_FRAME) {
+ struct vdec_input_status_s status;
+
+ /*
+ *todo: check upper layer for decoder
+ * handler lifecycle
+ */
+ if (priv->vdec == NULL) {
+ r = -EINVAL;
+ break;
+ }
+
+ r = vdec_input_get_status(&priv->vdec->input,
+ &status);
+ if (r == 0) {
+ p->status.size = status.size;
+ p->status.data_len = status.data_len;
+ p->status.free_len = status.free_len;
+ p->status.read_pointer =
+ status.read_pointer;
+ if (copy_to_user((void *)arg, p,
+ sizeof(para)))
+ r = -EFAULT;
+ }
+ break;
+ }
+
+ p->status.size = stbuf_canusesize(buf);
+ p->status.data_len = stbuf_level(buf);
+ p->status.free_len = stbuf_space(buf);
+ p->status.read_pointer = stbuf_rp(buf);
+ if (copy_to_user((void *)arg, p, sizeof(para)))
+ r = -EFAULT;
+ return r;
+ }
+ r = -EINVAL;
+ break;
+
+ case AMSTREAM_IOC_AB_STATUS:
+ if (this->type & PORT_TYPE_AUDIO) {
+ struct am_io_param para;
+ struct am_io_param *p = &para;
+ struct stream_buf_s *buf = &bufs[BUF_TYPE_AUDIO];
+
+ if (p == NULL)
+ r = -EINVAL;
+
+ p->status.size = stbuf_canusesize(buf);
+ p->status.data_len = stbuf_level(buf);
+ p->status.free_len = stbuf_space(buf);
+ p->status.read_pointer = stbuf_rp(buf);
+ if (copy_to_user((void *)arg, p, sizeof(para)))
+ r = -EFAULT;
+ return r;
+ }
+ r = -EINVAL;
+ break;
+
+ case AMSTREAM_IOC_SYSINFO:
+ if (this->type & PORT_TYPE_VIDEO)
+ r = vdec_set_decinfo(priv->vdec, (void *)arg);
+ else
+ r = -EINVAL;
+ break;
+
+ case AMSTREAM_IOC_ACHANNEL:
+ if (this->type & PORT_TYPE_AUDIO) {
+ this->achanl = (u32) arg;
+ set_ch_num_info((u32) arg);
+ } else
+ r = -EINVAL;
+ break;
+
+ case AMSTREAM_IOC_SAMPLERATE:
+ if (this->type & PORT_TYPE_AUDIO) {
+ this->asamprate = (u32) arg;
+ set_sample_rate_info((u32) arg);
+ } else
+ r = -EINVAL;
+ break;
+
+ case AMSTREAM_IOC_DATAWIDTH:
+ if (this->type & PORT_TYPE_AUDIO)
+ this->adatawidth = (u32) arg;
+ else
+ r = -EINVAL;
+ break;
+
+ case AMSTREAM_IOC_TSTAMP:
+ if ((this->type & (PORT_TYPE_AUDIO | PORT_TYPE_VIDEO)) ==
+ ((PORT_TYPE_AUDIO | PORT_TYPE_VIDEO)))
+ r = -EINVAL;
+ else if (this->type & PORT_TYPE_FRAME)
+ r = vdec_set_pts(priv->vdec, arg);
+ else if (has_hevc_vdec() && this->type & PORT_TYPE_HEVC)
+ r = es_vpts_checkin(&bufs[BUF_TYPE_HEVC], arg);
+ else if (this->type & PORT_TYPE_VIDEO)
+ r = es_vpts_checkin(&bufs[BUF_TYPE_VIDEO], arg);
+ else if (this->type & PORT_TYPE_AUDIO)
+ r = es_apts_checkin(&bufs[BUF_TYPE_AUDIO], arg);
+ break;
+
+ case AMSTREAM_IOC_TSTAMP_uS64:
+ if ((this->type & (PORT_TYPE_AUDIO | PORT_TYPE_VIDEO)) ==
+ ((PORT_TYPE_AUDIO | PORT_TYPE_VIDEO)))
+ r = -EINVAL;
+ else {
+ u64 pts;
+
+ if (copy_from_user
+ ((void *)&pts, (void *)arg, sizeof(u64)))
+ return -EFAULT;
+ if (this->type & PORT_TYPE_FRAME) {
+ /*
+ *todo: check upper layer for decoder handler
+ * life sequence or multi-tasking management
+ */
+ if (priv->vdec)
+ r = vdec_set_pts64(priv->vdec, pts);
+ } else if (has_hevc_vdec()) {
+ if (this->type & PORT_TYPE_HEVC) {
+ r = es_vpts_checkin_us64(
+ &bufs[BUF_TYPE_HEVC], pts);
+ } else if (this->type & PORT_TYPE_VIDEO) {
+ r = es_vpts_checkin_us64(
+ &bufs[BUF_TYPE_VIDEO], pts);
+ } else if (this->type & PORT_TYPE_AUDIO) {
+ r = es_vpts_checkin_us64(
+ &bufs[BUF_TYPE_AUDIO], pts);
+ }
+ } else {
+ if (this->type & PORT_TYPE_VIDEO) {
+ r = es_vpts_checkin_us64(
+ &bufs[BUF_TYPE_VIDEO], pts);
+ } else if (this->type & PORT_TYPE_AUDIO) {
+ r = es_vpts_checkin_us64(
+ &bufs[BUF_TYPE_AUDIO], pts);
+ }
+ }
+ }
+ break;
+
+ case AMSTREAM_IOC_VDECSTAT:
+ if ((this->type & PORT_TYPE_VIDEO) == 0)
+ return -EINVAL;
+ {
+ struct vdec_status vstatus;
+ struct am_io_param para;
+ struct am_io_param *p = &para;
+
+ if (p == NULL)
+ return -EINVAL;
+ if (vdec_status(priv->vdec, &vstatus) == -1)
+ return -ENODEV;
+ p->vstatus.width = vstatus.width;
+ p->vstatus.height = vstatus.height;
+ p->vstatus.fps = vstatus.fps;
+ p->vstatus.error_count = vstatus.error_count;
+ p->vstatus.status = vstatus.status;
+ if (copy_to_user((void *)arg, p, sizeof(para)))
+ r = -EFAULT;
+ return r;
+ }
+
+ case AMSTREAM_IOC_ADECSTAT:
+ if ((this->type & PORT_TYPE_AUDIO) == 0)
+ return -EINVAL;
+ if (amstream_adec_status == NULL)
+ return -ENODEV;
+ {
+ struct adec_status astatus;
+ struct am_io_param para;
+ struct am_io_param *p = &para;
+
+ if (p == NULL)
+ return -EINVAL;
+ amstream_adec_status(&astatus);
+ p->astatus.channels = astatus.channels;
+ p->astatus.sample_rate = astatus.sample_rate;
+ p->astatus.resolution = astatus.resolution;
+ p->astatus.error_count = astatus.error_count;
+ p->astatus.status = astatus.status;
+ if (copy_to_user((void *)arg, p, sizeof(para)))
+ r = -EFAULT;
+ return r;
+ }
+ case AMSTREAM_IOC_PORT_INIT:
+ r = amstream_port_init(priv);
+ break;
+
+ case AMSTREAM_IOC_VDEC_RESET:
+ if ((this->type & PORT_TYPE_VIDEO) == 0)
+ return -EINVAL;
+
+ if (priv->vdec == NULL)
+ return -ENODEV;
+
+ r = vdec_reset(priv->vdec);
+ break;
+
+ case AMSTREAM_IOC_TRICKMODE:
+ if ((this->type & PORT_TYPE_VIDEO) == 0)
+ return -EINVAL;
+ r = vdec_set_trickmode(priv->vdec, arg);
+ if (r == -1)
+ return -ENODEV;
+ break;
+
+ case AMSTREAM_IOC_AUDIO_INFO:
+ if ((this->type & PORT_TYPE_VIDEO)
+ || (this->type & PORT_TYPE_AUDIO)) {
+ if (copy_from_user
+ (&audio_dec_info, (void __user *)arg,
+ sizeof(audio_dec_info)))
+ r = -EFAULT;
+ } else
+ r = -EINVAL;
+ break;
+
+ case AMSTREAM_IOC_AUDIO_RESET:
+ if (this->type & PORT_TYPE_AUDIO) {
+ struct stream_buf_s *pabuf = &bufs[BUF_TYPE_AUDIO];
+
+ r = audio_port_reset(this, pabuf);
+ } else
+ r = -EINVAL;
+
+ break;
+
+ case AMSTREAM_IOC_SUB_RESET:
+ if (this->type & PORT_TYPE_SUB) {
+ struct stream_buf_s *psbuf = &bufs[BUF_TYPE_SUBTITLE];
+
+ r = sub_port_reset(this, psbuf);
+ } else
+ r = -EINVAL;
+ break;
+
+ case AMSTREAM_IOC_SUB_LENGTH:
+ if ((this->type & PORT_TYPE_SUB) ||
+ (this->type & PORT_TYPE_SUB_RD)) {
+ u32 sub_wp, sub_rp;
+ struct stream_buf_s *psbuf = &bufs[BUF_TYPE_SUBTITLE];
+ int val;
+
+ sub_wp = stbuf_sub_wp_get();
+ sub_rp = stbuf_sub_rp_get();
+
+ if (sub_wp == sub_rp)
+ val = 0;
+ else if (sub_wp > sub_rp)
+ val = sub_wp - sub_rp;
+ else
+ val = psbuf->buf_size - (sub_rp - sub_wp);
+ put_user(val, (unsigned long __user *)arg);
+ } else
+ r = -EINVAL;
+ break;
+
+ case AMSTREAM_IOC_UD_LENGTH:
+ if (this->type & PORT_TYPE_USERDATA) {
+ /* *((u32 *)arg) = userdata_length; */
+ put_user(userdata_length, (unsigned long __user *)arg);
+ userdata_length = 0;
+ } else
+ r = -EINVAL;
+ break;
+
+ case AMSTREAM_IOC_UD_POC:
+ if (this->type & PORT_TYPE_USERDATA) {
+ /* *((u32 *)arg) = userdata_length; */
+ int res;
+ struct userdata_poc_info_t userdata_poc =
+ userdata_poc_info[userdata_poc_ri];
+ /*
+ *put_user(userdata_poc.poc_number,
+ * (unsigned long __user *)arg);
+ */
+ res =
+ copy_to_user((unsigned long __user *)arg,
+ &userdata_poc,
+ sizeof(struct userdata_poc_info_t));
+ if (res < 0)
+ r = -EFAULT;
+ userdata_poc_ri++;
+ if (userdata_poc_ri == USERDATA_FIFO_NUM)
+ userdata_poc_ri = 0;
+ } else
+ r = -EINVAL;
+ break;
+
+ case AMSTREAM_IOC_SET_DEC_RESET:
+ tsync_set_dec_reset();
+ break;
+
+ case AMSTREAM_IOC_TS_SKIPBYTE:
+ if ((int)arg >= 0)
+ tsdemux_set_skipbyte(arg);
+ else
+ r = -EINVAL;
+ break;
+
+ case AMSTREAM_IOC_SUB_TYPE:
+ sub_type = (int)arg;
+ break;
+
+ case AMSTREAM_IOC_APTS_LOOKUP:
+ if (this->type & PORT_TYPE_AUDIO) {
+ u32 pts = 0, offset;
+
+ get_user(offset, (unsigned long __user *)arg);
+ pts_lookup_offset(PTS_TYPE_AUDIO, offset, &pts, 300);
+ put_user(pts, (int __user *)arg);
+ }
+ return 0;
+ case GET_FIRST_APTS_FLAG:
+ if (this->type & PORT_TYPE_AUDIO) {
+ put_user(first_pts_checkin_complete(PTS_TYPE_AUDIO),
+ (int __user *)arg);
+ }
+ break;
+
+ case AMSTREAM_IOC_APTS:
+ put_user(timestamp_apts_get(), (int __user *)arg);
+ break;
+
+ case AMSTREAM_IOC_VPTS:
+ put_user(timestamp_vpts_get(), (int __user *)arg);
+ break;
+
+ case AMSTREAM_IOC_PCRSCR:
+ put_user(timestamp_pcrscr_get(), (int __user *)arg);
+ break;
+
+ case AMSTREAM_IOC_SET_PCRSCR:
+ timestamp_pcrscr_set(arg);
+ break;
+ case AMSTREAM_IOC_GET_LAST_CHECKIN_APTS:
+ put_user(get_last_checkin_pts(PTS_TYPE_AUDIO), (int *)arg);
+ break;
+ case AMSTREAM_IOC_GET_LAST_CHECKIN_VPTS:
+ put_user(get_last_checkin_pts(PTS_TYPE_VIDEO), (int *)arg);
+ break;
+ case AMSTREAM_IOC_GET_LAST_CHECKOUT_APTS:
+ put_user(get_last_checkout_pts(PTS_TYPE_AUDIO), (int *)arg);
+ break;
+ case AMSTREAM_IOC_GET_LAST_CHECKOUT_VPTS:
+ put_user(get_last_checkout_pts(PTS_TYPE_VIDEO), (int *)arg);
+ break;
+ case AMSTREAM_IOC_SUB_NUM:
+ put_user(psparser_get_sub_found_num(), (int *)arg);
+ break;
+
+ case AMSTREAM_IOC_SUB_INFO:
+ if (arg > 0) {
+ struct subtitle_info msub_info[MAX_SUB_NUM];
+ struct subtitle_info *psub_info[MAX_SUB_NUM];
+ int i;
+
+ for (i = 0; i < MAX_SUB_NUM; i++)
+ psub_info[i] = &msub_info[i];
+
+ r = psparser_get_sub_info(psub_info);
+
+ if (r == 0) {
+ if (copy_to_user((void __user *)arg, msub_info,
+ sizeof(struct subtitle_info) * MAX_SUB_NUM))
+ r = -EFAULT;
+ }
+ }
+ break;
+ case AMSTREAM_IOC_SET_DEMUX:
+ tsdemux_set_demux((int)arg);
+ break;
+ case AMSTREAM_IOC_SET_VIDEO_DELAY_LIMIT_MS:
+ if (has_hevc_vdec())
+ bufs[BUF_TYPE_HEVC].max_buffer_delay_ms = (int)arg;
+ bufs[BUF_TYPE_VIDEO].max_buffer_delay_ms = (int)arg;
+ break;
+ case AMSTREAM_IOC_SET_AUDIO_DELAY_LIMIT_MS:
+ bufs[BUF_TYPE_AUDIO].max_buffer_delay_ms = (int)arg;
+ break;
+ case AMSTREAM_IOC_GET_VIDEO_DELAY_LIMIT_MS:
+ put_user(bufs[BUF_TYPE_VIDEO].max_buffer_delay_ms, (int *)arg);
+ break;
+ case AMSTREAM_IOC_GET_AUDIO_DELAY_LIMIT_MS:
+ put_user(bufs[BUF_TYPE_AUDIO].max_buffer_delay_ms, (int *)arg);
+ break;
+ case AMSTREAM_IOC_GET_VIDEO_CUR_DELAY_MS: {
+ int delay;
+
+ delay = calculation_stream_delayed_ms(
+ PTS_TYPE_VIDEO, NULL, NULL);
+ if (delay >= 0)
+ put_user(delay, (int *)arg);
+ else
+ put_user(0, (int *)arg);
+ }
+ break;
+
+ case AMSTREAM_IOC_GET_AUDIO_CUR_DELAY_MS: {
+ int delay;
+
+ delay = calculation_stream_delayed_ms(PTS_TYPE_AUDIO, NULL,
+ NULL);
+ if (delay >= 0)
+ put_user(delay, (int *)arg);
+ else
+ put_user(0, (int *)arg);
+ }
+ break;
+ case AMSTREAM_IOC_GET_AUDIO_AVG_BITRATE_BPS: {
+ int delay;
+ u32 avgbps;
+
+ delay = calculation_stream_delayed_ms(PTS_TYPE_AUDIO, NULL,
+ &avgbps);
+ if (delay >= 0)
+ put_user(avgbps, (int *)arg);
+ else
+ put_user(0, (int *)arg);
+ break;
+ }
+ case AMSTREAM_IOC_GET_VIDEO_AVG_BITRATE_BPS: {
+ int delay;
+ u32 avgbps;
+
+ delay = calculation_stream_delayed_ms(PTS_TYPE_VIDEO, NULL,
+ &avgbps);
+ if (delay >= 0)
+ put_user(avgbps, (int *)arg);
+ else
+ put_user(0, (int *)arg);
+ break;
+ }
+ case AMSTREAM_IOC_SET_DRMMODE:
+ if ((u32) arg == 1) {
+ pr_err("set drmmode\n");
+ this->flag |= PORT_FLAG_DRM;
+ } else {
+ this->flag &= (~PORT_FLAG_DRM);
+ pr_err("no drmmode\n");
+ }
+ break;
+ case AMSTREAM_IOC_SET_APTS: {
+ unsigned long pts;
+
+ if (get_user(pts, (unsigned long __user *)arg)) {
+ pr_err
+ ("Get audio pts from user space fault!\n");
+ return -EFAULT;
+ }
+ if (tsync_get_mode() == TSYNC_MODE_PCRMASTER)
+ tsync_pcr_set_apts(pts);
+ else
+ tsync_set_apts(pts);
+ break;
+ }
+ default:
+ r = -ENOIOCTLCMD;
+ break;
+ }
+
+ return r;
+}
+
+static long amstream_do_ioctl(struct port_priv_s *priv,
+ unsigned int cmd, ulong arg)
+{
+ long r = 0;
+
+ switch (cmd) {
+ case AMSTREAM_IOC_GET_VERSION:
+ case AMSTREAM_IOC_GET:
+ case AMSTREAM_IOC_SET:
+ case AMSTREAM_IOC_GET_EX:
+ case AMSTREAM_IOC_SET_EX:
+ case AMSTREAM_IOC_GET_PTR:
+ case AMSTREAM_IOC_SET_PTR:
+ case AMSTREAM_IOC_SYSINFO:
+ r = amstream_do_ioctl_new(priv, cmd, arg);
+ break;
+ default:
+ r = amstream_do_ioctl_old(priv, cmd, arg);
+ break;
+ }
+ if (r != 0)
+ pr_err("amstream_do_ioctl error :%lx, %x\n", r, cmd);
+
+ return r;
+}
+static long amstream_ioctl(struct file *file, unsigned int cmd, ulong arg)
+{
+ struct port_priv_s *priv = (struct port_priv_s *)file->private_data;
+ struct stream_port_s *this = priv->port;
+
+ if (!this)
+ return -ENODEV;
+
+ return amstream_do_ioctl(priv, cmd, arg);
+}
+
+#ifdef CONFIG_COMPAT
+struct dec_sysinfo32 {
+
+ u32 format;
+
+ u32 width;
+
+ u32 height;
+
+ u32 rate;
+
+ u32 extra;
+
+ u32 status;
+
+ u32 ratio;
+
+ compat_uptr_t param;
+
+ u64 ratio64;
+};
+
+struct am_ioctl_parm_ptr32 {
+ union {
+ compat_uptr_t pdata_audio_info;
+ compat_uptr_t pdata_sub_info;
+ compat_uptr_t pointer;
+ char data[8];
+ };
+ u32 cmd;
+ char reserved[4];
+};
+
+static long amstream_ioc_setget_ptr(struct port_priv_s *priv,
+ unsigned int cmd, struct am_ioctl_parm_ptr32 __user *arg)
+{
+ struct am_ioctl_parm_ptr __user *data;
+ struct am_ioctl_parm_ptr32 __user *data32 = arg;
+ int ret;
+
+ data = compat_alloc_user_space(sizeof(*data));
+ if (!access_ok(VERIFY_WRITE, data, sizeof(*data)))
+ return -EFAULT;
+
+ if (put_user(data32->cmd, &data->cmd) ||
+ put_user(compat_ptr(data32->pointer), &data->pointer))
+ return -EFAULT;
+
+
+ ret = amstream_do_ioctl(priv, cmd, (unsigned long)data);
+ if (ret < 0)
+ return ret;
+ return 0;
+
+}
+
+static long amstream_set_sysinfo(struct port_priv_s *priv,
+ struct dec_sysinfo32 __user *arg)
+{
+ struct dec_sysinfo __user *data;
+ struct dec_sysinfo32 __user *data32 = arg;
+ int ret;
+
+ data = compat_alloc_user_space(sizeof(*data));
+ if (!access_ok(VERIFY_WRITE, data, sizeof(*data)))
+ return -EFAULT;
+ if (copy_in_user(data, data32, 7 * sizeof(u32)))
+ return -EFAULT;
+ if (put_user(compat_ptr(data32->param), &data->param))
+ return -EFAULT;
+ if (copy_in_user(&data->ratio64, &data32->ratio64,
+ sizeof(data->ratio64)))
+ return -EFAULT;
+
+ ret = amstream_do_ioctl(priv, AMSTREAM_IOC_SYSINFO,
+ (unsigned long)data);
+ if (ret < 0)
+ return ret;
+
+ if (copy_in_user(&arg->format, &data->format, 7 * sizeof(u32)) ||
+ copy_in_user(&arg->ratio64, &data->ratio64,
+ sizeof(arg->ratio64)))
+ return -EFAULT;
+
+ return 0;
+}
+static long amstream_compat_ioctl(struct file *file,
+ unsigned int cmd, ulong arg)
+{
+ s32 r = 0;
+ struct port_priv_s *priv = (struct port_priv_s *)file->private_data;
+
+ switch (cmd) {
+ case AMSTREAM_IOC_GET_VERSION:
+ case AMSTREAM_IOC_GET:
+ case AMSTREAM_IOC_SET:
+ case AMSTREAM_IOC_GET_EX:
+ case AMSTREAM_IOC_SET_EX:
+ return amstream_do_ioctl(priv, cmd, (ulong)compat_ptr(arg));
+ case AMSTREAM_IOC_GET_PTR:
+ case AMSTREAM_IOC_SET_PTR:
+ return amstream_ioc_setget_ptr(priv, cmd, compat_ptr(arg));
+ case AMSTREAM_IOC_SYSINFO:
+ return amstream_set_sysinfo(priv, compat_ptr(arg));
+ default:
+ return amstream_do_ioctl(priv, cmd, (ulong)compat_ptr(arg));
+ }
+
+ return r;
+}
+#endif
+
+static ssize_t ports_show(struct class *class, struct class_attribute *attr,
+ char *buf)
+{
+ int i;
+ char *pbuf = buf;
+ struct stream_port_s *p = NULL;
+
+ for (i = 0; i < amstream_port_num; i++) {
+ p = &ports[i];
+ /*name */
+ pbuf += sprintf(pbuf, "%s\t:\n", p->name);
+ /*type */
+ pbuf += sprintf(pbuf, "\ttype:%d( ", p->type);
+ if (p->type & PORT_TYPE_VIDEO)
+ pbuf += sprintf(pbuf, "%s ", "Video");
+ if (p->type & PORT_TYPE_AUDIO)
+ pbuf += sprintf(pbuf, "%s ", "Audio");
+ if (p->type & PORT_TYPE_MPTS)
+ pbuf += sprintf(pbuf, "%s ", "TS");
+ if (p->type & PORT_TYPE_MPPS)
+ pbuf += sprintf(pbuf, "%s ", "PS");
+ if (p->type & PORT_TYPE_ES)
+ pbuf += sprintf(pbuf, "%s ", "ES");
+ if (p->type & PORT_TYPE_RM)
+ pbuf += sprintf(pbuf, "%s ", "RM");
+ if (p->type & PORT_TYPE_SUB)
+ pbuf += sprintf(pbuf, "%s ", "Subtitle");
+ if (p->type & PORT_TYPE_SUB_RD)
+ pbuf += sprintf(pbuf, "%s ", "Subtitle_Read");
+ if (p->type & PORT_TYPE_USERDATA)
+ pbuf += sprintf(pbuf, "%s ", "userdata");
+ pbuf += sprintf(pbuf, ")\n");
+ /*flag */
+ pbuf += sprintf(pbuf, "\tflag:%d( ", p->flag);
+ if (p->flag & PORT_FLAG_IN_USE)
+ pbuf += sprintf(pbuf, "%s ", "Used");
+ else
+ pbuf += sprintf(pbuf, "%s ", "Unused");
+ if ((p->type & PORT_TYPE_VIDEO) == 0) {
+ if (p->flag & PORT_FLAG_INITED)
+ pbuf += sprintf(pbuf, "%s ", "inited");
+ else
+ pbuf += sprintf(pbuf, "%s ", "uninited");
+ }
+ pbuf += sprintf(pbuf, ")\n");
+ /*others */
+ pbuf += sprintf(pbuf, "\tVformat:%d\n",
+ (p->flag & PORT_FLAG_VFORMAT) ? p->vformat : -1);
+ pbuf += sprintf(pbuf, "\tAformat:%d\n",
+ (p->flag & PORT_FLAG_AFORMAT) ? p->aformat : -1);
+ pbuf += sprintf(pbuf, "\tVid:%d\n",
+ (p->flag & PORT_FLAG_VID) ? p->vid : -1);
+ pbuf += sprintf(pbuf, "\tAid:%d\n",
+ (p->flag & PORT_FLAG_AID) ? p->aid : -1);
+ pbuf += sprintf(pbuf, "\tSid:%d\n",
+ (p->flag & PORT_FLAG_SID) ? p->sid : -1);
+ pbuf += sprintf(pbuf, "\tPCRid:%d\n",
+ (p->pcr_inited == 1) ? p->pcrid : -1);
+ pbuf += sprintf(pbuf, "\tachannel:%d\n", p->achanl);
+ pbuf += sprintf(pbuf, "\tasamprate:%d\n", p->asamprate);
+ pbuf += sprintf(pbuf, "\tadatawidth:%d\n\n", p->adatawidth);
+ }
+ return pbuf - buf;
+}
+
+static ssize_t bufs_show(struct class *class, struct class_attribute *attr,
+ char *buf)
+{
+ int i;
+ char *pbuf = buf;
+ struct stream_buf_s *p = NULL;
+ char buf_type[][12] = { "Video", "Audio", "Subtitle",
+ "UserData", "HEVC" };
+
+ for (i = 0; i < amstream_buf_num; i++) {
+ p = &bufs[i];
+ /*type */
+ pbuf += sprintf(pbuf, "%s buffer:", buf_type[p->type]);
+ /*flag */
+ pbuf += sprintf(pbuf, "\tflag:%d( ", p->flag);
+ if (p->flag & BUF_FLAG_ALLOC)
+ pbuf += sprintf(pbuf, "%s ", "Alloc");
+ else
+ pbuf += sprintf(pbuf, "%s ", "Unalloc");
+ if (p->flag & BUF_FLAG_IN_USE)
+ pbuf += sprintf(pbuf, "%s ", "Used");
+ else
+ pbuf += sprintf(pbuf, "%s ", "Noused");
+ if (p->flag & BUF_FLAG_PARSER)
+ pbuf += sprintf(pbuf, "%s ", "Parser");
+ else
+ pbuf += sprintf(pbuf, "%s ", "noParser");
+ if (p->flag & BUF_FLAG_FIRST_TSTAMP)
+ pbuf += sprintf(pbuf, "%s ", "firststamp");
+ else
+ pbuf += sprintf(pbuf, "%s ", "nofirststamp");
+ pbuf += sprintf(pbuf, ")\n");
+ /*buf stats */
+
+ pbuf += sprintf(pbuf, "\tbuf addr:%p\n", (void *)p->buf_start);
+
+ if (p->type != BUF_TYPE_SUBTITLE) {
+ pbuf += sprintf(pbuf, "\tbuf size:%#x\n", p->buf_size);
+ pbuf += sprintf(pbuf,
+ "\tbuf canusesize:%#x\n",
+ p->canusebuf_size);
+ pbuf += sprintf(pbuf,
+ "\tbuf regbase:%#lx\n", p->reg_base);
+
+ if (p->reg_base && p->flag & BUF_FLAG_IN_USE) {
+ if (get_cpu_type() >= MESON_CPU_MAJOR_ID_M6) {
+ /* TODO: mod gate */
+ /* switch_mod_gate_by_name("vdec", 1);*/
+ amports_switch_gate("vdec", 1);
+ }
+ pbuf += sprintf(pbuf, "\tbuf level:%#x\n",
+ stbuf_level(p));
+ pbuf += sprintf(pbuf, "\tbuf space:%#x\n",
+ stbuf_space(p));
+ pbuf += sprintf(pbuf,
+ "\tbuf read pointer:%#x\n",
+ stbuf_rp(p));
+ if (get_cpu_type() >= MESON_CPU_MAJOR_ID_M6) {
+ /* TODO: mod gate */
+ /* switch_mod_gate_by_name("vdec", 0);*/
+ amports_switch_gate("vdec", 0);
+ }
+ } else
+ pbuf += sprintf(pbuf, "\tbuf no used.\n");
+
+ if (p->type == BUF_TYPE_USERDATA) {
+ pbuf += sprintf(pbuf,
+ "\tbuf write pointer:%#x\n",
+ p->buf_wp);
+ pbuf += sprintf(pbuf,
+ "\tbuf read pointer:%#x\n",
+ p->buf_rp);
+ }
+ } else {
+ u32 sub_wp, sub_rp, data_size;
+
+ sub_wp = stbuf_sub_wp_get();
+ sub_rp = stbuf_sub_rp_get();
+ if (sub_wp >= sub_rp)
+ data_size = sub_wp - sub_rp;
+ else
+ data_size = p->buf_size - sub_rp + sub_wp;
+ pbuf += sprintf(pbuf, "\tbuf size:%#x\n", p->buf_size);
+ pbuf +=
+ sprintf(pbuf, "\tbuf canusesize:%#x\n",
+ p->canusebuf_size);
+ pbuf +=
+ sprintf(pbuf, "\tbuf start:%#x\n",
+ stbuf_sub_start_get());
+ pbuf += sprintf(pbuf,
+ "\tbuf write pointer:%#x\n", sub_wp);
+ pbuf += sprintf(pbuf,
+ "\tbuf read pointer:%#x\n", sub_rp);
+ pbuf += sprintf(pbuf, "\tbuf level:%#x\n", data_size);
+ }
+
+ pbuf += sprintf(pbuf, "\tbuf first_stamp:%#x\n",
+ p->first_tstamp);
+ pbuf += sprintf(pbuf, "\tbuf wcnt:%#x\n\n", p->wcnt);
+ pbuf += sprintf(pbuf, "\tbuf max_buffer_delay_ms:%dms\n",
+ p->max_buffer_delay_ms);
+
+ if (p->reg_base && p->flag & BUF_FLAG_IN_USE) {
+ int calc_delayms = 0;
+ u32 bitrate = 0, avg_bitrate = 0;
+
+ calc_delayms = calculation_stream_delayed_ms(
+ (p->type == BUF_TYPE_AUDIO) ? PTS_TYPE_AUDIO :
+ PTS_TYPE_VIDEO,
+ &bitrate,
+ &avg_bitrate);
+
+ if (calc_delayms >= 0) {
+ pbuf += sprintf(pbuf,
+ "\tbuf current delay:%dms\n",
+ calc_delayms);
+ pbuf += sprintf(pbuf,
+ "\tbuf bitrate latest:%dbps,avg:%dbps\n",
+ bitrate, avg_bitrate);
+ pbuf += sprintf(pbuf,
+ "\tbuf time after last pts:%d ms\n",
+ calculation_stream_ext_delayed_ms
+ ((p->type == BUF_TYPE_AUDIO) ? PTS_TYPE_AUDIO :
+ PTS_TYPE_VIDEO));
+
+ pbuf += sprintf(pbuf,
+ "\tbuf time after last write data :%d ms\n",
+ (int)(jiffies_64 -
+ p->last_write_jiffies64) * 1000 / HZ);
+ }
+ }
+ if (p->write_thread) {
+ pbuf += sprintf(pbuf,
+ "\twrite thread:%d/%d,fifo %d:%d,passed:%d\n",
+ threadrw_buffer_level(p),
+ threadrw_buffer_size(p),
+ threadrw_datafifo_len(p),
+ threadrw_freefifo_len(p),
+ threadrw_passed_len(p)
+ );
+ }
+ }
+
+ return pbuf - buf;
+}
+
+static ssize_t videobufused_show(struct class *class,
+ struct class_attribute *attr, char *buf)
+{
+ char *pbuf = buf;
+ struct stream_buf_s *p = NULL;
+ struct stream_buf_s *p_hevc = NULL;
+
+ p = &bufs[0];
+ if (has_hevc_vdec())
+ p_hevc = &bufs[BUF_TYPE_HEVC];
+
+ if (p->flag & BUF_FLAG_IN_USE)
+ pbuf += sprintf(pbuf, "%d ", 1);
+ else if (has_hevc_vdec() && (p_hevc->flag & BUF_FLAG_IN_USE))
+ pbuf += sprintf(pbuf, "%d ", 1);
+ else
+ pbuf += sprintf(pbuf, "%d ", 0);
+ return 1;
+}
+
+static ssize_t vcodec_profile_show(struct class *class,
+ struct class_attribute *attr, char *buf)
+{
+ return vcodec_profile_read(buf);
+}
+
+static int reset_canuse_buferlevel(int levelx10000)
+{
+ int i;
+ struct stream_buf_s *p = NULL;
+
+ if (levelx10000 >= 0 && levelx10000 <= 10000)
+ use_bufferlevelx10000 = levelx10000;
+ else
+ use_bufferlevelx10000 = 10000;
+ for (i = 0; i < amstream_buf_num; i++) {
+ p = &bufs[i];
+ p->canusebuf_size = ((p->buf_size / 1024) *
+ use_bufferlevelx10000 / 10000) * 1024;
+ p->canusebuf_size += 1023;
+ p->canusebuf_size &= ~1023;
+ if (p->canusebuf_size > p->buf_size)
+ p->canusebuf_size = p->buf_size;
+ }
+ return 0;
+}
+
+static ssize_t show_canuse_buferlevel(struct class *class,
+ struct class_attribute *attr, char *buf)
+{
+ ssize_t size = sprintf(buf,
+ "use_bufferlevel=%d/10000[=(set range[ 0~10000])=\n",
+ use_bufferlevelx10000);
+ return size;
+}
+
+static ssize_t store_canuse_buferlevel(struct class *class,
+ struct class_attribute *attr,
+ const char *buf, size_t size)
+{
+ unsigned val;
+ ssize_t ret;
+
+ /*ret = sscanf(buf, "%d", &val);*/
+ ret = kstrtoint(buf, 0, &val);
+
+ if (ret != 0)
+ return -EINVAL;
+ val = val;
+ reset_canuse_buferlevel(val);
+ return size;
+}
+
+static ssize_t store_maxdelay(struct class *class,
+ struct class_attribute *attr,
+ const char *buf, size_t size)
+{
+ unsigned val;
+ ssize_t ret;
+ int i;
+
+ /*ret = sscanf(buf, "%d", &val);*/
+ ret = kstrtoint(buf, 0, &val);
+ if (ret != 0)
+ return -EINVAL;
+ for (i = 0; i < amstream_buf_num; i++)
+ bufs[i].max_buffer_delay_ms = val;
+ return size;
+}
+
+static ssize_t show_maxdelay(struct class *class,
+ struct class_attribute *attr,
+ char *buf)
+{
+ ssize_t size = 0;
+
+ size += sprintf(buf, "%dms video max buffered data delay ms\n",
+ bufs[0].max_buffer_delay_ms);
+ size += sprintf(buf, "%dms audio max buffered data delay ms\n",
+ bufs[1].max_buffer_delay_ms);
+ return size;
+}
+
+static struct class_attribute amstream_class_attrs[] = {
+ __ATTR_RO(ports),
+ __ATTR_RO(bufs),
+ __ATTR_RO(vcodec_profile),
+ __ATTR_RO(videobufused),
+ __ATTR(canuse_buferlevel, S_IRUGO | S_IWUSR | S_IWGRP,
+ show_canuse_buferlevel, store_canuse_buferlevel),
+ __ATTR(max_buffer_delay_ms, S_IRUGO | S_IWUSR | S_IWGRP, show_maxdelay,
+ store_maxdelay),
+ __ATTR_NULL
+};
+
+static struct class amstream_class = {
+ .name = "amstream",
+ .class_attrs = amstream_class_attrs,
+};
+
+int amstream_request_firmware_from_sys(const char *file_name,
+ char *buf, int size)
+{
+ const struct firmware *firmware;
+ int err = 0;
+ struct device *micro_dev;
+
+ pr_info("try load %s ...", file_name);
+ micro_dev = device_create(&amstream_class,
+ NULL, MKDEV(AMSTREAM_MAJOR, 100),
+ NULL, "videodec");
+ if (micro_dev == NULL) {
+ pr_err("device_create failed =%d\n", err);
+ return -1;
+ }
+ err = request_firmware(&firmware, file_name, micro_dev);
+ if (err < 0) {
+ pr_err("can't load the %s,err=%d\n", file_name, err);
+ goto error1;
+ }
+ if (firmware->size > size) {
+ pr_err("not enough memory size for audiodsp code\n");
+ err = -ENOMEM;
+ goto release;
+ }
+
+ memcpy(buf, (char *)firmware->data, firmware->size);
+ /*mb(); don't need it*/
+ pr_err("load mcode size=%zd\n mcode name %s\n", firmware->size,
+ file_name);
+ err = firmware->size;
+release:
+ release_firmware(firmware);
+error1:
+ device_destroy(&amstream_class, MKDEV(AMSTREAM_MAJOR, 100));
+ return err;
+}
+
+/*static struct resource memobj;*/
+static int amstream_probe(struct platform_device *pdev)
+{
+ int i;
+ int r;
+ struct stream_port_s *st;
+
+ pr_err("Amlogic A/V streaming port init\n");
+
+ amstream_port_num = MAX_AMSTREAM_PORT_NUM;
+ amstream_buf_num = BUF_MAX_NUM;
+/*
+* r = of_reserved_mem_device_init(&pdev->dev);
+* if (r == 0)
+* pr_info("of probe done");
+* else {
+* r = -ENOMEM;
+* return r;
+* }
+*/
+ r = class_register(&amstream_class);
+ if (r) {
+ pr_err("amstream class create fail.\n");
+ return r;
+ }
+
+ r = astream_dev_register();
+ if (r)
+ return r;
+
+ r = register_chrdev(AMSTREAM_MAJOR, "amstream", &amstream_fops);
+ if (r < 0) {
+ pr_err("Can't allocate major for amstreaming device\n");
+
+ goto error2;
+ }
+
+ amstream_dev_class = class_create(THIS_MODULE, DEVICE_NAME);
+
+ for (st = &ports[0], i = 0; i < amstream_port_num; i++, st++) {
+ st->class_dev = device_create(amstream_dev_class, NULL,
+ MKDEV(AMSTREAM_MAJOR, i), NULL,
+ ports[i].name);
+ }
+
+ amstream_adec_status = NULL;
+ if (tsdemux_class_register() != 0) {
+ r = (-EIO);
+ goto error3;
+ }
+
+ init_waitqueue_head(&amstream_sub_wait);
+ init_waitqueue_head(&amstream_userdata_wait);
+ reset_canuse_buferlevel(10000);
+ amstream_pdev = pdev;
+ amports_clock_gate_init(&amstream_pdev->dev);
+
+ clock_set_init(&amstream_pdev->dev);
+
+ /*prealloc fetch buf to avoid no continue buffer later...*/
+ stbuf_fetch_init();
+ return 0;
+
+ /*
+ * error4:
+ * tsdemux_class_unregister();
+ */
+error3:
+ for (st = &ports[0], i = 0; i < amstream_port_num; i++, st++)
+ device_destroy(amstream_dev_class, MKDEV(AMSTREAM_MAJOR, i));
+ class_destroy(amstream_dev_class);
+error2:
+ unregister_chrdev(AMSTREAM_MAJOR, "amstream");
+ /* error1: */
+ astream_dev_unregister();
+ return r;
+}
+
+static int amstream_remove(struct platform_device *pdev)
+{
+ int i;
+ struct stream_port_s *st;
+
+ if (bufs[BUF_TYPE_VIDEO].flag & BUF_FLAG_ALLOC)
+ stbuf_change_size(&bufs[BUF_TYPE_VIDEO], 0);
+ if (bufs[BUF_TYPE_AUDIO].flag & BUF_FLAG_ALLOC)
+ stbuf_change_size(&bufs[BUF_TYPE_AUDIO], 0);
+ stbuf_fetch_release();
+ tsdemux_class_unregister();
+ for (st = &ports[0], i = 0; i < amstream_port_num; i++, st++)
+ device_destroy(amstream_dev_class, MKDEV(AMSTREAM_MAJOR, i));
+
+ class_destroy(amstream_dev_class);
+
+ unregister_chrdev(AMSTREAM_MAJOR, DEVICE_NAME);
+
+ astream_dev_unregister();
+
+ amstream_adec_status = NULL;
+
+ pr_err("Amlogic A/V streaming port release\n");
+
+ return 0;
+}
+
+void set_adec_func(int (*adec_func)(struct adec_status *))
+{
+ amstream_adec_status = adec_func;
+}
+
+void wakeup_sub_poll(void)
+{
+ atomic_inc(&subdata_ready);
+ wake_up_interruptible(&amstream_sub_wait);
+}
+
+int get_sub_type(void)
+{
+ return sub_type;
+}
+
+/*get pes buffers */
+
+struct stream_buf_s *get_stream_buffer(int id)
+{
+ if (id >= BUF_MAX_NUM)
+ return 0;
+ return &bufs[id];
+}
+
+static const struct of_device_id amlogic_mesonstream_dt_match[] = {
+ {
+ .compatible = "amlogic, codec, streambuf",
+ },
+ {},
+};
+
+static struct platform_driver amstream_driver = {
+ .probe = amstream_probe,
+ .remove = amstream_remove,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "mesonstream",
+ .of_match_table = amlogic_mesonstream_dt_match,
+ }
+};
+#if 0
+int amstream_module_init(void)
+{
+ if (platform_driver_register(&amstream_driver)) {
+ pr_err("failed to register amstream module\n");
+ return -ENODEV;
+ }
+ return 0;
+}
+EXPORT_SYMBOL(amstream_module_init);
+
+void amstream_module_exit(void)
+{
+ platform_driver_unregister(&amstream_driver);
+}
+EXPORT_SYMBOL(amstream_module_exit);
+#endif
+#if 1
+static int __init amstream_module_init(void)
+{
+ if (platform_driver_register(&amstream_driver)) {
+ pr_err("failed to register amstream module\n");
+ return -ENODEV;
+ }
+ return 0;
+}
+
+static void __exit amstream_module_exit(void)
+{
+ platform_driver_unregister(&amstream_driver);
+}
+#endif
+
+#if 0
+static int amstream_mem_device_init(struct reserved_mem *rmem,
+ struct device *dev)
+{
+ struct resource *res;
+ int r;
+
+ res = &memobj;
+
+ res->start = (phys_addr_t) rmem->base;
+ res->end = res->start + (phys_addr_t) rmem->size - 1;
+ if (!res) {
+ pr_err(
+ "Can not get I/O memory, and will allocate stream buffer!\n");
+
+ if (stbuf_change_size(&bufs[BUF_TYPE_VIDEO],
+ DEFAULT_VIDEO_BUFFER_SIZE) != 0) {
+ r = (-ENOMEM);
+ goto error4;
+ }
+ if (stbuf_change_size
+ (&bufs[BUF_TYPE_AUDIO],
+ DEFAULT_AUDIO_BUFFER_SIZE) != 0) {
+ r = (-ENOMEM);
+ goto error5;
+ }
+ if (stbuf_change_size
+ (&bufs[BUF_TYPE_SUBTITLE],
+ DEFAULT_SUBTITLE_BUFFER_SIZE) != 0) {
+ r = (-ENOMEM);
+ goto error6;
+ }
+ } else {
+ bufs[BUF_TYPE_VIDEO].buf_start = res->start;
+ bufs[BUF_TYPE_VIDEO].buf_size =
+ resource_size(res) - DEFAULT_AUDIO_BUFFER_SIZE -
+ DEFAULT_SUBTITLE_BUFFER_SIZE;
+ bufs[BUF_TYPE_VIDEO].flag |= BUF_FLAG_IOMEM;
+ bufs[BUF_TYPE_VIDEO].default_buf_size =
+ bufs[BUF_TYPE_VIDEO].buf_size;
+
+ bufs[BUF_TYPE_AUDIO].buf_start =
+ res->start + bufs[BUF_TYPE_VIDEO].buf_size;
+ bufs[BUF_TYPE_AUDIO].buf_size = DEFAULT_AUDIO_BUFFER_SIZE;
+ bufs[BUF_TYPE_AUDIO].flag |= BUF_FLAG_IOMEM;
+
+ if (stbuf_change_size
+ (&bufs[BUF_TYPE_SUBTITLE],
+ DEFAULT_SUBTITLE_BUFFER_SIZE) != 0) {
+ r = (-ENOMEM);
+ goto error4;
+ }
+ }
+
+ if (has_hevc_vdec()) {
+ bufs[BUF_TYPE_HEVC].buf_start = bufs[BUF_TYPE_VIDEO].buf_start;
+ bufs[BUF_TYPE_HEVC].buf_size = bufs[BUF_TYPE_VIDEO].buf_size;
+
+ if (bufs[BUF_TYPE_VIDEO].flag & BUF_FLAG_IOMEM)
+ bufs[BUF_TYPE_HEVC].flag |= BUF_FLAG_IOMEM;
+
+ bufs[BUF_TYPE_HEVC].default_buf_size =
+ bufs[BUF_TYPE_VIDEO].default_buf_size;
+ }
+
+ if (stbuf_fetch_init() != 0) {
+ r = (-ENOMEM);
+ goto error7;
+ }
+
+ return 0;
+
+error7:
+ if (bufs[BUF_TYPE_SUBTITLE].flag & BUF_FLAG_ALLOC)
+ stbuf_change_size(&bufs[BUF_TYPE_SUBTITLE], 0);
+error6:
+ if (bufs[BUF_TYPE_AUDIO].flag & BUF_FLAG_ALLOC)
+ stbuf_change_size(&bufs[BUF_TYPE_AUDIO], 0);
+error5:
+ if (bufs[BUF_TYPE_VIDEO].flag & BUF_FLAG_ALLOC)
+ stbuf_change_size(&bufs[BUF_TYPE_VIDEO], 0);
+error4:
+ return 0;
+}
+
+static const struct reserved_mem_ops rmem_amstream_ops = {
+ .device_init = amstream_mem_device_init,
+};
+
+static int __init amstream_mem_setup(struct reserved_mem *rmem)
+{
+ rmem->ops = &rmem_amstream_ops;
+ pr_err("share mem setup\n");
+
+ return 0;
+}
+RESERVEDMEM_OF_DECLARE(mesonstream,
+ "amlogic, stream-memory", amstream_mem_setup);
+
+#endif
+#if 1
+module_init(amstream_module_init);
+module_exit(amstream_module_exit);
+#endif
+
+module_param(debugflags, uint, 0664);
+MODULE_PARM_DESC(debugflags, "\n amstream debugflags\n");
+
+
+MODULE_DESCRIPTION("AMLOGIC streaming port driver");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Tim Yao <timyao@amlogic.com>");
diff --git a/drivers/stream_input/amports/amstream_profile.c b/drivers/stream_input/amports/amstream_profile.c
new file mode 100644
index 0000000..d65ee5c
--- a/dev/null
+++ b/drivers/stream_input/amports/amstream_profile.c
@@ -0,0 +1,54 @@
+/*
+ * drivers/amlogic/media/stream_input/amports/amstream_profile.c
+ *
+ * Copyright (C) 2016 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.
+ *
+*/
+
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/amlogic/media/utils/amstream.h>
+#include "amports_priv.h"
+
+static const struct codec_profile_t *vcodec_profile[SUPPORT_VDEC_NUM] = { 0 };
+
+static int vcodec_profile_idx;
+
+ssize_t vcodec_profile_read(char *buf)
+{
+ char *pbuf = buf;
+ int i = 0;
+
+ for (i = 0; i < vcodec_profile_idx; i++) {
+ pbuf += sprintf(pbuf, "%s:%s;\n", vcodec_profile[i]->name,
+ vcodec_profile[i]->profile);
+ }
+
+ return pbuf - buf;
+}
+
+int vcodec_profile_register(const struct codec_profile_t *vdec_profile)
+{
+ if (vcodec_profile_idx < SUPPORT_VDEC_NUM) {
+ vcodec_profile[vcodec_profile_idx] = vdec_profile;
+ vcodec_profile_idx++;
+ pr_debug("regist %s codec profile\n", vdec_profile->name);
+
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(vcodec_profile_register);
+
diff --git a/drivers/stream_input/parser/Makefile b/drivers/stream_input/parser/Makefile
new file mode 100644
index 0000000..5fb0dce
--- a/dev/null
+++ b/drivers/stream_input/parser/Makefile
@@ -0,0 +1,6 @@
+obj-y += thread_rw.o
+obj-y += streambuf.o
+obj-y += esparser.o
+obj-y += tsdemux.o
+obj-y += psparser.o
+#obj-y += rmparser.o
diff --git a/drivers/stream_input/parser/esparser.c b/drivers/stream_input/parser/esparser.c
new file mode 100644
index 0000000..d9de02c
--- a/dev/null
+++ b/drivers/stream_input/parser/esparser.c
@@ -0,0 +1,911 @@
+/*
+ * drivers/amlogic/media/stream_input/parser/esparser.c
+ *
+ * Copyright (C) 2016 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.
+ *
+*/
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/wait.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/dma-mapping.h>
+#include <linux/amlogic/media/frame_sync/ptsserv.h>
+
+#include <linux/uaccess.h>
+#include <linux/atomic.h>
+
+/* #include <mach/am_regs.h> */
+#include <linux/delay.h>
+
+#include "../../frame_provider/decoder/utils/vdec.h"
+#include <linux/amlogic/media/utils/vdec_reg.h>
+#include "streambuf_reg.h"
+#include "streambuf.h"
+#include "esparser.h"
+#include "../amports/amports_priv.h"
+#include "thread_rw.h"
+
+#include <linux/amlogic/media/codec_mm/codec_mm.h>
+
+
+
+#define SAVE_SCR 0
+
+#define ES_START_CODE_PATTERN 0x00000100
+#define ES_START_CODE_MASK 0xffffff00
+#define SEARCH_PATTERN_LEN 512
+#define ES_PARSER_POP READ_MPEG_REG(PFIFO_DATA)
+
+#define PARSER_WRITE (ES_WRITE | ES_PARSER_START)
+#define PARSER_VIDEO (ES_TYPE_VIDEO)
+#define PARSER_AUDIO (ES_TYPE_AUDIO)
+#define PARSER_SUBPIC (ES_TYPE_SUBTITLE)
+#define PARSER_PASSTHROUGH (ES_PASSTHROUGH | ES_PARSER_START)
+#define PARSER_AUTOSEARCH (ES_SEARCH | ES_PARSER_START)
+#define PARSER_DISCARD (ES_DISCARD | ES_PARSER_START)
+#define PARSER_BUSY (ES_PARSER_BUSY)
+
+static unsigned char *search_pattern;
+static dma_addr_t search_pattern_map;
+static u32 audio_real_wp;
+static u32 audio_buf_start;
+static u32 audio_buf_end;
+
+static const char esparser_id[] = "esparser-id";
+
+static DECLARE_WAIT_QUEUE_HEAD(wq);
+
+
+static u32 search_done;
+static u32 video_data_parsed;
+static u32 audio_data_parsed;
+static atomic_t esparser_use_count = ATOMIC_INIT(0);
+static DEFINE_MUTEX(esparser_mutex);
+
+static inline u32 get_buf_wp(u32 type)
+{
+ if (type == BUF_TYPE_AUDIO)
+ return audio_real_wp;
+ else
+ return 0;
+}
+static inline u32 get_buf_start(u32 type)
+{
+ if (type == BUF_TYPE_AUDIO)
+ return audio_buf_start;
+ else
+ return 0;
+}
+static inline u32 get_buf_end(u32 type)
+{
+ if (type == BUF_TYPE_AUDIO)
+ return audio_buf_end;
+ else
+ return 0;
+}
+static void set_buf_wp(u32 type, u32 wp)
+{
+ if (type == BUF_TYPE_AUDIO) {
+ audio_real_wp = wp;
+ WRITE_MPEG_REG(AIU_MEM_AIFIFO_MAN_WP, wp/* & 0xffffff00*/);
+ }
+}
+
+static irqreturn_t esparser_isr(int irq, void *dev_id)
+{
+ u32 int_status = READ_MPEG_REG(PARSER_INT_STATUS);
+
+ WRITE_MPEG_REG(PARSER_INT_STATUS, int_status);
+
+ if (int_status & PARSER_INTSTAT_SC_FOUND) {
+ WRITE_MPEG_REG(PFIFO_RD_PTR, 0);
+ WRITE_MPEG_REG(PFIFO_WR_PTR, 0);
+ search_done = 1;
+ wake_up_interruptible(&wq);
+ }
+ return IRQ_HANDLED;
+}
+
+static inline u32 buf_wp(u32 type)
+{
+ u32 wp;
+
+ if ((READ_MPEG_REG(PARSER_ES_CONTROL) & ES_VID_MAN_RD_PTR) == 0) {
+ wp =
+#if 1/* MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON8 */
+ (type == BUF_TYPE_HEVC) ? READ_VREG(HEVC_STREAM_WR_PTR) :
+#endif
+ (type == BUF_TYPE_VIDEO) ? READ_VREG(VLD_MEM_VIFIFO_WP) :
+ (type == BUF_TYPE_AUDIO) ?
+ READ_MPEG_REG(AIU_MEM_AIFIFO_MAN_WP) :
+ READ_MPEG_REG(PARSER_SUB_START_PTR);
+ } else {
+ wp =
+#if 1/* MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON8 */
+ (type == BUF_TYPE_HEVC) ? READ_MPEG_REG(PARSER_VIDEO_WP) :
+#endif
+ (type == BUF_TYPE_VIDEO) ? READ_MPEG_REG(PARSER_VIDEO_WP) :
+ (type == BUF_TYPE_AUDIO) ?
+ READ_MPEG_REG(AIU_MEM_AIFIFO_MAN_WP) :
+ READ_MPEG_REG(PARSER_SUB_START_PTR);
+ }
+
+ return wp;
+}
+
+static ssize_t _esparser_write(const char __user *buf,
+ size_t count, u32 type, int isphybuf)
+{
+ size_t r = count;
+ const char __user *p = buf;
+
+ u32 len = 0;
+ u32 parser_type;
+ int ret;
+ u32 wp;
+ dma_addr_t dma_addr = 0;
+
+ if (type == BUF_TYPE_HEVC)
+ parser_type = PARSER_VIDEO;
+ else if (type == BUF_TYPE_VIDEO)
+ parser_type = PARSER_VIDEO;
+ else if (type == BUF_TYPE_AUDIO)
+ parser_type = PARSER_AUDIO;
+ else
+ parser_type = PARSER_SUBPIC;
+
+ wp = buf_wp(type);
+
+ if (r > 0) {
+ if (isphybuf)
+ len = count;
+ else {
+ len = min_t(size_t, r, (size_t) FETCHBUF_SIZE);
+
+ if (copy_from_user(fetchbuf, p, len))
+ return -EFAULT;
+ dma_addr = dma_map_single(
+ amports_get_dma_device(), fetchbuf,
+ FETCHBUF_SIZE, DMA_TO_DEVICE);
+ if (dma_mapping_error(amports_get_dma_device(),
+ (dma_addr_t) dma_addr))
+ return -EFAULT;
+
+ }
+
+ /* wmb(); don't need */
+ /* reset the Write and read pointer to zero again */
+ WRITE_MPEG_REG(PFIFO_RD_PTR, 0);
+ WRITE_MPEG_REG(PFIFO_WR_PTR, 0);
+
+ WRITE_MPEG_REG_BITS(PARSER_CONTROL, len, ES_PACK_SIZE_BIT,
+ ES_PACK_SIZE_WID);
+ WRITE_MPEG_REG_BITS(PARSER_CONTROL,
+ parser_type | PARSER_WRITE |
+ PARSER_AUTOSEARCH, ES_CTRL_BIT,
+ ES_CTRL_WID);
+
+ if (isphybuf) {
+ u32 buf_32 = (unsigned long)buf & 0xffffffff;
+
+ WRITE_MPEG_REG(PARSER_FETCH_ADDR, buf_32);
+ } else {
+ WRITE_MPEG_REG(PARSER_FETCH_ADDR, dma_addr);
+ dma_unmap_single(amports_get_dma_device(), dma_addr,
+ FETCHBUF_SIZE, DMA_TO_DEVICE);
+ }
+
+ search_done = 0;
+ if (!(isphybuf & TYPE_PATTERN)) {
+ WRITE_MPEG_REG(PARSER_FETCH_CMD,
+ (7 << FETCH_ENDIAN) | len);
+ WRITE_MPEG_REG(PARSER_FETCH_ADDR, search_pattern_map);
+ WRITE_MPEG_REG(PARSER_FETCH_CMD,
+ (7 << FETCH_ENDIAN) | SEARCH_PATTERN_LEN);
+ } else {
+ WRITE_MPEG_REG(PARSER_FETCH_CMD,
+ (7 << FETCH_ENDIAN) | (len + 512));
+ }
+ ret = wait_event_interruptible_timeout(wq, search_done != 0,
+ HZ / 5);
+ if (ret == 0) {
+ WRITE_MPEG_REG(PARSER_FETCH_CMD, 0);
+
+ if (wp == buf_wp(type))
+ /*no data fetched */
+ return -EAGAIN;
+
+ pr_info("W Timeout, but fetch ok,");
+ pr_info("type %d len=%d,wpdiff=%d, isphy %x\n",
+ type, len, wp - buf_wp(type), isphybuf);
+
+ } else if (ret < 0)
+ return -ERESTARTSYS;
+ }
+
+ if ((type == BUF_TYPE_VIDEO)
+ || (has_hevc_vdec() && (type == BUF_TYPE_HEVC)))
+ video_data_parsed += len;
+ else if (type == BUF_TYPE_AUDIO)
+ audio_data_parsed += len;
+
+ return len;
+}
+
+static ssize_t _esparser_write_s(const char __user *buf,
+ size_t count, u32 type)
+{
+ size_t r = count;
+ const char __user *p = buf;
+ u32 len = 0;
+ int ret;
+ u32 wp, buf_start, buf_end;
+ dma_addr_t buf_wp_map;
+
+ if (type != BUF_TYPE_AUDIO)
+ WARN_ON(1);/*BUG();*/
+ wp = get_buf_wp(type);
+ buf_end = get_buf_end(type) + 8;
+ buf_start = get_buf_start(type);
+ /*pr_info("write wp 0x%x, count %d, start 0x%x, end 0x%x\n",*/
+ /* wp, (u32)count, buf_start, buf_end);*/
+ if (wp + count > buf_end) {
+ ret = copy_from_user(codec_mm_phys_to_virt(wp),
+ p, buf_end - wp);
+ if (ret > 0) {
+ len += buf_end - wp - ret;
+ buf_wp_map = dma_map_single(amports_get_dma_device(),
+ codec_mm_phys_to_virt(wp), len, DMA_TO_DEVICE);
+ wp += len;
+ pr_info("copy from user not finished\n");
+ dma_unmap_single(NULL, buf_wp_map, len, DMA_TO_DEVICE);
+ set_buf_wp(type, wp);
+ goto end_write;
+ } else if (ret == 0) {
+ len += buf_end - wp;
+ buf_wp_map = dma_map_single(amports_get_dma_device(),
+ codec_mm_phys_to_virt(wp), len, DMA_TO_DEVICE);
+ wp = buf_start;
+ r = count - len;
+ dma_unmap_single(NULL, buf_wp_map, len, DMA_TO_DEVICE);
+ set_buf_wp(type, wp);
+ } else {
+ pr_info("copy from user failed 1\n");
+ pr_info("w wp 0x%x, count %d, start 0x%x end 0x%x\n",
+ wp, (u32)count, buf_start, buf_end);
+ return -EAGAIN;
+ }
+ }
+ ret = copy_from_user(codec_mm_phys_to_virt(wp), p + len, r);
+ if (ret >= 0) {
+ len += r - ret;
+ buf_wp_map = dma_map_single(amports_get_dma_device(),
+ codec_mm_phys_to_virt(wp), r - ret, DMA_TO_DEVICE);
+
+ if (ret > 0)
+ pr_info("copy from user not finished 2\n");
+ wp += r - ret;
+ dma_unmap_single(NULL, buf_wp_map, r - ret, DMA_TO_DEVICE);
+ set_buf_wp(type, wp);
+ } else {
+ pr_info("copy from user failed 2\n");
+ return -EAGAIN;
+ }
+
+end_write:
+ if (type == BUF_TYPE_AUDIO)
+ audio_data_parsed += len;
+
+ return len;
+}
+
+s32 es_vpts_checkin_us64(struct stream_buf_s *buf, u64 us64)
+{
+ u32 passed;
+
+ if (buf->write_thread)
+ passed = threadrw_dataoffset(buf);
+ else
+ passed = video_data_parsed;
+ return pts_checkin_offset_us64(PTS_TYPE_VIDEO, passed, us64);
+
+}
+
+s32 es_apts_checkin_us64(struct stream_buf_s *buf, u64 us64)
+{
+ u32 passed;
+
+ if (buf->write_thread)
+ passed = threadrw_dataoffset(buf);
+ else
+ passed = audio_data_parsed;
+ return pts_checkin_offset_us64(PTS_TYPE_AUDIO, passed, us64);
+}
+
+s32 es_vpts_checkin(struct stream_buf_s *buf, u32 pts)
+{
+#if 0
+ if (buf->first_tstamp == INVALID_PTS) {
+ buf->flag |= BUF_FLAG_FIRST_TSTAMP;
+ buf->first_tstamp = pts;
+ return 0;
+ }
+#endif
+ u32 passed = video_data_parsed + threadrw_buffer_level(buf);
+
+ return pts_checkin_offset(PTS_TYPE_VIDEO, passed, pts);
+
+}
+
+s32 es_apts_checkin(struct stream_buf_s *buf, u32 pts)
+{
+#if 0
+ if (buf->first_tstamp == INVALID_PTS) {
+ buf->flag |= BUF_FLAG_FIRST_TSTAMP;
+ buf->first_tstamp = pts;
+
+ return 0;
+ }
+#endif
+ u32 passed = audio_data_parsed + threadrw_buffer_level(buf);
+
+ return pts_checkin_offset(PTS_TYPE_AUDIO, passed, pts);
+}
+
+s32 esparser_init(struct stream_buf_s *buf, struct vdec_s *vdec)
+{
+ s32 r = 0;
+ u32 pts_type;
+ u32 parser_sub_start_ptr;
+ u32 parser_sub_end_ptr;
+ u32 parser_sub_rp;
+ bool first_use = false;
+ /* #if MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON8 */
+ if (has_hevc_vdec() && (buf->type == BUF_TYPE_HEVC))
+ pts_type = PTS_TYPE_HEVC;
+ else
+ /* #endif */
+ if (buf->type == BUF_TYPE_VIDEO)
+ pts_type = PTS_TYPE_VIDEO;
+ else if (buf->type == BUF_TYPE_AUDIO)
+ pts_type = PTS_TYPE_AUDIO;
+ else if (buf->type == BUF_TYPE_SUBTITLE)
+ pts_type = PTS_TYPE_MAX;
+ else
+ return -EINVAL;
+ mutex_lock(&esparser_mutex);
+ parser_sub_start_ptr = READ_MPEG_REG(PARSER_SUB_START_PTR);
+ parser_sub_end_ptr = READ_MPEG_REG(PARSER_SUB_END_PTR);
+ parser_sub_rp = READ_MPEG_REG(PARSER_SUB_RP);
+
+ buf->flag |= BUF_FLAG_PARSER;
+
+ if (atomic_add_return(1, &esparser_use_count) == 1) {
+ first_use = true;
+
+ if (fetchbuf == 0) {
+ pr_info("%s: no fetchbuf\n", __func__);
+ r = -ENOMEM;
+ goto Err_1;
+ }
+
+ if (search_pattern == NULL) {
+ search_pattern = kcalloc(1,
+ SEARCH_PATTERN_LEN,
+ GFP_KERNEL);
+
+ if (search_pattern == NULL) {
+ pr_err("%s: no search_pattern\n", __func__);
+ r = -ENOMEM;
+ goto Err_1;
+ }
+
+ /* build a fake start code to get parser interrupt */
+ search_pattern[0] = 0x00;
+ search_pattern[1] = 0x00;
+ search_pattern[2] = 0x01;
+ search_pattern[3] = 0xff;
+
+ search_pattern_map = dma_map_single(
+ amports_get_dma_device(),
+ search_pattern,
+ SEARCH_PATTERN_LEN,
+ DMA_TO_DEVICE);
+ }
+
+ /* reset PARSER with first esparser_init() call */
+ WRITE_MPEG_REG(RESET1_REGISTER, RESET_PARSER);
+
+ /* TS data path */
+#ifndef CONFIG_AM_DVB
+ WRITE_MPEG_REG(FEC_INPUT_CONTROL, 0);
+#else
+ tsdemux_set_reset_flag();
+#endif
+ CLEAR_MPEG_REG_MASK(TS_HIU_CTL, 1 << USE_HI_BSF_INTERFACE);
+ CLEAR_MPEG_REG_MASK(TS_HIU_CTL_2, 1 << USE_HI_BSF_INTERFACE);
+ CLEAR_MPEG_REG_MASK(TS_HIU_CTL_3, 1 << USE_HI_BSF_INTERFACE);
+
+ CLEAR_MPEG_REG_MASK(TS_FILE_CONFIG, (1 << TS_HIU_ENABLE));
+
+ WRITE_MPEG_REG(PARSER_CONFIG,
+ (10 << PS_CFG_PFIFO_EMPTY_CNT_BIT) |
+ (1 << PS_CFG_MAX_ES_WR_CYCLE_BIT) |
+ (16 << PS_CFG_MAX_FETCH_CYCLE_BIT));
+
+ WRITE_MPEG_REG(PFIFO_RD_PTR, 0);
+ WRITE_MPEG_REG(PFIFO_WR_PTR, 0);
+
+ WRITE_MPEG_REG(PARSER_SEARCH_PATTERN, ES_START_CODE_PATTERN);
+ WRITE_MPEG_REG(PARSER_SEARCH_MASK, ES_START_CODE_MASK);
+
+ WRITE_MPEG_REG(PARSER_CONFIG,
+ (10 << PS_CFG_PFIFO_EMPTY_CNT_BIT) |
+ (1 << PS_CFG_MAX_ES_WR_CYCLE_BIT) |
+ PS_CFG_STARTCODE_WID_24 |
+ PS_CFG_PFIFO_ACCESS_WID_8 |
+ /* single byte pop */
+ (16 << PS_CFG_MAX_FETCH_CYCLE_BIT));
+
+ WRITE_MPEG_REG(PARSER_CONTROL, PARSER_AUTOSEARCH);
+
+ }
+ /* #if MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON8 */
+ /* hook stream buffer with PARSER */
+ if (has_hevc_vdec() && (pts_type == PTS_TYPE_HEVC)) {
+ WRITE_MPEG_REG(PARSER_VIDEO_START_PTR, vdec->input.start);
+ WRITE_MPEG_REG(PARSER_VIDEO_END_PTR, vdec->input.start
+ + vdec->input.size - 8);
+
+ if (vdec_stream_auto(vdec)) {
+ CLEAR_MPEG_REG_MASK(PARSER_ES_CONTROL,
+ ES_VID_MAN_RD_PTR);
+
+ /* set vififo_vbuf_rp_sel=>hevc */
+ WRITE_VREG(DOS_GEN_CTRL0, 3 << 1);
+
+ /* set use_parser_vbuf_wp */
+ SET_VREG_MASK(HEVC_STREAM_CONTROL,
+ (1 << 3) | (0 << 4));
+ /* set stream_fetch_enable */
+ SET_VREG_MASK(HEVC_STREAM_CONTROL, 1);
+
+ /* set stream_buffer_hole with 256 bytes */
+ SET_VREG_MASK(HEVC_STREAM_FIFO_CTL,
+ (1 << 29));
+ } else {
+ SET_MPEG_REG_MASK(PARSER_ES_CONTROL,
+ ES_VID_MAN_RD_PTR);
+ WRITE_MPEG_REG(PARSER_VIDEO_WP, vdec->input.start);
+ WRITE_MPEG_REG(PARSER_VIDEO_RP, vdec->input.start);
+ }
+ video_data_parsed = 0;
+ } else
+ /* #endif */
+ if (pts_type == PTS_TYPE_VIDEO) {
+ WRITE_MPEG_REG(PARSER_VIDEO_START_PTR,
+ vdec->input.start);
+ WRITE_MPEG_REG(PARSER_VIDEO_END_PTR,
+ vdec->input.start + vdec->input.size - 8);
+ if (vdec_stream_auto(vdec)) {
+ CLEAR_MPEG_REG_MASK(PARSER_ES_CONTROL,
+ ES_VID_MAN_RD_PTR);
+ WRITE_VREG(VLD_MEM_VIFIFO_BUF_CNTL,
+ MEM_BUFCTRL_INIT);
+ CLEAR_VREG_MASK(VLD_MEM_VIFIFO_BUF_CNTL,
+ MEM_BUFCTRL_INIT);
+
+ if (has_hevc_vdec()) {
+ /* set vififo_vbuf_rp_sel=>vdec */
+ WRITE_VREG(DOS_GEN_CTRL0, 0);
+
+ }
+ } else {
+ SET_MPEG_REG_MASK(PARSER_ES_CONTROL,
+ ES_VID_MAN_RD_PTR);
+ WRITE_MPEG_REG(PARSER_VIDEO_WP,
+ vdec->input.start);
+ WRITE_MPEG_REG(PARSER_VIDEO_RP,
+ vdec->input.start);
+ }
+ video_data_parsed = 0;
+ } else if (pts_type == PTS_TYPE_AUDIO) {
+ /* set wp as buffer start */
+ SET_MPEG_REG_MASK(AIU_MEM_AIFIFO_BUF_CNTL,
+ MEM_BUFCTRL_MANUAL);
+ WRITE_MPEG_REG(AIU_MEM_AIFIFO_MAN_RP,
+ READ_MPEG_REG(AIU_MEM_AIFIFO_START_PTR));
+ WRITE_MPEG_REG_BITS(AIU_MEM_AIFIFO_CONTROL, 7, 3, 3);
+ SET_MPEG_REG_MASK(AIU_MEM_AIFIFO_BUF_CNTL,
+ MEM_BUFCTRL_INIT);
+ CLEAR_MPEG_REG_MASK(AIU_MEM_AIFIFO_BUF_CNTL,
+ MEM_BUFCTRL_INIT);
+ WRITE_MPEG_REG(AIU_MEM_AIFIFO_MAN_WP,
+ READ_MPEG_REG(AIU_MEM_AIFIFO_START_PTR));
+ audio_data_parsed = 0;
+ audio_buf_start =
+ READ_MPEG_REG(AIU_MEM_AIFIFO_START_PTR);
+ audio_real_wp = audio_buf_start;
+ audio_buf_end = READ_MPEG_REG(AIU_MEM_AIFIFO_END_PTR);
+ } else if (buf->type == BUF_TYPE_SUBTITLE) {
+ WRITE_MPEG_REG(PARSER_SUB_START_PTR,
+ parser_sub_start_ptr);
+ WRITE_MPEG_REG(PARSER_SUB_END_PTR,
+ parser_sub_end_ptr);
+ WRITE_MPEG_REG(PARSER_SUB_RP, parser_sub_rp);
+ SET_MPEG_REG_MASK(PARSER_ES_CONTROL,
+ (7 << ES_SUB_WR_ENDIAN_BIT) |
+ ES_SUB_MAN_RD_PTR);
+ }
+
+ if (pts_type < PTS_TYPE_MAX) {
+ r = pts_start(pts_type);
+
+ if (r < 0) {
+ pr_info("esparser_init: pts_start failed\n");
+ goto Err_1;
+ }
+ }
+#if 0
+ if (buf->flag & BUF_FLAG_FIRST_TSTAMP) {
+ if (buf->type == BUF_TYPE_VIDEO)
+ es_vpts_checkin(buf, buf->first_tstamp);
+ else if (buf->type == BUF_TYPE_AUDIO)
+ es_apts_checkin(buf, buf->first_tstamp);
+
+ buf->flag &= ~BUF_FLAG_FIRST_TSTAMP;
+ }
+#endif
+
+ if (first_use) {
+ /*TODO irq */
+ r = vdec_request_irq(PARSER_IRQ, esparser_isr,
+ "parser", (void *)esparser_id);
+
+ if (r) {
+ pr_info("esparser_init: irq register failed.\n");
+ goto Err_2;
+ }
+
+ WRITE_MPEG_REG(PARSER_INT_STATUS, 0xffff);
+ WRITE_MPEG_REG(PARSER_INT_ENABLE,
+ PARSER_INTSTAT_SC_FOUND <<
+ PARSER_INT_HOST_EN_BIT);
+ }
+ mutex_unlock(&esparser_mutex);
+ if (!(amports_get_debug_flags() & 1) &&
+ !codec_mm_video_tvp_enabled()) {
+ int block_size = (buf->type == BUF_TYPE_AUDIO) ?
+ PAGE_SIZE << 2 : PAGE_SIZE << 4;
+ int buf_num = (buf->type == BUF_TYPE_AUDIO) ?
+ 5 : 10;
+ if (!(buf->type == BUF_TYPE_SUBTITLE))
+ buf->write_thread = threadrw_alloc(buf_num,
+ block_size,
+ esparser_write_ex,
+ (buf->type == BUF_TYPE_AUDIO) ? 1 : 0);
+ /*manul mode for audio*/
+ }
+ return 0;
+
+Err_2:
+ pts_stop(pts_type);
+
+Err_1:
+ atomic_dec(&esparser_use_count);
+ buf->flag &= ~BUF_FLAG_PARSER;
+ mutex_unlock(&esparser_mutex);
+ return r;
+}
+
+void esparser_audio_reset_s(struct stream_buf_s *buf)
+{
+ ulong flags;
+ DEFINE_SPINLOCK(lock);
+
+ spin_lock_irqsave(&lock, flags);
+
+ SET_MPEG_REG_MASK(AIU_MEM_AIFIFO_BUF_CNTL, MEM_BUFCTRL_MANUAL);
+ WRITE_MPEG_REG(AIU_MEM_AIFIFO_MAN_RP,
+ READ_MPEG_REG(AIU_MEM_AIFIFO_START_PTR));
+ WRITE_MPEG_REG_BITS(AIU_MEM_AIFIFO_CONTROL, 7, 3, 3);
+ SET_MPEG_REG_MASK(AIU_MEM_AIFIFO_BUF_CNTL, MEM_BUFCTRL_INIT);
+ CLEAR_MPEG_REG_MASK(AIU_MEM_AIFIFO_BUF_CNTL, MEM_BUFCTRL_INIT);
+ WRITE_MPEG_REG(AIU_MEM_AIFIFO_MAN_WP,
+ READ_MPEG_REG(AIU_MEM_AIFIFO_START_PTR));
+
+ buf->flag |= BUF_FLAG_PARSER;
+
+ audio_data_parsed = 0;
+ audio_real_wp = READ_MPEG_REG(AIU_MEM_AIFIFO_START_PTR);
+ spin_unlock_irqrestore(&lock, flags);
+
+}
+
+void esparser_audio_reset(struct stream_buf_s *buf)
+{
+ ulong flags;
+ DEFINE_SPINLOCK(lock);
+
+ spin_lock_irqsave(&lock, flags);
+
+ WRITE_MPEG_REG(PARSER_AUDIO_WP,
+ READ_MPEG_REG(AIU_MEM_AIFIFO_START_PTR));
+ WRITE_MPEG_REG(PARSER_AUDIO_RP,
+ READ_MPEG_REG(AIU_MEM_AIFIFO_START_PTR));
+
+ WRITE_MPEG_REG(PARSER_AUDIO_START_PTR,
+ READ_MPEG_REG(AIU_MEM_AIFIFO_START_PTR));
+ WRITE_MPEG_REG(PARSER_AUDIO_END_PTR,
+ READ_MPEG_REG(AIU_MEM_AIFIFO_END_PTR));
+ CLEAR_MPEG_REG_MASK(PARSER_ES_CONTROL, ES_AUD_MAN_RD_PTR);
+
+ WRITE_MPEG_REG(AIU_MEM_AIFIFO_BUF_CNTL, MEM_BUFCTRL_INIT);
+ CLEAR_MPEG_REG_MASK(AIU_MEM_AIFIFO_BUF_CNTL, MEM_BUFCTRL_INIT);
+
+ buf->flag |= BUF_FLAG_PARSER;
+
+ audio_data_parsed = 0;
+ audio_real_wp = 0;
+ audio_buf_start = 0;
+ audio_buf_end = 0;
+ spin_unlock_irqrestore(&lock, flags);
+
+}
+
+void esparser_release(struct stream_buf_s *buf)
+{
+ u32 pts_type;
+
+ /* check if esparser_init() is ever called */
+ if ((buf->flag & BUF_FLAG_PARSER) == 0)
+ return;
+
+ if (atomic_read(&esparser_use_count) == 0) {
+ pr_info
+ ("[%s:%d]###warning, esparser has been released already\n",
+ __func__, __LINE__);
+ return;
+ }
+ if (buf->write_thread)
+ threadrw_release(buf);
+ if (atomic_dec_and_test(&esparser_use_count)) {
+ WRITE_MPEG_REG(PARSER_INT_ENABLE, 0);
+ /*TODO irq */
+
+ vdec_free_irq(PARSER_IRQ, (void *)esparser_id);
+
+ if (search_pattern) {
+ dma_unmap_single(amports_get_dma_device(),
+ search_pattern_map,
+ SEARCH_PATTERN_LEN, DMA_TO_DEVICE);
+ kfree(search_pattern);
+ search_pattern = NULL;
+ }
+ }
+
+ if (has_hevc_vdec() && (buf->type == BUF_TYPE_HEVC))
+ pts_type = PTS_TYPE_VIDEO;
+ else if (buf->type == BUF_TYPE_VIDEO)
+ pts_type = PTS_TYPE_VIDEO;
+ else if (buf->type == BUF_TYPE_AUDIO)
+ pts_type = PTS_TYPE_AUDIO;
+ else if (buf->type == BUF_TYPE_SUBTITLE) {
+ buf->flag &= ~BUF_FLAG_PARSER;
+ return;
+ } else
+ return;
+
+ buf->flag &= ~BUF_FLAG_PARSER;
+ pts_stop(pts_type);
+}
+
+ssize_t drm_write(struct file *file, struct stream_buf_s *stbuf,
+ const char __user *buf, size_t count)
+{
+ s32 r;
+ u32 len;
+ u32 realcount, totalcount;
+ u32 re_count = count;
+ u32 havewritebytes = 0;
+ u32 leftcount = 0;
+
+ struct drm_info tmpmm;
+ struct drm_info *drm = &tmpmm;
+ u32 res = 0;
+ int isphybuf = 0;
+ unsigned long realbuf;
+
+ if (buf == NULL || count == 0)
+ return -EINVAL;
+ if (stbuf->write_thread) {
+ r = threadrw_flush_buffers(stbuf);
+ if (r < 0)
+ pr_info("Warning. drm flush threadrw failed[%d]\n", r);
+ }
+ res = copy_from_user(drm, buf, sizeof(struct drm_info));
+ if (res) {
+ pr_info("drm kmalloc failed res[%d]\n", res);
+ return -EFAULT;
+ }
+
+ if ((drm->drm_flag & TYPE_DRMINFO) && (drm->drm_hasesdata == 0)) {
+ /* buf only has drminfo not have esdata; */
+ realbuf = drm->drm_phy;
+ realcount = drm->drm_pktsize;
+ isphybuf = drm->drm_flag;
+ /* DRM_PRNT("drm_get_rawdata
+ *onlydrminfo drm->drm_hasesdata[0x%x]
+ * stbuf->type %d buf[0x%x]\n",
+ *drm->drm_hasesdata,stbuf->type,buf);
+ */
+ } else if (drm->drm_hasesdata == 1) { /* buf is drminfo+es; */
+ realcount = drm->drm_pktsize;
+ realbuf = (unsigned long)buf + sizeof(struct drm_info);
+ isphybuf = 0;
+ /* DRM_PRNT("drm_get_rawdata
+ * drminfo+es drm->drm_hasesdata[0x%x]
+ * stbuf->type %d\n",drm->drm_hasesdata,stbuf->type);
+ */
+ } else { /* buf is hwhead; */
+ realcount = count;
+ isphybuf = 0;
+ realbuf = (unsigned long)buf;
+ /* DRM_PRNT("drm_get_rawdata
+ * drm->drm_hasesdata[0x%x]
+ * len[%d] count[%d] realcout[%d]\n",
+ * drm->drm_hasesdata,len,count,realcount);
+ */
+ }
+
+ len = realcount;
+ count = realcount;
+ totalcount = realcount;
+
+ while (len > 0) {
+ if (stbuf->type != BUF_TYPE_SUBTITLE
+ && stbuf_space(stbuf) < count) {
+ len = min(stbuf_canusesize(stbuf) / 8, len);
+ if (stbuf_space(stbuf) < len) {
+ r = stbuf_wait_space(stbuf, len);
+ /* write part data , not allow return ; */
+ if ((r < leftcount) && (leftcount > 0))
+ continue;
+ else if ((r < 0) && (leftcount == 0))/*full; */
+ return -EAGAIN;
+ }
+ }
+ len = min_t(u32, len, count);
+
+ mutex_lock(&esparser_mutex);
+
+ if (stbuf->type != BUF_TYPE_AUDIO)
+ r = _esparser_write((const char __user *)realbuf, len,
+ stbuf->type, isphybuf);
+ else
+ r = _esparser_write_s((const char __user *)realbuf, len,
+ stbuf->type);
+ if (r < 0) {
+ pr_info("drm_write _esparser_write failed [%d]\n", r);
+ return r;
+ }
+ havewritebytes += r;
+ leftcount = totalcount - havewritebytes;
+ if (havewritebytes == totalcount) {
+
+ mutex_unlock(&esparser_mutex);
+ break; /* write ok; */
+ } else if ((len > 0) && (havewritebytes < totalcount)) {
+ DRM_PRNT
+ ("d writebytes[%d] want[%d] total[%d] real[%d]\n",
+ havewritebytes, len, totalcount, realcount);
+ len = len - r; /* write again; */
+ realbuf = realbuf + r;
+ } else {
+ pr_info
+ ("e writebytes[%d] want[%d] total[%d] real[%d]\n",
+ havewritebytes, len, totalcount, realcount);
+ }
+ mutex_unlock(&esparser_mutex);
+ }
+
+ return re_count;
+}
+/*
+*flags:
+*1:phy
+*2:noblock
+*/
+ssize_t esparser_write_ex(struct file *file,
+ struct stream_buf_s *stbuf,
+ const char __user *buf, size_t count,
+ int flags)
+{
+
+ s32 r;
+ u32 len = count;
+
+ if (buf == NULL || count == 0)
+ return -EINVAL;
+
+ /*subtitle have no level to check, */
+ if (stbuf->type != BUF_TYPE_SUBTITLE && stbuf_space(stbuf) < count) {
+ if ((flags & 2) || ((file != NULL) &&
+ (file->f_flags & O_NONBLOCK))) {
+ len = stbuf_space(stbuf);
+
+ if (len < 256) /* <1k.do eagain, */
+ return -EAGAIN;
+ } else {
+ len = min(stbuf_canusesize(stbuf) / 8, len);
+
+ if (stbuf_space(stbuf) < len) {
+ r = stbuf_wait_space(stbuf, len);
+ if (r < 0)
+ return r;
+ }
+ }
+ }
+
+ stbuf->last_write_jiffies64 = jiffies_64;
+
+ len = min_t(u32, len, count);
+
+ mutex_lock(&esparser_mutex);
+
+ if (stbuf->type == BUF_TYPE_AUDIO)
+ r = _esparser_write_s(buf, len, stbuf->type);
+ else
+ r = _esparser_write(buf, len, stbuf->type, flags & 1);
+
+ mutex_unlock(&esparser_mutex);
+
+ return r;
+}
+ssize_t esparser_write(struct file *file,
+ struct stream_buf_s *stbuf,
+ const char __user *buf, size_t count)
+{
+ if (stbuf->write_thread)
+ return threadrw_write(file, stbuf, buf, count);
+ return esparser_write_ex(file, stbuf, buf, count, 0);
+}
+
+
+void esparser_sub_reset(void)
+{
+ ulong flags;
+ DEFINE_SPINLOCK(lock);
+ u32 parser_sub_start_ptr;
+ u32 parser_sub_end_ptr;
+
+ spin_lock_irqsave(&lock, flags);
+
+ parser_sub_start_ptr = READ_MPEG_REG(PARSER_SUB_START_PTR);
+ parser_sub_end_ptr = READ_MPEG_REG(PARSER_SUB_END_PTR);
+
+ WRITE_MPEG_REG(PARSER_SUB_START_PTR, parser_sub_start_ptr);
+ WRITE_MPEG_REG(PARSER_SUB_END_PTR, parser_sub_end_ptr);
+ WRITE_MPEG_REG(PARSER_SUB_RP, parser_sub_start_ptr);
+ WRITE_MPEG_REG(PARSER_SUB_WP, parser_sub_start_ptr);
+ SET_MPEG_REG_MASK(PARSER_ES_CONTROL,
+ (7 << ES_SUB_WR_ENDIAN_BIT) | ES_SUB_MAN_RD_PTR);
+
+ spin_unlock_irqrestore(&lock, flags);
+}
diff --git a/drivers/stream_input/parser/esparser.h b/drivers/stream_input/parser/esparser.h
new file mode 100644
index 0000000..62396a2
--- a/dev/null
+++ b/drivers/stream_input/parser/esparser.h
@@ -0,0 +1,149 @@
+/*
+ * drivers/amlogic/media/stream_input/parser/esparser.h
+ *
+ * Copyright (C) 2016 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 ESPARSER_H
+#define ESPARSER_H
+
+#include "../../frame_provider/decoder/utils/vdec.h"
+
+extern ssize_t drm_write(struct file *file,
+ struct stream_buf_s *stbuf, const char __user *buf, size_t count);
+
+extern s32 esparser_init(struct stream_buf_s *buf, struct vdec_s *vdec);
+extern s32 esparser_init_s(struct stream_buf_s *buf);
+extern void esparser_release(struct stream_buf_s *buf);
+extern ssize_t esparser_write(struct file *file,
+ struct stream_buf_s *stbuf, const char __user *buf, size_t count);
+extern ssize_t esparser_write_ex(struct file *file,
+ struct stream_buf_s *stbuf,
+ const char __user *buf, size_t count, int is_phy);
+
+extern s32 es_vpts_checkin_us64(struct stream_buf_s *buf, u64 us64);
+
+extern s32 es_apts_checkin_us64(struct stream_buf_s *buf, u64 us64);
+
+extern int es_vpts_checkin(struct stream_buf_s *buf, u32 pts);
+
+extern int es_apts_checkin(struct stream_buf_s *buf, u32 pts);
+
+extern void esparser_audio_reset(struct stream_buf_s *buf);
+extern void esparser_audio_reset_s(struct stream_buf_s *buf);
+
+extern void esparser_sub_reset(void);
+
+#ifdef CONFIG_AM_DVB
+extern int tsdemux_set_reset_flag(void);
+#endif
+
+/* TODO: move to register headers */
+#define ES_PACK_SIZE_BIT 8
+#define ES_PACK_SIZE_WID 24
+
+#define ES_CTRL_WID 8
+#define ES_CTRL_BIT 0
+#define ES_TYPE_MASK (3 << 6)
+#define ES_TYPE_VIDEO (0 << 6)
+#define ES_TYPE_AUDIO (1 << 6)
+#define ES_TYPE_SUBTITLE (2 << 6)
+
+#define ES_WRITE (1<<5)
+#define ES_PASSTHROUGH (1<<4)
+#define ES_INSERT_BEFORE_ES_WRITE (1<<3)
+#define ES_DISCARD (1<<2)
+#define ES_SEARCH (1<<1)
+#define ES_PARSER_START (1<<0)
+#define ES_PARSER_BUSY (1<<0)
+
+#define PARSER_INTSTAT_FETCH_CMD (1<<7)
+#define PARSER_INTSTAT_PARSE (1<<4)
+#define PARSER_INTSTAT_DISCARD (1<<3)
+#define PARSER_INTSTAT_INSZERO (1<<2)
+#define PARSER_INTSTAT_ACT_NOSSC (1<<1)
+#define PARSER_INTSTAT_SC_FOUND (1<<0)
+
+#define FETCH_CIR_BUF (1<<31)
+#define FETCH_CHK_BUF_STOP (1<<30)
+#define FETCH_PASSTHROUGH (1<<29)
+#define FETCH_ENDIAN 27
+#define FETCH_PASSTHROUGH_TYPE_MASK (0x3<<27)
+#define FETCH_ENDIAN_MASK (0x7<<27)
+#define FETCH_BUF_SIZE_MASK (0x7ffffff)
+#define FETCH_CMD_PTR_MASK 3
+#define FETCH_CMD_RD_PTR_BIT 5
+#define FETCH_CMD_WR_PTR_BIT 3
+#define FETCH_CMD_NUM_MASK 3
+#define FETCH_CMD_NUM_BIT 0
+
+#define ES_COUNT_MASK 0xfff
+#define ES_COUNT_BIT 20
+#define ES_REQ_PENDING (1<<19)
+#define ES_PASSTHROUGH_EN (1<<18)
+#define ES_PASSTHROUGH_TYPE_MASK (3<<16)
+#define ES_PASSTHROUGH_TYPE_VIDEO (0<<16)
+#define ES_PASSTHROUGH_TYPE_AUDIO (1<<16)
+#define ES_PASSTHROUGH_TYPE_SUBTITLE (2<<16)
+#define ES_WR_ENDIAN_MASK (0x7)
+#define ES_SUB_WR_ENDIAN_BIT 9
+#define ES_SUB_MAN_RD_PTR (1<<8)
+#define ES_AUD_WR_ENDIAN_BIT 5
+#define ES_AUD_MAN_RD_PTR (1<<4)
+#define ES_VID_WR_ENDIAN_BIT 1
+#define ES_VID_MAN_RD_PTR (1<<0)
+
+#define PS_CFG_FETCH_DMA_URGENT (1<<31)
+#define PS_CFG_STREAM_DMA_URGENT (1<<30)
+#define PS_CFG_FORCE_PFIFO_REN (1<<29)
+#define PS_CFG_PFIFO_PEAK_EN (1<<28)
+#define PS_CFG_SRC_SEL_BIT 24
+#define PS_CFG_SRC_SEL_MASK (3<<PS_CFG_SRC_SEL_BIT)
+#define PS_CFG_SRC_SEL_FETCH (0<<PS_CFG_SRC_SEL_BIT)
+#define PS_CFG_SRC_SEL_AUX1 (1<<PS_CFG_SRC_SEL_BIT) /*from NDMA */
+#define PS_CFG_SRC_SEL_AUX2 (2<<PS_CFG_SRC_SEL_BIT)
+#define PS_CFG_SRC_SEL_AUX3 (3<<PS_CFG_SRC_SEL_BIT)
+#define PS_CFG_PFIFO_EMPTY_CNT_BIT 16
+#define PS_CFG_PFIFO_EMPTY_CNT_MASK 0xff
+#define PS_CFG_MAX_ES_WR_CYCLE_BIT 12
+#define PS_CFG_MAX_ES_WR_CYCLE_MASK 0xf
+#define PS_CFG_STARTCODE_WID_MASK (0x3<<10)
+#define PS_CFG_STARTCODE_WID_8 (0x0<<10)
+#define PS_CFG_STARTCODE_WID_16 (0x1<<10)
+#define PS_CFG_STARTCODE_WID_24 (0x2<<10)
+#define PS_CFG_STARTCODE_WID_32 (0x3<<10)
+#define PS_CFG_PFIFO_ACCESS_WID_MASK (0x3<<8)
+#define PS_CFG_PFIFO_ACCESS_WID_8 (0x0<<8)
+#define PS_CFG_PFIFO_ACCESS_WID_16 (0x1<<8)
+#define PS_CFG_PFIFO_ACCESS_WID_24 (0x2<<8)
+#define PS_CFG_PFIFO_ACCESS_WID_32 (0x3<<8)
+#define PS_CFG_MAX_FETCH_CYCLE_BIT 0
+#define PS_CFG_MAX_FETCH_CYCLE_MASK 0xff
+
+#define PARSER_INT_DISABLE_CNT_MASK 0xffff
+#define PARSER_INT_DISABLE_CNT_BIT 16
+#define PARSER_INT_HOST_EN_MASK 0xff
+#define PARSER_INT_HOST_EN_BIT 8
+#define PARSER_INT_AMRISC_EN_MASK 0xff
+#define PARSER_INT_AMRISC_EN_BIT 0
+#define PARSER_INT_ALL 0xff
+
+#define RESET_PARSER (1<<8)
+#define TS_HIU_ENABLE 5
+#define USE_HI_BSF_INTERFACE 7
+
+#define DRM_PRNT(fmt, args...)
+#define TRACE() pr_info("drm--[%s::%d]\n", __func__, __LINE__)
+
+#endif /* ESPARSER_H */
diff --git a/drivers/stream_input/parser/psparser.c b/drivers/stream_input/parser/psparser.c
new file mode 100644
index 0000000..80e5001
--- a/dev/null
+++ b/drivers/stream_input/parser/psparser.c
@@ -0,0 +1,1158 @@
+/*
+ * drivers/amlogic/media/stream_input/parser/psparser.c
+ *
+ * Copyright (C) 2016 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.
+ *
+*/
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/wait.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/slab.h>
+#include <linux/dma-mapping.h>
+#include <linux/amlogic/media/frame_sync/ptsserv.h>
+#include <linux/amlogic/media/utils/amstream.h>
+
+#include <linux/uaccess.h>
+/* #include <mach/am_regs.h> */
+#include <linux/amlogic/media/utils/vdec_reg.h>
+#include "streambuf_reg.h"
+#include "streambuf.h"
+#include "psparser.h"
+#include "../amports/amports_priv.h"
+
+
+#define TIMESTAMP_IONLY 1
+#define SAVE_SCR 0
+
+#define MPEG_START_CODE_PATTERN (0x00000100L)
+#define MPEG_START_CODE_MASK (0xffffff00L)
+#define MAX_MPG_AUDIOPK_SIZE 0x1000
+
+#define SUB_INSERT_START_CODE_HIGH 0x414d4c55
+#define SUB_INSERT_START_CODE_LOW 0xaa000000
+
+#define PARSER_WRITE (ES_WRITE | ES_PARSER_START)
+#define PARSER_VIDEO (ES_TYPE_VIDEO)
+#define PARSER_AUDIO (ES_TYPE_AUDIO)
+#define PARSER_SUBPIC (ES_TYPE_SUBTITLE)
+#define PARSER_PASSTHROUGH (ES_PASSTHROUGH | ES_PARSER_START)
+#define PARSER_AUTOSEARCH (ES_SEARCH | ES_PARSER_START)
+#define PARSER_DISCARD (ES_DISCARD | ES_PARSER_START)
+#define PARSER_BUSY (ES_PARSER_BUSY)
+
+#define PARSER_PARAMETER_LENGTH_BIT 16
+#define PARSER_PARAMETER_LOOP_BIT 24
+
+#define PARSER_POP READ_MPEG_REG(PFIFO_DATA)
+#define SET_BLOCK(size) \
+WRITE_MPEG_REG_BITS(PARSER_CONTROL, size, ES_PACK_SIZE_BIT, ES_PACK_SIZE_WID)
+#define SET_DISCARD_SIZE(size) WRITE_MPEG_REG(PARSER_PARAMETER, size)
+
+#define VIDEO_AUTO_FLUSH
+#ifdef VIDEO_AUTO_FLUSH
+static u32 video_auto_flush_state;
+#define VIDEO_AUTO_FLUSH_IDLE 0
+#define VIDEO_AUTO_FLUSH_MONITOR 1
+#define VIDEO_AUTO_FLUSH_TRIGGER 2
+#define VIDEO_AUTO_FLUSH_DONE 3
+#define VIDEO_AUTO_FLUSH_PTS_THRESHOLD 90000
+#define VIDEO_AUTO_FLUSH_BYTE_COUNT 1024
+
+static s32 audio_last_pts;
+static s32 audio_monitor_pts;
+#endif
+
+enum {
+ SEARCH_START_CODE = 0,
+ SEND_VIDEO_SEARCH,
+ SEND_AUDIO_SEARCH,
+ SEND_SUBPIC_SEARCH,
+ DISCARD_SEARCH,
+ DISCARD_ONLY
+#ifdef VIDEO_AUTO_FLUSH
+ ,
+ SEARCH_START_CODE_VIDEO_FLUSH
+#endif
+};
+
+enum {
+ AUDIO_FIRST_ACCESS_ARM = 0,
+ AUDIO_FIRST_ACCESS_POPING,
+ AUDIO_FIRST_ACCESS_DONE
+};
+
+static const char psparser_id[] = "psparser-id";
+
+static DECLARE_WAIT_QUEUE_HEAD(wq);
+
+static struct tasklet_struct psparser_tasklet;
+static u32 fetch_done;
+static u8 audio_id, video_id, sub_id, sub_id_max;
+static u32 audio_first_access;
+static u32 packet_remaining;
+static u32 video_data_parsed;
+static u32 audio_data_parsed;
+static u32 pts_equ_dts_flag;
+
+static unsigned first_apts, first_vpts;
+static unsigned audio_got_first_pts, video_got_first_dts, sub_got_first_pts;
+atomic_t sub_block_found = ATOMIC_INIT(0);
+
+#define DEBUG_VOB_SUB
+#ifdef DEBUG_VOB_SUB
+static u8 sub_found_num;
+static struct subtitle_info *sub_info[MAX_SUB_NUM];
+#endif
+
+static bool ptsmgr_first_vpts_ready(void)
+{
+ return (video_got_first_dts != 0) ? true : false;
+}
+
+static bool ptsmgr_first_apts_ready(void)
+{
+ return (audio_got_first_pts != 0) ? true : false;
+}
+
+static void ptsmgr_vpts_checkin(u32 pts)
+{
+ if (video_got_first_dts == 0) {
+ video_got_first_dts = 1;
+ first_vpts = pts;
+ }
+
+ pts_checkin_offset(PTS_TYPE_VIDEO, video_data_parsed, pts);
+}
+
+static void ptsmgr_apts_checkin(u32 pts)
+{
+ if (audio_got_first_pts == 0) {
+ audio_got_first_pts = 1;
+ first_apts = pts;
+ }
+ /* apts_checkin(pts); */
+ pts_checkin_offset(PTS_TYPE_AUDIO, audio_data_parsed, pts);
+
+#ifdef VIDEO_AUTO_FLUSH
+ audio_last_pts = pts;
+
+ if ((video_auto_flush_state == VIDEO_AUTO_FLUSH_IDLE)
+ && ptsmgr_first_vpts_ready()) {
+ video_auto_flush_state = VIDEO_AUTO_FLUSH_MONITOR;
+ audio_monitor_pts = pts;
+ }
+
+ if (video_auto_flush_state == VIDEO_AUTO_FLUSH_MONITOR) {
+ if ((audio_last_pts - audio_monitor_pts) >
+ VIDEO_AUTO_FLUSH_PTS_THRESHOLD)
+ video_auto_flush_state = VIDEO_AUTO_FLUSH_TRIGGER;
+ }
+#endif
+}
+
+static u32 parser_process(s32 type, s32 packet_len)
+{
+ s16 temp, header_len, misc_flags, i;
+ u32 pts = 0, dts = 0;
+ u32 pts_dts_flag = 0;
+ u16 invalid_pts = 0;
+
+ temp = PARSER_POP;
+ packet_len--;
+
+ if ((temp >> 6) == 0x02) {
+ /* mpeg-2 system */
+ misc_flags = PARSER_POP;
+ header_len = PARSER_POP;
+ packet_len -= 2;
+ packet_len -= header_len;
+
+ if ((misc_flags >> 6) > 1) {
+ /* PTS exist */
+ pts = ((PARSER_POP >> 1) & 7) << 30; /* bit 32-30 */
+ pts |= PARSER_POP << 22; /* bit 29-22 */
+ pts |= (PARSER_POP >> 1) << 15; /* bit 21-15 */
+ pts |= (PARSER_POP << 7); /* bit 14-07 */
+ pts |= (PARSER_POP >> 1); /* bit 06-00 */
+ header_len -= 5;
+ pts_dts_flag |= 2;
+ }
+
+ if ((misc_flags >> 6) > 2) {
+ /* DTS exist */
+ dts = ((PARSER_POP >> 1) & 7) << 30; /* bit 32-30 */
+ dts |= PARSER_POP << 22; /* bit 29-22 */
+ dts |= (PARSER_POP >> 1) << 15; /* bit 21-15 */
+ dts |= (PARSER_POP << 7); /* bit 14-07 */
+ dts |= (PARSER_POP >> 1); /* bit 06-00 */
+ header_len -= 5;
+ pts_dts_flag |= 1;
+ }
+
+ if (misc_flags & 0x20) {
+ /* ESCR_flag */
+ PARSER_POP;
+ PARSER_POP;
+ PARSER_POP;
+ PARSER_POP;
+ PARSER_POP;
+ PARSER_POP;
+ header_len -= 5;
+ }
+
+ if (misc_flags & 0x10) {
+ /* ES_rate_flag */
+ PARSER_POP;
+ PARSER_POP;
+ PARSER_POP;
+ header_len -= 3;
+ }
+
+ if (misc_flags & 0x08) {
+ /* DSM_trick_mode_flag */
+ PARSER_POP;
+ header_len -= 1;
+ }
+
+ if (misc_flags & 0x04) {
+ /* additional_copy_info_flag */
+ PARSER_POP;
+ header_len -= 1;
+ }
+
+ if (misc_flags & 0x02) {
+ /* PES_CRC_flag */
+ PARSER_POP;
+ PARSER_POP;
+ header_len -= 2;
+ }
+
+ if (misc_flags & 0x01) {
+ /* PES_extension_flag */
+ misc_flags = PARSER_POP;
+ header_len--;
+
+ if ((misc_flags & 0x80) && (header_len >= 128)) {
+ /* PES_private_data_flag */
+ for (i = 0; i < 128; i++)
+ PARSER_POP;
+
+ header_len -= 128;
+ }
+#if 0
+ if (misc_flags & 0x40) {
+ /* pack_header_field_flag */
+ /* Invalid case */
+ }
+#endif
+ if (misc_flags & 0x20) {
+ /* program_packet_sequence_counter_flag */
+ PARSER_POP;
+ PARSER_POP;
+ header_len -= 2;
+ }
+
+ if (misc_flags & 0x10) {
+ /* PSTD_buffer_flag */
+ PARSER_POP;
+ PARSER_POP;
+ header_len -= 2;
+ }
+
+ if (misc_flags & 1) {
+ /* PES_extension_flag_2 */
+ temp = PARSER_POP & 0x7f;
+
+ while (temp) {
+ PARSER_POP;
+ temp--;
+ header_len--;
+ }
+ }
+
+ while (header_len) {
+ PARSER_POP;
+ header_len--;
+ }
+ }
+
+ while (header_len) {
+ PARSER_POP;
+ header_len--;
+ }
+
+ } else {
+ /* mpeg-1 system */
+ while (temp == 0xff) {
+ temp = PARSER_POP;
+ packet_len--;
+ }
+
+ if ((temp >> 6) == 1) {
+ PARSER_POP; /* STD buffer size */
+ temp = PARSER_POP;
+ packet_len -= 2;
+ }
+
+ if (((temp >> 4) == 2) || ((temp >> 4) == 3)) {
+ pts = ((temp >> 1) & 7) << 30; /* bit 32-30 */
+ pts |= PARSER_POP << 22; /* bit 29-22 */
+ pts |= (PARSER_POP >> 1) << 15; /* bit 21-15 */
+ pts |= (PARSER_POP << 7); /* bit 14-07 */
+ pts |= (PARSER_POP >> 1); /* bit 06-00 */
+ packet_len -= 4;
+ pts_dts_flag |= 2;
+ }
+
+ if ((temp >> 4) == 3) {
+ dts = ((PARSER_POP >> 1) & 7) << 30; /* bit 32-30 */
+ dts |= PARSER_POP << 22; /* bit 29-22 */
+ dts |= (PARSER_POP >> 1) << 15; /* bit 21-15 */
+ dts |= (PARSER_POP << 7); /* bit 14-07 */
+ dts |= (PARSER_POP >> 1); /* bit 06-00 */
+ packet_len -= 5;
+ pts_dts_flag |= 1;
+ }
+ }
+
+ if ((pts == 0) && (dts == 0xffffffff)) {
+ invalid_pts = 1;
+ pr_info("invalid pts\n");
+ }
+
+ if (!packet_len)
+ return SEARCH_START_CODE;
+
+ else if (type == 0) {
+#ifdef VIDEO_AUTO_FLUSH
+ if (video_auto_flush_state == VIDEO_AUTO_FLUSH_MONITOR)
+ audio_monitor_pts = audio_last_pts;
+#endif
+
+ if ((pts_dts_flag) && (!invalid_pts)) {
+#if TIMESTAMP_IONLY
+ if (!ptsmgr_first_vpts_ready()) {
+ if (pts_dts_flag & 2)
+ ptsmgr_vpts_checkin(pts);
+ else
+ ptsmgr_vpts_checkin(dts);
+ } else if ((pts_dts_flag & 3) == 3) {
+ if (pts_equ_dts_flag) {
+ if (dts == pts)
+ ptsmgr_vpts_checkin(pts);
+ } else {
+ if (dts == pts)
+ pts_equ_dts_flag = 1;
+ ptsmgr_vpts_checkin(pts);
+ }
+ }
+#else
+ if (!ptsmgr_first_vpts_ready()) {
+ if (pts_dts_flag & 2)
+ ptsmgr_vpts_checkin(pts);
+ else
+ ptsmgr_vpts_checkin(dts);
+ } else if (pts_dts_flag & 2)
+ ptsmgr_vpts_checkin(pts);
+#endif
+ }
+
+ if (ptsmgr_first_vpts_ready() || invalid_pts) {
+ SET_BLOCK(packet_len);
+ video_data_parsed += packet_len;
+ return SEND_VIDEO_SEARCH;
+
+ } else {
+ SET_DISCARD_SIZE(packet_len);
+ return DISCARD_SEARCH;
+ }
+
+ } else if (type == 1) {
+ /* mpeg audio */
+ if (pts_dts_flag & 2)
+ ptsmgr_apts_checkin(pts);
+
+ if (ptsmgr_first_apts_ready()) {
+ SET_BLOCK(packet_len);
+ audio_data_parsed += packet_len;
+ return SEND_AUDIO_SEARCH;
+
+ } else {
+ SET_DISCARD_SIZE(packet_len);
+ return DISCARD_SEARCH;
+ }
+
+ } else if (type == 2) {
+ /* Private stream */
+ temp = PARSER_POP; /* sub_stream_id */
+ packet_len--;
+
+ if (((temp & 0xf8) == 0xa0) && (temp == audio_id)) {
+ /* DVD_VIDEO Audio LPCM data */
+ PARSER_POP;
+ temp = (PARSER_POP << 8) | PARSER_POP;
+ if (temp == 0)
+ temp = 4;
+ temp--;
+ packet_len -= 3;
+
+ if (audio_first_access == AUDIO_FIRST_ACCESS_ARM) {
+ if (temp) {
+ packet_remaining = packet_len - temp;
+ SET_DISCARD_SIZE(temp);
+ audio_first_access =
+ AUDIO_FIRST_ACCESS_POPING;
+ return DISCARD_ONLY;
+ }
+
+ audio_first_access = AUDIO_FIRST_ACCESS_DONE;
+
+ if (packet_len) {
+ SET_BLOCK(packet_len);
+ audio_data_parsed += packet_len;
+ return SEND_AUDIO_SEARCH;
+
+ } else
+ return SEARCH_START_CODE;
+
+ } else {
+ PARSER_POP;
+ PARSER_POP;
+ PARSER_POP;
+ packet_len -= 3;
+ }
+
+ if (pts_dts_flag & 2)
+ ptsmgr_apts_checkin(pts);
+
+ if (ptsmgr_first_apts_ready()) {
+ SET_BLOCK(packet_len);
+ audio_data_parsed += packet_len;
+ return SEND_AUDIO_SEARCH;
+
+ } else {
+ SET_DISCARD_SIZE(packet_len);
+ return DISCARD_SEARCH;
+ }
+
+ } else if (((temp & 0xf8) == 0x80) && (temp == audio_id)) {
+ /* Audio AC3 data */
+ PARSER_POP;
+ temp = (PARSER_POP << 8) | PARSER_POP;
+ packet_len -= 3;
+
+ if (audio_first_access == AUDIO_FIRST_ACCESS_ARM) {
+ if (pts_dts_flag & 2)
+ ptsmgr_apts_checkin(pts);
+
+ if ((temp > 2) && (packet_len > (temp - 2))) {
+ temp -= 2;
+ packet_remaining = packet_len - temp;
+ SET_DISCARD_SIZE(temp);
+ audio_first_access =
+ AUDIO_FIRST_ACCESS_POPING;
+ return DISCARD_ONLY;
+ }
+
+ audio_first_access = AUDIO_FIRST_ACCESS_DONE;
+
+ if (packet_len) {
+ SET_BLOCK(packet_len);
+ audio_data_parsed += packet_len;
+ return SEND_AUDIO_SEARCH;
+
+ } else
+ return SEARCH_START_CODE;
+ }
+
+ if (pts_dts_flag & 2)
+ ptsmgr_apts_checkin(pts);
+
+ if (ptsmgr_first_apts_ready()) {
+ SET_BLOCK(packet_len);
+ audio_data_parsed += packet_len;
+ return SEND_AUDIO_SEARCH;
+
+ } else {
+ SET_DISCARD_SIZE(packet_len);
+ return DISCARD_SEARCH;
+ }
+
+ } else if (((temp & 0xf8) == 0x88) && (temp == audio_id)) {
+ /* Audio DTS data */
+ PARSER_POP;
+ PARSER_POP;
+ PARSER_POP;
+ packet_len -= 3;
+
+ if (audio_first_access == AUDIO_FIRST_ACCESS_ARM)
+ audio_first_access = AUDIO_FIRST_ACCESS_DONE;
+
+ if (pts_dts_flag & 2)
+ ptsmgr_apts_checkin(pts);
+
+ if (ptsmgr_first_apts_ready()) {
+ SET_BLOCK(packet_len);
+ audio_data_parsed += packet_len;
+ return SEND_AUDIO_SEARCH;
+
+ } else {
+ SET_DISCARD_SIZE(packet_len);
+ return DISCARD_SEARCH;
+ }
+ } else if ((temp & 0xe0) == 0x20) {
+ if (temp > sub_id_max)
+ sub_id_max = temp;
+#ifdef DEBUG_VOB_SUB
+ for (i = 0; i < sub_found_num; i++) {
+ if (!sub_info[i])
+ break;
+ if (temp == sub_info[i]->id)
+ break;
+ }
+ if (i == sub_found_num && i < MAX_SUB_NUM) {
+ if (sub_info[sub_found_num]) {
+ sub_info[sub_found_num]->id = temp;
+ sub_found_num++;
+ pr_info
+ ("[%s]found new sub_id=0x%x (num %d)\n",
+ __func__, temp, sub_found_num);
+ } else {
+ pr_info
+ ("[%s]sub info NULL!\n", __func__);
+ }
+ }
+#endif
+
+ if (temp == sub_id) {
+ /* DVD sub-picture data */
+ if (!packet_len)
+ return SEARCH_START_CODE;
+
+ else {
+#if 0
+ if (pts_dts_flag & 2)
+ ptsmgr_spts_checkin(pts);
+
+ if (ptsmgr_first_spts_ready()) {
+ SET_BLOCK(packet_len);
+ return SEND_SUBPIC_SEARCH;
+
+ } else {
+ SET_DISCARD_SIZE(packet_len);
+ return DISCARD_SEARCH;
+ }
+#else
+ if (pts_dts_flag & 2)
+ sub_got_first_pts = 1;
+
+ if (sub_got_first_pts) {
+ pr_info
+ ("sub pts 0x%x, len %d\n",
+ pts, packet_len);
+ SET_BLOCK(packet_len);
+ WRITE_MPEG_REG
+ (PARSER_PARAMETER,
+ 16 <<
+ PARSER_PARAMETER_LENGTH_BIT);
+ WRITE_MPEG_REG
+ (PARSER_INSERT_DATA,
+ SUB_INSERT_START_CODE_HIGH);
+ WRITE_MPEG_REG
+ (PARSER_INSERT_DATA,
+ SUB_INSERT_START_CODE_LOW |
+ get_sub_type());
+ WRITE_MPEG_REG
+ (PARSER_INSERT_DATA,
+ packet_len);
+ WRITE_MPEG_REG
+ (PARSER_INSERT_DATA, pts);
+ atomic_set(&sub_block_found, 1);
+ return SEND_SUBPIC_SEARCH;
+ }
+
+ SET_DISCARD_SIZE(packet_len);
+ return DISCARD_SEARCH;
+#endif
+ }
+ } else {
+ SET_DISCARD_SIZE(packet_len);
+ return DISCARD_SEARCH;
+ }
+ } else {
+ SET_DISCARD_SIZE(packet_len);
+ return DISCARD_SEARCH;
+ }
+
+ if (!packet_len)
+ return SEARCH_START_CODE;
+
+ else {
+ SET_BLOCK(packet_len);
+ audio_data_parsed += packet_len;
+ return SEND_AUDIO_SEARCH;
+ }
+ }
+
+ return SEARCH_START_CODE;
+}
+
+static void on_start_code_found(int start_code)
+{
+ unsigned short packet_len;
+ unsigned short temp;
+ unsigned next_action;
+#if SAVE_SCR
+ unsigned scr;
+#endif
+
+ if (atomic_read(&sub_block_found)) {
+ wakeup_sub_poll();
+ atomic_set(&sub_block_found, 0);
+ }
+
+ if (audio_first_access == AUDIO_FIRST_ACCESS_POPING) {
+ /*
+ *we are in the procedure of poping data for audio first
+ * access, continue with last packet
+ */
+ audio_first_access = AUDIO_FIRST_ACCESS_DONE;
+
+ if (packet_remaining) {
+ next_action = SEND_AUDIO_SEARCH;
+ SET_BLOCK(packet_remaining);
+
+ } else
+ next_action = SEARCH_START_CODE;
+
+ } else if (start_code == 0xba) { /* PACK_START_CODE */
+ temp = PARSER_POP;
+
+ if ((temp >> 6) == 0x01) {
+#if SAVE_SCR
+ scr = ((temp >> 3) & 0x3) << 30; /* bit 31-30 */
+ scr |= (temp & 0x3) << 28; /* bit 29-28 */
+ scr |= (PARSER_POP) << 20; /* bit 27-20 */
+ temp = PARSER_POP;
+ scr |= (temp >> 4) << 16; /* bit 19-16 */
+ scr |= (temp & 7) << 13; /* bit 15-13 */
+ scr |= (PARSER_POP) << 5; /* bit 12-05 */
+ scr |= (PARSER_POP) >> 3; /* bit 04-00 */
+#else
+ PARSER_POP;
+ PARSER_POP;
+ PARSER_POP;
+ PARSER_POP;
+#endif
+ PARSER_POP;
+ PARSER_POP;
+ PARSER_POP;
+ PARSER_POP;
+ temp = PARSER_POP & 7;
+
+ while (temp) { /* stuff byte */
+ PARSER_POP;
+ temp--;
+ }
+
+ } else {
+ /* mpeg-1 Pack Header */
+#if SAVE_SCR
+ scr = ((temp >> 1) & 0x3) << 30; /* bit 31-30 */
+ scr |= (PARSER_POP) << 22; /* bit 29-22 */
+ scr |= (PARSER_POP >> 1) << 15; /* bit 21-15 */
+ scr |= (PARSER_POP) << 7; /* bit 14-07 */
+ scr |= (PARSER_POP >> 1); /* bit 06-00 */
+#else
+ PARSER_POP;
+ PARSER_POP;
+ PARSER_POP;
+ PARSER_POP;
+#endif
+ }
+
+#ifdef VIDEO_AUTO_FLUSH
+ if (video_auto_flush_state == VIDEO_AUTO_FLUSH_TRIGGER) {
+ next_action = SEARCH_START_CODE_VIDEO_FLUSH;
+ video_auto_flush_state = VIDEO_AUTO_FLUSH_DONE;
+ } else
+#endif
+
+ next_action = SEARCH_START_CODE;
+
+ } else {
+ packet_len = (PARSER_POP << 8) | PARSER_POP;
+
+ if (start_code == video_id)
+ next_action = parser_process(0, packet_len);
+
+ else if (start_code == audio_id) {
+ /* add mpeg audio packet length check */
+ if (packet_len > MAX_MPG_AUDIOPK_SIZE)
+ next_action = SEARCH_START_CODE;
+
+ else
+ next_action = parser_process(1, packet_len);
+
+ } else if (start_code == 0xbb) {
+ SET_DISCARD_SIZE(packet_len);
+ next_action = DISCARD_SEARCH;
+ } else if (start_code == 0xbd)
+ next_action = parser_process(2, packet_len);
+
+ else if (start_code == 0xbf) {
+ SET_DISCARD_SIZE(packet_len);
+ next_action = DISCARD_SEARCH;
+ } else if ((start_code < 0xc0) || (start_code > 0xc8))
+ next_action = SEARCH_START_CODE;
+
+ else if (packet_len) {
+ SET_DISCARD_SIZE(packet_len);
+ next_action = DISCARD_SEARCH;
+
+ } else
+ next_action = SEARCH_START_CODE;
+ }
+
+ switch (next_action) {
+ case SEARCH_START_CODE:
+ WRITE_MPEG_REG(PARSER_CONTROL, PARSER_AUTOSEARCH);
+ break;
+
+ case SEND_VIDEO_SEARCH:
+ WRITE_MPEG_REG_BITS(PARSER_CONTROL,
+ PARSER_AUTOSEARCH | PARSER_VIDEO |
+ PARSER_WRITE, ES_CTRL_BIT, ES_CTRL_WID);
+ break;
+
+ case SEND_AUDIO_SEARCH:
+ WRITE_MPEG_REG_BITS(PARSER_CONTROL,
+ PARSER_AUTOSEARCH | PARSER_AUDIO |
+ PARSER_WRITE, ES_CTRL_BIT, ES_CTRL_WID);
+ break;
+
+ case SEND_SUBPIC_SEARCH:
+ WRITE_MPEG_REG_BITS(PARSER_CONTROL,
+ PARSER_AUTOSEARCH | PARSER_SUBPIC |
+ PARSER_WRITE | ES_INSERT_BEFORE_ES_WRITE,
+ ES_CTRL_BIT, ES_CTRL_WID);
+ break;
+
+ case DISCARD_SEARCH:
+ WRITE_MPEG_REG_BITS(PARSER_CONTROL,
+ PARSER_AUTOSEARCH | PARSER_DISCARD,
+ ES_CTRL_BIT, ES_CTRL_WID);
+ break;
+
+ case DISCARD_ONLY:
+ WRITE_MPEG_REG_BITS(PARSER_CONTROL,
+ PARSER_DISCARD, ES_CTRL_BIT, ES_CTRL_WID);
+ break;
+
+#ifdef VIDEO_AUTO_FLUSH
+ case SEARCH_START_CODE_VIDEO_FLUSH:
+ WRITE_MPEG_REG(PARSER_INSERT_DATA, 0xffffffff);
+ WRITE_MPEG_REG(PARSER_INSERT_DATA, 0xffffffff);
+ WRITE_MPEG_REG(PARSER_PARAMETER,
+ ((VIDEO_AUTO_FLUSH_BYTE_COUNT /
+ 8) << PARSER_PARAMETER_LOOP_BIT) | (8 <<
+ PARSER_PARAMETER_LENGTH_BIT));
+ WRITE_MPEG_REG(PARSER_CONTROL,
+ PARSER_AUTOSEARCH | PARSER_VIDEO | PARSER_WRITE |
+ ES_INSERT_BEFORE_ES_WRITE);
+ break;
+#endif
+ }
+}
+
+static void parser_tasklet(ulong data)
+{
+ s32 sc;
+ u32 int_status = READ_MPEG_REG(PARSER_INT_STATUS);
+
+ WRITE_MPEG_REG(PARSER_INT_STATUS, int_status);
+
+ if (int_status & PARSER_INTSTAT_FETCH_CMD) {
+ fetch_done = 1;
+
+ wake_up_interruptible(&wq);
+ }
+
+ if (int_status & PARSER_INTSTAT_SC_FOUND) {
+ sc = PARSER_POP;
+
+ on_start_code_found(sc);
+
+ } else if (int_status & PARSER_INTSTAT_DISCARD)
+ on_start_code_found(0);
+}
+
+static irqreturn_t parser_isr(int irq, void *dev_id)
+{
+ tasklet_schedule(&psparser_tasklet);
+
+ return IRQ_HANDLED;
+}
+
+static ssize_t _psparser_write(const char __user *buf, size_t count)
+{
+ size_t r = count;
+ const char __user *p = buf;
+ u32 len;
+ int ret;
+ dma_addr_t dma_addr = 0;
+
+ if (r > 0) {
+ len = min_t(size_t, r, FETCHBUF_SIZE);
+ if (copy_from_user(fetchbuf, p, len))
+ return -EFAULT;
+
+ dma_addr =
+ dma_map_single(amports_get_dma_device(),
+ fetchbuf, FETCHBUF_SIZE, DMA_TO_DEVICE);
+ if (dma_mapping_error(amports_get_dma_device(), dma_addr))
+ return -EFAULT;
+
+
+ fetch_done = 0;
+
+ wmb(); /* Ensure fetchbuf contents visible */
+
+ WRITE_MPEG_REG(PARSER_FETCH_ADDR, dma_addr);
+
+ WRITE_MPEG_REG(PARSER_FETCH_CMD, (7 << FETCH_ENDIAN) | len);
+ dma_unmap_single(amports_get_dma_device(), dma_addr,
+ FETCHBUF_SIZE, DMA_TO_DEVICE);
+ ret =
+ wait_event_interruptible_timeout(wq, fetch_done != 0,
+ HZ / 10);
+ if (ret == 0) {
+ WRITE_MPEG_REG(PARSER_FETCH_CMD, 0);
+ pr_info("write timeout, retry\n");
+ return -EAGAIN;
+ } else if (ret < 0)
+ return -ERESTARTSYS;
+
+ p += len;
+ r -= len;
+ }
+
+ return count - r;
+}
+
+s32 psparser_init(u32 vid, u32 aid, u32 sid, struct vdec_s *vdec)
+{
+ s32 r;
+ u32 parser_sub_start_ptr;
+ u32 parser_sub_end_ptr;
+ u32 parser_sub_rp;
+
+#ifdef DEBUG_VOB_SUB
+ u8 i;
+
+ for (i = 0; i < MAX_SUB_NUM; i++) {
+ sub_info[i] = kzalloc(sizeof(struct subtitle_info), GFP_KERNEL);
+ if (!sub_info[i]) {
+ pr_info
+ ("[psparser_init]alloc for subtitle info failed\n");
+ } else
+ sub_info[i]->id = -1;
+ }
+ sub_found_num = 0;
+#endif
+ parser_sub_start_ptr = READ_MPEG_REG(PARSER_SUB_START_PTR);
+ parser_sub_end_ptr = READ_MPEG_REG(PARSER_SUB_END_PTR);
+ parser_sub_rp = READ_MPEG_REG(PARSER_SUB_RP);
+
+ video_id = vid;
+ audio_id = aid;
+ sub_id = sid;
+ audio_got_first_pts = 0;
+ video_got_first_dts = 0;
+ sub_got_first_pts = 0;
+ first_apts = 0;
+ first_vpts = 0;
+ pts_equ_dts_flag = 0;
+
+#ifdef VIDEO_AUTO_FLUSH
+ video_auto_flush_state = VIDEO_AUTO_FLUSH_IDLE;
+#endif
+
+ pr_info("video 0x%x, audio 0x%x, sub 0x%x\n", video_id, audio_id,
+ sub_id);
+ if (fetchbuf == 0) {
+ pr_info("%s: no fetchbuf\n", __func__);
+ return -ENOMEM;
+ }
+
+ WRITE_MPEG_REG(RESET1_REGISTER, RESET_PARSER);
+
+ /* TS data path */
+#ifndef CONFIG_AM_DVB
+ WRITE_MPEG_REG(FEC_INPUT_CONTROL, 0);
+#else
+ tsdemux_set_reset_flag();
+#endif
+ CLEAR_MPEG_REG_MASK(TS_HIU_CTL, 1 << USE_HI_BSF_INTERFACE);
+ CLEAR_MPEG_REG_MASK(TS_HIU_CTL_2, 1 << USE_HI_BSF_INTERFACE);
+ CLEAR_MPEG_REG_MASK(TS_HIU_CTL_3, 1 << USE_HI_BSF_INTERFACE);
+ CLEAR_MPEG_REG_MASK(TS_FILE_CONFIG, (1 << TS_HIU_ENABLE));
+
+ /* hook stream buffer with PARSER */
+ WRITE_MPEG_REG(PARSER_VIDEO_START_PTR, vdec->input.start);
+ WRITE_MPEG_REG(PARSER_VIDEO_END_PTR,
+ vdec->input.start + vdec->input.size - 8);
+
+ if (vdec_stream_auto(vdec)) {
+ CLEAR_MPEG_REG_MASK(PARSER_ES_CONTROL, ES_VID_MAN_RD_PTR);
+ WRITE_VREG(VLD_MEM_VIFIFO_BUF_CNTL, MEM_BUFCTRL_INIT);
+ CLEAR_VREG_MASK(VLD_MEM_VIFIFO_BUF_CNTL, MEM_BUFCTRL_INIT);
+ } else {
+ SET_MPEG_REG_MASK(PARSER_ES_CONTROL, ES_VID_MAN_RD_PTR);
+ WRITE_MPEG_REG(PARSER_VIDEO_WP, vdec->input.start);
+ WRITE_MPEG_REG(PARSER_VIDEO_RP, vdec->input.start);
+ }
+
+ WRITE_MPEG_REG(PARSER_AUDIO_START_PTR,
+ READ_MPEG_REG(AIU_MEM_AIFIFO_START_PTR));
+ WRITE_MPEG_REG(PARSER_AUDIO_END_PTR,
+ READ_MPEG_REG(AIU_MEM_AIFIFO_END_PTR));
+ CLEAR_MPEG_REG_MASK(PARSER_ES_CONTROL, ES_AUD_MAN_RD_PTR);
+
+ WRITE_MPEG_REG(PARSER_CONFIG,
+ (10 << PS_CFG_PFIFO_EMPTY_CNT_BIT) |
+ (1 << PS_CFG_MAX_ES_WR_CYCLE_BIT) |
+ (16 << PS_CFG_MAX_FETCH_CYCLE_BIT));
+
+ WRITE_VREG(VLD_MEM_VIFIFO_BUF_CNTL, MEM_BUFCTRL_INIT);
+ CLEAR_VREG_MASK(VLD_MEM_VIFIFO_BUF_CNTL, MEM_BUFCTRL_INIT);
+
+ WRITE_MPEG_REG(AIU_MEM_AIFIFO_BUF_CNTL, MEM_BUFCTRL_INIT);
+ CLEAR_MPEG_REG_MASK(AIU_MEM_AIFIFO_BUF_CNTL, MEM_BUFCTRL_INIT);
+
+ WRITE_MPEG_REG(PARSER_SUB_START_PTR, parser_sub_start_ptr);
+ WRITE_MPEG_REG(PARSER_SUB_END_PTR, parser_sub_end_ptr);
+ WRITE_MPEG_REG(PARSER_SUB_RP, parser_sub_start_ptr);
+ WRITE_MPEG_REG(PARSER_SUB_WP, parser_sub_start_ptr);
+ SET_MPEG_REG_MASK(PARSER_ES_CONTROL,
+ (7 << ES_SUB_WR_ENDIAN_BIT) | ES_SUB_MAN_RD_PTR);
+
+ WRITE_MPEG_REG(PFIFO_RD_PTR, 0);
+ WRITE_MPEG_REG(PFIFO_WR_PTR, 0);
+
+ WRITE_MPEG_REG(PARSER_SEARCH_PATTERN, MPEG_START_CODE_PATTERN);
+ WRITE_MPEG_REG(PARSER_SEARCH_MASK, MPEG_START_CODE_MASK);
+
+ WRITE_MPEG_REG(PARSER_CONFIG,
+ (10 << PS_CFG_PFIFO_EMPTY_CNT_BIT) |
+ (1 << PS_CFG_MAX_ES_WR_CYCLE_BIT) |
+ PS_CFG_STARTCODE_WID_24 |
+ PS_CFG_PFIFO_ACCESS_WID_8 | /* single byte pop */
+ (16 << PS_CFG_MAX_FETCH_CYCLE_BIT));
+ WRITE_MPEG_REG(PARSER_CONTROL, PARSER_AUTOSEARCH);
+
+ tasklet_init(&psparser_tasklet, parser_tasklet, 0);
+ r = pts_start(PTS_TYPE_VIDEO);
+ if (r < 0)
+ goto Err_1;
+ r = pts_start(PTS_TYPE_AUDIO);
+ if (r < 0)
+ goto Err_2;
+
+ video_data_parsed = 0;
+ audio_data_parsed = 0;
+ /*TODO irq */
+
+ r = vdec_request_irq(PARSER_IRQ, parser_isr,
+ "psparser", (void *)psparser_id);
+
+ if (r) {
+ pr_info("PS Demux irq register failed.\n");
+
+ r = -ENOENT;
+ goto Err_3;
+ }
+
+ WRITE_MPEG_REG(PARSER_INT_STATUS, 0xffff);
+ WRITE_MPEG_REG(PARSER_INT_ENABLE,
+ PARSER_INT_ALL << PARSER_INT_HOST_EN_BIT);
+
+ return 0;
+
+Err_3:
+ pts_stop(PTS_TYPE_AUDIO);
+
+Err_2:
+ pts_stop(PTS_TYPE_VIDEO);
+
+Err_1:
+ return r;
+}
+
+void psparser_release(void)
+{
+ u8 i;
+
+ pr_info("psparser_release\n");
+
+ WRITE_MPEG_REG(PARSER_INT_ENABLE, 0);
+ /*TODO irq */
+
+ vdec_free_irq(PARSER_IRQ, (void *)psparser_id);
+
+ pts_stop(PTS_TYPE_VIDEO);
+ pts_stop(PTS_TYPE_AUDIO);
+#ifdef DEBUG_VOB_SUB
+ for (i = 0; i < MAX_SUB_NUM; i++)
+ kfree(sub_info[i]);
+ pr_info("psparser release subtitle info\n");
+#endif
+}
+
+ssize_t psparser_write(struct file *file,
+ struct stream_buf_s *vbuf,
+ struct stream_buf_s *abuf,
+ const char __user *buf, size_t count)
+{
+ s32 r;
+
+ struct port_priv_s *priv = (struct port_priv_s *)file->private_data;
+ struct stream_port_s *port = priv->port;
+
+ if ((stbuf_space(vbuf) < count) || (stbuf_space(abuf) < count)) {
+ if (file->f_flags & O_NONBLOCK)
+ return -EAGAIN;
+
+ if ((port->flag & PORT_FLAG_VID)
+ && (stbuf_space(vbuf) < count)) {
+ r = stbuf_wait_space(vbuf, count);
+ if (r < 0)
+ return r;
+ }
+ if ((port->flag & PORT_FLAG_AID)
+ && (stbuf_space(abuf) < count)) {
+ r = stbuf_wait_space(abuf, count);
+ if (r < 0)
+ return r;
+ }
+ }
+
+ return _psparser_write(buf, count);
+}
+
+void psparser_change_avid(unsigned int vid, unsigned int aid)
+{
+ video_id = vid;
+ audio_id = aid;
+}
+
+void psparser_change_sid(unsigned int sid)
+{
+ sub_id = sid;
+}
+
+void psparser_audio_reset(void)
+{
+ ulong flags;
+
+ DEFINE_SPINLOCK(lock);
+
+ spin_lock_irqsave(&lock, flags);
+
+ WRITE_MPEG_REG(PARSER_AUDIO_WP,
+ READ_MPEG_REG(AIU_MEM_AIFIFO_START_PTR));
+ WRITE_MPEG_REG(PARSER_AUDIO_RP,
+ READ_MPEG_REG(AIU_MEM_AIFIFO_START_PTR));
+
+ WRITE_MPEG_REG(PARSER_AUDIO_START_PTR,
+ READ_MPEG_REG(AIU_MEM_AIFIFO_START_PTR));
+ WRITE_MPEG_REG(PARSER_AUDIO_END_PTR,
+ READ_MPEG_REG(AIU_MEM_AIFIFO_END_PTR));
+ CLEAR_MPEG_REG_MASK(PARSER_ES_CONTROL, ES_AUD_MAN_RD_PTR);
+
+ WRITE_MPEG_REG(AIU_MEM_AIFIFO_BUF_CNTL, MEM_BUFCTRL_INIT);
+ CLEAR_MPEG_REG_MASK(AIU_MEM_AIFIFO_BUF_CNTL, MEM_BUFCTRL_INIT);
+
+ audio_data_parsed = 0;
+
+ spin_unlock_irqrestore(&lock, flags);
+
+}
+
+void psparser_sub_reset(void)
+{
+ ulong flags;
+
+ DEFINE_SPINLOCK(lock);
+ u32 parser_sub_start_ptr;
+ u32 parser_sub_end_ptr;
+
+ spin_lock_irqsave(&lock, flags);
+
+ parser_sub_start_ptr = READ_MPEG_REG(PARSER_SUB_START_PTR);
+ parser_sub_end_ptr = READ_MPEG_REG(PARSER_SUB_END_PTR);
+
+ WRITE_MPEG_REG(PARSER_SUB_START_PTR, parser_sub_start_ptr);
+ WRITE_MPEG_REG(PARSER_SUB_END_PTR, parser_sub_end_ptr);
+ WRITE_MPEG_REG(PARSER_SUB_RP, parser_sub_start_ptr);
+ WRITE_MPEG_REG(PARSER_SUB_WP, parser_sub_start_ptr);
+ SET_MPEG_REG_MASK(PARSER_ES_CONTROL,
+ (7 << ES_SUB_WR_ENDIAN_BIT) | ES_SUB_MAN_RD_PTR);
+
+ spin_unlock_irqrestore(&lock, flags);
+
+}
+
+u8 psparser_get_sub_found_num(void)
+{
+#ifdef DEBUG_VOB_SUB
+ return sub_found_num;
+#else
+ return 0;
+#endif
+}
+
+u8 psparser_get_sub_info(struct subtitle_info **sub_infos)
+{
+#ifdef DEBUG_VOB_SUB
+ u8 i = 0;
+ int ret = 0;
+ u8 size = sizeof(struct subtitle_info);
+
+ for (i = 0; i < sub_found_num; i++) {
+ if (!sub_info[i]) {
+ pr_info
+ ("[psparser_get_sub_info:%d] sub_info[%d] NULL\n",
+ __LINE__, i);
+ ret = -1;
+ break;
+ }
+ if (!sub_infos[i]) {
+ pr_info
+ ("[psparser_get_sub_info:%d] sub_infos[%d] NULL\n",
+ __LINE__, i);
+ ret = -2;
+ break;
+ }
+ memcpy(sub_infos[i], sub_info[i], size);
+ }
+ return ret;
+#else
+ return 0;
+#endif
+}
diff --git a/drivers/stream_input/parser/psparser.h b/drivers/stream_input/parser/psparser.h
new file mode 100644
index 0000000..1280b6a
--- a/dev/null
+++ b/drivers/stream_input/parser/psparser.h
@@ -0,0 +1,141 @@
+/*
+ * drivers/amlogic/media/stream_input/parser/psparser.h
+ *
+ * Copyright (C) 2016 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 PSPARSER_H
+#define PSPARSER_H
+
+#include "../../frame_provider/decoder/utils/vdec.h"
+
+extern s32 psparser_init(u32 vid, u32 aid, u32 sid, struct vdec_s *vdec);
+
+extern void psparser_release(void);
+
+extern ssize_t psparser_write(struct file *file,
+ struct stream_buf_s *vbuf,
+ struct stream_buf_s *abuf, const char __user *buf, size_t count);
+
+extern void psparser_change_avid(unsigned int vid, unsigned int aid);
+
+extern void psparser_change_sid(unsigned int sid);
+
+extern void psparser_audio_reset(void);
+
+extern void psparser_sub_reset(void);
+
+extern u8 psparser_get_sub_found_num(void);
+
+extern u8 psparser_get_sub_info(struct subtitle_info *sub_infos[]);
+
+#ifdef CONFIG_AM_DVB
+extern int tsdemux_set_reset_flag(void);
+#endif
+
+/* TODO: move to register headers */
+#define ES_PACK_SIZE_BIT 8
+#define ES_PACK_SIZE_WID 24
+
+#define ES_CTRL_WID 8
+#define ES_CTRL_BIT 0
+#define ES_TYPE_MASK (3 << 6)
+#define ES_TYPE_VIDEO (0 << 6)
+#define ES_TYPE_AUDIO (1 << 6)
+#define ES_TYPE_SUBTITLE (2 << 6)
+
+#define ES_WRITE (1<<5)
+#define ES_PASSTHROUGH (1<<4)
+#define ES_INSERT_BEFORE_ES_WRITE (1<<3)
+#define ES_DISCARD (1<<2)
+#define ES_SEARCH (1<<1)
+#define ES_PARSER_START (1<<0)
+#define ES_PARSER_BUSY (1<<0)
+
+#define PARSER_INTSTAT_FETCH_CMD (1<<7)
+#define PARSER_INTSTAT_PARSE (1<<4)
+#define PARSER_INTSTAT_DISCARD (1<<3)
+#define PARSER_INTSTAT_INSZERO (1<<2)
+#define PARSER_INTSTAT_ACT_NOSSC (1<<1)
+#define PARSER_INTSTAT_SC_FOUND (1<<0)
+
+#define FETCH_CIR_BUF (1<<31)
+#define FETCH_CHK_BUF_STOP (1<<30)
+#define FETCH_PASSTHROUGH (1<<29)
+#define FETCH_ENDIAN 27
+#define FETCH_PASSTHROUGH_TYPE_MASK (0x3<<27)
+#define FETCH_ENDIAN_MASK (0x7<<27)
+#define FETCH_BUF_SIZE_MASK (0x7ffffff)
+#define FETCH_CMD_PTR_MASK 3
+#define FETCH_CMD_RD_PTR_BIT 5
+#define FETCH_CMD_WR_PTR_BIT 3
+#define FETCH_CMD_NUM_MASK 3
+#define FETCH_CMD_NUM_BIT 0
+
+#define ES_COUNT_MASK 0xfff
+#define ES_COUNT_BIT 20
+#define ES_REQ_PENDING (1<<19)
+#define ES_PASSTHROUGH_EN (1<<18)
+#define ES_PASSTHROUGH_TYPE_MASK (3<<16)
+#define ES_PASSTHROUGH_TYPE_VIDEO (0<<16)
+#define ES_PASSTHROUGH_TYPE_AUDIO (1<<16)
+#define ES_PASSTHROUGH_TYPE_SUBTITLE (2<<16)
+#define ES_WR_ENDIAN_MASK (0x7)
+#define ES_SUB_WR_ENDIAN_BIT 9
+#define ES_SUB_MAN_RD_PTR (1<<8)
+#define ES_AUD_WR_ENDIAN_BIT 5
+#define ES_AUD_MAN_RD_PTR (1<<4)
+#define ES_VID_WR_ENDIAN_BIT 1
+#define ES_VID_MAN_RD_PTR (1<<0)
+
+#define PS_CFG_FETCH_DMA_URGENT (1<<31)
+#define PS_CFG_STREAM_DMA_URGENT (1<<30)
+#define PS_CFG_FORCE_PFIFO_REN (1<<29)
+#define PS_CFG_PFIFO_PEAK_EN (1<<28)
+#define PS_CFG_SRC_SEL_BIT 24
+#define PS_CFG_SRC_SEL_MASK (3<<PS_CFG_SRC_SEL_BIT)
+#define PS_CFG_SRC_SEL_FETCH (0<<PS_CFG_SRC_SEL_BIT)
+#define PS_CFG_SRC_SEL_AUX1 (1<<PS_CFG_SRC_SEL_BIT) /* from NDMA */
+#define PS_CFG_SRC_SEL_AUX2 (2<<PS_CFG_SRC_SEL_BIT)
+#define PS_CFG_SRC_SEL_AUX3 (3<<PS_CFG_SRC_SEL_BIT)
+#define PS_CFG_PFIFO_EMPTY_CNT_BIT 16
+#define PS_CFG_PFIFO_EMPTY_CNT_MASK 0xff
+#define PS_CFG_MAX_ES_WR_CYCLE_BIT 12
+#define PS_CFG_MAX_ES_WR_CYCLE_MASK 0xf
+#define PS_CFG_STARTCODE_WID_MASK (0x3<<10)
+#define PS_CFG_STARTCODE_WID_8 (0x0<<10)
+#define PS_CFG_STARTCODE_WID_16 (0x1<<10)
+#define PS_CFG_STARTCODE_WID_24 (0x2<<10)
+#define PS_CFG_STARTCODE_WID_32 (0x3<<10)
+#define PS_CFG_PFIFO_ACCESS_WID_MASK (0x3<<8)
+#define PS_CFG_PFIFO_ACCESS_WID_8 (0x0<<8)
+#define PS_CFG_PFIFO_ACCESS_WID_16 (0x1<<8)
+#define PS_CFG_PFIFO_ACCESS_WID_24 (0x2<<8)
+#define PS_CFG_PFIFO_ACCESS_WID_32 (0x3<<8)
+#define PS_CFG_MAX_FETCH_CYCLE_BIT 0
+#define PS_CFG_MAX_FETCH_CYCLE_MASK 0xff
+
+#define PARSER_INT_DISABLE_CNT_MASK 0xffff
+#define PARSER_INT_DISABLE_CNT_BIT 16
+#define PARSER_INT_HOST_EN_MASK 0xff
+#define PARSER_INT_HOST_EN_BIT 8
+#define PARSER_INT_AMRISC_EN_MASK 0xff
+#define PARSER_INT_AMRISC_EN_BIT 0
+#define PARSER_INT_ALL 0xff
+
+#define RESET_PARSER (1<<8)
+#define TS_HIU_ENABLE 5
+#define USE_HI_BSF_INTERFACE 7
+
+#endif /* PSPARSER_H */
diff --git a/drivers/stream_input/parser/streambuf.c b/drivers/stream_input/parser/streambuf.c
new file mode 100644
index 0000000..15056d6
--- a/dev/null
+++ b/drivers/stream_input/parser/streambuf.c
@@ -0,0 +1,426 @@
+/*
+ * drivers/amlogic/media/stream_input/parser/streambuf.c
+ *
+ * Copyright (C) 2016 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.
+ *
+*/
+
+#include <linux/kernel.h>
+#include <linux/spinlock.h>
+#include <linux/timer.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/io.h>
+#include <linux/amlogic/media/frame_sync/ptsserv.h>
+#include <linux/amlogic/media/utils/vformat.h>
+#include <linux/amlogic/iomap.h>
+#include <asm/cacheflush.h>
+#include <linux/uaccess.h>
+#include <linux/vmalloc.h>
+/* #include <mach/am_regs.h> */
+
+#include <linux/amlogic/media/utils/vdec_reg.h>
+#include "../../frame_provider/decoder/utils/vdec.h"
+#include "streambuf_reg.h"
+#include "streambuf.h"
+#include <linux/amlogic/media/utils/amports_config.h>
+#include "../amports/amports_priv.h"
+#include <linux/dma-mapping.h>
+#include <linux/dma-contiguous.h>
+#include <linux/amlogic/media/codec_mm/codec_mm.h>
+
+#define STBUF_SIZE (64*1024)
+#define STBUF_WAIT_INTERVAL (HZ/100)
+#define MEM_NAME "streambuf"
+
+void *fetchbuf;
+
+static s32 _stbuf_alloc(struct stream_buf_s *buf)
+{
+ if (buf->buf_size == 0)
+ return -ENOBUFS;
+
+ while (buf->buf_start == 0) {
+ int flags = CODEC_MM_FLAGS_DMA;
+
+ buf->buf_page_num = PAGE_ALIGN(buf->buf_size) / PAGE_SIZE;
+ if (buf->type == BUF_TYPE_SUBTITLE)
+ flags = CODEC_MM_FLAGS_DMA_CPU;
+
+ /*
+ *if 4k,
+ *used cma first,for less mem fragments.
+ */
+ if (((buf->type == BUF_TYPE_HEVC) ||
+ (buf->type == BUF_TYPE_VIDEO)) &&
+ buf->for_4k)
+ flags |= CODEC_MM_FLAGS_CMA_FIRST;
+ if (buf->buf_size > 20 * 1024 * 1024)
+ flags |= CODEC_MM_FLAGS_CMA_FIRST;
+
+ if ((buf->type == BUF_TYPE_HEVC) ||
+ (buf->type == BUF_TYPE_VIDEO)) {
+ flags |= CODEC_MM_FLAGS_FOR_VDECODER;
+ } else if (buf->type == BUF_TYPE_AUDIO) {
+ flags |= CODEC_MM_FLAGS_FOR_ADECODER;
+ flags |= CODEC_MM_FLAGS_DMA_CPU;
+ }
+
+ if (buf->type == BUF_TYPE_VIDEO
+ && codec_mm_get_total_size() <= 68 * SZ_1M) {
+ buf->buf_size = (1024*1024*10);
+ buf->buf_page_num =
+ PAGE_ALIGN(buf->buf_size) / PAGE_SIZE;
+ }
+
+ buf->buf_start = codec_mm_alloc_for_dma(MEM_NAME,
+ buf->buf_page_num, 4+PAGE_SHIFT, flags);
+ if (!buf->buf_start) {
+ int is_video = (buf->type == BUF_TYPE_HEVC) ||
+ (buf->type == BUF_TYPE_VIDEO);
+ if (is_video && buf->buf_size >= 9 * SZ_1M) {/*min 6M*/
+ int old_size = buf->buf_size;
+
+ buf->buf_size =
+ PAGE_ALIGN(buf->buf_size * 2/3);
+ pr_info("%s stbuf alloced size = %d failed try small %d size\n",
+ (buf->type == BUF_TYPE_HEVC) ? "HEVC" :
+ (buf->type == BUF_TYPE_VIDEO) ? "Video" :
+ (buf->type == BUF_TYPE_AUDIO) ? "Audio" :
+ "Subtitle", old_size, buf->buf_size);
+ continue;
+ }
+ pr_info("%s stbuf alloced size = %d failed\n",
+ (buf->type == BUF_TYPE_HEVC) ? "HEVC" :
+ (buf->type == BUF_TYPE_VIDEO) ? "Video" :
+ (buf->type == BUF_TYPE_AUDIO) ? "Audio" :
+ "Subtitle", buf->buf_size);
+ return -ENOMEM;
+ }
+ pr_info("%s stbuf alloced at %p, size = %d\n",
+ (buf->type == BUF_TYPE_HEVC) ? "HEVC" :
+ (buf->type == BUF_TYPE_VIDEO) ? "Video" :
+ (buf->type == BUF_TYPE_AUDIO) ? "Audio" :
+ "Subtitle", (void *)buf->buf_start,
+ buf->buf_size);
+ }
+
+ buf->flag |= BUF_FLAG_ALLOC;
+
+ return 0;
+}
+
+int stbuf_change_size(struct stream_buf_s *buf, int size)
+{
+ unsigned long old_buf;
+ int old_size, old_pagenum;
+ int ret;
+
+ pr_info("buffersize=%d,%d,start=%p\n", size, buf->buf_size,
+ (void *)buf->buf_start);
+
+ if (buf->buf_size == size && buf->buf_start != 0)
+ return 0;
+
+ old_buf = buf->buf_start;
+ old_size = buf->buf_size;
+ old_pagenum = buf->buf_page_num;
+ buf->buf_start = 0;
+ buf->buf_size = size;
+ ret = size;
+
+ if (size == 0 || _stbuf_alloc(buf) == 0) {
+ /*
+ * size=0:We only free the old memory;
+ * alloc ok,changed to new buffer
+ */
+ if (old_buf != 0)
+ codec_mm_free_for_dma(MEM_NAME, old_buf);
+
+ pr_info("changed the (%d) buffer size from %d to %d\n",
+ buf->type, old_size, size);
+ return 0;
+ }
+ /* alloc failed */
+ buf->buf_start = old_buf;
+ buf->buf_size = old_size;
+ buf->buf_page_num = old_pagenum;
+ pr_info("changed the (%d) buffer size from %d to %d,failed\n",
+ buf->type, old_size, size);
+
+ return ret;
+}
+
+int stbuf_fetch_init(void)
+{
+ if (fetchbuf != NULL)
+ return 0;
+
+ fetchbuf = (void *)__get_free_pages(GFP_KERNEL,
+ get_order(FETCHBUF_SIZE));
+
+ if (!fetchbuf) {
+ pr_info("%s: Can not allocate fetch working buffer\n",
+ __func__);
+ return -ENOMEM;
+ }
+ return 0;
+}
+
+void stbuf_fetch_release(void)
+{
+ if (0 && fetchbuf) {
+ /* always don't free.for safe alloc/free*/
+ free_pages((unsigned long)fetchbuf, get_order(FETCHBUF_SIZE));
+ fetchbuf = 0;
+ }
+}
+
+static void _stbuf_timer_func(unsigned long arg)
+{
+ struct stream_buf_s *p = (struct stream_buf_s *)arg;
+
+ if (stbuf_space(p) < p->wcnt) {
+ p->timer.expires = jiffies + STBUF_WAIT_INTERVAL;
+
+ add_timer(&p->timer);
+ } else
+ wake_up_interruptible(&p->wq);
+
+}
+
+u32 stbuf_level(struct stream_buf_s *buf)
+{
+ if ((buf->type == BUF_TYPE_HEVC) || (buf->type == BUF_TYPE_VIDEO)) {
+ if (READ_MPEG_REG(PARSER_ES_CONTROL) & 1) {
+ int level = READ_MPEG_REG(PARSER_VIDEO_WP) -
+ READ_MPEG_REG(PARSER_VIDEO_RP);
+ if (level < 0)
+ level += READ_MPEG_REG(PARSER_VIDEO_END_PTR) -
+ READ_MPEG_REG(PARSER_VIDEO_START_PTR) + 8;
+ return (u32)level;
+ } else
+ return (buf->type == BUF_TYPE_HEVC) ?
+ READ_VREG(HEVC_STREAM_LEVEL) :
+ _READ_ST_REG(LEVEL);
+ }
+
+ return _READ_ST_REG(LEVEL);
+}
+
+u32 stbuf_rp(struct stream_buf_s *buf)
+{
+ if ((buf->type == BUF_TYPE_HEVC) || (buf->type == BUF_TYPE_VIDEO)) {
+ if (READ_MPEG_REG(PARSER_ES_CONTROL) & 1)
+ return READ_MPEG_REG(PARSER_VIDEO_RP);
+ else
+ return (buf->type == BUF_TYPE_HEVC) ?
+ READ_VREG(HEVC_STREAM_RD_PTR) :
+ _READ_ST_REG(RP);
+ }
+
+ return _READ_ST_REG(RP);
+}
+
+u32 stbuf_space(struct stream_buf_s *buf)
+{
+ /*
+ *reserved space for safe write,
+ * the parser fifo size is 1024byts, so reserve it
+ */
+ int size;
+
+ size = buf->canusebuf_size - stbuf_level(buf);
+
+ if (buf->canusebuf_size >= buf->buf_size / 2) {
+ /* old reversed value,tobe full, reversed only... */
+ size = size - 6 * 1024;
+ }
+
+ if ((buf->type == BUF_TYPE_VIDEO)
+ || (has_hevc_vdec() && buf->type == BUF_TYPE_HEVC))
+ size -= READ_MPEG_REG(PARSER_VIDEO_HOLE);
+
+ return size > 0 ? size : 0;
+}
+
+u32 stbuf_size(struct stream_buf_s *buf)
+{
+ return buf->buf_size;
+}
+
+u32 stbuf_canusesize(struct stream_buf_s *buf)
+{
+ return buf->canusebuf_size;
+}
+
+s32 stbuf_init(struct stream_buf_s *buf, struct vdec_s *vdec)
+{
+ s32 r;
+ u32 dummy;
+ u32 addr32;
+
+ if (!buf->buf_start) {
+ r = _stbuf_alloc(buf);
+ if (r < 0)
+ return r;
+ }
+ addr32 = buf->buf_start & 0xffffffff;
+ init_waitqueue_head(&buf->wq);
+
+ if ((buf->type == BUF_TYPE_VIDEO) || (buf->type == BUF_TYPE_HEVC)) {
+ if (vdec) {
+ if (vdec_stream_based(vdec))
+ vdec_input_set_buffer(&vdec->input, addr32,
+ buf->buf_size);
+ else
+ return vdec_input_set_buffer(&vdec->input,
+ addr32, buf->buf_size);
+ }
+ }
+
+ buf->write_thread = 0;
+ if (has_hevc_vdec() && buf->type == BUF_TYPE_HEVC) {
+ CLEAR_VREG_MASK(HEVC_STREAM_CONTROL, 1);
+ WRITE_VREG(HEVC_STREAM_START_ADDR, addr32);
+ WRITE_VREG(HEVC_STREAM_END_ADDR, addr32 + buf->buf_size);
+ WRITE_VREG(HEVC_STREAM_RD_PTR, addr32);
+ WRITE_VREG(HEVC_STREAM_WR_PTR, addr32);
+
+ return 0;
+ }
+
+ if (buf->type == BUF_TYPE_VIDEO) {
+ _WRITE_ST_REG(CONTROL, 0);
+ /* reset VLD before setting all pointers */
+ WRITE_VREG(VLD_MEM_VIFIFO_WRAP_COUNT, 0);
+ /*TODO: only > m6*/
+#if 1/* MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON6 */
+ WRITE_VREG(DOS_SW_RESET0, (1 << 4));
+ WRITE_VREG(DOS_SW_RESET0, 0);
+#else
+ WRITE_MPEG_REG(RESET0_REGISTER, RESET_VLD);
+#endif
+
+ dummy = READ_MPEG_REG(RESET0_REGISTER);
+ WRITE_VREG(POWER_CTL_VLD, 1 << 4);
+ } else if (buf->type == BUF_TYPE_AUDIO) {
+ _WRITE_ST_REG(CONTROL, 0);
+
+ WRITE_MPEG_REG(AIU_AIFIFO_GBIT, 0x80);
+ }
+
+ if (buf->type == BUF_TYPE_SUBTITLE) {
+ WRITE_MPEG_REG(PARSER_SUB_RP, addr32);
+ WRITE_MPEG_REG(PARSER_SUB_START_PTR, addr32);
+ WRITE_MPEG_REG(PARSER_SUB_END_PTR,
+ addr32 + buf->buf_size - 8);
+
+ return 0;
+ }
+
+ _WRITE_ST_REG(START_PTR, addr32);
+ _WRITE_ST_REG(CURR_PTR, addr32);
+ _WRITE_ST_REG(END_PTR, addr32 + buf->buf_size - 8);
+
+ _SET_ST_REG_MASK(CONTROL, MEM_BUFCTRL_INIT);
+ _CLR_ST_REG_MASK(CONTROL, MEM_BUFCTRL_INIT);
+
+ _WRITE_ST_REG(BUF_CTRL, MEM_BUFCTRL_MANUAL);
+ _WRITE_ST_REG(WP, addr32);
+
+ _SET_ST_REG_MASK(BUF_CTRL, MEM_BUFCTRL_INIT);
+ _CLR_ST_REG_MASK(BUF_CTRL, MEM_BUFCTRL_INIT);
+
+ _SET_ST_REG_MASK(CONTROL,
+ (0x11 << 16) | MEM_FILL_ON_LEVEL | MEM_CTRL_FILL_EN |
+ MEM_CTRL_EMPTY_EN);
+ return 0;
+}
+
+void stbuf_vdec2_init(struct stream_buf_s *buf)
+{
+
+ _WRITE_VDEC2_ST_REG(CONTROL, 0);
+
+ _WRITE_VDEC2_ST_REG(START_PTR, _READ_ST_REG(START_PTR));
+ _WRITE_VDEC2_ST_REG(END_PTR, _READ_ST_REG(END_PTR));
+ _WRITE_VDEC2_ST_REG(CURR_PTR, _READ_ST_REG(CURR_PTR));
+
+ _WRITE_VDEC2_ST_REG(CONTROL, MEM_FILL_ON_LEVEL | MEM_BUFCTRL_INIT);
+ _WRITE_VDEC2_ST_REG(CONTROL, MEM_FILL_ON_LEVEL);
+
+ _WRITE_VDEC2_ST_REG(BUF_CTRL, MEM_BUFCTRL_INIT);
+ _WRITE_VDEC2_ST_REG(BUF_CTRL, 0);
+
+ _WRITE_VDEC2_ST_REG(CONTROL,
+ (0x11 << 16) | MEM_FILL_ON_LEVEL | MEM_CTRL_FILL_EN
+ | MEM_CTRL_EMPTY_EN);
+}
+
+s32 stbuf_wait_space(struct stream_buf_s *stream_buf, size_t count)
+{
+ struct stream_buf_s *p = stream_buf;
+ long time_out = 20;
+
+ p->wcnt = count;
+
+ setup_timer(&p->timer, _stbuf_timer_func, (ulong) p);
+
+ mod_timer(&p->timer, jiffies + STBUF_WAIT_INTERVAL);
+
+ if (wait_event_interruptible_timeout
+ (p->wq, stbuf_space(p) >= count, time_out) == 0) {
+ del_timer_sync(&p->timer);
+
+ return -EAGAIN;
+ }
+
+ del_timer_sync(&p->timer);
+
+ return 0;
+}
+
+void stbuf_release(struct stream_buf_s *buf)
+{
+ buf->first_tstamp = INVALID_PTS;
+
+ stbuf_init(buf, NULL); /* reinit buffer */
+
+ if (buf->flag & BUF_FLAG_ALLOC && buf->buf_start) {
+ codec_mm_free_for_dma(MEM_NAME, buf->buf_start);
+ buf->flag &= ~BUF_FLAG_ALLOC;
+ buf->buf_start = 0;
+ }
+ buf->flag &= ~BUF_FLAG_IN_USE;
+}
+
+u32 stbuf_sub_rp_get(void)
+{
+ return READ_MPEG_REG(PARSER_SUB_RP);
+}
+
+void stbuf_sub_rp_set(unsigned int sub_rp)
+{
+ WRITE_MPEG_REG(PARSER_SUB_RP, sub_rp);
+}
+
+u32 stbuf_sub_wp_get(void)
+{
+ return READ_MPEG_REG(PARSER_SUB_WP);
+}
+
+u32 stbuf_sub_start_get(void)
+{
+ return READ_MPEG_REG(PARSER_SUB_START_PTR);
+}
diff --git a/drivers/stream_input/parser/streambuf.h b/drivers/stream_input/parser/streambuf.h
new file mode 100644
index 0000000..cdae0a8
--- a/dev/null
+++ b/drivers/stream_input/parser/streambuf.h
@@ -0,0 +1,136 @@
+/*
+ * drivers/amlogic/media/stream_input/parser/streambuf.h
+ *
+ * Copyright (C) 2016 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 STREAMBUF_H
+#define STREAMBUF_H
+#include <linux/amlogic/media/utils/amports_config.h>
+
+#define BUF_FLAG_ALLOC 0x01
+#define BUF_FLAG_IN_USE 0x02
+#define BUF_FLAG_PARSER 0x04
+#define BUF_FLAG_FIRST_TSTAMP 0x08
+#define BUF_FLAG_IOMEM 0x10
+
+#define BUF_TYPE_VIDEO 0
+#define BUF_TYPE_AUDIO 1
+#define BUF_TYPE_SUBTITLE 2
+#define BUF_TYPE_USERDATA 3
+#define BUF_TYPE_HEVC 4
+#define BUF_MAX_NUM 5
+
+#define INVALID_PTS 0xffffffff
+
+#define FETCHBUF_SIZE (64*1024)
+#define USER_DATA_SIZE (8*1024)
+
+struct vdec_s;
+
+struct stream_buf_s {
+ s32 flag;
+ u32 type;
+ unsigned long buf_start;
+ struct page *buf_pages;
+ int buf_page_num;
+ u32 buf_size;
+ u32 default_buf_size;
+ u32 canusebuf_size;
+ u32 first_tstamp;
+ const ulong reg_base;
+ wait_queue_head_t wq;
+ struct timer_list timer;
+ u32 wcnt;
+ u32 buf_wp;
+ u32 buf_rp;
+ u32 max_buffer_delay_ms;
+ u64 last_write_jiffies64;
+ void *write_thread;
+ int for_4k;
+} /*stream_buf_t */;
+
+struct stream_port_s {
+ /* driver info */
+ const char *name;
+ struct device *class_dev;
+ const struct file_operations *fops;
+
+ /* ports control */
+ s32 type;
+ s32 flag;
+ s32 pcr_inited;
+
+ /* decoder info */
+ s32 vformat;
+ s32 aformat;
+ s32 achanl;
+ s32 asamprate;
+ s32 adatawidth;
+
+ /* parser info */
+ u32 vid;
+ u32 aid;
+ u32 sid;
+ u32 pcrid;
+} /*stream_port_t */;
+enum drm_level_e {
+ DRM_LEVEL1 = 1,
+ DRM_LEVEL2 = 2,
+ DRM_LEVEL3 = 3,
+ DRM_NONE = 4,
+};
+
+struct drm_info {
+ enum drm_level_e drm_level;
+ u32 drm_flag;
+ u32 drm_hasesdata;
+ u32 drm_priv;
+ u32 drm_pktsize;
+ u32 drm_pktpts;
+ u32 drm_phy;
+ u32 drm_vir;
+ u32 drm_remap;
+ u32 data_offset;
+ u32 extpad[8];
+} /*drminfo_t */;
+
+#define TYPE_DRMINFO 0x80
+#define TYPE_PATTERN 0x40
+
+struct vdec_s;
+
+extern void *fetchbuf;
+
+extern u32 stbuf_level(struct stream_buf_s *buf);
+extern u32 stbuf_rp(struct stream_buf_s *buf);
+extern u32 stbuf_space(struct stream_buf_s *buf);
+extern u32 stbuf_size(struct stream_buf_s *buf);
+extern u32 stbuf_canusesize(struct stream_buf_s *buf);
+extern s32 stbuf_init(struct stream_buf_s *buf, struct vdec_s *vdec);
+extern s32 stbuf_wait_space(struct stream_buf_s *stream_buf, size_t count);
+extern void stbuf_release(struct stream_buf_s *buf);
+extern int stbuf_change_size(struct stream_buf_s *buf, int size);
+extern int stbuf_fetch_init(void);
+extern void stbuf_fetch_release(void);
+extern u32 stbuf_sub_rp_get(void);
+extern void stbuf_sub_rp_set(unsigned int sub_rp);
+extern u32 stbuf_sub_wp_get(void);
+extern u32 stbuf_sub_start_get(void);
+extern u32 stbuf_userdata_start_get(void);
+extern struct stream_buf_s *get_stream_buffer(int id);
+
+extern void stbuf_vdec2_init(struct stream_buf_s *buf);
+
+#endif /* STREAMBUF_H */
diff --git a/drivers/stream_input/parser/streambuf_reg.h b/drivers/stream_input/parser/streambuf_reg.h
new file mode 100644
index 0000000..f00c705
--- a/dev/null
+++ b/drivers/stream_input/parser/streambuf_reg.h
@@ -0,0 +1,111 @@
+/*
+ * drivers/amlogic/media/stream_input/parser/streambuf_reg.h
+ *
+ * Copyright (C) 2016 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 STREAMBUF_REG_H
+#define STREAMBUF_REG_H
+
+#define HEVC_STREAM_REG_BASE HEVC_STREAM_START_ADDR
+
+#define VLD_MEM_VIFIFO_REG_BASE VLD_MEM_VIFIFO_START_PTR
+#define AIU_MEM_AIFIFO_REG_BASE AIU_MEM_AIFIFO_START_PTR
+
+#define START_PTR 0
+#define CURR_PTR 1
+#define END_PTR 2
+#define BYTES_AVAIL 3
+#define CONTROL 4
+#define WP 5
+#define RP 6
+#define LEVEL 7
+#define BUF_CTRL 8
+
+/*
+*#if MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON6
+*#define _WRITE_ST_REG(r, val) \
+* __raw_writel(val, (volatile void __iomem *)(buf->reg_base+(r<<2)))
+*#define _WRITE_ST_REG_BITS(r, val, s, e) \
+* __raw_writel((((_READ_ST_REG(r) & \
+* (((1L<<(e)-1)<<(s))-1)<<(s)))|((unsigned)((val)&((1L<<(e))-1))<<(s))), \
+* (volatile void __iomem *)(buf->reg_base+(r<<2)))
+*#define _SET_ST_REG_MASK(r, val) \
+* __raw_writel(_READ_ST_REG(r)| (val), \
+* (volatile void __iomem *)(buf->reg_base+(r<<2)))
+*#define _CLR_ST_REG_MASK(r, val) \
+* __raw_writel(_READ_ST_REG(r)&~(val), \
+* (volatile void __iomem *)(buf->reg_base+(r<<2)))
+*#define _READ_ST_REG(r) \
+* (__raw_readl((volatile void __iomem *)(buf->reg_base+(r<<2))))
+*
+*#if MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON6TVD
+*#define _READ_VDEC2_ST_REG(r) \
+* (__raw_readl((volatile void __iomem *)(buf->reg_base + \
+* DOS_REG_ADDR(VDEC2_VLD_MEM_VIFIFO_START_PTR) - \
+* DOS_REG_ADDR(VLD_MEM_VIFIFO_START_PTR) + (r<<2))))
+*#define _WRITE_VDEC2_ST_REG(r, val) \
+* __raw_writel(val, (volatile void __iomem *)(buf->reg_base + \
+* DOS_REG_ADDR(VDEC2_VLD_MEM_VIFIFO_START_PTR) - \
+* DOS_REG_ADDR(VLD_MEM_VIFIFO_START_PTR) + (r<<2)))
+*#endif
+*
+*#define MEM_BUFCTRL_MANUAL (1<<1)
+*#define MEM_BUFCTRL_INIT (1<<0)
+*#define MEM_LEVEL_CNT_BIT 18
+*#define MEM_FIFO_CNT_BIT 16
+*#define MEM_FILL_ON_LEVEL (1<<10)
+*#define MEM_CTRL_EMPTY_EN (1<<2)
+*#define MEM_CTRL_FILL_EN (1<<1)
+*#define MEM_CTRL_INIT (1<<0)
+*
+*#else
+*#define _WRITE_ST_REG(r, val) WRITE_MPEG_REG(buf->reg_base + (r), \
+* (val))
+*#define _WRITE_ST_REG_BITS(r, val, s, e) WRITE_MPEG_REG(buf->reg_base + (r), \
+* (val), (s), (e))
+*#define _SET_ST_REG_MASK(r, val) SET_MPEG_REG_MASK(buf->reg_base + \
+* (r), (val))
+*#define _CLR_ST_REG_MASK(r, val) CLEAR_MPEG_REG_MASK(buf->reg_base + \
+* (r), (val))
+*#define _READ_ST_REG(r) READ_MPEG_REG(buf->reg_base + (r))
+*#endif
+*/
+
+ /*TODO*/
+#define _WRITE_ST_REG(r, val) do { \
+ if (buf->reg_base == VLD_MEM_VIFIFO_REG_BASE) \
+ codec_dosbus_write((buf->reg_base+(r)), (val)); \
+ else \
+ codec_cbus_write((buf->reg_base+(r)), (val)); \
+ } while (0)
+#define _READ_ST_REG(r) \
+ ((buf->reg_base == VLD_MEM_VIFIFO_REG_BASE) ? \
+ codec_dosbus_read(buf->reg_base+(r)) : \
+ codec_cbus_read(buf->reg_base+(r)))
+#define _SET_ST_REG_MASK(r, val) _WRITE_ST_REG(r, _READ_ST_REG(r) | (val))
+#define _CLR_ST_REG_MASK(r, val) _WRITE_ST_REG(r, _READ_ST_REG(r)&~(val))
+#define _READ_VDEC2_ST_REG(r) (codec_dosbus_read(\
+ (VDEC2_VLD_MEM_VIFIFO_START_PTR+(r))))
+#define _WRITE_VDEC2_ST_REG(r, val) codec_dosbus_write(\
+ (VDEC2_VLD_MEM_VIFIFO_START_PTR+r), val)
+#define MEM_BUFCTRL_MANUAL (1<<1)
+#define MEM_BUFCTRL_INIT (1<<0)
+#define MEM_LEVEL_CNT_BIT 18
+#define MEM_FIFO_CNT_BIT 16
+#define MEM_FILL_ON_LEVEL (1<<10)
+#define MEM_CTRL_EMPTY_EN (1<<2)
+#define MEM_CTRL_FILL_EN (1<<1)
+#define MEM_CTRL_INIT (1<<0)
+#endif /* STREAMBUF_REG_H */
diff --git a/drivers/stream_input/parser/thread_rw.c b/drivers/stream_input/parser/thread_rw.c
new file mode 100644
index 0000000..e725407
--- a/dev/null
+++ b/drivers/stream_input/parser/thread_rw.c
@@ -0,0 +1,505 @@
+/*
+ * drivers/amlogic/media/stream_input/parser/thread_rw.c
+ *
+ * Copyright (C) 2016 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.
+ *
+*/
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/timer.h>
+#include <linux/kfifo.h>
+#include <linux/workqueue.h>
+#include <linux/dma-mapping.h>
+#include <linux/dma-contiguous.h>
+#include <linux/uaccess.h>
+#include <linux/fs.h>
+#include <linux/vmalloc.h>
+#include <linux/amlogic/media/codec_mm/codec_mm.h>
+
+/* #include <mach/am_regs.h> */
+#include <linux/delay.h>
+
+#include "../../stream_input/parser/streambuf.h"
+#include "../../stream_input/amports/amports_priv.h"
+#include "thread_rw.h"
+
+#define BUF_NAME "fetchbuf"
+
+#define DEFAULT_BLOCK_SIZE (64*1024)
+
+struct threadrw_buf {
+ void *vbuffer;
+ dma_addr_t dma_handle;
+ int write_off;
+ int data_size;
+ int buffer_size;
+};
+
+struct threadrw_write_task {
+ struct file *file;
+ struct delayed_work write_work;
+ DECLARE_KFIFO_PTR(datafifo, void *);
+ DECLARE_KFIFO_PTR(freefifo, void *);
+ int max_buf;
+ int errors;
+ spinlock_t lock;
+ struct stream_buf_s *sbuf;
+ int buffered_data_size;
+ int passed_data_len;
+ int buffer_size;
+ int data_offset;
+ int writework_on;
+ unsigned long codec_mm_buffer;
+ int manual_write;
+ wait_queue_head_t wq;
+ ssize_t (*write)(struct file *,
+ struct stream_buf_s *,
+ const char __user *,
+ size_t, int);
+ struct threadrw_buf buf[1];
+ /*don't add any after buf[] define */
+};
+
+static int free_task_buffers(struct threadrw_write_task *task);
+
+static struct workqueue_struct *threadrw_wq_get(void)
+{
+ static struct workqueue_struct *threadrw_wq;
+
+ if (!threadrw_wq)
+ threadrw_wq = create_singlethread_workqueue("threadrw");
+ return threadrw_wq;
+}
+
+static int threadrw_schedule_delayed_work(
+ struct threadrw_write_task *task,
+ unsigned long delay)
+{
+ bool ret;
+
+ if (threadrw_wq_get()) {
+ ret = queue_delayed_work(threadrw_wq_get(),
+ &task->write_work, delay);
+ } else
+ ret = schedule_delayed_work(&task->write_work, delay);
+ if (!ret) {
+ cancel_delayed_work(&task->write_work);
+ if (threadrw_wq_get())
+ ret = queue_delayed_work(threadrw_wq_get(),
+ &task->write_work, 0);
+ else
+ ret = schedule_delayed_work(&task->write_work, 0);
+ }
+ return 0;
+}
+
+static ssize_t threadrw_write_onece(
+ struct threadrw_write_task *task,
+ struct file *file,
+ struct stream_buf_s *stbuf,
+ const char __user *buf, size_t count)
+{
+ struct threadrw_buf *rwbuf = NULL;
+ int ret = 0;
+ int to_write;
+
+ if (!kfifo_get(&task->freefifo, (void *)&rwbuf)) {
+ if (task->errors)
+ return task->errors;
+ return -EAGAIN;
+ }
+
+ to_write = min_t(u32, rwbuf->buffer_size, count);
+ if (copy_from_user(rwbuf->vbuffer, buf, to_write)) {
+ kfifo_put(&task->freefifo, (const void *)buf);
+ ret = -EFAULT;
+ goto err;
+ }
+ rwbuf->data_size = to_write;
+ rwbuf->write_off = 0;
+ kfifo_put(&task->datafifo, (const void *)rwbuf);
+ threadrw_schedule_delayed_work(task, 0);
+ return to_write;
+err:
+ return ret;
+}
+
+static ssize_t threadrw_write_in(
+ struct threadrw_write_task *task,
+ struct stream_buf_s *stbuf,
+ const char __user *buf, size_t count)
+{
+ int ret = 0;
+ int off = 0;
+ int left = count;
+ int wait_num = 0;
+ unsigned long flags;
+
+ while (left > 0) {
+ ret = threadrw_write_onece(task,
+ task->file,
+ stbuf, buf + off, left);
+ if (ret >= left) {
+ off = count;
+ left = 0;
+ } else if (ret > 0) {
+ off += ret;
+ left -= ret;
+
+ } else if (ret < 0) {
+ if (off > 0) {
+ break; /*have write ok some data. */
+ } else if (ret == -EAGAIN) {
+ if (!(task->file->f_flags & O_NONBLOCK) &&
+ (++wait_num < 10)) {
+ wait_event_interruptible_timeout(
+ task->wq,
+ !kfifo_is_empty(
+ &task->freefifo),
+ HZ / 100);
+ continue; /* write again. */
+ }
+ ret = -EAGAIN;
+ break;
+ }
+ break; /*to end */
+ }
+ }
+
+ /*end: */
+ spin_lock_irqsave(&task->lock, flags);
+ if (off > 0) {
+ task->buffered_data_size += off;
+ task->data_offset += off;
+ }
+ spin_unlock_irqrestore(&task->lock, flags);
+ if (off > 0)
+ return off;
+ else
+ return ret;
+}
+
+static int do_write_work_in(struct threadrw_write_task *task)
+{
+ struct threadrw_buf *rwbuf = NULL;
+ int ret;
+ int need_re_write = 0;
+ int write_len = 0;
+ unsigned long flags;
+
+ if (kfifo_is_empty(&task->datafifo))
+ return 0;
+ if (!kfifo_peek(&task->datafifo, (void *)&rwbuf))
+ return 0;
+ if (task->codec_mm_buffer && !rwbuf->write_off)
+ codec_mm_dma_flush(rwbuf->vbuffer,
+ rwbuf->data_size,
+ DMA_TO_DEVICE);
+ if (task->manual_write) {
+ ret = task->write(task->file, task->sbuf,
+ (const char __user *)rwbuf->vbuffer + rwbuf->write_off,
+ rwbuf->data_size,
+ 2); /* noblock,virtual addr */
+ } else {
+ ret = task->write(task->file, task->sbuf,
+ (const char __user *)rwbuf->dma_handle + rwbuf->write_off,
+ rwbuf->data_size,
+ 3); /* noblock,phy addr */
+ }
+ if (ret == -EAGAIN) {
+ need_re_write = 0;
+ /*do later retry. */
+ } else if (ret >= rwbuf->data_size) {
+ write_len += rwbuf->data_size;
+ if (kfifo_get(&task->datafifo, (void *)&rwbuf)) {
+ rwbuf->data_size = 0;
+ kfifo_put(&task->freefifo, (const void *)rwbuf);
+ /*wakeup write thread. */
+ wake_up_interruptible(&task->wq);
+ } else
+ pr_err("write ok,but kfifo_get data failed.!!!\n");
+ need_re_write = 1;
+ } else if (ret > 0) {
+ rwbuf->data_size -= ret; /* half data write */
+ rwbuf->write_off += ret;
+ write_len += ret;
+ need_re_write = 1;
+ } else { /*ret <=0 */
+ pr_err("get errors ret=%d size=%d\n", ret,
+ rwbuf->data_size);
+ task->errors = ret;
+ }
+ if (write_len > 0) {
+ spin_lock_irqsave(&task->lock, flags);
+ task->passed_data_len += write_len;
+ task->buffered_data_size -= write_len;
+ spin_unlock_irqrestore(&task->lock, flags);
+ }
+ return need_re_write;
+
+}
+
+static void do_write_work(struct work_struct *work)
+{
+ struct threadrw_write_task *task = container_of(work,
+ struct threadrw_write_task,
+ write_work.work);
+
+ task->writework_on = 1;
+ while (do_write_work_in(task))
+ ;
+ threadrw_schedule_delayed_work(task, HZ / 10);
+ task->writework_on = 0;
+}
+
+static int init_task_buffers(struct threadrw_write_task *task, int num,
+ int block_size)
+{
+ struct threadrw_buf *rwbuf;
+ int i;
+ int used_codec_mm = 1;
+ int buffers_num = num;
+
+ if (used_codec_mm && (block_size * buffers_num) >= 128 * 1024) {
+ int total_mm = ALIGN(block_size * buffers_num, (1 << 17));
+
+ task->codec_mm_buffer = codec_mm_alloc_for_dma(BUF_NAME,
+ total_mm / PAGE_SIZE, 0,
+ CODEC_MM_FLAGS_DMA_CPU);
+ task->buffer_size = total_mm;
+ buffers_num = total_mm / block_size;
+ }
+ for (i = 0; i < buffers_num; i++) {
+ rwbuf = &task->buf[i];
+ rwbuf->buffer_size = block_size > 0 ?
+ block_size : DEFAULT_BLOCK_SIZE;
+ if (task->codec_mm_buffer) {
+ rwbuf->buffer_size = block_size;
+ if (i == buffers_num - 1)
+ rwbuf->buffer_size = task->buffer_size -
+ block_size * i;
+ rwbuf->dma_handle = (dma_addr_t) task->codec_mm_buffer +
+ block_size * i;
+ rwbuf->vbuffer = codec_mm_phys_to_virt(
+ rwbuf->dma_handle);
+
+ } else {
+ rwbuf->vbuffer = dma_alloc_coherent(
+ amports_get_dma_device(),
+ rwbuf->buffer_size,
+ &rwbuf->dma_handle, GFP_KERNEL);
+ if (!rwbuf->vbuffer) {
+ rwbuf->buffer_size = 0;
+ rwbuf->dma_handle = 0;
+ task->max_buf = i + 1;
+ break;
+ }
+ task->buffer_size += rwbuf->buffer_size;
+ }
+
+ kfifo_put(&task->freefifo, (const void *)rwbuf);
+ task->max_buf = i + 1;
+ }
+ if (task->max_buf >= 3 || task->max_buf == num)
+ return 0; /*must >=3 for swap buffers. */
+ if (task->max_buf > 0)
+ free_task_buffers(task);
+ return -1;
+}
+
+static int free_task_buffers(struct threadrw_write_task *task)
+{
+ int i;
+
+ if (task->codec_mm_buffer)
+ codec_mm_free_for_dma(BUF_NAME, task->codec_mm_buffer);
+ else {
+ for (i = 0; i < task->max_buf; i++) {
+ if (task->buf[i].vbuffer)
+ dma_free_coherent(amports_get_dma_device(),
+ task->buf[i].buffer_size,
+ task->buf[i].vbuffer,
+ task->buf[i].dma_handle);
+ }
+ }
+ return 0;
+}
+
+static struct threadrw_write_task *threadrw_buf_alloc_in(int num,
+ int block_size,
+ ssize_t (*write)(struct file *,
+ struct stream_buf_s *,
+ const char __user *, size_t, int),
+ int flags)
+{
+ int task_buffer_size = sizeof(struct threadrw_write_task) +
+ sizeof(struct threadrw_buf) * (num - 1) + 4;
+ struct threadrw_write_task *task = vmalloc(task_buffer_size);
+ int ret;
+
+ if (!task)
+ return NULL;
+ memset(task, 0, task_buffer_size);
+
+ spin_lock_init(&task->lock);
+ INIT_DELAYED_WORK(&task->write_work, do_write_work);
+ init_waitqueue_head(&task->wq);
+ ret = kfifo_alloc(&task->datafifo, num, GFP_KERNEL);
+ if (ret)
+ goto err1;
+ ret = kfifo_alloc(&task->freefifo, num, GFP_KERNEL);
+ if (ret)
+ goto err2;
+ task->write = write;
+ task->file = NULL;
+ task->buffer_size = 0;
+ task->manual_write = flags & 1;
+ ret = init_task_buffers(task, num, block_size);
+ if (ret < 0)
+ goto err3;
+ threadrw_wq_get(); /*start thread. */
+ return task;
+
+err3:
+ kfifo_free(&task->freefifo);
+err2:
+ kfifo_free(&task->datafifo);
+err1:
+ vfree(task);
+ pr_err("alloc threadrw failed num:%d,block:%d\n", num, block_size);
+ return NULL;
+}
+
+/*
+*fifo data size;
+*/
+
+int threadrw_buffer_level(struct stream_buf_s *stbuf)
+{
+ struct threadrw_write_task *task = stbuf->write_thread;
+
+ if (task)
+ return task->buffered_data_size;
+ return 0;
+}
+
+int threadrw_buffer_size(struct stream_buf_s *stbuf)
+{
+ struct threadrw_write_task *task = stbuf->write_thread;
+
+ if (task)
+ return task->buffer_size;
+ return 0;
+}
+
+int threadrw_datafifo_len(struct stream_buf_s *stbuf)
+{
+ struct threadrw_write_task *task = stbuf->write_thread;
+
+ if (task)
+ return kfifo_len(&task->datafifo);
+ return 0;
+}
+
+int threadrw_freefifo_len(struct stream_buf_s *stbuf)
+{
+ struct threadrw_write_task *task = stbuf->write_thread;
+
+ if (task)
+ return kfifo_len(&task->freefifo);
+ return 0;
+}
+
+/*
+*data len out fifo;
+*/
+int threadrw_passed_len(struct stream_buf_s *stbuf)
+{
+ struct threadrw_write_task *task = stbuf->write_thread;
+
+ if (task)
+ return task->passed_data_len;
+ return 0;
+
+}
+/*
+*all data writed.;
+*/
+int threadrw_dataoffset(struct stream_buf_s *stbuf)
+{
+ struct threadrw_write_task *task = stbuf->write_thread;
+ int offset = 0;
+
+ if (task)
+ return task->data_offset;
+ return offset;
+
+}
+
+ssize_t threadrw_write(struct file *file, struct stream_buf_s *stbuf,
+ const char __user *buf, size_t count)
+{
+ struct threadrw_write_task *task = stbuf->write_thread;
+
+ if (!task->file) {
+ task->file = file;
+ task->sbuf = stbuf;
+ }
+ return threadrw_write_in(task, stbuf, buf, count);
+}
+
+int threadrw_flush_buffers(struct stream_buf_s *stbuf)
+{
+ struct threadrw_write_task *task = stbuf->write_thread;
+ int max_retry = 20;
+
+ if (!task)
+ return 0;
+ while (!kfifo_is_empty(&task->datafifo) && max_retry-- > 0) {
+ threadrw_schedule_delayed_work(task, 0);
+ msleep(20);
+ }
+ if (!kfifo_is_empty(&task->datafifo))
+ return -1;/*data not flushed*/
+ return 0;
+}
+
+void *threadrw_alloc(int num,
+ int block_size,
+ ssize_t (*write)(struct file *,
+ struct stream_buf_s *,
+ const char __user *,
+ size_t, int),
+ int flags)
+{
+ return threadrw_buf_alloc_in(num, block_size, write, flags);
+}
+
+void threadrw_release(struct stream_buf_s *stbuf)
+{
+ struct threadrw_write_task *task = stbuf->write_thread;
+
+ if (task) {
+ wake_up_interruptible(&task->wq);
+ cancel_delayed_work_sync(&task->write_work);
+ free_task_buffers(task);
+ kfifo_free(&task->freefifo);
+ kfifo_free(&task->datafifo);
+ vfree(task);
+ }
+ stbuf->write_thread = NULL;
+}
diff --git a/drivers/stream_input/parser/thread_rw.h b/drivers/stream_input/parser/thread_rw.h
new file mode 100644
index 0000000..b16cf7b
--- a/dev/null
+++ b/drivers/stream_input/parser/thread_rw.h
@@ -0,0 +1,42 @@
+/*
+ * drivers/amlogic/media/stream_input/parser/thread_rw.h
+ *
+ * Copyright (C) 2016 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 THREAD_RW_H
+#define THREAD_RW_H
+#include "../../stream_input/parser/streambuf_reg.h"
+#include "../../stream_input/parser/streambuf.h"
+#include "../../stream_input/parser/esparser.h"
+#include "../../stream_input/amports/amports_priv.h"
+
+ssize_t threadrw_write(struct file *file,
+ struct stream_buf_s *stbuf, const char __user *buf, size_t count);
+
+void *threadrw_alloc(int num, int block_size, ssize_t(*write) (struct file *,
+ struct stream_buf_s *, const char __user *, size_t, int),
+ int flags); /*flags &1: manual mode */
+
+void threadrw_release(struct stream_buf_s *stbuf);
+
+int threadrw_buffer_level(struct stream_buf_s *stbuf);
+int threadrw_buffer_size(struct stream_buf_s *stbuf);
+int threadrw_datafifo_len(struct stream_buf_s *stbuf);
+int threadrw_freefifo_len(struct stream_buf_s *stbuf);
+int threadrw_passed_len(struct stream_buf_s *stbuf);
+int threadrw_flush_buffers(struct stream_buf_s *stbuf);
+int threadrw_dataoffset(struct stream_buf_s *stbuf);
+
+#endif
diff --git a/drivers/stream_input/parser/tsdemux.c b/drivers/stream_input/parser/tsdemux.c
new file mode 100644
index 0000000..a747b25
--- a/dev/null
+++ b/drivers/stream_input/parser/tsdemux.c
@@ -0,0 +1,1129 @@
+/*
+ * drivers/amlogic/media/stream_input/parser/tsdemux.c
+ *
+ * Copyright (C) 2016 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.
+ *
+*/
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/wait.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/dma-mapping.h>
+#include <linux/amlogic/media/frame_sync/ptsserv.h>
+#include <linux/amlogic/media/utils/amstream.h>
+#include <linux/amlogic/media/vfm/vframe_provider.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+
+#include <linux/uaccess.h>
+/* #include <mach/am_regs.h> */
+#include <linux/clk.h>
+/* #if MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON6 */
+/* #include <mach/mod_gate.h> */
+/* #endif */
+
+#include "../../frame_provider/decoder/utils/vdec.h"
+#include <linux/amlogic/media/utils/vdec_reg.h>
+#include "streambuf_reg.h"
+#include "streambuf.h"
+#include <linux/amlogic/media/utils/amports_config.h>
+
+#include "tsdemux.h"
+#include <linux/reset.h>
+#include "../amports/amports_priv.h"
+
+
+static const char tsdemux_fetch_id[] = "tsdemux-fetch-id";
+static const char tsdemux_irq_id[] = "tsdemux-irq-id";
+
+static u32 curr_pcr_num = 0xffff;
+static u32 curr_vid_id = 0xffff;
+static u32 curr_aud_id = 0xffff;
+static u32 curr_sub_id = 0xffff;
+static u32 curr_pcr_id = 0xffff;
+
+static DECLARE_WAIT_QUEUE_HEAD(wq);
+static u32 fetch_done;
+static u32 discontinued_counter;
+static u32 first_pcr;
+static u8 pcrscr_valid;
+
+static int demux_skipbyte;
+
+static struct tsdemux_ops *demux_ops;
+static DEFINE_SPINLOCK(demux_ops_lock);
+
+static int enable_demux_driver(void)
+{
+#ifdef ENABLE_DEMUX_DRIVER
+ return demux_ops ? 1 : 0;
+#else
+ return 0;
+#endif
+}
+
+void tsdemux_set_ops(struct tsdemux_ops *ops)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&demux_ops_lock, flags);
+ demux_ops = ops;
+ spin_unlock_irqrestore(&demux_ops_lock, flags);
+}
+EXPORT_SYMBOL(tsdemux_set_ops);
+
+int tsdemux_set_reset_flag_ext(void)
+{
+ int r = 0;
+
+ if (demux_ops && demux_ops->set_reset_flag)
+ r = demux_ops->set_reset_flag();
+
+ return r;
+}
+
+int tsdemux_set_reset_flag(void)
+{
+ unsigned long flags;
+ int r;
+
+ spin_lock_irqsave(&demux_ops_lock, flags);
+ r = tsdemux_set_reset_flag_ext();
+ spin_unlock_irqrestore(&demux_ops_lock, flags);
+
+ return r;
+}
+
+static int tsdemux_reset(void)
+{
+ unsigned long flags;
+ int r;
+
+ spin_lock_irqsave(&demux_ops_lock, flags);
+ if (demux_ops && demux_ops->reset) {
+ tsdemux_set_reset_flag_ext();
+ r = demux_ops->reset();
+ }
+ spin_unlock_irqrestore(&demux_ops_lock, flags);
+
+ return r;
+}
+
+static int tsdemux_request_irq(irq_handler_t handler, void *data)
+{
+ unsigned long flags;
+ int r;
+
+ spin_lock_irqsave(&demux_ops_lock, flags);
+ if (demux_ops && demux_ops->request_irq)
+ r = demux_ops->request_irq(handler, data);
+ spin_unlock_irqrestore(&demux_ops_lock, flags);
+
+ return r;
+}
+
+static int tsdemux_free_irq(void)
+{
+ unsigned long flags;
+ int r;
+
+ spin_lock_irqsave(&demux_ops_lock, flags);
+ if (demux_ops && demux_ops->free_irq)
+ r = demux_ops->free_irq();
+ spin_unlock_irqrestore(&demux_ops_lock, flags);
+
+ return r;
+}
+
+static int tsdemux_set_vid(int vpid)
+{
+ unsigned long flags;
+ int r = 0;
+
+ spin_lock_irqsave(&demux_ops_lock, flags);
+ if (demux_ops && demux_ops->set_vid)
+ r = demux_ops->set_vid(vpid);
+ spin_unlock_irqrestore(&demux_ops_lock, flags);
+
+ return r;
+}
+
+static int tsdemux_set_aid(int apid)
+{
+ unsigned long flags;
+ int r = 0;
+
+ spin_lock_irqsave(&demux_ops_lock, flags);
+ if (demux_ops && demux_ops->set_aid)
+ r = demux_ops->set_aid(apid);
+ spin_unlock_irqrestore(&demux_ops_lock, flags);
+
+ return r;
+}
+
+static int tsdemux_set_sid(int spid)
+{
+ unsigned long flags;
+ int r = 0;
+
+ spin_lock_irqsave(&demux_ops_lock, flags);
+ if (demux_ops && demux_ops->set_sid)
+ r = demux_ops->set_sid(spid);
+ spin_unlock_irqrestore(&demux_ops_lock, flags);
+
+ return r;
+}
+
+static int tsdemux_set_pcrid(int pcrpid)
+{
+ unsigned long flags;
+ int r = 0;
+
+ spin_lock_irqsave(&demux_ops_lock, flags);
+ if (demux_ops && demux_ops->set_pcrid)
+ r = demux_ops->set_pcrid(pcrpid);
+ spin_unlock_irqrestore(&demux_ops_lock, flags);
+
+ return r;
+}
+
+static int tsdemux_set_skip_byte(int skipbyte)
+{
+ unsigned long flags;
+ int r = 0;
+
+ spin_lock_irqsave(&demux_ops_lock, flags);
+ if (demux_ops && demux_ops->set_skipbyte)
+ r = demux_ops->set_skipbyte(skipbyte);
+ spin_unlock_irqrestore(&demux_ops_lock, flags);
+
+ return r;
+}
+
+static int tsdemux_config(void)
+{
+ return 0;
+}
+
+/*TODO irq*/
+static irqreturn_t tsdemux_isr(int irq, void *dev_id)
+{
+ u32 int_status = 0;
+ int id = (long)dev_id;
+
+ if (!enable_demux_driver()) {
+ int_status = READ_MPEG_REG(STB_INT_STATUS);
+ } else {
+ if (id == 0)
+ int_status = READ_MPEG_REG(STB_INT_STATUS);
+ else if (id == 1)
+ int_status = READ_MPEG_REG(STB_INT_STATUS_2);
+ else if (id == 2)
+ int_status = READ_MPEG_REG(STB_INT_STATUS_3);
+ }
+
+ if (int_status & (1 << NEW_PDTS_READY)) {
+ if (!enable_demux_driver()) {
+ u32 pdts_status = READ_MPEG_REG(STB_PTS_DTS_STATUS);
+
+ if (pdts_status & (1 << VIDEO_PTS_READY))
+ pts_checkin_wrptr(PTS_TYPE_VIDEO,
+ READ_MPEG_REG(VIDEO_PDTS_WR_PTR),
+ READ_MPEG_REG(VIDEO_PTS_DEMUX));
+
+ if (pdts_status & (1 << AUDIO_PTS_READY))
+ pts_checkin_wrptr(PTS_TYPE_AUDIO,
+ READ_MPEG_REG(AUDIO_PDTS_WR_PTR),
+ READ_MPEG_REG(AUDIO_PTS_DEMUX));
+
+ WRITE_MPEG_REG(STB_PTS_DTS_STATUS, pdts_status);
+ } else {
+#define DMX_READ_REG(i, r)\
+ ((i) ? ((i == 1) ? READ_MPEG_REG(r##_2) : \
+ READ_MPEG_REG(r##_3)) : READ_MPEG_REG(r))
+
+ u32 pdts_status = DMX_READ_REG(id, STB_PTS_DTS_STATUS);
+
+ if (pdts_status & (1 << VIDEO_PTS_READY))
+ pts_checkin_wrptr(PTS_TYPE_VIDEO,
+ DMX_READ_REG(id, VIDEO_PDTS_WR_PTR),
+ DMX_READ_REG(id, VIDEO_PTS_DEMUX));
+
+ if (pdts_status & (1 << AUDIO_PTS_READY))
+ pts_checkin_wrptr(PTS_TYPE_AUDIO,
+ DMX_READ_REG(id, AUDIO_PDTS_WR_PTR),
+ DMX_READ_REG(id, AUDIO_PTS_DEMUX));
+
+ if (id == 1)
+ WRITE_MPEG_REG(STB_PTS_DTS_STATUS_2,
+ pdts_status);
+ else if (id == 2)
+ WRITE_MPEG_REG(STB_PTS_DTS_STATUS_3,
+ pdts_status);
+ else
+ WRITE_MPEG_REG(STB_PTS_DTS_STATUS,
+ pdts_status);
+ }
+ }
+ if (int_status & (1 << DIS_CONTINUITY_PACKET)) {
+ discontinued_counter++;
+ /* pr_info("discontinued counter=%d\n",discontinued_counter); */
+ }
+ if (int_status & (1 << SUB_PES_READY)) {
+ /* TODO: put data to somewhere */
+ /* pr_info("subtitle pes ready\n"); */
+ wakeup_sub_poll();
+ }
+
+ if (!enable_demux_driver())
+ WRITE_MPEG_REG(STB_INT_STATUS, int_status);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t parser_isr(int irq, void *dev_id)
+{
+ u32 int_status = READ_MPEG_REG(PARSER_INT_STATUS);
+
+ WRITE_MPEG_REG(PARSER_INT_STATUS, int_status);
+
+ if (int_status & PARSER_INTSTAT_FETCH_CMD) {
+ fetch_done = 1;
+
+ wake_up_interruptible(&wq);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static ssize_t _tsdemux_write(const char __user *buf, size_t count,
+ int isphybuf)
+{
+ size_t r = count;
+ const char __user *p = buf;
+ u32 len;
+ int ret;
+ dma_addr_t dma_addr = 0;
+
+ if (r > 0) {
+ if (isphybuf)
+ len = count;
+ else {
+ len = min_t(size_t, r, FETCHBUF_SIZE);
+ if (copy_from_user(fetchbuf, p, len))
+ return -EFAULT;
+
+ dma_addr =
+ dma_map_single(amports_get_dma_device(),
+ fetchbuf,
+ FETCHBUF_SIZE, DMA_TO_DEVICE);
+ if (dma_mapping_error(amports_get_dma_device(),
+ dma_addr))
+ return -EFAULT;
+
+
+ }
+
+ fetch_done = 0;
+
+ wmb(); /* Ensure fetchbuf contents visible */
+
+ if (isphybuf) {
+ u32 buf_32 = (unsigned long)buf & 0xffffffff;
+
+ WRITE_MPEG_REG(PARSER_FETCH_ADDR, buf_32);
+ } else {
+ WRITE_MPEG_REG(PARSER_FETCH_ADDR, dma_addr);
+ dma_unmap_single(amports_get_dma_device(), dma_addr,
+ FETCHBUF_SIZE, DMA_TO_DEVICE);
+ }
+
+ WRITE_MPEG_REG(PARSER_FETCH_CMD, (7 << FETCH_ENDIAN) | len);
+
+
+ ret =
+ wait_event_interruptible_timeout(wq, fetch_done != 0,
+ HZ / 2);
+ if (ret == 0) {
+ WRITE_MPEG_REG(PARSER_FETCH_CMD, 0);
+ pr_info("write timeout, retry\n");
+ return -EAGAIN;
+ } else if (ret < 0)
+ return -ERESTARTSYS;
+
+ p += len;
+ r -= len;
+ }
+
+ return count - r;
+}
+
+static int reset_pcr_regs(void)
+{
+ u32 pcr_num;
+
+ if (curr_pcr_id >= 0x1FFF)
+ return 0;
+
+ /* set parameter to fetch pcr */
+ pcr_num = 0;
+ if (curr_pcr_id == curr_vid_id)
+ pcr_num = 0;
+ else if (curr_pcr_id == curr_aud_id)
+ pcr_num = 1;
+ else if (curr_pcr_id == curr_sub_id)
+ pcr_num = 2;
+ else
+ pcr_num = 3;
+
+ if (pcr_num != curr_pcr_num) {
+ u32 clk_unit = 0;
+ u32 clk_81 = 0;
+ struct clk *clk;
+
+ clk = clk_get_sys("clk81", "clk81");
+ if (IS_ERR(clk) || clk == 0) {
+ pr_info("[%s:%d] error clock\n", __func__,
+ __LINE__);
+ return 0;
+ }
+
+ clk_81 = clk_get_rate(clk);
+ clk_unit = clk_81 / 80000;
+
+ pr_info("[%s:%d] clk_81 = %x clk_unit =%x\n", __func__,
+ __LINE__, clk_81, clk_unit);
+
+ if (READ_MPEG_REG(TS_HIU_CTL_2) & 0x80) {
+ WRITE_MPEG_REG(PCR90K_CTL_2, (12 << 1) | clk_unit);
+ WRITE_MPEG_REG(ASSIGN_PID_NUMBER_2, pcr_num);
+ pr_info("[tsdemux_init] To use device 2,pcr_num=%d\n",
+ pcr_num);
+ } else if (READ_MPEG_REG(TS_HIU_CTL_3) & 0x80) {
+ WRITE_MPEG_REG(PCR90K_CTL_3, (12 << 1) | clk_unit);
+ WRITE_MPEG_REG(ASSIGN_PID_NUMBER_3, pcr_num);
+ pr_info("[tsdemux_init] To use device 3,pcr_num=%d\n",
+ pcr_num);
+ } else {
+ WRITE_MPEG_REG(PCR90K_CTL, (12 << 1) | clk_unit);
+ WRITE_MPEG_REG(ASSIGN_PID_NUMBER, pcr_num);
+ pr_info("[tsdemux_init] To use device 1,pcr_num=%d\n",
+ pcr_num);
+ }
+
+ curr_pcr_num = pcr_num;
+ }
+
+ return 1;
+}
+
+s32 tsdemux_init(u32 vid, u32 aid, u32 sid, u32 pcrid, bool is_hevc,
+ struct vdec_s *vdec)
+{
+ s32 r;
+ u32 parser_sub_start_ptr;
+ u32 parser_sub_end_ptr;
+ u32 parser_sub_rp;
+
+ /* #if MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON6 */
+ /*TODO clk */
+ /*
+ *switch_mod_gate_by_type(MOD_DEMUX, 1);
+ */
+ /* #endif */
+
+ amports_switch_gate("demux", 1);
+
+ parser_sub_start_ptr = READ_MPEG_REG(PARSER_SUB_START_PTR);
+ parser_sub_end_ptr = READ_MPEG_REG(PARSER_SUB_END_PTR);
+ parser_sub_rp = READ_MPEG_REG(PARSER_SUB_RP);
+
+ WRITE_MPEG_REG(RESET1_REGISTER, RESET_PARSER);
+
+ if (enable_demux_driver()) {
+ tsdemux_reset();
+ } else {
+ WRITE_MPEG_REG(RESET1_REGISTER, RESET_PARSER | RESET_DEMUXSTB);
+
+ WRITE_MPEG_REG(STB_TOP_CONFIG, 0);
+ WRITE_MPEG_REG(DEMUX_CONTROL, 0);
+ }
+
+ /* set PID filter */
+ pr_info
+ ("tsdemux video_pid = 0x%x, audio_pid = 0x%x,",
+ vid, aid);
+ pr_info
+ ("sub_pid = 0x%x, pcrid = 0x%x\n",
+ sid, pcrid);
+
+ if (!enable_demux_driver()) {
+ WRITE_MPEG_REG(FM_WR_DATA,
+ (((vid < 0x1fff)
+ ? (vid & 0x1fff) | (VIDEO_PACKET << 13)
+ : 0xffff) << 16)
+ | ((aid < 0x1fff)
+ ? (aid & 0x1fff) | (AUDIO_PACKET << 13)
+ : 0xffff));
+ WRITE_MPEG_REG(FM_WR_ADDR, 0x8000);
+ while (READ_MPEG_REG(FM_WR_ADDR) & 0x8000)
+ ;
+
+ WRITE_MPEG_REG(FM_WR_DATA,
+ (((sid < 0x1fff)
+ ? (sid & 0x1fff) | (SUB_PACKET << 13)
+ : 0xffff) << 16)
+ | 0xffff);
+ WRITE_MPEG_REG(FM_WR_ADDR, 0x8001);
+ while (READ_MPEG_REG(FM_WR_ADDR) & 0x8000)
+ ;
+
+ WRITE_MPEG_REG(MAX_FM_COMP_ADDR, 1);
+
+ WRITE_MPEG_REG(STB_INT_MASK, 0);
+ WRITE_MPEG_REG(STB_INT_STATUS, 0xffff);
+
+ /* TS data path */
+ WRITE_MPEG_REG(FEC_INPUT_CONTROL, 0x7000);
+ WRITE_MPEG_REG(DEMUX_MEM_REQ_EN,
+ (1 << VIDEO_PACKET) |
+ (1 << AUDIO_PACKET) | (1 << SUB_PACKET));
+ WRITE_MPEG_REG(DEMUX_ENDIAN,
+ (7 << OTHER_ENDIAN) |
+ (7 << BYPASS_ENDIAN) | (0 << SECTION_ENDIAN));
+ WRITE_MPEG_REG(TS_HIU_CTL, 1 << USE_HI_BSF_INTERFACE);
+ WRITE_MPEG_REG(TS_FILE_CONFIG,
+ (demux_skipbyte << 16) |
+ (6 << DES_OUT_DLY) |
+ (3 << TRANSPORT_SCRAMBLING_CONTROL_ODD) |
+ (1 << TS_HIU_ENABLE) | (4 << FEC_FILE_CLK_DIV));
+
+ /* enable TS demux */
+ WRITE_MPEG_REG(DEMUX_CONTROL,
+ (1 << STB_DEMUX_ENABLE) |
+ (1 << KEEP_DUPLICATE_PACKAGE));
+ }
+
+ if (fetchbuf == 0) {
+ pr_info("%s: no fetchbuf\n", __func__);
+ return -ENOMEM;
+ }
+
+ /* hook stream buffer with PARSER */
+ /* #if MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON8 */
+ if (has_hevc_vdec() && is_hevc) {
+ WRITE_MPEG_REG(PARSER_VIDEO_START_PTR, vdec->input.start);
+ WRITE_MPEG_REG(PARSER_VIDEO_END_PTR, vdec->input.start +
+ vdec->input.size - 8);
+
+ if (vdec_stream_based(vdec)) {
+ CLEAR_MPEG_REG_MASK(PARSER_ES_CONTROL,
+ ES_VID_MAN_RD_PTR);
+ /* set vififo_vbuf_rp_sel=>hevc */
+ WRITE_VREG(DOS_GEN_CTRL0, 3 << 1);
+ /* set use_parser_vbuf_wp */
+ SET_VREG_MASK(HEVC_STREAM_CONTROL,
+ (1 << 3) | (0 << 4));
+ /* set stream_fetch_enable */
+ SET_VREG_MASK(HEVC_STREAM_CONTROL, 1);
+ /* set stream_buffer_hole with 256 bytes */
+ SET_VREG_MASK(HEVC_STREAM_FIFO_CTL,
+ (1 << 29));
+ } else {
+ SET_MPEG_REG_MASK(PARSER_ES_CONTROL, ES_VID_MAN_RD_PTR);
+ WRITE_MPEG_REG(PARSER_VIDEO_WP, vdec->input.start);
+ WRITE_MPEG_REG(PARSER_VIDEO_RP, vdec->input.start);
+ }
+ } else
+ /* #endif */
+ {
+ WRITE_MPEG_REG(PARSER_VIDEO_START_PTR, vdec->input.start);
+ WRITE_MPEG_REG(PARSER_VIDEO_END_PTR, vdec->input.start +
+ vdec->input.size - 8);
+
+ if (vdec_stream_based(vdec)) {
+ CLEAR_MPEG_REG_MASK(PARSER_ES_CONTROL,
+ ES_VID_MAN_RD_PTR);
+
+ WRITE_VREG(VLD_MEM_VIFIFO_BUF_CNTL, MEM_BUFCTRL_INIT);
+ CLEAR_VREG_MASK(VLD_MEM_VIFIFO_BUF_CNTL,
+ MEM_BUFCTRL_INIT);
+ /* set vififo_vbuf_rp_sel=>vdec */
+ if (has_hevc_vdec())
+ WRITE_VREG(DOS_GEN_CTRL0, 0);
+ } else {
+ SET_MPEG_REG_MASK(PARSER_ES_CONTROL, ES_VID_MAN_RD_PTR);
+ WRITE_MPEG_REG(PARSER_VIDEO_WP, vdec->input.start);
+ WRITE_MPEG_REG(PARSER_VIDEO_RP, vdec->input.start);
+ }
+ }
+
+ WRITE_MPEG_REG(PARSER_AUDIO_START_PTR,
+ READ_MPEG_REG(AIU_MEM_AIFIFO_START_PTR));
+ WRITE_MPEG_REG(PARSER_AUDIO_END_PTR,
+ READ_MPEG_REG(AIU_MEM_AIFIFO_END_PTR));
+ CLEAR_MPEG_REG_MASK(PARSER_ES_CONTROL, ES_AUD_MAN_RD_PTR);
+
+ WRITE_MPEG_REG(PARSER_CONFIG,
+ (10 << PS_CFG_PFIFO_EMPTY_CNT_BIT) |
+ (1 << PS_CFG_MAX_ES_WR_CYCLE_BIT) |
+ (16 << PS_CFG_MAX_FETCH_CYCLE_BIT));
+
+ WRITE_MPEG_REG(AIU_MEM_AIFIFO_BUF_CNTL, MEM_BUFCTRL_INIT);
+ CLEAR_MPEG_REG_MASK(AIU_MEM_AIFIFO_BUF_CNTL, MEM_BUFCTRL_INIT);
+
+ WRITE_MPEG_REG(PARSER_SUB_START_PTR, parser_sub_start_ptr);
+ WRITE_MPEG_REG(PARSER_SUB_END_PTR, parser_sub_end_ptr);
+ WRITE_MPEG_REG(PARSER_SUB_RP, parser_sub_rp);
+ SET_MPEG_REG_MASK(PARSER_ES_CONTROL,
+ (7 << ES_SUB_WR_ENDIAN_BIT) | ES_SUB_MAN_RD_PTR);
+
+ /* #if MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON8 */
+ if (has_hevc_vdec())
+ r = pts_start((is_hevc) ? PTS_TYPE_HEVC : PTS_TYPE_VIDEO);
+ else
+ /* #endif */
+ r = pts_start(PTS_TYPE_VIDEO);
+
+ if (r < 0) {
+ pr_info("Video pts start failed.(%d)\n", r);
+ goto err1;
+ }
+ r = pts_start(PTS_TYPE_AUDIO);
+ if (r < 0) {
+ pr_info("Audio pts start failed.(%d)\n", r);
+ goto err2;
+ }
+ /*TODO irq */
+
+ r = vdec_request_irq(PARSER_IRQ, parser_isr,
+ "tsdemux-fetch", (void *)tsdemux_fetch_id);
+
+ if (r)
+ goto err3;
+
+ WRITE_MPEG_REG(PARSER_INT_STATUS, 0xffff);
+ WRITE_MPEG_REG(PARSER_INT_ENABLE,
+ PARSER_INTSTAT_FETCH_CMD << PARSER_INT_HOST_EN_BIT);
+
+ WRITE_MPEG_REG(PARSER_VIDEO_HOLE, 0x400);
+ WRITE_MPEG_REG(PARSER_AUDIO_HOLE, 0x400);
+
+ discontinued_counter = 0;
+
+ if (!enable_demux_driver()) {
+ /*TODO irq */
+
+ r = vdec_request_irq(DEMUX_IRQ, tsdemux_isr,
+ "tsdemux-irq", (void *)tsdemux_irq_id);
+
+ WRITE_MPEG_REG(STB_INT_MASK, (1 << SUB_PES_READY)
+ | (1 << NEW_PDTS_READY)
+ | (1 << DIS_CONTINUITY_PACKET));
+ if (r)
+ goto err4;
+ } else {
+ tsdemux_config();
+ tsdemux_request_irq(tsdemux_isr, (void *)tsdemux_irq_id);
+ if (vid < 0x1FFF) {
+ curr_vid_id = vid;
+ tsdemux_set_vid(vid);
+ }
+ if (aid < 0x1FFF) {
+ curr_aud_id = aid;
+ tsdemux_set_aid(aid);
+ }
+ if (sid < 0x1FFF) {
+ curr_sub_id = sid;
+ tsdemux_set_sid(sid);
+ }
+
+ curr_pcr_id = pcrid;
+ if ((pcrid < 0x1FFF) && (pcrid != vid) && (pcrid != aid)
+ && (pcrid != sid))
+ tsdemux_set_pcrid(pcrid);
+ }
+
+ pcrscr_valid = reset_pcr_regs();
+ first_pcr = 0;
+
+ return 0;
+
+err4:
+ /*TODO irq */
+
+ if (!enable_demux_driver())
+ vdec_free_irq(PARSER_IRQ, (void *)tsdemux_fetch_id);
+
+err3:
+ pts_stop(PTS_TYPE_AUDIO);
+err2:
+ /* #if MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON8 */
+ if (has_hevc_vdec())
+ pts_stop((is_hevc) ? PTS_TYPE_HEVC : PTS_TYPE_VIDEO);
+ else
+ /* #endif */
+ pts_stop(PTS_TYPE_VIDEO);
+err1:
+ pr_info("TS Demux init failed.\n");
+ return -ENOENT;
+}
+
+void tsdemux_release(void)
+{
+ pcrscr_valid = 0;
+ first_pcr = 0;
+
+ WRITE_MPEG_REG(PARSER_INT_ENABLE, 0);
+ WRITE_MPEG_REG(PARSER_VIDEO_HOLE, 0);
+ WRITE_MPEG_REG(PARSER_AUDIO_HOLE, 0);
+ /*TODO irq */
+
+ vdec_free_irq(PARSER_IRQ, (void *)tsdemux_fetch_id);
+
+ if (!enable_demux_driver()) {
+ WRITE_MPEG_REG(STB_INT_MASK, 0);
+ /*TODO irq */
+
+ vdec_free_irq(DEMUX_IRQ, (void *)tsdemux_irq_id);
+ } else {
+
+ tsdemux_set_aid(0xffff);
+ tsdemux_set_vid(0xffff);
+ tsdemux_set_sid(0xffff);
+ tsdemux_set_pcrid(0xffff);
+ tsdemux_free_irq();
+
+ curr_vid_id = 0xffff;
+ curr_aud_id = 0xffff;
+ curr_sub_id = 0xffff;
+ curr_pcr_id = 0xffff;
+ curr_pcr_num = 0xffff;
+ }
+
+ pts_stop(PTS_TYPE_VIDEO);
+ pts_stop(PTS_TYPE_AUDIO);
+
+ WRITE_MPEG_REG(RESET1_REGISTER, RESET_PARSER);
+
+ /* #if MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON6 */
+ /*TODO clk */
+ /*
+ *switch_mod_gate_by_type(MOD_DEMUX, 0);
+ */
+ /* #endif */
+ amports_switch_gate("demux", 0);
+
+}
+
+static int limited_delay_check(struct file *file,
+ struct stream_buf_s *vbuf,
+ struct stream_buf_s *abuf,
+ const char __user *buf, size_t count)
+{
+ int write_size;
+
+ if (vbuf->max_buffer_delay_ms > 0 && abuf->max_buffer_delay_ms > 0 &&
+ stbuf_level(vbuf) > 1024 && stbuf_level(abuf) > 256) {
+ int vdelay =
+ calculation_stream_delayed_ms(PTS_TYPE_VIDEO,
+ NULL, NULL);
+ int adelay =
+ calculation_stream_delayed_ms(PTS_TYPE_AUDIO,
+ NULL, NULL);
+ /*max wait 100ms,if timeout,try again top level. */
+ int maxretry = 10;
+ /*too big delay,do wait now. */
+ /*if noblock mode,don't do wait. */
+ if (!(file->f_flags & O_NONBLOCK)) {
+ while (vdelay > vbuf->max_buffer_delay_ms
+ && adelay > abuf->max_buffer_delay_ms
+ && maxretry-- > 0) {
+ msleep(20);
+ vdelay =
+ calculation_stream_delayed_ms
+ (PTS_TYPE_VIDEO, NULL, NULL);
+ adelay =
+ calculation_stream_delayed_ms
+ (PTS_TYPE_AUDIO, NULL, NULL);
+ }
+ }
+ if (vdelay > vbuf->max_buffer_delay_ms
+ && adelay > abuf->max_buffer_delay_ms)
+ return 0;
+ }
+ write_size = min(stbuf_space(vbuf), stbuf_space(abuf));
+ write_size = min_t(int, count, write_size);
+ return write_size;
+}
+
+ssize_t drm_tswrite(struct file *file,
+ struct stream_buf_s *vbuf,
+ struct stream_buf_s *abuf,
+ const char __user *buf, size_t count)
+{
+ s32 r;
+ u32 realcount = count;
+ u32 re_count = count;
+ u32 havewritebytes = 0;
+
+ struct drm_info tmpmm;
+ struct drm_info *drm = &tmpmm;
+ u32 res = 0;
+ int isphybuf = 0;
+ unsigned long realbuf;
+
+ struct port_priv_s *priv = (struct port_priv_s *)file->private_data;
+ struct stream_port_s *port = priv->port;
+ size_t wait_size, write_size;
+
+ if (buf == NULL || count == 0)
+ return -EINVAL;
+
+ res = copy_from_user(drm, buf, sizeof(struct drm_info));
+ if (res) {
+ pr_info("drm kmalloc failed res[%d]\n", res);
+ return -EFAULT;
+ }
+
+ if (drm->drm_flag == TYPE_DRMINFO && drm->drm_level == DRM_LEVEL1) {
+ /* buf only has drminfo not have esdata; */
+ realcount = drm->drm_pktsize;
+ realbuf = drm->drm_phy;
+ isphybuf = 1;
+ } else
+ realbuf = (unsigned long)buf;
+ /* pr_info("drm->drm_flag = 0x%x,realcount = %d , buf = 0x%x ",*/
+ /*drm->drm_flag,realcount, buf); */
+
+ count = realcount;
+
+ while (count > 0) {
+ if ((stbuf_space(vbuf) < count) ||
+ (stbuf_space(abuf) < count)) {
+ if (file->f_flags & O_NONBLOCK) {
+ int v_stbuf_space = stbuf_space(vbuf);
+ int a_stbuf_space = stbuf_space(abuf);
+
+ write_size = min(v_stbuf_space, a_stbuf_space);
+ /*have 188 bytes,write now., */
+ if (write_size <= 188)
+ return -EAGAIN;
+ } else {
+ wait_size =
+ min(stbuf_canusesize(vbuf) / 8,
+ stbuf_canusesize(abuf) / 4);
+ if ((port->flag & PORT_FLAG_VID)
+ && (stbuf_space(vbuf) < wait_size)) {
+ r = stbuf_wait_space(vbuf, wait_size);
+
+ if (r < 0) {
+ pr_info
+ ("write no space--- ");
+ pr_info
+ ("no space,%d--%d,r-%d\n",
+ stbuf_space(vbuf),
+ stbuf_space(abuf), r);
+ return r;
+ }
+ }
+
+ if ((port->flag & PORT_FLAG_AID)
+ && (stbuf_space(abuf) < wait_size)) {
+ r = stbuf_wait_space(abuf, wait_size);
+
+ if (r < 0) {
+ pr_info
+ ("write no stbuf_wait_space--");
+ pr_info
+ ("no space,%d--%d,r-%d\n",
+ stbuf_space(vbuf),
+ stbuf_space(abuf), r);
+ return r;
+ }
+ }
+ }
+ }
+
+ write_size = min(stbuf_space(vbuf), stbuf_space(abuf));
+ write_size = min(count, write_size);
+ /* pr_info("write_size = %d,count = %d,\n",*/
+ /*write_size, count); */
+ if (write_size > 0)
+ r = _tsdemux_write((const char __user *)realbuf,
+ write_size, isphybuf);
+ else
+ return -EAGAIN;
+
+ havewritebytes += r;
+
+ /* pr_info("havewritebytes = %d, r = %d,\n",*/
+ /*havewritebytes, r); */
+ if (havewritebytes == realcount)
+ break; /* write ok; */
+ else if (havewritebytes > realcount)
+ pr_info(" error ! write too much\n");
+
+ count -= r;
+ }
+ return re_count;
+}
+
+ssize_t tsdemux_write(struct file *file,
+ struct stream_buf_s *vbuf,
+ struct stream_buf_s *abuf,
+ const char __user *buf, size_t count)
+{
+ s32 r;
+ struct port_priv_s *priv = (struct port_priv_s *)file->private_data;
+ struct stream_port_s *port = priv->port;
+ size_t wait_size, write_size;
+
+ if ((stbuf_space(vbuf) < count) || (stbuf_space(abuf) < count)) {
+ if (file->f_flags & O_NONBLOCK) {
+ write_size = min(stbuf_space(vbuf), stbuf_space(abuf));
+ if (write_size <= 188) /*have 188 bytes,write now., */
+ return -EAGAIN;
+ } else {
+ wait_size =
+ min(stbuf_canusesize(vbuf) / 8,
+ stbuf_canusesize(abuf) / 4);
+ if ((port->flag & PORT_FLAG_VID)
+ && (stbuf_space(vbuf) < wait_size)) {
+ r = stbuf_wait_space(vbuf, wait_size);
+
+ if (r < 0) {
+ /*
+ *pr_info("write no space--- ");
+ *pr_info("no space,%d--%d,r-%d\n",
+ *stbuf_space(vbuf),
+ *stbuf_space(abuf),r);
+ */
+ return r;
+ }
+ }
+
+ if ((port->flag & PORT_FLAG_AID)
+ && (stbuf_space(abuf) < wait_size)) {
+ r = stbuf_wait_space(abuf, wait_size);
+
+ if (r < 0) {
+ /*
+ *pr_info("write no stbuf_wait_space")'
+ *pr_info{"--- no space,%d--%d,r-%d\n",
+ *stbuf_space(vbuf),
+ *stbuf_space(abuf),r);
+ */
+ return r;
+ }
+ }
+ }
+ }
+ vbuf->last_write_jiffies64 = jiffies_64;
+ abuf->last_write_jiffies64 = jiffies_64;
+ write_size = limited_delay_check(file, vbuf, abuf, buf, count);
+ if (write_size > 0)
+ return _tsdemux_write(buf, write_size, 0);
+ else
+ return -EAGAIN;
+}
+
+static ssize_t show_discontinue_counter(struct class *class,
+ struct class_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%d\n", discontinued_counter);
+}
+
+static struct class_attribute tsdemux_class_attrs[] = {
+ __ATTR(discontinue_counter, S_IRUGO, show_discontinue_counter, NULL),
+ __ATTR_NULL
+};
+
+static struct class tsdemux_class = {
+ .name = "tsdemux",
+ .class_attrs = tsdemux_class_attrs,
+ };
+
+int tsdemux_class_register(void)
+{
+ int r = class_register(&tsdemux_class);
+
+ if (r < 0)
+ pr_info("register tsdemux class error!\n");
+ discontinued_counter = 0;
+ return r;
+}
+
+void tsdemux_class_unregister(void)
+{
+ class_unregister(&tsdemux_class);
+}
+
+void tsdemux_change_avid(unsigned int vid, unsigned int aid)
+{
+ if (!enable_demux_driver()) {
+ WRITE_MPEG_REG(FM_WR_DATA,
+ (((vid & 0x1fff) | (VIDEO_PACKET << 13)) << 16)
+ | ((aid & 0x1fff) | (AUDIO_PACKET << 13)));
+ WRITE_MPEG_REG(FM_WR_ADDR, 0x8000);
+ while (READ_MPEG_REG(FM_WR_ADDR) & 0x8000)
+ ;
+ } else {
+ curr_vid_id = vid;
+ curr_aud_id = aid;
+
+ tsdemux_set_vid(vid);
+ tsdemux_set_aid(aid);
+
+ reset_pcr_regs();
+ }
+
+}
+
+void tsdemux_change_sid(unsigned int sid)
+{
+ if (!enable_demux_driver()) {
+ WRITE_MPEG_REG(FM_WR_DATA,
+ (((sid & 0x1fff) | (SUB_PACKET << 13)) << 16)
+ | 0xffff);
+ WRITE_MPEG_REG(FM_WR_ADDR, 0x8001);
+ while (READ_MPEG_REG(FM_WR_ADDR) & 0x8000)
+ ;
+ } else {
+ curr_sub_id = sid;
+
+ tsdemux_set_sid(sid);
+
+ reset_pcr_regs();
+ }
+
+}
+
+void tsdemux_audio_reset(void)
+{
+ ulong flags;
+
+ DEFINE_SPINLOCK(lock);
+
+ spin_lock_irqsave(&lock, flags);
+
+ WRITE_MPEG_REG(PARSER_AUDIO_WP,
+ READ_MPEG_REG(AIU_MEM_AIFIFO_START_PTR));
+ WRITE_MPEG_REG(PARSER_AUDIO_RP,
+ READ_MPEG_REG(AIU_MEM_AIFIFO_START_PTR));
+
+ WRITE_MPEG_REG(PARSER_AUDIO_START_PTR,
+ READ_MPEG_REG(AIU_MEM_AIFIFO_START_PTR));
+ WRITE_MPEG_REG(PARSER_AUDIO_END_PTR,
+ READ_MPEG_REG(AIU_MEM_AIFIFO_END_PTR));
+ CLEAR_MPEG_REG_MASK(PARSER_ES_CONTROL, ES_AUD_MAN_RD_PTR);
+
+ WRITE_MPEG_REG(AIU_MEM_AIFIFO_BUF_CNTL, MEM_BUFCTRL_INIT);
+ CLEAR_MPEG_REG_MASK(AIU_MEM_AIFIFO_BUF_CNTL, MEM_BUFCTRL_INIT);
+
+ spin_unlock_irqrestore(&lock, flags);
+
+}
+
+void tsdemux_sub_reset(void)
+{
+ ulong flags;
+ DEFINE_SPINLOCK(lock);
+ u32 parser_sub_start_ptr;
+ u32 parser_sub_end_ptr;
+
+ spin_lock_irqsave(&lock, flags);
+
+ parser_sub_start_ptr = READ_MPEG_REG(PARSER_SUB_START_PTR);
+ parser_sub_end_ptr = READ_MPEG_REG(PARSER_SUB_END_PTR);
+
+ WRITE_MPEG_REG(PARSER_SUB_START_PTR, parser_sub_start_ptr);
+ WRITE_MPEG_REG(PARSER_SUB_END_PTR, parser_sub_end_ptr);
+ WRITE_MPEG_REG(PARSER_SUB_RP, parser_sub_start_ptr);
+ WRITE_MPEG_REG(PARSER_SUB_WP, parser_sub_start_ptr);
+ SET_MPEG_REG_MASK(PARSER_ES_CONTROL,
+ (7 << ES_SUB_WR_ENDIAN_BIT) | ES_SUB_MAN_RD_PTR);
+
+ spin_unlock_irqrestore(&lock, flags);
+
+}
+
+void tsdemux_set_skipbyte(int skipbyte)
+{
+ if (!enable_demux_driver())
+ demux_skipbyte = skipbyte;
+ else
+ tsdemux_set_skip_byte(skipbyte);
+
+}
+
+void tsdemux_set_demux(int dev)
+{
+ if (enable_demux_driver()) {
+ unsigned long flags;
+ int r = 0;
+
+ spin_lock_irqsave(&demux_ops_lock, flags);
+ if (demux_ops && demux_ops->set_demux)
+ r = demux_ops->set_demux(dev);
+ spin_unlock_irqrestore(&demux_ops_lock, flags);
+ }
+}
+
+u32 tsdemux_pcrscr_get(void)
+{
+ u32 pcr = 0;
+
+ if (pcrscr_valid == 0)
+ return 0;
+
+ if (READ_MPEG_REG(TS_HIU_CTL_2) & 0x80)
+ pcr = READ_MPEG_REG(PCR_DEMUX_2);
+ else if (READ_MPEG_REG(TS_HIU_CTL_3) & 0x80)
+ pcr = READ_MPEG_REG(PCR_DEMUX_3);
+ else
+ pcr = READ_MPEG_REG(PCR_DEMUX);
+ if (first_pcr == 0)
+ first_pcr = pcr;
+ return pcr;
+}
+
+u32 tsdemux_first_pcrscr_get(void)
+{
+ if (pcrscr_valid == 0)
+ return 0;
+
+ if (first_pcr == 0) {
+ u32 pcr;
+
+ if (READ_MPEG_REG(TS_HIU_CTL_2) & 0x80)
+ pcr = READ_MPEG_REG(PCR_DEMUX_2);
+ else if (READ_MPEG_REG(TS_HIU_CTL_3) & 0x80)
+ pcr = READ_MPEG_REG(PCR_DEMUX_3);
+ else
+ pcr = READ_MPEG_REG(PCR_DEMUX);
+ first_pcr = pcr;
+ /* pr_info("set first_pcr = 0x%x\n", pcr); */
+ }
+
+ return first_pcr;
+}
+
+u8 tsdemux_pcrscr_valid(void)
+{
+ return pcrscr_valid;
+}
diff --git a/drivers/stream_input/parser/tsdemux.h b/drivers/stream_input/parser/tsdemux.h
new file mode 100644
index 0000000..13ab4cd
--- a/dev/null
+++ b/drivers/stream_input/parser/tsdemux.h
@@ -0,0 +1,93 @@
+/*
+ * drivers/amlogic/media/stream_input/parser/tsdemux.h
+ *
+ * Copyright (C) 2016 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 TSDEMUX_H
+#define TSDEMUX_H
+#include <linux/amlogic/media/utils/amports_config.h>
+
+/* TODO: move to register headers */
+#define NEW_PDTS_READY 4
+#define AUDIO_PTS_READY 2
+#define VIDEO_PTS_READY 0
+#define DIS_CONTINUITY_PACKET 6
+#define SUB_PES_READY 7
+
+#define PARSER_INTSTAT_FETCH_CMD (1<<7)
+
+#define FETCH_ENDIAN 27
+#define FETCH_ENDIAN_MASK (0x7<<27)
+
+#define RESET_DEMUXSTB (1<<1)
+#define RESET_PARSER (1<<8)
+
+#define VIDEO_PACKET 0
+#define AUDIO_PACKET 1
+#define SUB_PACKET 2
+
+#define OTHER_ENDIAN 6
+#define BYPASS_ENDIAN 3
+#define SECTION_ENDIAN 0
+
+#define USE_HI_BSF_INTERFACE 7
+#define DES_OUT_DLY 8
+#define TRANSPORT_SCRAMBLING_CONTROL_ODD 6
+#define TS_HIU_ENABLE 5
+#define FEC_FILE_CLK_DIV 0
+#define STB_DEMUX_ENABLE 4
+#define KEEP_DUPLICATE_PACKAGE 6
+
+#define ES_VID_MAN_RD_PTR (1<<0)
+#define ES_AUD_MAN_RD_PTR (1<<4)
+
+#define PS_CFG_PFIFO_EMPTY_CNT_BIT 16
+#define PS_CFG_MAX_ES_WR_CYCLE_BIT 12
+#define PS_CFG_MAX_FETCH_CYCLE_BIT 0
+
+#define ES_SUB_WR_ENDIAN_BIT 9
+#define ES_SUB_MAN_RD_PTR (1<<8)
+#define PARSER_INTSTAT_FETCH_CMD (1<<7)
+
+#define PARSER_INT_HOST_EN_BIT 8
+
+struct stream_buf_s;
+struct vdec_s;
+
+extern s32 tsdemux_init(u32 vid, u32 aid, u32 sid, u32 pcrid, bool is_hevc,
+ struct vdec_s *vdec);
+
+extern void tsdemux_release(void);
+extern ssize_t drm_tswrite(struct file *file,
+ struct stream_buf_s *vbuf,
+ struct stream_buf_s *abuf, const char __user *buf, size_t count);
+
+extern ssize_t tsdemux_write(struct file *file,
+ struct stream_buf_s *vbuf,
+ struct stream_buf_s *abuf, const char __user *buf, size_t count);
+
+extern u32 tsdemux_pcrscr_get(void);
+extern u8 tsdemux_pcrscr_valid(void);
+extern u32 tsdemux_first_pcrscr_get(void);
+
+int tsdemux_class_register(void);
+void tsdemux_class_unregister(void);
+void tsdemux_change_avid(unsigned int vid, unsigned int aid);
+void tsdemux_change_sid(unsigned int sid);
+void tsdemux_audio_reset(void);
+void tsdemux_sub_reset(void);
+void tsdemux_set_skipbyte(int skipbyte);
+void tsdemux_set_demux(int dev);
+#endif /* TSDEMUX_H */