author | Nanxin Qin <nanxin.qin@amlogic.com> | 2017-06-09 07:52:28 (GMT) |
---|---|---|
committer | Nanxin Qin <nanxin.qin@amlogic.com> | 2017-06-10 05:37:11 (GMT) |
commit | b9164398172ee6bbcdbc70c4ac1d87b450bdf13b (patch) | |
tree | ce1db9e64a310adeafb3f8f4a571e49768359ee7 | |
parent | 3c9b6b880f22a58b22d2785f0f974573e819f677 (diff) | |
download | media_modules-b9164398172ee6bbcdbc70c4ac1d87b450bdf13b.zip media_modules-b9164398172ee6bbcdbc70c4ac1d87b450bdf13b.tar.gz media_modules-b9164398172ee6bbcdbc70c4ac1d87b450bdf13b.tar.bz2 |
add drivers of the multimedia
Change-Id: Icde0895b71770e393cb6a61bedf04aa199f6463d
Signed-off-by: Nanxin Qin <nanxin.qin@amlogic.com>
92 files changed, 79453 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c9423b3 --- 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..2dbf863 --- a/dev/null +++ b/Media.mk @@ -0,0 +1,91 @@ +KERNEL_ARCH := arm64 +CONFIGS := CONFIG_AMLOGIC_MEDIA_VDEC_MPEG12=m \ + CONFIG_AMLOGIC_MEDIA_VDEC_MPEG4=m \ + CONFIG_AMLOGIC_MEDIA_VDEC_MPEG4_MULTI=m \ + CONFIG_AMLOGIC_MEDIA_VDEC_VC1=m \ + CONFIG_AMLOGIC_MEDIA_VDEC_H264=m \ + CONFIG_AMLOGIC_MEDIA_VDEC_H264_MULTI=m \ + CONFIG_AMLOGIC_MEDIA_VDEC_H264_MVC=m \ + CONFIG_AMLOGIC_MEDIA_VDEC_H264_4K2K=m \ + CONFIG_AMLOGIC_MEDIA_VDEC_H264_MVC=m \ + CONFIG_AMLOGIC_MEDIA_VDEC_H265=m \ + CONFIG_AMLOGIC_MEDIA_VDEC_VP9=m \ + CONFIG_AMLOGIC_MEDIA_VDEC_MJPEG=m \ + CONFIG_AMLOGIC_MEDIA_VDEC_MJPEG_MULTI=m \ + CONFIG_AMLOGIC_MEDIA_VDEC_REAL=m \ + CONFIG_AMLOGIC_MEDIA_VDEC_AVS=m \ + CONFIG_AMLOGIC_MEDIA_VENC_H264=m \ + CONFIG_AMLOGIC_MEDIA_VECN_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_modules/drivers +ifeq (,$(wildcard $(MEDIA_DRIVERS))) +$(error No find the dir of drivers.) +endif + +INCLUDE := $(MEDIA_DRIVERS)/include +ifeq (,$(wildcard $(INCLUDE))) +$(error No find the dir of include.) +endif + +MEDIA_MODULES := $(ANDROID_BUILD_TOP)/$(PRODUCT_OUT)/obj/media_modules +ifeq (,$(wildcard $(MEDIA_MODULES))) +$(shell mkdir $(MEDIA_MODULES) -p) +endif + +MODS_OUT := $(ANDROID_BUILD_TOP)/$(TARGET_OUT)/lib + +$(shell cp $(MEDIA_DRIVERS)/* $(MEDIA_MODULES) -rfa) + +define media-modules + @$(MAKE) -C $(KDIR) M=$(MEDIA_MODULES) ARCH=$(KERNEL_ARCH) \ + CROSS_COMPILE=$(PREFIX_CROSS_COMPILE) $(CONFIGS) \ + EXTRA_CFLAGS+=-I$(INCLUDE) modules; \ + find $(MEDIA_MODULES) -name "*.ko" | xargs -i cp {} $(MODS_OUT) +endef + +else +KDIR := $(PWD)/kernel +ifeq (,$(wildcard $(KDIR))) +$(error No find the dir of kernel.) +endif + +MEDIA_DRIVERS := $(PWD)/media_modules/drivers +ifeq (,$(wildcard $(MEDIA_DRIVERS))) +$(error No find the dir of drivers.) +endif + +INCLUDE := $(MEDIA_DRIVERS)/include +ifeq (,$(wildcard $(INCLUDE))) +$(error No find the dir of include.) +endif + +MODS_OUT ?= $(MEDIA_DRIVERS)/../modules +ifeq (,$(wildcard $(MODS_OUT))) +$(shell mkdir $(MODS_OUT) -p) +endif + +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=$(KERNEL_ARCH) \ + CROSS_COMPILE=$(TOOLS) $(CONFIGS) \ + EXTRA_CFLAGS+=-I$(INCLUDE) -j64 + +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=$(KERNEL_ARCH) clean + +endif diff --git a/drivers/Makefile b/drivers/Makefile new file mode 100644 index 0000000..f2a5148 --- a/dev/null +++ b/drivers/Makefile @@ -0,0 +1,4 @@ +obj-y += common/ +obj-y += frame_provider/ +obj-y += frame_sink/ +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 ¤t_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..748039c --- a/dev/null +++ b/drivers/common/firmware/Makefile @@ -0,0 +1,3 @@ +obj-m += firmware.o +firmware-objs += firmware_drv.o +firmware-objs += firmware_type.o diff --git a/drivers/common/firmware/firmware.h b/drivers/common/firmware/firmware.h new file mode 100644 index 0000000..b7d56d7 --- a/dev/null +++ b/drivers/common/firmware/firmware.h @@ -0,0 +1,113 @@ +/* + * 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/cdev.h> +#include "firmware_type.h" +#include <linux/amlogic/media/utils/vformat.h> + + +struct firmware_mgr_s { + struct list_head head; + spinlock_t lock; +}; + +struct firmware_info_s { + struct list_head node; + char name[32]; + char path[64]; + enum firmware_type_e type; + struct firmware_s *data; +}; + +struct ucode_info_s { + int cpu; + enum firmware_type_e type; + const char *name; +}; + +struct firmware_header_s { + int magic; + int checksum; + char name[32]; + char cpu[16]; + char format[32]; + char version[32]; + char author[32]; + char date[32]; + char commit[16]; + int data_size; + unsigned int time; + char reserved[128]; +}; + +struct firmware_s { + union { + struct firmware_header_s header; + char buf[512]; + }; + 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]; +}; + + +struct firmware_dev_s { + struct cdev cdev; + struct device *dev; + dev_t dev_no; +}; + +int get_decoder_firmware_data(enum vformat_e type, + const char *file_name, char *buf, int size); +int get_data_from_name(const char *name, char *buf); +int get_firmware_data(enum firmware_type_e type, char *buf); + +#endif diff --git a/drivers/common/firmware/firmware_cfg.h b/drivers/common/firmware/firmware_cfg.h new file mode 100644 index 0000000..be919ff --- a/dev/null +++ b/drivers/common/firmware/firmware_cfg.h @@ -0,0 +1,32 @@ +/* + * drivers/amlogic/media/common/firmware/firmware_cfg.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. + * +*/ + +/*all firmwares in one bin.*/ +{MESON_CPU_MAJOR_ID_GXBB, VIDEO_PACKAGE, "video_ucode.bin"}, +{MESON_CPU_MAJOR_ID_GXTVBB, VIDEO_PACKAGE, "video_ucode.bin"}, +{MESON_CPU_MAJOR_ID_GXL, VIDEO_PACKAGE, "video_ucode.bin"}, +{MESON_CPU_MAJOR_ID_GXM, VIDEO_PACKAGE, "video_ucode.bin"}, +{MESON_CPU_MAJOR_ID_TXL, VIDEO_PACKAGE, "video_ucode.bin"}, + +/*firmware for a special format, to replace the format in the package.*/ +{MESON_CPU_MAJOR_ID_GXL, VIDEO_DEC_HEVC, "h265.bin"}, +{MESON_CPU_MAJOR_ID_GXL, VIDEO_DEC_H264, "h264.bin"}, +{MESON_CPU_MAJOR_ID_GXL, VIDEO_DEC_H264_MULTI, "h264_multi.bin"}, +{MESON_CPU_MAJOR_ID_GXM, VIDEO_ENC_H264, "gx_h264_enc.bin"}, +{MESON_CPU_MAJOR_ID_GXL, VIDEO_ENC_H264, "gx_h264_enc.bin"}, + + diff --git a/drivers/common/firmware/firmware_drv.c b/drivers/common/firmware/firmware_drv.c new file mode 100644 index 0000000..56420e1 --- a/dev/null +++ b/drivers/common/firmware/firmware_drv.c @@ -0,0 +1,682 @@ +/* + * 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/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 (64*1024) /*64k*/ +#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); + +static struct ucode_info_s ucode_info[] = { +#include "firmware_cfg.h" +}; + +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; + +int get_firmware_data(enum firmware_type_e type, char *buf) +{ + int data_len, ret = -1; + 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) { + if (type != info->type) + continue; + + data_len = info->data->header.data_size; + memcpy(buf, info->data->data, data_len); + ret = data_len; + + break; + } + + return ret; +} +EXPORT_SYMBOL(get_firmware_data); + +int get_data_from_name(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->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_data_from_name); + +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 firmware 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_data_from_name(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); + 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->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->name); + pr_info( "%10s : %d\n", "size", + info->data->header.data_size); + pr_info( "%10s : %s\n", "ver", + info->data->header.version); + pr_info( "%10s : 0x%x\n", "sum", + 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 = get_cpu_type(); + char *path = __getname(); + const char *name; + + if (IS_ERR_OR_NULL(path)) + return -ENOMEM; + + for (i = 0; i < info_size; i++) { + if (cpu != ucode_info[i].cpu) + 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->name, name); + info->type = ucode_info[i].type; + 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, enum firmware_type_e type) +{ + 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 -1; + } + + list_for_each_entry(info, &mgr->head, node) { + if (info->type != type) + continue; + + if (IS_ERR_OR_NULL(info->data)) + info->data = data; + + return 1; + } + + return 0; +} + +static int firmware_parse_package(char *buf, int size) +{ + int ret = 0; + struct package_info_s *pack_info; + struct firmware_info_s *info; + struct firmware_s *data; + char *pack_data; + int info_len, len; + char *path = __getname(); + + if (IS_ERR_OR_NULL(path)) + return -ENOMEM; + + pack_data = ((struct package_s *)buf)->data; + pack_info = (struct package_info_s *)pack_data; + info_len = sizeof(struct package_info_s); + + for (;;) { + if (!pack_info->header.length) + break; + + 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->name, pack_info->header.name); + info->type = get_firmware_type(pack_info->header.format); + + 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; + + ret = checksum(data); + if (!ret) { + pr_info("check sum fail !\n"); + kfree(data); + kfree(info); + goto out; + } + + ret = check_repeat(data, info->type); + if (ret < 0) { + kfree(data); + kfree(info); + goto out; + } + + if (ret) { + kfree(info); + continue; + } + + info->data = data; + add_info(info); + } +out: + __putname(path); + + return ret; +} + +static int firmware_parse_code(struct firmware_info_s *info, + char *buf, int size) +{ + if (!IS_ERR_OR_NULL(info->data)) + kfree(info->data); + + 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 ret = 0, magic = 0; + struct firmware_mgr_s *mgr = g_mgr; + struct firmware_info_s *info, *temp; + char *buf = NULL; + 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: + ret = firmware_parse_package(buf, size); + + del_info(info); + kfree(info); + break; + + case CODE: + ret = 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 ret; +} + +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); + + kfree(g_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); +} + +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_driver_exit(); + pr_info("Firmware driver cleaned up.\n"); +} + +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_type.c b/drivers/common/firmware/firmware_type.c new file mode 100644 index 0000000..cf5e306 --- a/dev/null +++ b/drivers/common/firmware/firmware_type.c @@ -0,0 +1,57 @@ +#include "firmware_type.h" + +static const struct type_name_s type_name[] = { + {VIDEO_DEC_MPEG12, "mpeg12"}, + {VIDEO_DEC_MPEG4_3, "divx311"}, + {VIDEO_DEC_MPEG4_4, "divx4x"}, + {VIDEO_DEC_MPEG4_5, "xvid"}, + {VIDEO_DEC_H263, "h263"}, + {VIDEO_DEC_MJPEG, "mjpeg"}, + {VIDEO_DEC_MJPEG_MULTI, "mjpeg_multi"}, + {VIDEO_DEC_REAL_V8, "real_v8"}, + {VIDEO_DEC_REAL_V9, "real_v9"}, + {VIDEO_DEC_VC1, "vc1"}, + {VIDEO_DEC_AVS, "avs"}, + {VIDEO_DEC_H264, "h264"}, + {VIDEO_DEC_H264_4k2K, "h264_4k2k"}, + {VIDEO_DEC_H264_4k2K_SINGLE, "h264_4k2k_single"}, + {VIDEO_DEC_H264_MVC, "h264_mvc"}, + {VIDEO_DEC_H264_MULTI, "h264_multi"}, + {VIDEO_DEC_HEVC, "hevc"}, + {VIDEO_DEC_HEVC_MMU, "hevc_mmu"}, + {VIDEO_DEC_VP9, "vp9"}, + {VIDEO_DEC_VP9_MMU, "vp9_mmu"}, + {VIDEO_ENC_H264, "h264_enc"}, + {VIDEO_ENC_JPEG, "jpeg_enc"}, + {FIRMWARE_MAX, "unknown"}, +}; + + +const char *get_firmware_type_name(enum firmware_type_e type) +{ + const char *name = "unknown"; + int i, size = ARRAY_SIZE(type_name); + + for (i = 0; i < size; i++) { + if (type == type_name[i].type) + name = type_name[i].name; + } + + return name; +} +EXPORT_SYMBOL(get_firmware_type_name); + +enum firmware_type_e get_firmware_type(const char *name) +{ + enum firmware_type_e type = FIRMWARE_MAX; + int i, size = ARRAY_SIZE(type_name); + + for (i = 0; i < size; i++) { + if (!strcmp(name, type_name[i].name)) + type = type_name[i].type; + } + + return type; +} +EXPORT_SYMBOL(get_firmware_type); + diff --git a/drivers/common/firmware/firmware_type.h b/drivers/common/firmware/firmware_type.h new file mode 100644 index 0000000..74c962f --- a/dev/null +++ b/drivers/common/firmware/firmware_type.h @@ -0,0 +1,41 @@ +#ifndef __VIDEO_FIRMWARE_FORMAT_ +#define __VIDEO_FIRMWARE_FORMAT_ + +#include <linux/slab.h> + +enum firmware_type_e { + VIDEO_DEC_MPEG12, + VIDEO_DEC_MPEG4_3, + VIDEO_DEC_MPEG4_4, + VIDEO_DEC_MPEG4_5, + VIDEO_DEC_H263, + VIDEO_DEC_MJPEG, + VIDEO_DEC_MJPEG_MULTI, + VIDEO_DEC_REAL_V8, + VIDEO_DEC_REAL_V9, + VIDEO_DEC_VC1, + VIDEO_DEC_AVS, + VIDEO_DEC_H264, + VIDEO_DEC_H264_4k2K, + VIDEO_DEC_H264_4k2K_SINGLE, + VIDEO_DEC_H264_MVC, + VIDEO_DEC_H264_MULTI, + VIDEO_DEC_HEVC, + VIDEO_DEC_HEVC_MMU, + VIDEO_DEC_VP9, + VIDEO_DEC_VP9_MMU, + VIDEO_ENC_H264, + VIDEO_ENC_JPEG, + VIDEO_PACKAGE, + FIRMWARE_MAX +}; + +struct type_name_s { + enum firmware_type_e type; + const char *name; +}; + +const char *get_firmware_type_name(enum firmware_type_e type); +enum firmware_type_e get_firmware_type(const char *name); + +#endif 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..18d308f --- a/dev/null +++ b/drivers/common/media_clock/clk/clk.c @@ -0,0 +1,405 @@ +/* + * 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); + +int unregister_vdec_clk_mgr(enum vdec_type_e vdec_type) +{ + kfree(get_current_vdec_chip()->clk_mgr[vdec_type]); + + return 0; +} +EXPORT_SYMBOL(unregister_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); + +int unregister_vdec_clk_setting(void) +{ + kfree(get_current_vdec_chip()->clk_setting_array); + + return 0; +} +EXPORT_SYMBOL(unregister_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..1c979b4 --- a/dev/null +++ b/drivers/common/media_clock/clk/clk.h @@ -0,0 +1,155 @@ +/* + * 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); +void set_clock_gate(struct gate_switch_node *nodes, int num); + +#endif +int register_vdec_clk_mgr(int cputype[], + enum vdec_type_e vdec_type, struct chip_vdec_clk_s *t_mgr); + +int unregister_vdec_clk_mgr(enum vdec_type_e vdec_type); + +int register_vdec_clk_setting(int cputype[], + struct clk_set_setting *p_seting, int size); + +int unregister_vdec_clk_setting(void); + +#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; +} + +static void __exit vdec_clk_exit(void) +{ + unregister_vdec_clk_mgr(VDEC_1); +#ifdef VDEC_HAS_VDEC2 + unregister_vdec_clk_mgr(VDEC_2); +#endif +#ifdef VDEC_HAS_HEVC + unregister_vdec_clk_mgr(VDEC_HEVC); +#endif +#ifdef VDEC_HAS_VDEC_HCODEC + unregister_vdec_clk_mgr(VDEC_HCODEC); +#endif +#ifdef VDEC_HAS_CLK_SETTINGS + unregister_vdec_clk_setting(); +#endif + pr_info("media clock exit.\n"); +} + +#define ARCH_VDEC_CLK_INIT()\ + module_init(vdec_init_clk) + +#define ARCH_VDEC_CLK_EXIT()\ + module_exit(vdec_clk_exit) + +#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..8520738 --- a/dev/null +++ b/drivers/common/media_clock/clk/clkgx.c @@ -0,0 +1,620 @@ +/* + * 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> +#include "../switch/amports_gate.h" +#define MHz (1000000) + +struct clk_mux_s { + struct gate_switch_node *vdec_mux_node; + struct gate_switch_node *hcodec_mux_node; + struct gate_switch_node *hevc_mux_node; +}; + +struct clk_mux_s gclk; + +void vdec1_set_clk(int source, int div) +{ + pr_info("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) +{ + pr_info("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 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); + +/* 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]; + +/* +*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}, + } + }, + +}; + +void set_clock_gate(struct gate_switch_node *nodes, int num) +{ + struct gate_switch_node *node = NULL; + + do { + node = &nodes[num - 1]; + if (IS_ERR_OR_NULL(node)) + pr_info("get mux clk err.\n"); + + if (!strcmp(node->name, "clk_vdec_mux")) + gclk.vdec_mux_node = node; + else if (!strcmp(node->name, "clk_hcodec_mux")) + gclk.hcodec_mux_node = node; + else if (!strcmp(node->name, "clk_hevc_mux")) + gclk.hevc_mux_node = node; + } while(--num); +} +EXPORT_SYMBOL(set_clock_gate); + +static int vdec_set_clk(int dec, int rate) +{ + struct clk *clk = NULL; + + switch (dec) { + case VDEC_1: + clk = gclk.vdec_mux_node->clk; + WRITE_VREG_BITS(DOS_GCLK_EN0, 0x3ff, 0, 10); + break; + + case VDEC_HCODEC: + clk = gclk.hcodec_mux_node->clk; + WRITE_VREG_BITS(DOS_GCLK_EN0, 0x7fff, 12, 15); + break; + + case VDEC_2: + clk = gclk.vdec_mux_node->clk; + WRITE_VREG(DOS_GCLK_EN1, 0x3ff); + break; + + case VDEC_HEVC: + clk = gclk.hevc_mux_node->clk; + WRITE_VREG(DOS_GCLK_EN3, 0xffffffff); + break; + + case VDEC_MAX: + break; + + default: + pr_info("invaild vdec type.\n"); + } + + if (IS_ERR_OR_NULL(clk)) { + pr_info("the mux clk err.\n"); + return -1; + } + + clk_set_rate(clk, rate); + + return 0; +} + +static int vdec_clock_init(void) +{ + return 0; +} + +static int vdec_clock_set(int clk) +{ + 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) { + 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; + clk = 667; + } + + vdec_set_clk(VDEC_1, clk * MHz); + + clock_real_clk[VDEC_1] = clk; + + pr_info("vdec mux clock is %lu Hz\n", + clk_get_rate(gclk.vdec_mux_node->clk)); + + return clk; +} + +static int hevc_clock_init(void) +{ + return 0; +} + +static int hevc_clock_set(int 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) { + 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]; + } + + if ((clk > 500 && clk != 667)) { + if (clock_real_clk[VDEC_HEVC] == 648) + return 648; + clk = 667; + } + + vdec_set_clk(VDEC_HEVC, clk * MHz); + + clock_real_clk[VDEC_HEVC] = clk; + + pr_info("hevc mux clock is %lu Hz\n", + clk_get_rate(gclk.hevc_mux_node->clk)); + + return clk; +} + +static int hcodec_clock_set(int clk) +{ + if (clk == 1) + clk = 200; + else if (clk == 2) { + if (clock_real_clk[VDEC_HCODEC] != 648) + clk = 500; + else + clk = 648; + } else if (clk == 0) { + if (clock_real_clk[VDEC_HCODEC] == 667 || + (clock_real_clk[VDEC_HCODEC] == 648) || + clock_real_clk[VDEC_HCODEC] <= 0) + clk = 200; + else + clk = clock_real_clk[VDEC_HCODEC]; + } + + if ((clk > 500 && clk != 667)) { + if (clock_real_clk[VDEC_HCODEC] == 648) + return 648; + clk = 667; + } + + vdec_set_clk(VDEC_HCODEC, clk * MHz); + + clock_real_clk[VDEC_HCODEC] = clk; + + pr_info("hcodec mux clock is %lu Hz\n", + clk_get_rate(gclk.hcodec_mux_node->clk)); + + return clk; +} + +static void vdec_clock_on(void) +{ + spin_lock_irqsave(&gclk.vdec_mux_node->lock, + gclk.vdec_mux_node->flags); + if (!gclk.vdec_mux_node->ref_count) + clk_prepare_enable(gclk.vdec_mux_node->clk); + + gclk.vdec_mux_node->ref_count++; + spin_unlock_irqrestore(&gclk.vdec_mux_node->lock, + gclk.vdec_mux_node->flags); + + pr_info("the %-15s clock off, ref cnt: %d\n", + gclk.vdec_mux_node->name, + gclk.vdec_mux_node->ref_count); +} + +static void vdec_clock_off(void) +{ + spin_lock_irqsave(&gclk.vdec_mux_node->lock, + gclk.vdec_mux_node->flags); + gclk.vdec_mux_node->ref_count--; + if (!gclk.vdec_mux_node->ref_count) + clk_disable_unprepare(gclk.vdec_mux_node->clk); + + clock_real_clk[VDEC_1] = 0; + spin_unlock_irqrestore(&gclk.vdec_mux_node->lock, + gclk.vdec_mux_node->flags); + + pr_info("the %-15s clock off, ref cnt: %d\n", + gclk.vdec_mux_node->name, + gclk.vdec_mux_node->ref_count); +} + +static void hcodec_clock_on(void) +{ + spin_lock_irqsave(&gclk.hcodec_mux_node->lock, + gclk.hcodec_mux_node->flags); + if (!gclk.hcodec_mux_node->ref_count) + clk_prepare_enable(gclk.hcodec_mux_node->clk); + + gclk.hcodec_mux_node->ref_count++; + spin_unlock_irqrestore(&gclk.hcodec_mux_node->lock, + gclk.hcodec_mux_node->flags); + + pr_info("the %-15s clock off, ref cnt: %d\n", + gclk.hcodec_mux_node->name, + gclk.hcodec_mux_node->ref_count); +} + +static void hcodec_clock_off(void) +{ + spin_lock_irqsave(&gclk.hcodec_mux_node->lock, + gclk.hcodec_mux_node->flags); + gclk.hcodec_mux_node->ref_count--; + if (!gclk.hcodec_mux_node->ref_count) + clk_disable_unprepare(gclk.hcodec_mux_node->clk); + + spin_unlock_irqrestore(&gclk.hcodec_mux_node->lock, + gclk.hcodec_mux_node->flags); + + pr_info("the %-15s clock off, ref cnt: %d\n", + gclk.hcodec_mux_node->name, + gclk.hcodec_mux_node->ref_count); +} + +static void hevc_clock_on(void) +{ + spin_lock_irqsave(&gclk.hevc_mux_node->lock, + gclk.hevc_mux_node->flags); + if (!gclk.hevc_mux_node->ref_count) + clk_prepare_enable(gclk.hevc_mux_node->clk); + + gclk.hevc_mux_node->ref_count++; + WRITE_VREG(DOS_GCLK_EN3, 0xffffffff); + spin_unlock_irqrestore(&gclk.hevc_mux_node->lock, + gclk.hevc_mux_node->flags); + + pr_info("the %-15s clock off, ref cnt: %d\n", + gclk.hevc_mux_node->name, + gclk.hevc_mux_node->ref_count); +} + +static void hevc_clock_off(void) +{ + spin_lock_irqsave(&gclk.hevc_mux_node->lock, + gclk.hevc_mux_node->flags); + gclk.hevc_mux_node->ref_count--; + if (!gclk.hevc_mux_node->ref_count) + clk_disable_unprepare(gclk.hevc_mux_node->clk); + + clock_real_clk[VDEC_HEVC] = 0; + spin_unlock_irqrestore(&gclk.hevc_mux_node->lock, + gclk.hevc_mux_node->flags); + + pr_info("the %-15s clock off, ref cnt: %d\n", + gclk.hevc_mux_node->name, + gclk.hevc_mux_node->ref_count); +} + +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(); +ARCH_VDEC_CLK_EXIT(); + +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..ade914a --- a/dev/null +++ b/drivers/common/media_clock/switch/amports_gate.c @@ -0,0 +1,189 @@ +/* + * 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" +#include "../../../frame_provider/decoder/utils/vdec.h" +#include "../clk/clk.h" + + +#define DEBUG_REF 1 +#define GATE_RESET_OK + +#ifdef GATE_RESET_OK + +struct gate_switch_node gates[] = { + { + .name = "demux", + }, + { + .name = "parser_top", + }, + { + .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_DEMUX + &clkc CLKID_DOS + &clkc CLKID_VDEC_MUX + &clkc CLKID_HCODEC_MUX + &clkc CLKID_HEVC_MUX>; + clock-names = "parser_top", + "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_switch_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); + } + + set_clock_gate(gates, ARRAY_SIZE(gates)); + + return 0; +} +EXPORT_SYMBOL(amports_clock_gate_init); + +static int amports_gate_clk(struct gate_switch_node *gate_node, int enable) +{ + spin_lock_irqsave(&gate_node->lock, gate_node->flags); + if (enable) { + if (gate_node->ref_count == 0) + clk_prepare_enable(gate_node->clk); + + gate_node->ref_count++; + + if (DEBUG_REF) + pr_info("the %-15s clock on, ref cnt: %d\n", + gate_node->name, gate_node->ref_count); + } else { + gate_node->ref_count--; + if (gate_node->ref_count == 0) + clk_disable_unprepare(gate_node->clk); + + if (DEBUG_REF) + pr_info("the %-15s clock off, ref cnt: %d\n", + gate_node->name, gate_node->ref_count); + } + 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_switch_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_switch_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..75270ab --- a/dev/null +++ b/drivers/common/media_clock/switch/amports_gate.h @@ -0,0 +1,33 @@ +/* + * 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> + +struct gate_switch_node { + struct clk *clk; + const char *name; + spinlock_t lock; + unsigned long flags; + int ref_count; +}; + +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..3a5774a --- a/dev/null +++ b/drivers/frame_provider/decoder/Makefile @@ -0,0 +1,16 @@ +obj-y += utils/ +obj-$(CONFIG_AMLOGIC_MEDIA_VDEC_MPEG12) += mpeg12/vmpeg12.o +obj-$(CONFIG_AMLOGIC_MEDIA_VDEC_MPEG4) += mpeg4/vmpeg4.o +obj-$(CONFIG_AMLOGIC_MEDIA_VDEC_MPEG4_MULTI) += mpeg4/vmpeg4_multi.o +obj-$(CONFIG_AMLOGIC_MEDIA_VDEC_VC1) += vc1/vvc1.o +obj-$(CONFIG_AMLOGIC_MEDIA_VDEC_H264) += h264/vh264.o +obj-$(CONFIG_AMLOGIC_MEDIA_VDEC_H264_MULTI) += h264_multi/ +obj-$(CONFIG_AMLOGIC_MEDIA_VDEC_H264_MVC) += h264/vh264_mvc.o +obj-$(CONFIG_AMLOGIC_MEDIA_VDEC_H264_4K2K) += h264/vh264_4k2k.o +obj-$(CONFIG_AMLOGIC_MEDIA_VDEC_H264_MVC) += h264/vh264_mvc.o +obj-$(CONFIG_AMLOGIC_MEDIA_VDEC_H265) += h265/vh265.o +obj-$(CONFIG_AMLOGIC_MEDIA_VDEC_VP9) += vp9/vvp9.o +obj-$(CONFIG_AMLOGIC_MEDIA_VDEC_MJPEG) += mjpeg/vmjpeg.o +obj-$(CONFIG_AMLOGIC_MEDIA_VDEC_MJPEG_MULTI) += mjpeg/vmjpeg_multi.o +obj-$(CONFIG_AMLOGIC_MEDIA_VDEC_REAL) += real/vreal.o +obj-$(CONFIG_AMLOGIC_MEDIA_VDEC_AVS) += avs/ diff --git a/drivers/frame_provider/decoder/avs/Makefile b/drivers/frame_provider/decoder/avs/Makefile new file mode 100644 index 0000000..cf154c9 --- a/dev/null +++ b/drivers/frame_provider/decoder/avs/Makefile @@ -0,0 +1,2 @@ +obj-m += vavs.o +vavs-objs += avs.o avsp_trans.o diff --git a/drivers/frame_provider/decoder/avs/avs.c b/drivers/frame_provider/decoder/avs/avs.c new file mode 100644 index 0000000..aed8497 --- a/dev/null +++ b/drivers/frame_provider/decoder/avs/avs.c @@ -0,0 +1,1541 @@ +/* + * drivers/amlogic/amports/vavs.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/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/canvas/canvas.h> +#include <linux/amlogic/media/vfm/vframe_provider.h> +#include <linux/amlogic/media/vfm/vframe_receiver.h> +#include <linux/amlogic/media/vfm/vframe.h> +#include <linux/amlogic/media/utils/vdec_reg.h> +#include "../../../stream_input/parser/streambuf_reg.h" +#include "../utils/amvdec.h" +#include <linux/amlogic/media/registers/register.h> +#include "../../../stream_input/amports/amports_priv.h" +#include <linux/dma-mapping.h> +#include <linux/amlogic/media/codec_mm/codec_mm.h> +#include <linux/slab.h> +#include "avs.h" + +#define DRIVER_NAME "amvdec_avs" +#define MODULE_NAME "amvdec_avs" + +#if 1/* MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON6 */ +#define NV21 +#endif + +#define USE_AVS_SEQ_INFO +#define HANDLE_AVS_IRQ +#define DEBUG_PTS + +#define I_PICTURE 0 +#define P_PICTURE 1 +#define B_PICTURE 2 + +/* #define ORI_BUFFER_START_ADDR 0x81000000 */ +#define ORI_BUFFER_START_ADDR 0x80000000 + +#define INTERLACE_FLAG 0x80 +#define TOP_FIELD_FIRST_FLAG 0x40 + +/* protocol registers */ +#define AVS_PIC_RATIO AV_SCRATCH_0 +#define AVS_PIC_WIDTH AV_SCRATCH_1 +#define AVS_PIC_HEIGHT AV_SCRATCH_2 +#define AVS_FRAME_RATE AV_SCRATCH_3 + +#define AVS_ERROR_COUNT AV_SCRATCH_6 +#define AVS_SOS_COUNT AV_SCRATCH_7 +#define AVS_BUFFERIN AV_SCRATCH_8 +#define AVS_BUFFEROUT AV_SCRATCH_9 +#define AVS_REPEAT_COUNT AV_SCRATCH_A +#define AVS_TIME_STAMP AV_SCRATCH_B +#define AVS_OFFSET_REG AV_SCRATCH_C +#define MEM_OFFSET_REG AV_SCRATCH_F +#define AVS_ERROR_RECOVERY_MODE AV_SCRATCH_G + +#define VF_POOL_SIZE 32 +#define PUT_INTERVAL (HZ/100) + +#if 1 /*MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON8*/ +#define INT_AMVENCODER INT_DOS_MAILBOX_1 +#else +/* #define AMVENC_DEV_VERSION "AML-MT" */ +#define INT_AMVENCODER INT_MAILBOX_1A +#endif + +#define VPP_VD1_POSTBLEND (1 << 10) + +static int debug_flag; + +static int firmware_sel; /* 0, normal; 1, old ucode */ + +int avs_get_debug_flag(void) +{ + return debug_flag; +} + +static struct vframe_s *vavs_vf_peek(void *); +static struct vframe_s *vavs_vf_get(void *); +static void vavs_vf_put(struct vframe_s *, void *); +static int vavs_vf_states(struct vframe_states *states, void *); + +static const char vavs_dec_id[] = "vavs-dev"; + +#define PROVIDER_NAME "decoder.avs" +static DEFINE_SPINLOCK(lock); +static DEFINE_MUTEX(vavs_mutex); + +static const struct vframe_operations_s vavs_vf_provider = { + .peek = vavs_vf_peek, + .get = vavs_vf_get, + .put = vavs_vf_put, + .vf_states = vavs_vf_states, +}; + +static struct vframe_provider_s vavs_vf_prov; + +#define VF_BUF_NUM_MAX 16 + +/*static u32 vf_buf_num = 4*/ +static u32 vf_buf_num = 4; +static u32 vf_buf_num_used; +static u32 canvas_base = 128; +#ifdef NV21 + int canvas_num = 2; /*NV21*/ +#else + int canvas_num = 3; +#endif +static u32 work_buf_size; + +static struct vframe_s vfpool[VF_POOL_SIZE]; +/*static struct vframe_s vfpool2[VF_POOL_SIZE];*/ +static struct vframe_s *cur_vfpool; +static unsigned char recover_flag; +static s32 vfbuf_use[VF_BUF_NUM_MAX]; +static u32 saved_resolution; +static u32 frame_width, frame_height, frame_dur, frame_prog; +static struct timer_list recycle_timer; +static u32 stat; +static unsigned long buf_start; +static u32 buf_size, buf_offset; +static u32 avi_flag; +static u32 vavs_ratio; +static u32 pic_type; +static u32 pts_by_offset = 1; +static u32 total_frame; +static u32 next_pts; +static unsigned char throw_pb_flag; +#ifdef DEBUG_PTS +static u32 pts_hit, pts_missed, pts_i_hit, pts_i_missed; +#endif + +static u32 radr, rval; +static struct dec_sysinfo vavs_amstream_dec_info; + +#ifdef AVSP_LONG_CABAC +static struct work_struct long_cabac_wd_work; +void *es_write_addr_virt; +dma_addr_t es_write_addr_phy; + +void *bitstream_read_tmp; +dma_addr_t bitstream_read_tmp_phy; +void *avsp_heap_adr; + +#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 inline u32 index2canvas(u32 index) +{ + const u32 canvas_tab[VF_BUF_NUM_MAX] = { + 0x010100, 0x030302, 0x050504, 0x070706, + 0x090908, 0x0b0b0a, 0x0d0d0c, 0x0f0f0e, + 0x111110, 0x131312, 0x151514, 0x171716, + 0x191918, 0x1b1b1a, 0x1d1d1c, 0x1f1f1e, + }; + const u32 canvas_tab_3[4] = { + 0x010100, 0x040403, 0x070706, 0x0a0a09 + }; + + if (canvas_num == 2) + return canvas_tab[index] + (canvas_base << 16) + + (canvas_base << 8) + canvas_base; + + return canvas_tab_3[index] + (canvas_base << 16) + + (canvas_base << 8) + canvas_base; +} + +static const u32 frame_rate_tab[16] = { + 96000 / 30, /* forbidden */ + 96000 / 24, /* 24000/1001 (23.967) */ + 96000 / 24, + 96000 / 25, + 96000 / 30, /* 30000/1001 (29.97) */ + 96000 / 30, + 96000 / 50, + 96000 / 60, /* 60000/1001 (59.94) */ + 96000 / 60, + /* > 8 reserved, use 24 */ + 96000 / 24, 96000 / 24, 96000 / 24, 96000 / 24, + 96000 / 24, 96000 / 24, 96000 / 24 +}; + +static void set_frame_info(struct vframe_s *vf, unsigned *duration) +{ + int ar = 0; + + unsigned pixel_ratio = READ_VREG(AVS_PIC_RATIO); +#ifndef USE_AVS_SEQ_INFO + if (vavs_amstream_dec_info.width > 0 + && vavs_amstream_dec_info.height > 0) { + vf->width = vavs_amstream_dec_info.width; + vf->height = vavs_amstream_dec_info.height; + } else +#endif + { + vf->width = READ_VREG(AVS_PIC_WIDTH); + vf->height = READ_VREG(AVS_PIC_HEIGHT); + frame_width = vf->width; + frame_height = vf->height; + /* pr_info("%s: (%d,%d)\n", __func__,vf->width, vf->height);*/ + } + +#ifndef USE_AVS_SEQ_INFO + if (vavs_amstream_dec_info.rate > 0) + *duration = vavs_amstream_dec_info.rate; + else +#endif + { + *duration = frame_rate_tab[READ_VREG(AVS_FRAME_RATE) & 0xf]; + /* pr_info("%s: duration = %d\n", __func__, *duration); */ + frame_dur = *duration; + } + + if (vavs_ratio == 0) { + /* always stretch to 16:9 */ + vf->ratio_control |= (0x90 << + DISP_RATIO_ASPECT_RATIO_BIT); + } else { + switch (pixel_ratio) { + case 1: + ar = (vf->height * vavs_ratio) / vf->width; + break; + case 2: + ar = (vf->height * 3 * vavs_ratio) / (vf->width * 4); + break; + case 3: + ar = (vf->height * 9 * vavs_ratio) / (vf->width * 16); + break; + case 4: + ar = (vf->height * 100 * vavs_ratio) / (vf->width * + 221); + break; + default: + ar = (vf->height * vavs_ratio) / vf->width; + break; + } + } + + ar = min(ar, DISP_RATIO_ASPECT_RATIO_MAX); + + vf->ratio_control = (ar << DISP_RATIO_ASPECT_RATIO_BIT); + /*vf->ratio_control |= DISP_RATIO_FORCECONFIG | DISP_RATIO_KEEPRATIO; */ + + vf->flag = 0; +} + +#ifdef HANDLE_AVS_IRQ +static irqreturn_t vavs_isr(int irq, void *dev_id) +#else +static void vavs_isr(void) +#endif +{ + u32 reg; + struct vframe_s *vf; + u32 dur; + u32 repeat_count; + u32 picture_type; + u32 buffer_index; + u32 picture_struct; + unsigned int pts, pts_valid = 0, offset; + if (debug_flag & AVS_DEBUG_UCODE) { + if (READ_VREG(AV_SCRATCH_E) != 0) { + pr_info("dbg%x: %x\n", READ_VREG(AV_SCRATCH_E), + READ_VREG(AV_SCRATCH_D)); + WRITE_VREG(AV_SCRATCH_E, 0); + } + } +#ifdef AVSP_LONG_CABAC + if (firmware_sel == 0 && READ_VREG(LONG_CABAC_REQ)) { +#ifdef PERFORMANCE_DEBUG + pr_info("%s:schedule long_cabac_wd_work\r\n", __func__); +#endif + schedule_work(&long_cabac_wd_work); + } +#endif + reg = READ_VREG(AVS_BUFFEROUT); + + if (reg) { + picture_struct = READ_VREG(AV_SCRATCH_5); + if (debug_flag & AVS_DEBUG_PRINT) + pr_info("AVS_BUFFEROUT=%x, picture_struct is 0x%x\n", + reg, picture_struct); + if (pts_by_offset) { + offset = READ_VREG(AVS_OFFSET_REG); + if (debug_flag & AVS_DEBUG_PRINT) + pr_info("AVS OFFSET=%x\n", offset); + if (pts_lookup_offset(PTS_TYPE_VIDEO, offset, &pts, 0) + == 0) { + pts_valid = 1; +#ifdef DEBUG_PTS + pts_hit++; +#endif + } else { +#ifdef DEBUG_PTS + pts_missed++; +#endif + } + } + + repeat_count = READ_VREG(AVS_REPEAT_COUNT); + if (firmware_sel == 0) + buffer_index = + ((reg & 0x7) + + (((reg >> 8) & 0x3) << 3) - 1) & 0x1f; + else + buffer_index = + ((reg & 0x7) - 1) & 3; + + picture_type = (reg >> 3) & 7; +#ifdef DEBUG_PTS + if (picture_type == I_PICTURE) { + /* pr_info("I offset 0x%x, pts_valid %d\n", + offset, pts_valid); */ + if (!pts_valid) + pts_i_missed++; + else + pts_i_hit++; + } +#endif + + if (throw_pb_flag && picture_type != I_PICTURE) { + + if (debug_flag & AVS_DEBUG_PRINT) { + pr_info("picture type %d throwed\n", + picture_type); + } + WRITE_VREG(AVS_BUFFERIN, ~(1 << buffer_index)); + } else if (reg & INTERLACE_FLAG) { /* interlace */ + throw_pb_flag = 0; + + if (debug_flag & AVS_DEBUG_PRINT) { + pr_info("interlace, picture type %d\n", + picture_type); + } + + if (kfifo_get(&newframe_q, &vf) == 0) { + pr_info + ("fatal error, no available buffer slot."); + return IRQ_HANDLED; + } + set_frame_info(vf, &dur); + vf->bufWidth = 1920; + pic_type = 2; + if ((I_PICTURE == picture_type) && pts_valid) { + vf->pts = pts; + if ((repeat_count > 1) && avi_flag) { + /* next_pts = pts + + (vavs_amstream_dec_info.rate * + repeat_count >> 1)*15/16; */ + next_pts = + pts + + (dur * repeat_count >> 1) * + 15 / 16; + } else + next_pts = 0; + } else { + vf->pts = next_pts; + if ((repeat_count > 1) && avi_flag) { + /* vf->duration = + vavs_amstream_dec_info.rate * + repeat_count >> 1; */ + vf->duration = dur * repeat_count >> 1; + if (next_pts != 0) { + next_pts += + ((vf->duration) - + ((vf->duration) >> 4)); + } + } else { + /* vf->duration = + vavs_amstream_dec_info.rate >> 1; */ + vf->duration = dur >> 1; + next_pts = 0; + } + } + vf->signal_type = 0; + vf->index = buffer_index; + vf->duration_pulldown = 0; + vf->type = + (reg & TOP_FIELD_FIRST_FLAG) + ? VIDTYPE_INTERLACE_TOP + : VIDTYPE_INTERLACE_BOTTOM; +#ifdef NV21 + vf->type |= VIDTYPE_VIU_NV21; +#endif + vf->canvas0Addr = vf->canvas1Addr = + index2canvas(buffer_index); + vf->type_original = vf->type; + + if (debug_flag & AVS_DEBUG_PRINT) { + pr_info("buffer_index %d, canvas addr %x\n", + buffer_index, vf->canvas0Addr); + } + + vfbuf_use[buffer_index]++; + + kfifo_put(&display_q, + (const struct vframe_s *)vf); + vf_notify_receiver(PROVIDER_NAME, + VFRAME_EVENT_PROVIDER_VFRAME_READY, + NULL); + + if (kfifo_get(&newframe_q, &vf) == 0) { + pr_info("fatal error, no available buffer slot."); + return IRQ_HANDLED; + } + set_frame_info(vf, &dur); + vf->bufWidth = 1920; + + vf->pts = next_pts; + if ((repeat_count > 1) && avi_flag) { + /* vf->duration = vavs_amstream_dec_info.rate * + repeat_count >> 1; */ + vf->duration = dur * repeat_count >> 1; + if (next_pts != 0) { + next_pts += + ((vf->duration) - + ((vf->duration) >> 4)); + } + } else { + /* vf->duration = vavs_amstream_dec_info.rate + >> 1; */ + vf->duration = dur >> 1; + next_pts = 0; + } + vf->signal_type = 0; + vf->index = buffer_index; + vf->duration_pulldown = 0; + vf->type = + (reg & TOP_FIELD_FIRST_FLAG) ? + VIDTYPE_INTERLACE_BOTTOM : + VIDTYPE_INTERLACE_TOP; +#ifdef NV21 + vf->type |= VIDTYPE_VIU_NV21; +#endif + vf->canvas0Addr = vf->canvas1Addr = + index2canvas(buffer_index); + vf->type_original = vf->type; + vfbuf_use[buffer_index]++; + + kfifo_put(&display_q, + (const struct vframe_s *)vf); + vf_notify_receiver(PROVIDER_NAME, + VFRAME_EVENT_PROVIDER_VFRAME_READY, + NULL); + total_frame++; + } else { /* progressive */ + throw_pb_flag = 0; + + if (debug_flag & AVS_DEBUG_PRINT) { + pr_info("progressive picture type %d\n", + picture_type); + } + if (kfifo_get(&newframe_q, &vf) == 0) { + pr_info + ("fatal error, no available buffer slot."); + return IRQ_HANDLED; + } + set_frame_info(vf, &dur); + vf->bufWidth = 1920; + pic_type = 1; + + if ((I_PICTURE == picture_type) && pts_valid) { + vf->pts = pts; + if ((repeat_count > 1) && avi_flag) { + /* next_pts = pts + + (vavs_amstream_dec_info.rate * + repeat_count)*15/16; */ + next_pts = + pts + + (dur * repeat_count) * 15 / 16; + } else + next_pts = 0; + } else { + vf->pts = next_pts; + if ((repeat_count > 1) && avi_flag) { + /* vf->duration = + vavs_amstream_dec_info.rate * + repeat_count; */ + vf->duration = dur * repeat_count; + if (next_pts != 0) { + next_pts += + ((vf->duration) - + ((vf->duration) >> 4)); + } + } else { + /* vf->duration = + vavs_amstream_dec_info.rate; */ + vf->duration = dur; + next_pts = 0; + } + } + vf->signal_type = 0; + vf->index = buffer_index; + vf->duration_pulldown = 0; + vf->type = VIDTYPE_PROGRESSIVE | VIDTYPE_VIU_FIELD; +#ifdef NV21 + vf->type |= VIDTYPE_VIU_NV21; +#endif + vf->canvas0Addr = vf->canvas1Addr = + index2canvas(buffer_index); + vf->type_original = vf->type; + if (debug_flag & AVS_DEBUG_PRINT) { + pr_info("buffer_index %d, canvas addr %x\n", + buffer_index, vf->canvas0Addr); + } + + vfbuf_use[buffer_index]++; + kfifo_put(&display_q, + (const struct vframe_s *)vf); + vf_notify_receiver(PROVIDER_NAME, + VFRAME_EVENT_PROVIDER_VFRAME_READY, + NULL); + total_frame++; + } + + /* pr_info("PicType = %d, PTS = 0x%x\n", + picture_type, vf->pts); */ + WRITE_VREG(AVS_BUFFEROUT, 0); + } + + WRITE_VREG(ASSIST_MBOX1_CLR_REG, 1); + +#ifdef HANDLE_AVS_IRQ + return IRQ_HANDLED; +#else + return; +#endif +} +/* +static int run_flag = 1; +static int step_flag; +*/ +static int error_recovery_mode; /*0: blocky 1: mosaic*/ +/* +static uint error_watchdog_threshold=10; +static uint error_watchdog_count; +static uint error_watchdog_buf_threshold = 0x4000000; +*/ +static uint long_cabac_busy; + +static struct vframe_s *vavs_vf_peek(void *op_arg) +{ + struct vframe_s *vf; + + if (recover_flag) + return NULL; + + if (kfifo_peek(&display_q, &vf)) + return vf; + + return NULL; + +} + +static struct vframe_s *vavs_vf_get(void *op_arg) +{ + struct vframe_s *vf; + + if (recover_flag) + return NULL; + + if (kfifo_get(&display_q, &vf)) + return vf; + + return NULL; + +} + +static void vavs_vf_put(struct vframe_s *vf, void *op_arg) +{ + int i; + if (recover_flag) + return; + + for (i = 0; i < VF_POOL_SIZE; i++) { + if (vf == &cur_vfpool[i]) + break; + } + if (i < VF_POOL_SIZE) + kfifo_put(&recycle_q, (const struct vframe_s *)vf); + +} + +int vavs_dec_status(struct vdec_s *vdec, struct vdec_status *vstatus) +{ + vstatus->width = frame_width; /* vavs_amstream_dec_info.width; */ + vstatus->height = frame_height; /* vavs_amstream_dec_info.height; */ + if (0 != frame_dur /*vavs_amstream_dec_info.rate */) + vstatus->fps = 96000 / frame_dur; + else + vstatus->fps = 96000; + vstatus->error_count = READ_VREG(AVS_ERROR_COUNT); + vstatus->status = stat; + + return 0; +} + +/****************************************/ +static void vavs_canvas_init(void) +{ + int i; + u32 canvas_width, canvas_height; + u32 decbuf_size, decbuf_y_size, decbuf_uv_size; + u32 disp_addr = 0xffffffff; + int vf_buf_num_avail = 0; + vf_buf_num_used = vf_buf_num; + if (buf_size <= 0x00400000) { + /* SD only */ + canvas_width = 768; + canvas_height = 576; + decbuf_y_size = 0x80000; + decbuf_uv_size = 0x20000; + decbuf_size = 0x100000; + vf_buf_num_avail = + ((buf_size - work_buf_size) / decbuf_size) - 1; + pr_info + ("avs(SD):buf_start %p, size %x, offset %x avail %d\n", + (void *)buf_start, buf_size, buf_offset, + vf_buf_num_avail); + } else { + /* HD & SD */ + canvas_width = 1920; + canvas_height = 1088; + decbuf_y_size = 0x200000; + decbuf_uv_size = 0x80000; + decbuf_size = 0x300000; + vf_buf_num_avail = + ((buf_size - work_buf_size) / decbuf_size) - 1; + pr_info("avs: buf_start %p, buf_size %x, buf_offset %x buf avail %d\n", + (void *)buf_start, buf_size, buf_offset, + vf_buf_num_avail); + } + if (vf_buf_num_used > vf_buf_num_avail) + vf_buf_num_used = vf_buf_num_avail; + + if (firmware_sel == 0) + buf_offset = buf_offset + ((vf_buf_num_used + 1) * decbuf_size); + + if (READ_MPEG_REG(VPP_MISC) & VPP_VD1_POSTBLEND) { + struct canvas_s cur_canvas; + + canvas_read((READ_MPEG_REG(VD1_IF0_CANVAS0) & 0xff), + &cur_canvas); + disp_addr = (cur_canvas.addr + 7) >> 3; + } + + for (i = 0; i < vf_buf_num_used; i++) { + if (((buf_start + i * decbuf_size + 7) >> 3) == disp_addr) { +#ifdef NV21 + canvas_config(canvas_base + canvas_num * i + 0, + buf_start + + vf_buf_num_used * decbuf_size, + canvas_width, canvas_height, + CANVAS_ADDR_NOWRAP, + CANVAS_BLKMODE_32X32); + canvas_config(canvas_base + canvas_num * i + 1, + buf_start + + vf_buf_num_used * decbuf_size + + decbuf_y_size, canvas_width, + canvas_height / 2, + CANVAS_ADDR_NOWRAP, + CANVAS_BLKMODE_32X32); +#else + canvas_config(canvas_num * i + 0, + buf_start + 4 * decbuf_size, + canvas_width, canvas_height, + CANVAS_ADDR_NOWRAP, + CANVAS_BLKMODE_32X32); + canvas_config(canvas_num * i + 1, + buf_start + 4 * decbuf_size + + decbuf_y_size, canvas_width / 2, + canvas_height / 2, + CANVAS_ADDR_NOWRAP, + CANVAS_BLKMODE_32X32); + canvas_config(canvas_num * i + 2, + buf_start + 4 * decbuf_size + + decbuf_y_size + decbuf_uv_size, + canvas_width / 2, canvas_height / 2, + CANVAS_ADDR_NOWRAP, + CANVAS_BLKMODE_32X32); +#endif + if (debug_flag & AVS_DEBUG_PRINT) { + pr_info("canvas config %d, addr %p\n", + vf_buf_num_used, + (void *)(buf_start + + vf_buf_num_used * decbuf_size)); + } + + } else { +#ifdef NV21 + canvas_config(canvas_base + canvas_num * i + 0, + buf_start + i * decbuf_size, + canvas_width, canvas_height, + CANVAS_ADDR_NOWRAP, + CANVAS_BLKMODE_32X32); + canvas_config(canvas_base + canvas_num * i + 1, + buf_start + i * decbuf_size + + decbuf_y_size, canvas_width, + canvas_height / 2, + CANVAS_ADDR_NOWRAP, + CANVAS_BLKMODE_32X32); +#else + canvas_config(canvas_num * i + 0, + buf_start + i * decbuf_size, + canvas_width, canvas_height, + CANVAS_ADDR_NOWRAP, + CANVAS_BLKMODE_32X32); + canvas_config(canvas_num * i + 1, + buf_start + i * decbuf_size + + decbuf_y_size, canvas_width / 2, + canvas_height / 2, + CANVAS_ADDR_NOWRAP, + CANVAS_BLKMODE_32X32); + canvas_config(canvas_num * i + 2, + buf_start + i * decbuf_size + + decbuf_y_size + decbuf_uv_size, + canvas_width / 2, canvas_height / 2, + CANVAS_ADDR_NOWRAP, + CANVAS_BLKMODE_32X32); +#endif + if (debug_flag & AVS_DEBUG_PRINT) { + pr_info("canvas config %d, addr %p\n", i, + (void *)(buf_start + + i * decbuf_size)); + } + } + } +} + +void vavs_recover(void) +{ + WRITE_VREG(DOS_SW_RESET0, (1 << 7) | (1 << 6) | (1 << 4)); + WRITE_VREG(DOS_SW_RESET0, 0); + + 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); + + if (firmware_sel == 1) { + WRITE_VREG(POWER_CTL_VLD, 0x10); + WRITE_VREG_BITS(VLD_MEM_VIFIFO_CONTROL, 2, + MEM_FIFO_CNT_BIT, 2); + WRITE_VREG_BITS(VLD_MEM_VIFIFO_CONTROL, 8, + MEM_LEVEL_CNT_BIT, 6); + } + if (firmware_sel == 0) + WRITE_VREG(AV_SCRATCH_5, 0); + + if (firmware_sel == 0) { + /* fixed canvas index */ + WRITE_VREG(AV_SCRATCH_0, canvas_base); + WRITE_VREG(AV_SCRATCH_1, vf_buf_num_used); + } else { + int ii; + for (ii = 0; ii < 4; ii++) { + WRITE_VREG(AV_SCRATCH_0 + ii, + (canvas_base + canvas_num * ii) | + ((canvas_base + canvas_num * ii + 1) + << 8) | + ((canvas_base + canvas_num * ii + 1) + << 16) + ); + } + } + + /* notify ucode the buffer offset */ + WRITE_VREG(AV_SCRATCH_F, buf_offset); + + /* disable PSCALE for hardware sharing */ + WRITE_VREG(PSCALE_CTRL, 0); + + WRITE_VREG(AVS_SOS_COUNT, 0); + WRITE_VREG(AVS_BUFFERIN, 0); + WRITE_VREG(AVS_BUFFEROUT, 0); + if (error_recovery_mode) + WRITE_VREG(AVS_ERROR_RECOVERY_MODE, 0); + else + WRITE_VREG(AVS_ERROR_RECOVERY_MODE, 1); + /* clear mailbox interrupt */ + WRITE_VREG(ASSIST_MBOX1_CLR_REG, 1); + + /* enable mailbox interrupt */ + WRITE_VREG(ASSIST_MBOX1_MASK, 1); +#if 1 /* def DEBUG_UCODE */ + WRITE_VREG(AV_SCRATCH_D, 0); +#endif + +#ifdef NV21 + SET_VREG_MASK(MDEC_PIC_DC_CTRL, 1 << 17); +#endif + +#ifdef PIC_DC_NEED_CLEAR + CLEAR_VREG_MASK(MDEC_PIC_DC_CTRL, 1 << 31); +#endif + +#ifdef AVSP_LONG_CABAC + if (firmware_sel == 0) { + WRITE_VREG(LONG_CABAC_DES_ADDR, es_write_addr_phy); + WRITE_VREG(LONG_CABAC_REQ, 0); + WRITE_VREG(LONG_CABAC_PIC_SIZE, 0); + WRITE_VREG(LONG_CABAC_SRC_ADDR, 0); + } +#endif + +} + +static void vavs_prot_init(void) +{ +#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); + + 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); + +#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 + + /***************** reset vld **********************************/ + WRITE_VREG(POWER_CTL_VLD, 0x10); + WRITE_VREG_BITS(VLD_MEM_VIFIFO_CONTROL, 2, MEM_FIFO_CNT_BIT, 2); + WRITE_VREG_BITS(VLD_MEM_VIFIFO_CONTROL, 8, MEM_LEVEL_CNT_BIT, 6); + /*************************************************************/ + + vavs_canvas_init(); + if (firmware_sel == 0) + WRITE_VREG(AV_SCRATCH_5, 0); +#ifdef NV21 + if (firmware_sel == 0) { + /* fixed canvas index */ + WRITE_VREG(AV_SCRATCH_0, canvas_base); + WRITE_VREG(AV_SCRATCH_1, vf_buf_num_used); + } else { + int ii; + for (ii = 0; ii < 4; ii++) { + WRITE_VREG(AV_SCRATCH_0 + ii, + (canvas_base + canvas_num * ii) | + ((canvas_base + canvas_num * ii + 1) + << 8) | + ((canvas_base + canvas_num * ii + 1) + << 16) + ); + } + /* + WRITE_VREG(AV_SCRATCH_0, 0x010100); + WRITE_VREG(AV_SCRATCH_1, 0x040403); + WRITE_VREG(AV_SCRATCH_2, 0x070706); + WRITE_VREG(AV_SCRATCH_3, 0x0a0a09); + */ + } +#else + /* index v << 16 | u << 8 | y */ + WRITE_VREG(AV_SCRATCH_0, 0x020100); + WRITE_VREG(AV_SCRATCH_1, 0x050403); + WRITE_VREG(AV_SCRATCH_2, 0x080706); + WRITE_VREG(AV_SCRATCH_3, 0x0b0a09); +#endif + /* notify ucode the buffer offset */ + WRITE_VREG(AV_SCRATCH_F, buf_offset); + + /* disable PSCALE for hardware sharing */ + WRITE_VREG(PSCALE_CTRL, 0); + + WRITE_VREG(AVS_SOS_COUNT, 0); + WRITE_VREG(AVS_BUFFERIN, 0); + WRITE_VREG(AVS_BUFFEROUT, 0); + if (error_recovery_mode) + WRITE_VREG(AVS_ERROR_RECOVERY_MODE, 0); + else + WRITE_VREG(AVS_ERROR_RECOVERY_MODE, 1); + /* clear mailbox interrupt */ + WRITE_VREG(ASSIST_MBOX1_CLR_REG, 1); + + /* enable mailbox interrupt */ + WRITE_VREG(ASSIST_MBOX1_MASK, 1); +#if 1 /* def DEBUG_UCODE */ + WRITE_VREG(AV_SCRATCH_D, 0); +#endif + +#ifdef NV21 + SET_VREG_MASK(MDEC_PIC_DC_CTRL, 1 << 17); +#endif + +#ifdef PIC_DC_NEED_CLEAR + CLEAR_VREG_MASK(MDEC_PIC_DC_CTRL, 1 << 31); +#endif + +#ifdef AVSP_LONG_CABAC + if (firmware_sel == 0) { + WRITE_VREG(LONG_CABAC_DES_ADDR, es_write_addr_phy); + WRITE_VREG(LONG_CABAC_REQ, 0); + WRITE_VREG(LONG_CABAC_PIC_SIZE, 0); + WRITE_VREG(LONG_CABAC_SRC_ADDR, 0); + } +#endif +} + +#if 0 +#ifdef AVSP_LONG_CABAC +static unsigned char es_write_addr[MAX_CODED_FRAME_SIZE] __aligned(64); +#endif +#endif + +static void vavs_local_init(void) +{ + int i; + + vavs_ratio = vavs_amstream_dec_info.ratio; + + avi_flag = (unsigned long) vavs_amstream_dec_info.param; + + frame_width = frame_height = frame_dur = frame_prog = 0; + + throw_pb_flag = 1; + + total_frame = 0; + saved_resolution = 0; + next_pts = 0; + +#ifdef DEBUG_PTS + pts_hit = pts_missed = pts_i_hit = pts_i_missed = 0; +#endif + INIT_KFIFO(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); + } + for (i = 0; i < vf_buf_num; i++) + vfbuf_use[i] = 0; + + cur_vfpool = vfpool; + +} + +static int vavs_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); + states->buf_recycle_num = kfifo_len(&recycle_q); + spin_unlock_irqrestore(&lock, flags); + return 0; +} + +#ifdef CONFIG_AMLOGIC_POST_PROCESS_MANAGER +static void vavs_ppmgr_reset(void) +{ + vf_notify_receiver(PROVIDER_NAME, VFRAME_EVENT_PROVIDER_RESET, NULL); + + vavs_local_init(); + + pr_info("vavs: vf_ppmgr_reset\n"); +} +#endif + +static void vavs_local_reset(void) +{ + mutex_lock(&vavs_mutex); + recover_flag = 1; + pr_info("error, local reset\n"); + amvdec_stop(); + vf_notify_receiver(PROVIDER_NAME, VFRAME_EVENT_PROVIDER_RESET, NULL); + vavs_local_init(); + vavs_recover(); + amvdec_start(); + recover_flag = 0; +#if 0 + error_watchdog_count = 0; + + pr_info("pc %x stream buf wp %x rp %x level %x\n", + READ_VREG(MPC_E), + READ_VREG(VLD_MEM_VIFIFO_WP), + READ_VREG(VLD_MEM_VIFIFO_RP), + READ_VREG(VLD_MEM_VIFIFO_LEVEL)); +#endif + mutex_unlock(&vavs_mutex); +} + +static void vavs_put_timer_func(unsigned long arg) +{ + struct timer_list *timer = (struct timer_list *)arg; + +#ifndef HANDLE_AVS_IRQ + vavs_isr(); +#endif + + if (READ_VREG(AVS_SOS_COUNT)) { + if (!error_recovery_mode) { + if (debug_flag & AVS_DEBUG_OLD_ERROR_HANDLE) { + mutex_lock(&vavs_mutex); + pr_info("vavs fatal error reset !\n"); + amvdec_stop(); +#ifdef CONFIG_AMLOGIC_POST_PROCESS_MANAGER + vavs_ppmgr_reset(); +#else + vf_light_unreg_provider(&vavs_vf_prov); + vavs_local_init(); + vf_reg_provider(&vavs_vf_prov); +#endif + vavs_recover(); + amvdec_start(); + mutex_unlock(&vavs_mutex); + } else { + vavs_local_reset(); + } + } + } +#if 0 + if (long_cabac_busy == 0 && + error_watchdog_threshold > 0 && + kfifo_len(&display_q) == 0 && + READ_VREG(VLD_MEM_VIFIFO_LEVEL) > + error_watchdog_buf_threshold) { + pr_info("newq %d dispq %d recyq %d\r\n", + kfifo_len(&newframe_q), + kfifo_len(&display_q), + kfifo_len(&recycle_q)); + pr_info("pc %x stream buf wp %x rp %x level %x\n", + READ_VREG(MPC_E), + READ_VREG(VLD_MEM_VIFIFO_WP), + READ_VREG(VLD_MEM_VIFIFO_RP), + READ_VREG(VLD_MEM_VIFIFO_LEVEL)); + error_watchdog_count++; + if (error_watchdog_count >= error_watchdog_threshold) + vavs_local_reset(); + } else + error_watchdog_count = 0; +#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 (!kfifo_is_empty(&recycle_q) && (READ_VREG(AVS_BUFFERIN) == 0)) { + struct vframe_s *vf; + if (kfifo_get(&recycle_q, &vf)) { + if ((vf->index < vf_buf_num) && + (--vfbuf_use[vf->index] == 0)) { + WRITE_VREG(AVS_BUFFERIN, ~(1 << vf->index)); + vf->index = vf_buf_num; + } + kfifo_put(&newframe_q, + (const struct vframe_s *)vf); + } + + } + if (frame_dur > 0 && saved_resolution != + frame_width * frame_height * (96000 / frame_dur)) { + int fps = 96000 / frame_dur; + saved_resolution = frame_width * frame_height * fps; + if (firmware_sel == 0 && + (debug_flag & AVS_DEBUG_USE_FULL_SPEED)) { + vdec_source_changed(VFORMAT_AVS, + 4096, 2048, 60); + } else { + vdec_source_changed(VFORMAT_AVS, + frame_width, frame_height, fps); + } + + } + + timer->expires = jiffies + PUT_INTERVAL; + + add_timer(timer); +} + +#ifdef AVSP_LONG_CABAC + +static void long_cabac_do_work(struct work_struct *work) +{ + int status = 0; +#ifdef PERFORMANCE_DEBUG + pr_info("enter %s buf level (new %d, display %d, recycle %d)\r\n", + __func__, + kfifo_len(&newframe_q), + kfifo_len(&display_q), + kfifo_len(&recycle_q) + ); +#endif + mutex_lock(&vavs_mutex); + long_cabac_busy = 1; + while (READ_VREG(LONG_CABAC_REQ)) { + if (process_long_cabac() < 0) { + status = -1; + break; + } + } + long_cabac_busy = 0; + mutex_unlock(&vavs_mutex); +#ifdef PERFORMANCE_DEBUG + pr_info("exit %s buf level (new %d, display %d, recycle %d)\r\n", + __func__, + kfifo_len(&newframe_q), + kfifo_len(&display_q), + kfifo_len(&recycle_q) + ); +#endif + if (status < 0) { + pr_info("transcoding error, local reset\r\n"); + vavs_local_reset(); + } + +} +#endif + +#if 0 +#ifdef AVSP_LONG_CABAC +static void init_avsp_long_cabac_buf(void) +{ +#if 0 + es_write_addr_phy = (unsigned long)codec_mm_alloc_for_dma( + "vavs", + PAGE_ALIGN(MAX_CODED_FRAME_SIZE)/PAGE_SIZE, + 0, CODEC_MM_FLAGS_DMA_CPU); + es_write_addr_virt = codec_mm_phys_to_virt(es_write_addr_phy); + +#elif 0 + es_write_addr_virt = + (void *)dma_alloc_coherent(amports_get_dma_device(), + MAX_CODED_FRAME_SIZE, &es_write_addr_phy, + GFP_KERNEL); +#else + /*es_write_addr_virt = kmalloc(MAX_CODED_FRAME_SIZE, GFP_KERNEL); + es_write_addr_virt = (void *)__get_free_pages(GFP_KERNEL, + get_order(MAX_CODED_FRAME_SIZE)); + */ + es_write_addr_virt = &es_write_addr[0]; + if (es_write_addr_virt == NULL) { + pr_err("%s: failed to alloc es_write_addr_virt buffer\n", + __func__); + return; + } + + es_write_addr_phy = dma_map_single(amports_get_dma_device(), + es_write_addr_virt, + MAX_CODED_FRAME_SIZE, DMA_BIDIRECTIONAL); + if (dma_mapping_error(amports_get_dma_device(), + es_write_addr_phy)) { + pr_err("%s: failed to map es_write_addr_virt buffer\n", + __func__); + /*kfree(es_write_addr_virt);*/ + es_write_addr_virt = NULL; + return; + } +#endif + + +#ifdef BITSTREAM_READ_TMP_NO_CACHE + bitstream_read_tmp = + (void *)dma_alloc_coherent(amports_get_dma_device(), + SVA_STREAM_BUF_SIZE, &bitstream_read_tmp_phy, + GFP_KERNEL); + +#else + + bitstream_read_tmp = kmalloc(SVA_STREAM_BUF_SIZE, GFP_KERNEL); + /*bitstream_read_tmp = (void *)__get_free_pages(GFP_KERNEL, + get_order(MAX_CODED_FRAME_SIZE)); + */ + if (bitstream_read_tmp == NULL) { + pr_err("%s: failed to alloc bitstream_read_tmp buffer\n", + __func__); + return; + } + + bitstream_read_tmp_phy = dma_map_single(amports_get_dma_device(), + bitstream_read_tmp, + SVA_STREAM_BUF_SIZE, DMA_FROM_DEVICE); + if (dma_mapping_error(amports_get_dma_device(), + bitstream_read_tmp_phy)) { + pr_err("%s: failed to map rpm buffer\n", __func__); + kfree(bitstream_read_tmp); + bitstream_read_tmp = NULL; + return; + } +#endif +} +#endif +#endif + +static s32 vavs_init(void) +{ + int size = -1; + char *buf = vmalloc(0x1000 * 16); + if (IS_ERR_OR_NULL(buf)) + return -ENOMEM; + + pr_info("vavs_init\n"); + init_timer(&recycle_timer); + + stat |= STAT_TIMER_INIT; + + amvdec_enable(); + + vavs_local_init(); + + size = get_firmware_data(VIDEO_DEC_AVS, buf); + if (size < 0) { + pr_err("get firmware fail."); + vfree(buf); + return -1; + } + + if (amvdec_loadmc_ex(VFORMAT_AVS, NULL, buf) < 0) { + amvdec_disable(); + vfree(buf); + return -EBUSY; + } + + vfree(buf); + + stat |= STAT_MC_LOAD; + + /* enable AMRISC side protocol */ + vavs_prot_init(); + +#ifdef HANDLE_AVS_IRQ + if (vdec_request_irq(VDEC_IRQ_1, vavs_isr, + "vavs-irq", (void *)vavs_dec_id)) { + amvdec_disable(); + pr_info("vavs irq register error.\n"); + return -ENOENT; + } +#endif + + stat |= STAT_ISR_REG; + +#ifdef CONFIG_AMLOGIC_POST_PROCESS_MANAGER + vf_provider_init(&vavs_vf_prov, PROVIDER_NAME, &vavs_vf_provider, NULL); + vf_reg_provider(&vavs_vf_prov); + vf_notify_receiver(PROVIDER_NAME, VFRAME_EVENT_PROVIDER_START, NULL); +#else + vf_provider_init(&vavs_vf_prov, PROVIDER_NAME, &vavs_vf_provider, NULL); + vf_reg_provider(&vavs_vf_prov); +#endif + + vf_notify_receiver(PROVIDER_NAME, VFRAME_EVENT_PROVIDER_FR_HINT, + (void *)((unsigned long) + vavs_amstream_dec_info.rate)); + + stat |= STAT_VF_HOOK; + + recycle_timer.data = (ulong)(&recycle_timer); + recycle_timer.function = vavs_put_timer_func; + recycle_timer.expires = jiffies + PUT_INTERVAL; + + add_timer(&recycle_timer); + + stat |= STAT_TIMER_ARM; + +#ifdef AVSP_LONG_CABAC + if (firmware_sel == 0) + INIT_WORK(&long_cabac_wd_work, long_cabac_do_work); +#endif + + amvdec_start(); + + stat |= STAT_VDEC_RUN; + + return 0; +} + +static int amvdec_avs_probe(struct platform_device *pdev) +{ + struct vdec_s *pdata = *(struct vdec_s **)pdev->dev.platform_data; + + if (pdata == NULL) { + pr_info("amvdec_avs memory resource undefined.\n"); + return -EFAULT; + } + if (get_cpu_type() >= MESON_CPU_MAJOR_ID_GXM) + firmware_sel = 1; + + if (firmware_sel == 1) { + vf_buf_num = 4; + canvas_base = 0; + canvas_num = 3; + } else { + /*if(vf_buf_num <= 4) + canvas_base = 0; + else */ + canvas_base = 128; + canvas_num = 2; /*NV21*/ + } + +#ifdef AVSP_LONG_CABAC + buf_start = pdata->mem_start; + buf_size = pdata->mem_end - pdata->mem_start + 1 + - (MAX_CODED_FRAME_SIZE * 2) + - LOCAL_HEAP_SIZE; + avsp_heap_adr = codec_mm_phys_to_virt( + pdata->mem_start + buf_size); +#else + buf_start = pdata->mem_start; + buf_size = pdata->mem_end - pdata->mem_start + 1; +#endif + + if (buf_start > ORI_BUFFER_START_ADDR) + buf_offset = buf_start - ORI_BUFFER_START_ADDR; + else + buf_offset = buf_start; + + if (pdata->sys_info) + vavs_amstream_dec_info = *pdata->sys_info; + + pr_info("%s (%d,%d) %d\n", __func__, vavs_amstream_dec_info.width, + vavs_amstream_dec_info.height, vavs_amstream_dec_info.rate); + + pdata->dec_status = vavs_dec_status; + + if (vavs_init() < 0) { + pr_info("amvdec_avs init failed.\n"); + + return -ENODEV; + } + + return 0; +} + +static int amvdec_avs_remove(struct platform_device *pdev) +{ + if (stat & STAT_VDEC_RUN) { + amvdec_stop(); + stat &= ~STAT_VDEC_RUN; + } + + if (stat & STAT_ISR_REG) { + vdec_free_irq(VDEC_IRQ_1, (void *)vavs_dec_id); + stat &= ~STAT_ISR_REG; + } + + if (stat & STAT_TIMER_ARM) { + del_timer_sync(&recycle_timer); + stat &= ~STAT_TIMER_ARM; + } +#ifdef AVSP_LONG_CABAC + if (firmware_sel == 0) { + mutex_lock(&vavs_mutex); + cancel_work_sync(&long_cabac_wd_work); + mutex_unlock(&vavs_mutex); + + if (es_write_addr_virt) { +#if 0 + codec_mm_free_for_dma("vavs", es_write_addr_phy); +#else + dma_unmap_single(amports_get_dma_device(), + es_write_addr_phy, + MAX_CODED_FRAME_SIZE, DMA_FROM_DEVICE); + /*kfree(es_write_addr_virt);*/ + es_write_addr_virt = NULL; +#endif + } + +#ifdef BITSTREAM_READ_TMP_NO_CACHE + if (bitstream_read_tmp) { + dma_free_coherent(amports_get_dma_device(), + SVA_STREAM_BUF_SIZE, bitstream_read_tmp, + bitstream_read_tmp_phy); + bitstream_read_tmp = NULL; + } +#else + if (bitstream_read_tmp) { + dma_unmap_single(amports_get_dma_device(), + bitstream_read_tmp_phy, + SVA_STREAM_BUF_SIZE, DMA_FROM_DEVICE); + kfree(bitstream_read_tmp); + bitstream_read_tmp = NULL; + } +#endif + } +#endif + if (stat & STAT_VF_HOOK) { + vf_notify_receiver(PROVIDER_NAME, + VFRAME_EVENT_PROVIDER_FR_END_HINT, NULL); + + vf_unreg_provider(&vavs_vf_prov); + stat &= ~STAT_VF_HOOK; + } + + amvdec_disable(); + + pic_type = 0; +#ifdef DEBUG_PTS + pr_info("pts hit %d, pts missed %d, i hit %d, missed %d\n", pts_hit, + pts_missed, pts_i_hit, pts_i_missed); + pr_info("total frame %d, avi_flag %d, rate %d\n", total_frame, avi_flag, + vavs_amstream_dec_info.rate); +#endif + + return 0; +} + +/****************************************/ + +static struct platform_driver amvdec_avs_driver = { + .probe = amvdec_avs_probe, + .remove = amvdec_avs_remove, + .driver = { + .name = DRIVER_NAME, + } +}; + +static struct codec_profile_t amvdec_avs_profile = { + .name = "avs", + .profile = "" +}; + +static int __init amvdec_avs_driver_init_module(void) +{ + pr_debug("amvdec_avs module init\n"); + + if (platform_driver_register(&amvdec_avs_driver)) { + pr_info("failed to register amvdec_avs driver\n"); + return -ENODEV; + } + + if (get_cpu_type() >= MESON_CPU_MAJOR_ID_GXBB) + amvdec_avs_profile.profile = "avs+"; + + vcodec_profile_register(&amvdec_avs_profile); + + return 0; +} + +static void __exit amvdec_avs_driver_remove_module(void) +{ + pr_debug("amvdec_avs module remove.\n"); + + platform_driver_unregister(&amvdec_avs_driver); +} + +/****************************************/ + +module_param(stat, uint, 0664); +MODULE_PARM_DESC(stat, "\n amvdec_avs stat\n"); + +/****************************************** +module_param(run_flag, uint, 0664); +MODULE_PARM_DESC(run_flag, "\n run_flag\n"); + +module_param(step_flag, uint, 0664); +MODULE_PARM_DESC(step_flag, "\n step_flag\n"); +*******************************************/ + +module_param(debug_flag, uint, 0664); +MODULE_PARM_DESC(debug_flag, "\n debug_flag\n"); + +module_param(error_recovery_mode, uint, 0664); +MODULE_PARM_DESC(error_recovery_mode, "\n error_recovery_mode\n"); + +/****************************************** +module_param(error_watchdog_threshold, uint, 0664); +MODULE_PARM_DESC(error_watchdog_threshold, "\n error_watchdog_threshold\n"); + +module_param(error_watchdog_buf_threshold, uint, 0664); +MODULE_PARM_DESC(error_watchdog_buf_threshold, + "\n error_watchdog_buf_threshold\n"); +*******************************************/ + +module_param(pic_type, uint, 0444); +MODULE_PARM_DESC(pic_type, "\n amdec_vas picture type\n"); + +module_param(radr, uint, 0664); +MODULE_PARM_DESC(radr, "\nradr\n"); + +module_param(rval, uint, 0664); +MODULE_PARM_DESC(rval, "\nrval\n"); + +module_param(vf_buf_num, uint, 0664); +MODULE_PARM_DESC(vf_buf_num, "\nvf_buf_num\n"); + +module_param(vf_buf_num_used, uint, 0664); +MODULE_PARM_DESC(vf_buf_num_used, "\nvf_buf_num_used\n"); + +module_param(canvas_base, uint, 0664); +MODULE_PARM_DESC(canvas_base, "\ncanvas_base\n"); + +module_param(work_buf_size, uint, 0664); +MODULE_PARM_DESC(work_buf_size, "\nwork_buf_size\n"); + +module_param(firmware_sel, uint, 0664); +MODULE_PARM_DESC(firmware_sel, "\firmware_sel\n"); + + +module_init(amvdec_avs_driver_init_module); +module_exit(amvdec_avs_driver_remove_module); + +MODULE_DESCRIPTION("AMLOGIC AVS Video Decoder Driver"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Qi Wang <qi.wang@amlogic.com>"); diff --git a/drivers/frame_provider/decoder/avs/avs.h b/drivers/frame_provider/decoder/avs/avs.h new file mode 100644 index 0000000..d415645 --- a/dev/null +++ b/drivers/frame_provider/decoder/avs/avs.h @@ -0,0 +1,70 @@ +#ifndef AVS_H_ +#define AVS_H_ + +#define AVSP_LONG_CABAC +/*#define BITSTREAM_READ_TMP_NO_CACHE*/ + +#ifdef AVSP_LONG_CABAC +#define MAX_CODED_FRAME_SIZE 1500000 /*!< bytes for one frame*/ +#define LOCAL_HEAP_SIZE (1024*1024*10) +/* +#define MAX_CODED_FRAME_SIZE 240000 +#define MAX_CODED_FRAME_SIZE 700000 +*/ +#define SVA_STREAM_BUF_SIZE 1024 + +extern void *es_write_addr_virt; +extern dma_addr_t es_write_addr_phy; + +extern void *bitstream_read_tmp; +extern dma_addr_t bitstream_read_tmp_phy; +extern void *avsp_heap_adr; + +int avs_get_debug_flag(void); + +int process_long_cabac(void); + +/* bit [6] - skip_mode_flag + bit [5:4] - picture_type + bit [3] - picture_structure (0-Field, 1-Frame) + bit [2] - fixed_picture_qp + bit [1] - progressive_sequence + bit [0] - active +*/ +#define LONG_CABAC_REQ AV_SCRATCH_K +#define LONG_CABAC_SRC_ADDR AV_SCRATCH_H +#define LONG_CABAC_DES_ADDR AV_SCRATCH_I +/* bit[31:16] - vertical_size + bit[15:0] - horizontal_size +*/ +#define LONG_CABAC_PIC_SIZE AV_SCRATCH_J + +#endif + +/* +#define PERFORMANCE_DEBUG +#define DUMP_DEBUG +*/ +#define AVS_DEBUG_PRINT 0x01 +#define AVS_DEBUG_UCODE 0x02 +#define AVS_DEBUG_OLD_ERROR_HANDLE 0x10 +#define AVS_DEBUG_USE_FULL_SPEED 0x80 +#define AEC_DUMP 0x100 +#define STREAM_INFO_DUMP 0x200 +#define SLICE_INFO_DUMP 0x400 +#define MB_INFO_DUMP 0x800 +#define MB_NUM_DUMP 0x1000 +#define BLOCK_NUM_DUMP 0x2000 +#define COEFF_DUMP 0x4000 +#define ES_DUMP 0x8000 +#define DQUANT_DUMP 0x10000 +#define STREAM_INFO_DUMP_MORE 0x20000 +#define STREAM_INFO_DUMP_MORE2 0x40000 + +extern void *es_write_addr_virt; +extern void *bitstream_read_tmp; +extern dma_addr_t bitstream_read_tmp_phy; +int read_bitstream(unsigned char *Buf, int size); +int u_v(int LenInBits, char *tracestring); + +#endif diff --git a/drivers/frame_provider/decoder/avs/avsp_trans.c b/drivers/frame_provider/decoder/avs/avsp_trans.c new file mode 100644 index 0000000..3c7f3ab --- a/dev/null +++ b/drivers/frame_provider/decoder/avs/avsp_trans.c @@ -0,0 +1,4944 @@ +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/interrupt.h> +#include <linux/timer.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/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/utils/vformat.h> +#include <linux/dma-mapping.h> +#include <linux/amlogic/media/codec_mm/codec_mm.h> +#include <linux/slab.h> +/* #include <mach/am_regs.h> */ +#include <linux/module.h> +#include <linux/amlogic/media/utils/vdec_reg.h> +#include "../../../stream_input/parser/streambuf_reg.h" +#include "../utils/amvdec.h" +#include <linux/amlogic/media/registers/register.h> +#include "../../../stream_input/amports/amports_priv.h" + +#include "avs.h" +#ifdef AVSP_LONG_CABAC + +#define DECODING_SANITY_CHECK + +#define TRACE 0 +#define LIWR_FIX 0 +#define pow2(a, b) (1<<b) +#define io_printf pr_info + +static unsigned char *local_heap_adr; +static int local_heap_size; +static int local_heap_pos; +static int transcoding_error_flag; + +unsigned char *local_alloc(int num, int size) +{ + unsigned char *ret_buf = NULL; + int alloc_size = num * size; + if ((local_heap_pos + alloc_size) <= local_heap_size) { + ret_buf = local_heap_adr + local_heap_pos; + local_heap_pos += alloc_size; + } else { + pr_info( + "!!!local_alloc(%d) error, local_heap (size %d) is not enough\r\n", + alloc_size, local_heap_size); + } + return ret_buf; +} + +int local_heap_init(int size) +{ + /*local_heap_adr = &local_heap[0];*/ + local_heap_adr = (unsigned char *)(avsp_heap_adr + + MAX_CODED_FRAME_SIZE); + memset(local_heap_adr, 0, LOCAL_HEAP_SIZE); + + local_heap_size = LOCAL_HEAP_SIZE; + local_heap_pos = 0; + return 0; +} + +void local_heap_uninit(void) +{ + local_heap_adr = NULL; + local_heap_size = 0; + local_heap_pos = 0; +} + +#define CODE2D_ESCAPE_SYMBOL 59 + +const int vlc_golomb_order[3][7][2] = + +{{{2, 9}, {2, 9}, {2, 9}, {2, 9}, {2, 9}, {2, 9}, {2, 9}, }, {{3, 9}, {2, 9}, { + 2, 9}, {2, 9}, {2, 9}, {2, 9}, {2, 9}, }, {{2, 9}, {0, 9}, + {1, 9}, {1, 9}, {0, 9}, {-1, -1}, {-1, -1}, }, }; + +const int MaxRun[3][7] = {{22, 14, 9, 6, 4, 2, 1}, {25, 18, 13, 9, 6, 4, 3}, { + 24, 19, 10, 7, 4, -1, -1} }; + +const int refabslevel[19][26] = {{4, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, -1, -1, -1}, {7, 4, 4, 3, 3, 3, 3, 3, 2, + 2, 2, 2, 2, 2, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, { + 10, 6, 4, 4, 3, 3, 3, 2, 2, 2, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1}, {13, 7, 5, 4, 3, 2, 2, -1, -1, + -1 - 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1}, {18, 8, 4, 2, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {22, 7, 3, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1}, {27, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {4, + 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2}, {5, 4, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, -1, -1, -1, -1, -1, -1, -1}, {7, 5, 4, 4, 3, 3, 3, 2, 2, + 2, 2, 2, 2, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {10, 6, 5, 4, 3, 3, 2, 2, 2, 2, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1}, {13, 7, 5, 4, + 3, 2, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1}, {17, 8, 4, + 3, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, { + 22, 6, 3, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1}, {5, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, -1}, {6, 4, 3, + 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, -1, -1, -1, -1, -1, -1}, {10, 6, 4, 4, 3, 3, + 2, 2, 2, 2, 2, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1}, {14, 7, 4, 3, 3, 2, + 2, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1}, {20, 7, 3, 2, + 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1} }; + +static const int incvlc_intra[7] = {0, 1, 2, 4, 7, 10, 3000}; +static const int incvlc_chroma[5] = {0, 1, 2, 4, 3000}; + +const int AVS_2DVLC_INTRA[7][26][27] = {{{0, 22, 38, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1}, {2, 32, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {4, 44, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1}, {6, 50, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1}, {8, 54, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {10, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1}, {12, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1}, {14, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1}, {16, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {18, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1}, {20, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1}, {24, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1}, {26, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {28, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1}, {30, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1}, {34, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1}, {36, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {40, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1}, {42, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1}, {46, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1}, {48, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {52, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1}, {56, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1}, {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1}, {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {-1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1}, }, {{8, 0, 4, 15, 27, 41, + 55, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1}, {-1, 2, 17, 35, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {-1, 6, 25, 53, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, { + -1, 9, 33, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1}, {-1, 11, 39, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1}, {-1, 13, 45, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, { + -1, 19, 49, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1}, {-1, 21, 51, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1}, {-1, 23, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, { + -1, 29, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1}, {-1, 31, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1}, {-1, 37, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, { + -1, 43, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1}, {-1, 47, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1}, {-1, 57, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1}, {-1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1}, {-1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1}, {-1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1}, {-1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1}, {-1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1}, {-1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1}, {-1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1}, }, {{8, 0, 2, 6, + 13, 17, 27, 35, 45, 55, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1}, {-1, 4, 11, 21, 33, 49, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1}, {-1, 9, 23, 37, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {-1, 15, + 29, 51, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1}, {-1, 19, 39, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1}, {-1, 25, 43, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1}, {-1, 31, 53, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {-1, 41, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1}, {-1, 47, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1}, {-1, 57, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1}, {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {-1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1}, {-1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1}, {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1}, {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {-1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1}, {-1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1}, {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1}, {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {-1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1}, {-1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1}, {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1}, {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {-1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1}, {-1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1}, {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1}, }, {{8, 0, 2, 4, 9, 11, 17, 21, 25, 33, 39, 45, 55, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {-1, 6, 13, 19, + 29, 35, 47, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1}, {-1, 15, 27, 41, 57, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1}, {-1, 23, 37, 53, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, { + -1, 31, 51, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {-1, 43, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1}, {-1, 49, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1}, {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {-1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1}, {-1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1}, {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {-1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1}, {-1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1}, {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {-1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1}, {-1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1}, {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {-1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1}, {-1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1}, {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {-1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1}, }, {{6, 0, 2, 4, 7, 9, 11, 15, 17, + 21, 23, 29, 33, 35, 43, 47, 49, 57, -1, -1, -1, -1, -1, -1, -1, + -1, -1}, {-1, 13, 19, 27, 31, 37, 45, 55, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {-1, + 25, 41, 51, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {-1, 39, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1}, {-1, 53, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1}, {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {-1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {-1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1}, {-1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1}, {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {-1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {-1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1}, {-1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1}, {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {-1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {-1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1}, {-1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1}, {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {-1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {-1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1}, {-1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1}, {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {-1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {-1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1}, {-1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1}, {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, }, {{0, + 1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 23, 25, 27, 31, 33, 37, 41, + 45, 49, 51, 55, -1, -1, -1, -1, -1}, {-1, 21, 29, 35, 43, 47, + 53, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1}, {-1, 39, 57, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1}, {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {-1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1}, {-1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1}, {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1}, {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {-1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1}, {-1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1}, {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1}, {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {-1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1}, {-1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1}, {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1}, {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {-1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1}, {-1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1}, {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1}, {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {-1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1}, {-1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1}, {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1}, {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {-1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1}, {-1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1}, }, {{0, 1, 3, 5, 7, 9, 11, 13, 15, 17, 19, + 21, 23, 25, 27, 29, 31, 35, 37, 39, 41, 43, 47, 49, 51, 53, 57}, + {-1, 33, 45, 55, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1}, {-1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1}, {-1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1}, {-1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1}, {-1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1}, {-1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1}, {-1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1}, {-1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1}, {-1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1}, {-1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1}, {-1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1}, {-1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1}, {-1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1}, {-1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1}, {-1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1}, {-1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1}, {-1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1} } }; + +const int AVS_2DVLC_CHROMA[5][26][27] = {{{0, 14, 32, 56, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1}, {2, 48, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {4, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1}, {6, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1}, {8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {10, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {12, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1}, {16, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1}, {18, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {20, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {22, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1}, {24, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1}, {26, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {28, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {30, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1}, {34, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1}, {36, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {38, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {40, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1}, {42, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1}, {44, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {46, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {50, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1}, {52, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1}, {54, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {-1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, }, {{0, 1, 5, 15, 29, + 43, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1}, {-1, 3, 21, 45, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1}, {-1, 7, 37, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {-1, 9, 41, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1}, {-1, 11, 53, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1}, {-1, 13, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, { + -1, 17, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {-1, 19, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1}, {-1, 23, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1}, {-1, 25, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, { + -1, 27, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {-1, 31, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1}, {-1, 33, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1}, {-1, 35, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, { + -1, 39, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {-1, 47, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1}, {-1, 49, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1}, {-1, 51, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, { + -1, 55, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {-1, 57, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1}, {-1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1}, {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {-1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1}, {-1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1}, {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, }, + {{2, 0, 3, 7, 11, 17, 27, 33, 47, 53, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, { + -1, 5, 13, 21, 37, 55, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1}, {-1, 9, 23, 41, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1}, {-1, 15, 31, 57, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {-1, + 19, 43, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1}, {-1, 25, 45, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1}, {-1, 29, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {-1, + 35, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1}, {-1, 39, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1}, {-1, 49, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {-1, + 51, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1}, {-1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1}, {-1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {-1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1}, {-1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1}, {-1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {-1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1}, {-1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1}, {-1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {-1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1}, {-1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1}, {-1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {-1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1}, {-1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1}, {-1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {-1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1}, }, {{0, 1, 3, 5, 7, 11, 15, 19, 23, 29, + 35, 43, 47, 53, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1}, {-1, 9, 13, 21, 31, 39, 51, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1}, {-1, 17, 27, + 37, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {-1, 25, 41, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1}, { + -1, 33, 55, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1}, {-1, 45, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1}, { + -1, 49, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1}, {-1, 57, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1}, { + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1}, {-1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1}, { + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1}, {-1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1}, { + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1}, {-1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1}, { + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1}, {-1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1}, { + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1}, {-1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1}, { + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1}, {-1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1}, { + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1}, {-1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1}, { + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1}, {-1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1}, { + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1}, {-1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1}, }, + {{0, 1, 3, 5, 7, 9, 11, 13, 15, 19, 21, 23, 27, 29, 33, 37, 41, + 43, 51, 55, -1, -1, -1, -1, -1, -1, -1}, {-1, + 17, 25, 31, 39, 45, 53, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1}, {-1, 35, 49, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1}, {-1, 47, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {-1, + 57, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1}, {-1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1}, {-1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {-1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1}, {-1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1}, {-1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {-1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1}, {-1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1}, {-1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {-1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1}, {-1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1}, {-1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {-1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1}, {-1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1}, {-1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {-1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1}, {-1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1}, {-1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {-1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1}, {-1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1}, {-1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {-1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1}, } }; + +const int UE[64][2] = {{1, 1}, {2, 3}, {3, 3}, {4, 5}, {5, 5}, {6, 5}, {7, 5}, { + 8, 7}, {9, 7}, {10, 7}, {11, 7}, {12, 7}, {13, 7}, {14, 7}, {15, + 7}, {16, 9}, {17, 9}, {18, 9}, {19, 9}, {20, 9}, {21, 9}, + {22, 9}, {23, 9}, {24, 9}, {25, 9}, {26, 9}, {27, 9}, {28, 9}, { + 29, 9}, {30, 9}, {31, 9}, {32, 11}, {33, 11}, { + 34, 11}, {35, 11}, {36, 11}, {37, 11}, {38, 11}, + {39, 11}, {40, 11}, {41, 11}, {42, 11}, {43, 11}, {44, 11}, {45, + 11}, {46, 11}, {47, 11}, {48, 11}, {49, 11}, { + 50, 11}, {51, 11}, {52, 11}, {53, 11}, {54, 11}, + {55, 11}, {56, 11}, {57, 11}, {58, 11}, {59, 11}, {60, 11}, {61, + 11}, {62, 11}, {63, 11}, {64, 13} }; + +unsigned int src_start; +unsigned int des_start; + +#ifdef AVSP_LONG_CABAC + +unsigned char *es_buf; +int es_buf_ptr; +int es_write_addr; +#else +FILE *f_es; +#endif +int es_ptr; +unsigned int es_res; +int es_res_ptr; +unsigned int previous_es; + +void init_es(void) +{ + +#ifdef AVSP_LONG_CABAC + es_write_addr = des_start; + es_buf[0] = 0x00; + es_buf[1] = 0x00; + es_buf[2] = 0x01; + es_buf_ptr = 3; + es_ptr = 3; +#else + f_es = fopen("es.out", "wb"); + if (f_es == NULL) + io_printf(" ERROR : Can not open es.out for write\n"); + putc(0x00, f_es); + putc(0x00, f_es); + putc(0x01, f_es); + + es_ptr = 3; +#endif + es_res = 0; + es_res_ptr = 0; + previous_es = 0xff; + +} + +void push_es(int value, int num) +{ + unsigned char wr_es_data; + int push_num; + int push_value; + +#ifdef DUMP_DEBUG + if (avs_get_debug_flag() & ES_DUMP) + io_printf(" push_es : value : 0x%x, num : %d\n", value, num); +#endif + while (num > 0) { + if (num >= 8) + push_num = 8; + else + push_num = num; + + num = num - push_num; + push_value = (value >> num); + + es_res = (es_res << push_num) | push_value; + es_res_ptr = es_res_ptr + push_num; + +#ifdef DUMP_DEBUG + if (avs_get_debug_flag() & ES_DUMP) + io_printf(" #### es_res : 0x%X, es_res_ptr : %d\n", + es_res, es_res_ptr); +#endif + + while (es_res_ptr >= 8) { + es_res_ptr = es_res_ptr & 7; + wr_es_data = (es_res >> es_res_ptr) & 0xff; + if ((previous_es == 0) & (wr_es_data < 4)) { + io_printf( + " Insert 2'b10 for emu at position : %d\n", + es_ptr); + + es_res_ptr = es_res_ptr + 2; + wr_es_data = 2; + } +#ifdef AVSP_LONG_CABAC +#ifdef DUMP_DEBUG + if (avs_get_debug_flag() & ES_DUMP) + pr_info("es_buf[%d] = 0x%02x\r\n", + es_buf_ptr, wr_es_data); +#endif + es_buf[es_buf_ptr++] = wr_es_data; +#else + putc(wr_es_data, f_es); +#endif + es_ptr++; + previous_es = ((previous_es << 8) | wr_es_data) + & 0xffff; + } + + } +} + +#define MIN_QP 0 +#define MAX_QP 63 + +#ifdef BLOCK_SIZE +#undef BLOCK_SIZE +#endif + +#define BLOCK_SIZE 4 +#define B8_SIZE 8 +#define MB_BLOCK_SIZE 16 + +#define BLOCK_MULTIPLE (MB_BLOCK_SIZE/(BLOCK_SIZE*2)) + +#define DECODE_COPY_MB 0 +#define DECODE_MB 1 + +#define NO_INTRA_PMODE 5 +#define INTRA_PMODE_4x4 10 +#define NO_INTRA_PMODE_4x4 19 +/* 8x8 intra prediction modes */ +#define VERT_PRED 0 +#define HOR_PRED 1 +#define DC_PRED 2 +#define DOWN_LEFT_PRED 3 +#define DOWN_RIGHT_PRED 4 + +#define VERT_PRED_4x4 0 +#define HOR_PRED_4x4 1 +#define DC_PRED_4x4 2 +#define DOWN_LEFT_PRED_4x4 3 +#define DOWN_RIGHT_PRED_4x4 4 + +#define HOR_DOWN_PRED_4x4 5 +#define VERT_LEFT_PRED_4x4 6 +#define HOR_UP_PRED_4x4 7 +#define VERT_RIGHT_PRED_4x4 8 + +#define DC_PRED_8 0 +#define HOR_PRED_8 1 +#define VERT_PRED_8 2 +#define PLANE_8 3 + +#define LUMA_16DC 0 +#define LUMA_16AC 1 +#define LUMA_8x8 2 +#define LUMA_8x4 3 +#define LUMA_4x8 4 +#define LUMA_4x4 5 +#define CHROMA_DC 6 +#define CHROMA_AC 7 +#define NUM_BLOCK_TYPES 8 + +#define I_PICTURE_START_CODE 0xB3 +#define PB_PICTURE_START_CODE 0xB6 +#define SLICE_START_CODE_MIN 0x00 +#define SLICE_START_CODE_MAX 0xAF +#define USER_DATA_START_CODE 0xB2 +#define SEQUENCE_HEADER_CODE 0xB0 +#define EXTENSION_START_CODE 0xB5 +#define SEQUENCE_END_CODE 0xB1 +#define VIDEO_EDIT_CODE 0xB7 + +#define EOS 1 +#define SOP 2 +#define SOS 3 +#define P8x8 8 +#define I8MB 9 +#define I4MB 10 +#define IBLOCK 11 +#define SI4MB 12 +#define MAXMODE 13 + +#define IS_INTRA(MB) ((MB)->mb_type == I8MB || (MB)->mb_type == I4MB) +#define IS_NEWINTRA(MB) ((MB)->mb_type == I4MB) +#define IS_OLDINTRA(MB) ((MB)->mb_type == I8MB) +#define IS_INTER(MB) ((MB)->mb_type != I8MB && (MB)->mb_type != I4MB) +#define IS_INTERMV(MB) ((MB)->mb_type != I8MB && (MB)->mb_type != I4MB\ + && (MB)->mb_type != 0) + +#define IS_DIRECT(MB) ((MB)->mb_type == 0 && (img->type == B_IMG)) +#define IS_COPY(MB) ((MB)->mb_type == 0 && (img->type == P_IMG)) +#define IS_P8x8(MB) ((MB)->mb_type == P8x8) + +#define P_IMG 0 +#define B_IMG 1 +#define I_IMG 2 + +#define FIELD 0 +#define FRAME 1 + +#define SE_CABP 21 +struct decoding_environment_s { + unsigned int dbuffer; + int dbits_to_go; + unsigned char *dcodestrm; + int *dcodestrm_len; +}; + +struct bi_context_type_s { + unsigned char MPS; + unsigned int LG_PMPS; + unsigned char cycno; +}; + + +/********************************************************************** + * C O N T E X T S F O R R M S Y N T A X E L E M E N T S + ********************************************************************** + */ + +#define NUM_MB_TYPE_CTX 11 +#define NUM_B8_TYPE_CTX 9 +#define NUM_MV_RES_CTX 10 +#define NUM_REF_NO_CTX 6 +#define NUM_DELTA_QP_CTX 4 +#define NUM_MB_AFF_CTX 4 + +struct motion_info_contexts_s { + struct bi_context_type_s mb_type_contexts[4][NUM_MB_TYPE_CTX]; + struct bi_context_type_s b8_type_contexts[2][NUM_B8_TYPE_CTX]; + struct bi_context_type_s mv_res_contexts[2][NUM_MV_RES_CTX]; + struct bi_context_type_s ref_no_contexts[2][NUM_REF_NO_CTX]; + struct bi_context_type_s delta_qp_contexts[NUM_DELTA_QP_CTX]; + struct bi_context_type_s mb_aff_contexts[NUM_MB_AFF_CTX]; +#ifdef TEST_WEIGHTING_AEC +struct bi_context_type_s mb_weighting_pred; +#endif +}; + +#define NUM_IPR_CTX 2 +#define NUM_CIPR_CTX 4 +#define NUM_CBP_CTX 4 +#define NUM_BCBP_CTX 4 +#define NUM_MAP_CTX 16 +#define NUM_LAST_CTX 16 + +#define NUM_ONE_CTX 5 +#define NUM_ABS_CTX 5 + +struct texture_info_contexts { + struct bi_context_type_s ipr_contexts[NUM_IPR_CTX]; + struct bi_context_type_s cipr_contexts[NUM_CIPR_CTX]; + struct bi_context_type_s cbp_contexts[3][NUM_CBP_CTX]; + struct bi_context_type_s bcbp_contexts[NUM_BLOCK_TYPES][NUM_BCBP_CTX]; + struct bi_context_type_s one_contexts[NUM_BLOCK_TYPES][NUM_ONE_CTX]; + struct bi_context_type_s abs_contexts[NUM_BLOCK_TYPES][NUM_ABS_CTX]; + struct bi_context_type_s fld_map_contexts[NUM_BLOCK_TYPES][NUM_MAP_CTX]; + struct bi_context_type_s fld_last_contexts + [NUM_BLOCK_TYPES][NUM_LAST_CTX]; + struct bi_context_type_s map_contexts[NUM_BLOCK_TYPES][NUM_MAP_CTX]; + struct bi_context_type_s last_contexts[NUM_BLOCK_TYPES][NUM_LAST_CTX]; +}; +struct img_par; + +struct syntaxelement { + int type; + int value1; + int value2; + int len; + int inf; + unsigned int bitpattern; + int context; + int k; + int golomb_grad; + int golomb_maxlevels; +#if TRACE +#define TRACESTRING_SIZE 100 + char tracestring[TRACESTRING_SIZE]; +#endif + + void (*mapping)(int len, int info, int *value1, int *value2); + + void (*reading)(struct syntaxelement *, struct img_par *, + struct decoding_environment_s *); + +}; + +struct bitstream_s { + + int read_len; + int code_len; + + int frame_bitoffset; + int bitstream_length; + + unsigned char *stream_buffer; +}; + +struct datapartition { + + struct bitstream_s *bitstream; + struct decoding_environment_s de_aec; + + int (*read_syntax_element)(struct syntaxelement *, struct img_par *, + struct datapartition *); +/*!< virtual function; + actual method depends on chosen data partition and + entropy coding method */ +}; + +struct slice_s { + int picture_id; + int qp; + int picture_type; + int start_mb_nr; + int max_part_nr; + int num_mb; + + struct datapartition *part_arr; + struct motion_info_contexts_s *mot_ctx; + struct texture_info_contexts *tex_ctx; + int field_ctx[3][2]; +}; + +struct img_par { + int number; + int current_mb_nr; + int max_mb_nr; + int current_slice_nr; + int tr; + int qp; + int type; + + int typeb; + + int width; + int height; + int width_cr; + int height_cr; + int source_bitdepth; + int mb_y; + int mb_x; + int block_y; + int pix_y; + int pix_x; + int pix_c_y; + int block_x; + int pix_c_x; + + int ***mv; + int mpr[16][16]; + + int m7[16][16]; + int m8[/*2*/4][8][8]; + int cof[4][/*6*/8][4][4]; + int cofu[4]; + int **ipredmode; + int quad[256]; + int cod_counter; + + int ***dfmv; + int ***dbmv; + int **fw_reffrarr; + int **bw_reffrarr; + + int ***mv_frm; + int **fw_reffrarr_frm; + int **bw_reffrarr_frm; + int imgtr_next_p; + int imgtr_last_p; + int tr_frm; + int tr_fld; + int imgtr_last_prev_p; + + int no_forward_reference; + int seq_header_indicate; + int b_discard_flag; + + int ***fw_mv; + int ***bw_mv; + int subblock_x; + int subblock_y; + + int buf_cycle; + + int direct_type; + + int ***mv_top; + int ***mv_bot; + int **fw_reffrarr_top; + int **bw_reffrarr_top; + int **fw_reffrarr_bot; + int **bw_reffrarr_bot; + + int **ipredmode_top; + int **ipredmode_bot; + int ***fw_mv_top; + int ***fw_mv_bot; + int ***bw_mv_top; + int ***bw_mv_bot; + int ***dfmv_top; + int ***dbmv_top; + int ***dfmv_bot; + int ***dbm_bot; + + int toppoc; + int bottompoc; + int framepoc; + unsigned int frame_num; + + unsigned int pic_distance; + int delta_pic_order_cnt_bottom; + + signed int pic_distance_msb; + unsigned int prev_pic_distance_lsb; + signed int curr_pic_distance_msb; + unsigned int this_poc; + + int pic_width_inmbs; + int pic_height_inmbs; + int pic_size_inmbs; + + int block8_x, block8_y; + int structure; + int pn; + int buf_used; + int buf_size; + int picture_structure; + int advanced_pred_mode_disable; + int types; + int current_mb_nr_fld; + + int p_field_enhanced; + int b_field_enhanced; + + int slice_weighting_flag; + int lum_scale[4]; + int lum_shift[4]; + int chroma_scale[4]; + int chroma_shift[4]; + int mb_weighting_flag; + int weighting_prediction; + int mpr_weight[16][16]; + int top_bot; + int bframe_number; + + int auto_crop_right; + int auto_crop_bottom; + + struct slice_s *current_slice; + int is_v_block; + int is_intra_block; + + int new_seq_header_flag; + int new_sequence_flag; + int last_pic_bbv_delay; + + int sequence_end_flag; + int is_top_field; + + int abt_flag; + int qp_shift; + +#ifdef EIGHTH +int eighth_subpixel_flag; +int subpixel_precision; +int unit_length; +int subpixel_mask; + +int max_mvd; +int min_mvd; +#endif + +}; + +struct macroblock { + int qp; + int slice_nr; + int delta_quant; + struct macroblock *mb_available[3][3]; + /*!< pointer to neighboring MBs in a 3x3 window of current MB, + which is located at [1][1] + NULL pointer identifies neighboring MBs which are unavailable */ + + int mb_type; + int mvd[2][BLOCK_MULTIPLE][BLOCK_MULTIPLE][2]; + int cbp, cbp_blk, cbp01; + unsigned long cbp_bits; + + int b8mode[4]; + int b8pdir[4]; + int mb_type_2; + int c_ipred_mode_2; + int dct_mode; + + int c_ipred_mode; + int lf_disable; + int lf_alpha_c0_offset; + int lf_beta_offset; + + int CABT[4]; + int CABP[4]; + int cbp_4x4[4]; + + int skip_flag; + + struct macroblock *mb_available_up; + struct macroblock *mb_available_left; + int mbaddr_a, mbaddr_b, mbaddr_c, mbaddr_d; + int mbavail_a, mbavail_b, mbavail_c, mbavail_d; + +}; + +struct macroblock *mb_data; + +struct img_par *img; + +struct bitstream_s *curr_stream; + +struct datapartition *alloc_partition(int n); + +unsigned int vld_mem_start_addr; +unsigned int vld_mem_end_addr; + +int marker_bit; + +int progressive_sequence; +int horizontal_size; +int vertical_size; + +int second_ifield; +int pre_img_type; + +/* slice_header() */ +int slice_vertical_position; +int slice_vertical_position_extension; +int fixed_picture_qp; +int fixed_slice_qp; +int slice_qp; + +/* + ************************************************************************* + * Function:ue_v, reads an u(v) syntax element, the length in bits is stored in + the global UsedBits variable + * Input: + tracestring + the string for the trace file + bitstream + the stream to be read from + * Output: + * Return: the value of the coded syntax element + * Attention: + ************************************************************************* + */ +/*! + * definition of AVS syntaxelements + * order of elements follow dependencies for picture reconstruction + */ +/*! + * \brief Assignment of old TYPE partition elements to new + * elements + * + * old element | new elements + * TYPE_HEADER | SE_HEADER, SE_PTYPE + * TYPE_MBHEADER | SE_MBTYPE, SE_REFFRAME, SE_INTRAPREDMODE + * TYPE_MVD | SE_MVD + * TYPE_CBP | SE_CBP_INTRA, SE_CBP_INTER * SE_DELTA_QUANT_INTER + * SE_DELTA_QUANT_INTRA + * TYPE_COEFF_Y | SE_LUM_DC_INTRA, SE_LUM_AC_INTRA, + SE_LUM_DC_INTER, SE_LUM_AC_INTER + * TYPE_2x2DC | SE_CHR_DC_INTRA, SE_CHR_DC_INTER + * TYPE_COEFF_C | SE_CHR_AC_INTRA, SE_CHR_AC_INTER + * TYPE_EOS | SE_EOS + */ + +#define SE_HEADER 0 +#define SE_PTYPE 1 +#define SE_MBTYPE 2 +#define SE_REFFRAME 3 +#define SE_INTRAPREDMODE 4 +#define SE_MVD 5 +#define SE_CBP_INTRA 6 +#define SE_LUM_DC_INTRA 7 +#define SE_CHR_DC_INTRA 8 +#define SE_LUM_AC_INTRA 9 +#define SE_CHR_AC_INTRA 10 +#define SE_CBP_INTER 11 +#define SE_LUM_DC_INTER 12 +#define SE_CHR_DC_INTER 13 +#define SE_LUM_AC_INTER 14 +#define SE_CHR_AC_INTER 15 +#define SE_DELTA_QUANT_INTER 16 +#define SE_DELTA_QUANT_INTRA 17 +#define SE_BFRAME 18 +#define SE_EOS 19 +#define SE_MAX_ELEMENTS 20 +#define SE_CBP01 21 +int chroma_format; +/* + ************************************************************************* + * Function:Reads bits from the bitstream buffer + * Input: + byte buffer[] + containing VLC-coded data bits + int totbitoffset + bit offset from start of partition + int bytecount + total bytes in bitstream + int numbits + number of bits to read + * Output: + * Return: + * Attention: + ************************************************************************* + */ + +int get_bits(unsigned char buffer[], int totbitoffset, int *info, int bytecount, + int numbits) +{ + register int inf; + long byteoffset; + int bitoffset; + + int bitcounter = numbits; + + byteoffset = totbitoffset / 8; + bitoffset = 7 - (totbitoffset % 8); + + inf = 0; + while (numbits) { + inf <<= 1; + inf |= (buffer[byteoffset] & (0x01 << bitoffset)) >> bitoffset; + numbits--; + bitoffset--; + if (bitoffset < 0) { + byteoffset++; + bitoffset += 8; + if (byteoffset > bytecount) + return -1; + } + } + + *info = inf; + + + return bitcounter; +} + +/* + ************************************************************************* + * Function:read FLC codeword from UVLC-partition + * Input: + * Output: + * Return: + * Attention: + ************************************************************************* + */ + +int read_syntaxelement_flc(struct syntaxelement *sym) +{ + int frame_bitoffset = curr_stream->frame_bitoffset; + unsigned char *buf = curr_stream->stream_buffer; + int bitstreamlengthinbytes = curr_stream->bitstream_length; + + if ((get_bits(buf, frame_bitoffset, &(sym->inf), bitstreamlengthinbytes, + sym->len)) < 0) + return -1; + + curr_stream->frame_bitoffset += sym->len; + sym->value1 = sym->inf; + +#if TRACE + tracebits2(sym->tracestring, sym->len, sym->inf); +#endif + + return 1; +} + +/* + ************************************************************************* + * Function:ue_v, reads an u(1) syntax element, the length in bits is stored in + the global UsedBits variable + * Input: + tracestring + the string for the trace file + bitstream + the stream to be read from + * Output: + * Return: the value of the coded syntax element + * Attention: + ************************************************************************* + */ +int u_1(char *tracestring) +{ + return u_v(1, tracestring); +} + +/* + ************************************************************************* + * Function:mapping rule for ue(v) syntax elements + * Input:lenght and info + * Output:number in the code table + * Return: + * Attention: + ************************************************************************* + */ +void linfo_ue(int len, int info, int *value1, int *dummy) +{ + *value1 = (int)pow2(2, (len / 2)) + info - 1; +} + +int u_v(int leninbits, char *tracestring) +{ + struct syntaxelement symbol, *sym = &symbol; + +#ifdef AVSP_LONG_CABAC +#else + assert(curr_stream->stream_buffer != NULL); +#endif + sym->type = SE_HEADER; + sym->mapping = linfo_ue; + sym->len = leninbits; + read_syntaxelement_flc(sym); + + return sym->inf; +} + +/* + ************************************************************************* + * Function:mapping rule for se(v) syntax elements + * Input:lenght and info + * Output:signed mvd + * Return: + * Attention: + ************************************************************************* + */ + +void linfo_se(int len, int info, int *value1, int *dummy) +{ + int n; + n = (int)pow2(2, (len / 2)) + info - 1; + *value1 = (n + 1) / 2; + if ((n & 0x01) == 0) + *value1 = -*value1; + +} + +/* + ************************************************************************* + * Function:lenght and info + * Input: + * Output:cbp (intra) + * Return: + * Attention: + ************************************************************************* + */ + +void linfo_cbp_intra(int len, int info, int *cbp, int *dummy) +{ +} + +const int NCBP[64][2] = {{4, 0}, {16, 19}, {17, 16}, {19, 15}, {14, 18}, + {9, 11}, {22, 31}, {8, 13}, {11, 17}, {21, 30}, {10, 12}, + {7, 9}, {12, 10}, {6, 7}, {5, 8}, {1, 1}, {35, 4}, {47, 42}, { + 48, 38}, {38, 27}, {46, 39}, {36, 33}, {50, 59}, + {26, 26}, {45, 40}, {52, 58}, {41, 35}, {28, 25}, {37, 29}, {23, + 24}, {31, 28}, {2, 3}, {43, 5}, {51, 51}, {56, + 52}, {39, 37}, {55, 50}, {33, 43}, {62, 63}, { + 27, 44}, {54, 53}, {60, 62}, {40, 48}, {32, 47}, + {42, 34}, {24, 45}, {29, 49}, {3, 6}, {49, 14}, {53, 55}, {57, + 56}, {25, 36}, {58, 54}, {30, 41}, {59, 60}, { + 15, 21}, {61, 57}, {63, 61}, {44, 46}, {18, 22}, + {34, 32}, {13, 20}, {20, 23}, {0, 2} }; + +unsigned int s1, t1, value_s, value_t; +unsigned char dec_bypass, dec_final; + +#define get_byte() { \ + dbuffer = dcodestrm[(*dcodestrm_len)++];\ + dbits_to_go = 7; \ +} + +#define dbuffer (dep->dbuffer) +#define dbits_to_go (dep->dbits_to_go) +#define dcodestrm (dep->dcodestrm) +#define dcodestrm_len (dep->dcodestrm_len) + +#define B_BITS 10 + +#define LG_PMPS_SHIFTNO 2 + +#define HALF (1 << (B_BITS-1)) +#define QUARTER (1 << (B_BITS-2)) + +unsigned int biari_decode_symbol(struct decoding_environment_s *dep, + struct bi_context_type_s *bi_ct) +{ + register unsigned char bit; + register unsigned char s_flag; + register unsigned char is_lps = 0; + register unsigned char cwr; + register unsigned char cycno = bi_ct->cycno; + register unsigned int lg_pmps = bi_ct->LG_PMPS; + register unsigned int t_rlps; + register unsigned int s2, t2; + +#ifdef DUMP_DEBUG + if (avs_get_debug_flag() & AEC_DUMP) + io_printf("LG_PMPS : %03X, MPS : %d, cycno : %d -- %p\n", + bi_ct->LG_PMPS, bi_ct->MPS, bi_ct->cycno, bi_ct); +#endif + + bit = bi_ct->MPS; + + cwr = (cycno <= 1) ? 3 : (cycno == 2) ? 4 : 5; + + if (t1 >= (lg_pmps >> LG_PMPS_SHIFTNO)) { + s2 = s1; + t2 = t1 - (lg_pmps >> LG_PMPS_SHIFTNO); + s_flag = 0; + } else { + s2 = s1 + 1; + t2 = 256 + t1 - (lg_pmps >> LG_PMPS_SHIFTNO); + s_flag = 1; + } + +#ifdef DUMP_DEBUG + if (avs_get_debug_flag() & AEC_DUMP) + io_printf(" s2 : %d, t2 : %03X\n", s2, t2); +#endif + + if (s2 > value_s || (s2 == value_s && value_t >= t2)) { + is_lps = 1; + bit = !bit; + + t_rlps = (s_flag == 0) ? + (lg_pmps >> LG_PMPS_SHIFTNO) : + (t1 + (lg_pmps >> LG_PMPS_SHIFTNO)); + + if (s2 == value_s) + value_t = (value_t - t2); + else { + if (--dbits_to_go < 0) + get_byte(); + + value_t = (value_t << 1) + | ((dbuffer >> dbits_to_go) & 0x01); + value_t = 256 + value_t - t2; + + } + + while (t_rlps < QUARTER) { + t_rlps = t_rlps << 1; + if (--dbits_to_go < 0) + get_byte(); + + value_t = (value_t << 1) + | ((dbuffer >> dbits_to_go) & 0x01); + } + + s1 = 0; + t1 = t_rlps & 0xff; + + value_s = 0; + while (value_t < QUARTER) { + int j; + if (--dbits_to_go < 0) + get_byte(); + j = (dbuffer >> dbits_to_go) & 0x01; + + value_t = (value_t << 1) | j; + value_s++; + } + value_t = value_t & 0xff; + } else { + + s1 = s2; + t1 = t2; + } + + if (dec_bypass) + return bit; + + if (is_lps) + cycno = (cycno <= 2) ? (cycno + 1) : 3; + else if (cycno == 0) + cycno = 1; + bi_ct->cycno = cycno; + + if (is_lps) { + switch (cwr) { + case 3: + lg_pmps = lg_pmps + 197; + break; + case 4: + lg_pmps = lg_pmps + 95; + break; + default: + lg_pmps = lg_pmps + 46; + } + + if (lg_pmps >= (256 << LG_PMPS_SHIFTNO)) { + lg_pmps = (512 << LG_PMPS_SHIFTNO) - 1 - lg_pmps; + bi_ct->MPS = !(bi_ct->MPS); + } + } else { +#ifdef DUMP_DEBUG + if (avs_get_debug_flag() & AEC_DUMP) + io_printf(" - lg_pmps_MPS : %X (%X - %X - %X)\n", + lg_pmps - (unsigned int)(lg_pmps>>cwr) + - (unsigned int)(lg_pmps>>(cwr+2)), + lg_pmps, + (unsigned int)(lg_pmps>>cwr), + (unsigned int)(lg_pmps>>(cwr+2)) + ); +#endif + lg_pmps = lg_pmps - (unsigned int)(lg_pmps >> cwr) + - (unsigned int)(lg_pmps >> (cwr + 2)); + } + + bi_ct->LG_PMPS = lg_pmps; + + return bit; +} + +unsigned int biari_decode_symbolw(struct decoding_environment_s *dep, + struct bi_context_type_s *bi_ct1, + struct bi_context_type_s *bi_ct2) +{ + register unsigned char bit1, bit2; + register unsigned char pred_mps, bit; + register unsigned int lg_pmps; + register unsigned char cwr1, cycno1 = bi_ct1->cycno; + register unsigned char cwr2, cycno2 = bi_ct2->cycno; + register unsigned int lg_pmps1 = bi_ct1->LG_PMPS; + register unsigned int lg_pmps2 = + bi_ct2->LG_PMPS; + register unsigned int t_rlps; + register unsigned char s_flag, is_lps = 0; + register unsigned int s2, t2; + + + bit1 = bi_ct1->MPS; + bit2 = bi_ct2->MPS; + + cwr1 = (cycno1 <= 1) ? 3 : (cycno1 == 2) ? 4 : 5; + cwr2 = (cycno2 <= 1) ? 3 : (cycno2 == 2) ? 4 : 5; + + if (bit1 == bit2) { + pred_mps = bit1; + lg_pmps = (lg_pmps1 + lg_pmps2) / 2; + } else { + if (lg_pmps1 < lg_pmps2) { + pred_mps = bit1; + lg_pmps = (256 << LG_PMPS_SHIFTNO) - 1 + - ((lg_pmps2 - lg_pmps1) >> 1); + } else { + pred_mps = bit2; + lg_pmps = (256 << LG_PMPS_SHIFTNO) - 1 + - ((lg_pmps1 - lg_pmps2) >> 1); + } + } + +#ifdef DUMP_DEBUG + if (avs_get_debug_flag() & AEC_DUMP) + io_printf(" - Begin - LG_PMPS : %03X, MPS : %d\n", + lg_pmps, pred_mps); +#endif + if (t1 >= (lg_pmps >> LG_PMPS_SHIFTNO)) { + s2 = s1; + t2 = t1 - (lg_pmps >> LG_PMPS_SHIFTNO); + s_flag = 0; + } else { + s2 = s1 + 1; + t2 = 256 + t1 - (lg_pmps >> LG_PMPS_SHIFTNO); + s_flag = 1; + } + + bit = pred_mps; + if (s2 > value_s || (s2 == value_s && value_t >= t2)) { + is_lps = 1; + bit = !bit; + t_rlps = (s_flag == 0) ? + (lg_pmps >> LG_PMPS_SHIFTNO) : + (t1 + (lg_pmps >> LG_PMPS_SHIFTNO)); + + if (s2 == value_s) + value_t = (value_t - t2); + else { + if (--dbits_to_go < 0) + get_byte(); + + value_t = (value_t << 1) + | ((dbuffer >> dbits_to_go) & 0x01); + value_t = 256 + value_t - t2; + } + + while (t_rlps < QUARTER) { + t_rlps = t_rlps << 1; + if (--dbits_to_go < 0) + get_byte(); + + value_t = (value_t << 1) + | ((dbuffer >> dbits_to_go) & 0x01); + } + s1 = 0; + t1 = t_rlps & 0xff; + + value_s = 0; + while (value_t < QUARTER) { + int j; + if (--dbits_to_go < 0) + get_byte(); + j = (dbuffer >> dbits_to_go) & 0x01; + + value_t = (value_t << 1) | j; + value_s++; + } + value_t = value_t & 0xff; + } else { + s1 = s2; + t1 = t2; + } + + if (bit != bit1) { + cycno1 = (cycno1 <= 2) ? (cycno1 + 1) : 3; + } else { + if (cycno1 == 0) + cycno1 = 1; + } + + if (bit != bit2) { + cycno2 = (cycno2 <= 2) ? (cycno2 + 1) : 3; + } else { + if (cycno2 == 0) + cycno2 = 1; + } + bi_ct1->cycno = cycno1; + bi_ct2->cycno = cycno2; + + { + + if (bit == bit1) { + lg_pmps1 = + lg_pmps1 + - (unsigned int)(lg_pmps1 + >> cwr1) + - (unsigned int)(lg_pmps1 + >> (cwr1 + + 2)); + } else { + switch (cwr1) { + case 3: + lg_pmps1 = lg_pmps1 + 197; + break; + case 4: + lg_pmps1 = lg_pmps1 + 95; + break; + default: + lg_pmps1 = lg_pmps1 + 46; + } + + if (lg_pmps1 >= (256 << LG_PMPS_SHIFTNO)) { + lg_pmps1 = (512 << LG_PMPS_SHIFTNO) - 1 + - lg_pmps1; + bi_ct1->MPS = !(bi_ct1->MPS); + } + } + bi_ct1->LG_PMPS = lg_pmps1; + + if (bit == bit2) { + lg_pmps2 = + lg_pmps2 + - (unsigned int)(lg_pmps2 + >> cwr2) + - (unsigned int)(lg_pmps2 + >> (cwr2 + + 2)); + } else { + switch (cwr2) { + case 3: + lg_pmps2 = lg_pmps2 + 197; + break; + case 4: + lg_pmps2 = lg_pmps2 + 95; + break; + default: + lg_pmps2 = lg_pmps2 + 46; + } + + if (lg_pmps2 >= (256 << LG_PMPS_SHIFTNO)) { + lg_pmps2 = (512 << LG_PMPS_SHIFTNO) - 1 + - lg_pmps2; + bi_ct2->MPS = !(bi_ct2->MPS); + } + } + bi_ct2->LG_PMPS = lg_pmps2; + } + + + return bit; +} + +/*! + ************************************************************************ + * \brief + * biari_decode_symbol_eq_prob(): + * \return + * the decoded symbol + ************************************************************************ + */ +unsigned int biari_decode_symbol_eq_prob(struct decoding_environment_s *dep) +{ + unsigned char bit; + struct bi_context_type_s octx; + struct bi_context_type_s *ctx = &octx; + ctx->LG_PMPS = (QUARTER << LG_PMPS_SHIFTNO) - 1; + ctx->MPS = 0; + ctx->cycno = 0xfe; + dec_bypass = 1; + bit = biari_decode_symbol(dep, ctx); + dec_bypass = 0; + return bit; +} + +unsigned int biari_decode_final(struct decoding_environment_s *dep) +{ + unsigned char bit; + struct bi_context_type_s octx; + struct bi_context_type_s *ctx = &octx; + ctx->LG_PMPS = 1 << LG_PMPS_SHIFTNO; + ctx->MPS = 0; + ctx->cycno = 0xff; + dec_final = 1; + bit = biari_decode_symbol(dep, ctx); + dec_final = 0; + return bit; +} + +int i_8(char *tracestring) +{ + int frame_bitoffset = curr_stream->frame_bitoffset; + unsigned char *buf = curr_stream->stream_buffer; + int bitstreamlengthinbytes = curr_stream->bitstream_length; + struct syntaxelement symbol, *sym = &symbol; +#ifdef AVSP_LONG_CABAC +#else + assert(curr_stream->stream_buffer != NULL); +#endif + + sym->len = 8; + sym->type = SE_HEADER; + sym->mapping = linfo_ue; + + if ((get_bits(buf, frame_bitoffset, &(sym->inf), bitstreamlengthinbytes, + sym->len)) < 0) + return -1; + curr_stream->frame_bitoffset += sym->len; + sym->value1 = sym->inf; + if (sym->inf & 0x80) + sym->inf = -(~((int)0xffffff00 | sym->inf) + 1); +#if TRACE + tracebits2(sym->tracestring, sym->len, sym->inf); +#endif + return sym->inf; +} + +/*! + ************************************************************************ + * \brief + * arideco_bits_read + ************************************************************************ + */ +int arideco_bits_read(struct decoding_environment_s *dep) +{ + + return 8 * ((*dcodestrm_len) - 1) + (8 - dbits_to_go); +} + +/*! + ************************************************************************ + * \brief + * arithmetic decoding + ************************************************************************ + */ +int read_syntaxelement_aec(struct syntaxelement *se, struct img_par *img, + struct datapartition *this_data_part) +{ + int curr_len; + struct decoding_environment_s *dep_dp = &(this_data_part->de_aec); + + curr_len = arideco_bits_read(dep_dp); + + se->reading(se, img, dep_dp); + + se->len = (arideco_bits_read(dep_dp) - curr_len); + return se->len; +} + +/*! + ************************************************************************ + * \brief + * This function is used to arithmetically decode the + * run length info of the skip mb + ************************************************************************ + */ +void readrunlenghtfrombuffer_aec(struct syntaxelement *se, struct img_par *img, + struct decoding_environment_s *dep_dp) +{ + struct bi_context_type_s *pctx; + int ctx, symbol; + pctx = img->current_slice->tex_ctx->one_contexts[0]; + symbol = 0; + ctx = 0; + while (biari_decode_symbol(dep_dp, pctx + ctx) == 0) { + symbol += 1; + ctx++; + if (ctx >= 3) + ctx = 3; + } + se->value1 = symbol; +#if TRACE + fprintf(p_trace, "@%d%s\t\t\t%d\n", + symbol_count++, se->tracestring, se->value1); + fflush(p_trace); +#endif +} + +/*! + ************************************************************************ + * \brief + * This function is used to arithmetically decode a pair of + * intra prediction modes of a given MB. + ************************************************************************ + */ +int mapd_intrap[5] = {0, 2, 3, 4, 1}; +void read_intrapredmode_aec(struct syntaxelement *se, struct img_par *img, + struct decoding_environment_s *dep_dp) +{ + struct bi_context_type_s *pctx; + int ctx, symbol; + pctx = img->current_slice->tex_ctx->one_contexts[1]; + symbol = 0; + ctx = 0; +#ifdef DUMP_DEBUG + if (avs_get_debug_flag() & AEC_DUMP) + io_printf(" -- read_intrapredmode_aec ctx : %d\n", ctx); +#endif + while (biari_decode_symbol(dep_dp, pctx + ctx) == 0) { + symbol += 1; + ctx++; + if (ctx >= 3) + ctx = 3; +#ifdef DUMP_DEBUG + if (avs_get_debug_flag() & AEC_DUMP) + io_printf(" -- read_intrapredmode_aec ctx : %d\n", ctx); +#endif + if (symbol == 4) + break; + } + se->value1 = mapd_intrap[symbol] - 1; + +#if TRACE + fprintf(p_trace, "@%d %s\t\t\t%d\n", + symbol_count++, se->tracestring, se->value1); + fflush(p_trace); +#endif +} + +/*! + ************************************************************************ + * \brief + * decoding of unary binarization using one or 2 distinct + * models for the first and all remaining bins; no terminating + * "0" for max_symbol + *********************************************************************** + */ +unsigned int unary_bin_max_decode(struct decoding_environment_s *dep_dp, + struct bi_context_type_s *ctx, + int ctx_offset, unsigned int max_symbol) +{ + unsigned int l; + unsigned int symbol; + struct bi_context_type_s *ictx; + + symbol = biari_decode_symbol(dep_dp, ctx); + + if (symbol == 0) + return 0; + else { + if (max_symbol == 1) + return symbol; + symbol = 0; + ictx = ctx + ctx_offset; + do { + l = biari_decode_symbol(dep_dp, ictx); + symbol++; + } while ((l != 0) && (symbol < max_symbol - 1)); + if ((l != 0) && (symbol == max_symbol - 1)) + symbol++; + return symbol; + } +} + +/*! + ************************************************************************ + * \brief + * decoding of unary binarization using one or 2 distinct + * models for the first and all remaining bins + *********************************************************************** + */ +unsigned int unary_bin_decode(struct decoding_environment_s *dep_dp, + struct bi_context_type_s *ctx, int ctx_offset) +{ + unsigned int l; + unsigned int symbol; + struct bi_context_type_s *ictx; + + symbol = 1 - biari_decode_symbol(dep_dp, ctx); + + if (symbol == 0) + return 0; + else { + symbol = 0; + ictx = ctx + ctx_offset; + do { + l = 1 - biari_decode_symbol(dep_dp, ictx); + symbol++; + } while (l != 0); + return symbol; + } +} + +/*! + ************************************************************************ + * \brief + * This function is used to arithmetically decode the chroma + * intra prediction mode of a given MB. + ************************************************************************ + */ +void read_cipredmode_aec(struct syntaxelement *se, + struct img_par *img, + struct decoding_environment_s *dep_dp) +{ + struct texture_info_contexts *ctx = img->current_slice->tex_ctx; + struct macroblock *curr_mb = &mb_data[img->current_mb_nr]; + int act_ctx, a, b; + int act_sym = se->value1; + + if (curr_mb->mb_available_up == NULL) + b = 0; + else { + /*if ( (curr_mb->mb_available_up)->mb_type==IPCM) + b=0; + else*/ + b = (((curr_mb->mb_available_up)->c_ipred_mode != 0) ? 1 : 0); + } + + if (curr_mb->mb_available_left == NULL) + a = 0; + else { + /* if ( (curr_mb->mb_available_left)->mb_type==IPCM) + a=0; + else*/ + a = (((curr_mb->mb_available_left)->c_ipred_mode != 0) ? 1 : 0); + } + + act_ctx = a + b; + + + act_sym = biari_decode_symbol(dep_dp, ctx->cipr_contexts + act_ctx); + + if (act_sym != 0) + act_sym = unary_bin_max_decode(dep_dp, ctx->cipr_contexts + 3, + 0, 2) + 1; + + se->value1 = act_sym; + +#if TRACE + fprintf(p_trace, "@%d %s\t\t%d\n", + symbol_count++, se->tracestring, se->value1); + fflush(p_trace); +#endif + +} + +int slice_header(char *buf, int startcodepos, int length) +{ + int i; + + int weight_para_num = 0; + int mb_row; + int mb_column; + int mb_index; + int mb_width, mb_height; + + mb_column = 0; + + memcpy(curr_stream->stream_buffer, buf, length); + curr_stream->code_len = curr_stream->bitstream_length = length; + + curr_stream->read_len = + curr_stream->frame_bitoffset = (startcodepos) * 8; + slice_vertical_position = u_v(8, "slice vertical position"); + + push_es(slice_vertical_position, 8); + +#ifdef DUMP_DEBUG + if (avs_get_debug_flag() & SLICE_INFO_DUMP) + io_printf(" * 8-bits slice_vertical_position : %d\n", + slice_vertical_position); +#endif + + if (vertical_size > 2800) { + slice_vertical_position_extension = u_v(3, + "slice vertical position extension"); + push_es(slice_vertical_position_extension, 3); + + } + + if (vertical_size > 2800) + mb_row = (slice_vertical_position_extension << 7) + + slice_vertical_position; + else + mb_row = slice_vertical_position; + + mb_width = (horizontal_size + 15) / 16; + if (!progressive_sequence) + mb_height = 2 * ((vertical_size + 31) / 32); + else + mb_height = (vertical_size + 15) / 16; + + + mb_index = mb_row * mb_width + mb_column; + + if (!img->picture_structure && img->type == I_IMG + && (mb_index >= mb_width * mb_height / 2)) { + second_ifield = 1; + img->type = P_IMG; + pre_img_type = P_IMG; + } + + { + if (!fixed_picture_qp) { + fixed_slice_qp = u_v(1, "fixed_slice_qp"); + push_es(fixed_slice_qp, 1); +#ifdef DUMP_DEBUG + if (avs_get_debug_flag() & SLICE_INFO_DUMP) + io_printf(" * 1-bit fixed_slice_qp : %d\n", + fixed_slice_qp); +#endif + slice_qp = u_v(6, "slice_qp"); + push_es(slice_qp, 6); +#ifdef DUMP_DEBUG + if (avs_get_debug_flag() & SLICE_INFO_DUMP) + io_printf(" * 6-bits slice_qp : %d\n", + slice_qp); +#endif + + img->qp = slice_qp; + } + + if (img->type != I_IMG) { + img->slice_weighting_flag = u_v(1, + "slice weighting flag"); + + if (img->slice_weighting_flag) { + + if (second_ifield && !img->picture_structure) + weight_para_num = 1; + else if (img->type == P_IMG + && img->picture_structure) + weight_para_num = 2; + else if (img->type == P_IMG + && !img->picture_structure) + weight_para_num = 4; + else if (img->type == B_IMG + && img->picture_structure) + weight_para_num = 2; + else if (img->type == B_IMG + && !img->picture_structure) + weight_para_num = 4; + +#ifdef DUMP_DEBUG + if (avs_get_debug_flag() & SLICE_INFO_DUMP) + io_printf(" - weight_para_num : %d\n", + weight_para_num); +#endif + for (i = 0; i < weight_para_num; i++) { + img->lum_scale[i] = u_v(8, + "luma scale"); + + img->lum_shift[i] = i_8("luma shift"); + + marker_bit = u_1("insert bit"); + + + { + img->chroma_scale[i] = u_v(8, + "chroma scale"); + + img->chroma_shift[i] = i_8( + "chroma shift"); + + marker_bit = u_1("insert bit"); + + } + } + img->mb_weighting_flag = u_v(1, + "MB weighting flag"); + + } + } + } + + +#if 1 + return mb_index; +#endif +} + +void no_mem_exit(char *where) +{ + io_printf("%s\r\n", where); +} + +unsigned char bit[8] = {0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01}; + +struct inputstream_s { + /*FILE *f;*/ + unsigned char buf[SVA_STREAM_BUF_SIZE]; + unsigned int uclear_bits; + unsigned int upre_3bytes; + int ibyte_position; + int ibuf_bytesnum; + int iclear_bitsnum; + int istuff_bitsnum; + int ibits_count; +}; + +struct inputstream_s IRABS; +struct inputstream_s *p_irabs = &IRABS; + +struct stat_bits { + int curr_frame_bits; + int prev_frame_bits; + int emulate_bits; + int prev_emulate_bits; + int last_unit_bits; + int bitrate; + int total_bitrate[1000]; + int coded_pic_num; + int time_s; +}; + +struct stat_bits *stat_bits_ptr; + +unsigned char *temp_slice_buf; +int start_codeposition; +int first_slice_length; +int first_slice_startpos; + +int bitstream_buf_used; +int startcode_offset; + +int bitstream_read_ptr; + +int demulate_enable; + +int last_dquant; + +int total_mb_count; + +int current_mb_skip; + +int skip_mode_flag; + +int current_mb_intra; + +/* + ************************************************************************* + * Function: Check start code's type + * Input: + * Output: + * Return: + * Author: XZHENG, 20080515 + ************************************************************************* + */ +void check_type(int startcode) +{ + startcode = startcode & 0x000000ff; + switch (startcode) { + case 0xb0: + case 0xb2: + case 0xb5: + demulate_enable = 0; + break; + default: + demulate_enable = 1; + break; + } + +} +/* + ************************************************************************* + * Function: + * Input: + * Output: + * Return: 0 : OK + -1 : arrive at stream end + -2 : meet another start code + * Attention: + ************************************************************************* + */ +int clear_nextbyte(struct inputstream_s *p) +{ + int i, k, j; + unsigned char temp[3]; + i = p->ibyte_position; + k = p->ibuf_bytesnum - i; + if (k < 3) { + for (j = 0; j < k; j++) + temp[j] = p->buf[i + j]; + + p->ibuf_bytesnum = read_bitstream(p->buf + k, + SVA_STREAM_BUF_SIZE - k); + bitstream_buf_used++; + if (p->ibuf_bytesnum == 0) { + if (k > 0) { + while (k > 0) { + p->upre_3bytes = ((p->upre_3bytes << 8) + | p->buf[i]) + & 0x00ffffff; + if (p->upre_3bytes < 4 + && demulate_enable) { + p->uclear_bits = + (p->uclear_bits + << 6) + | (p->buf[i] + >> 2); + p->iclear_bitsnum += 6; + stat_bits_ptr->emulate_bits + += 2; + } else { + p->uclear_bits = (p->uclear_bits + << 8) + | p->buf[i]; + p->iclear_bitsnum += 8; + } + p->ibyte_position++; + k--; + i++; + } + return 0; + } else { + return -1; + } + } else { + for (j = 0; j < k; j++) + p->buf[j] = temp[j]; + p->ibuf_bytesnum += k; + i = p->ibyte_position = 0; + } + } + if (p->buf[i] == 0 && p->buf[i + 1] == 0 && p->buf[i + 2] == 1) + return -2; + p->upre_3bytes = ((p->upre_3bytes << 8) | p->buf[i]) & 0x00ffffff; + if (p->upre_3bytes < 4 && demulate_enable) { + p->uclear_bits = (p->uclear_bits << 6) | (p->buf[i] >> 2); + p->iclear_bitsnum += 6; + stat_bits_ptr->emulate_bits += 2; + } else { + p->uclear_bits = (p->uclear_bits << 8) | p->buf[i]; + p->iclear_bitsnum += 8; + } + p->ibyte_position++; + return 0; +} + +/* + ************************************************************************* + * Function: + * Input: + * Output: + * Return: 0 : OK + -1 : arrive at stream end + -2 : meet another start code + * Attention: + ************************************************************************* + */ +int read_n_bit(struct inputstream_s *p, int n, int *v) +{ + int r; + unsigned int t; + while (n > p->iclear_bitsnum) { + r = clear_nextbyte(p); + if (r) { + if (r == -1) { + if (p->ibuf_bytesnum - p->ibyte_position > 0) + break; + } + return r; + } + } + t = p->uclear_bits; + r = 32 - p->iclear_bitsnum; + *v = (t << r) >> (32 - n); + p->iclear_bitsnum -= n; + return 0; +} + +#ifdef AVSP_LONG_CABAC +unsigned char TMP_BUF[2 * SVA_STREAM_BUF_SIZE]; +int tmp_buf_wr_ptr; +int tmp_buf_rd_ptr; +int tmp_buf_count; +#endif +void open_irabs(struct inputstream_s *p) +{ + p->uclear_bits = 0xffffffff; + p->ibyte_position = 0; + p->ibuf_bytesnum = 0; + p->iclear_bitsnum = 0; + p->istuff_bitsnum = 0; + p->ibits_count = 0; + p->upre_3bytes = 0; + + bitstream_buf_used = 0; + bitstream_read_ptr = (src_start - 16) & 0xfffffff0; + +#ifdef AVSP_LONG_CABAC + tmp_buf_count = 0; + tmp_buf_wr_ptr = 0; + tmp_buf_rd_ptr = 0; +#endif + +} + +void move_bitstream(unsigned int move_from_addr, unsigned int move_to_addr, + int move_size) +{ + int move_bytes_left = move_size; + unsigned int move_read_addr; + unsigned int move_write_addr = move_to_addr; + + int move_byte; + unsigned int data32; + + while (move_from_addr > vld_mem_end_addr) { + move_from_addr = move_from_addr + vld_mem_start_addr + - vld_mem_end_addr - 8; + } + move_read_addr = move_from_addr; + while (move_bytes_left > 0) { + move_byte = move_bytes_left; + if (move_byte > 512) + move_byte = 512; + if ((move_read_addr + move_byte) > vld_mem_end_addr) + move_byte = (vld_mem_end_addr + 8) - move_read_addr; + + WRITE_VREG(LMEM_DMA_ADR, move_read_addr); + WRITE_VREG(LMEM_DMA_COUNT, move_byte / 2); + WRITE_VREG(LMEM_DMA_CTRL, 0xc200); + + data32 = 0x8000; + while (data32 & 0x8000) + data32 = READ_VREG(LMEM_DMA_CTRL); + + WRITE_VREG(LMEM_DMA_ADR, move_write_addr); + WRITE_VREG(LMEM_DMA_COUNT, move_byte / 2); + WRITE_VREG(LMEM_DMA_CTRL, 0x8200); + + data32 = 0x8000; + while (data32 & 0x8000) + data32 = READ_VREG(LMEM_DMA_CTRL); + + data32 = 0x0fff; + while (data32 & 0x0fff) + data32 = READ_VREG(WRRSP_LMEM); + +#ifdef DUMP_DEBUG + if (avs_get_debug_flag() & STREAM_INFO_DUMP) + io_printf(" 2 MOVE %d Bytes from 0x%x to 0x%x\n", + move_byte, move_read_addr, move_write_addr); +#endif + + move_read_addr = move_read_addr + move_byte; + if (move_read_addr > vld_mem_end_addr) + move_read_addr = vld_mem_start_addr; + move_write_addr = move_write_addr + move_byte; + move_bytes_left = move_bytes_left - move_byte; + } + +} + +int read_bitstream(unsigned char *buf, int size) +{ + int i; + +#ifdef AVSP_LONG_CABAC + + unsigned int *TMP_BUF_32 = (unsigned int *)bitstream_read_tmp; + if (tmp_buf_count < size) { + dma_sync_single_for_cpu(amports_get_dma_device(), + bitstream_read_tmp_phy, SVA_STREAM_BUF_SIZE, + DMA_FROM_DEVICE); + + move_bitstream(bitstream_read_ptr, bitstream_read_tmp_phy, + SVA_STREAM_BUF_SIZE); + + for (i = 0; i < SVA_STREAM_BUF_SIZE / 8; i++) { + TMP_BUF[tmp_buf_wr_ptr++] = + (TMP_BUF_32[2 * i + 1] >> 24) & 0xff; + if (tmp_buf_wr_ptr >= (2 * SVA_STREAM_BUF_SIZE)) + tmp_buf_wr_ptr = 0; + TMP_BUF[tmp_buf_wr_ptr++] = + (TMP_BUF_32[2 * i + 1] >> 16) & 0xff; + if (tmp_buf_wr_ptr >= (2 * SVA_STREAM_BUF_SIZE)) + tmp_buf_wr_ptr = 0; + TMP_BUF[tmp_buf_wr_ptr++] = (TMP_BUF_32[2 * i + 1] >> 8) + & 0xff; + if (tmp_buf_wr_ptr >= (2 * SVA_STREAM_BUF_SIZE)) + tmp_buf_wr_ptr = 0; + TMP_BUF[tmp_buf_wr_ptr++] = (TMP_BUF_32[2 * i + 1] >> 0) + & 0xff; + if (tmp_buf_wr_ptr >= (2 * SVA_STREAM_BUF_SIZE)) + tmp_buf_wr_ptr = 0; + TMP_BUF[tmp_buf_wr_ptr++] = + (TMP_BUF_32[2 * i + 0] >> 24) & 0xff; + if (tmp_buf_wr_ptr >= (2 * SVA_STREAM_BUF_SIZE)) + tmp_buf_wr_ptr = 0; + TMP_BUF[tmp_buf_wr_ptr++] = + (TMP_BUF_32[2 * i + 0] >> 16) & 0xff; + if (tmp_buf_wr_ptr >= (2 * SVA_STREAM_BUF_SIZE)) + tmp_buf_wr_ptr = 0; + TMP_BUF[tmp_buf_wr_ptr++] = (TMP_BUF_32[2 * i + 0] >> 8) + & 0xff; + if (tmp_buf_wr_ptr >= (2 * SVA_STREAM_BUF_SIZE)) + tmp_buf_wr_ptr = 0; + TMP_BUF[tmp_buf_wr_ptr++] = (TMP_BUF_32[2 * i + 0] >> 0) + & 0xff; + if (tmp_buf_wr_ptr >= (2 * SVA_STREAM_BUF_SIZE)) + tmp_buf_wr_ptr = 0; + } + tmp_buf_count = tmp_buf_count + SVA_STREAM_BUF_SIZE; + bitstream_read_ptr = bitstream_read_ptr + SVA_STREAM_BUF_SIZE; + } + +#ifdef DUMP_DEBUG + if (avs_get_debug_flag() & STREAM_INFO_DUMP) + io_printf(" Read %d bytes from %d, size left : %d\n", + size, tmp_buf_rd_ptr, tmp_buf_count); +#endif + for (i = 0; i < size; i++) { + buf[i] = TMP_BUF[tmp_buf_rd_ptr++]; + if (tmp_buf_rd_ptr >= (2 * SVA_STREAM_BUF_SIZE)) + tmp_buf_rd_ptr = 0; + } + tmp_buf_count = tmp_buf_count - size; + +#else + for (i = 0; i < size; i++) + buf[i] = tmp_stream[bitstream_read_ptr + i]; + bitstream_read_ptr = bitstream_read_ptr + size; +#endif + + return size; +} + +int next_startcode(struct inputstream_s *p) +{ + int i, m; + unsigned char a = 0, b = 0; + m = 0; + + while (1) { + if (p->ibyte_position >= p->ibuf_bytesnum - 2) { + m = p->ibuf_bytesnum - p->ibyte_position; + if (m < 0) + return -2; + if (m == 1) + b = p->buf[p->ibyte_position + 1]; + if (m == 2) { + b = p->buf[p->ibyte_position + 1]; + a = p->buf[p->ibyte_position]; + } + p->ibuf_bytesnum = read_bitstream(p->buf, + SVA_STREAM_BUF_SIZE); + p->ibyte_position = 0; + bitstream_buf_used++; + } + + if (p->ibuf_bytesnum + m < 3) + return -1; + + if (m == 1 && b == 0 && p->buf[0] == 0 && p->buf[1] == 1) { + p->ibyte_position = 2; + p->iclear_bitsnum = 0; + p->istuff_bitsnum = 0; + p->ibits_count += 24; + p->upre_3bytes = 1; + return 0; + } + + if (m == 2 && b == 0 && a == 0 && p->buf[0] == 1) { + p->ibyte_position = 1; + p->iclear_bitsnum = 0; + p->istuff_bitsnum = 0; + p->ibits_count += 24; + p->upre_3bytes = 1; + return 0; + } + + if (m == 2 && b == 0 && p->buf[0] == 0 && p->buf[1] == 1) { + p->ibyte_position = 2; + p->iclear_bitsnum = 0; + p->istuff_bitsnum = 0; + p->ibits_count += 24; + p->upre_3bytes = 1; + return 0; + } + + for (i = p->ibyte_position; i < p->ibuf_bytesnum - 2; i++) { + if (p->buf[i] == 0 && p->buf[i + 1] == 0 + && p->buf[i + 2] == 1) { + p->ibyte_position = i + 3; + p->iclear_bitsnum = 0; + p->istuff_bitsnum = 0; + p->ibits_count += 24; + p->upre_3bytes = 1; + return 0; + } + p->ibits_count += 8; + } + p->ibyte_position = i; + } +} + +int get_oneunit(char *buf, int *startcodepos, int *length) +{ + int i, j, k; + i = next_startcode(p_irabs); + + if (i != 0) { + if (i == -1) + io_printf( + "\narrive at stream end and start code is not found!"); + if (i == -2) + io_printf("\np->ibyte_position error!"); + + } + startcode_offset = + p_irabs->ibyte_position + - 3 + (bitstream_buf_used-1) + * SVA_STREAM_BUF_SIZE; + buf[0] = 0; + buf[1] = 0; + buf[2] = 1; + *startcodepos = 3; + i = read_n_bit(p_irabs, 8, &j); + buf[3] = (char)j; + + check_type(buf[3]); + if (buf[3] == SEQUENCE_END_CODE) { + *length = 4; + return -1; + } + k = 4; + while (1) { + i = read_n_bit(p_irabs, 8, &j); + if (i < 0) + break; + buf[k++] = (char)j; + if (k >= (MAX_CODED_FRAME_SIZE - 1)) + break; + } + if (p_irabs->iclear_bitsnum > 0) { + int shift; + shift = 8 - p_irabs->iclear_bitsnum; + i = read_n_bit(p_irabs, p_irabs->iclear_bitsnum, &j); + + if (j != 0) + buf[k++] = (char)(j << shift); + stat_bits_ptr->last_unit_bits += shift; + } + *length = k; + return k; +} + +/*unsigned char tmp_buf[MAX_CODED_FRAME_SIZE] __attribute__ ((aligned(64)));*/ +/*unsigned char tmp_buf[MAX_CODED_FRAME_SIZE] __aligned(64);*/ +int header(void) +{ + unsigned char *buf; + int startcodepos, length; + + unsigned char *tmp_buf; + tmp_buf = (unsigned char *)avsp_heap_adr; + + buf = &tmp_buf[0]; + while (1) { + start_codeposition = get_oneunit(buf, &startcodepos, &length); + + switch (buf[startcodepos]) { + case SEQUENCE_HEADER_CODE: + io_printf( + "# SEQUENCE_HEADER_CODE (0x%02x) found at offset %d (0x%x)\n", + buf[startcodepos], startcode_offset, + startcode_offset); + break; + case EXTENSION_START_CODE: + io_printf( + "# EXTENSION_START_CODE (0x%02x) found at offset %d (0x%x)\n", + buf[startcodepos], startcode_offset, + startcode_offset); + break; + case USER_DATA_START_CODE: + io_printf( + "# USER_DATA_START_CODE (0x%02x) found at offset %d (0x%x)\n", + buf[startcodepos], startcode_offset, + startcode_offset); + break; + case VIDEO_EDIT_CODE: + io_printf( + "# VIDEO_EDIT_CODE (0x%02x) found at offset %d (0x%x)\n", + buf[startcodepos], startcode_offset, + startcode_offset); + break; + case I_PICTURE_START_CODE: + io_printf( + "# I_PICTURE_START_CODE (0x%02x) found at offset %d (0x%x)\n", + buf[startcodepos], startcode_offset, + startcode_offset); + break; + case PB_PICTURE_START_CODE: + io_printf( + "# PB_PICTURE_START_CODE (0x%02x) found at offset %d (0x%x)\n", + buf[startcodepos], startcode_offset, + startcode_offset); + break; + case SEQUENCE_END_CODE: + io_printf( + "# SEQUENCE_END_CODE (0x%02x) found at offset %d (0x%x)\n", + buf[startcodepos], startcode_offset, + startcode_offset); + break; + default: + io_printf( + "# SLICE_START_CODE (0x%02x) found at offset %d (0x%x)\n", + buf[startcodepos], startcode_offset, + startcode_offset); +#if 0 + io_printf("VLD_MEM_VIFIFO_START_PTR %x\r\n", + READ_VREG(VLD_MEM_VIFIFO_START_PTR)); + io_printf("VLD_MEM_VIFIFO_CURR_PTR %x\r\n", + READ_VREG(VLD_MEM_VIFIFO_CURR_PTR)); + io_printf("VLD_MEM_VIFIFO_END_PTR %x\r\n", + READ_VREG(VLD_MEM_VIFIFO_END_PTR)); + io_printf("VLD_MEM_VIFIFO_WP %x\r\n" + READ_VREG(VLD_MEM_VIFIFO_WP)); + io_printf("VLD_MEM_VIFIFO_RP %x\r\n", + READ_VREG(VLD_MEM_VIFIFO_RP)); + io_printf("VLD_MEM_VBUF_RD_PTR %x\r\n" + READ_VREG(VLD_MEM_VBUF_RD_PTR)); + io_printf("VLD_MEM_VIFIFO_BUF_CNTL %x\r\n", + READ_VREG(VLD_MEM_VIFIFO_BUF_CNTL)); + io_printf("PARSER_VIDEO_HOLE %x\r\n", + READ_MPEG_REG(PARSER_VIDEO_HOLE)); +#endif + if ((buf[startcodepos] >= SLICE_START_CODE_MIN + && buf[startcodepos] + <= SLICE_START_CODE_MAX) + && ((!img->seq_header_indicate) + || (img->type == B_IMG + && img->b_discard_flag + == 1 + && !img->no_forward_reference))) { + break; + } else if (buf[startcodepos] >= SLICE_START_CODE_MIN) { + + first_slice_length = length; + first_slice_startpos = startcodepos; + + temp_slice_buf = &tmp_buf[0]; + return SOP; + } else { + io_printf("Can't find start code"); + return -EOS; + } + } + } + +} + +/* + ************************************************************************* + * Function:Allocates a Bitstream + * Input: + * Output:allocated Bitstream point + * Return: + * Attention: + ************************************************************************* + */ +struct bitstream_s *alloc_bitstream(void) +{ + struct bitstream_s *bitstream; + + bitstream = (struct bitstream_s *)local_alloc(1, + sizeof(struct bitstream_s)); + if (bitstream == NULL) { + io_printf( + "AllocBitstream: Memory allocation for Bitstream failed"); + } + bitstream->stream_buffer = (unsigned char *)local_alloc( + MAX_CODED_FRAME_SIZE, + sizeof(unsigned char)); + if (bitstream->stream_buffer == NULL) { + io_printf( + "AllocBitstream: Memory allocation for streamBuffer failed"); + } + + return bitstream; +} + +void biari_init_context_logac(struct bi_context_type_s *ctx) +{ + ctx->LG_PMPS = (QUARTER << LG_PMPS_SHIFTNO) - 1; + ctx->MPS = 0; + ctx->cycno = 0; +} + +#define BIARI_CTX_INIT1_LOG(jj, ctx)\ +{\ + for (j = 0; j < jj; j++)\ + biari_init_context_logac(&(ctx[j]));\ +} + +#define BIARI_CTX_INIT2_LOG(ii, jj, ctx)\ +{\ + for (i = 0; i < ii; i++)\ + for (j = 0; j < jj; j++)\ + biari_init_context_logac(&(ctx[i][j]));\ +} + +#define BIARI_CTX_INIT3_LOG(ii, jj, kk, ctx)\ +{\ + for (i = 0; i < ii; i++)\ + for (j = 0; j < jj; j++)\ + for (k = 0; k < kk; k++)\ + biari_init_context_logac(&(ctx[i][j][k]));\ +} + +#define BIARI_CTX_INIT4_LOG(ii, jj, kk, ll, ctx)\ +{\ + for (i = 0; i < ii; i++)\ + for (j = 0; j < jj; j++)\ + for (k = 0; k < kk; k++)\ + for (l = 0; l < ll; l++)\ + biari_init_context_logac\ + (&(ctx[i][j][k][l]));\ +} + +void init_contexts(struct img_par *img) +{ + struct motion_info_contexts_s *mc = img->current_slice->mot_ctx; + struct texture_info_contexts *tc = img->current_slice->tex_ctx; + int i, j; + +#ifdef DUMP_DEBUG + if (avs_get_debug_flag() & SLICE_INFO_DUMP) + io_printf(" ---- init_contexts ----\n"); +#endif + + BIARI_CTX_INIT2_LOG(3, NUM_MB_TYPE_CTX, mc->mb_type_contexts); + BIARI_CTX_INIT2_LOG(2, NUM_B8_TYPE_CTX, mc->b8_type_contexts); + BIARI_CTX_INIT2_LOG(2, NUM_MV_RES_CTX, mc->mv_res_contexts); + BIARI_CTX_INIT2_LOG(2, NUM_REF_NO_CTX, mc->ref_no_contexts); + BIARI_CTX_INIT1_LOG(NUM_DELTA_QP_CTX, mc->delta_qp_contexts); + BIARI_CTX_INIT1_LOG(NUM_MB_AFF_CTX, mc->mb_aff_contexts); + + BIARI_CTX_INIT1_LOG(NUM_IPR_CTX, tc->ipr_contexts); + BIARI_CTX_INIT1_LOG(NUM_CIPR_CTX, tc->cipr_contexts); + BIARI_CTX_INIT2_LOG(3, NUM_CBP_CTX, tc->cbp_contexts); + BIARI_CTX_INIT2_LOG(NUM_BLOCK_TYPES, NUM_BCBP_CTX, tc->bcbp_contexts); + BIARI_CTX_INIT2_LOG(NUM_BLOCK_TYPES, NUM_ONE_CTX, tc->one_contexts); + BIARI_CTX_INIT2_LOG(NUM_BLOCK_TYPES, NUM_ABS_CTX, tc->abs_contexts); + BIARI_CTX_INIT2_LOG(NUM_BLOCK_TYPES, NUM_MAP_CTX, tc->fld_map_contexts); + BIARI_CTX_INIT2_LOG(NUM_BLOCK_TYPES, NUM_LAST_CTX, + tc->fld_last_contexts); + BIARI_CTX_INIT2_LOG(NUM_BLOCK_TYPES, NUM_MAP_CTX, tc->map_contexts); + BIARI_CTX_INIT2_LOG(NUM_BLOCK_TYPES, NUM_LAST_CTX, tc->last_contexts); +#ifdef TEST_WEIGHTING_AEC + biari_init_context_logac(&mc->mb_weighting_pred); +#endif +} + +/*! + ************************************************************************ + * \brief + * Allocation of contexts models for the motion info + * used for arithmetic decoding + * + ************************************************************************ + */ +struct motion_info_contexts_s *create_contexts_motioninfo(void) +{ + struct motion_info_contexts_s *deco_ctx; + + deco_ctx = (struct motion_info_contexts_s *)local_alloc(1, + sizeof(struct motion_info_contexts_s)); + if (deco_ctx == NULL) + no_mem_exit("create_contexts_motioninfo: deco_ctx"); + + return deco_ctx; +} + +/*! + ************************************************************************ + * \brief + * Allocates of contexts models for the texture info + * used for arithmetic decoding + ************************************************************************ + */ +struct texture_info_contexts *create_contexts_textureinfo(void) +{ + struct texture_info_contexts *deco_ctx; + + deco_ctx = (struct texture_info_contexts *)local_alloc(1, + sizeof(struct texture_info_contexts)); + if (deco_ctx == NULL) + no_mem_exit("create_contexts_textureinfo: deco_ctx"); + + return deco_ctx; +} + +struct datapartition *alloc_partition(int n) +{ + struct datapartition *part_arr, *datapart; + int i; + + part_arr = + (struct datapartition *)local_alloc(n, sizeof(struct datapartition)); + if (part_arr == NULL) { + no_mem_exit( + "alloc_partition: Memory allocation for Data Partition failed"); + } + +#if LIWR_FIX + part_arr[0].bitstream = NULL; +#else + for (i = 0; i < n; i++) { + datapart = &(part_arr[i]); + datapart->bitstream = (struct bitstream_s *)local_alloc(1, + sizeof(struct bitstream_s)); + if (datapart->bitstream == NULL) { + no_mem_exit( + "alloc_partition: Memory allocation for Bitstream failed"); + } + } +#endif + return part_arr; +} + +void malloc_slice(struct img_par *img) +{ + struct slice_s *currslice; + + img->current_slice = + (struct slice_s *)local_alloc(1, sizeof(struct slice_s)); + currslice = img->current_slice; + if (currslice == NULL) + no_mem_exit( + "Memory allocation for struct slice_s datastruct Failed" + ); + if (1) { + + currslice->mot_ctx = create_contexts_motioninfo(); + currslice->tex_ctx = create_contexts_textureinfo(); + } +#if LIWR_FIX + currslice->max_part_nr = 1; +#else + currslice->max_part_nr = 3; +#endif + currslice->part_arr = alloc_partition(currslice->max_part_nr); +} + +void init(struct img_par *img) +{ + int i; + + for (i = 0; i < 256; i++) + img->quad[i] = i * i; +} + +/* + ************************************************************************* + * Function:Allocate 2D memory array -> int array2D[rows][columns] + * Input: + * Output: memory size in bytes + * Return: + * Attention: + ************************************************************************* + */ + +int get_mem2Dint(int ***array2D, int rows, int columns) +{ + int i; + + *array2D = (int **)local_alloc(rows, sizeof(int *)); + if (*array2D == NULL) + no_mem_exit("get_mem2Dint: array2D"); + (*array2D)[0] = (int *)local_alloc(rows * columns, sizeof(int)); + if ((*array2D)[0] == NULL) + no_mem_exit("get_mem2Dint: array2D"); + + for (i = 1; i < rows; i++) + (*array2D)[i] = (*array2D)[i - 1] + columns; + + return rows * columns * sizeof(int); +} + +void initial_decode(void) +{ + int i, j; + int img_height = (vertical_size + img->auto_crop_bottom); + int memory_size = 0; + + malloc_slice(img); + mb_data = (struct macroblock *)local_alloc( + (img->width / MB_BLOCK_SIZE) + * (img_height /*vertical_size*/ + / MB_BLOCK_SIZE), sizeof(struct macroblock)); + if (mb_data == NULL) + no_mem_exit("init_global_buffers: mb_data"); + + if (progressive_sequence) + memory_size += get_mem2Dint(&(img->ipredmode), + img->width / B8_SIZE * 2 + 4, + vertical_size / B8_SIZE * 2 + 4); + else + memory_size += get_mem2Dint(&(img->ipredmode), + img->width / B8_SIZE * 2 + 4, + (vertical_size + 32) / (2 * B8_SIZE) * 4 + 4); + + for (i = 0; i < img->width / (B8_SIZE) * 2 + 4; i++) { + for (j = 0; j < img->height / (B8_SIZE) * 2 + 4; j++) + img->ipredmode[i][j] = -1; + } + + init(img); + img->number = 0; + img->type = I_IMG; + img->imgtr_last_p = 0; + img->imgtr_next_p = 0; + + img->new_seq_header_flag = 1; + img->new_sequence_flag = 1; + +} + +void aec_new_slice(void) +{ + last_dquant = 0; +} + +/*! + ************************************************************************ + * \brief + * Initializes the DecodingEnvironment for the arithmetic coder + ************************************************************************ + */ + +void arideco_start_decoding(struct decoding_environment_s *dep, + unsigned char *cpixcode, + int firstbyte, int *cpixcode_len, int slice_type) +{ + + dcodestrm = cpixcode; + dcodestrm_len = cpixcode_len; + *dcodestrm_len = firstbyte; + + s1 = 0; + t1 = QUARTER - 1; + value_s = 0; + + value_t = 0; + + { + int i; + dbits_to_go = 0; + for (i = 0; i < B_BITS - 1; i++) { + if (--dbits_to_go < 0) + get_byte(); + + value_t = (value_t << 1) + | ((dbuffer >> dbits_to_go) & 0x01); + } + } + + while (value_t < QUARTER) { + if (--dbits_to_go < 0) + get_byte(); + + value_t = (value_t << 1) | ((dbuffer >> dbits_to_go) & 0x01); + value_s++; + } + value_t = value_t & 0xff; + + dec_final = dec_bypass = 0; + + + +} + +/* + ************************************************************************* + * Function:Checks the availability of neighboring macroblocks of + the current macroblock for prediction and context determination; + marks the unavailable MBs for intra prediction in the + ipredmode-array by -1. Only neighboring MBs in the causal + past of the current MB are checked. + * Input: + * Output: + * Return: + * Attention: + ************************************************************************* + */ + +void checkavailabilityofneighbors(struct img_par *img) +{ + int i, j; + const int mb_width = img->width / MB_BLOCK_SIZE; + const int mb_nr = img->current_mb_nr; + struct macroblock *curr_mb = &mb_data[mb_nr]; + int check_value; + int remove_prediction; + + curr_mb->mb_available_up = NULL; + curr_mb->mb_available_left = NULL; + + for (i = 0; i < 3; i++) + for (j = 0; j < 3; j++) + mb_data[mb_nr].mb_available[i][j] = NULL; + + mb_data[mb_nr].mb_available[1][1] = curr_mb; + + if (img->pix_x >= MB_BLOCK_SIZE) { + remove_prediction = curr_mb->slice_nr + != mb_data[mb_nr - 1].slice_nr; + + if (remove_prediction) + + { + + img->ipredmode[(img->block_x + 1) * 2 - 1][(img->block_y + + 1) * 2] = -1; + img->ipredmode[(img->block_x + 1) * 2 - 1][(img->block_y + + 1) * 2 + 1] = -1; + img->ipredmode[(img->block_x + 1) * 2 - 1][(img->block_y + + 2) * 2] = -1; + img->ipredmode[(img->block_x + 1) * 2 - 1][(img->block_y + + 2) * 2 + 1] = -1; + } + if (!remove_prediction) + curr_mb->mb_available[1][0] = &(mb_data[mb_nr - 1]); + + } + + check_value = (img->pix_y >= MB_BLOCK_SIZE); + if (check_value) { + remove_prediction = curr_mb->slice_nr + != mb_data[mb_nr - mb_width].slice_nr; + + if (remove_prediction) { + img->ipredmode + [(img->block_x + 1) * 2][(img->block_y + 1) + * 2 - 1] = -1; + img->ipredmode[(img->block_x + 1) * 2 + 1][(img->block_y + + 1) * 2 - 1] = -1; + img->ipredmode[(img->block_x + 1) * 2 + 2][(img->block_y + + 1) * 2 - 1] = -1; + img->ipredmode[(img->block_x + 1) * 2 + 3][(img->block_y + + 1) * 2 - 1] = -1; + } + + if (!remove_prediction) { + curr_mb->mb_available[0][1] = + &(mb_data[mb_nr - mb_width]); + } + } + + if (img->pix_y >= MB_BLOCK_SIZE && img->pix_x >= MB_BLOCK_SIZE) { + remove_prediction = curr_mb->slice_nr + != mb_data[mb_nr - mb_width - 1].slice_nr; + + if (remove_prediction) { + img->ipredmode[img->block_x * 2 + 1][img->block_y * 2 + + 1] = -1; + } + if (!remove_prediction) { + curr_mb->mb_available[0][0] = &(mb_data[mb_nr - mb_width + - 1]); + } + } + + if (img->pix_y >= MB_BLOCK_SIZE + && img->pix_x < (img->width - MB_BLOCK_SIZE)) { + if (curr_mb->slice_nr == mb_data[mb_nr - mb_width + 1].slice_nr) + curr_mb->mb_available[0][2] = &(mb_data[mb_nr - mb_width + + 1]); + } + + if (1) { + curr_mb->mbaddr_a = mb_nr - 1; + curr_mb->mbaddr_b = mb_nr - img->pic_width_inmbs; + curr_mb->mbaddr_c = mb_nr - img->pic_width_inmbs + 1; + curr_mb->mbaddr_d = mb_nr - img->pic_width_inmbs - 1; + + curr_mb->mbavail_a = + (curr_mb->mb_available[1][0] != NULL) ? 1 : 0; + curr_mb->mbavail_b = + (curr_mb->mb_available[0][1] != NULL) ? 1 : 0; + curr_mb->mbavail_c = + (curr_mb->mb_available[0][2] != NULL) ? 1 : 0; + curr_mb->mbavail_d = + (curr_mb->mb_available[0][0] != NULL) ? 1 : 0; + + } + +} + +void checkavailabilityofneighborsaec(void) +{ + + int i, j; + const int mb_width = img->width / MB_BLOCK_SIZE; + const int mb_nr = img->current_mb_nr; + struct macroblock *curr_mb = &(mb_data[mb_nr]); + int check_value; + for (i = 0; i < 3; i++) + for (j = 0; j < 3; j++) + mb_data[mb_nr].mb_available[i][j] = NULL; + mb_data[mb_nr].mb_available[1][1] = &(mb_data[mb_nr]); + + if (img->pix_x >= MB_BLOCK_SIZE) { + int remove_prediction = curr_mb->slice_nr + != mb_data[mb_nr - 1].slice_nr; + if (!remove_prediction) + curr_mb->mb_available[1][0] = &(mb_data[mb_nr - 1]); + } + + check_value = (img->pix_y >= MB_BLOCK_SIZE); + if (check_value) { + int remove_prediction = curr_mb->slice_nr + != mb_data[mb_nr - mb_width].slice_nr; + + if (!remove_prediction) { + curr_mb->mb_available[0][1] = + &(mb_data[mb_nr - mb_width]); + } + } + + if (img->pix_y >= MB_BLOCK_SIZE && img->pix_x >= MB_BLOCK_SIZE) { + int remove_prediction = curr_mb->slice_nr + != mb_data[mb_nr - mb_width - 1].slice_nr; + if (!remove_prediction) { + curr_mb->mb_available[0][0] = &(mb_data[mb_nr - mb_width + - 1]); + } + } + + if (img->pix_y >= MB_BLOCK_SIZE + && img->pix_x < (img->width - MB_BLOCK_SIZE)) { + if (curr_mb->slice_nr == mb_data[mb_nr - mb_width + 1].slice_nr) + curr_mb->mb_available[0][2] = &(mb_data[mb_nr - mb_width + + 1]); + } + curr_mb->mb_available_left = curr_mb->mb_available[1][0]; + curr_mb->mb_available_up = curr_mb->mb_available[0][1]; + curr_mb->mbaddr_a = mb_nr - 1; + curr_mb->mbaddr_b = mb_nr - img->pic_width_inmbs; + curr_mb->mbaddr_c = mb_nr - img->pic_width_inmbs + 1; + curr_mb->mbaddr_d = mb_nr - img->pic_width_inmbs - 1; + + curr_mb->mbavail_a = (curr_mb->mb_available[1][0] != NULL) ? 1 : 0; + curr_mb->mbavail_b = (curr_mb->mb_available[0][1] != NULL) ? 1 : 0; + curr_mb->mbavail_c = (curr_mb->mb_available[0][2] != NULL) ? 1 : 0; + curr_mb->mbavail_d = (curr_mb->mb_available[0][0] != NULL) ? 1 : 0; +} + +/* + ************************************************************************* + * Function:initializes the current macroblock + * Input: + * Output: + * Return: + * Attention: + ************************************************************************* + */ + +void start_macroblock(struct img_par *img) +{ + int i, j, k, l; + struct macroblock *curr_mb; + +#ifdef AVSP_LONG_CABAC +#else + +#endif + + curr_mb = &mb_data[img->current_mb_nr]; + + /* Update coordinates of the current macroblock */ + img->mb_x = (img->current_mb_nr) % (img->width / MB_BLOCK_SIZE); + img->mb_y = (img->current_mb_nr) / (img->width / MB_BLOCK_SIZE); + +#ifdef DUMP_DEBUG + if (avs_get_debug_flag() & MB_NUM_DUMP) + io_printf(" #Begin MB : %d, (%x, %x) es_ptr %d\n", + img->current_mb_nr, img->mb_x, img->mb_y, es_ptr); +#endif + + + total_mb_count = total_mb_count + 1; + + /* Define vertical positions */ + img->block_y = img->mb_y * BLOCK_SIZE / 2; /* luma block position */ + img->block8_y = img->mb_y * BLOCK_SIZE / 2; + img->pix_y = img->mb_y * MB_BLOCK_SIZE; /* luma macroblock position */ + if (chroma_format == 2) + img->pix_c_y = img->mb_y * + MB_BLOCK_SIZE; /* chroma macroblock position */ + else + img->pix_c_y = img->mb_y * + MB_BLOCK_SIZE / 2; /* chroma macroblock position */ + + /* Define horizontal positions */ + img->block_x = img->mb_x * BLOCK_SIZE / 2; /* luma block position */ + img->block8_x = img->mb_x * BLOCK_SIZE / 2; + img->pix_x = img->mb_x * MB_BLOCK_SIZE; /* luma pixel position */ + img->pix_c_x = img->mb_x * + MB_BLOCK_SIZE / 2; /* chroma pixel position */ + + checkavailabilityofneighbors(img); + + /*<!*******EDIT START BY lzhang ******************/ + + if (1) + checkavailabilityofneighborsaec(); + /*<!*******EDIT end BY lzhang ******************/ + + curr_mb->qp = img->qp; + curr_mb->mb_type = 0; + curr_mb->delta_quant = 0; + curr_mb->cbp = 0; + curr_mb->cbp_blk = 0; + curr_mb->c_ipred_mode = DC_PRED_8; + curr_mb->c_ipred_mode_2 = DC_PRED_8; + + for (l = 0; l < 2; l++) + for (j = 0; j < BLOCK_MULTIPLE; j++) + for (i = 0; i < BLOCK_MULTIPLE; i++) + for (k = 0; k < 2; k++) + curr_mb->mvd[l][j][i][k] = 0; + + curr_mb->cbp_bits = 0; + + for (j = 0; j < MB_BLOCK_SIZE; j++) + for (i = 0; i < MB_BLOCK_SIZE; i++) + img->m7[i][j] = 0; + + for (j = 0; j < 2 * BLOCK_SIZE; j++) + for (i = 0; i < 2 * BLOCK_SIZE; i++) { + img->m8[0][i][j] = 0; + img->m8[1][i][j] = 0; + img->m8[2][i][j] = 0; + img->m8[3][i][j] = 0; + } + + curr_mb->lf_disable = 1; + + img->weighting_prediction = 0; +} + +/* + ************************************************************************* + * Function:init macroblock I and P frames + * Input: + * Output: + * Return: + * Attention: + ************************************************************************* + */ + +void init_macroblock(struct img_par *img) +{ + int i, j; + + + for (i = 0; i < 4; i++) { + for (j = 0; j < 4; j++) { + img->ipredmode[img->block_x * 2 + i + 2][img->block_y + * 2 + j + 2] = -1; + } + } + +} + +/* + ************************************************************************* + * Function:Interpret the mb mode for I-Frames + * Input: + * Output: + * Return: + * Attention: + ************************************************************************* + */ + +void interpret_mb_mode_i(struct img_par *img) +{ + int i; + + struct macroblock *curr_mb = &mb_data[img->current_mb_nr]; + int num = 4; + + curr_mb->mb_type = I8MB; + + + current_mb_intra = 1; + + for (i = 0; i < 4; i++) { + curr_mb->b8mode[i] = IBLOCK; + curr_mb->b8pdir[i] = -1; + } + + for (i = num; i < 4; i++) { + curr_mb->b8mode[i] = + curr_mb->mb_type_2 == P8x8 ? + 4 : curr_mb->mb_type_2; + curr_mb->b8pdir[i] = 0; + } +} + +const int pred_4x4[9][9] = {{0, 0, 0, 0, 0, 0, 0, 0, 0}, {1, 1, 1, 1, 1, 1, 1, + 1, 1}, {0, 1, 2, 3, 4, 5, 6, 7, 8}, {0, 0, 0, 3, 3, 3, 3, 3, 3}, + {0, 1, 4, 4, 4, 4, 4, 4, 4}, {0, 1, 5, 5, 5, 5, 5, 5, 5}, {0, 0, + 0, 0, 0, 0, 6, 0, 0}, + {0, 1, 7, 7, 7, 7, 7, 7, 7}, {0, 0, 0, 0, 4, 5, 6, 7, 8} + +}; + +const int pred_4x4to8x8[9] = {0, 1, 2, 3, 4, 1, 0, 1, 0 + +}; + +const int pred_8x8to4x4[5] = {0, 1, 2, 3, 4}; + +void read_ipred_block_modes(struct img_par *img, int b8) +{ + int bi, bj, dec; + struct syntaxelement curr_se; + struct macroblock *curr_mb; + int j2; + int mostprobableintrapredmode; + int upintrapredmode; + int uprightintrapredmode; + int leftintrapredmode; + int leftdownintrapredmode; + int intrachromapredmodeflag; + + struct slice_s *currslice = img->current_slice; + struct datapartition *dp; + + curr_mb = mb_data + img->current_mb_nr; + intrachromapredmodeflag = IS_INTRA(curr_mb); + + curr_se.type = SE_INTRAPREDMODE; +#if TRACE + strncpy(curr_se.tracestring, "Ipred Mode", TRACESTRING_SIZE); +#endif + + if (b8 < 4) { + if (curr_mb->b8mode[b8] == IBLOCK) { + intrachromapredmodeflag = 1; + + if (1) { + dp = &(currslice->part_arr[0]); + curr_se.reading = read_intrapredmode_aec; + dp->read_syntax_element(&curr_se, img, dp); + + if (curr_se.value1 == -1) + push_es(1, 1); + else + push_es(curr_se.value1, 3); + + + } + bi = img->block_x + (b8 & 1); + bj = img->block_y + (b8 / 2); + + upintrapredmode = img->ipredmode[(bi + 1) * 2][(bj) * 2 + + 1]; + uprightintrapredmode = + img->ipredmode[(bi + 1) * 2 + 1][(bj) + * 2 + 1]; + leftintrapredmode = + img->ipredmode[(bi) * 2 + 1][(bj + 1) + * 2]; + leftdownintrapredmode = img->ipredmode[(bi) * 2 + 1][(bj + + 1) * 2 + 1]; + + if ((upintrapredmode < 0) || (leftintrapredmode < 0)) { + mostprobableintrapredmode = DC_PRED; + } else if ((upintrapredmode < NO_INTRA_PMODE) + && (leftintrapredmode < + NO_INTRA_PMODE)) { + mostprobableintrapredmode = + upintrapredmode + < leftintrapredmode ? + upintrapredmode : + leftintrapredmode; + } else if (upintrapredmode < NO_INTRA_PMODE) { + mostprobableintrapredmode = upintrapredmode; + } else if (leftintrapredmode < NO_INTRA_PMODE) { + mostprobableintrapredmode = leftintrapredmode; + } else { + mostprobableintrapredmode = + pred_4x4[leftintrapredmode + - INTRA_PMODE_4x4][upintrapredmode + - INTRA_PMODE_4x4]; + mostprobableintrapredmode = + pred_4x4to8x8[mostprobableintrapredmode]; + } + + + + dec = + (curr_se.value1 == -1) ? + mostprobableintrapredmode : + curr_se.value1 + + (curr_se.value1 + >= mostprobableintrapredmode); + +#ifdef DUMP_DEBUG + if (avs_get_debug_flag() & MB_INFO_DUMP) + io_printf(" - ipredmode[%d] : %d\n", b8, dec); +#endif + + img->ipredmode[(1 + bi) * 2][(1 + bj) * 2] = dec; + img->ipredmode[(1 + bi) * 2 + 1][(1 + bj) * 2] = dec; + img->ipredmode[(1 + bi) * 2][(1 + bj) * 2 + 1] = dec; + img->ipredmode[(1 + bi) * 2 + 1][(1 + bj) * 2 + 1] = + dec; + + j2 = bj; + } + } else if (b8 == 4 && curr_mb->b8mode[b8 - 3] == IBLOCK) { + + curr_se.type = SE_INTRAPREDMODE; +#if TRACE + strncpy(curr_se.tracestring, + "Chroma intra pred mode", TRACESTRING_SIZE); +#endif + + if (1) { + dp = &(currslice->part_arr[0]); + curr_se.reading = read_cipredmode_aec; + dp->read_syntax_element(&curr_se, img, dp); + } else + + { + } + curr_mb->c_ipred_mode = curr_se.value1; + + push_es(UE[curr_se.value1][0], UE[curr_se.value1][1]); + +#ifdef DUMP_DEBUG + if (avs_get_debug_flag() & MB_INFO_DUMP) + io_printf(" * UE c_ipred_mode read : %d\n", + curr_mb->c_ipred_mode); +#endif + + if (curr_se.value1 < DC_PRED_8 || curr_se.value1 > PLANE_8) { +#ifdef DUMP_DEBUG + if (avs_get_debug_flag() & MB_INFO_DUMP) + io_printf("%d\n", img->current_mb_nr); +#endif + pr_info("illegal chroma intra pred mode!\n"); + } + } +} + +/*! + ************************************************************************ + * \brief + * This function is used to arithmetically decode the coded + * block pattern of a given MB. + ************************************************************************ + */ +void readcp_aec(struct syntaxelement *se, struct img_par *img, + struct decoding_environment_s *dep_dp) +{ + struct texture_info_contexts *ctx = img->current_slice->tex_ctx; + struct macroblock *curr_mb = &mb_data[img->current_mb_nr]; + + int mb_x, mb_y; + int a, b; + int curr_cbp_ctx, curr_cbp_idx; + int cbp = 0; + int cbp_bit; + int mask; + + for (mb_y = 0; mb_y < 4; mb_y += 2) { + for (mb_x = 0; mb_x < 4; mb_x += 2) { + if (curr_mb->b8mode[mb_y + (mb_x / 2)] == IBLOCK) + curr_cbp_idx = 0; + else + curr_cbp_idx = 1; + + if (mb_y == 0) { + if (curr_mb->mb_available_up == NULL) + b = 0; + else { + b = ((((curr_mb->mb_available_up)->cbp + & (1 << (2 + mb_x / 2))) + == 0) ? 1 : 0); + } + + } else + b = (((cbp & (1 << (mb_x / 2))) == 0) ? 1 : 0); + + if (mb_x == 0) { + if (curr_mb->mb_available_left == NULL) + a = 0; + else { + a = + ((((curr_mb->mb_available_left)->cbp + & (1 + << (2 + * (mb_y + / 2) + + 1))) + == 0) ? + 1 : 0); + } + } else + a = (((cbp & (1 << mb_y)) == 0) ? 1 : 0); + curr_cbp_ctx = a + 2 * b; + mask = (1 << (mb_y + mb_x / 2)); + cbp_bit = biari_decode_symbol(dep_dp, + ctx->cbp_contexts[0] + curr_cbp_ctx); + + if (cbp_bit) + cbp += mask; + } + } + curr_cbp_ctx = 0; + cbp_bit = biari_decode_symbol(dep_dp, + ctx->cbp_contexts[1] + curr_cbp_ctx); + + if (cbp_bit) { + curr_cbp_ctx = 1; + cbp_bit = biari_decode_symbol(dep_dp, + ctx->cbp_contexts[1] + curr_cbp_ctx); + if (cbp_bit) { + cbp += 48; + + } else { + curr_cbp_ctx = 1; + cbp_bit = biari_decode_symbol(dep_dp, + ctx->cbp_contexts[1] + curr_cbp_ctx); + cbp += (cbp_bit == 1) ? 32 : 16; + + } + } + + se->value1 = cbp; + if (!cbp) + last_dquant = 0; + + + +} + +/*! + ************************************************************************ + * \brief + * This function is used to arithmetically decode the delta qp + * of a given MB. + ************************************************************************ + */ +void readdquant_aec(struct syntaxelement *se, struct img_par *img, + struct decoding_environment_s *dep_dp) +{ + struct motion_info_contexts_s *ctx = img->current_slice->mot_ctx; + + int act_ctx; + int act_sym; + int dquant; + + + act_ctx = ((last_dquant != 0) ? 1 : 0); + + act_sym = 1 + - biari_decode_symbol(dep_dp, + ctx->delta_qp_contexts + act_ctx); + if (act_sym != 0) { + act_ctx = 2; + act_sym = unary_bin_decode(dep_dp, + ctx->delta_qp_contexts + act_ctx, 1); + act_sym++; + } + act_sym &= 0x3f; + push_es(UE[act_sym][0], UE[act_sym][1]); + + dquant = (act_sym + 1) / 2; + if ((act_sym & 0x01) == 0) + dquant = -dquant; + se->value1 = dquant; + + last_dquant = dquant; + +} + +int csyntax; + +#define CHECKDELTAQP {\ + if (img->qp+curr_mb->delta_quant > 63\ + || img->qp+curr_mb->delta_quant < 0) {\ + csyntax = 0;\ + transcoding_error_flag = 1;\ + io_printf("error(0) (%3d|%3d) @ MB%d\n",\ + curr_mb->delta_quant,\ + img->qp+curr_mb->delta_quant,\ + img->picture_structure == 0 \ + ? img->current_mb_nr_fld : img->current_mb_nr);\ + } } + +int dct_level[65]; +int dct_run[65]; +int pair_pos; +int dct_pairs = -1; +const int t_chr[5] = {0, 1, 2, 4, 3000}; + +void readrunlevel_aec_ref(struct syntaxelement *se, struct img_par *img, + struct decoding_environment_s *dep_dp) +{ + int pairs, rank, pos; + int run, level, abslevel, symbol; + int sign; + + if (dct_pairs < 0) { + struct bi_context_type_s (*primary)[NUM_MAP_CTX]; + struct bi_context_type_s *pctx; + struct bi_context_type_s *pCTX2; + int ctx, ctx2, offset; + if (se->context == LUMA_8x8) { + if (img->picture_structure == 0) { + primary = + img->current_slice->tex_ctx->fld_map_contexts; + } else { + primary = + img->current_slice->tex_ctx->map_contexts; + } + } else { + if (img->picture_structure == 0) { + primary = + img->current_slice->tex_ctx->fld_last_contexts; + } else { + primary = + img->current_slice->tex_ctx->last_contexts; + } + } + + rank = 0; + pos = 0; + for (pairs = 0; pairs < 65; pairs++) { +#ifdef DECODING_SANITY_CHECK + /*max index is NUM_BLOCK_TYPES - 1*/ + pctx = primary[rank & 0x7]; +#else + pctx = primary[rank]; +#endif + if (rank > 0) { +#ifdef DECODING_SANITY_CHECK + /*max index is NUM_BLOCK_TYPES - 1*/ + pCTX2 = primary[(5 + (pos >> 5)) & 0x7]; +#else + pCTX2 = primary[5 + (pos >> 5)]; +#endif + ctx2 = (pos >> 1) & 0x0f; + ctx = 0; + + + if (biari_decode_symbolw(dep_dp, pctx + ctx, + pCTX2 + ctx2)) { + break; + } + } + + ctx = 1; + symbol = 0; + while (biari_decode_symbol(dep_dp, pctx + ctx) == 0) { + symbol += 1; + ctx++; + if (ctx >= 2) + ctx = 2; + } + abslevel = symbol + 1; + + if (biari_decode_symbol_eq_prob(dep_dp)) { + level = -abslevel; + sign = 1; + } else { + level = abslevel; + sign = 0; + } +#if TRACE + tracebits2("level", 1, level); +#endif + + if (abslevel == 1) + offset = 4; + else + offset = 6; + symbol = 0; + ctx = 0; + while (biari_decode_symbol(dep_dp, pctx + ctx + offset) + == 0) { + symbol += 1; + ctx++; + if (ctx >= 1) + ctx = 1; + } + run = symbol; + +#if TRACE + tracebits2("run", 1, run); +#endif + dct_level[pairs] = level; + dct_run[pairs] = run; + if (abslevel > t_chr[rank]) { + if (abslevel <= 2) + rank = abslevel; + else if (abslevel <= 4) + rank = 3; + else + rank = 4; + } + pos += (run + 1); + if (pos >= 64) + pos = 63; + } + dct_pairs = pairs; + pair_pos = dct_pairs; + } + + if (dct_pairs > 0) { + se->value1 = dct_level[pair_pos - 1]; + se->value2 = dct_run[pair_pos - 1]; + pair_pos--; + } else { + + se->value1 = se->value2 = 0; + } + + if ((dct_pairs--) == 0) + pair_pos = 0; +} + +int b8_ctr; +#if 0 +int curr_residual_chroma[4][16][16]; +int curr_residual_luma[16][16]; +#endif + +const int SCAN[2][64][2] = {{{0, 0}, {0, 1}, {0, 2}, {1, 0}, {0, 3}, {0, 4}, {1, + 1}, {1, 2}, {0, 5}, {0, 6}, {1, 3}, {2, 0}, {2, 1}, {0, 7}, {1, + 4}, {2, 2}, {3, 0}, {1, 5}, {1, 6}, {2, 3}, {3, 1}, {3, 2}, {4, + 0}, {1, 7}, {2, 4}, {4, 1}, {2, 5}, {3, 3}, {4, 2}, {2, 6}, {3, + 4}, {4, 3}, {5, 0}, {5, 1}, {2, 7}, {3, 5}, {4, 4}, {5, 2}, {6, + 0}, {5, 3}, {3, 6}, {4, 5}, {6, 1}, {6, 2}, {5, 4}, {3, 7}, {4, + 6}, {6, 3}, {5, 5}, {4, 7}, {6, 4}, {5, 6}, {6, 5}, {5, 7}, {6, + 6}, {7, 0}, {6, 7}, {7, 1}, {7, 2}, {7, 3}, {7, 4}, {7, 5}, {7, + 6}, {7, 7} }, {{0, 0}, {1, 0}, {0, 1}, {0, 2}, {1, 1}, {2, 0}, { + 3, 0}, {2, 1}, {1, 2}, {0, 3}, {0, 4}, {1, 3}, {2, 2}, {3, 1}, { + 4, 0}, {5, 0}, {4, 1}, {3, 2}, {2, 3}, {1, 4}, {0, 5}, {0, 6}, { + 1, 5}, {2, 4}, {3, 3}, {4, 2}, {5, 1}, {6, 0}, {7, 0}, {6, 1}, { + 5, 2}, {4, 3}, {3, 4}, {2, 5}, {1, 6}, {0, 7}, {1, 7}, {2, 6}, { + 3, 5}, {4, 4}, {5, 3}, {6, 2}, {7, 1}, {7, 2}, {6, 3}, {5, 4}, { + 4, 5}, {3, 6}, {2, 7}, {3, 7}, {4, 6}, {5, 5}, {6, 4}, {7, 3}, { + 7, 4}, {6, 5}, {5, 6}, {4, 7}, {5, 7}, {6, 6}, {7, 5}, {7, 6}, { + 6, 7}, {7, 7} } }; + +const int SCAN_4x4[16][2] = {{0, 0}, {1, 0}, {0, 1}, {0, 2}, {1, 1}, {2, 0}, {3, + 0}, {2, 1}, {1, 2}, {0, 3}, {1, 3}, {2, 2}, {3, 1}, {3, 2}, {2, + 3}, {3, 3} }; + +/* + ************************************************************************* + * Function: + * Input: + * Output: + * Return: + * Attention: + ************************************************************************* + */ + +void encode_golomb_word(unsigned int symbol, unsigned int grad0, + unsigned int max_levels, unsigned int *res_bits, + unsigned int *res_len) +{ + unsigned int level, res, numbits; + + res = 1UL << grad0; + level = 1UL; + numbits = 1UL + grad0; + + while (symbol >= res && level < max_levels) { + symbol -= res; + res = res << 1; + level++; + numbits += 2UL; + } + + if (level >= max_levels) { + if (symbol >= res) + symbol = res - 1UL; + } + + *res_bits = res | symbol; + *res_len = numbits; +} + +/* + ************************************************************************* + * Function: + * Input: + * Output: + * Return: + * Attention: + ************************************************************************* + */ + +void encode_multilayer_golomb_word(unsigned int symbol, + const unsigned int *grad, const unsigned int *max_levels, + unsigned int *res_bits, unsigned int *res_len) +{ + unsigned accbits, acclen, bits, len, tmp; + + accbits = acclen = 0UL; + + while (1) { + encode_golomb_word(symbol, *grad, *max_levels, &bits, &len); + accbits = (accbits << len) | bits; + acclen += len; +#ifdef AVSP_LONG_CABAC +#else + assert(acclen <= 32UL); +#endif + tmp = *max_levels - 1UL; + + if (!((len == (tmp << 1) + (*grad)) + && (bits == (1UL << (tmp + *grad)) - 1UL))) + break; + + tmp = *max_levels; + symbol -= (((1UL << tmp) - 1UL) << (*grad)) - 1UL; + grad++; + max_levels++; + } + *res_bits = accbits; + *res_len = acclen; +} + +/* + ************************************************************************* + * Function: + * Input: + * Output: + * Return: + * Attention: + ************************************************************************* + */ + +int writesyntaxelement_golomb(struct syntaxelement *se, int write_to_stream) +{ + unsigned int bits, len, i; + unsigned int grad[4], max_lev[4]; + + if (!(se->golomb_maxlevels & ~0xFF)) + encode_golomb_word(se->value1, se->golomb_grad, + se->golomb_maxlevels, &bits, &len); + else { + for (i = 0UL; i < 4UL; i++) { + grad[i] = (se->golomb_grad >> (i << 3)) & 0xFFUL; + max_lev[i] = (se->golomb_maxlevels >> (i << 3)) + & 0xFFUL; + } + encode_multilayer_golomb_word(se->value1, grad, max_lev, &bits, + &len); + } + + se->len = len; + se->bitpattern = bits; + + if (write_to_stream) + push_es(bits, len); + return se->len; +} + +/* + ************************************************************************* + * Function:Get coded block pattern and coefficients (run/level) + from the bitstream + * Input: + * Output: + * Return: + * Attention: + ************************************************************************* + */ + +void read_cbpandcoeffsfrom_nal(struct img_par *img) +{ + + int tablenum; + int inumblk; + int inumcoeff; + int symbol2D; + int escape_level_diff; + const int (*AVS_2DVLC_table_intra)[26][27]; + const int (*AVS_2DVLC_table_chroma)[26][27]; + int write_to_stream; + struct syntaxelement currse_enc; + struct syntaxelement *e_currse = &currse_enc; + + int coeff_save[65][2]; + int coeff_ptr; + + int ii, jj; + int mb_nr = img->current_mb_nr; + + int m2, jg2; + struct macroblock *curr_mb = &mb_data[mb_nr]; + + int block8x8; + + int block_x, block_y; + + struct slice_s *currslice = img->current_slice; + int level, run, coef_ctr, len, k, i0, j0, uv, qp; + + int boff_x, boff_y, start_scan; + struct syntaxelement curr_se; + struct datapartition *dp; + + AVS_2DVLC_table_intra = AVS_2DVLC_INTRA; + AVS_2DVLC_table_chroma = AVS_2DVLC_CHROMA; + write_to_stream = 1; + + dct_pairs = -1; + + curr_mb->qp = img->qp; + qp = curr_mb->qp; + + + for (block_y = 0; block_y < 4; block_y += 2) {/* all modes */ + for (block_x = 0; block_x < 4; block_x += 2) { + block8x8 = 2 * (block_y / 2) + block_x / 2; + if (curr_mb->cbp & (1 << block8x8)) { + tablenum = 0; + inumblk = 1; + inumcoeff = 65; + coeff_save[0][0] = 0; + coeff_save[0][1] = 0; + coeff_ptr = 1; + + b8_ctr = block8x8; + + boff_x = (block8x8 % 2) << 3; + boff_y = (block8x8 / 2) << 3; + + img->subblock_x = boff_x >> 2; + img->subblock_y = boff_y >> 2; + + start_scan = 0; + coef_ctr = start_scan - 1; + level = 1; + img->is_v_block = 0; + img->is_intra_block = IS_INTRA(curr_mb); + for (k = start_scan; + (k < 65) && (level != 0); + k++) { + + curr_se.context = LUMA_8x8; + curr_se.type = + (IS_INTRA(curr_mb)) ? + SE_LUM_AC_INTRA : + SE_LUM_AC_INTER; + + dp = &(currslice->part_arr[0]); + curr_se.reading = + readrunlevel_aec_ref; + dp-> + read_syntax_element(&curr_se, + img, dp); + level = curr_se.value1; + run = curr_se.value2; + len = curr_se.len; + + if (level != 0) { + coeff_save[coeff_ptr][0] = + run; + coeff_save[coeff_ptr][1] = + level; + coeff_ptr++; + } + + + + if (level != 0) {/* leave if len = 1 */ + coef_ctr += run + 1; + if ((img->picture_structure + == FRAME)) { + ii = + SCAN[img->picture_structure] + [coef_ctr][0]; + jj = + SCAN[img->picture_structure] + [coef_ctr][1]; + } else { + ii = + SCAN[img->picture_structure] + [coef_ctr][0]; + jj = + SCAN[img->picture_structure] + [coef_ctr][1]; + } + + } + } + + while (coeff_ptr > 0) { + run = + coeff_save[coeff_ptr + - 1][0]; + level = + coeff_save[coeff_ptr + - 1][1]; + + coeff_ptr--; + + symbol2D = CODE2D_ESCAPE_SYMBOL; + if (level > -27 && level < 27 + && run < 26) { + if (tablenum == 0) + + symbol2D = + AVS_2DVLC_table_intra + [tablenum] + [run][abs( + level) + - 1]; + else + + symbol2D = + AVS_2DVLC_table_intra + [tablenum] + [run][abs( + level)]; + if (symbol2D >= 0 + && level + < 0) + symbol2D++; + if (symbol2D < 0) + + symbol2D = + (CODE2D_ESCAPE_SYMBOL + + (run + << 1) + + ((level + > 0) ? + 1 : + 0)); + } + + else { + + symbol2D = + (CODE2D_ESCAPE_SYMBOL + + (run + << 1) + + ((level + > 0) ? + 1 : + 0)); + } + + + + e_currse->type = SE_LUM_AC_INTER; + e_currse->value1 = symbol2D; + e_currse->value2 = 0; + + e_currse->golomb_grad = + vlc_golomb_order + [0][tablenum][0]; + e_currse->golomb_maxlevels = + vlc_golomb_order + [0][tablenum][1]; + + writesyntaxelement_golomb( + e_currse, + write_to_stream); + + if (symbol2D + >= CODE2D_ESCAPE_SYMBOL) { + + e_currse->type = + SE_LUM_AC_INTER; + e_currse->golomb_grad = + 1; + e_currse->golomb_maxlevels = + 11; + escape_level_diff = + abs( + level) + - ((run + > MaxRun[0][tablenum]) ? + 1 : + refabslevel[tablenum][run]); + e_currse->value1 = + escape_level_diff; + + writesyntaxelement_golomb( + e_currse, + write_to_stream); + + } + + if (abs(level) + > incvlc_intra[tablenum]) { + if (abs(level) <= 2) + tablenum = + abs( + level); + else if (abs(level) <= 4) + tablenum = 3; + else if (abs(level) <= 7) + tablenum = 4; + else if (abs(level) + <= 10) + tablenum = 5; + else + tablenum = 6; + } + } + + + } + } + } + + + + m2 = img->mb_x * 2; + jg2 = img->mb_y * 2; + + + uv = -1; + block_y = 4; +#if 0 + qp = QP_SCALE_CR[curr_mb->qp]; +#endif + for (block_x = 0; block_x < 4; block_x += 2) { + + uv++; + + + b8_ctr = (uv + 4); + if ((curr_mb->cbp >> (uv + 4)) & 0x1) { + + tablenum = 0; + inumblk = 1; + inumcoeff = 65; + coeff_save[0][0] = 0; + coeff_save[0][1] = 0; + coeff_ptr = 1; + + coef_ctr = -1; + level = 1; + img->subblock_x = 0; + img->subblock_y = 0; + curr_se.context = CHROMA_AC; + curr_se.type = (IS_INTRA(curr_mb) ? + SE_CHR_AC_INTRA : + SE_CHR_AC_INTER); + dp = &(currslice->part_arr[0]); + curr_se.reading = readrunlevel_aec_ref; + img->is_v_block = uv; + img->is_intra_block = IS_INTRA(curr_mb); + for (k = 0; (k < 65) && (level != 0); k++) { + + dp->read_syntax_element + (&curr_se, img, dp); + level = curr_se.value1; + run = curr_se.value2; + len = curr_se.len; + + if (level != 0) { + coeff_save[coeff_ptr][0] = run; + coeff_save[coeff_ptr][1] = + level; + coeff_ptr++; + } + + + if (level != 0) { + coef_ctr = coef_ctr + run + 1; + if ((img->picture_structure + == FRAME) + /*&& (!curr_mb->mb_field)*/) { + i0 = + SCAN[img->picture_structure] + [coef_ctr][0]; + j0 = + SCAN[img->picture_structure] + [coef_ctr][1]; + } else { + i0 = + SCAN[img->picture_structure] + [coef_ctr][0]; + j0 = + SCAN[img->picture_structure] + [coef_ctr][1]; + } + + } + } + + while (coeff_ptr > 0) { + + run = coeff_save[coeff_ptr - 1][0]; + level = coeff_save[coeff_ptr - 1][1]; + + coeff_ptr--; + + symbol2D = CODE2D_ESCAPE_SYMBOL; + if (level > -27 && level < 27 + && run < 26) { + if (tablenum == 0) + + symbol2D = + AVS_2DVLC_table_chroma + [tablenum][run][abs( + level) + - 1]; + else + symbol2D = + AVS_2DVLC_table_chroma + [tablenum][run][abs( + level)]; + if (symbol2D >= 0 + && level < 0) + symbol2D++; + if (symbol2D < 0) + symbol2D = + (CODE2D_ESCAPE_SYMBOL + + (run + << 1) + + ((level + > 0) ? + 1 : + 0)); + } + + else { + symbol2D = + (CODE2D_ESCAPE_SYMBOL + + (run + << 1) + + ((level + > 0) ? + 1 : + 0)); + } + + e_currse->type = SE_LUM_AC_INTER; + e_currse->value1 = symbol2D; + e_currse->value2 = 0; + e_currse->golomb_grad = + vlc_golomb_order[2] + [tablenum][0]; + e_currse->golomb_maxlevels = + vlc_golomb_order[2] + [tablenum][1]; + + writesyntaxelement_golomb(e_currse, + write_to_stream); + + /* + if (write_to_stream) + { + bitCount[BITS_COEFF_UV_MB]+=e_currse->len; + e_currse++; + curr_mb->currSEnr++; + } + no_bits+=e_currse->len; + + + if (icoef == 0) break; + */ + + if (symbol2D >= CODE2D_ESCAPE_SYMBOL) { + + e_currse->type = SE_LUM_AC_INTER; + e_currse->golomb_grad = 0; + e_currse->golomb_maxlevels = 11; + escape_level_diff = + abs(level) + - ((run + > MaxRun[2][tablenum]) ? + 1 : + refabslevel[tablenum + + 14][run]); + e_currse->value1 = + escape_level_diff; + + writesyntaxelement_golomb( + e_currse, + write_to_stream); + + } + + if (abs(level) + > incvlc_chroma[tablenum]) { + if (abs(level) <= 2) + tablenum = abs(level); + else if (abs(level) <= 4) + tablenum = 3; + else + tablenum = 4; + } + } + + } + } +} + +/* + ************************************************************************* + * Function:Get the syntax elements from the NAL + * Input: + * Output: + * Return: + * Attention: + ************************************************************************* + */ + +int read_one_macroblock(struct img_par *img) +{ + int i, j; + + struct syntaxelement curr_se; + struct macroblock *curr_mb = &mb_data[img->current_mb_nr]; + + int cabp_flag; + + int tempcbp; + int fixqp; + + struct slice_s *currslice = img->current_slice; + struct datapartition *dp; + + fixqp = (fixed_picture_qp || fixed_slice_qp); + + for (i = 0; i < 8; i++) + for (j = 0; j < 8; j++) { + img->m8[0][i][j] = 0; + img->m8[1][i][j] = 0; + img->m8[2][i][j] = 0; + img->m8[3][i][j] = 0; + } + + current_mb_skip = 0; + + curr_mb->qp = img->qp; + curr_se.type = SE_MBTYPE; + curr_se.mapping = linfo_ue; + + curr_mb->mb_type_2 = 0; + + if (img->type == I_IMG) + curr_mb->mb_type = 0; + + interpret_mb_mode_i(img); + + init_macroblock(img); + + if ((IS_INTRA(curr_mb)) && (img->abt_flag)) { + +#if TRACE + strncpy(curr_se.tracestring, "cabp_flag", TRACESTRING_SIZE); +#endif + + curr_se.len = 1; + curr_se.type = SE_CABP; + read_syntaxelement_flc(&curr_se); + cabp_flag = curr_se.value1; + if (cabp_flag == 0) { + curr_mb->CABP[0] = 0; + curr_mb->CABP[1] = 0; + curr_mb->CABP[2] = 0; + curr_mb->CABP[3] = 0; + } else { + for (i = 0; i < 4; i++) { + curr_se.len = 1; + curr_se.type = SE_CABP; + read_syntaxelement_flc(&curr_se); + curr_mb->CABP[i] = curr_se.value1; + } + } + + } else { + curr_mb->CABP[0] = 0; + curr_mb->CABP[1] = 0; + curr_mb->CABP[2] = 0; + curr_mb->CABP[3] = 0; + + } + + if (IS_INTRA(curr_mb)) { + for (i = 0; i < /*5*/(chroma_format + 4); i++) + + read_ipred_block_modes(img, i); + } + + curr_se.type = SE_CBP_INTRA; + curr_se.mapping = linfo_cbp_intra; + +#if TRACE + snprintf(curr_se.tracestring, TRACESTRING_SIZE, "CBP"); +#endif + + if (img->type == I_IMG || IS_INTER(curr_mb)) { + curr_se.golomb_maxlevels = 0; + + if (1) { + dp = &(currslice->part_arr[0]); + curr_se.reading = readcp_aec; + dp->read_syntax_element(&curr_se, img, dp); + } + + + curr_mb->cbp = curr_se.value1; + push_es(UE[NCBP[curr_se.value1][0]][0], + UE[NCBP[curr_se.value1][0]][1]); + + } + +# if 1 + if (curr_mb->cbp != 0) + tempcbp = 1; + else + tempcbp = 0; +#else + + if (chroma_format == 2) { +#if TRACE + snprintf(curr_se.tracestring, TRACESTRING_SIZE, "CBP422"); +#endif + curr_se.mapping = /*linfo_se*/linfo_ue; + curr_se.type = SE_CBP_INTRA; + readsyntaxelement_uvlc(&curr_se, inp); + curr_mb->cbp01 = curr_se.value1; + io_printf(" * UE cbp01 read : 0x%02X\n", curr_mb->cbp01); + } + + if (chroma_format == 2) { + if (curr_mb->cbp != 0 || curr_mb->cbp01 != 0) + tempcbp = 1; + else + tempcbp = 0; + + } else { + if (curr_mb->cbp != 0) + tempcbp = 1; + else + tempcbp = 0; + } + +#endif + + if (IS_INTRA(curr_mb) && (img->abt_flag) && (curr_mb->cbp & (0xF))) { + curr_mb->CABT[0] = curr_mb->CABP[0]; + curr_mb->CABT[1] = curr_mb->CABP[1]; + curr_mb->CABT[2] = curr_mb->CABP[2]; + curr_mb->CABT[3] = curr_mb->CABP[3]; + } else { + + curr_mb->CABT[0] = 0; + curr_mb->CABT[1] = 0; + curr_mb->CABT[2] = 0; + curr_mb->CABT[3] = 0; + + if (!fixqp && (tempcbp)) { + if (IS_INTER(curr_mb)) + curr_se.type = SE_DELTA_QUANT_INTER; + else + curr_se.type = SE_DELTA_QUANT_INTRA; + +#if TRACE + snprintf(curr_se.tracestring, + TRACESTRING_SIZE, "Delta quant "); +#endif + + if (1) { + dp = &(currslice->part_arr[0]); + curr_se.reading = readdquant_aec; + dp->read_syntax_element(&curr_se, img, dp); + } + + curr_mb->delta_quant = curr_se.value1; +#ifdef DUMP_DEBUG + if (avs_get_debug_flag() & MB_INFO_DUMP) { + io_printf(" * SE delta_quant read : %d\n", + curr_mb->delta_quant); + } +#endif + CHECKDELTAQP + + img->qp = (img->qp - MIN_QP + curr_mb->delta_quant + + (MAX_QP - MIN_QP + 1)) + % (MAX_QP - MIN_QP + 1) + MIN_QP; + } + + if (fixqp) { + curr_mb->delta_quant = 0; + img->qp = (img->qp - MIN_QP + curr_mb->delta_quant + + (MAX_QP - MIN_QP + 1)) + % (MAX_QP - MIN_QP + 1) + MIN_QP; + + } +#ifdef DUMP_DEBUG + if (avs_get_debug_flag() & MB_INFO_DUMP) + io_printf(" - img->qp : %d\n", img->qp); +#endif + } + + read_cbpandcoeffsfrom_nal(img); + return DECODE_MB; +} + +/*! + ************************************************************************ + * \brief + * finding end of a slice in case this is not the end of a frame + * + * Unsure whether the "correction" below actually solves an off-by-one + * problem or whether it introduces one in some cases :-( Anyway, + * with this change the bit stream format works with AEC again. + * StW, 8.7.02 + ************************************************************************ + */ +int aec_startcode_follows(struct img_par *img, int eos_bit) +{ + struct slice_s *currslice = img->current_slice; + struct datapartition *dp; + unsigned int bit; + struct decoding_environment_s *dep_dp; + + dp = &(currslice->part_arr[0]); + dep_dp = &(dp->de_aec); + + if (eos_bit) + bit = biari_decode_final(dep_dp); + else + bit = 0; + + return bit == 1 ? 1 : 0; +} + +#ifdef AVSP_LONG_CABAC +int process_long_cabac(void) +#else +void main(void) +#endif +{ + int data32; + int current_header; + int i; + int tmp; + + int byte_startposition; + int aec_mb_stuffing_bit; + struct slice_s *currslice; +#ifdef PERFORMANCE_DEBUG + pr_info("enter %s\r\n", __func__); +#endif + transcoding_error_flag = 0; + es_buf = es_write_addr_virt; + + if (local_heap_init(MAX_CODED_FRAME_SIZE * 4) < 0) + return -1; + + img = (struct img_par *)local_alloc(1, sizeof(struct img_par)); + if (img == NULL) + no_mem_exit("main: img"); + stat_bits_ptr = (struct stat_bits *)local_alloc(1, + sizeof(struct stat_bits)); + if (stat_bits_ptr == NULL) + no_mem_exit("main: stat_bits"); + + curr_stream = alloc_bitstream(); + + chroma_format = 1; + demulate_enable = 0; + img->seq_header_indicate = 1; + +#ifdef AVSP_LONG_CABAC + data32 = READ_VREG(LONG_CABAC_REQ); + progressive_sequence = (data32 >> 1) & 1; + fixed_picture_qp = (data32 >> 2) & 1; + img->picture_structure = (data32 >> 3) & 1; + img->type = (data32 >> 4) & 3; + skip_mode_flag = (data32 >> 6) & 1; + + src_start = READ_VREG(LONG_CABAC_SRC_ADDR); + des_start = READ_VREG(LONG_CABAC_DES_ADDR); + + data32 = READ_VREG(LONG_CABAC_PIC_SIZE); + horizontal_size = (data32 >> 0) & 0xffff; + vertical_size = (data32 >> 16) & 0xffff; + + vld_mem_start_addr = READ_VREG(VLD_MEM_VIFIFO_START_PTR); + vld_mem_end_addr = READ_VREG(VLD_MEM_VIFIFO_END_PTR); + +#else + progressive_sequence = 0; + fixed_picture_qp = 0; + img->picture_structure = 0; + img->type = I_IMG; + skip_mode_flag = 1; + horizontal_size = 1920; + vertical_size = 1080; + + src_start = 0; +#endif + + if (horizontal_size % 16 != 0) + img->auto_crop_right = 16 - (horizontal_size % 16); + else + img->auto_crop_right = 0; + + if (!progressive_sequence) { + if (vertical_size % 32 != 0) + img->auto_crop_bottom = 32 - (vertical_size % 32); + else + img->auto_crop_bottom = 0; + } else { + if (vertical_size % 16 != 0) + img->auto_crop_bottom = 16 - (vertical_size % 16); + else + img->auto_crop_bottom = 0; + } + + img->width = (horizontal_size + img->auto_crop_right); + if (img->picture_structure) + img->height = (vertical_size + img->auto_crop_bottom); + else + img->height = (vertical_size + img->auto_crop_bottom) / 2; + img->width_cr = (img->width >> 1); + + img->pic_width_inmbs = img->width / MB_BLOCK_SIZE; + img->pic_height_inmbs = img->height / MB_BLOCK_SIZE; + img->pic_size_inmbs = img->pic_width_inmbs * img->pic_height_inmbs; + + io_printf( + "[LONG CABAC] Start Transcoding from 0x%x to 0x%x Size : %d x %d\r\n", + src_start, des_start, horizontal_size, vertical_size); +#if 0 + io_printf("VLD_MEM_VIFIFO_START_PTR %x\r\n", + READ_VREG(VLD_MEM_VIFIFO_START_PTR)); + io_printf("VLD_MEM_VIFIFO_CURR_PTR %x\r\n", + READ_VREG(VLD_MEM_VIFIFO_CURR_PTR)); + io_printf("VLD_MEM_VIFIFO_END_PTR %x\r\n", + READ_VREG(VLD_MEM_VIFIFO_END_PTR)); + io_printf("VLD_MEM_VIFIFO_WP %x\r\n", + READ_VREG(VLD_MEM_VIFIFO_WP)); + io_printf("VLD_MEM_VIFIFO_RP %x\r\n", + READ_VREG(VLD_MEM_VIFIFO_RP)); + io_printf("VLD_MEM_VBUF_RD_PTR %x\r\n", + READ_VREG(VLD_MEM_VBUF_RD_PTR)); + io_printf("VLD_MEM_VIFIFO_BUF_CNTL %x\r\n", + READ_VREG(VLD_MEM_VIFIFO_BUF_CNTL)); +#endif + io_printf( + "[LONG CABAC] progressive_sequence : %d, fixed_picture_qp : %d, skip_mode_flag : %d\r\n", + progressive_sequence, fixed_picture_qp, skip_mode_flag); + io_printf("[LONG CABAC] picture_structure : %d, picture_type : %d\r\n", + img->picture_structure, img->type); + + open_irabs(p_irabs); + + initial_decode(); + + init_es(); + + current_header = header(); + io_printf("[LONG CABAC] header Return : %d\n", current_header); + + tmp = slice_header(temp_slice_buf, first_slice_startpos, + first_slice_length); + + init_contexts(img); + aec_new_slice(); + byte_startposition = (curr_stream->frame_bitoffset) / 8; + + currslice = img->current_slice; + + if (1) { + for (i = 0; i < 1; i++) { + img->current_slice->part_arr[i].read_syntax_element = + read_syntaxelement_aec; + img->current_slice->part_arr[i].bitstream = curr_stream; + } + curr_stream = currslice->part_arr[0].bitstream; + } + if ((curr_stream->frame_bitoffset) % 8 != 0) + byte_startposition++; + + arideco_start_decoding(&img->current_slice->part_arr[0].de_aec, + curr_stream->stream_buffer, (byte_startposition), + &(curr_stream->read_len), img->type); + + img->current_mb_nr = 0; + total_mb_count = 0; + while (img->current_mb_nr < img->pic_size_inmbs) + + { + start_macroblock(img); + read_one_macroblock(img); + if (img->cod_counter <= 0) + aec_mb_stuffing_bit = aec_startcode_follows(img, 1); + img->current_mb_nr++; + } + + push_es(0xff, 8); + io_printf(" Total ES_LENGTH : %d\n", es_ptr); + +#ifdef AVSP_LONG_CABAC + push_es(0xff, 64); + + if (transcoding_error_flag == 0) { +#if 1 + dma_sync_single_for_device(amports_get_dma_device(), + es_write_addr_phy, + es_ptr, DMA_TO_DEVICE); + + wmb(); /**/ +#endif + WRITE_VREG(LONG_CABAC_REQ, 0); + } +#else + fclose(f_es); +#endif + + local_heap_uninit(); +#ifdef PERFORMANCE_DEBUG + pr_info("exit %s\r\n", __func__); +#endif + return (transcoding_error_flag == 0) ? 0 : -1; +} +#endif diff --git a/drivers/frame_provider/decoder/h264/vh264.c b/drivers/frame_provider/decoder/h264/vh264.c new file mode 100644 index 0000000..732ee5b --- a/dev/null +++ b/drivers/frame_provider/decoder/h264/vh264.c @@ -0,0 +1,2960 @@ +/* + * 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 "../utils/decoder_bmmu_box.h" +#include "vh264.h" +#include "../../../stream_input/parser/streambuf.h" +#include <linux/delay.h> +#include <linux/amlogic/media/video_sink/video.h> + +#include <linux/amlogic/media/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]; +#define MAX_BLK_BUFFERS (VF_BUF_NUM + 3) +#define FENSE_BUFFER_IDX(n) (VF_BUF_NUM + n) +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 high_bandwith; + +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; +static u32 dpb_size_adj = 6; + +#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; +static unsigned int enable_switch_fense = 1; +#define EN_SWITCH_FENCE() (enable_switch_fense && !is_4k) +#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; +static void *mm_blk_handle; + +/*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; +static uint saved_idc_level; +#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; + +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; +} + +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) + || !EN_SWITCH_FENCE()) + 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_AMLOGIC_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_AMLOGIC_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_AMLOGIC_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_AMLOGIC_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); + if (level_idc > 0) + saved_idc_level = level_idc; + else if (saved_idc_level > 0) + level_idc = saved_idc_level; + 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 || dpb_size_adj) { + /*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 + 5; + if (dpb_size_adj) + actual_dpb_size + = max_reference_size + dpb_size_adj; + if (actual_dpb_size > VF_BUF_NUM) + actual_dpb_size = VF_BUF_NUM; + pr_info + ("actual_dpb_size %d max_ref_size %d\n", + actual_dpb_size, max_reference_size); + } 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; + 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 + buffer_spec[i].alloc_count = page_count; + if (!decoder_bmmu_box_alloc_idx_wait( + mm_blk_handle, + i, + page_count << PAGE_SHIFT, + -1, + -1, + BMMU_ALLOC_FLAGS_WAITCLEAR + )) { + buffer_spec[i].phy_addr = + decoder_bmmu_box_get_phy_addr( + mm_blk_handle, + i); + pr_info("CMA malloc ok %d\n", i); + alloc_count++; + } else { + 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; + } + addr = buffer_spec[i].phy_addr; + } else { + if (buffer_spec[i].phy_addr) { + decoder_bmmu_box_free_idx( + mm_blk_handle, + i); + 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 < h264pts1) { + if (h264_pts_count > 24) { + pr_info("invalid h264pts1, reset\n"); + h264pts1 = pts; + h264_pts_count = 0; + } + } + 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]++; + vf->mem_handle = + decoder_bmmu_box_get_mem_handle( + mm_blk_handle, + 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; + + high_bandwith |= + ((codec_mm_get_total_size() < 80 * SZ_1M) + & ((READ_VREG(AV_SCRATCH_N) & 0xf) == 3) + & ((frame_width * frame_height) >= 1920*1080)); + if (high_bandwith) + vf->flag |= VFRAME_FLAG_HIGH_BANDWIDTH; + + 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; + vf->mem_handle = + decoder_bmmu_box_get_mem_handle( + mm_blk_handle, + buffer_index); + 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]++; + if (high_bandwith) + vf->flag |= VFRAME_FLAG_HIGH_BANDWIDTH; + + p_last_vf = vf; + vf->ready_jiffies64 = jiffies_64; + vf->mem_handle = + decoder_bmmu_box_get_mem_handle( + mm_blk_handle, + buffer_index); + 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; + vf->mem_handle = decoder_bmmu_box_get_mem_handle( + mm_blk_handle, + buffer_index); + 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_AMLOGIC_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_AMLOGIC_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_AMLOGIC_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); + WRITE_VREG(AV_SCRATCH_N, 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); + if (!mm_blk_handle) + mm_blk_handle = decoder_bmmu_box_alloc_box( + DRIVER_NAME, + 0, + MAX_BLK_BUFFERS, + 4 + PAGE_SHIFT, + CODEC_MM_FLAGS_CMA_CLEAR | + CODEC_MM_FLAGS_FOR_VDECODER); + + 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; + high_bandwith = 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; + saved_idc_level = 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; + char *buf = vmalloc(0x1000 * 16); + if (IS_ERR_OR_NULL(buf)) + return -ENOMEM; + + size = get_firmware_data(VIDEO_DEC_H264, buf); + if (size < 0) { + pr_err("get firmware fail."); + vfree(buf); + return -1; + } + + ret = amvdec_loadmc_ex(VFORMAT_H264, NULL, 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); + + 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; + if (enable_switch_fense) { + for (i = 0; i < ARRAY_SIZE(fense_buffer_spec); i++) { + struct buffer_spec_s *s = &fense_buffer_spec[i]; + s->alloc_count = 3 * SZ_1M / PAGE_SIZE; + if (!decoder_bmmu_box_alloc_idx_wait( + mm_blk_handle, + FENSE_BUFFER_IDX(i), + 3 * SZ_1M, + -1, + -1, + BMMU_ALLOC_FLAGS_WAITCLEAR + )) { + s->phy_addr = decoder_bmmu_box_get_phy_addr( + mm_blk_handle, + FENSE_BUFFER_IDX(i)); + } else { + return -ENOMEM; + } + 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_AMLOGIC_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) +{ + + + 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(); + if (mm_blk_handle) { + decoder_bmmu_box_free(mm_blk_handle); + mm_blk_handle = NULL; + } + memset(&fense_buffer_spec, 0, sizeof(fense_buffer_spec)); + memset(&buffer_spec, 0, sizeof(buffer_spec)); + 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_AMLOGIC_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 1 + 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 (EN_SWITCH_FENCE()) { + 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)); + + ge2d_canvas_dup(&csy, &csu, &cyd, + GE2D_FORMAT_M24_NV21, + src_index, + des_index); + } + vf->mem_handle = decoder_bmmu_box_get_mem_handle( + mm_blk_handle, + FENSE_BUFFER_IDX(i)); + fense_vf[i] = *vf; + fense_vf[i].index = -1; + + if (EN_SWITCH_FENCE()) + 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 (NULL == sei_data_buffer) { + 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(); + + 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 + && (codec_mm_get_total_size() > 80 * SZ_1M)) { + amvdec_h264_profile.profile = "4k"; + dpb_size_adj = 0; + } + if (get_cpu_type() <= MESON_CPU_MAJOR_ID_GXBB) + dpb_size_adj = 0; + + 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(); +} + +/****************************************/ + +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(dpb_size_adj, uint, 0664); +MODULE_PARM_DESC(dpb_size_adj, + "\n amvdec_h264 dpb_size_adj\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_param(enable_switch_fense, uint, 0664); +MODULE_PARM_DESC(enable_switch_fense, + "\n enable switch fense\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..45d2849 --- a/dev/null +++ b/drivers/frame_provider/decoder/h264/vh264.h @@ -0,0 +1,25 @@ +/* + * 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 s32 vh264_init(void); */ + +extern s32 vh264_release(void); + +#endif /* VMPEG4_H */ diff --git a/drivers/frame_provider/decoder/h264/vh264_4k2k.c b/drivers/frame_provider/decoder/h264/vh264_4k2k.c new file mode 100644 index 0000000..1c56b03 --- a/dev/null +++ b/drivers/frame_provider/decoder/h264/vh264_4k2k.c @@ -0,0 +1,1839 @@ +/* + * drivers/amlogic/amports/vh264_4k2k.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/timer.h> +#include <linux/kfifo.h> +#include <linux/platform_device.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/delay.h> + +#include <linux/amlogic/media/codec_mm/codec_mm.h> +#include <linux/amlogic/media/video_sink/video_keeper.h> + +#define MEM_NAME "codec_264_4k" + +/* #include <mach/am_regs.h> */ +#if 1 /* MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON8 */ + +#include <linux/amlogic/media/vpu/vpu.h> +#endif + +#include <linux/amlogic/media/utils/vdec_reg.h> +#include "../../../stream_input/amports/amports_priv.h" +#include "../utils/vdec.h" +#include "../utils/amvdec.h" + +#if 0 /* MESON_CPU_TYPE == MESON_CPU_TYPE_MESON6TVD */ +#define DOUBLE_WRITE +#endif + +#define DRIVER_NAME "amvdec_h264_4k2k" +#define MODULE_NAME "amvdec_h264_4k2k" + +#define PUT_INTERVAL (HZ/100) +#define ERROR_RESET_COUNT 500 + +#if 1 /* MESON_CPU_TYPE == MESON_CPU_TYPE_MESONG9TV */ +#define H264_4K2K_SINGLE_CORE 1 +#else +#define H264_4K2K_SINGLE_CORE IS_MESON_M8M2_CPU +#endif + +#define SLICE_TYPE_I 2 + +static int vh264_4k2k_vf_states(struct vframe_states *states, void *); +static struct vframe_s *vh264_4k2k_vf_peek(void *); +static struct vframe_s *vh264_4k2k_vf_get(void *); +static void vh264_4k2k_vf_put(struct vframe_s *, void *); +static int vh264_4k2k_event_cb(int type, void *data, void *private_data); + +static void vh264_4k2k_prot_init(void); +static void vh264_4k2k_local_init(void); +static void vh264_4k2k_put_timer_func(unsigned long arg); + +static const char vh264_4k2k_dec_id[] = "vh264_4k2k-dev"; +static const char vh264_4k2k_dec_id2[] = "vh264_4k2k-vdec2-dev"; + +#define PROVIDER_NAME "decoder.h264_4k2k" + +static const struct vframe_operations_s vh264_4k2k_vf_provider = { + .peek = vh264_4k2k_vf_peek, + .get = vh264_4k2k_vf_get, + .put = vh264_4k2k_vf_put, + .event_cb = vh264_4k2k_event_cb, + .vf_states = vh264_4k2k_vf_states, +}; + +static struct vframe_provider_s vh264_4k2k_vf_prov; + +static u32 mb_width_old, mb_height_old; +static u32 frame_width, frame_height, frame_dur, frame_ar; +static u32 saved_resolution; +static struct timer_list recycle_timer; +static u32 stat; +static u32 error_watchdog_count; +static uint error_recovery_mode; +static u32 sync_outside; +static u32 vh264_4k2k_rotation; +static u32 first_i_recieved; +static struct vframe_s *p_last_vf; + +#ifdef DEBUG_PTS +static unsigned long pts_missed, pts_hit; +#endif + +static struct dec_sysinfo vh264_4k2k_amstream_dec_info; +static dma_addr_t mc_dma_handle; +static void *mc_cpu_addr; + +#define AMVDEC_H264_4K2K_CANVAS_INDEX 0x80 +#define AMVDEC_H264_4K2K_CANVAS_MAX 0xc6 +static DEFINE_SPINLOCK(lock); +static int fatal_error; + +static atomic_t vh264_4k2k_active = ATOMIC_INIT(0); + +static DEFINE_MUTEX(vh264_4k2k_mutex); + +static void (*probe_callback)(void); +static void (*remove_callback)(void); +static struct device *cma_dev; + +/* bit[3:0] command : */ +/* 0 - command finished */ +/* (DATA0 - {level_idc_mmco, max_reference_frame_num, width, height} */ +/* 1 - alloc view_0 display_buffer and reference_data_area */ +/* 2 - alloc view_1 display_buffer and reference_data_area */ +#define MAILBOX_COMMAND AV_SCRATCH_0 +#define MAILBOX_DATA_0 AV_SCRATCH_1 +#define MAILBOX_DATA_1 AV_SCRATCH_2 +#define MAILBOX_DATA_2 AV_SCRATCH_3 +#define MAILBOX_DATA_3 AV_SCRATCH_4 +#define MAILBOX_DATA_4 AV_SCRATCH_5 +#define CANVAS_START AV_SCRATCH_6 +#define BUFFER_RECYCLE AV_SCRATCH_7 +#define PICTURE_COUNT AV_SCRATCH_9 +#define DECODE_STATUS AV_SCRATCH_A +#define SPS_STATUS AV_SCRATCH_B +#define PPS_STATUS AV_SCRATCH_C +#define MS_ID AV_SCRATCH_D +#define WORKSPACE_START AV_SCRATCH_E +#define DECODED_PIC_NUM AV_SCRATCH_F +#define DECODE_ERROR_CNT AV_SCRATCH_G +#define CURRENT_UCODE AV_SCRATCH_H +/* bit[15:9]-SPS, bit[8:0]-PPS */ +#define CURRENT_SPS_PPS AV_SCRATCH_I +#define DECODE_SKIP_PICTURE AV_SCRATCH_J +#define DECODE_MODE AV_SCRATCH_K +#define RESERVED_REG_L AV_SCRATCH_L +#define REF_START_VIEW_0 AV_SCRATCH_M +#define REF_START_VIEW_1 AV_SCRATCH_N + +#define VDEC2_MAILBOX_COMMAND VDEC2_AV_SCRATCH_0 +#define VDEC2_MAILBOX_DATA_0 VDEC2_AV_SCRATCH_1 +#define VDEC2_MAILBOX_DATA_1 VDEC2_AV_SCRATCH_2 +#define VDEC2_MAILBOX_DATA_2 VDEC2_AV_SCRATCH_3 +#define VDEC2_MAILBOX_DATA_3 VDEC2_AV_SCRATCH_4 +#define VDEC2_MAILBOX_DATA_4 VDEC2_AV_SCRATCH_5 +#define VDEC2_CANVAS_START VDEC2_AV_SCRATCH_6 +#define VDEC2_BUFFER_RECYCLE VDEC2_AV_SCRATCH_7 +#define VDEC2_PICTURE_COUNT VDEC2_AV_SCRATCH_9 +#define VDEC2_DECODE_STATUS VDEC2_AV_SCRATCH_A +#define VDEC2_SPS_STATUS VDEC2_AV_SCRATCH_B +#define VDEC2_PPS_STATUS VDEC2_AV_SCRATCH_C +#define VDEC2_MS_ID VDEC2_AV_SCRATCH_D +#define VDEC2_WORKSPACE_START VDEC2_AV_SCRATCH_E +#define VDEC2_DECODED_PIC_NUM VDEC2_AV_SCRATCH_F +#define VDEC2_DECODE_ERROR_CNT VDEC2_AV_SCRATCH_G +#define VDEC2_CURRENT_UCODE VDEC2_AV_SCRATCH_H +/* bit[15:9]-SPS, bit[8:0]-PPS */ +#define VDEC2_CURRENT_SPS_PPS VDEC2_AV_SCRATCH_I +#define VDEC2_DECODE_SKIP_PICTURE VDEC2_AV_SCRATCH_J +#define VDEC2_RESERVED_REG_K VDEC2_AV_SCRATCH_K +#define VDEC2_RESERVED_REG_L VDEC2_AV_SCRATCH_L +#define VDEC2_REF_START_VIEW_0 VDEC2_AV_SCRATCH_M +#define VDEC2_REF_START_VIEW_1 VDEC2_AV_SCRATCH_N + +/******************************************** + * DECODE_STATUS Define +********************************************/ +#define DECODE_IDLE 0 +#define DECODE_START_HEADER 1 +#define DECODE_HEADER 2 +#define DECODE_START_MMCO 3 +#define DECODE_MMCO 4 +#define DECODE_START_SLICE 5 +#define DECODE_SLICE 6 +#define DECODE_WAIT_BUFFER 7 + +/******************************************** + * Dual Core Communication +********************************************/ +#define FATAL_ERROR DOS_SCRATCH16 +#define PRE_MASTER_UPDATE_TIMES DOS_SCRATCH20 +/* bit[31] - REQUEST */ +/* bit[30:0] - MASTER_UPDATE_TIMES */ +#define SLAVE_WAIT_DPB_UPDATE DOS_SCRATCH21 +/* [15:8] - current_ref, [7:0] current_dpb (0x80 means no buffer found) */ +#define SLAVE_REF_DPB DOS_SCRATCH22 +#define SAVE_MVC_ENTENSION_0 DOS_SCRATCH23 +#define SAVE_I_POC DOS_SCRATCH24 +/* bit[31:30] - core_status 0-idle, 1-mmco, 2-decoding, 3-finished */ +/* bit[29:0] - core_pic_count */ +#define CORE_STATUS_M DOS_SCRATCH25 +#define CORE_STATUS_S DOS_SCRATCH26 +#define SAVE_ref_status_view_0 DOS_SCRATCH27 +#define SAVE_ref_status_view_1 DOS_SCRATCH28 +#define ALLOC_INFO_0 DOS_SCRATCH29 +#define ALLOC_INFO_1 DOS_SCRATCH30 + +/******************************************** + * Mailbox command + ********************************************/ +#define CMD_FINISHED 0 +#define CMD_ALLOC_VIEW 1 +#define CMD_FRAME_DISPLAY 3 +#define CMD_DEBUG 10 + +#define MC_TOTAL_SIZE (28*SZ_1K) +#define MC_SWAP_SIZE (4*SZ_1K) + +static unsigned long work_space_adr, decoder_buffer_start, decoder_buffer_end; +static unsigned long reserved_buffer; + +#define DECODE_BUFFER_NUM_MAX 32 +#define DISPLAY_BUFFER_NUM 6 + +#define video_domain_addr(adr) (adr&0x7fffffff) +#define DECODER_WORK_SPACE_SIZE 0x400000 + +struct buffer_spec_s { + unsigned int y_addr; + unsigned int uv_addr; +#ifdef DOUBLE_WRITE + unsigned int y_dw_addr; + unsigned int uv_dw_addr; +#endif + + int y_canvas_index; + int uv_canvas_index; +#ifdef DOUBLE_WRITE + int y_dw_canvas_index; + int uv_dw_canvas_index; +#endif + + struct page *alloc_pages; + unsigned long phy_addr; + int alloc_count; +}; + +static struct buffer_spec_s buffer_spec[DECODE_BUFFER_NUM_MAX + + DISPLAY_BUFFER_NUM]; + +#ifdef DOUBLE_WRITE +#define spec2canvas(x) \ + (((x)->uv_dw_canvas_index << 16) | \ + ((x)->uv_dw_canvas_index << 8) | \ + ((x)->y_dw_canvas_index << 0)) +#else +#define spec2canvas(x) \ + (((x)->uv_canvas_index << 16) | \ + ((x)->uv_canvas_index << 8) | \ + ((x)->y_canvas_index << 0)) +#endif + +#define VF_POOL_SIZE 32 + +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 s32 vfbuf_use[DECODE_BUFFER_NUM_MAX]; +static struct vframe_s vfpool[VF_POOL_SIZE]; + +static struct work_struct alloc_work; + +static void set_frame_info(struct vframe_s *vf) +{ + unsigned int ar; + +#ifdef DOUBLE_WRITE + vf->width = frame_width / 2; + vf->height = frame_height / 2; +#else + vf->width = frame_width; + vf->height = frame_height; +#endif + vf->duration = frame_dur; + vf->duration_pulldown = 0; + vf->flag = 0; + + ar = min_t(u32, frame_ar, DISP_RATIO_ASPECT_RATIO_MAX); + vf->ratio_control = (ar << DISP_RATIO_ASPECT_RATIO_BIT); + vf->orientation = vh264_4k2k_rotation; + + return; +} + +static int vh264_4k2k_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); + states->buf_recycle_num = kfifo_len(&recycle_q); + + spin_unlock_irqrestore(&lock, flags); + return 0; +} + +static struct vframe_s *vh264_4k2k_vf_peek(void *op_arg) +{ + struct vframe_s *vf; + + if (kfifo_peek(&display_q, &vf)) + return vf; + + return NULL; +} + +static struct vframe_s *vh264_4k2k_vf_get(void *op_arg) +{ + struct vframe_s *vf; + + if (kfifo_get(&display_q, &vf)) + return vf; + + return NULL; +} + +static void vh264_4k2k_vf_put(struct vframe_s *vf, void *op_arg) +{ + kfifo_put(&recycle_q, (const struct vframe_s *)vf); +} + +static int vh264_4k2k_event_cb(int type, void *data, void *private_data) +{ + if (type & VFRAME_EVENT_RECEIVER_RESET) { + unsigned long flags; + amvdec_stop(); + + if (!H264_4K2K_SINGLE_CORE) + amvdec2_stop(); +#ifndef CONFIG_AMLOGIC_POST_PROCESS_MANAGER + vf_light_unreg_provider(&vh264_4k2k_vf_prov); +#endif + spin_lock_irqsave(&lock, flags); + vh264_4k2k_local_init(); + vh264_4k2k_prot_init(); + spin_unlock_irqrestore(&lock, flags); +#ifndef CONFIG_AMLOGIC_POST_PROCESS_MANAGER + vf_reg_provider(&vh264_4k2k_vf_prov); +#endif + amvdec_start(); + + if (!H264_4K2K_SINGLE_CORE) + amvdec2_start(); + } + + return 0; +} + +int init_canvas(int start_addr, long dpb_size, int dpb_number, int mb_width, + int mb_height, struct buffer_spec_s *buffer_spec) +{ + unsigned long dpb_addr, addr; + int i; + int mb_total; + int canvas_addr = ANC0_CANVAS_ADDR; + int vdec2_canvas_addr = VDEC2_ANC0_CANVAS_ADDR; + int index = AMVDEC_H264_4K2K_CANVAS_INDEX; + u32 disp_addr = 0xffffffff; + bool use_alloc = false; + int alloc_count = 0; + struct canvas_s cur_canvas; + + dpb_addr = start_addr + dpb_size; + + mb_total = mb_width * mb_height; + + canvas_read((READ_VCBUS_REG(VD1_IF0_CANVAS0) & 0xff), &cur_canvas); + disp_addr = (cur_canvas.addr + 7) >> 3; + + mutex_lock(&vh264_4k2k_mutex); + + for (i = 0; i < dpb_number; i++) { + WRITE_VREG(canvas_addr++, + index | ((index + 1) << 8) | + ((index + 1) << 16)); + if (!H264_4K2K_SINGLE_CORE) { + WRITE_VREG(vdec2_canvas_addr++, + index | ((index + 1) << 8) | + ((index + 1) << 16)); + } + + if (((dpb_addr + (mb_total << 8) + (mb_total << 7)) >= + decoder_buffer_end) && (!use_alloc)) { + pr_info("start alloc for %d/%d\n", i, dpb_number); + 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 buffer%d\n", + i); + + /*dma_release_from_contiguous(cma_dev, + buffer_spec[i]. + alloc_pages, + buffer_spec[i]. + alloc_count); + */ + codec_mm_free_for_dma(MEM_NAME, + buffer_spec[i].phy_addr); + buffer_spec[i].phy_addr = 0; + buffer_spec[i].alloc_pages = NULL; + 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_get_free_size() + < (page_count * PAGE_SIZE)) { + pr_err + ("CMA not enough free keep buf! %d\n", + i); + try_free_keep_video(1); + } + if (!codec_mm_enough_for_size( + page_count * PAGE_SIZE, 1)) { + buffer_spec[i].alloc_count = 0; + fatal_error = + DECODER_FATAL_ERROR_NO_MEM; + mutex_unlock(&vh264_4k2k_mutex); + return -1; + } + 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); + } + alloc_count++; + + if (!buffer_spec[i].phy_addr) { + buffer_spec[i].alloc_count = 0; + pr_info + ("264 4K2K decoder memory alloc failed %d.\n", + i); + mutex_unlock(&vh264_4k2k_mutex); + return -1; + } + addr = buffer_spec[i].phy_addr; + dpb_addr = 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_pages = NULL; + buffer_spec[i].alloc_count = 0; + } + + addr = dpb_addr; + dpb_addr += dpb_size; +#ifdef DOUBLE_WRITE + dpb_addr += dpb_size / 4; +#endif + } + + if (((addr + 7) >> 3) == disp_addr) + addr = start_addr; + + buffer_spec[i].y_addr = addr; + buffer_spec[i].y_canvas_index = index; + canvas_config(index, + addr, + mb_width << 4, + mb_height << 4, + CANVAS_ADDR_NOWRAP, CANVAS_BLKMODE_32X32); + + addr += mb_total << 8; + index++; + + buffer_spec[i].uv_addr = addr; + buffer_spec[i].uv_canvas_index = index; + canvas_config(index, + addr, + mb_width << 4, + mb_height << 3, + CANVAS_ADDR_NOWRAP, CANVAS_BLKMODE_32X32); + + addr += mb_total << 7; + index++; + +#ifdef DOUBLE_WRITE + buffer_spec[i].y_dw_addr = addr; + buffer_spec[i].y_dw_canvas_index = index; + canvas_config(index, + addr, + mb_width << 3, + mb_height << 3, + CANVAS_ADDR_NOWRAP, CANVAS_BLKMODE_32X32); + + addr += mb_total << 6; + index++; + + buffer_spec[i].uv_dw_addr = addr; + buffer_spec[i].uv_dw_canvas_index = index; + canvas_config(index, + addr, + mb_width << 3, + mb_height << 2, + CANVAS_ADDR_NOWRAP, CANVAS_BLKMODE_32X32); + + addr += mb_total << 5; + index++; +#endif + } + + mutex_unlock(&vh264_4k2k_mutex); + + pr_info + ("H264 4k2k decoder canvas allocation successful, "); + pr_info("%d CMA blocks allocated, canvas %d-%d\n", + alloc_count, AMVDEC_H264_4K2K_CANVAS_INDEX, index - 1); + + return 0; +} + +static int get_max_dec_frame_buf_size(int level_idc, + int max_reference_frame_num, int mb_width, + int mb_height) +{ + int pic_size = mb_width * mb_height * 384; + + int size = 0; + + switch (level_idc) { + case 9: + size = 152064; + break; + case 10: + size = 152064; + break; + case 11: + size = 345600; + break; + case 12: + size = 912384; + break; + case 13: + size = 912384; + break; + case 20: + size = 912384; + break; + case 21: + size = 1824768; + break; + case 22: + size = 3110400; + break; + case 30: + size = 3110400; + break; + case 31: + size = 6912000; + break; + case 32: + size = 7864320; + break; + case 40: + size = 12582912; + break; + case 41: + size = 12582912; + break; + case 42: + size = 13369344; + break; + case 50: + size = 42393600; + break; + case 51: + case 52: + default: + size = 70778880; + break; + } + + size /= pic_size; + size = size + 1; /* need one more buffer */ + + if (max_reference_frame_num > size) + size = max_reference_frame_num; + + if (size > DECODE_BUFFER_NUM_MAX) + size = DECODE_BUFFER_NUM_MAX; + + return size; +} + +static void do_alloc_work(struct work_struct *work) +{ + int level_idc, max_reference_frame_num, mb_width, mb_height, + frame_mbs_only_flag; + int dpb_size, ref_size; + int dpb_start_addr, ref_start_addr, max_dec_frame_buffering, + total_dec_frame_buffering; + unsigned int chroma444; + unsigned int crop_infor, crop_bottom, crop_right; + int ret = READ_VREG(MAILBOX_COMMAND); + + ref_start_addr = decoder_buffer_start; + ret = READ_VREG(MAILBOX_DATA_0); + /* MAILBOX_DATA_1 : + bit15 : frame_mbs_only_flag + bit 0-7 : chroma_format_idc + MAILBOX_DATA_2: + bit31-16: (left << 8 | right ) << 1 + bit15-0 : (top << 8 | bottom ) << (2 - frame_mbs_only_flag) + */ + frame_mbs_only_flag = READ_VREG(MAILBOX_DATA_1); + crop_infor = READ_VREG(MAILBOX_DATA_2); + level_idc = (ret >> 24) & 0xff; + max_reference_frame_num = (ret >> 16) & 0xff; + mb_width = (ret >> 8) & 0xff; + if (mb_width == 0) + mb_width = 256; + mb_height = (ret >> 0) & 0xff; + max_dec_frame_buffering = + get_max_dec_frame_buf_size(level_idc, max_reference_frame_num, + mb_width, mb_height); + total_dec_frame_buffering = + max_dec_frame_buffering + DISPLAY_BUFFER_NUM; + + chroma444 = ((frame_mbs_only_flag&0xffff) == 3) ? 1 : 0; + frame_mbs_only_flag = (frame_mbs_only_flag >> 16) & 0x01; + crop_bottom = (crop_infor & 0xff) >> (2 - frame_mbs_only_flag); + crop_right = ((crop_infor >> 16) & 0xff) >> 1; + pr_info("crop_right = 0x%x crop_bottom = 0x%x chroma_format_idc = 0x%x\n", + crop_right, crop_bottom, chroma444); + + if ((frame_width == 0) || (frame_height == 0) || crop_infor || + mb_width != mb_width_old || + mb_height != mb_height_old) { + frame_width = mb_width << 4; + frame_height = mb_height << 4; + mb_width_old = mb_width; + mb_height_old = mb_height; + if (frame_mbs_only_flag) { + frame_height -= (2 >> chroma444) * + min(crop_bottom, + (unsigned int)((8 << chroma444) - 1)); + frame_width -= (2 >> chroma444) * + min(crop_right, + (unsigned int)((8 << chroma444) - 1)); + } else { + frame_height -= (4 >> chroma444) * + min(crop_bottom, + (unsigned int)((8 << chroma444) - 1)); + frame_width -= (4 >> chroma444) * + min(crop_right, + (unsigned int)((8 << chroma444) - 1)); + } + pr_info("frame_mbs_only_flag %d, crop_bottom %d frame_height %d, mb_height %d crop_right %d, frame_width %d, mb_width %d\n", + frame_mbs_only_flag, crop_bottom, frame_height, + mb_height, crop_right, frame_width, mb_height); + } + + mb_width = (mb_width + 3) & 0xfffffffc; + mb_height = (mb_height + 3) & 0xfffffffc; + + dpb_size = mb_width * mb_height * 384; + ref_size = mb_width * mb_height * 96; + dpb_start_addr = + ref_start_addr + (ref_size * (max_reference_frame_num + 1)) * 2; + /* dpb_start_addr = reserved_buffer + dpb_size; */ + + pr_info + ("dpb_start_addr=0x%x, dpb_size=%d, total_dec_frame_buffering=%d, ", + dpb_start_addr, dpb_size, total_dec_frame_buffering); + pr_info("mb_width=%d, mb_height=%d\n", + mb_width, mb_height); + + ret = init_canvas(dpb_start_addr, dpb_size, + total_dec_frame_buffering, mb_width, mb_height, + buffer_spec); + + if (ret == -1) { + pr_info(" Un-expected memory alloc problem\n"); + return; + } + + if (frame_width == 0) + frame_width = mb_width << 4; + if (frame_height == 0) + frame_height = mb_height << 4; + + WRITE_VREG(REF_START_VIEW_0, video_domain_addr(ref_start_addr)); + if (!H264_4K2K_SINGLE_CORE) { + WRITE_VREG(VDEC2_REF_START_VIEW_0, + video_domain_addr(ref_start_addr)); + } + + WRITE_VREG(MAILBOX_DATA_0, + (max_dec_frame_buffering << 8) | + (total_dec_frame_buffering << 0)); + WRITE_VREG(MAILBOX_DATA_1, ref_size); + WRITE_VREG(MAILBOX_COMMAND, CMD_FINISHED); + + /* ///////////// FAKE FIRST PIC */ +#if 0 + + pr_info("Debug: send a fake picture to config VPP %dx%d\n", frame_width, + frame_height); + WRITE_VREG(DOS_SCRATCH0, 4); + WRITE_VREG(DOS_SCRATCH1, 0x004c); + + if (kfifo_get(&newframe_q, &vf)) { + vfbuf_use[0]++; + vf->index = 0; + vf->pts = 0; + vf->type = VIDTYPE_PROGRESSIVE | VIDTYPE_VIU_FIELD; + vf->canvas0Addr = vf->canvas1Addr = + spec2canvas(&buffer_spec[0]); + set_frame_info(vf); + kfifo_put(&display_q, (const struct vframe_s *)vf); + vf_notify_receiver(PROVIDER_NAME, + VFRAME_EVENT_PROVIDER_VFRAME_READY, NULL); + } + /* ///////////// FAKE END */ +#endif +} + +static irqreturn_t vh264_4k2k_isr(int irq, void *dev_id) +{ + int drop_status, display_buff_id, display_POC, slice_type, error; + unsigned stream_offset; + struct vframe_s *vf = NULL; + int ret = READ_VREG(MAILBOX_COMMAND); + + switch (ret & 0xff) { + case CMD_ALLOC_VIEW: + schedule_work(&alloc_work); + break; + + case CMD_FRAME_DISPLAY: + ret >>= 8; + display_buff_id = (ret >> 0) & 0x3f; + drop_status = (ret >> 8) & 0x1; + slice_type = (ret >> 9) & 0x7; + error = (ret >> 12) & 0x1; + display_POC = READ_VREG(MAILBOX_DATA_0); + stream_offset = READ_VREG(MAILBOX_DATA_1); + + smp_rmb();/* rmb smp */ + + WRITE_VREG(MAILBOX_COMMAND, CMD_FINISHED); + + if (kfifo_get(&newframe_q, &vf) == 0) { + pr_info("fatal error, no available buffer slot."); + return IRQ_HANDLED; + } + + if (vf) { + vfbuf_use[display_buff_id]++; + + vf->pts = 0; + vf->pts_us64 = 0; + + if ((!sync_outside) + || (sync_outside && + (slice_type == SLICE_TYPE_I))) { + pts_lookup_offset_us64(PTS_TYPE_VIDEO, + stream_offset, + &vf->pts, + 0, + &vf->pts_us64); + } +#ifdef H264_4K2K_SINGLE_CORE + if (READ_VREG(DECODE_MODE) & 1) { + /* for I only mode, ignore the PTS information + and only uses 10fps for each + I frame decoded */ + if (p_last_vf) { + vf->pts = 0; + vf->pts_us64 = 0; + } + frame_dur = 96000 / 10; + } +#endif + vf->signal_type = 0; + vf->index = display_buff_id; + vf->type = VIDTYPE_PROGRESSIVE | VIDTYPE_VIU_FIELD; + vf->type |= VIDTYPE_VIU_NV21; + vf->canvas0Addr = vf->canvas1Addr = + spec2canvas(&buffer_spec[display_buff_id]); + set_frame_info(vf); + + if (((error_recovery_mode & 2) && error) + || (!first_i_recieved + && (slice_type != SLICE_TYPE_I))) { + kfifo_put(&recycle_q, + (const struct vframe_s *)vf); + } else { + p_last_vf = vf; + first_i_recieved = 1; + kfifo_put(&display_q, + (const struct vframe_s *)vf); + + vf_notify_receiver(PROVIDER_NAME, + VFRAME_EVENT_PROVIDER_VFRAME_READY, + NULL); + } + } + break; + + case CMD_DEBUG: + pr_info("M: core_status 0x%08x 0x%08x; ", + READ_VREG(CORE_STATUS_M), READ_VREG(CORE_STATUS_S)); + switch (READ_VREG(MAILBOX_DATA_0)) { + case 1: + pr_info("H264_BUFFER_INFO_INDEX = 0x%x\n", + READ_VREG(MAILBOX_DATA_1)); + WRITE_VREG(MAILBOX_COMMAND, CMD_FINISHED); + break; + case 2: + pr_info("H264_BUFFER_INFO_DATA = 0x%x\n", + READ_VREG(MAILBOX_DATA_1)); + WRITE_VREG(MAILBOX_COMMAND, CMD_FINISHED); + break; + case 3: + pr_info("REC_CANVAS_ADDR = 0x%x\n", + READ_VREG(MAILBOX_DATA_1)); + WRITE_VREG(MAILBOX_COMMAND, CMD_FINISHED); + break; + case 4: + pr_info("after DPB_MMCO\n"); + WRITE_VREG(MAILBOX_COMMAND, CMD_FINISHED); + break; + case 5: + pr_info("MBY = 0x%x, S_MBXY = 0x%x\n", + READ_VREG(MAILBOX_DATA_1), + READ_VREG(0x2c07)); + WRITE_VREG(MAILBOX_COMMAND, CMD_FINISHED); + break; + case 6: + pr_info("after FIFO_OUT_FRAME\n"); + WRITE_VREG(MAILBOX_COMMAND, CMD_FINISHED); + break; + case 7: + pr_info("after RELEASE_EXCEED_REF_BUFF\n"); + WRITE_VREG(MAILBOX_COMMAND, CMD_FINISHED); + break; + case 0x5a: + pr_info("\n"); + break; + default: + pr_info("\n"); + break; + } + break; + + default: + break; + } + + return IRQ_HANDLED; +} + +#if 1 /*MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON8*/ +static irqreturn_t vh264_4k2k_vdec2_isr(int irq, void *dev_id) +{ + int ret = READ_VREG(VDEC2_MAILBOX_COMMAND); + + switch (ret & 0xff) { + case CMD_DEBUG: + pr_info("S: core_status 0x%08x 0x%08x; ", + READ_VREG(CORE_STATUS_M), READ_VREG(CORE_STATUS_S)); + switch (READ_VREG(VDEC2_MAILBOX_DATA_0)) { + case 1: + pr_info("H264_BUFFER_INFO_INDEX = 0x%x\n", + READ_VREG(VDEC2_MAILBOX_DATA_1)); + WRITE_VREG(VDEC2_MAILBOX_COMMAND, CMD_FINISHED); + break; + case 2: + pr_info("H264_BUFFER_INFO_DATA = 0x%x\n", + READ_VREG(VDEC2_MAILBOX_DATA_1)); + WRITE_VREG(VDEC2_MAILBOX_COMMAND, CMD_FINISHED); + break; + case 3: + pr_info("REC_CANVAS_ADDR = 0x%x\n", + READ_VREG(VDEC2_MAILBOX_DATA_1)); + WRITE_VREG(VDEC2_MAILBOX_COMMAND, CMD_FINISHED); + break; + case 4: + pr_info("after DPB_MMCO\n"); + WRITE_VREG(VDEC2_MAILBOX_COMMAND, CMD_FINISHED); + break; + case 5: + pr_info("MBY = 0x%x, M/S_MBXY = 0x%x-0x%x\n", + READ_VREG(VDEC2_MAILBOX_DATA_1), + READ_VREG(0xc07), READ_VREG(0x2c07)); + WRITE_VREG(VDEC2_MAILBOX_COMMAND, CMD_FINISHED); + break; + case 6: + pr_info("after FIFO_OUT_FRAME\n"); + WRITE_VREG(VDEC2_MAILBOX_COMMAND, CMD_FINISHED); + break; + case 7: + pr_info("after RELEASE_EXCEED_REF_BUFF\n"); + WRITE_VREG(VDEC2_MAILBOX_COMMAND, CMD_FINISHED); + break; + case 0x5a: + pr_info("\n"); + break; + default: + pr_info("\n"); + break; + } + break; + + default: + break; + } + + return IRQ_HANDLED; +} +#endif + +static void vh264_4k2k_put_timer_func(unsigned long arg) +{ + struct timer_list *timer = (struct timer_list *)arg; + enum receviver_start_e state = RECEIVER_INACTIVE; + + 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)) + state = RECEIVER_INACTIVE; + } else + state = RECEIVER_INACTIVE; + + /* error watchdog */ + if (((READ_VREG(VLD_MEM_VIFIFO_CONTROL) & 0x100) == 0) &&/* dec has in*/ + (state == RECEIVER_INACTIVE) && /* rec has no buf to recycle */ + (kfifo_is_empty(&display_q)) && /* no buf in display queue */ + (kfifo_is_empty(&recycle_q)) && /* no buf to recycle */ + (READ_VREG(MS_ID) & 0x100) +#ifdef CONFIG_H264_2K4K_SINGLE_CORE + && (READ_VREG(VDEC2_MS_ID) & 0x100)/* with both decoder + have started decoding */ +#endif + && first_i_recieved) { + if (++error_watchdog_count == ERROR_RESET_COUNT) { + /* and it lasts for a while */ + pr_info("H264 4k2k decoder fatal error watchdog.\n"); + fatal_error = DECODER_FATAL_ERROR_UNKNOWN; + } + } else + error_watchdog_count = 0; + + if (READ_VREG(FATAL_ERROR) != 0) { + pr_info("H264 4k2k decoder ucode fatal error.\n"); + fatal_error = DECODER_FATAL_ERROR_UNKNOWN; + WRITE_VREG(FATAL_ERROR, 0); + } + + while (!kfifo_is_empty(&recycle_q) && + (READ_VREG(BUFFER_RECYCLE) == 0)) { + struct vframe_s *vf; + if (kfifo_get(&recycle_q, &vf)) { + if ((vf->index >= 0) + && (vf->index < DECODE_BUFFER_NUM_MAX) + && (--vfbuf_use[vf->index] == 0)) { + WRITE_VREG(BUFFER_RECYCLE, vf->index + 1); + vf->index = DECODE_BUFFER_NUM_MAX; + } + + kfifo_put(&newframe_q, (const struct vframe_s *)vf); + } + } + if (first_i_recieved &&/*do switch after first i frame ready.*/ + frame_dur > 0 && saved_resolution != + frame_width * frame_height * (96000 / frame_dur)) { + int fps = 96000 / frame_dur; + pr_info("H264 4k2k resolution changed!!\n"); + if (vdec_source_changed(VFORMAT_H264_4K2K, + frame_width, frame_height, fps) > 0)/*changed clk ok*/ + saved_resolution = frame_width * frame_height * fps; + } + timer->expires = jiffies + PUT_INTERVAL; + + add_timer(timer); +} + +int vh264_4k2k_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 = 0; + vstatus->status = stat | fatal_error; + return 0; +} + +int vh264_4k2k_set_trickmode(struct vdec_s *vdec, unsigned long trickmode) +{ + if (trickmode == TRICKMODE_I) { + WRITE_VREG(DECODE_MODE, 1); + trickmode_i = 1; + } else if (trickmode == TRICKMODE_NONE) { + WRITE_VREG(DECODE_MODE, 0); + trickmode_i = 0; + } + + return 0; +} + +static void H264_DECODE_INIT(void) +{ + int i; + + WRITE_VREG(GCLK_EN, 0x3ff); + + 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); + + /* fill_weight_pred */ + WRITE_VREG(MC_MPORT_CTRL, 0x0300); + for (i = 0; i < 192; i++) + WRITE_VREG(MC_MPORT_DAT, 0x100); + WRITE_VREG(MC_MPORT_CTRL, 0); + + WRITE_VREG(MB_WIDTH, 0xff); /* invalid mb_width */ + + /* set slice start to 0x000000 or 0x000001 for check more_rbsp_data */ + WRITE_VREG(SLICE_START_BYTE_01, 0x00000000); + WRITE_VREG(SLICE_START_BYTE_23, 0x01010000); + /* set to mpeg2 to enable mismatch logic */ + WRITE_VREG(MPEG1_2_REG, 1); + WRITE_VREG(VLD_ERROR_MASK, + 0x1011); + + /* Config MCPU Amrisc interrupt */ + WRITE_VREG(ASSIST_AMR1_INT0, 0x1); /* viu_vsync_int */ + WRITE_VREG(ASSIST_AMR1_INT1, 0x5); /* mbox_isr */ + WRITE_VREG(ASSIST_AMR1_INT2, 0x8); /* vld_isr */ + /* WRITE_VREG(ASSIST_AMR1_INT3, 0x15); // vififo_empty */ + WRITE_VREG(ASSIST_AMR1_INT4, 0xd); /* rv_ai_mb_finished_int */ + WRITE_VREG(ASSIST_AMR1_INT7, 0x14); /* dcac_dma_done */ + WRITE_VREG(ASSIST_AMR1_INT8, 0x15); /* vififo_empty */ + + /* Config MCPU Amrisc interrupt */ + WRITE_VREG(ASSIST_AMR1_INT5, 0x9); /* MCPU interrupt */ + WRITE_VREG(ASSIST_AMR1_INT6, 0x17); /* CCPU interrupt */ + + WRITE_VREG(CPC_P, 0xc00); /* CCPU Code will start from 0xc00 */ + WRITE_VREG(CINT_VEC_BASE, (0xc20 >> 5)); + WRITE_VREG(POWER_CTL_VLD, (1 << 10) | /* disable cabac_step_2 */ + (1 << 9) | /* viff_drop_flag_en */ + (1 << 6)); /* h264_000003_en */ + WRITE_VREG(M4_CONTROL_REG, (1 << 13)); /* H264_DECODE_INFO - h264_en */ + + WRITE_VREG(CANVAS_START, AMVDEC_H264_4K2K_CANVAS_INDEX); + /* Start Address of Workspace (UCODE, temp_data...) */ + WRITE_VREG(WORKSPACE_START, + video_domain_addr(work_space_adr)); + /* Clear all sequence parameter set available */ + WRITE_VREG(SPS_STATUS, 0); + /* Clear all picture parameter set available */ + WRITE_VREG(PPS_STATUS, 0); + /* Set current microcode to NULL */ + WRITE_VREG(CURRENT_UCODE, 0xff); + /* Set current SPS/PPS to NULL */ + WRITE_VREG(CURRENT_SPS_PPS, 0xffff); + /* Set decode status to DECODE_START_HEADER */ + WRITE_VREG(DECODE_STATUS, 1); +} + +static void H264_DECODE2_INIT(void) +{ + int i; + + WRITE_VREG(VDEC2_GCLK_EN, 0x3ff); + + WRITE_VREG(DOS_SW_RESET2, (1 << 7) | (1 << 6) | (1 << 4)); + WRITE_VREG(DOS_SW_RESET2, 0); + + READ_VREG(DOS_SW_RESET2); + READ_VREG(DOS_SW_RESET2); + READ_VREG(DOS_SW_RESET2); + + WRITE_VREG(DOS_SW_RESET2, (1 << 7) | (1 << 6) | (1 << 4)); + WRITE_VREG(DOS_SW_RESET2, 0); + + WRITE_VREG(DOS_SW_RESET2, (1 << 9) | (1 << 8)); + WRITE_VREG(DOS_SW_RESET2, 0); + + READ_VREG(DOS_SW_RESET2); + READ_VREG(DOS_SW_RESET2); + READ_VREG(DOS_SW_RESET2); + + /* fill_weight_pred */ + WRITE_VREG(VDEC2_MC_MPORT_CTRL, 0x0300); + for (i = 0; i < 192; i++) + WRITE_VREG(VDEC2_MC_MPORT_DAT, 0x100); + WRITE_VREG(VDEC2_MC_MPORT_CTRL, 0); + + WRITE_VREG(VDEC2_MB_WIDTH, 0xff); /* invalid mb_width */ + + /* set slice start to 0x000000 or 0x000001 for check more_rbsp_data */ + WRITE_VREG(VDEC2_SLICE_START_BYTE_01, 0x00000000); + WRITE_VREG(VDEC2_SLICE_START_BYTE_23, 0x01010000); + /* set to mpeg2 to enable mismatch logic */ + WRITE_VREG(VDEC2_MPEG1_2_REG, 1); + /* disable COEF_GT_64 , error_m4_table and voff_rw_err */ + WRITE_VREG(VDEC2_VLD_ERROR_MASK, + 0x1011); + + /* Config MCPU Amrisc interrupt */ + WRITE_VREG(VDEC2_ASSIST_AMR1_INT0, 0x1);/* viu_vsync_int */ + WRITE_VREG(VDEC2_ASSIST_AMR1_INT1, 0x5);/* mbox_isr */ + WRITE_VREG(VDEC2_ASSIST_AMR1_INT2, 0x8);/* vld_isr */ + /* WRITE_VREG(VDEC2_ASSIST_AMR1_INT3, 0x15); // vififo_empty */ + WRITE_VREG(VDEC2_ASSIST_AMR1_INT4, 0xd);/* rv_ai_mb_finished_int */ + WRITE_VREG(VDEC2_ASSIST_AMR1_INT7, 0x14);/* dcac_dma_done */ + WRITE_VREG(VDEC2_ASSIST_AMR1_INT8, 0x15);/* vififo_empty */ + + /* Config MCPU Amrisc interrupt */ + WRITE_VREG(VDEC2_ASSIST_AMR1_INT5, 0x9);/* MCPU interrupt */ + WRITE_VREG(VDEC2_ASSIST_AMR1_INT6, 0x17);/* CCPU interrupt */ + + WRITE_VREG(VDEC2_CPC_P, 0xc00); /* CCPU Code will start from 0xc00 */ + WRITE_VREG(VDEC2_CINT_VEC_BASE, (0xc20 >> 5)); + WRITE_VREG(VDEC2_POWER_CTL_VLD, (1 << 10) |/* disable cabac_step_2 */ + (1 << 9) | /* viff_drop_flag_en */ + (1 << 6)); /* h264_000003_en */ + /* H264_DECODE_INFO - h264_en */ + WRITE_VREG(VDEC2_M4_CONTROL_REG, (1 << 13)); + + WRITE_VREG(VDEC2_CANVAS_START, AMVDEC_H264_4K2K_CANVAS_INDEX); + /* Start Address of Workspace (UCODE, temp_data...) */ + WRITE_VREG(VDEC2_WORKSPACE_START, + video_domain_addr(work_space_adr)); + /* Clear all sequence parameter set available */ + WRITE_VREG(VDEC2_SPS_STATUS, 0); + /* Clear all picture parameter set available */ + WRITE_VREG(VDEC2_PPS_STATUS, 0); + /* Set current microcode to NULL */ + WRITE_VREG(VDEC2_CURRENT_UCODE, 0xff); + /* Set current SPS/PPS to NULL */ + WRITE_VREG(VDEC2_CURRENT_SPS_PPS, 0xffff); + /* Set decode status to DECODE_START_HEADER */ + WRITE_VREG(VDEC2_DECODE_STATUS, 1); +} + +static void vh264_4k2k_prot_init(void) +{ + /* clear mailbox interrupt */ +#if 1 /* MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON8 */ + if (!H264_4K2K_SINGLE_CORE) + WRITE_VREG(VDEC2_ASSIST_MBOX0_CLR_REG, 1); +#endif + WRITE_VREG(VDEC_ASSIST_MBOX1_CLR_REG, 1); + + /* enable mailbox interrupt */ +#if 1 /* MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON8 */ + if (!H264_4K2K_SINGLE_CORE) + WRITE_VREG(VDEC2_ASSIST_MBOX0_MASK, 1); +#endif + WRITE_VREG(VDEC_ASSIST_MBOX1_MASK, 1); + + /* disable PSCALE for hardware sharing */ + WRITE_VREG(PSCALE_CTRL, 0); + + H264_DECODE_INIT(); + if (!H264_4K2K_SINGLE_CORE) + H264_DECODE2_INIT(); + + WRITE_VREG(DOS_SW_RESET0, (1 << 11)); + WRITE_VREG(DOS_SW_RESET0, 0); + + READ_VREG(DOS_SW_RESET0); + READ_VREG(DOS_SW_RESET0); + READ_VREG(DOS_SW_RESET0); + + if (!H264_4K2K_SINGLE_CORE) { + WRITE_VREG(DOS_SW_RESET2, (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(MAILBOX_COMMAND, 0); + WRITE_VREG(BUFFER_RECYCLE, 0); + + if (!H264_4K2K_SINGLE_CORE) { + WRITE_VREG(VDEC2_MAILBOX_COMMAND, 0); + WRITE_VREG(VDEC2_BUFFER_RECYCLE, 0); + } + + CLEAR_VREG_MASK(MDEC_PIC_DC_CTRL, 1 << 17); + if (!H264_4K2K_SINGLE_CORE) + CLEAR_VREG_MASK(VDEC2_MDEC_PIC_DC_CTRL, 1 << 17); + + /* set VDEC Master/ID 0 */ + WRITE_VREG(MS_ID, (1 << 7) | (0 << 0)); + if (!H264_4K2K_SINGLE_CORE) { + /* set VDEC2 Slave/ID 0 */ + WRITE_VREG(VDEC2_MS_ID, (0 << 7) | (1 << 0)); + } + WRITE_VREG(DECODE_SKIP_PICTURE, 0); + if (!H264_4K2K_SINGLE_CORE) + WRITE_VREG(VDEC2_DECODE_SKIP_PICTURE, 0); + + WRITE_VREG(PRE_MASTER_UPDATE_TIMES, 0); + WRITE_VREG(SLAVE_WAIT_DPB_UPDATE, 0); + WRITE_VREG(SLAVE_REF_DPB, 0); + WRITE_VREG(SAVE_MVC_ENTENSION_0, 0); + WRITE_VREG(SAVE_I_POC, 0); + WRITE_VREG(CORE_STATUS_M, 0); + WRITE_VREG(CORE_STATUS_S, 0); + WRITE_VREG(SAVE_ref_status_view_0, 0); + WRITE_VREG(SAVE_ref_status_view_1, 0); + WRITE_VREG(ALLOC_INFO_0, 0); + WRITE_VREG(ALLOC_INFO_1, 0); + WRITE_VREG(FATAL_ERROR, 0); + + SET_VREG_MASK(MDEC_PIC_DC_CTRL, 1 << 17); + if (!H264_4K2K_SINGLE_CORE) + SET_VREG_MASK(VDEC2_MDEC_PIC_DC_CTRL, 1 << 17); + + WRITE_VREG(MDEC_PIC_DC_THRESH, 0x404038aa); + if (!H264_4K2K_SINGLE_CORE) { + WRITE_VREG(VDEC2_MDEC_PIC_DC_THRESH, 0x404038aa); + /*TODO for M8 + amvenc_dos_top_reg_fix();*/ + } +#ifdef DOUBLE_WRITE + WRITE_VREG(MDEC_DOUBLEW_CFG0, (0 << 31) | /* half y address */ + (1 << 30) | /* 0:No Merge 1:Automatic Merge */ + (0 << 28) | /* Field Picture, 0x:no skip + 10:top only + 11:bottom only */ + (0 << 27) | /* Source from, 1:MCW 0:DBLK */ + (0 << 24) | /* Endian Control for Chroma */ + (0 << 18) | /* DMA ID */ + (0 << 12) | /* DMA Burst Number */ + (0 << 11) | /* DMA Urgent */ + (0 << 10) | /* 1:Round 0:Truncation */ + (1 << 9) | /* Size by vertical, 0:original size + 1: 1/2 shrunken size */ + (1 << 8) | /* Size by horizontal, 0:original size + 1: 1/2 shrunken size */ + (0 << 6) | /* Pixel sel by vertical, 0x:1/2 + 10:up + 11:down */ + (0 << 4) | /* Pixel sel by horizontal, 0x:1/2 + 10:left + 11:right */ + (0 << 1) | /* Endian Control for Luma */ + (1 << 0)); /* Double Write Enable */ + if (!H264_4K2K_SINGLE_CORE) { + WRITE_VREG(VDEC2_MDEC_DOUBLEW_CFG0, + (0 << 31) | /* half y address */ + (1 << 30) | /* 0:No Merge + 1:Automatic Merge */ + (0 << 28) | /* Field Picture, 0x:no skip + 10:top only + 11:bottom only */ + (0 << 27) | /* Source from, 1:MCW 0:DBLK */ + (0 << 24) | /* Endian Control for Chroma */ + (0 << 18) | /* DMA ID */ + (0 << 12) | /* DMA Burst Number */ + (0 << 11) | /* DMA Urgent */ + (0 << 10) | /* 1:Round 0:Truncation */ + (1 << 9) | /* Size by vertical, + 0:original size + 1: 1/2 shrunken size */ + (1 << 8) | /* Size by horizontal, + 0:original size + 1: 1/2 shrunken size */ + (0 << 6) | /* Pixel sel by vertical, + 0x:1/2 + 10:up + 11:down */ + (0 << 4) | /* Pixel sel by horizontal, + 0x:1/2 + 10:left + 11:right */ + (0 << 1) | /* Endian Control for Luma */ + (1 << 0)); /* Double Write Enable */ + } +#endif +} + +static void vh264_4k2k_local_init(void) +{ + int i; + +#ifdef DEBUG_PTS + pts_missed = 0; + pts_hit = 0; +#endif + mb_width_old = 0; + mb_height_old = 0; + saved_resolution = 0; + vh264_4k2k_rotation = + (((unsigned long) vh264_4k2k_amstream_dec_info.param) >> 16) + & 0xffff; + frame_width = vh264_4k2k_amstream_dec_info.width; + frame_height = vh264_4k2k_amstream_dec_info.height; + frame_dur = + (vh264_4k2k_amstream_dec_info.rate == + 0) ? 3600 : vh264_4k2k_amstream_dec_info.rate; + if (frame_width && frame_height) + frame_ar = frame_height * 0x100 / frame_width; + sync_outside = ((unsigned long) vh264_4k2k_amstream_dec_info.param + & 0x02) >> 1; + error_watchdog_count = 0; + + pr_info("H264_4K2K: decinfo: %dx%d rate=%d\n", + frame_width, frame_height, + frame_dur); + + if (frame_dur == 0) + frame_dur = 96000 / 24; + + INIT_KFIFO(display_q); + INIT_KFIFO(recycle_q); + INIT_KFIFO(newframe_q); + + for (i = 0; i < DECODE_BUFFER_NUM_MAX; i++) + vfbuf_use[i] = 0; + + for (i = 0; i < VF_POOL_SIZE; i++) { + const struct vframe_s *vf = &vfpool[i]; + vfpool[i].index = DECODE_BUFFER_NUM_MAX; + kfifo_put(&newframe_q, vf); + } + + reserved_buffer = 0; + p_last_vf = NULL; + first_i_recieved = 0; + INIT_WORK(&alloc_work, do_alloc_work); + + return; +} + +static s32 vh264_4k2k_init(void) +{ + int ret = -1, size = -1; + char *buf = vmalloc(0x1000 * 16); + if (IS_ERR_OR_NULL(buf)) + return -ENOMEM; + + pr_info("\nvh264_4k2k_init\n"); + + init_timer(&recycle_timer); + + stat |= STAT_TIMER_INIT; + + vh264_4k2k_local_init(); + + 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(); + vfree(buf); + pr_err("vh264_4k2k init: Can not allocate mc memory.\n"); + return -ENOMEM; + } + + WRITE_VREG(AV_SCRATCH_L, mc_dma_handle); + if (!H264_4K2K_SINGLE_CORE) + WRITE_VREG(VDEC2_AV_SCRATCH_L, mc_dma_handle); + + if (H264_4K2K_SINGLE_CORE) + size = get_firmware_data(VIDEO_DEC_H264_4k2K_SINGLE, buf); + + else + size = get_firmware_data(VIDEO_DEC_H264_4k2K, buf); + + if (size < 0) { + pr_err("get firmware fail."); + vfree(buf); + return -1; + } + + if (amvdec_loadmc_ex(VFORMAT_H264_4K2K, NULL, buf) < 0) { + amvdec_disable(); + dma_free_coherent(amports_get_dma_device(), + MC_TOTAL_SIZE, mc_cpu_addr, mc_dma_handle); + mc_cpu_addr = NULL; + return -EBUSY; + } + + if (!H264_4K2K_SINGLE_CORE) { + amvdec2_enable(); + + if (amvdec2_loadmc_ex(VFORMAT_H264_4K2K, NULL, buf) < 0) { + amvdec_disable(); + amvdec2_disable(); + dma_free_coherent(amports_get_dma_device(), + MC_TOTAL_SIZE, mc_cpu_addr, mc_dma_handle); + mc_cpu_addr = NULL; + return -EBUSY; + } + } + + /*header*/ + memcpy((u8 *) mc_cpu_addr, buf + 0x1000, 0x1000); + + /*mmco*/ + memcpy((u8 *) mc_cpu_addr + 0x1000, buf + 0x2000, 0x2000); + + /*slice*/ + memcpy((u8 *) mc_cpu_addr + 0x3000, buf + 0x4000, 0x3000); + + if (ret < 0) { + amvdec_disable(); + if (!H264_4K2K_SINGLE_CORE) + amvdec2_disable(); + pr_info("vh264_4k2k load firmware error.\n"); + 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; + + /* enable AMRISC side protocol */ + vh264_4k2k_prot_init(); + + if (vdec_request_irq(VDEC_IRQ_1, vh264_4k2k_isr, + "vh264_4k2k-irq", (void *)vh264_4k2k_dec_id)) { + pr_info("vh264_4k2k irq register error.\n"); + amvdec_disable(); + if (!H264_4K2K_SINGLE_CORE) + amvdec2_disable(); + + return -ENOENT; + } +#if 1 /* MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON8 */ + if (!H264_4K2K_SINGLE_CORE) { + if (vdec_request_irq(VDEC_IRQ_0, vh264_4k2k_vdec2_isr, + "vh264_4k2k-vdec2-irq", + (void *)vh264_4k2k_dec_id2)) { + pr_info("vh264_4k2k irq register error.\n"); + vdec_free_irq(VDEC_IRQ_1, (void *)vh264_4k2k_dec_id); + amvdec_disable(); + amvdec2_disable(); + return -ENOENT; + } + } +#endif + + stat |= STAT_ISR_REG; + + vf_provider_init(&vh264_4k2k_vf_prov, PROVIDER_NAME, + &vh264_4k2k_vf_provider, NULL); + vf_reg_provider(&vh264_4k2k_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) + vh264_4k2k_amstream_dec_info.rate)); + + stat |= STAT_VF_HOOK; + + recycle_timer.data = (ulong) (&recycle_timer); + recycle_timer.function = vh264_4k2k_put_timer_func; + recycle_timer.expires = jiffies + PUT_INTERVAL; + + add_timer(&recycle_timer); + + stat |= STAT_TIMER_ARM; + + amvdec_start(); + if (!H264_4K2K_SINGLE_CORE) + amvdec2_start(); + + stat |= STAT_VDEC_RUN; + + return 0; +} + +static int vh264_4k2k_stop(void) +{ + int i; + u32 disp_addr = 0xffffffff; + struct canvas_s cur_canvas; + + if (stat & STAT_VDEC_RUN) { + amvdec_stop(); + if (!H264_4K2K_SINGLE_CORE) + amvdec2_stop(); + stat &= ~STAT_VDEC_RUN; + } + + if (stat & STAT_ISR_REG) { + WRITE_VREG(VDEC_ASSIST_MBOX1_MASK, 0); + if (!H264_4K2K_SINGLE_CORE) + WRITE_VREG(VDEC2_ASSIST_MBOX0_MASK, 0); + + vdec_free_irq(VDEC_IRQ_1, (void *)vh264_4k2k_dec_id); +#if 1 /* MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON8 */ + if (!H264_4K2K_SINGLE_CORE) + vdec_free_irq(VDEC_IRQ_0, (void *)vh264_4k2k_dec_id2); +#endif + stat &= ~STAT_ISR_REG; + } + + if (stat & STAT_TIMER_ARM) { + del_timer_sync(&recycle_timer); + stat &= ~STAT_TIMER_ARM; + } + + if (stat & STAT_VF_HOOK) { + vf_notify_receiver(PROVIDER_NAME, + VFRAME_EVENT_PROVIDER_FR_END_HINT, NULL); + + vf_unreg_provider(&vh264_4k2k_vf_prov); + stat &= ~STAT_VF_HOOK; + } +#ifdef DOUBLE_WRITE + WRITE_VREG(MDEC_DOUBLEW_CFG0, 0); + if (!H264_4K2K_SINGLE_CORE) + WRITE_VREG(VDEC2_MDEC_DOUBLEW_CFG0, 0); +#endif + + 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; + } + + stat &= ~STAT_MC_LOAD; + } + + amvdec_disable(); + if (!H264_4K2K_SINGLE_CORE) + amvdec2_disable(); +#ifdef CONFIG_VSYNC_RDMA + msleep(100); +#endif + if (!get_blackout_policy()) { + canvas_read((READ_VCBUS_REG(VD1_IF0_CANVAS0) & 0xff), + &cur_canvas); + disp_addr = cur_canvas.addr; + } + + for (i = 0; i < ARRAY_SIZE(buffer_spec); i++) { + if (buffer_spec[i].phy_addr) { + if (disp_addr == + (u32)buffer_spec[i].phy_addr) + 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_pages = NULL; + buffer_spec[i].alloc_count = 0; + } + } + + if (buffer_spec[i].y_addr == disp_addr) { + pr_info("4K2K dec stop, keeping buffer index = %d\n", + i); + } + } + + return 0; +} + +void vh264_4k_free_cmabuf(void) +{ + int i; + if (atomic_read(&vh264_4k2k_active)) + return; + mutex_lock(&vh264_4k2k_mutex); + for (i = 0; i < ARRAY_SIZE(buffer_spec); i++) { + 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_pages = NULL; + buffer_spec[i].alloc_count = 0; + pr_info("force free CMA buffer %d\n", i); + } + } + mutex_unlock(&vh264_4k2k_mutex); +} + +#if 0 /* (MESON_CPU_TYPE == MESON_CPU_TYPE_MESON8) && (HAS_HDEC) */ +/* extern void AbortEncodeWithVdec2(int abort); */ +#endif + +static int amvdec_h264_4k2k_probe(struct platform_device *pdev) +{ + struct vdec_s *pdata = *(struct vdec_s **)pdev->dev.platform_data; + + pr_info("amvdec_h264_4k2k probe start.\n"); + + mutex_lock(&vh264_4k2k_mutex); + + fatal_error = 0; + + if (pdata == NULL) { + pr_info("\namvdec_h264_4k2k memory resource undefined.\n"); + mutex_unlock(&vh264_4k2k_mutex); + return -EFAULT; + } + + work_space_adr = pdata->mem_start; + decoder_buffer_start = pdata->mem_start + DECODER_WORK_SPACE_SIZE; + decoder_buffer_end = pdata->mem_end + 1; + + if (pdata->sys_info) + vh264_4k2k_amstream_dec_info = *pdata->sys_info; + cma_dev = pdata->cma_dev; + + pr_info("H.264 4k2k decoder mem resource 0x%x -- 0x%x\n", + (u32)decoder_buffer_start, (u32)decoder_buffer_end); + pr_info(" sysinfo: %dx%d, rate = %d, param = 0x%lx\n", + vh264_4k2k_amstream_dec_info.width, + vh264_4k2k_amstream_dec_info.height, + vh264_4k2k_amstream_dec_info.rate, + (unsigned long) vh264_4k2k_amstream_dec_info.param); + + if (!H264_4K2K_SINGLE_CORE) { +#if 1 /* (MESON_CPU_TYPE == MESON_CPU_TYPE_MESON8) && (has_hdec()) */ + int count = 0; + if (get_vdec2_usage() != USAGE_NONE) + /* AbortEncodeWithVdec2(1); */ /*TODO*/ + while ((get_vdec2_usage() != USAGE_NONE) && (count < 10)) { + msleep(50); + count++; + } + + if (get_vdec2_usage() != USAGE_NONE) { + pr_info + ("\namvdec_h264_4k2k - vdec2 is used by encode now.\n"); + mutex_unlock(&vh264_4k2k_mutex); + return -EBUSY; + } +#endif + + if (vdec_on(VDEC_2)) { /* ++++ */ + vdec_poweroff(VDEC_2); /* ++++ */ + mdelay(10); + } +#if 1 /* (MESON_CPU_TYPE == MESON_CPU_TYPE_MESON8) && (has_hdec()) */ + set_vdec2_usage(USAGE_DEC_4K2K); + /* AbortEncodeWithVdec2(0); */ /*TODO*/ +#endif + vdec_poweron(VDEC_2); + } + + + if (!H264_4K2K_SINGLE_CORE) + vdec2_power_mode(1); + + pdata->dec_status = vh264_4k2k_dec_status; + if (H264_4K2K_SINGLE_CORE) + pdata->set_trickmode = vh264_4k2k_set_trickmode; + + if (vh264_4k2k_init() < 0) { + pr_info("\namvdec_h264_4k2k init failed.\n"); +#if 1/* (MESON_CPU_TYPE == MESON_CPU_TYPE_MESON8) && (has_hdec()) */ + if (!H264_4K2K_SINGLE_CORE) { + set_vdec2_usage(USAGE_NONE); + /*AbortEncodeWithVdec2(0);*/ /*TODO*/ + } +#endif + mutex_unlock(&vh264_4k2k_mutex); + return -ENODEV; + } +#if 1/*MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON8*/ + request_vpu_clk_vmod(360000000, VPU_VIU_VD1); +#endif + + if (probe_callback) + probe_callback(); + /*set the max clk for smooth playing...*/ + vdec_source_changed(VFORMAT_H264_4K2K, + 4096, 2048, 30); + atomic_set(&vh264_4k2k_active, 1); + mutex_unlock(&vh264_4k2k_mutex); + + return 0; +} + +static int amvdec_h264_4k2k_remove(struct platform_device *pdev) +{ + cancel_work_sync(&alloc_work); + + mutex_lock(&vh264_4k2k_mutex); + atomic_set(&vh264_4k2k_active, 0); + + vh264_4k2k_stop(); + + vdec_source_changed(VFORMAT_H264_4K2K , 0 , 0 , 0); + + if (!H264_4K2K_SINGLE_CORE) { + vdec_poweroff(VDEC_2); +#if 1/*(MESON_CPU_TYPE == MESON_CPU_TYPE_MESON8) && (has_hdec())*/ + set_vdec2_usage(USAGE_NONE); +#endif + } +#ifdef DEBUG_PTS + pr_info("pts missed %ld, pts hit %ld, duration %d\n", + pts_missed, pts_hit, frame_dur); +#endif + + if (remove_callback) + remove_callback(); + + mutex_unlock(&vh264_4k2k_mutex); + + pr_info("amvdec_h264_4k2k_remove\n"); + return 0; +} + +void vh264_4k2k_register_module_callback(void (*enter_func)(void), + void (*remove_func)(void)) +{ + probe_callback = enter_func; + remove_callback = remove_func; +} +EXPORT_SYMBOL(vh264_4k2k_register_module_callback); + +/****************************************/ + +static struct platform_driver amvdec_h264_4k2k_driver = { + .probe = amvdec_h264_4k2k_probe, + .remove = amvdec_h264_4k2k_remove, +#ifdef CONFIG_PM + .suspend = amvdec_suspend, + .resume = amvdec_resume, +#endif + .driver = { + .name = DRIVER_NAME, + } +}; + +static struct codec_profile_t amvdec_h264_4k2k_profile = { + .name = "h264_4k2k", + .profile = "" +}; + +static int __init amvdec_h264_4k2k_driver_init_module(void) +{ + pr_debug("amvdec_h264_4k2k module init\n"); + + if (platform_driver_register(&amvdec_h264_4k2k_driver)) { + pr_err("failed to register amvdec_h264_4k2k driver\n"); + return -ENODEV; + } + if (get_cpu_type() < MESON_CPU_MAJOR_ID_GXTVBB) + vcodec_profile_register(&amvdec_h264_4k2k_profile); + + return 0; +} + +static void __exit amvdec_h264_4k2k_driver_remove_module(void) +{ + pr_debug("amvdec_h264_4k2k module remove.\n"); + + platform_driver_unregister(&amvdec_h264_4k2k_driver); +} + +/****************************************/ + +module_param(stat, uint, 0664); +MODULE_PARM_DESC(stat, "\n amvdec_h264_4k2k stat\n"); + +module_param(error_recovery_mode, uint, 0664); +MODULE_PARM_DESC(error_recovery_mode, "\n amvdec_h264 error_recovery_mode\n"); + +module_init(amvdec_h264_4k2k_driver_init_module); +module_exit(amvdec_h264_4k2k_driver_remove_module); + +MODULE_DESCRIPTION("AMLOGIC h264_4k2k Video Decoder Driver"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Tim Yao <tim.yao@amlogic.com>"); diff --git a/drivers/frame_provider/decoder/h264/vh264_mvc.c b/drivers/frame_provider/decoder/h264/vh264_mvc.c new file mode 100644 index 0000000..1683c31 --- a/dev/null +++ b/drivers/frame_provider/decoder/h264/vh264_mvc.c @@ -0,0 +1,1594 @@ +/* + * drivers/amlogic/amports/vh264mvc.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/types.h> +#include <linux/errno.h> +#include <linux/interrupt.h> +#include <linux/timer.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/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/utils/vformat.h> +#include <linux/workqueue.h> +#include <linux/dma-mapping.h> +#include <linux/atomic.h> + +#include <linux/module.h> +#include <linux/slab.h> + +#include <linux/amlogic/media/utils/vdec_reg.h> +#include "../../../stream_input/amports/amports_priv.h" +#include "../utils/vdec.h" +#include "../utils/amvdec.h" + +#define TIME_TASK_PRINT_ENABLE 0x100 +#define PUT_PRINT_ENABLE 0x200 + +#define DRIVER_NAME "amvdec_h264mvc" +#define MODULE_NAME "amvdec_h264mvc" + +#define HANDLE_h264mvc_IRQ + +#define DEBUG_PTS +#define DEBUG_SKIP + +#define PUT_INTERVAL (HZ/100) + +#define STAT_TIMER_INIT 0x01 +#define STAT_MC_LOAD 0x02 +#define STAT_ISR_REG 0x04 +#define STAT_VF_HOOK 0x08 +#define STAT_TIMER_ARM 0x10 +#define STAT_VDEC_RUN 0x20 + +#define DROPPING_THREAD_HOLD 4 +#define DROPPING_FIRST_WAIT 16 +#define DISPLAY_INVALID_POS -65536 + +#define INIT_DROP_FRAME_CNT 8 + +static int vh264mvc_vf_states(struct vframe_states *states, void *); +static struct vframe_s *vh264mvc_vf_peek(void *); +static struct vframe_s *vh264mvc_vf_get(void *); +static void vh264mvc_vf_put(struct vframe_s *, void *); +static int vh264mvc_event_cb(int type, void *data, void *private_data); + +static void vh264mvc_prot_init(void); +static void vh264mvc_local_init(void); +static void vh264mvc_put_timer_func(unsigned long arg); + +static const char vh264mvc_dec_id[] = "vh264mvc-dev"; + +#define PROVIDER_NAME "decoder.h264mvc" + +static const struct vframe_operations_s vh264mvc_vf_provider = { + .peek = vh264mvc_vf_peek, + .get = vh264mvc_vf_get, + .put = vh264mvc_vf_put, + .event_cb = vh264mvc_event_cb, + .vf_states = vh264mvc_vf_states, +}; + +static struct vframe_provider_s vh264mvc_vf_prov; + +static u32 frame_width, frame_height, frame_dur; +static u32 saved_resolution; +static struct timer_list recycle_timer; +static u32 stat; +static u32 pts_outside; +static u32 sync_outside; +static u32 vh264mvc_ratio; +static u32 h264mvc_ar; +static u32 no_dropping_cnt; +static s32 init_drop_cnt; + +#ifdef DEBUG_SKIP +static unsigned long view_total, view_dropped; +#endif + +#ifdef DEBUG_PTS +static unsigned long pts_missed, pts_hit; +#endif + +static atomic_t vh264mvc_active = ATOMIC_INIT(0); +static struct work_struct error_wd_work; + +static struct dec_sysinfo vh264mvc_amstream_dec_info; +static dma_addr_t mc_dma_handle; +static void *mc_cpu_addr; + +static DEFINE_SPINLOCK(lock); + +static int vh264mvc_stop(void); +static s32 vh264mvc_init(void); + +/*************************** +* new +***************************/ + +/* bit[3:0] command : */ +/* 0 - command finished */ +/* (DATA0 - {level_idc_mmco, max_reference_frame_num, width, height} */ +/* 1 - alloc view_0 display_buffer and reference_data_area */ +/* 2 - alloc view_1 display_buffer and reference_data_area */ +#define MAILBOX_COMMAND AV_SCRATCH_0 +#define MAILBOX_DATA_0 AV_SCRATCH_1 +#define MAILBOX_DATA_1 AV_SCRATCH_2 +#define MAILBOX_DATA_2 AV_SCRATCH_3 +#define CANVAS_START AV_SCRATCH_6 +#define BUFFER_RECYCLE AV_SCRATCH_7 +#define DROP_CONTROL AV_SCRATCH_8 +#define PICTURE_COUNT AV_SCRATCH_9 +#define DECODE_STATUS AV_SCRATCH_A +#define SPS_STATUS AV_SCRATCH_B +#define PPS_STATUS AV_SCRATCH_C +#define SIM_RESERV_D AV_SCRATCH_D +#define WORKSPACE_START AV_SCRATCH_E +#define SIM_RESERV_F AV_SCRATCH_F +#define DECODE_ERROR_CNT AV_SCRATCH_G +#define CURRENT_UCODE AV_SCRATCH_H +#define CURRENT_SPS_PPS AV_SCRATCH_I/* bit[15:9]-SPS, bit[8:0]-PPS */ +#define DECODE_SKIP_PICTURE AV_SCRATCH_J +#define UCODE_START_ADDR AV_SCRATCH_K +#define SIM_RESERV_L AV_SCRATCH_L +#define REF_START_VIEW_0 AV_SCRATCH_M +#define REF_START_VIEW_1 AV_SCRATCH_N + +/******************************************** + * Mailbox command + ********************************************/ +#define CMD_FINISHED 0 +#define CMD_ALLOC_VIEW_0 1 +#define CMD_ALLOC_VIEW_1 2 +#define CMD_FRAME_DISPLAY 3 +#define CMD_FATAL_ERROR 4 + +#define CANVAS_INDEX_START 0x78 +/* /AMVDEC_H264MVC_CANVAS_INDEX */ + +#define MC_TOTAL_SIZE (28*SZ_1K) +#define MC_SWAP_SIZE (4*SZ_1K) + +unsigned DECODE_BUFFER_START = 0x00200000; +unsigned DECODE_BUFFER_END = 0x05000000; + +#define DECODE_BUFFER_NUM_MAX 16 +#define DISPLAY_BUFFER_NUM 4 + +static unsigned int ANC_CANVAS_ADDR; +static unsigned int index; +static unsigned int dpb_start_addr[3]; +static unsigned int ref_start_addr[2]; +static unsigned int max_dec_frame_buffering[2]; +static unsigned int total_dec_frame_buffering[2]; +static unsigned int level_idc, max_reference_frame_num, mb_width, mb_height; +static unsigned int dpb_size, ref_size; + +static int display_buff_id; +static int display_view_id; +static int display_POC; +static int stream_offset; + +#define video_domain_addr(adr) (adr&0x7fffffff) +static unsigned work_space_adr; +static unsigned work_space_size = 0xa0000; + +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; +}; +static struct buffer_spec_s buffer_spec0[DECODE_BUFFER_NUM_MAX + + DISPLAY_BUFFER_NUM]; +static struct buffer_spec_s buffer_spec1[DECODE_BUFFER_NUM_MAX + + DISPLAY_BUFFER_NUM]; + +/* + dbg_mode: + bit 0: 1, print debug information + bit 4: 1, recycle buffer without displaying; + bit 5: 1, buffer single frame step , set dbg_cmd to 1 to step + +*/ +static int dbg_mode; +static int dbg_cmd; +static int view_mode = + 3; /* 0, left; 1 ,right ; 2, left<->right 3, right<->left */ +static int drop_rate = 2; +static int drop_thread_hold; +/**/ +#define MVC_BUF_NUM (DECODE_BUFFER_NUM_MAX+DISPLAY_BUFFER_NUM) +struct mvc_buf_s { + struct list_head list; + struct vframe_s vframe; + int display_POC; + int view0_buff_id; + int view1_buff_id; + int view0_drop; + int view1_drop; + int stream_offset; + unsigned pts; +} /*mvc_buf_t */; + +#define spec2canvas(x) \ + (((x)->v_canvas_index << 16) | \ + ((x)->u_canvas_index << 8) | \ + ((x)->y_canvas_index << 0)) + +#define to_mvcbuf(vf) \ + container_of(vf, struct mvc_buf_s, vframe) + +static int vf_buf_init_flag; + +static void init_vf_buf(void) +{ + + vf_buf_init_flag = 1; +} + +static void uninit_vf_buf(void) +{ + +} + +/* #define QUEUE_SUPPORT */ + +struct mvc_info_s { + int view0_buf_id; + int view1_buf_id; + int view0_drop; + int view1_drop; + int display_pos; + int used; + int slot; + unsigned stream_offset; +}; + +#define VF_POOL_SIZE 20 +static struct vframe_s vfpool[VF_POOL_SIZE]; +static struct mvc_info_s vfpool_idx[VF_POOL_SIZE]; +static s32 view0_vfbuf_use[DECODE_BUFFER_NUM_MAX]; +static s32 view1_vfbuf_use[DECODE_BUFFER_NUM_MAX]; + +static s32 fill_ptr, get_ptr, putting_ptr, put_ptr; +static s32 dirty_frame_num; +static s32 enable_recycle; + +static s32 init_drop_frame_id[INIT_DROP_FRAME_CNT]; +#define INCPTR(p) ptr_atomic_wrap_inc(&p) +static inline void ptr_atomic_wrap_inc(u32 *ptr) +{ + u32 i = *ptr; + + i++; + + if (i >= VF_POOL_SIZE) + i = 0; + + *ptr = i; +} + +static void set_frame_info(struct vframe_s *vf) +{ + unsigned int ar = 0; + + vf->width = frame_width; + vf->height = frame_height; + vf->duration = frame_dur; + vf->duration_pulldown = 0; + + if (vh264mvc_ratio == 0) { + /* always stretch to 16:9 */ + vf->ratio_control |= (0x90 << + DISP_RATIO_ASPECT_RATIO_BIT); + } else { + /* h264mvc_ar = ((float)frame_height/frame_width) + *customer_ratio; */ + ar = min_t(u32, h264mvc_ar, DISP_RATIO_ASPECT_RATIO_MAX); + + vf->ratio_control = (ar << DISP_RATIO_ASPECT_RATIO_BIT); + } + + return; +} + +static int vh264mvc_vf_states(struct vframe_states *states, void *op_arg) +{ + unsigned long flags; + int i; + spin_lock_irqsave(&lock, flags); + states->vf_pool_size = VF_POOL_SIZE; + + i = put_ptr - fill_ptr; + if (i < 0) + i += VF_POOL_SIZE; + states->buf_free_num = i; + + i = putting_ptr - put_ptr; + if (i < 0) + i += VF_POOL_SIZE; + states->buf_recycle_num = i; + + i = fill_ptr - get_ptr; + if (i < 0) + i += VF_POOL_SIZE; + states->buf_avail_num = i; + + spin_unlock_irqrestore(&lock, flags); + return 0; +} + +void send_drop_cmd(void) +{ + int ready_cnt = 0; + int temp_get_ptr = get_ptr; + int temp_fill_ptr = fill_ptr; + while (temp_get_ptr != temp_fill_ptr) { + if ((vfpool_idx[temp_get_ptr].view0_buf_id >= 0) + && (vfpool_idx[temp_get_ptr].view1_buf_id >= 0) + && (vfpool_idx[temp_get_ptr].view0_drop == 0) + && (vfpool_idx[temp_get_ptr].view1_drop == 0)) + ready_cnt++; + INCPTR(temp_get_ptr); + } + if (dbg_mode & 0x40) { + pr_info("ready_cnt is %d ; no_dropping_cnt is %d\n", ready_cnt, + no_dropping_cnt); + } + if ((no_dropping_cnt >= DROPPING_FIRST_WAIT) + && (ready_cnt < drop_thread_hold)) + WRITE_VREG(DROP_CONTROL, (1 << 31) | (drop_rate)); + else + WRITE_VREG(DROP_CONTROL, 0); +} + +#if 0 +int get_valid_frame(void) +{ + int ready_cnt = 0; + int temp_get_ptr = get_ptr; + int temp_fill_ptr = fill_ptr; + while (temp_get_ptr != temp_fill_ptr) { + if ((vfpool_idx[temp_get_ptr].view0_buf_id >= 0) + && (vfpool_idx[temp_get_ptr].view1_buf_id >= 0) + && (vfpool_idx[temp_get_ptr].view0_drop == 0) + && (vfpool_idx[temp_get_ptr].view1_drop == 0)) + ready_cnt++; + INCPTR(temp_get_ptr); + } + return ready_cnt; +} +#endif +static struct vframe_s *vh264mvc_vf_peek(void *op_arg) +{ + + if (get_ptr == fill_ptr) + return NULL; + send_drop_cmd(); + return &vfpool[get_ptr]; + +} + +static struct vframe_s *vh264mvc_vf_get(void *op_arg) +{ + + struct vframe_s *vf; + int view0_buf_id; + int view1_buf_id; + if (get_ptr == fill_ptr) + return NULL; + + view0_buf_id = vfpool_idx[get_ptr].view0_buf_id; + view1_buf_id = vfpool_idx[get_ptr].view1_buf_id; + vf = &vfpool[get_ptr]; + + if ((view0_buf_id >= 0) && (view1_buf_id >= 0)) { + if (view_mode == 0 || view_mode == 1) { + vf->type = VIDTYPE_PROGRESSIVE | VIDTYPE_VIU_FIELD; + vf->canvas0Addr = vf->canvas1Addr = + (view_mode == + 0) ? spec2canvas(&buffer_spec0[view0_buf_id]) : + spec2canvas(&buffer_spec1[view1_buf_id]); + } else { + vf->type = VIDTYPE_PROGRESSIVE | VIDTYPE_MVC; + + vf->left_eye.start_x = 0; + vf->left_eye.start_y = 0; + vf->left_eye.width = vf->width; + vf->left_eye.height = vf->height; + vf->right_eye.start_x = 0; + vf->right_eye.start_y = 0; + vf->right_eye.width = vf->width; + vf->right_eye.height = vf->height; + vf->trans_fmt = TVIN_TFMT_3D_TB; + + if (view_mode == 2) { + vf->canvas0Addr = + spec2canvas(&buffer_spec1[ + view1_buf_id]); + vf->canvas1Addr = + spec2canvas(&buffer_spec0[ + view0_buf_id]); + } else { + vf->canvas0Addr = + spec2canvas(&buffer_spec0[ + view0_buf_id]); + vf->canvas1Addr = + spec2canvas(&buffer_spec1[ + view1_buf_id]); + } + } + } + vf->type_original = vf->type; + if (((vfpool_idx[get_ptr].view0_drop != 0) + || (vfpool_idx[get_ptr].view1_drop != 0)) + && ((no_dropping_cnt >= DROPPING_FIRST_WAIT))) + vf->frame_dirty = 1; + else + vf->frame_dirty = 0; + + INCPTR(get_ptr); + + if (vf) { + if (frame_width == 0) + frame_width = vh264mvc_amstream_dec_info.width; + if (frame_height == 0) + frame_height = vh264mvc_amstream_dec_info.height; + + vf->width = frame_width; + vf->height = frame_height; + } + if ((no_dropping_cnt < DROPPING_FIRST_WAIT) && (vf->frame_dirty == 0)) + no_dropping_cnt++; + return vf; + +} + +static void vh264mvc_vf_put(struct vframe_s *vf, void *op_arg) +{ + + if (vf_buf_init_flag == 0) + return; + if (vf->frame_dirty) { + + vf->frame_dirty = 0; + dirty_frame_num++; + enable_recycle = 0; + if (dbg_mode & PUT_PRINT_ENABLE) { + pr_info("invalid: dirty_frame_num is !!! %d\n", + dirty_frame_num); + } + } else { + INCPTR(putting_ptr); + while (dirty_frame_num > 0) { + INCPTR(putting_ptr); + dirty_frame_num--; + } + enable_recycle = 1; + if (dbg_mode & PUT_PRINT_ENABLE) { + pr_info("valid: dirty_frame_num is @@@ %d\n", + dirty_frame_num); + } + /* send_drop_cmd(); */ + } + +} + +static int vh264mvc_event_cb(int type, void *data, void *private_data) +{ + if (type & VFRAME_EVENT_RECEIVER_RESET) { + unsigned long flags; + amvdec_stop(); +#ifndef CONFIG_AMLOGIC_POST_PROCESS_MANAGER + vf_light_unreg_provider(&vh264mvc_vf_prov); +#endif + spin_lock_irqsave(&lock, flags); + vh264mvc_local_init(); + vh264mvc_prot_init(); + spin_unlock_irqrestore(&lock, flags); +#ifndef CONFIG_AMLOGIC_POST_PROCESS_MANAGER + vf_reg_provider(&vh264mvc_vf_prov); +#endif + amvdec_start(); + } + return 0; +} + +/**/ +static long init_canvas(int start_addr, long dpb_size, int dpb_number, + int mb_width, int mb_height, + struct buffer_spec_s *buffer_spec) +{ + + int dpb_addr, addr; + int i; + int mb_total; + + /* cav_con canvas; */ + + dpb_addr = start_addr; + + mb_total = mb_width * mb_height; + + for (i = 0; i < dpb_number; i++) { + WRITE_VREG(ANC_CANVAS_ADDR, + index | ((index + 1) << 8) | + ((index + 2) << 16)); + ANC_CANVAS_ADDR++; + + addr = dpb_addr; + buffer_spec[i].y_addr = addr; + buffer_spec[i].y_canvas_index = index; + canvas_config(index, + addr, + mb_width << 4, + mb_height << 4, + CANVAS_ADDR_NOWRAP, CANVAS_BLKMODE_32X32); + + addr += mb_total << 8; + index++; + buffer_spec[i].u_addr = addr; + buffer_spec[i].u_canvas_index = index; + canvas_config(index, + addr, + mb_width << 3, + mb_height << 3, + CANVAS_ADDR_NOWRAP, CANVAS_BLKMODE_32X32); + + addr += mb_total << 6; + index++; + buffer_spec[i].v_addr = addr; + buffer_spec[i].v_canvas_index = index; + canvas_config(index, + addr, + mb_width << 3, + mb_height << 3, + CANVAS_ADDR_NOWRAP, CANVAS_BLKMODE_32X32); + + addr += mb_total << 6; + index++; + + dpb_addr = dpb_addr + dpb_size; + if (dpb_addr >= DECODE_BUFFER_END) + return -1; + } + + return dpb_addr; +} + +static int get_max_dec_frame_buf_size(int level_idc, + int max_reference_frame_num, int mb_width, + int mb_height) +{ + int pic_size = mb_width * mb_height * 384; + + int size = 0; + + switch (level_idc) { + case 9: + size = 152064; + break; + case 10: + size = 152064; + break; + case 11: + size = 345600; + break; + case 12: + size = 912384; + break; + case 13: + size = 912384; + break; + case 20: + size = 912384; + break; + case 21: + size = 1824768; + break; + case 22: + size = 3110400; + break; + case 30: + size = 3110400; + break; + case 31: + size = 6912000; + break; + case 32: + size = 7864320; + break; + case 40: + size = 12582912; + break; + case 41: + size = 12582912; + break; + case 42: + size = 13369344; + break; + case 50: + size = 42393600; + break; + case 51: + size = 70778880; + break; + default: + break; + } + + size /= pic_size; + size = size + 1; /* For MVC need onr more buffer */ + if (max_reference_frame_num > size) + size = max_reference_frame_num; + if (size > DECODE_BUFFER_NUM_MAX) + size = DECODE_BUFFER_NUM_MAX; + + return size; +} + +int check_in_list(int pos, int *slot) +{ + int i; + int ret = 0; + for (i = 0; i < VF_POOL_SIZE; i++) { + if ((vfpool_idx[i].display_pos == pos) + && (vfpool_idx[i].used == 0)) { + ret = 1; + *slot = vfpool_idx[i].slot; + break; + } + } + return ret; +} + +#ifdef HANDLE_h264mvc_IRQ +static irqreturn_t vh264mvc_isr(int irq, void *dev_id) +#else +static void vh264mvc_isr(void) +#endif +{ + int drop_status; + struct vframe_s *vf; + unsigned int pts, pts_valid = 0; + u64 pts_us64; + int ret = READ_VREG(MAILBOX_COMMAND); + /* pr_info("vh264mvc_isr, cmd =%x\n", ret); */ + switch (ret & 0xff) { + case CMD_ALLOC_VIEW_0: + if (dbg_mode & 0x1) { + pr_info + ("Start H264 display buffer allocation for view 0\n"); + } + if ((dpb_start_addr[0] != -1) | (dpb_start_addr[1] != -1)) { + dpb_start_addr[0] = -1; + dpb_start_addr[1] = -1; + } + dpb_start_addr[0] = DECODE_BUFFER_START; + ret = READ_VREG(MAILBOX_DATA_0); + level_idc = (ret >> 24) & 0xff; + max_reference_frame_num = (ret >> 16) & 0xff; + mb_width = (ret >> 8) & 0xff; + mb_height = (ret >> 0) & 0xff; + max_dec_frame_buffering[0] = + get_max_dec_frame_buf_size(level_idc, + max_reference_frame_num, + mb_width, mb_height); + + total_dec_frame_buffering[0] = + max_dec_frame_buffering[0] + DISPLAY_BUFFER_NUM; + + mb_width = (mb_width + 3) & 0xfffffffc; + mb_height = (mb_height + 3) & 0xfffffffc; + + dpb_size = mb_width * mb_height * 384; + ref_size = mb_width * mb_height * 96; + + if (dbg_mode & 0x1) { + pr_info("dpb_size: 0x%x\n", dpb_size); + pr_info("ref_size: 0x%x\n", ref_size); + pr_info("total_dec_frame_buffering[0] : 0x%x\n", + total_dec_frame_buffering[0]); + pr_info("max_reference_frame_num: 0x%x\n", + max_reference_frame_num); + } + ref_start_addr[0] = dpb_start_addr[0] + + (dpb_size * total_dec_frame_buffering[0]); + dpb_start_addr[1] = ref_start_addr[0] + + (ref_size * (max_reference_frame_num + 1)); + + if (dbg_mode & 0x1) { + pr_info("dpb_start_addr[0]: 0x%x\n", dpb_start_addr[0]); + pr_info("ref_start_addr[0]: 0x%x\n", ref_start_addr[0]); + pr_info("dpb_start_addr[1]: 0x%x\n", dpb_start_addr[1]); + } + if (dpb_start_addr[1] >= DECODE_BUFFER_END) { + pr_info(" No enough memory for alloc view 0\n"); + goto exit; + } + + index = CANVAS_INDEX_START; + ANC_CANVAS_ADDR = ANC0_CANVAS_ADDR; + + ret = + init_canvas(dpb_start_addr[0], dpb_size, + total_dec_frame_buffering[0], mb_width, + mb_height, buffer_spec0); + + if (ret == -1) { + pr_info(" Un-expected memory alloc problem\n"); + goto exit; + } + + WRITE_VREG(REF_START_VIEW_0, + video_domain_addr(ref_start_addr[0])); + WRITE_VREG(MAILBOX_DATA_0, + (max_dec_frame_buffering[0] << 8) | + (total_dec_frame_buffering[0] << 0) + ); + WRITE_VREG(MAILBOX_DATA_1, ref_size); + WRITE_VREG(MAILBOX_COMMAND, CMD_FINISHED); + + if (dbg_mode & 0x1) { + pr_info + ("End H264 display buffer allocation for view 0\n"); + } + if (frame_width == 0) { + if (vh264mvc_amstream_dec_info.width) + frame_width = vh264mvc_amstream_dec_info.width; + else + frame_width = mb_width << 4; + } + if (frame_height == 0) { + frame_height = mb_height << 4; + if (frame_height == 1088) + frame_height = 1080; + } + break; + case CMD_ALLOC_VIEW_1: + if (dbg_mode & 0x1) { + pr_info + ("Start H264 display buffer allocation for view 1\n"); + } + if ((dpb_start_addr[0] == -1) | (dpb_start_addr[1] == -1)) { + pr_info("Error: allocation view 1 before view 0 !!!\n"); + break; + } + ret = READ_VREG(MAILBOX_DATA_0); + level_idc = (ret >> 24) & 0xff; + max_reference_frame_num = (ret >> 16) & 0xff; + mb_width = (ret >> 8) & 0xff; + mb_height = (ret >> 0) & 0xff; + max_dec_frame_buffering[1] = + get_max_dec_frame_buf_size(level_idc, + max_reference_frame_num, + mb_width, mb_height); + if (max_dec_frame_buffering[1] != max_dec_frame_buffering[0]) { + pr_info + (" Warning: view0/1 max_dec_frame_buffering "); + pr_info("different : 0x%x/0x%x, Use View0\n", + max_dec_frame_buffering[0], + max_dec_frame_buffering[1]); + max_dec_frame_buffering[1] = max_dec_frame_buffering[0]; + } + + total_dec_frame_buffering[1] = + max_dec_frame_buffering[1] + DISPLAY_BUFFER_NUM; + + mb_width = (mb_width + 3) & 0xfffffffc; + mb_height = (mb_height + 3) & 0xfffffffc; + + dpb_size = mb_width * mb_height * 384; + ref_size = mb_width * mb_height * 96; + + if (dbg_mode & 0x1) { + pr_info("dpb_size: 0x%x\n", dpb_size); + pr_info("ref_size: 0x%x\n", ref_size); + pr_info("total_dec_frame_buffering[1] : 0x%x\n", + total_dec_frame_buffering[1]); + pr_info("max_reference_frame_num: 0x%x\n", + max_reference_frame_num); + } + ref_start_addr[1] = dpb_start_addr[1] + + (dpb_size * total_dec_frame_buffering[1]); + dpb_start_addr[2] = ref_start_addr[1] + + (ref_size * (max_reference_frame_num + 1)); + + if (dbg_mode & 0x1) { + pr_info("dpb_start_addr[1]: 0x%x\n", dpb_start_addr[1]); + pr_info("ref_start_addr[1]: 0x%x\n", ref_start_addr[1]); + pr_info("dpb_start_addr[2]: 0x%x\n", dpb_start_addr[2]); + } + if (dpb_start_addr[2] >= DECODE_BUFFER_END) { + pr_info(" No enough memory for alloc view 1\n"); + goto exit; + } + + index = CANVAS_INDEX_START + total_dec_frame_buffering[0] * 3; + ANC_CANVAS_ADDR = + ANC0_CANVAS_ADDR + total_dec_frame_buffering[0]; + + ret = + init_canvas(dpb_start_addr[1], dpb_size, + total_dec_frame_buffering[1], mb_width, + mb_height, buffer_spec1); + + if (ret == -1) { + pr_info(" Un-expected memory alloc problem\n"); + goto exit; + } + + WRITE_VREG(REF_START_VIEW_1, + video_domain_addr(ref_start_addr[1])); + WRITE_VREG(MAILBOX_DATA_0, + (max_dec_frame_buffering[1] << 8) | + (total_dec_frame_buffering[1] << 0) + ); + WRITE_VREG(MAILBOX_DATA_1, ref_size); + WRITE_VREG(MAILBOX_COMMAND, CMD_FINISHED); + + if (dbg_mode & 0x1) { + pr_info + ("End H264 display buffer allocation for view 1\n"); + } + if (frame_width == 0) { + if (vh264mvc_amstream_dec_info.width) + frame_width = vh264mvc_amstream_dec_info.width; + else + frame_width = mb_width << 4; + } + if (frame_height == 0) { + frame_height = mb_height << 4; + if (frame_height == 1088) + frame_height = 1080; + } + break; + case CMD_FRAME_DISPLAY: + ret = READ_VREG(MAILBOX_DATA_0); + display_buff_id = (ret >> 0) & 0x3f; + display_view_id = (ret >> 6) & 0x3; + drop_status = (ret >> 8) & 0x1; + display_POC = READ_VREG(MAILBOX_DATA_1); + stream_offset = READ_VREG(MAILBOX_DATA_2); + /* if (display_view_id == 0) */ + WRITE_VREG(MAILBOX_COMMAND, CMD_FINISHED); + +#ifdef DEBUG_SKIP + view_total++; + if (drop_status) + view_dropped++; +#endif + if (dbg_mode & 0x1) { + pr_info + (" H264 display frame ready - View : %x, Buffer : %x\n", + display_view_id, display_buff_id); + pr_info + (" H264 display frame POC -- Buffer : %x, POC : %x\n", + display_buff_id, display_POC); + pr_info("H264 display frame ready\n"); + } + if (dbg_mode & 0x10) { + if ((dbg_mode & 0x20) == 0) { + while (READ_VREG(BUFFER_RECYCLE) != 0) + ; + WRITE_VREG(BUFFER_RECYCLE, + (display_view_id << 8) | + (display_buff_id + 1)); + display_buff_id = -1; + display_view_id = -1; + display_POC = -1; + } + } else { + unsigned char in_list_flag = 0; + + int slot = 0; + in_list_flag = check_in_list(display_POC, &slot); + + if ((dbg_mode & 0x40) && (drop_status)) { + pr_info + ("drop_status:%dview_id=%d,buff_id=%d,", + drop_status, display_view_id, display_buff_id); + pr_info + ("offset=%d, display_POC = %d,fill_ptr=0x%x\n", + stream_offset, display_POC, fill_ptr); + } + + if ((in_list_flag) && (stream_offset != 0)) { + pr_info + ("error case ,display_POC is %d, slot is %d\n", + display_POC, slot); + in_list_flag = 0; + } + if (!in_list_flag) { + if (display_view_id == 0) { + vfpool_idx[fill_ptr].view0_buf_id = + display_buff_id; + view0_vfbuf_use[display_buff_id]++; + vfpool_idx[fill_ptr].stream_offset = + stream_offset; + vfpool_idx[fill_ptr].view0_drop = + drop_status; + } + if (display_view_id == 1) { + vfpool_idx[fill_ptr].view1_buf_id = + display_buff_id; + vfpool_idx[fill_ptr].view1_drop = + drop_status; + view1_vfbuf_use[display_buff_id]++; + } + vfpool_idx[fill_ptr].slot = fill_ptr; + vfpool_idx[fill_ptr].display_pos = display_POC; + + } else { + if (display_view_id == 0) { + vfpool_idx[slot].view0_buf_id = + display_buff_id; + view0_vfbuf_use[display_buff_id]++; + vfpool_idx[slot].stream_offset = + stream_offset; + vfpool_idx[slot].view0_drop = + drop_status; + + } + if (display_view_id == 1) { + vfpool_idx[slot].view1_buf_id = + display_buff_id; + view1_vfbuf_use[display_buff_id]++; + vfpool_idx[slot].view1_drop = + drop_status; + } + vf = &vfpool[slot]; +#if 0 + if (pts_lookup_offset + (PTS_TYPE_VIDEO, + vfpool_idx[slot].stream_offset, + &vf->pts, + 0) != 0) + vf->pts = 0; +#endif + if (vfpool_idx[slot].stream_offset == 0) { + pr_info + ("error case, invalid stream offset\n"); + } + if (pts_lookup_offset_us64 + (PTS_TYPE_VIDEO, + vfpool_idx[slot].stream_offset, &pts, + 0x10000, &pts_us64) == 0) + pts_valid = 1; + else + pts_valid = 0; + vf->pts = (pts_valid) ? pts : 0; + vf->pts_us64 = (pts_valid) ? pts_us64 : 0; + /* vf->pts = vf->pts_us64 ? vf->pts_us64 + : vf->pts ; */ + /* vf->pts = vf->pts_us64; */ + if (dbg_mode & 0x80) + pr_info("vf->pts:%d\n", vf->pts); + vfpool_idx[slot].used = 1; + INCPTR(fill_ptr); + set_frame_info(vf); + vf_notify_receiver(PROVIDER_NAME, + VFRAME_EVENT_PROVIDER_VFRAME_READY, + NULL); + + } + } + break; + case CMD_FATAL_ERROR: + pr_info("fatal error !!!\n"); + schedule_work(&error_wd_work); + break; + default: + break; + } +exit: +#ifdef HANDLE_h264mvc_IRQ + return IRQ_HANDLED; +#else + return; +#endif +} + +static void vh264mvc_put_timer_func(unsigned long arg) +{ + struct timer_list *timer = (struct timer_list *)arg; + + int valid_frame = 0; + if (enable_recycle == 0) { + if (dbg_mode & TIME_TASK_PRINT_ENABLE) { + /* valid_frame = get_valid_frame(); */ + pr_info("dirty_frame_num is %d , valid frame is %d\n", + dirty_frame_num, valid_frame); + + } + /* goto RESTART; */ + } + + while ((putting_ptr != put_ptr) && (READ_VREG(BUFFER_RECYCLE) == 0)) { + int view0_buf_id = vfpool_idx[put_ptr].view0_buf_id; + int view1_buf_id = vfpool_idx[put_ptr].view1_buf_id; + if ((view0_buf_id >= 0) && + (view0_vfbuf_use[view0_buf_id] == 1)) { + if (dbg_mode & 0x100) { + pr_info + ("round 0: put_ptr is %d ;view0_buf_id is %d\n", + put_ptr, view0_buf_id); + } + WRITE_VREG(BUFFER_RECYCLE, + (0 << 8) | (view0_buf_id + 1)); + view0_vfbuf_use[view0_buf_id] = 0; + vfpool_idx[put_ptr].view0_buf_id = -1; + vfpool_idx[put_ptr].view0_drop = 0; + } else if ((view1_buf_id >= 0) + && (view1_vfbuf_use[view1_buf_id] == 1)) { + if (dbg_mode & 0x100) { + pr_info + ("round 1: put_ptr is %d ;view1_buf_id %d==\n", + put_ptr, view1_buf_id); + } + WRITE_VREG(BUFFER_RECYCLE, + (1 << 8) | (view1_buf_id + 1)); + view1_vfbuf_use[view1_buf_id] = 0; + vfpool_idx[put_ptr].display_pos = DISPLAY_INVALID_POS; + vfpool_idx[put_ptr].view1_buf_id = -1; + vfpool_idx[put_ptr].view1_drop = 0; + vfpool_idx[put_ptr].used = 0; + INCPTR(put_ptr); + } + } + + + if (frame_dur > 0 && saved_resolution != + frame_width * frame_height * (96000 / frame_dur)) { + int fps = 96000 / frame_dur; + saved_resolution = frame_width * frame_height * fps; + vdec_source_changed(VFORMAT_H264MVC, + frame_width, frame_height, fps * 2); + } + + /* RESTART: */ + timer->expires = jiffies + PUT_INTERVAL; + + add_timer(timer); +} + +int vh264mvc_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; + return 0; +} + +int vh264mvc_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 H264_DECODE_INIT(void) +{ + int i; + i = READ_VREG(DECODE_SKIP_PICTURE); + +#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 + + /* Wait for some time for RESET */ + READ_VREG(DECODE_SKIP_PICTURE); + READ_VREG(DECODE_SKIP_PICTURE); + + WRITE_VREG(DECODE_SKIP_PICTURE, i); + + /* fill_weight_pred */ + WRITE_VREG(MC_MPORT_CTRL, 0x0300); + for (i = 0; i < 192; i++) + WRITE_VREG(MC_MPORT_DAT, 0x100); + WRITE_VREG(MC_MPORT_CTRL, 0); + + WRITE_VREG(MB_WIDTH, 0xff); /* invalid mb_width */ + + /* set slice start to 0x000000 or 0x000001 for check more_rbsp_data */ + WRITE_VREG(SLICE_START_BYTE_01, 0x00000000); + WRITE_VREG(SLICE_START_BYTE_23, 0x01010000); + /* set to mpeg2 to enable mismatch logic */ + WRITE_VREG(MPEG1_2_REG, 1); + /* disable COEF_GT_64 , error_m4_table and voff_rw_err */ + WRITE_VREG(VLD_ERROR_MASK, 0x1011); + + /* Config MCPU Amrisc interrupt */ + WRITE_VREG(ASSIST_AMR1_INT0, 0x1); /* viu_vsync_int */ + WRITE_VREG(ASSIST_AMR1_INT1, 0x5); /* mbox_isr */ + WRITE_VREG(ASSIST_AMR1_INT2, 0x8); /* vld_isr */ + WRITE_VREG(ASSIST_AMR1_INT3, 0x15); /* vififo_empty */ + WRITE_VREG(ASSIST_AMR1_INT4, 0xd); /* rv_ai_mb_finished_int */ + WRITE_VREG(ASSIST_AMR1_INT7, 0x14); /* dcac_dma_done */ + + /* Config MCPU Amrisc interrupt */ + WRITE_VREG(ASSIST_AMR1_INT5, 0x9); /* MCPU interrupt */ + WRITE_VREG(ASSIST_AMR1_INT6, 0x17); /* CCPU interrupt */ + + WRITE_VREG(CPC_P, 0xc00); /* CCPU Code will start from 0xc00 */ + WRITE_VREG(CINT_VEC_BASE, (0xc20 >> 5)); +#if 0 + WRITE_VREG(POWER_CTL_VLD, + READ_VREG(POWER_CTL_VLD) | (0 << 10) | + (1 << 9) | (1 << 6)); +#else + WRITE_VREG(POWER_CTL_VLD, ((1 << 10) | /* disable cabac_step_2 */ + (1 << 9) | /* viff_drop_flag_en */ + (1 << 6) /* h264_000003_en */ + ) + ); +#endif + WRITE_VREG(M4_CONTROL_REG, (1 << 13)); /* H264_DECODE_INFO - h264_en */ + + WRITE_VREG(CANVAS_START, CANVAS_INDEX_START); +#if 1 + /* Start Address of Workspace (UCODE, temp_data...) */ + WRITE_VREG(WORKSPACE_START, + video_domain_addr(work_space_adr)); +#else + /* Start Address of Workspace (UCODE, temp_data...) */ + WRITE_VREG(WORKSPACE_START, + 0x05000000); +#endif + /* Clear all sequence parameter set available */ + WRITE_VREG(SPS_STATUS, 0); + /* Clear all picture parameter set available */ + WRITE_VREG(PPS_STATUS, 0); + /* Set current microcode to NULL */ + WRITE_VREG(CURRENT_UCODE, 0xff); + /* Set current SPS/PPS to NULL */ + WRITE_VREG(CURRENT_SPS_PPS, 0xffff); + /* Set decode status to DECODE_START_HEADER */ + WRITE_VREG(DECODE_STATUS, 1); +} + +static void vh264mvc_prot_init(void) +{ + while (READ_VREG(DCAC_DMA_CTRL) & 0x8000) + ; + while (READ_VREG(LMEM_DMA_CTRL) & 0x8000) + ; /* reg address is 0x350 */ + /* clear mailbox interrupt */ + WRITE_VREG(ASSIST_MBOX1_CLR_REG, 1); + + /* enable mailbox interrupt */ + WRITE_VREG(ASSIST_MBOX1_MASK, 1); + + /* disable PSCALE for hardware sharing */ + WRITE_VREG(PSCALE_CTRL, 0); + + H264_DECODE_INIT(); + +#if 1 /* /MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON6 */ + WRITE_VREG(DOS_SW_RESET0, (1 << 11)); + 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, 0x80); /* RESET MCPU */ +#endif + + WRITE_VREG(MAILBOX_COMMAND, 0); + WRITE_VREG(BUFFER_RECYCLE, 0); + WRITE_VREG(DROP_CONTROL, 0); + CLEAR_VREG_MASK(MDEC_PIC_DC_CTRL, 1 << 17); +#if 1 /* /MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON8 */ + WRITE_VREG(MDEC_PIC_DC_THRESH, 0x404038aa); +#endif +} + +static void vh264mvc_local_init(void) +{ + int i; + display_buff_id = -1; + display_view_id = -1; + display_POC = -1; + no_dropping_cnt = 0; + init_drop_cnt = INIT_DROP_FRAME_CNT; + + for (i = 0; i < INIT_DROP_FRAME_CNT; i++) + init_drop_frame_id[i] = 0; + +#ifdef DEBUG_PTS + pts_missed = 0; + pts_hit = 0; +#endif + +#ifdef DEBUG_SKIP + view_total = 0; + view_dropped = 0; +#endif + + /* vh264mvc_ratio = vh264mvc_amstream_dec_info.ratio; */ + vh264mvc_ratio = 0x100; + + /* frame_width = vh264mvc_amstream_dec_info.width; */ + /* frame_height = vh264mvc_amstream_dec_info.height; */ + frame_dur = vh264mvc_amstream_dec_info.rate; + if (frame_dur == 0) + frame_dur = 96000 / 24; + + pts_outside = ((unsigned long) vh264mvc_amstream_dec_info.param) & 0x01; + sync_outside = ((unsigned long) vh264mvc_amstream_dec_info.param & 0x02) + >> 1; + + /**/ dpb_start_addr[0] = -1; + dpb_start_addr[1] = -1; + max_dec_frame_buffering[0] = -1; + max_dec_frame_buffering[1] = -1; + fill_ptr = get_ptr = put_ptr = putting_ptr = 0; + dirty_frame_num = 0; + for (i = 0; i < DECODE_BUFFER_NUM_MAX; i++) { + view0_vfbuf_use[i] = 0; + view1_vfbuf_use[i] = 0; + } + + for (i = 0; i < VF_POOL_SIZE; i++) { + vfpool_idx[i].display_pos = -1; + vfpool_idx[i].view0_buf_id = DISPLAY_INVALID_POS; + vfpool_idx[i].view1_buf_id = -1; + vfpool_idx[i].view0_drop = 0; + vfpool_idx[i].view1_drop = 0; + vfpool_idx[i].used = 0; + } + for (i = 0; i < VF_POOL_SIZE; i++) + memset(&vfpool[i], 0, sizeof(struct vframe_s)); + init_vf_buf(); + return; +} + +static s32 vh264mvc_init(void) +{ + int ret = -1, size = -1; + char *buf = vmalloc(0x1000 * 16); + if (IS_ERR_OR_NULL(buf)) + return -ENOMEM; + + pr_info("\nvh264mvc_init\n"); + init_timer(&recycle_timer); + + stat |= STAT_TIMER_INIT; + + vh264mvc_local_init(); + + 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(); + vfree(buf); + pr_err("vh264_mvc init: Can not allocate mc memory.\n"); + return -ENOMEM; + } + + WRITE_VREG(UCODE_START_ADDR, mc_dma_handle); + + size = get_firmware_data(VIDEO_DEC_H264_MVC, buf); + if (size < 0) { + pr_err("get firmware fail."); + vfree(buf); + return -1; + } + + ret = amvdec_loadmc_ex(VFORMAT_H264MVC, NULL, buf); + + /*header*/ + memcpy((u8 *) mc_cpu_addr, buf + 0x1000, 0x1000); + /*mmco*/ + memcpy((u8 *) mc_cpu_addr + 0x1000, buf + 0x2000, 0x2000); + /*slice*/ + memcpy((u8 *) mc_cpu_addr + 0x3000, buf + 0x4000, 0x3000); + + vfree(buf); + + if (ret < 0) { + amvdec_disable(); + + 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; + + /* enable AMRISC side protocol */ + vh264mvc_prot_init(); + +#ifdef HANDLE_h264mvc_IRQ + if (vdec_request_irq(VDEC_IRQ_1, vh264mvc_isr, + "vh264mvc-irq", (void *)vh264mvc_dec_id)) { + pr_info("vh264mvc irq register error.\n"); + amvdec_disable(); + return -ENOENT; + } +#endif + + stat |= STAT_ISR_REG; + + vf_provider_init(&vh264mvc_vf_prov, PROVIDER_NAME, + &vh264mvc_vf_provider, NULL); + vf_reg_provider(&vh264mvc_vf_prov); + vf_notify_receiver(PROVIDER_NAME, VFRAME_EVENT_PROVIDER_START, NULL); + + stat |= STAT_VF_HOOK; + + recycle_timer.data = (ulong) (&recycle_timer); + recycle_timer.function = vh264mvc_put_timer_func; + recycle_timer.expires = jiffies + PUT_INTERVAL; + + add_timer(&recycle_timer); + + stat |= STAT_TIMER_ARM; + + amvdec_start(); + + stat |= STAT_VDEC_RUN; + + return 0; +} + +static int vh264mvc_stop(void) +{ + if (stat & STAT_VDEC_RUN) { + amvdec_stop(); + stat &= ~STAT_VDEC_RUN; + } + + if (stat & STAT_ISR_REG) { + WRITE_VREG(ASSIST_MBOX1_MASK, 0); +#ifdef HANDLE_h264mvc_IRQ + vdec_free_irq(VDEC_IRQ_1, (void *)vh264mvc_dec_id); +#endif + stat &= ~STAT_ISR_REG; + } + + if (stat & STAT_TIMER_ARM) { + del_timer_sync(&recycle_timer); + stat &= ~STAT_TIMER_ARM; + } + + if (stat & STAT_VF_HOOK) { + ulong flags; + spin_lock_irqsave(&lock, flags); + spin_unlock_irqrestore(&lock, flags); + vf_unreg_provider(&vh264mvc_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; + } + + stat &= ~STAT_MC_LOAD; + } + + amvdec_disable(); + + uninit_vf_buf(); + return 0; +} + +static void error_do_work(struct work_struct *work) +{ + if (atomic_read(&vh264mvc_active)) { + vh264mvc_stop(); + vh264mvc_init(); + } +} + +static int amvdec_h264mvc_probe(struct platform_device *pdev) +{ + struct resource mem; + int buf_size; + + struct vdec_s *pdata = *(struct vdec_s **)pdev->dev.platform_data; + + pr_info("amvdec_h264mvc probe start.\n"); + +#if 0 + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!mem) { + pr_info("\namvdec_h264mvc memory resource undefined.\n"); + return -EFAULT; + } +#endif + + if (pdata == NULL) { + pr_info("\namvdec_h264mvc memory resource undefined.\n"); + return -EFAULT; + } + mem.start = pdata->mem_start; + mem.end = pdata->mem_end; + + buf_size = mem.end - mem.start + 1; + /* buf_offset = mem->start - DEF_BUF_START_ADDR; */ + work_space_adr = mem.start; + DECODE_BUFFER_START = work_space_adr + work_space_size; + DECODE_BUFFER_END = mem.start + buf_size; + + pr_info + ("work_space_adr %x, DECODE_BUFFER_START %x, DECODE_BUFFER_END %x\n", + work_space_adr, DECODE_BUFFER_START, DECODE_BUFFER_END); + if (pdata->sys_info) + vh264mvc_amstream_dec_info = *pdata->sys_info; + + pdata->dec_status = vh264mvc_dec_status; + /* pdata->set_trickmode = vh264mvc_set_trickmode; */ + + if (vh264mvc_init() < 0) { + pr_info("\namvdec_h264mvc init failed.\n"); + + return -ENODEV; + } + + INIT_WORK(&error_wd_work, error_do_work); + + atomic_set(&vh264mvc_active, 1); + + pr_info("amvdec_h264mvc probe end.\n"); + + return 0; +} + +static int amvdec_h264mvc_remove(struct platform_device *pdev) +{ + pr_info("amvdec_h264mvc_remove\n"); + cancel_work_sync(&error_wd_work); + vh264mvc_stop(); + frame_width = 0; + frame_height = 0; + vdec_source_changed(VFORMAT_H264MVC, 0, 0, 0); + atomic_set(&vh264mvc_active, 0); + +#ifdef DEBUG_PTS + pr_info + ("pts missed %ld, pts hit %ld, pts_outside %d, ", + pts_missed, pts_hit, pts_outside); + pr_info("duration %d, sync_outside %d\n", + frame_dur, sync_outside); +#endif + +#ifdef DEBUG_SKIP + pr_info("view_total = %ld, dropped %ld\n", view_total, view_dropped); +#endif + + return 0; +} + +/****************************************/ + +static struct platform_driver amvdec_h264mvc_driver = { + .probe = amvdec_h264mvc_probe, + .remove = amvdec_h264mvc_remove, +#ifdef CONFIG_PM + .suspend = amvdec_suspend, + .resume = amvdec_resume, +#endif + .driver = { + .name = DRIVER_NAME, + } +}; + +static struct codec_profile_t amvdec_hmvc_profile = { + .name = "hmvc", + .profile = "" +}; + +static int __init amvdec_h264mvc_driver_init_module(void) +{ + pr_debug("amvdec_h264mvc module init\n"); + + if (platform_driver_register(&amvdec_h264mvc_driver)) { + pr_err("failed to register amvdec_h264mvc driver\n"); + return -ENODEV; + } + + vcodec_profile_register(&amvdec_hmvc_profile); + + return 0; +} + +static void __exit amvdec_h264mvc_driver_remove_module(void) +{ + pr_debug("amvdec_h264mvc module remove.\n"); + + platform_driver_unregister(&amvdec_h264mvc_driver); +} + +/****************************************/ + +module_param(stat, uint, 0664); +MODULE_PARM_DESC(stat, "\n amvdec_h264mvc stat\n"); + +module_param(dbg_mode, uint, 0664); +MODULE_PARM_DESC(dbg_mode, "\n amvdec_h264mvc dbg mode\n"); + +module_param(view_mode, uint, 0664); +MODULE_PARM_DESC(view_mode, "\n amvdec_h264mvc view mode\n"); + +module_param(dbg_cmd, uint, 0664); +MODULE_PARM_DESC(dbg_mode, "\n amvdec_h264mvc cmd mode\n"); + +module_param(drop_rate, uint, 0664); +MODULE_PARM_DESC(dbg_mode, "\n amvdec_h264mvc drop rate\n"); + +module_param(drop_thread_hold, uint, 0664); +MODULE_PARM_DESC(dbg_mode, "\n amvdec_h264mvc drop thread hold\n"); +module_init(amvdec_h264mvc_driver_init_module); +module_exit(amvdec_h264mvc_driver_remove_module); + +MODULE_DESCRIPTION("AMLOGIC h264mvc Video Decoder Driver"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Chen Zhang <chen.zhang@amlogic.com>"); diff --git a/drivers/frame_provider/decoder/h264_multi/Makefile b/drivers/frame_provider/decoder/h264_multi/Makefile new file mode 100644 index 0000000..17cb87e --- a/dev/null +++ b/drivers/frame_provider/decoder/h264_multi/Makefile @@ -0,0 +1,2 @@ +obj-m += vh264_multi.o +vh264_multi-objs += vmh264.o h264_dpb.o diff --git a/drivers/frame_provider/decoder/h264_multi/h264_dpb.c b/drivers/frame_provider/decoder/h264_multi/h264_dpb.c new file mode 100644 index 0000000..b3e8f0f --- a/dev/null +++ b/drivers/frame_provider/decoder/h264_multi/h264_dpb.c @@ -0,0 +1,5238 @@ +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/errno.h> + +#include <linux/amlogic/media/utils/vdec_reg.h> +#include "../utils/vdec.h" +#include "../utils/amvdec.h" + +#include "h264_dpb.h" + +/* #define OLD_OUTPUT_CODE */ +#undef pr_info +#define pr_info printk +int dpb_print(int index, int debug_flag, const char *fmt, ...) +{ + if (((h264_debug_flag & debug_flag) && + ((1 << index) & h264_debug_mask)) + || (debug_flag == PRINT_FLAG_ERROR)) { + unsigned char buf[512]; + int len = 0; + va_list args; + va_start(args, fmt); + if ((index & 0x100) == 0) + len = sprintf(buf, "%d: ", index); + vsnprintf(buf + len, 512-len, fmt, args); + pr_info("%s", buf); + va_end(args); + } + return 0; +} + +unsigned char dpb_is_debug(int index, int debug_flag) +{ + if (((h264_debug_flag & debug_flag) && + ((1 << index) & h264_debug_mask)) + || (debug_flag == PRINT_FLAG_ERROR)) + return 1; + return 0; +} + +#define CHECK_VALID(list_size, mark) {\ + if (list_size > MAX_LIST_SIZE || list_size < 0) { \ + dpb_print(p_H264_Dpb->decoder_index, PRINT_FLAG_ERROR, \ + "%s(%d): listXsize[%d] %d is larger than max size\r\n",\ + __func__, __LINE__, mark, list_size);\ + list_size = 0; \ + } \ + } + +static struct DecRefPicMarking_s + dummy_dec_ref_pic_marking_buffer + [DEC_REF_PIC_MARKING_BUFFER_NUM_MAX]; +static struct StorablePicture dummy_pic; +static struct FrameStore dummy_fs; +static struct StorablePicture *get_new_pic( + struct h264_dpb_stru *p_H264_Dpb, + enum PictureStructure structure, unsigned char is_output); +static void dump_dpb(struct DecodedPictureBuffer *p_Dpb); + +static void init_dummy_fs(void) +{ + dummy_fs.frame = &dummy_pic; + dummy_fs.top_field = &dummy_pic; + dummy_fs.bottom_field = &dummy_pic; + + dummy_pic.top_field = &dummy_pic; + dummy_pic.bottom_field = &dummy_pic; + dummy_pic.frame = &dummy_pic; + + dummy_pic.dec_ref_pic_marking_buffer = + &dummy_dec_ref_pic_marking_buffer[0]; +} + +enum { + LIST_0 = 0, + LIST_1 = 1, + BI_PRED = 2, + BI_PRED_L0 = 3, + BI_PRED_L1 = 4 +}; + +void ref_pic_list_reordering(struct h264_dpb_stru *p_H264_Dpb, + struct Slice *currSlice) +{ + /* struct VideoParameters *p_Vid = currSlice->p_Vid; + byte dP_nr = assignSE2partition[currSlice->dp_mode][SE_HEADER]; + DataPartition *partition = &(currSlice->partArr[dP_nr]); + Bitstream *currStream = partition->bitstream; + */ + int i, j, val; + unsigned short *reorder_cmd = + &p_H264_Dpb->dpb_param.mmco.l0_reorder_cmd[0]; + /* alloc_ref_pic_list_reordering_buffer(currSlice); */ + dpb_print(p_H264_Dpb->decoder_index, PRINT_FLAG_DPB_DETAIL, + "%s\n", __func__); + if (currSlice->slice_type != I_SLICE && + currSlice->slice_type != SI_SLICE) { + /* val = currSlice->ref_pic_list_reordering_flag[LIST_0] = + read_u_1 ("SH: ref_pic_list_reordering_flag_l0", + currStream, &p_Dec->UsedBits); */ + if (reorder_cmd[0] != 3) { + val = currSlice-> + ref_pic_list_reordering_flag[LIST_0] = 1; + } else { + val = currSlice-> + ref_pic_list_reordering_flag[LIST_0] = 0; + } + if (val) { + dpb_print(p_H264_Dpb->decoder_index, + PRINT_FLAG_DPB_DETAIL, + "%s, ref_pic_list_reordering_flag[LIST_0] is 1\n", + __func__); + + j = 0; + i = 0; + do { + val = currSlice-> + modification_of_pic_nums_idc[LIST_0][i] = + reorder_cmd[j++]; + /* read_ue_v( + "SH: modification_of_pic_nums_idc_l0", + currStream, &p_Dec->UsedBits); */ + dpb_print(p_H264_Dpb->decoder_index, + PRINT_FLAG_DPB_DETAIL, + "%d(%d):val %x\n", i, j, val); + if (j >= 66) { + currSlice-> + ref_pic_list_reordering_flag[LIST_0] = + 0; /* by rain */ + dpb_print(p_H264_Dpb->decoder_index, + PRINT_FLAG_ERROR, + "%s error\n", __func__); + break; + } + if (val == 0 || val == 1) { + currSlice-> + abs_diff_pic_num_minus1[LIST_0][i] = + reorder_cmd[j++]; + /* read_ue_v("SH: " + "abs_diff_pic_num_minus1_l0", + currStream, &p_Dec->UsedBits); */ + } else { + if (val == 2) { + currSlice-> + long_term_pic_idx[LIST_0][i] = + reorder_cmd[j++]; + /* read_ue_v( + "SH: long_term_pic_idx_l0", + currStream, + &p_Dec->UsedBits); */ + } + } + i++; + /* assert (i>currSlice-> + num_ref_idx_active[LIST_0]); */ + if (/* + i>currSlice->num_ref_idx_active[LIST_0] || + */ + i >= REORDERING_COMMAND_MAX_SIZE) { + dpb_print(p_H264_Dpb->decoder_index, + PRINT_FLAG_ERROR, + "%s error %d %d\n", + __func__, i, + currSlice-> + num_ref_idx_active[LIST_0]); + currSlice-> + ref_pic_list_reordering_flag[LIST_0] = + 0; /* by rain */ + break; + } + if (j >= 66) { + dpb_print(p_H264_Dpb->decoder_index, + PRINT_FLAG_ERROR, "%s error\n", + __func__); + currSlice-> + ref_pic_list_reordering_flag[LIST_0] = + 0; /* by rain */ + break; + } + + } while (val != 3); + } + } + + if (currSlice->slice_type == B_SLICE) { + reorder_cmd = &p_H264_Dpb->dpb_param.mmco.l1_reorder_cmd[0]; + /* val = currSlice->ref_pic_list_reordering_flag[LIST_1] + = read_u_1 ("SH: ref_pic_list_reordering_flag_l1", + currStream, + &p_Dec->UsedBits); */ + + if (reorder_cmd[0] != 3) { + val = + currSlice->ref_pic_list_reordering_flag[LIST_1] = 1; + } else { + val = + currSlice->ref_pic_list_reordering_flag[LIST_1] = 0; + } + + if (val) { + dpb_print(p_H264_Dpb->decoder_index, + PRINT_FLAG_DPB_DETAIL, + "%s, ref_pic_list_reordering_flag[LIST_1] is 1\n", + __func__); + + j = 0; + i = 0; + do { + val = currSlice-> + modification_of_pic_nums_idc[LIST_1][i] = + reorder_cmd[j++]; + /* read_ue_v( + "SH: modification_of_pic_nums_idc_l1", + currStream, + &p_Dec->UsedBits); */ + dpb_print(p_H264_Dpb->decoder_index, + PRINT_FLAG_DPB_DETAIL, + "%d(%d):val %x\n", + i, j, val); + if (j >= 66) { + dpb_print(p_H264_Dpb->decoder_index, + PRINT_FLAG_ERROR, "%s error\n", + __func__); + currSlice-> + ref_pic_list_reordering_flag[LIST_1] = + 0; /* by rain */ + break; + } + if (val == 0 || val == 1) { + currSlice-> + abs_diff_pic_num_minus1[LIST_1][i] = + reorder_cmd[j++]; + /* read_ue_v( + "SH: abs_diff_pic_num_minus1_l1", + currStream, &p_Dec->UsedBits); */ + } else { + if (val == 2) { + currSlice-> + long_term_pic_idx[LIST_1][i] = + reorder_cmd[j++]; + /* read_ue_v( + "SH: long_term_pic_idx_l1", + currStream, + &p_Dec->UsedBits); */ + } + } + i++; + /* assert(i>currSlice-> + num_ref_idx_active[LIST_1]); */ + if ( + /*i>currSlice->num_ref_idx_active[LIST_1] || */ + i >= REORDERING_COMMAND_MAX_SIZE) { + dpb_print(p_H264_Dpb->decoder_index, + PRINT_FLAG_ERROR, + "%s error %d %d\n", + __func__, i, + currSlice-> + num_ref_idx_active[LIST_0]); + currSlice-> + ref_pic_list_reordering_flag[LIST_1] = + 0; /* by rain */ + break; + } + if (j >= 66) { + dpb_print(p_H264_Dpb->decoder_index, + PRINT_FLAG_ERROR, + "%s error\n", __func__); + break; + } + } while (val != 3); + } + } + + /* set reference index of redundant slices. */ + /* + if (currSlice->redundant_pic_cnt && (currSlice->slice_type != I_SLICE)) + { + currSlice->redundant_slice_ref_idx = + currSlice->abs_diff_pic_num_minus1[LIST_0][0] + 1; + }*/ +} + +void slice_prepare(struct h264_dpb_stru *p_H264_Dpb, + struct DecodedPictureBuffer *p_Dpb, + struct VideoParameters *p_Vid, + struct SPSParameters *sps, struct Slice *pSlice) +{ + int i, j; + /* p_Vid->active_sps = sps; */ + unsigned short *mmco_cmd = &p_H264_Dpb->dpb_param.mmco.mmco_cmd[0]; + /* for decode_poc */ + sps->pic_order_cnt_type = + p_H264_Dpb->dpb_param.l.data[PIC_ORDER_CNT_TYPE]; + sps->log2_max_pic_order_cnt_lsb_minus4 = + p_H264_Dpb->dpb_param.l.data[LOG2_MAX_PIC_ORDER_CNT_LSB] - 4; + sps->num_ref_frames_in_pic_order_cnt_cycle = + p_H264_Dpb-> + dpb_param.l.data[NUM_REF_FRAMES_IN_PIC_ORDER_CNT_CYCLE]; + for (i = 0; i < 128; i++) + sps->offset_for_ref_frame[i] = + (short) p_H264_Dpb-> + dpb_param.mmco.offset_for_ref_frame_base[i]; + sps->offset_for_non_ref_pic = + (short) p_H264_Dpb->dpb_param.l.data[OFFSET_FOR_NON_REF_PIC]; + sps->offset_for_top_to_bottom_field = + (short) p_H264_Dpb->dpb_param.l.data + [OFFSET_FOR_TOP_TO_BOTTOM_FIELD]; + + pSlice->frame_num = p_H264_Dpb->dpb_param.dpb.frame_num; + pSlice->idr_flag = + (p_H264_Dpb->dpb_param.dpb.NAL_info_mmco & 0x1f) + == 5 ? 1 : 0; + pSlice->nal_reference_idc = + (p_H264_Dpb->dpb_param.dpb.NAL_info_mmco >> 5) + & 0x3; + pSlice->pic_order_cnt_lsb = + p_H264_Dpb->dpb_param.dpb.pic_order_cnt_lsb; + pSlice->field_pic_flag = 0; + pSlice->bottom_field_flag = 0; + pSlice->delta_pic_order_cnt_bottom = val( + p_H264_Dpb->dpb_param.dpb.delta_pic_order_cnt_bottom); + pSlice->delta_pic_order_cnt[0] = val( + p_H264_Dpb->dpb_param.dpb.delta_pic_order_cnt_0); + pSlice->delta_pic_order_cnt[1] = val( + p_H264_Dpb->dpb_param.dpb.delta_pic_order_cnt_1); + + p_Vid->last_has_mmco_5 = 0; + /* last memory_management_control_operation is 5 */ + p_Vid->last_pic_bottom_field = 0; + p_Vid->max_frame_num = 1 << + (p_H264_Dpb->dpb_param.l.data[LOG2_MAX_FRAME_NUM]); + + /**/ + pSlice->structure = (p_H264_Dpb-> + dpb_param.l.data[NEW_PICTURE_STRUCTURE] == 3) ? + FRAME : p_H264_Dpb->dpb_param.l.data[NEW_PICTURE_STRUCTURE]; + sps->num_ref_frames = p_H264_Dpb-> + dpb_param.l.data[MAX_REFERENCE_FRAME_NUM]; + sps->max_dpb_size = p_H264_Dpb->dpb_param.l.data[MAX_DPB_SIZE]; + if (pSlice->idr_flag) { + pSlice->long_term_reference_flag = mmco_cmd[0] & 1; + pSlice->no_output_of_prior_pics_flag = (mmco_cmd[0] >> 1) & 1; + dpb_print(p_H264_Dpb->decoder_index, + PRINT_FLAG_DPB_DETAIL, + "IDR: long_term_reference_flag %d no_output_of_prior_pics_flag %d\r\n", + pSlice->long_term_reference_flag, + pSlice->no_output_of_prior_pics_flag); + + dpb_print(p_H264_Dpb->decoder_index, + PRINT_FLAG_DPB_DETAIL, + "idr set pre_frame_num(%d) to frame_num (%d)\n", + p_Vid->pre_frame_num, pSlice->frame_num); + + p_Vid->pre_frame_num = pSlice->frame_num; + } + /* pSlice->adaptive_ref_pic_buffering_flag; */ + sps->log2_max_frame_num_minus4 = + p_H264_Dpb->dpb_param.l.data[LOG2_MAX_FRAME_NUM] - 4; + + p_Vid->non_conforming_stream = + p_H264_Dpb->dpb_param.l.data[NON_CONFORMING_STREAM]; + p_Vid->recovery_point = + p_H264_Dpb->dpb_param.l.data[RECOVERY_POINT]; + switch (p_H264_Dpb->dpb_param.l.data[SLICE_TYPE]) { + case I_Slice: + pSlice->slice_type = I_SLICE; + break; + case P_Slice: + pSlice->slice_type = P_SLICE; + break; + case B_Slice: + pSlice->slice_type = B_SLICE; + break; + default: + pSlice->slice_type = NUM_SLICE_TYPES; + break; + } + + pSlice->num_ref_idx_active[LIST_0] = + p_H264_Dpb->dpb_param.dpb.num_ref_idx_l0_active_minus1 + + 1; + /* p_H264_Dpb->dpb_param.l.data[PPS_NUM_REF_IDX_L0_ACTIVE_MINUS1]; */ + pSlice->num_ref_idx_active[LIST_1] = + p_H264_Dpb->dpb_param.dpb.num_ref_idx_l1_active_minus1 + + 1; + /* p_H264_Dpb->dpb_param.l.data[PPS_NUM_REF_IDX_L1_ACTIVE_MINUS1]; */ + + pSlice->p_Vid = p_Vid; + pSlice->p_Dpb = p_Dpb; + + p_H264_Dpb->colocated_buf_size = + p_H264_Dpb->dpb_param.l.data[FRAME_SIZE_IN_MB] * 96; + pSlice->first_mb_in_slice = + p_H264_Dpb->dpb_param.l.data[FIRST_MB_IN_SLICE]; + pSlice->mode_8x8_flags = p_H264_Dpb->dpb_param.l.data[MODE_8X8_FLAGS]; + pSlice->picture_structure_mmco = + p_H264_Dpb->dpb_param.dpb.picture_structure_mmco; + dpb_print(p_H264_Dpb->decoder_index, PRINT_FLAG_DPB_DETAIL, + "%s slice_type is %d, num_ref_idx_active[0]=%d, num_ref_idx_active[1]=%d nal_reference_idc %d\n", + __func__, pSlice->slice_type, + pSlice->num_ref_idx_active[LIST_0], + pSlice->num_ref_idx_active[LIST_1], + pSlice->nal_reference_idc); +#ifdef ERROR_CHECK + if (pSlice->num_ref_idx_active[LIST_0] >= MAX_LIST_SIZE) + pSlice->num_ref_idx_active[LIST_0] = MAX_LIST_SIZE - 1; + if (pSlice->num_ref_idx_active[LIST_1] >= MAX_LIST_SIZE) + pSlice->num_ref_idx_active[LIST_1] = MAX_LIST_SIZE - 1; +#endif + +#if 1 + /* dec_ref_pic_marking_buffer */ + pSlice->adaptive_ref_pic_buffering_flag = 0; + if (pSlice->nal_reference_idc) { + for (i = 0, j = 0; i < 44; j++) { + unsigned short val; + struct DecRefPicMarking_s *tmp_drpm = + &pSlice->dec_ref_pic_marking_buffer[j]; + memset(tmp_drpm, 0, sizeof(struct DecRefPicMarking_s)); + val = tmp_drpm-> + memory_management_control_operation = + mmco_cmd[i++]; + tmp_drpm->Next = NULL; + if (j > 0) { + pSlice-> + dec_ref_pic_marking_buffer[j - 1].Next = + tmp_drpm; + } + if (val == 0 || i >= 44) + break; + pSlice->adaptive_ref_pic_buffering_flag = 1; + if ((val == 1) || (val == 3)) { + tmp_drpm->difference_of_pic_nums_minus1 = + mmco_cmd[i++]; + } + if (val == 2) + tmp_drpm->long_term_pic_num = mmco_cmd[i++]; + if (i >= 44) + break; + if ((val == 3) || (val == 6)) + tmp_drpm->long_term_frame_idx = mmco_cmd[i++]; + if (val == 4) { + tmp_drpm->max_long_term_frame_idx_plus1 = + mmco_cmd[i++]; + } + dpb_print(p_H264_Dpb->decoder_index, + PRINT_FLAG_DPB_DETAIL, + "dec_ref_pic_marking_buffer[%d]:operation %x diff_pic_minus1 %x long_pic_num %x long_frame_idx %x max_long_frame_idx_plus1 %x\n", + j, + tmp_drpm->memory_management_control_operation, + tmp_drpm->difference_of_pic_nums_minus1, + tmp_drpm->long_term_pic_num, + tmp_drpm->long_term_frame_idx, + tmp_drpm->max_long_term_frame_idx_plus1); + } + } + + ref_pic_list_reordering(p_H264_Dpb, pSlice); +#endif + dpb_print(p_H264_Dpb->decoder_index, PRINT_FLAG_DPB_DETAIL, + "%s return\n", __func__); +} + +static void decode_poc(struct VideoParameters *p_Vid, struct Slice *pSlice) +{ + struct h264_dpb_stru *p_H264_Dpb = container_of(p_Vid, + struct h264_dpb_stru, mVideo); + struct SPSParameters *active_sps = p_Vid->active_sps; + int i; + /* for POC mode 0: */ + unsigned int MaxPicOrderCntLsb = (1 << + (active_sps->log2_max_pic_order_cnt_lsb_minus4 + 4)); + + dpb_print(p_H264_Dpb->decoder_index, + PRINT_FLAG_DEBUG_POC, + "%s:pic_order_cnt_type %d, idr_flag %d last_has_mmco_5 %d last_pic_bottom_field %d pic_order_cnt_lsb %d PrevPicOrderCntLsb %d\r\n", + __func__, + active_sps->pic_order_cnt_type, + pSlice->idr_flag, + p_Vid->last_has_mmco_5, + p_Vid->last_pic_bottom_field, + pSlice->pic_order_cnt_lsb, + p_Vid->PrevPicOrderCntLsb + ); + + dpb_print(p_H264_Dpb->decoder_index, PRINT_FLAG_DEBUG_POC, + "%s:field_pic_flag %d, bottom_field_flag %d frame_num %d PreviousFrameNum %d PreviousFrameNumOffset %d ax_frame_num %d num_ref_frames_in_pic_order_cnt_cycle %d offset_for_non_ref_pic %d\r\n", + __func__, + pSlice->field_pic_flag, + pSlice->bottom_field_flag, + pSlice->frame_num, + p_Vid->PreviousFrameNum, + p_Vid->PreviousFrameNumOffset, + p_Vid->max_frame_num, + active_sps->num_ref_frames_in_pic_order_cnt_cycle, + active_sps->offset_for_non_ref_pic + ); + + dpb_print(p_H264_Dpb->decoder_index, PRINT_FLAG_DEBUG_POC, + "%s: delta_pic_order_cnt %d %d nal_reference_idc %d\r\n", + __func__, + pSlice->delta_pic_order_cnt[0], pSlice->delta_pic_order_cnt[1], + pSlice->nal_reference_idc + ); + + + switch (active_sps->pic_order_cnt_type) { + case 0: /* POC MODE 0 */ + /* 1st */ + if (pSlice->idr_flag) { + p_Vid->PrevPicOrderCntMsb = 0; + p_Vid->PrevPicOrderCntLsb = 0; + } else { + if (p_Vid->last_has_mmco_5) { + if (p_Vid->last_pic_bottom_field) { + p_Vid->PrevPicOrderCntMsb = 0; + p_Vid->PrevPicOrderCntLsb = 0; + } else { + p_Vid->PrevPicOrderCntMsb = 0; + p_Vid->PrevPicOrderCntLsb = + pSlice->toppoc; + } + } + } + /* Calculate the MSBs of current picture */ + if (pSlice->pic_order_cnt_lsb < p_Vid->PrevPicOrderCntLsb && + (p_Vid->PrevPicOrderCntLsb - pSlice->pic_order_cnt_lsb) >= + (MaxPicOrderCntLsb / 2)) + pSlice->PicOrderCntMsb = p_Vid->PrevPicOrderCntMsb + + MaxPicOrderCntLsb; + else if (pSlice->pic_order_cnt_lsb > + p_Vid->PrevPicOrderCntLsb && + (pSlice->pic_order_cnt_lsb - + p_Vid->PrevPicOrderCntLsb) > + (MaxPicOrderCntLsb / 2)) + pSlice->PicOrderCntMsb = p_Vid->PrevPicOrderCntMsb - + MaxPicOrderCntLsb; + else + pSlice->PicOrderCntMsb = p_Vid->PrevPicOrderCntMsb; + + /* 2nd */ + if (pSlice->field_pic_flag == 0) { + /* frame pix */ + pSlice->toppoc = pSlice->PicOrderCntMsb + + pSlice->pic_order_cnt_lsb; + pSlice->bottompoc = pSlice->toppoc + + pSlice->delta_pic_order_cnt_bottom; + pSlice->ThisPOC = pSlice->framepoc = + (pSlice->toppoc < pSlice->bottompoc) ? + pSlice->toppoc : pSlice->bottompoc; + /* POC200301 */ + } else if (pSlice->bottom_field_flag == FALSE) { + /* top field */ + pSlice->ThisPOC = pSlice->toppoc = + pSlice->PicOrderCntMsb + + pSlice->pic_order_cnt_lsb; + } else { + /* bottom field */ + pSlice->ThisPOC = pSlice->bottompoc = + pSlice->PicOrderCntMsb + + pSlice->pic_order_cnt_lsb; + } + pSlice->framepoc = pSlice->ThisPOC; + + p_Vid->ThisPOC = pSlice->ThisPOC; + + /* if ( pSlice->frame_num != p_Vid->PreviousFrameNum) + Seems redundant */ + p_Vid->PreviousFrameNum = pSlice->frame_num; + + if (pSlice->nal_reference_idc) { + p_Vid->PrevPicOrderCntLsb = pSlice->pic_order_cnt_lsb; + p_Vid->PrevPicOrderCntMsb = pSlice->PicOrderCntMsb; + } + + break; + + case 1: /* POC MODE 1 */ + /* 1st */ + if (pSlice->idr_flag) { + p_Vid->FrameNumOffset = 0; /* first pix of IDRGOP */ + if (pSlice->frame_num) + dpb_print(p_H264_Dpb->decoder_index, + PRINT_FLAG_DPB_DETAIL, + "frame_num not equal to zero in IDR picture %d", + -1020); + } else { + if (p_Vid->last_has_mmco_5) { + p_Vid->PreviousFrameNumOffset = 0; + p_Vid->PreviousFrameNum = 0; + } + if (pSlice->frame_num < p_Vid->PreviousFrameNum) { + /* not first pix of IDRGOP */ + p_Vid->FrameNumOffset = + p_Vid->PreviousFrameNumOffset + + p_Vid->max_frame_num; + } else { + p_Vid->FrameNumOffset = + p_Vid->PreviousFrameNumOffset; + } + } + + /* 2nd */ + if (active_sps->num_ref_frames_in_pic_order_cnt_cycle) + pSlice->AbsFrameNum = + p_Vid->FrameNumOffset + pSlice->frame_num; + else + pSlice->AbsFrameNum = 0; + if ((!pSlice->nal_reference_idc) && pSlice->AbsFrameNum > 0) + pSlice->AbsFrameNum--; + + /* 3rd */ + p_Vid->ExpectedDeltaPerPicOrderCntCycle = 0; + + if (active_sps->num_ref_frames_in_pic_order_cnt_cycle) + for (i = 0; i < (int) active_sps-> + num_ref_frames_in_pic_order_cnt_cycle; i++) { + p_Vid->ExpectedDeltaPerPicOrderCntCycle += + active_sps->offset_for_ref_frame[i]; + dpb_print(p_H264_Dpb->decoder_index, + PRINT_FLAG_DEBUG_POC, + "%s: offset_for_ref_frame %d\r\n", + __func__, + active_sps-> + offset_for_ref_frame[i]); + } + + if (pSlice->AbsFrameNum) { + p_Vid->PicOrderCntCycleCnt = + (pSlice->AbsFrameNum - 1) / + active_sps-> + num_ref_frames_in_pic_order_cnt_cycle; + p_Vid->FrameNumInPicOrderCntCycle = + (pSlice->AbsFrameNum - 1) % + active_sps-> + num_ref_frames_in_pic_order_cnt_cycle; + p_Vid->ExpectedPicOrderCnt = + p_Vid->PicOrderCntCycleCnt * + p_Vid->ExpectedDeltaPerPicOrderCntCycle; + for (i = 0; i <= (int)p_Vid-> + FrameNumInPicOrderCntCycle; i++) { + p_Vid->ExpectedPicOrderCnt += + active_sps->offset_for_ref_frame[i]; + dpb_print(p_H264_Dpb->decoder_index, + PRINT_FLAG_DEBUG_POC, + "%s: offset_for_ref_frame %d\r\n", + __func__, + active_sps-> + offset_for_ref_frame[i]); + } + } else + p_Vid->ExpectedPicOrderCnt = 0; + + if (!pSlice->nal_reference_idc) + p_Vid->ExpectedPicOrderCnt += + active_sps->offset_for_non_ref_pic; + + if (pSlice->field_pic_flag == 0) { + /* frame pix */ + pSlice->toppoc = p_Vid->ExpectedPicOrderCnt + + pSlice->delta_pic_order_cnt[0]; + pSlice->bottompoc = pSlice->toppoc + + active_sps->offset_for_top_to_bottom_field + + pSlice->delta_pic_order_cnt[1]; + pSlice->ThisPOC = pSlice->framepoc = + (pSlice->toppoc < pSlice->bottompoc) ? + pSlice->toppoc : pSlice->bottompoc; + /* POC200301 */ + } else if (pSlice->bottom_field_flag == FALSE) { + /* top field */ + pSlice->ThisPOC = pSlice->toppoc = + p_Vid->ExpectedPicOrderCnt + + pSlice->delta_pic_order_cnt[0]; + } else { + /* bottom field */ + pSlice->ThisPOC = pSlice->bottompoc = + p_Vid->ExpectedPicOrderCnt + + active_sps->offset_for_top_to_bottom_field + + pSlice->delta_pic_order_cnt[0]; + } + pSlice->framepoc = pSlice->ThisPOC; + + p_Vid->PreviousFrameNum = pSlice->frame_num; + p_Vid->PreviousFrameNumOffset = p_Vid->FrameNumOffset; + + break; + + + case 2: /* POC MODE 2 */ + if (pSlice->idr_flag) { /* IDR picture */ + p_Vid->FrameNumOffset = 0; /* first pix of IDRGOP */ + pSlice->ThisPOC = pSlice->framepoc = pSlice->toppoc = + pSlice->bottompoc = 0; + if (pSlice->frame_num) + dpb_print(p_H264_Dpb->decoder_index, + PRINT_FLAG_DPB_DETAIL, + "frame_num not equal to zero in IDR picture %d", + -1020); + } else { + if (p_Vid->last_has_mmco_5) { + p_Vid->PreviousFrameNum = 0; + p_Vid->PreviousFrameNumOffset = 0; + } + if (pSlice->frame_num < p_Vid->PreviousFrameNum) + p_Vid->FrameNumOffset = + p_Vid->PreviousFrameNumOffset + + p_Vid->max_frame_num; + else + p_Vid->FrameNumOffset = + p_Vid->PreviousFrameNumOffset; + + pSlice->AbsFrameNum = p_Vid->FrameNumOffset + + pSlice->frame_num; + if (!pSlice->nal_reference_idc) + pSlice->ThisPOC = + (2 * pSlice->AbsFrameNum - 1); + else + pSlice->ThisPOC = (2 * pSlice->AbsFrameNum); + + if (pSlice->field_pic_flag == 0) + pSlice->toppoc = pSlice->bottompoc = + pSlice->framepoc = pSlice->ThisPOC; + else if (pSlice->bottom_field_flag == FALSE) + pSlice->toppoc = pSlice->framepoc = + pSlice->ThisPOC; + else + pSlice->bottompoc = pSlice->framepoc = + pSlice->ThisPOC; + } + + p_Vid->PreviousFrameNum = pSlice->frame_num; + p_Vid->PreviousFrameNumOffset = p_Vid->FrameNumOffset; + break; + + + default: + /* error must occurs */ + /* assert( 1==0 ); */ + break; + } +} + +void fill_frame_num_gap(struct VideoParameters *p_Vid, struct Slice *currSlice) +{ + struct h264_dpb_stru *p_H264_Dpb = + container_of(p_Vid, struct h264_dpb_stru, mVideo); + struct SPSParameters *active_sps = p_Vid->active_sps; + int CurrFrameNum; + int UnusedShortTermFrameNum; + struct StorablePicture *picture = NULL; + int tmp1 = currSlice->delta_pic_order_cnt[0]; + int tmp2 = currSlice->delta_pic_order_cnt[1]; + currSlice->delta_pic_order_cnt[0] = + currSlice->delta_pic_order_cnt[1] = 0; + + dpb_print(p_H264_Dpb->decoder_index, + PRINT_FLAG_DPB_DETAIL, + "A gap in frame number is found, try to fill it.(pre_frame_num %d, max_frame_num %d\n", + p_Vid->pre_frame_num, p_Vid->max_frame_num + ); + + UnusedShortTermFrameNum = (p_Vid->pre_frame_num + 1) + % p_Vid->max_frame_num; + CurrFrameNum = currSlice->frame_num; /*p_Vid->frame_num;*/ + + while (CurrFrameNum != UnusedShortTermFrameNum) { + /*picture = alloc_storable_picture + (p_Vid, FRAME, p_Vid->width, + p_Vid->height, + p_Vid->width_cr, + p_Vid->height_cr, 1);*/ + picture = get_new_pic(p_H264_Dpb, + p_H264_Dpb->mSlice.structure, + /*p_Vid->width, p_Vid->height, + p_Vid->width_cr, + p_Vid->height_cr,*/ 1); + + if (picture == NULL) { + struct DecodedPictureBuffer *p_Dpb = &p_H264_Dpb->mDPB; + dpb_print(p_H264_Dpb->decoder_index, + PRINT_FLAG_ERROR, + "%s Error: get_new_pic return NULL\r\n", + __func__); + h264_debug_flag |= PRINT_FLAG_DUMP_DPB; + dump_dpb(p_Dpb); + return; + } + + picture->colocated_buf_index = -1; + picture->buf_spec_num = -1; + + picture->coded_frame = 1; + picture->pic_num = UnusedShortTermFrameNum; + picture->frame_num = UnusedShortTermFrameNum; + picture->non_existing = 1; + picture->is_output = 1; + picture->used_for_reference = 1; + picture->adaptive_ref_pic_buffering_flag = 0; + #if (MVC_EXTENSION_ENABLE) + picture->view_id = currSlice->view_id; + #endif + + currSlice->frame_num = UnusedShortTermFrameNum; + if (active_sps->pic_order_cnt_type != 0) { + /*decode_poc(p_Vid, p_Vid->ppSliceList[0]);*/ + decode_poc(&p_H264_Dpb->mVideo, &p_H264_Dpb->mSlice); + } + picture->top_poc = currSlice->toppoc; + picture->bottom_poc = currSlice->bottompoc; + picture->frame_poc = currSlice->framepoc; + picture->poc = currSlice->framepoc; + + store_picture_in_dpb(p_H264_Dpb, picture); + + picture = NULL; + p_Vid->pre_frame_num = UnusedShortTermFrameNum; + UnusedShortTermFrameNum = + (UnusedShortTermFrameNum + 1) % + p_Vid->max_frame_num; + } + currSlice->delta_pic_order_cnt[0] = tmp1; + currSlice->delta_pic_order_cnt[1] = tmp2; + currSlice->frame_num = CurrFrameNum; +} + +void dpb_init_global(struct h264_dpb_stru *p_H264_Dpb, + int id, int actual_dpb_size, int max_reference_size) +{ + int i; + init_dummy_fs(); + + memset(&p_H264_Dpb->mDPB, 0, sizeof(struct DecodedPictureBuffer)); + + memset(&p_H264_Dpb->mSlice, 0, sizeof(struct Slice)); + memset(&p_H264_Dpb->mVideo, 0, sizeof(struct VideoParameters)); + memset(&p_H264_Dpb->mSPS, 0, sizeof(struct SPSParameters)); + + for (i = 0; i < DPB_SIZE_MAX; i++) { + memset(&(p_H264_Dpb->mFrameStore[i]), 0, + sizeof(struct FrameStore)); + } + + for (i = 0; i < MAX_PIC_BUF_NUM; i++) { + memset(&(p_H264_Dpb->m_PIC[i]), 0, + sizeof(struct StorablePicture)); + p_H264_Dpb->m_PIC[i].index = i; + } + p_H264_Dpb->decoder_index = id; + + /* make sure dpb_init_global + can be called during decoding + (in DECODE_STATE_IDLE or DECODE_STATE_READY state) */ + p_H264_Dpb->mDPB.size = actual_dpb_size; + p_H264_Dpb->max_reference_size = max_reference_size; +} + +static void init_picture(struct h264_dpb_stru *p_H264_Dpb, + struct Slice *currSlice, + struct StorablePicture *dec_picture) +{ + /* struct VideoParameters *p_Vid = &(p_H264_Dpb->mVideo); */ + dpb_print(p_H264_Dpb->decoder_index, PRINT_FLAG_DPB_DETAIL, + "%s dec_picture %p\n", __func__, dec_picture); + dec_picture->top_poc = currSlice->toppoc; + dec_picture->bottom_poc = currSlice->bottompoc; + dec_picture->frame_poc = currSlice->framepoc; + switch (currSlice->structure) { + case TOP_FIELD: { + dec_picture->poc = currSlice->toppoc; + /* p_Vid->number *= 2; */ + break; + } + case BOTTOM_FIELD: { + dec_picture->poc = currSlice->bottompoc; + /* p_Vid->number = p_Vid->number * 2 + 1; */ + break; + } + case FRAME: { + dec_picture->poc = currSlice->framepoc; + break; + } + default: + dpb_print(p_H264_Dpb->decoder_index, PRINT_FLAG_DPB_DETAIL, + "p_Vid->structure not initialized %d\n", 235); + } + + /* dec_picture->slice_type = p_Vid->type; */ + dec_picture->used_for_reference = (currSlice->nal_reference_idc != 0); + dec_picture->idr_flag = currSlice->idr_flag; + dec_picture->no_output_of_prior_pics_flag = + currSlice->no_output_of_prior_pics_flag; + dec_picture->long_term_reference_flag = + currSlice->long_term_reference_flag; +#if 1 + dec_picture->adaptive_ref_pic_buffering_flag = + currSlice->adaptive_ref_pic_buffering_flag; + dec_picture->dec_ref_pic_marking_buffer = + &currSlice->dec_ref_pic_marking_buffer[0]; +#endif + /* currSlice->dec_ref_pic_marking_buffer = NULL; */ + + /* dec_picture->mb_aff_frame_flag = currSlice->mb_aff_frame_flag; */ + /* dec_picture->PicWidthInMbs = p_Vid->PicWidthInMbs; */ + + /* p_Vid->get_mb_block_pos = + dec_picture->mb_aff_frame_flag ? get_mb_block_pos_mbaff : + get_mb_block_pos_normal; */ + /* p_Vid->getNeighbour = + dec_picture->mb_aff_frame_flag ? getAffNeighbour : + getNonAffNeighbour; */ + + dec_picture->pic_num = currSlice->frame_num; + dec_picture->frame_num = currSlice->frame_num; + + /* dec_picture->recovery_frame = + (unsigned int) ((int) currSlice->frame_num == + p_Vid->recovery_frame_num); */ + + dec_picture->coded_frame = (currSlice->structure == FRAME); + + /* dec_picture->chroma_format_idc = active_sps->chroma_format_idc; */ + + /* dec_picture->frame_mbs_only_flag = + active_sps->frame_mbs_only_flag; */ + /* dec_picture->frame_cropping_flag = + active_sps->frame_cropping_flag; */ + + if ((currSlice->picture_structure_mmco & 0x3) == 3) { + dec_picture->mb_aff_frame_flag = 1; + dpb_print(p_H264_Dpb->decoder_index, PRINT_FLAG_DPB_DETAIL, + "%s, picture_structure_mmco is %x, set mb_aff_frame_flag to 1\n", + __func__, + currSlice->picture_structure_mmco); + } + +} + +static struct StorablePicture *get_new_pic(struct h264_dpb_stru *p_H264_Dpb, + enum PictureStructure structure, unsigned char is_output) +{ + struct StorablePicture *s = NULL; + struct StorablePicture *pic; + struct VideoParameters *p_Vid = &(p_H264_Dpb->mVideo); + /* recycle un-used pic */ + int ii = 0; + + for (ii = 0; ii < MAX_PIC_BUF_NUM; ii++) { + pic = &(p_H264_Dpb->m_PIC[ii]); + if (pic->is_used == 0) { + pic->is_used = 1; + s = pic; + break; + } + } + + if (s) { + s->pic_num = 0; + s->frame_num = 0; + s->long_term_frame_idx = 0; + s->long_term_pic_num = 0; + s->used_for_reference = 0; + s->is_long_term = 0; + s->non_existing = 0; + s->is_output = 0; + s->pre_output = 0; + s->max_slice_id = 0; +#if (MVC_EXTENSION_ENABLE) + s->view_id = -1; +#endif + + s->structure = structure; + +#if 0 + s->size_x = size_x; + s->size_y = size_y; + s->size_x_cr = size_x_cr; + s->size_y_cr = size_y_cr; + s->size_x_m1 = size_x - 1; + s->size_y_m1 = size_y - 1; + s->size_x_cr_m1 = size_x_cr - 1; + s->size_y_cr_m1 = size_y_cr - 1; + + s->top_field = p_Vid->no_reference_picture; + s->bottom_field = p_Vid->no_reference_picture; + s->frame = p_Vid->no_reference_picture; +#endif + /* s->dec_ref_pic_marking_buffer = NULL; */ + + s->coded_frame = 0; + s->mb_aff_frame_flag = 0; + + s->top_poc = s->bottom_poc = s->poc = 0; + s->seiHasTone_mapping = 0; + + if (!p_Vid->active_sps->frame_mbs_only_flag && + structure != FRAME) { + int i, j; + for (j = 0; j < MAX_NUM_SLICES; j++) { + for (i = 0; i < 2; i++) { + /* s->listX[j][i] = + calloc(MAX_LIST_SIZE, + sizeof (struct StorablePicture *)); + +1 for reordering ??? + + if (NULL == s->listX[j][i]) + no_mem_exit("alloc_storable_picture: + s->listX[i]"); */ + } + } + } + } + dpb_print(p_H264_Dpb->decoder_index, PRINT_FLAG_DPB_DETAIL, + "%s %p\n", __func__, s); + return s; +} + +static void free_picture(struct h264_dpb_stru *p_H264_Dpb, + struct StorablePicture *pic) +{ + dpb_print(p_H264_Dpb->decoder_index, PRINT_FLAG_DPB_DETAIL, + "%s %p %d\n", __func__, pic, pic->index); + /* assert(pic->index<MAX_PIC_BUF_NUM); */ + p_H264_Dpb->m_PIC[pic->index].is_used = 0; +} + +static void gen_field_ref_ids(struct VideoParameters *p_Vid, + struct StorablePicture *p) +{ + int i, j; + struct h264_dpb_stru *p_H264_Dpb = container_of(p_Vid, + struct h264_dpb_stru, mVideo); + /* ! Generate Frame parameters from field information. */ + dpb_print(p_H264_Dpb->decoder_index, PRINT_FLAG_DPB_DETAIL, + "%s\n", __func__); + + /* copy the list; */ + for (j = 0; j < p_Vid->iSliceNumOfCurrPic; j++) { + if (p->listX[j][LIST_0]) { + p->listXsize[j][LIST_0] = + p_Vid->ppSliceList[j]->listXsize[LIST_0]; + for (i = 0; i < p->listXsize[j][LIST_0]; i++) + p->listX[j][LIST_0][i] = + p_Vid->ppSliceList[j]->listX[LIST_0][i]; + } + if (p->listX[j][LIST_1]) { + p->listXsize[j][LIST_1] = + p_Vid->ppSliceList[j]->listXsize[LIST_1]; + for (i = 0; i < p->listXsize[j][LIST_1]; i++) + p->listX[j][LIST_1][i] = + p_Vid->ppSliceList[j]->listX[LIST_1][i]; + } + } +} + +static void init_dpb(struct h264_dpb_stru *p_H264_Dpb, int type) +{ + unsigned i; + struct VideoParameters *p_Vid = &p_H264_Dpb->mVideo; + struct DecodedPictureBuffer *p_Dpb = &p_H264_Dpb->mDPB; + struct SPSParameters *active_sps = &p_H264_Dpb->mSPS; + + p_Vid->active_sps = active_sps; + dpb_print(p_H264_Dpb->decoder_index, PRINT_FLAG_DPB_DETAIL, + "%s\n", __func__); + + p_Dpb->p_Vid = p_Vid; + if (p_Dpb->init_done) { + /* free_dpb(p_Dpb); */ + if (p_Vid->no_reference_picture) { + free_picture(p_H264_Dpb, p_Vid->no_reference_picture); + p_Vid->no_reference_picture = NULL; + } + p_Dpb->init_done = 0; + } + + /* p_Dpb->size = 10; //active_sps->max_dpb_size; //16; + getDpbSize(p_Vid, active_sps) + + p_Vid->p_Inp->dpb_plus[type==2? 1: 0]; + p_Dpb->size = active_sps->max_dpb_size; //16; + getDpbSize(p_Vid, active_sps) + + p_Vid->p_Inp->dpb_plus[type==2? 1: 0]; + p_Dpb->size initialzie in vh264.c */ + p_Dpb->num_ref_frames = active_sps->num_ref_frames; + /* p_Dpb->num_ref_frames initialzie in vh264.c */ + dpb_print(p_H264_Dpb->decoder_index, PRINT_FLAG_DPB_DETAIL, + "%s dpb_size is %d (%d) num_ref_frames = %d (%d)\n", + __func__, p_Dpb->size, active_sps->max_dpb_size, + p_Dpb->num_ref_frames, + active_sps->num_ref_frames); + +#if 0 + /* ??? */ +#if (MVC_EXTENSION_ENABLE) + if ((unsigned int)active_sps->max_dec_frame_buffering < + active_sps->num_ref_frames) { +#else + if (p_Dpb->size < active_sps->num_ref_frames) { +#endif + error( + "DPB size at specified level is smaller than the specified number of reference frames. This is not allowed.\n", + 1000); + } +#endif + + p_Dpb->used_size = 0; + p_Dpb->last_picture = NULL; + + p_Dpb->ref_frames_in_buffer = 0; + p_Dpb->ltref_frames_in_buffer = 0; + +#if 0 + p_Dpb->fs = calloc(p_Dpb->size, sizeof(struct FrameStore *)); + if (NULL == p_Dpb->fs) + no_mem_exit("init_dpb: p_Dpb->fs"); + + p_Dpb->fs_ref = calloc(p_Dpb->size, sizeof(struct FrameStore *)); + if (NULL == p_Dpb->fs_ref) + no_mem_exit("init_dpb: p_Dpb->fs_ref"); + + p_Dpb->fs_ltref = calloc(p_Dpb->size, sizeof(struct FrameStore *)); + if (NULL == p_Dpb->fs_ltref) + no_mem_exit("init_dpb: p_Dpb->fs_ltref"); +#endif + +#if (MVC_EXTENSION_ENABLE) + p_Dpb->fs_ilref = calloc(1, sizeof(struct FrameStore *)); + if (NULL == p_Dpb->fs_ilref) + no_mem_exit("init_dpb: p_Dpb->fs_ilref"); +#endif + + for (i = 0; i < p_Dpb->size; i++) { + p_Dpb->fs[i] = &(p_H264_Dpb->mFrameStore[i]); + /* alloc_frame_store(); */ + p_Dpb->fs[i]->index = i; + p_Dpb->fs_ref[i] = NULL; + p_Dpb->fs_ltref[i] = NULL; + p_Dpb->fs[i]->layer_id = 0; /* MVC_INIT_VIEW_ID; */ +#if (MVC_EXTENSION_ENABLE) + p_Dpb->fs[i]->view_id = MVC_INIT_VIEW_ID; + p_Dpb->fs[i]->inter_view_flag[0] = + p_Dpb->fs[i]->inter_view_flag[1] = 0; + p_Dpb->fs[i]->anchor_pic_flag[0] = + p_Dpb->fs[i]->anchor_pic_flag[1] = 0; +#endif + } +#if (MVC_EXTENSION_ENABLE) + if (type == 2) { + p_Dpb->fs_ilref[0] = alloc_frame_store(); + /* These may need some cleanups */ + p_Dpb->fs_ilref[0]->view_id = MVC_INIT_VIEW_ID; + p_Dpb->fs_ilref[0]->inter_view_flag[0] = + p_Dpb->fs_ilref[0]->inter_view_flag[1] = 0; + p_Dpb->fs_ilref[0]->anchor_pic_flag[0] = + p_Dpb->fs_ilref[0]->anchor_pic_flag[1] = 0; + /* given that this is in a different buffer, + do we even need proc_flag anymore? */ + } else + p_Dpb->fs_ilref[0] = NULL; +#endif + + /* + for (i = 0; i < 6; i++) + { + currSlice->listX[i] = + calloc(MAX_LIST_SIZE, sizeof (struct StorablePicture *)); + +1 for reordering + if (NULL == currSlice->listX[i]) + no_mem_exit("init_dpb: currSlice->listX[i]"); + } + */ + /* allocate a dummy storable picture */ + if (!p_Vid->no_reference_picture) { + p_Vid->no_reference_picture = get_new_pic(p_H264_Dpb, + FRAME, + /*p_Vid->width, p_Vid->height, + p_Vid->width_cr, p_Vid->height_cr,*/ 1); + p_Vid->no_reference_picture->top_field = + p_Vid->no_reference_picture; + p_Vid->no_reference_picture->bottom_field = + p_Vid->no_reference_picture; + p_Vid->no_reference_picture->frame = + p_Vid->no_reference_picture; + } + p_Dpb->last_output_poc = INT_MIN; + +#if (MVC_EXTENSION_ENABLE) + p_Dpb->last_output_view_id = -1; +#endif + + p_Vid->last_has_mmco_5 = 0; + + init_colocate_buf(p_H264_Dpb, p_H264_Dpb->max_reference_size); + + p_Dpb->init_done = 1; + +#if 0 +/* ??? */ + /* picture error concealment */ + if (p_Vid->conceal_mode != 0 && !p_Vid->last_out_fs) + p_Vid->last_out_fs = alloc_frame_store(); +#endif +} + +static void dpb_split_field(struct h264_dpb_stru *p_H264_Dpb, + struct FrameStore *fs) +{ + struct StorablePicture *fs_top = NULL, *fs_btm = NULL; + struct StorablePicture *frame = fs->frame; + + dpb_print(p_H264_Dpb->decoder_index, PRINT_FLAG_DPB_DETAIL, + "%s %p %p\n", __func__, fs, frame); + + fs->poc = frame->poc; + + if (!frame->frame_mbs_only_flag) { + fs_top = fs->top_field = get_new_pic(p_H264_Dpb, + TOP_FIELD, + /* frame->size_x, frame->size_y, + frame->size_x_cr, frame->size_y_cr,*/ 1); + fs_btm = fs->bottom_field = get_new_pic(p_H264_Dpb, + BOTTOM_FIELD, + /*frame->size_x, frame->size_y, + frame->size_x_cr, frame->size_y_cr,*/ 1); + if (fs_top == NULL || fs_btm == NULL) + return; +#if 1 +/* rain */ + fs_top->buf_spec_num = frame->buf_spec_num; + fs_btm->buf_spec_num = frame->buf_spec_num; + + fs_top->colocated_buf_index = frame->colocated_buf_index; + fs_btm->colocated_buf_index = frame->colocated_buf_index; +#endif + fs_top->poc = frame->top_poc; + fs_btm->poc = frame->bottom_poc; + +#if (MVC_EXTENSION_ENABLE) + fs_top->view_id = frame->view_id; + fs_btm->view_id = frame->view_id; +#endif + + fs_top->frame_poc = frame->frame_poc; + + fs_top->bottom_poc = fs_btm->bottom_poc = frame->bottom_poc; + fs_top->top_poc = fs_btm->top_poc = frame->top_poc; + fs_btm->frame_poc = frame->frame_poc; + + fs_top->used_for_reference = fs_btm->used_for_reference + = frame->used_for_reference; + fs_top->is_long_term = fs_btm->is_long_term + = frame->is_long_term; + fs->long_term_frame_idx = fs_top->long_term_frame_idx + = fs_btm->long_term_frame_idx + = frame->long_term_frame_idx; + + fs_top->coded_frame = fs_btm->coded_frame = 1; + fs_top->mb_aff_frame_flag = fs_btm->mb_aff_frame_flag + = frame->mb_aff_frame_flag; + + frame->top_field = fs_top; + frame->bottom_field = fs_btm; + frame->frame = frame; + fs_top->bottom_field = fs_btm; + fs_top->frame = frame; + fs_top->top_field = fs_top; + fs_btm->top_field = fs_top; + fs_btm->frame = frame; + fs_btm->bottom_field = fs_btm; + +#if (MVC_EXTENSION_ENABLE) + fs_top->view_id = fs_btm->view_id = fs->view_id; + fs_top->inter_view_flag = fs->inter_view_flag[0]; + fs_btm->inter_view_flag = fs->inter_view_flag[1]; +#endif + + fs_top->chroma_format_idc = fs_btm->chroma_format_idc = + frame->chroma_format_idc; + fs_top->iCodingType = fs_btm->iCodingType = frame->iCodingType; + } else { + fs->top_field = NULL; + fs->bottom_field = NULL; + frame->top_field = NULL; + frame->bottom_field = NULL; + frame->frame = frame; + } + +} + + +static void dpb_combine_field(struct h264_dpb_stru *p_H264_Dpb, + struct FrameStore *fs) +{ + + dpb_print(p_H264_Dpb->decoder_index, PRINT_FLAG_DPB_DETAIL, + "%s\n", __func__); + + if (!fs->frame) { + fs->frame = get_new_pic(p_H264_Dpb, + FRAME, + /* fs->top_field->size_x, fs->top_field->size_y*2, + fs->top_field->size_x_cr, fs->top_field->size_y_cr*2, + */ 1); + } + if (!fs->frame) + return; +#if 1 +/* rain */ + fs->frame->buf_spec_num = fs->top_field->buf_spec_num; + fs->frame->colocated_buf_index = fs->top_field->colocated_buf_index; +#endif + + + fs->poc = fs->frame->poc = fs->frame->frame_poc = imin( + fs->top_field->poc, fs->bottom_field->poc); + + fs->bottom_field->frame_poc = fs->top_field->frame_poc = fs->frame->poc; + + fs->bottom_field->top_poc = fs->frame->top_poc = fs->top_field->poc; + fs->top_field->bottom_poc = fs->frame->bottom_poc = + fs->bottom_field->poc; + + fs->frame->used_for_reference = (fs->top_field->used_for_reference && + fs->bottom_field->used_for_reference); + fs->frame->is_long_term = (fs->top_field->is_long_term && + fs->bottom_field->is_long_term); + + if (fs->frame->is_long_term) + fs->frame->long_term_frame_idx = fs->long_term_frame_idx; + + fs->frame->top_field = fs->top_field; + fs->frame->bottom_field = fs->bottom_field; + fs->frame->frame = fs->frame; + + fs->frame->coded_frame = 0; + + fs->frame->chroma_format_idc = fs->top_field->chroma_format_idc; + fs->frame->frame_cropping_flag = fs->top_field->frame_cropping_flag; + if (fs->frame->frame_cropping_flag) { + fs->frame->frame_crop_top_offset = + fs->top_field->frame_crop_top_offset; + fs->frame->frame_crop_bottom_offset = + fs->top_field->frame_crop_bottom_offset; + fs->frame->frame_crop_left_offset = + fs->top_field->frame_crop_left_offset; + fs->frame->frame_crop_right_offset = + fs->top_field->frame_crop_right_offset; + } + + fs->top_field->frame = fs->bottom_field->frame = fs->frame; + fs->top_field->top_field = fs->top_field; + fs->top_field->bottom_field = fs->bottom_field; + fs->bottom_field->top_field = fs->top_field; + fs->bottom_field->bottom_field = fs->bottom_field; + + /**/ +#if (MVC_EXTENSION_ENABLE) + fs->frame->view_id = fs->view_id; +#endif + fs->frame->iCodingType = fs->top_field->iCodingType; + /* FIELD_CODING ;*/ +} + +static void calculate_frame_no(struct VideoParameters *p_Vid, + struct StorablePicture *p) +{ +#if 0 +/* ??? */ + InputParameters *p_Inp = p_Vid->p_Inp; + /* calculate frame number */ + int psnrPOC = p_Vid->active_sps->mb_adaptive_frame_field_flag ? + p->poc / (p_Inp->poc_scale) : p->poc / (p_Inp->poc_scale); + + if (psnrPOC == 0) { /* && p_Vid->psnr_number) */ + p_Vid->idr_psnr_number = + p_Vid->g_nFrame * p_Vid->ref_poc_gap / (p_Inp->poc_scale); + } + p_Vid->psnr_number = imax(p_Vid->psnr_number, + p_Vid->idr_psnr_number + psnrPOC); + + p_Vid->frame_no = p_Vid->idr_psnr_number + psnrPOC; +#endif +} + +static void insert_picture_in_dpb(struct h264_dpb_stru *p_H264_Dpb, + struct FrameStore *fs, + struct StorablePicture *p) +{ + struct VideoParameters *p_Vid = &p_H264_Dpb->mVideo; + /* InputParameters *p_Inp = p_Vid->p_Inp; + dpb_print(p_H264_Dpb->decoder_index, PRINT_FLAG_DPB_DETAIL, + "insert (%s) pic with frame_num #%d, poc %d\n", + (p->structure == FRAME)?"FRAME": + (p->structure == TOP_FIELD)?"TOP_FIELD": + "BOTTOM_FIELD", p->pic_num, p->poc); + assert (p!=NULL); + assert (fs!=NULL);*/ + dpb_print(p_H264_Dpb->decoder_index, PRINT_FLAG_DPB_DETAIL, + "%s %p %p\n", __func__, fs, p); +#if 1 +/* rain */ +/* p->buf_spec_num = fs->index; */ + fs->buf_spec_num = p->buf_spec_num; + fs->colocated_buf_index = p->colocated_buf_index; +#endif + switch (p->structure) { + case FRAME: + fs->frame = p; + fs->is_used = 3; + if (p->used_for_reference) { + fs->is_reference = 3; + fs->is_orig_reference = 3; + if (p->is_long_term) { + fs->is_long_term = 3; + fs->long_term_frame_idx = + p->long_term_frame_idx; + } + } + fs->layer_id = p->layer_id; +#if (MVC_EXTENSION_ENABLE) + fs->view_id = p->view_id; + fs->inter_view_flag[0] = fs->inter_view_flag[1] = + p->inter_view_flag; + fs->anchor_pic_flag[0] = fs->anchor_pic_flag[1] = + p->anchor_pic_flag; +#endif + /* generate field views */ + /* return; */ + dpb_split_field(p_H264_Dpb, fs); + /* return; */ + break; + case TOP_FIELD: + fs->top_field = p; + fs->is_used |= 1; + fs->layer_id = p->layer_id; +#if (MVC_EXTENSION_ENABLE) + fs->view_id = p->view_id; + fs->inter_view_flag[0] = p->inter_view_flag; + fs->anchor_pic_flag[0] = p->anchor_pic_flag; +#endif + if (p->used_for_reference) { + fs->is_reference |= 1; + fs->is_orig_reference |= 1; + if (p->is_long_term) { + fs->is_long_term |= 1; + fs->long_term_frame_idx = + p->long_term_frame_idx; + } + } + if (fs->is_used == 3) { + /* generate frame view */ + dpb_combine_field(p_H264_Dpb, fs); + } else { + fs->poc = p->poc; + } + gen_field_ref_ids(p_Vid, p); + break; + case BOTTOM_FIELD: + fs->bottom_field = p; + fs->is_used |= 2; + fs->layer_id = p->layer_id; +#if (MVC_EXTENSION_ENABLE) + fs->view_id = p->view_id; + fs->inter_view_flag[1] = p->inter_view_flag; + fs->anchor_pic_flag[1] = p->anchor_pic_flag; +#endif + if (p->used_for_reference) { + fs->is_reference |= 2; + fs->is_orig_reference |= 2; + if (p->is_long_term) { + fs->is_long_term |= 2; + fs->long_term_frame_idx = + p->long_term_frame_idx; + } + } + if (fs->is_used == 3) { + /* generate frame view */ + dpb_combine_field(p_H264_Dpb, fs); + } else { + fs->poc = p->poc; + } + gen_field_ref_ids(p_Vid, p); + break; + } + fs->frame_num = p->pic_num; + fs->recovery_frame = p->recovery_frame; + + fs->is_output = p->is_output; + fs->pre_output = p->pre_output; + + if (fs->is_used == 3) { + calculate_frame_no(p_Vid, p); +#if 0 +/* ??? */ + if (-1 != p_Vid->p_ref && !p_Inp->silent) + find_snr(p_Vid, fs->frame, &p_Vid->p_ref); +#endif + } + + fs->pts = p->pts; + fs->pts64 = p->pts64; +} + +void reset_frame_store(struct h264_dpb_stru *p_H264_Dpb, + struct FrameStore *f) +{ + dpb_print(p_H264_Dpb->decoder_index, PRINT_FLAG_DPB_DETAIL, + "%s\n", __func__); + + if (f) { + if (f->frame) { + free_picture(p_H264_Dpb, f->frame); + f->frame = NULL; + } + if (f->top_field) { + free_picture(p_H264_Dpb, f->top_field); + f->top_field = NULL; + } + if (f->bottom_field) { + free_picture(p_H264_Dpb, f->bottom_field); + f->bottom_field = NULL; + } + + /**/ + f->is_used = 0; + f->is_reference = 0; + f->is_long_term = 0; + f->is_orig_reference = 0; + + f->is_output = 0; + f->pre_output = 0; + + f->frame = NULL; + f->top_field = NULL; + f->bottom_field = NULL; + + /* free(f); */ + } +} + +static void unmark_for_reference(struct DecodedPictureBuffer *p_Dpb, + struct FrameStore *fs) +{ + struct h264_dpb_stru *p_H264_Dpb = container_of(p_Dpb, + struct h264_dpb_stru, mDPB); + dpb_print(p_H264_Dpb->decoder_index, PRINT_FLAG_DPB_DETAIL, + "%s %p %p %p %p\n", __func__, + fs, fs->frame, fs->top_field, fs->bottom_field); + /* return; */ + if (fs->is_used & 1) { + if (fs->top_field) + fs->top_field->used_for_reference = 0; + } + if (fs->is_used & 2) { + if (fs->bottom_field) + fs->bottom_field->used_for_reference = 0; + } + if (fs->is_used == 3) { + if (fs->top_field && fs->bottom_field) { + fs->top_field->used_for_reference = 0; + fs->bottom_field->used_for_reference = 0; + } + fs->frame->used_for_reference = 0; + } + + fs->is_reference = 0; + +} + +int get_long_term_flag_by_buf_spec_num(struct h264_dpb_stru *p_H264_Dpb, + int buf_spec_num) +{ + struct DecodedPictureBuffer *p_Dpb = &p_H264_Dpb->mDPB; + unsigned i; + for (i = 0; i < p_Dpb->used_size; i++) { + if (p_Dpb->fs[i]->buf_spec_num == buf_spec_num) + return p_Dpb->fs[i]->is_long_term; + } + return -1; +} + +static void update_pic_num(struct Slice *currSlice) +{ + unsigned int i; + struct VideoParameters *p_Vid = currSlice->p_Vid; + struct DecodedPictureBuffer *p_Dpb = currSlice->p_Dpb; + struct SPSParameters *active_sps = p_Vid->active_sps; + + int add_top = 0, add_bottom = 0; + int max_frame_num = 1 << (active_sps->log2_max_frame_num_minus4 + 4); + + if (currSlice->structure == FRAME) { + for (i = 0; i < p_Dpb->ref_frames_in_buffer; i++) { + if (p_Dpb->fs_ref[i]->is_used == 3) { + if ((p_Dpb->fs_ref[i]->frame-> + used_for_reference) && + (!p_Dpb->fs_ref[i]->frame-> + is_long_term)) { + if (p_Dpb->fs_ref[i]->frame_num > + currSlice->frame_num) { + p_Dpb->fs_ref[i]-> + frame_num_wrap = + p_Dpb->fs_ref[i]->frame_num + - max_frame_num; + } else { + p_Dpb->fs_ref[i]-> + frame_num_wrap = + p_Dpb->fs_ref[i]->frame_num; + } + p_Dpb->fs_ref[i]->frame->pic_num = + p_Dpb->fs_ref[i]->frame_num_wrap; + } + } + } + /* update long_term_pic_num */ + for (i = 0; i < p_Dpb->ltref_frames_in_buffer; i++) { + if (p_Dpb->fs_ltref[i]->is_used == 3) { + if (p_Dpb->fs_ltref[i]->frame->is_long_term) { + p_Dpb->fs_ltref[i]->frame-> + long_term_pic_num = + p_Dpb->fs_ltref[i]->frame-> + long_term_frame_idx; + } + } + } + } else { + if (currSlice->structure == TOP_FIELD) { + add_top = 1; + add_bottom = 0; + } else { + add_top = 0; + add_bottom = 1; + } + + for (i = 0; i < p_Dpb->ref_frames_in_buffer; i++) { + if (p_Dpb->fs_ref[i]->is_reference) { + if (p_Dpb->fs_ref[i]->frame_num > currSlice-> + frame_num) { + p_Dpb->fs_ref[i]->frame_num_wrap = + p_Dpb->fs_ref[i]->frame_num - + max_frame_num; + } else { + p_Dpb->fs_ref[i]->frame_num_wrap = + p_Dpb->fs_ref[i]->frame_num; + } + if (p_Dpb->fs_ref[i]->is_reference & 1) { + p_Dpb->fs_ref[i]->top_field-> + pic_num = (2 * p_Dpb->fs_ref[i]-> + frame_num_wrap) + add_top; + } + if (p_Dpb->fs_ref[i]->is_reference & 2) { + p_Dpb->fs_ref[i]->bottom_field-> + pic_num = (2 * p_Dpb->fs_ref[i]-> + frame_num_wrap) + add_bottom; + } + } + } + /* update long_term_pic_num */ + for (i = 0; i < p_Dpb->ltref_frames_in_buffer; i++) { + if (p_Dpb->fs_ltref[i]->is_long_term & 1) { + p_Dpb->fs_ltref[i]->top_field-> + long_term_pic_num = 2 * + p_Dpb->fs_ltref[i]->top_field-> + long_term_frame_idx + add_top; + } + if (p_Dpb->fs_ltref[i]->is_long_term & 2) { + p_Dpb->fs_ltref[i]->bottom_field-> + long_term_pic_num = 2 * + p_Dpb->fs_ltref[i]->bottom_field-> + long_term_frame_idx + add_bottom; + } + } + } +} + +static void remove_frame_from_dpb(struct h264_dpb_stru *p_H264_Dpb, int pos) +{ + struct DecodedPictureBuffer *p_Dpb = &p_H264_Dpb->mDPB; + struct FrameStore *fs = p_Dpb->fs[pos]; + struct FrameStore *tmp; + unsigned i; + + dpb_print(p_H264_Dpb->decoder_index, PRINT_FLAG_DPB_DETAIL, + "%s pos %d %p\n", __func__, pos, fs); + + /* dpb_print(p_H264_Dpb->decoder_index, PRINT_FLAG_DPB_DETAIL, + "remove frame with frame_num #%d\n", fs->frame_num); */ + switch (fs->is_used) { + case 3: + free_picture(p_H264_Dpb, fs->frame); + free_picture(p_H264_Dpb, fs->top_field); + free_picture(p_H264_Dpb, fs->bottom_field); + fs->frame = NULL; + fs->top_field = NULL; + fs->bottom_field = NULL; + break; + case 2: + free_picture(p_H264_Dpb, fs->bottom_field); + fs->bottom_field = NULL; + break; + case 1: + free_picture(p_H264_Dpb, fs->top_field); + fs->top_field = NULL; + break; + case 0: + break; + default: + dpb_print(p_H264_Dpb->decoder_index, PRINT_FLAG_DPB_DETAIL, + "invalid frame store type %x", 500); + } + fs->is_used = 0; + fs->is_long_term = 0; + fs->is_reference = 0; + fs->is_orig_reference = 0; + + /* move empty framestore to end of buffer */ + tmp = p_Dpb->fs[pos]; + + for (i = pos; i < p_Dpb->used_size - 1; i++) + p_Dpb->fs[i] = p_Dpb->fs[i + 1]; + p_Dpb->fs[p_Dpb->used_size - 1] = tmp; + p_Dpb->used_size--; +} + +static int is_used_for_reference(struct FrameStore *fs) +{ + if (fs->is_reference) + return 1; + + if (fs->is_used == 3) { /* frame */ + if (fs->frame->used_for_reference) + return 1; + } + + if (fs->is_used & 1) { /* top field */ + if (fs->top_field) { + if (fs->top_field->used_for_reference) + return 1; + } + } + + if (fs->is_used & 2) { /* bottom field */ + if (fs->bottom_field) { + if (fs->bottom_field->used_for_reference) + return 1; + } + } + return 0; +} + +static int remove_unused_frame_from_dpb(struct h264_dpb_stru *p_H264_Dpb) +{ + unsigned i; + struct DecodedPictureBuffer *p_Dpb = &p_H264_Dpb->mDPB; + /* check for frames that were already output and no longer + used for reference */ + for (i = 0; i < p_Dpb->used_size; i++) { + if ((!is_used_for_reference(p_Dpb->fs[i])) && + (p_Dpb->fs[i]->colocated_buf_index >= 0)) { + dpb_print(p_H264_Dpb->decoder_index, + PRINT_FLAG_DPB_DETAIL, + "release_colocate_buf[%d] for fs[%d]\n", + p_Dpb->fs[i]->colocated_buf_index, i); + + release_colocate_buf(p_H264_Dpb, + p_Dpb->fs[i]->colocated_buf_index); /* rain */ + p_Dpb->fs[i]->colocated_buf_index = -1; + } + } + + for (i = 0; i < p_Dpb->used_size; i++) { + if (p_Dpb->fs[i]->is_output && + (!is_used_for_reference(p_Dpb->fs[i]))) { + release_buf_spec_num(p_H264_Dpb->vdec, + p_Dpb->fs[i]->buf_spec_num); + p_Dpb->fs[i]->buf_spec_num = -1; + remove_frame_from_dpb(p_H264_Dpb, i); + + dpb_print(p_H264_Dpb->decoder_index, + PRINT_FLAG_DPB_DETAIL, "%s[%d]\n", + __func__, i); + + return 1; + } + } + return 0; +} + +void bufmgr_h264_remove_unused_frame(struct h264_dpb_stru *p_H264_Dpb) +{ + struct DecodedPictureBuffer *p_Dpb = &p_H264_Dpb->mDPB; + int ret = 0; + unsigned char print_flag = 0; + do { + ret = remove_unused_frame_from_dpb(p_H264_Dpb); + if (ret != 0) + print_flag = 1; + } while (ret != 0); + if (print_flag) { + dpb_print(p_H264_Dpb->decoder_index, + PRINT_FLAG_DPB_DETAIL, "%s\r\n", __func__); + dump_dpb(p_Dpb); + } +} + +#ifdef OUTPUT_BUFFER_IN_C +int is_there_unused_frame_from_dpb(struct DecodedPictureBuffer *p_Dpb) +{ + unsigned i; + + /* check for frames that were already output and no longer + * used for reference */ + for (i = 0; i < p_Dpb->used_size; i++) { + if (p_Dpb->fs[i]->is_output && + (!is_used_for_reference(p_Dpb->fs[i]))) { + return 1; + } + } + return 0; +} +#endif + +static void get_smallest_poc(struct DecodedPictureBuffer *p_Dpb, int *poc, + int *pos) +{ + unsigned i; + struct h264_dpb_stru *p_H264_Dpb = container_of(p_Dpb, + struct h264_dpb_stru, mDPB); + dpb_print(p_H264_Dpb->decoder_index, + PRINT_FLAG_DPB_DETAIL, "%s\n", __func__); + if (p_Dpb->used_size < 1) { + dpb_print(p_H264_Dpb->decoder_index, PRINT_FLAG_DPB_DETAIL, + "Cannot determine smallest POC, DPB empty. %d\n", + 150); + } + + *pos = -1; + *poc = INT_MAX; + for (i = 0; i < p_Dpb->used_size; i++) { +#ifdef OUTPUT_BUFFER_IN_C + /* rain */ + if ((*poc > p_Dpb->fs[i]->poc) && + (!p_Dpb->fs[i]->is_output) && + (!p_Dpb->fs[i]->pre_output)) { +#else + if ((*poc > p_Dpb->fs[i]->poc) && (!p_Dpb->fs[i]->is_output)) { +#endif + *poc = p_Dpb->fs[i]->poc; + *pos = i; + } + } +} + +#ifdef OLD_OUTPUT_CODE +static int output_one_frame_from_dpb(struct h264_dpb_stru *p_H264_Dpb) +{ + struct DecodedPictureBuffer *p_Dpb = &p_H264_Dpb->mDPB; + struct VideoParameters *p_Vid = &p_H264_Dpb->mVideo; + int poc, pos; + /* diagnostics */ + dpb_print(p_H264_Dpb->decoder_index, PRINT_FLAG_DPB_DETAIL, + "%s\n", __func__); + + if (p_Dpb->used_size < 1) { + dpb_print(p_H264_Dpb->decoder_index, PRINT_FLAG_DPB_DETAIL, + "Cannot output frame, DPB empty. %d\n", 150); + } + + /* find smallest POC */ + get_smallest_poc(p_Dpb, &poc, &pos); + + if (pos == -1) + return 0; + + /* call the output function */ + /* dpb_print(p_H264_Dpb->decoder_index, + PRINT_FLAG_DPB_DETAIL, + "output frame with frame_num #%d, poc %d" + "(dpb. p_Dpb->size = %d, p_Dpb->used_size = %d)\n", + p_Dpb->fs[pos]->frame_num, p_Dpb->fs[pos]->frame->poc, + p_Dpb->size, p_Dpb->used_size); + */ + +#if 0 + /* ??? */ + /* picture error concealment */ + if (p_Vid->conceal_mode != 0) { + if (p_Dpb->last_output_poc == 0) + write_lost_ref_after_idr(p_Dpb, pos); +#if (MVC_EXTENSION_ENABLE) + write_lost_non_ref_pic(p_Dpb, poc, + p_Vid->p_out_mvc[p_Dpb->layer_id]); +#else + write_lost_non_ref_pic(p_Dpb, poc, p_Vid->p_out); +#endif + } +#endif +/* JVT-P072 ends */ + +#if 0 +/* ??? */ +#if (MVC_EXTENSION_ENABLE) + write_stored_frame(p_Vid, p_Dpb->fs[pos], + p_Vid->p_out_mvc[p_Dpb->layer_id]); +#else + write_stored_frame(p_Vid, p_Dpb->fs[pos], p_Vid->p_out); +#endif +#endif + /* picture error concealment */ + if (p_Vid->conceal_mode == 0) { + if (p_Dpb->last_output_poc >= poc) { + dpb_print(p_H264_Dpb->decoder_index, + PRINT_FLAG_DPB_DETAIL, + "output POC must be in ascending order %d\n", + 150); + } + } + + p_Dpb->last_output_poc = poc; + + /* free frame store and move empty store to end of buffer */ +/* #ifdef OUTPUT_BUFFER_IN_C */ + if ((h264_debug_flag & OUTPUT_CURRENT_BUF) == 0) { + if (prepare_display_buf(p_H264_Dpb->vdec, + p_Dpb->fs[pos]) >= 0) { + p_Dpb->fs[pos]->pre_output = 1; + } + } +/* #else */ + else { + if (!is_used_for_reference(p_Dpb->fs[pos])) { + release_colocate_buf(p_H264_Dpb, + p_Dpb->fs[pos]->colocated_buf_index); /*rain*/ + p_Dpb->fs[pos]->colocated_buf_index = -1; + + release_buf_spec_num(p_H264_Dpb->vdec, + p_Dpb->fs[pos]->buf_spec_num); + p_Dpb->fs[pos]->buf_spec_num = -1; + + remove_frame_from_dpb(p_H264_Dpb, pos); + } +/* #endif */ + } + return 1; +} + +#else +/* none OLD_OUTPUT_CODE */ + +int output_frames(struct h264_dpb_stru *p_H264_Dpb, unsigned char flush_flag) +{ + int poc, pos; + struct DecodedPictureBuffer *p_Dpb = &p_H264_Dpb->mDPB; + int i; + int none_displayed_num = 0; + if (!flush_flag) { + for (i = 0; i < p_Dpb->used_size; i++) { + if ((!p_Dpb->fs[i]->is_output) && + (!p_Dpb->fs[i]->pre_output)) + none_displayed_num++; + } + if (none_displayed_num < p_H264_Dpb->reorder_pic_num) + return 0; + } + + get_smallest_poc(p_Dpb, &poc, &pos); + + if (pos == -1) + return 0; +#if 0 + if (is_used_for_reference(p_Dpb->fs[pos])) + return 0; +#endif + p_Dpb->last_output_poc = poc; + + if (prepare_display_buf(p_H264_Dpb->vdec, p_Dpb->fs[pos]) >= 0) + p_Dpb->fs[pos]->pre_output = 1; + + dpb_print(p_H264_Dpb->decoder_index, PRINT_FLAG_DPB_DETAIL, + "%s[%d] poc %d\n", __func__, pos, poc); + + return 1; + +} +#endif + + +static void flush_dpb(struct h264_dpb_stru *p_H264_Dpb) +{ + /* struct VideoParameters *p_Vid = p_Dpb->p_Vid; */ + struct DecodedPictureBuffer *p_Dpb = &p_H264_Dpb->mDPB; + unsigned i; + + dpb_print(p_H264_Dpb->decoder_index, PRINT_FLAG_DPB_DETAIL, + "%s\n", __func__); + + /* diagnostics */ + /* dpb_print(p_H264_Dpb->decoder_index, + PRINT_FLAG_DPB_DETAIL, + "Flush remaining frames from the dpb." + "p_Dpb->size = %d, p_Dpb->used_size = %d\n", + p_Dpb->size, p_Dpb->used_size); + */ + + if (!p_Dpb->init_done) + return; +/* if(p_Vid->conceal_mode == 0) */ +#if 0 +/* ??? */ + if (p_Vid->conceal_mode != 0) + conceal_non_ref_pics(p_Dpb, 0); +#endif + /* mark all frames unused */ + for (i = 0; i < p_Dpb->used_size; i++) { +#if MVC_EXTENSION_ENABLE + assert(p_Dpb->fs[i]->view_id == p_Dpb->layer_id); +#endif + unmark_for_reference(p_Dpb, p_Dpb->fs[i]); + + if (h264_debug_flag & OUTPUT_CURRENT_BUF) + set_frame_output_flag(p_H264_Dpb, i); + + } + + while (remove_unused_frame_from_dpb(p_H264_Dpb)) + ; + + /* output frames in POC order */ +#ifndef OLD_OUTPUT_CODE + if ((h264_debug_flag & OUTPUT_CURRENT_BUF) == 0) { + while (output_frames(p_H264_Dpb, 1)) + ; + } +#else + while (p_Dpb->used_size && output_one_frame_from_dpb(p_H264_Dpb)) + ; +#endif + + p_Dpb->last_output_poc = INT_MIN; +} + +static int is_short_term_reference(struct DecodedPictureBuffer *p_Dpb, + struct FrameStore *fs) +{ + struct h264_dpb_stru *p_H264_Dpb = container_of(p_Dpb, + struct h264_dpb_stru, mDPB); + if (fs->is_used == 3) { /* frame */ + if ((fs->frame->used_for_reference) && + (!fs->frame->is_long_term)) { + dpb_print(p_H264_Dpb->decoder_index, + PRINT_FLAG_DPB_DETAIL, "[[%s 1]]", + __func__); + return 1; + } + } + + if (fs->is_used & 1) { /* top field */ + if (fs->top_field) { + if ((fs->top_field->used_for_reference) && + (!fs->top_field->is_long_term)) { + dpb_print(p_H264_Dpb->decoder_index, + PRINT_FLAG_DPB_DETAIL, "[[%s 2]]", + __func__); + return 1; + } + } + } + + if (fs->is_used & 2) { /* bottom field */ + if (fs->bottom_field) { + if ((fs->bottom_field->used_for_reference) && + (!fs->bottom_field->is_long_term)) { + dpb_print(p_H264_Dpb->decoder_index, + PRINT_FLAG_DPB_DETAIL, "[[%s 3]]", + __func__); + return 1; + } + } + } + return 0; +} + +static int is_long_term_reference(struct FrameStore *fs) +{ + + if (fs->is_used == 3) { /* frame */ + if ((fs->frame->used_for_reference) && + (fs->frame->is_long_term)) { + return 1; + } + } + + if (fs->is_used & 1) { /* top field */ + if (fs->top_field) { + if ((fs->top_field->used_for_reference) && + (fs->top_field->is_long_term)) { + return 1; + } + } + } + + if (fs->is_used & 2) { /* bottom field */ + if (fs->bottom_field) { + if ((fs->bottom_field->used_for_reference) && + (fs->bottom_field->is_long_term)) { + return 1; + } + } + } + return 0; +} + +static void update_ref_list(struct DecodedPictureBuffer *p_Dpb) +{ + unsigned i, j; + + struct h264_dpb_stru *p_H264_Dpb = container_of(p_Dpb, + struct h264_dpb_stru, mDPB); + dpb_print(p_H264_Dpb->decoder_index, PRINT_FLAG_DPB_DETAIL, + "%s (%d, %d)\n", __func__, p_Dpb->size, p_Dpb->used_size); + for (i = 0, j = 0; i < p_Dpb->used_size; i++) { +#if 1 + dpb_print(p_H264_Dpb->decoder_index, PRINT_FLAG_DPB_DETAIL, + "fs[%d]: fs %p frame %p is_reference %d %d %d\n", + i, p_Dpb->fs[i], p_Dpb->fs[i]->frame, + p_Dpb->fs[i]->frame != NULL ? + p_Dpb->fs[i]->frame->used_for_reference : 0, + p_Dpb->fs[i]->top_field != NULL ? + p_Dpb->fs[i]->top_field->used_for_reference : + 0, + p_Dpb->fs[i]->bottom_field != NULL ? + p_Dpb->fs[i]->bottom_field->used_for_reference : 0); +#endif + if (is_short_term_reference(p_Dpb, p_Dpb->fs[i])) { + dpb_print(p_H264_Dpb->decoder_index, + PRINT_FLAG_DPB_DETAIL, + "fs_ref[%d]=fs[%d]: fs %p\n", j, i, p_Dpb->fs[i]); + p_Dpb->fs_ref[j++] = p_Dpb->fs[i]; + } + } + + p_Dpb->ref_frames_in_buffer = j; + dpb_print(p_H264_Dpb->decoder_index, PRINT_FLAG_DPB_DETAIL, + "%s dpb size is %d, %d\n", __func__, p_Dpb->size, j); + while (j < p_Dpb->size) { + /* dpb_print(p_H264_Dpb->decoder_index, PRINT_FLAG_DPB_DETAIL, + "fs_ref[%d]=null\n", j); */ + p_Dpb->fs_ref[j++] = NULL; + } +#ifdef ERROR_CHECK + for (i = 0; i < DPB_SIZE_MAX; i++) { + if (p_Dpb->fs_ref[i] == NULL) + p_Dpb->fs_ref[i] = &dummy_fs; + } +#endif +} + +static void update_ltref_list(struct DecodedPictureBuffer *p_Dpb) +{ + unsigned i, j; + struct h264_dpb_stru *p_H264_Dpb = container_of(p_Dpb, + struct h264_dpb_stru, mDPB); + + dpb_print(p_H264_Dpb->decoder_index, PRINT_FLAG_DPB_DETAIL, + "%s\n", __func__); + for (i = 0, j = 0; i < p_Dpb->used_size; i++) { + if (is_long_term_reference(p_Dpb->fs[i])) + p_Dpb->fs_ltref[j++] = p_Dpb->fs[i]; + } + + p_Dpb->ltref_frames_in_buffer = j; + + while (j < p_Dpb->size) + p_Dpb->fs_ltref[j++] = NULL; +#ifdef ERROR_CHECK + for (i = 0; i < DPB_SIZE_MAX; i++) { + if (p_Dpb->fs_ltref[i] == NULL) + p_Dpb->fs_ltref[i] = &dummy_fs; + } +#endif +} + +static void idr_memory_management(struct h264_dpb_stru *p_H264_Dpb, + struct StorablePicture *p) +{ + struct DecodedPictureBuffer *p_Dpb = &p_H264_Dpb->mDPB; + + dpb_print(p_H264_Dpb->decoder_index, PRINT_FLAG_DPB_DETAIL, + "%s %d %d\n", __func__, p_Dpb->ref_frames_in_buffer, + p_Dpb->ltref_frames_in_buffer); + + if (p->no_output_of_prior_pics_flag) { +#if 0 + /*???*/ + /* free all stored pictures */ + int i; + for (i = 0; i < p_Dpb->used_size; i++) { + /* reset all reference settings + * free_frame_store(p_Dpb->fs[i]); + * p_Dpb->fs[i] = alloc_frame_store(); + */ + reset_frame_store(p_H264_Dpb, p_Dpb->fs[i]); /* ??? */ + } + for (i = 0; i < p_Dpb->ref_frames_in_buffer; i++) + p_Dpb->fs_ref[i] = NULL; + for (i = 0; i < p_Dpb->ltref_frames_in_buffer; i++) + p_Dpb->fs_ltref[i] = NULL; + p_Dpb->used_size = 0; +#endif + } else { + flush_dpb(p_H264_Dpb); + } + p_Dpb->last_picture = NULL; + + update_ref_list(p_Dpb); + update_ltref_list(p_Dpb); + p_Dpb->last_output_poc = INT_MIN; + + if (p->long_term_reference_flag) { + p_Dpb->max_long_term_pic_idx = 0; + p->is_long_term = 1; + p->long_term_frame_idx = 0; + } else { + p_Dpb->max_long_term_pic_idx = -1; + p->is_long_term = 0; + } + +#if (MVC_EXTENSION_ENABLE) + p_Dpb->last_output_view_id = -1; +#endif + +} + +static void sliding_window_memory_management( + struct DecodedPictureBuffer *p_Dpb, + struct StorablePicture *p) +{ + unsigned i; + struct h264_dpb_stru *p_H264_Dpb = container_of(p_Dpb, + struct h264_dpb_stru, mDPB); + + /* assert (!p->idr_flag); */ + dpb_print(p_H264_Dpb->decoder_index, PRINT_FLAG_DPB_DETAIL, + "%s\n", __func__); + + /* if this is a reference pic with sliding window, + unmark first ref frame */ + if (p_Dpb->ref_frames_in_buffer == imax( + 1, p_Dpb->num_ref_frames) - p_Dpb->ltref_frames_in_buffer) { + for (i = 0; i < p_Dpb->used_size; i++) { + if (p_Dpb->fs[i]->is_reference && + (!(p_Dpb->fs[i]->is_long_term))) { + dpb_print(p_H264_Dpb->decoder_index, + PRINT_FLAG_DPB_DETAIL, "unmark %d\n", i); + unmark_for_reference(p_Dpb, p_Dpb->fs[i]); + update_ref_list(p_Dpb); + break; + } + } + } + + p->is_long_term = 0; +} + +static void check_num_ref(struct DecodedPictureBuffer *p_Dpb) +{ + if ((int)(p_Dpb->ltref_frames_in_buffer + + p_Dpb->ref_frames_in_buffer) > + imax(1, p_Dpb->num_ref_frames)) { + struct h264_dpb_stru *p_H264_Dpb = container_of(p_Dpb, + struct h264_dpb_stru, mDPB); + dpb_print(p_H264_Dpb->decoder_index, PRINT_FLAG_DPB_DETAIL, + "Max. number of reference frames exceeded. Invalid stream. lt %d ref %d mum_ref %d\n", + p_Dpb->ltref_frames_in_buffer, + p_Dpb->ref_frames_in_buffer, + p_Dpb->num_ref_frames); + } +} + +static void dump_dpb(struct DecodedPictureBuffer *p_Dpb) +{ + unsigned i; + struct h264_dpb_stru *p_H264_Dpb = + container_of(p_Dpb, struct h264_dpb_stru, mDPB); + if ((h264_debug_flag & PRINT_FLAG_DUMP_DPB) == 0) + return; + for (i = 0; i < p_Dpb->used_size; i++) { + dpb_print(p_H264_Dpb->decoder_index, + PRINT_FLAG_DUMP_DPB, + "("); + dpb_print(p_H264_Dpb->decoder_index, + PRINT_FLAG_DUMP_DPB | 0x100, + "fn=%d ", p_Dpb->fs[i]->frame_num); + if (p_Dpb->fs[i]->is_used & 1) { + if (p_Dpb->fs[i]->top_field) + dpb_print(p_H264_Dpb->decoder_index, + PRINT_FLAG_DUMP_DPB | 0x100, + "T: poc=%d ", + p_Dpb->fs[i]->top_field->poc); + else + dpb_print(p_H264_Dpb->decoder_index, + PRINT_FLAG_DUMP_DPB | 0x100, + "T: poc=%d ", + p_Dpb->fs[i]->frame->top_poc); + } + if (p_Dpb->fs[i]->is_used & 2) { + if (p_Dpb->fs[i]->bottom_field) + dpb_print(p_H264_Dpb->decoder_index, + PRINT_FLAG_DUMP_DPB | 0x100, + "B: poc=%d ", + p_Dpb->fs[i]->bottom_field->poc); + else + dpb_print(p_H264_Dpb->decoder_index, + PRINT_FLAG_DUMP_DPB | 0x100, + "B: poc=%d ", + p_Dpb->fs[i]->frame->bottom_poc); + } + if (p_Dpb->fs[i]->is_used == 3) + dpb_print(p_H264_Dpb->decoder_index, + PRINT_FLAG_DUMP_DPB | 0x100, + "F: poc=%d ", + p_Dpb->fs[i]->frame->poc); + dpb_print(p_H264_Dpb->decoder_index, + PRINT_FLAG_DUMP_DPB | 0x100, + "G: poc=%d) ", p_Dpb->fs[i]->poc); + if (p_Dpb->fs[i]->is_reference) + dpb_print(p_H264_Dpb->decoder_index, + PRINT_FLAG_DUMP_DPB | 0x100, + "ref (%d) ", p_Dpb->fs[i]->is_reference); + if (p_Dpb->fs[i]->is_long_term) + dpb_print(p_H264_Dpb->decoder_index, + PRINT_FLAG_DUMP_DPB | 0x100, + "lt_ref (%d) ", p_Dpb->fs[i]->is_reference); + if (p_Dpb->fs[i]->is_output) + dpb_print(p_H264_Dpb->decoder_index, + PRINT_FLAG_DUMP_DPB | 0x100, + "out "); + if (p_Dpb->fs[i]->pre_output) + dpb_print(p_H264_Dpb->decoder_index, + PRINT_FLAG_DUMP_DPB | 0x100, + "for_out "); + if (p_Dpb->fs[i]->is_used == 3) { + if (p_Dpb->fs[i]->frame->non_existing) + dpb_print(p_H264_Dpb->decoder_index, + PRINT_FLAG_DUMP_DPB | 0x100, + "ne "); + } +#if (MVC_EXTENSION_ENABLE) + if (p_Dpb->fs[i]->is_reference) + dpb_print(p_H264_Dpb->decoder_index, + PRINT_FLAG_DUMP_DPB | 0x100, + "view_id (%d) ", p_Dpb->fs[i]->view_id); +#endif + dpb_print(p_H264_Dpb->decoder_index, + PRINT_FLAG_DUMP_DPB | 0x100, + "\n"); + } +} + + +/*! + ************************************************************************ + * \brief + * adaptive memory management + * + ************************************************************************ + */ + +static int get_pic_num_x(struct StorablePicture *p, + int difference_of_pic_nums_minus1) +{ + int currPicNum; + + if (p->structure == FRAME) + currPicNum = p->frame_num; + else + currPicNum = 2 * p->frame_num + 1; + + return currPicNum - (difference_of_pic_nums_minus1 + 1); +} + +/*! + ************************************************************************ + * \brief + * Adaptive Memory Management: Mark short term picture unused + ************************************************************************ + */ +static void mm_unmark_short_term_for_reference(struct DecodedPictureBuffer + *p_Dpb, struct StorablePicture *p, + int difference_of_pic_nums_minus1) +{ + int picNumX; + + unsigned int i; + + picNumX = get_pic_num_x(p, difference_of_pic_nums_minus1); + + for (i = 0; i < p_Dpb->ref_frames_in_buffer; i++) { + if (p->structure == FRAME) { + if ((p_Dpb->fs_ref[i]->is_reference == 3) && + (p_Dpb->fs_ref[i]->is_long_term == 0)) { + if (p_Dpb->fs_ref[i]->frame->pic_num == + picNumX) { + unmark_for_reference(p_Dpb, + p_Dpb->fs_ref[i]); + return; + } + } + } else { + if ((p_Dpb->fs_ref[i]->is_reference & 1) && + (!(p_Dpb->fs_ref[i]->is_long_term & 1))) { + if (p_Dpb->fs_ref[i]->top_field->pic_num == + picNumX) { + p_Dpb->fs_ref[i]-> + top_field->used_for_reference = 0; + p_Dpb->fs_ref[i]->is_reference &= 2; + if (p_Dpb->fs_ref[i]->is_used == 3) { + p_Dpb->fs_ref[i]->frame-> + used_for_reference = 0; + } + return; + } + } + if ((p_Dpb->fs_ref[i]->is_reference & 2) && + (!(p_Dpb->fs_ref[i]->is_long_term & 2))) { + if (p_Dpb->fs_ref[i]->bottom_field->pic_num == + picNumX) { + p_Dpb->fs_ref[i]->bottom_field-> + used_for_reference = 0; + p_Dpb->fs_ref[i]->is_reference &= 1; + if (p_Dpb->fs_ref[i]->is_used == 3) { + p_Dpb->fs_ref[i]->frame-> + used_for_reference = 0; + } + return; + } + } + } + } +} + +static void unmark_for_long_term_reference(struct FrameStore *fs) +{ + if (fs->is_used & 1) { + if (fs->top_field) { + fs->top_field->used_for_reference = 0; + fs->top_field->is_long_term = 0; + } + } + if (fs->is_used & 2) { + if (fs->bottom_field) { + fs->bottom_field->used_for_reference = 0; + fs->bottom_field->is_long_term = 0; + } + } + if (fs->is_used == 3) { + if (fs->top_field && fs->bottom_field) { + fs->top_field->used_for_reference = 0; + fs->top_field->is_long_term = 0; + fs->bottom_field->used_for_reference = 0; + fs->bottom_field->is_long_term = 0; + } + fs->frame->used_for_reference = 0; + fs->frame->is_long_term = 0; + } + + fs->is_reference = 0; + fs->is_long_term = 0; +} + +/*! + ************************************************************************ + * \brief + * Adaptive Memory Management: Mark long term picture unused + ************************************************************************ + */ +static void mm_unmark_long_term_for_reference(struct DecodedPictureBuffer + *p_Dpb, struct StorablePicture *p, int long_term_pic_num) +{ + unsigned int i; + for (i = 0; i < p_Dpb->ltref_frames_in_buffer; i++) { + if (p->structure == FRAME) { + if ((p_Dpb->fs_ltref[i]->is_reference == 3) && + (p_Dpb->fs_ltref[i]->is_long_term == 3)) { + if (p_Dpb->fs_ltref[i]->frame-> + long_term_pic_num == + long_term_pic_num) { + unmark_for_long_term_reference( + p_Dpb->fs_ltref[i]); + } + } + } else { + if ((p_Dpb->fs_ltref[i]->is_reference & 1) && + ((p_Dpb->fs_ltref[i]->is_long_term & 1))) { + if (p_Dpb->fs_ltref[i]->top_field-> + long_term_pic_num == + long_term_pic_num) { + p_Dpb->fs_ltref[i]->top_field-> + used_for_reference = 0; + p_Dpb->fs_ltref[i]->top_field-> + is_long_term = 0; + p_Dpb->fs_ltref[i]->is_reference &= 2; + p_Dpb->fs_ltref[i]->is_long_term &= 2; + if (p_Dpb->fs_ltref[i]->is_used == 3) { + p_Dpb->fs_ltref[i]->frame-> + used_for_reference = 0; + p_Dpb->fs_ltref[i]->frame-> + is_long_term = 0; + } + return; + } + } + if ((p_Dpb->fs_ltref[i]->is_reference & 2) && + ((p_Dpb->fs_ltref[i]->is_long_term & 2))) { + if (p_Dpb->fs_ltref[i]->bottom_field-> + long_term_pic_num == + long_term_pic_num) { + p_Dpb->fs_ltref[i]->bottom_field-> + used_for_reference = 0; + p_Dpb->fs_ltref[i]->bottom_field-> + is_long_term = 0; + p_Dpb->fs_ltref[i]->is_reference &= 1; + p_Dpb->fs_ltref[i]->is_long_term &= 1; + if (p_Dpb->fs_ltref[i]->is_used == 3) { + p_Dpb->fs_ltref[i]->frame-> + used_for_reference = 0; + p_Dpb->fs_ltref[i]->frame-> + is_long_term = 0; + } + return; + } + } + } + } +} + + +/*! + ************************************************************************ + * \brief + * Mark a long-term reference frame or complementary + * field pair unused for referemce + ************************************************************************ + */ +static void unmark_long_term_frame_for_reference_by_frame_idx( + struct DecodedPictureBuffer *p_Dpb, int long_term_frame_idx) +{ + unsigned int i; + for (i = 0; i < p_Dpb->ltref_frames_in_buffer; i++) { + if (p_Dpb->fs_ltref[i]->long_term_frame_idx == + long_term_frame_idx) + unmark_for_long_term_reference(p_Dpb->fs_ltref[i]); + } +} + + +static void unmark1(struct DecodedPictureBuffer *p_Dpb, + unsigned curr_frame_num, int i) +{ + if (p_Dpb->last_picture) { + if ((p_Dpb->last_picture != p_Dpb->fs_ltref[i]) || + p_Dpb->last_picture->frame_num != curr_frame_num) { + unmark_for_long_term_reference(p_Dpb->fs_ltref[i]); + } else { + unmark_for_long_term_reference(p_Dpb->fs_ltref[i]); + } + } +} + +static void unmark2(struct DecodedPictureBuffer *p_Dpb, + int curr_pic_num, int i) +{ + if ((p_Dpb->fs_ltref[i]->frame_num) != (unsigned)(curr_pic_num >> 1)) + unmark_for_long_term_reference(p_Dpb->fs_ltref[i]); +} + +static void unmark3_top(struct DecodedPictureBuffer *p_Dpb, + unsigned curr_frame_num, int curr_pic_num, int mark_current, int i) +{ + if (p_Dpb->fs_ltref[i]->is_long_term == 3) { + unmark_for_long_term_reference(p_Dpb->fs_ltref[i]); + } else { + if (p_Dpb->fs_ltref[i]->is_long_term == 1) { + unmark_for_long_term_reference(p_Dpb->fs_ltref[i]); + } else { + if (mark_current) + unmark1(p_Dpb, curr_frame_num, i); + else + unmark2(p_Dpb, curr_pic_num, i); + } + } +} + +static void unmark3_bottom(struct DecodedPictureBuffer *p_Dpb, + unsigned curr_frame_num, int curr_pic_num, int mark_current, int i) +{ + if (p_Dpb->fs_ltref[i]->is_long_term == 2) { + unmark_for_long_term_reference(p_Dpb->fs_ltref[i]); + } else { + if (mark_current) + unmark1(p_Dpb, curr_frame_num, i); + else + unmark2(p_Dpb, curr_pic_num, i); + } +} + +static void unmark_long_term_field_for_reference_by_frame_idx( + struct DecodedPictureBuffer *p_Dpb, enum PictureStructure structure, + int long_term_frame_idx, int mark_current, unsigned curr_frame_num, + int curr_pic_num) +{ + struct VideoParameters *p_Vid = p_Dpb->p_Vid; + unsigned i; + + /* assert(structure!=FRAME); */ + if (curr_pic_num < 0) + curr_pic_num += (2 * p_Vid->max_frame_num); + + for (i = 0; i < p_Dpb->ltref_frames_in_buffer; i++) { + if (p_Dpb->fs_ltref[i]->long_term_frame_idx == + long_term_frame_idx) { + if (structure == TOP_FIELD) + unmark3_top(p_Dpb, curr_frame_num, + curr_pic_num, mark_current, i); + + if (structure == BOTTOM_FIELD) + unmark3_bottom(p_Dpb, curr_frame_num, + curr_pic_num, mark_current, i); + } + } +} + +/*! + ************************************************************************ + * \brief + * mark a picture as long-term reference + ************************************************************************ + */ +static void mark_pic_long_term(struct DecodedPictureBuffer *p_Dpb, + struct StorablePicture *p, + int long_term_frame_idx, int picNumX) +{ + struct h264_dpb_stru *p_H264_Dpb = container_of(p_Dpb, + struct h264_dpb_stru, mDPB); + unsigned int i; + int add_top, add_bottom; + + if (p->structure == FRAME) { + for (i = 0; i < p_Dpb->ref_frames_in_buffer; i++) { + if (p_Dpb->fs_ref[i]->is_reference == 3) { + if ((!p_Dpb->fs_ref[i]->frame-> + is_long_term) && + (p_Dpb->fs_ref[i]->frame->pic_num == + picNumX)) { + p_Dpb->fs_ref[i]-> + long_term_frame_idx = + p_Dpb->fs_ref[i]->frame-> + long_term_frame_idx = + long_term_frame_idx; + p_Dpb->fs_ref[i]->frame-> + long_term_pic_num = + long_term_frame_idx; + p_Dpb->fs_ref[i]->frame-> + is_long_term = 1; + + if (p_Dpb->fs_ref[i]->top_field && + p_Dpb->fs_ref[i]->bottom_field) { + p_Dpb->fs_ref[i]->top_field-> + long_term_frame_idx = + p_Dpb->fs_ref[i]-> + bottom_field-> + long_term_frame_idx = + long_term_frame_idx; + p_Dpb->fs_ref[i]->top_field-> + long_term_pic_num = + long_term_frame_idx; + p_Dpb->fs_ref[i]-> + bottom_field-> + long_term_pic_num = + long_term_frame_idx; + + p_Dpb->fs_ref[i]->top_field-> + is_long_term = + p_Dpb->fs_ref[i]-> + bottom_field-> + is_long_term + = 1; + + } + p_Dpb->fs_ref[i]->is_long_term = 3; + return; + } + } + } + dpb_print(p_H264_Dpb->decoder_index, PRINT_FLAG_DPB_DETAIL, + "Warning: reference frame for long term marking not found\n"); + } else { + if (p->structure == TOP_FIELD) { + add_top = 1; + add_bottom = 0; + } else { + add_top = 0; + add_bottom = 1; + } + for (i = 0; i < p_Dpb->ref_frames_in_buffer; i++) { + if (p_Dpb->fs_ref[i]->is_reference & 1) { + if ((!p_Dpb->fs_ref[i]->top_field-> + is_long_term) && + (p_Dpb->fs_ref[i]->top_field->pic_num == + picNumX)) { + if ((p_Dpb->fs_ref[i]-> + is_long_term) && + (p_Dpb->fs_ref[i]-> + long_term_frame_idx != + long_term_frame_idx)) { + dpb_print(p_H264_Dpb-> + decoder_index, + PRINT_FLAG_DPB_DETAIL, + "Warning: assigning long_term_frame_idx different from other field\n"); + } + + p_Dpb->fs_ref[i]-> + long_term_frame_idx = + p_Dpb->fs_ref[i]->top_field-> + long_term_frame_idx + = long_term_frame_idx; + p_Dpb->fs_ref[i]->top_field-> + long_term_pic_num = + 2 * long_term_frame_idx + + add_top; + p_Dpb->fs_ref[i]->top_field-> + is_long_term = 1; + p_Dpb->fs_ref[i]->is_long_term |= 1; + if (p_Dpb->fs_ref[i]->is_long_term == + 3) { + p_Dpb->fs_ref[i]->frame-> + is_long_term = 1; + p_Dpb->fs_ref[i]->frame-> + long_term_frame_idx = + p_Dpb->fs_ref[i]-> + frame-> + long_term_pic_num = + long_term_frame_idx; + } + return; + } + } + if (p_Dpb->fs_ref[i]->is_reference & 2) { + if ((!p_Dpb->fs_ref[i]->bottom_field-> + is_long_term) && + (p_Dpb->fs_ref[i]->bottom_field->pic_num + == picNumX)) { + if ((p_Dpb->fs_ref[i]-> + is_long_term) && + (p_Dpb->fs_ref[i]-> + long_term_frame_idx != + long_term_frame_idx)) { + dpb_print(p_H264_Dpb-> + decoder_index, + PRINT_FLAG_DPB_DETAIL, + "Warning: assigning long_term_frame_idx different from other field\n"); + } + + p_Dpb->fs_ref[i]-> + long_term_frame_idx = + p_Dpb->fs_ref[i]->bottom_field + ->long_term_frame_idx + = long_term_frame_idx; + p_Dpb->fs_ref[i]->bottom_field-> + long_term_pic_num = 2 * + long_term_frame_idx + + add_bottom; + p_Dpb->fs_ref[i]->bottom_field-> + is_long_term = 1; + p_Dpb->fs_ref[i]->is_long_term |= 2; + if (p_Dpb->fs_ref[i]-> + is_long_term == 3) { + p_Dpb->fs_ref[i]->frame-> + is_long_term = 1; + p_Dpb->fs_ref[i]->frame-> + long_term_frame_idx = + p_Dpb->fs_ref[i]-> + frame-> + long_term_pic_num = + long_term_frame_idx; + } + return; + } + } + } + dpb_print(p_H264_Dpb->decoder_index, PRINT_FLAG_DPB_DETAIL, + "Warning: reference field for long term marking not found\n"); + } +} + + +/*! + ************************************************************************ + * \brief + * Assign a long term frame index to a short term picture + ************************************************************************ + */ +static void mm_assign_long_term_frame_idx(struct DecodedPictureBuffer *p_Dpb, + struct StorablePicture *p, int difference_of_pic_nums_minus1, + int long_term_frame_idx) +{ + struct h264_dpb_stru *p_H264_Dpb = container_of(p_Dpb, + struct h264_dpb_stru, mDPB); + int picNumX = get_pic_num_x(p, difference_of_pic_nums_minus1); + + /* remove frames/fields with same long_term_frame_idx */ + if (p->structure == FRAME) { + unmark_long_term_frame_for_reference_by_frame_idx(p_Dpb, + long_term_frame_idx); + } else { + unsigned i; + enum PictureStructure structure = FRAME; + + for (i = 0; i < p_Dpb->ref_frames_in_buffer; i++) { + if (p_Dpb->fs_ref[i]->is_reference & 1) { + if (p_Dpb->fs_ref[i]->top_field-> + pic_num == picNumX) { + structure = TOP_FIELD; + break; + } + } + if (p_Dpb->fs_ref[i]->is_reference & 2) { + if (p_Dpb->fs_ref[i]->bottom_field-> + pic_num == picNumX) { + structure = BOTTOM_FIELD; + break; + } + } + } + if (structure == FRAME) { + dpb_print(p_H264_Dpb->decoder_index, + PRINT_FLAG_DPB_DETAIL, + "field for long term marking not found %d", + 200); + } + + unmark_long_term_field_for_reference_by_frame_idx(p_Dpb, + structure, + long_term_frame_idx, 0, 0, picNumX); + } + + mark_pic_long_term(p_Dpb, p, long_term_frame_idx, picNumX); +} + +/*! + ************************************************************************ + * \brief + * Set new max long_term_frame_idx + ************************************************************************ + */ +static void mm_update_max_long_term_frame_idx(struct DecodedPictureBuffer + *p_Dpb, int max_long_term_frame_idx_plus1) +{ + unsigned int i; + + p_Dpb->max_long_term_pic_idx = max_long_term_frame_idx_plus1 - 1; + + /* check for invalid frames */ + for (i = 0; i < p_Dpb->ltref_frames_in_buffer; i++) { + if (p_Dpb->fs_ltref[i]->long_term_frame_idx > + p_Dpb->max_long_term_pic_idx) { + unmark_for_long_term_reference(p_Dpb->fs_ltref[i]); + } + } +} + + +/*! + ************************************************************************ + * \brief + * Mark all long term reference pictures unused for reference + ************************************************************************ + */ +static void mm_unmark_all_long_term_for_reference(struct DecodedPictureBuffer + *p_Dpb) +{ + mm_update_max_long_term_frame_idx(p_Dpb, 0); +} + +/*! + ************************************************************************ + * \brief + * Mark all short term reference pictures unused for reference + ************************************************************************ + */ +static void mm_unmark_all_short_term_for_reference(struct DecodedPictureBuffer + *p_Dpb) +{ + unsigned int i; + for (i = 0; i < p_Dpb->ref_frames_in_buffer; i++) + unmark_for_reference(p_Dpb, p_Dpb->fs_ref[i]); + update_ref_list(p_Dpb); +} + + +/*! + ************************************************************************ + * \brief + * Mark the current picture used for long term reference + ************************************************************************ + */ +static void mm_mark_current_picture_long_term(struct DecodedPictureBuffer + *p_Dpb, struct StorablePicture *p, int long_term_frame_idx) +{ + /* remove long term pictures with same long_term_frame_idx */ + if (p->structure == FRAME) { + unmark_long_term_frame_for_reference_by_frame_idx(p_Dpb, + long_term_frame_idx); + } else { + unmark_long_term_field_for_reference_by_frame_idx(p_Dpb, + p->structure, long_term_frame_idx, + 1, p->pic_num, 0); + } + + p->is_long_term = 1; + p->long_term_frame_idx = long_term_frame_idx; +} + +static void adaptive_memory_management(struct h264_dpb_stru *p_H264_Dpb, + struct StorablePicture *p) +{ + struct DecodedPictureBuffer *p_Dpb = &p_H264_Dpb->mDPB; + struct DecRefPicMarking_s *tmp_drpm; + struct VideoParameters *p_Vid = p_Dpb->p_Vid; + dpb_print(p_H264_Dpb->decoder_index, PRINT_FLAG_DPB_DETAIL, + "%s\n", __func__); + p_Vid->last_has_mmco_5 = 0; + + /* assert (!p->idr_flag); */ + /* assert (p->adaptive_ref_pic_buffering_flag); */ + + while (p->dec_ref_pic_marking_buffer) { + tmp_drpm = p->dec_ref_pic_marking_buffer; + switch (tmp_drpm->memory_management_control_operation) { + case 0: + if (tmp_drpm->Next != NULL) + dpb_print(p_H264_Dpb->decoder_index, + PRINT_FLAG_ERROR, + "error, memory_management_control_operation = 0 not last operation in buffer\n"); + break; + case 1: + mm_unmark_short_term_for_reference(p_Dpb, p, + tmp_drpm->difference_of_pic_nums_minus1); + update_ref_list(p_Dpb); + break; + case 2: + mm_unmark_long_term_for_reference(p_Dpb, p, + tmp_drpm->long_term_pic_num); + update_ltref_list(p_Dpb); + break; + case 3: + mm_assign_long_term_frame_idx(p_Dpb, p, + tmp_drpm->difference_of_pic_nums_minus1, + tmp_drpm->long_term_frame_idx); + update_ref_list(p_Dpb); + update_ltref_list(p_Dpb); + break; + case 4: + mm_update_max_long_term_frame_idx(p_Dpb, + tmp_drpm->max_long_term_frame_idx_plus1); + update_ltref_list(p_Dpb); + break; + case 5: + mm_unmark_all_short_term_for_reference(p_Dpb); + mm_unmark_all_long_term_for_reference(p_Dpb); + p_Vid->last_has_mmco_5 = 1; + break; + case 6: + mm_mark_current_picture_long_term(p_Dpb, p, + tmp_drpm->long_term_frame_idx); + check_num_ref(p_Dpb); + break; + default: + dpb_print(p_H264_Dpb->decoder_index, + PRINT_FLAG_ERROR, + "error, invalid memory_management_control_operation in buffer\n"); + } + p->dec_ref_pic_marking_buffer = tmp_drpm->Next; + /* free (tmp_drpm); */ + } + if (p_Vid->last_has_mmco_5) { + p->pic_num = p->frame_num = 0; + + switch (p->structure) { + case TOP_FIELD: { + /* p->poc = p->top_poc = p_Vid->toppoc =0; */ + p->poc = p->top_poc = 0; + break; + } + case BOTTOM_FIELD: { + /* p->poc = p->bottom_poc = p_Vid->bottompoc = 0; */ + p->poc = p->bottom_poc = 0; + break; + } + case FRAME: { + p->top_poc -= p->poc; + p->bottom_poc -= p->poc; + + /* p_Vid->toppoc = p->top_poc; */ + /* p_Vid->bottompoc = p->bottom_poc; */ + + p->poc = imin(p->top_poc, p->bottom_poc); + /* p_Vid->framepoc = p->poc; */ + break; + } + } + /* currSlice->ThisPOC = p->poc; */ +#if (MVC_EXTENSION_ENABLE) + if (p->view_id == 0) { + flush_dpb(p_Vid->p_Dpb_layer[0]); + flush_dpb(p_Vid->p_Dpb_layer[1]); + } else { + flush_dpb(p_Dpb); + } +#else + flush_dpb(p_H264_Dpb); +#endif + } +} + + +void store_picture_in_dpb(struct h264_dpb_stru *p_H264_Dpb, + struct StorablePicture *p) +{ + /* struct VideoParameters *p_Vid = p_Dpb->p_Vid; */ + struct VideoParameters *p_Vid = &p_H264_Dpb->mVideo; + struct DecodedPictureBuffer *p_Dpb = &p_H264_Dpb->mDPB; + unsigned i; +#if 0 + int poc, pos; +#endif + dpb_print(p_H264_Dpb->decoder_index, PRINT_FLAG_DPB_DETAIL, + "%s p_Vid %p\n", __func__, p_Vid); + + /* picture error concealment */ + + /* diagnostics */ + /* dpb_print(p_H264_Dpb->decoder_index, PRINT_FLAG_DPB_DETAIL, + "Storing (%s) non-ref pic with frame_num #%d\n", + (p->type == FRAME)?"FRAME":(p->type == TOP_FIELD)? + "TOP_FIELD":"BOTTOM_FIELD", p->pic_num); */ + /* if frame, check for new store, */ + /* assert (p!=NULL); */ + + p_Vid->last_has_mmco_5 = 0; + p_Vid->last_pic_bottom_field = (p->structure == BOTTOM_FIELD); + + if (p->idr_flag) { + idr_memory_management(p_H264_Dpb, p); +#if 0 +/* ??? */ + /* picture error concealment */ + memset(p_Vid->pocs_in_dpb, 0, sizeof(int) * 100); +#endif + } else { +#if 1 +/* ??? */ + /* adaptive memory management */ + if (p->used_for_reference && + (p->adaptive_ref_pic_buffering_flag)) + adaptive_memory_management(p_H264_Dpb, p); +#endif + } + + if ((p->structure == TOP_FIELD) || (p->structure == BOTTOM_FIELD)) { + /* check for frame store with same pic_number */ + if (p_Dpb->last_picture) { + if ((int)p_Dpb->last_picture->frame_num == + p->pic_num) { + if (((p->structure == TOP_FIELD) && + (p_Dpb->last_picture->is_used == 2)) || + ((p->structure == BOTTOM_FIELD) && + (p_Dpb->last_picture->is_used == 1))) { + if ((p->used_for_reference && + (p_Dpb->last_picture-> + is_orig_reference != 0)) || + (!p->used_for_reference && + (p_Dpb->last_picture-> + is_orig_reference == 0))) { + insert_picture_in_dpb( + p_H264_Dpb, + p_Dpb->last_picture, + p); + update_ref_list(p_Dpb); + update_ltref_list(p_Dpb); + dump_dpb(p_Dpb); + p_Dpb->last_picture = NULL; + return; + } + } + } + } + } + /* this is a frame or a field which has no stored + * complementary field */ + + /* sliding window, if necessary */ + if ((!p->idr_flag) && (p->used_for_reference && + (!p->adaptive_ref_pic_buffering_flag))) { + sliding_window_memory_management(p_Dpb, p); + } + + /* picture error concealment */ + if (p_Vid->conceal_mode != 0) { + for (i = 0; i < p_Dpb->size; i++) + if (p_Dpb->fs[i]->is_reference) + p_Dpb->fs[i]->concealment_reference = 1; + } + +#ifndef OLD_OUTPUT_CODE + while (remove_unused_frame_from_dpb(p_H264_Dpb)) + ; + + if ((h264_debug_flag & OUTPUT_CURRENT_BUF) == 0) { + while (output_frames(p_H264_Dpb, 0)) + ; + } +#else + /* OLD_OUTPUT_CODE */ + + /* first try to remove unused frames */ + if (p_Dpb->used_size == p_Dpb->size) { +#if 0 + /* ??? */ + /* picture error concealment */ + if (p_Vid->conceal_mode != 0) + conceal_non_ref_pics(p_Dpb, 2); +#endif + remove_unused_frame_from_dpb(p_H264_Dpb); + +#if 0 + /* ??? */ + if (p_Vid->conceal_mode != 0) + sliding_window_poc_management(p_Dpb, p); +#endif + } + + /* then output frames until one can be removed */ +/* #ifdef OUTPUT_BUFFER_IN_C */ + if ((h264_debug_flag & OUTPUT_CURRENT_BUF) == 0) { + if (p_Dpb->used_size > (p_Dpb->size - 5)) + output_one_frame_from_dpb(p_H264_Dpb); + } else { +/* #else */ + while (p_Dpb->used_size == p_Dpb->size) { +#if 0 + /* non-reference frames may be output directly */ + if (!p->used_for_reference) { + get_smallest_poc(p_Dpb, &poc, &pos); + if ((-1 == pos) || (p->poc < poc)) { +#if (MVC_EXTENSION_ENABLE) + if (p_Vid->profile_idc >= MVC_HIGH) + dpb_print( + p_H264_Dpb->decoder_index, + PRINT_FLAG_DPB_DETAIL, + "Display order might not be correct, %d, %d\n", + p->view_id, p->poc); +#endif +#if 0 +/* ??? */ +#if (MVC_EXTENSION_ENABLE) + direct_output(p_Vid, p, p_Vid-> + p_out_mvc[p_Dpb->layer_id]); +#else + direct_output(p_Vid, p, p_Vid->p_out); +#endif +#endif + return; + } + } +#endif + /* flush a frame */ + output_one_frame_from_dpb(p_H264_Dpb); + } + + } +/* #endif */ + /* OLD_OUTPUT_CODE */ +#endif + + /* check for duplicate frame number in short term reference buffer */ + if ((p->used_for_reference) && (!p->is_long_term)) { + for (i = 0; i < p_Dpb->ref_frames_in_buffer; i++) { + if (p_Dpb->fs_ref[i]->frame_num == p->frame_num) { + dpb_print(p_H264_Dpb->decoder_index, + PRINT_FLAG_DPB_DETAIL, + "duplicate frame_num in short-term reference picture buffer %d\n", + 500); + } + } + } + /* store at end of buffer */ + + dpb_print(p_H264_Dpb->decoder_index, PRINT_FLAG_DPB_DETAIL, + "%s p_Dpb->used_size %d\n", __func__, p_Dpb->used_size); + if (p_Dpb->used_size >= p_Dpb->size) { + dpb_print(p_H264_Dpb->decoder_index, + PRINT_FLAG_ERROR, + "%s Error: used_sizd %d is large than dpb size\r\n", + __func__, p_Dpb->used_size); + h264_debug_flag |= PRINT_FLAG_DUMP_DPB; + dump_dpb(p_Dpb); + return; + } + + insert_picture_in_dpb(p_H264_Dpb, p_Dpb->fs[p_Dpb->used_size], p); + if (h264_debug_flag & OUTPUT_CURRENT_BUF) { + prepare_display_buf(p_H264_Dpb->vdec, + p_Dpb->fs[p_Dpb->used_size]); + set_frame_output_flag(p_H264_Dpb, p_Dpb->used_size); + + } + + /* picture error concealment */ + if (p->idr_flag) + p_Vid->earlier_missing_poc = 0; + + if (p->structure != FRAME) + p_Dpb->last_picture = p_Dpb->fs[p_Dpb->used_size]; + else + p_Dpb->last_picture = NULL; + + p_Dpb->used_size++; +#if 0 +/* ??? */ + if (p_Vid->conceal_mode != 0) + p_Vid->pocs_in_dpb[p_Dpb->used_size - 1] = p->poc; +#endif + update_ref_list(p_Dpb); + update_ltref_list(p_Dpb); + + check_num_ref(p_Dpb); + + dump_dpb(p_Dpb); +} + +void bufmgr_post(struct h264_dpb_stru *p_H264_Dpb) +{ + /*VideoParameters *p_Vid = p_Dpb->p_Vid;*/ + struct VideoParameters *p_Vid = &p_H264_Dpb->mVideo; + if (p_Vid->last_has_mmco_5) + p_Vid->pre_frame_num = 0; +} +/********************************** +* +* Initialize reference lists +***********************************/ +#define __COMPARE(context, p1, p2) comp(p1, p2) +#define __SHORTSORT(lo, hi, width, comp, context) \ + shortsort(lo, hi, width, comp) +#define CUTOFF 8 /* testing shows that this is good value */ +#define STKSIZ (8*sizeof(void *) - 2) + +#undef swap +static void swap( + char *a, + char *b, + size_t width +) +{ + char tmp; + + if (a != b) + /* Do the swap one character at a time to avoid potential + alignment problems. */ + while (width--) { + tmp = *a; + *a++ = *b; + *b++ = tmp; + } +} + +static void shortsort( + char *lo, + char *hi, + size_t width, + int (*comp)(const void *, const void *) +) +{ + char *p, *max; + + /* Note: in assertions below, i and j are alway inside original + bound of array to sort. */ + + while (hi > lo) { + /* A[i] <= A[j] for i <= j, j > hi */ + max = lo; + for (p = lo + width; p <= hi; p += width) { + /* A[i] <= A[max] for lo <= i < p */ + if (__COMPARE(context, p, max) > 0) + max = p; + /* A[i] <= A[max] for lo <= i <= p */ + } + + /* A[i] <= A[max] for lo <= i <= hi */ + + swap(max, hi, width); + + /* A[i] <= A[hi] for i <= hi, so A[i] <= A[j] for i <= j, + j >= hi */ + + hi -= width; + + /* A[i] <= A[j] for i <= j, j > hi, loop top condition + established */ + } + /* A[i] <= A[j] for i <= j, j > lo, which implies A[i] <= A[j] + for i < j, so array is sorted */ +} + +static void qsort( + void *base, + size_t num, + size_t width, + int (*comp)(const void *, const void *) +) +{ + char *lo, *hi; /* ends of sub-array currently sorting */ + char *mid; /* points to middle of subarray */ + char *loguy, *higuy; /* traveling pointers for partition step */ + size_t size; /* size of the sub-array */ + char *lostk[STKSIZ], *histk[STKSIZ]; + int stkptr; /* stack for saving sub-array to be + processed */ +#if 0 + /* validation section */ + _VALIDATE_RETURN_VOID(base != NULL || num == 0, EINVAL); + _VALIDATE_RETURN_VOID(width > 0, EINVAL); + _VALIDATE_RETURN_VOID(comp != NULL, EINVAL); +#endif + if (num < 2) + return; /* nothing to do */ + + stkptr = 0; /* initialize stack */ + + lo = (char *)base; + hi = (char *)base + width * (num - 1); /* initialize limits */ + + /* this entry point is for pseudo-recursion calling: setting + lo and hi and jumping to here is like recursion, but stkptr is + preserved, locals aren't, so we preserve stuff on the stack */ +recurse: + + size = (hi - lo) / width + 1; /* number of el's to sort */ + + /* below a certain size, it is faster to use a O(n^2) sorting method */ + if (size <= CUTOFF) { + __SHORTSORT(lo, hi, width, comp, context); + } else { + /* First we pick a partitioning element. The efficiency of + the algorithm demands that we find one that is approximately + the median of the values, but also that we select one fast. + We choose the median of the first, middle, and last + elements, to avoid bad performance in the face of already + sorted data, or data that is made up of multiple sorted + runs appended together. Testing shows that a + median-of-three algorithm provides better performance than + simply picking the middle element for the latter case. */ + + mid = lo + (size / 2) * width; /* find middle element */ + + /* Sort the first, middle, last elements into order */ + if (__COMPARE(context, lo, mid) > 0) + swap(lo, mid, width); + if (__COMPARE(context, lo, hi) > 0) + swap(lo, hi, width); + if (__COMPARE(context, mid, hi) > 0) + swap(mid, hi, width); + + /* We now wish to partition the array into three pieces, one + consisting of elements <= partition element, one of elements + equal to the partition element, and one of elements > than + it. This is done below; comments indicate conditions + established at every step. */ + + loguy = lo; + higuy = hi; + + /* Note that higuy decreases and loguy increases on every + iteration, so loop must terminate. */ + for (;;) { + /* lo <= loguy < hi, lo < higuy <= hi, + A[i] <= A[mid] for lo <= i <= loguy, + A[i] > A[mid] for higuy <= i < hi, + A[hi] >= A[mid] */ + + /* The doubled loop is to avoid calling comp(mid,mid), + since some existing comparison funcs don't work + when passed the same value for both pointers. */ + + if (mid > loguy) { + do { + loguy += width; + } while (loguy < mid && + __COMPARE(context, loguy, mid) <= 0); + } + if (mid <= loguy) { + do { + loguy += width; + } while (loguy <= hi && + __COMPARE(context, loguy, mid) <= 0); + } + + /* lo < loguy <= hi+1, A[i] <= A[mid] for + lo <= i < loguy, + either loguy > hi or A[loguy] > A[mid] */ + + do { + higuy -= width; + } while (higuy > mid && + __COMPARE(context, higuy, mid) > 0); + + /* lo <= higuy < hi, A[i] > A[mid] for higuy < i < hi, + either higuy == lo or A[higuy] <= A[mid] */ + + if (higuy < loguy) + break; + + /* if loguy > hi or higuy == lo, then we would have + exited, so A[loguy] > A[mid], A[higuy] <= A[mid], + loguy <= hi, higuy > lo */ + + swap(loguy, higuy, width); + + /* If the partition element was moved, follow it. + Only need to check for mid == higuy, since before + the swap, A[loguy] > A[mid] implies loguy != mid. */ + + if (mid == higuy) + mid = loguy; + + /* A[loguy] <= A[mid], A[higuy] > A[mid]; so condition + at top of loop is re-established */ + } + + /* A[i] <= A[mid] for lo <= i < loguy, + A[i] > A[mid] for higuy < i < hi, + A[hi] >= A[mid] + higuy < loguy + implying: + higuy == loguy-1 + or higuy == hi - 1, loguy == hi + 1, A[hi] == A[mid] */ + + /* Find adjacent elements equal to the partition element. The + doubled loop is to avoid calling comp(mid,mid), since some + existing comparison funcs don't work when passed the same + value for both pointers. */ + + higuy += width; + if (mid < higuy) { + do { + higuy -= width; + } while (higuy > mid && + __COMPARE(context, higuy, mid) == 0); + } + if (mid >= higuy) { + do { + higuy -= width; + } while (higuy > lo && + __COMPARE(context, higuy, mid) == 0); + } + + /* OK, now we have the following: + higuy < loguy + lo <= higuy <= hi + A[i] <= A[mid] for lo <= i <= higuy + A[i] == A[mid] for higuy < i < loguy + A[i] > A[mid] for loguy <= i < hi + A[hi] >= A[mid] */ + + /* We've finished the partition, now we want to sort the + subarrays [lo, higuy] and [loguy, hi]. + We do the smaller one first to minimize stack usage. + We only sort arrays of length 2 or more.*/ + + if (higuy - lo >= hi - loguy) { + if (lo < higuy) { + lostk[stkptr] = lo; + histk[stkptr] = higuy; + ++stkptr; + } /* save big recursion for later */ + + if (loguy < hi) { + lo = loguy; + goto recurse; /* do small recursion */ + } + } else { + if (loguy < hi) { + lostk[stkptr] = loguy; + histk[stkptr] = hi; + ++stkptr; /* save big recursion for later */ + } + + if (lo < higuy) { + hi = higuy; + goto recurse; /* do small recursion */ + } + } + } + + /* We have sorted the array, except for any pending sorts on the stack. + Check if there are any, and do them. */ + + --stkptr; + if (stkptr >= 0) { + lo = lostk[stkptr]; + hi = histk[stkptr]; + goto recurse; /* pop subarray from stack */ + } else + return; /* all subarrays done */ +} + +/*! + ************************************************************************ + * \brief + * compares two stored pictures by picture number for qsort in + * descending order + * + ************************************************************************ + */ +static inline int compare_pic_by_pic_num_desc(const void *arg1, + const void *arg2) +{ + int pic_num1 = (*(struct StorablePicture **)arg1)->pic_num; + int pic_num2 = (*(struct StorablePicture **)arg2)->pic_num; + + if (pic_num1 < pic_num2) + return 1; + if (pic_num1 > pic_num2) + return -1; + else + return 0; +} + +/*! + ************************************************************************ + * \brief + * compares two stored pictures by picture number for qsort in + * descending order + * + ************************************************************************ + */ +static inline int compare_pic_by_lt_pic_num_asc(const void *arg1, + const void *arg2) +{ + int long_term_pic_num1 = + (*(struct StorablePicture **)arg1)->long_term_pic_num; + int long_term_pic_num2 = + (*(struct StorablePicture **)arg2)->long_term_pic_num; + + if (long_term_pic_num1 < long_term_pic_num2) + return -1; + if (long_term_pic_num1 > long_term_pic_num2) + return 1; + else + return 0; +} + +/*! + ************************************************************************ + * \brief + * compares two frame stores by pic_num for qsort in descending order + * + ************************************************************************ + */ +static inline int compare_fs_by_frame_num_desc(const void *arg1, + const void *arg2) +{ + int frame_num_wrap1 = (*(struct FrameStore **)arg1)->frame_num_wrap; + int frame_num_wrap2 = (*(struct FrameStore **)arg2)->frame_num_wrap; + if (frame_num_wrap1 < frame_num_wrap2) + return 1; + if (frame_num_wrap1 > frame_num_wrap2) + return -1; + else + return 0; +} + + +/*! + ************************************************************************ + * \brief + * compares two frame stores by lt_pic_num for qsort in descending order + * + ************************************************************************ + */ +static inline int compare_fs_by_lt_pic_idx_asc(const void *arg1, + const void *arg2) +{ + int long_term_frame_idx1 = + (*(struct FrameStore **)arg1)->long_term_frame_idx; + int long_term_frame_idx2 = + (*(struct FrameStore **)arg2)->long_term_frame_idx; + + if (long_term_frame_idx1 < long_term_frame_idx2) + return -1; + else if (long_term_frame_idx1 > long_term_frame_idx2) + return 1; + else + return 0; +} + + +/*! + ************************************************************************ + * \brief + * compares two stored pictures by poc for qsort in ascending order + * + ************************************************************************ + */ +static inline int compare_pic_by_poc_asc(const void *arg1, const void *arg2) +{ + int poc1 = (*(struct StorablePicture **)arg1)->poc; + int poc2 = (*(struct StorablePicture **)arg2)->poc; + + if (poc1 < poc2) + return -1; + else if (poc1 > poc2) + return 1; + else + return 0; +} + + +/*! + ************************************************************************ + * \brief + * compares two stored pictures by poc for qsort in descending order + * + ************************************************************************ + */ +static inline int compare_pic_by_poc_desc(const void *arg1, const void *arg2) +{ + int poc1 = (*(struct StorablePicture **)arg1)->poc; + int poc2 = (*(struct StorablePicture **)arg2)->poc; + + if (poc1 < poc2) + return 1; + else if (poc1 > poc2) + return -1; + else + return 0; +} + + +/*! + ************************************************************************ + * \brief + * compares two frame stores by poc for qsort in ascending order + * + ************************************************************************ + */ +static inline int compare_fs_by_poc_asc(const void *arg1, const void *arg2) +{ + int poc1 = (*(struct FrameStore **)arg1)->poc; + int poc2 = (*(struct FrameStore **)arg2)->poc; + + if (poc1 < poc2) + return -1; + else if (poc1 > poc2) + return 1; + else + return 0; +} + + +/*! + ************************************************************************ + * \brief + * compares two frame stores by poc for qsort in descending order + * + ************************************************************************ + */ +static inline int compare_fs_by_poc_desc(const void *arg1, const void *arg2) +{ + int poc1 = (*(struct FrameStore **)arg1)->poc; + int poc2 = (*(struct FrameStore **)arg2)->poc; + + if (poc1 < poc2) + return 1; + else if (poc1 > poc2) + return -1; + else + return 0; +} + +/*! + ************************************************************************ + * \brief + * returns true, if picture is short term reference picture + * + ************************************************************************ + */ +static inline int is_short_ref(struct StorablePicture *s) +{ +#ifdef ERROR_CHECK + return (s && + (s->used_for_reference) && (!(s->is_long_term))); +#else + return (s->used_for_reference) && (!(s->is_long_term)); +#endif +} + + +/*! + ************************************************************************ + * \brief + * returns true, if picture is long term reference picture + * + ************************************************************************ + */ +static inline int is_long_ref(struct StorablePicture *s) +{ +#ifdef ERROR_CHECK + return (s && + s->used_for_reference) && (s->is_long_term); +#else + return (s->used_for_reference) && (s->is_long_term); +#endif +} + +/*! + ************************************************************************ + * \brief + * Initialize reference lists for a P Slice + * + ************************************************************************ + */ +/*! + ************************************************************************ + * \brief + * Generates a alternating field list from a given FrameStore list + * + ************************************************************************ + */ +static void gen_pic_list_from_frame_list(enum PictureStructure currStructure, + struct FrameStore **fs_list, int list_idx, + struct StorablePicture **list, + char *list_size, int long_term) +{ + int top_idx = 0; + int bot_idx = 0; + + int (*is_ref)(struct StorablePicture *s) = (long_term) ? is_long_ref : + is_short_ref; + + + if (currStructure == TOP_FIELD) { + while ((top_idx < list_idx) || (bot_idx < list_idx)) { + for (; top_idx < list_idx; top_idx++) { + if (fs_list[top_idx]->is_used & 1) { + if (is_ref(fs_list[top_idx]-> + top_field)) { + /* short term ref pic */ + list[(short) *list_size] = + fs_list[top_idx]->top_field; + (*list_size)++; + top_idx++; + break; + } + } + } + for (; bot_idx < list_idx; bot_idx++) { + if (fs_list[bot_idx]->is_used & 2) { + if (is_ref(fs_list[bot_idx]-> + bottom_field)) { + /* short term ref pic */ + list[(short) *list_size] = + fs_list[bot_idx]->bottom_field; + (*list_size)++; + bot_idx++; + break; + } + } + } + } + } + if (currStructure == BOTTOM_FIELD) { + while ((top_idx < list_idx) || (bot_idx < list_idx)) { + for (; bot_idx < list_idx; bot_idx++) { + if (fs_list[bot_idx]->is_used & 2) { + if (is_ref(fs_list[bot_idx]-> + bottom_field)) { + /* short term ref pic */ + list[(short) *list_size] = + fs_list[bot_idx]->bottom_field; + (*list_size)++; + bot_idx++; + break; + } + } + } + for (; top_idx < list_idx; top_idx++) { + if (fs_list[top_idx]->is_used & 1) { + if (is_ref(fs_list[top_idx]-> + top_field)) { + /* short term ref pic */ + list[(short) *list_size] = + fs_list[top_idx]->top_field; + (*list_size)++; + top_idx++; + break; + } + } + } + } + } +} + +static void init_lists_p_slice(struct Slice *currSlice) +{ + struct VideoParameters *p_Vid = currSlice->p_Vid; + struct DecodedPictureBuffer *p_Dpb = currSlice->p_Dpb; + struct h264_dpb_stru *p_H264_Dpb = container_of(p_Dpb, + struct h264_dpb_stru, mDPB); + + unsigned int i; + + int list0idx = 0; + int listltidx = 0; + + struct FrameStore **fs_list0; + struct FrameStore **fs_listlt; + +#if (MVC_EXTENSION_ENABLE) + currSlice->listinterviewidx0 = 0; + currSlice->listinterviewidx1 = 0; +#endif + + if (currSlice->structure == FRAME) { + for (i = 0; i < p_Dpb->ref_frames_in_buffer; i++) { + if (p_Dpb->fs_ref[i]->is_used == 3) { + if ((p_Dpb->fs_ref[i]->frame-> + used_for_reference) && + (!p_Dpb->fs_ref[i]->frame-> + is_long_term)) { + currSlice->listX[0][list0idx++] = + p_Dpb->fs_ref[i]->frame; + } + } + } + /* order list 0 by PicNum */ + qsort((void *)currSlice->listX[0], list0idx, + sizeof(struct StorablePicture *), + compare_pic_by_pic_num_desc); + currSlice->listXsize[0] = (char) list0idx; + CHECK_VALID(currSlice->listXsize[0], 0); + if (h264_debug_flag & PRINT_FLAG_DPB_DETAIL) { + dpb_print(p_H264_Dpb->decoder_index, + PRINT_FLAG_DPB_DETAIL, + "listX[0] (PicNum): "); + for (i = 0; i < list0idx; i++) { + dpb_print(p_H264_Dpb->decoder_index, + PRINT_FLAG_DPB_DETAIL, "%d ", + currSlice->listX[0][i]->pic_num); + } + dpb_print(p_H264_Dpb->decoder_index, + PRINT_FLAG_DPB_DETAIL, "\n"); + } + /* long term handling */ + for (i = 0; i < p_Dpb->ltref_frames_in_buffer; i++) { + if (p_Dpb->fs_ltref[i]->is_used == 3) { + if (p_Dpb->fs_ltref[i]->frame->is_long_term) { + currSlice->listX[0][list0idx++] = + p_Dpb->fs_ltref[i]->frame; + } + } + } + qsort((void *)&currSlice->listX[0][ + (short) currSlice->listXsize[0]], + list0idx - currSlice->listXsize[0], + sizeof(struct StorablePicture *), + compare_pic_by_lt_pic_num_asc); + currSlice->listXsize[0] = (char) list0idx; + CHECK_VALID(currSlice->listXsize[0], 0); + } else { +#if 0 + fs_list0 = calloc(p_Dpb->size, sizeof(struct FrameStore *)); + if (NULL == fs_list0) + no_mem_exit("init_lists: fs_list0"); + fs_listlt = calloc(p_Dpb->size, sizeof(struct FrameStore *)); + if (NULL == fs_listlt) + no_mem_exit("init_lists: fs_listlt"); +#else + fs_list0 = &(p_Dpb->fs_list0[0]); + fs_listlt = &(p_Dpb->fs_listlt[0]); +#endif + for (i = 0; i < p_Dpb->ref_frames_in_buffer; i++) { + if (p_Dpb->fs_ref[i]->is_reference) + fs_list0[list0idx++] = p_Dpb->fs_ref[i]; + } + + qsort((void *)fs_list0, list0idx, sizeof(struct FrameStore *), + compare_fs_by_frame_num_desc); + + dpb_print(p_H264_Dpb->decoder_index, PRINT_FLAG_DPB_DETAIL, + "fs_list0 (FrameNum): "); + for (i = 0; i < list0idx; i++) { + dpb_print(p_H264_Dpb->decoder_index, + PRINT_FLAG_DPB_DETAIL, "%d ", + fs_list0[i]->frame_num_wrap); + } + dpb_print(p_H264_Dpb->decoder_index, PRINT_FLAG_DPB_DETAIL, + "\n"); + + currSlice->listXsize[0] = 0; + gen_pic_list_from_frame_list(currSlice->structure, fs_list0, + list0idx, currSlice->listX[0], + &currSlice->listXsize[0], 0); + + dpb_print(p_H264_Dpb->decoder_index, PRINT_FLAG_DPB_DETAIL, + "listX[0] (PicNum): "); + for (i = 0; i < currSlice->listXsize[0]; i++) { + dpb_print(p_H264_Dpb->decoder_index, + PRINT_FLAG_DPB_DETAIL, "%d ", + currSlice->listX[0][i]->pic_num); + } + dpb_print(p_H264_Dpb->decoder_index, PRINT_FLAG_DPB_DETAIL, + "\n"); + + /* long term handling */ + for (i = 0; i < p_Dpb->ltref_frames_in_buffer; i++) + fs_listlt[listltidx++] = p_Dpb->fs_ltref[i]; + + qsort((void *)fs_listlt, listltidx, sizeof(struct FrameStore *), + compare_fs_by_lt_pic_idx_asc); + + gen_pic_list_from_frame_list(currSlice->structure, fs_listlt, + listltidx, currSlice->listX[0], + &currSlice->listXsize[0], 1); + + /* free(fs_list0); */ + /* free(fs_listlt); */ + } + currSlice->listXsize[1] = 0; + + + /* set max size */ + currSlice->listXsize[0] = (char) imin(currSlice->listXsize[0], + currSlice->num_ref_idx_active[LIST_0]); + currSlice->listXsize[1] = (char) imin(currSlice->listXsize[1], + currSlice->num_ref_idx_active[LIST_1]); + CHECK_VALID(currSlice->listXsize[0], 0); + CHECK_VALID(currSlice->listXsize[1], 1); + + /* set the unused list entries to NULL */ + for (i = currSlice->listXsize[0]; i < (MAX_LIST_SIZE); i++) + currSlice->listX[0][i] = p_Vid->no_reference_picture; + for (i = currSlice->listXsize[1]; i < (MAX_LIST_SIZE); i++) + currSlice->listX[1][i] = p_Vid->no_reference_picture; + +#if PRINTREFLIST +#if (MVC_EXTENSION_ENABLE) + /* print out for h264_debug_flag purpose */ + if ((p_Vid->profile_idc == MVC_HIGH || + p_Vid->profile_idc == STEREO_HIGH) && + currSlice->current_slice_nr == 0) { + if (currSlice->listXsize[0] > 0) { + dpb_print(p_H264_Dpb->decoder_index, + PRINT_FLAG_DPB_DETAIL, "\n"); + dpb_print(p_H264_Dpb->decoder_index, + PRINT_FLAG_DPB_DETAIL, + " ** (CurViewID:%d %d) %s Ref Pic List 0 ****\n", + currSlice->view_id, + currSlice->ThisPOC, + currSlice->structure == FRAME ? "FRM" : + (currSlice->structure == TOP_FIELD ? + "TOP" : "BOT")); + for (i = 0; i < (unsigned int)(currSlice-> + listXsize[0]); i++) { /* ref list 0 */ + dpb_print(p_H264_Dpb->decoder_index, + PRINT_FLAG_DPB_DETAIL, + " %2d -> POC: %4d PicNum: %4d ViewID: %d\n", + i, + currSlice->listX[0][i]->poc, + currSlice->listX[0][i]->pic_num, + currSlice->listX[0][i]->view_id); + } + } + } +#endif +#endif +} + + +/*! + ************************************************************************ + * \brief + * Initialize reference lists + * + ************************************************************************ + */ +static void init_mbaff_lists(struct VideoParameters *p_Vid, + struct Slice *currSlice) +{ + unsigned j; + int i; + + for (i = 2; i < 6; i++) { + for (j = 0; j < MAX_LIST_SIZE; j++) + currSlice->listX[i][j] = p_Vid->no_reference_picture; + currSlice->listXsize[i] = 0; + } + + for (i = 0; i < currSlice->listXsize[0]; i++) { +#ifdef ERROR_CHECK + if (currSlice->listX[0][i] == NULL) { + pr_info( + "error currSlice->listX[0][%d] is NULL\r\n", i); + break; + } +#endif + currSlice->listX[2][2 * i] = + currSlice->listX[0][i]->top_field; + currSlice->listX[2][2 * i + 1] = + currSlice->listX[0][i]->bottom_field; + currSlice->listX[4][2 * i] = + currSlice->listX[0][i]->bottom_field; + currSlice->listX[4][2 * i + 1] = + currSlice->listX[0][i]->top_field; + } + currSlice->listXsize[2] = currSlice->listXsize[4] = + currSlice->listXsize[0] * 2; + + for (i = 0; i < currSlice->listXsize[1]; i++) { +#ifdef ERROR_CHECK + if (currSlice->listX[1][i] == NULL) { + pr_info( + "error currSlice->listX[1][%d] is NULL\r\n", i); + break; + } +#endif + currSlice->listX[3][2 * i] = + currSlice->listX[1][i]->top_field; + currSlice->listX[3][2 * i + 1] = + currSlice->listX[1][i]->bottom_field; + currSlice->listX[5][2 * i] = + currSlice->listX[1][i]->bottom_field; + currSlice->listX[5][2 * i + 1] = + currSlice->listX[1][i]->top_field; + } + currSlice->listXsize[3] = currSlice->listXsize[5] = + currSlice->listXsize[1] * 2; +} + + + +static void init_lists_i_slice(struct Slice *currSlice) +{ + +#if (MVC_EXTENSION_ENABLE) + currSlice->listinterviewidx0 = 0; + currSlice->listinterviewidx1 = 0; +#endif + + currSlice->listXsize[0] = 0; + currSlice->listXsize[1] = 0; +} + +static void init_lists_b_slice(struct Slice *currSlice) +{ + struct VideoParameters *p_Vid = currSlice->p_Vid; + struct DecodedPictureBuffer *p_Dpb = currSlice->p_Dpb; + struct h264_dpb_stru *p_H264_Dpb = container_of(p_Dpb, + struct h264_dpb_stru, mDPB); + + unsigned int i; + int j; + + int list0idx = 0; + int list0idx_1 = 0; + int listltidx = 0; + + struct FrameStore **fs_list0; + struct FrameStore **fs_list1; + struct FrameStore **fs_listlt; + +#if (MVC_EXTENSION_ENABLE) + currSlice->listinterviewidx0 = 0; + currSlice->listinterviewidx1 = 0; +#endif + + { + /* B-Slice */ + if (currSlice->structure == FRAME) { + for (i = 0; i < p_Dpb->ref_frames_in_buffer; i++) { + if ((p_Dpb->fs_ref[i]->is_used == 3) && + ((p_Dpb->fs_ref[i]->frame-> + used_for_reference) && + (!p_Dpb->fs_ref[i]->frame-> + is_long_term)) && + (currSlice->framepoc >= + p_Dpb->fs_ref[i]->frame->poc)) { + /* !KS use >= for error + concealment */ + currSlice->listX[0][list0idx++] = + p_Dpb->fs_ref[i]->frame; + } + } + qsort((void *)currSlice->listX[0], list0idx, + sizeof(struct StorablePicture *), + compare_pic_by_poc_desc); + + /* get the backward reference picture + (POC>current POC) in list0; */ + list0idx_1 = list0idx; + for (i = 0; i < p_Dpb->ref_frames_in_buffer; i++) { + if ((p_Dpb->fs_ref[i]->is_used == 3) && + ((p_Dpb->fs_ref[i]->frame-> + used_for_reference) && + (!p_Dpb->fs_ref[i]->frame-> + is_long_term)) && + (currSlice->framepoc < + p_Dpb->fs_ref[i]->frame->poc)) { + currSlice-> + listX[0][list0idx++] = + p_Dpb->fs_ref[i]->frame; + } + } + qsort((void *)&currSlice->listX[0][list0idx_1], + list0idx - list0idx_1, + sizeof(struct StorablePicture *), + compare_pic_by_poc_asc); + + for (j = 0; j < list0idx_1; j++) { + currSlice-> + listX[1][list0idx - list0idx_1 + j] = + currSlice->listX[0][j]; + } + for (j = list0idx_1; j < list0idx; j++) { + currSlice->listX[1][j - list0idx_1] = + currSlice->listX[0][j]; + } + + currSlice->listXsize[0] = currSlice->listXsize[1] = + (char) list0idx; + CHECK_VALID(currSlice->listXsize[0], 0); + CHECK_VALID(currSlice->listXsize[1], 1); + + dpb_print(p_H264_Dpb->decoder_index, + PRINT_FLAG_DPB_DETAIL, + "listX[0] (PicNum): "); + for (i = 0; i < currSlice->listXsize[0]; i++) { + dpb_print(p_H264_Dpb->decoder_index, + PRINT_FLAG_DPB_DETAIL, "%d ", + currSlice->listX[0][i]->pic_num); + } + dpb_print(p_H264_Dpb->decoder_index, + PRINT_FLAG_DPB_DETAIL, "\n"); + dpb_print(p_H264_Dpb->decoder_index, + PRINT_FLAG_DPB_DETAIL, + "listX[1] (PicNum): "); + for (i = 0; i < currSlice->listXsize[1]; i++) { + dpb_print(p_H264_Dpb->decoder_index, + PRINT_FLAG_DPB_DETAIL, "%d ", + currSlice->listX[1][i]->pic_num); + } + dpb_print(p_H264_Dpb->decoder_index, + PRINT_FLAG_DPB_DETAIL, "\n"); + /* dpb_print(p_H264_Dpb->decoder_index, + PRINT_FLAG_DPB_DETAIL, + "currSlice->listX[0] currPoc=%d (Poc): ", + p_Vid->framepoc); + for (i=0; i<currSlice->listXsize[0]; i++) { + dpb_print(p_H264_Dpb->decoder_index, + PRINT_FLAG_DPB_DETAIL, + "%d ", currSlice->listX[0][i]->poc); + } + dpb_print(p_H264_Dpb->decoder_index, + PRINT_FLAG_DPB_DETAIL, "\n"); + dpb_print(p_H264_Dpb->decoder_index, + PRINT_FLAG_DPB_DETAIL, + "currSlice->listX[1] currPoc=%d (Poc): ", + p_Vid->framepoc); + for (i=0; i<currSlice->listXsize[1]; i++) { + dpb_print(p_H264_Dpb->decoder_index, + PRINT_FLAG_DPB_DETAIL, + "%d ", + currSlice->listX[1][i]->poc); + } + dpb_print(p_H264_Dpb->decoder_index, + PRINT_FLAG_DPB_DETAIL, "\n"); */ + + /* long term handling */ + for (i = 0; i < p_Dpb->ltref_frames_in_buffer; i++) { + if (p_Dpb->fs_ltref[i]->is_used == 3) { + if (p_Dpb->fs_ltref[i]->frame-> + is_long_term) { + currSlice-> + listX[0][list0idx] = + p_Dpb->fs_ltref[i]->frame; + currSlice-> + listX[1][list0idx++] = + p_Dpb->fs_ltref[i]->frame; + } + } + } + qsort((void *)&currSlice-> + listX[0][(short) currSlice->listXsize[0]], + list0idx - currSlice->listXsize[0], + sizeof(struct StorablePicture *), + compare_pic_by_lt_pic_num_asc); + qsort((void *)&currSlice-> + listX[1][(short) currSlice->listXsize[0]], + list0idx - currSlice->listXsize[0], + sizeof(struct StorablePicture *), + compare_pic_by_lt_pic_num_asc); + currSlice->listXsize[0] = currSlice->listXsize[1] = + (char) list0idx; + CHECK_VALID(currSlice->listXsize[0], 0); + CHECK_VALID(currSlice->listXsize[1], 1); + } else { +#if 0 + fs_list0 = calloc(p_Dpb->size, + sizeof(struct FrameStore *)); + if (NULL == fs_list0) + no_mem_exit("init_lists: fs_list0"); + fs_list1 = calloc(p_Dpb->size, + sizeof(struct FrameStore *)); + if (NULL == fs_list1) + no_mem_exit("init_lists: fs_list1"); + fs_listlt = calloc(p_Dpb->size, + sizeof(struct FrameStore *)); + if (NULL == fs_listlt) + no_mem_exit("init_lists: fs_listlt"); +#else + fs_list0 = &(p_Dpb->fs_list0[0]); + fs_list1 = &(p_Dpb->fs_list1[0]); + fs_listlt = &(p_Dpb->fs_listlt[0]); + +#endif + currSlice->listXsize[0] = 0; + currSlice->listXsize[1] = 1; + + for (i = 0; i < p_Dpb->ref_frames_in_buffer; i++) { + if (p_Dpb->fs_ref[i]->is_used) { + if (currSlice->ThisPOC >= + p_Dpb->fs_ref[i]->poc) { + fs_list0[list0idx++] = + p_Dpb->fs_ref[i]; + } + } + } + qsort((void *)fs_list0, list0idx, + sizeof(struct FrameStore *), + compare_fs_by_poc_desc); + list0idx_1 = list0idx; + for (i = 0; i < p_Dpb->ref_frames_in_buffer; i++) { + if (p_Dpb->fs_ref[i]->is_used) { + if (currSlice->ThisPOC < + p_Dpb->fs_ref[i]->poc) { + fs_list0[list0idx++] = + p_Dpb->fs_ref[i]; + } + } + } + qsort((void *)&fs_list0[list0idx_1], + list0idx - list0idx_1, + sizeof(struct FrameStore *), + compare_fs_by_poc_asc); + + for (j = 0; j < list0idx_1; j++) { + fs_list1[list0idx - list0idx_1 + j] = + fs_list0[j]; + } + for (j = list0idx_1; j < list0idx; j++) + fs_list1[j - list0idx_1] = fs_list0[j]; + + dpb_print(p_H264_Dpb->decoder_index, + PRINT_FLAG_DPB_DETAIL, + "fs_list0 currPoc=%d (Poc): ", + currSlice->ThisPOC); + for (i = 0; i < list0idx; i++) { + dpb_print(p_H264_Dpb->decoder_index, + PRINT_FLAG_DPB_DETAIL, "%d ", + fs_list0[i]->poc); + } + dpb_print(p_H264_Dpb->decoder_index, + PRINT_FLAG_DPB_DETAIL, "\n"); + dpb_print(p_H264_Dpb->decoder_index, + PRINT_FLAG_DPB_DETAIL, + "fs_list1 currPoc=%d (Poc): ", + currSlice->ThisPOC); + for (i = 0; i < list0idx; i++) { + dpb_print(p_H264_Dpb->decoder_index, + PRINT_FLAG_DPB_DETAIL, "%d ", + fs_list1[i]->poc); + } + dpb_print(p_H264_Dpb->decoder_index, + PRINT_FLAG_DPB_DETAIL, "\n"); + + currSlice->listXsize[0] = 0; + currSlice->listXsize[1] = 0; + gen_pic_list_from_frame_list(currSlice->structure, + fs_list0, list0idx, + currSlice->listX[0], + &currSlice->listXsize[0], 0); + gen_pic_list_from_frame_list(currSlice->structure, + fs_list1, list0idx, + currSlice->listX[1], + &currSlice->listXsize[1], 0); + + /* dpb_print(p_H264_Dpb->decoder_index, + PRINT_FLAG_DPB_DETAIL, + "currSlice->listX[0] currPoc=%d (Poc): ", + p_Vid->framepoc); + for (i=0; i<currSlice->listXsize[0]; i++) { + dpb_print(p_H264_Dpb->decoder_index, + PRINT_FLAG_DPB_DETAIL, "%d ", + currSlice->listX[0][i]->poc); + } + dpb_print(p_H264_Dpb->decoder_index, + PRINT_FLAG_DPB_DETAIL, "\n"); */ + /* dpb_print(p_H264_Dpb->decoder_index, + PRINT_FLAG_DPB_DETAIL, + "currSlice->listX[1] currPoc=%d (Poc): ", + p_Vid->framepoc); + for (i=0; i<currSlice->listXsize[1]; i++) { + dpb_print(p_H264_Dpb->decoder_index, + PRINT_FLAG_DPB_DETAIL, "%d ", + currSlice->listX[1][i]->poc); + } + dpb_print(p_H264_Dpb->decoder_index, + PRINT_FLAG_DPB_DETAIL, + "\n"); */ + + /* long term handling */ + for (i = 0; i < p_Dpb->ltref_frames_in_buffer; i++) + fs_listlt[listltidx++] = p_Dpb->fs_ltref[i]; + + qsort((void *)fs_listlt, listltidx, + sizeof(struct FrameStore *), + compare_fs_by_lt_pic_idx_asc); + + gen_pic_list_from_frame_list(currSlice->structure, + fs_listlt, listltidx, + currSlice->listX[0], + &currSlice->listXsize[0], 1); + gen_pic_list_from_frame_list(currSlice->structure, + fs_listlt, listltidx, + currSlice->listX[1], + &currSlice->listXsize[1], 1); + + /* free(fs_list0); */ + /* free(fs_list1); */ + /* free(fs_listlt); */ + } + } + + if ((currSlice->listXsize[0] == currSlice->listXsize[1]) && + (currSlice->listXsize[0] > 1)) { + /* check if lists are identical, + if yes swap first two elements of currSlice->listX[1] */ + int diff = 0; + for (j = 0; j < currSlice->listXsize[0]; j++) { + if (currSlice->listX[0][j] != + currSlice->listX[1][j]) { + diff = 1; + break; + } + } + if (!diff) { + struct StorablePicture *tmp_s = + currSlice->listX[1][0]; + currSlice->listX[1][0] = currSlice->listX[1][1]; + currSlice->listX[1][1] = tmp_s; + } + } + + /* set max size */ + currSlice->listXsize[0] = (char) imin(currSlice->listXsize[0], + currSlice->num_ref_idx_active[LIST_0]); + currSlice->listXsize[1] = (char) imin(currSlice->listXsize[1], + currSlice->num_ref_idx_active[LIST_1]); + CHECK_VALID(currSlice->listXsize[0], 0); + CHECK_VALID(currSlice->listXsize[1], 1); + + /* set the unused list entries to NULL */ + for (i = currSlice->listXsize[0]; i < (MAX_LIST_SIZE); i++) + currSlice->listX[0][i] = p_Vid->no_reference_picture; + for (i = currSlice->listXsize[1]; i < (MAX_LIST_SIZE); i++) + currSlice->listX[1][i] = p_Vid->no_reference_picture; + +#if PRINTREFLIST +#if (MVC_EXTENSION_ENABLE) + /* print out for h264_debug_flag purpose */ + if ((p_Vid->profile_idc == MVC_HIGH || + p_Vid->profile_idc == STEREO_HIGH) && + currSlice->current_slice_nr == 0) { + if ((currSlice->listXsize[0] > 0) || + (currSlice->listXsize[1] > 0)) + dpb_print(p_H264_Dpb->decoder_index, + PRINT_FLAG_DPB_DETAIL, "\n"); + if (currSlice->listXsize[0] > 0) { + dpb_print(p_H264_Dpb->decoder_index, + PRINT_FLAG_DPB_DETAIL, + " ** (CurViewID:%d %d) %s Ref Pic List 0 ****\n", + currSlice->view_id, + currSlice->ThisPOC, + currSlice->structure == FRAME ? "FRM" : + (currSlice->structure == TOP_FIELD ? + "TOP" : "BOT")); + for (i = 0; i < (unsigned int)(currSlice-> + listXsize[0]); i++) { /* ref list 0 */ + dpb_print(p_H264_Dpb->decoder_index, + PRINT_FLAG_DPB_DETAIL, + " %2d -> POC: %4d PicNum: %4d ViewID: %d\n", + i, + currSlice->listX[0][i]->poc, + currSlice->listX[0][i]->pic_num, + currSlice->listX[0][i]->view_id); + } + } + if (currSlice->listXsize[1] > 0) { + dpb_print(p_H264_Dpb->decoder_index, + PRINT_FLAG_DPB_DETAIL, + " ** (CurViewID:%d %d) %s Ref Pic List 1 ****\n", + currSlice->view_id, + currSlice->ThisPOC, + currSlice->structure == FRAME ? "FRM" : + (currSlice->structure == TOP_FIELD ? "TOP" : + "BOT")); + for (i = 0; i < (unsigned int)(currSlice-> + listXsize[1]); i++) { /* ref list 1 */ + dpb_print(p_H264_Dpb->decoder_index, + PRINT_FLAG_DPB_DETAIL, + " %2d -> POC: %4d PicNum: %4d ViewID: %d\n", + i, + currSlice->listX[1][i]->poc, + currSlice->listX[1][i]->pic_num, + currSlice->listX[1][i]->view_id); + } + } + } +#endif +#endif +} + +static struct StorablePicture *get_short_term_pic(struct Slice *currSlice, + struct DecodedPictureBuffer *p_Dpb, int picNum) +{ + unsigned i; + + for (i = 0; i < p_Dpb->ref_frames_in_buffer; i++) { + if (currSlice->structure == FRAME) { + if (p_Dpb->fs_ref[i]->is_reference == 3) + if ((!p_Dpb->fs_ref[i]->frame-> + is_long_term) && + (p_Dpb->fs_ref[i]->frame-> + pic_num == picNum)) + return p_Dpb->fs_ref[i]->frame; + } else { + if (p_Dpb->fs_ref[i]->is_reference & 1) + if ((!p_Dpb->fs_ref[i]->top_field-> + is_long_term) && + (p_Dpb->fs_ref[i]->top_field-> + pic_num == picNum)) + return p_Dpb->fs_ref[i]->top_field; + if (p_Dpb->fs_ref[i]->is_reference & 2) + if ((!p_Dpb->fs_ref[i]->bottom_field-> + is_long_term) && + (p_Dpb->fs_ref[i]->bottom_field-> + pic_num == picNum)) + return p_Dpb->fs_ref[i]->bottom_field; + } + } + + return currSlice->p_Vid->no_reference_picture; +} + + +static void reorder_short_term(struct Slice *currSlice, int cur_list, + int num_ref_idx_lX_active_minus1, + int picNumLX, int *refIdxLX) +{ + struct h264_dpb_stru *p_H264_Dpb = container_of(currSlice->p_Vid, + struct h264_dpb_stru, mVideo); + + struct StorablePicture **RefPicListX = currSlice->listX[cur_list]; + int cIdx, nIdx; + + struct StorablePicture *picLX; + + picLX = get_short_term_pic(currSlice, currSlice->p_Dpb, picNumLX); + + for (cIdx = num_ref_idx_lX_active_minus1 + 1; cIdx > *refIdxLX; + cIdx--) { + dpb_print(p_H264_Dpb->decoder_index, PRINT_FLAG_DPB_DETAIL, + "%s: RefPicListX[ %d ] = RefPicListX[ %d ]\n", + __func__, cIdx, cIdx - 1); + RefPicListX[cIdx] = RefPicListX[cIdx - 1]; + } + + dpb_print(p_H264_Dpb->decoder_index, PRINT_FLAG_DPB_DETAIL, + "%s: RefPicListX[ %d ] = pic %x (%d)\n", __func__, + *refIdxLX, picLX, picNumLX); + + RefPicListX[(*refIdxLX)++] = picLX; + + nIdx = *refIdxLX; + + for (cIdx = *refIdxLX; cIdx <= num_ref_idx_lX_active_minus1 + 1; + cIdx++) { + if (RefPicListX[cIdx]) + if ((RefPicListX[cIdx]->is_long_term) || + (RefPicListX[cIdx]->pic_num != picNumLX)) { + dpb_print(p_H264_Dpb->decoder_index, + PRINT_FLAG_DPB_DETAIL, + "%s: RefPicListX[ %d ] = RefPicListX[ %d ]\n", + __func__, nIdx, cIdx); + RefPicListX[nIdx++] = RefPicListX[cIdx]; + } + } +} + + +static struct StorablePicture *get_long_term_pic(struct Slice *currSlice, + struct DecodedPictureBuffer *p_Dpb, int LongtermPicNum) +{ + unsigned int i; + + for (i = 0; i < p_Dpb->ltref_frames_in_buffer; i++) { + if (currSlice->structure == FRAME) { + if (p_Dpb->fs_ltref[i]->is_reference == 3) + if ((p_Dpb->fs_ltref[i]->frame-> + is_long_term) && + (p_Dpb->fs_ltref[i]->frame-> + long_term_pic_num == + LongtermPicNum)) + return p_Dpb->fs_ltref[i]->frame; + } else { + if (p_Dpb->fs_ltref[i]->is_reference & 1) + if ((p_Dpb->fs_ltref[i]->top_field-> + is_long_term) && + (p_Dpb->fs_ltref[i]->top_field-> + long_term_pic_num == LongtermPicNum)) + return p_Dpb->fs_ltref[i]->top_field; + if (p_Dpb->fs_ltref[i]->is_reference & 2) + if ((p_Dpb->fs_ltref[i]->bottom_field-> + is_long_term) && + (p_Dpb->fs_ltref[i]->bottom_field-> + long_term_pic_num == + LongtermPicNum)) + return p_Dpb->fs_ltref[i]-> + bottom_field; + } + } + return NULL; +} + +/*! + ************************************************************************ + * \brief + * Reordering process for long-term reference pictures + * + ************************************************************************ + */ +static void reorder_long_term(struct Slice *currSlice, + struct StorablePicture **RefPicListX, + int num_ref_idx_lX_active_minus1, + int LongTermPicNum, int *refIdxLX) +{ + int cIdx, nIdx; + + struct StorablePicture *picLX; + + picLX = get_long_term_pic(currSlice, currSlice->p_Dpb, LongTermPicNum); + + for (cIdx = num_ref_idx_lX_active_minus1 + 1; cIdx > *refIdxLX; cIdx--) + RefPicListX[cIdx] = RefPicListX[cIdx - 1]; + + RefPicListX[(*refIdxLX)++] = picLX; + + nIdx = *refIdxLX; + + for (cIdx = *refIdxLX; cIdx <= num_ref_idx_lX_active_minus1 + 1; + cIdx++) { + if (RefPicListX[cIdx]) { + if ((!RefPicListX[cIdx]->is_long_term) || + (RefPicListX[cIdx]->long_term_pic_num != + LongTermPicNum)) + RefPicListX[nIdx++] = RefPicListX[cIdx]; + } + } +} + +static void reorder_ref_pic_list(struct Slice *currSlice, int cur_list) +{ + int *modification_of_pic_nums_idc = + currSlice->modification_of_pic_nums_idc[cur_list]; + int *abs_diff_pic_num_minus1 = + currSlice->abs_diff_pic_num_minus1[cur_list]; + int *long_term_pic_idx = currSlice->long_term_pic_idx[cur_list]; + int num_ref_idx_lX_active_minus1 = + currSlice->num_ref_idx_active[cur_list] - 1; + + struct VideoParameters *p_Vid = currSlice->p_Vid; + int i; + + int maxPicNum, currPicNum, picNumLXNoWrap, picNumLXPred, picNumLX; + int refIdxLX = 0; + + if (currSlice->structure == FRAME) { + maxPicNum = p_Vid->max_frame_num; + currPicNum = currSlice->frame_num; + } else { + maxPicNum = 2 * p_Vid->max_frame_num; + currPicNum = 2 * currSlice->frame_num + 1; + } + + picNumLXPred = currPicNum; + + for (i = 0; i < REORDERING_COMMAND_MAX_SIZE && + modification_of_pic_nums_idc[i] != 3; i++) { + if (modification_of_pic_nums_idc[i] > 3) { + struct h264_dpb_stru *p_H264_Dpb = + container_of(p_Vid, struct h264_dpb_stru, mVideo); + dpb_print(p_H264_Dpb->decoder_index, + PRINT_FLAG_ERROR, + "error, Invalid modification_of_pic_nums_idc command\n"); + /*h264_debug_flag = 0x1f;*/ + break; + } + if (modification_of_pic_nums_idc[i] < 2) { + if (modification_of_pic_nums_idc[i] == 0) { + if (picNumLXPred - (abs_diff_pic_num_minus1[i] + + 1) < 0) + picNumLXNoWrap = picNumLXPred - + (abs_diff_pic_num_minus1[i] + 1) + + maxPicNum; + else + picNumLXNoWrap = picNumLXPred - + (abs_diff_pic_num_minus1[i] + 1); + } else { /* (modification_of_pic_nums_idc[i] == 1) */ + if (picNumLXPred + (abs_diff_pic_num_minus1[i] + + 1) >= maxPicNum) + picNumLXNoWrap = picNumLXPred + + (abs_diff_pic_num_minus1[i] + 1) - + maxPicNum; + else + picNumLXNoWrap = picNumLXPred + + (abs_diff_pic_num_minus1[i] + 1); + } + picNumLXPred = picNumLXNoWrap; + + if (picNumLXNoWrap > currPicNum) + picNumLX = picNumLXNoWrap - maxPicNum; + else + picNumLX = picNumLXNoWrap; + +#if (MVC_EXTENSION_ENABLE) + reorder_short_term(currSlice, cur_list, + num_ref_idx_lX_active_minus1, picNumLX, + &refIdxLX, -1); +#else + reorder_short_term(currSlice, cur_list, + num_ref_idx_lX_active_minus1, picNumLX, + &refIdxLX); +#endif + } else { /* (modification_of_pic_nums_idc[i] == 2) */ +#if (MVC_EXTENSION_ENABLE) + reorder_long_term(currSlice, currSlice->listX[cur_list], + num_ref_idx_lX_active_minus1, + long_term_pic_idx[i], &refIdxLX, -1); +#else + reorder_long_term(currSlice, currSlice->listX[cur_list], + num_ref_idx_lX_active_minus1, + long_term_pic_idx[i], &refIdxLX); +#endif + } + + } + /* that's a definition */ + currSlice->listXsize[cur_list] = + (char)(num_ref_idx_lX_active_minus1 + 1); +} + + +static void reorder_lists(struct Slice *currSlice) +{ + struct VideoParameters *p_Vid = currSlice->p_Vid; + struct h264_dpb_stru *p_H264_Dpb = container_of(p_Vid, + struct h264_dpb_stru, mVideo); + int i; + if ((currSlice->slice_type != I_SLICE) && + (currSlice->slice_type != SI_SLICE)) { + if (currSlice->ref_pic_list_reordering_flag[LIST_0]) + reorder_ref_pic_list(currSlice, LIST_0); + if (p_Vid->no_reference_picture == + currSlice-> + listX[0][currSlice->num_ref_idx_active[LIST_0] - 1]) { + if (p_Vid->non_conforming_stream) + dpb_print(p_H264_Dpb->decoder_index, + PRINT_FLAG_DPB_DETAIL, + "RefPicList0[ %d ] is equal to 'no reference picture'\n", + currSlice-> + num_ref_idx_active[LIST_0] - 1); + else + dpb_print(p_H264_Dpb->decoder_index, + PRINT_FLAG_DPB_DETAIL, + "RefPicList0 [ num_ref_idx_l0_active_minus1 ] is equal to 'no reference picture', invalid bitstream %d\n", + 500); + } + /* that's a definition */ + currSlice->listXsize[0] = + (char)currSlice->num_ref_idx_active[LIST_0]; + CHECK_VALID(currSlice->listXsize[0], 0); + if (h264_debug_flag & PRINT_FLAG_DPB_DETAIL) { + dpb_print(p_H264_Dpb->decoder_index, + PRINT_FLAG_DPB_DETAIL, + "listX[0] reorder (PicNum): "); + for (i = 0; i < currSlice->listXsize[0]; i++) { + dpb_print(p_H264_Dpb->decoder_index, + PRINT_FLAG_DPB_DETAIL, "%d ", + currSlice->listX[0][i]->pic_num); + } + dpb_print(p_H264_Dpb->decoder_index, + PRINT_FLAG_DPB_DETAIL, "\n"); + } + } + + if (currSlice->slice_type == B_SLICE) { + if (currSlice->ref_pic_list_reordering_flag[LIST_1]) + reorder_ref_pic_list(currSlice, LIST_1); + if (p_Vid->no_reference_picture == + currSlice->listX[1][currSlice-> + num_ref_idx_active[LIST_1] - 1]) { + if (p_Vid->non_conforming_stream) + dpb_print(p_H264_Dpb->decoder_index, + PRINT_FLAG_DPB_DETAIL, + "RefPicList1[ %d ] is equal to 'no reference picture'\n", + currSlice-> + num_ref_idx_active[LIST_1] - 1); + else + dpb_print(p_H264_Dpb->decoder_index, + PRINT_FLAG_DPB_DETAIL, + "RefPicList1 [ num_ref_idx_l1_active_minus1 ] is equal to 'no reference picture', invalid bitstream %d\n", + 500); + } + /* that's a definition */ + currSlice->listXsize[1] = + (char)currSlice->num_ref_idx_active[LIST_1]; + if (h264_debug_flag & PRINT_FLAG_DPB_DETAIL) { + dpb_print(p_H264_Dpb->decoder_index, + PRINT_FLAG_DPB_DETAIL, + "listX[1] reorder (PicNum): "); + for (i = 0; i < currSlice->listXsize[1]; i++) { + dpb_print(p_H264_Dpb->decoder_index, + PRINT_FLAG_DPB_DETAIL, "%d ", + currSlice->listX[1][i]->pic_num); + } + dpb_print(p_H264_Dpb->decoder_index, + PRINT_FLAG_DPB_DETAIL, "\n"); + } + } + + /* free_ref_pic_list_reordering_buffer(currSlice); */ + + if (currSlice->slice_type == P_SLICE) { +#if PRINTREFLIST + unsigned int i; +#if (MVC_EXTENSION_ENABLE) + /* print out for h264_debug_flag purpose */ + if ((p_Vid->profile_idc == MVC_HIGH || + p_Vid->profile_idc == STEREO_HIGH) && + currSlice->current_slice_nr == 0) { + if (currSlice->listXsize[0] > 0 + && (h264_debug_flag & PRINT_FLAG_DPB_DETAIL)) { + dpb_print(p_H264_Dpb->decoder_index, + PRINT_FLAG_DPB_DETAIL, "\n"); + dpb_print(p_H264_Dpb->decoder_index, + PRINT_FLAG_DPB_DETAIL, + " ** (FinalViewID:%d) %s Ref Pic List 0 ****\n", + currSlice->view_id, + currSlice->structure == FRAME ? + "FRM" : + (currSlice->structure == TOP_FIELD ? + "TOP" : "BOT")); + for (i = 0; i < (unsigned int)(currSlice-> + listXsize[0]); i++) { /* ref list 0 */ + dpb_print(p_H264_Dpb->decoder_index, + PRINT_FLAG_DPB_DETAIL, + " %2d -> POC: %4d PicNum: %4d ViewID: %d\n", + i, + currSlice->listX[0][i]->poc, + currSlice->listX[0][i]-> + pic_num, + currSlice->listX[0][i]-> + view_id); + } + } + } +#endif +#endif + } else if (currSlice->slice_type == B_SLICE) { +#if PRINTREFLIST + unsigned int i; +#if (MVC_EXTENSION_ENABLE) + /* print out for h264_debug_flag purpose */ + if ((p_Vid->profile_idc == MVC_HIGH || + p_Vid->profile_idc == STEREO_HIGH) && + currSlice->current_slice_nr == 0) { + if ((currSlice->listXsize[0] > 0) || + (currSlice->listXsize[1] > 0)) + dpb_print(p_H264_Dpb->decoder_index, + PRINT_FLAG_DPB_DETAIL, "\n"); + if (currSlice->listXsize[0] > 0 + && (h264_debug_flag & PRINT_FLAG_DPB_DETAIL)) { + dpb_print(p_H264_Dpb->decoder_index, + PRINT_FLAG_DPB_DETAIL, + " ** (FinalViewID:%d) %s Ref Pic List 0 ****\n", + currSlice->view_id, + currSlice->structure == FRAME ? + "FRM" : + (currSlice->structure == TOP_FIELD ? + "TOP" : "BOT")); + for (i = 0; i < (unsigned int)(currSlice-> + listXsize[0]); i++) { /* ref list 0 */ + dpb_print(p_H264_Dpb->decoder_index, + PRINT_FLAG_DPB_DETAIL, + " %2d -> POC: %4d PicNum: %4d ViewID: %d\n", + i, + currSlice->listX[0][i]->poc, + currSlice->listX[0][i]-> + pic_num, + currSlice->listX[0][i]-> + view_id); + } + } + if (currSlice->listXsize[1] > 0 + && (h264_debug_flag & PRINT_FLAG_DPB_DETAIL)) { + dpb_print(p_H264_Dpb->decoder_index, + PRINT_FLAG_DPB_DETAIL, + " ** (FinalViewID:%d) %s Ref Pic List 1 ****\n", + currSlice->view_id, + currSlice->structure == FRAME ? + "FRM" : + (currSlice->structure == TOP_FIELD ? + "TOP" : "BOT")); + for (i = 0; i < (unsigned int)(currSlice-> + listXsize[1]); i++) { /* ref list 1 */ + dpb_print(p_H264_Dpb->decoder_index, + PRINT_FLAG_DPB_DETAIL, + " %2d -> POC: %4d PicNum: %4d ViewID: %d\n", + i, + currSlice->listX[1][i]->poc, + currSlice->listX[1][i]-> + pic_num, + currSlice->listX[1][i]-> + view_id); + } + } + } +#endif + +#endif + } +} + +void init_colocate_buf(struct h264_dpb_stru *p_H264_Dpb, int count) +{ + p_H264_Dpb->colocated_buf_map = 0; + p_H264_Dpb->colocated_buf_count = count; +} + +int allocate_colocate_buf(struct h264_dpb_stru *p_H264_Dpb) +{ + int i; + for (i = 0; i < p_H264_Dpb->colocated_buf_count; i++) { + if (((p_H264_Dpb->colocated_buf_map >> i) & 0x1) == 0) { + p_H264_Dpb->colocated_buf_map |= (1 << i); + break; + } + } + if (i == p_H264_Dpb->colocated_buf_count) + i = -1; + return i; +} + +int release_colocate_buf(struct h264_dpb_stru *p_H264_Dpb, int index) +{ + if (index >= 0) { + if (index >= p_H264_Dpb->colocated_buf_count) { + dpb_print(p_H264_Dpb->decoder_index, PRINT_FLAG_ERROR, + "%s error, index %d is bigger than buf count %d\n", + __func__, index, + p_H264_Dpb->colocated_buf_count); + } else { + if (((p_H264_Dpb->colocated_buf_map >> + index) & 0x1) == 0x1) { + p_H264_Dpb->colocated_buf_map &= + (~(1 << index)); + } else { + dpb_print(p_H264_Dpb->decoder_index, + PRINT_FLAG_ERROR, + "%s error, index %d is not allocated\n", + __func__, index); + } + } + } + return 0; +} + +void set_frame_output_flag(struct h264_dpb_stru *p_H264_Dpb, int index) +{ + struct DecodedPictureBuffer *p_Dpb = &p_H264_Dpb->mDPB; + p_H264_Dpb->mFrameStore[index].is_output = 1; + p_H264_Dpb->mFrameStore[index].pre_output = 0; + dump_dpb(p_Dpb); +} + +#if 0 +void init_old_slice(OldSliceParams *p_old_slice) +{ + p_old_slice->field_pic_flag = 0; + p_old_slice->pps_id = INT_MAX; + p_old_slice->frame_num = INT_MAX; + p_old_slice->nal_ref_idc = INT_MAX; + p_old_slice->idr_flag = FALSE; + + p_old_slice->pic_oder_cnt_lsb = UINT_MAX; + p_old_slice->delta_pic_oder_cnt_bottom = INT_MAX; + + p_old_slice->delta_pic_order_cnt[0] = INT_MAX; + p_old_slice->delta_pic_order_cnt[1] = INT_MAX; +} + + +void copy_slice_info(struct Slice *currSlice, OldSliceParams *p_old_slice) +{ + struct VideoParameters *p_Vid = currSlice->p_Vid; + + p_old_slice->pps_id = currSlice->pic_parameter_set_id; + p_old_slice->frame_num = currSlice->frame_num; + /* p_Vid->frame_num; */ + p_old_slice->field_pic_flag = + currSlice->field_pic_flag; + /* p_Vid->field_pic_flag; */ + + if (currSlice->field_pic_flag) + p_old_slice->bottom_field_flag = currSlice->bottom_field_flag; + + p_old_slice->nal_ref_idc = currSlice->nal_reference_idc; + p_old_slice->idr_flag = (byte) currSlice->idr_flag; + + if (currSlice->idr_flag) + p_old_slice->idr_pic_id = currSlice->idr_pic_id; + + if (p_Vid->active_sps->pic_order_cnt_type == 0) { + p_old_slice->pic_oder_cnt_lsb = + currSlice->pic_order_cnt_lsb; + p_old_slice->delta_pic_oder_cnt_bottom = + currSlice->delta_pic_order_cnt_bottom; + } + + if (p_Vid->active_sps->pic_order_cnt_type == 1) { + p_old_slice->delta_pic_order_cnt[0] = + currSlice->delta_pic_order_cnt[0]; + p_old_slice->delta_pic_order_cnt[1] = + currSlice->delta_pic_order_cnt[1]; + } +#if (MVC_EXTENSION_ENABLE) + p_old_slice->view_id = currSlice->view_id; + p_old_slice->inter_view_flag = currSlice->inter_view_flag; + p_old_slice->anchor_pic_flag = currSlice->anchor_pic_flag; +#endif + p_old_slice->layer_id = currSlice->layer_id; +} + +int is_new_picture(StorablePicture *dec_picture, struct Slice *currSlice, + OldSliceParams *p_old_slice) +{ + struct VideoParameters *p_Vid = currSlice->p_Vid; + + int result = 0; + + result |= (NULL == dec_picture); + + result |= (p_old_slice->pps_id != currSlice->pic_parameter_set_id); + + result |= (p_old_slice->frame_num != currSlice->frame_num); + + result |= (p_old_slice->field_pic_flag != currSlice->field_pic_flag); + + if (currSlice->field_pic_flag && p_old_slice->field_pic_flag) { + result |= (p_old_slice->bottom_field_flag != + currSlice->bottom_field_flag); + } + + result |= (p_old_slice->nal_ref_idc != + currSlice->nal_reference_idc) && + ((p_old_slice->nal_ref_idc == 0) || + (currSlice->nal_reference_idc == 0)); + result |= (p_old_slice->idr_flag != currSlice->idr_flag); + + if (currSlice->idr_flag && p_old_slice->idr_flag) + result |= (p_old_slice->idr_pic_id != currSlice->idr_pic_id); + + if (p_Vid->active_sps->pic_order_cnt_type == 0) { + result |= (p_old_slice->pic_oder_cnt_lsb != + currSlice->pic_order_cnt_lsb); + if (p_Vid->active_pps-> + bottom_field_pic_order_in_frame_present_flag == 1 && + !currSlice->field_pic_flag) { + result |= (p_old_slice->delta_pic_oder_cnt_bottom != + currSlice->delta_pic_order_cnt_bottom); + } + } + + if (p_Vid->active_sps->pic_order_cnt_type == 1) { + if (!p_Vid->active_sps->delta_pic_order_always_zero_flag) { + result |= (p_old_slice->delta_pic_order_cnt[0] != + currSlice->delta_pic_order_cnt[0]); + if (p_Vid->active_pps-> + bottom_field_pic_order_in_frame_present_flag == 1 && + !currSlice->field_pic_flag) { + result |= (p_old_slice-> + delta_pic_order_cnt[1] != + currSlice->delta_pic_order_cnt[1]); + } + } + } + +#if (MVC_EXTENSION_ENABLE) + result |= (currSlice->view_id != p_old_slice->view_id); + result |= (currSlice->inter_view_flag != p_old_slice->inter_view_flag); + result |= (currSlice->anchor_pic_flag != p_old_slice->anchor_pic_flag); +#endif + result |= (currSlice->layer_id != p_old_slice->layer_id); + return result; +} +#else +int is_new_picture(struct StorablePicture *dec_picture, + struct h264_dpb_stru *p_H264_Dpb, + struct OldSliceParams *p_old_slice) +{ + int ret = 0; + if (p_H264_Dpb->dpb_param.l.data[FIRST_MB_IN_SLICE] == 0) + ret = 1; + return ret; +} + +#endif + +int remove_picture(struct h264_dpb_stru *p_H264_Dpb, + struct StorablePicture *pic) +{ + struct DecodedPictureBuffer *p_Dpb = &p_H264_Dpb->mDPB; + if (p_Dpb->last_picture == NULL) { + if (pic->colocated_buf_index >= 0) { + release_colocate_buf(p_H264_Dpb, + pic->colocated_buf_index); + pic->colocated_buf_index = -1; + } + release_buf_spec_num(p_H264_Dpb->vdec, pic->buf_spec_num); + } + free_picture(p_H264_Dpb, pic); + return 0; +} + +static void check_frame_store_same_pic_num(struct DecodedPictureBuffer *p_Dpb, + struct StorablePicture *p, struct Slice *currSlice) +{ + if (p_Dpb->last_picture) { + if ((int)p_Dpb->last_picture->frame_num == p->pic_num) { + if (((p->structure == TOP_FIELD) && + (p_Dpb->last_picture->is_used == 2)) || + ((p->structure == BOTTOM_FIELD) && + (p_Dpb->last_picture->is_used == 1))) { + if ((p->used_for_reference && + (p_Dpb->last_picture-> + is_orig_reference != 0)) || + (!p->used_for_reference && + (p_Dpb->last_picture-> + is_orig_reference == 0))) { + p->buf_spec_num = + p_Dpb->last_picture-> + buf_spec_num; + p->colocated_buf_index = p_Dpb-> + last_picture-> + colocated_buf_index; + if (currSlice->structure == + TOP_FIELD) { + p->bottom_poc = + p_Dpb->last_picture-> + bottom_field->poc; + } else { + p->top_poc = + p_Dpb->last_picture-> + top_field->poc; + } + p->frame_poc = imin(p->bottom_poc, + p->top_poc); + } + } + } + } +} + +int h264_slice_header_process(struct h264_dpb_stru *p_H264_Dpb) +{ + + int new_pic_flag = 0; + struct Slice *currSlice = &p_H264_Dpb->mSlice; + struct VideoParameters *p_Vid = &p_H264_Dpb->mVideo; +#if 0 + new_pic_flag = is_new_picture(p_H264_Dpb->mVideo.dec_picture, + p_H264_Dpb, + &p_H264_Dpb->mVideo.old_slice); + + if (new_pic_flag) { /* new picture */ + if (p_H264_Dpb->mVideo.dec_picture) { + store_picture_in_dpb(p_H264_Dpb, + p_H264_Dpb->mVideo.dec_picture); + /* dump_dpb(&p_H264_Dpb->mDPB); */ + } + } +#else + new_pic_flag = (p_H264_Dpb->mVideo.dec_picture == NULL); +#endif + + slice_prepare(p_H264_Dpb, &p_H264_Dpb->mDPB, &p_H264_Dpb->mVideo, + &p_H264_Dpb->mSPS, &p_H264_Dpb->mSlice); + + /* if (p_Vid->active_sps != sps) { */ + if (p_H264_Dpb->mDPB.init_done == 0) { + /*init_global_buffers(p_Vid, 0); + + if (!p_Vid->no_output_of_prior_pics_flag) + { + flush_dpb(p_Vid->p_Dpb_layer[0]); + } + init_dpb(p_Vid, p_Vid->p_Dpb_layer[0], 0); + */ + init_dpb(p_H264_Dpb, 0); + } + + + if (new_pic_flag) { /* new picture */ + dpb_print(p_H264_Dpb->decoder_index, PRINT_FLAG_DPB_DETAIL, + "check frame_num gap: cur frame_num %d pre_frame_num %d max_frmae_num %d\r\n", + currSlice->frame_num, + p_Vid->pre_frame_num, + p_Vid->max_frame_num); + if (p_Vid->recovery_point == 0 && + currSlice->frame_num != p_Vid->pre_frame_num && + currSlice->frame_num != + (p_Vid->pre_frame_num + 1) % p_Vid->max_frame_num) { + /*if (active_sps-> + gaps_in_frame_num_value_allowed_flag + == 0) { + error("An unintentional + loss of pictures occurs! Exit\n", + 100); + } + if (p_Vid->conceal_mode == 0)*/ + fill_frame_num_gap(p_Vid, currSlice); + } + + if (currSlice->nal_reference_idc) { + dpb_print(p_H264_Dpb->decoder_index, + PRINT_FLAG_DPB_DETAIL, + "nal_reference_idc not 0, set pre_frame_num(%d) to frame_num (%d)\n", + p_Vid->pre_frame_num, currSlice->frame_num); + p_Vid->pre_frame_num = currSlice->frame_num; + } + + decode_poc(&p_H264_Dpb->mVideo, &p_H264_Dpb->mSlice); + p_H264_Dpb->mVideo.dec_picture = get_new_pic(p_H264_Dpb, + p_H264_Dpb->mSlice.structure, + /*p_Vid->width, p_Vid->height, + p_Vid->width_cr, + p_Vid->height_cr,*/ + 1); + if (p_H264_Dpb->mVideo.dec_picture) { + struct DecodedPictureBuffer *p_Dpb = + &p_H264_Dpb->mDPB; + struct StorablePicture *p = + p_H264_Dpb->mVideo.dec_picture; + init_picture(p_H264_Dpb, &p_H264_Dpb->mSlice, + p_H264_Dpb->mVideo.dec_picture); +#if 1 + /* rain */ + p_H264_Dpb->mVideo.dec_picture->offset_delimiter_lo = + p_H264_Dpb->dpb_param.l.data[OFFSET_DELIMITER_LO]; + p_H264_Dpb->mVideo.dec_picture->offset_delimiter_hi = + p_H264_Dpb->dpb_param.l.data[OFFSET_DELIMITER_HI]; + + p_H264_Dpb->mVideo.dec_picture->buf_spec_num = -1; + p_H264_Dpb->mVideo.dec_picture-> + colocated_buf_index = -1; + update_pic_num(&p_H264_Dpb->mSlice); + + if ((currSlice->structure == TOP_FIELD) || + (currSlice->structure == BOTTOM_FIELD)) { + /* check for frame store with same + pic_number */ + check_frame_store_same_pic_num(p_Dpb, p, + currSlice); + } + + if (p_H264_Dpb->mVideo.dec_picture->buf_spec_num == + -1) { + p_H264_Dpb->mVideo.dec_picture->buf_spec_num = + get_free_buf_idx(p_H264_Dpb->vdec); + if (p_H264_Dpb->mVideo.dec_picture-> + used_for_reference) { + p_H264_Dpb->mVideo.dec_picture-> + colocated_buf_index = + allocate_colocate_buf( + p_H264_Dpb); + } + } +#endif + } + } + + if (p_H264_Dpb->mSlice.slice_type == P_SLICE) + init_lists_p_slice(&p_H264_Dpb->mSlice); + else if (p_H264_Dpb->mSlice.slice_type == B_SLICE) + init_lists_b_slice(&p_H264_Dpb->mSlice); + else + init_lists_i_slice(&p_H264_Dpb->mSlice); + + reorder_lists(&p_H264_Dpb->mSlice); + + if (p_H264_Dpb->mSlice.structure == FRAME) + init_mbaff_lists(&p_H264_Dpb->mVideo, &p_H264_Dpb->mSlice); + + if (new_pic_flag) + return 1; + + return 0; +} diff --git a/drivers/frame_provider/decoder/h264_multi/h264_dpb.h b/drivers/frame_provider/decoder/h264_multi/h264_dpb.h new file mode 100644 index 0000000..e58f084 --- a/dev/null +++ b/drivers/frame_provider/decoder/h264_multi/h264_dpb.h @@ -0,0 +1,788 @@ +#ifndef H264_DPB_H_ +#define H264_DPB_H_ + +#define ERROR_CHECK + +#define OUTPUT_BUFFER_IN_C + +#define PRINT_FLAG_ERROR 0x0 +#define PRINT_FLAG_DPB 0X0001 +#define PRINT_FLAG_DPB_DETAIL 0x0002 +#define PRINT_FLAG_DUMP_DPB 0x0004 +#define PRINT_FLAG_UCODE_EVT 0x0008 +#define PRINT_FLAG_VDEC_STATUS 0x0010 +#define PRINT_FLAG_VDEC_DETAIL 0x0020 +#define PRINT_FLAG_UCODE_DBG 0x0040 +#define PRINT_FLAG_TIME_STAMP 0x0080 +#define PRINT_FLAG_RUN_SCHEDULE 0x0100 +#define PRINT_FLAG_DEBUG_POC 0x0200 +#define PRINT_FLAG_VDEC_DATA 0x0400 +#define DISABLE_ERROR_HANDLE 0x10000 +#define OUTPUT_CURRENT_BUF 0x20000 +#define ONLY_RESET_AT_START 0x40000 +#define LOAD_UCODE_ALWAYS 0x80000 +#define FORCE_NO_SLICE 0x100000 +#define REINIT_DPB_TEST 0x200000 + + +#define MVC_EXTENSION_ENABLE 0 +#define PRINTREFLIST 0 + +#define MAX_LIST_SIZE 33 + +#define FALSE 0 + +#define H264_SLICE_HEAD_DONE 0x01 +#define H264_PIC_DATA_DONE 0x02 +/*#define H264_SPS_DONE 0x03*/ +/*#define H264_PPS_DONE 0x04*/ +/*#define H264_SLICE_DATA_DONE 0x05*/ +/*#define H264_DATA_END 0x06*/ + +#define H264_CONFIG_REQUEST 0x11 +#define H264_DATA_REQUEST 0x12 + +#define H264_DECODE_BUFEMPTY 0x20 +#define H264_DECODE_TIMEOUT 0x21 +#define H264_SEARCH_BUFEMPTY 0x22 + /* 0x8x, search state*/ +#define H264_STATE_SEARCH_AFTER_SPS 0x80 +#define H264_STATE_SEARCH_AFTER_PPS 0x81 +#define H264_STATE_PARSE_SLICE_HEAD 0x82 +#define H264_STATE_SEARCH_HEAD 0x83 + /**/ +#define H264_ACTION_SEARCH_HEAD 0xf0 +#define H264_ACTION_DECODE_SLICE 0xf1 +#define H264_ACTION_CONFIG_DONE 0xf2 +#define H264_ACTION_DECODE_NEWPIC 0xf3 +#define H264_ACTION_DECODE_START 0xff + +#define RPM_BEGIN 0x0 +#define RPM_END 0x400 + +#define val(s) (s[0]|(s[1]<<16)) + +#define FRAME_IN_DPB 24 +#define DPB_OFFSET 0x100 +#define MMCO_OFFSET 0x200 +union param { +#define H_TIME_STAMP_START 0X00 +#define H_TIME_STAMP_END 0X17 +#define PTS_ZERO_0 0X18 +#define PTS_ZERO_1 0X19 +#define FIXED_FRAME_RATE_FLAG 0X1A + +#define OFFSET_DELIMITER_LO 0x2f +#define OFFSET_DELIMITER_HI 0x30 + + +#define SLICE_IPONLY_BREAK 0X5C +#define PREV_MAX_REFERENCE_FRAME_NUM 0X5D +#define EOS 0X5E +#define FRAME_PACKING_TYPE 0X5F +#define OLD_POC_PAR_1 0X60 +#define OLD_POC_PAR_2 0X61 +#define PREV_MBX 0X62 +#define PREV_MBY 0X63 +#define ERROR_SKIP_MB_NUM 0X64 +#define ERROR_MB_STATUS 0X65 +#define L0_PIC0_STATUS 0X66 +#define TIMEOUT_COUNTER 0X67 +#define BUFFER_SIZE 0X68 +#define BUFFER_SIZE_HI 0X69 +#define CROPPING_LEFT_RIGHT 0X6A +#define CROPPING_TOP_BOTTOM 0X6B +#define POC_SELECT_NEED_SWAP 0X6C +#define POC_SELECT_SWAP 0X6D +#define MAX_BUFFER_FRAME 0X6E + +#define NON_CONFORMING_STREAM 0X70 +#define RECOVERY_POINT 0X71 +#define POST_CANVAS 0X72 +#define POST_CANVAS_H 0X73 +#define SKIP_PIC_COUNT 0X74 +#define TARGET_NUM_SCALING_LIST 0X75 +#define FF_POST_ONE_FRAME 0X76 +#define PREVIOUS_BIT_CNT 0X77 +#define MB_NOT_SHIFT_COUNT 0X78 +#define PIC_STATUS 0X79 +#define FRAME_COUNTER 0X7A +#define NEW_SLICE_TYPE 0X7B +#define NEW_PICTURE_STRUCTURE 0X7C +#define NEW_FRAME_NUM 0X7D +#define NEW_IDR_PIC_ID 0X7E +#define IDR_PIC_ID 0X7F + +/* h264 LOCAL */ +#define NAL_UNIT_TYPE 0X80 +#define NAL_REF_IDC 0X81 +#define SLICE_TYPE 0X82 +#define LOG2_MAX_FRAME_NUM 0X83 +#define FRAME_MBS_ONLY_FLAG 0X84 +#define PIC_ORDER_CNT_TYPE 0X85 +#define LOG2_MAX_PIC_ORDER_CNT_LSB 0X86 +#define PIC_ORDER_PRESENT_FLAG 0X87 +#define REDUNDANT_PIC_CNT_PRESENT_FLAG 0X88 +#define PIC_INIT_QP_MINUS26 0X89 +#define DEBLOCKING_FILTER_CONTROL_PRESENT_FLAG 0X8A +#define NUM_SLICE_GROUPS_MINUS1 0X8B +#define MODE_8X8_FLAGS 0X8C +#define ENTROPY_CODING_MODE_FLAG 0X8D +#define SLICE_QUANT 0X8E +#define TOTAL_MB_HEIGHT 0X8F +#define PICTURE_STRUCTURE 0X90 +#define TOP_INTRA_TYPE 0X91 +#define RV_AI_STATUS 0X92 +#define AI_READ_START 0X93 +#define AI_WRITE_START 0X94 +#define AI_CUR_BUFFER 0X95 +#define AI_DMA_BUFFER 0X96 +#define AI_READ_OFFSET 0X97 +#define AI_WRITE_OFFSET 0X98 +#define AI_WRITE_OFFSET_SAVE 0X99 +#define RV_AI_BUFF_START 0X9A +#define I_PIC_MB_COUNT 0X9B +#define AI_WR_DCAC_DMA_CTRL 0X9C +#define SLICE_MB_COUNT 0X9D +#define PICTYPE 0X9E +#define SLICE_GROUP_MAP_TYPE 0X9F +#define MB_TYPE 0XA0 +#define MB_AFF_ADDED_DMA 0XA1 +#define PREVIOUS_MB_TYPE 0XA2 +#define WEIGHTED_PRED_FLAG 0XA3 +#define WEIGHTED_BIPRED_IDC 0XA4 +/* bit 3:2 - PICTURE_STRUCTURE + * bit 1 - MB_ADAPTIVE_FRAME_FIELD_FLAG + * bit 0 - FRAME_MBS_ONLY_FLAG + */ +#define MBFF_INFO 0XA5 +#define TOP_INTRA_TYPE_TOP 0XA6 + +#define RV_AI_BUFF_INC 0xa7 + +#define DEFAULT_MB_INFO_LO 0xa8 + +/* 0 -- no need to read + * 1 -- need to wait Left + * 2 -- need to read Intra + * 3 -- need to read back MV + */ +#define NEED_READ_TOP_INFO 0xa9 +/* 0 -- idle + * 1 -- wait Left + * 2 -- reading top Intra + * 3 -- reading back MV + */ +#define READ_TOP_INFO_STATE 0xaa +#define DCAC_MBX 0xab +#define TOP_MB_INFO_OFFSET 0xac +#define TOP_MB_INFO_RD_IDX 0xad +#define TOP_MB_INFO_WR_IDX 0xae + +#define VLD_NO_WAIT 0 +#define VLD_WAIT_BUFFER 1 +#define VLD_WAIT_HOST 2 +#define VLD_WAIT_GAP 3 + +#define VLD_WAITING 0xaf + +#define MB_X_NUM 0xb0 +/* #define MB_WIDTH 0xb1 */ +#define MB_HEIGHT 0xb2 +#define MBX 0xb3 +#define TOTAL_MBY 0xb4 +#define INTR_MSK_SAVE 0xb5 + +/* #define has_time_stamp 0xb6 */ +#define NEED_DISABLE_PPE 0xb6 +#define IS_NEW_PICTURE 0XB7 +#define PREV_NAL_REF_IDC 0XB8 +#define PREV_NAL_UNIT_TYPE 0XB9 +#define FRAME_MB_COUNT 0XBA +#define SLICE_GROUP_UCODE 0XBB +#define SLICE_GROUP_CHANGE_RATE 0XBC +#define SLICE_GROUP_CHANGE_CYCLE_LEN 0XBD +#define DELAY_LENGTH 0XBE +#define PICTURE_STRUCT 0XBF +/* #define pre_picture_struct 0xc0 */ +#define DCAC_PREVIOUS_MB_TYPE 0xc1 + +#define TIME_STAMP 0XC2 +#define H_TIME_STAMP 0XC3 +#define VPTS_MAP_ADDR 0XC4 +#define H_VPTS_MAP_ADDR 0XC5 + +#define MAX_DPB_SIZE 0XC6 +#define PIC_INSERT_FLAG 0XC7 + +#define TIME_STAMP_START 0XC8 +#define TIME_STAMP_END 0XDF + +#define OFFSET_FOR_NON_REF_PIC 0XE0 +#define OFFSET_FOR_TOP_TO_BOTTOM_FIELD 0XE2 +#define MAX_REFERENCE_FRAME_NUM 0XE4 +#define FRAME_NUM_GAP_ALLOWED 0XE5 +#define NUM_REF_FRAMES_IN_PIC_ORDER_CNT_CYCLE 0XE6 +#define PROFILE_IDC_MMCO 0XE7 +#define LEVEL_IDC_MMCO 0XE8 +#define FRAME_SIZE_IN_MB 0XE9 +#define DELTA_PIC_ORDER_ALWAYS_ZERO_FLAG 0XEA +#define PPS_NUM_REF_IDX_L0_ACTIVE_MINUS1 0XEB +#define PPS_NUM_REF_IDX_L1_ACTIVE_MINUS1 0XEC +#define CURRENT_SPS_ID 0XED +#define CURRENT_PPS_ID 0XEE +/* bit 0 - sequence parameter set may change + * bit 1 - picture parameter set may change + * bit 2 - new dpb just inited + * bit 3 - IDR picture not decoded yet + * bit 5:4 - 0: mb level code loaded 1: picture + * level code loaded 2: slice level code loaded + */ +#define DECODE_STATUS 0XEF +#define FIRST_MB_IN_SLICE 0XF0 +#define PREV_MB_WIDTH 0XF1 +#define PREV_FRAME_SIZE_IN_MB 0XF2 +#define MAX_REFERENCE_FRAME_NUM_IN_MEM 0XF3 +/* bit 0 - aspect_ratio_info_present_flag + * bit 1 - timing_info_present_flag + * bit 2 - nal_hrd_parameters_present_flag + * bit 3 - vcl_hrd_parameters_present_flag + * bit 4 - pic_struct_present_flag + * bit 5 - bitstream_restriction_flag + */ +#define VUI_STATUS 0XF4 +#define ASPECT_RATIO_IDC 0XF5 +#define ASPECT_RATIO_SAR_WIDTH 0XF6 +#define ASPECT_RATIO_SAR_HEIGHT 0XF7 +#define NUM_UNITS_IN_TICK 0XF8 +#define TIME_SCALE 0XFA +#define CURRENT_PIC_INFO 0XFC +#define DPB_BUFFER_INFO 0XFD +#define REFERENCE_POOL_INFO 0XFE +#define REFERENCE_LIST_INFO 0XFF + struct{ + unsigned short data[RPM_END-RPM_BEGIN]; + } l; + struct{ + unsigned short dump[DPB_OFFSET]; + unsigned short dpb_base[FRAME_IN_DPB<<3]; + + unsigned short dpb_max_buffer_frame; + unsigned short actual_dpb_size; + + unsigned short colocated_buf_status; + + unsigned short num_forward_short_term_reference_pic; + unsigned short num_short_term_reference_pic; + unsigned short num_reference_pic; + + unsigned short current_dpb_index; + unsigned short current_decoded_frame_num; + unsigned short current_reference_frame_num; + + unsigned short l0_size; + unsigned short l1_size; + + /* [6:5] : nal_ref_idc */ + /* [4:0] : nal_unit_type */ + unsigned short NAL_info_mmco; + + /* [1:0] : 00 - top field, 01 - bottom field, + 10 - frame, 11 - mbaff frame */ + unsigned short picture_structure_mmco; + + unsigned short frame_num; + unsigned short pic_order_cnt_lsb; + + unsigned short num_ref_idx_l0_active_minus1; + unsigned short num_ref_idx_l1_active_minus1; + + unsigned short PrevPicOrderCntLsb; + unsigned short PreviousFrameNum; + + /* 32 bits variables */ + unsigned short delta_pic_order_cnt_bottom[2]; + unsigned short delta_pic_order_cnt_0[2]; + unsigned short delta_pic_order_cnt_1[2]; + + unsigned short PrevPicOrderCntMsb[2]; + unsigned short PrevFrameNumOffset[2]; + + unsigned short frame_pic_order_cnt[2]; + unsigned short top_field_pic_order_cnt[2]; + unsigned short bottom_field_pic_order_cnt[2]; + + unsigned short colocated_mv_addr_start[2]; + unsigned short colocated_mv_addr_end[2]; + unsigned short colocated_mv_wr_addr[2]; + } dpb; + struct { + unsigned short dump[MMCO_OFFSET]; + + /* array base address for offset_for_ref_frame */ + unsigned short offset_for_ref_frame_base[128]; + + /* 0 - Index in DPB + * 1 - Picture Flag + * [ 2] : 0 - short term reference, + * 1 - long term reference + * [ 1] : bottom field + * [ 0] : top field + * 2 - Picture Number (short term or long term) low 16 bits + * 3 - Picture Number (short term or long term) high 16 bits + */ + unsigned short reference_base[128]; + + /* command and parameter, until command is 3 */ + unsigned short l0_reorder_cmd[66]; + unsigned short l1_reorder_cmd[66]; + + /* command and parameter, until command is 0 */ + unsigned short mmco_cmd[44]; + + unsigned short l0_base[40]; + unsigned short l1_base[40]; + } mmco; + struct { + /* from ucode lmem, do not change this struct */ + } p; +}; + + +struct StorablePicture; +struct VideoParameters; +struct DecodedPictureBuffer; + +/* New enum for field processing */ +enum PictureStructure { + FRAME, + TOP_FIELD, + BOTTOM_FIELD +}; + +#define I_Slice 2 +#define P_Slice 5 +#define B_Slice 6 +#define P_Slice_0 0 +#define B_Slice_1 1 +#define I_Slice_7 7 + +enum SliceType { + P_SLICE = 0, + B_SLICE = 1, + I_SLICE = 2, + SP_SLICE = 3, + SI_SLICE = 4, + NUM_SLICE_TYPES = 5 +}; + +struct SPSParameters { + int pic_order_cnt_type; + int log2_max_pic_order_cnt_lsb_minus4; + int num_ref_frames_in_pic_order_cnt_cycle; + short offset_for_ref_frame[128]; + short offset_for_non_ref_pic; + short offset_for_top_to_bottom_field; + + /**/ + int frame_mbs_only_flag; + int num_ref_frames; + int max_dpb_size; + + int log2_max_frame_num_minus4; +}; + +#define DEC_REF_PIC_MARKING_BUFFER_NUM_MAX 45 +struct DecRefPicMarking_s { + int memory_management_control_operation; + int difference_of_pic_nums_minus1; + int long_term_pic_num; + int long_term_frame_idx; + int max_long_term_frame_idx_plus1; + struct DecRefPicMarking_s *Next; +}; + +#define REORDERING_COMMAND_MAX_SIZE 33 +struct Slice { + int first_mb_in_slice; + int mode_8x8_flags; + int picture_structure_mmco; + + int frame_num; + int idr_flag; + int toppoc; + int bottompoc; + int framepoc; + int pic_order_cnt_lsb; + int PicOrderCntMsb; + unsigned char field_pic_flag; + unsigned char bottom_field_flag; + int ThisPOC; + int nal_reference_idc; + int AbsFrameNum; + int delta_pic_order_cnt_bottom; + int delta_pic_order_cnt[2]; + + /**/ + char listXsize[6]; + struct StorablePicture *listX[6][MAX_LIST_SIZE * 2]; + + /**/ + enum PictureStructure structure; + int long_term_reference_flag; + int no_output_of_prior_pics_flag; + int adaptive_ref_pic_buffering_flag; + + struct VideoParameters *p_Vid; + struct DecodedPictureBuffer *p_Dpb; + int num_ref_idx_active[2]; /* number of available list references */ + + /*modification*/ + int slice_type; /* slice type */ + int ref_pic_list_reordering_flag[2]; + int modification_of_pic_nums_idc[2][REORDERING_COMMAND_MAX_SIZE]; + int abs_diff_pic_num_minus1[2][REORDERING_COMMAND_MAX_SIZE]; + int long_term_pic_idx[2][REORDERING_COMMAND_MAX_SIZE]; + /**/ + unsigned char dec_ref_pic_marking_buffer_valid; + struct DecRefPicMarking_s + dec_ref_pic_marking_buffer[DEC_REF_PIC_MARKING_BUFFER_NUM_MAX]; +}; + +struct OldSliceParams { + unsigned field_pic_flag; + unsigned frame_num; + int nal_ref_idc; + unsigned pic_oder_cnt_lsb; + int delta_pic_oder_cnt_bottom; + int delta_pic_order_cnt[2]; + unsigned char bottom_field_flag; + unsigned char idr_flag; + int idr_pic_id; + int pps_id; +#if (MVC_EXTENSION_ENABLE) + int view_id; + int inter_view_flag; + int anchor_pic_flag; +#endif + int layer_id; +}; + +struct VideoParameters { + int PrevPicOrderCntMsb; + int PrevPicOrderCntLsb; + unsigned char last_has_mmco_5; + unsigned char last_pic_bottom_field; + int ThisPOC; + int PreviousFrameNum; + int FrameNumOffset; + int PreviousFrameNumOffset; + int max_frame_num; + unsigned int pre_frame_num; + int ExpectedDeltaPerPicOrderCntCycle; + int PicOrderCntCycleCnt; + int FrameNumInPicOrderCntCycle; + int ExpectedPicOrderCnt; + + /**/ + struct SPSParameters *active_sps; + struct Slice **ppSliceList; + int iSliceNumOfCurrPic; + int conceal_mode; + int earlier_missing_poc; + int pocs_in_dpb[100]; + + struct OldSliceParams old_slice; + /**/ + struct StorablePicture *dec_picture; + struct StorablePicture *no_reference_picture; + + /*modification*/ + int non_conforming_stream; + int recovery_point; +}; + +static inline int imin(int a, int b) +{ + return ((a) < (b)) ? (a) : (b); +} + +static inline int imax(int a, int b) +{ + return ((a) > (b)) ? (a) : (b); +} + +#define MAX_PIC_BUF_NUM 128 +#define MAX_NUM_SLICES 50 + +struct StorablePicture { +/**/ + int width; + int height; + + int y_canvas_index; + int u_canvas_index; + int v_canvas_index; +/**/ + int index; + unsigned char is_used; + + enum PictureStructure structure; + + int poc; + int top_poc; + int bottom_poc; + int frame_poc; + unsigned int frame_num; + unsigned int recovery_frame; + + int pic_num; + int buf_spec_num; + int colocated_buf_index; + int long_term_pic_num; + int long_term_frame_idx; + + unsigned char is_long_term; + int used_for_reference; + int is_output; +#if 1 + /* rain */ + int pre_output; +#endif + int non_existing; + int separate_colour_plane_flag; + + short max_slice_id; + + int size_x, size_y, size_x_cr, size_y_cr; + int size_x_m1, size_y_m1, size_x_cr_m1, size_y_cr_m1; + int coded_frame; + int mb_aff_frame_flag; + unsigned PicWidthInMbs; + unsigned PicSizeInMbs; + int iLumaPadY, iLumaPadX; + int iChromaPadY, iChromaPadX; + + /* for mb aff, if frame for referencing the top field */ + struct StorablePicture *top_field; + /* for mb aff, if frame for referencing the bottom field */ + struct StorablePicture *bottom_field; + /* for mb aff, if field for referencing the combined frame */ + struct StorablePicture *frame; + + int slice_type; + int idr_flag; + int no_output_of_prior_pics_flag; + int long_term_reference_flag; + int adaptive_ref_pic_buffering_flag; + + int chroma_format_idc; + int frame_mbs_only_flag; + int frame_cropping_flag; + int frame_crop_left_offset; + int frame_crop_right_offset; + int frame_crop_top_offset; + int frame_crop_bottom_offset; + int qp; + int chroma_qp_offset[2]; + int slice_qp_delta; + /* stores the memory management control operations */ + struct DecRefPicMarking_s *dec_ref_pic_marking_buffer; + + /* picture error concealment */ + /*indicates if this is a concealed picture */ + int concealed_pic; + + /* variables for tone mapping */ + int seiHasTone_mapping; + int tone_mapping_model_id; + int tonemapped_bit_depth; + /* imgpel* tone_mapping_lut; tone mapping look up table */ + + int proc_flag; +#if (MVC_EXTENSION_ENABLE) + int view_id; + int inter_view_flag; + int anchor_pic_flag; +#endif + int iLumaStride; + int iChromaStride; + int iLumaExpandedHeight; + int iChromaExpandedHeight; + /* imgpel **cur_imgY; for more efficient get_block_luma */ + int no_ref; + int iCodingType; + + char listXsize[MAX_NUM_SLICES][2]; + struct StorablePicture **listX[MAX_NUM_SLICES][2]; + int layer_id; + + int offset_delimiter_lo; + int offset_delimiter_hi; + + u32 pts; + u64 pts64; +}; + +struct FrameStore { + /* rain */ + int buf_spec_num; + /* rain */ + int colocated_buf_index; + + /* 0=empty; 1=top; 2=bottom; 3=both fields (or frame) */ + int is_used; + /* 0=not used for ref; 1=top used; 2=bottom used; + * 3=both fields (or frame) used */ + int is_reference; + /* 0=not used for ref; 1=top used; 2=bottom used; + * 3=both fields (or frame) used */ + int is_long_term; + /* original marking by nal_ref_idc: 0=not used for ref; 1=top used; + * 2=bottom used; 3=both fields (or frame) used */ + int is_orig_reference; + + int is_non_existent; + + unsigned frame_num; + unsigned recovery_frame; + + int frame_num_wrap; + int long_term_frame_idx; + int is_output; +#if 1 + /* rain */ + int pre_output; + /* index in gFrameStore */ + int index; +#endif + int poc; + + /* picture error concealment */ + int concealment_reference; + + struct StorablePicture *frame; + struct StorablePicture *top_field; + struct StorablePicture *bottom_field; + +#if (MVC_EXTENSION_ENABLE) + int view_id; + int inter_view_flag[2]; + int anchor_pic_flag[2]; +#endif + int layer_id; + + u32 pts; + u64 pts64; +}; + +int prepare_display_buf(struct vdec_s *vdec, struct FrameStore *frame); + + +/* #define DPB_SIZE_MAX 16 */ +#define DPB_SIZE_MAX 32 +struct DecodedPictureBuffer { + struct VideoParameters *p_Vid; + /* InputParameters *p_Inp; ??? */ + struct FrameStore *fs[DPB_SIZE_MAX]; + struct FrameStore *fs_ref[DPB_SIZE_MAX]; + struct FrameStore *fs_ltref[DPB_SIZE_MAX]; + /* inter-layer reference (for multi-layered codecs) */ + struct FrameStore *fs_ilref[DPB_SIZE_MAX]; + /**/ + struct FrameStore *fs_list0[DPB_SIZE_MAX]; + struct FrameStore *fs_list1[DPB_SIZE_MAX]; + struct FrameStore *fs_listlt[DPB_SIZE_MAX]; + + /**/ + unsigned size; + unsigned used_size; + unsigned ref_frames_in_buffer; + unsigned ltref_frames_in_buffer; + int last_output_poc; +#if (MVC_EXTENSION_ENABLE) + int last_output_view_id; +#endif + int max_long_term_pic_idx; + + + int init_done; + int num_ref_frames; + + struct FrameStore *last_picture; + unsigned used_size_il; + int layer_id; + + /* DPB related function; */ +}; + +struct h264_dpb_stru { + struct vdec_s *vdec; + int decoder_index; + + union param dpb_param; + + int decode_idx; + int buf_num; + int curr_POC; + int reorder_pic_num; + /**/ + unsigned int max_reference_size; + + unsigned int colocated_buf_map; + unsigned int colocated_buf_count; + unsigned int colocated_mv_addr_start; + unsigned int colocated_mv_addr_end; + unsigned int colocated_buf_size; + + struct DecodedPictureBuffer mDPB; + struct Slice mSlice; + struct VideoParameters mVideo; + struct SPSParameters mSPS; + + struct StorablePicture m_PIC[MAX_PIC_BUF_NUM]; + struct FrameStore mFrameStore[DPB_SIZE_MAX]; + +}; + + +extern unsigned int h264_debug_flag; +extern unsigned int h264_debug_mask; + +int dpb_print(int indext, int debug_flag, const char *fmt, ...); + +unsigned char dpb_is_debug(int index, int debug_flag); + +int prepare_display_buf(struct vdec_s *vdec, struct FrameStore *frame); + +int release_buf_spec_num(struct vdec_s *vdec, int buf_spec_num); + +void set_frame_output_flag(struct h264_dpb_stru *p_H264_Dpb, int index); + +int is_there_unused_frame_from_dpb(struct DecodedPictureBuffer *p_Dpb); + +int h264_slice_header_process(struct h264_dpb_stru *p_H264_Dpb); + +void dpb_init_global(struct h264_dpb_stru *p_H264_Dpb, + int id, int actual_dpb_size, int max_reference_size); + +void init_colocate_buf(struct h264_dpb_stru *p_H264_Dpb, int count); + +int release_colocate_buf(struct h264_dpb_stru *p_H264_Dpb, int index); + +int get_free_buf_idx(struct vdec_s *vdec); + +void store_picture_in_dpb(struct h264_dpb_stru *p_H264_Dpb, + struct StorablePicture *p); + +int remove_picture(struct h264_dpb_stru *p_H264_Dpb, + struct StorablePicture *pic); + +void bufmgr_post(struct h264_dpb_stru *p_H264_Dpb); + +int get_long_term_flag_by_buf_spec_num(struct h264_dpb_stru *p_H264_Dpb, + int buf_spec_num); + +void bufmgr_h264_remove_unused_frame(struct h264_dpb_stru *p_H264_Dpb); + +#endif diff --git a/drivers/frame_provider/decoder/h264_multi/vmh264.c b/drivers/frame_provider/decoder/h264_multi/vmh264.c new file mode 100644 index 0000000..59b7457 --- a/dev/null +++ b/drivers/frame_provider/decoder/h264_multi/vmh264.c @@ -0,0 +1,2776 @@ +/* + * drivers/amlogic/amports/vh264.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/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/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/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 <linux/dma-mapping.h> +#include <linux/dma-contiguous.h> +#include "../../../stream_input/amports/amports_priv.h" +#include <linux/amlogic/media/codec_mm/codec_mm.h> + +#include "../utils/vdec_input.h" + +#include <linux/amlogic/media/utils/vdec_reg.h> +#include "../utils/vdec.h" +#include "../utils/amvdec.h" +#include "../h264/vh264.h" +#include "../../../stream_input/parser/streambuf.h" +#include <linux/delay.h> + +#undef pr_info +#define pr_info printk + +#define DEBUG_UCODE +#define USE_CMA +#define MEM_NAME "codec_m264" +#define MULTI_INSTANCE_FRAMEWORK +/* #define ONE_COLOCATE_BUF_PER_DECODE_BUF */ +#include "h264_dpb.h" +/* #define SEND_PARAM_WITH_REG */ + +#define DRIVER_NAME "ammvdec_h264" +#define MODULE_NAME "ammvdec_h264" + +#define CHECK_INTERVAL (HZ/100) + +#define RATE_MEASURE_NUM 8 +#define RATE_CORRECTION_THRESHOLD 5 +#define RATE_2397_FPS 4004 /* 23.97 */ +#define RATE_25_FPS 3840 /* 25 */ +#define RATE_2997_FPS 3203 /* 29.97 */ +#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_IFRAME_NUM 2 + +#define FIX_FRAME_RATE_OFF 0 +#define FIX_FRAME_RATE_ON 1 +#define FIX_FRAME_RATE_SMOOTH_CHECKING 2 + +#define DEC_CONTROL_FLAG_FORCE_2997_1080P_INTERLACE 0x0001 +#define DEC_CONTROL_FLAG_FORCE_2500_576P_INTERLACE 0x0002 +#define DEC_CONTROL_FLAG_FORCE_RATE_2397_FPS_FIX_FRAME_RATE 0x0010 +#define DEC_CONTROL_FLAG_FORCE_RATE_2997_FPS_FIX_FRAME_RATE 0x0020 + + +#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 H264_DEV_NUM 5 + +unsigned int h264_debug_flag; /* 0xa0000000; */ +unsigned int h264_debug_mask = 0xff; + /* + h264_debug_cmd: + 0x1xx, force decoder id of xx to be disconnected + */ +unsigned int h264_debug_cmd; +static unsigned int dec_control; +static unsigned int force_rate_streambase; +static unsigned int force_rate_framebase; +static unsigned int fixed_frame_rate_mode; +static unsigned int error_recovery_mode_in; +static unsigned int start_decode_buf_level = 0x8000; + +static unsigned int reorder_dpb_size_margin = 6; +static unsigned int reference_buf_margin = 4; + +static unsigned int decode_timeout_val = 50; +static unsigned int radr; +static unsigned int rval; +static unsigned int max_decode_instance_num = H264_DEV_NUM; +static unsigned int decode_frame_count[H264_DEV_NUM]; +static unsigned int max_process_time[H264_DEV_NUM]; +static unsigned int max_get_frame_interval[H264_DEV_NUM]; + /* bit[3:0]: + 0, run ; 1, pause; 3, step + bit[4]: + 1, schedule run + */ +static unsigned int step[H264_DEV_NUM]; + +#define is_in_parsing_state(status) \ + ((status == H264_ACTION_SEARCH_HEAD) || \ + ((status & 0xf0) == 0x80)) + +static inline bool close_to(int a, int b, int m) +{ + return (abs(a - b) < m) ? true : false; +} + +/* #if MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON6 */ +#define NV21 +/* #endif */ + +/* 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 (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 MAX_VF_BUF_NUM 28 +#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 DEC_CONTROL_FLAG_FORCE_2997_1080P_INTERLACE 0x0001 +#define DEC_CONTROL_FLAG_FORCE_2500_576P_INTERLACE 0x0002 + +#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 { + /* + used: + 0, free; 1, used by dpb; 2, + used for display; + 3 isolated (do not use for dpb when vf_put) + */ + unsigned int used; + unsigned int info0; + unsigned int info1; + unsigned int info2; + unsigned int y_addr; + unsigned int u_addr; + unsigned int v_addr; + + int y_canvas_index; + int u_canvas_index; + int v_canvas_index; + +#ifdef NV21 + struct canvas_config_s canvas_config[2]; +#else + struct canvas_config_s canvas_config[3]; +#endif +#ifdef USE_CMA + /* struct page *cma_alloc_pages; */ + unsigned long cma_alloc_addr; + int cma_alloc_count; +#endif + unsigned int buf_adr; +}; + +#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_work(struct work_struct *work); + +static const char vh264_dec_id[] = "vh264-dev"; + +#define PROVIDER_NAME "vdec.h264" + +static const struct vframe_operations_s 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, +}; + +#define DEC_RESULT_NONE 0 +#define DEC_RESULT_DONE 1 +#define DEC_RESULT_AGAIN 2 +#define DEC_RESULT_CONFIG_PARAM 3 +#define DEC_RESULT_GET_DATA 4 +#define DEC_RESULT_GET_DATA_RETRY 5 +#define DEC_RESULT_ERROR 6 + +/* +static const char *dec_result_str[] = { + "DEC_RESULT_NONE ", + "DEC_RESULT_DONE ", + "DEC_RESULT_AGAIN ", + "DEC_RESULT_CONFIG_PARAM", + "DEC_RESULT_GET_DATA ", + "DEC_RESULT_GET_DA_RETRY", + "DEC_RESULT_ERROR ", +}; +*/ + +#define UCODE_IP_ONLY 2 +#define UCODE_IP_ONLY_PARAM 1 + +#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_OFFSET_MAIN 0x5000 + +#define MC_TOTAL_SIZE ((20+16)*SZ_1K) +#define MC_SWAP_SIZE (4*SZ_1K) +#define MODE_ERROR 0 +#define MODE_FULL 1 + +#define DFS_HIGH_THEASHOLD 3 + +#define INIT_FLAG_REG AV_SCRATCH_2 +#define HEAD_PADING_REG AV_SCRATCH_3 +#define UCODE_WATCHDOG_REG AV_SCRATCH_7 +#define LMEM_DUMP_ADR AV_SCRATCH_L +#define DEBUG_REG1 AV_SCRATCH_M +#define DEBUG_REG2 AV_SCRATCH_N +#define FRAME_COUNTER_REG AV_SCRATCH_I +#define RPM_CMD_REG AV_SCRATCH_A +#define H264_DECODE_SIZE AV_SCRATCH_E +#define H264_DECODE_INFO M4_CONTROL_REG /* 0xc29 */ +#define DPB_STATUS_REG AV_SCRATCH_J +#define MBY_MBX MB_MOTION_MODE /*0xc07*/ + +struct vdec_h264_hw_s { + spinlock_t lock; + + struct platform_device *platform_dev; + struct device *cma_dev; + /* struct page *cma_alloc_pages; */ + unsigned long cma_alloc_addr; + int cma_alloc_count; + /* struct page *collocate_cma_alloc_pages; */ + unsigned long collocate_cma_alloc_addr; + int collocate_cma_alloc_count; + + ulong lmem_addr; + dma_addr_t lmem_addr_remap; + + DECLARE_KFIFO(newframe_q, struct vframe_s *, VF_POOL_SIZE); + DECLARE_KFIFO(display_q, struct vframe_s *, VF_POOL_SIZE); + + struct vframe_s vfpool[VF_POOL_SIZE]; + struct buffer_spec_s buffer_spec[MAX_VF_BUF_NUM]; + int buffer_spec_num; + struct vframe_s switching_fense_vf; + struct h264_dpb_stru dpb; + u8 init_flag; + u8 set_params_done; + u32 max_reference_size; + u32 decode_pic_count; + int start_search_pos; + + unsigned char buffer_empty_flag; + + u32 frame_width; + u32 frame_height; + u32 frame_dur; + u32 frame_prog; + u32 frame_packing_type; + + struct vframe_chunk_s *chunk; + + u32 stat; + unsigned long buf_start; + u32 buf_offset; + u32 buf_size; + /* u32 ucode_map_start; */ + u32 pts_outside; + u32 sync_outside; + u32 vh264_ratio; + u32 vh264_rotation; + u32 use_idr_framerate; + + u32 seq_info; + u32 timing_info_present_flag; + u32 fixed_frame_rate_flag; + u32 fixed_frame_rate_check_count; + u32 iframe_count; + u32 aspect_ratio_info; + u32 num_units_in_tick; + u32 time_scale; + u32 h264_ar; + bool h264_first_valid_pts_ready; + u32 h264pts1; + u32 h264pts2; + u32 pts_duration; + u32 h264_pts_count; + u32 duration_from_pts_done; + u32 pts_unstable; u32 duration_on_correcting; + u32 last_checkout_pts; + u32 fatal_error_flag; + u32 fatal_error_reset; + u32 max_refer_buf; + + s32 vh264_stream_switching_state; + struct vframe_s *p_last_vf; + u32 last_pts; + u32 last_pts_remainder; + u32 last_duration; + u32 saved_resolution; + u32 last_mb_width, last_mb_height; + bool check_pts_discontinue; + bool pts_discontinue; + u32 wait_buffer_counter; + u32 first_offset; + u32 first_pts; + u64 first_pts64; + bool first_pts_cached; + + void *sei_data_buffer; + dma_addr_t sei_data_buffer_phys; + + uint error_recovery_mode; + uint mb_total; + uint mb_width; + uint mb_height; + + uint ucode_type; + dma_addr_t mc_dma_handle; + void *mc_cpu_addr; + int vh264_reset; + + atomic_t vh264_active; + + struct dec_sysinfo vh264_amstream_dec_info; + + struct work_struct error_wd_work; + + int dec_result; + struct work_struct work; + + void (*vdec_cb)(struct vdec_s *, void *); + void *vdec_cb_arg; + + struct timer_list check_timer; + + /**/ + unsigned last_frame_time; + + /* timeout handle */ + unsigned long int start_process_time; + unsigned last_mby_mbx; + unsigned last_ucode_watchdog_reg_val; + unsigned decode_timeout_count; + unsigned timeout_num; + unsigned search_dataempty_num; + unsigned decode_timeout_num; + unsigned decode_dataempty_num; + unsigned buffer_empty_recover_num; + + /**/ + + /*log*/ + unsigned int packet_write_success_count; + unsigned int packet_write_EAGAIN_count; + unsigned int packet_write_ENOMEM_count; + unsigned int packet_write_EFAULT_count; + unsigned int total_read_size_pre; + unsigned int total_read_size; + unsigned int frame_count_pre; +}; + + +static void vh264_local_init(struct vdec_h264_hw_s *hw); +static int vh264_hw_ctx_restore(struct vdec_h264_hw_s *hw); +static int vh264_stop(struct vdec_h264_hw_s *hw, int mode); +static s32 vh264_init(struct vdec_h264_hw_s *hw); +static void set_frame_info(struct vdec_h264_hw_s *hw, struct vframe_s *vf, + u32 index); + +unsigned char have_free_buf_spec(struct vdec_s *vdec) +{ + int i; + struct vdec_h264_hw_s *hw = (struct vdec_h264_hw_s *)vdec->private; + if ((h264_debug_flag&OUTPUT_CURRENT_BUF) == 0) { + for (i = 0; i < hw->buffer_spec_num; i++) { + if (hw->buffer_spec[i].used == 0) + return 1; + } + return 0; + } else + return 1; +} + +#if 0 +static void buf_spec_recover(struct vdec_h264_hw_s *hw) +{ /* do not clear buf_spec used by display */ + int i; + dpb_print(hw->dpb.decoder_index, + PRINT_FLAG_DPB, "%s\n", __func__); + for (i = 0; i < hw->buffer_spec_num; i++) { + if (hw->buffer_spec[i].used == 2) + hw->buffer_spec[i].used = 3; + else + hw->buffer_spec[i].used = 0; + } +} +#endif + +int get_free_buf_idx(struct vdec_s *vdec) +{ + int i; + int index = -1; + struct vdec_h264_hw_s *hw = (struct vdec_h264_hw_s *)vdec->private; + + if ((h264_debug_flag&OUTPUT_CURRENT_BUF) == 0) { + for (i = hw->start_search_pos; i < hw->buffer_spec_num; i++) { + if (hw->buffer_spec[i].used == 0) { + hw->buffer_spec[i].used = 1; + hw->start_search_pos = i+1; + index = i; + break; + } + } + if (index < 0) { + for (i = 0; i < hw->start_search_pos; i++) { + if (hw->buffer_spec[i].used == 0) { + hw->buffer_spec[i].used = 1; + hw->start_search_pos = i+1; + index = i; + break; + } + } + } + } else { + index = hw->start_search_pos; + hw->start_search_pos++; + } + + if (hw->start_search_pos >= hw->buffer_spec_num) + hw->start_search_pos = 0; + + dpb_print(hw->dpb.decoder_index, PRINT_FLAG_DPB_DETAIL, + "%s, buf_spec_num %d\n", __func__, index); + + return index; +} + +int release_buf_spec_num(struct vdec_s *vdec, int buf_spec_num) +{ + struct vdec_h264_hw_s *hw = (struct vdec_h264_hw_s *)vdec->private; + dpb_print(hw->dpb.decoder_index, PRINT_FLAG_DPB_DETAIL, + "%s buf_spec_num %d\n", + __func__, buf_spec_num); + if (buf_spec_num >= 0 && buf_spec_num < hw->buffer_spec_num) + hw->buffer_spec[buf_spec_num].used = 0; + return 0; +} + +static int get_buf_spec_idx_by_canvas_config(struct vdec_h264_hw_s *hw, + struct canvas_config_s *cfg) +{ + int i; + for (i = 0; i < hw->buffer_spec_num; i++) { + if (hw->buffer_spec[i].canvas_config[0].phy_addr + == cfg->phy_addr) + return i; + } + return -1; +} + +int prepare_display_buf(struct vdec_s *vdec, struct FrameStore *frame) +{ + struct vdec_h264_hw_s *hw = (struct vdec_h264_hw_s *)vdec->private; + struct vframe_s *vf = NULL; + + if (kfifo_get(&hw->newframe_q, &vf) == 0) { + dpb_print(hw->dpb.decoder_index, PRINT_FLAG_VDEC_STATUS, + "%s fatal error, no available buffer slot.\n", + __func__); + return -1; + } + + if (vf) { + int buffer_index = frame->buf_spec_num; + dpb_print(hw->dpb.decoder_index, PRINT_FLAG_DPB_DETAIL, + "%s, fs[%d] poc %d, buf_spec_num %d\n", + __func__, frame->index, frame->poc, + frame->buf_spec_num); + vf->index = frame->index; + vf->type = VIDTYPE_PROGRESSIVE | VIDTYPE_VIU_FIELD | + VIDTYPE_VIU_NV21; + vf->duration_pulldown = 0; + vf->pts = frame->pts; + vf->pts_us64 = frame->pts64; + vf->canvas0Addr = vf->canvas1Addr = + spec2canvas(&hw->buffer_spec[buffer_index]); + set_frame_info(hw, vf, buffer_index); + kfifo_put(&hw->display_q, (const struct vframe_s *)vf); + hw->buffer_spec[buffer_index].used = 2; + + vf_notify_receiver(vdec->vf_provider_name, + VFRAME_EVENT_PROVIDER_VFRAME_READY, NULL); + decode_frame_count[hw->dpb.decoder_index]++; + } + + return 0; +} + +/****************** + * Hardware config + */ +char *slice_type_name[] = { + "P_SLICE ", + "B_SLICE ", + "I_SLICE ", + "SP_SLICE", + "SI_SLICE", +}; + +char *picture_structure_name[] = { + "FRAME", + "TOP_FIELD", + "BOTTOM_FIELD" +}; + +void print_pic_info(int decindex, const char *info, + struct StorablePicture *pic, + int slice_type) +{ + dpb_print(decindex, PRINT_FLAG_UCODE_EVT, + "%s: %s (original %s), %s, mb_aff_frame_flag %d poc %d, pic_num %d, buf_spec_num %d\n", + info, + picture_structure_name[pic->structure], + pic->coded_frame ? "Frame" : "Field", + (slice_type < 0) ? "" : slice_type_name[slice_type], + pic->mb_aff_frame_flag, + pic->poc, + pic->pic_num, + pic->buf_spec_num); +} + +static void reset_process_time(struct vdec_h264_hw_s *hw) +{ + if (hw->start_process_time) { + unsigned process_time = + 1000 * (jiffies - hw->start_process_time) / HZ; + hw->start_process_time = 0; + if (process_time > max_process_time[hw->dpb.decoder_index]) + max_process_time[hw->dpb.decoder_index] = process_time; + } +} + +void config_decode_buf(struct vdec_h264_hw_s *hw, struct StorablePicture *pic) +{ + /* static int count = 0; */ + struct h264_dpb_stru *p_H264_Dpb = &hw->dpb; + struct Slice *pSlice = &(p_H264_Dpb->mSlice); + unsigned int colocate_adr_offset; + unsigned int val; + +#define H264_BUFFER_INFO_INDEX PMV3_X /* 0xc24 */ +#define H264_BUFFER_INFO_DATA PMV2_X /* 0xc22 */ +#define H264_CURRENT_POC_IDX_RESET LAST_SLICE_MV_ADDR /* 0xc30 */ +#define H264_CURRENT_POC LAST_MVY /* 0xc32 shared with conceal MV */ + +#define H264_CO_MB_WR_ADDR VLD_C38 /* 0xc38 */ +/* bit 31:30 -- L1[0] picture coding structure, + 00 - top field, 01 - bottom field, + 10 - frame, 11 - mbaff frame + bit 29 - L1[0] top/bot for B field pciture , 0 - top, 1 - bot + bit 28:0 h264_co_mb_mem_rd_addr[31:3] + -- only used for B Picture Direct mode [2:0] will set to 3'b000 +*/ +#define H264_CO_MB_RD_ADDR VLD_C39 /* 0xc39 */ + +/* bit 15 -- flush co_mb_data to DDR -- W-Only + bit 14 -- h264_co_mb_mem_wr_addr write Enable -- W-Only + bit 13 -- h264_co_mb_info_wr_ptr write Enable -- W-Only + bit 9 -- soft_reset -- W-Only + bit 8 -- upgent + bit 7:2 -- h264_co_mb_mem_wr_addr + bit 1:0 -- h264_co_mb_info_wr_ptr +*/ +#define H264_CO_MB_RW_CTL VLD_C3D /* 0xc3d */ + + unsigned long canvas_adr; + unsigned ref_reg_val; + unsigned one_ref_cfg = 0; + int h264_buffer_info_data_write_count; + int i, j; + unsigned colocate_wr_adr; + unsigned colocate_rd_adr; + unsigned char use_direct_8x8; + + WRITE_VREG(H264_CURRENT_POC_IDX_RESET, 0); + WRITE_VREG(H264_CURRENT_POC, pic->frame_poc); + WRITE_VREG(H264_CURRENT_POC, pic->top_poc); + WRITE_VREG(H264_CURRENT_POC, pic->bottom_poc); + + dpb_print(hw->dpb.decoder_index, PRINT_FLAG_UCODE_EVT, + "%s: pic_num is %d, poc is %d (%d, %d, %d), buf_spec_num %d\n", + __func__, pic->pic_num, pic->poc, pic->frame_poc, + pic->top_poc, pic->bottom_poc, pic->buf_spec_num); + print_pic_info(hw->dpb.decoder_index, "cur", pic, pSlice->slice_type); + + WRITE_VREG(CURR_CANVAS_CTRL, pic->buf_spec_num << 24); + + canvas_adr = READ_VREG(CURR_CANVAS_CTRL) & 0xffffff; + + WRITE_VREG(REC_CANVAS_ADDR, canvas_adr); + WRITE_VREG(DBKR_CANVAS_ADDR, canvas_adr); + WRITE_VREG(DBKW_CANVAS_ADDR, canvas_adr); + + if (pic->mb_aff_frame_flag) + hw->buffer_spec[pic->buf_spec_num].info0 = 0xf4c0; + else if (pic->structure == TOP_FIELD) + hw->buffer_spec[pic->buf_spec_num].info0 = 0xf400; + else if (pic->structure == BOTTOM_FIELD) + hw->buffer_spec[pic->buf_spec_num].info0 = 0xf440; + else + hw->buffer_spec[pic->buf_spec_num].info0 = 0xf480; + + if (pic->bottom_poc < pic->top_poc) + hw->buffer_spec[pic->buf_spec_num].info0 |= 0x100; + + hw->buffer_spec[pic->buf_spec_num].info1 = pic->top_poc; + hw->buffer_spec[pic->buf_spec_num].info2 = pic->bottom_poc; + WRITE_VREG(H264_BUFFER_INFO_INDEX, 16); + + for (i = 0; i < hw->buffer_spec_num; i++) { + int long_term_flag = + get_long_term_flag_by_buf_spec_num(p_H264_Dpb, i); + if (long_term_flag > 0) { + if (long_term_flag & 0x1) + hw->buffer_spec[i].info0 |= (1 << 4); + else + hw->buffer_spec[i].info0 &= ~(1 << 4); + + if (long_term_flag & 0x2) + hw->buffer_spec[i].info0 |= (1 << 5); + else + hw->buffer_spec[i].info0 &= ~(1 << 5); + } + + if (i == pic->buf_spec_num) + WRITE_VREG(H264_BUFFER_INFO_DATA, + hw->buffer_spec[i].info0 | 0xf); + else + WRITE_VREG(H264_BUFFER_INFO_DATA, + hw->buffer_spec[i].info0); + WRITE_VREG(H264_BUFFER_INFO_DATA, hw->buffer_spec[i].info1); + WRITE_VREG(H264_BUFFER_INFO_DATA, hw->buffer_spec[i].info2); + } + + /* config reference buffer */ + dpb_print(hw->dpb.decoder_index, PRINT_FLAG_UCODE_EVT, + "list0 size %d\n", pSlice->listXsize[0]); + WRITE_VREG(H264_BUFFER_INFO_INDEX, 0); + ref_reg_val = 0; + j = 0; + h264_buffer_info_data_write_count = 0; + + for (i = 0; i < (unsigned int)(pSlice->listXsize[0]); i++) { + /*ref list 0 */ + struct StorablePicture *ref = pSlice->listX[0][i]; + unsigned cfg; + /* bit[6:5] - frame/field info, + * 01 - top, 10 - bottom, 11 - frame + */ +#ifdef ERROR_CHECK + if (ref == NULL) + return; +#endif + if (ref->structure == TOP_FIELD) + cfg = 0x1; + else if (ref->structure == BOTTOM_FIELD) + cfg = 0x2; + else /* FRAME */ + cfg = 0x3; + one_ref_cfg = (ref->buf_spec_num & 0x1f) | (cfg << 5); + ref_reg_val <<= 8; + ref_reg_val |= one_ref_cfg; + j++; + + if (j == 4) { + dpb_print(hw->dpb.decoder_index, PRINT_FLAG_UCODE_EVT, + "H264_BUFFER_INFO_DATA: %x\n", ref_reg_val); + WRITE_VREG(H264_BUFFER_INFO_DATA, ref_reg_val); + h264_buffer_info_data_write_count++; + j = 0; + } + print_pic_info(hw->dpb.decoder_index, "list0", + pSlice->listX[0][i], -1); + } + if (j != 0) { + while (j != 4) { + ref_reg_val <<= 8; + ref_reg_val |= one_ref_cfg; + j++; + } + dpb_print(hw->dpb.decoder_index, PRINT_FLAG_UCODE_EVT, + "H264_BUFFER_INFO_DATA: %x\n", + ref_reg_val); + WRITE_VREG(H264_BUFFER_INFO_DATA, ref_reg_val); + h264_buffer_info_data_write_count++; + } + ref_reg_val = (one_ref_cfg << 24) | (one_ref_cfg<<16) | + (one_ref_cfg << 8) | one_ref_cfg; + for (i = h264_buffer_info_data_write_count; i < 8; i++) + WRITE_VREG(H264_BUFFER_INFO_DATA, ref_reg_val); + + dpb_print(hw->dpb.decoder_index, PRINT_FLAG_UCODE_EVT, + "list1 size %d\n", pSlice->listXsize[1]); + WRITE_VREG(H264_BUFFER_INFO_INDEX, 8); + ref_reg_val = 0; + j = 0; + + for (i = 0; i < (unsigned int)(pSlice->listXsize[1]); i++) { + /* ref list 0 */ + struct StorablePicture *ref = pSlice->listX[1][i]; + unsigned cfg; + /* bit[6:5] - frame/field info, + * 01 - top, 10 - bottom, 11 - frame + */ +#ifdef ERROR_CHECK + if (ref == NULL) + return; +#endif + if (ref->structure == TOP_FIELD) + cfg = 0x1; + else if (ref->structure == BOTTOM_FIELD) + cfg = 0x2; + else /* FRAME */ + cfg = 0x3; + one_ref_cfg = (ref->buf_spec_num & 0x1f) | (cfg << 5); + ref_reg_val <<= 8; + ref_reg_val |= one_ref_cfg; + j++; + + if (j == 4) { + dpb_print(hw->dpb.decoder_index, PRINT_FLAG_UCODE_EVT, + "H264_BUFFER_INFO_DATA: %x\n", + ref_reg_val); + WRITE_VREG(H264_BUFFER_INFO_DATA, ref_reg_val); + j = 0; + } + print_pic_info(hw->dpb.decoder_index, "list1", + pSlice->listX[1][i], -1); + } + if (j != 0) { + while (j != 4) { + ref_reg_val <<= 8; + ref_reg_val |= one_ref_cfg; + j++; + } + dpb_print(hw->dpb.decoder_index, PRINT_FLAG_UCODE_EVT, + "H264_BUFFER_INFO_DATA: %x\n", ref_reg_val); + WRITE_VREG(H264_BUFFER_INFO_DATA, ref_reg_val); + } + + /* configure co-locate buffer */ + while ((READ_VREG(H264_CO_MB_RW_CTL) >> 11) & 0x1) + ; + if ((pSlice->mode_8x8_flags & 0x4) && + (pSlice->mode_8x8_flags & 0x2)) + use_direct_8x8 = 1; + else + use_direct_8x8 = 0; + +#ifndef ONE_COLOCATE_BUF_PER_DECODE_BUF + colocate_adr_offset = + ((pic->structure == FRAME && pic->mb_aff_frame_flag == 0) + ? 1 : 2) * 96; + if (use_direct_8x8) + colocate_adr_offset >>= 2; + + dpb_print(hw->dpb.decoder_index, PRINT_FLAG_UCODE_EVT, + "colocate buf size of each mb 0x%x first_mb_in_slice 0x%x colocate_adr_offset 0x%x\r\n", + colocate_adr_offset, pSlice->first_mb_in_slice, + colocate_adr_offset * pSlice->first_mb_in_slice); + + colocate_adr_offset *= pSlice->first_mb_in_slice; + + if ((pic->colocated_buf_index >= 0) && + (pic->colocated_buf_index < p_H264_Dpb->colocated_buf_count)) { + colocate_wr_adr = p_H264_Dpb->colocated_mv_addr_start + + ((p_H264_Dpb->colocated_buf_size * + pic->colocated_buf_index) + >> (use_direct_8x8 ? 2 : 0)); + if ((colocate_wr_adr + p_H264_Dpb->colocated_buf_size) > + p_H264_Dpb->colocated_mv_addr_end) + dpb_print(hw->dpb.decoder_index, PRINT_FLAG_ERROR, + "Error, colocate buf is not enough, index is %d\n", + pic->colocated_buf_index); + val = colocate_wr_adr + colocate_adr_offset; + WRITE_VREG(H264_CO_MB_WR_ADDR, val); + dpb_print(hw->dpb.decoder_index, PRINT_FLAG_UCODE_EVT, + "WRITE_VREG(H264_CO_MB_WR_ADDR) = %x, first_mb_in_slice %x pic_structure %x colocate_adr_offset %x mode_8x8_flags %x colocated_buf_size %x\n", + val, pSlice->first_mb_in_slice, pic->structure, + colocate_adr_offset, pSlice->mode_8x8_flags, + p_H264_Dpb->colocated_buf_size); + } else { + WRITE_VREG(H264_CO_MB_WR_ADDR, 0xffffffff); + dpb_print(hw->dpb.decoder_index, PRINT_FLAG_UCODE_EVT, + "WRITE_VREG(H264_CO_MB_WR_ADDR) = 0xffffffff\n"); + } +#else + colocate_adr_offset = + ((pic->structure == FRAME && pic->mb_aff_frame_flag == 0) ? 1 : 2) * 96; + if (use_direct_8x8) + colocate_adr_offset >>= 2; + + dpb_print(hw->dpb.decoder_index, PRINT_FLAG_UCODE_EVT, + "colocate buf size of each mb 0x%x first_mb_in_slice 0x%x colocate_adr_offset 0x%x\r\n", + colocate_adr_offset, pSlice->first_mb_in_slice, + colocate_adr_offset * pSlice->first_mb_in_slice); + + colocate_adr_offset *= pSlice->first_mb_in_slice; + + colocate_wr_adr = p_H264_Dpb->colocated_mv_addr_start + + ((p_H264_Dpb->colocated_buf_size * pic->buf_spec_num) >> + (use_direct_8x8 ? 2 : 0)); + + if ((colocate_wr_adr + p_H264_Dpb->colocated_buf_size) > + p_H264_Dpb->colocated_mv_addr_end) + dpb_print(hw->dpb.decoder_index, PRINT_FLAG_ERROR, + "Error, colocate buf is not enough, pic index is %d\n", + pic->buf_spec_num); + val = colocate_wr_adr + colocate_adr_offset; + WRITE_VREG(H264_CO_MB_WR_ADDR, val); + dpb_print(hw->dpb.decoder_index, PRINT_FLAG_UCODE_EVT, + "WRITE_VREG(H264_CO_MB_WR_ADDR) = %x, first_mb_in_slice %x pic_structure %x colocate_adr_offset %x mode_8x8_flags %x colocated_buf_size %x\n", + val, pSlice->first_mb_in_slice, pic->structure, + colocate_adr_offset, pSlice->mode_8x8_flags, + p_H264_Dpb->colocated_buf_size); +#endif + if (pSlice->listXsize[1] > 0) { + struct StorablePicture *colocate_pic = pSlice->listX[1][0]; + /* H264_CO_MB_RD_ADDR[bit 31:30], + * original picture structure of L1[0], + * 00 - top field, 01 - bottom field, + * 10 - frame, 11 - mbaff frame + */ + int l10_structure; + int cur_colocate_ref_type; + /* H264_CO_MB_RD_ADDR[bit 29], top/bot for B field pciture, + * 0 - top, 1 - bot + */ + unsigned int val; +#ifdef ERROR_CHECK + if (colocate_pic == NULL) + return; +#endif + + if (colocate_pic->mb_aff_frame_flag) + l10_structure = 3; + else { + if (colocate_pic->coded_frame) + l10_structure = 2; + else + l10_structure = (colocate_pic->structure == + BOTTOM_FIELD) ? 1 : 0; + } +#if 0 + /*case0016, p16, + cur_colocate_ref_type should be configured base on current pic*/ + if (pic->structure == FRAME && + pic->mb_aff_frame_flag) + cur_colocate_ref_type = 0; + else if (pic->structure == BOTTOM_FIELD) + cur_colocate_ref_type = 1; + else + cur_colocate_ref_type = 0; +#else + dpb_print(hw->dpb.decoder_index, PRINT_FLAG_UCODE_EVT, + " CUR TMP DEBUG : mb_aff_frame_flag : %d, structure : %d coded_frame %d\n", + pic->mb_aff_frame_flag, + pic->structure, + pic->coded_frame); + dpb_print(hw->dpb.decoder_index, PRINT_FLAG_UCODE_EVT, + " COL TMP DEBUG : mb_aff_frame_flag : %d, structure : %d coded_frame %d\n", + colocate_pic->mb_aff_frame_flag, + colocate_pic->structure, + colocate_pic->coded_frame); + if (pic->structure == FRAME || pic->mb_aff_frame_flag) { + cur_colocate_ref_type = + (abs(pic->poc - colocate_pic->top_poc) + < abs(pic->poc - + colocate_pic->bottom_poc)) ? 0 : 1; + } else + cur_colocate_ref_type = + (colocate_pic->structure + == BOTTOM_FIELD) ? 1 : 0; +#endif + +#ifndef ONE_COLOCATE_BUF_PER_DECODE_BUF + if ((colocate_pic->colocated_buf_index >= 0) && + (colocate_pic->colocated_buf_index < + p_H264_Dpb->colocated_buf_count)) { + colocate_rd_adr = p_H264_Dpb->colocated_mv_addr_start + + ((p_H264_Dpb->colocated_buf_size * + colocate_pic->colocated_buf_index) + >> (use_direct_8x8 ? 2 : 0)); + if ((colocate_rd_adr + p_H264_Dpb->colocated_buf_size) > + p_H264_Dpb->colocated_mv_addr_end) + dpb_print(hw->dpb.decoder_index, + PRINT_FLAG_ERROR, + "Error, colocate buf is not enough, index is %d\n", + colocate_pic->colocated_buf_index); + /* bit 31:30 -- L1[0] picture coding structure, + * 00 - top field, 01 - bottom field, + * 10 - frame, 11 - mbaff frame + * bit 29 - L1[0] top/bot for B field pciture, + * 0 - top, 1 - bot + * bit 28:0 h264_co_mb_mem_rd_addr[31:3] + * -- only used for B Picture Direct mode + * [2:0] will set to 3'b000 + */ + /* #define H264_CO_MB_RD_ADDR VLD_C39 0xc39 */ + val = ((colocate_rd_adr+colocate_adr_offset) >> 3) | + (l10_structure << 30) | + (cur_colocate_ref_type << 29); + WRITE_VREG(H264_CO_MB_RD_ADDR, val); + dpb_print(hw->dpb.decoder_index, PRINT_FLAG_UCODE_EVT, + "co idx %d, WRITE_VREG(H264_CO_MB_RD_ADDR) = %x, addr %x L1(0) pic_structure %d mbaff %d\n", + colocate_pic->colocated_buf_index, + val, colocate_rd_adr + colocate_adr_offset, + colocate_pic->structure, + colocate_pic->mb_aff_frame_flag); + } else + dpb_print(hw->dpb.decoder_index, PRINT_FLAG_ERROR, + "Error, reference pic has no colocated buf\n"); +#else + colocate_rd_adr = p_H264_Dpb->colocated_mv_addr_start + + ((p_H264_Dpb->colocated_buf_size * + colocate_pic->buf_spec_num) + >> (use_direct_8x8 ? 2 : 0)); + if ((colocate_rd_adr + p_H264_Dpb->colocated_buf_size) > + p_H264_Dpb->colocated_mv_addr_end) + dpb_print(hw->dpb.decoder_index, PRINT_FLAG_ERROR, + "Error, colocate buf is not enough, pic index is %d\n", + colocate_pic->buf_spec_num); + /* bit 31:30 -- L1[0] picture coding structure, + * 00 - top field, 01 - bottom field, + * 10 - frame, 11 - mbaff frame + * bit 29 - L1[0] top/bot for B field pciture, + * 0 - top, 1 - bot + * bit 28:0 h264_co_mb_mem_rd_addr[31:3] + * -- only used for B Picture Direct mode + * [2:0] will set to 3'b000 + */ + /* #define H264_CO_MB_RD_ADDR VLD_C39 0xc39 */ + val = ((colocate_rd_adr+colocate_adr_offset)>>3) | + (l10_structure << 30) | (cur_colocate_ref_type << 29); + WRITE_VREG(H264_CO_MB_RD_ADDR, val); + dpb_print(hw->dpb.decoder_index, PRINT_FLAG_UCODE_EVT, + "WRITE_VREG(H264_CO_MB_RD_ADDR) = %x, L1(0) pic_structure %d mbaff %d\n", + val, colocate_pic->structure, + colocate_pic->mb_aff_frame_flag); +#endif + } +} + +static int vh264_vf_states(struct vframe_states *states, void *op_arg) +{ + unsigned long flags; + struct vdec_s *vdec = op_arg; + struct vdec_h264_hw_s *hw = (struct vdec_h264_hw_s *)vdec->private; + + spin_lock_irqsave(&hw->lock, flags); + + states->vf_pool_size = VF_POOL_SIZE; + states->buf_free_num = kfifo_len(&hw->newframe_q); + states->buf_avail_num = kfifo_len(&hw->display_q); + + spin_unlock_irqrestore(&hw->lock, flags); + + return 0; +} + +static struct vframe_s *vh264_vf_peek(void *op_arg) +{ + struct vframe_s *vf; + struct vdec_s *vdec = op_arg; + struct vdec_h264_hw_s *hw = (struct vdec_h264_hw_s *)vdec->private; + + if (!hw) + return NULL; + + if (kfifo_peek(&hw->display_q, &vf)) + return vf; + + return NULL; +} + +static struct vframe_s *vh264_vf_get(void *op_arg) +{ + struct vframe_s *vf; + struct vdec_s *vdec = op_arg; + struct vdec_h264_hw_s *hw = (struct vdec_h264_hw_s *)vdec->private; + + if (!hw) + return NULL; + + if (kfifo_get(&hw->display_q, &vf)) { + int time = jiffies; + unsigned int frame_interval = + 1000*(time - hw->last_frame_time)/HZ; + if ((h264_debug_flag & OUTPUT_CURRENT_BUF) == 0) { + struct h264_dpb_stru *p_H264_Dpb = &hw->dpb; + dpb_print(hw->dpb.decoder_index, PRINT_FLAG_UCODE_EVT, + "%s from fs[%d], poc %d buf_spec_num %d vf %p\n", + __func__, vf->index, + p_H264_Dpb->mFrameStore[vf->index].poc, + p_H264_Dpb->mFrameStore[vf->index] + .buf_spec_num, vf); + } + if (hw->last_frame_time > 0) { + dpb_print(hw->dpb.decoder_index, + PRINT_FLAG_TIME_STAMP, + "%s duration %d pts %d interval %dms\r\n", + __func__, vf->duration, vf->pts, frame_interval); + if (frame_interval > + max_get_frame_interval[hw->dpb.decoder_index]) + max_get_frame_interval[hw->dpb.decoder_index] + = frame_interval; + } + hw->last_frame_time = time; + return vf; + } + + return NULL; +} + +static void vh264_vf_put(struct vframe_s *vf, void *op_arg) +{ + struct vdec_s *vdec = op_arg; + struct vdec_h264_hw_s *hw = (struct vdec_h264_hw_s *)vdec->private; + struct h264_dpb_stru *p_H264_Dpb = &hw->dpb; + int buf_spec_num; + + if ((h264_debug_flag & OUTPUT_CURRENT_BUF) == 0) { + buf_spec_num = + get_buf_spec_idx_by_canvas_config(hw, + &vf->canvas0_config[0]); + dpb_print(hw->dpb.decoder_index, PRINT_FLAG_UCODE_EVT, + "%s to fs[%d], poc %d buf_spec_num %d used %d\n", + __func__, vf->index, + p_H264_Dpb->mFrameStore[vf->index].poc, + buf_spec_num, + hw->buffer_spec[buf_spec_num].used); + + if (hw->buffer_spec[buf_spec_num].used != 3) + set_frame_output_flag(&hw->dpb, vf->index); + } + + kfifo_put(&hw->newframe_q, (const struct vframe_s *)vf); + +#define ASSIST_MBOX1_IRQ_REG VDEC_ASSIST_MBOX1_IRQ_REG + if (hw->buffer_empty_flag) + WRITE_VREG(ASSIST_MBOX1_IRQ_REG, 0x1); +} + +static int vh264_event_cb(int type, void *data, void *private_data) +{ + return 0; +} + +static void set_frame_info(struct vdec_h264_hw_s *hw, struct vframe_s *vf, + u32 index) +{ + int force_rate = input_frame_based(hw_to_vdec(hw)) ? + force_rate_framebase : force_rate_streambase; + dpb_print(hw->dpb.decoder_index, PRINT_FLAG_UCODE_EVT, + "%s (%d,%d) dur %d, vf %p, index %d\n", __func__, + hw->frame_width, hw->frame_height, hw->frame_dur, vf, index); + + vf->width = hw->frame_width; + vf->height = hw->frame_height; + if (force_rate) { + if (force_rate == -1) + vf->duration = 0; + else + vf->duration = 96000/force_rate; + } else + vf->duration = hw->frame_dur; + vf->ratio_control = + (min(hw->h264_ar, (u32) DISP_RATIO_ASPECT_RATIO_MAX)) << + DISP_RATIO_ASPECT_RATIO_BIT; + vf->orientation = hw->vh264_rotation; + vf->flag = 0; + + vf->canvas0Addr = vf->canvas1Addr = -1; +#ifdef NV21 + vf->plane_num = 2; +#else + vf->plane_num = 3; +#endif + vf->canvas0_config[0] = hw->buffer_spec[index].canvas_config[0]; + vf->canvas0_config[1] = hw->buffer_spec[index].canvas_config[1]; +#ifndef NV21 + vf->canvas0_config[2] = hw->buffer_spec[index].canvas_config[2]; +#endif + vf->canvas1_config[0] = hw->buffer_spec[index].canvas_config[0]; + vf->canvas1_config[1] = hw->buffer_spec[index].canvas_config[1]; +#ifndef NV21 + vf->canvas1_config[2] = hw->buffer_spec[index].canvas_config[2]; +#endif + + return; +} + +static int get_max_dec_frame_buf_size(int level_idc, + int max_reference_frame_num, int mb_width, + int mb_height) +{ + int pic_size = mb_width * mb_height * 384; + + int size = 0; + + switch (level_idc) { + case 9: + size = 152064; + break; + case 10: + size = 152064; + break; + case 11: + size = 345600; + break; + case 12: + size = 912384; + break; + case 13: + size = 912384; + break; + case 20: + size = 912384; + break; + case 21: + size = 1824768; + break; + case 22: + size = 3110400; + break; + case 30: + size = 3110400; + break; + case 31: + size = 6912000; + break; + case 32: + size = 7864320; + break; + case 40: + size = 12582912; + break; + case 41: + size = 12582912; + break; + case 42: + size = 13369344; + break; + case 50: + size = 42393600; + break; + case 51: + case 52: + default: + size = 70778880; + break; + } + + size /= pic_size; + size = size + 1; /* need one more buffer */ + + if (max_reference_frame_num > size) + size = max_reference_frame_num; + + return size; +} + +static int vh264_set_params(struct vdec_h264_hw_s *hw) +{ + int mb_width, mb_total; + int max_reference_size, level_idc; + int i, mb_height, addr; + int mb_mv_byte; + struct vdec_s *vdec = hw_to_vdec(hw); + int reg_val; + int ret = 0; +#ifdef USE_CMA + unsigned int buf_size; +#endif + + if (hw->set_params_done) { + WRITE_VREG(AV_SCRATCH_0, + (hw->max_reference_size << 24) | + (hw->buffer_spec_num << 16) | + (hw->buffer_spec_num << 8)); + return 0; + } + dpb_print(hw->dpb.decoder_index, PRINT_FLAG_UCODE_EVT, "%s\n", + __func__); + +#ifndef USE_CMA + addr = hw->buf_start; +#endif + + /* Read AV_SCRATCH_1 */ + reg_val = READ_VREG(AV_SCRATCH_1); + hw->seq_info = READ_VREG(AV_SCRATCH_2); + hw->num_units_in_tick = READ_VREG(AV_SCRATCH_4); + hw->time_scale = READ_VREG(AV_SCRATCH_5); + dpb_print(hw->dpb.decoder_index, PRINT_FLAG_UCODE_EVT, "get %x\n", + reg_val); + mb_mv_byte = (reg_val & 0x80000000) ? 24 : 96; + + mb_width = reg_val & 0xff; + mb_total = (reg_val >> 8) & 0xffff; + if (!mb_width && mb_total) /*for 4k2k*/ + mb_width = 256; + + mb_height = mb_total/mb_width; +#if 1 + /* if (hw->frame_width == 0 || hw->frame_height == 0) { */ + hw->frame_width = mb_width * 16; + hw->frame_height = mb_height * 16; + /* } */ + + if (hw->frame_dur == 0) + hw->frame_dur = 96000 / 30; +#endif + + mb_width = (mb_width+3) & 0xfffffffc; + mb_height = (mb_height+3) & 0xfffffffc; + mb_total = mb_width * mb_height; + + reg_val = READ_VREG(AV_SCRATCH_B); + level_idc = reg_val & 0xff; + max_reference_size = (reg_val >> 8) & 0xff; + dpb_print(hw->dpb.decoder_index, PRINT_FLAG_UCODE_EVT, + "mb height/widht/total: %x/%x/%x level_idc %x max_ref_num %x\n", + mb_height, mb_width, mb_total, level_idc, max_reference_size); + + + hw->mb_total = mb_total; + hw->mb_width = mb_width; + hw->mb_height = mb_height; + + hw->dpb.reorder_pic_num = + get_max_dec_frame_buf_size(level_idc, + max_reference_size, mb_width, mb_height); + hw->buffer_spec_num = + hw->dpb.reorder_pic_num + + reorder_dpb_size_margin; + hw->max_reference_size = max_reference_size + reference_buf_margin; + + if (hw->buffer_spec_num > MAX_VF_BUF_NUM) { + hw->buffer_spec_num = MAX_VF_BUF_NUM; + hw->dpb.reorder_pic_num = hw->buffer_spec_num + - reorder_dpb_size_margin; + } + hw->dpb.mDPB.size = hw->buffer_spec_num; + hw->dpb.max_reference_size = hw->max_reference_size; + + pr_info("%s buf_spec_num %d reorder_pic_num %d collocate_buf_num %d\r\n", + __func__, hw->buffer_spec_num, + hw->dpb.reorder_pic_num, + hw->max_reference_size); + +#ifdef USE_CMA + buf_size = (hw->mb_total << 8) + (hw->mb_total << 7); +#endif + for (i = 0; i < hw->buffer_spec_num; i++) { + int canvas = vdec->get_canvas(i, 2); + +#ifdef USE_CMA + if (hw->buffer_spec[i].cma_alloc_count == 0) { + hw->buffer_spec[i].cma_alloc_count = + PAGE_ALIGN(buf_size) / PAGE_SIZE; + hw->buffer_spec[i].cma_alloc_addr = + codec_mm_alloc_for_dma(MEM_NAME, + hw->buffer_spec[i].cma_alloc_count, + 16, CODEC_MM_FLAGS_FOR_VDECODER); + } + + if (!hw->buffer_spec[i].cma_alloc_addr) { + dpb_print(hw->dpb.decoder_index, PRINT_FLAG_ERROR, + "CMA alloc failed, request buf size 0x%lx\n", + hw->buffer_spec[i].cma_alloc_count * PAGE_SIZE); + hw->buffer_spec[i].cma_alloc_count = 0; + ret = -1; + break; + } + hw->buffer_spec[i].buf_adr = + hw->buffer_spec[i].cma_alloc_addr; + addr = hw->buffer_spec[i].buf_adr; +#else + hw->buffer_spec[i].buf_adr = addr; +#endif + + hw->buffer_spec[i].used = 0; + hw->buffer_spec[i].y_addr = addr; + addr += hw->mb_total << 8; + + hw->buffer_spec[i].u_addr = addr; + hw->buffer_spec[i].v_addr = addr; + addr += hw->mb_total << 7; + + hw->buffer_spec[i].y_canvas_index = canvas_y(canvas); + hw->buffer_spec[i].u_canvas_index = canvas_u(canvas); + hw->buffer_spec[i].v_canvas_index = canvas_v(canvas); + + canvas_config(hw->buffer_spec[i].y_canvas_index, + hw->buffer_spec[i].y_addr, + hw->mb_width << 4, + hw->mb_height << 4, + CANVAS_ADDR_NOWRAP, + CANVAS_BLKMODE_32X32); + + hw->buffer_spec[i].canvas_config[0].phy_addr = + hw->buffer_spec[i].y_addr; + hw->buffer_spec[i].canvas_config[0].width = + hw->mb_width << 4; + hw->buffer_spec[i].canvas_config[0].height = + hw->mb_height << 4; + hw->buffer_spec[i].canvas_config[0].block_mode = + CANVAS_BLKMODE_32X32; + + canvas_config(hw->buffer_spec[i].u_canvas_index, + hw->buffer_spec[i].u_addr, + hw->mb_width << 4, + hw->mb_height << 3, + CANVAS_ADDR_NOWRAP, + CANVAS_BLKMODE_32X32); + + hw->buffer_spec[i].canvas_config[1].phy_addr = + hw->buffer_spec[i].u_addr; + hw->buffer_spec[i].canvas_config[1].width = + hw->mb_width << 4; + hw->buffer_spec[i].canvas_config[1].height = + hw->mb_height << 3; + hw->buffer_spec[i].canvas_config[1].block_mode = + CANVAS_BLKMODE_32X32; + + WRITE_VREG(ANC0_CANVAS_ADDR + i, + spec2canvas(&hw->buffer_spec[i])); + + pr_info("config canvas (%d)\r\n", i); + } + + +#ifdef USE_CMA + if (hw->collocate_cma_alloc_count == 0) { + hw->collocate_cma_alloc_count = + PAGE_ALIGN(hw->mb_total * mb_mv_byte * + hw->max_reference_size) / PAGE_SIZE; + hw->collocate_cma_alloc_addr = + codec_mm_alloc_for_dma(MEM_NAME, + hw->collocate_cma_alloc_count, + 16, CODEC_MM_FLAGS_FOR_VDECODER); + } + + if (!hw->collocate_cma_alloc_addr) { + dpb_print(hw->dpb.decoder_index, PRINT_FLAG_ERROR, + "codec_mm alloc failed, request buf size 0x%lx\n", + hw->collocate_cma_alloc_count * PAGE_SIZE); + hw->collocate_cma_alloc_count = 0; + ret = -1; + } else { + hw->dpb.colocated_mv_addr_start = + hw->collocate_cma_alloc_addr; + hw->dpb.colocated_mv_addr_end = + hw->dpb.colocated_mv_addr_start + + (hw->mb_total * mb_mv_byte * hw->max_reference_size); + pr_info("callocate cma %d, %lx, %x\n", + hw->collocate_cma_alloc_count, + hw->collocate_cma_alloc_addr, + hw->dpb.colocated_mv_addr_start); + } +#else + hw->dpb.colocated_mv_addr_start = addr; + hw->dpb.colocated_mv_addr_end = addr + (hw->mb_total * mb_mv_byte + * hw->max_reference_size); +#endif + dpb_print(hw->dpb.decoder_index, PRINT_FLAG_UCODE_EVT, + "colocated_mv_addr_start %x colocated_mv_addr_end %x\n", + hw->dpb.colocated_mv_addr_start, + hw->dpb.colocated_mv_addr_end); + + hw->timing_info_present_flag = hw->seq_info & 0x2; + hw->fixed_frame_rate_flag = 0; + if (hw->timing_info_present_flag) { + hw->fixed_frame_rate_flag = hw->seq_info & 0x40; + + if (((hw->num_units_in_tick * 120) >= hw->time_scale && + ((!hw->sync_outside) || + (!hw->frame_dur))) + && hw->num_units_in_tick && hw->time_scale) { + if (hw->use_idr_framerate || + hw->fixed_frame_rate_flag || + !hw->frame_dur || + !hw->duration_from_pts_done + /*|| vh264_running*/) { + u32 frame_dur_es = + div_u64(96000ULL * 2 * hw->num_units_in_tick, + hw->time_scale); + if (hw->frame_dur != frame_dur_es) { + hw->h264_first_valid_pts_ready = false; + hw->h264pts1 = 0; + hw->h264pts2 = 0; + hw->h264_pts_count = 0; + hw->duration_from_pts_done = 0; + fixed_frame_rate_mode = + FIX_FRAME_RATE_OFF; + hw->pts_duration = 0; + hw->frame_dur = frame_dur_es; + pr_info("frame_dur %d from timing_info\n", + hw->frame_dur); + } + + /*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 infomation + is more reliable + if ((frame_dur * 2) != frame_dur_es) { + frame_dur = frame_dur_es; + }*/ + } + } + } else { + dpb_print(hw->dpb.decoder_index, PRINT_FLAG_UCODE_EVT, + "H.264: timing_info not present\n"); + } + + if (hw->pts_unstable && (hw->fixed_frame_rate_flag == 0)) { + if (((RATE_2397_FPS == hw->frame_dur) + && (dec_control + & DEC_CONTROL_FLAG_FORCE_RATE_2397_FPS_FIX_FRAME_RATE)) + || ((RATE_2997_FPS == + hw->frame_dur) && + (dec_control & + DEC_CONTROL_FLAG_FORCE_RATE_2997_FPS_FIX_FRAME_RATE))) { + dpb_print(hw->dpb.decoder_index, PRINT_FLAG_UCODE_EVT, + "force fix frame rate\n"); + hw->fixed_frame_rate_flag = 0x40; + } + } + + hw->set_params_done = 1; + + WRITE_VREG(AV_SCRATCH_0, (hw->max_reference_size<<24) | + (hw->buffer_spec_num<<16) | + (hw->buffer_spec_num<<8)); + + return ret; +} + +static bool is_buffer_available(struct vdec_s *vdec) +{ + bool buffer_available = 1; + struct vdec_h264_hw_s *hw = (struct vdec_h264_hw_s *)(vdec->private); + struct h264_dpb_stru *p_H264_Dpb = &hw->dpb; + + if ((kfifo_len(&hw->newframe_q) <= 0) || + ((hw->set_params_done) && (!have_free_buf_spec(vdec))) || + ((p_H264_Dpb->mDPB.init_done) && + (p_H264_Dpb->mDPB.used_size == p_H264_Dpb->mDPB.size) && + (is_there_unused_frame_from_dpb(&p_H264_Dpb->mDPB) == 0))) { + dpb_print(hw->dpb.decoder_index, PRINT_FLAG_UCODE_EVT, + "%s, empty, newq(%d), free_spec(%d), initdon(%d), used_size(%d/%d), unused_fr_dpb(%d)\n", + __func__, + kfifo_len(&hw->newframe_q), + have_free_buf_spec(vdec), + p_H264_Dpb->mDPB.init_done, + p_H264_Dpb->mDPB.used_size, p_H264_Dpb->mDPB.size, + is_there_unused_frame_from_dpb(&p_H264_Dpb->mDPB) + ); + buffer_available = 0; + + bufmgr_h264_remove_unused_frame(p_H264_Dpb); + } + + return buffer_available; +} + +static irqreturn_t vh264_isr(struct vdec_s *vdec) +{ + unsigned int dec_dpb_status; + struct vdec_h264_hw_s *hw = (struct vdec_h264_hw_s *)(vdec->private); + struct h264_dpb_stru *p_H264_Dpb = &hw->dpb; + int i; + + WRITE_VREG(ASSIST_MBOX1_CLR_REG, 1); + + if (!hw) { + dpb_print(hw->dpb.decoder_index, PRINT_FLAG_VDEC_STATUS, + "decoder is not running\n"); + return IRQ_HANDLED; + } + + p_H264_Dpb->vdec = vdec; + dec_dpb_status = READ_VREG(DPB_STATUS_REG); + + dpb_print(hw->dpb.decoder_index, PRINT_FLAG_UCODE_EVT, + "%s DPB_STATUS_REG: %x, sb (%x %x %x) bitcnt %x\n", + __func__, + dec_dpb_status, + READ_VREG(VLD_MEM_VIFIFO_LEVEL), + READ_VREG(VLD_MEM_VIFIFO_WP), + READ_VREG(VLD_MEM_VIFIFO_RP), + READ_VREG(VIFF_BIT_CNT)); + + if (dec_dpb_status == H264_CONFIG_REQUEST) { + WRITE_VREG(DPB_STATUS_REG, H264_ACTION_CONFIG_DONE); +#ifdef USE_CMA + if (hw->set_params_done) { + WRITE_VREG(AV_SCRATCH_0, + (hw->max_reference_size<<24) | + (hw->buffer_spec_num<<16) | + (hw->buffer_spec_num<<8)); + } else { + hw->dec_result = DEC_RESULT_CONFIG_PARAM; + schedule_work(&hw->work); + } +#else + if (vh264_set_params(hw) < 0) { + hw->fatal_error_flag = DECODER_FATAL_ERROR_UNKNOW; + if (!hw->fatal_error_reset) + schedule_work(&hw->error_wd_work); + } +#endif + } else if (dec_dpb_status == H264_SLICE_HEAD_DONE) { + int slice_header_process_status = 0; + unsigned short *p = (unsigned short *)hw->lmem_addr; + + dma_sync_single_for_cpu( + amports_get_dma_device(), + hw->lmem_addr_remap, + PAGE_SIZE, + DMA_FROM_DEVICE); +#if 0 + if (p_H264_Dpb->mVideo.dec_picture == NULL) { + if (!is_buffer_available(vdec)) { + hw->buffer_empty_flag = 1; + dpb_print(hw->dpb.decoder_index, + PRINT_FLAG_UCODE_EVT, + "%s, buffer_empty, newframe_q(%d), have_free_buf_spec(%d), init_done(%d), used_size(%d/%d), is_there_unused_frame_from_dpb(%d)\n", + __func__, + kfifo_len(&hw->newframe_q), + have_free_buf_spec(vdec), + p_H264_Dpb->mDPB.init_done, + p_H264_Dpb->mDPB.used_size, + p_H264_Dpb->mDPB.size, + is_there_unused_frame_from_dpb( + &p_H264_Dpb->mDPB)); + return IRQ_HANDLED; + } + } + + hw->buffer_empty_flag = 0; +#endif +#ifdef SEND_PARAM_WITH_REG + for (i = 0; i < (RPM_END-RPM_BEGIN); i++) { + unsigned int data32; + do { + data32 = READ_VREG(RPM_CMD_REG); + /* printk("%x\n", data32); */ + } while ((data32&0x10000) == 0); + p_H264_Dpb->dpb_param.l.data[i] = data32 & 0xffff; + WRITE_VREG(RPM_CMD_REG, 0); + /* printk("%x:%x\n", i,data32); */ + } +#else + for (i = 0; i < (RPM_END-RPM_BEGIN); i += 4) { + int ii; + for (ii = 0; ii < 4; ii++) { + p_H264_Dpb->dpb_param.l.data[i+ii] = + p[i+3-ii]; + } + } +#endif + dpb_print(hw->dpb.decoder_index, PRINT_FLAG_UCODE_EVT, + "current dpb index %d, poc %d, top/bot poc (%d,%d)\n", + p_H264_Dpb->dpb_param.dpb.current_dpb_index, + val(p_H264_Dpb->dpb_param.dpb.frame_pic_order_cnt), + val(p_H264_Dpb->dpb_param.dpb.top_field_pic_order_cnt), + val(p_H264_Dpb->dpb_param.dpb.top_field_pic_order_cnt)); + + slice_header_process_status = + h264_slice_header_process(p_H264_Dpb); + + if (p_H264_Dpb->mVideo.dec_picture) { + if (slice_header_process_status == 1) { + dpb_print(hw->dpb.decoder_index, + PRINT_FLAG_UCODE_EVT, + "==================> frame count %d\n", + hw->decode_pic_count+1); + } + config_decode_buf(hw, p_H264_Dpb->mVideo.dec_picture); + } + if (slice_header_process_status == 1) + WRITE_VREG(DPB_STATUS_REG, H264_ACTION_DECODE_NEWPIC); + else + WRITE_VREG(DPB_STATUS_REG, H264_ACTION_DECODE_SLICE); + hw->last_mby_mbx = 0; + hw->last_ucode_watchdog_reg_val = 0; + hw->decode_timeout_count = 2; + } else if (dec_dpb_status == H264_PIC_DATA_DONE) { + if (p_H264_Dpb->mVideo.dec_picture) { + if (hw->chunk) { + p_H264_Dpb->mVideo.dec_picture->pts = + hw->chunk->pts; + p_H264_Dpb->mVideo.dec_picture->pts64 = + hw->chunk->pts64; + } else { + struct StorablePicture *pic = + p_H264_Dpb->mVideo.dec_picture; + u32 offset = pic->offset_delimiter_lo | + (pic->offset_delimiter_hi << 16); + if (pts_lookup_offset_us64(PTS_TYPE_VIDEO, + offset, &pic->pts, 0, &pic->pts64)) { + pic->pts = 0; + pic->pts64 = 0; + } + } + store_picture_in_dpb(p_H264_Dpb, + p_H264_Dpb->mVideo.dec_picture); + + bufmgr_post(p_H264_Dpb); + + p_H264_Dpb->mVideo.dec_picture = NULL; + /* dump_dpb(&p_H264_Dpb->mDPB); */ + } + + if ((h264_debug_flag&ONLY_RESET_AT_START) == 0) + amvdec_stop(); + hw->decode_pic_count++, + dpb_print(hw->dpb.decoder_index, PRINT_FLAG_VDEC_STATUS, + "%s H264_PIC_DATA_DONE decode slice count %d\n", + __func__, + hw->decode_pic_count); + /* WRITE_VREG(DPB_STATUS_REG, H264_ACTION_SEARCH_HEAD); */ + hw->dec_result = DEC_RESULT_DONE; + reset_process_time(hw); + schedule_work(&hw->work); + } else if (/*(dec_dpb_status == H264_DATA_REQUEST) ||*/ + (dec_dpb_status == H264_SEARCH_BUFEMPTY) || + (dec_dpb_status == H264_DECODE_BUFEMPTY) || + (dec_dpb_status == H264_DECODE_TIMEOUT)) { +empty_proc: + if (p_H264_Dpb->mVideo.dec_picture) { + remove_picture(p_H264_Dpb, + p_H264_Dpb->mVideo.dec_picture); + p_H264_Dpb->mVideo.dec_picture = NULL; + } + + if (input_frame_based(vdec) || + (READ_VREG(VLD_MEM_VIFIFO_LEVEL) > 0x200)) { + if (h264_debug_flag & + DISABLE_ERROR_HANDLE) { + dpb_print(hw->dpb.decoder_index, + PRINT_FLAG_ERROR, + "%s decoding error, level 0x%x\n", + __func__, + READ_VREG(VLD_MEM_VIFIFO_LEVEL)); + goto send_again; + } + if ((h264_debug_flag & ONLY_RESET_AT_START) == 0) + amvdec_stop(); + dpb_print(hw->dpb.decoder_index, PRINT_FLAG_UCODE_EVT, + "%s %s\n", __func__, + (dec_dpb_status == H264_SEARCH_BUFEMPTY) ? + "H264_SEARCH_BUFEMPTY" : + (dec_dpb_status == H264_DECODE_BUFEMPTY) ? + "H264_DECODE_BUFEMPTY" : "H264_DECODE_TIMEOUT"); + hw->dec_result = DEC_RESULT_DONE; + + if (dec_dpb_status == H264_SEARCH_BUFEMPTY) + hw->search_dataempty_num++; + else if (dec_dpb_status == H264_DECODE_TIMEOUT) + hw->decode_timeout_num++; + else if (dec_dpb_status == H264_DECODE_BUFEMPTY) + hw->decode_dataempty_num++; + + + reset_process_time(hw); + schedule_work(&hw->work); + } else { + /* WRITE_VREG(DPB_STATUS_REG, H264_ACTION_INIT); */ + dpb_print(hw->dpb.decoder_index, PRINT_FLAG_VDEC_STATUS, + "%s DEC_RESULT_AGAIN\n", __func__); +send_again: + hw->dec_result = DEC_RESULT_AGAIN; + schedule_work(&hw->work); + } + } else if (dec_dpb_status == H264_DATA_REQUEST) { + if (input_frame_based(vdec)) { + dpb_print(hw->dpb.decoder_index, + PRINT_FLAG_VDEC_STATUS, + "%s H264_DATA_REQUEST\n", __func__); + hw->dec_result = DEC_RESULT_GET_DATA; + schedule_work(&hw->work); + } else + goto empty_proc; + } + + /* ucode debug */ + if (READ_VREG(DEBUG_REG1) & 0x10000) { + int i; + unsigned short *p = (unsigned short *)hw->lmem_addr; + + dma_sync_single_for_cpu( + amports_get_dma_device(), + hw->lmem_addr_remap, + PAGE_SIZE, + DMA_FROM_DEVICE); + + pr_info("LMEM<tag %x>:\n", READ_VREG(DEBUG_REG1)); + for (i = 0; i < 0x400; i += 4) { + int ii; + if ((i & 0xf) == 0) + pr_info("%03x: ", i); + for (ii = 0; ii < 4; ii++) + pr_info("%04x ", p[i+3-ii]); + if (((i+ii) & 0xf) == 0) + pr_info("\n"); + } + WRITE_VREG(DEBUG_REG1, 0); + } else if (READ_VREG(DEBUG_REG1) != 0) { + pr_info("dbg%x: %x\n", READ_VREG(DEBUG_REG1), + READ_VREG(DEBUG_REG2)); + WRITE_VREG(DEBUG_REG1, 0); + } + /**/ + + return IRQ_HANDLED; +} + +static void timeout_process(struct vdec_h264_hw_s *hw) +{ + hw->timeout_num++; + if ((h264_debug_flag & ONLY_RESET_AT_START) == 0) + amvdec_stop(); + dpb_print(hw->dpb.decoder_index, + PRINT_FLAG_ERROR, "%s decoder timeout\n", __func__); + hw->dec_result = DEC_RESULT_DONE; + reset_process_time(hw); + schedule_work(&hw->work); +} + +static void check_timer_func(unsigned long arg) +{ + struct vdec_h264_hw_s *hw = (struct vdec_h264_hw_s *)arg; + + if ((h264_debug_cmd & 0x100) != 0 && + hw_to_vdec(hw)->id == (h264_debug_cmd & 0xff)) { + hw->dec_result = DEC_RESULT_DONE; + schedule_work(&hw->work); + pr_info("vdec %d is forced to be disconnected\n", + h264_debug_cmd & 0xff); + h264_debug_cmd = 0; + return; + } + + if (hw_to_vdec(hw)->next_status == VDEC_STATUS_DISCONNECTED) { + hw->dec_result = DEC_RESULT_DONE; + schedule_work(&hw->work); + pr_info("vdec requested to be disconnected\n"); + return; + } + + 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 ((input_frame_based(hw_to_vdec(hw)) || + (READ_VREG(VLD_MEM_VIFIFO_LEVEL) > 0x200)) && + ((h264_debug_flag & DISABLE_ERROR_HANDLE) == 0) && + (decode_timeout_val > 0) && + (hw->start_process_time > 0) && + ((1000 * (jiffies - hw->start_process_time) / HZ) + > decode_timeout_val) + ) { + u32 dpb_status = READ_VREG(DPB_STATUS_REG); + u32 mby_mbx = READ_VREG(MBY_MBX); + if ((dpb_status == H264_ACTION_DECODE_NEWPIC) || + (dpb_status == H264_ACTION_DECODE_SLICE)) { + if (hw->last_mby_mbx == mby_mbx) { + if (hw->decode_timeout_count > 0) + hw->decode_timeout_count--; + if (hw->decode_timeout_count == 0) + timeout_process(hw); + } + } else if (is_in_parsing_state(dpb_status)) { + if (hw->last_ucode_watchdog_reg_val == + READ_VREG(UCODE_WATCHDOG_REG)) { + if (hw->decode_timeout_count > 0) + hw->decode_timeout_count--; + if (hw->decode_timeout_count == 0) + timeout_process(hw); + } + } + hw->last_ucode_watchdog_reg_val = + READ_VREG(UCODE_WATCHDOG_REG); + hw->last_mby_mbx = mby_mbx; + } + + hw->check_timer.expires = jiffies + CHECK_INTERVAL; + + add_timer(&hw->check_timer); +} + +static int dec_status(struct vdec_s *vdec, struct vdec_status *vstatus) +{ + struct vdec_h264_hw_s *hw = (struct vdec_h264_hw_s *)vdec->private; + vstatus->width = hw->frame_width; + vstatus->height = hw->frame_height; + if (hw->frame_dur != 0) + vstatus->fps = 96000 / hw->frame_dur; + else + vstatus->fps = -1; + vstatus->error_count = READ_VREG(AV_SCRATCH_D); + vstatus->status = hw->stat; + /* if (fatal_error_reset) + vstatus->status |= hw->fatal_error_flag; */ + return 0; +} + +static int vh264_hw_ctx_restore(struct vdec_h264_hw_s *hw) +{ + int i; + + /* if (hw->init_flag == 0) { */ + if (h264_debug_flag & 0x40000000) { + /* if (1) */ + dpb_print(hw->dpb.decoder_index, PRINT_FLAG_VDEC_STATUS, + "%s, reset register\n", __func__); + + 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)); + } else { + /* WRITE_VREG(POWER_CTL_VLD, + READ_VREG(POWER_CTL_VLD) | (0 << 10) | (1 << 9) ); */ + 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); + + /* clear mailbox interrupt */ + WRITE_VREG(ASSIST_MBOX1_CLR_REG, 1); + + /* enable mailbox interrupt */ + WRITE_VREG(ASSIST_MBOX1_MASK, 1); + +#ifdef NV21 + SET_VREG_MASK(MDEC_PIC_DC_CTRL, 1<<17); +#endif + +#if 1 /* #if MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON8 */ + /* pr_info("vh264 meson8 prot init\n"); */ + WRITE_VREG(MDEC_PIC_DC_THRESH, 0x404038aa); +#endif + if (hw->decode_pic_count > 0) { + WRITE_VREG(AV_SCRATCH_7, (hw->max_reference_size << 24) | + (hw->buffer_spec_num << 16) | + (hw->buffer_spec_num << 8)); + for (i = 0; i < hw->buffer_spec_num; i++) { + canvas_config(hw->buffer_spec[i].y_canvas_index, + hw->buffer_spec[i].y_addr, + hw->mb_width << 4, + hw->mb_height << 4, + CANVAS_ADDR_NOWRAP, + CANVAS_BLKMODE_32X32); + + canvas_config(hw->buffer_spec[i].u_canvas_index, + hw->buffer_spec[i].u_addr, + hw->mb_width << 4, + hw->mb_height << 3, + CANVAS_ADDR_NOWRAP, + CANVAS_BLKMODE_32X32); + + WRITE_VREG(ANC0_CANVAS_ADDR + i, + spec2canvas(&hw->buffer_spec[i])); + } + } else { + WRITE_VREG(AV_SCRATCH_0, 0); + WRITE_VREG(AV_SCRATCH_9, 0); + } + + if (hw->init_flag == 0) + WRITE_VREG(DPB_STATUS_REG, 0); + else + WRITE_VREG(DPB_STATUS_REG, H264_ACTION_DECODE_START); + + WRITE_VREG(FRAME_COUNTER_REG, hw->decode_pic_count); + WRITE_VREG(AV_SCRATCH_8, hw->buf_offset); + WRITE_VREG(AV_SCRATCH_G, hw->mc_dma_handle); + + /* hw->error_recovery_mode = (error_recovery_mode != 0) ? + error_recovery_mode : error_recovery_mode_in; */ + /* WRITE_VREG(AV_SCRATCH_F, + (READ_VREG(AV_SCRATCH_F) & 0xffffffc3) ); */ + WRITE_VREG(AV_SCRATCH_F, (READ_VREG(AV_SCRATCH_F) & 0xffffffc3) | + ((error_recovery_mode_in & 0x1) << 4)); + if (hw->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(LMEM_DUMP_ADR, (u32)hw->lmem_addr_remap); +#if 1 /* #if MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON8 */ + WRITE_VREG(MDEC_PIC_DC_THRESH, 0x404038aa); +#endif + + WRITE_VREG(DEBUG_REG1, 0); + WRITE_VREG(DEBUG_REG2, 0); + return 0; +} + +static unsigned char amvdec_enable_flag; +static void vh264_local_init(struct vdec_h264_hw_s *hw) +{ + int i; + hw->decode_timeout_count = 0; + + hw->vh264_ratio = hw->vh264_amstream_dec_info.ratio; + /* vh264_ratio = 0x100; */ + + hw->vh264_rotation = (((unsigned long) + hw->vh264_amstream_dec_info.param) >> 16) & 0xffff; + + hw->frame_prog = 0; + hw->frame_width = hw->vh264_amstream_dec_info.width; + hw->frame_height = hw->vh264_amstream_dec_info.height; + hw->frame_dur = hw->vh264_amstream_dec_info.rate; + hw->pts_outside = ((unsigned long) + hw->vh264_amstream_dec_info.param) & 0x01; + hw->sync_outside = ((unsigned long) + hw->vh264_amstream_dec_info.param & 0x02) >> 1; + hw->use_idr_framerate = ((unsigned long) + hw->vh264_amstream_dec_info.param & 0x04) >> 2; + hw->max_refer_buf = !(((unsigned long) + hw->vh264_amstream_dec_info.param & 0x10) >> 4); + if (hw->frame_dur < 96000/960) { + /*more than 960fps,it should not be a correct value, + give default 30fps*/ + hw->frame_dur = 96000/30; + } + + pr_info + ("H264 sysinfo: %dx%d duration=%d, pts_outside=%d, ", + hw->frame_width, hw->frame_height, hw->frame_dur, hw->pts_outside); + pr_info("sync_outside=%d, use_idr_framerate=%d\n", + hw->sync_outside, hw->use_idr_framerate); + + if ((unsigned long) hw->vh264_amstream_dec_info.param & 0x08) + hw->ucode_type = UCODE_IP_ONLY_PARAM; + else + hw->ucode_type = 0; + + if ((unsigned long) hw->vh264_amstream_dec_info.param & 0x20) + error_recovery_mode_in = 1; + else + error_recovery_mode_in = 3; + + INIT_KFIFO(hw->display_q); + INIT_KFIFO(hw->newframe_q); + + for (i = 0; i < VF_POOL_SIZE; i++) { + const struct vframe_s *vf = &hw->vfpool[i]; + hw->vfpool[i].index = -1; /* VF_BUF_NUM; */ + hw->vfpool[i].bufWidth = 1920; + kfifo_put(&hw->newframe_q, vf); + } + + hw->duration_from_pts_done = 0; + + hw->p_last_vf = NULL; + hw->fatal_error_flag = 0; + hw->vh264_stream_switching_state = SWITCHING_STATE_OFF; + + INIT_WORK(&hw->work, vh264_work); + + return; +} + +static s32 vh264_init(struct vdec_h264_hw_s *hw) +{ + /* int trickmode_fffb = 0; */ + int firmwareloaded = 0; + + hw->init_flag = 0; + hw->set_params_done = 0; + hw->start_process_time = 0; + + /* pr_info("\nvh264_init\n"); */ + /* init_timer(&hw->recycle_timer); */ + + /* timer init */ + init_timer(&hw->check_timer); + + hw->check_timer.data = (unsigned long)hw; + hw->check_timer.function = check_timer_func; + hw->check_timer.expires = jiffies + CHECK_INTERVAL; + + /* add_timer(&hw->check_timer); */ + hw->stat |= STAT_TIMER_ARM; + + hw->duration_on_correcting = 0; + hw->fixed_frame_rate_check_count = 0; + hw->saved_resolution = 0; + + vh264_local_init(hw); + + if (!amvdec_enable_flag) { + amvdec_enable_flag = true; + amvdec_enable(); + } + + /* -- ucode loading (amrisc and swap code) */ + hw->mc_cpu_addr = + dma_alloc_coherent(amports_get_dma_device(), MC_TOTAL_SIZE, + &hw->mc_dma_handle, GFP_KERNEL); + if (!hw->mc_cpu_addr) { + amvdec_enable_flag = false; + amvdec_disable(); + + pr_info("vh264_init: Can not allocate mc memory.\n"); + return -ENOMEM; + } + + pr_info("264 ucode swap area: phyaddr %p, cpu vaddr %p\n", + (void *)hw->mc_dma_handle, hw->mc_cpu_addr); + if (!firmwareloaded) { + int ret = 0, size = -1; + char *buf = vmalloc(0x1000 * 16); + if (IS_ERR_OR_NULL(buf)) + return -ENOMEM; + + pr_info("start load orignal firmware ...\n"); + + size = get_firmware_data(VIDEO_DEC_H264_MULTI, buf); + if (size < 0) { + pr_err("get firmware fail.\n"); + vfree(buf); + return -1; + } + + /*ret = amvdec_loadmc_ex(VFORMAT_H264, NULL, buf);*/ + + /*header*/ + memcpy((u8 *) hw->mc_cpu_addr + MC_OFFSET_HEADER, + buf + 0x4000, MC_SWAP_SIZE); + /*data*/ + memcpy((u8 *) hw->mc_cpu_addr + MC_OFFSET_DATA, + buf + 0x2000, MC_SWAP_SIZE); + /*mmco*/ + memcpy((u8 *) hw->mc_cpu_addr + MC_OFFSET_MMCO, + buf + 0x6000, MC_SWAP_SIZE); + /*list*/ + memcpy((u8 *) hw->mc_cpu_addr + MC_OFFSET_LIST, + buf + 0x3000, MC_SWAP_SIZE); + /*slice*/ + memcpy((u8 *) hw->mc_cpu_addr + MC_OFFSET_SLICE, + buf + 0x5000, MC_SWAP_SIZE); + /*main*/ + memcpy((u8 *) hw->mc_cpu_addr + MC_OFFSET_MAIN, + buf, 0x2000); + /*data*/ + memcpy((u8 *) hw->mc_cpu_addr + MC_OFFSET_MAIN + 0x2000, + buf + 0x2000, 0x1000); + /*slice*/ + memcpy((u8 *) hw->mc_cpu_addr + MC_OFFSET_MAIN + 0x3000, + buf + 0x5000, 0x1000); + + vfree(buf); + + if (ret < 0) { + dpb_print(hw->dpb.decoder_index, PRINT_FLAG_ERROR, + "264 load orignal firmware error.\n"); + amvdec_disable(); + if (hw->mc_cpu_addr) { + dma_free_coherent(amports_get_dma_device(), + MC_TOTAL_SIZE, hw->mc_cpu_addr, + hw->mc_dma_handle); + hw->mc_cpu_addr = NULL; + } + return -EBUSY; + } + } + +#if 1 /* #ifdef BUFFER_MGR_IN_C */ + dpb_init_global(&hw->dpb, + hw_to_vdec(hw)->id, 0, 0); + hw->lmem_addr = __get_free_page(GFP_KERNEL); + if (!hw->lmem_addr) { + pr_info("%s: failed to alloc lmem_addr\n", __func__); + return -ENOMEM; + } else { + hw->lmem_addr_remap = dma_map_single( + amports_get_dma_device(), + (void *)hw->lmem_addr, + PAGE_SIZE, DMA_FROM_DEVICE); + if (dma_mapping_error(amports_get_dma_device(), + hw->lmem_addr_remap)) { + dpb_print(hw->dpb.decoder_index, PRINT_FLAG_ERROR, + "%s: failed to map lmem_addr\n", __func__); + free_page(hw->lmem_addr); + hw->lmem_addr = 0; + hw->lmem_addr_remap = 0; + return -ENOMEM; + } + + pr_info("%s, vaddr=%lx phy_addr=%p\n", + __func__, hw->lmem_addr, (void *)hw->lmem_addr_remap); + } + /* BUFFER_MGR_IN_C */ +#endif + hw->stat |= STAT_MC_LOAD; + + /* add memory barrier */ + wmb(); + + return 0; +} + +static int vh264_stop(struct vdec_h264_hw_s *hw, int mode) +{ + int i; + + if (hw->stat & STAT_MC_LOAD) { + if (hw->mc_cpu_addr != NULL) { + dma_free_coherent(amports_get_dma_device(), + MC_TOTAL_SIZE, hw->mc_cpu_addr, + hw->mc_dma_handle); + hw->mc_cpu_addr = NULL; + } + } +#ifdef USE_CMA + if (hw->cma_alloc_addr) { + pr_info("codec_mm release buffer 0x%lx\n", hw->cma_alloc_addr); + codec_mm_free_for_dma(MEM_NAME, hw->cma_alloc_addr); + hw->cma_alloc_count = 0; + } + + if (hw->collocate_cma_alloc_addr) { + dpb_print(hw->dpb.decoder_index, PRINT_FLAG_ERROR, + "codec_mm release collocate buffer 0x%lx\n", + hw->collocate_cma_alloc_addr); + codec_mm_free_for_dma(MEM_NAME, hw->collocate_cma_alloc_addr); + hw->collocate_cma_alloc_count = 0; + } + + for (i = 0; i < hw->buffer_spec_num; i++) { + if (hw->buffer_spec[i].cma_alloc_addr) { + pr_info("codec_mm release buffer_spec[%d], 0x%lx\n", i, + hw->buffer_spec[i].cma_alloc_addr); + codec_mm_free_for_dma(MEM_NAME, + hw->buffer_spec[i].cma_alloc_addr); + hw->buffer_spec[i].cma_alloc_count = 0; + } + } + +#endif + + if (hw->lmem_addr_remap) { + dma_unmap_single(amports_get_dma_device(), + hw->lmem_addr_remap, + PAGE_SIZE, DMA_FROM_DEVICE); + hw->lmem_addr_remap = 0; + } + if (hw->lmem_addr) { + free_page(hw->lmem_addr); + hw->lmem_addr = 0; + } + cancel_work_sync(&hw->work); + + /* amvdec_disable(); */ + + return 0; +} + +static void vh264_work(struct work_struct *work) +{ + struct vdec_h264_hw_s *hw = container_of(work, + struct vdec_h264_hw_s, work); + struct vdec_s *vdec = hw_to_vdec(hw); + + /* finished decoding one frame or error, + * notify vdec core to switch context + */ + dpb_print(hw->dpb.decoder_index, PRINT_FLAG_VDEC_DETAIL, + "%s %x %x %x\n", __func__, + READ_VREG(0xc47), READ_VREG(0xc45), READ_VREG(0xc46)); + +#ifdef USE_CMA + if (hw->dec_result == DEC_RESULT_CONFIG_PARAM) { + if (vh264_set_params(hw) < 0) { + hw->fatal_error_flag = DECODER_FATAL_ERROR_UNKNOWN; + if (!hw->fatal_error_reset) + schedule_work(&hw->error_wd_work); + } + return; + } else +#endif + if ((hw->dec_result == DEC_RESULT_GET_DATA) || + (hw->dec_result == DEC_RESULT_GET_DATA_RETRY)) { + if (hw->dec_result == DEC_RESULT_GET_DATA) { + dpb_print(hw->dpb.decoder_index, PRINT_FLAG_VDEC_STATUS, + "%s DEC_RESULT_GET_DATA %x %x %x\n", + __func__, + READ_VREG(VLD_MEM_VIFIFO_LEVEL), + READ_VREG(VLD_MEM_VIFIFO_WP), + READ_VREG(VLD_MEM_VIFIFO_RP)); + vdec_vframe_dirty(vdec, hw->chunk); + vdec_clean_input(vdec); + } + + if (is_buffer_available(vdec)) { + int r; + r = vdec_prepare_input(vdec, &hw->chunk); + if (r < 0) { + hw->dec_result = DEC_RESULT_GET_DATA_RETRY; + + dpb_print(hw->dpb.decoder_index, + PRINT_FLAG_VDEC_DETAIL, + "ammvdec_vh264: Insufficient data\n"); + + schedule_work(&hw->work); + return; + } + hw->dec_result = DEC_RESULT_NONE; + dpb_print(hw->dpb.decoder_index, PRINT_FLAG_VDEC_STATUS, + "%s: chunk size 0x%x\n", + __func__, hw->chunk->size); + WRITE_VREG(POWER_CTL_VLD, + READ_VREG(POWER_CTL_VLD) | + (0 << 10) | (1 << 9) | (1 << 6)); + WRITE_VREG(H264_DECODE_INFO, (1<<13)); + WRITE_VREG(H264_DECODE_SIZE, hw->chunk->size); + WRITE_VREG(VIFF_BIT_CNT, (hw->chunk->size * 8)); + vdec_enable_input(vdec); + + WRITE_VREG(DPB_STATUS_REG, H264_ACTION_SEARCH_HEAD); + } else{ + hw->dec_result = DEC_RESULT_GET_DATA_RETRY; + + dpb_print(hw->dpb.decoder_index, PRINT_FLAG_VDEC_DETAIL, + "ammvdec_vh264: Insufficient data\n"); + + schedule_work(&hw->work); + } + return; + } else if (hw->dec_result == DEC_RESULT_DONE) { + /* if (!hw->ctx_valid) + hw->ctx_valid = 1; */ + dpb_print(hw->dpb.decoder_index, PRINT_FLAG_VDEC_STATUS, + "%s dec_result %d %x %x %x\n", + __func__, + hw->dec_result, + READ_VREG(VLD_MEM_VIFIFO_LEVEL), + READ_VREG(VLD_MEM_VIFIFO_WP), + READ_VREG(VLD_MEM_VIFIFO_RP)); + vdec_vframe_dirty(hw_to_vdec(hw), hw->chunk); + } else { + dpb_print(hw->dpb.decoder_index, PRINT_FLAG_VDEC_DETAIL, + "%s dec_result %d %x %x %x\n", + __func__, + hw->dec_result, + READ_VREG(VLD_MEM_VIFIFO_LEVEL), + READ_VREG(VLD_MEM_VIFIFO_WP), + READ_VREG(VLD_MEM_VIFIFO_RP)); + } + + del_timer_sync(&hw->check_timer); + hw->stat &= ~STAT_TIMER_ARM; + + /* mark itself has all HW resource released and input released */ + vdec_set_status(hw_to_vdec(hw), VDEC_STATUS_CONNECTED); + + if (hw->vdec_cb) + hw->vdec_cb(hw_to_vdec(hw), hw->vdec_cb_arg); +} + +static bool run_ready(struct vdec_s *vdec) +{ + if (vdec->master) + return false; + + if ((!input_frame_based(vdec)) && (start_decode_buf_level > 0)) { + u32 rp, wp; + u32 level; + + rp = READ_MPEG_REG(PARSER_VIDEO_RP); + wp = READ_MPEG_REG(PARSER_VIDEO_WP); + + if (wp < rp) + level = vdec->input.size + wp - rp; + else + level = wp - rp; + + if (level < start_decode_buf_level) { + struct vdec_h264_hw_s *hw = + (struct vdec_h264_hw_s *)vdec->private; + dpb_print(hw->dpb.decoder_index, PRINT_FLAG_VDEC_DETAIL, + "%s vififo level low %x(<%x) (lev%x wp%x rp%x)\n", + __func__, level, + start_decode_buf_level, + READ_VREG(VLD_MEM_VIFIFO_LEVEL), + READ_VREG(VLD_MEM_VIFIFO_WP), + READ_VREG(VLD_MEM_VIFIFO_RP)); + return 0; + } + } + + if (h264_debug_flag & 0x20000000) { + /* pr_info("%s, a\n", __func__); */ + return 1; + } else { + return is_buffer_available(vdec); + } +} + +static void run(struct vdec_s *vdec, + void (*callback)(struct vdec_s *, void *), void *arg) +{ + struct vdec_h264_hw_s *hw = + (struct vdec_h264_hw_s *)vdec->private; + int size; + + hw->vdec_cb_arg = arg; + hw->vdec_cb = callback; + + /* hw->chunk = vdec_prepare_input(vdec); */ + size = vdec_prepare_input(vdec, &hw->chunk); + if ((size < 0) || + (input_frame_based(vdec) && hw->chunk == NULL)) { + hw->dec_result = DEC_RESULT_AGAIN; + + dpb_print(hw->dpb.decoder_index, PRINT_FLAG_VDEC_DETAIL, + "ammvdec_vh264: Insufficient data\n"); + + schedule_work(&hw->work); + return; + } + + hw->dec_result = DEC_RESULT_NONE; +#if 0 + if ((!input_frame_based(vdec)) && (start_decode_buf_level > 0)) { + if (READ_VREG(VLD_MEM_VIFIFO_LEVEL) < + start_decode_buf_level) { + dpb_print(hw->dpb.decoder_index, + PRINT_FLAG_VDEC_DETAIL, + "%s: VIFIFO_LEVEL %x is low (<%x)\n", + __func__, + READ_VREG(VLD_MEM_VIFIFO_LEVEL), + start_decode_buf_level); + + hw->dec_result = DEC_RESULT_AGAIN; + schedule_work(&hw->work); + return; + } + } +#endif + + if (input_frame_based(vdec)) { + u8 *data = ((u8 *)hw->chunk->block->start_virt) + + hw->chunk->offset; + + dpb_print(hw->dpb.decoder_index, PRINT_FLAG_VDEC_STATUS, + "%s: size 0x%x %02x %02x %02x %02x %02x %02x .. %02x %02x %02x %02x\n", + __func__, size, + data[0], data[1], data[2], data[3], + data[4], data[5], data[size - 4], + data[size - 3], data[size - 2], + data[size - 1]); + } else + dpb_print(hw->dpb.decoder_index, PRINT_FLAG_VDEC_STATUS, + "%s: %x %x %x size 0x%x\n", + __func__, + READ_VREG(VLD_MEM_VIFIFO_LEVEL), + READ_VREG(VLD_MEM_VIFIFO_WP), + READ_VREG(VLD_MEM_VIFIFO_RP), size); + + hw->start_process_time = jiffies; + + if (((h264_debug_flag & ONLY_RESET_AT_START) == 0) || + (hw->init_flag == 0)) { + if (amvdec_vdec_loadmc_ex(vdec, "vmh264_mc") < 0) { + amvdec_enable_flag = false; + amvdec_disable(); + pr_info("%s: Error amvdec_vdec_loadmc fail\n", + __func__); + return; + } + + if (vh264_hw_ctx_restore(hw) < 0) { + schedule_work(&hw->work); + return; + } + if (input_frame_based(vdec)) { + WRITE_VREG(H264_DECODE_INFO, (1<<13)); + WRITE_VREG(H264_DECODE_SIZE, hw->chunk->size); + WRITE_VREG(VIFF_BIT_CNT, (hw->chunk->size * 8)); + } else { + if (size <= 0) + size = 0x7fffffff; /*error happen*/ + WRITE_VREG(H264_DECODE_INFO, (1<<13)); + WRITE_VREG(H264_DECODE_SIZE, size); + WRITE_VREG(VIFF_BIT_CNT, size * 8); + } + + vdec_enable_input(vdec); + + add_timer(&hw->check_timer); + + amvdec_start(); + + /* if (hw->init_flag) { */ + WRITE_VREG(DPB_STATUS_REG, H264_ACTION_SEARCH_HEAD); + /* } */ + + hw->init_flag = 1; + } else { + WRITE_VREG(H264_DECODE_INFO, (1 << 13)); + vdec_enable_input(vdec); + + WRITE_VREG(DPB_STATUS_REG, H264_ACTION_SEARCH_HEAD); + } +} + +static void reset(struct vdec_s *vdec) +{ + pr_info("ammvdec_h264: reset.\n"); + +#if 0 + struct vdec_h264_hw_s *hw = (struct vdec_h264_hw_s *)vdec->private; + + hw->init_flag = 0; + hw->set_params_done = 0; + + vh264_local_init(hw); + + dpb_init_global(&hw->dpb); +#endif +} + +static int ammvdec_h264_probe(struct platform_device *pdev) +{ + struct vdec_s *pdata = *(struct vdec_s **)pdev->dev.platform_data; + struct vdec_h264_hw_s *hw = NULL; + + if (pdata == NULL) { + pr_info("\nammvdec_h264 memory resource undefined.\n"); + return -EFAULT; + } + + hw = (struct vdec_h264_hw_s *)devm_kzalloc(&pdev->dev, + sizeof(struct vdec_h264_hw_s), GFP_KERNEL); + if (hw == NULL) { + pr_info("\nammvdec_h264 device data allocation failed\n"); + return -ENOMEM; + } + + pdata->private = hw; + pdata->dec_status = dec_status; + /* pdata->set_trickmode = set_trickmode; */ + pdata->run_ready = run_ready; + pdata->run = run; + pdata->reset = reset; + pdata->irq_handler = vh264_isr; + + pdata->id = pdev->id; + + if (pdata->use_vfm_path) + snprintf(pdata->vf_provider_name, VDEC_PROVIDER_NAME_SIZE, + VFM_DEC_PROVIDER_NAME); + else if (vdec_dual(pdata)) { + snprintf(pdata->vf_provider_name, VDEC_PROVIDER_NAME_SIZE, + (pdata->master) ? VFM_DEC_DVEL_PROVIDER_NAME : + VFM_DEC_DVBL_PROVIDER_NAME); + } else + snprintf(pdata->vf_provider_name, VDEC_PROVIDER_NAME_SIZE, + PROVIDER_NAME ".%02x", pdev->id & 0xff); + + vf_provider_init(&pdata->vframe_provider, pdata->vf_provider_name, + &vf_provider_ops, pdata); + + platform_set_drvdata(pdev, pdata); + + hw->platform_dev = pdev; +#ifndef USE_CMA + hw->buf_start = pdata->mem_start; + hw->buf_size = pdata->mem_end - pdata->mem_start + 1; + /* hw->ucode_map_start = pdata->mem_start; */ + if (hw->buf_size < DEFAULT_MEM_SIZE) { + pr_info("\nammvdec_h264 memory size not enough.\n"); + return -ENOMEM; + } +#endif + +#ifdef USE_CMA + hw->cma_dev = pdata->cma_dev; + if (hw->cma_alloc_count == 0) { + hw->cma_alloc_count = PAGE_ALIGN(V_BUF_ADDR_OFFSET) / PAGE_SIZE; + hw->cma_alloc_addr = codec_mm_alloc_for_dma(MEM_NAME, + hw->cma_alloc_count, + 4, CODEC_MM_FLAGS_FOR_VDECODER); + } + if (!hw->cma_alloc_addr) { + dpb_print(hw->dpb.decoder_index, PRINT_FLAG_ERROR, + "codec_mm alloc failed, request buf size 0x%lx\n", + hw->cma_alloc_count * PAGE_SIZE); + hw->cma_alloc_count = 0; + return -ENOMEM; + } + hw->buf_offset = hw->cma_alloc_addr - DEF_BUF_START_ADDR; +#else + hw->buf_offset = pdata->mem_start - DEF_BUF_START_ADDR; + hw->buf_start = V_BUF_ADDR_OFFSET + pdata->mem_start; +#endif + + if (pdata->sys_info) + hw->vh264_amstream_dec_info = *pdata->sys_info; + if (NULL == hw->sei_data_buffer) { + hw->sei_data_buffer = + dma_alloc_coherent(amports_get_dma_device(), + USER_DATA_SIZE, + &hw->sei_data_buffer_phys, GFP_KERNEL); + if (!hw->sei_data_buffer) { + pr_info("%s: Can not allocate sei_data_buffer\n", + __func__); + 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); */ + } +#ifdef USE_CMA + pr_info("ammvdec_h264 mem-addr=%lx,buff_offset=%x,buf_start=%lx\n", + pdata->mem_start, hw->buf_offset, hw->cma_alloc_addr); +#else + pr_info("ammvdec_h264 mem-addr=%lx,buff_offset=%x,buf_start=%lx\n", + pdata->mem_start, hw->buf_offset, hw->buf_start); +#endif + + vdec_source_changed(VFORMAT_H264, 3840, 2160, 60); + + if (vh264_init(hw) < 0) { + pr_info("\nammvdec_h264 init failed.\n"); + return -ENODEV; + } + atomic_set(&hw->vh264_active, 1); + + return 0; +} + +static int ammvdec_h264_remove(struct platform_device *pdev) +{ + struct vdec_h264_hw_s *hw = + (struct vdec_h264_hw_s *) + (((struct vdec_s *)(platform_get_drvdata(pdev)))->private); + + atomic_set(&hw->vh264_active, 0); + + if (hw->stat & STAT_TIMER_ARM) { + del_timer_sync(&hw->check_timer); + hw->stat &= ~STAT_TIMER_ARM; + } + + vh264_stop(hw, MODE_FULL); + + /* vdec_source_changed(VFORMAT_H264, 0, 0, 0); */ + + atomic_set(&hw->vh264_active, 0); + + vdec_set_status(hw_to_vdec(hw), VDEC_STATUS_DISCONNECTED); + + return 0; +} + +/****************************************/ + +static struct platform_driver ammvdec_h264_driver = { + .probe = ammvdec_h264_probe, + .remove = ammvdec_h264_remove, +#ifdef CONFIG_PM + .suspend = amvdec_suspend, + .resume = amvdec_resume, +#endif + .driver = { + .name = DRIVER_NAME, + } +}; + +static struct codec_profile_t ammvdec_h264_profile = { + .name = "mh264", + .profile = "" +}; + +static int __init ammvdec_h264_driver_init_module(void) +{ + pr_info("ammvdec_h264 module init\n"); + if (platform_driver_register(&ammvdec_h264_driver)) { + pr_info("failed to register ammvdec_h264 driver\n"); + return -ENODEV; + } + vcodec_profile_register(&ammvdec_h264_profile); + return 0; +} + +static void __exit ammvdec_h264_driver_remove_module(void) +{ + pr_info("ammvdec_h264 module remove.\n"); + + platform_driver_unregister(&ammvdec_h264_driver); +} + +/****************************************/ + +module_param(h264_debug_flag, uint, 0664); +MODULE_PARM_DESC(h264_debug_flag, "\n ammvdec_h264 h264_debug_flag\n"); + +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(fixed_frame_rate_mode, uint, 0664); +MODULE_PARM_DESC(fixed_frame_rate_mode, "\namvdec_h264 fixed_frame_rate_mode\n"); + +module_param(decode_timeout_val, uint, 0664); +MODULE_PARM_DESC(decode_timeout_val, "\n amvdec_h264 decode_timeout_val\n"); + +module_param(reorder_dpb_size_margin, uint, 0664); +MODULE_PARM_DESC(reorder_dpb_size_margin, "\n ammvdec_h264 reorder_dpb_size_margin\n"); + +module_param(reference_buf_margin, uint, 0664); +MODULE_PARM_DESC(reference_buf_margin, "\n ammvdec_h264 reference_buf_margin\n"); + +module_param(radr, uint, 0664); +MODULE_PARM_DESC(radr, "\nradr\n"); + +module_param(rval, uint, 0664); +MODULE_PARM_DESC(rval, "\nrval\n"); + +module_param(h264_debug_mask, uint, 0664); +MODULE_PARM_DESC(h264_debug_mask, "\n amvdec_h264 h264_debug_mask\n"); + +module_param(h264_debug_cmd, uint, 0664); +MODULE_PARM_DESC(h264_debug_cmd, "\n amvdec_h264 h264_debug_cmd\n"); + +module_param(force_rate_streambase, int, 0664); +MODULE_PARM_DESC(force_rate_streambase, "\n amvdec_h264 force_rate_streambase\n"); + +module_param(dec_control, int, 0664); +MODULE_PARM_DESC(dec_control, "\n amvdec_h264 dec_control\n"); + +module_param(force_rate_framebase, int, 0664); +MODULE_PARM_DESC(force_rate_framebase, "\n amvdec_h264 force_rate_framebase\n"); + +/* +module_param(trigger_task, uint, 0664); +MODULE_PARM_DESC(trigger_task, "\n amvdec_h264 trigger_task\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(step, uint, &max_decode_instance_num, 0664); + +module_init(ammvdec_h264_driver_init_module); +module_exit(ammvdec_h264_driver_remove_module); + +MODULE_DESCRIPTION("AMLOGIC H264 Video Decoder Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/frame_provider/decoder/h265/vh265.c b/drivers/frame_provider/decoder/h265/vh265.c new file mode 100644 index 0000000..7e09b7b --- a/dev/null +++ b/drivers/frame_provider/decoder/h265/vh265.c @@ -0,0 +1,8969 @@ +/* + * 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" +#include "../utils/decoder_bmmu_box.h" +#include "../utils/config_parser.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.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 MAX_DECODE_INSTANCE_NUM 12 +#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) + +struct hevc_state_s; +static int hevc_print(struct hevc_state_s *hevc, + int debug_flag, const char *fmt, ...); +static int hevc_print_cont(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); +static bool run_ready(struct vdec_s *vdec); +static void reset_process_time(struct hevc_state_s *hevc); +static void start_process_time(struct hevc_state_s *hevc); +static void timeout_process(struct hevc_state_s *hevc); +#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_check_timer_func(unsigned long arg); +static void config_decode_mode(struct hevc_state_s *hevc); + +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; + +static unsigned int start_decode_buf_level = 0x8000; + +static unsigned int decode_timeout_val; +#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 */ +/* hevc->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_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_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 IGNORE_PARAM_FROM_CONFIG 0x08000000 +#define PRINT_FRAMEBASE_DATA 0x10000000 +#define PRINT_FLAG_VDEC_STATUS 0x20000000 +#define PRINT_FLAG_VDEC_DETAIL 0x40000000 +#endif +#define MAX_BUF_NUM 24 +#define MAX_REF_PIC_NUM 24 +#define MAX_REF_ACTIVE 16 + +#define BMMU_MAX_BUFFERS (MAX_BUF_NUM + 1) +#define BMMU_WORKSPACE_ID (MAX_BUF_NUM) + +const u32 h265_version = 201602101; +static u32 debug_mask = 0xffffffff; +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 max_buf_num = 16; +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; + +#define AUX_BUF_ALIGN(adr) ((adr + 0xf) & (~0xf)) +static u32 prefix_aux_buf_size; +static u32 suffix_aux_buf_size; + +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; + /* + parser_sei_enable: + bit 0, sei; + bit 1, sei_suffix (fill aux buf) + bit 2, fill sei to aux buf (when bit 0 is 1) + bit 8, debug flag + */ +static u32 parser_sei_enable; +#ifdef CONFIG_AM_VDEC_DV +static u32 parser_dolby_vision_enable; +#endif +/* this is only for h265 mmu enable */ + +#ifdef CONFIG_MULTI_DEC +static u32 mmu_enable; +#else +static u32 mmu_enable = 1; +#endif + +#ifdef MULTI_INSTANCE_SUPPORT +static u32 work_buf_size = 48 * 1024 * 1024; +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]; + +#ifdef CONFIG_MULTI_DEC +static unsigned char get_idx(struct hevc_state_s *hevc); +#endif + +#ifdef CONFIG_AM_VDEC_DV +static u32 dv_toggle_prov_name; + +static u32 dv_debug; +#endif +#endif + + +#ifdef CONFIG_MULTI_DEC +#define get_dbg_flag(hevc) ((debug_mask & (1 << hevc->index)) ? debug : 0) +#define get_dbg_flag2(hevc) ((debug_mask & (1 << get_idx(hevc))) ? debug : 0) +#else +#define get_dbg_flag(hevc) debug +#define get_dbg_flag2(hevc) debug +#define get_double_write_mode(hevc) double_write_mode +#define get_buf_alloc_width(hevc) buf_alloc_width +#define get_buf_alloc_height(hevc) buf_alloc_height +#define get_dynamic_buf_num_margin(hevc) dynamic_buf_num_margin +#endif +#define get_buffer_mode(hevc) buffer_mode + + +DEFINE_SPINLOCK(lock); +struct task_struct *h265_task = NULL; +#undef DEBUG_REG +#ifdef DEBUG_REG +void WRITE_VREG_DBG(unsigned adr, unsigned 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_DATA_REQUEST 0x12 + +#define HEVC_DECODE_BUFEMPTY 0x20 +#define HEVC_DECODE_TIMEOUT 0x21 +#define HEVC_SEARCH_BUFEMPTY 0x22 + +#define HEVC_FIND_NEXT_PIC_NAL 0x50 +#define HEVC_FIND_NEXT_DVEL_NAL 0x51 + +#define HEVC_DUMP_LMEM 0x30 + +#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 +#ifdef ENABLE_SWAP_TEST +#define HEVC_STREAM_SWAP_TEST HEVC_ASSIST_SCRATCH_L +#endif + +/*#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 + /*do not define ENABLE_SWAP_TEST*/ +#define HEVC_AUX_ADR HEVC_ASSIST_SCRATCH_L +#define HEVC_AUX_DATA_SIZE HEVC_ASSIST_SCRATCH_M + +#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/fetch SEI in ucode; + 1, parse/fetch SEI in ucode +bit [18]: for NAL_SEI_SUFFIX when bit0 is 0: + 0, do not fetch NAL_SEI_SUFFIX to aux buf; + 1, fetch NAL_SEL_SUFFIX data to aux buf +bit [19]: + 0, parse NAL_SEI in ucode + 1, fetch NAL_SEI to aux buf +bit [20]: for DOLBY_VISION_META + 0, do not fetch DOLBY_VISION_META to aux buf + 1, fetch DOLBY_VISION_META to aux buf +*/ +#define NAL_SEARCH_CTL HEVC_ASSIST_SCRATCH_I + /*read only*/ +#define CUR_NAL_UNIT_TYPE HEVC_ASSIST_SCRATCH_J + /*set before start decoder*/ +#define HEVC_DECODE_MODE HEVC_ASSIST_SCRATCH_J +#define DECODE_STOP_POS HEVC_ASSIST_SCRATCH_K + +#define DECODE_MODE_SINGLE 0x0 +#define DECODE_MODE_MULTI_FRAMEBASE 0x1 +#define DECODE_MODE_MULTI_STREAMBASE 0x2 +#define DECODE_MODE_MULTI_DVBAL 0x3 +#define DECODE_MODE_MULTI_DVENL 0x4 + +#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 (get_dbg_flag2(hevc) & 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 (get_dbg_flag2(hevc) & 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 (get_dbg_flag2(hevc)) { + 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 (get_dbg_flag2(hevc)) { + 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 ((get_dbg_flag2(hevc) + & + 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; + /*buffer */ + 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; +#ifdef CONFIG_AM_VDEC_DV + unsigned char dv_enhance_exist; +#endif + char *aux_data_buf; + int aux_data_size; + unsigned long cma_alloc_addr; + 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; + + u32 pts; + u64 pts64; +} /*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 SEI_CONTENT_LIGHT_LEVEL_MASK 0x00000002 + +#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 +#define DEC_RESULT_GET_DATA 7 +#define DEC_RESULT_GET_DATA_RETRY 8 + +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; + /* timeout handle */ + unsigned long int start_process_time; + unsigned last_lcu_idx; + unsigned decode_timeout_count; + unsigned timeout_num; +#ifdef CONFIG_AM_VDEC_DV + unsigned char switch_dvlayer_flag; +#endif + unsigned start_parser_type; +#endif + char *provider_name; + int index; + struct device *cma_dev; + unsigned char m_ins_flag; + unsigned char dolby_enhance_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; + + u32 prefix_aux_size; + u32 suffix_aux_size; + void *aux_addr; + void *rpm_addr; + void *lmem_addr; + dma_addr_t aux_phy_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; + + + 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; + void *bmmu_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]; + /* data for SEI_CONTENT_LIGHT_LEVEL */ + unsigned int content_light_level[2]; + + struct PIC_s *pre_top_pic; + struct PIC_s *pre_bot_pic; + +#ifdef MULTI_INSTANCE_SUPPORT + int double_write_mode; + int buf_alloc_width; + int buf_alloc_height; + int dynamic_buf_num_margin; + int start_action; +#endif +} /*hevc_stru_t */; + +#ifdef CONFIG_MULTI_DEC +static int get_double_write_mode(struct hevc_state_s *hevc) +{ + return hevc->double_write_mode; +} + +static int get_buf_alloc_width(struct hevc_state_s *hevc) +{ + return hevc->buf_alloc_width; +} + +static int get_buf_alloc_height(struct hevc_state_s *hevc) +{ + return hevc->buf_alloc_height; +} + +static int get_dynamic_buf_num_margin(struct hevc_state_s *hevc) +{ + return hevc->dynamic_buf_num_margin; +} +#endif + +#ifdef CONFIG_MULTI_DEC +static unsigned char get_idx(struct hevc_state_s *hevc) +{ + return hevc->index; +} +#endif + +#undef pr_info +#define pr_info printk +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 CONFIG_MULTI_DEC + if (hevc == NULL || + (flag == 0) || + ((debug_mask & + (1 << hevc->index)) + && (debug & flag))) { +#endif + va_list args; + + va_start(args, fmt); + if (hevc) + len = sprintf(buf, "[%d]", hevc->index); + vsnprintf(buf + len, HEVC_PRINT_BUF - len, fmt, args); + pr_info("%s", buf); + va_end(args); +#ifdef CONFIG_MULTI_DEC + } +#endif + return 0; +} + +static int hevc_print_cont(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 CONFIG_MULTI_DEC + if (hevc == NULL || + (flag == 0) || + ((debug_mask & + (1 << hevc->index)) + && (debug & flag))) { +#endif + va_list args; + va_start(args, fmt); + vsnprintf(buf + len, HEVC_PRINT_BUF - len, fmt, args); + pr_info("%s", buf); + va_end(args); +#ifdef CONFIG_MULTI_DEC + } +#endif + return 0; +} + +static void set_canvas(struct hevc_state_s *hevc, struct PIC_s *pic); + +static void release_aux_data(struct hevc_state_s *hevc, + 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->prefix_aux_size = 0; + hevc->suffix_aux_size = 0; + hevc->aux_addr = NULL; + 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] = NULL; + 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; +#ifdef MULTI_INSTANCE_SUPPORT + hevc->start_process_time = 0; + hevc->last_lcu_idx = 0; + hevc->decode_timeout_count = 0; + hevc->timeout_num = 0; +#endif +} + +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); + /* hevc_print(hevc, 0, "%x\n", data32); */ + } while ((data32 & 0x10000) == 0); + params->l.data[i] = data32 & 0xffff; + /* hevc_print(hevc, 0, "%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 == NULL || 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 == NULL || 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 (get_dbg_flag(hevc)) { + hevc_print(hevc, 0, + "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) +{ + + if (hevc->mmu_box) + decoder_mmu_box_free(hevc->mmu_box); + hevc->mmu_box = NULL; + + if (hevc->bmmu_box) + decoder_bmmu_box_free(hevc->bmmu_box); + hevc->bmmu_box = NULL; +} +static int init_mmu_buffers(struct hevc_state_s *hevc) +{ + if (mmu_enable) { + hevc->mmu_box = decoder_mmu_box_alloc_box(DRIVER_NAME, + hevc->index, + MAX_REF_PIC_NUM, + 64 * SZ_1M + ); + if (!hevc->mmu_box) { + pr_err("h265 alloc mmu box failed!!\n"); + return -1; + } + } + hevc->bmmu_box = decoder_bmmu_box_alloc_box(DRIVER_NAME, + hevc->index, + BMMU_MAX_BUFFERS, + 4 + PAGE_SHIFT, + CODEC_MM_FLAGS_CMA_CLEAR | + CODEC_MM_FLAGS_FOR_VDECODER); + if (!hevc->bmmu_box) { + if (hevc->mmu_box) + decoder_mmu_box_free(hevc->mmu_box); + hevc->mmu_box = NULL; + pr_err("h265 alloc mmu box failed!!\n"); + return -1; + } + return 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 (get_dynamic_buf_num_margin(hevc) > 0) + hevc->used_buf_num = hevc->sps_num_reorder_pics_0 + + get_dynamic_buf_num_margin(hevc); + 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 (get_dbg_flag(hevc)) + hevc_print(hevc, 0, + "[Buffer Management] init_buf_list:\n"); + } else { + int pic_width = get_buf_alloc_width(hevc) + ? get_buf_alloc_width(hevc) : hevc->pic_w; + int pic_height = + get_buf_alloc_height(hevc) + ? get_buf_alloc_height(hevc) : 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 (get_double_write_mode(hevc)) { + int pic_width_dw = ((get_double_write_mode(hevc) == 2) || + (get_double_write_mode(hevc) == 3)) ? + pic_width / 4 : pic_width; + int pic_height_dw = ((get_double_write_mode(hevc) == 2) || + (get_double_write_mode(hevc) == 3)) ? + pic_height / 4 : 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 (mmu_enable) { + if (get_double_write_mode(hevc) == 1) + buf_size += (mc_buffer_size_h << 16); + } else { + if ((get_double_write_mode(hevc) & 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 (get_dbg_flag(hevc)) { + hevc_print(hevc, 0, + "init_buf_list num %d (width %d height %d):\n", + hevc->used_buf_num, pic_width, pic_height); + } + } + + hevc_print(hevc, 0, "allocate begin\n"); + //get_cma_alloc_ref();//DEBUG_TMP + 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 (get_dbg_flag(hevc)) { + hevc_print(hevc, 0, + "%s maximum buf size is used\n", + __func__); + } + break; + } + } + + + if (!mmu_enable) { + hevc->m_BUF[i].index = i; + + if (use_cma == 2) + hevc->use_cma_flag = 1; + if (hevc->use_cma_flag) { + if (decoder_bmmu_box_alloc_idx_wait( + hevc->bmmu_box, + i, + buf_size, + -1, + -1, + BMMU_ALLOC_FLAGS_WAITCLEAR + ) < 0) { + /* + not enough mem for buffer. + */ + hevc_print(hevc, 0, + "not enought buffer for [%d],%d\n", + i, buf_size); + hevc->m_BUF[i].start_adr = 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].start_adr = + decoder_bmmu_box_get_phy_addr( + hevc->bmmu_box, + i); + pr_debug("allocate cma buffer[%d] %ld\n", + i, + hevc->m_BUF[i].start_adr); + } else { + hevc->m_BUF[i].start_adr = + hevc->mc_buf->buf_start + i * buf_size; + if (((hevc->m_BUF[i].start_adr + buf_size) > + mc_buffer_end)) { + if (get_dbg_flag(hevc)) { + hevc_print(hevc, 0, + "Max mc buffer or mpred_mv buffer is used\n"); + } + break; + } + } + hevc->m_BUF[i].size = buf_size; + hevc->m_BUF[i].free_start_adr = + hevc->m_BUF[i].start_adr; + + if (get_dbg_flag(hevc)) { + hevc_print(hevc, 0, + "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();//DEBUG_TMP + hevc_print(hevc, 0, "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) && + get_buf_alloc_width(hevc)) ? + get_buf_alloc_width(hevc) : hevc->pic_w; + int pic_height = ((re_config_pic_flag == 0) && + get_buf_alloc_height(hevc)) ? + get_buf_alloc_height(hevc) : 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 (get_double_write_mode(hevc)) { + int pic_width_dw = ((get_double_write_mode(hevc) == 2) || + (get_double_write_mode(hevc) == 3)) ? + pic_width / 4 : pic_width; + int pic_height_dw = ((get_double_write_mode(hevc) == 2) || + (get_double_write_mode(hevc) == 3)) ? + pic_height / 4 : 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 (mmu_enable) { + if (get_double_write_mode(hevc) == 1) + buf_size += (mc_buffer_size_h << 16); + } else { + if ((get_double_write_mode(hevc) & 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)) { + hevc_print(hevc, 0, + "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 (get_dbg_flag(hevc)&H265_DEBUG_BUFMGR) { + hevc_print(hevc, 0, + "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 (get_double_write_mode(hevc)) { + 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 { + if (decoder_bmmu_box_alloc_idx_wait( + hevc->bmmu_box, + pic->BUF_index, + buf_size, + -1, + -1, + BMMU_ALLOC_FLAGS_WAITCLEAR + ) < 0) { + return -1; + } + pic->cma_alloc_addr = + decoder_bmmu_box_get_phy_addr( + hevc->bmmu_box, + pic->BUF_index); + 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 && get_double_write_mode(hevc) & 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 (get_double_write_mode(hevc)) { + 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 (get_dbg_flag(hevc)) { + hevc_print(hevc, 0, + "%s index %d BUF_index %d mc_y_adr %x ", + __func__, pic->index, + pic->BUF_index, pic->mc_y_adr); +#ifdef LOSLESS_COMPRESS_MODE + hevc_print_cont(hevc, 0, + "comp_body_size %x comp_buf_size %x ", + pic->comp_body_size, pic->buf_size); + hevc_print_cont(hevc, 0, + "mpred_mv_wr_start_adr %x\n", + pic->mpred_mv_wr_start_addr); + if (mmu_enable && get_double_write_mode(hevc)) + hevc_print(hevc, 0, + "mmu double write adr %ld\n", + pic->cma_alloc_addr); + +#else + hevc_print(hevc, 0, + ("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 == NULL || 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 (get_dbg_flag(hevc)) { + hevc_print(hevc, 0, + "%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 == NULL || 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; +} + +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 = + vmalloc(sizeof(struct PIC_s)); + if (pic == NULL) { + hevc_print(hevc, 0, + "alloc pic %d fail\n", i); + break; + } + memset(pic, 0, sizeof(struct PIC_s)); + hevc->m_PIC[i] = pic; + pic->index = i; + pic->BUF_index = -1; + if (config_pic(hevc, pic, disp_addr) < 0) { + if (get_dbg_flag(hevc)) + hevc_print(hevc, 0, + "Config_pic %d fail\n", pic->index); + pic->index = -1; + i++; + break; + } + pic->width = hevc->pic_w; + pic->height = hevc->pic_h; + if (get_double_write_mode(hevc)) + set_canvas(hevc, pic); + } + + for (; i < MAX_REF_PIC_NUM; i++) { + struct PIC_s *pic = + vmalloc(sizeof(struct PIC_s)); + if (pic == NULL) { + hevc_print(hevc, 0, + "alloc pic %d fail\n", i); + break; + } + memset(pic, 0, sizeof(struct PIC_s)); + hevc->m_PIC[i] = pic; + pic->index = -1; + pic->BUF_index = -1; + } + +} + +static void uninit_pic_list(struct hevc_state_s *hevc) +{ + int i; + for (i = 0; i < MAX_REF_PIC_NUM; i++) { + struct PIC_s *pic = hevc->m_PIC[i]; + if (pic) { + release_aux_data(hevc, pic); + vfree(pic); + hevc->m_PIC[i] = NULL; + } + } +} + +#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); + } + + if (!hevc->m_ins_flag) + hevc_print(hevc, 0, + "%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] == NULL || + 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 (get_double_write_mode(hevc) & 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); + } + } + if (cur_pic_num == 0) + return; + 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 ((get_double_write_mode(hevc) & 0x10) == 0) + init_decode_head_hw(hevc); +#endif + +} + + +static void dump_pic_list(struct hevc_state_s *hevc) +{ + int i; + struct PIC_s *pic; + hevc_print(hevc, 0, + "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 == NULL || pic->index == -1) + continue; + hevc_print_cont(hevc, 0, + "index %d decode_idx:%d, POC:%d, referenced:%d, ", + pic->index, pic->decode_idx, pic->POC, pic->referenced); + hevc_print_cont(hevc, 0, + "num_reorder_pic:%d, output_mark:%d, w/h %d,%d", + pic->num_reorder_pic, pic->output_mark, + pic->width, pic->height); + hevc_print_cont(hevc, 0, + "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 == NULL || + (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 == NULL || + (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 == NULL || + (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; + hevc_print(hevc, 0, + "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 (get_dbg_flag(hevc) & H265_DEBUG_BUFMGR) + hevc_print(hevc, 0, + "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 (get_dbg_flag(hevc) & H265_DEBUG_BUFMGR) { + hevc_print_cont(hevc, 0, + "refid %x mc_canvas_u_v %x", + i, pic->mc_canvas_u_v); + hevc_print_cont(hevc, 0, + " mc_canvas_y %x\n", + pic->mc_canvas_y); + } + } else { + if (get_dbg_flag(hevc)) { + hevc_print_cont(hevc, 0, + "Error %s, %dth poc (%d)", + __func__, i, + cur_pic->m_aiRefPOCList0[cur_pic-> + slice_idx][i]); + hevc_print_cont(hevc, 0, + " 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 (get_dbg_flag(hevc) & H265_DEBUG_BUFMGR) + hevc_print(hevc, 0, + "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 (get_dbg_flag(hevc) & H265_DEBUG_BUFMGR) { + hevc_print_cont(hevc, 0, + "refid %x mc_canvas_u_v %x", + i, pic->mc_canvas_u_v); + hevc_print_cont(hevc, 0, + " mc_canvas_y %x\n", + pic->mc_canvas_y); + } + } else { + if (get_dbg_flag(hevc)) { + hevc_print_cont(hevc, 0, + "Error %s, %dth poc (%d)", + __func__, i, + cur_pic->m_aiRefPOCList1[cur_pic-> + slice_idx][i]); + hevc_print_cont(hevc, 0, + " 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 == NULL || + 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); + /* hevc_print(hevc, 0, + "RefPicSetStCurr0 %x %x %x\n", + RefPicSetStCurr0[num_neg], pic->POC, + (0x800-(params[i]&0x7ff))); */ + num_neg++; + } else { + RefPicSetStCurr1[num_pos] = pic->POC + delt; + /* hevc_print(hevc, 0, + "RefPicSetStCurr1 %d\n", + RefPicSetStCurr1[num_pos]); */ + num_pos++; + } + } + } + total_num = num_neg + num_pos; + if (get_dbg_flag(hevc) & H265_DEBUG_BUFMGR) { + hevc_print(hevc, 0, + "%s: curpoc %d slice_type %d, total %d ", + __func__, pic->POC, params->p.slice_type, total_num); + hevc_print_cont(hevc, 0, + "num_neg %d num_list0 %d num_list1 %d\n", + num_neg, num_ref_idx_l0_active, num_ref_idx_l1_active); + } + + if (get_dbg_flag(hevc) & H265_DEBUG_BUFMGR) { + hevc_print(hevc, 0, + "HEVC Stream buf start "); + hevc_print_cont(hevc, 0, + "%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 (get_dbg_flag(hevc) & H265_DEBUG_BUFMGR) + hevc_print(hevc, 0, "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 (get_dbg_flag(hevc) & H265_DEBUG_BUFMGR) { + hevc_print_cont(hevc, 0, "%d ", + pic->m_aiRefPOCList0[pic-> + slice_idx] + [rIdx]); + } + } + } else { + if (get_dbg_flag(hevc) & H265_DEBUG_BUFMGR) + hevc_print(hevc, 0, "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 (get_dbg_flag(hevc) & H265_DEBUG_BUFMGR) { + hevc_print_cont(hevc, 0, "%d ", + pic->m_aiRefPOCList0[pic-> + slice_idx] + [rIdx]); + } + } + } + if (get_dbg_flag(hevc) & H265_DEBUG_BUFMGR) + hevc_print_cont(hevc, 0, "\n"); + if (params->p.slice_type == B_SLICE) { + if (params->p.modification_flag & 0x2) { + if (get_dbg_flag(hevc) & H265_DEBUG_BUFMGR) + hevc_print(hevc, 0, + "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 (get_dbg_flag(hevc) & + H265_DEBUG_BUFMGR) { + hevc_print_cont(hevc, 0, "%d ", + pic-> + m_aiRefPOCList1[pic-> + slice_idx] + [rIdx]); + } + } + } else { + if (get_dbg_flag(hevc) & + H265_DEBUG_BUFMGR) + hevc_print(hevc, 0, "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 (get_dbg_flag(hevc) & + H265_DEBUG_BUFMGR) { + hevc_print_cont(hevc, 0, "%d ", + pic-> + m_aiRefPOCList1[pic-> + slice_idx] + [rIdx]); + } + } + } + if (get_dbg_flag(hevc) & H265_DEBUG_BUFMGR) + hevc_print_cont(hevc, 0, "\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; + hevc_print(hevc, 0, + "%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; + hevc_print(hevc, 0, + "%s: num_tile_columns_minus1 (%d) error!!\n", + __func__, params->p.num_tile_columns_minus1); + } + if (get_dbg_flag(hevc) & H265_DEBUG_BUFMGR) { + hevc_print(hevc, 0, + "%s pic_w_cu %d pic_h_cu %d tile_enabled ", + __func__, pic_width_cu, pic_height_cu); + hevc_print_cont(hevc, 0, + "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 (get_dbg_flag(hevc) & + H265_DEBUG_BUFMGR) { + hevc_print_cont(hevc, 0, + "{y=%d, x=%d w %d h %d ", + i, j, hevc->m_tile[i][j].width, + hevc->m_tile[i][j].height); + hevc_print_cont(hevc, 0, + "start_x %d start_y %d ", + hevc->m_tile[i][j].start_cu_x, + hevc->m_tile[i][j].start_cu_y); + hevc_print_cont(hevc, 0, + "sao_vb_start 0x%x ", + hevc->m_tile[i][j]. + sao_vb_start_addr); + hevc_print_cont(hevc, 0, + "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 (get_dbg_flag(hevc) & + H265_DEBUG_BUFMGR) { + hevc_print_cont(hevc, 0, + "{y=%d, x=%d w %d h %d ", + i, j, hevc->m_tile[i][j].width, + hevc->m_tile[i][j].height); + hevc_print_cont(hevc, 0, + "start_x %d start_y %d ", + hevc->m_tile[i][j].start_cu_x, + hevc->m_tile[i][j].start_cu_y); + hevc_print_cont(hevc, 0, + "sao_vb_start 0x%x ", + hevc->m_tile[i][j]. + sao_vb_start_addr); + hevc_print_cont(hevc, 0, + "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 (get_dbg_flag(hevc)) { + hevc_print(hevc, 0, + "%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 (get_dbg_flag(hevc)) { + hevc_print(hevc, 0, + " 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 (get_dbg_flag(hevc)) + hevc_print(hevc, 0, + "%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 ((get_dbg_flag(hevc) & 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 (get_dbg_flag(hevc) & 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 (get_dbg_flag(hevc) & H265_DEBUG_BUFMGR) + hevc_print(hevc, 0, + "[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) + hevc_print(hevc, 0, + "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 (get_dbg_flag(hevc) & H265_DEBUG_BUFMGR) + hevc_print(hevc, 0, + "[test.c] Enable BitStream Fetch\n"); + ; + if (!hevc->m_ins_flag) { + 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); + + if (get_dbg_flag(hevc) & H265_DEBUG_BUFMGR) + hevc_print(hevc, 0, + "[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 (get_dbg_flag(hevc) & H265_DEBUG_BUFMGR) + hevc_print(hevc, 0, + "[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 (get_dbg_flag(hevc) & H265_DEBUG_BUFMGR) { + hevc_print(hevc, 0, + "[test.c] Initial IQIT_SCALELUT memory --"); + hevc_print_cont(hevc, 0, + " 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); +#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_COUNT, 0);*/ + /* Send parser_cmd */ + if (get_dbg_flag(hevc)) + hevc_print(hevc, 0, + "[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 */ + /* + hevc_print(hevc, 0, "[test.c] Start MPRED\n"); + WRITE_VREG(HEVC_MPRED_INT_STATUS, + (1<<31) + ); + */ + + if (get_dbg_flag(hevc)) + hevc_print(hevc, 0, + "[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 (get_double_write_mode(hevc) & 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 (get_double_write_mode(hevc) & 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); + return; +} +#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_aux_buf(struct hevc_state_s *hevc) +{ + WRITE_VREG(HEVC_AUX_ADR, hevc->aux_phy_addr); + WRITE_VREG(HEVC_AUX_DATA_SIZE, + ((hevc->prefix_aux_size >> 4) << 16) | + (hevc->suffix_aux_size >> 4) + ); +} + +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 (get_dbg_flag(hevc) & H265_DEBUG_BUFMGR) { + hevc_print(hevc, 0, + "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 ((get_double_write_mode(hevc) & 0x10) == 0) { + data32 = READ_VREG(HEVC_SAO_CTRL5); + data32 &= (~(0xff << 16)); + if (get_double_write_mode(hevc) != 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 (get_double_write_mode(hevc)) + WRITE_VREG(HEVC_SAO_Y_START_ADDR, cur_pic->dw_y_adr); + + if ((get_double_write_mode(hevc) & 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 (get_double_write_mode(hevc)) + 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 (get_double_write_mode(hevc)) { + 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 (get_double_write_mode(hevc) == 0) + data32 |= 0x2; /*disable double write*/ + else if (!mmu_enable && (get_double_write_mode(hevc) & 0x10)) + data32 |= 0x1; /*disable cm*/ + WRITE_VREG(HEVC_SAO_CTRL1, data32); + + if (get_double_write_mode(hevc) & 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 (get_dbg_flag(hevc) & H265_DEBUG_BUFMGR) + hevc_print_cont(hevc, 0, + "(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 (get_dbg_flag(hevc) & H265_DEBUG_BUFMGR) + hevc_print_cont(hevc, 0, + "(2,%x)", data32); + } + } else { + data32 |= + ((misc_flag0 >> + PPS_DEBLOCKING_FILTER_DISABLED_FLAG_BIT) & + 0x1) << 2; + if (get_dbg_flag(hevc) & H265_DEBUG_BUFMGR) + hevc_print_cont(hevc, 0, + "(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 (get_dbg_flag(hevc) & H265_DEBUG_BUFMGR) + hevc_print_cont(hevc, 0, + "(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 (get_dbg_flag(hevc) & H265_DEBUG_BUFMGR) + hevc_print_cont(hevc, 0, + "(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 (get_dbg_flag(hevc) & H265_DEBUG_BUFMGR) + hevc_print_cont(hevc, 0, + "(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 (get_dbg_flag(hevc) & H265_DEBUG_NOT_USE_LAST_DISPBUF) + return; + + for (i = 0; i < MAX_REF_PIC_NUM; i++) { + pic = hevc->m_PIC[i]; + if (pic) + 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 == NULL || 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] == NULL || + hevc->m_PIC[ii]->index == -1) + break; + } + if (ii < MAX_REF_PIC_NUM) { + new_pic = hevc->m_PIC[ii]; + if (new_pic) { + 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 (get_dbg_flag(hevc) & + H265_DEBUG_BUFMGR_MORE) { + hevc_print(hevc, 0, + "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(hevc, 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 (new_pic->aux_data_buf) + release_aux_data(hevc, new_pic); + } + + 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 == NULL || + 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)) + || (get_dbg_flag(hevc) & + H265_DEBUG_DISPLAY_CUR_FRAME) + || (get_dbg_flag(hevc) & + H265_DEBUG_NO_DISPLAY)) { + pic_display->output_ready = 0; + if (get_dbg_flag(hevc) & H265_DEBUG_BUFMGR) { + hevc_print(hevc, 0, + "[BM] Display: POC %d, ", + pic_display->POC); + hevc_print_cont(hevc, 0, + "decoding index %d ==> ", + pic_display->decode_idx); + hevc_print_cont(hevc, 0, + "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 (get_dbg_flag(hevc) + & H265_DEBUG_BUFMGR) { + hevc_print(hevc, 0, + "[BM] flush Display: POC %d, ", + pic_display->POC); + hevc_print_cont(hevc, 0, + "decoding index %d\n", + pic_display->decode_idx); + } + } + } + } + } while (pic_display); +} + +static void set_aux_data(struct hevc_state_s *hevc, + struct PIC_s *pic, unsigned char suffix_flag) +{ + int i; + unsigned short *aux_adr; + unsigned size_reg_val = + READ_VREG(HEVC_AUX_DATA_SIZE); + unsigned aux_count = 0; + int aux_size = 0; + if (suffix_flag) { + aux_adr = (unsigned short *) + (hevc->aux_addr + + hevc->prefix_aux_size); + aux_count = + ((size_reg_val & 0xffff) << 4) + >> 1; + aux_size = + hevc->suffix_aux_size; + } else { + aux_adr = + (unsigned short *)hevc->aux_addr; + aux_count = + ((size_reg_val >> 16) << 4) + >> 1; + aux_size = + hevc->prefix_aux_size; + } + if (get_dbg_flag(hevc) & H265_DEBUG_BUFMGR_MORE) { + hevc_print(hevc, 0, "%s:old size %d count %d,suf %d\r\n", + __func__, pic->aux_data_size, aux_count, suffix_flag); + } + if (aux_size > 0 && aux_count > 0) { + int heads_size = 0; + int new_size; + char *new_buf; + for (i = 0; i < aux_count; i++) { + unsigned char tag = aux_adr[i] >> 8; + if (tag != 0 && tag != 0xff) + heads_size += 8; + } + new_size = pic->aux_data_size + aux_count + heads_size; + new_buf = krealloc(pic->aux_data_buf, + new_size, + GFP_KERNEL); + if (new_buf) { + unsigned char *p = + new_buf + + pic->aux_data_size; + unsigned char *h = p; + int h_bytes = 8; + int len = 0; + int padding_len = 0; + pic->aux_data_buf = new_buf; + pic->aux_data_size += (aux_count + heads_size); + for (i = 0; i < aux_count; i += 4) { + int ii; + unsigned char tag = aux_adr[i + 3] >> 8; + if (tag != 0 && tag != 0xff) { + if (i > 0) { + h[0] = (len >> 24) & 0xff; + h[1] = (len >> 16) & 0xff; + h[2] = (len >> 8) & 0xff; + h[3] = (len >> 0) & 0xff; + h[6] = (padding_len >> 8) + & 0xff; + h[7] = (padding_len) & 0xff; + h += (len + 8); + h_bytes += 8; + len = 0; + padding_len = 0; + } + h[4] = tag; + h[5] = 0; + h[6] = 0; + h[7] = 0; + } + for (ii = 0; ii < 4; ii++) { + unsigned short aa = + aux_adr[i + 3 + - ii]; + p[h_bytes + i + ii] = + aa & 0xff; + len++; + if ((aa >> 8) == 0xff) + padding_len++; + } + } + h[0] = (len >> 24) & 0xff; + h[1] = (len >> 16) & 0xff; + h[2] = (len >> 8) & 0xff; + h[3] = (len >> 0) & 0xff; + h[6] = (padding_len >> 8) & 0xff; + h[7] = (padding_len) & 0xff; + if (get_dbg_flag(hevc) & H265_DEBUG_BUFMGR_MORE) { + hevc_print(hevc, 0, + "aux: (size %d) suffix_flag %d\n", + pic->aux_data_size, suffix_flag); + for (i = 0; i < pic->aux_data_size; i++) { + hevc_print_cont(hevc, 0, + "%02x ", pic->aux_data_buf[i]); + if (((i + 1) & 0xf) == 0) + hevc_print_cont(hevc, 0, "\n"); + } + hevc_print_cont(hevc, 0, "\n"); + } + + } + } + +} + +static void release_aux_data(struct hevc_state_s *hevc, + struct PIC_s *pic) +{ + kfree(pic->aux_data_buf); + pic->aux_data_buf = NULL; + pic->aux_data_size = 0; +} + +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)) + || (get_dbg_flag(hevc) & + H265_DEBUG_DISPLAY_CUR_FRAME) + || (get_dbg_flag(hevc) & + H265_DEBUG_NO_DISPLAY)) { + pic_display->output_ready = 0; + if (get_dbg_flag(hevc) & + H265_DEBUG_BUFMGR) { + hevc_print(hevc, 0, + "[BM] Display: POC %d, ", + pic_display->POC); + hevc_print_cont(hevc, 0, + "decoding index %d ==> ", + pic_display-> + decode_idx); + hevc_print_cont(hevc, 0, + "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 (get_dbg_flag(hevc) & + H265_DEBUG_BUFMGR) { + hevc_print(hevc, 0, + "[BM] Display: POC %d, ", + pic_display->POC); + hevc_print_cont(hevc, 0, + "decoding index %d\n", + pic_display-> + decode_idx); + } + } + } + } + } while (pic_display); + } else { + if (get_dbg_flag(hevc) & H265_DEBUG_BUFMGR) { + hevc_print(hevc, 0, + "[BM] current pic is IDR, "); + hevc_print(hevc, 0, + "clear referenced flag of all buffers\n"); + } + if (get_dbg_flag(hevc) & 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 (get_dbg_flag(hevc) & H265_DEBUG_BUFMGR) { + hevc_print(hevc, 0, + "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) + hevc_print(hevc, 0, + "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) +{ +#ifdef CONFIG_AM_VDEC_DV + struct vdec_s *vdec = hw_to_vdec(hevc); +#endif + 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) { + hevc_print(hevc, 0, + "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 && + (get_double_write_mode(hevc) & 0x10) == 0) + init_decode_head_hw(hevc); +#endif + } + + if (HEVC_SIZE < hevc->pic_w * hevc->pic_h) { + pr_info("over size : %u x %u.\n", + hevc->pic_w, hevc->pic_h); + if (!hevc->m_ins_flag) + 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 (get_dbg_flag(hevc)) { + hevc_print(hevc, 0, + "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) { + hevc_print(hevc, 0, + "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) { + hevc_print(hevc, 0, + "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 (get_dbg_flag(hevc) & H265_DEBUG_BUFMGR) { + hevc_print(hevc, 0, + "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 (get_dbg_flag(hevc) & H265_DEBUG_BUFMGR) { + hevc_print(hevc, 0, + "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 (get_dbg_flag(hevc)) { + hevc_print(hevc, 0, + "RASL picture with POC %d < %d ", + hevc->curr_POC, hevc->m_pocRandomAccess); + hevc_print(hevc, 0, + "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 (get_dbg_flag(hevc) & 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); + } + + } +#ifdef CONFIG_AM_VDEC_DV + if (vdec->master) { + struct hevc_state_s *hevc_ba = + (struct hevc_state_s *) + vdec->master->private; + if (hevc_ba->cur_pic != NULL) + hevc_ba->cur_pic->dv_enhance_exist = 1; + } + if (vdec->master == NULL && + vdec->slave == NULL) { + if (hevc->cur_pic != NULL) + set_aux_data(hevc, hevc->cur_pic, 1); + } +#else + if (hevc->cur_pic != NULL) + set_aux_data(hevc, hevc->cur_pic, 1); +#endif + /* new pic */ + hevc->cur_pic = get_new_pic(hevc, rpm_param); + if (hevc->cur_pic == NULL) { + if (get_dbg_flag(hevc) & H265_DEBUG_BUFMGR) + dump_pic_list(hevc); + hevc->wait_buf = 1; + return -1; + } +#ifdef CONFIG_AM_VDEC_DV + hevc->cur_pic->dv_enhance_exist = 0; + if (vdec->master == NULL && + vdec->slave == NULL) + set_aux_data(hevc, hevc->cur_pic, 0); +#else + set_aux_data(hevc, hevc->cur_pic, 0); +#endif + if (get_dbg_flag(hevc) & 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 { +#ifdef CONFIG_AM_VDEC_DV + if (vdec->master == NULL && + vdec->slave == NULL) { + if (hevc->cur_pic != NULL) { + set_aux_data(hevc, hevc->cur_pic, 1); + set_aux_data(hevc, hevc->cur_pic, 0); + } + } +#else + if (hevc->cur_pic != NULL) { + set_aux_data(hevc, hevc->cur_pic, 1); + set_aux_data(hevc, hevc->cur_pic, 0); + } +#endif + 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; + +#ifdef CONFIG_AM_VDEC_DV + hevc->cur_pic->dv_enhance_exist = 0; + if (vdec->master == NULL && + vdec->slave == NULL) + set_aux_data(hevc, hevc->cur_pic, 0); +#else + set_aux_data(hevc, hevc->cur_pic, 0); +#endif + hevc->wait_buf = 0; + } else if (hevc->wait_buf == + 2) { + if (get_display_pic_num(hevc) > + 1) + return -1; + hevc->wait_buf = 0; + } + if (get_dbg_flag(hevc) & 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 (get_dbg_flag(hevc) & H265_DEBUG_BUFMGR) { + hevc_print(hevc, 0, + "==>%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 (get_dbg_flag(hevc) & H265_DEBUG_BUFMGR) { + hevc_print(hevc, 0, + "new_tile (new_pic) tile_x=%d, tile_y=%d\n", + hevc->tile_x, hevc->tile_y); + } + } else if (hevc->tile_enabled) { + if (get_dbg_flag(hevc) & H265_DEBUG_BUFMGR) { + hevc_print(hevc, 0, + "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 (get_dbg_flag(hevc) & H265_DEBUG_BUFMGR) { + hevc_print(hevc, 0, + "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 (get_dbg_flag(hevc)) { + hevc_print(hevc, 0, + "WRONG,fail to get the pic Col_POC\n"); + } + } else if (hevc->col_pic->error_mark) { + hevc->cur_pic->error_mark = 1; + if (get_dbg_flag(hevc)) { + hevc_print(hevc, 0, + "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 (get_dbg_flag(hevc)) + hevc_print(hevc, 0, + "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->aux_addr) { + dma_unmap_single(amports_get_dma_device(), + hevc->aux_phy_addr, + hevc->prefix_aux_size + hevc->suffix_aux_size, + DMA_FROM_DEVICE); + kfree(hevc->aux_addr); + hevc->aux_addr = 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); + + + + 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 ((get_dbg_flag(hevc) & 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 (prefix_aux_buf_size > 0 || + suffix_aux_buf_size > 0) { + u32 aux_buf_size; + hevc->prefix_aux_size = AUX_BUF_ALIGN(prefix_aux_buf_size); + hevc->suffix_aux_size = AUX_BUF_ALIGN(suffix_aux_buf_size); + aux_buf_size = hevc->prefix_aux_size + hevc->suffix_aux_size; + hevc->aux_addr = kmalloc(aux_buf_size, GFP_KERNEL); + if (hevc->aux_addr == NULL) { + pr_err("%s: failed to alloc rpm buffer\n", __func__); + return -1; + } + + hevc->aux_phy_addr = dma_map_single(amports_get_dma_device(), + hevc->aux_addr, aux_buf_size, DMA_FROM_DEVICE); + if (dma_mapping_error(amports_get_dma_device(), + hevc->aux_phy_addr)) { + pr_err("%s: failed to map rpm buffer\n", __func__); + kfree(hevc->aux_addr); + hevc->aux_addr = NULL; + return -1; + } + } + + if (get_dbg_flag(hevc) & 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 hevc_state_s *hevc, 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 (get_double_write_mode(hevc)) { + canvas_w = pic->width; + canvas_h = pic->height; + if ((get_double_write_mode(hevc) == 2) || + (get_double_write_mode(hevc) == 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; + + /* hevc_print(hevc, 0, + "%s1: %d %d\n", __func__, hevc->pic_w, hevc->pic_h); */ + hevc_print(hevc, 0, + "%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 int parse_sei(struct hevc_state_s *hevc, char *sei_buf, uint32_t size) +{ + char *p = sei_buf; + char *p_sei; + uint16_t header; + uint8_t nal_unit_type; + uint8_t payload_type, payload_size; + int i, j; + + if (size < 2) + return 0; + header = *p++; + header <<= 8; + header += *p++; + nal_unit_type = header >> 9; + if ((nal_unit_type != NAL_UNIT_SEI) + && (nal_unit_type != NAL_UNIT_SEI_SUFFIX)) + return 0; + while (p+2 <= sei_buf+size) { + payload_type = *p++; + payload_size = *p++; + if (p+payload_size <= sei_buf+size) { + switch (payload_type) { + case SEI_MasteringDisplayColorVolume: + hevc_print(hevc, 0, + "sei type: primary display color volume %d, size %d\n", + payload_type, + payload_size); + /* master_display_colour */ + p_sei = p; + for (i = 0; i < 3; i++) { + for (j = 0; j < 2; j++) { + hevc->primaries[i][j] + = (*p_sei<<8) + | *(p_sei+1); + p_sei += 2; + } + } + for (i = 0; i < 2; i++) { + hevc->white_point[i] + = (*p_sei<<8) + | *(p_sei+1); + p_sei += 2; + } + for (i = 0; i < 2; i++) { + hevc->luminance[i] + = (*p_sei<<24) + | (*(p_sei+1)<<16) + | (*(p_sei+2)<<8) + | *(p_sei+3); + p_sei += 4; + } + hevc->sei_present_flag |= + SEI_MASTER_DISPLAY_COLOR_MASK; + for (i = 0; i < 3; i++) + for (j = 0; j < 2; j++) + hevc_print(hevc, 0, + "\tprimaries[%1d][%1d] = %04x\n", + i, j, + hevc->primaries[i][j]); + hevc_print(hevc, 0, + "\twhite_point = (%04x, %04x)\n", + hevc->white_point[0], + hevc->white_point[1]); + hevc_print(hevc, 0, + "\tmax,min luminance = %08x, %08x\n", + hevc->luminance[0], + hevc->luminance[1]); + break; + case SEI_ContentLightLevel: + hevc_print(hevc, 0, + "sei type: max content light level %d, size %d\n", + payload_type, payload_size); + /* content_light_level */ + p_sei = p; + hevc->content_light_level[0] + = (*p_sei<<8) | *(p_sei+1); + p_sei += 2; + hevc->content_light_level[1] + = (*p_sei<<8) | *(p_sei+1); + p_sei += 2; + hevc->sei_present_flag |= + SEI_CONTENT_LIGHT_LEVEL_MASK; + hevc_print(hevc, 0, + "\tmax cll = %04x, max_pa_cll = %04x\n", + hevc->content_light_level[0], + hevc->content_light_level[1]); + break; + default: + break; + } + } + p += payload_size; + } + return 0; +} + +static void set_frame_info(struct hevc_state_s *hevc, struct vframe_s *vf) +{ + unsigned int ar; + int i, j; + unsigned char index; + char *p; + unsigned size = 0; + unsigned type = 0; + struct vframe_master_display_colour_s *vf_dp + = &vf->prop.master_display_colour; + + if ((get_double_write_mode(hevc) == 2) || + (get_double_write_mode(hevc) == 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; + + /* parser sei */ + index = vf->index & 0xff; + if (index != 0xff && index >= 0 + && index < MAX_REF_PIC_NUM + && hevc->m_PIC[index] + && hevc->m_PIC[index]->aux_data_buf + && hevc->m_PIC[index]->aux_data_size) { + p = hevc->m_PIC[index]->aux_data_buf; + while (p < hevc->m_PIC[index]->aux_data_buf + + hevc->m_PIC[index]->aux_data_size - 8) { + size = *p++; + size = (size << 8) | *p++; + size = (size << 8) | *p++; + size = (size << 8) | *p++; + type = *p++; + type = (type << 8) | *p++; + type = (type << 8) | *p++; + type = (type << 8) | *p++; + if (type == 0x02000000) { + /* hevc_print(hevc, 0, "sei(%d)\n", size); */ + parse_sei(hevc, p, size); + } + p += size; + } + } + + /* 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_dp->primaries[i][j] = hevc->primaries[i][j]; + for (i = 0; i < 2; i++) { + vf_dp->white_point[i] = hevc->white_point[i]; + vf_dp->luminance[i] + = hevc->luminance[i]; + } + vf_dp->present_flag = 1; + } else + vf_dp->present_flag = 0; + + /* content_light_level */ + if (hevc->sei_present_flag & SEI_CONTENT_LIGHT_LEVEL_MASK) { + vf_dp->content_light_level.max_content + = hevc->content_light_level[0]; + vf_dp->content_light_level.max_pic_average + = hevc->content_light_level[1]; + vf_dp->content_light_level.present_flag = 1; + } else + vf_dp->content_light_level.present_flag = 0; + return; +} + +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 (get_dbg_flag(hevc) & H265_DEBUG_PIC_STRUCT) + hevc_print(hevc, 0, + "%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 (get_dbg_flag(hevc) & H265_DEBUG_PIC_STRUCT) + hevc_print(hevc, 0, + "%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 + && hevc->m_PIC[index_top]) { + 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 + && hevc->m_PIC[index_bot]) { + 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 *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 + if (type & VFRAME_EVENT_RECEIVER_RESET) { +#if 0 + amhevc_stop(); +#ifndef CONFIG_AMLOGIC_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_AMLOGIC_POST_PROCESS_MANAGER + vf_reg_provider(&vh265_vf_prov); +#endif + amhevc_start(); +#endif + } else if (type & VFRAME_EVENT_RECEIVER_GET_AUX_DATA) { + struct provider_aux_req_s *req = + (struct provider_aux_req_s *)data; + unsigned char index; + spin_lock_irqsave(&lock, flags); + index = req->vf->index & 0xff; + req->aux_buf = NULL; + req->aux_size = 0; + if (req->bot_flag) + index = (req->vf->index >> 8) & 0xff; + if (index != 0xff && index >= 0 + && index < MAX_REF_PIC_NUM + && hevc->m_PIC[index]) { + req->aux_buf = hevc->m_PIC[index]->aux_data_buf; + req->aux_size = hevc->m_PIC[index]->aux_data_size; +#ifdef CONFIG_AM_VDEC_DV + req->dv_enhance_exist = + hevc->m_PIC[index]->dv_enhance_exist; +#else + req->dv_enhance_exist = 0; +#endif + } + spin_unlock_irqrestore(&lock, flags); + + if (get_dbg_flag(hevc) & H265_DEBUG_PIC_STRUCT) + hevc_print(hevc, 0, + "%s(type 0x%x vf index 0x%x)=>size 0x%x\n", + __func__, type, index, req->aux_size); + } + + 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 (get_dbg_flag(hevc) & H265_DEBUG_PIC_STRUCT) + hevc_print(hevc, 0, + "%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) { + hevc_print(hevc, 0, + "fatal error, no available buffer slot."); + return -1; + } + if (get_dbg_flag(hevc) & H265_DEBUG_PIC_STRUCT) + hevc_print(hevc, 0, + "%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) { + hevc_print(hevc, 0, + "fatal error, no available buffer slot."); + return -1; + } + if (get_dbg_flag(hevc) & H265_DEBUG_PIC_STRUCT) + hevc_print(hevc, 0, + "%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) { + hevc_print(hevc, 0, + "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 (get_dbg_flag(hevc) & H265_DEBUG_PIC_STRUCT) + hevc_print(hevc, 0, + "%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) { + hevc_print(hevc, 0, + "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 (get_dbg_flag(hevc) & H265_DEBUG_PIC_STRUCT) + hevc_print(hevc, 0, + "%s vf => display_q: (index 0x%x)\n", + __func__, vf->index); + } + } + } + return 0; +} +#endif +static void update_vf_memhandle(struct hevc_state_s *hevc, + struct vframe_s *vf, int index) +{ + if (index < 0) + vf->mem_handle = NULL; + else if (vf->type & VIDTYPE_SCATTER) + vf->mem_handle = + decoder_mmu_box_get_mem_handle( + hevc->mmu_box, index); + else + vf->mem_handle = + decoder_bmmu_box_get_mem_handle( + hevc->bmmu_box, index); + return; +} +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) { + hevc_print(hevc, 0, + "fatal error, no available buffer slot."); + return -1; + } + + if (vf) { + if (hevc->m_ins_flag) { + vf->pts = pic->pts; + vf->pts_us64 = pic->pts64; + } + /* if (pts_lookup_offset(PTS_TYPE_VIDEO, + stream_offset, &vf->pts, 0) != 0) { */ + else 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; + hevc_print(hevc, 0, + "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 ((get_dbg_flag(hevc) & H265_DEBUG_OUT_PTS) != 0) { + hevc_print(hevc, 0, + "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 (get_double_write_mode(hevc) & 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 (get_double_write_mode(hevc)) { + vf->type = VIDTYPE_PROGRESSIVE | VIDTYPE_VIU_FIELD; + vf->type |= VIDTYPE_VIU_NV21; + if (get_double_write_mode(hevc) == 3) + vf->type |= VIDTYPE_COMPRESS; + if (mmu_enable) + vf->type |= VIDTYPE_SCATTER; +#ifdef MULTI_INSTANCE_SUPPORT + if (hevc->m_ins_flag) { + vf->canvas0Addr = vf->canvas1Addr = -1; + vf->plane_num = 2; + 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; + update_vf_memhandle(hevc, vf, pic->index); + 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)) */ + /* hevc_print(hevc, 0, + "aaa: %d/%d, %d/%d\n", + vf->width,vf->height, pic->width, pic->height); */ + if ((get_double_write_mode(hevc) == 2) || + (get_double_write_mode(hevc) == 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 && + (get_dbg_flag(hevc) & + 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 (get_dbg_flag(hevc) & H265_DEBUG_BUFMGR) + hevc_print(hevc, 0, + "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 (get_dbg_flag(hevc) & H265_DEBUG_PIC_STRUCT) + hevc_print(hevc, 0, + "pic_struct = %d index 0x%x\n", + pic->pic_struct, + pic->index); + + if (kfifo_get(&hevc->newframe_q, &vf2) == 0) { + hevc_print(hevc, 0, + "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 (get_dbg_flag(hevc) & H265_DEBUG_PIC_STRUCT) + hevc_print(hevc, 0, + "pic_struct = %d index 0x%x\n", + pic->pic_struct, + pic->index); + + if (kfifo_get(&hevc->newframe_q, &vf2) == 0) { + hevc_print(hevc, 0, + "fatal error, no available buffer slot."); + return -1; + } + if (kfifo_get(&hevc->newframe_q, &vf3) == 0) { + hevc_print(hevc, 0, + "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 (get_dbg_flag(hevc) & H265_DEBUG_PIC_STRUCT) + hevc_print(hevc, 0, + "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 (get_dbg_flag(hevc) & H265_DEBUG_PIC_STRUCT) + hevc_print(hevc, 0, + "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 (get_dbg_flag(hevc) & H265_DEBUG_PIC_STRUCT) + hevc_print(hevc, 0, + "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; + 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 (get_dbg_flag(hevc) & H265_DEBUG_PRINT_SEI) + hevc_print(hevc, 0, + "\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 (get_dbg_flag(hevc) & H265_DEBUG_PRINT_SEI) + hevc_print(hevc, 0, + "\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 (get_dbg_flag(hevc) & + H265_DEBUG_PRINT_SEI) + hevc_print(hevc, 0, + "\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 (get_dbg_flag(hevc) & H265_DEBUG_PRINT_SEI) + hevc_print(hevc, 0, + "\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 (get_dbg_flag(hevc) & + H265_DEBUG_PRINT_SEI) + hevc_print(hevc, 0, + "\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); + hevc_print(hevc, 0, "\t\tskip byte %02x\n", data); + } + } +} + +static int hevc_recover(struct hevc_state_s *hevc) +{ + int ret = -1; + 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; + mutex_lock(&vh265_mutex); +#if 0 + for (i = 0; i < (hevc->debug_ptr_size / 2); i += 4) { + int ii; + + for (ii = 0; ii < 4; ii++) + hevc_print(hevc, 0, + "%04x ", hevc->debug_ptr[i + 3 - ii]); + if (((i + ii) & 0xf) == 0) + hevc_print(hevc, 0, "\n"); + } +#endif +#define ES_VID_MAN_RD_PTR (1<<0) + if (!hevc->init_flag) { + hevc_print(hevc, 0, "h265 has stopped, recover return!\n"); + mutex_unlock(&vh265_mutex); + return ret; + } + amhevc_stop(); + ret = 0; + /* 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 (get_double_write_mode(hevc) & 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 (get_dbg_flag(hevc) & 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 (get_dbg_flag(hevc) & H265_DEBUG_NO_EOS_SEARCH_DONE) + WRITE_VREG(NAL_SEARCH_CTL, READ_VREG(NAL_SEARCH_CTL) | 0x10000); + WRITE_VREG(NAL_SEARCH_CTL, + READ_VREG(NAL_SEARCH_CTL) + | ((parser_sei_enable & 0x7) << 17)); +#ifdef CONFIG_AM_VDEC_DV + WRITE_VREG(NAL_SEARCH_CTL, + READ_VREG(NAL_SEARCH_CTL) | + ((parser_dolby_vision_enable & 0x1) << 20)); +#endif + config_decode_mode(hevc); + 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++; */ + hevc_print(hevc, 0, + "%04x ", hevc->debug_ptr[i + 3 - ii]); + } + if (((i + ii) & 0xf) == 0) + hevc_print(hevc, 0, "\n"); + } +#endif + init_pic_list_hw(hevc); + + hevc_print(hevc, 0, "%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; + mutex_unlock(&vh265_mutex); + return ret; + } +#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; + mutex_unlock(&vh265_mutex); + return ret; +} + +static void dump_aux_buf(struct hevc_state_s *hevc) +{ + int i; + unsigned short *aux_adr = + (unsigned short *) + hevc->aux_addr; + unsigned aux_size = + (READ_VREG(HEVC_AUX_DATA_SIZE) + >> 16) << 4; + + if (hevc->prefix_aux_size > 0) { + hevc_print(hevc, 0, + "prefix aux: (size %d)\n", + aux_size); + for (i = 0; i < + (aux_size >> 1); i++) { + hevc_print_cont(hevc, 0, + "%04x ", + *(aux_adr + i)); + if (((i + 1) & 0xf) + == 0) + hevc_print_cont(hevc, + 0, "\n"); + } + } + if (hevc->suffix_aux_size > 0) { + aux_adr = (unsigned short *) + (hevc->aux_addr + + hevc->prefix_aux_size); + aux_size = + (READ_VREG(HEVC_AUX_DATA_SIZE) & 0xffff) + << 4; + hevc_print(hevc, 0, + "suffix aux: (size %d)\n", + aux_size); + for (i = 0; i < + (aux_size >> 1); i++) { + hevc_print_cont(hevc, 0, + "%04x ", *(aux_adr + i)); + if (((i + 1) & 0xf) == 0) + hevc_print_cont(hevc, 0, "\n"); + } + } +} + +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; +#ifdef CONFIG_AM_VDEC_DV + struct vdec_s *vdec = hw_to_vdec(hevc); +#endif + 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 (get_dbg_flag(hevc) & H265_DEBUG_NO_EOS_SEARCH_DONE) { + WRITE_VREG(NAL_SEARCH_CTL, + READ_VREG(NAL_SEARCH_CTL) | 0x10000); + } + WRITE_VREG(NAL_SEARCH_CTL, + READ_VREG(NAL_SEARCH_CTL) + | ((parser_sei_enable & 0x7) << 17)); +#ifdef CONFIG_AM_VDEC_DV + WRITE_VREG(NAL_SEARCH_CTL, + READ_VREG(NAL_SEARCH_CTL) | + ((parser_dolby_vision_enable & 0x1) << 20)); +#endif + config_decode_mode(hevc); + /* 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); + + /* hevc_print(hevc, 0, + "%s: error handle\n", __func__); */ + hevc->error_flag = 2; + return IRQ_HANDLED; + } else if (hevc->error_flag == 3) { + hevc_print(hevc, 0, "error_flag=3, hevc_recover\n"); + 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_DECPIC_DATA_DONE || + dec_status == HEVC_FIND_NEXT_PIC_NAL || + dec_status == HEVC_FIND_NEXT_DVEL_NAL) + && (hevc->m_ins_flag)) { + if (hevc->chunk) { + hevc->cur_pic->pts = hevc->chunk->pts; + hevc->cur_pic->pts64 = hevc->chunk->pts64; + } else if (pts_lookup_offset_us64 + (PTS_TYPE_VIDEO, + hevc->cur_pic->stream_offset, + &hevc->cur_pic->pts, + 0, + &hevc->cur_pic->pts64) != 0) { +#ifdef DEBUG_PTS + hevc->pts_missed++; +#endif + hevc->cur_pic->pts = 0; + hevc->cur_pic->pts64 = 0; + } + } + + if ((dec_status == HEVC_SEARCH_BUFEMPTY) || + (dec_status == HEVC_DECODE_BUFEMPTY) || + (dec_status == HEVC_NAL_DECODE_DONE) + ) { + if (hevc->m_ins_flag) { +#if 1 + if (!vdec_frame_based(hw_to_vdec(hevc))) { + hevc->dec_result = DEC_RESULT_AGAIN; + amhevc_stop(); + } else + hevc->dec_result = DEC_RESULT_GET_DATA; +#else + if (!vdec_frame_based(hw_to_vdec(hevc))) + hevc->dec_result = DEC_RESULT_AGAIN; + else + hevc->dec_result = DEC_RESULT_DONE; + amhevc_stop(); +#endif + reset_process_time(hevc); + schedule_work(&hevc->work); + } + + return IRQ_HANDLED; + } else if (dec_status == HEVC_DECPIC_DATA_DONE) { + if (hevc->m_ins_flag) { + hevc->dec_result = DEC_RESULT_DONE; + amhevc_stop(); + + reset_process_time(hevc); + schedule_work(&hevc->work); + } + + return IRQ_HANDLED; +#ifdef CONFIG_AM_VDEC_DV + } else if (dec_status == HEVC_FIND_NEXT_PIC_NAL || + dec_status == HEVC_FIND_NEXT_DVEL_NAL) { + if (hevc->m_ins_flag) { + unsigned next_parser_type = + READ_HREG(CUR_NAL_UNIT_TYPE); + if (vdec->slave && + dec_status == HEVC_FIND_NEXT_DVEL_NAL) { + /*cur is base, found enhance*/ + struct hevc_state_s *hevc_el = + (struct hevc_state_s *) + vdec->slave->private; + hevc->switch_dvlayer_flag = 1; + hevc_el->start_parser_type = + next_parser_type; + } else if (vdec->master && + dec_status == HEVC_FIND_NEXT_PIC_NAL) { + /*cur is enhance, found base*/ + struct hevc_state_s *hevc_ba = + (struct hevc_state_s *) + vdec->master->private; + hevc->switch_dvlayer_flag = 1; + hevc_ba->start_parser_type = + next_parser_type; + } else { + hevc->switch_dvlayer_flag = 0; + hevc->start_parser_type = + next_parser_type; + } + hevc->dec_result = DEC_RESULT_DONE; + amhevc_stop(); + reset_process_time(hevc); + if (READ_VREG(HEVC_AUX_DATA_SIZE) != 0) { + dma_sync_single_for_cpu( + amports_get_dma_device(), + hevc->aux_phy_addr, + hevc->prefix_aux_size + hevc->suffix_aux_size, + DMA_FROM_DEVICE); + if (get_dbg_flag(hevc) & + H265_DEBUG_BUFMGR_MORE) + dump_aux_buf(hevc); + if (hevc->cur_pic) + set_aux_data(hevc, hevc->cur_pic, 0); + } + + schedule_work(&hevc->work); + } + + return IRQ_HANDLED; +#endif + } else if (dec_status == HEVC_DECODE_TIMEOUT) { + if (vdec_frame_based(hw_to_vdec(hevc)) || + (READ_VREG(HEVC_STREAM_LEVEL) > 0x200)) { + if ((get_dbg_flag(hevc) + & H265_DEBUG_DIS_LOC_ERROR_PROC)) { + hevc_print(hevc, 0, + "%s decoding error, level 0x%x\n", + __func__, READ_VREG(HEVC_STREAM_LEVEL)); + goto send_again; + } + amhevc_stop(); + hevc_print(hevc, PRINT_FLAG_VDEC_STATUS, + "%s %s\n", __func__, + (dec_status == HEVC_SEARCH_BUFEMPTY) ? + "HEVC_SEARCH_BUFEMPTY" : + (dec_status == HEVC_DECODE_BUFEMPTY) ? + "HEVC_DECODE_BUFEMPTY" : "HEVC_DECODE_TIMEOUT"); + hevc->dec_result = DEC_RESULT_DONE; + + reset_process_time(hevc); + schedule_work(&hevc->work); + } else { + /* WRITE_VREG(dec_status_REG, H264_ACTION_INIT); */ + hevc_print(hevc, PRINT_FLAG_VDEC_STATUS, + "%s DEC_RESULT_AGAIN\n", __func__); +send_again: + hevc->dec_result = DEC_RESULT_AGAIN; + reset_process_time(hevc); + schedule_work(&hevc->work); + } + return IRQ_HANDLED; + } + +#endif + + 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 && + get_dbg_flag(hevc) & H265_DEBUG_DISCARD_NAL) { + hevc_print(hevc, 0, + "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; + hevc_print(hevc, 0, "get NAL_UNIT_EOS, flush output\n"); + 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) { + hevc_print(hevc, 0, + "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) { + hevc_print(hevc, 0, + "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) { + hevc_print(hevc, 0, + "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 ((get_dbg_flag(hevc) & + 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 (get_dbg_flag(hevc) & + H265_DEBUG_NO_EOS_SEARCH_DONE) { + WRITE_VREG(NAL_SEARCH_CTL, + READ_VREG(NAL_SEARCH_CTL) | + 0x10000); + } + WRITE_VREG(NAL_SEARCH_CTL, + READ_VREG(NAL_SEARCH_CTL) + | ((parser_sei_enable & 0x7) << 17)); +#ifdef CONFIG_AM_VDEC_DV + WRITE_VREG(NAL_SEARCH_CTL, + READ_VREG(NAL_SEARCH_CTL) | + ((parser_dolby_vision_enable & 0x1) << 20)); +#endif + config_decode_mode(hevc); + } + + if (get_dbg_flag(hevc) & H265_DEBUG_BUFMGR) { + hevc_print(hevc, 0, + "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; + hevc_print(hevc, 0, "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 (get_dbg_flag(hevc) & 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 (get_dbg_flag(hevc) & H265_DEBUG_BUFMGR_MORE) { + hevc_print(hevc, 0, + "rpm_param: (%d)\n", hevc->slice_idx); + hevc->slice_idx++; + for (i = 0; i < (RPM_END - RPM_BEGIN); i++) { + hevc_print_cont(hevc, 0, + "%04x ", hevc->param.l.data[i]); + if (((i + 1) & 0xf) == 0) + hevc_print_cont(hevc, 0, "\n"); + } + + hevc_print(hevc, 0, + "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); + } + if ( +#ifdef CONFIG_AM_VDEC_DV + vdec->master == NULL && + vdec->slave == NULL && +#endif + READ_VREG(HEVC_AUX_DATA_SIZE) != 0 + ) { + dma_sync_single_for_cpu( + amports_get_dma_device(), + hevc->aux_phy_addr, + hevc->prefix_aux_size + hevc->suffix_aux_size, + DMA_FROM_DEVICE); + if (get_dbg_flag(hevc) & + H265_DEBUG_BUFMGR_MORE) + dump_aux_buf(hevc); + } + + 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)) { + hevc_print(hevc, 0, "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)) { + hevc_print(hevc, 0, "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) { + hevc_print(hevc, 0, + "video_signal_type present:\n"); + hevc_print(hevc, 0, " %s %s\n", + video_format_names[(v >> 10) & 7], + ((v >> 9) & 1) ? + "full_range" : "limited"); + if (v & 0x100) { + hevc_print(hevc, 0, + " color_description present:\n"); + hevc_print(hevc, 0, + " color_primarie = %s\n", + color_primaries_names + [v & 0xff]); + hevc_print(hevc, 0, + " transfer_characteristic = %s\n", + transfer_characteristics_names + [(c >> 8) & 0xff]); + hevc_print(hevc, 0, + " 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) { + reset_process_time(hevc); + schedule_work(&hevc->work); + } else +#endif + up(&hevc->h265_sema); + hevc_print(hevc, 0, "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; +#if 1 + /*to do..., copy aux data to hevc->cur_pic*/ +#endif +#ifdef MULTI_INSTANCE_SUPPORT + } else if (hevc->m_ins_flag) { + hevc_print(hevc, PRINT_FLAG_VDEC_STATUS, + "%s, bufmgr ret %d skip, DEC_RESULT_DONE\n", + __func__, ret); + hevc->dec_result = DEC_RESULT_DONE; + amhevc_stop(); + reset_process_time(hevc); + schedule_work(&hevc->work); +#endif + } 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 && + pic->output_mark == 0 && pic->referenced == 0 + && pic->output_ready == 0 + && pic->used_by_display == 0 + && (pic->index != -1)) { + decoder_mmu_box_free_idx(hevc->mmu_box, i); + hevc->last_put_idx_a = -1; + /* hevc_print(hevc, 0, "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 && + pic->output_mark == 0 && pic->referenced == 0 + && pic->output_ready == 0 + && pic->used_by_display == 0 + && (pic->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 (get_dbg_flag(hevc) & H265_DEBUG_BUFMGR) + hevc_print(hevc, 0, + "265 isr dec status = 0x%x\n", dec_status); + + if (get_dbg_flag(hevc) & 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); + + hevc_print(hevc, 0, + "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) + hevc_print_cont(hevc, 0, "%03x: ", i); + for (ii = 0; ii < 4; ii++) { + hevc_print_cont(hevc, 0, "%04x ", + hevc->lmem_ptr[i + 3 - ii]); + } + if (((i + ii) & 0xf) == 0) + hevc_print_cont(hevc, 0, "\n"); + } + WRITE_HREG(DEBUG_REG1, 0); + } else if (READ_HREG(DEBUG_REG1) != 0) { + hevc_print(hevc, 0, + "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; + + return IRQ_WAKE_THREAD; + +} + +static void vh265_check_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; + } +#ifdef MULTI_INSTANCE_SUPPORT + if (hevc->m_ins_flag && + hw_to_vdec(hevc)->next_status == + VDEC_STATUS_DISCONNECTED) { + hevc->dec_result = DEC_RESULT_DONE; + schedule_work(&hevc->work); + hevc_print(hevc, + 0, "vdec requested to be disconnected\n"); + return; + } + + if (hevc->m_ins_flag) { + if ((input_frame_based(hw_to_vdec(hevc)) || + (READ_VREG(HEVC_STREAM_LEVEL) > 0x200)) && + ((get_dbg_flag(hevc) & + H265_DEBUG_DIS_LOC_ERROR_PROC) == 0) && + (decode_timeout_val > 0) && + (hevc->start_process_time > 0) && + ((1000 * (jiffies - hevc->start_process_time) / HZ) + > decode_timeout_val) + ) { + u32 dec_status = READ_VREG(HEVC_DEC_STATUS_REG); + int current_lcu_idx = + READ_VREG(HEVC_PARSER_LCU_START)&0xffffff; + if (dec_status == HEVC_CODED_SLICE_SEGMENT_DAT) { + if (hevc->last_lcu_idx == current_lcu_idx) { + if (hevc->decode_timeout_count > 0) + hevc->decode_timeout_count--; + if (hevc->decode_timeout_count == 0) + timeout_process(hevc); + } + hevc->last_lcu_idx = current_lcu_idx; + } else + timeout_process(hevc); + } + } else { +#endif + 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 ((get_dbg_flag(hevc) & + 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) { + hevc_print(hevc, 0, + "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 ((get_dbg_flag(hevc) + & 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 ((get_dbg_flag(hevc) & + 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 */ + hevc_print(hevc, 0, + "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; + } +#ifdef MULTI_INSTANCE_SUPPORT + } +#endif + if (decode_stop_pos != decode_stop_pos_pre) { + WRITE_VREG(DECODE_STOP_POS, decode_stop_pos); + decode_stop_pos_pre = decode_stop_pos; + } + + if (get_dbg_flag(hevc) & H265_DEBUG_DUMP_PIC_LIST) { + dump_pic_list(hevc); + debug &= ~H265_DEBUG_DUMP_PIC_LIST; + } + if (get_dbg_flag(hevc) & H265_DEBUG_TRIG_SLICE_SEGMENT_PROC) { + WRITE_VREG(HEVC_ASSIST_MBOX1_IRQ_REG, 0x1); + debug &= ~H265_DEBUG_TRIG_SLICE_SEGMENT_PROC; + } + if (get_dbg_flag(hevc) & 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 (get_dbg_flag(hevc) & 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); + hevc_print(hevc, 0, + "WRITE_VREG(%x,%x)\n", radr, rval); + } else + hevc_print(hevc, 0, + "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 && + get_double_write_mode(hevc) == 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; + } + hevc_print(hevc, 0, + "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) { + hevc_print(hevc, 0, + "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; + hevc_print(hevc, 0, "set pic_list_init_flag to 2\n"); + + WRITE_VREG(HEVC_ASSIST_MBOX1_IRQ_REG, 0x1); + + } + + if (hevc->uninit_list) { + /*USE_BUF_BLOCK*/ + uninit_pic_list(hevc); + hevc_print(hevc, 0, "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; + } + + 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 config_decode_mode(struct hevc_state_s *hevc) +{ +#ifdef CONFIG_AM_VDEC_DV + struct vdec_s *vdec = hw_to_vdec(hevc); +#endif + if (!hevc->m_ins_flag) + WRITE_VREG(HEVC_DECODE_MODE, + DECODE_MODE_SINGLE); + else if (vdec_frame_based(hw_to_vdec(hevc))) + WRITE_VREG(HEVC_DECODE_MODE, + DECODE_MODE_MULTI_FRAMEBASE); +#ifdef CONFIG_AM_VDEC_DV + else if (vdec->slave) + WRITE_VREG(HEVC_DECODE_MODE, + (hevc->start_parser_type << 8) + | DECODE_MODE_MULTI_DVBAL); + else if (vdec->master) + WRITE_VREG(HEVC_DECODE_MODE, + (hevc->start_parser_type << 8) + | DECODE_MODE_MULTI_DVENL); +#endif + else + WRITE_VREG(HEVC_DECODE_MODE, + DECODE_MODE_MULTI_STREAMBASE); +} + +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 (get_dbg_flag(hevc) & H265_DEBUG_UCODE) + WRITE_VREG(DEBUG_REG1, 0x1); + else + WRITE_VREG(DEBUG_REG1, 0x0); + + if ((get_dbg_flag(hevc) & + (H265_DEBUG_MAN_SKIP_NAL | + H265_DEBUG_MAN_SEARCH_NAL))/* || + hevc->m_ins_flag*/) { + WRITE_VREG(NAL_SEARCH_CTL, 0x1); /* manual parser NAL */ + } else { + /* check vps/sps/pps/i-slice in ucode */ + unsigned ctl_val = 0x8; +#ifdef MULTI_INSTANCE_SUPPORT + if (hevc->m_ins_flag && + hevc->init_flag) { + /* do not check vps/sps/pps/i-slice in ucode + from the 2nd picture*/ + ctl_val = 0x2; + } else +#endif + 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 (get_dbg_flag(hevc) & H265_DEBUG_NO_EOS_SEARCH_DONE) + WRITE_VREG(NAL_SEARCH_CTL, READ_VREG(NAL_SEARCH_CTL) | 0x10000); + + WRITE_VREG(NAL_SEARCH_CTL, + READ_VREG(NAL_SEARCH_CTL) + | ((parser_sei_enable & 0x7) << 17)); +#ifdef CONFIG_AM_VDEC_DV + WRITE_VREG(NAL_SEARCH_CTL, + READ_VREG(NAL_SEARCH_CTL) | + ((parser_dolby_vision_enable & 0x1) << 20)); +#endif + WRITE_VREG(DECODE_STOP_POS, decode_stop_pos); + + config_decode_mode(hevc); + config_aux_buf(hevc); +} + +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_SIZE < hevc->frame_width * hevc->frame_height) { + 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; + hevc_print(hevc, 0, + "h265:pts_unstable=%d\n", pts_unstable); +/* +*TODO:FOR VERSION +*/ + hevc_print(hevc, 0, + "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 + int size = -1; + char *buf = vmalloc(0x1000 * 16); + if (IS_ERR_OR_NULL(buf)) + return -ENOMEM; + + init_timer(&hevc->timer); + + hevc->stat |= STAT_TIMER_INIT; + if (vh265_local_init(hevc) < 0) { + vfree(buf); + return -EBUSY; + } + +#ifdef MULTI_INSTANCE_SUPPORT + if (hevc->m_ins_flag) { + hevc->timer.data = (ulong) hevc; + hevc->timer.function = vh265_check_timer_func; + hevc->timer.expires = jiffies + PUT_INTERVAL; + + /*add_timer(&hevc->timer); + + hevc->stat |= STAT_TIMER_ARM;*/ + + INIT_WORK(&hevc->work, vh265_work); + vfree(buf); + return 0; + } +#endif + amhevc_enable(); + + if (mmu_enable && (get_cpu_type() >= MESON_CPU_MAJOR_ID_GXL)) { + size = get_firmware_data(VIDEO_DEC_HEVC_MMU, buf); + hevc_print(hevc, 0, "vh265 mmu ucode loaded!\n"); + } else + size = get_firmware_data(VIDEO_DEC_HEVC, buf); + + if (size < 0) { + pr_err("get firmware fail.\n"); + vfree(buf); + return -1; + } + + if (amhevc_loadmc_ex(VFORMAT_HEVC, NULL, buf) < 0) { + amhevc_disable(); + vfree(buf); + return -EBUSY; + } + + vfree(buf); + + 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)) { + hevc_print(hevc, 0, "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_check_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 (get_dbg_flag(hevc) & H265_DEBUG_FORCE_CLK) { + hevc_print(hevc, 0, "%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 (get_dbg_flag(hevc) & + 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 + + } + uninit_mmu_buffers(hevc); + amhevc_disable(); + + return 0; +} + +#ifdef MULTI_INSTANCE_SUPPORT +static void reset_process_time(struct hevc_state_s *hevc) +{ + if (hevc->start_process_time) { + unsigned process_time = + 1000 * (jiffies - hevc->start_process_time) / HZ; + hevc->start_process_time = 0; + if (process_time > max_process_time[hevc->index]) + max_process_time[hevc->index] = process_time; + } +} + +static void start_process_time(struct hevc_state_s *hevc) +{ + hevc->start_process_time = jiffies; + hevc->decode_timeout_count = 2; + hevc->last_lcu_idx = 0; +} + +static void timeout_process(struct hevc_state_s *hevc) +{ + hevc->timeout_num++; + amhevc_stop(); + hevc_print(hevc, + 0, "%s decoder timeout\n", __func__); + + hevc->dec_result = DEC_RESULT_DONE; + reset_process_time(hevc); + schedule_work(&hevc->work); +} + +static unsigned char is_new_pic_available(struct hevc_state_s *hevc) +{ + struct PIC_s *new_pic = NULL; + struct PIC_s *pic; + /* recycle un-used pic */ + int i; + /*return 1 if pic_list is not initialized yet*/ + if (hevc->pic_list_init_flag != 3) + return 1; + + for (i = 0; i < MAX_REF_PIC_NUM; i++) { + pic = hevc->m_PIC[i]; + if (pic == NULL || pic->index == -1) + continue; + if ((pic->used_by_display) + && ((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; + } + } + return (new_pic != NULL) ? 1 : 0; +} + +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; + reset_process_time(hevc); + 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); + uninit_mmu_buffers(hevc); + return 0; +} + +static unsigned int start_decode_buf_level; /* = 0x80000;*/ + +static unsigned char get_data_check_sum + (struct hevc_state_s *hevc, int size) +{ + int jj; + int sum = 0; + u8 *data = ((u8 *)hevc->chunk->block->start_virt) + + hevc->chunk->offset; + for (jj = 0; jj < size; jj++) + sum += data[jj]; + return sum; +} + +static void vh265_work(struct work_struct *work) +{ + struct hevc_state_s *hevc = container_of(work, + struct hevc_state_s, work); + struct vdec_s *vdec = hw_to_vdec(hevc); + /* 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; + hevc_print(hevc, 0, + "set pic_list_init_flag to 2\n"); + + start_process_time(hevc); + WRITE_VREG(HEVC_ASSIST_MBOX1_IRQ_REG, 0x1); + return; + } + + if (hevc->uninit_list) { + /*USE_BUF_BLOCK*/ + uninit_pic_list(hevc); + hevc_print(hevc, 0, "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_GET_DATA) || + (hevc->dec_result == DEC_RESULT_GET_DATA_RETRY)) { + if (hevc->dec_result == DEC_RESULT_GET_DATA) { + hevc_print(hevc, PRINT_FLAG_VDEC_STATUS, + "%s DEC_RESULT_GET_DATA %x %x %x mpc %x\n", + __func__, + READ_VREG(HEVC_STREAM_LEVEL), + READ_VREG(HEVC_STREAM_WR_PTR), + READ_VREG(HEVC_STREAM_RD_PTR), + READ_VREG(HEVC_MPC_E)); + vdec_vframe_dirty(vdec, hevc->chunk); + vdec_clean_input(vdec); + } + + /*if (is_new_pic_available(hevc)) {*/ + if (run_ready(vdec)) { + int r; + r = vdec_prepare_input(vdec, &hevc->chunk); + if (r < 0) { + hevc->dec_result = DEC_RESULT_GET_DATA_RETRY; + + hevc_print(hevc, + PRINT_FLAG_VDEC_DETAIL, + "amvdec_vh265: Insufficient data\n"); + + schedule_work(&hevc->work); + return; + } + hevc->dec_result = DEC_RESULT_NONE; + hevc_print(hevc, PRINT_FLAG_VDEC_STATUS, + "%s: chunk size 0x%x sum 0x%x mpc %x\n", + __func__, r, + (get_dbg_flag(hevc) & PRINT_FLAG_VDEC_STATUS) ? + get_data_check_sum(hevc, r) : 0, + READ_VREG(HEVC_MPC_E)); + if ((get_dbg_flag(hevc) & PRINT_FRAMEBASE_DATA) && + input_frame_based(vdec)) { + int jj; + u8 *data = + ((u8 *)hevc->chunk->block->start_virt) + + hevc->chunk->offset; + for (jj = 0; jj < r; jj++) { + if ((jj & 0xf) == 0) + hevc_print(hevc, + PRINT_FRAMEBASE_DATA, + "%06x:", jj); + hevc_print_cont(hevc, + PRINT_FRAMEBASE_DATA, + "%02x ", data[jj]); + if (((jj + 1) & 0xf) == 0) + hevc_print_cont(hevc, + PRINT_FRAMEBASE_DATA, + "\n"); + } + } + + WRITE_VREG(HEVC_DECODE_SIZE, r); + + vdec_enable_input(vdec); + + hevc_print(hevc, PRINT_FLAG_VDEC_STATUS, + "%s: mpc %x\n", + __func__, READ_VREG(HEVC_MPC_E)); + + start_process_time(hevc); + WRITE_VREG(HEVC_DEC_STATUS_REG, HEVC_ACTION_DONE); + } else{ + hevc->dec_result = DEC_RESULT_GET_DATA_RETRY; + + /*hevc_print(hevc, PRINT_FLAG_VDEC_DETAIL, + "amvdec_vh265: Insufficient data\n"); + */ + + schedule_work(&hevc->work); + } + return; + } else 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)); +#ifdef CONFIG_AM_VDEC_DV +#if 1 + if (vdec->slave) { + if (dv_debug & 0x1) + vdec_set_flag(vdec->slave, 0); + else + vdec_set_flag(vdec->slave, + VDEC_FLAG_INPUT_KEEP_CONTEXT); + } +#else + if (vdec->slave) { + if (no_interleaved_el_slice) + vdec_set_flag(vdec->slave, + VDEC_FLAG_INPUT_KEEP_CONTEXT); + /* this will move real HW pointer for input */ + else + vdec_set_flag(vdec->slave, 0); + /* this will not move real HW pointer + and SL layer decoding + will start from same stream position + as current BL decoder */ + } +#endif +#endif + 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)); + } + + if (hevc->stat & STAT_TIMER_ARM) { + del_timer_sync(&hevc->timer); + hevc->stat &= ~STAT_TIMER_ARM; + } + /* mark itself has all HW resource released and input released */ + vdec_set_status(hw_to_vdec(hevc), VDEC_STATUS_CONNECTED); + +#ifdef CONFIG_AM_VDEC_DV + if (hevc->switch_dvlayer_flag) { + if (vdec->slave) + vdec_set_next_sched(vdec, vdec->slave); + else if (vdec->master) + vdec_set_next_sched(vdec, vdec->master); + } else if (vdec->slave || vdec->master) + vdec_set_next_sched(vdec, vdec); +#endif + + 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 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__); + */ + if ((!vdec_frame_based(vdec)) && (start_decode_buf_level > 0)) { + u32 rp, wp; + u32 level; + + rp = READ_MPEG_REG(PARSER_VIDEO_RP); + wp = READ_MPEG_REG(PARSER_VIDEO_WP); + + if (wp < rp) + level = vdec->input.size + wp - rp; + else + level = wp - rp; + + if (level < start_decode_buf_level) { + hevc_print(hevc, 0, + "level %d not run_ready\n", level); + return false; + } + } else if (vdec_frame_based(vdec)) { + if (!vdec_input_next_input_chunk(&vdec->input)) + return false; + } + + return is_new_pic_available(hevc); +} + +static void reset_dec_hw(struct vdec_s *vdec) +{ + if (input_frame_based(vdec)) + WRITE_VREG(HEVC_STREAM_CONTROL, 0); + + /* + * 2: assist + * 3: parser + * 4: parser_state + * 8: dblk + * 11:mcpu + * 12:ccpu + * 13:ddr + * 14:iqit + * 15:ipp + * 17:qdct + * 18:mpred + * 19:sao + * 24:hevc_afifo + */ + 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); +} + +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; + + reset_dec_hw(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; + + hevc_print(hevc, PRINT_FLAG_VDEC_STATUS, + "%s: size 0x%x sum 0x%x (%x %x %x)\n", + __func__, r, + (vdec_frame_based(vdec) && + (get_dbg_flag(hevc) & PRINT_FLAG_VDEC_STATUS)) ? + get_data_check_sum(hevc, r) : 0, + READ_VREG(HEVC_STREAM_LEVEL), + READ_VREG(HEVC_STREAM_WR_PTR), + READ_VREG(HEVC_STREAM_RD_PTR)); + if ((get_dbg_flag(hevc) & PRINT_FRAMEBASE_DATA) && + input_frame_based(vdec)) { + int jj; + u8 *data = ((u8 *)hevc->chunk->block->start_virt) + + hevc->chunk->offset; + for (jj = 0; jj < r; jj++) { + if ((jj & 0xf) == 0) + hevc_print(hevc, PRINT_FRAMEBASE_DATA, + "%06x:", jj); + hevc_print_cont(hevc, PRINT_FRAMEBASE_DATA, + "%02x ", data[jj]); + if (((jj + 1) & 0xf) == 0) + hevc_print_cont(hevc, PRINT_FRAMEBASE_DATA, + "\n"); + } + } + if (hevc->init_flag == 0) { + if (amhevc_vdec_loadmc_ex(vdec, "vh265_mc") < 0) { + amhevc_disable(); + hevc_print(hevc, 0, + "%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); + + if (vdec_frame_based(vdec)) + WRITE_VREG(HEVC_SHIFT_BYTE_COUNT, 0); + + WRITE_VREG(HEVC_DECODE_SIZE, r); + /*WRITE_VREG(HEVC_DECODE_COUNT, hevc->decode_idx);*/ + hevc->init_flag = 1; + + if (hevc->pic_list_init_flag == 3) + init_pic_list_hw(hevc); + + start_process_time(hevc); + add_timer(&hevc->timer); + hevc->stat |= STAT_TIMER_ARM; + amhevc_start(); + +} + +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 (get_dbg_flag(hevc)) + hevc_print(hevc, 0, "%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) { + hevc_print(hevc, 0, + "\namvdec_h265 memory resource undefined.\n"); + mutex_unlock(&vh265_mutex); + return -EFAULT; + } +#ifndef CONFIG_MULTI_DEC + if (get_cpu_type() < MESON_CPU_MAJOR_ID_GXL + || double_write_mode == 0x10) + mmu_enable = 0; + else + mmu_enable = 1; +#endif + if (init_mmu_buffers(hevc)) { + hevc_print(hevc, 0, + "\n 265 mmu init failed!\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 (get_dbg_flag(hevc)) { + hevc_print(hevc, 0, + "===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; + hevc_print(hevc, 0, + "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 + hevc_print(hevc, 0, + "\namvdec_h265 init failed.\n"); + hevc_local_uninit(hevc); + uninit_mmu_buffers(hevc); + mutex_unlock(&vh265_mutex); + return -ENODEV; + } + /*set the max clk for smooth playing...*/ + hevc_source_changed(VFORMAT_HEVC, + 3840, 2160, 60); + mutex_unlock(&vh265_mutex); + + return 0; +} + +static int amvdec_h265_remove(struct platform_device *pdev) +{ + struct hevc_state_s *hevc = &gHevc; + + if (get_dbg_flag(hevc)) + hevc_print(hevc, 0, "%s\r\n", __func__); + + mutex_lock(&vh265_mutex); + + vh265_stop(hevc); + + hevc_source_changed(VFORMAT_HEVC, 0, 0, 0); + + +#ifdef DEBUG_PTS + hevc_print(hevc, 0, + "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; +#ifdef CONFIG_MULTI_DEC + int config_val; +#endif + 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; + } + 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; + hevc->index = pdev->id; + + if (pdata->use_vfm_path) + snprintf(pdata->vf_provider_name, + VDEC_PROVIDER_NAME_SIZE, + VFM_DEC_PROVIDER_NAME); +#ifdef CONFIG_AM_VDEC_DV + else if (vdec_dual(pdata)) { + if (dv_toggle_prov_name) /*debug purpose*/ + snprintf(pdata->vf_provider_name, + VDEC_PROVIDER_NAME_SIZE, + (pdata->master) ? VFM_DEC_DVBL_PROVIDER_NAME : + VFM_DEC_DVEL_PROVIDER_NAME); + else + snprintf(pdata->vf_provider_name, + VDEC_PROVIDER_NAME_SIZE, + (pdata->master) ? VFM_DEC_DVEL_PROVIDER_NAME : + VFM_DEC_DVBL_PROVIDER_NAME); + hevc->dolby_enhance_flag = pdata->master ? 1 : 0; + } +#endif + 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; + if (init_mmu_buffers(hevc) < 0) { + hevc_print(hevc, 0, + "\n 265 mmu init failed!\n"); + mutex_unlock(&vh265_mutex); + devm_kfree(&pdev->dev, (void *)hevc); + return -EFAULT; + } +#if 0 + hevc->buf_start = pdata->mem_start; + hevc->buf_size = pdata->mem_end - pdata->mem_start + 1; +#else + + if (decoder_bmmu_box_alloc_idx_wait( + hevc->bmmu_box, + BMMU_WORKSPACE_ID, + work_buf_size, + -1, + -1, + BMMU_ALLOC_FLAGS_WAITCLEAR) < 0) { + hevc_print(hevc, 0, + "workbuf alloc failed, request buf size 0x%lx\n", + work_buf_size); + uninit_mmu_buffers(hevc); + devm_kfree(&pdev->dev, (void *)hevc); + return -ENOMEM; + } + hevc->buf_start = decoder_bmmu_box_get_phy_addr( + hevc->bmmu_box, + BMMU_WORKSPACE_ID); + hevc->buf_size = work_buf_size; +#endif + 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) { + hevc_print(hevc, 0, + "\namvdec_h265 memory resource undefined.\n"); + uninit_mmu_buffers(hevc); + devm_kfree(&pdev->dev, (void *)hevc); + 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 (get_dbg_flag(hevc)) { + hevc_print(hevc, 0, + "===H.265 decoder mem resource 0x%lx -- 0x%lx\n", + hevc->buf_start, hevc->buf_start + hevc->buf_size); + } + + if (((get_dbg_flag(hevc) & IGNORE_PARAM_FROM_CONFIG) == 0) && + pdata->config && pdata->config_len) { +#ifdef CONFIG_MULTI_DEC + /*use ptr config for doubel_write_mode, etc*/ + hevc_print(hevc, 0, "pdata->config=%s\n", pdata->config); + if (get_config_int(pdata->config, "hevc_buf_width", + &config_val) == 0) + hevc->buf_alloc_width = config_val; + else + hevc->buf_alloc_width = buf_alloc_width; + + if (get_config_int(pdata->config, "hevc_buf_height", + &config_val) == 0) + hevc->buf_alloc_height = config_val; + else + hevc->buf_alloc_height = buf_alloc_height; + + if (get_config_int(pdata->config, "hevc_buf_margin", + &config_val) == 0) + hevc->dynamic_buf_num_margin = config_val; + else + hevc->dynamic_buf_num_margin = dynamic_buf_num_margin; + + if (get_config_int(pdata->config, "hevc_double_write_mode", + &config_val) == 0) + hevc->double_write_mode = config_val; + else + hevc->double_write_mode = double_write_mode; +#endif + } else { + hevc->vh265_amstream_dec_info.width = 0; + hevc->vh265_amstream_dec_info.height = 0; + hevc->vh265_amstream_dec_info.rate = 30; + hevc->buf_alloc_width = buf_alloc_width; + hevc->buf_alloc_height = buf_alloc_height; + hevc->dynamic_buf_num_margin = dynamic_buf_num_margin; + hevc->double_write_mode = double_write_mode; + } + hevc_print(hevc, 0, + "buf_alloc_width=%d\n", + hevc->buf_alloc_width); + hevc_print(hevc, 0, + "buf_alloc_height=%d\n", + hevc->buf_alloc_height); + hevc_print(hevc, 0, + "dynamic_buf_num_margin=%d\n", + hevc->dynamic_buf_num_margin); + hevc_print(hevc, 0, + "double_write_mode=%d\n", + hevc->double_write_mode); + + hevc->cma_dev = pdata->cma_dev; + + if (vh265_init(pdata) < 0) { + hevc_print(hevc, 0, + "\namvdec_h265 init failed.\n"); + hevc_local_uninit(hevc); + uninit_mmu_buffers(hevc); + devm_kfree(&pdev->dev, (void *)hevc); + return -ENODEV; + } + + /*set the max clk for smooth playing...*/ + hevc_source_changed(VFORMAT_HEVC, + 3840, 2160, 60); + + 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 (get_dbg_flag(hevc)) + hevc_print(hevc, 0, "%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"); + +#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(debug, uint, 0664); +MODULE_PARM_DESC(debug, "\n amvdec_h265 debug\n"); + +module_param(debug_mask, uint, 0664); +MODULE_PARM_DESC(debug_mask, "\n amvdec_h265 debug mask\n"); + +module_param(buffer_mode, uint, 0664); +MODULE_PARM_DESC(buffer_mode, "\n buffer_mode\n"); + +module_param(double_write_mode, uint, 0664); +MODULE_PARM_DESC(double_write_mode, "\n double_write_mode\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(dynamic_buf_num_margin, uint, 0664); +MODULE_PARM_DESC(dynamic_buf_num_margin, "\n dynamic_buf_num_margin\n"); + +module_param(max_buf_num, uint, 0664); +MODULE_PARM_DESC(max_buf_num, "\n max_buf_num\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_dbg, uint, 0664); +MODULE_PARM_DESC(buffer_mode_dbg, "\n buffer_mode_dbg\n"); + +module_param(mem_map_mode, uint, 0664); +MODULE_PARM_DESC(mem_map_mode, "\n mem_map_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"); + +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(prefix_aux_buf_size, uint, 0664); +MODULE_PARM_DESC(prefix_aux_buf_size, "\n prefix_aux_buf_size\n"); + +module_param(suffix_aux_buf_size, uint, 0664); +MODULE_PARM_DESC(suffix_aux_buf_size, "\n suffix_aux_buf_size\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"); + +#ifdef CONFIG_AM_VDEC_DV +module_param(parser_dolby_vision_enable, uint, 0664); +MODULE_PARM_DESC(parser_dolby_vision_enable, + "\n parser_dolby_vision_enable\n"); +#endif + +#ifdef MULTI_INSTANCE_SUPPORT +module_param(start_decode_buf_level, uint, 0664); +MODULE_PARM_DESC(start_decode_buf_level, + "\n h265 start_decode_buf_level\n"); + +module_param(decode_timeout_val, uint, 0664); +MODULE_PARM_DESC(decode_timeout_val, + "\n h265 decode_timeout_val\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); + +#endif +#ifdef CONFIG_AM_VDEC_DV +module_param(dv_toggle_prov_name, uint, 0664); +MODULE_PARM_DESC(dv_toggle_prov_name, "\n dv_toggle_prov_name\n"); + +module_param(dv_debug, uint, 0664); +MODULE_PARM_DESC(dv_debug, "\n dv_debug\n"); +#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/mjpeg/vmjpeg.c b/drivers/frame_provider/decoder/mjpeg/vmjpeg.c new file mode 100644 index 0000000..db27382 --- a/dev/null +++ b/drivers/frame_provider/decoder/mjpeg/vmjpeg.c @@ -0,0 +1,912 @@ +/* + * drivers/amlogic/amports/vmjpeg.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/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/frame_sync/ptsserv.h> +#include <linux/amlogic/media/utils/amstream.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/utils/vdec_reg.h> +#include "../../../stream_input/amports/amports_priv.h" +#include <linux/amlogic/media/registers/register.h> + +#ifdef CONFIG_AM_VDEC_MJPEG_LOG +#define AMLOG +#define LOG_LEVEL_VAR amlog_level_vmjpeg +#define LOG_MASK_VAR amlog_mask_vmjpeg +#define LOG_LEVEL_ERROR 0 +#define LOG_LEVEL_INFO 1 +#define LOG_LEVEL_DESC "0:ERROR, 1:INFO" +#endif +#include <linux/amlogic/media/utils/amlog.h> +MODULE_AMLOG(LOG_LEVEL_ERROR, 0, LOG_LEVEL_DESC, LOG_DEFAULT_MASK_DESC); + +#include "../utils/amvdec.h" + +#define DRIVER_NAME "amvdec_mjpeg" +#define MODULE_NAME "amvdec_mjpeg" + +/* protocol register usage + AV_SCRATCH_0 - AV_SCRATCH_1 : initial display buffer fifo + AV_SCRATCH_2 - AV_SCRATCH_3 : decoder settings + AV_SCRATCH_4 - AV_SCRATCH_7 : display buffer spec + AV_SCRATCH_8 - AV_SCRATCH_9 : amrisc/host display buffer management + AV_SCRATCH_a : time stamp +*/ + +#define MREG_DECODE_PARAM AV_SCRATCH_2 /* bit 0-3: pico_addr_mode */ +/* bit 15-4: reference height */ +#define MREG_TO_AMRISC AV_SCRATCH_8 +#define MREG_FROM_AMRISC AV_SCRATCH_9 +#define MREG_FRAME_OFFSET AV_SCRATCH_A + +#define PICINFO_BUF_IDX_MASK 0x0007 +#define PICINFO_AVI1 0x0080 +#define PICINFO_INTERLACE 0x0020 +#define PICINFO_INTERLACE_AVI1_BOT 0x0010 +#define PICINFO_INTERLACE_FIRST 0x0010 + +#define VF_POOL_SIZE 16 +#define DECODE_BUFFER_NUM_MAX 4 +#define PUT_INTERVAL (HZ/100) + +#if 1/*MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON6*/ +/* #define NV21 */ +#endif +static DEFINE_MUTEX(vmjpeg_mutex); + +static struct dec_sysinfo vmjpeg_amstream_dec_info; + +static struct vframe_s *vmjpeg_vf_peek(void *); +static struct vframe_s *vmjpeg_vf_get(void *); +static void vmjpeg_vf_put(struct vframe_s *, void *); +static int vmjpeg_vf_states(struct vframe_states *states, void *); +static int vmjpeg_event_cb(int type, void *data, void *private_data); + +static void vmjpeg_prot_init(void); +static void vmjpeg_local_init(void); + +static const char vmjpeg_dec_id[] = "vmjpeg-dev"; + +#define PROVIDER_NAME "decoder.mjpeg" +static const struct vframe_operations_s vmjpeg_vf_provider = { + .peek = vmjpeg_vf_peek, + .get = vmjpeg_vf_get, + .put = vmjpeg_vf_put, + .event_cb = vmjpeg_event_cb, + .vf_states = vmjpeg_vf_states, +}; + +static struct vframe_provider_s vmjpeg_vf_prov; + +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 struct vframe_s vfpool[VF_POOL_SIZE]; +static s32 vfbuf_use[DECODE_BUFFER_NUM_MAX]; + +static u32 frame_width, frame_height, frame_dur; +static u32 saved_resolution; +static struct timer_list recycle_timer; +static u32 stat; +static unsigned long buf_start; +static u32 buf_size; +static DEFINE_SPINLOCK(lock); + +static inline u32 index2canvas0(u32 index) +{ + const u32 canvas_tab[4] = { +#ifdef NV21 + 0x010100, 0x030302, 0x050504, 0x070706 +#else + 0x020100, 0x050403, 0x080706, 0x0b0a09 +#endif + }; + + return canvas_tab[index]; +} + +static inline u32 index2canvas1(u32 index) +{ + const u32 canvas_tab[4] = { +#ifdef NV21 + 0x0d0d0c, 0x0f0f0e, 0x171716, 0x191918 +#else + 0x0e0d0c, 0x181716, 0x222120, 0x252423 +#endif + }; + + return canvas_tab[index]; +} + +static void set_frame_info(struct vframe_s *vf) +{ + vf->width = frame_width; + vf->height = frame_height; + vf->duration = frame_dur; + vf->ratio_control = 0; + vf->duration_pulldown = 0; + vf->flag = 0; +} + +static irqreturn_t vmjpeg_isr(int irq, void *dev_id) +{ + u32 reg, offset, pts, pts_valid = 0; + struct vframe_s *vf = NULL; + u64 pts_us64; + + WRITE_VREG(ASSIST_MBOX1_CLR_REG, 1); + + reg = READ_VREG(MREG_FROM_AMRISC); + + if (reg & PICINFO_BUF_IDX_MASK) { + offset = READ_VREG(MREG_FRAME_OFFSET); + + if (pts_lookup_offset_us64 + (PTS_TYPE_VIDEO, offset, &pts, 0, &pts_us64) == 0) + pts_valid = 1; + + if ((reg & PICINFO_INTERLACE) == 0) { + u32 index = ((reg & PICINFO_BUF_IDX_MASK) - 1) & 3; + + if (index >= DECODE_BUFFER_NUM_MAX) { + pr_err("fatal error, invalid buffer index."); + return IRQ_HANDLED; + } + + if (kfifo_get(&newframe_q, &vf) == 0) { + pr_info( + "fatal error, no available buffer slot."); + return IRQ_HANDLED; + } + + set_frame_info(vf); + vf->signal_type = 0; + vf->index = index; +#ifdef NV21 + vf->type = + VIDTYPE_PROGRESSIVE | VIDTYPE_VIU_FIELD | + VIDTYPE_VIU_NV21; +#else + vf->type = VIDTYPE_PROGRESSIVE | VIDTYPE_VIU_FIELD; +#endif + vf->canvas0Addr = vf->canvas1Addr = + index2canvas0(index); + vf->pts = (pts_valid) ? pts : 0; + vf->pts_us64 = (pts_valid) ? pts_us64 : 0; + vf->orientation = 0; + vf->type_original = vf->type; + vfbuf_use[index]++; + + kfifo_put(&display_q, (const struct vframe_s *)vf); + + vf_notify_receiver(PROVIDER_NAME, + VFRAME_EVENT_PROVIDER_VFRAME_READY, + NULL); + + } else { + u32 index = ((reg & PICINFO_BUF_IDX_MASK) - 1) & 3; + + if (index >= DECODE_BUFFER_NUM_MAX) { + pr_info("fatal error, invalid buffer index."); + return IRQ_HANDLED; + } + + if (kfifo_get(&newframe_q, &vf) == 0) { + pr_info + ("fatal error, no available buffer slot."); + return IRQ_HANDLED; + } + + set_frame_info(vf); + vf->signal_type = 0; + vf->index = index; +#if 0 + if (reg & PICINFO_AVI1) { + /* AVI1 format */ + if (reg & PICINFO_INTERLACE_AVI1_BOT) { + vf->type = + VIDTYPE_INTERLACE_BOTTOM | + VIDTYPE_INTERLACE_FIRST; + } else + vf->type = VIDTYPE_INTERLACE_TOP; + } else { + if (reg & PICINFO_INTERLACE_FIRST) { + vf->type = + VIDTYPE_INTERLACE_TOP | + VIDTYPE_INTERLACE_FIRST; + } else + vf->type = VIDTYPE_INTERLACE_BOTTOM; + } + + vf->type |= VIDTYPE_VIU_FIELD; +#ifdef NV21 + vf->type |= VIDTYPE_VIU_NV21; +#endif + vf->duration >>= 1; + vf->canvas0Addr = vf->canvas1Addr = + index2canvas0(index); + vf->orientation = 0; + if ((vf->type & VIDTYPE_INTERLACE_FIRST) && + (pts_valid)) + vf->pts = pts; + else + vf->pts = 0; + + vfbuf_use[index]++; + + kfifo_put(&display_q, (const struct vframe_s *)vf); +#else + /* send whole frame by weaving top & bottom field */ +#ifdef NV21 + vf->type = VIDTYPE_PROGRESSIVE | VIDTYPE_VIU_NV21; +#else + vf->type = VIDTYPE_PROGRESSIVE; +#endif + vf->canvas0Addr = index2canvas0(index); + vf->canvas1Addr = index2canvas1(index); + vf->orientation = 0; + if (pts_valid) { + vf->pts = pts; + vf->pts_us64 = pts_us64; + } else { + vf->pts = 0; + vf->pts_us64 = 0; + } + vf->type_original = vf->type; + vfbuf_use[index]++; + + kfifo_put(&display_q, (const struct vframe_s *)vf); + + vf_notify_receiver(PROVIDER_NAME, + VFRAME_EVENT_PROVIDER_VFRAME_READY, + NULL); +#endif + } + + WRITE_VREG(MREG_FROM_AMRISC, 0); + } + + return IRQ_HANDLED; +} + +static struct vframe_s *vmjpeg_vf_peek(void *op_arg) +{ + struct vframe_s *vf; + + if (kfifo_peek(&display_q, &vf)) + return vf; + + return NULL; +} + +static struct vframe_s *vmjpeg_vf_get(void *op_arg) +{ + struct vframe_s *vf; + + if (kfifo_get(&display_q, &vf)) + return vf; + + return NULL; +} + +static void vmjpeg_vf_put(struct vframe_s *vf, void *op_arg) +{ + kfifo_put(&recycle_q, (const struct vframe_s *)vf); +} + +static int vmjpeg_event_cb(int type, void *data, void *private_data) +{ + if (type & VFRAME_EVENT_RECEIVER_RESET) { + unsigned long flags; + amvdec_stop(); +#ifndef CONFIG_AMLOGIC_POST_PROCESS_MANAGER + vf_light_unreg_provider(&vmjpeg_vf_prov); +#endif + spin_lock_irqsave(&lock, flags); + vmjpeg_local_init(); + vmjpeg_prot_init(); + spin_unlock_irqrestore(&lock, flags); +#ifndef CONFIG_AMLOGIC_POST_PROCESS_MANAGER + vf_reg_provider(&vmjpeg_vf_prov); +#endif + amvdec_start(); + } + return 0; +} + +static int vmjpeg_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); + states->buf_recycle_num = kfifo_len(&recycle_q); + + spin_unlock_irqrestore(&lock, flags); + + return 0; +} + +static void vmjpeg_put_timer_func(unsigned long arg) +{ + struct timer_list *timer = (struct timer_list *)arg; + + while (!kfifo_is_empty(&recycle_q) && + (READ_VREG(MREG_TO_AMRISC) == 0)) { + struct vframe_s *vf; + if (kfifo_get(&recycle_q, &vf)) { + if ((vf->index >= 0) + && (vf->index < DECODE_BUFFER_NUM_MAX) + && (--vfbuf_use[vf->index] == 0)) { + WRITE_VREG(MREG_TO_AMRISC, vf->index + 1); + vf->index = DECODE_BUFFER_NUM_MAX; + } + + kfifo_put(&newframe_q, (const struct vframe_s *)vf); + } + } + if (frame_dur > 0 && saved_resolution != + frame_width * frame_height * (96000 / frame_dur)) { + int fps = 96000 / frame_dur; + saved_resolution = frame_width * frame_height * fps; + vdec_source_changed(VFORMAT_MJPEG, + frame_width, frame_height, fps); + } + timer->expires = jiffies + PUT_INTERVAL; + + add_timer(timer); +} + +int vmjpeg_dec_status(struct vdec_s *vdec, struct vdec_status *vstatus) +{ + vstatus->width = frame_width; + vstatus->height = frame_height; + if (0 != frame_dur) + vstatus->fps = 96000 / frame_dur; + else + vstatus->fps = 96000; + vstatus->error_count = 0; + vstatus->status = stat; + + return 0; +} + +/****************************************/ +static void vmjpeg_canvas_init(void) +{ + int i; + u32 canvas_width, canvas_height; + u32 decbuf_size, decbuf_y_size, decbuf_uv_size; + u32 disp_addr = 0xffffffff; + + if (buf_size <= 0x00400000) { + /* SD only */ + canvas_width = 768; + canvas_height = 576; + decbuf_y_size = 0x80000; + decbuf_uv_size = 0x20000; + decbuf_size = 0x100000; + } else { + /* HD & SD */ + canvas_width = 1920; + canvas_height = 1088; + decbuf_y_size = 0x200000; + decbuf_uv_size = 0x80000; + decbuf_size = 0x300000; + } + + if (is_vpp_postblend()) { + struct canvas_s cur_canvas; + + canvas_read((READ_VCBUS_REG(VD1_IF0_CANVAS0) & 0xff), + &cur_canvas); + disp_addr = (cur_canvas.addr + 7) >> 3; + } + + for (i = 0; i < 4; i++) { + if (((buf_start + i * decbuf_size + 7) >> 3) == disp_addr) { +#ifdef NV21 + canvas_config(index2canvas0(i) & 0xff, + buf_start + 4 * decbuf_size, + canvas_width, canvas_height, + CANVAS_ADDR_NOWRAP, + CANVAS_BLKMODE_LINEAR); + canvas_config((index2canvas0(i) >> 8) & 0xff, + buf_start + 4 * decbuf_size + + decbuf_y_size, canvas_width, + canvas_height / 2, CANVAS_ADDR_NOWRAP, + CANVAS_BLKMODE_LINEAR); + canvas_config(index2canvas1(i) & 0xff, + buf_start + 4 * decbuf_size + + decbuf_size / 2, canvas_width, + canvas_height, CANVAS_ADDR_NOWRAP, + CANVAS_BLKMODE_LINEAR); + canvas_config((index2canvas1(i) >> 8) & 0xff, + buf_start + 4 * decbuf_size + + decbuf_y_size + decbuf_uv_size / 2, + canvas_width, canvas_height / 2, + CANVAS_ADDR_NOWRAP, + CANVAS_BLKMODE_LINEAR); +#else + canvas_config(index2canvas0(i) & 0xff, + buf_start + 4 * decbuf_size, + canvas_width, canvas_height, + CANVAS_ADDR_NOWRAP, + CANVAS_BLKMODE_LINEAR); + canvas_config((index2canvas0(i) >> 8) & 0xff, + buf_start + 4 * decbuf_size + + decbuf_y_size, canvas_width / 2, + canvas_height / 2, CANVAS_ADDR_NOWRAP, + CANVAS_BLKMODE_LINEAR); + canvas_config((index2canvas0(i) >> 16) & 0xff, + buf_start + 4 * decbuf_size + + decbuf_y_size + decbuf_uv_size, + canvas_width / 2, canvas_height / 2, + CANVAS_ADDR_NOWRAP, + CANVAS_BLKMODE_LINEAR); + canvas_config(index2canvas1(i) & 0xff, + buf_start + 4 * decbuf_size + + decbuf_size / 2, canvas_width, + canvas_height, CANVAS_ADDR_NOWRAP, + CANVAS_BLKMODE_LINEAR); + canvas_config((index2canvas1(i) >> 8) & 0xff, + buf_start + 4 * decbuf_size + + decbuf_y_size + decbuf_uv_size / 2, + canvas_width / 2, canvas_height / 2, + CANVAS_ADDR_NOWRAP, + CANVAS_BLKMODE_LINEAR); + canvas_config((index2canvas1(i) >> 16) & 0xff, + buf_start + 4 * decbuf_size + + decbuf_y_size + decbuf_uv_size + + decbuf_uv_size / 2, canvas_width / 2, + canvas_height / 2, CANVAS_ADDR_NOWRAP, + CANVAS_BLKMODE_LINEAR); +#endif + } else { +#ifdef NV21 + canvas_config(index2canvas0(i) & 0xff, + buf_start + i * decbuf_size, + canvas_width, canvas_height, + CANVAS_ADDR_NOWRAP, + CANVAS_BLKMODE_LINEAR); + canvas_config((index2canvas0(i) >> 8) & 0xff, + buf_start + i * decbuf_size + + decbuf_y_size, canvas_width, + canvas_height / 2, CANVAS_ADDR_NOWRAP, + CANVAS_BLKMODE_LINEAR); + canvas_config(index2canvas1(i) & 0xff, + buf_start + i * decbuf_size + + decbuf_size / 2, canvas_width, + canvas_height, CANVAS_ADDR_NOWRAP, + CANVAS_BLKMODE_LINEAR); + canvas_config((index2canvas1(i) >> 8) & 0xff, + buf_start + i * decbuf_size + + decbuf_y_size + decbuf_uv_size / 2, + canvas_width, canvas_height / 2, + CANVAS_ADDR_NOWRAP, + CANVAS_BLKMODE_LINEAR); +#else + canvas_config(index2canvas0(i) & 0xff, + buf_start + i * decbuf_size, + canvas_width, canvas_height, + CANVAS_ADDR_NOWRAP, + CANVAS_BLKMODE_LINEAR); + canvas_config((index2canvas0(i) >> 8) & 0xff, + buf_start + i * decbuf_size + + decbuf_y_size, canvas_width / 2, + canvas_height / 2, CANVAS_ADDR_NOWRAP, + CANVAS_BLKMODE_LINEAR); + canvas_config((index2canvas0(i) >> 16) & 0xff, + buf_start + i * decbuf_size + + decbuf_y_size + decbuf_uv_size, + canvas_width / 2, canvas_height / 2, + CANVAS_ADDR_NOWRAP, + CANVAS_BLKMODE_LINEAR); + canvas_config(index2canvas1(i) & 0xff, + buf_start + i * decbuf_size + + decbuf_size / 2, canvas_width, + canvas_height, CANVAS_ADDR_NOWRAP, + CANVAS_BLKMODE_LINEAR); + canvas_config((index2canvas1(i) >> 8) & 0xff, + buf_start + i * decbuf_size + + decbuf_y_size + decbuf_uv_size / 2, + canvas_width / 2, canvas_height / 2, + CANVAS_ADDR_NOWRAP, + CANVAS_BLKMODE_LINEAR); + canvas_config((index2canvas1(i) >> 16) & 0xff, + buf_start + i * decbuf_size + + decbuf_y_size + decbuf_uv_size + + decbuf_uv_size / 2, canvas_width / 2, + canvas_height / 2, CANVAS_ADDR_NOWRAP, + CANVAS_BLKMODE_LINEAR); +#endif + } + } +} + +static void init_scaler(void) +{ + /* 4 point triangle */ + const unsigned filt_coef[] = { + 0x20402000, 0x20402000, 0x1f3f2101, 0x1f3f2101, + 0x1e3e2202, 0x1e3e2202, 0x1d3d2303, 0x1d3d2303, + 0x1c3c2404, 0x1c3c2404, 0x1b3b2505, 0x1b3b2505, + 0x1a3a2606, 0x1a3a2606, 0x19392707, 0x19392707, + 0x18382808, 0x18382808, 0x17372909, 0x17372909, + 0x16362a0a, 0x16362a0a, 0x15352b0b, 0x15352b0b, + 0x14342c0c, 0x14342c0c, 0x13332d0d, 0x13332d0d, + 0x12322e0e, 0x12322e0e, 0x11312f0f, 0x11312f0f, + 0x10303010 + }; + int i; + + /* pscale enable, PSCALE cbus bmem enable */ + WRITE_VREG(PSCALE_CTRL, 0xc000); + + /* write filter coefs */ + WRITE_VREG(PSCALE_BMEM_ADDR, 0); + for (i = 0; i < 33; i++) { + WRITE_VREG(PSCALE_BMEM_DAT, 0); + WRITE_VREG(PSCALE_BMEM_DAT, filt_coef[i]); + } + + /* Y horizontal initial info */ + WRITE_VREG(PSCALE_BMEM_ADDR, 37 * 2); + /* [35]: buf repeat pix0, + * [34:29] => buf receive num, + * [28:16] => buf blk x, + * [15:0] => buf phase + */ + WRITE_VREG(PSCALE_BMEM_DAT, 0x0008); + WRITE_VREG(PSCALE_BMEM_DAT, 0x60000000); + + /* C horizontal initial info */ + WRITE_VREG(PSCALE_BMEM_ADDR, 41 * 2); + WRITE_VREG(PSCALE_BMEM_DAT, 0x0008); + WRITE_VREG(PSCALE_BMEM_DAT, 0x60000000); + + /* Y vertical initial info */ + WRITE_VREG(PSCALE_BMEM_ADDR, 39 * 2); + WRITE_VREG(PSCALE_BMEM_DAT, 0x0008); + WRITE_VREG(PSCALE_BMEM_DAT, 0x60000000); + + /* C vertical initial info */ + WRITE_VREG(PSCALE_BMEM_ADDR, 43 * 2); + WRITE_VREG(PSCALE_BMEM_DAT, 0x0008); + WRITE_VREG(PSCALE_BMEM_DAT, 0x60000000); + + /* Y horizontal phase step */ + WRITE_VREG(PSCALE_BMEM_ADDR, 36 * 2 + 1); + /* [19:0] => Y horizontal phase step */ + WRITE_VREG(PSCALE_BMEM_DAT, 0x10000); + /* C horizontal phase step */ + WRITE_VREG(PSCALE_BMEM_ADDR, 40 * 2 + 1); + /* [19:0] => C horizontal phase step */ + WRITE_VREG(PSCALE_BMEM_DAT, 0x10000); + + /* Y vertical phase step */ + WRITE_VREG(PSCALE_BMEM_ADDR, 38 * 2 + 1); + /* [19:0] => Y vertical phase step */ + WRITE_VREG(PSCALE_BMEM_DAT, 0x10000); + /* C vertical phase step */ + WRITE_VREG(PSCALE_BMEM_ADDR, 42 * 2 + 1); + /* [19:0] => C horizontal phase step */ + WRITE_VREG(PSCALE_BMEM_DAT, 0x10000); + + /* reset pscaler */ +#if 1/*MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON6*/ + WRITE_VREG(DOS_SW_RESET0, (1 << 10)); + WRITE_VREG(DOS_SW_RESET0, 0); +#else + WRITE_MPEG_REG(RESET2_REGISTER, RESET_PSCALE); +#endif + READ_MPEG_REG(RESET2_REGISTER); + READ_MPEG_REG(RESET2_REGISTER); + READ_MPEG_REG(RESET2_REGISTER); + + WRITE_VREG(PSCALE_RST, 0x7); + WRITE_VREG(PSCALE_RST, 0x0); +} + +static void vmjpeg_prot_init(void) +{ +#if 1/*MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON6*/ + WRITE_VREG(DOS_SW_RESET0, (1 << 7) | (1 << 6)); + WRITE_VREG(DOS_SW_RESET0, 0); +#else + WRITE_MPEG_REG(RESET0_REGISTER, RESET_IQIDCT | RESET_MC); +#endif + + vmjpeg_canvas_init(); + + WRITE_VREG(AV_SCRATCH_0, 12); + WRITE_VREG(AV_SCRATCH_1, 0x031a); +#ifdef NV21 + WRITE_VREG(AV_SCRATCH_4, 0x010100); + WRITE_VREG(AV_SCRATCH_5, 0x030302); + WRITE_VREG(AV_SCRATCH_6, 0x050504); + WRITE_VREG(AV_SCRATCH_7, 0x070706); +#else + WRITE_VREG(AV_SCRATCH_4, 0x020100); + WRITE_VREG(AV_SCRATCH_5, 0x050403); + WRITE_VREG(AV_SCRATCH_6, 0x080706); + WRITE_VREG(AV_SCRATCH_7, 0x0b0a09); +#endif + init_scaler(); + + /* clear buffer IN/OUT registers */ + WRITE_VREG(MREG_TO_AMRISC, 0); + WRITE_VREG(MREG_FROM_AMRISC, 0); + + WRITE_VREG(MCPU_INTR_MSK, 0xffff); + WRITE_VREG(MREG_DECODE_PARAM, (frame_height << 4) | 0x8000); + + /* clear mailbox interrupt */ + WRITE_VREG(ASSIST_MBOX1_CLR_REG, 1); + /* enable mailbox interrupt */ + WRITE_VREG(ASSIST_MBOX1_MASK, 1); + /* set interrupt mapping for vld */ + WRITE_VREG(ASSIST_AMR1_INT8, 8); +#if 1/*MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON6*/ +#ifdef NV21 + SET_VREG_MASK(MDEC_PIC_DC_CTRL, 1 << 17); +#else + CLEAR_VREG_MASK(MDEC_PIC_DC_CTRL, 1 << 17); +#endif +#endif +} + +static void vmjpeg_local_init(void) +{ + int i; + + frame_width = vmjpeg_amstream_dec_info.width; + frame_height = vmjpeg_amstream_dec_info.height; + frame_dur = vmjpeg_amstream_dec_info.rate; + saved_resolution = 0; + amlog_level(LOG_LEVEL_INFO, "mjpegdec: w(%d), h(%d), dur(%d)\n", + frame_width, frame_height, frame_dur); + + for (i = 0; i < DECODE_BUFFER_NUM_MAX; i++) + vfbuf_use[i] = 0; + + INIT_KFIFO(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 = DECODE_BUFFER_NUM_MAX; + kfifo_put(&newframe_q, vf); + } +} + +static s32 vmjpeg_init(void) +{ + int ret = -1,size = -1; + char *buf = vmalloc(0x1000 * 16); + if (IS_ERR_OR_NULL(buf)) + return -ENOMEM; + + init_timer(&recycle_timer); + + stat |= STAT_TIMER_INIT; + + amvdec_enable(); + + vmjpeg_local_init(); + + size = get_firmware_data(VIDEO_DEC_MJPEG, buf); + if (size < 0) { + pr_err("get firmware fail."); + vfree(buf); + return -1; + } + + if (amvdec_loadmc_ex(VFORMAT_MJPEG, NULL, buf) < 0) { + amvdec_disable(); + vfree(buf); + return -EBUSY; + } + + vfree(buf); + + stat |= STAT_MC_LOAD; + + /* enable AMRISC side protocol */ + vmjpeg_prot_init(); + + ret = vdec_request_irq(VDEC_IRQ_1, vmjpeg_isr, + "vmjpeg-irq", (void *)vmjpeg_dec_id); + + if (ret) { + amvdec_disable(); + + amlog_level(LOG_LEVEL_ERROR, "vmjpeg irq register error.\n"); + return -ENOENT; + } + + stat |= STAT_ISR_REG; + +#ifdef CONFIG_AMLOGIC_POST_PROCESS_MANAGER + vf_provider_init(&vmjpeg_vf_prov, PROVIDER_NAME, &vmjpeg_vf_provider, + NULL); + vf_reg_provider(&vmjpeg_vf_prov); + vf_notify_receiver(PROVIDER_NAME, VFRAME_EVENT_PROVIDER_START, NULL); +#else + vf_provider_init(&vmjpeg_vf_prov, PROVIDER_NAME, &vmjpeg_vf_provider, + NULL); + vf_reg_provider(&vmjpeg_vf_prov); +#endif + + vf_notify_receiver(PROVIDER_NAME, VFRAME_EVENT_PROVIDER_FR_HINT, + (void *)((unsigned long)vmjpeg_amstream_dec_info.rate)); + + stat |= STAT_VF_HOOK; + + recycle_timer.data = (ulong)&recycle_timer; + recycle_timer.function = vmjpeg_put_timer_func; + recycle_timer.expires = jiffies + PUT_INTERVAL; + + add_timer(&recycle_timer); + + stat |= STAT_TIMER_ARM; + + amvdec_start(); + + stat |= STAT_VDEC_RUN; + + return 0; +} + +static int amvdec_mjpeg_probe(struct platform_device *pdev) +{ + struct vdec_s *pdata = *(struct vdec_s **)pdev->dev.platform_data; + + mutex_lock(&vmjpeg_mutex); + + amlog_level(LOG_LEVEL_INFO, "amvdec_mjpeg probe start.\n"); + + if (pdata == NULL) { + amlog_level(LOG_LEVEL_ERROR, + "amvdec_mjpeg memory resource undefined.\n"); + mutex_unlock(&vmjpeg_mutex); + + return -EFAULT; + } + + buf_start = pdata->mem_start; + buf_size = pdata->mem_end - pdata->mem_start + 1; + + if (pdata->sys_info) + vmjpeg_amstream_dec_info = *pdata->sys_info; + + pdata->dec_status = vmjpeg_dec_status; + + if (vmjpeg_init() < 0) { + amlog_level(LOG_LEVEL_ERROR, "amvdec_mjpeg init failed.\n"); + mutex_unlock(&vmjpeg_mutex); + + return -ENODEV; + } + + mutex_unlock(&vmjpeg_mutex); + + amlog_level(LOG_LEVEL_INFO, "amvdec_mjpeg probe end.\n"); + + return 0; +} + +static int amvdec_mjpeg_remove(struct platform_device *pdev) +{ + mutex_lock(&vmjpeg_mutex); + + if (stat & STAT_VDEC_RUN) { + amvdec_stop(); + stat &= ~STAT_VDEC_RUN; + } + + if (stat & STAT_ISR_REG) { + vdec_free_irq(VDEC_IRQ_1, (void *)vmjpeg_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) { + vf_notify_receiver(PROVIDER_NAME, + VFRAME_EVENT_PROVIDER_FR_END_HINT, NULL); + + vf_unreg_provider(&vmjpeg_vf_prov); + stat &= ~STAT_VF_HOOK; + } + + amvdec_disable(); + + mutex_unlock(&vmjpeg_mutex); + + amlog_level(LOG_LEVEL_INFO, "amvdec_mjpeg remove.\n"); + + return 0; +} + +/****************************************/ + +static struct platform_driver amvdec_mjpeg_driver = { + .probe = amvdec_mjpeg_probe, + .remove = amvdec_mjpeg_remove, +#ifdef CONFIG_PM + .suspend = amvdec_suspend, + .resume = amvdec_resume, +#endif + .driver = { + .name = DRIVER_NAME, + } +}; + +static struct codec_profile_t amvdec_mjpeg_profile = { + .name = "mjpeg", + .profile = "" +}; + +static int __init amvdec_mjpeg_driver_init_module(void) +{ + amlog_level(LOG_LEVEL_INFO, "amvdec_mjpeg module init\n"); + + if (platform_driver_register(&amvdec_mjpeg_driver)) { + amlog_level(LOG_LEVEL_ERROR, + "failed to register amvdec_mjpeg driver\n"); + return -ENODEV; + } + vcodec_profile_register(&amvdec_mjpeg_profile); + return 0; +} + +static void __exit amvdec_mjpeg_driver_remove_module(void) +{ + amlog_level(LOG_LEVEL_INFO, "amvdec_mjpeg module remove.\n"); + + platform_driver_unregister(&amvdec_mjpeg_driver); +} + +/****************************************/ + +module_param(stat, uint, 0664); +MODULE_PARM_DESC(stat, "\n amvdec_mjpeg stat\n"); + +module_init(amvdec_mjpeg_driver_init_module); +module_exit(amvdec_mjpeg_driver_remove_module); + +MODULE_DESCRIPTION("AMLOGIC MJMPEG Video Decoder Driver"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Tim Yao <timyao@amlogic.com>"); diff --git a/drivers/frame_provider/decoder/mjpeg/vmjpeg_multi.c b/drivers/frame_provider/decoder/mjpeg/vmjpeg_multi.c new file mode 100644 index 0000000..850e0b6 --- a/dev/null +++ b/drivers/frame_provider/decoder/mjpeg/vmjpeg_multi.c @@ -0,0 +1,723 @@ +/* + * drivers/amlogic/amports/vmjpeg.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/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/frame_sync/ptsserv.h> +#include <linux/amlogic/media/utils/amstream.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/utils/vdec_reg.h> +#include <linux/amlogic/media/registers/register.h> +#include "../../../stream_input/amports/amports_priv.h" + +#include <linux/amlogic/media/codec_mm/codec_mm.h> + +#include "../utils/vdec_input.h" +#include "../utils/vdec.h" +#include "../utils/amvdec.h" + +#define MEM_NAME "codec_mmjpeg" + +#define DRIVER_NAME "ammvdec_mjpeg" +#define MODULE_NAME "ammvdec_mjpeg" + +/* protocol register usage + AV_SCRATCH_4 : decode buffer spec + AV_SCRATCH_5 : decode buffer index +*/ + +#define MREG_DECODE_PARAM AV_SCRATCH_2 /* bit 0-3: pico_addr_mode */ +/* bit 15-4: reference height */ +#define MREG_TO_AMRISC AV_SCRATCH_8 +#define MREG_FROM_AMRISC AV_SCRATCH_9 +#define MREG_FRAME_OFFSET AV_SCRATCH_A + +#define PICINFO_BUF_IDX_MASK 0x0007 +#define PICINFO_AVI1 0x0080 +#define PICINFO_INTERLACE 0x0020 +#define PICINFO_INTERLACE_AVI1_BOT 0x0010 +#define PICINFO_INTERLACE_FIRST 0x0010 + +#define VF_POOL_SIZE 16 +#define DECODE_BUFFER_NUM_MAX 4 + +static struct vframe_s *vmjpeg_vf_peek(void *); +static struct vframe_s *vmjpeg_vf_get(void *); +static void vmjpeg_vf_put(struct vframe_s *, void *); +static int vmjpeg_vf_states(struct vframe_states *states, void *); +static int vmjpeg_event_cb(int type, void *data, void *private_data); +static void vmjpeg_work(struct work_struct *work); + +static const char vmjpeg_dec_id[] = "vmmjpeg-dev"; + +#define PROVIDER_NAME "vdec.mjpeg" +static const struct vframe_operations_s vf_provider_ops = { + .peek = vmjpeg_vf_peek, + .get = vmjpeg_vf_get, + .put = vmjpeg_vf_put, + .event_cb = vmjpeg_event_cb, + .vf_states = vmjpeg_vf_states, +}; + +#define DEC_RESULT_NONE 0 +#define DEC_RESULT_DONE 1 +#define DEC_RESULT_AGAIN 2 + +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; + + struct canvas_config_s canvas_config[3]; + unsigned long cma_alloc_addr; + int cma_alloc_count; + unsigned int buf_adr; +}; + +#define spec2canvas(x) \ + (((x)->v_canvas_index << 16) | \ + ((x)->u_canvas_index << 8) | \ + ((x)->y_canvas_index << 0)) + +struct vdec_mjpeg_hw_s { + spinlock_t lock; + struct mutex vmjpeg_mutex; + + struct platform_device *platform_dev; + DECLARE_KFIFO(newframe_q, struct vframe_s *, VF_POOL_SIZE); + DECLARE_KFIFO(display_q, struct vframe_s *, VF_POOL_SIZE); + + struct vframe_s vfpool[VF_POOL_SIZE]; + struct buffer_spec_s buffer_spec[DECODE_BUFFER_NUM_MAX]; + s32 vfbuf_use[DECODE_BUFFER_NUM_MAX]; + + u32 frame_width; + u32 frame_height; + u32 frame_dur; + u32 saved_resolution; + + u32 stat; + u32 dec_result; + unsigned long buf_start; + u32 buf_size; + + struct dec_sysinfo vmjpeg_amstream_dec_info; + + struct vframe_chunk_s *chunk; + struct work_struct work; + void (*vdec_cb)(struct vdec_s *, void *); + void *vdec_cb_arg; +}; + +static void set_frame_info(struct vdec_mjpeg_hw_s *hw, struct vframe_s *vf) +{ + vf->width = hw->frame_width; + vf->height = hw->frame_height; + vf->duration = hw->frame_dur; + vf->ratio_control = 0; + vf->duration_pulldown = 0; + vf->flag = 0; + + vf->canvas0Addr = vf->canvas1Addr = -1; + vf->plane_num = 3; + + vf->canvas0_config[0] = hw->buffer_spec[vf->index].canvas_config[0]; + vf->canvas0_config[1] = hw->buffer_spec[vf->index].canvas_config[1]; + vf->canvas0_config[2] = hw->buffer_spec[vf->index].canvas_config[2]; + + vf->canvas1_config[0] = hw->buffer_spec[vf->index].canvas_config[0]; + vf->canvas1_config[1] = hw->buffer_spec[vf->index].canvas_config[1]; + vf->canvas1_config[2] = hw->buffer_spec[vf->index].canvas_config[2]; +} + +static irqreturn_t vmjpeg_isr(struct vdec_s *vdec) +{ + struct vdec_mjpeg_hw_s *hw = (struct vdec_mjpeg_hw_s *)(vdec->private); + u32 reg; + struct vframe_s *vf = NULL; + u32 index; + + WRITE_VREG(ASSIST_MBOX1_CLR_REG, 1); + + if (!hw) + return IRQ_HANDLED; + + reg = READ_VREG(MREG_FROM_AMRISC); + index = READ_VREG(AV_SCRATCH_5); + + if (index >= DECODE_BUFFER_NUM_MAX) { + pr_err("fatal error, invalid buffer index."); + return IRQ_HANDLED; + } + + if (kfifo_get(&hw->newframe_q, &vf) == 0) { + pr_info( + "fatal error, no available buffer slot."); + return IRQ_HANDLED; + } + + vf->index = index; + set_frame_info(hw, vf); + + vf->type = VIDTYPE_PROGRESSIVE | VIDTYPE_VIU_FIELD; + /* vf->pts = (pts_valid) ? pts : 0; */ + /* vf->pts_us64 = (pts_valid) ? pts_us64 : 0; */ + vf->pts = hw->chunk->pts; + vf->pts_us64 = hw->chunk->pts64; + vf->orientation = 0; + hw->vfbuf_use[index]++; + + kfifo_put(&hw->display_q, (const struct vframe_s *)vf); + + vf_notify_receiver(vdec->vf_provider_name, + VFRAME_EVENT_PROVIDER_VFRAME_READY, + NULL); + + hw->dec_result = DEC_RESULT_DONE; + + schedule_work(&hw->work); + + return IRQ_HANDLED; +} + +static struct vframe_s *vmjpeg_vf_peek(void *op_arg) +{ + struct vframe_s *vf; + struct vdec_s *vdec = op_arg; + struct vdec_mjpeg_hw_s *hw = (struct vdec_mjpeg_hw_s *)vdec->private; + + if (!hw) + return NULL; + + if (kfifo_peek(&hw->display_q, &vf)) + return vf; + + return NULL; +} + +static struct vframe_s *vmjpeg_vf_get(void *op_arg) +{ + struct vframe_s *vf; + struct vdec_s *vdec = op_arg; + struct vdec_mjpeg_hw_s *hw = (struct vdec_mjpeg_hw_s *)vdec->private; + + if (!hw) + return NULL; + + if (kfifo_get(&hw->display_q, &vf)) + return vf; + + return NULL; +} + +static void vmjpeg_vf_put(struct vframe_s *vf, void *op_arg) +{ + struct vdec_s *vdec = op_arg; + struct vdec_mjpeg_hw_s *hw = (struct vdec_mjpeg_hw_s *)vdec->private; + + hw->vfbuf_use[vf->index]--; + kfifo_put(&hw->newframe_q, (const struct vframe_s *)vf); +} + +static int vmjpeg_event_cb(int type, void *data, void *private_data) +{ + return 0; +} + +static int vmjpeg_vf_states(struct vframe_states *states, void *op_arg) +{ + unsigned long flags; + struct vdec_s *vdec = op_arg; + struct vdec_mjpeg_hw_s *hw = (struct vdec_mjpeg_hw_s *)vdec->private; + + spin_lock_irqsave(&hw->lock, flags); + + states->vf_pool_size = VF_POOL_SIZE; + states->buf_free_num = kfifo_len(&hw->newframe_q); + states->buf_avail_num = kfifo_len(&hw->display_q); + states->buf_recycle_num = 0; + + spin_unlock_irqrestore(&hw->lock, flags); + + return 0; +} + +static int vmjpeg_dec_status(struct vdec_s *vdec, struct vdec_status *vstatus) +{ + struct vdec_mjpeg_hw_s *hw = (struct vdec_mjpeg_hw_s *)vdec->private; + vstatus->width = hw->frame_width; + vstatus->height = hw->frame_height; + if (0 != hw->frame_dur) + vstatus->fps = 96000 / hw->frame_dur; + else + vstatus->fps = 96000; + vstatus->error_count = 0; + vstatus->status = hw->stat; + + return 0; +} + +/****************************************/ +static void vmjpeg_canvas_init(struct vdec_s *vdec) +{ + int i; + u32 canvas_width, canvas_height; + u32 decbuf_size, decbuf_y_size, decbuf_uv_size; + struct vdec_mjpeg_hw_s *hw = + (struct vdec_mjpeg_hw_s *)vdec->private; + ulong addr; + + canvas_width = 1920; + canvas_height = 1088; + decbuf_y_size = 0x200000; + decbuf_uv_size = 0x80000; + decbuf_size = 0x300000; + + for (i = 0; i < DECODE_BUFFER_NUM_MAX; i++) { + int canvas; + + canvas = vdec->get_canvas(i, 3); + if (hw->buffer_spec[i].cma_alloc_count == 0) { + hw->buffer_spec[i].cma_alloc_count = + PAGE_ALIGN(decbuf_size) / PAGE_SIZE; + hw->buffer_spec[i].cma_alloc_addr = + codec_mm_alloc_for_dma(MEM_NAME, + hw->buffer_spec[i].cma_alloc_count, + 16, CODEC_MM_FLAGS_FOR_VDECODER); + } + + if (!hw->buffer_spec[i].cma_alloc_addr) { + pr_err("CMA alloc failed, request buf size 0x%lx\n", + hw->buffer_spec[i].cma_alloc_count * PAGE_SIZE); + hw->buffer_spec[i].cma_alloc_count = 0; + break; + } + + hw->buffer_spec[i].buf_adr = + hw->buffer_spec[i].cma_alloc_addr; + addr = hw->buffer_spec[i].buf_adr; + + hw->buffer_spec[i].y_addr = addr; + addr += decbuf_y_size; + hw->buffer_spec[i].u_addr = addr; + addr += decbuf_uv_size; + hw->buffer_spec[i].v_addr = addr; + + hw->buffer_spec[i].y_canvas_index = canvas_y(canvas); + hw->buffer_spec[i].u_canvas_index = canvas_u(canvas); + hw->buffer_spec[i].v_canvas_index = canvas_v(canvas); + + canvas_config(hw->buffer_spec[i].y_canvas_index, + hw->buffer_spec[i].y_addr, + canvas_width, + canvas_height, + CANVAS_ADDR_NOWRAP, + CANVAS_BLKMODE_LINEAR); + hw->buffer_spec[i].canvas_config[0].phy_addr = + hw->buffer_spec[i].y_addr; + hw->buffer_spec[i].canvas_config[0].width = + canvas_width; + hw->buffer_spec[i].canvas_config[0].height = + canvas_height; + hw->buffer_spec[i].canvas_config[0].block_mode = + CANVAS_BLKMODE_LINEAR; + + canvas_config(hw->buffer_spec[i].u_canvas_index, + hw->buffer_spec[i].u_addr, + canvas_width / 2, + canvas_height / 2, + CANVAS_ADDR_NOWRAP, + CANVAS_BLKMODE_LINEAR); + hw->buffer_spec[i].canvas_config[1].phy_addr = + hw->buffer_spec[i].u_addr; + hw->buffer_spec[i].canvas_config[1].width = + canvas_width / 2; + hw->buffer_spec[i].canvas_config[1].height = + canvas_height / 2; + hw->buffer_spec[i].canvas_config[1].block_mode = + CANVAS_BLKMODE_LINEAR; + + canvas_config(hw->buffer_spec[i].v_canvas_index, + hw->buffer_spec[i].v_addr, + canvas_width / 2, + canvas_height / 2, + CANVAS_ADDR_NOWRAP, + CANVAS_BLKMODE_LINEAR); + hw->buffer_spec[i].canvas_config[2].phy_addr = + hw->buffer_spec[i].v_addr; + hw->buffer_spec[i].canvas_config[2].width = + canvas_width / 2; + hw->buffer_spec[i].canvas_config[2].height = + canvas_height / 2; + hw->buffer_spec[i].canvas_config[2].block_mode = + CANVAS_BLKMODE_LINEAR; + } +} + +static void init_scaler(void) +{ + /* 4 point triangle */ + const unsigned filt_coef[] = { + 0x20402000, 0x20402000, 0x1f3f2101, 0x1f3f2101, + 0x1e3e2202, 0x1e3e2202, 0x1d3d2303, 0x1d3d2303, + 0x1c3c2404, 0x1c3c2404, 0x1b3b2505, 0x1b3b2505, + 0x1a3a2606, 0x1a3a2606, 0x19392707, 0x19392707, + 0x18382808, 0x18382808, 0x17372909, 0x17372909, + 0x16362a0a, 0x16362a0a, 0x15352b0b, 0x15352b0b, + 0x14342c0c, 0x14342c0c, 0x13332d0d, 0x13332d0d, + 0x12322e0e, 0x12322e0e, 0x11312f0f, 0x11312f0f, + 0x10303010 + }; + int i; + + /* pscale enable, PSCALE cbus bmem enable */ + WRITE_VREG(PSCALE_CTRL, 0xc000); + + /* write filter coefs */ + WRITE_VREG(PSCALE_BMEM_ADDR, 0); + for (i = 0; i < 33; i++) { + WRITE_VREG(PSCALE_BMEM_DAT, 0); + WRITE_VREG(PSCALE_BMEM_DAT, filt_coef[i]); + } + + /* Y horizontal initial info */ + WRITE_VREG(PSCALE_BMEM_ADDR, 37 * 2); + /* [35]: buf repeat pix0, + * [34:29] => buf receive num, + * [28:16] => buf blk x, + * [15:0] => buf phase + */ + WRITE_VREG(PSCALE_BMEM_DAT, 0x0008); + WRITE_VREG(PSCALE_BMEM_DAT, 0x60000000); + + /* C horizontal initial info */ + WRITE_VREG(PSCALE_BMEM_ADDR, 41 * 2); + WRITE_VREG(PSCALE_BMEM_DAT, 0x0008); + WRITE_VREG(PSCALE_BMEM_DAT, 0x60000000); + + /* Y vertical initial info */ + WRITE_VREG(PSCALE_BMEM_ADDR, 39 * 2); + WRITE_VREG(PSCALE_BMEM_DAT, 0x0008); + WRITE_VREG(PSCALE_BMEM_DAT, 0x60000000); + + /* C vertical initial info */ + WRITE_VREG(PSCALE_BMEM_ADDR, 43 * 2); + WRITE_VREG(PSCALE_BMEM_DAT, 0x0008); + WRITE_VREG(PSCALE_BMEM_DAT, 0x60000000); + + /* Y horizontal phase step */ + WRITE_VREG(PSCALE_BMEM_ADDR, 36 * 2 + 1); + /* [19:0] => Y horizontal phase step */ + WRITE_VREG(PSCALE_BMEM_DAT, 0x10000); + /* C horizontal phase step */ + WRITE_VREG(PSCALE_BMEM_ADDR, 40 * 2 + 1); + /* [19:0] => C horizontal phase step */ + WRITE_VREG(PSCALE_BMEM_DAT, 0x10000); + + /* Y vertical phase step */ + WRITE_VREG(PSCALE_BMEM_ADDR, 38 * 2 + 1); + /* [19:0] => Y vertical phase step */ + WRITE_VREG(PSCALE_BMEM_DAT, 0x10000); + /* C vertical phase step */ + WRITE_VREG(PSCALE_BMEM_ADDR, 42 * 2 + 1); + /* [19:0] => C horizontal phase step */ + WRITE_VREG(PSCALE_BMEM_DAT, 0x10000); + + /* reset pscaler */ +#if 1/*MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON6*/ + WRITE_VREG(DOS_SW_RESET0, (1 << 10)); + WRITE_VREG(DOS_SW_RESET0, 0); +#else + WRITE_MPEG_REG(RESET2_REGISTER, RESET_PSCALE); +#endif + READ_MPEG_REG(RESET2_REGISTER); + READ_MPEG_REG(RESET2_REGISTER); + READ_MPEG_REG(RESET2_REGISTER); + + WRITE_VREG(PSCALE_RST, 0x7); + WRITE_VREG(PSCALE_RST, 0x0); +} + +static void vmjpeg_hw_ctx_restore(struct vdec_s *vdec, int index) +{ + struct vdec_mjpeg_hw_s *hw = + (struct vdec_mjpeg_hw_s *)vdec->private; + + WRITE_VREG(DOS_SW_RESET0, (1 << 7) | (1 << 6)); + WRITE_VREG(DOS_SW_RESET0, 0); + + vmjpeg_canvas_init(vdec); + + /* find next decode buffer index */ + WRITE_VREG(AV_SCRATCH_4, spec2canvas(&hw->buffer_spec[index])); + WRITE_VREG(AV_SCRATCH_5, index); + + init_scaler(); + + /* clear buffer IN/OUT registers */ + WRITE_VREG(MREG_TO_AMRISC, 0); + WRITE_VREG(MREG_FROM_AMRISC, 0); + + WRITE_VREG(MCPU_INTR_MSK, 0xffff); + WRITE_VREG(MREG_DECODE_PARAM, (hw->frame_height << 4) | 0x8000); + + /* clear mailbox interrupt */ + WRITE_VREG(ASSIST_MBOX1_CLR_REG, 1); + /* enable mailbox interrupt */ + WRITE_VREG(ASSIST_MBOX1_MASK, 1); + /* set interrupt mapping for vld */ + WRITE_VREG(ASSIST_AMR1_INT8, 8); +#if 1/*MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON6*/ + CLEAR_VREG_MASK(MDEC_PIC_DC_CTRL, 1 << 17); +#endif +} + +static s32 vmjpeg_init(struct vdec_s *vdec) +{ + int i; + struct vdec_mjpeg_hw_s *hw = + (struct vdec_mjpeg_hw_s *)vdec->private; + + hw->frame_width = hw->vmjpeg_amstream_dec_info.width; + hw->frame_height = hw->vmjpeg_amstream_dec_info.height; + hw->frame_dur = hw->vmjpeg_amstream_dec_info.rate; + hw->saved_resolution = 0; + + for (i = 0; i < DECODE_BUFFER_NUM_MAX; i++) + hw->vfbuf_use[i] = 0; + + INIT_KFIFO(hw->display_q); + INIT_KFIFO(hw->newframe_q); + + for (i = 0; i < VF_POOL_SIZE; i++) { + const struct vframe_s *vf = &hw->vfpool[i]; + hw->vfpool[i].index = -1; + kfifo_put(&hw->newframe_q, vf); + } + + INIT_WORK(&hw->work, vmjpeg_work); + + return 0; +} + +static bool run_ready(struct vdec_s *vdec) +{ + return true; +} + +static void run(struct vdec_s *vdec, + void (*callback)(struct vdec_s *, void *), void *arg) +{ + struct vdec_mjpeg_hw_s *hw = + (struct vdec_mjpeg_hw_s *)vdec->private; + int i,ret = -1,size = -1; + char *buf = vmalloc(0x1000 * 16); + if (IS_ERR_OR_NULL(buf)) + return; + + hw->vdec_cb_arg = arg; + hw->vdec_cb = callback; + + for (i = 0; i < DECODE_BUFFER_NUM_MAX; i++) { + if (hw->vfbuf_use[i] == 0) + break; + } + + if (i == DECODE_BUFFER_NUM_MAX) { + hw->dec_result = DEC_RESULT_AGAIN; + schedule_work(&hw->work); + return; + } + + ret = vdec_prepare_input(vdec, &hw->chunk); + if (ret < 0) { + hw->dec_result = DEC_RESULT_AGAIN; + schedule_work(&hw->work); + return; + } + + hw->dec_result = DEC_RESULT_NONE; + + size = get_firmware_data(VIDEO_DEC_MJPEG_MULTI, buf); + if (size < 0) { + pr_err("get firmware fail."); + vfree(buf); + return; + } + + if (amvdec_vdec_loadmc_buf_ex(vdec, buf, size) < 0) { + pr_err("%s: Error amvdec_loadmc fail\n", __func__); + vfree(buf); + return; + } + + vfree(buf); + + vmjpeg_hw_ctx_restore(vdec, i); + + vdec_enable_input(vdec); + + amvdec_start(); +} + +static void vmjpeg_work(struct work_struct *work) +{ + struct vdec_mjpeg_hw_s *hw = container_of(work, + struct vdec_mjpeg_hw_s, work); + + if (hw->dec_result == DEC_RESULT_DONE) + vdec_vframe_dirty(hw_to_vdec(hw), hw->chunk); + + /* mark itself has all HW resource released and input released */ + vdec_set_status(hw_to_vdec(hw), VDEC_STATUS_CONNECTED); + + if (hw->vdec_cb) + hw->vdec_cb(hw_to_vdec(hw), hw->vdec_cb_arg); +} + +static int amvdec_mjpeg_probe(struct platform_device *pdev) +{ + struct vdec_s *pdata = *(struct vdec_s **)pdev->dev.platform_data; + struct vdec_mjpeg_hw_s *hw = NULL; + + if (pdata == NULL) { + pr_info("amvdec_mjpeg memory resource undefined.\n"); + return -EFAULT; + } + + hw = (struct vdec_mjpeg_hw_s *)devm_kzalloc(&pdev->dev, + sizeof(struct vdec_mjpeg_hw_s), GFP_KERNEL); + if (hw == NULL) { + pr_info("\nammvdec_mjpeg device data allocation failed\n"); + return -ENOMEM; + } + + pdata->private = hw; + pdata->dec_status = vmjpeg_dec_status; + + pdata->run = run; + pdata->run_ready = run_ready; + pdata->irq_handler = vmjpeg_isr; + + pdata->id = pdev->id; + + if (pdata->use_vfm_path) + snprintf(pdata->vf_provider_name, VDEC_PROVIDER_NAME_SIZE, + VFM_DEC_PROVIDER_NAME); + else + snprintf(pdata->vf_provider_name, VDEC_PROVIDER_NAME_SIZE, + PROVIDER_NAME ".%02x", pdev->id & 0xff); + + vf_provider_init(&pdata->vframe_provider, pdata->vf_provider_name, + &vf_provider_ops, pdata); + + platform_set_drvdata(pdev, pdata); + + hw->platform_dev = pdev; + hw->buf_start = pdata->mem_start; + hw->buf_size = pdata->mem_end - pdata->mem_start + 1; + + if (pdata->sys_info) + hw->vmjpeg_amstream_dec_info = *pdata->sys_info; + + if (vmjpeg_init(pdata) < 0) { + pr_info("amvdec_mjpeg init failed.\n"); + return -ENODEV; + } + + return 0; +} + +static int amvdec_mjpeg_remove(struct platform_device *pdev) +{ + struct vdec_mjpeg_hw_s *hw = + (struct vdec_mjpeg_hw_s *) + (((struct vdec_s *)(platform_get_drvdata(pdev)))->private); + int i; + + for (i = 0; i < DECODE_BUFFER_NUM_MAX; i++) { + if (hw->buffer_spec[i].cma_alloc_addr) { + pr_info("codec_mm release buffer_spec[%d], 0x%lx\n", i, + hw->buffer_spec[i].cma_alloc_addr); + codec_mm_free_for_dma(MEM_NAME, + hw->buffer_spec[i].cma_alloc_addr); + hw->buffer_spec[i].cma_alloc_count = 0; + } + } + + cancel_work_sync(&hw->work); + + vdec_set_status(hw_to_vdec(hw), VDEC_STATUS_DISCONNECTED); + + return 0; +} + +/****************************************/ + +static struct platform_driver amvdec_mjpeg_driver = { + .probe = amvdec_mjpeg_probe, + .remove = amvdec_mjpeg_remove, +#ifdef CONFIG_PM + .suspend = amvdec_suspend, + .resume = amvdec_resume, +#endif + .driver = { + .name = DRIVER_NAME, + } +}; + +static struct codec_profile_t amvdec_mjpeg_profile = { + .name = "mmjpeg", + .profile = "" +}; + +static int __init amvdec_mjpeg_driver_init_module(void) +{ + if (platform_driver_register(&amvdec_mjpeg_driver)) { + pr_err("failed to register amvdec_mjpeg driver\n"); + return -ENODEV; + } + vcodec_profile_register(&amvdec_mjpeg_profile); + return 0; +} + +static void __exit amvdec_mjpeg_driver_remove_module(void) +{ + platform_driver_unregister(&amvdec_mjpeg_driver); +} + +/****************************************/ + +module_init(amvdec_mjpeg_driver_init_module); +module_exit(amvdec_mjpeg_driver_remove_module); + +MODULE_DESCRIPTION("AMLOGIC MJMPEG Video Decoder Driver"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Tim Yao <timyao@amlogic.com>"); diff --git a/drivers/frame_provider/decoder/mpeg12/vmpeg12.c b/drivers/frame_provider/decoder/mpeg12/vmpeg12.c new file mode 100644 index 0000000..1d15e11 --- a/dev/null +++ b/drivers/frame_provider/decoder/mpeg12/vmpeg12.c @@ -0,0 +1,1110 @@ +/* + * drivers/amlogic/amports/vmpeg12.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/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/frame_sync/ptsserv.h> +#include <linux/amlogic/media/utils/amstream.h> +#include <linux/amlogic/media/canvas/canvas.h> +#include <linux/module.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/cpu_version.h> +#include <linux/amlogic/media/codec_mm/codec_mm.h> +#include <linux/dma-mapping.h> + +#include <linux/amlogic/media/utils/vdec_reg.h> +#include "vmpeg12.h" +#include <linux/amlogic/media/registers/register.h> +#include "../../../stream_input/amports/amports_priv.h" + +#ifdef CONFIG_AM_VDEC_MPEG12_LOG +#define AMLOG +#define LOG_LEVEL_VAR amlog_level_vmpeg +#define LOG_MASK_VAR amlog_mask_vmpeg +#define LOG_LEVEL_ERROR 0 +#define LOG_LEVEL_INFO 1 +#define LOG_LEVEL_DESC "0:ERROR, 1:INFO" +#endif +#include <linux/amlogic/media/utils/amlog.h> +MODULE_AMLOG(LOG_LEVEL_ERROR, 0, LOG_LEVEL_DESC, LOG_DEFAULT_MASK_DESC); + +#include "../utils/amvdec.h" +#include "../utils/vdec.h" + +#define DRIVER_NAME "amvdec_mpeg12" +#define MODULE_NAME "amvdec_mpeg12" + +/* protocol registers */ +#define MREG_SEQ_INFO AV_SCRATCH_4 +#define MREG_PIC_INFO AV_SCRATCH_5 +#define MREG_PIC_WIDTH AV_SCRATCH_6 +#define MREG_PIC_HEIGHT AV_SCRATCH_7 +#define MREG_BUFFERIN AV_SCRATCH_8 +#define MREG_BUFFEROUT AV_SCRATCH_9 + +#define MREG_CMD AV_SCRATCH_A +#define MREG_CO_MV_START AV_SCRATCH_B +#define MREG_ERROR_COUNT AV_SCRATCH_C +#define MREG_FRAME_OFFSET AV_SCRATCH_D +#define MREG_WAIT_BUFFER AV_SCRATCH_E +#define MREG_FATAL_ERROR AV_SCRATCH_F + +#define PICINFO_ERROR 0x80000000 +#define PICINFO_TYPE_MASK 0x00030000 +#define PICINFO_TYPE_I 0x00000000 +#define PICINFO_TYPE_P 0x00010000 +#define PICINFO_TYPE_B 0x00020000 + +#define PICINFO_PROG 0x8000 +#define PICINFO_RPT_FIRST 0x4000 +#define PICINFO_TOP_FIRST 0x2000 +#define PICINFO_FRAME 0x1000 + +#define SEQINFO_EXT_AVAILABLE 0x80000000 +#define SEQINFO_PROG 0x00010000 + +#define VF_POOL_SIZE 32 +#define DECODE_BUFFER_NUM_MAX 8 +#define PUT_INTERVAL (HZ/100) + +#define INCPTR(p) ptr_atomic_wrap_inc(&p) + +#define DEC_CONTROL_FLAG_FORCE_2500_720_576_INTERLACE 0x0002 +#define DEC_CONTROL_FLAG_FORCE_3000_704_480_INTERLACE 0x0004 +#define DEC_CONTROL_FLAG_FORCE_2500_704_576_INTERLACE 0x0008 +#define DEC_CONTROL_FLAG_FORCE_2500_544_576_INTERLACE 0x0010 +#define DEC_CONTROL_FLAG_FORCE_2500_480_576_INTERLACE 0x0020 +#define DEC_CONTROL_INTERNAL_MASK 0x0fff +#define DEC_CONTROL_FLAG_FORCE_SEQ_INTERLACE 0x1000 + +#define INTERLACE_SEQ_ALWAYS + +#if 1/* MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON6 */ +#define NV21 +#endif +#define CCBUF_SIZE (5*1024) + +enum { + FRAME_REPEAT_TOP, + FRAME_REPEAT_BOT, + FRAME_REPEAT_NONE +}; + +static struct vframe_s *vmpeg_vf_peek(void *); +static struct vframe_s *vmpeg_vf_get(void *); +static void vmpeg_vf_put(struct vframe_s *, void *); +static int vmpeg_vf_states(struct vframe_states *states, void *); +static int vmpeg_event_cb(int type, void *data, void *private_data); + +static void vmpeg12_prot_init(void); +static void vmpeg12_local_init(void); + +static const char vmpeg12_dec_id[] = "vmpeg12-dev"; +#define PROVIDER_NAME "decoder.mpeg12" +static const struct vframe_operations_s vmpeg_vf_provider = { + .peek = vmpeg_vf_peek, + .get = vmpeg_vf_get, + .put = vmpeg_vf_put, + .event_cb = vmpeg_event_cb, + .vf_states = vmpeg_vf_states, +}; + +static struct vframe_provider_s vmpeg_vf_prov; + +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 const u32 frame_rate_tab[16] = { + 96000 / 30, 96000 / 24, 96000 / 24, 96000 / 25, + 96000 / 30, 96000 / 30, 96000 / 50, 96000 / 60, + 96000 / 60, + /* > 8 reserved, use 24 */ + 96000 / 24, 96000 / 24, 96000 / 24, 96000 / 24, + 96000 / 24, 96000 / 24, 96000 / 24 +}; + +static struct vframe_s vfpool[VF_POOL_SIZE]; +static struct vframe_s vfpool2[VF_POOL_SIZE]; +static int cur_pool_idx; +static s32 vfbuf_use[DECODE_BUFFER_NUM_MAX]; +static u32 dec_control; +static u32 frame_width, frame_height, frame_dur, frame_prog; +static u32 saved_resolution; +static struct timer_list recycle_timer; +static u32 stat; +static unsigned long buf_start; +static u32 buf_size, ccbuf_phyAddress; +static void *ccbuf_phyAddress_virt; +static int ccbuf_phyAddress_is_remaped_nocache; + +static DEFINE_SPINLOCK(lock); + +static u32 frame_rpt_state; + +static struct dec_sysinfo vmpeg12_amstream_dec_info; + +/* for error handling */ +static s32 frame_force_skip_flag; +static s32 error_frame_skip_level; +static s32 wait_buffer_counter; +static u32 first_i_frame_ready; + +static inline int pool_index(struct vframe_s *vf) +{ + if ((vf >= &vfpool[0]) && (vf <= &vfpool[VF_POOL_SIZE - 1])) + return 0; + else if ((vf >= &vfpool2[0]) && (vf <= &vfpool2[VF_POOL_SIZE - 1])) + return 1; + else + return -1; +} + +static inline u32 index2canvas(u32 index) +{ + const u32 canvas_tab[8] = { +#ifdef NV21 + 0x010100, 0x030302, 0x050504, 0x070706, + 0x090908, 0x0b0b0a, 0x0d0d0c, 0x0f0f0e +#else + 0x020100, 0x050403, 0x080706, 0x0b0a09, + 0x0e0d0c, 0x11100f, 0x141312, 0x171615 +#endif + }; + + return canvas_tab[index]; +} + +static void set_frame_info(struct vframe_s *vf) +{ + unsigned ar_bits; + u32 temp; + +#ifdef CONFIG_AM_VDEC_MPEG12_LOG + bool first = (frame_width == 0) && (frame_height == 0); +#endif + temp = READ_VREG(MREG_PIC_WIDTH); + if (temp > 1920) + vf->width = frame_width = 1920; + else + vf->width = frame_width = temp; + + temp = READ_VREG(MREG_PIC_HEIGHT); + if (temp > 1088) + vf->height = frame_height = 1088; + else + vf->height = frame_height = temp; + + vf->flag = 0; + + if (frame_dur > 0) + vf->duration = frame_dur; + else { + vf->duration = frame_dur = + frame_rate_tab[(READ_VREG(MREG_SEQ_INFO) >> 4) & 0xf]; + } + + ar_bits = READ_VREG(MREG_SEQ_INFO) & 0xf; + + if (ar_bits == 0x2) + vf->ratio_control = 0xc0 << DISP_RATIO_ASPECT_RATIO_BIT; + + else if (ar_bits == 0x3) + vf->ratio_control = 0x90 << DISP_RATIO_ASPECT_RATIO_BIT; + + else if (ar_bits == 0x4) + vf->ratio_control = 0x74 << DISP_RATIO_ASPECT_RATIO_BIT; + + else + vf->ratio_control = 0; + + amlog_level_if(first, LOG_LEVEL_INFO, + "mpeg2dec: w(%d), h(%d), dur(%d), dur-ES(%d)\n", + frame_width, frame_height, frame_dur, + frame_rate_tab[(READ_VREG(MREG_SEQ_INFO) >> 4) & 0xf]); +} + +static bool error_skip(u32 info, struct vframe_s *vf) +{ + if (error_frame_skip_level) { + /* skip error frame */ + if ((info & PICINFO_ERROR) || (frame_force_skip_flag)) { + if ((info & PICINFO_ERROR) == 0) { + if ((info & PICINFO_TYPE_MASK) == + PICINFO_TYPE_I) + frame_force_skip_flag = 0; + } else { + if (error_frame_skip_level >= 2) + frame_force_skip_flag = 1; + } + if ((info & PICINFO_ERROR) || (frame_force_skip_flag)) + return true; + } + } + + return false; +} + +static irqreturn_t vmpeg12_isr(int irq, void *dev_id) +{ + u32 reg, info, seqinfo, offset, pts, pts_valid = 0; + struct vframe_s *vf; + u64 pts_us64 = 0; + + WRITE_VREG(ASSIST_MBOX1_CLR_REG, 1); + + reg = READ_VREG(MREG_BUFFEROUT); + + if ((reg >> 16) == 0xfe) { + if (!ccbuf_phyAddress_is_remaped_nocache && + ccbuf_phyAddress && + ccbuf_phyAddress_virt) { + codec_mm_dma_flush( + ccbuf_phyAddress_virt, + CCBUF_SIZE, + DMA_FROM_DEVICE); + } + wakeup_userdata_poll( + reg & 0xffff, + (unsigned long)ccbuf_phyAddress_virt, + CCBUF_SIZE, 0); + WRITE_VREG(MREG_BUFFEROUT, 0); + } else if (reg) { + info = READ_VREG(MREG_PIC_INFO); + offset = READ_VREG(MREG_FRAME_OFFSET); + + if ((first_i_frame_ready == 0) && + ((info & PICINFO_TYPE_MASK) == PICINFO_TYPE_I) && + ((info & PICINFO_ERROR) == 0)) + first_i_frame_ready = 1; + + if ((pts_lookup_offset_us64 + (PTS_TYPE_VIDEO, offset, &pts, 0, &pts_us64) == 0) + && (((info & PICINFO_TYPE_MASK) == PICINFO_TYPE_I) + || ((info & PICINFO_TYPE_MASK) == + PICINFO_TYPE_P))) + pts_valid = 1; + + /*if (frame_prog == 0) */ + { + frame_prog = info & PICINFO_PROG; + } + + if ((dec_control & + DEC_CONTROL_FLAG_FORCE_2500_720_576_INTERLACE) + && (frame_width == 720 || frame_width == 480) + && (frame_height == 576) + && (frame_dur == 3840)) + frame_prog = 0; + else if ((dec_control & + DEC_CONTROL_FLAG_FORCE_3000_704_480_INTERLACE) + && (frame_width == 704) && (frame_height == 480) + && (frame_dur == 3200)) + frame_prog = 0; + else if ((dec_control & + DEC_CONTROL_FLAG_FORCE_2500_704_576_INTERLACE) + && (frame_width == 704) && (frame_height == 576) + && (frame_dur == 3840)) + frame_prog = 0; + else if ((dec_control & + DEC_CONTROL_FLAG_FORCE_2500_544_576_INTERLACE) + && (frame_width == 544) && (frame_height == 576) + && (frame_dur == 3840)) + frame_prog = 0; + else if ((dec_control & + DEC_CONTROL_FLAG_FORCE_2500_480_576_INTERLACE) + && (frame_width == 480) && (frame_height == 576) + && (frame_dur == 3840)) + frame_prog = 0; + else if (dec_control & DEC_CONTROL_FLAG_FORCE_SEQ_INTERLACE) + frame_prog = 0; + if (frame_prog & PICINFO_PROG) { + u32 index = ((reg & 0xf) - 1) & 7; + + seqinfo = READ_VREG(MREG_SEQ_INFO); + + if (kfifo_get(&newframe_q, &vf) == 0) { + pr_info + ("fatal error, no available buffer slot."); + return IRQ_HANDLED; + } + + set_frame_info(vf); + vf->signal_type = 0; + vf->index = index; +#ifdef NV21 + vf->type = + VIDTYPE_PROGRESSIVE | VIDTYPE_VIU_FIELD | + VIDTYPE_VIU_NV21; +#else + vf->type = VIDTYPE_PROGRESSIVE | VIDTYPE_VIU_FIELD; +#endif + if ((seqinfo & SEQINFO_EXT_AVAILABLE) + && (seqinfo & SEQINFO_PROG)) { + if (info & PICINFO_RPT_FIRST) { + if (info & PICINFO_TOP_FIRST) { + vf->duration = + vf->duration * 3; + /* repeat three times */ + } else { + vf->duration = + vf->duration * 2; + /* repeat two times */ + } + } + vf->duration_pulldown = 0; + /* no pull down */ + + } else { + vf->duration_pulldown = + (info & PICINFO_RPT_FIRST) ? + vf->duration >> 1 : 0; + } + + vf->duration += vf->duration_pulldown; + vf->canvas0Addr = vf->canvas1Addr = + index2canvas(index); + vf->orientation = 0; + vf->pts = (pts_valid) ? pts : 0; + vf->pts_us64 = (pts_valid) ? pts_us64 : 0; + vf->type_original = vf->type; + + vfbuf_use[index] = 1; + + if ((error_skip(info, vf)) || + ((first_i_frame_ready == 0) + && ((PICINFO_TYPE_MASK & info) != + PICINFO_TYPE_I))) { + kfifo_put(&recycle_q, + (const struct vframe_s *)vf); + } else { + kfifo_put(&display_q, + (const struct vframe_s *)vf); + vf_notify_receiver(PROVIDER_NAME, + VFRAME_EVENT_PROVIDER_VFRAME_READY, + NULL); + } + + } else { + u32 index = ((reg & 0xf) - 1) & 7; + int first_field_type = (info & PICINFO_TOP_FIRST) ? + VIDTYPE_INTERLACE_TOP : + VIDTYPE_INTERLACE_BOTTOM; + +#ifdef INTERLACE_SEQ_ALWAYS + /* once an interlaced sequence exist, + always force interlaced type */ + /* to make DI easy. */ + dec_control |= DEC_CONTROL_FLAG_FORCE_SEQ_INTERLACE; +#endif + + if (info & PICINFO_FRAME) { + frame_rpt_state = + (info & PICINFO_TOP_FIRST) ? + FRAME_REPEAT_TOP : FRAME_REPEAT_BOT; + } else { + if (frame_rpt_state == FRAME_REPEAT_TOP) { + first_field_type = + VIDTYPE_INTERLACE_TOP; + } else if (frame_rpt_state == + FRAME_REPEAT_BOT) { + first_field_type = + VIDTYPE_INTERLACE_BOTTOM; + } + frame_rpt_state = FRAME_REPEAT_NONE; + } + + if (kfifo_get(&newframe_q, &vf) == 0) { + pr_info + ("fatal error, no available buffer slot."); + return IRQ_HANDLED; + } + + vfbuf_use[index] = 2; + + set_frame_info(vf); + vf->signal_type = 0; + vf->index = index; + vf->type = + (first_field_type == VIDTYPE_INTERLACE_TOP) ? + VIDTYPE_INTERLACE_TOP : + VIDTYPE_INTERLACE_BOTTOM; +#ifdef NV21 + vf->type |= VIDTYPE_VIU_NV21; +#endif + vf->duration >>= 1; + vf->duration_pulldown = (info & PICINFO_RPT_FIRST) ? + vf->duration >> 1 : 0; + vf->duration += vf->duration_pulldown; + vf->orientation = 0; + vf->canvas0Addr = vf->canvas1Addr = + index2canvas(index); + vf->pts = (pts_valid) ? pts : 0; + vf->pts_us64 = (pts_valid) ? pts_us64 : 0; + vf->type_original = vf->type; + + if ((error_skip(info, vf)) || + ((first_i_frame_ready == 0) + && ((PICINFO_TYPE_MASK & info) != + PICINFO_TYPE_I))) { + kfifo_put(&recycle_q, + (const struct vframe_s *)vf); + } else { + kfifo_put(&display_q, + (const struct vframe_s *)vf); + vf_notify_receiver(PROVIDER_NAME, + VFRAME_EVENT_PROVIDER_VFRAME_READY, + NULL); + } + + if (kfifo_get(&newframe_q, &vf) == 0) { + pr_info + ("fatal error, no available buffer slot."); + return IRQ_HANDLED; + } + + set_frame_info(vf); + vf->signal_type = 0; + vf->index = index; + vf->type = (first_field_type == + VIDTYPE_INTERLACE_TOP) ? + VIDTYPE_INTERLACE_BOTTOM : + VIDTYPE_INTERLACE_TOP; +#ifdef NV21 + vf->type |= VIDTYPE_VIU_NV21; +#endif + vf->duration >>= 1; + vf->duration_pulldown = (info & PICINFO_RPT_FIRST) ? + vf->duration >> 1 : 0; + vf->duration += vf->duration_pulldown; + vf->orientation = 0; + vf->canvas0Addr = vf->canvas1Addr = + index2canvas(index); + vf->pts = 0; + vf->pts_us64 = 0; + vf->type_original = vf->type; + + if ((error_skip(info, vf)) || + ((first_i_frame_ready == 0) + && ((PICINFO_TYPE_MASK & info) != + PICINFO_TYPE_I))) { + kfifo_put(&recycle_q, + (const struct vframe_s *)vf); + } else { + kfifo_put(&display_q, + (const struct vframe_s *)vf); + vf_notify_receiver(PROVIDER_NAME, + VFRAME_EVENT_PROVIDER_VFRAME_READY, + NULL); + } + } + + WRITE_VREG(MREG_BUFFEROUT, 0); + } + + return IRQ_HANDLED; +} + +static struct vframe_s *vmpeg_vf_peek(void *op_arg) +{ + struct vframe_s *vf; + + if (kfifo_peek(&display_q, &vf)) + return vf; + + return NULL; +} + +static struct vframe_s *vmpeg_vf_get(void *op_arg) +{ + struct vframe_s *vf; + + if (kfifo_get(&display_q, &vf)) + return vf; + + return NULL; +} + +static void vmpeg_vf_put(struct vframe_s *vf, void *op_arg) +{ + if (pool_index(vf) == cur_pool_idx) + kfifo_put(&recycle_q, (const struct vframe_s *)vf); +} + +static int vmpeg_event_cb(int type, void *data, void *private_data) +{ + if (type & VFRAME_EVENT_RECEIVER_RESET) { + unsigned long flags; + amvdec_stop(); +#ifndef CONFIG_AMLOGIC_POST_PROCESS_MANAGER + vf_light_unreg_provider(&vmpeg_vf_prov); +#endif + spin_lock_irqsave(&lock, flags); + vmpeg12_local_init(); + vmpeg12_prot_init(); + spin_unlock_irqrestore(&lock, flags); +#ifndef CONFIG_AMLOGIC_POST_PROCESS_MANAGER + vf_reg_provider(&vmpeg_vf_prov); +#endif + amvdec_start(); + } + return 0; +} + +static int vmpeg_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); + states->buf_recycle_num = kfifo_len(&recycle_q); + + spin_unlock_irqrestore(&lock, flags); + + return 0; +} + +#ifdef CONFIG_AMLOGIC_POST_PROCESS_MANAGER +static void vmpeg12_ppmgr_reset(void) +{ + vf_notify_receiver(PROVIDER_NAME, VFRAME_EVENT_PROVIDER_RESET, NULL); + + vmpeg12_local_init(); + + pr_info("vmpeg12dec: vf_ppmgr_reset\n"); +} +#endif + +static void vmpeg_put_timer_func(unsigned long arg) +{ + struct timer_list *timer = (struct timer_list *)arg; + int fatal_reset = 0; + + enum receviver_start_e state = RECEIVER_INACTIVE; + 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; + + if (READ_VREG(MREG_FATAL_ERROR) == 1) + fatal_reset = 1; + + if ((READ_VREG(MREG_WAIT_BUFFER) != 0) && + (kfifo_is_empty(&recycle_q)) && + (kfifo_is_empty(&display_q)) && (state == RECEIVER_INACTIVE)) { + if (++wait_buffer_counter > 4) + fatal_reset = 1; + + } else + wait_buffer_counter = 0; + + if (fatal_reset && (kfifo_is_empty(&display_q))) { + pr_info("$$$$decoder is waiting for buffer or fatal reset.\n"); + + amvdec_stop(); + +#ifdef CONFIG_AMLOGIC_POST_PROCESS_MANAGER + vmpeg12_ppmgr_reset(); +#else + vf_light_unreg_provider(&vmpeg_vf_prov); + vmpeg12_local_init(); + vf_reg_provider(&vmpeg_vf_prov); +#endif + vmpeg12_prot_init(); + amvdec_start(); + } + + while (!kfifo_is_empty(&recycle_q) && (READ_VREG(MREG_BUFFERIN) == 0)) { + struct vframe_s *vf; + if (kfifo_get(&recycle_q, &vf)) { + if ((vf->index < DECODE_BUFFER_NUM_MAX) && + (--vfbuf_use[vf->index] == 0)) { + WRITE_VREG(MREG_BUFFERIN, vf->index + 1); + vf->index = DECODE_BUFFER_NUM_MAX; + } + + if (pool_index(vf) == cur_pool_idx) { + kfifo_put(&newframe_q, + (const struct vframe_s *)vf); + } + } + } + + if (frame_dur > 0 && saved_resolution != + frame_width * frame_height * (96000 / frame_dur)) { + int fps = 96000 / frame_dur; + saved_resolution = frame_width * frame_height * fps; + vdec_source_changed(VFORMAT_MPEG12, + frame_width, frame_height, fps); + } + + timer->expires = jiffies + PUT_INTERVAL; + + add_timer(timer); +} + +int vmpeg12_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 = 96000; + vstatus->error_count = READ_VREG(AV_SCRATCH_C); + vstatus->status = stat; + + return 0; +} + +/****************************************/ +static void vmpeg12_canvas_init(void) +{ + int i; + u32 canvas_width, canvas_height; + u32 decbuf_size, decbuf_y_size, decbuf_uv_size; + u32 disp_addr = 0xffffffff; + + if (buf_size <= 0x00400000) { + /* SD only */ + canvas_width = 768; + canvas_height = 576; + decbuf_y_size = 0x80000; + decbuf_uv_size = 0x20000; + decbuf_size = 0x100000; + } else { + /* HD & SD */ + canvas_width = 1920; + canvas_height = 1088; + decbuf_y_size = 0x200000; + decbuf_uv_size = 0x80000; + decbuf_size = 0x300000; + } + + if (is_vpp_postblend()) { + struct canvas_s cur_canvas; + + canvas_read((READ_VCBUS_REG(VD1_IF0_CANVAS0) & 0xff), + &cur_canvas); + disp_addr = (cur_canvas.addr + 7) >> 3; + } + + for (i = 0; i < 8; i++) { + if (((buf_start + i * decbuf_size + 7) >> 3) == disp_addr) { +#ifdef NV21 + canvas_config(2 * i + 0, + buf_start + 8 * decbuf_size, + canvas_width, canvas_height, + CANVAS_ADDR_NOWRAP, CANVAS_BLKMODE_32X32); + canvas_config(2 * i + 1, + buf_start + 8 * decbuf_size + + decbuf_y_size, canvas_width, + canvas_height / 2, CANVAS_ADDR_NOWRAP, + CANVAS_BLKMODE_32X32); +#else + canvas_config(3 * i + 0, + buf_start + 8 * decbuf_size, + canvas_width, canvas_height, + CANVAS_ADDR_NOWRAP, CANVAS_BLKMODE_32X32); + canvas_config(3 * i + 1, + buf_start + 8 * decbuf_size + + decbuf_y_size, canvas_width / 2, + canvas_height / 2, CANVAS_ADDR_NOWRAP, + CANVAS_BLKMODE_32X32); + canvas_config(3 * i + 2, + buf_start + 8 * decbuf_size + + decbuf_y_size + decbuf_uv_size, + canvas_width / 2, canvas_height / 2, + CANVAS_ADDR_NOWRAP, CANVAS_BLKMODE_32X32); +#endif + } else { +#ifdef NV21 + canvas_config(2 * i + 0, + buf_start + i * decbuf_size, + canvas_width, canvas_height, + CANVAS_ADDR_NOWRAP, CANVAS_BLKMODE_32X32); + canvas_config(2 * i + 1, + buf_start + i * decbuf_size + + decbuf_y_size, canvas_width, + canvas_height / 2, CANVAS_ADDR_NOWRAP, + CANVAS_BLKMODE_32X32); +#else + canvas_config(3 * i + 0, + buf_start + i * decbuf_size, + canvas_width, canvas_height, + CANVAS_ADDR_NOWRAP, CANVAS_BLKMODE_32X32); + canvas_config(3 * i + 1, + buf_start + i * decbuf_size + + decbuf_y_size, canvas_width / 2, + canvas_height / 2, CANVAS_ADDR_NOWRAP, + CANVAS_BLKMODE_32X32); + canvas_config(3 * i + 2, + buf_start + i * decbuf_size + + decbuf_y_size + decbuf_uv_size, + canvas_width / 2, canvas_height / 2, + CANVAS_ADDR_NOWRAP, CANVAS_BLKMODE_32X32); +#endif + } + } + + WRITE_VREG(MREG_CO_MV_START, + buf_start + 9 * decbuf_size + CCBUF_SIZE); + if (!ccbuf_phyAddress) { + ccbuf_phyAddress = (u32)(buf_start + 9 * decbuf_size); + ccbuf_phyAddress_virt = codec_mm_phys_to_virt(ccbuf_phyAddress); + if (!ccbuf_phyAddress_virt) { + ccbuf_phyAddress_virt = ioremap_nocache( + ccbuf_phyAddress, + CCBUF_SIZE); + ccbuf_phyAddress_is_remaped_nocache = 1; + } + } + +} + +static void vmpeg12_prot_init(void) +{ + if (get_cpu_type() >= MESON_CPU_MAJOR_ID_M6) { + int save_reg = READ_VREG(POWER_CTL_VLD); + + WRITE_VREG(DOS_SW_RESET0, (1 << 7) | (1 << 6) | (1 << 4)); + WRITE_VREG(DOS_SW_RESET0, 0); + + if (get_cpu_type() >= MESON_CPU_MAJOR_ID_M8) { + + 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); + + WRITE_VREG(MDEC_SW_RESET, (1 << 7)); + WRITE_VREG(MDEC_SW_RESET, 0); + } + + WRITE_VREG(POWER_CTL_VLD, save_reg); + + } else + WRITE_MPEG_REG(RESET0_REGISTER, RESET_IQIDCT | RESET_MC); + + vmpeg12_canvas_init(); + +#ifdef NV21 + WRITE_VREG(AV_SCRATCH_0, 0x010100); + WRITE_VREG(AV_SCRATCH_1, 0x030302); + WRITE_VREG(AV_SCRATCH_2, 0x050504); + WRITE_VREG(AV_SCRATCH_3, 0x070706); + WRITE_VREG(AV_SCRATCH_4, 0x090908); + WRITE_VREG(AV_SCRATCH_5, 0x0b0b0a); + WRITE_VREG(AV_SCRATCH_6, 0x0d0d0c); + WRITE_VREG(AV_SCRATCH_7, 0x0f0f0e); +#else + WRITE_VREG(AV_SCRATCH_0, 0x020100); + WRITE_VREG(AV_SCRATCH_1, 0x050403); + WRITE_VREG(AV_SCRATCH_2, 0x080706); + WRITE_VREG(AV_SCRATCH_3, 0x0b0a09); + WRITE_VREG(AV_SCRATCH_4, 0x0e0d0c); + WRITE_VREG(AV_SCRATCH_5, 0x11100f); + WRITE_VREG(AV_SCRATCH_6, 0x141312); + WRITE_VREG(AV_SCRATCH_7, 0x171615); +#endif + + /* set to mpeg1 default */ + WRITE_VREG(MPEG1_2_REG, 0); + /* disable PSCALE for hardware sharing */ + WRITE_VREG(PSCALE_CTRL, 0); + /* for Mpeg1 default value */ + WRITE_VREG(PIC_HEAD_INFO, 0x380); + /* disable mpeg4 */ + WRITE_VREG(M4_CONTROL_REG, 0); + /* clear mailbox interrupt */ + WRITE_VREG(ASSIST_MBOX1_CLR_REG, 1); + /* clear buffer IN/OUT registers */ + WRITE_VREG(MREG_BUFFERIN, 0); + WRITE_VREG(MREG_BUFFEROUT, 0); + /* set reference width and height */ + if ((frame_width != 0) && (frame_height != 0)) + WRITE_VREG(MREG_CMD, (frame_width << 16) | frame_height); + else + WRITE_VREG(MREG_CMD, 0); + /* clear error count */ + WRITE_VREG(MREG_ERROR_COUNT, 0); + WRITE_VREG(MREG_FATAL_ERROR, 0); + /* clear wait buffer status */ + WRITE_VREG(MREG_WAIT_BUFFER, 0); +#ifdef NV21 + SET_VREG_MASK(MDEC_PIC_DC_CTRL, 1 << 17); +#endif +} + +static void vmpeg12_local_init(void) +{ + int i; + + INIT_KFIFO(display_q); + INIT_KFIFO(recycle_q); + INIT_KFIFO(newframe_q); + + cur_pool_idx ^= 1; + + for (i = 0; i < VF_POOL_SIZE; i++) { + const struct vframe_s *vf; + if (cur_pool_idx == 0) { + vf = &vfpool[i]; + vfpool[i].index = DECODE_BUFFER_NUM_MAX; + } else { + vf = &vfpool2[i]; + vfpool2[i].index = DECODE_BUFFER_NUM_MAX; + } + kfifo_put(&newframe_q, (const struct vframe_s *)vf); + } + + for (i = 0; i < DECODE_BUFFER_NUM_MAX; i++) + vfbuf_use[i] = 0; + + + frame_width = frame_height = frame_dur = frame_prog = 0; + frame_force_skip_flag = 0; + wait_buffer_counter = 0; + first_i_frame_ready = 0; + saved_resolution = 0; + dec_control &= DEC_CONTROL_INTERNAL_MASK; +} + +static s32 vmpeg12_init(void) +{ + int ret = -1, size = -1; + char *buf = vmalloc(0x1000 * 16); + if (IS_ERR_OR_NULL(buf)) + return -ENOMEM; + + init_timer(&recycle_timer); + + stat |= STAT_TIMER_INIT; + + vmpeg12_local_init(); + + amvdec_enable(); + + size = get_firmware_data(VIDEO_DEC_MPEG12, buf); + if (size < 0) { + pr_err("get firmware fail."); + vfree(buf); + return -1; + } + + if (amvdec_loadmc_ex(VFORMAT_MPEG12, NULL, buf) < 0) { + amvdec_disable(); + vfree(buf); + return -EBUSY; + } + + vfree(buf); + + stat |= STAT_MC_LOAD; + + /* enable AMRISC side protocol */ + vmpeg12_prot_init(); + + ret = vdec_request_irq(VDEC_IRQ_1, vmpeg12_isr, + "vmpeg12-irq", (void *)vmpeg12_dec_id); + + if (ret) { + amvdec_disable(); + amlog_level(LOG_LEVEL_ERROR, "vmpeg12 irq register error.\n"); + return -ENOENT; + } + + stat |= STAT_ISR_REG; +#ifdef CONFIG_AMLOGIC_POST_PROCESS_MANAGER + vf_provider_init(&vmpeg_vf_prov, PROVIDER_NAME, &vmpeg_vf_provider, + NULL); + vf_reg_provider(&vmpeg_vf_prov); + vf_notify_receiver(PROVIDER_NAME, VFRAME_EVENT_PROVIDER_START, NULL); +#else + vf_provider_init(&vmpeg_vf_prov, PROVIDER_NAME, &vmpeg_vf_provider, + NULL); + vf_reg_provider(&vmpeg_vf_prov); +#endif + + vf_notify_receiver(PROVIDER_NAME, VFRAME_EVENT_PROVIDER_FR_HINT, + (void *)((unsigned long)vmpeg12_amstream_dec_info.rate)); + + stat |= STAT_VF_HOOK; + + recycle_timer.data = (ulong)&recycle_timer; + recycle_timer.function = vmpeg_put_timer_func; + recycle_timer.expires = jiffies + PUT_INTERVAL; + + add_timer(&recycle_timer); + + stat |= STAT_TIMER_ARM; + + amvdec_start(); + + stat |= STAT_VDEC_RUN; + + return 0; +} + +static int amvdec_mpeg12_probe(struct platform_device *pdev) +{ + struct vdec_s *pdata = *(struct vdec_s **)pdev->dev.platform_data; + + amlog_level(LOG_LEVEL_INFO, "amvdec_mpeg12 probe start.\n"); + + if (pdata == NULL) { + amlog_level(LOG_LEVEL_ERROR, + "amvdec_mpeg12 platform data undefined.\n"); + return -EFAULT; + } + + if (pdata->sys_info) + vmpeg12_amstream_dec_info = *pdata->sys_info; + + buf_start = pdata->mem_start; + buf_size = pdata->mem_end - pdata->mem_start + 1; + + pdata->dec_status = vmpeg12_dec_status; + + if (vmpeg12_init() < 0) { + amlog_level(LOG_LEVEL_ERROR, "amvdec_mpeg12 init failed.\n"); + + return -ENODEV; + } + + amlog_level(LOG_LEVEL_INFO, "amvdec_mpeg12 probe end.\n"); + + return 0; +} + +static int amvdec_mpeg12_remove(struct platform_device *pdev) +{ + if (stat & STAT_VDEC_RUN) { + amvdec_stop(); + stat &= ~STAT_VDEC_RUN; + } + + if (stat & STAT_ISR_REG) { + vdec_free_irq(VDEC_IRQ_1, (void *)vmpeg12_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) { + vf_notify_receiver(PROVIDER_NAME, + VFRAME_EVENT_PROVIDER_FR_END_HINT, NULL); + + vf_unreg_provider(&vmpeg_vf_prov); + stat &= ~STAT_VF_HOOK; + } + + amvdec_disable(); + if (ccbuf_phyAddress_is_remaped_nocache) + iounmap(ccbuf_phyAddress_virt); + + ccbuf_phyAddress_virt = NULL; + ccbuf_phyAddress = 0; + ccbuf_phyAddress_is_remaped_nocache = 0; + amlog_level(LOG_LEVEL_INFO, "amvdec_mpeg12 remove.\n"); + + return 0; +} + +/****************************************/ + +static struct platform_driver amvdec_mpeg12_driver = { + .probe = amvdec_mpeg12_probe, + .remove = amvdec_mpeg12_remove, +#ifdef CONFIG_PM + .suspend = amvdec_suspend, + .resume = amvdec_resume, +#endif + .driver = { + .name = DRIVER_NAME, + } +}; + +static struct codec_profile_t amvdec_mpeg12_profile = { + .name = "mpeg12", + .profile = "" +}; + +static int __init amvdec_mpeg12_driver_init_module(void) +{ + amlog_level(LOG_LEVEL_INFO, "amvdec_mpeg12 module init\n"); + + if (platform_driver_register(&amvdec_mpeg12_driver)) { + amlog_level(LOG_LEVEL_ERROR, + "failed to register amvdec_mpeg12 driver\n"); + return -ENODEV; + } + vcodec_profile_register(&amvdec_mpeg12_profile); + return 0; +} + +static void __exit amvdec_mpeg12_driver_remove_module(void) +{ + amlog_level(LOG_LEVEL_INFO, "amvdec_mpeg12 module remove.\n"); + + platform_driver_unregister(&amvdec_mpeg12_driver); +} + +/****************************************/ + +module_param(stat, uint, 0664); +MODULE_PARM_DESC(stat, "\n amvdec_mpeg12 stat\n"); +module_param(dec_control, uint, 0664); +MODULE_PARM_DESC(dec_control, "\n amvmpeg12 decoder control\n"); +module_param(error_frame_skip_level, uint, 0664); +MODULE_PARM_DESC(error_frame_skip_level, + "\n amvdec_mpeg12 error_frame_skip_level\n"); + +module_init(amvdec_mpeg12_driver_init_module); +module_exit(amvdec_mpeg12_driver_remove_module); + +MODULE_DESCRIPTION("AMLOGIC MPEG1/2 Video Decoder Driver"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Tim Yao <timyao@amlogic.com>"); diff --git a/drivers/frame_provider/decoder/mpeg12/vmpeg12.h b/drivers/frame_provider/decoder/mpeg12/vmpeg12.h new file mode 100644 index 0000000..2038c06 --- a/dev/null +++ b/drivers/frame_provider/decoder/mpeg12/vmpeg12.h @@ -0,0 +1,26 @@ +/* + * drivers/amlogic/amports/vmpeg12.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 VMPEG12_H +#define VMPEG12_H + +/* /#if MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON6 */ +/* TODO: move to register headers */ +#define VPP_VD1_POSTBLEND (1 << 10) +/* /#endif */ + +#endif /* VMPEG12_H */ diff --git a/drivers/frame_provider/decoder/mpeg4/vmpeg4.c b/drivers/frame_provider/decoder/mpeg4/vmpeg4.c new file mode 100644 index 0000000..1f1541a --- a/dev/null +++ b/drivers/frame_provider/decoder/mpeg4/vmpeg4.c @@ -0,0 +1,1127 @@ +/* + * drivers/amlogic/amports/vmpeg4.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/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/dma-mapping.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/canvas/canvas.h> + +#include <linux/amlogic/media/utils/vdec_reg.h> +#include "vmpeg4.h" +#include <linux/amlogic/media/registers/register.h> +#include "../../../stream_input/amports/amports_priv.h" + + +/* #define CONFIG_AM_VDEC_MPEG4_LOG */ +#ifdef CONFIG_AM_VDEC_MPEG4_LOG +#define AMLOG +#define LOG_LEVEL_VAR amlog_level_vmpeg4 +#define LOG_MASK_VAR amlog_mask_vmpeg4 +#define LOG_LEVEL_ERROR 0 +#define LOG_LEVEL_INFO 1 +#define LOG_LEVEL_DESC "0:ERROR, 1:INFO" +#define LOG_MASK_PTS 0x01 +#define LOG_MASK_DESC "0x01:DEBUG_PTS" +#endif + +#include <linux/amlogic/media/utils/amlog.h> + +MODULE_AMLOG(LOG_LEVEL_ERROR, 0, LOG_LEVEL_DESC, LOG_DEFAULT_MASK_DESC); + +#include "../utils/amvdec.h" +#include "../utils/vdec.h" + +#define DRIVER_NAME "amvdec_mpeg4" +#define MODULE_NAME "amvdec_mpeg4" + +#define DEBUG_PTS + +/* /#if MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON6 */ +#define NV21 +/* /#endif */ + +#define I_PICTURE 0 +#define P_PICTURE 1 +#define B_PICTURE 2 + +#define ORI_BUFFER_START_ADDR 0x01000000 + +#define INTERLACE_FLAG 0x80 +#define TOP_FIELD_FIRST_FLAG 0x40 + +/* protocol registers */ +#define MP4_PIC_RATIO AV_SCRATCH_5 +#define MP4_RATE AV_SCRATCH_3 +#define MP4_ERR_COUNT AV_SCRATCH_6 +#define MP4_PIC_WH AV_SCRATCH_7 +#define MREG_BUFFERIN AV_SCRATCH_8 +#define MREG_BUFFEROUT AV_SCRATCH_9 +#define MP4_NOT_CODED_CNT AV_SCRATCH_A +#define MP4_VOP_TIME_INC AV_SCRATCH_B +#define MP4_OFFSET_REG AV_SCRATCH_C +#define MP4_SYS_RATE AV_SCRATCH_E +#define MEM_OFFSET_REG AV_SCRATCH_F + +#define PARC_FORBIDDEN 0 +#define PARC_SQUARE 1 +#define PARC_CIF 2 +#define PARC_10_11 3 +#define PARC_16_11 4 +#define PARC_40_33 5 +#define PARC_RESERVED 6 +/* values between 6 and 14 are reserved */ +#define PARC_EXTENDED 15 + +#define VF_POOL_SIZE 32 +#define DECODE_BUFFER_NUM_MAX 8 +#define PUT_INTERVAL (HZ/100) + +#define RATE_DETECT_COUNT 5 +#define DURATION_UNIT 96000 +#define PTS_UNIT 90000 + +#define DUR2PTS(x) ((x) - ((x) >> 4)) + +static struct vframe_s *vmpeg_vf_peek(void *); +static struct vframe_s *vmpeg_vf_get(void *); +static void vmpeg_vf_put(struct vframe_s *, void *); +static int vmpeg_vf_states(struct vframe_states *states, void *); +static int vmpeg_event_cb(int type, void *data, void *private_data); + +static void vmpeg4_prot_init(void); +static void vmpeg4_local_init(void); + +static const char vmpeg4_dec_id[] = "vmpeg4-dev"; + +#define PROVIDER_NAME "decoder.mpeg4" + +/* +int query_video_status(int type, int *value); +*/ +static const struct vframe_operations_s vmpeg_vf_provider = { + .peek = vmpeg_vf_peek, + .get = vmpeg_vf_get, + .put = vmpeg_vf_put, + .event_cb = vmpeg_event_cb, + .vf_states = vmpeg_vf_states, +}; + +static struct vframe_provider_s vmpeg_vf_prov; + +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 struct vframe_s vfpool[VF_POOL_SIZE]; +static s32 vfbuf_use[DECODE_BUFFER_NUM_MAX]; +static u32 frame_width, frame_height, frame_dur, frame_prog; +static u32 saved_resolution; +static struct timer_list recycle_timer; +static u32 stat; +static unsigned long buf_start; +static u32 buf_size, buf_offset; +static u32 vmpeg4_ratio; +static u64 vmpeg4_ratio64; +static u32 rate_detect; +static u32 vmpeg4_rotation; + +static u32 total_frame; +static u32 last_vop_time_inc, last_duration; +static u32 last_anch_pts, vop_time_inc_since_last_anch, + frame_num_since_last_anch; +static u64 last_anch_pts_us64; + +#ifdef CONFIG_AM_VDEC_MPEG4_LOG +u32 pts_hit, pts_missed, pts_i_hit, pts_i_missed; +#endif + +static DEFINE_SPINLOCK(lock); + +static struct dec_sysinfo vmpeg4_amstream_dec_info; + +static unsigned char aspect_ratio_table[16] = { + PARC_FORBIDDEN, + PARC_SQUARE, + PARC_CIF, + PARC_10_11, + PARC_16_11, + PARC_40_33, + PARC_RESERVED, PARC_RESERVED, PARC_RESERVED, PARC_RESERVED, + PARC_RESERVED, PARC_RESERVED, PARC_RESERVED, PARC_RESERVED, + PARC_RESERVED, PARC_EXTENDED +}; + +static inline u32 index2canvas(u32 index) +{ + const u32 canvas_tab[8] = { +#ifdef NV21 + 0x010100, 0x030302, 0x050504, 0x070706, + 0x090908, 0x0b0b0a, 0x0d0d0c, 0x0f0f0e +#else + 0x020100, 0x050403, 0x080706, 0x0b0a09, + 0x0e0d0c, 0x11100f, 0x141312, 0x171615 +#endif + }; + + return canvas_tab[index]; +} + +static void set_aspect_ratio(struct vframe_s *vf, unsigned pixel_ratio) +{ + int ar = 0; + unsigned int num = 0; + unsigned int den = 0; + + if (vmpeg4_ratio64 != 0) { + num = vmpeg4_ratio64 >> 32; + den = vmpeg4_ratio64 & 0xffffffff; + } else { + num = vmpeg4_ratio >> 16; + den = vmpeg4_ratio & 0xffff; + + } + if ((num == 0) || (den == 0)) { + num = 1; + den = 1; + } + + if (vmpeg4_ratio == 0) { + vf->ratio_control |= (0x90 << DISP_RATIO_ASPECT_RATIO_BIT); + /* always stretch to 16:9 */ + } else if (pixel_ratio > 0x0f) { + num = (pixel_ratio >> 8) * + vmpeg4_amstream_dec_info.width * num; + ar = div_u64((pixel_ratio & 0xff) * + vmpeg4_amstream_dec_info.height * den * 0x100ULL + + (num >> 1), num); + } else { + switch (aspect_ratio_table[pixel_ratio]) { + case 0: + num = vmpeg4_amstream_dec_info.width * num; + ar = (vmpeg4_amstream_dec_info.height * den * 0x100 + + (num >> 1)) / num; + break; + case 1: + num = vf->width * num; + ar = (vf->height * den * 0x100 + (num >> 1)) / num; + break; + case 2: + num = (vf->width * 12) * num; + ar = (vf->height * den * 0x100 * 11 + + ((num) >> 1)) / num; + break; + case 3: + num = (vf->width * 10) * num; + ar = (vf->height * den * 0x100 * 11 + (num >> 1)) / + num; + break; + case 4: + num = (vf->width * 16) * num; + ar = (vf->height * den * 0x100 * 11 + (num >> 1)) / + num; + break; + case 5: + num = (vf->width * 40) * num; + ar = (vf->height * den * 0x100 * 33 + (num >> 1)) / + num; + break; + default: + num = vf->width * num; + ar = (vf->height * den * 0x100 + (num >> 1)) / num; + break; + } + } + + ar = min(ar, DISP_RATIO_ASPECT_RATIO_MAX); + + vf->ratio_control = (ar << DISP_RATIO_ASPECT_RATIO_BIT); +} + +static irqreturn_t vmpeg4_isr(int irq, void *dev_id) +{ + u32 reg; + struct vframe_s *vf = NULL; + u32 picture_type; + u32 buffer_index; + u32 pts, pts_valid = 0, offset = 0; + u64 pts_us64 = 0; + u32 rate, vop_time_inc, repeat_cnt, duration = 3200; + + reg = READ_VREG(MREG_BUFFEROUT); + + if (reg) { + buffer_index = reg & 0x7; + picture_type = (reg >> 3) & 7; + rate = READ_VREG(MP4_RATE); + repeat_cnt = READ_VREG(MP4_NOT_CODED_CNT); + vop_time_inc = READ_VREG(MP4_VOP_TIME_INC); + + if (buffer_index >= DECODE_BUFFER_NUM_MAX) { + pr_err("fatal error, invalid buffer index."); + return IRQ_HANDLED; + } + + if (vmpeg4_amstream_dec_info.width == 0) { + vmpeg4_amstream_dec_info.width = + READ_VREG(MP4_PIC_WH) >> 16; + } +#if 0 + else { + pr_info("info width = %d, ucode width = %d\n", + vmpeg4_amstream_dec_info.width, + READ_VREG(MP4_PIC_WH) >> 16); + } +#endif + + if (vmpeg4_amstream_dec_info.height == 0) { + vmpeg4_amstream_dec_info.height = + READ_VREG(MP4_PIC_WH) & 0xffff; + } +#if 0 + else { + pr_info("info height = %d, ucode height = %d\n", + vmpeg4_amstream_dec_info.height, + READ_VREG(MP4_PIC_WH) & 0xffff); + } +#endif + if (vmpeg4_amstream_dec_info.rate == 0 + || vmpeg4_amstream_dec_info.rate > 96000) { + /* if ((rate >> 16) != 0) { */ + if ((rate & 0xffff) != 0 && (rate >> 16) != 0) { + vmpeg4_amstream_dec_info.rate = + (rate >> 16) * DURATION_UNIT / + (rate & 0xffff); + duration = vmpeg4_amstream_dec_info.rate; + } else if (rate_detect < RATE_DETECT_COUNT) { + if (vop_time_inc < last_vop_time_inc) { + duration = + vop_time_inc + rate - + last_vop_time_inc; + } else { + duration = + vop_time_inc - last_vop_time_inc; + } + + if (duration == last_duration) { + rate_detect++; + if (rate_detect >= RATE_DETECT_COUNT) { + vmpeg4_amstream_dec_info.rate = + duration * DURATION_UNIT / + rate; + duration = + vmpeg4_amstream_dec_info.rate; + } + } else + rate_detect = 0; + + last_duration = duration; + } + } else { + duration = vmpeg4_amstream_dec_info.rate; +#if 0 + pr_info("info rate = %d, ucode rate = 0x%x:0x%x\n", + vmpeg4_amstream_dec_info.rate, + READ_VREG(MP4_RATE), vop_time_inc); +#endif + } + + if ((I_PICTURE == picture_type) || + (P_PICTURE == picture_type)) { + offset = READ_VREG(MP4_OFFSET_REG); + /*2500-->3000,because some mpeg4 + video may checkout failed; + may have av sync problem.can changed small later. + 263 may need small? + */ + if (pts_lookup_offset_us64 + (PTS_TYPE_VIDEO, offset, &pts, 3000, + &pts_us64) == 0) { + pts_valid = 1; + last_anch_pts = pts; + last_anch_pts_us64 = pts_us64; +#ifdef CONFIG_AM_VDEC_MPEG4_LOG + pts_hit++; +#endif + } else { +#ifdef CONFIG_AM_VDEC_MPEG4_LOG + pts_missed++; +#endif + } +#ifdef CONFIG_AM_VDEC_MPEG4_LOG + amlog_mask(LOG_MASK_PTS, + "I offset 0x%x, pts_valid %d pts=0x%x\n", + offset, pts_valid, pts); +#endif + } + + if (pts_valid) { + last_anch_pts = pts; + last_anch_pts_us64 = pts_us64; + frame_num_since_last_anch = 0; + vop_time_inc_since_last_anch = 0; + } else { + pts = last_anch_pts; + pts_us64 = last_anch_pts_us64; + + if ((rate != 0) && ((rate >> 16) == 0) + && vmpeg4_amstream_dec_info.rate == 0) { + /* variable PTS rate */ + /*bug on variable pts calc, + do as dixed vop first if we + have rate setting before. + */ + if (vop_time_inc > last_vop_time_inc) { + vop_time_inc_since_last_anch += + vop_time_inc - last_vop_time_inc; + } else { + vop_time_inc_since_last_anch += + vop_time_inc + rate - + last_vop_time_inc; + } + + pts += vop_time_inc_since_last_anch * + PTS_UNIT / rate; + pts_us64 += (vop_time_inc_since_last_anch * + PTS_UNIT / rate) * 100 / 9; + + if (vop_time_inc_since_last_anch > (1 << 14)) { + /* avoid overflow */ + last_anch_pts = pts; + last_anch_pts_us64 = pts_us64; + vop_time_inc_since_last_anch = 0; + } + } else { + /* fixed VOP rate */ + frame_num_since_last_anch++; + pts += DUR2PTS(frame_num_since_last_anch * + vmpeg4_amstream_dec_info.rate); + pts_us64 += DUR2PTS(frame_num_since_last_anch * + vmpeg4_amstream_dec_info.rate) * + 100 / 9; + + if (frame_num_since_last_anch > (1 << 15)) { + /* avoid overflow */ + last_anch_pts = pts; + last_anch_pts_us64 = pts_us64; + frame_num_since_last_anch = 0; + } + } + } + + if (reg & INTERLACE_FLAG) { /* interlace */ + if (kfifo_get(&newframe_q, &vf) == 0) { + printk + ("fatal error, no available buffer slot."); + return IRQ_HANDLED; + } + vf->signal_type = 0; + vf->index = buffer_index; + vf->width = vmpeg4_amstream_dec_info.width; + vf->height = vmpeg4_amstream_dec_info.height; + vf->bufWidth = 1920; + vf->flag = 0; + vf->orientation = vmpeg4_rotation; + vf->pts = pts; + vf->pts_us64 = pts_us64; + vf->duration = duration >> 1; + vf->duration_pulldown = 0; + vf->type = (reg & TOP_FIELD_FIRST_FLAG) ? + VIDTYPE_INTERLACE_TOP : + VIDTYPE_INTERLACE_BOTTOM; +#ifdef NV21 + vf->type |= VIDTYPE_VIU_NV21; +#endif + vf->canvas0Addr = vf->canvas1Addr = + index2canvas(buffer_index); + vf->type_original = vf->type; + + set_aspect_ratio(vf, READ_VREG(MP4_PIC_RATIO)); + + vfbuf_use[buffer_index]++; + + kfifo_put(&display_q, (const struct vframe_s *)vf); + + vf_notify_receiver(PROVIDER_NAME, + VFRAME_EVENT_PROVIDER_VFRAME_READY, + NULL); + + if (kfifo_get(&newframe_q, &vf) == 0) { + printk( + "fatal error, no available buffer slot."); + return IRQ_HANDLED; + } + vf->signal_type = 0; + vf->index = buffer_index; + vf->width = vmpeg4_amstream_dec_info.width; + vf->height = vmpeg4_amstream_dec_info.height; + vf->bufWidth = 1920; + vf->flag = 0; + vf->orientation = vmpeg4_rotation; + + vf->pts = 0; + vf->pts_us64 = 0; + vf->duration = duration >> 1; + + vf->duration_pulldown = 0; + vf->type = (reg & TOP_FIELD_FIRST_FLAG) ? + VIDTYPE_INTERLACE_BOTTOM : VIDTYPE_INTERLACE_TOP; +#ifdef NV21 + vf->type |= VIDTYPE_VIU_NV21; +#endif + vf->canvas0Addr = vf->canvas1Addr = + index2canvas(buffer_index); + vf->type_original = vf->type; + + set_aspect_ratio(vf, READ_VREG(MP4_PIC_RATIO)); + + vfbuf_use[buffer_index]++; + + amlog_mask(LOG_MASK_PTS, + "[%s:%d] [inte] dur=0x%x rate=%d picture_type=%d\n", + __func__, __LINE__, vf->duration, + vmpeg4_amstream_dec_info.rate, picture_type); + + kfifo_put(&display_q, (const struct vframe_s *)vf); + + vf_notify_receiver(PROVIDER_NAME, + VFRAME_EVENT_PROVIDER_VFRAME_READY, + NULL); + + } else { /* progressive */ + if (kfifo_get(&newframe_q, &vf) == 0) { + printk + ("fatal error, no available buffer slot."); + return IRQ_HANDLED; + } + vf->signal_type = 0; + vf->index = buffer_index; + vf->width = vmpeg4_amstream_dec_info.width; + vf->height = vmpeg4_amstream_dec_info.height; + vf->bufWidth = 1920; + vf->flag = 0; + vf->orientation = vmpeg4_rotation; + vf->pts = pts; + vf->pts_us64 = pts_us64; + vf->duration = duration; + vf->duration_pulldown = repeat_cnt * duration; +#ifdef NV21 + vf->type = + VIDTYPE_PROGRESSIVE | VIDTYPE_VIU_FIELD | + VIDTYPE_VIU_NV21; +#else + vf->type = VIDTYPE_PROGRESSIVE | VIDTYPE_VIU_FIELD; +#endif + vf->canvas0Addr = vf->canvas1Addr = + index2canvas(buffer_index); + vf->type_original = vf->type; + + set_aspect_ratio(vf, READ_VREG(MP4_PIC_RATIO)); + + amlog_mask(LOG_MASK_PTS, + "[%s:%d] [prog] dur=0x%x rate=%d picture_type=%d\n", + __func__, __LINE__, vf->duration, + vmpeg4_amstream_dec_info.rate, picture_type); + + vfbuf_use[buffer_index]++; + + kfifo_put(&display_q, (const struct vframe_s *)vf); + + vf_notify_receiver(PROVIDER_NAME, + VFRAME_EVENT_PROVIDER_VFRAME_READY, + NULL); + } + + total_frame += repeat_cnt + 1; + + WRITE_VREG(MREG_BUFFEROUT, 0); + + last_vop_time_inc = vop_time_inc; + } + + WRITE_VREG(ASSIST_MBOX1_CLR_REG, 1); + + return IRQ_HANDLED; +} + +static struct vframe_s *vmpeg_vf_peek(void *op_arg) +{ + struct vframe_s *vf; + + if (kfifo_peek(&display_q, &vf)) + return vf; + + return NULL; +} + +static struct vframe_s *vmpeg_vf_get(void *op_arg) +{ + struct vframe_s *vf; + + if (kfifo_get(&display_q, &vf)) + return vf; + + return NULL; +} + +static void vmpeg_vf_put(struct vframe_s *vf, void *op_arg) +{ + kfifo_put(&recycle_q, (const struct vframe_s *)vf); +} + +static int vmpeg_event_cb(int type, void *data, void *private_data) +{ + if (type & VFRAME_EVENT_RECEIVER_RESET) { + unsigned long flags; + amvdec_stop(); +#ifndef CONFIG_AMLOGIC_POST_PROCESS_MANAGER + vf_light_unreg_provider(&vmpeg_vf_prov); +#endif + spin_lock_irqsave(&lock, flags); + vmpeg4_local_init(); + vmpeg4_prot_init(); + spin_unlock_irqrestore(&lock, flags); +#ifndef CONFIG_AMLOGIC_POST_PROCESS_MANAGER + vf_reg_provider(&vmpeg_vf_prov); +#endif + amvdec_start(); + } + return 0; +} + +static int vmpeg_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); + states->buf_recycle_num = kfifo_len(&recycle_q); + + spin_unlock_irqrestore(&lock, flags); + + return 0; +} + +static void vmpeg_put_timer_func(unsigned long arg) +{ + struct timer_list *timer = (struct timer_list *)arg; + + while (!kfifo_is_empty(&recycle_q) && (READ_VREG(MREG_BUFFERIN) == 0)) { + struct vframe_s *vf; + if (kfifo_get(&recycle_q, &vf)) { + if ((vf->index >= 0) + && (vf->index < DECODE_BUFFER_NUM_MAX) + && (--vfbuf_use[vf->index] == 0)) { + WRITE_VREG(MREG_BUFFERIN, ~(1 << vf->index)); + vf->index = DECODE_BUFFER_NUM_MAX; + } + kfifo_put(&newframe_q, (const struct vframe_s *)vf); + } + } + if (frame_dur > 0 && saved_resolution != + frame_width * frame_height * (96000 / frame_dur)) { + int fps = 96000 / frame_dur; + saved_resolution = frame_width * frame_height * fps; + vdec_source_changed(VFORMAT_MPEG4, + frame_width, frame_height, fps); + } + if (READ_VREG(AV_SCRATCH_L)) { + unsigned long flags; + pr_info("mpeg4 fatal error happened,need reset !!\n"); + amvdec_stop(); +#ifndef CONFIG_AMLOGIC_POST_PROCESS_MANAGER + vf_light_unreg_provider(&vmpeg_vf_prov); +#endif + spin_lock_irqsave(&lock, flags); + vmpeg4_local_init(); + vmpeg4_prot_init(); + spin_unlock_irqrestore(&lock, flags); +#ifndef CONFIG_AMLOGIC_POST_PROCESS_MANAGER + vf_reg_provider(&vmpeg_vf_prov); +#endif + amvdec_start(); + } + + + timer->expires = jiffies + PUT_INTERVAL; + + add_timer(timer); +} + +int vmpeg4_dec_status(struct vdec_s *vdec, struct vdec_status *vstatus) +{ + vstatus->width = vmpeg4_amstream_dec_info.width; + vstatus->height = vmpeg4_amstream_dec_info.height; + if (0 != vmpeg4_amstream_dec_info.rate) + vstatus->fps = DURATION_UNIT / vmpeg4_amstream_dec_info.rate; + else + vstatus->fps = DURATION_UNIT; + vstatus->error_count = READ_VREG(MP4_ERR_COUNT); + vstatus->status = stat; + + return 0; +} + +/****************************************/ +static void vmpeg4_canvas_init(void) +{ + int i; + u32 canvas_width, canvas_height; + u32 decbuf_size, decbuf_y_size, decbuf_uv_size; + u32 disp_addr = 0xffffffff; + u32 buff_off = 0; + + if (buf_size <= 0x00400000) { + /* SD only */ + canvas_width = 768; + canvas_height = 576; + decbuf_y_size = 0x80000; + decbuf_uv_size = 0x20000; + decbuf_size = 0x100000; + } else { + int w = vmpeg4_amstream_dec_info.width; + int h = vmpeg4_amstream_dec_info.height; + int align_w, align_h; + int max, min; + align_w = ALIGN(w, 64); + align_h = ALIGN(h, 64); + if (align_w > align_h) { + max = align_w; + min = align_h; + } else { + max = align_h; + min = align_w; + } + /* HD & SD */ + if ((max > 1920 || min > 1088) && + ALIGN(align_w * align_h * 3/2, SZ_64K) * 9 <= + buf_size) { + canvas_width = align_w; + canvas_height = align_h; + decbuf_y_size = ALIGN(align_w * align_h, SZ_64K); + decbuf_uv_size = ALIGN(align_w * align_h/4, SZ_64K); + decbuf_size = ALIGN(align_w * align_h * 3/2, SZ_64K); + } else { /*1080p*/ + if (h > w) { + canvas_width = 1088; + canvas_height = 1920; + } else { + canvas_width = 1920; + canvas_height = 1088; + } + decbuf_y_size = 0x200000; + decbuf_uv_size = 0x80000; + decbuf_size = 0x300000; + } + } + + if (is_vpp_postblend()) { + struct canvas_s cur_canvas; + + canvas_read((READ_VCBUS_REG(VD1_IF0_CANVAS0) & 0xff), + &cur_canvas); + disp_addr = (cur_canvas.addr + 7) >> 3; + } + + for (i = 0; i < 8; i++) { + u32 one_buf_start = buf_start + buff_off; + if (((one_buf_start + 7) >> 3) == disp_addr) { + /*last disp buffer, to next..*/ + buff_off += decbuf_size; + one_buf_start = buf_start + buff_off; + pr_info("one_buf_start %d,=== %x disp_addr %x", + i, one_buf_start, disp_addr); + } + if (buff_off < 0x02000000 && + buff_off + decbuf_size > 0x01b00000){ + /*0x01b00000 is references buffer. + to next 32M;*/ + buff_off = 32 * SZ_1M;/*next 32M*/ + one_buf_start = buf_start + buff_off; + } + if (buff_off + decbuf_size > buf_size) { + pr_err("ERROR::too small buffer for buf%d %d x%d ,size =%d\n", + i, + canvas_width, + canvas_height, + buf_size); + } + pr_debug("alloced buffer %d at %x,%d\n", + i, one_buf_start, decbuf_size); +#ifdef NV21 + canvas_config(2 * i + 0, + one_buf_start, + canvas_width, canvas_height, + CANVAS_ADDR_NOWRAP, CANVAS_BLKMODE_32X32); + canvas_config(2 * i + 1, + one_buf_start + + decbuf_y_size, canvas_width, + canvas_height / 2, CANVAS_ADDR_NOWRAP, + CANVAS_BLKMODE_32X32); +#else + canvas_config(3 * i + 0, + one_buf_start, + canvas_width, canvas_height, + CANVAS_ADDR_NOWRAP, CANVAS_BLKMODE_32X32); + canvas_config(3 * i + 1, + one_buf_start + + decbuf_y_size, canvas_width / 2, + canvas_height / 2, CANVAS_ADDR_NOWRAP, + CANVAS_BLKMODE_32X32); + canvas_config(3 * i + 2, + one_buf_start + + decbuf_y_size + decbuf_uv_size, + canvas_width / 2, canvas_height / 2, + CANVAS_ADDR_NOWRAP, CANVAS_BLKMODE_32X32); +#endif + buff_off = buff_off + decbuf_size; + } +} + +static void vmpeg4_prot_init(void) +{ +#if 1 /* /MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON6 */ + WRITE_VREG(DOS_SW_RESET0, (1 << 7) | (1 << 6)); + WRITE_VREG(DOS_SW_RESET0, 0); +#else + WRITE_MPEG_REG(RESET0_REGISTER, RESET_IQIDCT | RESET_MC); +#endif + + vmpeg4_canvas_init(); + + /* index v << 16 | u << 8 | y */ +#ifdef NV21 + WRITE_VREG(AV_SCRATCH_0, 0x010100); + WRITE_VREG(AV_SCRATCH_1, 0x030302); + WRITE_VREG(AV_SCRATCH_2, 0x050504); + WRITE_VREG(AV_SCRATCH_3, 0x070706); + WRITE_VREG(AV_SCRATCH_G, 0x090908); + WRITE_VREG(AV_SCRATCH_H, 0x0b0b0a); + WRITE_VREG(AV_SCRATCH_I, 0x0d0d0c); + WRITE_VREG(AV_SCRATCH_J, 0x0f0f0e); +#else + WRITE_VREG(AV_SCRATCH_0, 0x020100); + WRITE_VREG(AV_SCRATCH_1, 0x050403); + WRITE_VREG(AV_SCRATCH_2, 0x080706); + WRITE_VREG(AV_SCRATCH_3, 0x0b0a09); + WRITE_VREG(AV_SCRATCH_G, 0x0e0d0c); + WRITE_VREG(AV_SCRATCH_H, 0x11100f); + WRITE_VREG(AV_SCRATCH_I, 0x141312); + WRITE_VREG(AV_SCRATCH_J, 0x171615); +#endif + WRITE_VREG(AV_SCRATCH_L, 0);/*clearfatal error flag*/ + + /* notify ucode the buffer offset */ + WRITE_VREG(AV_SCRATCH_F, buf_offset); + + /* disable PSCALE for hardware sharing */ + WRITE_VREG(PSCALE_CTRL, 0); + + WRITE_VREG(MREG_BUFFERIN, 0); + WRITE_VREG(MREG_BUFFEROUT, 0); + + /* clear mailbox interrupt */ + WRITE_VREG(ASSIST_MBOX1_CLR_REG, 1); + + /* enable mailbox interrupt */ + WRITE_VREG(ASSIST_MBOX1_MASK, 1); + + /* clear repeat count */ + WRITE_VREG(MP4_NOT_CODED_CNT, 0); + +#ifdef NV21 + SET_VREG_MASK(MDEC_PIC_DC_CTRL, 1 << 17); +#endif + +#if 1/* /MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON8 */ + printk("mpeg4 meson8 prot init\n"); + WRITE_VREG(MDEC_PIC_DC_THRESH, 0x404038aa); +#endif + + WRITE_VREG(MP4_PIC_WH, (vmpeg4_amstream_dec_info. + width << 16) | vmpeg4_amstream_dec_info.height); + WRITE_VREG(MP4_SYS_RATE, vmpeg4_amstream_dec_info.rate); +} + +static void vmpeg4_local_init(void) +{ + int i; + + vmpeg4_ratio = vmpeg4_amstream_dec_info.ratio; + + vmpeg4_ratio64 = vmpeg4_amstream_dec_info.ratio64; + + vmpeg4_rotation = + (((unsigned long) vmpeg4_amstream_dec_info.param) + >> 16) & 0xffff; + + frame_width = frame_height = frame_dur = frame_prog = 0; + + total_frame = 0; + saved_resolution = 0; + last_anch_pts = 0; + + last_anch_pts_us64 = 0; + + last_vop_time_inc = last_duration = 0; + + vop_time_inc_since_last_anch = 0; + + frame_num_since_last_anch = 0; + +#ifdef CONFIG_AM_VDEC_MPEG4_LOG + pts_hit = pts_missed = pts_i_hit = pts_i_missed = 0; +#endif + + for (i = 0; i < DECODE_BUFFER_NUM_MAX; i++) + vfbuf_use[i] = 0; + + INIT_KFIFO(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 = DECODE_BUFFER_NUM_MAX; + kfifo_put(&newframe_q, (const struct vframe_s *)vf); + } +} + +static s32 vmpeg4_init(void) +{ + int trickmode_fffb = 0; + int size = -1; + char *buf = vmalloc(0x1000 * 16); + if (IS_ERR_OR_NULL(buf)) + return -ENOMEM; + + query_video_status(0, &trickmode_fffb); + + amlog_level(LOG_LEVEL_INFO, "vmpeg4_init\n"); + init_timer(&recycle_timer); + + stat |= STAT_TIMER_INIT; + + amvdec_enable(); + + vmpeg4_local_init(); + + if (vmpeg4_amstream_dec_info.format == VIDEO_DEC_FORMAT_MPEG4_3) { + size = get_firmware_data(VIDEO_DEC_MPEG4_3, buf); + + amlog_level(LOG_LEVEL_INFO, "load VIDEO_DEC_FORMAT_MPEG4_3\n"); + } else if (vmpeg4_amstream_dec_info.format == + VIDEO_DEC_FORMAT_MPEG4_4) { + size = get_firmware_data(VIDEO_DEC_MPEG4_4, buf); + + amlog_level(LOG_LEVEL_INFO, "load VIDEO_DEC_FORMAT_MPEG4_4\n"); + } else if (vmpeg4_amstream_dec_info.format == + VIDEO_DEC_FORMAT_MPEG4_5) { + size = get_firmware_data(VIDEO_DEC_MPEG4_5, buf); + + amlog_level(LOG_LEVEL_INFO, "load VIDEO_DEC_FORMAT_MPEG4_5\n"); + } else if (vmpeg4_amstream_dec_info.format == VIDEO_DEC_FORMAT_H263) { + size = get_firmware_data(VIDEO_DEC_H263, buf); + + amlog_level(LOG_LEVEL_INFO, "load VIDEO_DEC_FORMAT_H263\n"); + } else + amlog_level(LOG_LEVEL_ERROR, "not supported MPEG4 format\n"); + + if (size < 0) { + pr_err("get firmware fail."); + vfree(buf); + return -1; + } + + if (amvdec_loadmc_ex(VFORMAT_MPEG4, NULL, buf) < 0) { + amvdec_disable(); + vfree(buf); + return -EBUSY; + } + + vfree(buf); + + stat |= STAT_MC_LOAD; + + /* enable AMRISC side protocol */ + vmpeg4_prot_init(); + + if (vdec_request_irq(VDEC_IRQ_1, vmpeg4_isr, + "vmpeg4-irq", (void *)vmpeg4_dec_id)) { + amvdec_disable(); + + amlog_level(LOG_LEVEL_ERROR, "vmpeg4 irq register error.\n"); + return -ENOENT; + } + + stat |= STAT_ISR_REG; +#ifdef CONFIG_AMLOGIC_POST_PROCESS_MANAGER + vf_provider_init(&vmpeg_vf_prov, PROVIDER_NAME, &vmpeg_vf_provider, + NULL); + vf_reg_provider(&vmpeg_vf_prov); + vf_notify_receiver(PROVIDER_NAME, VFRAME_EVENT_PROVIDER_START, NULL); +#else + vf_provider_init(&vmpeg_vf_prov, PROVIDER_NAME, &vmpeg_vf_provider, + NULL); + vf_reg_provider(&vmpeg_vf_prov); +#endif + vf_notify_receiver(PROVIDER_NAME, VFRAME_EVENT_PROVIDER_FR_HINT, + (void *)((unsigned long)vmpeg4_amstream_dec_info.rate)); + + stat |= STAT_VF_HOOK; + + recycle_timer.data = (ulong)&recycle_timer; + recycle_timer.function = vmpeg_put_timer_func; + recycle_timer.expires = jiffies + PUT_INTERVAL; + + add_timer(&recycle_timer); + + stat |= STAT_TIMER_ARM; + + amvdec_start(); + + stat |= STAT_VDEC_RUN; + + return 0; +} + +static int amvdec_mpeg4_probe(struct platform_device *pdev) +{ + struct vdec_s *pdata = *(struct vdec_s **)pdev->dev.platform_data; + + if (pdata == NULL) { + amlog_level(LOG_LEVEL_ERROR, + "amvdec_mpeg4 memory resource undefined.\n"); + return -EFAULT; + } + + buf_start = pdata->mem_start; + buf_size = pdata->mem_end - pdata->mem_start + 1; + buf_offset = buf_start - ORI_BUFFER_START_ADDR; + + if (pdata->sys_info) + vmpeg4_amstream_dec_info = *pdata->sys_info; + + pdata->dec_status = vmpeg4_dec_status; + + if (vmpeg4_init() < 0) { + amlog_level(LOG_LEVEL_ERROR, "amvdec_mpeg4 init failed.\n"); + + return -ENODEV; + } + + return 0; +} + +static int amvdec_mpeg4_remove(struct platform_device *pdev) +{ + if (stat & STAT_VDEC_RUN) { + amvdec_stop(); + stat &= ~STAT_VDEC_RUN; + } + + if (stat & STAT_ISR_REG) { + vdec_free_irq(VDEC_IRQ_1, (void *)vmpeg4_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) { + vf_notify_receiver(PROVIDER_NAME, + VFRAME_EVENT_PROVIDER_FR_END_HINT, NULL); + + vf_unreg_provider(&vmpeg_vf_prov); + stat &= ~STAT_VF_HOOK; + } + + amvdec_disable(); + + amlog_mask(LOG_MASK_PTS, + "pts hit %d, pts missed %d, i hit %d, missed %d\n", pts_hit, + pts_missed, pts_i_hit, pts_i_missed); + amlog_mask(LOG_MASK_PTS, "total frame %d, rate %d\n", total_frame, + vmpeg4_amstream_dec_info.rate); + + return 0; +} + +/****************************************/ + +static struct platform_driver amvdec_mpeg4_driver = { + .probe = amvdec_mpeg4_probe, + .remove = amvdec_mpeg4_remove, +#ifdef CONFIG_PM + .suspend = amvdec_suspend, + .resume = amvdec_resume, +#endif + .driver = { + .name = DRIVER_NAME, + } +}; + +static struct codec_profile_t amvdec_mpeg4_profile = { + .name = "mpeg4", + .profile = "" +}; + +static int __init amvdec_mpeg4_driver_init_module(void) +{ + amlog_level(LOG_LEVEL_INFO, "amvdec_mpeg4 module init\n"); + + if (platform_driver_register(&amvdec_mpeg4_driver)) { + amlog_level(LOG_LEVEL_ERROR, + "failed to register amvdec_mpeg4 driver\n"); + return -ENODEV; + } + vcodec_profile_register(&amvdec_mpeg4_profile); + return 0; +} + +static void __exit amvdec_mpeg4_driver_remove_module(void) +{ + amlog_level(LOG_LEVEL_INFO, "amvdec_mpeg4 module remove.\n"); + + platform_driver_unregister(&amvdec_mpeg4_driver); +} + +/****************************************/ + +module_param(stat, uint, 0664); +MODULE_PARM_DESC(stat, "\n amvdec_mpeg4 stat\n"); + +module_init(amvdec_mpeg4_driver_init_module); +module_exit(amvdec_mpeg4_driver_remove_module); + +MODULE_DESCRIPTION("AMLOGIC MPEG4 Video Decoder Driver"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Tim Yao <timyao@amlogic.com>"); diff --git a/drivers/frame_provider/decoder/mpeg4/vmpeg4.h b/drivers/frame_provider/decoder/mpeg4/vmpeg4.h new file mode 100644 index 0000000..21ff478 --- a/dev/null +++ b/drivers/frame_provider/decoder/mpeg4/vmpeg4.h @@ -0,0 +1,26 @@ +/* + * drivers/amlogic/amports/vmpeg4.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 VMPEG4_H +#define VMPEG4_H + +/* /#if MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON6 */ +/* TODO: move to register headers */ +#define VPP_VD1_POSTBLEND (1 << 10) +/* /#endif */ + +#endif /* VMPEG4_H */ diff --git a/drivers/frame_provider/decoder/mpeg4/vmpeg4_multi.c b/drivers/frame_provider/decoder/mpeg4/vmpeg4_multi.c new file mode 100644 index 0000000..5c4b242 --- a/dev/null +++ b/drivers/frame_provider/decoder/mpeg4/vmpeg4_multi.c @@ -0,0 +1,1304 @@ +/* + * drivers/amlogic/amports/vmpeg4.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/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/dma-mapping.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/canvas/canvas.h> +#include <linux/amlogic/media/codec_mm/codec_mm.h> + +#include <linux/amlogic/media/utils/vdec_reg.h> +#include "vmpeg4.h" +#include <linux/amlogic/media/registers/register.h> +#include "../../../stream_input/amports/amports_priv.h" + +#include "../utils/amvdec.h" +#include "../utils/vdec_input.h" +#include "../utils/vdec.h" + +#define DRIVER_NAME "ammvdec_mpeg4" +#define MODULE_NAME "ammvdec_mpeg4" + +#define MEM_NAME "codec_mpeg4" + +#define DEBUG_PTS + +#define NV21 +#define I_PICTURE 0 +#define P_PICTURE 1 +#define B_PICTURE 2 + +#define ORI_BUFFER_START_ADDR 0x01000000 +#define DEFAULT_MEM_SIZE (32*SZ_1M) + +#define INTERLACE_FLAG 0x80 +#define TOP_FIELD_FIRST_FLAG 0x40 + +/* protocol registers */ +#define MREG_REF0 AV_SCRATCH_1 +#define MREG_REF1 AV_SCRATCH_2 +#define MP4_PIC_RATIO AV_SCRATCH_5 +#define MP4_RATE AV_SCRATCH_3 +#define MP4_ERR_COUNT AV_SCRATCH_6 +#define MP4_PIC_WH AV_SCRATCH_7 +#define MREG_INPUT AV_SCRATCH_8 +#define MREG_BUFFEROUT AV_SCRATCH_9 +#define MP4_NOT_CODED_CNT AV_SCRATCH_A +#define MP4_VOP_TIME_INC AV_SCRATCH_B +#define MP4_OFFSET_REG AV_SCRATCH_C +#define MP4_SYS_RATE AV_SCRATCH_E +#define MEM_OFFSET_REG AV_SCRATCH_F + +#define PARC_FORBIDDEN 0 +#define PARC_SQUARE 1 +#define PARC_CIF 2 +#define PARC_10_11 3 +#define PARC_16_11 4 +#define PARC_40_33 5 +#define PARC_RESERVED 6 +/* values between 6 and 14 are reserved */ +#define PARC_EXTENDED 15 + +#define VF_POOL_SIZE 16 +#define DECODE_BUFFER_NUM_MAX 4 +#define PUT_INTERVAL (HZ/100) + +#define CTX_LMEM_SWAP_OFFSET 0 +#define CTX_QUANT_MATRIX_OFFSET 0x800 +/* dcac buffer must align at 4k boundary */ +#define CTX_DCAC_BUF_OFFSET 0x1000 +#define CTX_DECBUF_OFFSET (0x0c0000 + 0x1000) + +#define RATE_DETECT_COUNT 5 +#define DURATION_UNIT 96000 +#define PTS_UNIT 90000 + +#define DUR2PTS(x) ((x) - ((x) >> 4)) + +#define DEC_RESULT_NONE 0 +#define DEC_RESULT_DONE 1 +#define DEC_RESULT_AGAIN 2 +#define DEC_RESULT_ERROR 3 + +static struct vframe_s *vmpeg_vf_peek(void *); +static struct vframe_s *vmpeg_vf_get(void *); +static void vmpeg_vf_put(struct vframe_s *, void *); +static int vmpeg_vf_states(struct vframe_states *states, void *); +static int vmpeg_event_cb(int type, void *data, void *private_data); + +struct vdec_mpeg4_hw_s { + spinlock_t lock; + struct platform_device *platform_dev; + struct device *cma_dev; + + DECLARE_KFIFO(newframe_q, struct vframe_s *, VF_POOL_SIZE); + DECLARE_KFIFO(display_q, struct vframe_s *, VF_POOL_SIZE); + struct vframe_s vfpool[VF_POOL_SIZE]; + + s32 vfbuf_use[DECODE_BUFFER_NUM_MAX]; + u32 frame_width; + u32 frame_height; + u32 frame_dur; + u32 frame_prog; + + u32 ctx_valid; + u32 reg_vcop_ctrl_reg; + u32 reg_pic_head_info; + u32 reg_mpeg1_2_reg; + u32 reg_slice_qp; + u32 reg_mp4_pic_wh; + u32 reg_mp4_rate; + u32 reg_mb_info; + u32 reg_dc_ac_ctrl; + u32 reg_iqidct_control; + u32 reg_resync_marker_length; + u32 reg_rv_ai_mb_count; + + struct vframe_chunk_s *chunk; + u32 stat; + u32 buf_start; + u32 buf_size; + unsigned long cma_alloc_addr; + int cma_alloc_count; + u32 vmpeg4_ratio; + u64 vmpeg4_ratio64; + u32 rate_detect; + u32 vmpeg4_rotation; + u32 total_frame; + u32 last_vop_time_inc; + u32 last_duration; + u32 last_anch_pts; + u32 vop_time_inc_since_last_anch; + u32 frame_num_since_last_anch; + u64 last_anch_pts_us64; + + u32 pts_hit; + u32 pts_missed; + u32 pts_i_hit; + u32 pts_i_missed; + + u32 buffer_info[DECODE_BUFFER_NUM_MAX]; + u32 pts[DECODE_BUFFER_NUM_MAX]; + u64 pts64[DECODE_BUFFER_NUM_MAX]; + bool pts_valid[DECODE_BUFFER_NUM_MAX]; + u32 canvas_spec[DECODE_BUFFER_NUM_MAX]; +#ifdef NV21 + struct canvas_config_s canvas_config[DECODE_BUFFER_NUM_MAX][2]; +#else + struct canvas_config_s canvas_config[DECODE_BUFFER_NUM_MAX][3]; +#endif + struct dec_sysinfo vmpeg4_amstream_dec_info; + + s32 refs[2]; + int dec_result; + struct work_struct work; + + void (*vdec_cb)(struct vdec_s *, void *); + void *vdec_cb_arg; + +}; +static void vmpeg4_local_init(struct vdec_mpeg4_hw_s *hw); +static int vmpeg4_hw_ctx_restore(struct vdec_mpeg4_hw_s *hw); + +#define PROVIDER_NAME "vdec.mpeg4" + +/* +int query_video_status(int type, int *value); +*/ +static const struct vframe_operations_s vf_provider_ops = { + .peek = vmpeg_vf_peek, + .get = vmpeg_vf_get, + .put = vmpeg_vf_put, + .event_cb = vmpeg_event_cb, + .vf_states = vmpeg_vf_states, +}; + +static unsigned char aspect_ratio_table[16] = { + PARC_FORBIDDEN, + PARC_SQUARE, + PARC_CIF, + PARC_10_11, + PARC_16_11, + PARC_40_33, + PARC_RESERVED, PARC_RESERVED, PARC_RESERVED, PARC_RESERVED, + PARC_RESERVED, PARC_RESERVED, PARC_RESERVED, PARC_RESERVED, + PARC_RESERVED, PARC_EXTENDED +}; + +static int find_buffer(struct vdec_mpeg4_hw_s *hw) +{ + int i; + + for (i = 0; i < DECODE_BUFFER_NUM_MAX; i++) { + if (hw->vfbuf_use[i] == 0) + return i; + } + + return -1; +} + +static int spec_to_index(struct vdec_mpeg4_hw_s *hw, u32 spec) +{ + int i; + + for (i = 0; i < DECODE_BUFFER_NUM_MAX; i++) { + if (hw->canvas_spec[i] == spec) + return i; + } + + return -1; +} + +static void set_frame_info(struct vdec_mpeg4_hw_s *hw, struct vframe_s *vf, + int buffer_index) +{ + int ar = 0; + unsigned int num = 0; + unsigned int den = 0; + unsigned pixel_ratio = READ_VREG(MP4_PIC_RATIO); + + if (hw->vmpeg4_ratio64 != 0) { + num = hw->vmpeg4_ratio64>>32; + den = hw->vmpeg4_ratio64 & 0xffffffff; + } else { + num = hw->vmpeg4_ratio>>16; + den = hw->vmpeg4_ratio & 0xffff; + + } + if ((num == 0) || (den == 0)) { + num = 1; + den = 1; + } + + if (hw->vmpeg4_ratio == 0) { + vf->ratio_control |= (0x90 << DISP_RATIO_ASPECT_RATIO_BIT); + /* always stretch to 16:9 */ + } else if (pixel_ratio > 0x0f) { + num = (pixel_ratio >> 8) * + hw->vmpeg4_amstream_dec_info.width * num; + ar = div_u64((pixel_ratio & 0xff) * + hw->vmpeg4_amstream_dec_info.height * den * 0x100ULL + + (num >> 1), num); + } else { + switch (aspect_ratio_table[pixel_ratio]) { + case 0: + num = hw->vmpeg4_amstream_dec_info.width * num; + ar = (hw->vmpeg4_amstream_dec_info.height * den * + 0x100 + (num >> 1)) / num; + break; + case 1: + num = vf->width * num; + ar = (vf->height * den * 0x100 + (num >> 1)) / num; + break; + case 2: + num = (vf->width * 12) * num; + ar = (vf->height * den * 0x100 * 11 + + ((num) >> 1)) / num; + break; + case 3: + num = (vf->width * 10) * num; + ar = (vf->height * den * 0x100 * 11 + (num >> 1)) / + num; + break; + case 4: + num = (vf->width * 16) * num; + ar = (vf->height * den * 0x100 * 11 + (num >> 1)) / + num; + break; + case 5: + num = (vf->width * 40) * num; + ar = (vf->height * den * 0x100 * 33 + (num >> 1)) / + num; + break; + default: + num = vf->width * num; + ar = (vf->height * den * 0x100 + (num >> 1)) / num; + break; + } + } + + ar = min(ar, DISP_RATIO_ASPECT_RATIO_MAX); + + vf->signal_type = 0; + vf->type_original = vf->type; + vf->ratio_control = (ar << DISP_RATIO_ASPECT_RATIO_BIT); + vf->canvas0Addr = vf->canvas1Addr = -1; +#ifdef NV21 + vf->plane_num = 2; +#else + vf->plane_num = 3; +#endif + vf->canvas0_config[0] = hw->canvas_config[buffer_index][0]; + vf->canvas0_config[1] = hw->canvas_config[buffer_index][1]; +#ifndef NV21 + vf->canvas0_config[2] = hw->canvas_config[buffer_index][2]; +#endif + vf->canvas1_config[0] = hw->canvas_config[buffer_index][0]; + vf->canvas1_config[1] = hw->canvas_config[buffer_index][1]; +#ifndef NV21 + vf->canvas1_config[2] = hw->canvas_config[buffer_index][2]; +#endif +} + +static inline void vmpeg4_save_hw_context(struct vdec_mpeg4_hw_s *hw) +{ + hw->reg_mpeg1_2_reg = READ_VREG(MPEG1_2_REG); + hw->reg_vcop_ctrl_reg = READ_VREG(VCOP_CTRL_REG); + hw->reg_pic_head_info = READ_VREG(PIC_HEAD_INFO); + hw->reg_slice_qp = READ_VREG(SLICE_QP); + hw->reg_mp4_pic_wh = READ_VREG(MP4_PIC_WH); + hw->reg_mp4_rate = READ_VREG(MP4_RATE); + hw->reg_mb_info = READ_VREG(MB_INFO); + hw->reg_dc_ac_ctrl = READ_VREG(DC_AC_CTRL); + hw->reg_iqidct_control = READ_VREG(IQIDCT_CONTROL); + hw->reg_resync_marker_length = READ_VREG(RESYNC_MARKER_LENGTH); + hw->reg_rv_ai_mb_count = READ_VREG(RV_AI_MB_COUNT); +} + +static irqreturn_t vmpeg4_isr(struct vdec_s *vdec) +{ + u32 reg; + struct vframe_s *vf = NULL; + u32 picture_type; + int index; + u32 pts, offset = 0; + bool pts_valid = false; + u64 pts_us64 = 0; + u32 time_increment_resolution, fixed_vop_rate, vop_time_inc; + u32 repeat_cnt, duration = 3200; + struct vdec_mpeg4_hw_s *hw = (struct vdec_mpeg4_hw_s *)(vdec->private); + + WRITE_VREG(ASSIST_MBOX1_CLR_REG, 1); + + reg = READ_VREG(MREG_BUFFEROUT); + + time_increment_resolution = READ_VREG(MP4_RATE); + fixed_vop_rate = time_increment_resolution >> 16; + time_increment_resolution &= 0xffff; + + if (hw->vmpeg4_amstream_dec_info.rate == 0) { + if ((fixed_vop_rate != 0) && (time_increment_resolution != 0)) { + /* fixed VOP rate */ + hw->vmpeg4_amstream_dec_info.rate = fixed_vop_rate * + DURATION_UNIT / + time_increment_resolution; + } + } + + if (reg == 2) { + /* timeout when decoding next frame */ + + /* for frame based case, insufficient result may happen + * at the begining when only VOL head is available save + * HW context also, such as for the QTable from VCOP register + */ + if (input_frame_based(vdec)) + vmpeg4_save_hw_context(hw); + + hw->dec_result = DEC_RESULT_AGAIN; + + schedule_work(&hw->work); + + return IRQ_HANDLED; + } else { + picture_type = (reg >> 3) & 7; + repeat_cnt = READ_VREG(MP4_NOT_CODED_CNT); + vop_time_inc = READ_VREG(MP4_VOP_TIME_INC); + + index = spec_to_index(hw, READ_VREG(REC_CANVAS_ADDR)); + + if (index < 0) { + pr_err("invalid buffer index."); + hw->dec_result = DEC_RESULT_ERROR; + + schedule_work(&hw->work); + + return IRQ_HANDLED; + } + + hw->dec_result = DEC_RESULT_DONE; + + pr_debug("amvdec_mpeg4: offset = 0x%x\n", + READ_VREG(MP4_OFFSET_REG)); + + if (hw->vmpeg4_amstream_dec_info.width == 0) { + hw->vmpeg4_amstream_dec_info.width = + READ_VREG(MP4_PIC_WH) >> 16; + } +#if 0 + else { + pr_info("info width = %d, ucode width = %d\n", + hw->vmpeg4_amstream_dec_info.width, + READ_VREG(MP4_PIC_WH) >> 16); + } +#endif + + if (hw->vmpeg4_amstream_dec_info.height == 0) { + hw->vmpeg4_amstream_dec_info.height = + READ_VREG(MP4_PIC_WH) & 0xffff; + } +#if 0 + else { + pr_info("info height = %d, ucode height = %d\n", + hw->vmpeg4_amstream_dec_info.height, + READ_VREG(MP4_PIC_WH) & 0xffff); + } +#endif + if (hw->vmpeg4_amstream_dec_info.rate == 0) { + if (vop_time_inc < hw->last_vop_time_inc) { + duration = vop_time_inc + + time_increment_resolution - + hw->last_vop_time_inc; + } else { + duration = vop_time_inc - + hw->last_vop_time_inc; + } + + if (duration == hw->last_duration) { + hw->rate_detect++; + if (hw->rate_detect >= RATE_DETECT_COUNT) { + hw->vmpeg4_amstream_dec_info.rate = + duration * DURATION_UNIT / + time_increment_resolution; + duration = + hw->vmpeg4_amstream_dec_info.rate; + } + } else { + hw->rate_detect = 0; + hw->last_duration = duration; + } + } else { + duration = hw->vmpeg4_amstream_dec_info.rate; +#if 0 + pr_info("info rate = %d, ucode rate = 0x%x:0x%x\n", + hw->vmpeg4_amstream_dec_info.rate, + READ_VREG(MP4_RATE), vop_time_inc); +#endif + } + + if ((I_PICTURE == picture_type) || + (P_PICTURE == picture_type)) { + offset = READ_VREG(MP4_OFFSET_REG); + if (hw->chunk) { + hw->pts_valid[index] = hw->chunk->pts_valid; + hw->pts[index] = hw->chunk->pts; + hw->pts64[index] = hw->chunk->pts64; + } else { + if (pts_lookup_offset_us64 + (PTS_TYPE_VIDEO, offset, &pts, 3000, + &pts_us64) == 0) { + hw->pts_valid[index] = true; + hw->pts[index] = pts; + hw->pts64[index] = pts_us64; + hw->pts_hit++; + } else { + hw->pts_valid[index] = false; + hw->pts_missed++; + } + } + pr_debug("I/P offset 0x%x, pts_valid %d pts=0x%x\n", + offset, hw->pts_valid[index], + hw->pts[index]); + } else { + hw->pts_valid[index] = false; + hw->pts[index] = 0; + hw->pts64[index] = 0; + } + + hw->buffer_info[index] = reg; + hw->vfbuf_use[index] = 0; + + pr_debug("amvdec_mpeg4: decoded buffer %d, frame_type %s\n", + index, + (picture_type == I_PICTURE) ? "I" : + (picture_type == P_PICTURE) ? "P" : "B"); + + /* Buffer management + * todo: add sequence-end flush + */ + if ((picture_type == I_PICTURE) || + (picture_type == P_PICTURE)) { + hw->vfbuf_use[index]++; + + if (hw->refs[1] == -1) { + hw->refs[1] = index; + index = -1; + } else if (hw->refs[0] == -1) { + hw->refs[0] = hw->refs[1]; + hw->refs[1] = index; + index = hw->refs[0]; + } else { + hw->vfbuf_use[hw->refs[0]]--; + hw->refs[0] = hw->refs[1]; + hw->refs[1] = index; + index = hw->refs[0]; + } + } else { + /* if this is a B frame, then drop (depending on if + * there are two reference frames) or display + * immediately + */ + if (hw->refs[1] == -1) + index = -1; + } + + vmpeg4_save_hw_context(hw); + + if (index < 0) { + schedule_work(&hw->work); + return IRQ_HANDLED; + } + + reg = hw->buffer_info[index]; + pts_valid = hw->pts_valid[index]; + pts = hw->pts[index]; + pts_us64 = hw->pts64[index]; + + pr_debug("queued buffer %d, pts = 0x%x, pts_valid=%d\n", + index, pts, pts_valid); + + if (pts_valid) { + hw->last_anch_pts = pts; + hw->last_anch_pts_us64 = pts_us64; + hw->frame_num_since_last_anch = 0; + hw->vop_time_inc_since_last_anch = 0; + } else { + pts = hw->last_anch_pts; + pts_us64 = hw->last_anch_pts_us64; + + if ((time_increment_resolution != 0) && + (fixed_vop_rate == 0) && + (hw->vmpeg4_amstream_dec_info.rate == 0)) { + /* variable PTS rate */ + /*bug on variable pts calc, + do as dixed vop first if we + have rate setting before. + */ + if (vop_time_inc > hw->last_vop_time_inc) { + duration = vop_time_inc - + hw->last_vop_time_inc; + } else { + duration = vop_time_inc + + time_increment_resolution - + hw->last_vop_time_inc; + } + + hw->vop_time_inc_since_last_anch += duration; + + pts += hw->vop_time_inc_since_last_anch * + PTS_UNIT / time_increment_resolution; + pts_us64 += (hw->vop_time_inc_since_last_anch * + PTS_UNIT / time_increment_resolution) * + 100 / 9; + + if (hw->vop_time_inc_since_last_anch > + (1 << 14)) { + /* avoid overflow */ + hw->last_anch_pts = pts; + hw->last_anch_pts_us64 = pts_us64; + hw->vop_time_inc_since_last_anch = 0; + } + } else { + /* fixed VOP rate */ + hw->frame_num_since_last_anch++; + pts += DUR2PTS(hw->frame_num_since_last_anch * + hw->vmpeg4_amstream_dec_info.rate); + pts_us64 += DUR2PTS( + hw->frame_num_since_last_anch * + hw->vmpeg4_amstream_dec_info.rate) * + 100 / 9; + + if (hw->frame_num_since_last_anch > (1 << 15)) { + /* avoid overflow */ + hw->last_anch_pts = pts; + hw->last_anch_pts_us64 = pts_us64; + hw->frame_num_since_last_anch = 0; + } + } + } + + if (reg & INTERLACE_FLAG) { /* interlace */ + if (kfifo_get(&hw->newframe_q, &vf) == 0) { + pr_err + ("fatal error, no available buffer slot."); + return IRQ_HANDLED; + } + + vf->index = index; + vf->width = hw->vmpeg4_amstream_dec_info.width; + vf->height = hw->vmpeg4_amstream_dec_info.height; + vf->bufWidth = 1920; + vf->flag = 0; + vf->orientation = hw->vmpeg4_rotation; + vf->pts = pts; + vf->pts_us64 = pts_us64; + vf->duration = duration >> 1; + vf->duration_pulldown = 0; + vf->type = (reg & TOP_FIELD_FIRST_FLAG) ? + VIDTYPE_INTERLACE_TOP : + VIDTYPE_INTERLACE_BOTTOM; +#ifdef NV21 + vf->type |= VIDTYPE_VIU_NV21; +#endif + set_frame_info(hw, vf, index); + + hw->vfbuf_use[index]++; + + kfifo_put(&hw->display_q, (const struct vframe_s *)vf); + + vf_notify_receiver(vdec->vf_provider_name, + VFRAME_EVENT_PROVIDER_VFRAME_READY, + NULL); + + if (kfifo_get(&hw->newframe_q, &vf) == 0) { + pr_err("fatal error, no available buffer slot."); + hw->dec_result = DEC_RESULT_ERROR; + schedule_work(&hw->work); + return IRQ_HANDLED; + } + + vf->index = index; + vf->width = hw->vmpeg4_amstream_dec_info.width; + vf->height = hw->vmpeg4_amstream_dec_info.height; + vf->bufWidth = 1920; + vf->flag = 0; + vf->orientation = hw->vmpeg4_rotation; + + vf->pts = 0; + vf->pts_us64 = 0; + vf->duration = duration >> 1; + + vf->duration_pulldown = 0; + vf->type = (reg & TOP_FIELD_FIRST_FLAG) ? + VIDTYPE_INTERLACE_BOTTOM : VIDTYPE_INTERLACE_TOP; +#ifdef NV21 + vf->type |= VIDTYPE_VIU_NV21; +#endif + set_frame_info(hw, vf, index); + + hw->vfbuf_use[index]++; + + kfifo_put(&hw->display_q, (const struct vframe_s *)vf); + + vf_notify_receiver(vdec->vf_provider_name, + VFRAME_EVENT_PROVIDER_VFRAME_READY, + NULL); + + } else { /* progressive */ + if (kfifo_get(&hw->newframe_q, &vf) == 0) { + pr_err("fatal error, no available buffer slot."); + return IRQ_HANDLED; + } + + vf->index = index; + vf->width = hw->vmpeg4_amstream_dec_info.width; + vf->height = hw->vmpeg4_amstream_dec_info.height; + vf->bufWidth = 1920; + vf->flag = 0; + vf->orientation = hw->vmpeg4_rotation; + vf->pts = pts; + vf->pts_us64 = pts_us64; + vf->duration = duration; + vf->duration_pulldown = repeat_cnt * duration; +#ifdef NV21 + vf->type = + VIDTYPE_PROGRESSIVE | VIDTYPE_VIU_FIELD | + VIDTYPE_VIU_NV21; +#else + vf->type = + VIDTYPE_PROGRESSIVE | VIDTYPE_VIU_FIELD; +#endif + set_frame_info(hw, vf, index); + + + hw->vfbuf_use[index]++; + + kfifo_put(&hw->display_q, (const struct vframe_s *)vf); + + vf_notify_receiver(vdec->vf_provider_name, + VFRAME_EVENT_PROVIDER_VFRAME_READY, + NULL); + } + + hw->total_frame += repeat_cnt + 1; + hw->last_vop_time_inc = vop_time_inc; + + schedule_work(&hw->work); + } + + return IRQ_HANDLED; +} + +static void vmpeg4_work(struct work_struct *work) +{ + struct vdec_mpeg4_hw_s *hw = + container_of(work, struct vdec_mpeg4_hw_s, work); + + /* finished decoding one frame or error, + * notify vdec core to switch context + */ + amvdec_stop(); + + if ((hw->dec_result == DEC_RESULT_DONE) || + ((hw->chunk) && + (input_frame_based(&(hw_to_vdec(hw))->input)))) { + if (!hw->ctx_valid) + hw->ctx_valid = 1; + + vdec_vframe_dirty(hw_to_vdec(hw), hw->chunk); + } + + /* mark itself has all HW resource released and input released */ + vdec_set_status(hw_to_vdec(hw), VDEC_STATUS_CONNECTED); + + if (hw->vdec_cb) + hw->vdec_cb(hw_to_vdec(hw), hw->vdec_cb_arg); +} + +static struct vframe_s *vmpeg_vf_peek(void *op_arg) +{ + struct vframe_s *vf; + struct vdec_s *vdec = op_arg; + struct vdec_mpeg4_hw_s *hw = (struct vdec_mpeg4_hw_s *)vdec->private; + + if (!hw) + return NULL; + + if (kfifo_peek(&hw->display_q, &vf)) + return vf; + + return NULL; +} + +static struct vframe_s *vmpeg_vf_get(void *op_arg) +{ + struct vframe_s *vf; + struct vdec_s *vdec = op_arg; + struct vdec_mpeg4_hw_s *hw = (struct vdec_mpeg4_hw_s *)vdec->private; + + if (kfifo_get(&hw->display_q, &vf)) + return vf; + + return NULL; +} + +static void vmpeg_vf_put(struct vframe_s *vf, void *op_arg) +{ + struct vdec_s *vdec = op_arg; + struct vdec_mpeg4_hw_s *hw = (struct vdec_mpeg4_hw_s *)vdec->private; + + hw->vfbuf_use[vf->index]--; + + kfifo_put(&hw->newframe_q, (const struct vframe_s *)vf); +} + +static int vmpeg_event_cb(int type, void *data, void *private_data) +{ + return 0; +} + +static int vmpeg_vf_states(struct vframe_states *states, void *op_arg) +{ + unsigned long flags; + struct vdec_s *vdec = op_arg; + struct vdec_mpeg4_hw_s *hw = (struct vdec_mpeg4_hw_s *)vdec->private; + + spin_lock_irqsave(&hw->lock, flags); + + states->vf_pool_size = VF_POOL_SIZE; + states->buf_free_num = kfifo_len(&hw->newframe_q); + states->buf_avail_num = kfifo_len(&hw->display_q); + states->buf_recycle_num = 0; + + spin_unlock_irqrestore(&hw->lock, flags); + + return 0; +} + + +static int dec_status(struct vdec_s *vdec, struct vdec_status *vstatus) +{ + struct vdec_mpeg4_hw_s *hw = (struct vdec_mpeg4_hw_s *)vdec->private; + + vstatus->width = hw->vmpeg4_amstream_dec_info.width; + vstatus->height = hw->vmpeg4_amstream_dec_info.height; + if (0 != hw->vmpeg4_amstream_dec_info.rate) + vstatus->fps = DURATION_UNIT / + hw->vmpeg4_amstream_dec_info.rate; + else + vstatus->fps = DURATION_UNIT; + vstatus->error_count = READ_VREG(MP4_ERR_COUNT); + vstatus->status = hw->stat; + + return 0; +} + +/****************************************/ +static int vmpeg4_canvas_init(struct vdec_mpeg4_hw_s *hw) +{ + int i; + u32 decbuf_size, decbuf_y_size; + struct vdec_s *vdec = hw_to_vdec(hw); + u32 decbuf_start; + + int w = hw->vmpeg4_amstream_dec_info.width; + int h = hw->vmpeg4_amstream_dec_info.height; + + if (w == 0) + w = 1920; + if (h == 0) + h = 1088; + + w = ALIGN(w, 64); + h = ALIGN(h, 64); + decbuf_y_size = ALIGN(w * h, SZ_64K); + decbuf_size = ALIGN(w * h * 3/2, SZ_64K); + + decbuf_start = hw->buf_start + CTX_DECBUF_OFFSET; + + for (i = 0; i < DECODE_BUFFER_NUM_MAX; i++) { +#ifdef NV21 + unsigned int canvas = vdec->get_canvas(i, 2); +#else + unsigned int canvas = vdec->get_canvas(i, 3); +#endif + + hw->canvas_spec[i] = canvas; + +#ifdef NV21 + hw->canvas_config[i][0].phy_addr = decbuf_start + + i * decbuf_size; + hw->canvas_config[i][0].width = w; + hw->canvas_config[i][0].height = h; + hw->canvas_config[i][0].block_mode = CANVAS_BLKMODE_32X32; + + canvas_config_config(canvas_y(canvas), + &hw->canvas_config[i][0]); + + hw->canvas_config[i][1].phy_addr = decbuf_start + + i * decbuf_size + decbuf_y_size; + hw->canvas_config[i][1].width = w; + hw->canvas_config[i][1].height = h / 2; + hw->canvas_config[i][1].block_mode = CANVAS_BLKMODE_32X32; + + canvas_config_config(canvas_u(canvas), + &hw->canvas_config[i][1]); +#else + hw->canvas_config[i][0].phy_addr = decbuf_start + + i * decbuf_size; + hw->canvas_config[i][0].width = w; + hw->canvas_config[i][0].height = h; + hw->canvas_config[i][0].block_mode = CANVAS_BLKMODE_32X32; + + canvas_config_config(canvas_y(canvas), + &hw->canvas_config[i][0]); + + hw->canvas_config[i][1].phy_addr = decbuf_start + + i * decbuf_size + decbuf_y_size; + hw->canvas_config[i][1].width = w / 2; + hw->canvas_config[i][1].height = h / 2; + hw->canvas_config[i][1].block_mode = CANVAS_BLKMODE_32X32; + + canvas_config_config(canvas_u(canvas), + &hw->canvas_config[i][1]); + + hw->canvas_config[i][2].phy_addr = decbuf_start + + i * decbuf_size + decbuf_y_size + + decbuf_uv_size; + hw->canvas_config[i][2].width = w / 2; + hw->canvas_config[i][2].height = h / 2; + hw->canvas_config[i][2].block_mode = CANVAS_BLKMODE_32X32; + + canvas_config_config(canvas_v(canvas), + &hw->canvas_config[i][2]); +#endif + } + + return 0; +} + +static int vmpeg4_hw_ctx_restore(struct vdec_mpeg4_hw_s *hw) +{ + int index; + + + index = find_buffer(hw); + if (index < 0) + return -1; + + + if (vmpeg4_canvas_init(hw) < 0) + return -1; + + /* prepare REF0 & REF1 + * points to the past two IP buffers + * prepare REC_CANVAS_ADDR and ANC2_CANVAS_ADDR + * points to the output buffer + */ + if (hw->refs[0] == -1) { + WRITE_VREG(MREG_REF0, (hw->refs[1] == -1) ? 0xffffffff : + hw->canvas_spec[hw->refs[1]]); + } else { + WRITE_VREG(MREG_REF0, (hw->refs[0] == -1) ? 0xffffffff : + hw->canvas_spec[hw->refs[0]]); + } + WRITE_VREG(MREG_REF1, (hw->refs[1] == -1) ? 0xffffffff : + hw->canvas_spec[hw->refs[1]]); + + WRITE_VREG(MREG_REF0, (hw->refs[0] == -1) ? 0xffffffff : + hw->canvas_spec[hw->refs[0]]); + WRITE_VREG(MREG_REF1, (hw->refs[1] == -1) ? 0xffffffff : + hw->canvas_spec[hw->refs[1]]); + WRITE_VREG(REC_CANVAS_ADDR, hw->canvas_spec[index]); + WRITE_VREG(ANC2_CANVAS_ADDR, hw->canvas_spec[index]); + + pr_debug("vmpeg4_hw_ctx_restore ref0=0x%x, ref1=0x%x, rec=0x%x, ctx_valid=%d\n", + READ_VREG(MREG_REF0), + READ_VREG(MREG_REF1), + READ_VREG(REC_CANVAS_ADDR), + hw->ctx_valid); + + /* notify ucode the buffer start address */ + WRITE_VREG(MEM_OFFSET_REG, hw->buf_start); + + /* disable PSCALE for hardware sharing */ + WRITE_VREG(PSCALE_CTRL, 0); + + WRITE_VREG(MREG_BUFFEROUT, 0); + + /* clear mailbox interrupt */ + WRITE_VREG(ASSIST_MBOX1_CLR_REG, 1); + + /* enable mailbox interrupt */ + WRITE_VREG(ASSIST_MBOX1_MASK, 1); + + /* clear repeat count */ + WRITE_VREG(MP4_NOT_CODED_CNT, 0); + +#ifdef NV21 + SET_VREG_MASK(MDEC_PIC_DC_CTRL, 1 << 17); +#endif + +#if 1/* /MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON8 */ + WRITE_VREG(MDEC_PIC_DC_THRESH, 0x404038aa); +#endif + + WRITE_VREG(MP4_PIC_WH, (hw->ctx_valid) ? + hw->reg_mp4_pic_wh : + ((hw->vmpeg4_amstream_dec_info.width << 16) | + hw->vmpeg4_amstream_dec_info.height)); + WRITE_VREG(MP4_SYS_RATE, hw->vmpeg4_amstream_dec_info.rate); + + if (hw->ctx_valid) { + WRITE_VREG(DC_AC_CTRL, hw->reg_dc_ac_ctrl); + WRITE_VREG(IQIDCT_CONTROL, hw->reg_iqidct_control); + WRITE_VREG(RESYNC_MARKER_LENGTH, hw->reg_resync_marker_length); + WRITE_VREG(RV_AI_MB_COUNT, hw->reg_rv_ai_mb_count); + } + WRITE_VREG(MPEG1_2_REG, (hw->ctx_valid) ? hw->reg_mpeg1_2_reg : 1); + WRITE_VREG(VCOP_CTRL_REG, hw->reg_vcop_ctrl_reg); + WRITE_VREG(PIC_HEAD_INFO, hw->reg_pic_head_info); + WRITE_VREG(SLICE_QP, hw->reg_slice_qp); + WRITE_VREG(MB_INFO, hw->reg_mb_info); + + if (hw->chunk) { + /* frame based input */ + WRITE_VREG(MREG_INPUT, (hw->chunk->offset & 7) | (1<<7) | + (hw->ctx_valid<<6)); + } else { + /* stream based input */ + WRITE_VREG(MREG_INPUT, (hw->ctx_valid<<6)); + } + + return 0; +} + +static void vmpeg4_local_init(struct vdec_mpeg4_hw_s *hw) +{ + int i; + + hw->vmpeg4_ratio = hw->vmpeg4_amstream_dec_info.ratio; + + hw->vmpeg4_ratio64 = hw->vmpeg4_amstream_dec_info.ratio64; + + hw->vmpeg4_rotation = + (((unsigned long) hw->vmpeg4_amstream_dec_info.param) + >> 16) & 0xffff; + + hw->frame_width = hw->frame_height = hw->frame_dur = hw->frame_prog = 0; + + hw->total_frame = 0; + + hw->last_anch_pts = 0; + + hw->last_anch_pts_us64 = 0; + + hw->last_vop_time_inc = hw->last_duration = 0; + + hw->vop_time_inc_since_last_anch = 0; + + hw->frame_num_since_last_anch = 0; + + hw->pts_hit = hw->pts_missed = hw->pts_i_hit = hw->pts_i_missed = 0; + + for (i = 0; i < DECODE_BUFFER_NUM_MAX; i++) + hw->vfbuf_use[i] = 0; + + INIT_KFIFO(hw->display_q); + INIT_KFIFO(hw->newframe_q); + + for (i = 0; i < VF_POOL_SIZE; i++) { + const struct vframe_s *vf = &hw->vfpool[i]; + hw->vfpool[i].index = DECODE_BUFFER_NUM_MAX; + kfifo_put(&hw->newframe_q, (const struct vframe_s *)vf); + } + + INIT_WORK(&hw->work, vmpeg4_work); +} + +static s32 vmpeg4_init(struct vdec_mpeg4_hw_s *hw) +{ + int trickmode_fffb = 0; + + query_video_status(0, &trickmode_fffb); + + pr_info("vmpeg4_init\n"); + + amvdec_enable(); + + vmpeg4_local_init(hw); + + return 0; +} + +static bool run_ready(struct vdec_s *vdec) +{ + int index; + struct vdec_mpeg4_hw_s *hw = (struct vdec_mpeg4_hw_s *)vdec->private; + + index = find_buffer(hw); + + return index >= 0; +} + +static void run(struct vdec_s *vdec, void (*callback)(struct vdec_s *, void *), + void *arg) +{ + struct vdec_mpeg4_hw_s *hw = (struct vdec_mpeg4_hw_s *)vdec->private; + int save_reg = READ_VREG(POWER_CTL_VLD); + int ret = -1,size = -1; + char *buf = vmalloc(0x1000 * 16); + if (IS_ERR_OR_NULL(buf)) + return; + + /* reset everything except DOS_TOP[1] and APB_CBUS[0] */ + WRITE_VREG(DOS_SW_RESET0, 0xfffffff0); + WRITE_VREG(DOS_SW_RESET0, 0); + WRITE_VREG(POWER_CTL_VLD, save_reg); + + hw->vdec_cb_arg = arg; + hw->vdec_cb = callback; + + ret = vdec_prepare_input(vdec, &hw->chunk); + if (ret < 0) { + pr_debug("amvdec_mpeg4: Input not ready\n"); + hw->dec_result = DEC_RESULT_AGAIN; + schedule_work(&hw->work); + vfree(buf); + return; + } + + vdec_enable_input(vdec); + + if (hw->chunk) + pr_debug("input chunk offset %d, size %d\n", + hw->chunk->offset, hw->chunk->size); + + hw->dec_result = DEC_RESULT_NONE; + + if (hw->vmpeg4_amstream_dec_info.format == + VIDEO_DEC_FORMAT_MPEG4_3) { + size = get_firmware_data(VIDEO_DEC_MPEG4_3, buf); + + pr_info("load VIDEO_DEC_FORMAT_MPEG4_3\n"); + } else if (hw->vmpeg4_amstream_dec_info.format == + VIDEO_DEC_FORMAT_MPEG4_4) { + size = get_firmware_data(VIDEO_DEC_MPEG4_4, buf); + + pr_info("load VIDEO_DEC_FORMAT_MPEG4_4\n"); + } else if (hw->vmpeg4_amstream_dec_info.format == + VIDEO_DEC_FORMAT_MPEG4_5) { + size = get_firmware_data(VIDEO_DEC_MPEG4_5, buf); + + pr_info("load VIDEO_DEC_FORMAT_MPEG4_5\n"); + } else if (hw->vmpeg4_amstream_dec_info.format == + VIDEO_DEC_FORMAT_H263) { + size = get_firmware_data(VIDEO_DEC_H263, buf); + + pr_info("load VIDEO_DEC_FORMAT_H263\n"); + } + + if (size < 0) { + pr_err("get firmware fail."); + vfree(buf); + return; + } + + if (amvdec_vdec_loadmc_buf_ex(vdec, buf, size) < 0) { + hw->dec_result = DEC_RESULT_ERROR; + schedule_work(&hw->work); + vfree(buf); + return; + } + + vfree(buf); + + if (vmpeg4_hw_ctx_restore(hw) < 0) { + hw->dec_result = DEC_RESULT_ERROR; + pr_err("amvdec_mpeg4: error HW context restore\n"); + schedule_work(&hw->work); + return; + } + + /* wmb before ISR is handled */ + wmb(); + + amvdec_start(); +} + +static void reset(struct vdec_s *vdec) +{ + struct vdec_mpeg4_hw_s *hw = (struct vdec_mpeg4_hw_s *)vdec->private; + + pr_info("amvdec_mpeg4: reset.\n"); + + vmpeg4_local_init(hw); + + hw->ctx_valid = false; +} + +static int amvdec_mpeg4_probe(struct platform_device *pdev) +{ + struct vdec_s *pdata = *(struct vdec_s **)pdev->dev.platform_data; + struct vdec_mpeg4_hw_s *hw = NULL; + + pr_info("amvdec_mpeg4[%d] probe start.\n", pdev->id); + + if (pdata == NULL) { + pr_err("ammvdec_mpeg4 memory resource undefined.\n"); + return -EFAULT; + } + + hw = (struct vdec_mpeg4_hw_s *)devm_kzalloc(&pdev->dev, + sizeof(struct vdec_mpeg4_hw_s), GFP_KERNEL); + if (hw == NULL) { + pr_info("\namvdec_mpeg4 decoder driver alloc failed\n"); + return -ENOMEM; + } + + pdata->private = hw; + pdata->dec_status = dec_status; + /* pdata->set_trickmode = set_trickmode; */ + pdata->run_ready = run_ready; + pdata->run = run; + pdata->reset = reset; + pdata->irq_handler = vmpeg4_isr; + + pdata->id = pdev->id; + + if (pdata->use_vfm_path) + snprintf(pdata->vf_provider_name, VDEC_PROVIDER_NAME_SIZE, + VFM_DEC_PROVIDER_NAME); + else + snprintf(pdata->vf_provider_name, VDEC_PROVIDER_NAME_SIZE, + PROVIDER_NAME ".%02x", pdev->id & 0xff); + + vf_provider_init(&pdata->vframe_provider, pdata->vf_provider_name, + &vf_provider_ops, pdata); + + platform_set_drvdata(pdev, pdata); + + hw->platform_dev = pdev; + hw->cma_dev = pdata->cma_dev; + + hw->cma_alloc_count = PAGE_ALIGN(DEFAULT_MEM_SIZE) / PAGE_SIZE; + hw->cma_alloc_addr = codec_mm_alloc_for_dma(MEM_NAME, + hw->cma_alloc_count, + 4, CODEC_MM_FLAGS_FOR_VDECODER); + + if (!hw->cma_alloc_addr) { + pr_err("codec_mm alloc failed, request buf size 0x%lx\n", + hw->cma_alloc_count * PAGE_SIZE); + hw->cma_alloc_count = 0; + return -ENOMEM; + } + hw->buf_start = hw->cma_alloc_addr; + hw->buf_size = DEFAULT_MEM_SIZE; + + if (pdata->sys_info) + hw->vmpeg4_amstream_dec_info = *pdata->sys_info; + + if (vmpeg4_init(hw) < 0) { + pr_err("amvdec_mpeg4 init failed.\n"); + + return -ENODEV; + } + + return 0; +} + +static int amvdec_mpeg4_remove(struct platform_device *pdev) +{ + struct vdec_mpeg4_hw_s *hw = + (struct vdec_mpeg4_hw_s *) + (((struct vdec_s *)(platform_get_drvdata(pdev)))->private); + + + amvdec_disable(); + + if (hw->cma_alloc_addr) { + pr_info("codec_mm release buffer 0x%lx\n", hw->cma_alloc_addr); + codec_mm_free_for_dma(MEM_NAME, hw->cma_alloc_addr); + hw->cma_alloc_count = 0; + } + + pr_info("pts hit %d, pts missed %d, i hit %d, missed %d\n", hw->pts_hit, + hw->pts_missed, hw->pts_i_hit, hw->pts_i_missed); + pr_info("total frame %d, rate %d\n", hw->total_frame, + hw->vmpeg4_amstream_dec_info.rate); + + return 0; +} + +/****************************************/ + +static struct platform_driver amvdec_mpeg4_driver = { + .probe = amvdec_mpeg4_probe, + .remove = amvdec_mpeg4_remove, +#ifdef CONFIG_PM + .suspend = amvdec_suspend, + .resume = amvdec_resume, +#endif + .driver = { + .name = DRIVER_NAME, + } +}; + +static struct codec_profile_t amvdec_mpeg4_profile = { + .name = "mmpeg4", + .profile = "" +}; + +static int __init amvdec_mmpeg4_driver_init_module(void) +{ + pr_info("amvdec_mmpeg4 module init\n"); + + if (platform_driver_register(&amvdec_mpeg4_driver)) { + pr_err("failed to register amvdec_mpeg4 driver\n"); + return -ENODEV; + } + vcodec_profile_register(&amvdec_mpeg4_profile); + return 0; +} + +static void __exit amvdec_mmpeg4_driver_remove_module(void) +{ + pr_info("amvdec_mmpeg4 module remove.\n"); + + platform_driver_unregister(&amvdec_mpeg4_driver); +} + +/****************************************/ + +module_init(amvdec_mmpeg4_driver_init_module); +module_exit(amvdec_mmpeg4_driver_remove_module); + +MODULE_DESCRIPTION("AMLOGIC MPEG4 Video Decoder Driver"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Tim Yao <timyao@amlogic.com>"); + diff --git a/drivers/frame_provider/decoder/real/vreal.c b/drivers/frame_provider/decoder/real/vreal.c new file mode 100644 index 0000000..d47fa99 --- a/dev/null +++ b/drivers/frame_provider/decoder/real/vreal.c @@ -0,0 +1,1022 @@ +/* + * drivers/amlogic/amports/vreal.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/types.h> +#include <linux/errno.h> +#include <linux/interrupt.h> +#include <linux/timer.h> +#include <linux/fs.h> +#include <linux/kfifo.h> +#include <linux/platform_device.h> + +#include <linux/amlogic/media/canvas/canvas.h> + +#include <linux/dma-mapping.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/vfm/vframe.h> +#include <linux/amlogic/media/vfm/vframe_provider.h> +#include <linux/amlogic/media/vfm/vframe_receiver.h> +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/uaccess.h> +#include "../../../stream_input/amports/amports_priv.h" +#include "../utils/vdec.h" +#include <linux/amlogic/media/utils/vdec_reg.h> +#include "../utils/amvdec.h" + +#include "../../../stream_input/parser/streambuf.h" +#include "../../../stream_input/parser/streambuf_reg.h" +#include "../../../stream_input/parser/rmparser.h" + +#include "vreal.h" +#include <linux/amlogic/media/registers/register.h> + +#define DRIVER_NAME "amvdec_real" +#define MODULE_NAME "amvdec_real" + +#if 1 /* MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON6 */ +#define NV21 +#endif + +#define RM_DEF_BUFFER_ADDR 0x01000000 +/* protocol registers */ +#define STATUS_AMRISC AV_SCRATCH_4 + +#define RV_PIC_INFO AV_SCRATCH_5 +#define VPTS_TR AV_SCRATCH_6 +#define VDTS AV_SCRATCH_7 +#define FROM_AMRISC AV_SCRATCH_8 +#define TO_AMRISC AV_SCRATCH_9 +#define SKIP_B_AMRISC AV_SCRATCH_A +#define INT_REASON AV_SCRATCH_B +#define WAIT_BUFFER AV_SCRATCH_E + +#if 1 /* MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON6 */ +#define MDEC_WIDTH AV_SCRATCH_I +#define MDEC_HEIGHT AV_SCRATCH_J +#else +#define MDEC_WIDTH HARM_ASB_MB2 +#define MDEC_HEIGHT HASB_ARM_MB0 +#endif + +#define PARC_FORBIDDEN 0 +#define PARC_SQUARE 1 +#define PARC_CIF 2 +#define PARC_10_11 3 +#define PARC_16_11 4 +#define PARC_40_33 5 +#define PARC_RESERVED 6 +/* values between 6 and 14 are reserved */ +#define PARC_EXTENDED 15 + +#define VF_POOL_SIZE 16 +#define VF_BUF_NUM 4 +#define PUT_INTERVAL (HZ/100) + +static struct vframe_s *vreal_vf_peek(void *); +static struct vframe_s *vreal_vf_get(void *); +static void vreal_vf_put(struct vframe_s *, void *); +static int vreal_vf_states(struct vframe_states *states, void *); +static int vreal_event_cb(int type, void *data, void *private_data); + +static void vreal_prot_init(void); +static void vreal_local_init(void); + +static const char vreal_dec_id[] = "vreal-dev"; + +#define PROVIDER_NAME "decoder.real" + +/* +int query_video_status(int type, int *value); +*/ +static const struct vframe_operations_s vreal_vf_provider = { + .peek = vreal_vf_peek, + .get = vreal_vf_get, + .put = vreal_vf_put, + .event_cb = vreal_event_cb, + .vf_states = vreal_vf_states, +}; + +static struct vframe_provider_s vreal_vf_prov; + +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 struct vframe_s vfpool[VF_POOL_SIZE]; +static s32 vfbuf_use[VF_BUF_NUM]; + +static u32 frame_width, frame_height, frame_dur, frame_prog; +static u32 saved_resolution; +static struct timer_list recycle_timer; +static u32 stat; +static unsigned long buf_start; +static u32 buf_size, buf_offset; +static u32 vreal_ratio; +u32 vreal_format; +static u32 wait_key_frame; +static u32 last_tr; +static u32 frame_count; +static u32 current_vdts; +static u32 hold; +static u32 decoder_state; +static u32 real_err_count; + +static u32 fatal_flag; +static s32 wait_buffer_counter; + +static DEFINE_SPINLOCK(lock); + +static unsigned short pic_sz_tbl[12] ____cacheline_aligned; +static dma_addr_t pic_sz_tbl_map; +static const unsigned char RPR_size[9] = { 0, 1, 1, 2, 2, 3, 3, 3, 3 }; + +static struct dec_sysinfo vreal_amstream_dec_info; + +static unsigned char aspect_ratio_table[16] = { + PARC_FORBIDDEN, + PARC_SQUARE, + PARC_CIF, + PARC_10_11, + PARC_16_11, + PARC_40_33, + PARC_RESERVED, PARC_RESERVED, PARC_RESERVED, PARC_RESERVED, + PARC_RESERVED, PARC_RESERVED, PARC_RESERVED, PARC_RESERVED, + PARC_RESERVED, PARC_EXTENDED +}; + +static inline u32 index2canvas(u32 index) +{ + const u32 canvas_tab[4] = { +#ifdef NV21 + 0x010100, 0x030302, 0x050504, 0x070706 +#else + 0x020100, 0x050403, 0x080706, 0x0b0a09 +#endif + }; + + return canvas_tab[index]; +} + +static void set_aspect_ratio(struct vframe_s *vf, unsigned pixel_ratio) +{ + int ar = 0; + + if (vreal_ratio == 0) { + vf->ratio_control |= (0x90 << + DISP_RATIO_ASPECT_RATIO_BIT); + /* always stretch to 16:9 */ + } else { + switch (aspect_ratio_table[pixel_ratio]) { + case 0: + ar = vreal_amstream_dec_info.height * vreal_ratio / + vreal_amstream_dec_info.width; + break; + case 1: + case 0xff: + ar = vreal_ratio * vf->height / vf->width; + break; + case 2: + ar = (vreal_ratio * vf->height * 12) / (vf->width * 11); + break; + case 3: + ar = (vreal_ratio * vf->height * 11) / (vf->width * 10); + break; + case 4: + ar = (vreal_ratio * vf->height * 11) / (vf->width * 16); + break; + case 5: + ar = (vreal_ratio * vf->height * 33) / (vf->width * 40); + break; + default: + ar = vreal_ratio * vf->height / vf->width; + break; + } + } + + ar = min(ar, DISP_RATIO_ASPECT_RATIO_MAX); + + vf->ratio_control |= (ar << DISP_RATIO_ASPECT_RATIO_BIT); +} + +static irqreturn_t vreal_isr(int irq, void *dev_id) +{ + u32 from; + struct vframe_s *vf = NULL; + u32 buffer_index; + unsigned int status; + unsigned int vdts; + unsigned int info; + unsigned int tr; + unsigned int pictype; + u32 r = READ_VREG(INT_REASON); + + if (decoder_state == 0) + return IRQ_HANDLED; + + status = READ_VREG(STATUS_AMRISC); + if (status & (PARSER_ERROR_WRONG_PACKAGE_SIZE | + PARSER_ERROR_WRONG_HEAD_VER | + DECODER_ERROR_VLC_DECODE_TBL)) { + /* decoder or parser error */ + real_err_count++; + /* pr_info("real decoder or parser + error, status 0x%x\n", status); */ + } + + if (r == 2) { + pr_info("first vpts = 0x%x\n", READ_VREG(VDTS)); + pts_checkin_offset(PTS_TYPE_AUDIO, 0, READ_VREG(VDTS) * 90); + WRITE_VREG(AV_SCRATCH_B, 0); + WRITE_VREG(ASSIST_MBOX1_CLR_REG, 1); + return IRQ_HANDLED; + } else if (r == 3) { + pr_info("first apts = 0x%x\n", READ_VREG(VDTS)); + pts_checkin_offset(PTS_TYPE_VIDEO, 0, READ_VREG(VDTS) * 90); + WRITE_VREG(AV_SCRATCH_B, 0); + WRITE_VREG(ASSIST_MBOX1_CLR_REG, 1); + return IRQ_HANDLED; + } + + from = READ_VREG(FROM_AMRISC); + if ((hold == 0) && from) { + tr = READ_VREG(VPTS_TR); + pictype = (tr >> 13) & 3; + tr = (tr & 0x1fff) * 96; + + if (kfifo_get(&newframe_q, &vf) == 0) { + pr_info("fatal error, no available buffer slot."); + return IRQ_HANDLED; + } + + vdts = READ_VREG(VDTS); + if (last_tr == -1) /* ignore tr for first time */ + vf->duration = frame_dur; + else { + if (tr > last_tr) + vf->duration = tr - last_tr; + else + vf->duration = (96 << 13) + tr - last_tr; + + if (vf->duration > 10 * frame_dur) { + /* not a reasonable duration, + should not happen */ + vf->duration = frame_dur; + } +#if 0 + else { + if (check_frame_duration == 0) { + frame_dur = vf->duration; + check_frame_duration = 1; + } + } +#endif + } + + last_tr = tr; + buffer_index = from & 0x03; + + if (0 == pictype) { /* I */ + current_vdts = vdts * 90 + 1; + vf->pts = current_vdts; + if (wait_key_frame) + wait_key_frame = 0; + } else { + if (wait_key_frame) { + while (READ_VREG(TO_AMRISC)) + ; + WRITE_VREG(TO_AMRISC, ~(1 << buffer_index)); + WRITE_VREG(FROM_AMRISC, 0); + return IRQ_HANDLED; + } else { + current_vdts += + vf->duration - (vf->duration >> 4); + vf->pts = current_vdts; + } + } + + /* pr_info("pts %d, picture type %d\n", vf->pts, pictype); */ + + info = READ_VREG(RV_PIC_INFO); + vf->signal_type = 0; + vf->index = buffer_index; + vf->width = info >> 16; + vf->height = (info >> 4) & 0xfff; + vf->bufWidth = 1920; + vf->flag = 0; + vf->ratio_control = 0; + set_aspect_ratio(vf, info & 0x0f); + vf->duration_pulldown = 0; +#ifdef NV21 + vf->type = VIDTYPE_PROGRESSIVE | + VIDTYPE_VIU_FIELD | VIDTYPE_VIU_NV21; +#else + vf->type = VIDTYPE_PROGRESSIVE | VIDTYPE_VIU_FIELD; +#endif + vf->canvas0Addr = vf->canvas1Addr = index2canvas(buffer_index); + vf->orientation = 0; + vf->type_original = vf->type; + + vfbuf_use[buffer_index] = 1; + + kfifo_put(&display_q, (const struct vframe_s *)vf); + + frame_count++; + vf_notify_receiver(PROVIDER_NAME, + VFRAME_EVENT_PROVIDER_VFRAME_READY, NULL); + WRITE_VREG(FROM_AMRISC, 0); + } + + WRITE_VREG(ASSIST_MBOX1_CLR_REG, 1); + + return IRQ_HANDLED; +} + +static struct vframe_s *vreal_vf_peek(void *op_arg) +{ + struct vframe_s *vf; + + if (kfifo_peek(&display_q, &vf)) + return vf; + + return NULL; +} + +static struct vframe_s *vreal_vf_get(void *op_arg) +{ + struct vframe_s *vf; + + if (kfifo_get(&display_q, &vf)) + return vf; + + return NULL; +} + +static void vreal_vf_put(struct vframe_s *vf, void *op_arg) +{ + kfifo_put(&recycle_q, (const struct vframe_s *)vf); +} + +static int vreal_event_cb(int type, void *data, void *private_data) +{ + if (type & VFRAME_EVENT_RECEIVER_RESET) { + unsigned long flags; + amvdec_stop(); +#ifndef CONFIG_AMLOGIC_POST_PROCESS_MANAGER + vf_light_unreg_provider(&vreal_vf_prov); +#endif + spin_lock_irqsave(&lock, flags); + vreal_local_init(); + vreal_prot_init(); + spin_unlock_irqrestore(&lock, flags); +#ifndef CONFIG_AMLOGIC_POST_PROCESS_MANAGER + vf_reg_provider(&vreal_vf_prov); +#endif + amvdec_start(); + } + return 0; +} + +static int vreal_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); + states->buf_recycle_num = kfifo_len(&recycle_q); + + spin_unlock_irqrestore(&lock, flags); + + return 0; +} +#if 0 +#ifdef CONFIG_AMLOGIC_POST_PROCESS_MANAGER +static void vreal_ppmgr_reset(void) +{ + vf_notify_receiver(PROVIDER_NAME, VFRAME_EVENT_PROVIDER_RESET, NULL); + + vreal_local_init(); + + pr_info("vrealdec: vf_ppmgr_reset\n"); +} +#endif +#endif +static void vreal_put_timer_func(unsigned long arg) +{ + struct timer_list *timer = (struct timer_list *)arg; + /* unsigned int status; */ + +#if 0 + enum receviver_start_e state = RECEIVER_INACTIVE; + 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; + + if ((READ_VREG(WAIT_BUFFER) != 0) && + kfifo_is_empty(&display_q) && + kfifo_is_empty(&recycle_q) && (state == RECEIVER_INACTIVE)) { + pr_info("$$$$$$decoder is waiting for buffer\n"); + if (++wait_buffer_counter > 2) { + amvdec_stop(); + +#ifdef CONFIG_AMLOGIC_POST_PROCESS_MANAGER + vreal_ppmgr_reset(); +#else + vf_light_unreg_provider(&vreal_vf_prov); + vreal_local_init(); + vf_reg_provider(&vreal_vf_prov); +#endif + vreal_prot_init(); + amvdec_start(); + } + } +#endif + + while (!kfifo_is_empty(&recycle_q) && (READ_VREG(TO_AMRISC) == 0)) { + struct vframe_s *vf; + if (kfifo_get(&recycle_q, &vf)) { + if ((vf->index >= 0) && (vf->index < VF_BUF_NUM) + && (--vfbuf_use[vf->index] == 0)) { + WRITE_VREG(TO_AMRISC, ~(1 << vf->index)); + vf->index = VF_BUF_NUM; + } + + kfifo_put(&newframe_q, (const struct vframe_s *)vf); + } + } + + if (frame_dur > 0 && + saved_resolution != + frame_width * frame_height * (96000 / frame_dur)) { + int fps = 96000 / frame_dur; + saved_resolution = frame_width * frame_height * fps; + vdec_source_changed(VFORMAT_REAL, + frame_width, frame_height, fps); + } + + timer->expires = jiffies + PUT_INTERVAL; + + add_timer(timer); +} + +int vreal_dec_status(struct vdec_s *vdec, struct vdec_status *vstatus) +{ + vstatus->width = vreal_amstream_dec_info.width; + vstatus->height = vreal_amstream_dec_info.height; + if (0 != vreal_amstream_dec_info.rate) + vstatus->fps = 96000 / vreal_amstream_dec_info.rate; + else + vstatus->fps = 96000; + vstatus->error_count = real_err_count; + vstatus->status = + ((READ_VREG(STATUS_AMRISC) << 16) | fatal_flag) | stat; + /* pr_info("vreal_dec_status 0x%x\n", vstatus->status); */ + return 0; +} + +/****************************************/ +static void vreal_canvas_init(void) +{ + int i; + u32 canvas_width, canvas_height; + u32 decbuf_size, decbuf_y_size, decbuf_uv_size; + u32 disp_addr = 0xffffffff; + u32 buff_off = 0; + if (buf_size <= 0x00400000) { + /* SD only */ + canvas_width = 768; + canvas_height = 576; + decbuf_y_size = 0x80000; + decbuf_uv_size = 0x20000; + decbuf_size = 0x100000; + } else { + /* HD & SD */ + #if 1 + int w = vreal_amstream_dec_info.width; + int h = vreal_amstream_dec_info.height; + int align_w, align_h; + int max, min; + align_w = ALIGN(w, 64); + align_h = ALIGN(h, 64); + if (align_w > align_h) { + max = align_w; + min = align_h; + } else { + canvas_width = 1920; + canvas_height = 1088; + max = align_h; + min = align_w; + } + /* HD & SD */ + if ((max > 1920 || min > 1088) && + ALIGN(align_w * align_h * 3/2, SZ_64K) * 9 <= + buf_size) { + canvas_width = align_w; + canvas_height = align_h; + decbuf_y_size = ALIGN(align_w * align_h, SZ_64K); + decbuf_uv_size = ALIGN(align_w * align_h/4, SZ_64K); + decbuf_size = ALIGN(align_w * align_h * 3/2, SZ_64K); + } else { /*1080p*/ + if (h > w) { + canvas_width = 1088; + canvas_height = 1920; + } else { + canvas_width = 1920; + canvas_height = 1088; + } + decbuf_y_size = 0x200000; + decbuf_uv_size = 0x80000; + decbuf_size = 0x300000; + } + #endif + /* canvas_width = 1920; + canvas_height = 1088; + decbuf_y_size = 0x200000; + decbuf_uv_size = 0x80000; + decbuf_size = 0x300000;*/ + } + + if (is_vpp_postblend()) { + struct canvas_s cur_canvas; + + canvas_read((READ_VCBUS_REG(VD1_IF0_CANVAS0) & 0xff), + &cur_canvas); + disp_addr = (cur_canvas.addr + 7) >> 3; + } + + for (i = 0; i < 4; i++) { + u32 one_buf_start = buf_start + buff_off; + if (((one_buf_start + 7) >> 3) == disp_addr) { + /*last disp buffer, to next..*/ + buff_off += decbuf_size; + one_buf_start = buf_start + buff_off; + pr_info("one_buf_start %d,=== %x disp_addr %x", + i, one_buf_start, disp_addr); + } + if (buff_off < 0x01000000 && + buff_off + decbuf_size > 0x0f00000){ + /*0x01b00000 is references buffer. + to next 16M;*/ + buff_off = 16 * SZ_1M;/*next 16M*/ + one_buf_start = buf_start + buff_off; + } + if (buff_off + decbuf_size > buf_size) { + pr_err("ERROR::too small buffer for buf%d %d x%d ,size =%d\n", + i, + canvas_width, + canvas_height, + buf_size); + } + pr_info("alloced buffer %d at %x,%d\n", + i, one_buf_start, decbuf_size); + #ifdef NV21 + canvas_config(2 * i + 0, + one_buf_start, + canvas_width, canvas_height, + CANVAS_ADDR_NOWRAP, CANVAS_BLKMODE_32X32); + canvas_config(2 * i + 1, + one_buf_start + + decbuf_y_size, canvas_width, + canvas_height / 2, CANVAS_ADDR_NOWRAP, + CANVAS_BLKMODE_32X32); + #else + canvas_config(3 * i + 0, + one_buf_start, + canvas_width, canvas_height, + CANVAS_ADDR_NOWRAP, CANVAS_BLKMODE_32X32); + canvas_config(3 * i + 1, + one_buf_start + + decbuf_y_size, canvas_width / 2, + canvas_height / 2, CANVAS_ADDR_NOWRAP, + CANVAS_BLKMODE_32X32); + canvas_config(3 * i + 2, + one_buf_start + + decbuf_y_size + decbuf_uv_size, + canvas_width / 2, canvas_height / 2, + CANVAS_ADDR_NOWRAP, CANVAS_BLKMODE_32X32); + #endif + buff_off = buff_off + decbuf_size; + } +} + +static void vreal_prot_init(void) +{ +#if 1 /* MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON6 */ + WRITE_VREG(DOS_SW_RESET0, (1 << 7) | (1 << 6)); + WRITE_VREG(DOS_SW_RESET0, 0); +#else + WRITE_MPEG_REG(RESET0_REGISTER, RESET_IQIDCT | RESET_MC); +#endif + + vreal_canvas_init(); + + /* index v << 16 | u << 8 | y */ +#ifdef NV21 + WRITE_VREG(AV_SCRATCH_0, 0x010100); + WRITE_VREG(AV_SCRATCH_1, 0x030302); + WRITE_VREG(AV_SCRATCH_2, 0x050504); + WRITE_VREG(AV_SCRATCH_3, 0x070706); +#else + WRITE_VREG(AV_SCRATCH_0, 0x020100); + WRITE_VREG(AV_SCRATCH_1, 0x050403); + WRITE_VREG(AV_SCRATCH_2, 0x080706); + WRITE_VREG(AV_SCRATCH_3, 0x0b0a09); +#endif + + /* notify ucode the buffer offset */ + WRITE_VREG(AV_SCRATCH_F, buf_offset); + +#if 1 /* MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON6 */ + WRITE_VREG(DOS_SW_RESET0, (1 << 9) | (1 << 8)); + WRITE_VREG(DOS_SW_RESET0, 0); +#else + WRITE_MPEG_REG(RESET2_REGISTER, RESET_PIC_DC | RESET_DBLK); +#endif + + /* disable PSCALE for hardware sharing */ + WRITE_VREG(PSCALE_CTRL, 0); + + WRITE_VREG(FROM_AMRISC, 0); + WRITE_VREG(TO_AMRISC, 0); + WRITE_VREG(STATUS_AMRISC, 0); + + WRITE_VREG(RV_PIC_INFO, 0); + WRITE_VREG(VPTS_TR, 0); + WRITE_VREG(VDTS, 0); + WRITE_VREG(SKIP_B_AMRISC, 0); + + WRITE_VREG(MDEC_WIDTH, (frame_width + 15) & 0xfff0); + WRITE_VREG(MDEC_HEIGHT, (frame_height + 15) & 0xfff0); + + /* clear mailbox interrupt */ + WRITE_VREG(ASSIST_MBOX1_CLR_REG, 1); + + /* enable mailbox interrupt */ + WRITE_VREG(ASSIST_MBOX1_MASK, 1); + + /* clear wait buffer status */ + WRITE_VREG(WAIT_BUFFER, 0); + +#ifdef NV21 + SET_VREG_MASK(MDEC_PIC_DC_CTRL, 1 << 17); +#endif +} + +static void vreal_local_init(void) +{ + int i; + + /* vreal_ratio = vreal_amstream_dec_info.ratio; */ + vreal_ratio = 0x100; + + frame_prog = 0; + + frame_width = vreal_amstream_dec_info.width; + frame_height = vreal_amstream_dec_info.height; + frame_dur = vreal_amstream_dec_info.rate; + + for (i = 0; i < VF_BUF_NUM; i++) + vfbuf_use[i] = 0; + + INIT_KFIFO(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; + kfifo_put(&newframe_q, vf); + } + + decoder_state = 1; + hold = 0; + last_tr = -1; + wait_key_frame = 1; + frame_count = 0; + current_vdts = 0; + real_err_count = 0; + + pic_sz_tbl_map = 0; + saved_resolution = 0; + fatal_flag = 0; + wait_buffer_counter = 0; +} + +static void load_block_data(void *dest, unsigned int count) +{ + unsigned short *pdest = (unsigned short *)dest; + unsigned short src_tbl[12]; + unsigned int i; + + src_tbl[0] = RPR_size[vreal_amstream_dec_info.extra + 1]; + memcpy((void *)&src_tbl[1], vreal_amstream_dec_info.param, + 2 << src_tbl[0]); + +#if 0 + for (i = 0; i < 12; i++) + pr_info("src_tbl[%d]: 0x%x\n", i, src_tbl[i]); +#endif + + for (i = 0; i < count / 4; i++) { + pdest[i * 4] = src_tbl[i * 4 + 3]; + pdest[i * 4 + 1] = src_tbl[i * 4 + 2]; + pdest[i * 4 + 2] = src_tbl[i * 4 + 1]; + pdest[i * 4 + 3] = src_tbl[i * 4]; + } + + pic_sz_tbl_map = dma_map_single(amports_get_dma_device(), &pic_sz_tbl, + sizeof(pic_sz_tbl), DMA_TO_DEVICE); + + return; +} + +s32 vreal_init(struct vdec_s *vdec) +{ + int ret = -1,size = -1; + char *buf = vmalloc(0x1000 * 16); + if (IS_ERR_OR_NULL(buf)) + return -ENOMEM; + + pr_info("vreal_init\n"); + + init_timer(&recycle_timer); + + stat |= STAT_TIMER_INIT; + + amvdec_enable(); + + vreal_local_init(); + + ret = rmparser_init(vdec); + if (ret) { + amvdec_disable(); + + pr_info("rm parser init failed\n"); + return ret; + } + + if (vreal_amstream_dec_info.format == VIDEO_DEC_FORMAT_REAL_8) { + load_block_data((void *)pic_sz_tbl, 12); + + /* TODO: need to load the table into lmem */ + WRITE_VREG(LMEM_DMA_ADR, (unsigned)pic_sz_tbl_map); + WRITE_VREG(LMEM_DMA_COUNT, 10); + WRITE_VREG(LMEM_DMA_CTRL, 0xc178 | (3 << 11)); + while (READ_VREG(LMEM_DMA_CTRL) & 0x8000); + size = get_firmware_data(VIDEO_DEC_REAL_V8, buf); + + pr_info("load VIDEO_DEC_FORMAT_REAL_8\n"); + } else if (vreal_amstream_dec_info.format == VIDEO_DEC_FORMAT_REAL_9) { + size = get_firmware_data(VIDEO_DEC_REAL_V9, buf); + + pr_info("load VIDEO_DEC_FORMAT_REAL_9\n"); + } else + pr_info("unsurpported real format\n"); + + if (size < 0) { + pr_err("get firmware fail."); + vfree(buf); + return -1; + } + + if (amvdec_loadmc_ex(VFORMAT_REAL, NULL, buf) < 0) { + amvdec_disable(); + vfree(buf); + return -EBUSY; + } + + vfree(buf); + + stat |= STAT_MC_LOAD; + + /* enable AMRISC side protocol */ + vreal_prot_init(); + + if (vdec_request_irq(VDEC_IRQ_1, vreal_isr, + "vreal-irq", (void *)vreal_dec_id)) { + amvdec_disable(); + + pr_info("vreal irq register error.\n"); + return -ENOENT; + } + + stat |= STAT_ISR_REG; +#ifdef CONFIG_AMLOGIC_POST_PROCESS_MANAGER + vf_provider_init(&vreal_vf_prov, PROVIDER_NAME, &vreal_vf_provider, + NULL); + vf_reg_provider(&vreal_vf_prov); + vf_notify_receiver(PROVIDER_NAME, VFRAME_EVENT_PROVIDER_START, NULL); +#else + vf_provider_init(&vreal_vf_prov, PROVIDER_NAME, &vreal_vf_provider, + NULL); + vf_reg_provider(&vreal_vf_prov); +#endif + + vf_notify_receiver(PROVIDER_NAME, VFRAME_EVENT_PROVIDER_FR_HINT, + (void *)((unsigned long)vreal_amstream_dec_info.rate)); + + stat |= STAT_VF_HOOK; + + recycle_timer.data = (ulong)&recycle_timer; + recycle_timer.function = vreal_put_timer_func; + recycle_timer.expires = jiffies + PUT_INTERVAL; + + add_timer(&recycle_timer); + + stat |= STAT_TIMER_ARM; + + amvdec_start(); + + stat |= STAT_VDEC_RUN; + + pr_info("vreal init finished\n"); + + return 0; +} + +void vreal_set_fatal_flag(int flag) +{ + if (flag) + fatal_flag = PARSER_FATAL_ERROR; +} + +/*TODO encoder*/ +/* extern void AbortEncodeWithVdec2(int abort); */ + +static int amvdec_real_probe(struct platform_device *pdev) +{ + struct vdec_s *pdata = *(struct vdec_s **)pdev->dev.platform_data; + + if (pdata == NULL) { + pr_info("amvdec_real memory resource undefined.\n"); + return -EFAULT; + } + + buf_start = pdata->mem_start; + buf_size = pdata->mem_end - pdata->mem_start + 1; + buf_offset = buf_start - RM_DEF_BUFFER_ADDR; + + if (pdata->sys_info) + vreal_amstream_dec_info = *pdata->sys_info; + /* #if (MESON_CPU_TYPE == MESON_CPU_TYPE_MESON8)&&(HAS_HDEC)) */ + /* if(IS_MESON_M8_CPU){ */ + if (has_hdec()) { + /* disable vdec2 dblk when miracast. */ + int count = 0; + if (get_vdec2_usage() != USAGE_NONE) + /*TODO encoder */ + /* AbortEncodeWithVdec2(1); */ + while ((get_vdec2_usage() != USAGE_NONE) + && (count < 10)) { + msleep(50); + count++; + } + + if (get_vdec2_usage() != USAGE_NONE) { + pr_info("\namvdec_real_probe --- stop vdec2 fail.\n"); + return -EBUSY; + } + } + /* } */ + /* #endif */ + + pdata->dec_status = vreal_dec_status; + + if (vreal_init(pdata) < 0) { + pr_info("amvdec_real init failed.\n"); + /* #if (MESON_CPU_TYPE == MESON_CPU_TYPE_MESON8)&&(HAS_HDEC) */ + /* if(IS_MESON_M8_CPU) */ + if (has_hdec()) { + /*TODO encoder */ + /* AbortEncodeWithVdec2(0); */ + } + /* #endif */ + return -ENODEV; + } + + return 0; +} + +static int amvdec_real_remove(struct platform_device *pdev) +{ + if (stat & STAT_VDEC_RUN) { + amvdec_stop(); + stat &= ~STAT_VDEC_RUN; + } + + if (stat & STAT_ISR_REG) { + vdec_free_irq(VDEC_IRQ_1, (void *)vreal_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) { + vf_notify_receiver(PROVIDER_NAME, + VFRAME_EVENT_PROVIDER_FR_END_HINT, NULL); + + vf_unreg_provider(&vreal_vf_prov); + stat &= ~STAT_VF_HOOK; + } + + if (pic_sz_tbl_map != 0) { + dma_unmap_single(NULL, pic_sz_tbl_map, sizeof(pic_sz_tbl), + DMA_TO_DEVICE); + } + + rmparser_release(); + + vdec_source_changed(VFORMAT_REAL, 0, 0, 0); + + amvdec_disable(); + + /* #if (MESON_CPU_TYPE == MESON_CPU_TYPE_MESON8)&&(HAS_HDEC) */ + /* if(IS_MESON_M8_CPU) */ + if (has_hdec()) { + /*TODO encoder */ + /* AbortEncodeWithVdec2(0); */ + } + /* #endif */ + pr_info("frame duration %d, frames %d\n", frame_dur, frame_count); + return 0; +} + +/****************************************/ + +static struct platform_driver amvdec_real_driver = { + .probe = amvdec_real_probe, + .remove = amvdec_real_remove, +#ifdef CONFIG_PM + .suspend = amvdec_suspend, + .resume = amvdec_resume, +#endif + .driver = { + .name = DRIVER_NAME, + } +}; + +static struct codec_profile_t amvdec_real_profile = { + .name = "real", + .profile = "rmvb,1080p+" +}; + +static int __init amvdec_real_driver_init_module(void) +{ + pr_debug("amvdec_real module init\n"); + + if (platform_driver_register(&amvdec_real_driver)) { + pr_err("failed to register amvdec_real driver\n"); + return -ENODEV; + } + vcodec_profile_register(&amvdec_real_profile); + return 0; +} + +static void __exit amvdec_real_driver_remove_module(void) +{ + pr_debug("amvdec_real module remove.\n"); + + platform_driver_unregister(&amvdec_real_driver); +} + +/****************************************/ + +module_param(stat, uint, 0664); +MODULE_PARM_DESC(stat, "\n amvdec_real stat\n"); + +module_init(amvdec_real_driver_init_module); +module_exit(amvdec_real_driver_remove_module); + +MODULE_DESCRIPTION("AMLOGIC REAL Video Decoder Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/frame_provider/decoder/real/vreal.h b/drivers/frame_provider/decoder/real/vreal.h new file mode 100644 index 0000000..8c0d51a --- a/dev/null +++ b/drivers/frame_provider/decoder/real/vreal.h @@ -0,0 +1,26 @@ +/* + * drivers/amlogic/amports/vreal.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 VREAL_H +#define VREAL_H + +#if 1 /* MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON6 */ +/* TODO: move to register headers */ +#define VPP_VD1_POSTBLEND (1 << 10) +#endif + +#endif /* VREAL_H */ diff --git a/drivers/frame_provider/decoder/utils/Makefile b/drivers/frame_provider/decoder/utils/Makefile new file mode 100644 index 0000000..b7e6184 --- a/dev/null +++ b/drivers/frame_provider/decoder/utils/Makefile @@ -0,0 +1,4 @@ +obj-m += decoder_common.o +decoder_common-objs += utils.o vdec.o vdec_input.o amvdec.o +decoder_common-objs += decoder_mmu_box.o decoder_bmmu_box.o +decoder_common-objs += config_parser.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..a5e1462 --- a/dev/null +++ b/drivers/frame_provider/decoder/utils/amvdec.c @@ -0,0 +1,998 @@ +/* + * 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 * 16) + +#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_vdec_loadmc_ex(struct vdec_s *vdec, + const char *name, s32(*load)(const u32 *)) +{ + int err; + + if (!vdec->mc_loaded) { + int loaded; + loaded = get_decoder_firmware_data(vdec->format, + name, (u8 *)(vdec->mc), (4096 * 4 * 4)); + if (loaded <= 0) + return -1; + + vdec->mc_loaded = true; + } + + err = (*load)(vdec->mc); + if (err < 0) { + pr_err("loading firmware %s to vdec ram failed!\n", name); + return err; + } + pr_debug("loading firmware %s to vdec ram ok!\n", name); + return err; +} + +static s32 am_vdec_loadmc_buf_ex(struct vdec_s *vdec, + char *buf, int size, s32(*load)(const u32 *)) +{ + int err; + + if (!vdec->mc_loaded) { + memcpy((u8 *)(vdec->mc), buf, size); + vdec->mc_loaded = true; + } + + err = (*load)(vdec->mc); + if (err < 0) { + pr_err("loading firmware to vdec ram failed!\n"); + return err; + } + pr_debug("loading firmware to vdec ram ok!\n"); + return err; +} + +static s32 am_loadmc_ex(enum vformat_e type, + const char *name, char *def, s32(*load)(const u32 *)) +{ + char *mc_addr = vmalloc(4096 * 16); + char *pmc_addr = def; + int err; + + if (!def && mc_addr) { + int loaded; + + loaded = get_decoder_firmware_data(type, + name, mc_addr, (4096 * 16)); + 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); +s32 amvdec_vdec_loadmc_ex(struct vdec_s *vdec, const char *name) +{ + return am_vdec_loadmc_ex(vdec, name, &amvdec_loadmc); +} +EXPORT_SYMBOL(amvdec_vdec_loadmc_ex); + +s32 amvdec_vdec_loadmc_buf_ex(struct vdec_s *vdec, char *buf, int size) +{ + return am_vdec_loadmc_buf_ex(vdec, buf, size, &amvdec_loadmc); +} +EXPORT_SYMBOL(amvdec_vdec_loadmc_buf_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); +s32 amhevc_vdec_loadmc_ex(struct vdec_s *vdec, const char *name) +{ + if (has_hevc_vdec()) + return am_vdec_loadmc_ex(vdec, name, &amhevc_loadmc); + else + return 0; +} +EXPORT_SYMBOL(amhevc_vdec_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..c6f11d7 --- a/dev/null +++ b/drivers/frame_provider/decoder/utils/amvdec.h @@ -0,0 +1,86 @@ +/* + * 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> +#include "vdec.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); +s32 amvdec_vdec_loadmc_ex(struct vdec_s *vdec, const char *name); + +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); +s32 amhevc_vdec_loadmc_ex(struct vdec_s *vdec, const char *name); +s32 amvdec_vdec_loadmc_buf_ex(struct vdec_s *vdec, char *buf, int size); + +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/config_parser.c b/drivers/frame_provider/decoder/utils/config_parser.c new file mode 100644 index 0000000..b9c64f7 --- a/dev/null +++ b/drivers/frame_provider/decoder/utils/config_parser.c @@ -0,0 +1,62 @@ +/* + * drivers/amlogic/amports/config_parser.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 "config_parser.h" +/* +sample config: +configs: width:1920;height:1080; +need:width +ok: return 0; +*val = value; +*/ +int get_config_int(const char *configs, const char *need, int *val) +{ + const char *str; + int ret; + int lval = 0; + *val = 0; + + if (!configs || !need) + return -1; + str = strstr(configs, need); + if (str != NULL) { + if (str > configs && str[-1] != ';') { + /* + if not the first config val. + make sure before is ';' + to recognize: + ;crop_width:100 + ;width:100 + */ + return -2; + } + str += strlen(need); + if (str[0] != ':' || str[1] == '\0') + return -3; + ret = sscanf(str, ":%d", &lval); + if (ret == 1) { + *val = lval; + return 0; + } + } + + return -4; +} diff --git a/drivers/frame_provider/decoder/utils/config_parser.h b/drivers/frame_provider/decoder/utils/config_parser.h new file mode 100644 index 0000000..e10210a --- a/dev/null +++ b/drivers/frame_provider/decoder/utils/config_parser.h @@ -0,0 +1,21 @@ +/* + * drivers/amlogic/amports/config_parser.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. + * +*/ +#ifndef CONFIG_PARSER_HHH_ +#define CONFIG_PARSER_HHH_ +int get_config_int(const char *configs, const char *need, int *val); + +#endif/*CONFIG_PARSER_HHH_*/ diff --git a/drivers/frame_provider/decoder/utils/decoder_bmmu_box.c b/drivers/frame_provider/decoder/utils/decoder_bmmu_box.c new file mode 100644 index 0000000..7a858d5 --- a/dev/null +++ b/drivers/frame_provider/decoder/utils/decoder_bmmu_box.c @@ -0,0 +1,425 @@ +/* + * drivers/amlogic/amports/decoder/decoder_bmmu_box.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/slab.h> +#include <linux/amlogic/media/codec_mm/codec_mm_scatter.h> +#include <linux/platform_device.h> + +#include <linux/amlogic/media/video_sink/video_keeper.h> +#include "decoder_bmmu_box.h" +#include <linux/amlogic/media/codec_mm/codec_mm.h> + +struct decoder_bmmu_box { + int max_mm_num; + const char *name; + int channel_id; + struct mutex mutex; + struct list_head list; + int total_size; + int change_size_on_need_smaller; + int align2n; /*can overwite on idx alloc */ + int mem_flags; /*can overwite on idx alloc */ + struct codec_mm_s *mm_list[1]; +}; + +struct decoder_bmmu_box_mgr { + int num; + struct mutex mutex; + struct list_head box_list; +}; +static struct decoder_bmmu_box_mgr global_blk_mgr; +static struct decoder_bmmu_box_mgr *get_decoder_bmmu_box_mgr(void) +{ + return &global_blk_mgr; +} + +static int decoder_bmmu_box_mgr_add_box(struct decoder_bmmu_box *box) +{ + struct decoder_bmmu_box_mgr *mgr = get_decoder_bmmu_box_mgr(); + mutex_lock(&mgr->mutex); + list_add_tail(&box->list, &mgr->box_list); + mutex_unlock(&mgr->mutex); + return 0; +} + +static int decoder_bmmu_box_mgr_del_box(struct decoder_bmmu_box *box) +{ + struct decoder_bmmu_box_mgr *mgr = get_decoder_bmmu_box_mgr(); + mutex_lock(&mgr->mutex); + list_del(&box->list); + mutex_unlock(&mgr->mutex); + return 0; +} + +void *decoder_bmmu_box_alloc_box(const char *name, + int channel_id, int max_num, + int aligned, int mem_flags) +/*min_size_M:wait alloc this size*/ +{ + struct decoder_bmmu_box *box; + int size; + size = sizeof(struct decoder_bmmu_box) + sizeof(struct codec_mm_s *) * + 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_mm_num = max_num; + box->name = name; + box->channel_id = channel_id; + box->align2n = aligned; + box->mem_flags = mem_flags; + mutex_init(&box->mutex); + INIT_LIST_HEAD(&box->list); + decoder_bmmu_box_mgr_add_box(box); + return (void *)box; +} +EXPORT_SYMBOL(decoder_bmmu_box_alloc_box); + +int decoder_bmmu_box_alloc_idx(void *handle, int idx, int size, int aligned_2n, + int mem_flags) +/*align& flags if -1 user box default.*/ +{ + struct decoder_bmmu_box *box = handle; + struct codec_mm_s *mm; + int align = aligned_2n; + int memflags = mem_flags; + + if (!box || idx < 0 || idx >= box->max_mm_num) { + pr_err("can't alloc mmu box(%p),idx:%d\n", + box, idx); + return -1; + } + if (align == -1) + align = box->align2n; + if (memflags == -1) + memflags = box->mem_flags; + + mutex_lock(&box->mutex); + mm = box->mm_list[idx]; + if (mm) { + int invalid = 0; + if (mm->page_count * PAGE_SIZE < size) { + /*size is small. */ + invalid = 1; + } else if (box->change_size_on_need_smaller && + (mm->buffer_size > (size << 1))) { + /*size is too large. */ + invalid = 2; + } else if (mm->phy_addr & ((1 << align) - 1)) { + /*addr is not align */ + invalid = 4; + } + if (invalid) { + box->total_size -= mm->buffer_size; + codec_mm_release(mm, box->name); + box->mm_list[idx] = NULL; + mm = NULL; + } + } + if (!mm) { + mm = codec_mm_alloc(box->name, size, align, memflags); + if (mm) { + box->mm_list[idx] = mm; + box->total_size += mm->buffer_size; + } + } + mutex_unlock(&box->mutex); + return mm ? 0 : -ENOMEM; +} + +int decoder_bmmu_box_free_idx(void *handle, int idx) +{ + struct decoder_bmmu_box *box = handle; + struct codec_mm_s *mm; + if (!box || idx < 0 || idx >= box->max_mm_num) { + pr_err("can't free idx of box(%p),idx:%d in (%d-%d)\n", + box, idx, 0, + box->max_mm_num - 1); + return -1; + } + mutex_lock(&box->mutex); + mm = box->mm_list[idx]; + if (mm) { + box->total_size -= mm->buffer_size; + codec_mm_release(mm, box->name); + box->mm_list[idx] = NULL; + mm = NULL; + } + mutex_unlock(&box->mutex); + return 0; +} +EXPORT_SYMBOL(decoder_bmmu_box_free_idx); + +int decoder_bmmu_box_free(void *handle) +{ + struct decoder_bmmu_box *box = handle; + struct codec_mm_s *mm; + 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_mm_num; i++) { + mm = box->mm_list[i]; + if (mm) { + codec_mm_release(mm, box->name); + box->mm_list[i] = NULL; + } + } + mutex_unlock(&box->mutex); + decoder_bmmu_box_mgr_del_box(box); + kfree(box); + return 0; +} +EXPORT_SYMBOL(decoder_bmmu_box_free); + +void *decoder_bmmu_box_get_mem_handle(void *box_handle, int idx) +{ + struct decoder_bmmu_box *box = box_handle; + if (!box || idx < 0 || idx >= box->max_mm_num) + return NULL; + return box->mm_list[idx]; +} +EXPORT_SYMBOL(decoder_bmmu_box_get_mem_handle); + +int decoder_bmmu_box_get_mem_size(void *box_handle, int idx) +{ + struct decoder_bmmu_box *box = box_handle; + int size = 0; + if (!box || idx < 0 || idx >= box->max_mm_num) + return 0; + mutex_lock(&box->mutex); + if (box->mm_list[idx] != NULL) + size = box->mm_list[idx]->buffer_size; + mutex_unlock(&box->mutex); + return size; +} + + +unsigned long decoder_bmmu_box_get_phy_addr(void *box_handle, int idx) +{ + struct decoder_bmmu_box *box = box_handle; + struct codec_mm_s *mm; + if (!box || idx < 0 || idx >= box->max_mm_num) + return 0; + mm = box->mm_list[idx]; + if (!mm) + return 0; + return mm->phy_addr; +} +EXPORT_SYMBOL(decoder_bmmu_box_get_phy_addr); + +void *decoder_bmmu_box_get_virt_addr(void *box_handle, int idx) +{ + struct decoder_bmmu_box *box = box_handle; + struct codec_mm_s *mm; + if (!box || idx < 0 || idx >= box->max_mm_num) + return NULL; + mm = box->mm_list[idx]; + if (!mm) + return 0; + return codec_mm_phys_to_virt(mm->phy_addr); +} + +/*flags: &0x1 for wait,*/ +int decoder_bmmu_box_check_and_wait_size(int size, int flags) +{ + if ((flags & BMMU_ALLOC_FLAGS_CAN_CLEAR_KEEPER) && + codec_mm_get_free_size() < size) { + pr_err("CMA force free keep,for size = %d\n", size); + /*need free others? + */ + try_free_keep_video(1); + } + + return codec_mm_enough_for_size(size, + flags & BMMU_ALLOC_FLAGS_WAIT); +} + +int decoder_bmmu_box_alloc_idx_wait( + void *handle, int idx, + int size, int aligned_2n, + int mem_flags, + int wait_flags) +{ + int have_space; + int ret = -1; + if (decoder_bmmu_box_get_mem_size(handle, idx) >= size) + return 0;/*have alloced memery before.*/ + have_space = decoder_bmmu_box_check_and_wait_size( + size, + wait_flags); + if (have_space) { + ret = decoder_bmmu_box_alloc_idx(handle, + idx, size, aligned_2n, mem_flags); + } else { + ret = -ENOMEM; + } + return ret; +} +EXPORT_SYMBOL(decoder_bmmu_box_alloc_idx_wait); + +static int decoder_bmmu_box_dump(struct decoder_bmmu_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_mm_num; i++) { + struct codec_mm_s *mm = box->mm_list[i]; + if (buf && (size - tsize) < 128) + break; + if (mm) { + BUFPRINT("code mem[%d]:%p, addr=%p, size=%d,from=%d\n", + i, + (void *)mm, + (void *)mm->phy_addr, + mm->buffer_size, + mm->from_flags); + } + } +#undef BUFPRINT + if (!buf) + pr_info("%s", sbuf); + + return tsize; +} + +static int decoder_bmmu_box_dump_all(void *buf, int size) +{ + struct decoder_bmmu_box_mgr *mgr = get_decoder_bmmu_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_bmmu_box *box; + box = list_entry(list, struct decoder_bmmu_box, list); + BUFPRINT("box[%d]: %s, player_id:%d, max_num:%d, size:%d\n", + i, box->name, + box->channel_id, + box->max_mm_num, + box->total_size); + if (buf) { + tsize += decoder_bmmu_box_dump(box, pbuf, size - tsize); + if (tsize > 0) + pbuf += tsize; + } else { + pr_info("%s", sbuf); + pbuf = sbuf; + tsize += decoder_bmmu_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_bmmu_box_dump_all(buf, PAGE_SIZE); + return ret; +} + +static struct class_attribute decoder_bmmu_box_class_attrs[] = { + __ATTR_RO(box_dump), + __ATTR_NULL +}; + +static struct class decoder_bmmu_box_class = { + .name = "decoder_bmmu_box", + .class_attrs = decoder_bmmu_box_class_attrs, + }; + +int decoder_bmmu_box_init(void) +{ + int r; + memset(&global_blk_mgr, 0, sizeof(global_blk_mgr)); + INIT_LIST_HEAD(&global_blk_mgr.box_list); + mutex_init(&global_blk_mgr.mutex); + r = class_register(&decoder_bmmu_box_class); + return r; +} +EXPORT_SYMBOL(decoder_bmmu_box_init); + +void decoder_bmmu_box_exit(void) +{ + class_unregister(&decoder_bmmu_box_class); + pr_info("dec bmmu box exit.\n"); +} + +#if 0 +static int __init decoder_bmmu_box_init(void) +{ + int r; + memset(&global_blk_mgr, 0, sizeof(global_blk_mgr)); + INIT_LIST_HEAD(&global_blk_mgr.box_list); + mutex_init(&global_blk_mgr.mutex); + r = class_register(&decoder_bmmu_box_class); + return r; +} + +module_init(decoder_bmmu_box_init); +#endif diff --git a/drivers/frame_provider/decoder/utils/decoder_bmmu_box.h b/drivers/frame_provider/decoder/utils/decoder_bmmu_box.h new file mode 100644 index 0000000..99aa89b --- a/dev/null +++ b/drivers/frame_provider/decoder/utils/decoder_bmmu_box.h @@ -0,0 +1,63 @@ +/* + * drivers/amlogic/amports/decoder/decoder_bmmu_box.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 DECODER_BLOCK_BUFFER_BOX +#define DECODER_BLOCK_BUFFER_BOX + +void *decoder_bmmu_box_alloc_box(const char *name, + int channel_id, + int max_num, + int aligned, + int mem_flags); + +int decoder_bmmu_box_alloc_idx( + void *handle, int idx, int size, + int aligned_2n, int mem_flags); + +int decoder_bmmu_box_free_idx(void *handle, int idx); +int decoder_bmmu_box_free(void *handle); +void *decoder_bmmu_box_get_mem_handle( + void *box_handle, int idx); + +unsigned long decoder_bmmu_box_get_phy_addr( + void *box_handle, int idx); + +void *decoder_bmmu_box_get_virt_addr( + void *box_handle, int idx); + +/*flags: &0x1 for wait,*/ +int decoder_bmmu_box_check_and_wait_size( + int size, int flags); + +#define BMMU_ALLOC_FLAGS_WAIT (1 << 0) +#define BMMU_ALLOC_FLAGS_CAN_CLEAR_KEEPER (1 << 1) +#define BMMU_ALLOC_FLAGS_WAITCLEAR \ + (BMMU_ALLOC_FLAGS_WAIT |\ + BMMU_ALLOC_FLAGS_CAN_CLEAR_KEEPER) + +int decoder_bmmu_box_alloc_idx_wait( + void *handle, int idx, + int size, int aligned_2n, + int mem_flags, + int wait_flags); + +int decoder_bmmu_box_init(void); +void decoder_bmmu_box_exit(void); + +#endif /* + */ + 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..26440fe --- a/dev/null +++ b/drivers/frame_provider/decoder/utils/decoder_mmu_box.c @@ -0,0 +1,383 @@ +/* + * 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, + int min_size_M) +/*min_size_M:wait alloc this size*/ +{ + 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, 2000, + min_size_M); + 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\n", + box, idx); + 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, 0); + 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); + +void decoder_mmu_box_exit(void) +{ + class_unregister(&decoder_mmu_box_class); + pr_info("dec mmu box exit.\n"); +} + +#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..387dd24 --- a/dev/null +++ b/drivers/frame_provider/decoder/utils/decoder_mmu_box.h @@ -0,0 +1,45 @@ +/* + * 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 channel_id, + int max_num, + int min_size_M); + +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); +void decoder_mmu_box_exit(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..33ab765 --- a/dev/null +++ b/drivers/frame_provider/decoder/utils/utils.c @@ -0,0 +1,66 @@ +/* + * 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" +#include "decoder_bmmu_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?*/ + decoder_bmmu_box_init(); + + return 0; +} + +static void __exit decoder_common_exit(void) +{ + /*vdec exit.*/ + vdec_module_exit(); + + /*amvdec exit.*/ + amvdec_exit(); + + decoder_mmu_box_exit(); + decoder_bmmu_box_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..7c54882 --- a/dev/null +++ b/drivers/frame_provider/decoder/utils/vdec.c @@ -0,0 +1,2907 @@ +/* + * 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/ionvideo_ext.h> +#include <linux/amlogic/media/vfm/vfm_ext.h> + +#include <linux/amlogic/media/utils/vdec_reg.h> +#include "vdec.h" +#ifdef CONFIG_MULTI_DEC +#include "vdec_profile.h" +#endif +#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 "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> +#include <linux/amlogic/media/video_sink/video_keeper.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 int step_mode; +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]; + int power_ref_count[VDEC_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) +{ + int r; + + if (vdec->set_trickmode) { + r = vdec->set_trickmode(vdec, trickmode); + + if ((r == 0) && (vdec->slave) && (vdec->slave->set_trickmode)) + r = vdec->slave->set_trickmode(vdec->slave, + 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;//DEBUG_TMP +} + +#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", "ammvdec_vp9" +}; + +static int vdec_default_buf_size[] = { + 32, 32, /*"amvdec_mpeg12",*/ + 32, 0, /*"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(struct stream_port_s *port, + struct vdec_s *master) +{ + struct vdec_s *vdec; + 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; + + vdec = vzalloc(sizeof(struct vdec_s)); + + /* TBD */ + if (vdec) { + vdec->magic = 0x43454456; + vdec->id = 0; + vdec->type = type; + vdec->port = port; + vdec->sys_info = &vdec->sys_info_store; + + INIT_LIST_HEAD(&vdec->list); + + vdec_input_init(&vdec->input, vdec); + + atomic_inc(&vdec_core->vdec_nr); + + if (master) { + vdec->master = master; + master->slave = vdec; + master->sched = 1; + } + } + + 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; + + if (vdec->slave) + vdec->slave->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); + +int vdec_set_video_path(struct vdec_s *vdec, int video_path) +{ + vdec->frame_base_video_path = video_path; + return 0; +} +EXPORT_SYMBOL(vdec_set_video_path); + +/* 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->master) ? + &vdec->master->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) { +#if 0 + /*move to driver*/ + if (input_frame_based(input)) + WRITE_VREG(HEVC_STREAM_CONTROL, 0); + + /* + * 2: assist + * 3: parser + * 4: parser_state + * 8: dblk + * 11:mcpu + * 12:ccpu + * 13:ddr + * 14:iqit + * 15:ipp + * 17:qdct + * 18:mpred + * 19:sao + * 24:hevc_afifo + */ + 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); +#endif + } + + /* + *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)); + + /* set endian */ + SET_VREG_MASK(HEVC_STREAM_CONTROL, 7 << 4); + } + + *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) { + SET_VREG_MASK(HEVC_STREAM_CONTROL, 1); + + /* 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); + if (vdec_stream_based(vdec)) + CLEAR_VREG_MASK(HEVC_STREAM_CONTROL, 7 << 4); + else + SET_VREG_MASK(HEVC_STREAM_CONTROL, 7 << 4); + SET_VREG_MASK(HEVC_STREAM_FIFO_CTL, (1<<29)); + } +} +EXPORT_SYMBOL(vdec_enable_input); + +void vdec_set_flag(struct vdec_s *vdec, u32 flag) +{ + vdec->flag = flag; +} + +void vdec_set_next_sched(struct vdec_s *vdec, struct vdec_s *next_vdec) +{ + if (vdec && next_vdec) { + vdec->sched = 0; + next_vdec->sched = 1; + } +} +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)) { + if (vdec->slave && + ((vdec->slave->flag & + VDEC_FLAG_INPUT_KEEP_CONTEXT) == 0)) { + vdec->input.swap_needed = false; + } else + 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->master) ? + &vdec->master->input : &vdec->input; + +#ifdef CONFIG_MULTI_DEC + vdec_profile(vdec, VDEC_PROFILE_EVENT_SAVE_INPUT); +#endif + + if (input->target == VDEC_INPUT_TARGET_VLD) + WRITE_VREG(VLD_MEM_VIFIFO_CONTROL, 1<<15); + + 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; + + if (input->target == VDEC_INPUT_TARGET_VLD) + WRITE_MPEG_REG(PARSER_VIDEO_RP, + READ_VREG(VLD_MEM_VIFIFO_RP)); + else + WRITE_MPEG_REG(PARSER_VIDEO_RP, + READ_VREG(HEVC_STREAM_RD_PTR)); + } +} +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); + +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"; + } +} + +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"; + } +} + +const char *vdec_device_name_str(struct vdec_s *vdec) +{ + return vdec_device_name[vdec->format * 2 + 1]; +} + +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); + + if (vdec->slave) { + vdec_set_status(vdec->slave, VDEC_STATUS_CONNECTED); + vdec_set_next_status(vdec->slave, VDEC_STATUS_CONNECTED); + + init_completion(&vdec->slave->inactive_done); + } + + flags = vdec_core_lock(vdec_core); + + list_add_tail(&vdec->list, &vdec_core->connected_vdec_list); + + if (vdec->slave) { + list_add_tail(&vdec->slave->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) +{ +#ifdef CONFIG_MULTI_DEC + vdec_profile(vdec, VDEC_PROFILE_EVENT_DISCONNECT); +#endif + + 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); + + if (vdec->slave) + vdec_set_next_status(vdec->slave, VDEC_STATUS_DISCONNECTED); + else if (vdec->master) + vdec_set_next_status(vdec->master, VDEC_STATUS_DISCONNECTED); + + up(&vdec_core->sem); + + wait_for_completion(&vdec->inactive_done); + + if (vdec->slave) + wait_for_completion(&vdec->slave->inactive_done); + else if (vdec->master) + wait_for_completion(&vdec->master->inactive_done); + + return 0; +} +EXPORT_SYMBOL(vdec_disconnect); + +/* release vdec structure */ +int vdec_destroy(struct vdec_s *vdec) +{ + if (!vdec->master) + vdec_input_release(&vdec->input); + +#ifdef CONFIG_MULTI_DEC + vdec_profile_flush(vdec); +#endif + + vfree(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 +} + +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); +} + +/* +*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_single(vdec), 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) && !vdec_dual(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_single(vdec) && + ((vdec->format == VFORMAT_H264_4K2K) || + (vdec->format == VFORMAT_HEVC && is_4k))) { + try_free_keep_video(0); + } + + /* + *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_single(vdec) && + ((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); + 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 */ + if (!vdec_dual(vdec)) + p->use_vfm_path = vdec_stream_based(vdec); + + if (vdec_single(vdec)) { + 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) && (vdec_single(vdec))) { + int alloc_size; + +#ifdef CONFIG_MULTI_DEC + 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 + } + + if ((vdec->format == VFORMAT_H264) + && (get_cpu_type() >= MESON_CPU_MAJOR_ID_GXL) + && codec_mm_get_total_size() <= 80 * SZ_1M) { +#ifdef CONFIG_MULTI_DEC + if (p->use_vfm_path) + alloc_size = 32 * SZ_1M; + else + alloc_size = 32 * SZ_1M; +#else + alloc_size = 32 * 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); + 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 (vdec_single(vdec)) { + 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 (p->use_vfm_path) { + vdec->vf_receiver_inst = -1; + } else if (!vdec_dual(vdec)) { + /* 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 (p->frame_base_video_path == FRAME_BASE_PATH_IONVIDEO) { +#if 1 + //r = ionvideo_alloc_map(&vdec->vf_receiver_name, + //&vdec->vf_receiver_inst);//DEBUG_TMP +#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); + + } else if (p->frame_base_video_path == + FRAME_BASE_PATH_AMLVIDEO_AMVIDEO) { + snprintf(vdec->vfm_map_chain, VDEC_MAP_NAME_SIZE, + "%s %s", vdec->vf_provider_name, + "amlvideo.0 amvideo"); + snprintf(vdec->vfm_map_id, VDEC_MAP_NAME_SIZE, + "%s-%s", vdec->vf_provider_name, + "amlvideo.0 amvideo"); + } else if (p->frame_base_video_path == + FRAME_BASE_PATH_AMLVIDEO1_AMVIDEO2) { + snprintf(vdec->vfm_map_chain, VDEC_MAP_NAME_SIZE, + "%s %s", vdec->vf_provider_name, + "ppmgr amlvideo.1 amvide2"); + snprintf(vdec->vfm_map_id, VDEC_MAP_NAME_SIZE, + "%s-%s", vdec->vf_provider_name, + "ppmgr amlvideo.1 amvide2"); + } + + 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 + */ + + } + + if (!vdec_single(vdec)) { + 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 (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);//DEBUG_TMP + } + + 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()) { + 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->slave) && (vdec->slave->vframe_provider.name)) + vf_unreg_provider(&vdec->slave->vframe_provider); + + if (vdec->reset) { + vdec->reset(vdec); + if (vdec->slave) + vdec->slave->reset(vdec->slave); + } + + vdec_input_release(&vdec->input); + + vf_reg_provider(&vdec->vframe_provider); + vf_notify_receiver(vdec->vf_provider_name, + VFRAME_EVENT_PROVIDER_START, vdec); + + if (vdec->slave) { + vf_reg_provider(&vdec->slave->vframe_provider); + vf_notify_receiver(vdec->slave->vf_provider_name, + VFRAME_EVENT_PROVIDER_START, vdec->slave); + } + + vdec_connect(vdec); + + return 0; +} +EXPORT_SYMBOL(vdec_reset); + +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; + +#ifdef CONFIG_MULTI_DEC + vdec_profile(vdec, VDEC_PROFILE_EVENT_CB); +#endif + + 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) +{ + bool r; + + if (vdec->status != VDEC_STATUS_CONNECTED) + return false; + + if (!vdec->run_ready) + return false; + + if ((vdec->slave || vdec->master) && + (vdec->sched == 0)) + return false; + + if (step_mode) { + if ((step_mode & 0xff) != vdec->id) + return false; + } + + step_mode &= ~0xff; + +#ifdef CONFIG_MULTI_DEC + vdec_profile(vdec, VDEC_PROFILE_EVENT_CHK_RUN_READY); +#endif + + r = vdec->run_ready(vdec); + +#ifdef CONFIG_MULTI_DEC + if (r) + vdec_profile(vdec, VDEC_PROFILE_EVENT_RUN_READY); +#endif + + return r; +} + +/* 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, ¶m); + + 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; +#ifdef CONFIG_MULTI_DEC + vdec_profile(vdec, VDEC_PROFILE_EVENT_RUN); +#endif + 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; + + if (core >= VDEC_MAX) + return; + + mutex_lock(&vdec_mutex); + + vdec_core->power_ref_count[core]++; + if (vdec_core->power_ref_count[core] > 1) { + mutex_unlock(&vdec_mutex); + return; + } + + 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) +{ + if (core >= VDEC_MAX) + return; + + mutex_lock(&vdec_mutex); + + vdec_core->power_ref_count[core]--; + if (vdec_core->power_ref_count[core] > 0) { + mutex_unlock(&vdec_mutex); + return; + } + + 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,\ttype = %s\n", + vdec, vdec_device_name[vdec->format * 2], + vdec_status_str(vdec), + vdec_type_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_param(step_mode, int, 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..cb63f8d --- a/dev/null +++ b/drivers/frame_provider/decoder/utils/vdec.h @@ -0,0 +1,306 @@ +/* + * 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);//DEBUG_TMP + +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 + +#define VDEC_FLAG_INPUT_KEEP_CONTEXT 0x01 + +struct vdec_s { + u32 magic; + struct list_head list; + int id; + + struct vdec_s *master; + struct vdec_s *slave; + struct stream_port_s *port; + int status; + int next_status; + int type; + int port_flag; + int format; + u32 pts; + u64 pts64; + bool pts_valid; + int flag; + int sched; + + 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; + + /* mc cache */ + u32 mc[4096 * 4]; + bool mc_loaded; + + /* 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; + enum FRAME_BASE_VIDEO_PATH frame_base_video_path; + bool use_vfm_path; + char config[PAGE_SIZE]; + int config_len; + + /* 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 VFM_DEC_DVBL_PROVIDER_NAME "dvbldec" +#define VFM_DEC_DVEL_PROVIDER_NAME "dveldec" + +#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_single(vdec) \ + ((vdec)->type == VDEC_TYPE_SINGLE) +#define vdec_dual(vdec) \ + ((vdec)->port->type & PORT_TYPE_DUALDEC) + +/* construct vdec strcture */ +extern struct vdec_s *vdec_create(struct stream_port_s *port, + struct vdec_s *master); + +/* 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); + +/* set vfm map when use frame base decoder */ +extern int vdec_set_video_path(struct vdec_s *vdec, int video_path); + +/* 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); + +extern void vdec_set_flag(struct vdec_s *vdec, u32 flag); + +extern void vdec_set_next_sched(struct vdec_s *vdec, struct vdec_s *next_vdec); + +extern const char *vdec_status_str(struct vdec_s *vdec); + +extern const char *vdec_type_str(struct vdec_s *vdec); + +extern const char *vdec_device_name_str(struct vdec_s *vdec); + +#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..d6acac1 --- a/dev/null +++ b/drivers/frame_provider/decoder/utils/vdec_input.c @@ -0,0 +1,544 @@ +/* + * 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) +{ + 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; + } + + 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) { + pr_err("vframe_block structure allocation failed\n"); + 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); + } + + /* release swap page */ + if (input->swap_page) { + __free_page(input->swap_page); + input->swap_page = NULL; + input->swap_valid = false; + } +} +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..b029b89 --- 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/frame_provider/decoder/vc1/vvc1.c b/drivers/frame_provider/decoder/vc1/vvc1.c new file mode 100644 index 0000000..3495d92 --- a/dev/null +++ b/drivers/frame_provider/decoder/vc1/vvc1.c @@ -0,0 +1,1170 @@ +/* + * drivers/amlogic/amports/vvc1.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/types.h> +#include <linux/errno.h> +#include <linux/module.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/canvas/canvas.h> +#include <linux/amlogic/media/canvas/canvas_mgr.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/vdec_reg.h> +#include "../utils/amvdec.h" +#include "../utils/vdec.h" +#include <linux/amlogic/media/registers/register.h> +#include "../../../stream_input/amports/amports_priv.h" + +#define DRIVER_NAME "amvdec_vc1" +#define MODULE_NAME "amvdec_vc1" + +#define DEBUG_PTS +#if 1 /* //MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON6 */ +#define NV21 +#endif + +#define I_PICTURE 0 +#define P_PICTURE 1 +#define B_PICTURE 2 + +#define ORI_BUFFER_START_ADDR 0x01000000 + +#define INTERLACE_FLAG 0x80 +#define BOTTOM_FIELD_FIRST_FLAG 0x40 + +/* protocol registers */ +#define VC1_PIC_RATIO AV_SCRATCH_0 +#define VC1_ERROR_COUNT AV_SCRATCH_6 +#define VC1_SOS_COUNT AV_SCRATCH_7 +#define VC1_BUFFERIN AV_SCRATCH_8 +#define VC1_BUFFEROUT AV_SCRATCH_9 +#define VC1_REPEAT_COUNT AV_SCRATCH_A +#define VC1_TIME_STAMP AV_SCRATCH_B +#define VC1_OFFSET_REG AV_SCRATCH_C +#define MEM_OFFSET_REG AV_SCRATCH_F + +#define VF_POOL_SIZE 32 +#define DECODE_BUFFER_NUM_MAX 8 +#define PUT_INTERVAL (HZ/100) + +#if 1 /* /MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON6 */ +/* TODO: move to register headers */ +#define VPP_VD1_POSTBLEND (1 << 10) +#define MEM_FIFO_CNT_BIT 16 +#define MEM_LEVEL_CNT_BIT 18 +#endif + +static struct vframe_s *vvc1_vf_peek(void *); +static struct vframe_s *vvc1_vf_get(void *); +static void vvc1_vf_put(struct vframe_s *, void *); +static int vvc1_vf_states(struct vframe_states *states, void *); +static int vvc1_event_cb(int type, void *data, void *private_data); + +static void vvc1_prot_init(void); +static void vvc1_local_init(void); + +static const char vvc1_dec_id[] = "vvc1-dev"; + +#define PROVIDER_NAME "decoder.vc1" +static const struct vframe_operations_s vvc1_vf_provider = { + .peek = vvc1_vf_peek, + .get = vvc1_vf_get, + .put = vvc1_vf_put, + .event_cb = vvc1_event_cb, + .vf_states = vvc1_vf_states, +}; + +static struct vframe_provider_s vvc1_vf_prov; + +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 struct vframe_s vfpool[VF_POOL_SIZE]; +static struct vframe_s vfpool2[VF_POOL_SIZE]; +static int cur_pool_idx; + +static s32 vfbuf_use[DECODE_BUFFER_NUM_MAX]; +static struct timer_list recycle_timer; +static u32 stat; +static unsigned long buf_start; +static u32 buf_size, buf_offset; +static u32 avi_flag; +static u32 vvc1_ratio; +static u32 vvc1_format; + +static u32 intra_output; +static u32 frame_width, frame_height, frame_dur; +static u32 saved_resolution; +static u32 pts_by_offset = 1; +static u32 total_frame; +static u32 next_pts; +static u64 next_pts_us64; + +#ifdef DEBUG_PTS +static u32 pts_hit, pts_missed, pts_i_hit, pts_i_missed; +#endif +static DEFINE_SPINLOCK(lock); + +static struct dec_sysinfo vvc1_amstream_dec_info; + +struct frm_s { + int state; + u32 start_pts; + int num; + u32 end_pts; + u32 rate; + u32 trymax; +}; + +static struct frm_s frm; + +enum { + RATE_MEASURE_START_PTS = 0, + RATE_MEASURE_END_PTS, + RATE_MEASURE_DONE +}; +#define RATE_MEASURE_NUM 8 +#define RATE_CORRECTION_THRESHOLD 5 +#define RATE_24_FPS 3755 /* 23.97 */ +#define RATE_30_FPS 3003 /* 29.97 */ +#define DUR2PTS(x) ((x)*90/96) +#define PTS2DUR(x) ((x)*96/90) + +static inline int pool_index(struct vframe_s *vf) +{ + if ((vf >= &vfpool[0]) && (vf <= &vfpool[VF_POOL_SIZE - 1])) + return 0; + else if ((vf >= &vfpool2[0]) && (vf <= &vfpool2[VF_POOL_SIZE - 1])) + return 1; + else + return -1; +} + +static inline bool close_to(int a, int b, int m) +{ + return abs(a - b) < m; +} + +static inline u32 index2canvas(u32 index) +{ + const u32 canvas_tab[DECODE_BUFFER_NUM_MAX] = { +#if 1 /* ALWASY.MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON6 */ + 0x010100, 0x030302, 0x050504, 0x070706, + 0x090908, 0x0b0b0a, 0x0d0d0c, 0x0f0f0e +#else + 0x020100, 0x050403, 0x080706, 0x0b0a09 +#endif + }; + + return canvas_tab[index]; +} + +static void set_aspect_ratio(struct vframe_s *vf, unsigned pixel_ratio) +{ + int ar = 0; + + if (vvc1_ratio == 0) { + /* always stretch to 16:9 */ + vf->ratio_control |= (0x90 << DISP_RATIO_ASPECT_RATIO_BIT); + } else if (pixel_ratio > 0x0f) { + ar = (vvc1_amstream_dec_info.height * (pixel_ratio & 0xff) * + vvc1_ratio) / (vvc1_amstream_dec_info.width * + (pixel_ratio >> 8)); + } else { + switch (pixel_ratio) { + case 0: + ar = (vvc1_amstream_dec_info.height * vvc1_ratio) / + vvc1_amstream_dec_info.width; + break; + case 1: + ar = (vf->height * vvc1_ratio) / vf->width; + break; + case 2: + ar = (vf->height * 11 * vvc1_ratio) / (vf->width * 12); + break; + case 3: + ar = (vf->height * 11 * vvc1_ratio) / (vf->width * 10); + break; + case 4: + ar = (vf->height * 11 * vvc1_ratio) / (vf->width * 16); + break; + case 5: + ar = (vf->height * 33 * vvc1_ratio) / (vf->width * 40); + break; + case 6: + ar = (vf->height * 11 * vvc1_ratio) / (vf->width * 24); + break; + case 7: + ar = (vf->height * 11 * vvc1_ratio) / (vf->width * 20); + break; + case 8: + ar = (vf->height * 11 * vvc1_ratio) / (vf->width * 32); + break; + case 9: + ar = (vf->height * 33 * vvc1_ratio) / (vf->width * 80); + break; + case 10: + ar = (vf->height * 11 * vvc1_ratio) / (vf->width * 18); + break; + case 11: + ar = (vf->height * 11 * vvc1_ratio) / (vf->width * 15); + break; + case 12: + ar = (vf->height * 33 * vvc1_ratio) / (vf->width * 64); + break; + case 13: + ar = (vf->height * 99 * vvc1_ratio) / + (vf->width * 160); + break; + default: + ar = (vf->height * vvc1_ratio) / vf->width; + break; + } + } + + ar = min(ar, DISP_RATIO_ASPECT_RATIO_MAX); + + vf->ratio_control = (ar << DISP_RATIO_ASPECT_RATIO_BIT); + /*vf->ratio_control |= DISP_RATIO_FORCECONFIG | DISP_RATIO_KEEPRATIO;*/ +} + +static irqreturn_t vvc1_isr(int irq, void *dev_id) +{ + u32 reg; + struct vframe_s *vf = NULL; + u32 repeat_count; + u32 picture_type; + u32 buffer_index; + unsigned int pts, pts_valid = 0, offset; + u32 v_width, v_height; + u64 pts_us64 = 0; + + reg = READ_VREG(VC1_BUFFEROUT); + + if (reg) { + v_width = READ_VREG(AV_SCRATCH_J); + v_height = READ_VREG(AV_SCRATCH_K); + + if (v_width && v_width <= 4096 + && (v_width != vvc1_amstream_dec_info.width)) { + pr_info("frame width changed %d to %d\n", + vvc1_amstream_dec_info.width, v_width); + vvc1_amstream_dec_info.width = v_width; + frame_width = v_width; + } + if (v_height && v_height <= 4096 + && (v_height != vvc1_amstream_dec_info.height)) { + pr_info("frame height changed %d to %d\n", + vvc1_amstream_dec_info.height, v_height); + vvc1_amstream_dec_info.height = v_height; + frame_height = v_height; + } + + if (pts_by_offset) { + offset = READ_VREG(VC1_OFFSET_REG); + if (pts_lookup_offset_us64( + PTS_TYPE_VIDEO, + offset, &pts, 0, &pts_us64) == 0) { + pts_valid = 1; +#ifdef DEBUG_PTS + pts_hit++; +#endif + } else { +#ifdef DEBUG_PTS + pts_missed++; +#endif + } + } + + repeat_count = READ_VREG(VC1_REPEAT_COUNT); + buffer_index = reg & 0x7; + picture_type = (reg >> 3) & 7; + + if (buffer_index >= DECODE_BUFFER_NUM_MAX) { + pr_info("fatal error, invalid buffer index."); + return IRQ_HANDLED; + } + + if ((intra_output == 0) && (picture_type != 0)) { + WRITE_VREG(VC1_BUFFERIN, ~(1 << buffer_index)); + WRITE_VREG(VC1_BUFFEROUT, 0); + WRITE_VREG(ASSIST_MBOX1_CLR_REG, 1); + + return IRQ_HANDLED; + } + + intra_output = 1; + +#ifdef DEBUG_PTS + if (picture_type == I_PICTURE) { + /* pr_info("I offset 0x%x, + pts_valid %d\n", offset, pts_valid); */ + if (!pts_valid) + pts_i_missed++; + else + pts_i_hit++; + } +#endif + + if ((pts_valid) && (frm.state != RATE_MEASURE_DONE)) { + if (frm.state == RATE_MEASURE_START_PTS) { + frm.start_pts = pts; + frm.state = RATE_MEASURE_END_PTS; + frm.trymax = RATE_MEASURE_NUM; + } else if (frm.state == RATE_MEASURE_END_PTS) { + if (frm.num >= frm.trymax) { + frm.end_pts = pts; + frm.rate = (frm.end_pts - + frm.start_pts) / frm.num; + pr_info("frate before=%d,%d,num=%d\n", + frm.rate, + DUR2PTS(vvc1_amstream_dec_info.rate), + frm.num); + /* check if measured rate is same as + * settings from upper layer + * and correct it if necessary */ + if ((close_to(frm.rate, RATE_30_FPS, + RATE_CORRECTION_THRESHOLD) && + close_to( + DUR2PTS( + vvc1_amstream_dec_info.rate), + RATE_24_FPS, + RATE_CORRECTION_THRESHOLD)) + || + (close_to( + frm.rate, RATE_24_FPS, + RATE_CORRECTION_THRESHOLD) + && + close_to(DUR2PTS( + vvc1_amstream_dec_info.rate), + RATE_30_FPS, + RATE_CORRECTION_THRESHOLD))) { + pr_info( + "vvc1: frate from %d to %d\n", + vvc1_amstream_dec_info.rate, + PTS2DUR(frm.rate)); + + vvc1_amstream_dec_info.rate = + PTS2DUR(frm.rate); + frm.state = RATE_MEASURE_DONE; + } else if (close_to(frm.rate, + DUR2PTS( + vvc1_amstream_dec_info.rate), + RATE_CORRECTION_THRESHOLD)) + frm.state = RATE_MEASURE_DONE; + else { /*maybe still have problem, + try next double frames.... */ + frm.state = RATE_MEASURE_DONE; + frm.start_pts = pts; + frm.state = + RATE_MEASURE_END_PTS; + /*60 fps*60 S */ + frm.num = 0; + } + } + } + } + + if (frm.state != RATE_MEASURE_DONE) + frm.num += (repeat_count > 1) ? repeat_count : 1; + if (0 == vvc1_amstream_dec_info.rate) + vvc1_amstream_dec_info.rate = PTS2DUR(frm.rate); + + if (reg & INTERLACE_FLAG) { /* interlace */ + if (kfifo_get(&newframe_q, &vf) == 0) { + pr_info + ("fatal error, no available buffer slot."); + return IRQ_HANDLED; + } + vf->signal_type = 0; + vf->index = buffer_index; + vf->width = vvc1_amstream_dec_info.width; + vf->height = vvc1_amstream_dec_info.height; + vf->bufWidth = 1920; + vf->flag = 0; + + if (pts_valid) { + vf->pts = pts; + vf->pts_us64 = pts_us64; + if ((repeat_count > 1) && avi_flag) { + vf->duration = + vvc1_amstream_dec_info.rate * + repeat_count >> 1; + next_pts = pts + + (vvc1_amstream_dec_info.rate * + repeat_count >> 1) * 15 / 16; + next_pts_us64 = pts_us64 + + ((vvc1_amstream_dec_info.rate * + repeat_count >> 1) * 15 / 16) * + 100 / 9; + } else { + vf->duration = + vvc1_amstream_dec_info.rate >> 1; + next_pts = 0; + next_pts_us64 = 0; + } + } else { + vf->pts = next_pts; + vf->pts_us64 = next_pts_us64; + if ((repeat_count > 1) && avi_flag) { + vf->duration = + vvc1_amstream_dec_info.rate * + repeat_count >> 1; + if (next_pts != 0) { + next_pts += ((vf->duration) - + ((vf->duration) >> 4)); + } + if (next_pts_us64 != 0) { + next_pts_us64 += + ((vf->duration) - + ((vf->duration) >> 4)) * + 100 / 9; + } + } else { + vf->duration = + vvc1_amstream_dec_info.rate >> 1; + next_pts = 0; + next_pts_us64 = 0; + } + } + + vf->duration_pulldown = 0; + vf->type = (reg & BOTTOM_FIELD_FIRST_FLAG) ? + VIDTYPE_INTERLACE_BOTTOM : VIDTYPE_INTERLACE_TOP; +#ifdef NV21 + vf->type |= VIDTYPE_VIU_NV21; +#endif + vf->canvas0Addr = vf->canvas1Addr = + index2canvas(buffer_index); + vf->orientation = 0; + vf->type_original = vf->type; + set_aspect_ratio(vf, READ_VREG(VC1_PIC_RATIO)); + + vfbuf_use[buffer_index]++; + + kfifo_put(&display_q, (const struct vframe_s *)vf); + + vf_notify_receiver( + PROVIDER_NAME, + VFRAME_EVENT_PROVIDER_VFRAME_READY, + NULL); + + if (kfifo_get(&newframe_q, &vf) == 0) { + pr_info + ("fatal error, no available buffer slot."); + return IRQ_HANDLED; + } + vf->signal_type = 0; + vf->index = buffer_index; + vf->width = vvc1_amstream_dec_info.width; + vf->height = vvc1_amstream_dec_info.height; + vf->bufWidth = 1920; + vf->flag = 0; + + vf->pts = next_pts; + vf->pts_us64 = next_pts_us64; + if ((repeat_count > 1) && avi_flag) { + vf->duration = + vvc1_amstream_dec_info.rate * + repeat_count >> 1; + if (next_pts != 0) { + next_pts += + ((vf->duration) - + ((vf->duration) >> 4)); + } + if (next_pts_us64 != 0) { + next_pts_us64 += ((vf->duration) - + ((vf->duration) >> 4)) * 100 / 9; + } + } else { + vf->duration = + vvc1_amstream_dec_info.rate >> 1; + next_pts = 0; + next_pts_us64 = 0; + } + + vf->duration_pulldown = 0; + vf->type = (reg & BOTTOM_FIELD_FIRST_FLAG) ? + VIDTYPE_INTERLACE_TOP : VIDTYPE_INTERLACE_BOTTOM; +#ifdef NV21 + vf->type |= VIDTYPE_VIU_NV21; +#endif + vf->canvas0Addr = vf->canvas1Addr = + index2canvas(buffer_index); + vf->orientation = 0; + vf->type_original = vf->type; + set_aspect_ratio(vf, READ_VREG(VC1_PIC_RATIO)); + + vfbuf_use[buffer_index]++; + + kfifo_put(&display_q, (const struct vframe_s *)vf); + + vf_notify_receiver( + PROVIDER_NAME, + VFRAME_EVENT_PROVIDER_VFRAME_READY, + NULL); + } else { /* progressive */ + if (kfifo_get(&newframe_q, &vf) == 0) { + pr_info + ("fatal error, no available buffer slot."); + return IRQ_HANDLED; + } + vf->signal_type = 0; + vf->index = buffer_index; + vf->width = vvc1_amstream_dec_info.width; + vf->height = vvc1_amstream_dec_info.height; + vf->bufWidth = 1920; + vf->flag = 0; + + if (pts_valid) { + vf->pts = pts; + vf->pts_us64 = pts_us64; + if ((repeat_count > 1) && avi_flag) { + vf->duration = + vvc1_amstream_dec_info.rate * + repeat_count; + next_pts = + pts + + (vvc1_amstream_dec_info.rate * + repeat_count) * 15 / 16; + next_pts_us64 = pts_us64 + + ((vvc1_amstream_dec_info.rate * + repeat_count) * 15 / 16) * + 100 / 9; + } else { + vf->duration = + vvc1_amstream_dec_info.rate; + next_pts = 0; + next_pts_us64 = 0; + } + } else { + vf->pts = next_pts; + vf->pts_us64 = next_pts_us64; + if ((repeat_count > 1) && avi_flag) { + vf->duration = + vvc1_amstream_dec_info.rate * + repeat_count; + if (next_pts != 0) { + next_pts += ((vf->duration) - + ((vf->duration) >> 4)); + } + if (next_pts_us64 != 0) { + next_pts_us64 += + ((vf->duration) - + ((vf->duration) >> 4)) * + 100 / 9; + } + } else { + vf->duration = + vvc1_amstream_dec_info.rate; + next_pts = 0; + next_pts_us64 = 0; + } + } + + vf->duration_pulldown = 0; +#ifdef NV21 + vf->type = + VIDTYPE_PROGRESSIVE | VIDTYPE_VIU_FIELD | + VIDTYPE_VIU_NV21; +#else + vf->type = VIDTYPE_PROGRESSIVE | VIDTYPE_VIU_FIELD; +#endif + vf->canvas0Addr = vf->canvas1Addr = + index2canvas(buffer_index); + vf->orientation = 0; + vf->type_original = vf->type; + set_aspect_ratio(vf, READ_VREG(VC1_PIC_RATIO)); + + vfbuf_use[buffer_index]++; + + kfifo_put(&display_q, (const struct vframe_s *)vf); + + vf_notify_receiver(PROVIDER_NAME, + VFRAME_EVENT_PROVIDER_VFRAME_READY, + NULL); + } + frame_dur = vvc1_amstream_dec_info.rate; + total_frame++; + + /* pr_info("PicType = %d, PTS = 0x%x, repeat + count %d\n", picture_type, vf->pts, repeat_count); */ + WRITE_VREG(VC1_BUFFEROUT, 0); + } + + WRITE_VREG(ASSIST_MBOX1_CLR_REG, 1); + + return IRQ_HANDLED; +} + +static struct vframe_s *vvc1_vf_peek(void *op_arg) +{ + struct vframe_s *vf; + + if (kfifo_peek(&display_q, &vf)) + return vf; + + return NULL; +} + +static struct vframe_s *vvc1_vf_get(void *op_arg) +{ + struct vframe_s *vf; + + if (kfifo_get(&display_q, &vf)) + return vf; + + return NULL; +} + +static void vvc1_vf_put(struct vframe_s *vf, void *op_arg) +{ + if (pool_index(vf) == cur_pool_idx) + kfifo_put(&recycle_q, (const struct vframe_s *)vf); +} + +static int vvc1_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); + states->buf_recycle_num = kfifo_len(&recycle_q); + + spin_unlock_irqrestore(&lock, flags); + + return 0; +} + +static int vvc1_event_cb(int type, void *data, void *private_data) +{ + if (type & VFRAME_EVENT_RECEIVER_RESET) { + unsigned long flags; + amvdec_stop(); +#ifndef CONFIG_AMLOGIC_POST_PROCESS_MANAGER + vf_light_unreg_provider(&vvc1_vf_prov); +#endif + spin_lock_irqsave(&lock, flags); + vvc1_local_init(); + vvc1_prot_init(); + spin_unlock_irqrestore(&lock, flags); +#ifndef CONFIG_AMLOGIC_POST_PROCESS_MANAGER + vf_reg_provider(&vvc1_vf_prov); +#endif + amvdec_start(); + } + return 0; +} + +int vvc1_dec_status(struct vdec_s *vdec, struct vdec_status *vstatus) +{ + vstatus->width = vvc1_amstream_dec_info.width; + vstatus->height = vvc1_amstream_dec_info.height; + if (0 != vvc1_amstream_dec_info.rate) + vstatus->fps = 96000 / vvc1_amstream_dec_info.rate; + else + vstatus->fps = 96000; + vstatus->error_count = READ_VREG(AV_SCRATCH_4); + vstatus->status = stat; + + return 0; +} + +/****************************************/ +static void vvc1_canvas_init(void) +{ + int i; + u32 canvas_width, canvas_height; + u32 decbuf_size, decbuf_y_size, decbuf_uv_size; + u32 disp_addr = 0xffffffff; + + if (buf_size <= 0x00400000) { + /* SD only */ + canvas_width = 768; + canvas_height = 576; + decbuf_y_size = 0x80000; + decbuf_uv_size = 0x20000; + decbuf_size = 0x100000; + } else { + /* HD & SD */ + canvas_width = 1920; + canvas_height = 1088; + decbuf_y_size = 0x200000; + decbuf_uv_size = 0x80000; + decbuf_size = 0x300000; + } + + if (is_vpp_postblend()) { + struct canvas_s cur_canvas; + + canvas_read((READ_VCBUS_REG(VD1_IF0_CANVAS0) & 0xff), + &cur_canvas); + disp_addr = (cur_canvas.addr + 7) >> 3; + } + + for (i = 0; i < 8; i++) { + if (((buf_start + i * decbuf_size + 7) >> 3) == disp_addr) { +#ifdef NV21 + canvas_config(2 * i + 0, + buf_start + 8 * decbuf_size, + canvas_width, canvas_height, + CANVAS_ADDR_NOWRAP, CANVAS_BLKMODE_32X32); + canvas_config(2 * i + 1, + buf_start + 8 * decbuf_size + + decbuf_y_size, canvas_width, + canvas_height / 2, CANVAS_ADDR_NOWRAP, + CANVAS_BLKMODE_32X32); +#else + canvas_config(3 * i + 0, + buf_start + 8 * decbuf_size, + canvas_width, canvas_height, + CANVAS_ADDR_NOWRAP, CANVAS_BLKMODE_32X32); + canvas_config(3 * i + 1, + buf_start + 8 * decbuf_size + + decbuf_y_size, canvas_width / 2, + canvas_height / 2, CANVAS_ADDR_NOWRAP, + CANVAS_BLKMODE_32X32); + canvas_config(3 * i + 2, + buf_start + 8 * decbuf_size + + decbuf_y_size + decbuf_uv_size, + canvas_width / 2, canvas_height / 2, + CANVAS_ADDR_NOWRAP, CANVAS_BLKMODE_32X32); +#endif + } else { +#ifdef NV21 + canvas_config(2 * i + 0, + buf_start + i * decbuf_size, + canvas_width, canvas_height, + CANVAS_ADDR_NOWRAP, CANVAS_BLKMODE_32X32); + canvas_config(2 * i + 1, + buf_start + i * decbuf_size + + decbuf_y_size, canvas_width, + canvas_height / 2, CANVAS_ADDR_NOWRAP, + CANVAS_BLKMODE_32X32); +#else + canvas_config(3 * i + 0, + buf_start + i * decbuf_size, + canvas_width, canvas_height, + CANVAS_ADDR_NOWRAP, CANVAS_BLKMODE_32X32); + canvas_config(3 * i + 1, + buf_start + i * decbuf_size + + decbuf_y_size, canvas_width / 2, + canvas_height / 2, CANVAS_ADDR_NOWRAP, + CANVAS_BLKMODE_32X32); + canvas_config(3 * i + 2, + buf_start + i * decbuf_size + + decbuf_y_size + decbuf_uv_size, + canvas_width / 2, canvas_height / 2, + CANVAS_ADDR_NOWRAP, CANVAS_BLKMODE_32X32); +#endif + } + } +} + +static void vvc1_prot_init(void) +{ +#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); + + 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); + +#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, 0x10); + WRITE_VREG_BITS(VLD_MEM_VIFIFO_CONTROL, 2, MEM_FIFO_CNT_BIT, 2); + WRITE_VREG_BITS(VLD_MEM_VIFIFO_CONTROL, 8, MEM_LEVEL_CNT_BIT, 6); + + vvc1_canvas_init(); + + /* index v << 16 | u << 8 | y */ +#ifdef NV21 + WRITE_VREG(AV_SCRATCH_0, 0x010100); + WRITE_VREG(AV_SCRATCH_1, 0x030302); + WRITE_VREG(AV_SCRATCH_2, 0x050504); + WRITE_VREG(AV_SCRATCH_3, 0x070706); + WRITE_VREG(AV_SCRATCH_G, 0x090908); + WRITE_VREG(AV_SCRATCH_H, 0x0b0b0a); + WRITE_VREG(AV_SCRATCH_I, 0x0d0d0c); + WRITE_VREG(AV_SCRATCH_J, 0x0f0f0e); +#else + WRITE_VREG(AV_SCRATCH_0, 0x020100); + WRITE_VREG(AV_SCRATCH_1, 0x050403); + WRITE_VREG(AV_SCRATCH_2, 0x080706); + WRITE_VREG(AV_SCRATCH_3, 0x0b0a09); + WRITE_VREG(AV_SCRATCH_G, 0x090908); + WRITE_VREG(AV_SCRATCH_H, 0x0b0b0a); + WRITE_VREG(AV_SCRATCH_I, 0x0d0d0c); + WRITE_VREG(AV_SCRATCH_J, 0x0f0f0e); +#endif + + /* notify ucode the buffer offset */ + WRITE_VREG(AV_SCRATCH_F, buf_offset); + + /* disable PSCALE for hardware sharing */ + WRITE_VREG(PSCALE_CTRL, 0); + + WRITE_VREG(VC1_SOS_COUNT, 0); + WRITE_VREG(VC1_BUFFERIN, 0); + WRITE_VREG(VC1_BUFFEROUT, 0); + + /* clear mailbox interrupt */ + WRITE_VREG(ASSIST_MBOX1_CLR_REG, 1); + + /* enable mailbox interrupt */ + WRITE_VREG(ASSIST_MBOX1_MASK, 1); + +#ifdef NV21 + SET_VREG_MASK(MDEC_PIC_DC_CTRL, 1 << 17); +#endif +} + +static void vvc1_local_init(void) +{ + int i; + + /* vvc1_ratio = vvc1_amstream_dec_info.ratio; */ + vvc1_ratio = 0x100; + + avi_flag = (unsigned long) vvc1_amstream_dec_info.param; + + total_frame = 0; + + next_pts = 0; + + next_pts_us64 = 0; + saved_resolution = 0; + frame_width = frame_height = frame_dur = 0; +#ifdef DEBUG_PTS + pts_hit = pts_missed = pts_i_hit = pts_i_missed = 0; +#endif + + memset(&frm, 0, sizeof(frm)); + + for (i = 0; i < DECODE_BUFFER_NUM_MAX; i++) + vfbuf_use[i] = 0; + + INIT_KFIFO(display_q); + INIT_KFIFO(recycle_q); + INIT_KFIFO(newframe_q); + cur_pool_idx ^= 1; + for (i = 0; i < VF_POOL_SIZE; i++) { + const struct vframe_s *vf; + if (cur_pool_idx == 0) { + vf = &vfpool[i]; + vfpool[i].index = DECODE_BUFFER_NUM_MAX; + } else { + vf = &vfpool2[i]; + vfpool2[i].index = DECODE_BUFFER_NUM_MAX; + } + kfifo_put(&newframe_q, (const struct vframe_s *)vf); + } +} + +#ifdef CONFIG_AMLOGIC_POST_PROCESS_MANAGER +static void vvc1_ppmgr_reset(void) +{ + vf_notify_receiver(PROVIDER_NAME, VFRAME_EVENT_PROVIDER_RESET, NULL); + + vvc1_local_init(); + + /* vf_notify_receiver(PROVIDER_NAME, + * VFRAME_EVENT_PROVIDER_START,NULL); */ + + pr_info("vvc1dec: vf_ppmgr_reset\n"); +} +#endif + +static void vvc1_put_timer_func(unsigned long arg) +{ + struct timer_list *timer = (struct timer_list *)arg; + +#if 1 + if (READ_VREG(VC1_SOS_COUNT) > 10) { + amvdec_stop(); +#ifdef CONFIG_AMLOGIC_POST_PROCESS_MANAGER + vvc1_ppmgr_reset(); +#else + vf_light_unreg_provider(&vvc1_vf_prov); + vvc1_local_init(); + vf_reg_provider(&vvc1_vf_prov); +#endif + vvc1_prot_init(); + amvdec_start(); + } +#endif + + while (!kfifo_is_empty(&recycle_q) && (READ_VREG(VC1_BUFFERIN) == 0)) { + struct vframe_s *vf; + if (kfifo_get(&recycle_q, &vf)) { + if ((vf->index < DECODE_BUFFER_NUM_MAX) && + (--vfbuf_use[vf->index] == 0)) { + WRITE_VREG(VC1_BUFFERIN, ~(1 << vf->index)); + vf->index = DECODE_BUFFER_NUM_MAX; + } + if (pool_index(vf) == cur_pool_idx) + kfifo_put(&newframe_q, (const struct vframe_s *)vf); + } + } + if (frame_dur > 0 && saved_resolution != + frame_width * frame_height * (96000 / frame_dur)) { + int fps = 96000 / frame_dur; + saved_resolution = frame_width * frame_height * fps; + vdec_source_changed(VFORMAT_VC1, + frame_width, frame_height, fps); + } + timer->expires = jiffies + PUT_INTERVAL; + + add_timer(timer); +} + +static s32 vvc1_init(void) +{ + int size = -1; + char *buf = vmalloc(0x1000 * 16); + if (IS_ERR_OR_NULL(buf)) + return -ENOMEM; + + pr_info("vvc1_init, format %d\n", vvc1_amstream_dec_info.format); + init_timer(&recycle_timer); + + stat |= STAT_TIMER_INIT; + + intra_output = 0; + amvdec_enable(); + + vvc1_local_init(); + + if (vvc1_amstream_dec_info.format == VIDEO_DEC_FORMAT_WMV3) { + pr_info("WMV3 dec format\n"); + vvc1_format = VIDEO_DEC_FORMAT_WMV3; + WRITE_VREG(AV_SCRATCH_4, 0); + } else if (vvc1_amstream_dec_info.format == VIDEO_DEC_FORMAT_WVC1) { + pr_info("WVC1 dec format\n"); + vvc1_format = VIDEO_DEC_FORMAT_WVC1; + WRITE_VREG(AV_SCRATCH_4, 1); + } else + pr_info("not supported VC1 format\n"); + + size = get_firmware_data(VIDEO_DEC_VC1, buf); + if (size < 0) { + pr_err("get firmware fail."); + vfree(buf); + return -1; + } + + if (amvdec_loadmc_ex(VFORMAT_VC1, NULL, buf) < 0) { + amvdec_disable(); + vfree(buf); + return -EBUSY; + } + + vfree(buf); + + stat |= STAT_MC_LOAD; + + /* enable AMRISC side protocol */ + vvc1_prot_init(); + + if (vdec_request_irq(VDEC_IRQ_1, vvc1_isr, + "vvc1-irq", (void *)vvc1_dec_id)) { + amvdec_disable(); + + pr_info("vvc1 irq register error.\n"); + return -ENOENT; + } + + stat |= STAT_ISR_REG; +#ifdef CONFIG_AMLOGIC_POST_PROCESS_MANAGER + vf_provider_init(&vvc1_vf_prov, + PROVIDER_NAME, &vvc1_vf_provider, NULL); + vf_reg_provider(&vvc1_vf_prov); + vf_notify_receiver(PROVIDER_NAME, + VFRAME_EVENT_PROVIDER_START, NULL); +#else + vf_provider_init(&vvc1_vf_prov, + PROVIDER_NAME, &vvc1_vf_provider, NULL); + vf_reg_provider(&vvc1_vf_prov); +#endif + + vf_notify_receiver(PROVIDER_NAME, VFRAME_EVENT_PROVIDER_FR_HINT, + (void *)((unsigned long)vvc1_amstream_dec_info.rate)); + + stat |= STAT_VF_HOOK; + + recycle_timer.data = (ulong)&recycle_timer; + recycle_timer.function = vvc1_put_timer_func; + recycle_timer.expires = jiffies + PUT_INTERVAL; + + add_timer(&recycle_timer); + + stat |= STAT_TIMER_ARM; + + amvdec_start(); + + stat |= STAT_VDEC_RUN; + + return 0; +} + +static int amvdec_vc1_probe(struct platform_device *pdev) +{ + struct vdec_s *pdata = *(struct vdec_s **)pdev->dev.platform_data; + + if (pdata == NULL) { + pr_info("amvdec_vc1 memory resource undefined.\n"); + return -EFAULT; + } + + buf_start = pdata->mem_start; + buf_size = pdata->mem_end - pdata->mem_start + 1; + buf_offset = buf_start - ORI_BUFFER_START_ADDR; + + if (pdata->sys_info) + vvc1_amstream_dec_info = *pdata->sys_info; + + pdata->dec_status = vvc1_dec_status; + + if (vvc1_init() < 0) { + pr_info("amvdec_vc1 init failed.\n"); + + return -ENODEV; + } + + return 0; +} + +static int amvdec_vc1_remove(struct platform_device *pdev) +{ + if (stat & STAT_VDEC_RUN) { + amvdec_stop(); + stat &= ~STAT_VDEC_RUN; + } + + if (stat & STAT_ISR_REG) { + vdec_free_irq(VDEC_IRQ_1, (void *)vvc1_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) { + vf_notify_receiver(PROVIDER_NAME, + VFRAME_EVENT_PROVIDER_FR_END_HINT, NULL); + + vf_unreg_provider(&vvc1_vf_prov); + stat &= ~STAT_VF_HOOK; + } + + amvdec_disable(); + +#ifdef DEBUG_PTS + pr_info("pts hit %d, pts missed %d, i hit %d, missed %d\n", pts_hit, + pts_missed, pts_i_hit, pts_i_missed); + pr_info("total frame %d, avi_flag %d, rate %d\n", + total_frame, avi_flag, + vvc1_amstream_dec_info.rate); +#endif + + return 0; +} + +/****************************************/ + +static struct platform_driver amvdec_vc1_driver = { + .probe = amvdec_vc1_probe, + .remove = amvdec_vc1_remove, +#ifdef CONFIG_PM + .suspend = amvdec_suspend, + .resume = amvdec_resume, +#endif + .driver = { + .name = DRIVER_NAME, + } +}; + +#if defined(CONFIG_ARCH_MESON) /*meson1 only support progressive */ +static struct codec_profile_t amvdec_vc1_profile = { + .name = "vc1", + .profile = "progressive, wmv3" +}; +#else +static struct codec_profile_t amvdec_vc1_profile = { + .name = "vc1", + .profile = "progressive, interlace, wmv3" +}; +#endif + +static int __init amvdec_vc1_driver_init_module(void) +{ + pr_debug("amvdec_vc1 module init\n"); + + if (platform_driver_register(&amvdec_vc1_driver)) { + pr_err("failed to register amvdec_vc1 driver\n"); + return -ENODEV; + } + vcodec_profile_register(&amvdec_vc1_profile); + return 0; +} + +static void __exit amvdec_vc1_driver_remove_module(void) +{ + pr_debug("amvdec_vc1 module remove.\n"); + + platform_driver_unregister(&amvdec_vc1_driver); +} + +/****************************************/ + +module_param(stat, uint, 0664); +MODULE_PARM_DESC(stat, "\n amvdec_vc1 stat\n"); + +module_init(amvdec_vc1_driver_init_module); +module_exit(amvdec_vc1_driver_remove_module); + +MODULE_DESCRIPTION("AMLOGIC VC1 Video Decoder Driver"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Qi Wang <qi.wang@amlogic.com>"); diff --git a/drivers/frame_provider/decoder/vp9/vvp9.c b/drivers/frame_provider/decoder/vp9/vvp9.c new file mode 100644 index 0000000..0e819e7 --- a/dev/null +++ b/drivers/frame_provider/decoder/vp9/vvp9.c @@ -0,0 +1,6922 @@ + /* + * drivers/amlogic/amports/vvp9.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/spinlock.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_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" +#include "../utils/decoder_bmmu_box.h" + +#define MEM_NAME "codec_vp9" +/* #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.h> + +#define MIX_STREAM_SUPPORT +#define SUPPORT_4K2K + +#include "vvp9.h" +#define VP9D_MPP_REFINFO_TBL_ACCCONFIG 0x3442 +#define VP9D_MPP_REFINFO_DATA 0x3443 +#define VP9D_MPP_REF_SCALE_ENBL 0x3441 +#define HEVC_MPRED_CTRL4 0x324c +#define HEVC_CM_HEADER_START_ADDR 0x3628 +#define HEVC_DBLK_CFGB 0x350b +#define HEVCD_MPP_ANC2AXI_TBL_DATA 0x3464 +#define HEVC_SAO_MMU_VH1_ADDR 0x363b +#define HEVC_SAO_MMU_VH0_ADDR 0x363a +#define HEVC_SAO_MMU_STATUS 0x3639 + + +#define VP9_10B_DEC_IDLE 0 +#define VP9_10B_DEC_FRAME_HEADER 1 +#define VP9_10B_DEC_SLICE_SEGMENT 2 +#define VP9_10B_DECODE_SLICE 5 +#define VP9_10B_DISCARD_NAL 6 +#define VP9_DUMP_LMEM 7 +#define HEVC_DECPIC_DATA_DONE 0xa +#define HEVC_DECPIC_DATA_ERROR 0xb +#define HEVC_NAL_DECODE_DONE 0xe +#define HEVC_DECODE_BUFEMPTY 0x20 +#define HEVC_DECODE_TIMEOUT 0x21 +#define HEVC_SEARCH_BUFEMPTY 0x22 +#define VP9_HEAD_PARSER_DONE 0xf0 +#define VP9_HEAD_SEARCH_DONE 0xf1 +#define VP9_EOS 0xf2 +#define HEVC_ACTION_DONE 0xff + +#define MAX_BUF_NUM 24 +#define MAX_REF_ACTIVE 16 +#define VF_POOL_SIZE 32 + +#undef pr_info +#define pr_info printk + +#define DECODE_MODE_SINGLE 0 +#define DECODE_MODE_MULTI_STREAMBASE 1 +#define DECODE_MODE_MULTI_FRAMEBASE 2 + +/*--------------------------------------------------- + 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 + +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 +}; +/*#define HEVC_PIC_STRUCT_SUPPORT*/ +/* to remove, fix build error */ + +/*#define CODEC_MM_FLAGS_FOR_VDECODER 0*/ + +#define MULTI_INSTANCE_SUPPORT +#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 MAX_DECODE_INSTANCE_NUM 5 +#define MULTI_DRIVER_NAME "ammvdec_vp9" +#endif + +#define DRIVER_NAME "amvdec_vp9" +#define MODULE_NAME "amvdec_vp9" + +#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) + +struct VP9Decoder_s; +static int vvp9_vf_states(struct vframe_states *states, void *); +static struct vframe_s *vvp9_vf_peek(void *); +static struct vframe_s *vvp9_vf_get(void *); +static void vvp9_vf_put(struct vframe_s *, void *); +static int vvp9_event_cb(int type, void *data, void *private_data); + +static int vvp9_stop(struct VP9Decoder_s *pbi); +static s32 vvp9_init(struct VP9Decoder_s *pbi); +static void vvp9_prot_init(struct VP9Decoder_s *pbi); +static int vvp9_local_init(struct VP9Decoder_s *pbi); +static void vvp9_put_timer_func(unsigned long arg); +#ifdef VP9_10B_MMU +static int vp9_alloc_mmu( + struct VP9Decoder_s *pbi, + int cur_buf_idx, + int pic_width, + int pic_height, + unsigned short bit_depth, + unsigned int *mmu_index_adr); +#endif + +static const char vvp9_dec_id[] = "vvp9-dev"; + +#define PROVIDER_NAME "decoder.vp9" +#define MULTI_INSTANCE_PROVIDER_NAME "vdec.vp9" + +static const struct vframe_operations_s vvp9_vf_provider = { + .peek = vvp9_vf_peek, + .get = vvp9_vf_get, + .put = vvp9_vf_put, + .event_cb = vvp9_event_cb, + .vf_states = vvp9_vf_states, +}; + +static struct vframe_provider_s vvp9_vf_prov; + +static u32 bit_depth_luma; +static u32 bit_depth_chroma; +static u32 frame_width; +static u32 frame_height; +static u32 video_signal_type; +static u32 pts_unstable; +static u32 on_no_keyframe_skiped; + + +#define PROB_SIZE (496 * 2 * 4) +#define PROB_BUF_SIZE (0x5000) +#define COUNT_BUF_SIZE (0x300 * 4 * 4) +/*compute_losless_comp_body_size(4096, 2304, 1) = 18874368(0x1200000)*/ +#define MAX_FRAME_4K_NUM 0x1200 +#define FRAME_MMU_MAP_SIZE (MAX_FRAME_4K_NUM * 4) + +static inline int div_r32(int64_t m, int n) +{ +/* +return (int)(m/n) +*/ +#ifndef CONFIG_ARM64 + do_div(m, n); + return (int)m; +#else + return (int)(m/n); +#endif +} + +/*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 */; + + + /* #undef BUFMGR_ONLY to enable hardware configuration */ + +/*#define TEST_WR_PTR_INC*/ +#define WR_PTR_INC_NUM 128 + +#define SIMULATION +#define DOS_PROJECT +#undef MEMORY_MAP_IN_REAL_CHIP + +/*#undef DOS_PROJECT*/ +/*#define MEMORY_MAP_IN_REAL_CHIP*/ + +/*#define BUFFER_MGR_ONLY*/ +/*#define CONFIG_HEVC_CLK_FORCED_ON*/ +/*#define ENABLE_SWAP_TEST*/ +#define MCRCC_ENABLE + +#ifdef VP9_10B_NV21 +#define MEM_MAP_MODE 2 /* 0:linear 1:32x32 2:64x32*/ +#else +#define MEM_MAP_MODE 0 /* 0:linear 1:32x32 2:64x32*/ +#endif + +#define VP9_LPF_LVL_UPDATE +/*#define DBG_LF_PRINT*/ + +#ifdef VP9_10B_NV21 +#else +#define LOSLESS_COMPRESS_MODE +#endif + +#define DOUBLE_WRITE_YSTART_TEMP 0x02000000 +#define DOUBLE_WRITE_CSTART_TEMP 0x02900000 + + + +typedef unsigned int u32; +typedef unsigned short u16; + +#define VP9_DEBUG_BUFMGR 0x01 +#define VP9_DEBUG_BUFMGR_MORE 0x02 +#define VP9_DEBUG_UCODE 0x04 +#define VP9_DEBUG_REG 0x08 +#define VP9_DEBUG_MERGE 0x10 +#define VP9_DEBUG_BASIC 0x40 +#define VP9_DEBUG_SEND_PARAM_WITH_REG 0x100 +#define VP9_DEBUG_NO_DISPLAY 0x200 +#define VP9_DEBUG_DBG_LF_PRINT 0x400 +#define VP9_DEBUG_OUT_PTS 0x800 +#define VP9_DEBUG_VF_REF 0x1000 +#define VP9_DEBUG_DIS_LOC_ERROR_PROC 0x10000 +#define VP9_DEBUG_DIS_SYS_ERROR_PROC 0x20000 +#define VP9_DEBUG_DUMP_PIC_LIST 0x40000 +#define VP9_DEBUG_TRIG_SLICE_SEGMENT_PROC 0x80000 +#define VP9_DEBUG_HW_RESET 0x100000 +#define VP9_DEBUG_LOAD_UCODE_FROM_FILE 0x200000 +#define VP9_DEBUG_ERROR_TRIG 0x400000 +#define VP9_DEBUG_NOWAIT_DECODE_DONE_WHEN_STOP 0x4000000 +#ifdef MULTI_INSTANCE_SUPPORT +#define PRINT_FLAG_ERROR 0 +#define PRINT_FLAG_UCODE_EVT 0x10000000 +#define PRINT_FLAG_VDEC_STATUS 0x20000000 +#define PRINT_FLAG_VDEC_DETAIL 0x40000000 +#define PRINT_FLAG_VDEC_DATA 0x80000000 +#endif + +static u32 debug; + +#define DEBUG_REG +#ifdef DEBUG_REG +void WRITE_VREG_DBG2(unsigned adr, unsigned val) +{ + if (debug & VP9_DEBUG_REG) + pr_info("%s(%x, %x)\n", __func__, adr, val); + if (adr != 0) + WRITE_VREG(adr, val); +} + +#undef WRITE_VREG +#define WRITE_VREG WRITE_VREG_DBG2 +#endif + +/************************************************** + +VP9 buffer management start + +***************************************************/ +#ifdef VP9_10B_MMU +#define MMU_COMPRESS_HEADER_SIZE 0x48000 +#endif + +#define INVALID_IDX -1 /* Invalid buffer index.*/ + +#define RPM_BEGIN 0x200 +#define RPM_END 0x280 + +union param_u { + struct { + unsigned short data[RPM_END - RPM_BEGIN]; + } l; + struct { + /* from ucode lmem, do not change this struct */ + unsigned short profile; + unsigned short show_existing_frame; + unsigned short frame_to_show_idx; + unsigned short frame_type; /*1 bit*/ + unsigned short show_frame; /*1 bit*/ + unsigned short error_resilient_mode; /*1 bit*/ + unsigned short intra_only; /*1 bit*/ + unsigned short display_size_present; /*1 bit*/ + unsigned short reset_frame_context; + unsigned short refresh_frame_flags; + unsigned short width; + unsigned short height; + unsigned short display_width; + unsigned short display_height; + /* + bit[11:8] - ref_frame_info_0 (ref(3-bits), ref_frame_sign_bias(1-bit)) + bit[7:4] - ref_frame_info_1 (ref(3-bits), ref_frame_sign_bias(1-bit)) + bit[3:0] - ref_frame_info_2 (ref(3-bits), ref_frame_sign_bias(1-bit)) + */ + unsigned short ref_info; + /* + bit[2]: same_frame_size0 + bit[1]: same_frame_size1 + bit[0]: same_frame_size2 + */ + unsigned short same_frame_size; + + unsigned short mode_ref_delta_enabled; + unsigned short ref_deltas[4]; + unsigned short mode_deltas[2]; + unsigned short filter_level; + unsigned short sharpness_level; + unsigned short bit_depth; + unsigned short seg_quant_info[8]; + unsigned short seg_enabled; + unsigned short seg_abs_delta; + /* bit 15: feature enabled; bit 8, sign; bit[5:0], data */ + unsigned short seg_lf_info[8]; + } p; +}; + + +struct vpx_codec_frame_buffer_s { + uint8_t *data; /**< Pointer to the data buffer */ + size_t size; /**< Size of data in bytes */ + void *priv; /**< Frame's private data */ +}; + +enum vpx_color_space_t { + VPX_CS_UNKNOWN = 0, /**< Unknown */ + VPX_CS_BT_601 = 1, /**< BT.601 */ + VPX_CS_BT_709 = 2, /**< BT.709 */ + VPX_CS_SMPTE_170 = 3, /**< SMPTE.170 */ + VPX_CS_SMPTE_240 = 4, /**< SMPTE.240 */ + VPX_CS_BT_2020 = 5, /**< BT.2020 */ + VPX_CS_RESERVED = 6, /**< Reserved */ + VPX_CS_SRGB = 7 /**< sRGB */ +}; /**< alias for enum vpx_color_space */ + +enum vpx_bit_depth_t { + VPX_BITS_8 = 8, /**< 8 bits */ + VPX_BITS_10 = 10, /**< 10 bits */ + VPX_BITS_12 = 12, /**< 12 bits */ +}; + +#define MAX_SLICE_NUM 1024 +struct PIC_BUFFER_CONFIG_s { + int index; + int BUF_index; + int comp_body_size; + int buf_size; + int vf_ref; + int y_canvas_index; + int uv_canvas_index; +#ifdef MULTI_INSTANCE_SUPPORT + struct canvas_config_s canvas_config[2]; +#endif + int decode_idx; + int slice_type; + int num_reorder_pic; + int stream_offset; + uint8_t used_by_display; + uint8_t referenced; + uint8_t output_mark; + uint8_t recon_mark; + uint8_t output_ready; + uint8_t error_mark; + /**/ + int slice_idx; + /*buffer*/ +#ifdef VP9_10B_MMU + unsigned long header_adr; +#endif + unsigned long mpred_mv_wr_start_addr; + unsigned long mc_y_adr; + unsigned long mc_u_v_adr; + unsigned int dw_y_adr; + unsigned int dw_u_v_adr; + int mc_canvas_y; + int mc_canvas_u_v; + + int lcu_total; + /**/ + int y_width; + int y_height; + int y_crop_width; + int y_crop_height; + int y_stride; + + int uv_width; + int uv_height; + int uv_crop_width; + int uv_crop_height; + int uv_stride; + + int alpha_width; + int alpha_height; + int alpha_stride; + + uint8_t *y_buffer; + uint8_t *u_buffer; + uint8_t *v_buffer; + uint8_t *alpha_buffer; + + uint8_t *buffer_alloc; + int buffer_alloc_sz; + int border; + int frame_size; + int subsampling_x; + int subsampling_y; + unsigned int bit_depth; + enum vpx_color_space_t color_space; + + int corrupted; + int flags; + unsigned long cma_alloc_addr; +} PIC_BUFFER_CONFIG; + +enum BITSTREAM_PROFILE { + PROFILE_0, + PROFILE_1, + PROFILE_2, + PROFILE_3, + MAX_PROFILES +}; + +enum FRAME_TYPE { + KEY_FRAME = 0, + INTER_FRAME = 1, + FRAME_TYPES, +}; + +enum REFERENCE_MODE { + SINGLE_REFERENCE = 0, + COMPOUND_REFERENCE = 1, + REFERENCE_MODE_SELECT = 2, + REFERENCE_MODES = 3, +}; + +#define NONE -1 +#define INTRA_FRAME 0 +#define LAST_FRAME 1 +#define GOLDEN_FRAME 2 +#define ALTREF_FRAME 3 +#define MAX_REF_FRAMES 4 + +#define REFS_PER_FRAME 3 + +#define REF_FRAMES_LOG2 3 +#define REF_FRAMES (1 << REF_FRAMES_LOG2) + +/*4 scratch frames for the new frames to support a maximum of 4 cores decoding +in parallel, 3 for scaled references on the encoder. +TODO(hkuang): Add ondemand frame buffers instead of hardcoding the number +// of framebuffers. +TODO(jkoleszar): These 3 extra references could probably come from the +normal reference pool.*/ +#define FRAME_BUFFERS (REF_FRAMES + 7) + +#define FRAME_CONTEXTS_LOG2 2 +#define FRAME_CONTEXTS (1 << FRAME_CONTEXTS_LOG2) +#define MAX_BMMU_BUFFER_NUM (FRAME_BUFFERS + 1) +#define WORK_SPACE_BUF_ID (FRAME_BUFFERS) + +struct RefCntBuffer_s { + int ref_count; + /*MV_REF *mvs;*/ + int mi_rows; + int mi_cols; + struct vpx_codec_frame_buffer_s raw_frame_buffer; + struct PIC_BUFFER_CONFIG_s buf; + +/*The Following variables will only be used in frame parallel decode. + +frame_worker_owner indicates which FrameWorker owns this buffer. NULL means +that no FrameWorker owns, or is decoding, this buffer. +VP9Worker *frame_worker_owner; + +row and col indicate which position frame has been decoded to in real +pixel unit. They are reset to -1 when decoding begins and set to INT_MAX +when the frame is fully decoded.*/ + int row; + int col; +} RefCntBuffer; + +struct RefBuffer_s { +/*TODO(dkovalev): idx is not really required and should be removed, now it +is used in vp9_onyxd_if.c*/ + int idx; + struct PIC_BUFFER_CONFIG_s *buf; + /*struct scale_factors sf;*/ +} RefBuffer; + +struct InternalFrameBuffer_s { + uint8_t *data; + size_t size; + int in_use; +} InternalFrameBuffer; + +struct InternalFrameBufferList_s { + int num_internal_frame_buffers; + struct InternalFrameBuffer_s *int_fb; +} InternalFrameBufferList; + +struct BufferPool_s { +/*Protect BufferPool from being accessed by several FrameWorkers at +the same time during frame parallel decode. +TODO(hkuang): Try to use atomic variable instead of locking the whole pool. + +Private data associated with the frame buffer callbacks. +void *cb_priv; + +vpx_get_frame_buffer_cb_fn_t get_fb_cb; +vpx_release_frame_buffer_cb_fn_t release_fb_cb;*/ + + struct RefCntBuffer_s frame_bufs[FRAME_BUFFERS]; + +/*Frame buffers allocated internally by the codec.*/ + struct InternalFrameBufferList_s int_frame_buffers; + unsigned long flags; + spinlock_t lock; + +} BufferPool; + +static void lock_buffer_pool(struct BufferPool_s *pool) +{ + spin_lock_irqsave(&pool->lock, pool->flags); +} +static void unlock_buffer_pool(struct BufferPool_s *pool) +{ + spin_unlock_irqrestore(&pool->lock, pool->flags); +} + +struct VP9_Common_s { + enum vpx_color_space_t color_space; + int width; + int height; + int display_width; + int display_height; + int last_width; + int last_height; + + int subsampling_x; + int subsampling_y; + + int use_highbitdepth;/*Marks if we need to use 16bit frame buffers.*/ + + struct PIC_BUFFER_CONFIG_s *frame_to_show; + struct RefCntBuffer_s *prev_frame; + + /*TODO(hkuang): Combine this with cur_buf in macroblockd.*/ + struct RefCntBuffer_s *cur_frame; + + int ref_frame_map[REF_FRAMES]; /* maps fb_idx to reference slot */ + + /*Prepare ref_frame_map for the next frame. + Only used in frame parallel decode.*/ + int next_ref_frame_map[REF_FRAMES]; + + /* TODO(jkoleszar): could expand active_ref_idx to 4, + with 0 as intra, and roll new_fb_idx into it.*/ + + /*Each frame can reference REFS_PER_FRAME buffers*/ + struct RefBuffer_s frame_refs[REFS_PER_FRAME]; + + int prev_fb_idx; + int new_fb_idx; + + /*last frame's frame type for motion search*/ + enum FRAME_TYPE last_frame_type; + enum FRAME_TYPE frame_type; + + int show_frame; + int last_show_frame; + int show_existing_frame; + + /*Flag signaling that the frame is encoded using only INTRA modes.*/ + uint8_t intra_only; + uint8_t last_intra_only; + + int allow_high_precision_mv; + + /*Flag signaling that the frame context should be reset to default + values. 0 or 1 implies don't reset, 2 reset just the context + specified in the frame header, 3 reset all contexts.*/ + int reset_frame_context; + + /*MBs, mb_rows/cols is in 16-pixel units; mi_rows/cols is in + MODE_INFO (8-pixel) units.*/ + int MBs; + int mb_rows, mi_rows; + int mb_cols, mi_cols; + int mi_stride; + + /*Whether to use previous frame's motion vectors for prediction.*/ + int use_prev_frame_mvs; + + int refresh_frame_context; /* Two state 0 = NO, 1 = YES */ + + int ref_frame_sign_bias[MAX_REF_FRAMES]; /* Two state 0, 1 */ + + /*struct loopfilter lf;*/ + /*struct segmentation seg;*/ + + /*TODO(hkuang):Remove this as it is the same as frame_parallel_decode*/ + /* in pbi.*/ + int frame_parallel_decode; /* frame-based threading.*/ + + /*Context probabilities for reference frame prediction*/ + /*MV_REFERENCE_FRAME comp_fixed_ref;*/ + /*MV_REFERENCE_FRAME comp_var_ref[2];*/ + enum REFERENCE_MODE reference_mode; + + /*FRAME_CONTEXT *fc; */ /* this frame entropy */ + /*FRAME_CONTEXT *frame_contexts; */ /*FRAME_CONTEXTS*/ + /*unsigned int frame_context_idx; *//* Context to use/update */ + /*FRAME_COUNTS counts;*/ + + unsigned int current_video_frame; + enum BITSTREAM_PROFILE profile; + + enum vpx_bit_depth_t bit_depth; + + int error_resilient_mode; + int frame_parallel_decoding_mode; + + int byte_alignment; + int skip_loop_filter; + + /*External BufferPool passed from outside.*/ + struct BufferPool_s *buffer_pool; + + int above_context_alloc_cols; + +} VP9_COMMON; + +static void set_canvas(struct PIC_BUFFER_CONFIG_s *pic_config); +static int prepare_display_buf(struct VP9Decoder_s *pbi, + struct PIC_BUFFER_CONFIG_s *pic_config); +static int get_free_fb(struct VP9_Common_s *cm) +{ + struct RefCntBuffer_s *const frame_bufs = cm->buffer_pool->frame_bufs; + int i; + + lock_buffer_pool(cm->buffer_pool); + for (i = 0; i < FRAME_BUFFERS; ++i) { + if (debug & VP9_DEBUG_BUFMGR_MORE) + pr_info("%s:%d, ref_count %d vf_ref %d used_by_d %d index %d\r\n", + __func__, i, frame_bufs[i].ref_count, + frame_bufs[i].buf.vf_ref, + frame_bufs[i].buf.used_by_display, + frame_bufs[i].buf.index); + if ((frame_bufs[i].ref_count == 0) && + (frame_bufs[i].buf.vf_ref == 0) && + (frame_bufs[i].buf.used_by_display == 0) && + (frame_bufs[i].buf.index != -1) + ) + break; + } + if (i != FRAME_BUFFERS) { + frame_bufs[i].ref_count = 1; + /*pr_info("[MMU DEBUG 1] set ref_count[%d] : %d\r\n", + i, frame_bufs[i].ref_count);*/ + } else { + /* Reset i to be INVALID_IDX to indicate + no free buffer found*/ + i = INVALID_IDX; + } + + unlock_buffer_pool(cm->buffer_pool); + return i; +} + +static unsigned char is_buffer_empty(struct VP9_Common_s *cm) +{ + struct RefCntBuffer_s *const frame_bufs = cm->buffer_pool->frame_bufs; + int i; + + for (i = 0; i < FRAME_BUFFERS; ++i) + if ((frame_bufs[i].ref_count == 0) && + (frame_bufs[i].buf.vf_ref == 0) && + (frame_bufs[i].buf.used_by_display == 0) && + (frame_bufs[i].buf.index != -1) + ) + break; + if (i != FRAME_BUFFERS) + return 0; + + return 1; +} + +static struct PIC_BUFFER_CONFIG_s *get_frame_new_buffer(struct VP9_Common_s *cm) +{ + return &cm->buffer_pool->frame_bufs[cm->new_fb_idx].buf; +} + +static void ref_cnt_fb(struct RefCntBuffer_s *bufs, int *idx, int new_idx) +{ + const int ref_index = *idx; + + if (ref_index >= 0 && bufs[ref_index].ref_count > 0) { + bufs[ref_index].ref_count--; + /*pr_info("[MMU DEBUG 2] dec ref_count[%d] : %d\r\n", + ref_index, bufs[ref_index].ref_count);*/ + } + + *idx = new_idx; + + bufs[new_idx].ref_count++; + /*pr_info("[MMU DEBUG 3] inc ref_count[%d] : %d\r\n", + new_idx, bufs[new_idx].ref_count);*/ +} + +int vp9_release_frame_buffer(struct vpx_codec_frame_buffer_s *fb) +{ + struct InternalFrameBuffer_s *const int_fb = + (struct InternalFrameBuffer_s *)fb->priv; + if (int_fb) + int_fb->in_use = 0; + return 0; +} + +static int compute_losless_comp_body_size(int width, int height, + uint8_t is_bit_depth_10); + +static void setup_display_size(struct VP9_Common_s *cm, union param_u *params, + int print_header_info) +{ + cm->display_width = cm->width; + cm->display_height = cm->height; + if (params->p.display_size_present) { + if (print_header_info) + pr_info(" * 1-bit display_size_present read : 1\n"); + cm->display_width = params->p.display_width; + cm->display_height = params->p.display_height; + /*vp9_read_frame_size(rb, &cm->display_width, + &cm->display_height);*/ + } else { + if (print_header_info) + pr_info(" * 1-bit display_size_present read : 0\n"); + } +} + +static void resize_context_buffers(struct VP9_Common_s *cm, int width, + int height) +{ + if (cm->width != width || cm->height != height) { + /* to do ..*/ + cm->width = width; + cm->height = height; + pr_info("%s (%d,%d)=>(%d,%d)\r\n", __func__, cm->width, + cm->height, width, height); + } + /* + if (cm->cur_frame->mvs == NULL || + cm->mi_rows > cm->cur_frame->mi_rows || + cm->mi_cols > cm->cur_frame->mi_cols) { + resize_mv_buffer(cm); + } + */ +} + +static int valid_ref_frame_size(int ref_width, int ref_height, + int this_width, int this_height) { + return 2 * this_width >= ref_width && + 2 * this_height >= ref_height && + this_width <= 16 * ref_width && + this_height <= 16 * ref_height; +} + +/* +static int valid_ref_frame_img_fmt(enum vpx_bit_depth_t ref_bit_depth, + int ref_xss, int ref_yss, + enum vpx_bit_depth_t this_bit_depth, + int this_xss, int this_yss) { + return ref_bit_depth == this_bit_depth && ref_xss == this_xss && + ref_yss == this_yss; +} +*/ + + +static int setup_frame_size( + struct VP9Decoder_s *pbi, + struct VP9_Common_s *cm, union param_u *params, + unsigned int *mmu_index_adr, + int print_header_info) { + int width, height; + struct BufferPool_s * const pool = cm->buffer_pool; + struct PIC_BUFFER_CONFIG_s *ybf; + int ret = 0; + + width = params->p.width; + height = params->p.height; + /*vp9_read_frame_size(rb, &width, &height);*/ + if (print_header_info) + pr_info(" * 16-bits w read : %d (width : %d)\n", width, height); + if (print_header_info) + pr_info + (" * 16-bits h read : %d (height : %d)\n", width, height); + + WRITE_VREG(HEVC_PARSER_PICTURE_SIZE, (height << 16) | width); + +#ifdef VP9_10B_MMU + /* if(cm->prev_fb_idx >= 0) release_unused_4k(cm->prev_fb_idx);*/ + /* cm->prev_fb_idx = cm->new_fb_idx;*/ + /*pr_info + ("[DEBUG DEBUG]Before alloc_mmu, prev_fb_idx : %d, new_fb_idx : %d\r\n", + cm->prev_fb_idx, cm->new_fb_idx);*/ + ret = vp9_alloc_mmu(pbi, + cm->new_fb_idx, + params->p.width, + params->p.height, + params->p.bit_depth, + mmu_index_adr); + if (ret != 0) { + pr_err("can't alloc need mmu1,idx %d ret =%d\n", + cm->new_fb_idx, + ret); + return ret; + } +#endif + + resize_context_buffers(cm, width, height); + setup_display_size(cm, params, print_header_info); +#if 0 + lock_buffer_pool(pool); + if (vp9_realloc_frame_buffer( + get_frame_new_buffer(cm), cm->width, cm->height, + cm->subsampling_x, cm->subsampling_y, +#if CONFIG_VP9_HIGHBITDEPTH + cm->use_highbitdepth, +#endif + VP9_DEC_BORDER_IN_PIXELS, + cm->byte_alignment, + &pool->frame_bufs[cm->new_fb_idx].raw_frame_buffer, + pool->get_fb_cb, pool->cb_priv)) { + unlock_buffer_pool(pool); + vpx_internal_error(&cm->error, VPX_CODEC_MEM_ERROR, + "Failed to allocate frame buffer"); + } + unlock_buffer_pool(pool); +#else + /* porting */ + ybf = get_frame_new_buffer(cm); + ybf->y_crop_width = width; + ybf->y_crop_height = height; + ybf->bit_depth = params->p.bit_depth; +#endif + pool->frame_bufs[cm->new_fb_idx].buf.subsampling_x = cm->subsampling_x; + pool->frame_bufs[cm->new_fb_idx].buf.subsampling_y = cm->subsampling_y; + pool->frame_bufs[cm->new_fb_idx].buf.bit_depth = + (unsigned int)cm->bit_depth; + pool->frame_bufs[cm->new_fb_idx].buf.color_space = cm->color_space; + return ret; +} + +static int setup_frame_size_with_refs( + struct VP9Decoder_s *pbi, + struct VP9_Common_s *cm, + union param_u *params, + unsigned int *mmu_index_adr, + int print_header_info) { + + int width, height; + int found = 0, i; + int has_valid_ref_frame = 0; + struct PIC_BUFFER_CONFIG_s *ybf; + struct BufferPool_s * const pool = cm->buffer_pool; + int ret = 0; + + for (i = 0; i < REFS_PER_FRAME; ++i) { + if ((params->p.same_frame_size >> + (REFS_PER_FRAME - i - 1)) & 0x1) { + struct PIC_BUFFER_CONFIG_s *const buf = + cm->frame_refs[i].buf; + /*if (print_header_info) + pr_info + ("1-bit same_frame_size[%d] read : 1\n", i);*/ + width = buf->y_crop_width; + height = buf->y_crop_height; + /*if (print_header_info) + pr_info + (" - same_frame_size width : %d\n", width);*/ + /*if (print_header_info) + pr_info + (" - same_frame_size height : %d\n", height);*/ + found = 1; + break; + } else { + /*if (print_header_info) + pr_info + ("1-bit same_frame_size[%d] read : 0\n", i);*/ + } + } + + if (!found) { + /*vp9_read_frame_size(rb, &width, &height);*/ + width = params->p.width; + height = params->p.height; + /*if (print_header_info) + pr_info + (" * 16-bits w read : %d (width : %d)\n", + width, height); + if (print_header_info) + pr_info + (" * 16-bits h read : %d (height : %d)\n", + width, height);*/ + } + + if (width <= 0 || height <= 0) { + pr_err("Error: Invalid frame size\r\n"); + return -1; + } + + params->p.width = width; + params->p.height = height; + + WRITE_VREG(HEVC_PARSER_PICTURE_SIZE, (height << 16) | width); +#ifdef VP9_10B_MMU + /*if(cm->prev_fb_idx >= 0) release_unused_4k(cm->prev_fb_idx); + cm->prev_fb_idx = cm->new_fb_idx;*/ +/* pr_info + ("[DEBUG DEBUG]Before alloc_mmu, prev_fb_idx : %d, new_fb_idx : %d\r\n", + cm->prev_fb_idx, cm->new_fb_idx);*/ + ret = vp9_alloc_mmu(pbi, cm->new_fb_idx, + params->p.width, params->p.height, + params->p.bit_depth, mmu_index_adr); + if (ret != 0) { + pr_err("can't alloc need mmu,idx %d\r\n", + cm->new_fb_idx); + return ret; + } +#endif + + /*Check to make sure at least one of frames that this frame references + has valid dimensions.*/ + for (i = 0; i < REFS_PER_FRAME; ++i) { + struct RefBuffer_s * const ref_frame = &cm->frame_refs[i]; + has_valid_ref_frame |= + valid_ref_frame_size(ref_frame->buf->y_crop_width, + ref_frame->buf->y_crop_height, + width, height); + } + if (!has_valid_ref_frame) { + pr_err("Error: Referenced frame has invalid size\r\n"); + return -1; + } +#if 0 + for (i = 0; i < REFS_PER_FRAME; ++i) { + struct RefBuffer_s * const ref_frame = + &cm->frame_refs[i]; + if (!valid_ref_frame_img_fmt( + ref_frame->buf->bit_depth, + ref_frame->buf->subsampling_x, + ref_frame->buf->subsampling_y, + cm->bit_depth, + cm->subsampling_x, + cm->subsampling_y)) + pr_err + ("Referenced frame incompatible color fmt\r\n"); + return -1; + } +#endif + resize_context_buffers(cm, width, height); + setup_display_size(cm, params, print_header_info); + +#if 0 + lock_buffer_pool(pool); + if (vp9_realloc_frame_buffer( + get_frame_new_buffer(cm), cm->width, cm->height, + cm->subsampling_x, cm->subsampling_y, +#if CONFIG_VP9_HIGHBITDEPTH + cm->use_highbitdepth, +#endif + VP9_DEC_BORDER_IN_PIXELS, + cm->byte_alignment, + &pool->frame_bufs[cm->new_fb_idx].raw_frame_buffer, + pool->get_fb_cb, + pool->cb_priv)) { + unlock_buffer_pool(pool); + vpx_internal_error(&cm->error, VPX_CODEC_MEM_ERROR, + "Failed to allocate frame buffer"); + } + unlock_buffer_pool(pool); +#else + /* porting */ + ybf = get_frame_new_buffer(cm); + ybf->y_crop_width = width; + ybf->y_crop_height = height; + ybf->bit_depth = params->p.bit_depth; +#endif + pool->frame_bufs[cm->new_fb_idx].buf.subsampling_x = cm->subsampling_x; + pool->frame_bufs[cm->new_fb_idx].buf.subsampling_y = cm->subsampling_y; + pool->frame_bufs[cm->new_fb_idx].buf.bit_depth = + (unsigned int)cm->bit_depth; + pool->frame_bufs[cm->new_fb_idx].buf.color_space = cm->color_space; + return ret; +} + +uint8_t print_header_info = 0; + +struct buff_s { + u32 buf_start; + u32 buf_size; + u32 buf_end; +} buff_t; + +struct BuffInfo_s { + u32 max_width; + u32 max_height; + u32 start_adr; + u32 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 seg_map; +#ifdef VP9_10B_MMU + struct buff_s mmu_vbh; + struct buff_s cm_header; +#endif + struct buff_s mpred_above; + struct buff_s mpred_mv; + struct buff_s rpm; + struct buff_s lmem; +} BuffInfo_t; + +#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 +#define DEC_RESULT_GET_DATA 7 +#define DEC_RESULT_GET_DATA_RETRY 8 + +static void vp9_work(struct work_struct *work); +#endif + +struct VP9Decoder_s { +#ifdef MULTI_INSTANCE_SUPPORT + unsigned char index; + + struct device *cma_dev; + 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; + + struct BuffInfo_s work_space_buf_store; + unsigned long buf_start; + u32 buf_size; + u32 cma_alloc_count; + unsigned long cma_alloc_addr; +#endif + unsigned char m_ins_flag; + char *provider_name; + union param_u param; + int frame_count; + int pic_count; + u32 stat; + struct timer_list timer; + u32 frame_dur; + u32 frame_ar; + int fatal_error; + uint8_t init_flag; + uint8_t process_busy; + int show_frame_num; + struct buff_s mc_buf_spec; + struct dec_sysinfo vvp9_amstream_dec_info; + void *rpm_addr; + void *lmem_addr; + dma_addr_t rpm_phy_addr; + dma_addr_t lmem_phy_addr; + unsigned short *lmem_ptr; + unsigned short *debug_ptr; + + void *prob_buffer_addr; + void *count_buffer_addr; + dma_addr_t prob_buffer_phy_addr; + dma_addr_t count_buffer_phy_addr; + +#if 1 + /*VP9_10B_MMU*/ + void *frame_mmu_map_addr; + dma_addr_t frame_mmu_map_phy_addr; +#endif + unsigned int use_cma_flag; + + struct BUF_s m_BUF[MAX_BUF_NUM]; + u32 used_buf_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]; + int buf_num; + int pic_num; + int lcu_size_log2; + unsigned int losless_comp_body_size; + + u32 video_signal_type; + + int pts_mode; + int last_lookup_pts; + int last_pts; + u64 last_lookup_pts_us64; + u64 last_pts_us64; + u64 shift_byte_count; + u32 shift_byte_count_lo; + u32 shift_byte_count_hi; + int pts_mode_switching_count; + int pts_mode_recovery_count; + + bool get_frame_dur; + u32 saved_resolution; + + /**/ + struct VP9_Common_s common; + struct RefCntBuffer_s *cur_buf; + int refresh_frame_flags; + uint8_t need_resync; + uint8_t hold_ref_buf; + uint8_t ready_for_new_data; + struct BufferPool_s vp9_buffer_pool; + + struct BuffInfo_s *work_space_buf; + struct buff_s *mc_buf; + + unsigned int frame_width; + unsigned int frame_height; + + unsigned short *rpm_ptr; + int init_pic_w; + int init_pic_h; + int lcu_total; + int lcu_size; + + int slice_type; + + int skip_flag; + int decode_idx; + int slice_idx; + uint8_t has_keyframe; + uint8_t wait_buf; + uint8_t error_flag; + + /* bit 0, for decoding; bit 1, for displaying */ + uint8_t ignore_bufmgr_error; + int PB_skip_mode; + int PB_skip_count_after_decoding; + /*hw*/ + + u32 pre_stream_offset; + + unsigned int dec_status; + u32 last_put_idx; + int new_frame_displayed; + void *mmu_box; + void *bmmu_box; +} VP9Decoder; + +static int vp9_print(struct VP9Decoder_s *pbi, + int flag, const char *fmt, ...) +{ +#define HEVC_PRINT_BUF 128 + unsigned char buf[HEVC_PRINT_BUF]; + int len = 0; + if (pbi == NULL || + (flag == 0) || + (debug & flag)) { + va_list args; + va_start(args, fmt); + if (pbi) + len = sprintf(buf, "[%d]", pbi->index); + vsnprintf(buf + len, HEVC_PRINT_BUF - len, fmt, args); + pr_info("%s", buf); + va_end(args); + } + return 0; +} + +#ifdef MULTI_INSTANCE_SUPPORT +static int vp9_print_cont(struct VP9Decoder_s *pbi, + int flag, const char *fmt, ...) +{ + unsigned char buf[HEVC_PRINT_BUF]; + int len = 0; + if (pbi == NULL || + (flag == 0) || + (debug & flag)) { + va_list args; + va_start(args, fmt); + vsnprintf(buf + len, HEVC_PRINT_BUF - len, fmt, args); + pr_info("%s", buf); + va_end(args); + } + return 0; +} +#endif + +#ifdef VP9_10B_MMU +int vp9_alloc_mmu( + struct VP9Decoder_s *pbi, + int cur_buf_idx, + int pic_width, + int pic_height, + unsigned short bit_depth, + unsigned int *mmu_index_adr) +{ + int bit_depth_10 = (bit_depth == VPX_BITS_10); + int picture_size; + int cur_mmu_4k_number; + + picture_size = compute_losless_comp_body_size(pic_width, pic_height, + bit_depth_10); + cur_mmu_4k_number = ((picture_size + (1 << 12) - 1) >> 12); + return decoder_mmu_box_alloc_idx( + pbi->mmu_box, + cur_buf_idx, + cur_mmu_4k_number, + mmu_index_adr); +} +#endif +static void decrease_ref_count(int idx, struct RefCntBuffer_s *const frame_bufs, + struct BufferPool_s *const pool) +{ + if (idx >= 0) { + --frame_bufs[idx].ref_count; + /*pr_info("[MMU DEBUG 7] dec ref_count[%d] : %d\r\n", idx, + frame_bufs[idx].ref_count);*/ + /*A worker may only get a free framebuffer index when + calling get_free_fb. But the private buffer is not set up + until finish decoding header. So any error happens during + decoding header, the frame_bufs will not have valid priv + buffer.*/ + + if (frame_bufs[idx].ref_count == 0 && + frame_bufs[idx].raw_frame_buffer.priv) + vp9_release_frame_buffer + (&frame_bufs[idx].raw_frame_buffer); + } +} + +static void generate_next_ref_frames(struct VP9Decoder_s *pbi) +{ + struct VP9_Common_s *const cm = &pbi->common; + struct RefCntBuffer_s *frame_bufs = cm->buffer_pool->frame_bufs; + struct BufferPool_s *const pool = cm->buffer_pool; + int mask, ref_index = 0; + + /* Generate next_ref_frame_map.*/ + lock_buffer_pool(pool); + for (mask = pbi->refresh_frame_flags; mask; mask >>= 1) { + if (mask & 1) { + cm->next_ref_frame_map[ref_index] = cm->new_fb_idx; + ++frame_bufs[cm->new_fb_idx].ref_count; + /*pr_info("[MMU DEBUG 4] inc ref_count[%d] : %d\r\n", + cm->new_fb_idx, frame_bufs[cm->new_fb_idx].ref_count);*/ + } else + cm->next_ref_frame_map[ref_index] = + cm->ref_frame_map[ref_index]; + /* Current thread holds the reference frame.*/ + if (cm->ref_frame_map[ref_index] >= 0) { + ++frame_bufs[cm->ref_frame_map[ref_index]].ref_count; + /*pr_info + ("[MMU DEBUG 5] inc ref_count[%d] : %d\r\n", + cm->ref_frame_map[ref_index], + frame_bufs[cm->ref_frame_map[ref_index]].ref_count);*/ + } + ++ref_index; + } + + for (; ref_index < REF_FRAMES; ++ref_index) { + cm->next_ref_frame_map[ref_index] = + cm->ref_frame_map[ref_index]; + /* Current thread holds the reference frame.*/ + if (cm->ref_frame_map[ref_index] >= 0) { + ++frame_bufs[cm->ref_frame_map[ref_index]].ref_count; + /*pr_info("[MMU DEBUG 6] inc ref_count[%d] : %d\r\n", + cm->ref_frame_map[ref_index], + frame_bufs[cm->ref_frame_map[ref_index]].ref_count);*/ + } + } + unlock_buffer_pool(pool); + return; +} + +static void refresh_ref_frames(struct VP9Decoder_s *pbi) + +{ + struct VP9_Common_s *const cm = &pbi->common; + struct BufferPool_s *pool = cm->buffer_pool; + struct RefCntBuffer_s *frame_bufs = cm->buffer_pool->frame_bufs; + int mask, ref_index = 0; + + lock_buffer_pool(pool); + for (mask = pbi->refresh_frame_flags; mask; mask >>= 1) { + const int old_idx = cm->ref_frame_map[ref_index]; + /*Current thread releases the holding of reference frame.*/ + decrease_ref_count(old_idx, frame_bufs, pool); + + /*Release the reference frame in reference map.*/ + if ((mask & 1) && old_idx >= 0) + decrease_ref_count(old_idx, frame_bufs, pool); + cm->ref_frame_map[ref_index] = + cm->next_ref_frame_map[ref_index]; + ++ref_index; + } + + /*Current thread releases the holding of reference frame.*/ + for (; ref_index < REF_FRAMES && !cm->show_existing_frame; + ++ref_index) { + const int old_idx = cm->ref_frame_map[ref_index]; + decrease_ref_count(old_idx, frame_bufs, pool); + cm->ref_frame_map[ref_index] = + cm->next_ref_frame_map[ref_index]; + } + unlock_buffer_pool(pool); + return; +} +int vp9_bufmgr_process(struct VP9Decoder_s *pbi, union param_u *params) +{ + struct VP9_Common_s *const cm = &pbi->common; + struct BufferPool_s *pool = cm->buffer_pool; + struct RefCntBuffer_s *frame_bufs = cm->buffer_pool->frame_bufs; + int i; + int ret; + + pbi->ready_for_new_data = 0; + + if (pbi->has_keyframe == 0 && + params->p.frame_type != KEY_FRAME){ + on_no_keyframe_skiped++; + return -2; + } + pbi->has_keyframe = 1; + on_no_keyframe_skiped = 0; +#ifdef VP9_10B_MMU + if (cm->prev_fb_idx >= 0) { + long used_4k_num = (READ_VREG(HEVC_SAO_MMU_STATUS) >> 16); + decoder_mmu_box_free_idx_tail(pbi->mmu_box, + cm->prev_fb_idx, used_4k_num); + } +#endif + if (cm->new_fb_idx >= 0 + && frame_bufs[cm->new_fb_idx].ref_count == 0){ + vp9_release_frame_buffer + (&frame_bufs[cm->new_fb_idx].raw_frame_buffer); + } + /*pr_info("Before get_free_fb, prev_fb_idx : %d, new_fb_idx : %d\r\n", + cm->prev_fb_idx, cm->new_fb_idx);*/ + cm->new_fb_idx = get_free_fb(cm); + cm->cur_frame = &pool->frame_bufs[cm->new_fb_idx]; + /*if (debug & VP9_DEBUG_BUFMGR) + pr_info("[VP9 DEBUG]%s(get_free_fb): %d\r\n", __func__, + cm->new_fb_idx);*/ + if (cm->new_fb_idx == INVALID_IDX) { + pr_info("get_free_fb error\r\n"); + return -1; + } + pbi->cur_buf = &frame_bufs[cm->new_fb_idx]; +#ifdef VP9_10B_MMU + /* moved to after picture size ready + alloc_mmu(cm, params->p.width, params->p.height, + params->p.bit_depth, pbi->frame_mmu_map_addr);*/ + cm->prev_fb_idx = cm->new_fb_idx; +#endif + /*read_uncompressed_header()*/ + cm->last_frame_type = cm->frame_type; + cm->last_intra_only = cm->intra_only; + cm->profile = params->p.profile; + if (cm->profile >= MAX_PROFILES) { + pr_err("Error: Unsupported profile %d\r\n", cm->profile); + return -1; + } + cm->show_existing_frame = params->p.show_existing_frame; + if (cm->show_existing_frame) { + /* Show an existing frame directly.*/ + int frame_to_show_idx = params->p.frame_to_show_idx; + int frame_to_show; + if (frame_to_show_idx >= REF_FRAMES) { + pr_info("frame_to_show_idx %d exceed max index\r\n", + frame_to_show_idx); + return -1; + } + + frame_to_show = cm->ref_frame_map[frame_to_show_idx]; + /*pr_info("frame_to_show %d\r\n", frame_to_show);*/ + lock_buffer_pool(pool); + if (frame_to_show < 0 || + frame_bufs[frame_to_show].ref_count < 1) { + unlock_buffer_pool(pool); + pr_err + ("Error:Buffer %d does not contain a decoded frame", + frame_to_show); + return -1; + } + + ref_cnt_fb(frame_bufs, &cm->new_fb_idx, frame_to_show); + unlock_buffer_pool(pool); + pbi->refresh_frame_flags = 0; + /*cm->lf.filter_level = 0;*/ + cm->show_frame = 1; + + /* + if (pbi->frame_parallel_decode) { + for (i = 0; i < REF_FRAMES; ++i) + cm->next_ref_frame_map[i] = + cm->ref_frame_map[i]; + } + */ + /* do not decode, search next start code */ + return 1; + } + cm->frame_type = params->p.frame_type; + cm->show_frame = params->p.show_frame; + cm->error_resilient_mode = params->p.error_resilient_mode; + + + if (cm->frame_type == KEY_FRAME) { + pbi->refresh_frame_flags = (1 << REF_FRAMES) - 1; + + for (i = 0; i < REFS_PER_FRAME; ++i) { + cm->frame_refs[i].idx = INVALID_IDX; + cm->frame_refs[i].buf = NULL; + } + + ret = setup_frame_size(pbi, + cm, params, pbi->frame_mmu_map_addr, + print_header_info); + if (ret) + return -1; + if (pbi->need_resync) { + memset(&cm->ref_frame_map, -1, + sizeof(cm->ref_frame_map)); + pbi->need_resync = 0; + } + } else { + cm->intra_only = cm->show_frame ? 0 : params->p.intra_only; + /*if (print_header_info) { + if (cm->show_frame) + pr_info + ("intra_only set to 0 because of show_frame\n"); + else + pr_info + ("1-bit intra_only read: %d\n", cm->intra_only); + }*/ + + + cm->reset_frame_context = cm->error_resilient_mode ? + 0 : params->p.reset_frame_context; + if (print_header_info) { + if (cm->error_resilient_mode) + pr_info + ("reset to 0 error_resilient_mode\n"); + else + pr_info + (" * 2-bits reset_frame_context read : %d\n", + cm->reset_frame_context); + } + + if (cm->intra_only) { + if (cm->profile > PROFILE_0) { + /*read_bitdepth_colorspace_sampling(cm, + rb, print_header_info);*/ + } else { + /*NOTE: The intra-only frame header + does not include the specification + of either the color format or color sub-sampling + in profile 0. VP9 specifies that the default + color format should be YUV 4:2:0 in this + case (normative).*/ + cm->color_space = VPX_CS_BT_601; + cm->subsampling_y = cm->subsampling_x = 1; + cm->bit_depth = VPX_BITS_8; + cm->use_highbitdepth = 0; + } + + pbi->refresh_frame_flags = + params->p.refresh_frame_flags; + /*if (print_header_info) + pr_info("*%d-bits refresh_frame read:0x%x\n", + REF_FRAMES, pbi->refresh_frame_flags);*/ + ret = setup_frame_size(pbi, + cm, + params, + pbi->frame_mmu_map_addr, + print_header_info); + if (ret) { + return -1; + } + if (pbi->need_resync) { + memset(&cm->ref_frame_map, -1, + sizeof(cm->ref_frame_map)); + pbi->need_resync = 0; + } + } else if (pbi->need_resync != 1) { /* Skip if need resync */ + pbi->refresh_frame_flags = + params->p.refresh_frame_flags; + if (print_header_info) + pr_info + ("*%d-bits refresh_frame read:0x%x\n", + REF_FRAMES, pbi->refresh_frame_flags); + for (i = 0; i < REFS_PER_FRAME; ++i) { + const int ref = + (params->p.ref_info >> + (((REFS_PER_FRAME-i-1)*4)+1)) + & 0x7; + const int idx = + cm->ref_frame_map[ref]; + struct RefBuffer_s * const ref_frame = + &cm->frame_refs[i]; + if (print_header_info) + pr_info + ("*%d-bits ref[%d]read:%d\n", + REF_FRAMES_LOG2, i, ref); + ref_frame->idx = idx; + ref_frame->buf = &frame_bufs[idx].buf; + cm->ref_frame_sign_bias[LAST_FRAME + i] + = (params->p.ref_info >> + ((REFS_PER_FRAME-i-1)*4)) & 0x1; + if (print_header_info) + pr_info + ("1bit ref_frame_sign_bias"); + /*pr_info + ("%dread: %d\n", + LAST_FRAME+i, + cm->ref_frame_sign_bias + [LAST_FRAME + i]);*/ + /*pr_info + ("[VP9 DEBUG]%s(get ref):%d\r\n", + __func__, ref_frame->idx);*/ + + } + + ret = setup_frame_size_with_refs( + pbi, + cm, + params, + pbi->frame_mmu_map_addr, + print_header_info); + if (ret) { + return -1; + } + for (i = 0; i < REFS_PER_FRAME; ++i) { + /*struct RefBuffer_s *const ref_buf = + &cm->frame_refs[i];*/ + /* to do: + vp9_setup_scale_factors_for_frame*/ + } + } + } + + get_frame_new_buffer(cm)->bit_depth = cm->bit_depth; + get_frame_new_buffer(cm)->color_space = cm->color_space; + get_frame_new_buffer(cm)->slice_type = cm->frame_type; + + if (pbi->need_resync) + pr_err + ("Error: Keyframe/intra-only frame required to reset\r\n"); + generate_next_ref_frames(pbi); + pbi->hold_ref_buf = 1; + +#if 0 + if (frame_is_intra_only(cm) || cm->error_resilient_mode) + vp9_setup_past_independence(cm); + setup_loopfilter(&cm->lf, rb, print_header_info); + setup_quantization(cm, &pbi->mb, rb, print_header_info); + setup_segmentation(&cm->seg, rb, print_header_info); + setup_segmentation_dequant(cm, print_header_info); + + setup_tile_info(cm, rb, print_header_info); + sz = vp9_rb_read_literal(rb, 16); + if (print_header_info) + pr_info(" * 16-bits size read : %d (0x%x)\n", sz, sz); + + if (sz == 0) + vpx_internal_error(&cm->error, VPX_CODEC_CORRUPT_FRAME, + "Invalid header size"); +#endif + /*end read_uncompressed_header()*/ + cm->use_prev_frame_mvs = !cm->error_resilient_mode && + cm->width == cm->last_width && + cm->height == cm->last_height && + !cm->last_intra_only && + cm->last_show_frame && + (cm->last_frame_type != KEY_FRAME); + + /*pr_info + ("set use_prev_frame_mvs to %d (last_width %d last_height %d", + cm->use_prev_frame_mvs, cm->last_width, cm->last_height); + pr_info + (" last_intra_only %d last_show_frame %d last_frame_type %d)\n", + cm->last_intra_only, cm->last_show_frame, cm->last_frame_type);*/ + return 0; +} + + +void swap_frame_buffers(struct VP9Decoder_s *pbi) +{ + int ref_index = 0; + struct VP9_Common_s *const cm = &pbi->common; + struct BufferPool_s *const pool = cm->buffer_pool; + struct RefCntBuffer_s *const frame_bufs = cm->buffer_pool->frame_bufs; + refresh_ref_frames(pbi); + pbi->hold_ref_buf = 0; + cm->frame_to_show = get_frame_new_buffer(cm); + + /*if (!pbi->frame_parallel_decode || !cm->show_frame) {*/ + lock_buffer_pool(pool); + --frame_bufs[cm->new_fb_idx].ref_count; + /*pr_info("[MMU DEBUG 8] dec ref_count[%d] : %d\r\n", cm->new_fb_idx, + frame_bufs[cm->new_fb_idx].ref_count);*/ + unlock_buffer_pool(pool); + /*}*/ + + /*Invalidate these references until the next frame starts.*/ + for (ref_index = 0; ref_index < 3; ref_index++) + cm->frame_refs[ref_index].idx = -1; +} + +#if 0 +static void check_resync(vpx_codec_alg_priv_t *const ctx, + const struct VP9Decoder_s *const pbi) +{ + /* Clear resync flag if worker got a key frame or intra only frame.*/ + if (ctx->need_resync == 1 && pbi->need_resync == 0 && + (pbi->common.intra_only || pbi->common.frame_type == KEY_FRAME)) + ctx->need_resync = 0; +} +#endif + +int vp9_get_raw_frame(struct VP9Decoder_s *pbi, struct PIC_BUFFER_CONFIG_s *sd) +{ + struct VP9_Common_s *const cm = &pbi->common; + int ret = -1; + + if (pbi->ready_for_new_data == 1) + return ret; + + pbi->ready_for_new_data = 1; + + /* no raw frame to show!!! */ + if (!cm->show_frame) + return ret; + + pbi->ready_for_new_data = 1; + + *sd = *cm->frame_to_show; + ret = 0; + + return ret; +} + +int vp9_bufmgr_init(struct VP9Decoder_s *pbi, struct BuffInfo_s *buf_spec_i, + struct buff_s *mc_buf_i) { + struct VP9_Common_s *cm = &pbi->common; + + /*memset(pbi, 0, sizeof(struct VP9Decoder));*/ + pbi->frame_count = 0; + pbi->pic_count = 0; + pbi->pre_stream_offset = 0; + cm->buffer_pool = &pbi->vp9_buffer_pool; + spin_lock_init(&cm->buffer_pool->lock); + cm->prev_fb_idx = INVALID_IDX; + cm->new_fb_idx = INVALID_IDX; + pr_info + ("After vp9_bufmgr_init, prev_fb_idx : %d, new_fb_idx : %d\r\n", + cm->prev_fb_idx, cm->new_fb_idx); + pbi->need_resync = 1; + /* Initialize the references to not point to any frame buffers.*/ + memset(&cm->ref_frame_map, -1, sizeof(cm->ref_frame_map)); + memset(&cm->next_ref_frame_map, -1, sizeof(cm->next_ref_frame_map)); + cm->current_video_frame = 0; + pbi->ready_for_new_data = 1; + + /* private init */ + pbi->work_space_buf = buf_spec_i; + pbi->mc_buf = mc_buf_i; + pbi->rpm_addr = NULL; + pbi->lmem_addr = NULL; + + pbi->use_cma_flag = 0; + pbi->decode_idx = 0; + pbi->slice_idx = 0; + /*int m_uiMaxCUWidth = 1<<7;*/ + /*int m_uiMaxCUHeight = 1<<7;*/ + pbi->has_keyframe = 0; + pbi->skip_flag = 0; + pbi->wait_buf = 0; + pbi->error_flag = 0; + + pbi->pts_mode = PTS_NORMAL; + pbi->last_pts = 0; + pbi->last_lookup_pts = 0; + pbi->last_pts_us64 = 0; + pbi->last_lookup_pts_us64 = 0; + pbi->shift_byte_count = 0; + pbi->shift_byte_count_lo = 0; + pbi->shift_byte_count_hi = 0; + pbi->pts_mode_switching_count = 0; + pbi->pts_mode_recovery_count = 0; + + pbi->buf_num = 0; + pbi->pic_num = 0; + + return 0; +} + + +int vp9_bufmgr_postproc(struct VP9Decoder_s *pbi) +{ + struct VP9_Common_s *cm = &pbi->common; + struct PIC_BUFFER_CONFIG_s sd; + swap_frame_buffers(pbi); + if (!cm->show_existing_frame) { + cm->last_show_frame = cm->show_frame; + cm->prev_frame = cm->cur_frame; +#if 0 + if (cm->seg.enabled && !pbi->frame_parallel_decode) + vp9_swap_current_and_last_seg_map(cm); +#endif + } + cm->last_width = cm->width; + cm->last_height = cm->height; + if (cm->show_frame) + cm->current_video_frame++; + + if (vp9_get_raw_frame(pbi, &sd) == 0) { + /*pr_info("Display frame index %d\r\n", sd.index);*/ + sd.stream_offset = pbi->pre_stream_offset; + prepare_display_buf(pbi, &sd); + pbi->pre_stream_offset = READ_VREG(HEVC_SHIFT_BYTE_COUNT); + } /*else + pr_info + ("Not display this frame,ready_for_new_data%d show_frame%d\r\n", + pbi->ready_for_new_data, cm->show_frame);*/ + return 0; +} + +struct VP9Decoder_s vp9_decoder; +union param_u vp9_param; + +/************************************************** + +VP9 buffer management end + +***************************************************/ + + +#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 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 + 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 force_w_h; + +static u32 force_fps; + + +const u32 vp9_version = 201602101; +static u32 debug; +static u32 radr; +static u32 rval; +static u32 pop_shorts; +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 = 7; +#endif +static u32 buf_alloc_depth = 10; +static u32 buf_alloc_size; +/* +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 vp9) 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 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 (vp9_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 parser_sei_enable = 1;*/ + +static u32 max_buf_num = 12; + + +static DEFINE_MUTEX(vvp9_mutex); +#ifndef MULTI_INSTANCE_SUPPORT +static struct device *cma_dev; +#endif + +#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 VP9_ADAPT_PROB_REG HEVC_ASSIST_SCRATCH_3 +#define VP9_MMU_MAP_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 VP9_PROB_SWAP_BUFFER HEVC_ASSIST_SCRATCH_9 +#define VP9_COUNT_SWAP_BUFFER HEVC_ASSIST_SCRATCH_A +#define VP9_SEG_MAP_BUFFER HEVC_ASSIST_SCRATCH_B +#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 +#ifdef MULTI_INSTANCE_SUPPORT +#define HEVC_DECODE_COUNT HEVC_ASSIST_SCRATCH_M +#define HEVC_DECODE_SIZE HEVC_ASSIST_SCRATCH_N +#else +#define HEVC_DECODE_PIC_BEGIN_REG HEVC_ASSIST_SCRATCH_M +#define HEVC_DECODE_PIC_NUM_REG HEVC_ASSIST_SCRATCH_N +#endif +#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 DECODE_MODE HEVC_ASSIST_SCRATCH_J +#define DECODE_STOP_POS HEVC_ASSIST_SCRATCH_K + +#ifdef MULTI_INSTANCE_SUPPORT +#define RPM_BUF_SIZE (0x400 * 2) +#else +#define RPM_BUF_SIZE (0x80*2) +#endif +#define LMEM_BUF_SIZE (0x400 * 2) + +#define WORK_BUF_SPEC_NUM 2 +static struct BuffInfo_s amvvp9_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 = { + /* DBLK -> Max 256(4096/16) LCU, + each para 1024bytes(total:0x40000), + data 1024bytes(total:0x40000)*/ + .buf_size = 0x80000, + }, + .dblk_data = { + .buf_size = 0x80000, + }, + .seg_map = { + /*4096x2304/64/64 *24 = 0xd800 Bytes*/ + .buf_size = 0xd800, + }, +#ifdef VP9_10B_MMU + .mmu_vbh = { + .buf_size = 0x5000, /*2*16*(more than 2304)/4, 4K*/ + }, + .cm_header = { + /*add one for keeper.*/ + .buf_size = MMU_COMPRESS_HEADER_SIZE * + (FRAME_BUFFERS + 1), + /* 0x44000 = ((1088*2*1024*4)/32/4)*(32/8) */ + }, +#endif + .mpred_above = { + .buf_size = 0x10000, /* 2 * size of hevc*/ + }, + .mpred_mv = {/* 1080p, 0x40000 per buffer */ + .buf_size = 0x40000 * FRAME_BUFFERS, + }, + .rpm = { + .buf_size = RPM_BUF_SIZE, + }, + .lmem = { + .buf_size = 0x400 * 2, + } + }, + { + .max_width = 4096, + .max_height = 2304, + .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 1024bytes(total:0x40000), + data 1024bytes(total:0x40000)*/ + .buf_size = 0x80000, + }, + .dblk_data = { + .buf_size = 0x80000, + }, + .seg_map = { + /*4096x2304/64/64 *24 = 0xd800 Bytes*/ + .buf_size = 0xd800, + }, +#ifdef VP9_10B_MMU + .mmu_vbh = { + .buf_size = 0x5000,/*2*16*(more than 2304)/4, 4K*/ + }, + .cm_header = { + /*add one for keeper.*/ + .buf_size = MMU_COMPRESS_HEADER_SIZE * + (FRAME_BUFFERS + 1), + /* 0x44000 = ((1088*2*1024*4)/32/4)*(32/8) */ + }, +#endif + .mpred_above = { + .buf_size = 0x10000, /* 2 * size of hevc*/ + }, + .mpred_mv = { + /* .buf_size = 0x100000*16, + //4k2k , 0x100000 per buffer */ + /* 4096x2304 , 0x120000 per buffer */ + .buf_size = 0x120000 * FRAME_BUFFERS, + }, + .rpm = { + .buf_size = RPM_BUF_SIZE, + }, + .lmem = { + .buf_size = 0x400 * 2, + } + } +}; + + +/*Losless compression body buffer size 4K per 64x32 (jt)*/ +int compute_losless_comp_body_size(int width, int height, + uint8_t is_bit_depth_10) +{ + int width_x64; + int height_x32; + int bsize; + width_x64 = width + 63; + width_x64 >>= 6; + height_x32 = height + 31; + height_x32 >>= 5; +#ifdef VP9_10B_MMU + bsize = (is_bit_depth_10?4096:3200)*width_x64*height_x32; +#else + bsize = (is_bit_depth_10?4096:3072)*width_x64*height_x32; +#endif + if (debug & VP9_DEBUG_BUFMGR_MORE) + pr_info("%s(%d,%d,%d)=>%d\n", + __func__, width, height, + is_bit_depth_10, bsize); + + 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; + if (debug & VP9_DEBUG_BUFMGR_MORE) + pr_info("%s(%d,%d)=>%d\n", + __func__, width, height, + hsize); + + return hsize; +} + +static void init_buff_spec(struct VP9Decoder_s *pbi, + struct BuffInfo_s *buf_spec) +{ + void *mem_start_virt; + 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->seg_map.buf_start = + buf_spec->dblk_data.buf_start + buf_spec->dblk_data.buf_size; +#ifdef VP9_10B_MMU + buf_spec->mmu_vbh.buf_start = + buf_spec->seg_map.buf_start + buf_spec->seg_map.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; +#else + buf_spec->mpred_above.buf_start = + buf_spec->seg_map.buf_start + buf_spec->seg_map.buf_size; +#endif + buf_spec->mpred_mv.buf_start = + buf_spec->mpred_above.buf_start + + buf_spec->mpred_above.buf_size; + if (debug & VP9_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 & VP9_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; + } + } + mem_start_virt = codec_mm_phys_to_virt(buf_spec->dblk_para.buf_start); + if (mem_start_virt) { + memset(mem_start_virt, 0, buf_spec->dblk_para.buf_size); + codec_mm_dma_flush(mem_start_virt, + buf_spec->dblk_para.buf_size, + DMA_TO_DEVICE); + } else { + /*not virt for tvp playing, + may need clear on ucode.*/ + pr_err("mem_start_virt failed\n"); + } + if (debug) { + pr_info("%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) { + pr_info("ipp.buf_start :%x\n", + buf_spec->ipp.buf_start); + pr_info("sao_abv.buf_start :%x\n", + buf_spec->sao_abv.buf_start); + pr_info("sao_vb.buf_start :%x\n", + buf_spec->sao_vb.buf_start); + pr_info("short_term_rps.buf_start :%x\n", + buf_spec->short_term_rps.buf_start); + pr_info("vps.buf_start :%x\n", + buf_spec->vps.buf_start); + pr_info("sps.buf_start :%x\n", + buf_spec->sps.buf_start); + pr_info("pps.buf_start :%x\n", + buf_spec->pps.buf_start); + pr_info("sao_up.buf_start :%x\n", + buf_spec->sao_up.buf_start); + pr_info("swap_buf.buf_start :%x\n", + buf_spec->swap_buf.buf_start); + pr_info("swap_buf2.buf_start :%x\n", + buf_spec->swap_buf2.buf_start); + pr_info("scalelut.buf_start :%x\n", + buf_spec->scalelut.buf_start); + pr_info("dblk_para.buf_start :%x\n", + buf_spec->dblk_para.buf_start); + pr_info("dblk_data.buf_start :%x\n", + buf_spec->dblk_data.buf_start); + pr_info("seg_map.buf_start :%x\n", + buf_spec->seg_map.buf_start); +#ifdef VP9_10B_MMU + pr_info("mmu_vbh.buf_start :%x\n", + buf_spec->mmu_vbh.buf_start); + pr_info("cm_header.buf_start :%x\n", + buf_spec->cm_header.buf_start); +#endif + pr_info("mpred_above.buf_start :%x\n", + buf_spec->mpred_above.buf_start); + pr_info("mpred_mv.buf_start :%x\n", + buf_spec->mpred_mv.buf_start); + if ((debug & VP9_DEBUG_SEND_PARAM_WITH_REG) == 0) { + pr_info("rpm.buf_start :%x\n", + buf_spec->rpm.buf_start); + } + } + +} + +/*==================================================== +======================================================================== +vp9_prob define +========================================================================*/ +#define VP9_PARTITION_START 0 +#define VP9_PARTITION_SIZE_STEP (3 * 4) +#define VP9_PARTITION_ONE_SIZE (4 * VP9_PARTITION_SIZE_STEP) +#define VP9_PARTITION_KEY_START 0 +#define VP9_PARTITION_P_START VP9_PARTITION_ONE_SIZE +#define VP9_PARTITION_SIZE (2 * VP9_PARTITION_ONE_SIZE) +#define VP9_SKIP_START (VP9_PARTITION_START + VP9_PARTITION_SIZE) +#define VP9_SKIP_SIZE 4 /* only use 3*/ +#define VP9_TX_MODE_START (VP9_SKIP_START+VP9_SKIP_SIZE) +#define VP9_TX_MODE_8_0_OFFSET 0 +#define VP9_TX_MODE_8_1_OFFSET 1 +#define VP9_TX_MODE_16_0_OFFSET 2 +#define VP9_TX_MODE_16_1_OFFSET 4 +#define VP9_TX_MODE_32_0_OFFSET 6 +#define VP9_TX_MODE_32_1_OFFSET 9 +#define VP9_TX_MODE_SIZE 12 +#define VP9_COEF_START (VP9_TX_MODE_START+VP9_TX_MODE_SIZE) +#define VP9_COEF_BAND_0_OFFSET 0 +#define VP9_COEF_BAND_1_OFFSET (VP9_COEF_BAND_0_OFFSET + 3 * 3 + 1) +#define VP9_COEF_BAND_2_OFFSET (VP9_COEF_BAND_1_OFFSET + 6 * 3) +#define VP9_COEF_BAND_3_OFFSET (VP9_COEF_BAND_2_OFFSET + 6 * 3) +#define VP9_COEF_BAND_4_OFFSET (VP9_COEF_BAND_3_OFFSET + 6 * 3) +#define VP9_COEF_BAND_5_OFFSET (VP9_COEF_BAND_4_OFFSET + 6 * 3) +#define VP9_COEF_SIZE_ONE_SET 100 /* ((3 +5*6)*3 + 1 padding)*/ +#define VP9_COEF_4X4_START (VP9_COEF_START + 0 * VP9_COEF_SIZE_ONE_SET) +#define VP9_COEF_8X8_START (VP9_COEF_START + 4 * VP9_COEF_SIZE_ONE_SET) +#define VP9_COEF_16X16_START (VP9_COEF_START + 8 * VP9_COEF_SIZE_ONE_SET) +#define VP9_COEF_32X32_START (VP9_COEF_START + 12 * VP9_COEF_SIZE_ONE_SET) +#define VP9_COEF_SIZE_PLANE (2 * VP9_COEF_SIZE_ONE_SET) +#define VP9_COEF_SIZE (4 * 2 * 2 * VP9_COEF_SIZE_ONE_SET) +#define VP9_INTER_MODE_START (VP9_COEF_START+VP9_COEF_SIZE) +#define VP9_INTER_MODE_SIZE 24 /* only use 21 ( #*7)*/ +#define VP9_INTERP_START (VP9_INTER_MODE_START+VP9_INTER_MODE_SIZE) +#define VP9_INTERP_SIZE 8 +#define VP9_INTRA_INTER_START (VP9_INTERP_START+VP9_INTERP_SIZE) +#define VP9_INTRA_INTER_SIZE 4 +#define VP9_INTERP_INTRA_INTER_START VP9_INTERP_START +#define VP9_INTERP_INTRA_INTER_SIZE (VP9_INTERP_SIZE + VP9_INTRA_INTER_SIZE) +#define VP9_COMP_INTER_START \ + (VP9_INTERP_INTRA_INTER_START+VP9_INTERP_INTRA_INTER_SIZE) +#define VP9_COMP_INTER_SIZE 5 +#define VP9_COMP_REF_START (VP9_COMP_INTER_START+VP9_COMP_INTER_SIZE) +#define VP9_COMP_REF_SIZE 5 +#define VP9_SINGLE_REF_START (VP9_COMP_REF_START+VP9_COMP_REF_SIZE) +#define VP9_SINGLE_REF_SIZE 10 +#define VP9_REF_MODE_START VP9_COMP_INTER_START +#define VP9_REF_MODE_SIZE \ + (VP9_COMP_INTER_SIZE+VP9_COMP_REF_SIZE+VP9_SINGLE_REF_SIZE) +#define VP9_IF_Y_MODE_START (VP9_REF_MODE_START+VP9_REF_MODE_SIZE) +#define VP9_IF_Y_MODE_SIZE 36 +#define VP9_IF_UV_MODE_START (VP9_IF_Y_MODE_START+VP9_IF_Y_MODE_SIZE) +#define VP9_IF_UV_MODE_SIZE 92 /* only use 90*/ +#define VP9_MV_JOINTS_START (VP9_IF_UV_MODE_START+VP9_IF_UV_MODE_SIZE) +#define VP9_MV_JOINTS_SIZE 3 +#define VP9_MV_SIGN_0_START (VP9_MV_JOINTS_START+VP9_MV_JOINTS_SIZE) +#define VP9_MV_SIGN_0_SIZE 1 +#define VP9_MV_CLASSES_0_START (VP9_MV_SIGN_0_START+VP9_MV_SIGN_0_SIZE) +#define VP9_MV_CLASSES_0_SIZE 10 +#define VP9_MV_CLASS0_0_START (VP9_MV_CLASSES_0_START+VP9_MV_CLASSES_0_SIZE) +#define VP9_MV_CLASS0_0_SIZE 1 +#define VP9_MV_BITS_0_START (VP9_MV_CLASS0_0_START+VP9_MV_CLASS0_0_SIZE) +#define VP9_MV_BITS_0_SIZE 10 +#define VP9_MV_SIGN_1_START (VP9_MV_BITS_0_START+VP9_MV_BITS_0_SIZE) +#define VP9_MV_SIGN_1_SIZE 1 +#define VP9_MV_CLASSES_1_START \ + (VP9_MV_SIGN_1_START+VP9_MV_SIGN_1_SIZE) +#define VP9_MV_CLASSES_1_SIZE 10 +#define VP9_MV_CLASS0_1_START \ + (VP9_MV_CLASSES_1_START+VP9_MV_CLASSES_1_SIZE) +#define VP9_MV_CLASS0_1_SIZE 1 +#define VP9_MV_BITS_1_START \ + (VP9_MV_CLASS0_1_START+VP9_MV_CLASS0_1_SIZE) +#define VP9_MV_BITS_1_SIZE 10 +#define VP9_MV_CLASS0_FP_0_START \ + (VP9_MV_BITS_1_START+VP9_MV_BITS_1_SIZE) +#define VP9_MV_CLASS0_FP_0_SIZE 9 +#define VP9_MV_CLASS0_FP_1_START \ + (VP9_MV_CLASS0_FP_0_START+VP9_MV_CLASS0_FP_0_SIZE) +#define VP9_MV_CLASS0_FP_1_SIZE 9 +#define VP9_MV_CLASS0_HP_0_START \ + (VP9_MV_CLASS0_FP_1_START+VP9_MV_CLASS0_FP_1_SIZE) +#define VP9_MV_CLASS0_HP_0_SIZE 2 +#define VP9_MV_CLASS0_HP_1_START \ + (VP9_MV_CLASS0_HP_0_START+VP9_MV_CLASS0_HP_0_SIZE) +#define VP9_MV_CLASS0_HP_1_SIZE 2 +#define VP9_MV_START VP9_MV_JOINTS_START +#define VP9_MV_SIZE 72 /*only use 69*/ + +#define VP9_TOTAL_SIZE (VP9_MV_START + VP9_MV_SIZE) + + +/*======================================================================== + vp9_count_mem define +========================================================================*/ +#define VP9_COEF_COUNT_START 0 +#define VP9_COEF_COUNT_BAND_0_OFFSET 0 +#define VP9_COEF_COUNT_BAND_1_OFFSET \ + (VP9_COEF_COUNT_BAND_0_OFFSET + 3*5) +#define VP9_COEF_COUNT_BAND_2_OFFSET \ + (VP9_COEF_COUNT_BAND_1_OFFSET + 6*5) +#define VP9_COEF_COUNT_BAND_3_OFFSET \ + (VP9_COEF_COUNT_BAND_2_OFFSET + 6*5) +#define VP9_COEF_COUNT_BAND_4_OFFSET \ + (VP9_COEF_COUNT_BAND_3_OFFSET + 6*5) +#define VP9_COEF_COUNT_BAND_5_OFFSET \ + (VP9_COEF_COUNT_BAND_4_OFFSET + 6*5) +#define VP9_COEF_COUNT_SIZE_ONE_SET 165 /* ((3 +5*6)*5 */ +#define VP9_COEF_COUNT_4X4_START \ + (VP9_COEF_COUNT_START + 0*VP9_COEF_COUNT_SIZE_ONE_SET) +#define VP9_COEF_COUNT_8X8_START \ + (VP9_COEF_COUNT_START + 4*VP9_COEF_COUNT_SIZE_ONE_SET) +#define VP9_COEF_COUNT_16X16_START \ + (VP9_COEF_COUNT_START + 8*VP9_COEF_COUNT_SIZE_ONE_SET) +#define VP9_COEF_COUNT_32X32_START \ + (VP9_COEF_COUNT_START + 12*VP9_COEF_COUNT_SIZE_ONE_SET) +#define VP9_COEF_COUNT_SIZE_PLANE (2 * VP9_COEF_COUNT_SIZE_ONE_SET) +#define VP9_COEF_COUNT_SIZE (4 * 2 * 2 * VP9_COEF_COUNT_SIZE_ONE_SET) + +#define VP9_INTRA_INTER_COUNT_START \ + (VP9_COEF_COUNT_START+VP9_COEF_COUNT_SIZE) +#define VP9_INTRA_INTER_COUNT_SIZE (4*2) +#define VP9_COMP_INTER_COUNT_START \ + (VP9_INTRA_INTER_COUNT_START+VP9_INTRA_INTER_COUNT_SIZE) +#define VP9_COMP_INTER_COUNT_SIZE (5*2) +#define VP9_COMP_REF_COUNT_START \ + (VP9_COMP_INTER_COUNT_START+VP9_COMP_INTER_COUNT_SIZE) +#define VP9_COMP_REF_COUNT_SIZE (5*2) +#define VP9_SINGLE_REF_COUNT_START \ + (VP9_COMP_REF_COUNT_START+VP9_COMP_REF_COUNT_SIZE) +#define VP9_SINGLE_REF_COUNT_SIZE (10*2) +#define VP9_TX_MODE_COUNT_START \ + (VP9_SINGLE_REF_COUNT_START+VP9_SINGLE_REF_COUNT_SIZE) +#define VP9_TX_MODE_COUNT_SIZE (12*2) +#define VP9_SKIP_COUNT_START \ + (VP9_TX_MODE_COUNT_START+VP9_TX_MODE_COUNT_SIZE) +#define VP9_SKIP_COUNT_SIZE (3*2) +#define VP9_MV_SIGN_0_COUNT_START \ + (VP9_SKIP_COUNT_START+VP9_SKIP_COUNT_SIZE) +#define VP9_MV_SIGN_0_COUNT_SIZE (1*2) +#define VP9_MV_SIGN_1_COUNT_START \ + (VP9_MV_SIGN_0_COUNT_START+VP9_MV_SIGN_0_COUNT_SIZE) +#define VP9_MV_SIGN_1_COUNT_SIZE (1*2) +#define VP9_MV_BITS_0_COUNT_START \ + (VP9_MV_SIGN_1_COUNT_START+VP9_MV_SIGN_1_COUNT_SIZE) +#define VP9_MV_BITS_0_COUNT_SIZE (10*2) +#define VP9_MV_BITS_1_COUNT_START \ + (VP9_MV_BITS_0_COUNT_START+VP9_MV_BITS_0_COUNT_SIZE) +#define VP9_MV_BITS_1_COUNT_SIZE (10*2) +#define VP9_MV_CLASS0_HP_0_COUNT_START \ + (VP9_MV_BITS_1_COUNT_START+VP9_MV_BITS_1_COUNT_SIZE) +#define VP9_MV_CLASS0_HP_0_COUNT_SIZE (2*2) +#define VP9_MV_CLASS0_HP_1_COUNT_START \ + (VP9_MV_CLASS0_HP_0_COUNT_START+VP9_MV_CLASS0_HP_0_COUNT_SIZE) +#define VP9_MV_CLASS0_HP_1_COUNT_SIZE (2*2) +/* Start merge_tree*/ +#define VP9_INTER_MODE_COUNT_START \ + (VP9_MV_CLASS0_HP_1_COUNT_START+VP9_MV_CLASS0_HP_1_COUNT_SIZE) +#define VP9_INTER_MODE_COUNT_SIZE (7*4) +#define VP9_IF_Y_MODE_COUNT_START \ + (VP9_INTER_MODE_COUNT_START+VP9_INTER_MODE_COUNT_SIZE) +#define VP9_IF_Y_MODE_COUNT_SIZE (10*4) +#define VP9_IF_UV_MODE_COUNT_START \ + (VP9_IF_Y_MODE_COUNT_START+VP9_IF_Y_MODE_COUNT_SIZE) +#define VP9_IF_UV_MODE_COUNT_SIZE (10*10) +#define VP9_PARTITION_P_COUNT_START \ + (VP9_IF_UV_MODE_COUNT_START+VP9_IF_UV_MODE_COUNT_SIZE) +#define VP9_PARTITION_P_COUNT_SIZE (4*4*4) +#define VP9_INTERP_COUNT_START \ + (VP9_PARTITION_P_COUNT_START+VP9_PARTITION_P_COUNT_SIZE) +#define VP9_INTERP_COUNT_SIZE (4*3) +#define VP9_MV_JOINTS_COUNT_START \ + (VP9_INTERP_COUNT_START+VP9_INTERP_COUNT_SIZE) +#define VP9_MV_JOINTS_COUNT_SIZE (1 * 4) +#define VP9_MV_CLASSES_0_COUNT_START \ + (VP9_MV_JOINTS_COUNT_START+VP9_MV_JOINTS_COUNT_SIZE) +#define VP9_MV_CLASSES_0_COUNT_SIZE (1*11) +#define VP9_MV_CLASS0_0_COUNT_START \ + (VP9_MV_CLASSES_0_COUNT_START+VP9_MV_CLASSES_0_COUNT_SIZE) +#define VP9_MV_CLASS0_0_COUNT_SIZE (1*2) +#define VP9_MV_CLASSES_1_COUNT_START \ + (VP9_MV_CLASS0_0_COUNT_START+VP9_MV_CLASS0_0_COUNT_SIZE) +#define VP9_MV_CLASSES_1_COUNT_SIZE (1*11) +#define VP9_MV_CLASS0_1_COUNT_START \ + (VP9_MV_CLASSES_1_COUNT_START+VP9_MV_CLASSES_1_COUNT_SIZE) +#define VP9_MV_CLASS0_1_COUNT_SIZE (1*2) +#define VP9_MV_CLASS0_FP_0_COUNT_START \ + (VP9_MV_CLASS0_1_COUNT_START+VP9_MV_CLASS0_1_COUNT_SIZE) +#define VP9_MV_CLASS0_FP_0_COUNT_SIZE (3*4) +#define VP9_MV_CLASS0_FP_1_COUNT_START \ + (VP9_MV_CLASS0_FP_0_COUNT_START+VP9_MV_CLASS0_FP_0_COUNT_SIZE) +#define VP9_MV_CLASS0_FP_1_COUNT_SIZE (3*4) + + +#define DC_PRED 0 /* Average of above and left pixels*/ +#define V_PRED 1 /* Vertical*/ +#define H_PRED 2 /* Horizontal*/ +#define D45_PRED 3 /*Directional 45 deg = round(arctan(1/1) * 180/pi)*/ +#define D135_PRED 4 /* Directional 135 deg = 180 - 45*/ +#define D117_PRED 5 /* Directional 117 deg = 180 - 63*/ +#define D153_PRED 6 /* Directional 153 deg = 180 - 27*/ +#define D207_PRED 7 /* Directional 207 deg = 180 + 27*/ +#define D63_PRED 8 /*Directional 63 deg = round(arctan(2/1) * 180/pi)*/ +#define TM_PRED 9 /*True-motion*/ + +int clip_prob(int p) +{ + return (p > 255) ? 255 : (p < 1) ? 1 : p; +} + +#define ROUND_POWER_OF_TWO(value, n) \ + (((value) + (1 << ((n) - 1))) >> (n)) + +#define MODE_MV_COUNT_SAT 20 +static const int count_to_update_factor[MODE_MV_COUNT_SAT + 1] = { + 0, 6, 12, 19, 25, 32, 38, 44, 51, 57, 64, + 70, 76, 83, 89, 96, 102, 108, 115, 121, 128 +}; + +void vp9_tree_merge_probs(unsigned int *prev_prob, unsigned int *cur_prob, + int coef_node_start, int tree_left, int tree_right, int tree_i, + int node) { + + int prob_32, prob_res, prob_shift; + int pre_prob, new_prob; + int den, m_count, get_prob, factor; + prob_32 = prev_prob[coef_node_start / 4 * 2]; + prob_res = coef_node_start & 3; + prob_shift = prob_res * 8; + pre_prob = (prob_32 >> prob_shift) & 0xff; + + den = tree_left + tree_right; + + if (den == 0) + new_prob = pre_prob; + else { + m_count = (den < MODE_MV_COUNT_SAT) ? + den : MODE_MV_COUNT_SAT; + get_prob = clip_prob( + div_r32(((int64_t)tree_left * 256 + (den >> 1)), + den)); + /*weighted_prob*/ + factor = count_to_update_factor[m_count]; + new_prob = ROUND_POWER_OF_TWO(pre_prob * (256 - factor) + + get_prob * factor, 8); + } + cur_prob[coef_node_start / 4 * 2] = (cur_prob[coef_node_start / 4 * 2] + & (~(0xff << prob_shift))) | (new_prob << prob_shift); + + /*pr_info(" - [%d][%d] 0x%02X --> 0x%02X (0x%X 0x%X) (%X)\n", + tree_i, node, pre_prob, new_prob, tree_left, tree_right, + cur_prob[coef_node_start/4*2]);*/ +} + + +/*void adapt_coef_probs(void)*/ +void adapt_coef_probs(int pic_count, int prev_kf, int cur_kf, int pre_fc, + unsigned int *prev_prob, unsigned int *cur_prob, unsigned int *count) +{ + /* 80 * 64bits = 0xF00 ( use 0x1000 4K bytes) + unsigned int prev_prob[496*2]; + unsigned int cur_prob[496*2]; + 0x300 * 128bits = 0x3000 (32K Bytes) + unsigned int count[0x300*4];*/ + + int tx_size, coef_tx_size_start, coef_count_tx_size_start; + int plane, coef_plane_start, coef_count_plane_start; + int type, coef_type_start, coef_count_type_start; + int band, coef_band_start, coef_count_band_start; + int cxt_num; + int cxt, coef_cxt_start, coef_count_cxt_start; + int node, coef_node_start, coef_count_node_start; + + int tree_i, tree_left, tree_right; + int mvd_i; + + int count_sat = 24; + /*int update_factor = 112;*/ /*If COEF_MAX_UPDATE_FACTOR_AFTER_KEY, + use 128*/ + /* If COEF_MAX_UPDATE_FACTOR_AFTER_KEY, use 128*/ + /*int update_factor = (pic_count == 1) ? 128 : 112;*/ + int update_factor = cur_kf ? 112 : + prev_kf ? 128 : 112; + + int prob_32; + int prob_res; + int prob_shift; + int pre_prob; + + int num, den; + int get_prob; + int m_count; + int factor; + + int new_prob; + + if (debug & VP9_DEBUG_MERGE) + pr_info + ("\n ##adapt_coef_probs (pre_fc : %d ,prev_kf : %d,cur_kf : %d)##\n\n", + pre_fc, prev_kf, cur_kf); + + /*adapt_coef_probs*/ + for (tx_size = 0; tx_size < 4; tx_size++) { + coef_tx_size_start = VP9_COEF_START + + tx_size * 4 * VP9_COEF_SIZE_ONE_SET; + coef_count_tx_size_start = VP9_COEF_COUNT_START + + tx_size * 4 * VP9_COEF_COUNT_SIZE_ONE_SET; + coef_plane_start = coef_tx_size_start; + coef_count_plane_start = coef_count_tx_size_start; + for (plane = 0; plane < 2; plane++) { + coef_type_start = coef_plane_start; + coef_count_type_start = coef_count_plane_start; + for (type = 0; type < 2; type++) { + coef_band_start = coef_type_start; + coef_count_band_start = coef_count_type_start; + for (band = 0; band < 6; band++) { + if (band == 0) + cxt_num = 3; + else + cxt_num = 6; + coef_cxt_start = coef_band_start; + coef_count_cxt_start = + coef_count_band_start; + for (cxt = 0; cxt < cxt_num; cxt++) { + const int n0 = + count[coef_count_cxt_start]; + const int n1 = + count[coef_count_cxt_start + 1]; + const int n2 = + count[coef_count_cxt_start + 2]; + const int neob = + count[coef_count_cxt_start + 3]; + const int nneob = + count[coef_count_cxt_start + 4]; + const unsigned int + branch_ct[3][2] = { + { neob, nneob }, + { n0, n1 + n2 }, + { n1, n2 } + }; + coef_node_start = + coef_cxt_start; + for + (node = 0; node < 3; node++) { + prob_32 = + prev_prob[ + coef_node_start + / 4 * 2]; + prob_res = + coef_node_start & 3; + prob_shift = + prob_res * 8; + pre_prob = + (prob_32 >> prob_shift) + & 0xff; + + /*get_binary_prob*/ + num = + branch_ct[node][0]; + den = + branch_ct[node][0] + + branch_ct[node][1]; + m_count = (den < + count_sat) + ? den : count_sat; + + get_prob = + (den == 0) ? 128u : + clip_prob( + div_r32(((int64_t) + num * 256 + + (den >> 1)), + den)); + + factor = + update_factor * m_count + / count_sat; + new_prob = + ROUND_POWER_OF_TWO + (pre_prob * + (256 - factor) + + get_prob * factor, 8); + + cur_prob[coef_node_start + / 4 * 2] = + (cur_prob + [coef_node_start + / 4 * 2] & (~(0xff << + prob_shift))) | + (new_prob << + prob_shift); + + coef_node_start += 1; + } + + coef_cxt_start = + coef_cxt_start + 3; + coef_count_cxt_start = + coef_count_cxt_start + + 5; + } + if (band == 0) { + coef_band_start += 10; + coef_count_band_start += 15; + } else { + coef_band_start += 18; + coef_count_band_start += 30; + } + } + coef_type_start += VP9_COEF_SIZE_ONE_SET; + coef_count_type_start += + VP9_COEF_COUNT_SIZE_ONE_SET; + } + coef_plane_start += 2 * VP9_COEF_SIZE_ONE_SET; + coef_count_plane_start += + 2 * VP9_COEF_COUNT_SIZE_ONE_SET; + } + } + +if (cur_kf == 0) { + /*mode_mv_merge_probs - merge_intra_inter_prob*/ + for (coef_count_node_start = VP9_INTRA_INTER_COUNT_START; + coef_count_node_start < (VP9_MV_CLASS0_HP_1_COUNT_START + + VP9_MV_CLASS0_HP_1_COUNT_SIZE); coef_count_node_start += 2) { + + if (coef_count_node_start == + VP9_INTRA_INTER_COUNT_START) { + if (debug & VP9_DEBUG_MERGE) + pr_info(" # merge_intra_inter_prob\n"); + coef_node_start = VP9_INTRA_INTER_START; + } else if (coef_count_node_start == + VP9_COMP_INTER_COUNT_START) { + if (debug & VP9_DEBUG_MERGE) + pr_info(" # merge_comp_inter_prob\n"); + coef_node_start = VP9_COMP_INTER_START; + } + /* + else if (coef_count_node_start == + VP9_COMP_REF_COUNT_START) { + pr_info(" # merge_comp_inter_prob\n"); + coef_node_start = VP9_COMP_REF_START; + } + else if (coef_count_node_start == + VP9_SINGLE_REF_COUNT_START) { + pr_info(" # merge_comp_inter_prob\n"); + coef_node_start = VP9_SINGLE_REF_START; + } + */ + else if (coef_count_node_start == + VP9_TX_MODE_COUNT_START) { + if (debug & VP9_DEBUG_MERGE) + pr_info(" # merge_tx_mode_probs\n"); + coef_node_start = VP9_TX_MODE_START; + } else if (coef_count_node_start == + VP9_SKIP_COUNT_START) { + if (debug & VP9_DEBUG_MERGE) + pr_info(" # merge_skip_probs\n"); + coef_node_start = VP9_SKIP_START; + } else if (coef_count_node_start == + VP9_MV_SIGN_0_COUNT_START) { + if (debug & VP9_DEBUG_MERGE) + pr_info(" # merge_sign_0\n"); + coef_node_start = VP9_MV_SIGN_0_START; + } else if (coef_count_node_start == + VP9_MV_SIGN_1_COUNT_START) { + if (debug & VP9_DEBUG_MERGE) + pr_info(" # merge_sign_1\n"); + coef_node_start = VP9_MV_SIGN_1_START; + } else if (coef_count_node_start == + VP9_MV_BITS_0_COUNT_START) { + if (debug & VP9_DEBUG_MERGE) + pr_info(" # merge_bits_0\n"); + coef_node_start = VP9_MV_BITS_0_START; + } else if (coef_count_node_start == + VP9_MV_BITS_1_COUNT_START) { + if (debug & VP9_DEBUG_MERGE) + pr_info(" # merge_bits_1\n"); + coef_node_start = VP9_MV_BITS_1_START; + } else if (coef_count_node_start == + VP9_MV_CLASS0_HP_0_COUNT_START) { + if (debug & VP9_DEBUG_MERGE) + pr_info(" # merge_class0_hp\n"); + coef_node_start = VP9_MV_CLASS0_HP_0_START; + } + + + den = count[coef_count_node_start] + + count[coef_count_node_start + 1]; + + prob_32 = prev_prob[coef_node_start / 4 * 2]; + prob_res = coef_node_start & 3; + prob_shift = prob_res * 8; + pre_prob = (prob_32 >> prob_shift) & 0xff; + + if (den == 0) + new_prob = pre_prob; + else { + m_count = (den < MODE_MV_COUNT_SAT) ? + den : MODE_MV_COUNT_SAT; + get_prob = + clip_prob( + div_r32(((int64_t)count[coef_count_node_start] + * 256 + (den >> 1)), + den)); + /*weighted_prob*/ + factor = count_to_update_factor[m_count]; + new_prob = + ROUND_POWER_OF_TWO(pre_prob * (256 - factor) + + get_prob * factor, 8); + } + cur_prob[coef_node_start / 4 * 2] = + (cur_prob[coef_node_start / 4 * 2] & + (~(0xff << prob_shift))) + | (new_prob << prob_shift); + + coef_node_start = coef_node_start + 1; + } + if (debug & VP9_DEBUG_MERGE) + pr_info(" # merge_vp9_inter_mode_tree\n"); + coef_node_start = VP9_INTER_MODE_START; + coef_count_node_start = VP9_INTER_MODE_COUNT_START; + for (tree_i = 0; tree_i < 7; tree_i++) { + for (node = 0; node < 3; node++) { + switch (node) { + case 2: + tree_left = + count[coef_count_node_start + 1]; + tree_right = + count[coef_count_node_start + 3]; + break; + case 1: + tree_left = + count[coef_count_node_start + 0]; + tree_right = + count[coef_count_node_start + 1] + + count[coef_count_node_start + 3]; + break; + default: + tree_left = + count[coef_count_node_start + 2]; + tree_right = + count[coef_count_node_start + 0] + + count[coef_count_node_start + 1] + + count[coef_count_node_start + 3]; + break; + + } + + vp9_tree_merge_probs(prev_prob, cur_prob, + coef_node_start, tree_left, tree_right, + tree_i, node); + + coef_node_start = coef_node_start + 1; + } + coef_count_node_start = coef_count_node_start + 4; + } + if (debug & VP9_DEBUG_MERGE) + pr_info(" # merge_vp9_intra_mode_tree\n"); + coef_node_start = VP9_IF_Y_MODE_START; + coef_count_node_start = VP9_IF_Y_MODE_COUNT_START; + for (tree_i = 0; tree_i < 14; tree_i++) { + for (node = 0; node < 9; node++) { + switch (node) { + case 8: + tree_left = + count[coef_count_node_start+D153_PRED]; + tree_right = + count[coef_count_node_start+D207_PRED]; + break; + case 7: + tree_left = + count[coef_count_node_start+D63_PRED]; + tree_right = + count[coef_count_node_start+D207_PRED] + + count[coef_count_node_start+D153_PRED]; + break; + case 6: + tree_left = + count[coef_count_node_start + D45_PRED]; + tree_right = + count[coef_count_node_start+D207_PRED] + + count[coef_count_node_start+D153_PRED] + + count[coef_count_node_start+D63_PRED]; + break; + case 5: + tree_left = + count[coef_count_node_start+D135_PRED]; + tree_right = + count[coef_count_node_start+D117_PRED]; + break; + case 4: + tree_left = + count[coef_count_node_start+H_PRED]; + tree_right = + count[coef_count_node_start+D117_PRED] + + count[coef_count_node_start+D135_PRED]; + break; + case 3: + tree_left = + count[coef_count_node_start+H_PRED] + + count[coef_count_node_start+D117_PRED] + + count[coef_count_node_start+D135_PRED]; + tree_right = + count[coef_count_node_start+D45_PRED] + + count[coef_count_node_start+D207_PRED] + + count[coef_count_node_start+D153_PRED] + + count[coef_count_node_start+D63_PRED]; + break; + case 2: + tree_left = + count[coef_count_node_start+V_PRED]; + tree_right = + count[coef_count_node_start+H_PRED] + + count[coef_count_node_start+D117_PRED] + + count[coef_count_node_start+D135_PRED] + + count[coef_count_node_start+D45_PRED] + + count[coef_count_node_start+D207_PRED] + + count[coef_count_node_start+D153_PRED] + + count[coef_count_node_start+D63_PRED]; + break; + case 1: + tree_left = + count[coef_count_node_start+TM_PRED]; + tree_right = + count[coef_count_node_start+V_PRED] + + count[coef_count_node_start+H_PRED] + + count[coef_count_node_start+D117_PRED] + + count[coef_count_node_start+D135_PRED] + + count[coef_count_node_start+D45_PRED] + + count[coef_count_node_start+D207_PRED] + + count[coef_count_node_start+D153_PRED] + + count[coef_count_node_start+D63_PRED]; + break; + default: + tree_left = + count[coef_count_node_start+DC_PRED]; + tree_right = + count[coef_count_node_start+TM_PRED] + + count[coef_count_node_start+V_PRED] + + count[coef_count_node_start+H_PRED] + + count[coef_count_node_start+D117_PRED] + + count[coef_count_node_start+D135_PRED] + + count[coef_count_node_start+D45_PRED] + + count[coef_count_node_start+D207_PRED] + + count[coef_count_node_start+D153_PRED] + + count[coef_count_node_start+D63_PRED]; + break; + + } + + vp9_tree_merge_probs(prev_prob, cur_prob, + coef_node_start, tree_left, tree_right, + tree_i, node); + + coef_node_start = coef_node_start + 1; + } + coef_count_node_start = coef_count_node_start + 10; + } + + if (debug & VP9_DEBUG_MERGE) + pr_info(" # merge_vp9_partition_tree\n"); + coef_node_start = VP9_PARTITION_P_START; + coef_count_node_start = VP9_PARTITION_P_COUNT_START; + for (tree_i = 0; tree_i < 16; tree_i++) { + for (node = 0; node < 3; node++) { + switch (node) { + case 2: + tree_left = + count[coef_count_node_start + 2]; + tree_right = + count[coef_count_node_start + 3]; + break; + case 1: + tree_left = + count[coef_count_node_start + 1]; + tree_right = + count[coef_count_node_start + 2] + + count[coef_count_node_start + 3]; + break; + default: + tree_left = + count[coef_count_node_start + 0]; + tree_right = + count[coef_count_node_start + 1] + + count[coef_count_node_start + 2] + + count[coef_count_node_start + 3]; + break; + + } + + vp9_tree_merge_probs(prev_prob, cur_prob, + coef_node_start, + tree_left, tree_right, tree_i, node); + + coef_node_start = coef_node_start + 1; + } + coef_count_node_start = coef_count_node_start + 4; + } + + if (debug & VP9_DEBUG_MERGE) + pr_info(" # merge_vp9_switchable_interp_tree\n"); + coef_node_start = VP9_INTERP_START; + coef_count_node_start = VP9_INTERP_COUNT_START; + for (tree_i = 0; tree_i < 4; tree_i++) { + for (node = 0; node < 2; node++) { + switch (node) { + case 1: + tree_left = + count[coef_count_node_start + 1]; + tree_right = + count[coef_count_node_start + 2]; + break; + default: + tree_left = + count[coef_count_node_start + 0]; + tree_right = + count[coef_count_node_start + 1] + + count[coef_count_node_start + 2]; + break; + + } + + vp9_tree_merge_probs(prev_prob, cur_prob, + coef_node_start, + tree_left, tree_right, tree_i, node); + + coef_node_start = coef_node_start + 1; + } + coef_count_node_start = coef_count_node_start + 3; + } + + if (debug & VP9_DEBUG_MERGE) + pr_info("# merge_vp9_mv_joint_tree\n"); + coef_node_start = VP9_MV_JOINTS_START; + coef_count_node_start = VP9_MV_JOINTS_COUNT_START; + for (tree_i = 0; tree_i < 1; tree_i++) { + for (node = 0; node < 3; node++) { + switch (node) { + case 2: + tree_left = + count[coef_count_node_start + 2]; + tree_right = + count[coef_count_node_start + 3]; + break; + case 1: + tree_left = + count[coef_count_node_start + 1]; + tree_right = + count[coef_count_node_start + 2] + + count[coef_count_node_start + 3]; + break; + default: + tree_left = + count[coef_count_node_start + 0]; + tree_right = + count[coef_count_node_start + 1] + + count[coef_count_node_start + 2] + + count[coef_count_node_start + 3]; + break; + } + + vp9_tree_merge_probs(prev_prob, cur_prob, + coef_node_start, + tree_left, tree_right, tree_i, node); + + coef_node_start = coef_node_start + 1; + } + coef_count_node_start = coef_count_node_start + 4; + } + + for (mvd_i = 0; mvd_i < 2; mvd_i++) { + if (debug & VP9_DEBUG_MERGE) + pr_info(" # merge_vp9_mv_class_tree [%d] -\n", mvd_i); + coef_node_start = + mvd_i ? VP9_MV_CLASSES_1_START : VP9_MV_CLASSES_0_START; + coef_count_node_start = + mvd_i ? VP9_MV_CLASSES_1_COUNT_START + : VP9_MV_CLASSES_0_COUNT_START; + tree_i = 0; + for (node = 0; node < 10; node++) { + switch (node) { + case 9: + tree_left = + count[coef_count_node_start + 9]; + tree_right = + count[coef_count_node_start + 10]; + break; + case 8: + tree_left = + count[coef_count_node_start + 7]; + tree_right = + count[coef_count_node_start + 8]; + break; + case 7: + tree_left = + count[coef_count_node_start + 7] + + count[coef_count_node_start + 8]; + tree_right = + count[coef_count_node_start + 9] + + count[coef_count_node_start + 10]; + break; + case 6: + tree_left = + count[coef_count_node_start + 6]; + tree_right = + count[coef_count_node_start + 7] + + count[coef_count_node_start + 8] + + count[coef_count_node_start + 9] + + count[coef_count_node_start + 10]; + break; + case 5: + tree_left = + count[coef_count_node_start + 4]; + tree_right = + count[coef_count_node_start + 5]; + break; + case 4: + tree_left = + count[coef_count_node_start + 4] + + count[coef_count_node_start + 5]; + tree_right = + count[coef_count_node_start + 6] + + count[coef_count_node_start + 7] + + count[coef_count_node_start + 8] + + count[coef_count_node_start + 9] + + count[coef_count_node_start + 10]; + break; + case 3: + tree_left = + count[coef_count_node_start + 2]; + tree_right = + count[coef_count_node_start + 3]; + break; + case 2: + tree_left = + count[coef_count_node_start + 2] + + count[coef_count_node_start + 3]; + tree_right = + count[coef_count_node_start + 4] + + count[coef_count_node_start + 5] + + count[coef_count_node_start + 6] + + count[coef_count_node_start + 7] + + count[coef_count_node_start + 8] + + count[coef_count_node_start + 9] + + count[coef_count_node_start + 10]; + break; + case 1: + tree_left = + count[coef_count_node_start + 1]; + tree_right = + count[coef_count_node_start + 2] + + count[coef_count_node_start + 3] + + count[coef_count_node_start + 4] + + count[coef_count_node_start + 5] + + count[coef_count_node_start + 6] + + count[coef_count_node_start + 7] + + count[coef_count_node_start + 8] + + count[coef_count_node_start + 9] + + count[coef_count_node_start + 10]; + break; + default: + tree_left = + count[coef_count_node_start + 0]; + tree_right = + count[coef_count_node_start + 1] + + count[coef_count_node_start + 2] + + count[coef_count_node_start + 3] + + count[coef_count_node_start + 4] + + count[coef_count_node_start + 5] + + count[coef_count_node_start + 6] + + count[coef_count_node_start + 7] + + count[coef_count_node_start + 8] + + count[coef_count_node_start + 9] + + count[coef_count_node_start + 10]; + break; + + } + + vp9_tree_merge_probs(prev_prob, cur_prob, + coef_node_start, tree_left, tree_right, + tree_i, node); + + coef_node_start = coef_node_start + 1; + } + + if (debug & VP9_DEBUG_MERGE) + pr_info(" # merge_vp9_mv_class0_tree [%d] -\n", mvd_i); + coef_node_start = + mvd_i ? VP9_MV_CLASS0_1_START : VP9_MV_CLASS0_0_START; + coef_count_node_start = + mvd_i ? VP9_MV_CLASS0_1_COUNT_START : + VP9_MV_CLASS0_0_COUNT_START; + tree_i = 0; + node = 0; + tree_left = count[coef_count_node_start + 0]; + tree_right = count[coef_count_node_start + 1]; + + vp9_tree_merge_probs(prev_prob, cur_prob, coef_node_start, + tree_left, tree_right, tree_i, node); + if (debug & VP9_DEBUG_MERGE) + pr_info(" # merge_vp9_mv_fp_tree_class0_fp [%d] -\n", + mvd_i); + coef_node_start = + mvd_i ? VP9_MV_CLASS0_FP_1_START : + VP9_MV_CLASS0_FP_0_START; + coef_count_node_start = + mvd_i ? VP9_MV_CLASS0_FP_1_COUNT_START : + VP9_MV_CLASS0_FP_0_COUNT_START; + for (tree_i = 0; tree_i < 3; tree_i++) { + for (node = 0; node < 3; node++) { + switch (node) { + case 2: + tree_left = + count[coef_count_node_start + 2]; + tree_right = + count[coef_count_node_start + 3]; + break; + case 1: + tree_left = + count[coef_count_node_start + 1]; + tree_right = + count[coef_count_node_start + 2] + + count[coef_count_node_start + 3]; + break; + default: + tree_left = + count[coef_count_node_start + 0]; + tree_right = + count[coef_count_node_start + 1] + + count[coef_count_node_start + 2] + + count[coef_count_node_start + 3]; + break; + + } + + vp9_tree_merge_probs(prev_prob, cur_prob, + coef_node_start, tree_left, tree_right, + tree_i, node); + + coef_node_start = coef_node_start + 1; + } + coef_count_node_start = coef_count_node_start + 4; + } + + } /* for mvd_i (mvd_y or mvd_x)*/ +} + +} + + +static void uninit_mmu_buffers(struct VP9Decoder_s *pbi) +{ + + decoder_mmu_box_free(pbi->mmu_box); + pbi->mmu_box = NULL; + + if (pbi->bmmu_box) + decoder_bmmu_box_free(pbi->bmmu_box); + pbi->bmmu_box = NULL; +} + +#ifndef VP9_10B_MMU +static void init_buf_list(struct VP9Decoder_s *pbi) +{ + int i; + int buf_size; +#ifndef VP9_10B_MMU + int mc_buffer_end = pbi->mc_buf->buf_start + pbi->mc_buf->buf_size; +#endif + pbi->used_buf_num = max_buf_num; + + if (pbi->used_buf_num > MAX_BUF_NUM) + pbi->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 = pbi->init_pic_w; + int pic_height = pbi->init_pic_h; + + /*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, buf_alloc_depth == 10); + 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) ? + pic_width / 2 : pic_width; + int pic_height_dw = (double_write_mode == 2) ? + pic_height / 2 : pic_height; + int lcu_size = 64; /*fixed 64*/ + int pic_width_64 = (pic_width_dw + 63) & (~0x3f); + int pic_height_32 = (pic_height_dw + 31) & (~0x1f); + int pic_width_lcu = + (pic_width_64 % lcu_size) ? pic_width_64 / lcu_size + + 1 : pic_width_64 / lcu_size; + int pic_height_lcu = + (pic_height_32 % lcu_size) ? pic_height_32 / lcu_size + + 1 : pic_height_32 / 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); + if (debug) { + pr_info + ("init_buf_list num %d (width %d height %d):\n", + pbi->used_buf_num, pic_width, pic_height); + } + } + + for (i = 0; i < pbi->used_buf_num; i++) { + if (((i + 1) * buf_size) > pbi->mc_buf->buf_size) { + if (use_cma) + pbi->use_cma_flag = 1; + else { + if (debug) { + pr_info("%s maximum buf size is used\n", + __func__); + } + break; + } + } +#ifndef VP9_10B_MMU + pbi->m_BUF[i].alloc_flag = 0; + pbi->m_BUF[i].index = i; + + if (use_cma == 2) + pbi->use_cma_flag = 1; + if (pbi->use_cma_flag) { + if (!decoder_bmmu_box_alloc_idx_wait( + pbi->bmmu_box, + i, + buf_size, + -1, + -1, + BMMU_ALLOC_FLAGS_WAITCLEAR)) { + pbi->m_BUF[i].alloc_addr = + decoder_bmmu_box_get_phy_addr( + pbi->bmmu_box, + i); + pbi->m_BUF[i].cma_page_count = + PAGE_ALIGN(buf_size) / PAGE_SIZE; + pr_info("CMA malloc ok %d\n", i); + } else { + pbi->m_BUF[i].cma_page_count = 0; + pr_info("CMA malloc failed %d\n", i); + if (i <= 5) { + pbi->fatal_error |= + DECODER_FATAL_ERROR_NO_MEM; + } + break; + } + pbi->m_BUF[i].start_adr = pbi->m_BUF[i].alloc_addr; + } else { + pbi->m_BUF[i].cma_page_count = 0; + pbi->m_BUF[i].alloc_addr = 0; + pbi->m_BUF[i].start_adr = + pbi->mc_buf->buf_start + i * buf_size; + } + pbi->m_BUF[i].size = buf_size; + pbi->m_BUF[i].free_start_adr = pbi->m_BUF[i].start_adr; + + if (((pbi->m_BUF[i].start_adr + buf_size) > mc_buffer_end) + && (pbi->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 *)pbi->m_BUF[i].start_adr, + pbi->m_BUF[i].size); + } +#endif + } + pbi->buf_num = i; +} +#endif +static int config_pic(struct VP9Decoder_s *pbi, + struct PIC_BUFFER_CONFIG_s *pic_config, + unsigned long last_disp_addr) +{ + int ret = -1; + int i; + int pic_width = pbi->init_pic_w; + int pic_height = pbi->init_pic_h; + int MV_MEM_UNIT = 0x240; + int lcu_size = 64; /*fixed 64*/ + int pic_width_64 = (pic_width + 63) & (~0x3f); + int pic_height_32 = (pic_height + 31) & (~0x1f); + int pic_width_lcu = (pic_width_64 % lcu_size) ? + pic_width_64 / lcu_size + 1 + : pic_width_64 / lcu_size; + int pic_height_lcu = (pic_height_32 % lcu_size) ? + pic_height_32 / lcu_size + 1 + : pic_height_32 / lcu_size; + int lcu_total = pic_width_lcu * pic_height_lcu; + + u32 mpred_mv_end = pbi->work_space_buf->mpred_mv.buf_start + + pbi->work_space_buf->mpred_mv.buf_size; + u32 y_adr = 0; + int buf_size = 0; + + 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, buf_alloc_depth == 10); + 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) ? + pic_width / 2 : pic_width; + int pic_height_dw = (double_write_mode == 2) ? + pic_height / 2 : pic_height; + int pic_width_64_dw = (pic_width_dw + 63) & (~0x3f); + int pic_height_32_dw = (pic_height_dw + 31) & (~0x1f); + int pic_width_lcu_dw = (pic_width_64_dw % lcu_size) ? + pic_width_64_dw / lcu_size + 1 + : pic_width_64_dw / lcu_size; + int pic_height_lcu_dw = (pic_height_32_dw % lcu_size) ? + pic_height_32_dw / lcu_size + 1 + : pic_height_32_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); + buf_size = ((buf_size + 0xffff) >> 16) << 16; + } + if (mc_buffer_size & 0xffff) /*64k alignment*/ + mc_buffer_size_h += 1; +#ifndef VP9_10B_MMU + if ((double_write_mode & 0x10) == 0) + buf_size += (mc_buffer_size_h << 16); +#endif + +#ifdef VP9_10B_MMU + if ((pbi->work_space_buf->cm_header.buf_start + + ((pic_config->index + 2) + * MMU_COMPRESS_HEADER_SIZE)) + > (pbi->work_space_buf->cm_header.buf_start + + pbi->work_space_buf->cm_header.buf_size)) { + pr_info("MMU header_adr allocate fail\n"); + return -1; + } + + pic_config->header_adr = pbi->work_space_buf->cm_header.buf_start + + (pic_config->index * MMU_COMPRESS_HEADER_SIZE); + if (last_disp_addr && pic_config->header_adr == last_disp_addr) { + /*if same as disp add used last one.*/ + pr_info("same as disp %d: %ld\n", + pic_config->index, pic_config->header_adr); + pic_config->header_adr = + pbi->work_space_buf->cm_header.buf_start + + (FRAME_BUFFERS * MMU_COMPRESS_HEADER_SIZE); + } + if (debug & VP9_DEBUG_BUFMGR) { + pr_info("MMU header_adr %d: %ld\n", + pic_config->index, pic_config->header_adr); + } +#endif + + i = pic_config->index; + if ((pbi->work_space_buf->mpred_mv.buf_start + + (((i + 1) * lcu_total) * MV_MEM_UNIT)) + <= mpred_mv_end +#ifdef VP9_10B_MMU +#endif + ) { + if (debug) { + pr_err("start %x .size=%d\n", + pbi->mc_buf_spec.buf_start + i * buf_size, + buf_size); + } + +#ifndef VP9_10B_MMU + for (i = 0; i < pbi->buf_num; i++) { + y_adr = ((pbi->m_BUF[i].free_start_adr + + 0xffff) >> 16) << 16; + /*64k alignment*/ + if ((y_adr+buf_size) <= (pbi->m_BUF[i].start_adr+ + pbi->m_BUF[i].size)) { + pbi->m_BUF[i].free_start_adr = + y_adr + buf_size; + break; + } + } + if (i < pbi->buf_num) +#else + if ((pbi->mc_buf->buf_start + (i + 1) * buf_size) < + pbi->mc_buf->buf_end) + y_adr = pbi->mc_buf->buf_start + i * buf_size; + else { + if (!decoder_bmmu_box_alloc_idx_wait( + pbi->bmmu_box, + i, + buf_size, + -1, + -1, + BMMU_ALLOC_FLAGS_WAITCLEAR + )) { + pic_config->cma_alloc_addr = + decoder_bmmu_box_get_phy_addr( + pbi->bmmu_box, + i); + } else { + pr_err("alloc cma buffer failed %d\n", i); + } + if (pic_config->cma_alloc_addr) + y_adr = pic_config->cma_alloc_addr; + else + return -1; + } +#endif + { + /*ensure get_pic_by_POC() + not get the buffer not decoded*/ + pic_config->BUF_index = i; + pic_config->lcu_total = lcu_total; + + pic_config->comp_body_size = losless_comp_body_size; + pic_config->buf_size = buf_size; +#ifndef VP9_10B_MMU + pic_config->mc_y_adr = y_adr; +#endif + pic_config->mc_canvas_y = pic_config->index; + pic_config->mc_canvas_u_v = pic_config->index; +#ifndef VP9_10B_MMU + if (double_write_mode & 0x10) { + pic_config->mc_u_v_adr = y_adr + + ((mc_buffer_size_u_v_h << 16) << 1); + + pic_config->mc_canvas_y = + (pic_config->index << 1); + pic_config->mc_canvas_u_v = + (pic_config->index << 1) + 1; + + pic_config->dw_y_adr = y_adr; + pic_config->dw_u_v_adr = pic_config->mc_u_v_adr; + } else +#endif + if (double_write_mode) { + pic_config->dw_y_adr = y_adr +#ifndef VP9_10B_MMU + + (mc_buffer_size_h << 16) +#endif + ; + pic_config->dw_u_v_adr = pic_config->dw_y_adr + + ((mc_buffer_size_u_v_h << 16) << 1); + } + + pic_config->mpred_mv_wr_start_addr = + pbi->work_space_buf->mpred_mv.buf_start + + ((pic_config->index * lcu_total) + * MV_MEM_UNIT); + + if (debug) { + pr_info + ("%s index %d BUF_index %d mc_y_adr %lx ", + __func__, pic_config->index, + pic_config->BUF_index, + pic_config->mc_y_adr); + pr_info + ("comp_body_size %x comp_buf_size %x ", + pic_config->comp_body_size, + pic_config->buf_size); + pr_info + ("mpred_mv_wr_start_adr %ld\n", + pic_config->mpred_mv_wr_start_addr); + pr_info("dw_y_adr %d, pic_config->dw_u_v_adr =%d\n", + pic_config->dw_y_adr, + pic_config->dw_u_v_adr); + } + ret = 0; + } + } + return ret; +} + +static void init_pic_list(struct VP9Decoder_s *pbi) +{ + int i; + struct VP9_Common_s *cm = &pbi->common; + struct PIC_BUFFER_CONFIG_s *pic_config; + struct vframe_s vf; + unsigned long disp_addr = 0; + + if (!get_video0_frame_info(&vf)) { + if (vf.type & VIDTYPE_SCATTER) { + /*sc only used header.*/ + disp_addr = vf.compHeadAddr; + } else if (vf.type & VIDTYPE_COMPRESS) { + /*sc checked body.*/ + disp_addr = vf.compBodyAddr; + } else { + struct canvas_s cur_canvas; + canvas_read(vf.canvas0Addr & 0xff, &cur_canvas); + disp_addr = cur_canvas.addr; + } + } + + for (i = 0; i < FRAME_BUFFERS; i++) { + pic_config = &cm->buffer_pool->frame_bufs[i].buf; + pic_config->index = i; + pic_config->BUF_index = -1; + if (config_pic(pbi, pic_config, disp_addr) < 0) { + if (debug) + pr_info("Config_pic %d fail\n", + pic_config->index); + pic_config->index = -1; + break; + } + pic_config->y_crop_width = pbi->init_pic_w; + pic_config->y_crop_height = pbi->init_pic_h; + /*set_canvas(pic_config);*/ + } + for (; i < FRAME_BUFFERS; i++) { + pic_config = &cm->buffer_pool->frame_bufs[i].buf; + pic_config->index = -1; + pic_config->BUF_index = -1; + } + +} + + +static void init_pic_list_hw(struct VP9Decoder_s *pbi) +{ + int i; + struct VP9_Common_s *cm = &pbi->common; + struct PIC_BUFFER_CONFIG_s *pic_config; + /*WRITE_VREG(HEVCD_MPP_ANC2AXI_TBL_CONF_ADDR, 0x0);*/ + WRITE_VREG(HEVCD_MPP_ANC2AXI_TBL_CONF_ADDR, + (0x1 << 1) | (0x1 << 2)); + + + for (i = 0; i < FRAME_BUFFERS; i++) { + pic_config = &cm->buffer_pool->frame_bufs[i].buf; + if (pic_config->index < 0) + break; + +#ifdef VP9_10B_MMU + /*WRITE_VREG(HEVCD_MPP_ANC2AXI_TBL_CMD_ADDR, + pic_config->header_adr + | (pic_config->mc_canvas_y << 8)|0x1);*/ + WRITE_VREG(HEVCD_MPP_ANC2AXI_TBL_DATA, pic_config->header_adr >> 5); +#else + /*WRITE_VREG(HEVCD_MPP_ANC2AXI_TBL_CMD_ADDR, + pic_config->mc_y_adr + | (pic_config->mc_canvas_y << 8) | 0x1);*/ + WRITE_VREG(HEVCD_MPP_ANC2AXI_TBL_DATA, pic_config->mc_y_adr >> 5); +#endif +#ifndef LOSLESS_COMPRESS_MODE + /*WRITE_VREG(HEVCD_MPP_ANC2AXI_TBL_CMD_ADDR, + pic_config->mc_u_v_adr + | (pic_config->mc_canvas_u_v << 8)| 0x1);*/ + WRITE_VREG(HEVCD_MPP_ANC2AXI_TBL_DATA, pic_config->mc_u_v_adr >> 5); +#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); +} + + +static void dump_pic_list(struct VP9Decoder_s *pbi) +{ + return; +} + +static int config_pic_size(struct VP9Decoder_s *pbi, unsigned short bit_depth) +{ +#ifdef LOSLESS_COMPRESS_MODE + unsigned int data32; +#endif + int losless_comp_header_size, losless_comp_body_size; + struct VP9_Common_s *cm = &pbi->common; + struct PIC_BUFFER_CONFIG_s *cur_pic_config = &cm->cur_frame->buf; + frame_width = cur_pic_config->y_crop_width; + frame_height = cur_pic_config->y_crop_height; + cur_pic_config->bit_depth = bit_depth; + losless_comp_header_size = + compute_losless_comp_header_size(cur_pic_config->y_crop_width, + cur_pic_config->y_crop_height); + losless_comp_body_size = + compute_losless_comp_body_size(cur_pic_config->y_crop_width, + cur_pic_config->y_crop_height, (bit_depth == VPX_BITS_10)); + cur_pic_config->comp_body_size = losless_comp_body_size; +#ifdef LOSLESS_COMPRESS_MODE + data32 = READ_VREG(HEVC_SAO_CTRL5); + if (bit_depth == VPX_BITS_10) + data32 &= ~(1 << 9); + else + data32 |= (1 << 9); + + WRITE_VREG(HEVC_SAO_CTRL5, data32); + +#ifdef VP9_10B_MMU + /*bit[4] : paged_mem_mode*/ + WRITE_VREG(HEVCD_MPP_DECOMP_CTL1, (0x1 << 4)); +#else + /*bit[3] smem mdoe*/ + if (bit_depth == VPX_BITS_10) + WRITE_VREG(HEVCD_MPP_DECOMP_CTL1, (0 << 3)); + else + WRITE_VREG(HEVCD_MPP_DECOMP_CTL1, (1 << 3)); +#endif + WRITE_VREG(HEVCD_MPP_DECOMP_CTL2, (losless_comp_body_size >> 5)); + /*WRITE_VREG(HEVCD_MPP_DECOMP_CTL3,(0xff<<20) | (0xff<<10) | 0xff);*/ + 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); +#else + WRITE_VREG(HEVCD_MPP_DECOMP_CTL1, 0x1 << 31); +#endif + return 0; +} + +static int config_mc_buffer(struct VP9Decoder_s *pbi, unsigned short bit_depth) +{ + int i; + struct VP9_Common_s *cm = &pbi->common; + struct PIC_BUFFER_CONFIG_s *cur_pic_config = &cm->cur_frame->buf; + uint8_t scale_enable = 0; + + if (debug&VP9_DEBUG_BUFMGR) + pr_info("config_mc_buffer entered .....\n"); + + WRITE_VREG(HEVCD_MPP_ANC_CANVAS_ACCCONFIG_ADDR, + (0 << 8) | (0 << 1) | 1); + for (i = 0; i < REFS_PER_FRAME; ++i) { + struct PIC_BUFFER_CONFIG_s *pic_config = cm->frame_refs[i].buf; + + WRITE_VREG(HEVCD_MPP_ANC_CANVAS_DATA_ADDR, + (pic_config->mc_canvas_u_v << 16) + | (pic_config->mc_canvas_u_v << 8) + | pic_config->mc_canvas_y); + if (debug & VP9_DEBUG_BUFMGR_MORE) + pr_info("refid %x mc_canvas_u_v %x mc_canvas_y %x\n", + i, pic_config->mc_canvas_u_v, + pic_config->mc_canvas_y); + } + WRITE_VREG(HEVCD_MPP_ANC_CANVAS_ACCCONFIG_ADDR, + (16 << 8) | (0 << 1) | 1); + for (i = 0; i < REFS_PER_FRAME; ++i) { + struct PIC_BUFFER_CONFIG_s *pic_config = cm->frame_refs[i].buf; + + WRITE_VREG(HEVCD_MPP_ANC_CANVAS_DATA_ADDR, + (pic_config->mc_canvas_u_v << 16) + | (pic_config->mc_canvas_u_v << 8) + | pic_config->mc_canvas_y); + } + + /*auto_inc start index:0 field:0*/ + WRITE_VREG(VP9D_MPP_REFINFO_TBL_ACCCONFIG, 0x1 << 2); + /*index 0:last 1:golden 2:altref*/ + for (i = 0; i < REFS_PER_FRAME; i++) { + int ref_pic_body_size; + struct PIC_BUFFER_CONFIG_s *pic_config = cm->frame_refs[i].buf; + + WRITE_VREG(VP9D_MPP_REFINFO_DATA, pic_config->y_crop_width); + WRITE_VREG(VP9D_MPP_REFINFO_DATA, pic_config->y_crop_height); + + if (pic_config->y_crop_width != cur_pic_config->y_crop_width || + pic_config->y_crop_height != cur_pic_config->y_crop_height) { + scale_enable |= (1 << i); + } + ref_pic_body_size = + compute_losless_comp_body_size(pic_config->y_crop_width, + pic_config->y_crop_height, (bit_depth == VPX_BITS_10)); + WRITE_VREG(VP9D_MPP_REFINFO_DATA, + (pic_config->y_crop_width << 14) + / cur_pic_config->y_crop_width); + WRITE_VREG(VP9D_MPP_REFINFO_DATA, + (pic_config->y_crop_height << 14) + / cur_pic_config->y_crop_height); +#ifdef VP9_10B_MMU + WRITE_VREG(VP9D_MPP_REFINFO_DATA, 0); +#else + WRITE_VREG(VP9D_MPP_REFINFO_DATA, ref_pic_body_size >> 5); +#endif + } + WRITE_VREG(VP9D_MPP_REF_SCALE_ENBL, scale_enable); + return 0; +} + +static void clear_mpred_hw(struct VP9Decoder_s *pbi) +{ + unsigned int data32; + data32 = READ_VREG(HEVC_MPRED_CTRL4); + data32 &= (~(1 << 6)); + WRITE_VREG(HEVC_MPRED_CTRL4, data32); +} + +static void config_mpred_hw(struct VP9Decoder_s *pbi) +{ + struct VP9_Common_s *cm = &pbi->common; + struct PIC_BUFFER_CONFIG_s *cur_pic_config = &cm->cur_frame->buf; + struct PIC_BUFFER_CONFIG_s *last_frame_pic_config = + &cm->prev_frame->buf; + + unsigned int data32; + int mpred_curr_lcu_x; + int mpred_curr_lcu_y; + int mpred_mv_rd_end_addr; + int MV_MEM_UNIT = 0x240; + + + mpred_mv_rd_end_addr = last_frame_pic_config->mpred_mv_wr_start_addr + + (last_frame_pic_config->lcu_total * MV_MEM_UNIT); + + data32 = READ_VREG(HEVC_MPRED_CURR_LCU); + mpred_curr_lcu_x = data32 & 0xffff; + mpred_curr_lcu_y = (data32 >> 16) & 0xffff; + + if (debug & VP9_DEBUG_BUFMGR) + pr_info("cur pic_config index %d col pic_config index %d\n", + cur_pic_config->index, last_frame_pic_config->index); + WRITE_VREG(HEVC_MPRED_CTRL3, 0x24122412); + WRITE_VREG(HEVC_MPRED_ABV_START_ADDR, + pbi->work_space_buf->mpred_above.buf_start); + + data32 = READ_VREG(HEVC_MPRED_CTRL4); + + data32 &= (~(1 << 6)); + data32 |= (cm->use_prev_frame_mvs << 6); + WRITE_VREG(HEVC_MPRED_CTRL4, data32); + + WRITE_VREG(HEVC_MPRED_MV_WR_START_ADDR, + cur_pic_config->mpred_mv_wr_start_addr); + WRITE_VREG(HEVC_MPRED_MV_WPTR, cur_pic_config->mpred_mv_wr_start_addr); + + WRITE_VREG(HEVC_MPRED_MV_RD_START_ADDR, + last_frame_pic_config->mpred_mv_wr_start_addr); + WRITE_VREG(HEVC_MPRED_MV_RPTR, + last_frame_pic_config->mpred_mv_wr_start_addr); + /*data32 = ((pbi->lcu_x_num - pbi->tile_width_lcu)*MV_MEM_UNIT);*/ + /*WRITE_VREG(HEVC_MPRED_MV_WR_ROW_JUMP,data32);*/ + /*WRITE_VREG(HEVC_MPRED_MV_RD_ROW_JUMP,data32);*/ + WRITE_VREG(HEVC_MPRED_MV_RD_END_ADDR, mpred_mv_rd_end_addr); + +} + +static void config_sao_hw(struct VP9Decoder_s *pbi, union param_u *params) +{ + struct VP9_Common_s *cm = &pbi->common; + struct PIC_BUFFER_CONFIG_s *pic_config = &cm->cur_frame->buf; + + unsigned int data32; + int lcu_size = 64; + int mc_buffer_size_u_v = + pic_config->lcu_total * lcu_size*lcu_size/2; + int mc_buffer_size_u_v_h = + (mc_buffer_size_u_v + 0xffff) >> 16;/*64k alignment*/ + +#ifndef VP9_10B_MMU + if ((double_write_mode & 0x10) == 0) + WRITE_VREG(HEVC_CM_BODY_START_ADDR, pic_config->mc_y_adr); +#endif + if (double_write_mode) { + WRITE_VREG(HEVC_SAO_Y_START_ADDR, pic_config->dw_y_adr); + WRITE_VREG(HEVC_SAO_C_START_ADDR, pic_config->dw_u_v_adr); + WRITE_VREG(HEVC_SAO_Y_WPTR, pic_config->dw_y_adr); + WRITE_VREG(HEVC_SAO_C_WPTR, pic_config->dw_u_v_adr); + } else { + WRITE_VREG(HEVC_SAO_Y_START_ADDR, 0xffffffff); + WRITE_VREG(HEVC_SAO_C_START_ADDR, 0xffffffff); + } +#ifdef VP9_10B_MMU + WRITE_VREG(HEVC_CM_HEADER_START_ADDR, pic_config->header_adr); +#endif + data32 = (mc_buffer_size_u_v_h << 16) << 1; + /*pr_info("data32=%x,mc_buffer_size_u_v_h=%x,lcu_total=%x\n", + data32, mc_buffer_size_u_v_h, pic_config->lcu_total);*/ + WRITE_VREG(HEVC_SAO_Y_LENGTH, data32); + + data32 = (mc_buffer_size_u_v_h << 16); + WRITE_VREG(HEVC_SAO_C_LENGTH, data32); + +#ifdef VP9_10B_NV21 +#ifdef DOS_PROJECT + data32 = READ_VREG(HEVC_SAO_CTRL1); + data32 &= (~0x3000); + /*[13:12] axi_aformat, 0-Linear, 1-32x32, 2-64x32*/ + data32 |= (MEM_MAP_MODE << 12); + data32 &= (~0x3); + data32 |= 0x1; /* [1]:dw_disable [0]:cm_disable*/ + WRITE_VREG(HEVC_SAO_CTRL1, data32); + /*[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); + ata32 = READ_VREG(HEVCD_IPP_AXIIF_CONFIG); + data32 &= (~0x30); + /*[5:4] address_format 00:linear 01:32x32 10:64x32*/ + data32 |= (MEM_MAP_MODE << 4); + WRITE_VREG(HEVCD_IPP_AXIIF_CONFIG, data32); +#else + /*m8baby test1902*/ + data32 = READ_VREG(HEVC_SAO_CTRL1); + data32 &= (~0x3000); + /*[13:12] axi_aformat, 0-Linear, 1-32x32, 2-64x32*/ + data32 |= (MEM_MAP_MODE << 12); + data32 &= (~0xff0); + /*data32 |= 0x670;*/ /*Big-Endian per 64-bit*/ + data32 |= 0x880; /*.Big-Endian per 64-bit */ + data32 &= (~0x3); + data32 |= 0x1; /*[1]:dw_disable [0]:cm_disable*/ + WRITE_VREG(HEVC_SAO_CTRL1, data32); + /* [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 |= 0x8; /*Big-Endian per 64-bit*/ + WRITE_VREG(HEVCD_IPP_AXIIF_CONFIG, data32); +#endif +#else + 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*/ +#ifndef VP9_10B_MMU + else + if (double_write_mode & 0x10) + data32 |= 0x1; /*disable cm*/ +#endif + 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); + } else { + data32 = READ_VREG(HEVC_SAO_CTRL5); + data32 &= (~(0xff << 16)); + if (double_write_mode != 1) + 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 +} + +static void vp9_config_work_space_hw(struct VP9Decoder_s *pbi) +{ + struct BuffInfo_s *buf_spec = pbi->work_space_buf; +#ifdef LOSLESS_COMPRESS_MODE + int losless_comp_header_size = + compute_losless_comp_header_size(pbi->init_pic_w, + pbi->init_pic_h); + int losless_comp_body_size = + compute_losless_comp_body_size(pbi->init_pic_w, + pbi->init_pic_h, buf_alloc_depth == 10); +#endif +#ifdef VP9_10B_MMU + unsigned int data32; +#endif + 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 & VP9_DEBUG_SEND_PARAM_WITH_REG) == 0) + WRITE_VREG(HEVC_RPM_BUFFER, (u32)pbi->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); + 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); + +#ifdef LOSLESS_COMPRESS_MODE +#ifdef VP9_10B_MMU + /*bit[4] : paged_mem_mode*/ + WRITE_VREG(HEVCD_MPP_DECOMP_CTL1, (0x1 << 4)); + WRITE_VREG(HEVCD_MPP_DECOMP_CTL2, 0); +#else + /*if(cur_pic_config->bit_depth == VPX_BITS_10) + WRITE_VREG(P_HEVCD_MPP_DECOMP_CTL1, (0<<3));*/ + /*bit[3] smem mdoe*/ + /*else WRITE_VREG(P_HEVCD_MPP_DECOMP_CTL1, (1<<3));*/ + /*bit[3] smem mdoe*/ + WRITE_VREG(HEVCD_MPP_DECOMP_CTL2, (losless_comp_body_size >> 5)); +#endif + /*WRITE_VREG(P_HEVCD_MPP_DECOMP_CTL2,(losless_comp_body_size >> 5));*/ + /*WRITE_VREG(P_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); +#else + WRITE_VREG(HEVCD_MPP_DECOMP_CTL1, 0x1 << 31); +#endif + +#ifdef VP9_10B_MMU + 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(P_HEVC_SAO_CTRL9);*/ + /*data32 |= 0x1;*/ + /*WRITE_VREG(P_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); + +#endif + + WRITE_VREG(VP9_SEG_MAP_BUFFER, buf_spec->seg_map.buf_start); + + if (debug & VP9_DEBUG_UCODE) + WRITE_VREG(LMEM_DUMP_ADR, (u32)pbi->lmem_phy_addr); + + /**/ + WRITE_VREG(VP9_PROB_SWAP_BUFFER, pbi->prob_buffer_phy_addr); + WRITE_VREG(VP9_COUNT_SWAP_BUFFER, pbi->count_buffer_phy_addr); +#ifdef VP9_10B_MMU + WRITE_VREG(VP9_MMU_MAP_BUFFER, pbi->frame_mmu_map_phy_addr); +#endif + +} + + +#ifdef VP9_LPF_LVL_UPDATE +/* + * Defines, declarations, sub-functions for vp9 de-block loop + filter Thr/Lvl table update + * - struct segmentation is for loop filter only (removed something) + * - function "vp9_loop_filter_init" and "vp9_loop_filter_frame_init" will + be instantiated in C_Entry + * - vp9_loop_filter_init run once before decoding start + * - vp9_loop_filter_frame_init run before every frame decoding start + * - set video format to VP9 is in vp9_loop_filter_init + */ +#define MAX_LOOP_FILTER 63 +#define MAX_REF_LF_DELTAS 4 +#define MAX_MODE_LF_DELTAS 2 +/*#define INTRA_FRAME 0*/ +/*#define LAST_FRAME 1*/ +/*#define MAX_REF_FRAMES 4*/ +#define SEGMENT_DELTADATA 0 +#define SEGMENT_ABSDATA 1 +#define MAX_SEGMENTS 8 +/*.#define SEG_TREE_PROBS (MAX_SEGMENTS-1)*/ +/*no use for loop filter, if this struct for common use, pls add it back*/ +/*#define PREDICTION_PROBS 3*/ +/* no use for loop filter, if this struct for common use, pls add it back*/ + +enum SEG_LVL_FEATURES { + SEG_LVL_ALT_Q = 0, /*Use alternate Quantizer ....*/ + SEG_LVL_ALT_LF = 1, /*Use alternate loop filter value...*/ + SEG_LVL_REF_FRAME = 2, /*Optional Segment reference frame*/ + SEG_LVL_SKIP = 3, /*Optional Segment (0,0) + skip mode*/ + SEG_LVL_MAX = 4 /*Number of features supported*/ +}; + +struct segmentation { + uint8_t enabled; + uint8_t update_map; + uint8_t update_data; + uint8_t abs_delta; + uint8_t temporal_update; + + /*no use for loop filter, if this struct + for common use, pls add it back*/ + /*vp9_prob tree_probs[SEG_TREE_PROBS]; */ + /* no use for loop filter, if this struct + for common use, pls add it back*/ + /*vp9_prob pred_probs[PREDICTION_PROBS];*/ + + int16_t feature_data[MAX_SEGMENTS][SEG_LVL_MAX]; + unsigned int feature_mask[MAX_SEGMENTS]; +}; + +struct loop_filter_thresh { + uint8_t mblim; + uint8_t lim; + uint8_t hev_thr; +}; + +struct loop_filter_info_n { + struct loop_filter_thresh lfthr[MAX_LOOP_FILTER + 1]; + uint8_t lvl[MAX_SEGMENTS][MAX_REF_FRAMES][MAX_MODE_LF_DELTAS]; +}; + +struct loopfilter { + int filter_level; + + int sharpness_level; + int last_sharpness_level; + + uint8_t mode_ref_delta_enabled; + uint8_t mode_ref_delta_update; + + /*0 = Intra, Last, GF, ARF*/ + signed char ref_deltas[MAX_REF_LF_DELTAS]; + signed char last_ref_deltas[MAX_REF_LF_DELTAS]; + + /*0 = ZERO_MV, MV*/ + signed char mode_deltas[MAX_MODE_LF_DELTAS]; + signed char last_mode_deltas[MAX_MODE_LF_DELTAS]; +}; + +static int vp9_clamp(int value, int low, int high) +{ + return value < low ? low : (value > high ? high : value); +} + +int segfeature_active(struct segmentation *seg, + int segment_id, + enum SEG_LVL_FEATURES feature_id) { + return seg->enabled && + (seg->feature_mask[segment_id] & (1 << feature_id)); +} + +int get_segdata(struct segmentation *seg, int segment_id, + enum SEG_LVL_FEATURES feature_id) { + return seg->feature_data[segment_id][feature_id]; +} + +static void vp9_update_sharpness(struct loop_filter_info_n *lfi, + int sharpness_lvl) +{ + int lvl; + /*For each possible value for the loop filter fill out limits*/ + for (lvl = 0; lvl <= MAX_LOOP_FILTER; lvl++) { + /*Set loop filter parameters that control sharpness.*/ + int block_inside_limit = lvl >> ((sharpness_lvl > 0) + + (sharpness_lvl > 4)); + + if (sharpness_lvl > 0) { + if (block_inside_limit > (9 - sharpness_lvl)) + block_inside_limit = (9 - sharpness_lvl); + } + + if (block_inside_limit < 1) + block_inside_limit = 1; + + lfi->lfthr[lvl].lim = (uint8_t)block_inside_limit; + lfi->lfthr[lvl].mblim = (uint8_t)(2 * (lvl + 2) + + block_inside_limit); + } +} + +int default_filt_lvl; +struct loop_filter_info_n *lfi; +struct loopfilter *lf; +struct segmentation *seg_4lf; + +/*instantiate this function once when decode is started*/ +void vp9_loop_filter_init(void) +{ + int i; + if (!lfi) + lfi = kmalloc(sizeof(struct loop_filter_info_n), GFP_KERNEL); + if (!lf) + lf = kmalloc(sizeof(struct loopfilter), GFP_KERNEL); + if (!seg_4lf) + seg_4lf = kmalloc(sizeof(struct segmentation), GFP_KERNEL); + if (lfi == NULL || lf == NULL || seg_4lf == NULL) { + pr_err("[test.c] vp9_loop_filter init malloc error!!!\n"); + return; + } + memset(lfi, 0, sizeof(struct loop_filter_info_n)); + memset(lf, 0, sizeof(struct loopfilter)); + memset(seg_4lf, 0, sizeof(struct segmentation)); + lf->sharpness_level = 0; /*init to 0 */ + /*init limits for given sharpness*/ + vp9_update_sharpness(lfi, lf->sharpness_level); + lf->last_sharpness_level = lf->sharpness_level; + /*init hev threshold const vectors (actually no use) + for (i = 0; i <= MAX_LOOP_FILTER; i++) + lfi->lfthr[i].hev_thr = (uint8_t)(i >> 4);*/ + + /*Write to register*/ + for (i = 0; i < 32; i++) { + unsigned int thr; + thr = ((lfi->lfthr[i * 2 + 1].lim & 0x3f)<<8) | + (lfi->lfthr[i * 2 + 1].mblim & 0xff); + thr = (thr<<16) | ((lfi->lfthr[i*2].lim & 0x3f)<<8) | + (lfi->lfthr[i * 2].mblim & 0xff); + WRITE_VREG(HEVC_DBLK_CFG9, thr); + } + + /*video format is VP9*/ + WRITE_VREG(HEVC_DBLK_CFGB, 0x40400001); +} + /* perform this function per frame*/ +void vp9_loop_filter_frame_init(struct segmentation *seg, + struct loop_filter_info_n *lfi, struct loopfilter *lf, + int default_filt_lvl) { + int i; + int seg_id; + /*n_shift is the multiplier for lf_deltas + the multiplier is 1 for when filter_lvl is between 0 and 31; + 2 when filter_lvl is between 32 and 63*/ + const int scale = 1 << (default_filt_lvl >> 5); + + /*update limits if sharpness has changed*/ + if (lf->last_sharpness_level != lf->sharpness_level) { + vp9_update_sharpness(lfi, lf->sharpness_level); + lf->last_sharpness_level = lf->sharpness_level; + + /*Write to register*/ + for (i = 0; i < 32; i++) { + unsigned int thr; + thr = ((lfi->lfthr[i * 2 + 1].lim & 0x3f) << 8) + | (lfi->lfthr[i * 2 + 1].mblim & 0xff); + thr = (thr << 16) | ((lfi->lfthr[i * 2].lim & 0x3f) << 8) + | (lfi->lfthr[i * 2].mblim & 0xff); + WRITE_VREG(HEVC_DBLK_CFG9, thr); + } + } + + for (seg_id = 0; seg_id < MAX_SEGMENTS; seg_id++) {/*MAX_SEGMENTS = 8*/ + int lvl_seg = default_filt_lvl; + if (segfeature_active(seg, seg_id, SEG_LVL_ALT_LF)) { + const int data = get_segdata(seg, seg_id, + SEG_LVL_ALT_LF); + lvl_seg = vp9_clamp(seg->abs_delta == SEGMENT_ABSDATA ? + data : default_filt_lvl + data, + 0, MAX_LOOP_FILTER); +#ifdef DBG_LF_PRINT + pr_info("segfeature_active!!!seg_id=%d,lvl_seg=%d\n", seg_id, lvl_seg); +#endif + } + + if (!lf->mode_ref_delta_enabled) { + /*we could get rid of this if we assume that deltas are set to + zero when not in use; encoder always uses deltas*/ + memset(lfi->lvl[seg_id], lvl_seg, sizeof(lfi->lvl[seg_id])); + } else { + int ref, mode; + const int intra_lvl = lvl_seg + lf->ref_deltas[INTRA_FRAME] + * scale; +#ifdef DBG_LF_PRINT + pr_info("LF_PRINT:vp9_loop_filter_frame_init,seg_id=%d\n", seg_id); + pr_info("ref_deltas[INTRA_FRAME]=%d\n", lf->ref_deltas[INTRA_FRAME]); +#endif + lfi->lvl[seg_id][INTRA_FRAME][0] = + vp9_clamp(intra_lvl, 0, MAX_LOOP_FILTER); + + for (ref = LAST_FRAME; ref < MAX_REF_FRAMES; ++ref) { + /* LAST_FRAME = 1, MAX_REF_FRAMES = 4*/ + for (mode = 0; mode < MAX_MODE_LF_DELTAS; ++mode) { + /*MAX_MODE_LF_DELTAS = 2*/ + const int inter_lvl = + lvl_seg + lf->ref_deltas[ref] * scale + + lf->mode_deltas[mode] * scale; +#ifdef DBG_LF_PRINT +#endif + lfi->lvl[seg_id][ref][mode] = + vp9_clamp(inter_lvl, 0, + MAX_LOOP_FILTER); + } + } + } + } + +#ifdef DBG_LF_PRINT + /*print out thr/lvl table per frame*/ + for (i = 0; i <= MAX_LOOP_FILTER; i++) { + pr_info("LF_PRINT:(%d)thr=%d,blim=%d,lim=%d\n", + i, lfi->lfthr[i].hev_thr, lfi->lfthr[i].mblim, + lfi->lfthr[i].lim); + } + for (seg_id = 0; seg_id < MAX_SEGMENTS; seg_id++) { + pr_info("LF_PRINT:lvl(seg_id=%d)(mode=0,%d,%d,%d,%d)\n", + seg_id, lfi->lvl[seg_id][0][0], + lfi->lvl[seg_id][1][0], lfi->lvl[seg_id][2][0], + lfi->lvl[seg_id][3][0]); + pr_info("i(mode=1,%d,%d,%d,%d)\n", lfi->lvl[seg_id][0][1], + lfi->lvl[seg_id][1][1], lfi->lvl[seg_id][2][1], + lfi->lvl[seg_id][3][1]); + } +#endif + + /*Write to register */ + for (i = 0; i < 16; i++) { + unsigned int level; + level = ((lfi->lvl[i >> 1][3][i & 1] & 0x3f) << 24) | + ((lfi->lvl[i >> 1][2][i & 1] & 0x3f) << 16) | + ((lfi->lvl[i >> 1][1][i & 1] & 0x3f) << 8) | + (lfi->lvl[i >> 1][0][i & 1] & 0x3f); + if (!default_filt_lvl) + level = 0; + WRITE_VREG(HEVC_DBLK_CFGA, level); + } +} +/* VP9_LPF_LVL_UPDATE */ +#endif + +static void vp9_init_decoder_hw(struct VP9Decoder_s *pbi) +{ + unsigned int data32; + int i; + + if (debug & VP9_DEBUG_BUFMGR) + pr_info("[test.c] Enable HEVC Parser Interrupt\n"); + data32 = READ_VREG(HEVC_PARSER_INT_CONTROL); +#if 1 + /* set bit 31~29 to 3 if HEVC_STREAM_FIFO_CTL[29] is 1 */ + data32 &= ~(7 << 29); + data32 |= (3 << 29); +#endif + data32 = data32 | + (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 & VP9_DEBUG_BUFMGR) + pr_info("[test.c] Enable HEVC Parser Shift\n"); + + data32 = READ_VREG(HEVC_SHIFT_STATUS); + data32 = data32 | + (0 << 1) |/*emulation_check_off VP9 + do not have emulation*/ + (1 << 0)/*startcode_check_on*/ + ; + WRITE_VREG(HEVC_SHIFT_STATUS, data32); + WRITE_VREG(HEVC_SHIFT_CONTROL, + (0 << 14) | /*disable_start_code_protect*/ + (1 << 10) | /*length_zero_startcode_en for VP9*/ + (1 << 9) | /*length_valid_startcode_en for VP9*/ + (3 << 6) | /*sft_valid_wr_position*/ + (2 << 4) | /*emulate_code_length_sub_1*/ + (3 << 1) | /*start_code_length_sub_1 + VP9 use 0x00000001 as startcode (4 Bytes)*/ + (1 << 0) /*stream_shift_enable*/ + ); + + WRITE_VREG(HEVC_CABAC_CONTROL, + (1 << 0)/*cabac_enable*/ + ); + + WRITE_VREG(HEVC_PARSER_CORE_CONTROL, + (1 << 0)/* hevc_parser_core_clk_en*/ + ); + + + WRITE_VREG(HEVC_DEC_STATUS_REG, 0); + + /*Initial IQIT_SCALELUT memory -- just to avoid X in simulation*/ + if (debug & VP9_DEBUG_BUFMGR) + pr_info("Initial IQIT_SCALELUT memory\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 +#ifdef MULTI_INSTANCE_SUPPORT + if (pbi->platform_dev && vdec_frame_based(hw_to_vdec(pbi))) + WRITE_VREG(DECODE_MODE, DECODE_MODE_MULTI_FRAMEBASE); + else + WRITE_VREG(DECODE_MODE, DECODE_MODE_MULTI_STREAMBASE); + WRITE_VREG(HEVC_DECODE_SIZE, 0); + WRITE_VREG(HEVC_DECODE_COUNT, 0); +#else + WRITE_VREG(DECODE_MODE, DECODE_MODE_SINGLE); + WRITE_VREG(HEVC_DECODE_PIC_BEGIN_REG, 0); + WRITE_VREG(HEVC_DECODE_PIC_NUM_REG, 0x7fffffff); /*to remove*/ +#endif + /*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*/ + ); +#ifdef VP9_10B_NV21 + /*Enable NV21 reference read mode for MC*/ + WRITE_VREG(HEVCD_MPP_DECOMP_CTL1, 0x1 << 31); +#endif + + /*Initialize mcrcc and decomp perf counters + mcrcc_perfcount_reset(); + decomp_perfcount_reset();*/ + return; +} + + +#ifdef CONFIG_HEVC_CLK_FORCED_ON +static void config_vp9_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 | (0x1 << 6) | (0x1 << 3) | (0x1 << 1)); + + /*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 VP9Decoder_s *pbi) +{ + unsigned int rdata32; + unsigned short is_inter; + /*pr_info("Entered config_mcrcc_axi_hw...\n");*/ + WRITE_VREG(HEVCD_MCRCC_CTL1, 0x2);/* reset mcrcc*/ + is_inter = ((pbi->common.frame_type != KEY_FRAME) && + (!pbi->common.intra_only)) ? 1 : 0; + if (!is_inter) { /* I-PIC*/ + /*remove reset -- disables clock*/ + WRITE_VREG(HEVCD_MCRCC_CTL1, 0x0); + return; + } + +#if 0 + pr_info("before call mcrcc_get_hitrate\r\n"); + mcrcc_get_hitrate(); + decomp_get_hitrate(); + decomp_get_comprate(); +#endif + + 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); + /*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); + return; +} +#endif + + +static struct VP9Decoder_s gHevc; + +static void vp9_local_uninit(struct VP9Decoder_s *pbi) +{ + pbi->rpm_ptr = NULL; + pbi->lmem_ptr = NULL; + if (pbi->rpm_addr) { + dma_unmap_single(amports_get_dma_device(), + pbi->rpm_phy_addr, RPM_BUF_SIZE, + DMA_FROM_DEVICE); + kfree(pbi->rpm_addr); + pbi->rpm_addr = NULL; + } + if (pbi->lmem_addr) { + if (pbi->lmem_phy_addr) + dma_free_coherent(amports_get_dma_device(), + LMEM_BUF_SIZE, pbi->lmem_addr, + pbi->lmem_phy_addr); + + pbi->lmem_addr = NULL; + } + if (pbi->prob_buffer_addr) { + if (pbi->prob_buffer_phy_addr) + dma_free_coherent(amports_get_dma_device(), + PROB_BUF_SIZE, pbi->prob_buffer_addr, + pbi->prob_buffer_phy_addr); + + pbi->prob_buffer_addr = NULL; + } + if (pbi->count_buffer_addr) { + if (pbi->count_buffer_phy_addr) + dma_free_coherent(amports_get_dma_device(), + COUNT_BUF_SIZE, pbi->count_buffer_addr, + pbi->count_buffer_phy_addr); + + pbi->count_buffer_addr = NULL; + } +#ifdef VP9_10B_MMU + if (pbi->frame_mmu_map_addr) { + if (pbi->frame_mmu_map_phy_addr) + dma_free_coherent(amports_get_dma_device(), + FRAME_MMU_MAP_SIZE, pbi->frame_mmu_map_addr, + pbi->frame_mmu_map_phy_addr); + + pbi->frame_mmu_map_addr = NULL; + } +#endif + +#ifdef VP9_LPF_LVL_UPDATE + kfree(lfi); + lfi = NULL; + kfree(lf); + lf = NULL; + kfree(seg_4lf); + seg_4lf = NULL; +#endif +} + +static int vp9_local_init(struct VP9Decoder_s *pbi) +{ + int ret = -1; + /*int losless_comp_header_size, losless_comp_body_size;*/ + + struct BuffInfo_s *cur_buf_info = NULL; + memset(&pbi->param, 0, sizeof(union param_u)); + memset(&pbi->common, 0, sizeof(struct VP9_Common_s)); +#ifdef MULTI_INSTANCE_SUPPORT + cur_buf_info = &pbi->work_space_buf_store; +#ifdef SUPPORT_4K2K + memcpy(cur_buf_info, &amvvp9_workbuff_spec[1], /* 4k2k work space */ + sizeof(struct BuffInfo_s)); +#else + memcpy(cur_buf_info, &amvvp9_workbuff_spec[0], /* 1080p work space */ + sizeof(struct BuffInfo_s)); +#endif + cur_buf_info->start_adr = pbi->buf_start; + pbi->mc_buf_spec.buf_end = pbi->buf_start + pbi->buf_size; +#else +/*! MULTI_INSTANCE_SUPPORT*/ +#ifdef SUPPORT_4K2K + cur_buf_info = &amvvp9_workbuff_spec[1]; /* 4k2k work space */ +#else + cur_buf_info = &amvvp9_workbuff_spec[0]; /* 1080p work space */ +#endif +#endif + + init_buff_spec(pbi, cur_buf_info); + pbi->mc_buf_spec.buf_start = (cur_buf_info->end_adr + 0xffff) + & (~0xffff); + pbi->mc_buf_spec.buf_size = (pbi->mc_buf_spec.buf_end + - pbi->mc_buf_spec.buf_start); + if (debug) { + pr_err("pbi->mc_buf_spec.buf_start %x-%x\n", + pbi->mc_buf_spec.buf_start, + pbi->mc_buf_spec.buf_start + + pbi->mc_buf_spec.buf_size); + } + vp9_bufmgr_init(pbi, cur_buf_info, &pbi->mc_buf_spec); + + pbi->init_pic_w = buf_alloc_width ? buf_alloc_width : + (pbi->vvp9_amstream_dec_info.width ? + pbi->vvp9_amstream_dec_info.width : + pbi->work_space_buf->max_width); + pbi->init_pic_h = buf_alloc_height ? buf_alloc_height : + (pbi->vvp9_amstream_dec_info.height ? + pbi->vvp9_amstream_dec_info.height : + pbi->work_space_buf->max_height); +#ifndef VP9_10B_MMU + init_buf_list(pbi); +#endif + init_pic_list(pbi); + + pts_unstable = ((unsigned long)(pbi->vvp9_amstream_dec_info.param) + & 0x40) >> 6; + + pbi->video_signal_type = 0; + video_signal_type = pbi->video_signal_type; + + if ((debug & VP9_DEBUG_SEND_PARAM_WITH_REG) == 0) { + pbi->rpm_addr = kmalloc(RPM_BUF_SIZE, GFP_KERNEL); + if (pbi->rpm_addr == NULL) { + pr_err("%s: failed to alloc rpm buffer\n", __func__); + return -1; + } + + pbi->rpm_phy_addr = dma_map_single(amports_get_dma_device(), + pbi->rpm_addr, RPM_BUF_SIZE, DMA_FROM_DEVICE); + if (dma_mapping_error(amports_get_dma_device(), + pbi->rpm_phy_addr)) { + pr_err("%s: failed to map rpm buffer\n", __func__); + kfree(pbi->rpm_addr); + pbi->rpm_addr = NULL; + return -1; + } + + pbi->rpm_ptr = pbi->rpm_addr; + } + + if (debug & VP9_DEBUG_UCODE) { + pbi->lmem_addr = dma_alloc_coherent(amports_get_dma_device(), + LMEM_BUF_SIZE, + &pbi->lmem_phy_addr, GFP_KERNEL); + if (pbi->lmem_addr == NULL) { + pr_err("%s: failed to alloc lmem buffer\n", __func__); + return -1; + } +/* + pbi->lmem_phy_addr = dma_map_single(amports_get_dma_device(), + pbi->lmem_addr, LMEM_BUF_SIZE, DMA_BIDIRECTIONAL); + if (dma_mapping_error(amports_get_dma_device(), + pbi->lmem_phy_addr)) { + pr_err("%s: failed to map lmem buffer\n", __func__); + kfree(pbi->lmem_addr); + pbi->lmem_addr = NULL; + return -1; + } +*/ + pbi->lmem_ptr = pbi->lmem_addr; + } + pbi->prob_buffer_addr = dma_alloc_coherent(amports_get_dma_device(), + PROB_BUF_SIZE, + &pbi->prob_buffer_phy_addr, GFP_KERNEL); + if (pbi->prob_buffer_addr == NULL) { + pr_err("%s: failed to alloc prob_buffer\n", __func__); + return -1; + } + memset(pbi->prob_buffer_addr, 0, PROB_BUF_SIZE); +/* pbi->prob_buffer_phy_addr = dma_map_single(amports_get_dma_device(), + pbi->prob_buffer_addr, PROB_BUF_SIZE, DMA_BIDIRECTIONAL); + if (dma_mapping_error(amports_get_dma_device(), + pbi->prob_buffer_phy_addr)) { + pr_err("%s: failed to map prob_buffer\n", __func__); + kfree(pbi->prob_buffer_addr); + pbi->prob_buffer_addr = NULL; + return -1; + } +*/ + pbi->count_buffer_addr = dma_alloc_coherent(amports_get_dma_device(), + COUNT_BUF_SIZE, + &pbi->count_buffer_phy_addr, GFP_KERNEL); + if (pbi->count_buffer_addr == NULL) { + pr_err("%s: failed to alloc count_buffer\n", __func__); + return -1; + } + memset(pbi->count_buffer_addr, 0, COUNT_BUF_SIZE); +/* pbi->count_buffer_phy_addr = dma_map_single(amports_get_dma_device(), + pbi->count_buffer_addr, COUNT_BUF_SIZE, DMA_BIDIRECTIONAL); + if (dma_mapping_error(amports_get_dma_device(), + pbi->count_buffer_phy_addr)) { + pr_err("%s: failed to map count_buffer\n", __func__); + kfree(pbi->count_buffer_addr); + pbi->count_buffer_addr = NULL; + return -1; + } +*/ +#ifdef VP9_10B_MMU + pbi->frame_mmu_map_addr = dma_alloc_coherent(amports_get_dma_device(), + FRAME_MMU_MAP_SIZE, + &pbi->frame_mmu_map_phy_addr, GFP_KERNEL); + if (pbi->frame_mmu_map_addr == NULL) { + pr_err("%s: failed to alloc count_buffer\n", __func__); + return -1; + } + memset(pbi->frame_mmu_map_addr, 0, COUNT_BUF_SIZE); +/* pbi->frame_mmu_map_phy_addr = dma_map_single(amports_get_dma_device(), + pbi->frame_mmu_map_addr, FRAME_MMU_MAP_SIZE, DMA_BIDIRECTIONAL); + if (dma_mapping_error(amports_get_dma_device(), + pbi->frame_mmu_map_phy_addr)) { + pr_err("%s: failed to map count_buffer\n", __func__); + kfree(pbi->frame_mmu_map_addr); + pbi->frame_mmu_map_addr = NULL; + return -1; + }*/ +#endif + + 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_BUFFER_CONFIG_s *pic_config) +{ + int canvas_w = ALIGN(pic_config->y_crop_width, 64)/4; + int canvas_h = ALIGN(pic_config->y_crop_height, 32)/4; + int blkmode = mem_map_mode; + /*CANVAS_BLKMODE_64X32*/ + if (double_write_mode) { + canvas_w = pic_config->y_crop_width; + canvas_h = pic_config->y_crop_height; + if (double_write_mode == 2) { + 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_config->y_canvas_index = 128 + pic_config->index * 2; + pic_config->uv_canvas_index = 128 + pic_config->index * 2 + 1; + + canvas_config_ex(pic_config->y_canvas_index, + pic_config->dw_y_adr, canvas_w, canvas_h, + CANVAS_ADDR_NOWRAP, blkmode, 0x7); + canvas_config_ex(pic_config->uv_canvas_index, + pic_config->dw_u_v_adr, canvas_w, canvas_h, + CANVAS_ADDR_NOWRAP, blkmode, 0x7); +#ifdef MULTI_INSTANCE_SUPPORT + pic_config->canvas_config[0].phy_addr = + pic_config->dw_y_adr; + pic_config->canvas_config[0].width = + canvas_w; + pic_config->canvas_config[0].height = + canvas_h; + pic_config->canvas_config[0].block_mode = + blkmode; + pic_config->canvas_config[0].endian = 7; + + pic_config->canvas_config[1].phy_addr = + pic_config->dw_u_v_adr; + pic_config->canvas_config[1].width = + canvas_w; + pic_config->canvas_config[1].height = + canvas_h; + pic_config->canvas_config[1].block_mode = + blkmode; + pic_config->canvas_config[1].endian = 7; +#endif + } else { + #ifndef VP9_10B_MMU + pic_config->y_canvas_index = 128 + pic_config->index; + pic_config->uv_canvas_index = 128 + pic_config->index; + + canvas_config_ex(pic_config->y_canvas_index, + pic_config->mc_y_adr, canvas_w, canvas_h, + CANVAS_ADDR_NOWRAP, blkmode, 0x7); + canvas_config_ex(pic_config->uv_canvas_index, + pic_config->mc_u_v_adr, canvas_w, canvas_h, + CANVAS_ADDR_NOWRAP, blkmode, 0x7); + #endif + } + +} + + +static void set_frame_info(struct VP9Decoder_s *pbi, struct vframe_s *vf) +{ + unsigned int ar; + + vf->duration = pbi->frame_dur; + vf->duration_pulldown = 0; + vf->flag = 0; + + ar = min_t(u32, pbi->frame_ar, DISP_RATIO_ASPECT_RATIO_MAX); + vf->ratio_control = (ar << DISP_RATIO_ASPECT_RATIO_BIT); + + return; +} + +static int vvp9_vf_states(struct vframe_states *states, void *op_arg) +{ + struct VP9Decoder_s *pbi = (struct VP9Decoder_s *)op_arg; + + states->vf_pool_size = VF_POOL_SIZE; + states->buf_free_num = kfifo_len(&pbi->newframe_q); + states->buf_avail_num = kfifo_len(&pbi->display_q); + + if (step == 2) + states->buf_avail_num = 0; + return 0; +} + +static struct vframe_s *vvp9_vf_peek(void *op_arg) +{ + struct vframe_s *vf; + struct VP9Decoder_s *pbi = (struct VP9Decoder_s *)op_arg; + if (step == 2) + return NULL; + + if (kfifo_peek(&pbi->display_q, &vf)) + return vf; + + return NULL; +} + +static struct vframe_s *vvp9_vf_get(void *op_arg) +{ + struct vframe_s *vf; + struct VP9Decoder_s *pbi = (struct VP9Decoder_s *)op_arg; + if (step == 2) + return NULL; + else if (step == 1) + step = 2; + + if (kfifo_get(&pbi->display_q, &vf)) { + uint8_t index = vf->index & 0xff; + if (index >= 0 && index < FRAME_BUFFERS) + return vf; + } + return NULL; +} + +static void vvp9_vf_put(struct vframe_s *vf, void *op_arg) +{ + struct VP9Decoder_s *pbi = (struct VP9Decoder_s *)op_arg; + uint8_t index = vf->index & 0xff; + + kfifo_put(&pbi->newframe_q, (const struct vframe_s *)vf); + + if (index >= 0 + && index < FRAME_BUFFERS) { + struct VP9_Common_s *cm = &pbi->common; + struct BufferPool_s *pool = cm->buffer_pool; + lock_buffer_pool(pool); + if (pool->frame_bufs[index].buf.vf_ref > 0) + pool->frame_bufs[index].buf.vf_ref--; + + if (pbi->wait_buf) + WRITE_VREG(HEVC_ASSIST_MBOX1_IRQ_REG, + 0x1); + pbi->last_put_idx = index; + pbi->new_frame_displayed++; + unlock_buffer_pool(pool); + } + +} + +static int vvp9_event_cb(int type, void *data, void *private_data) +{ + if (type & VFRAME_EVENT_RECEIVER_RESET) { +#if 0 + unsigned long flags; + amhevc_stop(); +#ifndef CONFIG_AMLOGIC_POST_PROCESS_MANAGER + vf_light_unreg_provider(&vvp9_vf_prov); +#endif + spin_lock_irqsave(&pbi->lock, flags); + vvp9_local_init(); + vvp9_prot_init(); + spin_unlock_irqrestore(&pbi->lock, flags); +#ifndef CONFIG_AMLOGIC_POST_PROCESS_MANAGER + vf_reg_provider(&vvp9_vf_prov); +#endif + amhevc_start(); +#endif + } + + return 0; +} + +void inc_vf_ref(struct VP9Decoder_s *pbi, int index) +{ + struct VP9_Common_s *cm = &pbi->common; + cm->buffer_pool->frame_bufs[index].buf.vf_ref++; + + if (debug & VP9_DEBUG_BUFMGR) + pr_info("%s index = %d new vf_ref = %d\r\n", + __func__, index, + cm->buffer_pool->frame_bufs[index].buf.vf_ref); +} + + +static int prepare_display_buf(struct VP9Decoder_s *pbi, + struct PIC_BUFFER_CONFIG_s *pic_config) +{ + struct vframe_s *vf = NULL; + int stream_offset = pic_config->stream_offset; + unsigned short slice_type = pic_config->slice_type; + + if (debug & VP9_DEBUG_BUFMGR) + pr_info("%s index = %d\r\n", __func__, pic_config->index); + if (kfifo_get(&pbi->newframe_q, &vf) == 0) { + pr_info("fatal error, no available buffer slot."); + return -1; + } + + if (double_write_mode) { + set_canvas(pic_config); + } + 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 + pbi->pts_missed++; +#endif + vf->pts = 0; + vf->pts_us64 = 0; + } +#ifdef DEBUG_PTS + else + pbi->pts_hit++; +#endif + if (pts_unstable) + pbi->pts_mode = PTS_NONE_REF_USE_DURATION; + + if ((pbi->pts_mode == PTS_NORMAL) && (vf->pts != 0) + && pbi->get_frame_dur) { + int pts_diff = (int)vf->pts - pbi->last_lookup_pts; + + if (pts_diff < 0) { + pbi->pts_mode_switching_count++; + pbi->pts_mode_recovery_count = 0; + + if (pbi->pts_mode_switching_count >= + PTS_MODE_SWITCHING_THRESHOLD) { + pbi->pts_mode = + PTS_NONE_REF_USE_DURATION; + pr_info + ("HEVC: switch to n_d mode.\n"); + } + + } else { + int p = PTS_MODE_SWITCHING_RECOVERY_THREASHOLD; + pbi->pts_mode_recovery_count++; + if (pbi->pts_mode_recovery_count > p) { + pbi->pts_mode_switching_count = 0; + pbi->pts_mode_recovery_count = 0; + } + } + } + + if (vf->pts != 0) + pbi->last_lookup_pts = vf->pts; + + if ((pbi->pts_mode == PTS_NONE_REF_USE_DURATION) + && (slice_type != KEY_FRAME)) + vf->pts = pbi->last_pts + DUR2PTS(pbi->frame_dur); + pbi->last_pts = vf->pts; + + if (vf->pts_us64 != 0) + pbi->last_lookup_pts_us64 = vf->pts_us64; + + if ((pbi->pts_mode == PTS_NONE_REF_USE_DURATION) + && (slice_type != KEY_FRAME)) { + vf->pts_us64 = + pbi->last_pts_us64 + + (DUR2PTS(pbi->frame_dur) * 100 / 9); + } + pbi->last_pts_us64 = vf->pts_us64; + if ((debug & VP9_DEBUG_OUT_PTS) != 0) { + pr_info + ("VP9 dec out pts: vf->pts=%d, vf->pts_us64 = %lld\n", + vf->pts, vf->pts_us64); + } + + vf->index = 0xff00 | pic_config->index; +#if 1 +/*SUPPORT_10BIT*/ + if (double_write_mode & 0x10) { + /* double write only */ + vf->compBodyAddr = 0; + vf->compHeadAddr = 0; + } else { +#ifdef VP9_10B_MMU + vf->compBodyAddr = 0; + vf->compHeadAddr = pic_config->header_adr; +#else + vf->compBodyAddr = pic_config->mc_y_adr; /*body adr*/ + vf->compHeadAddr = pic_config->mc_y_adr + + pic_config->comp_body_size; + /*head adr*/ +#endif + } + if (double_write_mode) { + vf->type = VIDTYPE_PROGRESSIVE | + VIDTYPE_VIU_FIELD; + vf->type |= VIDTYPE_VIU_NV21; +#ifdef MULTI_INSTANCE_SUPPORT + if (pbi->m_ins_flag) { + vf->canvas0Addr = vf->canvas1Addr = -1; + vf->plane_num = 2; + vf->canvas0_config[0] = + pic_config->canvas_config[0]; + vf->canvas0_config[1] = + pic_config->canvas_config[1]; + + vf->canvas1_config[0] = + pic_config->canvas_config[0]; + vf->canvas1_config[1] = + pic_config->canvas_config[1]; + + } else +#endif + vf->canvas0Addr = vf->canvas1Addr = + spec2canvas(pic_config); + } else { + vf->canvas0Addr = vf->canvas1Addr = 0; + vf->type = VIDTYPE_COMPRESS | VIDTYPE_VIU_FIELD; +#ifdef VP9_10B_MMU + vf->type |= VIDTYPE_SCATTER; +#endif + switch (pic_config->bit_depth) { + case VPX_BITS_8: + vf->bitdepth = BITDEPTH_Y8 | + BITDEPTH_U8 | BITDEPTH_V8; + break; + case VPX_BITS_10: + case VPX_BITS_12: + vf->bitdepth = BITDEPTH_Y10 | + BITDEPTH_U10 | BITDEPTH_V10; + break; + default: + vf->bitdepth = BITDEPTH_Y10 | + BITDEPTH_U10 | BITDEPTH_V10; + break; + } + if (pic_config->bit_depth == VPX_BITS_8) + vf->bitdepth |= BITDEPTH_SAVING_MODE; + } +#else + vf->type = VIDTYPE_PROGRESSIVE | VIDTYPE_VIU_FIELD; + vf->type |= VIDTYPE_VIU_NV21; + vf->canvas0Addr = vf->canvas1Addr = spec2canvas(pic_config); +#endif + set_frame_info(pbi, vf); + /* if((vf->width!=pic_config->width)| + (vf->height!=pic_config->height)) */ + /* pr_info("aaa: %d/%d, %d/%d\n", + vf->width,vf->height, pic_config->width, + pic_config->height); */ + if (double_write_mode == 2) { + vf->width = pic_config->y_crop_width/4; + vf->height = pic_config->y_crop_height/4; + } else { + vf->width = pic_config->y_crop_width; + vf->height = pic_config->y_crop_height; + } + if (force_w_h != 0) { + vf->width = (force_w_h >> 16) & 0xffff; + vf->height = force_w_h & 0xffff; + } + vf->compWidth = pic_config->y_crop_width; + vf->compHeight = pic_config->y_crop_height; + if (force_fps & 0x100) { + u32 rate = force_fps & 0xff; + if (rate) + vf->duration = 96000/rate; + else + vf->duration = 0; + } + if (vf->type & VIDTYPE_SCATTER) { + vf->mem_handle = decoder_mmu_box_get_mem_handle( + pbi->mmu_box, + pic_config->index); + } else { + vf->mem_handle = decoder_bmmu_box_get_mem_handle( + pbi->bmmu_box, + pic_config->index); + } + inc_vf_ref(pbi, pic_config->index); + kfifo_put(&pbi->display_q, (const struct vframe_s *)vf); + vf_notify_receiver(pbi->provider_name, + VFRAME_EVENT_PROVIDER_VFRAME_READY, NULL); + } + + return 0; +} + +static void get_rpm_param(union param_u *params) +{ + int i; + unsigned int data32; + if (debug & VP9_DEBUG_BUFMGR) + pr_info("enter %s\r\n", __func__); + 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); + } + if (debug & VP9_DEBUG_BUFMGR) + pr_info("leave %s\r\n", __func__); +} +static void debug_buffer_mgr_more(struct VP9Decoder_s *pbi) +{ + int i; + if (!(debug & VP9_DEBUG_BUFMGR_MORE)) + return; + pr_info("vp9_param: (%d)\n", pbi->slice_idx); + for (i = 0; i < (RPM_END-RPM_BEGIN); i++) { + pr_info("%04x ", vp9_param.l.data[i]); + if (((i + 1) & 0xf) == 0) + pr_info("\n"); + } + pr_info("=============param==========\r\n"); + pr_info("profile %x\r\n", vp9_param.p.profile); + pr_info("show_existing_frame %x\r\n", + vp9_param.p.show_existing_frame); + pr_info("frame_to_show_idx %x\r\n", + vp9_param.p.frame_to_show_idx); + pr_info("frame_type %x\r\n", vp9_param.p.frame_type); + pr_info("show_frame %x\r\n", vp9_param.p.show_frame); + pr_info("e.r.r.o.r_resilient_mode %x\r\n", + vp9_param.p.error_resilient_mode); + pr_info("intra_only %x\r\n", vp9_param.p.intra_only); + pr_info("display_size_present %x\r\n", + vp9_param.p.display_size_present); + pr_info("reset_frame_context %x\r\n", + vp9_param.p.reset_frame_context); + pr_info("refresh_frame_flags %x\r\n", + vp9_param.p.refresh_frame_flags); + pr_info("bit_depth %x\r\n", vp9_param.p.bit_depth); + pr_info("width %x\r\n", vp9_param.p.width); + pr_info("height %x\r\n", vp9_param.p.height); + pr_info("display_width %x\r\n", vp9_param.p.display_width); + pr_info("display_height %x\r\n", vp9_param.p.display_height); + pr_info("ref_info %x\r\n", vp9_param.p.ref_info); + pr_info("same_frame_size %x\r\n", vp9_param.p.same_frame_size); + if (!(debug & VP9_DEBUG_DBG_LF_PRINT)) + return; + pr_info("mode_ref_delta_enabled: 0x%x\r\n", + vp9_param.p.mode_ref_delta_enabled); + pr_info("sharpness_level: 0x%x\r\n", + vp9_param.p.sharpness_level); + pr_info("ref_deltas: 0x%x, 0x%x, 0x%x, 0x%x\r\n", + vp9_param.p.ref_deltas[0], vp9_param.p.ref_deltas[1], + vp9_param.p.ref_deltas[2], vp9_param.p.ref_deltas[3]); + pr_info("mode_deltas: 0x%x, 0x%x\r\n", vp9_param.p.mode_deltas[0], + vp9_param.p.mode_deltas[1]); + pr_info("filter_level: 0x%x\r\n", vp9_param.p.filter_level); + pr_info("seg_enabled: 0x%x\r\n", vp9_param.p.seg_enabled); + pr_info("seg_abs_delta: 0x%x\r\n", vp9_param.p.seg_abs_delta); + pr_info("seg_lf_feature_enabled: 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\r\n", + (vp9_param.p.seg_lf_info[0]>>15 & 1), + (vp9_param.p.seg_lf_info[1]>>15 & 1), + (vp9_param.p.seg_lf_info[2]>>15 & 1), + (vp9_param.p.seg_lf_info[3]>>15 & 1), + (vp9_param.p.seg_lf_info[4]>>15 & 1), + (vp9_param.p.seg_lf_info[5]>>15 & 1), + (vp9_param.p.seg_lf_info[6]>>15 & 1), + (vp9_param.p.seg_lf_info[7]>>15 & 1)); + pr_info("seg_lf_feature_data: 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\r\n", + (vp9_param.p.seg_lf_info[0] & 0x13f), + (vp9_param.p.seg_lf_info[1] & 0x13f), + (vp9_param.p.seg_lf_info[2] & 0x13f), + (vp9_param.p.seg_lf_info[3] & 0x13f), + (vp9_param.p.seg_lf_info[4] & 0x13f), + (vp9_param.p.seg_lf_info[5] & 0x13f), + (vp9_param.p.seg_lf_info[6] & 0x13f), + (vp9_param.p.seg_lf_info[7] & 0x13f)); + +} + +static irqreturn_t vvp9_isr_thread_fn(int irq, void *data) +{ + struct VP9Decoder_s *pbi = (struct VP9Decoder_s *)data; + unsigned int dec_status = pbi->dec_status; + struct VP9_Common_s *const cm = &pbi->common; + int i, ret; + + /*if (pbi->wait_buf) + pr_info("set wait_buf to 0\r\n"); + */ + pbi->wait_buf = 0; + + if (dec_status == VP9_EOS) { + pr_info("VP9_EOS, flush buffer\r\n"); + + vp9_bufmgr_postproc(pbi); + + pr_info("send VP9_10B_DISCARD_NAL\r\n"); + WRITE_VREG(HEVC_DEC_STATUS_REG, VP9_10B_DISCARD_NAL); + pbi->process_busy = 0; + return IRQ_HANDLED; + } + + if (dec_status != VP9_HEAD_PARSER_DONE) { + pbi->process_busy = 0; + return IRQ_HANDLED; + } + if (pbi->frame_count > 0) + vp9_bufmgr_postproc(pbi); + + if (debug & VP9_DEBUG_SEND_PARAM_WITH_REG) { + get_rpm_param(&vp9_param); + } else { + dma_sync_single_for_cpu( + amports_get_dma_device(), + pbi->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++) + vp9_param.l.data[i + ii] = + pbi->rpm_ptr[i + 3 - ii]; + } + } + debug_buffer_mgr_more(pbi); + + bit_depth_luma = vp9_param.p.bit_depth; + bit_depth_chroma = vp9_param.p.bit_depth; + + ret = vp9_bufmgr_process(pbi, &vp9_param); + pbi->slice_idx++; + if (ret < 0) { + pr_info("vp9_bufmgr_process=> %d, VP9_10B_DISCARD_NAL\r\n", + ret); + WRITE_VREG(HEVC_DEC_STATUS_REG, VP9_10B_DISCARD_NAL); + pbi->process_busy = 0; + return IRQ_HANDLED; + } else if (ret == 0) { + pbi->frame_count++; + /*pr_info("Decode Frame Data %d\n", pbi->frame_count);*/ + config_pic_size(pbi, vp9_param.p.bit_depth); + if ((pbi->common.frame_type != KEY_FRAME) + && (!pbi->common.intra_only)) { + config_mc_buffer(pbi, vp9_param.p.bit_depth); + config_mpred_hw(pbi); + } else { + clear_mpred_hw(pbi); + } +#ifdef MCRCC_ENABLE + config_mcrcc_axi_hw(pbi); +#endif + config_sao_hw(pbi, &vp9_param); + +#ifdef VP9_LPF_LVL_UPDATE + /* + * Get loop filter related picture level parameters from Parser + */ + lf->mode_ref_delta_enabled = vp9_param.p.mode_ref_delta_enabled; + lf->sharpness_level = vp9_param.p.sharpness_level; + for (i = 0; i < 4; i++) + lf->ref_deltas[i] = vp9_param.p.ref_deltas[i]; + for (i = 0; i < 2; i++) + lf->mode_deltas[i] = vp9_param.p.mode_deltas[i]; + default_filt_lvl = vp9_param.p.filter_level; + seg_4lf->enabled = vp9_param.p.seg_enabled; + seg_4lf->abs_delta = vp9_param.p.seg_abs_delta; + for (i = 0; i < MAX_SEGMENTS; i++) + seg_4lf->feature_mask[i] = (vp9_param.p.seg_lf_info[i] & + 0x8000) ? (1 << SEG_LVL_ALT_LF) : 0; + + for (i = 0; i < MAX_SEGMENTS; i++) + seg_4lf->feature_data[i][SEG_LVL_ALT_LF] + = (vp9_param.p.seg_lf_info[i] + & 0x100) ? -(vp9_param.p.seg_lf_info[i] + & 0x3f) : (vp9_param.p.seg_lf_info[i] & 0x3f); + /* + * Update loop filter Thr/Lvl table for every frame + */ + /*pr_info + ("vp9_loop_filter (run before every frame decoding start)\n");*/ + vp9_loop_filter_frame_init(seg_4lf, lfi, lf, default_filt_lvl); +#endif + /*pr_info("HEVC_DEC_STATUS_REG <= VP9_10B_DECODE_SLICE\n");*/ + + WRITE_VREG(HEVC_DEC_STATUS_REG, VP9_10B_DECODE_SLICE); + } else { + pr_info("Skip search next start code\n"); + cm->prev_fb_idx = INVALID_IDX; + /*skip, search next start code*/ + WRITE_VREG(HEVC_DEC_STATUS_REG, VP9_10B_DECODE_SLICE); + } + pbi->process_busy = 0; +#ifdef VP9_10B_MMU + if (pbi->last_put_idx >= 0 && pbi->last_put_idx < FRAME_BUFFERS) { + struct RefCntBuffer_s *frame_bufs = cm->buffer_pool->frame_bufs; + int i = pbi->last_put_idx; + /*free not used buffers.*/ + if ((frame_bufs[i].ref_count == 0) && + (frame_bufs[i].buf.vf_ref == 0) && + (frame_bufs[i].buf.used_by_display == 0) && + (frame_bufs[i].buf.index != -1)) { + decoder_mmu_box_free_idx(pbi->mmu_box, i); + } + pbi->last_put_idx = -1; + } +#endif + return IRQ_HANDLED; +} + +static irqreturn_t vvp9_isr(int irq, void *data) +{ + int i; + unsigned int dec_status; + struct VP9Decoder_s *pbi = (struct VP9Decoder_s *)data; + unsigned int adapt_prob_status; + struct VP9_Common_s *const cm = &pbi->common; + dec_status = READ_VREG(HEVC_DEC_STATUS_REG); + adapt_prob_status = READ_VREG(VP9_ADAPT_PROB_REG); + if (pbi->init_flag == 0) + return IRQ_HANDLED; + if (pbi->process_busy)/*on process.*/ + return IRQ_HANDLED; + pbi->dec_status = dec_status; + pbi->process_busy = 1; + if (debug & VP9_DEBUG_BUFMGR) + pr_info("vp9 isr dec status = %d\n", dec_status); + + if (debug & VP9_DEBUG_UCODE) { + if (READ_HREG(DEBUG_REG1) & 0x10000) { + dma_sync_single_for_cpu( + amports_get_dma_device(), + pbi->lmem_phy_addr, + LMEM_BUF_SIZE, + DMA_FROM_DEVICE); + + pr_info("LMEM<tag %x>:\n", READ_HREG(DEBUG_REG1)); + for (i = 0; i < 0x400; i += 4) { + int ii; + if ((i & 0xf) == 0) + pr_info("%03x: ", i); + for (ii = 0; ii < 4; ii++) { + pr_info("%04x ", + pbi->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); + pbi->process_busy = 0; + return IRQ_HANDLED; + } + + } + + if (pbi->error_flag == 1) { + pbi->error_flag = 2; + pbi->process_busy = 0; + return IRQ_HANDLED; + } else if (pbi->error_flag == 3) { + pbi->process_busy = 0; + return IRQ_HANDLED; + } + + if (is_buffer_empty(cm)) { + /* + if (pbi->wait_buf == 0) + pr_info("set wait_buf to 1\r\n"); + */ + pbi->wait_buf = 1; + pbi->process_busy = 0; + return IRQ_HANDLED; + } + if ((adapt_prob_status & 0xff) == 0xfd) { + /*VP9_REQ_ADAPT_PROB*/ + int pre_fc = (cm->frame_type == KEY_FRAME) ? 1 : 0; + uint8_t *prev_prob_b = + ((uint8_t *)pbi->prob_buffer_addr) + + ((adapt_prob_status >> 8) * 0x1000); + uint8_t *cur_prob_b = + ((uint8_t *)pbi->prob_buffer_addr) + 0x4000; + uint8_t *count_b = (uint8_t *)pbi->count_buffer_addr; + + adapt_coef_probs(pbi->pic_count, + (cm->last_frame_type == KEY_FRAME), + pre_fc, (adapt_prob_status >> 8), + (unsigned int *)prev_prob_b, + (unsigned int *)cur_prob_b, (unsigned int *)count_b); + + memcpy(prev_prob_b, cur_prob_b, PROB_SIZE); + WRITE_VREG(VP9_ADAPT_PROB_REG, 0); + pbi->pic_count += 1; + + /*return IRQ_HANDLED;*/ + } +#ifdef MULTI_INSTANCE_SUPPORT +#if 0 + if ((dec_status == HEVC_DECPIC_DATA_DONE) && (pbi->m_ins_flag)) { + if (pbi->chunk) { + pbi->cur_pic->pts = pbi->chunk->pts; + pbi->cur_pic->pts64 = pbi->chunk->pts64; + } else if (pts_lookup_offset_us64 + (PTS_TYPE_VIDEO, + pbi->cur_pic->stream_offset, + &pbi->cur_pic->pts, + 0, + &pbi->cur_pic->pts64) != 0) { +#ifdef DEBUG_PTS + pbi->pts_missed++; +#endif + pbi->cur_pic->pts = 0; + pbi->cur_pic->pts64 = 0; + } + } +#endif + if (dec_status == HEVC_NAL_DECODE_DONE) { + if (pbi->m_ins_flag) { +#if 0 + if (!vdec_frame_based(hw_to_vdec(hevc))) { + pbi->dec_result = DEC_RESULT_AGAIN; + if ((debug & + ONLY_RESET_AT_START) == 0) + amhevc_stop(); + } else + pbi->dec_result = DEC_RESULT_GET_DATA; +#else + if (!vdec_frame_based(hw_to_vdec(pbi))) + pbi->dec_result = DEC_RESULT_AGAIN; + else + pbi->dec_result = DEC_RESULT_DONE; + amhevc_stop(); +#endif + schedule_work(&pbi->work); + } + pbi->process_busy = 0; + return IRQ_HANDLED; + } else if (dec_status == HEVC_DECPIC_DATA_DONE) { + if (pbi->m_ins_flag) { + pbi->dec_result = DEC_RESULT_DONE; + amhevc_stop(); + schedule_work(&pbi->work); + } + + pbi->process_busy = 0; + return IRQ_HANDLED; + } else if ( + (dec_status == HEVC_SEARCH_BUFEMPTY) || + (dec_status == HEVC_DECODE_BUFEMPTY) || + (dec_status == HEVC_DECODE_TIMEOUT)) { + if (vdec_frame_based(hw_to_vdec(pbi)) || + (READ_VREG(HEVC_STREAM_LEVEL) > 0x200)) { + if (debug & VP9_DEBUG_DIS_LOC_ERROR_PROC) { + vp9_print(pbi, PRINT_FLAG_ERROR, + "%s decoding error, level 0x%x\n", + __func__, READ_VREG(HEVC_STREAM_LEVEL)); + goto send_again; + } + amhevc_stop(); + vp9_print(pbi, PRINT_FLAG_UCODE_EVT, + "%s %s\n", __func__, + (dec_status == HEVC_SEARCH_BUFEMPTY) ? + "HEVC_SEARCH_BUFEMPTY" : + (dec_status == HEVC_DECODE_BUFEMPTY) ? + "HEVC_DECODE_BUFEMPTY" : "HEVC_DECODE_TIMEOUT"); + pbi->dec_result = DEC_RESULT_DONE; + + schedule_work(&pbi->work); + } else { + /* WRITE_VREG(DPB_STATUS_REG, H264_ACTION_INIT); */ + vp9_print(pbi, PRINT_FLAG_VDEC_STATUS, + "%s DEC_RESULT_AGAIN\n", __func__); +send_again: + pbi->dec_result = DEC_RESULT_AGAIN; + schedule_work(&pbi->work); + } + pbi->process_busy = 0; + return IRQ_HANDLED; + } +#endif + + return IRQ_WAKE_THREAD; +} + +static void vvp9_put_timer_func(unsigned long arg) +{ + struct VP9Decoder_s *pbi = (struct VP9Decoder_s *)arg; + struct timer_list *timer = &pbi->timer; + uint8_t empty_flag; + unsigned int buf_level; + + enum receviver_start_e state = RECEIVER_INACTIVE; + if (pbi->m_ins_flag) { + if (hw_to_vdec(pbi)->next_status + == VDEC_STATUS_DISCONNECTED) { + pbi->dec_result = DEC_RESULT_DONE; + schedule_work(&pbi->work); + pr_info( + "vdec requested to be disconnected\n"); + return; + } + } + if (pbi->init_flag == 0) { + if (pbi->stat & STAT_TIMER_ARM) { + timer->expires = jiffies + PUT_INTERVAL; + add_timer(&pbi->timer); + } + return; + } + if (pbi->m_ins_flag == 0) { + if (vf_get_receiver(pbi->provider_name)) { + state = + vf_notify_receiver(pbi->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 (empty_flag == 0) { + /* decoder has input */ + if ((debug & VP9_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(&pbi->display_q) && + buf_level > 0x200) + ) { + WRITE_VREG + (HEVC_ASSIST_MBOX1_IRQ_REG, + 0x1); + } + } + + if ((debug & VP9_DEBUG_DIS_SYS_ERROR_PROC) == 0) { + /* receiver has no buffer to recycle */ + /*if ((state == RECEIVER_INACTIVE) && + (kfifo_is_empty(&pbi->display_q))) { + pr_info("vp9 something error,need reset\n"); + }*/ + } + } + } + + 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 & VP9_DEBUG_DUMP_PIC_LIST) { + dump_pic_list(pbi); + debug &= ~VP9_DEBUG_DUMP_PIC_LIST; + } + if (debug & VP9_DEBUG_TRIG_SLICE_SEGMENT_PROC) { + WRITE_VREG(HEVC_ASSIST_MBOX1_IRQ_REG, 0x1); + debug &= ~VP9_DEBUG_TRIG_SLICE_SEGMENT_PROC; + } + /*if (debug & VP9_DEBUG_HW_RESET) { + }*/ + if (debug & VP9_DEBUG_ERROR_TRIG) { + WRITE_VREG(DECODE_STOP_POS, 1); + debug &= ~VP9_DEBUG_ERROR_TRIG; + } + + 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 (pop_shorts != 0) { + int i; + u32 sum = 0; + pr_info("pop stream 0x%x shorts\r\n", pop_shorts); + for (i = 0; i < pop_shorts; i++) { + u32 data = + (READ_HREG(HEVC_SHIFTED_DATA) >> 16); + WRITE_HREG(HEVC_SHIFT_COMMAND, + (1<<7)|16); + if ((i & 0xf) == 0) + pr_info("%04x:", i); + pr_info("%04x ", data); + if (((i + 1) & 0xf) == 0) + pr_info("\r\n"); + sum += data; + } + pr_info("\r\nsum = %x\r\n", sum); + pop_shorts = 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 (pbi->get_frame_dur && pbi->show_frame_num > 60 && + pbi->frame_dur > 0 && pbi->saved_resolution != + frame_width * frame_height * + (96000 / pbi->frame_dur)) { + int fps = 96000 / pbi->frame_dur; + if (hevc_source_changed(VFORMAT_VP9, + frame_width, frame_height, fps) > 0) + pbi->saved_resolution = frame_width * + frame_height * fps; + } + + timer->expires = jiffies + PUT_INTERVAL; + add_timer(timer); +} + + +int vvp9_dec_status(struct vdec_s *vdec, struct vdec_status *vstatus) +{ + struct VP9Decoder_s *pbi = &gHevc; + vstatus->width = frame_width; + vstatus->height = frame_height; + if (pbi->frame_dur != 0) + vstatus->fps = 96000 / pbi->frame_dur; + else + vstatus->fps = -1; + vstatus->error_count = 0; + vstatus->status = pbi->stat | pbi->fatal_error; + return 0; +} + +#if 0 +static void VP9_DECODE_INIT(void) +{ + /* enable vp9 clocks */ + WRITE_VREG(DOS_GCLK_EN3, 0xffffffff); + /* *************************************************************** */ + /* Power ON HEVC */ + /* *************************************************************** */ + /* Powerup HEVC */ + WRITE_VREG(AO_RTI_GEN_PWR_SLEEP0, + READ_VREG(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 vvp9_prot_init(struct VP9Decoder_s *pbi) +{ + unsigned int data32; + /* VP9_DECODE_INIT(); */ + vp9_config_work_space_hw(pbi); + init_pic_list_hw(pbi); + + vp9_init_decoder_hw(pbi); + +#ifdef VP9_LPF_LVL_UPDATE + vp9_loop_filter_init(); +#endif + +#if 1 + if (debug & VP9_DEBUG_BUFMGR) + pr_info("[test.c] Enable BitStream Fetch\n"); + data32 = READ_VREG(HEVC_STREAM_CONTROL); + data32 = data32 | + (1 << 0)/*stream_fetch_enable*/ + ; + WRITE_VREG(HEVC_STREAM_CONTROL, data32); +#if 0 + data32 = READ_VREG(HEVC_SHIFT_STARTCODE); + if (data32 != 0x00000100) { + pr_info("vp9 prot init error %d\n", __LINE__); + return; + } + data32 = READ_VREG(HEVC_SHIFT_EMULATECODE); + if (data32 != 0x00000300) { + pr_info("vp9 prot init error %d\n", __LINE__); + return; + } + WRITE_VREG(HEVC_SHIFT_STARTCODE, 0x12345678); + WRITE_VREG(HEVC_SHIFT_EMULATECODE, 0x9abcdef0); + data32 = READ_VREG(HEVC_SHIFT_STARTCODE); + if (data32 != 0x12345678) { + pr_info("vp9 prot init error %d\n", __LINE__); + return; + } + data32 = READ_VREG(HEVC_SHIFT_EMULATECODE); + if (data32 != 0x9abcdef0) { + pr_info("vp9 prot init error %d\n", __LINE__); + return; + } +#endif + WRITE_VREG(HEVC_SHIFT_STARTCODE, 0x000000001); + WRITE_VREG(HEVC_SHIFT_EMULATECODE, 0x00000300); +#endif + + + + WRITE_VREG(HEVC_WAIT_FLAG, 1); + + /* WRITE_VREG(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 & VP9_DEBUG_UCODE) + WRITE_VREG(DEBUG_REG1, 0x1); + else + WRITE_VREG(DEBUG_REG1, 0x0); + /*check vps/sps/pps/i-slice in ucode*/ + WRITE_VREG(NAL_SEARCH_CTL, 0x8); + + WRITE_VREG(DECODE_STOP_POS, decode_stop_pos); + +} + +static int vvp9_local_init(struct VP9Decoder_s *pbi) +{ + int i; + int ret; + int width, height; +#ifdef DEBUG_PTS + pbi->pts_missed = 0; + pbi->pts_hit = 0; +#endif + pbi->new_frame_displayed = 0; + pbi->last_put_idx = -1; + pbi->saved_resolution = 0; + pbi->get_frame_dur = false; + on_no_keyframe_skiped = 0; + width = pbi->vvp9_amstream_dec_info.width; + height = pbi->vvp9_amstream_dec_info.height; + pbi->frame_dur = + (pbi->vvp9_amstream_dec_info.rate == + 0) ? 3600 : pbi->vvp9_amstream_dec_info.rate; + if (width && height) + pbi->frame_ar = height * 0x100 / width; +/* +TODO:FOR VERSION +*/ + pr_info("vp9: ver (%d,%d) decinfo: %dx%d rate=%d\n", vp9_version, + 0, width, height, pbi->frame_dur); + + if (pbi->frame_dur == 0) + pbi->frame_dur = 96000 / 24; + + INIT_KFIFO(pbi->display_q); + INIT_KFIFO(pbi->newframe_q); + + + for (i = 0; i < VF_POOL_SIZE; i++) { + const struct vframe_s *vf = &pbi->vfpool[i]; + pbi->vfpool[i].index = -1; + kfifo_put(&pbi->newframe_q, vf); + } + + + ret = vp9_local_init(pbi); + + return ret; +} + +static s32 vvp9_init(struct VP9Decoder_s *pbi) +{ + int size = -1; + char *buf = vmalloc(0x1000 * 16); + if (IS_ERR_OR_NULL(buf)) + return -ENOMEM; + + init_timer(&pbi->timer); + + pbi->stat |= STAT_TIMER_INIT; + if (vvp9_local_init(pbi) < 0) { + vfree(buf); + return -EBUSY; + } + +#ifdef MULTI_INSTANCE_SUPPORT + if (pbi->m_ins_flag) { + pbi->timer.data = (ulong) pbi; + pbi->timer.function = vvp9_put_timer_func; + pbi->timer.expires = jiffies + PUT_INTERVAL; + + add_timer(&pbi->timer); + + pbi->stat |= STAT_TIMER_ARM; + + INIT_WORK(&pbi->work, vp9_work); + + vfree(buf); + return 0; + } +#endif + + amhevc_enable(); + + size = get_firmware_data(VIDEO_DEC_VP9_MMU, buf); + if (size < 0) { + pr_err("get firmware fail.\n"); + vfree(buf); + return -1; + } + + if (amhevc_loadmc_ex(VFORMAT_VP9, NULL, buf) < 0) { + amhevc_disable(); + vfree(buf); + return -EBUSY; + } + + vfree(buf); + + pbi->stat |= STAT_MC_LOAD; + + /* enable AMRISC side protocol */ + vvp9_prot_init(pbi); + + if (vdec_request_threaded_irq(VDEC_IRQ_1, + vvp9_isr, + vvp9_isr_thread_fn, + IRQF_ONESHOT,/*run thread on this irq disabled*/ + "vvp9-irq", (void *)pbi)) { + pr_info("vvp9 irq register error.\n"); + amhevc_disable(); + return -ENOENT; + } + + pbi->stat |= STAT_ISR_REG; + + pbi->provider_name = PROVIDER_NAME; + vf_provider_init(&vvp9_vf_prov, PROVIDER_NAME, &vvp9_vf_provider, + pbi); + vf_reg_provider(&vvp9_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)pbi->frame_dur)); + + pbi->stat |= STAT_VF_HOOK; + + pbi->timer.data = (ulong)pbi; + pbi->timer.function = vvp9_put_timer_func; + pbi->timer.expires = jiffies + PUT_INTERVAL; + + + add_timer(&pbi->timer); + + pbi->stat |= STAT_TIMER_ARM; + + /* pbi->stat |= STAT_KTHREAD; */ + + amhevc_start(); + + pbi->stat |= STAT_VDEC_RUN; + + pbi->init_flag = 1; + pbi->process_busy = 0; + pr_info("%d, vvp9_init, RP=0x%x\n", + __LINE__, READ_VREG(HEVC_STREAM_RD_PTR)); + return 0; +} + +static int vvp9_stop(struct VP9Decoder_s *pbi) +{ + + pbi->init_flag = 0; + /* + if ((debug & VP9_DEBUG_NOWAIT_DECODE_DONE_WHEN_STOP) == 0) { + int wait_timeout_count = 0; + while ((READ_VREG(HEVC_DEC_STATUS_REG) == + VP9_10B_DECODE_SLICE && + wait_timeout_count < 10) || + pbi->process_busy){ + wait_timeout_count++; + msleep(20); + } + } + */ + if (pbi->stat & STAT_VDEC_RUN) { + amhevc_stop(); + pbi->stat &= ~STAT_VDEC_RUN; + } + + if (pbi->stat & STAT_ISR_REG) { + WRITE_VREG(HEVC_ASSIST_MBOX1_MASK, 0); + vdec_free_irq(VDEC_IRQ_1, (void *)pbi); + pbi->stat &= ~STAT_ISR_REG; + } + + if (pbi->stat & STAT_TIMER_ARM) { + del_timer_sync(&pbi->timer); + pbi->stat &= ~STAT_TIMER_ARM; + } + + if (pbi->stat & STAT_VF_HOOK) { + vf_notify_receiver(pbi->provider_name, + VFRAME_EVENT_PROVIDER_FR_END_HINT, NULL); + + vf_unreg_provider(&vvp9_vf_prov); + pbi->stat &= ~STAT_VF_HOOK; + } + vp9_local_uninit(pbi); + +#ifdef MULTI_INSTANCE_SUPPORT + if (pbi->m_ins_flag) { + cancel_work_sync(&pbi->work); + } else { + amhevc_disable(); + } +#else + amhevc_disable(); +#endif + uninit_mmu_buffers(pbi); + + return 0; +} + +static int amvdec_vp9_mmu_init(struct VP9Decoder_s *pbi) +{ +#ifdef VP9_10B_MMU + pbi->mmu_box = decoder_mmu_box_alloc_box(DRIVER_NAME, + 0, FRAME_BUFFERS, + 48 * SZ_1M + ); + if (!pbi->mmu_box) { + pr_err("vp9 alloc mmu box failed!!\n"); + return -1; + } +#endif + pbi->bmmu_box = decoder_bmmu_box_alloc_box( + DRIVER_NAME, + pbi->index, + MAX_BMMU_BUFFER_NUM, + 4 + PAGE_SHIFT, + CODEC_MM_FLAGS_CMA_CLEAR | + CODEC_MM_FLAGS_FOR_VDECODER); + if (!pbi->bmmu_box) { + pr_err("vp9 alloc bmmu box failed!!\n"); + return -1; + } + return 0; +} +static int amvdec_vp9_probe(struct platform_device *pdev) +{ + struct vdec_s *pdata = *(struct vdec_s **)pdev->dev.platform_data; + struct BUF_s BUF[MAX_BUF_NUM]; + struct VP9Decoder_s *pbi = &gHevc; + pr_info("%s\n", __func__); + mutex_lock(&vvp9_mutex); + + memcpy(&BUF[0], &pbi->m_BUF[0], sizeof(struct BUF_s) * MAX_BUF_NUM); + memset(pbi, 0, sizeof(VP9Decoder)); + memcpy(&pbi->m_BUF[0], &BUF[0], sizeof(struct BUF_s) * MAX_BUF_NUM); + + pbi->init_flag = 0; + pbi->fatal_error = 0; + pbi->show_frame_num = 0; + if (pdata == NULL) { + pr_info("\namvdec_vp9 memory resource undefined.\n"); + mutex_unlock(&vvp9_mutex); + return -EFAULT; + } + pbi->m_ins_flag = 0; +#ifdef MULTI_INSTANCE_SUPPORT + pbi->buf_start = pdata->mem_start; + pbi->buf_size = pdata->mem_end - pdata->mem_start + 1; +#else + pbi->mc_buf_spec.buf_end = pdata->mem_end + 1; + for (i = 0; i < WORK_BUF_SPEC_NUM; i++) + amvvp9_workbuff_spec[i].start_adr = pdata->mem_start; +#endif + if (amvdec_vp9_mmu_init(pbi) < 0) { + pr_err("vp9 alloc bmmu box failed!!\n"); + return -1; + } + if (debug) { + pr_info("===VP9 decoder mem resource 0x%lx -- 0x%lx\n", + pdata->mem_start, pdata->mem_end + 1); + } + + if (pdata->sys_info) + pbi->vvp9_amstream_dec_info = *pdata->sys_info; + else { + pbi->vvp9_amstream_dec_info.width = 0; + pbi->vvp9_amstream_dec_info.height = 0; + pbi->vvp9_amstream_dec_info.rate = 30; + } +#ifdef MULTI_INSTANCE_SUPPORT + pbi->cma_dev = pdata->cma_dev; +#else + cma_dev = pdata->cma_dev; +#endif + pdata->dec_status = vvp9_dec_status; + + if (vvp9_init(pbi) < 0) { + pr_info("\namvdec_vp9 init failed.\n"); + vp9_local_uninit(pbi); + mutex_unlock(&vvp9_mutex); + return -ENODEV; + } + /*set the max clk for smooth playing...*/ + hevc_source_changed(VFORMAT_VP9, + 4096, 2048, 60); + mutex_unlock(&vvp9_mutex); + + return 0; +} + +static int amvdec_vp9_remove(struct platform_device *pdev) +{ + struct VP9Decoder_s *pbi = &gHevc; + if (debug) + pr_info("amvdec_vp9_remove\n"); + + mutex_lock(&vvp9_mutex); + + vvp9_stop(pbi); + + + hevc_source_changed(VFORMAT_VP9, 0, 0, 0); + + +#ifdef DEBUG_PTS + pr_info("pts missed %ld, pts hit %ld, duration %d\n", + pbi->pts_missed, pbi->pts_hit, pbi->frame_dur); +#endif + + mutex_unlock(&vvp9_mutex); + + return 0; +} + +/****************************************/ + +static struct platform_driver amvdec_vp9_driver = { + .probe = amvdec_vp9_probe, + .remove = amvdec_vp9_remove, +#ifdef CONFIG_PM + .suspend = amhevc_suspend, + .resume = amhevc_resume, +#endif + .driver = { + .name = DRIVER_NAME, + } +}; + +static struct codec_profile_t amvdec_vp9_profile = { + .name = "vp9", + .profile = "" +}; + +#ifdef MULTI_INSTANCE_SUPPORT +static unsigned int start_decode_buf_level = 0x8000; +#ifdef VP9_10B_MMU +static u32 work_buf_size = 24 * 1024 * 1024; +#else +static u32 work_buf_size = 32 * 1024 * 1024; +#endif + +static unsigned char decoder_id_used[MAX_DECODE_INSTANCE_NUM]; +static unsigned int get_free_decoder_id(struct vdec_s *vdec) +{ + /*stream base decoder always has id of 0*/ + int i; + if (vdec_frame_based(vdec)) { + for (i = 1; i < decoder_id_used[i]; i++) { + if (!decoder_id_used[i]) { + decoder_id_used[i] = 1; + return i; + } + } + } + return 0; +} + +static unsigned char get_data_check_sum + (struct VP9Decoder_s *pbi, int size) +{ + int jj; + int sum = 0; + u8 *data = ((u8 *)pbi->chunk->block->start_virt) + + pbi->chunk->offset; + for (jj = 0; jj < size; jj++) + sum += data[jj]; + return sum; +} + +static void dump_data(struct VP9Decoder_s *pbi, int size) +{ + int jj; + u8 *data = ((u8 *)pbi->chunk->block->start_virt) + + pbi->chunk->offset; + for (jj = 0; jj < size; jj++) { + if ((jj & 0xf) == 0) + vp9_print(pbi, + 0, + "%06x:", jj); + vp9_print_cont(pbi, + 0, + "%02x ", data[jj]); + if (((jj + 1) & 0xf) == 0) + vp9_print(pbi, + 0, + "\n"); + } + vp9_print(pbi, + 0, + "\n"); +} + +static void vp9_work(struct work_struct *work) +{ + struct VP9Decoder_s *pbi = container_of(work, + struct VP9Decoder_s, work); + struct VP9_Common_s *const cm = &pbi->common; + struct vdec_s *vdec = hw_to_vdec(pbi); + /* finished decoding one frame or error, + * notify vdec core to switch context + */ + + if ((pbi->dec_result == DEC_RESULT_GET_DATA) || + (pbi->dec_result == DEC_RESULT_GET_DATA_RETRY)) { + if (pbi->dec_result == DEC_RESULT_GET_DATA) { + vp9_print(pbi, PRINT_FLAG_VDEC_STATUS, + "%s DEC_RESULT_GET_DATA %x %x %x\n", + __func__, + READ_VREG(HEVC_STREAM_LEVEL), + READ_VREG(HEVC_STREAM_WR_PTR), + READ_VREG(HEVC_STREAM_RD_PTR)); + vdec_vframe_dirty(vdec, pbi->chunk); + vdec_clean_input(vdec); + } + + if (!is_buffer_empty(cm)) { + int r; + r = vdec_prepare_input(vdec, &pbi->chunk); + if (r < 0) { + pbi->dec_result = DEC_RESULT_GET_DATA_RETRY; + + vp9_print(pbi, + PRINT_FLAG_VDEC_DETAIL, + "amvdec_vh265: Insufficient data\n"); + + schedule_work(&pbi->work); + return; + } + pbi->dec_result = DEC_RESULT_NONE; + vp9_print(pbi, PRINT_FLAG_VDEC_STATUS, + "%s: chunk size 0x%x sum 0x%x\n", + __func__, r, + (debug & PRINT_FLAG_VDEC_STATUS) ? + get_data_check_sum(pbi, r) : 0 + ); + + if (debug & PRINT_FLAG_VDEC_DATA) + dump_data(pbi, pbi->chunk->size); + WRITE_VREG(HEVC_DECODE_SIZE, r); + + vdec_enable_input(vdec); + + WRITE_VREG(HEVC_DEC_STATUS_REG, HEVC_ACTION_DONE); + } else{ + pbi->dec_result = DEC_RESULT_GET_DATA_RETRY; + + vp9_print(pbi, PRINT_FLAG_VDEC_DETAIL, + "amvdec_vh265: Insufficient data\n"); + + schedule_work(&pbi->work); + } + return; + } else if (pbi->dec_result == DEC_RESULT_DONE) { + /* if (!pbi->ctx_valid) + pbi->ctx_valid = 1; */ + vp9_print(pbi, PRINT_FLAG_VDEC_STATUS, + "%s dec_result %d %x %x %x\n", + __func__, + pbi->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(pbi), pbi->chunk); + } else { + vp9_print(pbi, PRINT_FLAG_VDEC_DETAIL, + "%s dec_result %d %x %x %x\n", + __func__, + pbi->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(pbi), VDEC_STATUS_CONNECTED); + + if (pbi->vdec_cb) + pbi->vdec_cb(hw_to_vdec(pbi), pbi->vdec_cb_arg); +} + +static int vp9_hw_ctx_restore(struct VP9Decoder_s *pbi) +{ + /* new to do ... */ + vvp9_prot_init(pbi); + return 0; +} + +static bool run_ready(struct vdec_s *vdec) +{ + struct VP9Decoder_s *pbi = + (struct VP9Decoder_s *)vdec->private; + struct VP9_Common_s *const cm = &pbi->common; + + vp9_print(pbi, + PRINT_FLAG_VDEC_DETAIL, "%s\r\n", __func__); + + if ((!vdec_frame_based(vdec)) && (start_decode_buf_level > 0)) { + u32 rp, wp; + u32 level; + + rp = READ_MPEG_REG(PARSER_VIDEO_RP); + wp = READ_MPEG_REG(PARSER_VIDEO_WP); + + if (wp < rp) + level = vdec->input.size + wp - rp; + else + level = wp - rp; + + if (level < start_decode_buf_level) { + vp9_print(pbi, 0, + "level %d not run_ready\n", level); + return false; + } + } else if (vdec_frame_based(vdec)) { + if (!vdec_input_next_input_chunk(&vdec->input)) + return false; + } + + return !is_buffer_empty(cm); +} + +static void reset_dec_hw(struct vdec_s *vdec) +{ + if (input_frame_based(vdec)) + WRITE_VREG(HEVC_STREAM_CONTROL, 0); + + /* + * 2: assist + * 3: parser + * 4: parser_state + * 8: dblk + * 11:mcpu + * 12:ccpu + * 13:ddr + * 14:iqit + * 15:ipp + * 17:qdct + * 18:mpred + * 19:sao + * 24:hevc_afifo + */ + 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); +} + +static void run(struct vdec_s *vdec, + void (*callback)(struct vdec_s *, void *), void *arg) +{ + struct VP9Decoder_s *pbi = + (struct VP9Decoder_s *)vdec->private; + int r, size = -1; + + char *buf = vmalloc(0x1000 * 16); + if (IS_ERR_OR_NULL(buf)) + return; + + pbi->vdec_cb_arg = arg; + pbi->vdec_cb = callback; + /* pbi->chunk = vdec_prepare_input(vdec); */ + reset_dec_hw(vdec); + + r = vdec_prepare_input(vdec, &pbi->chunk); + if (r < 0) { + pbi->dec_result = DEC_RESULT_AGAIN; + + vp9_print(pbi, PRINT_FLAG_VDEC_DETAIL, + "ammvdec_vh265: Insufficient data\n"); + + schedule_work(&pbi->work); + return; + } + pbi->dec_result = DEC_RESULT_NONE; + + vp9_print(pbi, PRINT_FLAG_VDEC_STATUS, + "%s: size 0x%x sum 0x%x (%x %x %x)\n", + __func__, r, + (vdec_frame_based(vdec) && + (debug & PRINT_FLAG_VDEC_STATUS)) ? + get_data_check_sum(pbi, r) : 0, + READ_VREG(HEVC_STREAM_LEVEL), + READ_VREG(HEVC_STREAM_WR_PTR), + READ_VREG(HEVC_STREAM_RD_PTR)); + + size = get_firmware_data(VIDEO_DEC_VP9_MMU, buf); + if (size < 0) { + pr_err("get firmware fail.\n"); + vfree(buf); + return; + } + + if (amhevc_loadmc_ex(VFORMAT_VP9, NULL, buf) < 0) { + amhevc_disable(); + vfree(buf); + return; + } + + vfree(buf); + + if (vp9_hw_ctx_restore(pbi) < 0) { + schedule_work(&pbi->work); + return; + } + + vdec_enable_input(vdec); + + WRITE_VREG(HEVC_DEC_STATUS_REG, HEVC_ACTION_DONE); + + if (vdec_frame_based(vdec)) { + if (debug & PRINT_FLAG_VDEC_DATA) + dump_data(pbi, pbi->chunk->size); + + WRITE_VREG(HEVC_SHIFT_BYTE_COUNT, 0); + } + WRITE_VREG(HEVC_DECODE_SIZE, r); + WRITE_VREG(HEVC_DECODE_COUNT, pbi->slice_idx); + pbi->init_flag = 1; + + vp9_print(pbi, PRINT_FLAG_VDEC_STATUS, + "%s: start hevc (%x %x %x)\n", + __func__, + READ_VREG(HEVC_DEC_STATUS_REG), + READ_VREG(HEVC_MPC_E), + READ_VREG(HEVC_MPSR)); + + amhevc_start(); + +} + +static void reset(struct vdec_s *vdec) +{ + + struct VP9Decoder_s *pbi = + (struct VP9Decoder_s *)vdec->private; + + vp9_print(pbi, + PRINT_FLAG_VDEC_DETAIL, "%s\r\n", __func__); + +} + +static irqreturn_t vp9_irq_cb(struct vdec_s *vdec) +{ + struct VP9Decoder_s *pbi = + (struct VP9Decoder_s *)vdec->private; + return vvp9_isr(0, pbi); +} + +static irqreturn_t vp9_threaded_irq_cb(struct vdec_s *vdec) +{ + struct VP9Decoder_s *pbi = + (struct VP9Decoder_s *)vdec->private; + return vvp9_isr_thread_fn(0, pbi); +} + + +static int ammvdec_vp9_probe(struct platform_device *pdev) +{ + struct vdec_s *pdata = *(struct vdec_s **)pdev->dev.platform_data; + + struct BUF_s BUF[MAX_BUF_NUM]; + struct VP9Decoder_s *pbi = NULL; + pr_info("%s\n", __func__); + if (pdata == NULL) { + pr_info("\nammvdec_vp9 memory resource undefined.\n"); + return -EFAULT; + } + pbi = (struct VP9Decoder_s *)devm_kzalloc(&pdev->dev, + sizeof(struct VP9Decoder_s), GFP_KERNEL); + if (pbi == NULL) { + pr_info("\nammvdec_vp9 device data allocation failed\n"); + return -ENOMEM; + } + pdata->private = pbi; + pdata->dec_status = vvp9_dec_status; + /* pdata->set_trickmode = set_trickmode; */ + pdata->run_ready = run_ready; + pdata->run = run; + pdata->reset = reset; + pdata->irq_handler = vp9_irq_cb; + pdata->threaded_irq_handler = vp9_threaded_irq_cb; + + pdata->id = pdev->id; + + + memcpy(&BUF[0], &pbi->m_BUF[0], sizeof(struct BUF_s) * MAX_BUF_NUM); + memset(pbi, 0, sizeof(VP9Decoder)); + memcpy(&pbi->m_BUF[0], &BUF[0], sizeof(struct BUF_s) * MAX_BUF_NUM); + + pbi->index = get_free_decoder_id(pdata); + + if (pdata->use_vfm_path) + snprintf(pdata->vf_provider_name, VDEC_PROVIDER_NAME_SIZE, + VFM_DEC_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, + &vvp9_vf_provider, pbi); + + pbi->provider_name = pdata->vf_provider_name; + platform_set_drvdata(pdev, pdata); + + pbi->platform_dev = pdev; +#if 0 + pbi->buf_start = pdata->mem_start; + pbi->buf_size = pdata->mem_end - pdata->mem_start + 1; +#else + if (amvdec_vp9_mmu_init(pbi) < 0) { + pr_err("vp9 alloc bmmu box failed!!\n"); + devm_kfree(&pdev->dev, (void *)pbi); + return -1; + } + + pbi->cma_alloc_count = PAGE_ALIGN(work_buf_size) / PAGE_SIZE; + if (!decoder_bmmu_box_alloc_idx_wait( + pbi->bmmu_box, + WORK_SPACE_BUF_ID, + pbi->cma_alloc_count * PAGE_SIZE, + -1, + -1, + BMMU_ALLOC_FLAGS_WAITCLEAR + )) { + pbi->cma_alloc_addr = decoder_bmmu_box_get_phy_addr( + pbi->bmmu_box, + WORK_SPACE_BUF_ID); + } else { + vp9_print(pbi, 0, + "codec_mm alloc failed, request buf size 0x%lx\n", + pbi->cma_alloc_count * PAGE_SIZE); + pbi->cma_alloc_count = 0; + uninit_mmu_buffers(pbi); + devm_kfree(&pdev->dev, (void *)pbi); + return -ENOMEM; + } + pbi->buf_start = pbi->cma_alloc_addr; + pbi->buf_size = work_buf_size; +#endif + pbi->m_ins_flag = 1; + + pbi->init_flag = 0; + pbi->fatal_error = 0; + pbi->show_frame_num = 0; + if (pdata == NULL) { + pr_info("\namvdec_vp9 memory resource undefined.\n"); + uninit_mmu_buffers(pbi); + devm_kfree(&pdev->dev, (void *)pbi); + return -EFAULT; + } + + if (debug) { + pr_info("===VP9 decoder mem resource 0x%lx -- 0x%lx\n", + pbi->buf_start, + pbi->buf_start + pbi->buf_size); + } + + if (pdata->sys_info) + pbi->vvp9_amstream_dec_info = *pdata->sys_info; + else { + pbi->vvp9_amstream_dec_info.width = 0; + pbi->vvp9_amstream_dec_info.height = 0; + pbi->vvp9_amstream_dec_info.rate = 30; + } + + pbi->cma_dev = pdata->cma_dev; + + if (vvp9_init(pbi) < 0) { + pr_info("\namvdec_vp9 init failed.\n"); + vp9_local_uninit(pbi); + uninit_mmu_buffers(pbi); + devm_kfree(&pdev->dev, (void *)pbi); + return -ENODEV; + } + return 0; +} + +static int ammvdec_vp9_remove(struct platform_device *pdev) +{ + struct VP9Decoder_s *pbi = (struct VP9Decoder_s *) + (((struct vdec_s *)(platform_get_drvdata(pdev)))->private); + if (debug) + pr_info("amvdec_vp9_remove\n"); + + vvp9_stop(pbi); + + vdec_set_status(hw_to_vdec(pbi), VDEC_STATUS_DISCONNECTED); + + +#ifdef DEBUG_PTS + pr_info("pts missed %ld, pts hit %ld, duration %d\n", + pbi->pts_missed, pbi->pts_hit, pbi->frame_dur); +#endif + devm_kfree(&pdev->dev, (void *)pbi); + return 0; +} + +static struct platform_driver ammvdec_vp9_driver = { + .probe = ammvdec_vp9_probe, + .remove = ammvdec_vp9_remove, +#ifdef CONFIG_PM + .suspend = amvdec_suspend, + .resume = amvdec_resume, +#endif + .driver = { + .name = MULTI_DRIVER_NAME, + } +}; +#endif + +static int __init amvdec_vp9_driver_init_module(void) +{ + pr_debug("amvdec_vp9 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_vp9_driver)) + pr_err("failed to register ammvdec_vp9 driver\n"); + +#endif + if (platform_driver_register(&amvdec_vp9_driver)) { + pr_err("failed to register amvdec_vp9 driver\n"); + return -ENODEV; + } + + if (get_cpu_type() >= MESON_CPU_MAJOR_ID_GXL) { + amvdec_vp9_profile.profile = + "4k, 10bit, dwrite, compressed"; + } else { + amvdec_vp9_profile.name = "vp9_unsupport"; + } + + vcodec_profile_register(&amvdec_vp9_profile); + + return 0; +} + +static void __exit amvdec_vp9_driver_remove_module(void) +{ + pr_debug("amvdec_vp9 module remove.\n"); +#ifdef MULTI_INSTANCE_SUPPORT + platform_driver_unregister(&ammvdec_vp9_driver); +#endif + platform_driver_unregister(&amvdec_vp9_driver); +} + +/****************************************/ +/* +module_param(stat, uint, 0664); +MODULE_PARM_DESC(stat, "\n amvdec_vp9 stat\n"); +*/ +module_param(use_cma, uint, 0664); +MODULE_PARM_DESC(use_cma, "\n amvdec_vp9 use_cma\n"); + +module_param(bit_depth_luma, uint, 0664); +MODULE_PARM_DESC(bit_depth_luma, "\n amvdec_vp9 bit_depth_luma\n"); + +module_param(bit_depth_chroma, uint, 0664); +MODULE_PARM_DESC(bit_depth_chroma, "\n amvdec_vp9 bit_depth_chroma\n"); + +module_param(frame_width, uint, 0664); +MODULE_PARM_DESC(frame_width, "\n amvdec_vp9 frame_width\n"); + +module_param(frame_height, uint, 0664); +MODULE_PARM_DESC(frame_height, "\n amvdec_vp9 frame_height\n"); + +module_param(debug, uint, 0664); +MODULE_PARM_DESC(debug, "\n amvdec_vp9 debug\n"); + +module_param(radr, uint, 0664); +MODULE_PARM_DESC(radr, "\nradr\n"); + +module_param(rval, uint, 0664); +MODULE_PARM_DESC(rval, "\nrval\n"); + +module_param(pop_shorts, uint, 0664); +MODULE_PARM_DESC(pop_shorts, "\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_vp9 step\n"); + +module_param(decode_stop_pos, uint, 0664); +MODULE_PARM_DESC(decode_stop_pos, "\n amvdec_vp9 decode_stop_pos\n"); + +module_param(decode_pic_begin, uint, 0664); +MODULE_PARM_DESC(decode_pic_begin, "\n amvdec_vp9 decode_pic_begin\n"); + +module_param(slice_parse_begin, uint, 0664); +MODULE_PARM_DESC(slice_parse_begin, "\n amvdec_vp9 slice_parse_begin\n"); + +module_param(i_only_flag, uint, 0664); +MODULE_PARM_DESC(i_only_flag, "\n amvdec_vp9 i_only_flag\n"); + +module_param(error_handle_policy, uint, 0664); +MODULE_PARM_DESC(error_handle_policy, "\n amvdec_vp9 error_handle_policy\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_depth, uint, 0664); +MODULE_PARM_DESC(buf_alloc_depth, "\n buf_alloc_depth\n"); + +module_param(buf_alloc_size, uint, 0664); +MODULE_PARM_DESC(buf_alloc_size, "\n buf_alloc_size\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(on_no_keyframe_skiped, uint, 0664); +MODULE_PARM_DESC(on_no_keyframe_skiped, "\n on_no_keyframe_skiped\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"); +#endif + +module_init(amvdec_vp9_driver_init_module); +module_exit(amvdec_vp9_driver_remove_module); + +MODULE_DESCRIPTION("AMLOGIC vp9 Video Decoder Driver"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Tim Yao <tim.yao@amlogic.com>"); diff --git a/drivers/frame_provider/decoder/vp9/vvp9.h b/drivers/frame_provider/decoder/vp9/vvp9.h new file mode 100644 index 0000000..4cf3254 --- a/dev/null +++ b/drivers/frame_provider/decoder/vp9/vvp9.h @@ -0,0 +1,25 @@ +/* + * drivers/amlogic/amports/vvp9.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 VVP9_H +#define VVP9_H +#ifndef CONFIG_MULTI_DEC +#define VP9_10B_MMU +#endif +void adapt_coef_probs(int pic_count, int prev_kf, int cur_kf, int pre_fc, +unsigned int *prev_prob, unsigned int *cur_prob, unsigned int *count); +#endif diff --git a/drivers/frame_sink/Makefile b/drivers/frame_sink/Makefile new file mode 100644 index 0000000..2b9754a --- a/dev/null +++ b/drivers/frame_sink/Makefile @@ -0,0 +1 @@ +obj-y += encoder/ diff --git a/drivers/frame_sink/encoder/Makefile b/drivers/frame_sink/encoder/Makefile new file mode 100644 index 0000000..9afecec --- a/dev/null +++ b/drivers/frame_sink/encoder/Makefile @@ -0,0 +1,2 @@ +obj-$(CONFIG_AMLOGIC_MEDIA_VENC_H264) += h264/ +obj-$(CONFIG_AMLOGIC_MEDIA_VENC_H265) += h265/ diff --git a/drivers/frame_sink/encoder/h264/Makefile b/drivers/frame_sink/encoder/h264/Makefile new file mode 100644 index 0000000..c12d7c3 --- a/dev/null +++ b/drivers/frame_sink/encoder/h264/Makefile @@ -0,0 +1 @@ +obj-m += encoder.o diff --git a/drivers/frame_sink/encoder/h264/encoder.c b/drivers/frame_sink/encoder/h264/encoder.c new file mode 100644 index 0000000..9badd53 --- a/dev/null +++ b/drivers/frame_sink/encoder/h264/encoder.c @@ -0,0 +1,4237 @@ +/* + * drivers/amlogic/amports/encoder.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/types.h> +#include <linux/errno.h> +#include <linux/interrupt.h> +#include <linux/timer.h> +#include <linux/fs.h> +#include <linux/sched.h> +#include <linux/slab.h> +#include <linux/dma-mapping.h> +#include <linux/platform_device.h> +#include <linux/spinlock.h> +#include <linux/ctype.h> +#include <linux/amlogic/media/frame_sync/ptsserv.h> +#include <linux/amlogic/media/utils/amstream.h> +#include <linux/amlogic/media/canvas/canvas.h> +#include <linux/amlogic/media/canvas/canvas_mgr.h> +#include <linux/amlogic/media/codec_mm/codec_mm.h> + +#include <linux/amlogic/media/utils/vdec_reg.h> +#include "../../../frame_provider/decoder/utils/vdec.h" +#include <linux/delay.h> +#include <linux/poll.h> +#include <linux/of.h> +#include <linux/of_fdt.h> +#include <linux/dma-contiguous.h> +#include <linux/kthread.h> +#include <linux/sched/rt.h> +#include <linux/amlogic/media/utils/amports_config.h> +#include "encoder.h" +#include "../../../frame_provider/decoder/utils/amvdec.h" +#include <linux/amlogic/media/utils/amlog.h> +#include "../../../stream_input/amports/amports_priv.h" +#include <linux/of_reserved_mem.h> +#ifdef CONFIG_AM_JPEG_ENCODER +#include "jpegenc.h" +#endif + +#define ENCODE_NAME "encoder" +#define AMVENC_CANVAS_INDEX 0xE4 +#define AMVENC_CANVAS_MAX_INDEX 0xEF + +#define MIN_SIZE amvenc_buffspec[0].min_buffsize +#define DUMP_INFO_BYTES_PER_MB 80 + +#define ADJUSTED_QP_FLAG 64 + +static s32 avc_device_major; +static struct device *amvenc_avc_dev; +#define DRIVER_NAME "amvenc_avc" +#define CLASS_NAME "amvenc_avc" +#define DEVICE_NAME "amvenc_avc" + +static struct encode_manager_s encode_manager; + +#define MULTI_SLICE_MC +#define H264_ENC_CBR +/* #define MORE_MODULE_PARAM */ + +#define ENC_CANVAS_OFFSET AMVENC_CANVAS_INDEX + +#define UCODE_MODE_FULL 0 + +/* #define ENABLE_IGNORE_FUNCTION */ + +static u32 ie_me_mb_type; +static u32 ie_me_mode; +static u32 ie_pippeline_block = 3; +static u32 ie_cur_ref_sel; +/* static u32 avc_endian = 6; */ +static u32 clock_level = 5; + +static u32 encode_print_level = LOG_DEBUG; +static u32 no_timeout; +static int nr_mode = -1; +static u32 qp_table_debug; + +static u32 me_mv_merge_ctl = + (0x1 << 31) | /* [31] me_merge_mv_en_16 */ + (0x1 << 30) | /* [30] me_merge_small_mv_en_16 */ + (0x1 << 29) | /* [29] me_merge_flex_en_16 */ + (0x1 << 28) | /* [28] me_merge_sad_en_16 */ + (0x1 << 27) | /* [27] me_merge_mv_en_8 */ + (0x1 << 26) | /* [26] me_merge_small_mv_en_8 */ + (0x1 << 25) | /* [25] me_merge_flex_en_8 */ + (0x1 << 24) | /* [24] me_merge_sad_en_8 */ + /* [23:18] me_merge_mv_diff_16 - MV diff <= n pixel can be merged */ + (0x12 << 18) | + /* [17:12] me_merge_mv_diff_8 - MV diff <= n pixel can be merged */ + (0x2b << 12) | + /* [11:0] me_merge_min_sad - SAD >= 0x180 can be merged with other MV */ + (0x80 << 0); + /* ( 0x4 << 18) | + // [23:18] me_merge_mv_diff_16 - MV diff <= n pixel can be merged */ + /* ( 0x3f << 12) | + // [17:12] me_merge_mv_diff_8 - MV diff <= n pixel can be merged */ + /* ( 0xc0 << 0); + // [11:0] me_merge_min_sad - SAD >= 0x180 can be merged with other MV */ + +static u32 me_mv_weight_01 = (0x40 << 24) | (0x30 << 16) | (0x20 << 8) | 0x30; +static u32 me_mv_weight_23 = (0x40 << 8) | 0x30; +static u32 me_sad_range_inc = 0x03030303; +static u32 me_step0_close_mv = 0x003ffc21; +static u32 me_f_skip_sad; +static u32 me_f_skip_weight; +static u32 me_sad_enough_01;/* 0x00018010; */ +static u32 me_sad_enough_23;/* 0x00000020; */ + +/* [31:0] NUM_ROWS_PER_SLICE_P */ +/* [15:0] NUM_ROWS_PER_SLICE_I */ +static u32 fixed_slice_cfg; + +/* y tnr */ +static unsigned int y_tnr_mc_en = 1; +static unsigned int y_tnr_txt_mode; +static unsigned int y_tnr_mot_sad_margin = 1; +static unsigned int y_tnr_mot_cortxt_rate = 1; +static unsigned int y_tnr_mot_distxt_ofst = 5; +static unsigned int y_tnr_mot_distxt_rate = 4; +static unsigned int y_tnr_mot_dismot_ofst = 4; +static unsigned int y_tnr_mot_frcsad_lock = 8; +static unsigned int y_tnr_mot2alp_frc_gain = 10; +static unsigned int y_tnr_mot2alp_nrm_gain = 216; +static unsigned int y_tnr_mot2alp_dis_gain = 128; +static unsigned int y_tnr_mot2alp_dis_ofst = 32; +static unsigned int y_tnr_alpha_min = 32; +static unsigned int y_tnr_alpha_max = 63; +static unsigned int y_tnr_deghost_os; +/* c tnr */ +static unsigned int c_tnr_mc_en = 1; +static unsigned int c_tnr_txt_mode; +static unsigned int c_tnr_mot_sad_margin = 1; +static unsigned int c_tnr_mot_cortxt_rate = 1; +static unsigned int c_tnr_mot_distxt_ofst = 5; +static unsigned int c_tnr_mot_distxt_rate = 4; +static unsigned int c_tnr_mot_dismot_ofst = 4; +static unsigned int c_tnr_mot_frcsad_lock = 8; +static unsigned int c_tnr_mot2alp_frc_gain = 10; +static unsigned int c_tnr_mot2alp_nrm_gain = 216; +static unsigned int c_tnr_mot2alp_dis_gain = 128; +static unsigned int c_tnr_mot2alp_dis_ofst = 32; +static unsigned int c_tnr_alpha_min = 32; +static unsigned int c_tnr_alpha_max = 63; +static unsigned int c_tnr_deghost_os; +/* y snr */ +static unsigned int y_snr_err_norm = 1; +static unsigned int y_snr_gau_bld_core = 1; +static int y_snr_gau_bld_ofst = -1; +static unsigned int y_snr_gau_bld_rate = 48; +static unsigned int y_snr_gau_alp0_min; +static unsigned int y_snr_gau_alp0_max = 63; +static unsigned int y_bld_beta2alp_rate = 16; +static unsigned int y_bld_beta_min; +static unsigned int y_bld_beta_max = 63; +/* c snr */ +static unsigned int c_snr_err_norm = 1; +static unsigned int c_snr_gau_bld_core = 1; +static int c_snr_gau_bld_ofst = -1; +static unsigned int c_snr_gau_bld_rate = 48; +static unsigned int c_snr_gau_alp0_min; +static unsigned int c_snr_gau_alp0_max = 63; +static unsigned int c_bld_beta2alp_rate = 16; +static unsigned int c_bld_beta_min; +static unsigned int c_bld_beta_max = 63; + +static DEFINE_SPINLOCK(lock); + +#define ADV_MV_LARGE_16x8 1 +#define ADV_MV_LARGE_8x16 1 +#define ADV_MV_LARGE_16x16 1 + +/* me weight offset should not very small, it used by v1 me module. */ +/* the min real sad for me is 16 by hardware. */ +#define ME_WEIGHT_OFFSET 0x520 +#define I4MB_WEIGHT_OFFSET 0x655 +#define I16MB_WEIGHT_OFFSET 0x560 + +#define ADV_MV_16x16_WEIGHT 0x080 +#define ADV_MV_16_8_WEIGHT 0x0e0 +#define ADV_MV_8x8_WEIGHT 0x240 +#define ADV_MV_4x4x4_WEIGHT 0x3000 + +#define IE_SAD_SHIFT_I16 0x001 +#define IE_SAD_SHIFT_I4 0x001 +#define ME_SAD_SHIFT_INTER 0x001 + +#define STEP_2_SKIP_SAD 0 +#define STEP_1_SKIP_SAD 0 +#define STEP_0_SKIP_SAD 0 +#define STEP_2_SKIP_WEIGHT 0 +#define STEP_1_SKIP_WEIGHT 0 +#define STEP_0_SKIP_WEIGHT 0 + +#define ME_SAD_RANGE_0 0x1 /* 0x0 */ +#define ME_SAD_RANGE_1 0x0 +#define ME_SAD_RANGE_2 0x0 +#define ME_SAD_RANGE_3 0x0 + +/* use 0 for v3, 0x18 for v2 */ +#define ME_MV_PRE_WEIGHT_0 0x18 +/* use 0 for v3, 0x18 for v2 */ +#define ME_MV_PRE_WEIGHT_1 0x18 +#define ME_MV_PRE_WEIGHT_2 0x0 +#define ME_MV_PRE_WEIGHT_3 0x0 + +/* use 0 for v3, 0x18 for v2 */ +#define ME_MV_STEP_WEIGHT_0 0x18 +/* use 0 for v3, 0x18 for v2 */ +#define ME_MV_STEP_WEIGHT_1 0x18 +#define ME_MV_STEP_WEIGHT_2 0x0 +#define ME_MV_STEP_WEIGHT_3 0x0 + +#define ME_SAD_ENOUGH_0_DATA 0x00 +#define ME_SAD_ENOUGH_1_DATA 0x04 +#define ME_SAD_ENOUGH_2_DATA 0x11 +#define ADV_MV_8x8_ENOUGH_DATA 0x20 + +/* V4_COLOR_BLOCK_FIX */ +#define V3_FORCE_SKIP_SAD_0 0x10 +/* 4 Blocks */ +#define V3_FORCE_SKIP_SAD_1 0x60 +/* 16 Blocks + V3_SKIP_WEIGHT_2 */ +#define V3_FORCE_SKIP_SAD_2 0x250 +/* almost disable it -- use t_lac_coeff_2 output to F_ZERO is better */ +#define V3_ME_F_ZERO_SAD (ME_WEIGHT_OFFSET + 0x10) + +#define V3_IE_F_ZERO_SAD_I16 (I16MB_WEIGHT_OFFSET + 0x10) +#define V3_IE_F_ZERO_SAD_I4 (I4MB_WEIGHT_OFFSET + 0x20) + +#define V3_SKIP_WEIGHT_0 0x10 +/* 4 Blocks 8 seperate search sad can be very low */ +#define V3_SKIP_WEIGHT_1 0x8 /* (4 * ME_MV_STEP_WEIGHT_1 + 0x100) */ +#define V3_SKIP_WEIGHT_2 0x3 + +#define V3_LEVEL_1_F_SKIP_MAX_SAD 0x0 +#define V3_LEVEL_1_SKIP_MAX_SAD 0x6 + +#define I4_ipred_weight_most 0x18 +#define I4_ipred_weight_else 0x28 + +#define C_ipred_weight_V 0x04 +#define C_ipred_weight_H 0x08 +#define C_ipred_weight_DC 0x0c + +#define I16_ipred_weight_V 0x04 +#define I16_ipred_weight_H 0x08 +#define I16_ipred_weight_DC 0x0c + +/* 0x00 same as disable */ +#define v3_left_small_max_ie_sad 0x00 +#define v3_left_small_max_me_sad 0x40 + +#define v5_use_small_diff_cnt 0 +#define v5_simple_mb_inter_all_en 1 +#define v5_simple_mb_inter_8x8_en 1 +#define v5_simple_mb_inter_16_8_en 1 +#define v5_simple_mb_inter_16x16_en 1 +#define v5_simple_mb_intra_en 1 +#define v5_simple_mb_C_en 0 +#define v5_simple_mb_Y_en 1 +#define v5_small_diff_Y 0x10 +#define v5_small_diff_C 0x18 +/* shift 8-bits, 2, 1, 0, -1, -2, -3, -4 */ +#define v5_simple_dq_setting 0x43210fed +#define v5_simple_me_weight_setting 0 + +#ifdef H264_ENC_CBR +#define CBR_TABLE_SIZE 0x800 +#define CBR_SHORT_SHIFT 12 /* same as disable */ +#define CBR_LONG_MB_NUM 2 +#define START_TABLE_ID 8 +#define CBR_LONG_THRESH 4 +#endif + +static u32 v3_mv_sad[64] = { + /* For step0 */ + 0x00000004, + 0x00010008, + 0x00020010, + 0x00030018, + 0x00040020, + 0x00050028, + 0x00060038, + 0x00070048, + 0x00080058, + 0x00090068, + 0x000a0080, + 0x000b0098, + 0x000c00b0, + 0x000d00c8, + 0x000e00e8, + 0x000f0110, + /* For step1 */ + 0x00100002, + 0x00110004, + 0x00120008, + 0x0013000c, + 0x00140010, + 0x00150014, + 0x0016001c, + 0x00170024, + 0x0018002c, + 0x00190034, + 0x001a0044, + 0x001b0054, + 0x001c0064, + 0x001d0074, + 0x001e0094, + 0x001f00b4, + /* For step2 */ + 0x00200006, + 0x0021000c, + 0x0022000c, + 0x00230018, + 0x00240018, + 0x00250018, + 0x00260018, + 0x00270030, + 0x00280030, + 0x00290030, + 0x002a0030, + 0x002b0030, + 0x002c0030, + 0x002d0030, + 0x002e0030, + 0x002f0050, + /* For step2 4x4-8x8 */ + 0x00300001, + 0x00310002, + 0x00320002, + 0x00330004, + 0x00340004, + 0x00350004, + 0x00360004, + 0x00370006, + 0x00380006, + 0x00390006, + 0x003a0006, + 0x003b0006, + 0x003c0006, + 0x003d0006, + 0x003e0006, + 0x003f0006 +}; + +static struct BuffInfo_s amvenc_buffspec[] = { + { + .lev_id = 0, + .max_width = 1920, + .max_height = 1088, + .min_buffsize = 0x1400000, + .dct = { + .buf_start = 0, + .buf_size = 0x800000, /* 1920x1088x4 */ + }, + .dec0_y = { + .buf_start = 0x800000, + .buf_size = 0x300000, + }, + .dec1_y = { + .buf_start = 0xb00000, + .buf_size = 0x300000, + }, + .assit = { + .buf_start = 0xe10000, + .buf_size = 0xc0000, + }, + .bitstream = { + .buf_start = 0xf00000, + .buf_size = 0x100000, + }, + .scale_buff = { + .buf_start = 0x1000000, + .buf_size = 0x300000, + }, + .dump_info = { + .buf_start = 0x1300000, + .buf_size = 0xa0000, /* (1920x1088/256)x80 */ + }, + .cbr_info = { + .buf_start = 0x13b0000, + .buf_size = 0x2000, + } + } +}; + +enum ucode_type_e { + UCODE_GX, + UCODE_GXTV, + UCODE_GXL, + UCODE_TXL, + UCODE_MAX +}; + +const char *ucode_name[] = { + "h264_enc_mc_gx", + "h264_enc_mc_gxtv", + "gx_h264_enc", + "h264_enc_mc_txl", +}; + +static void dma_flush(u32 buf_start, u32 buf_size); +static void cache_flush(u32 buf_start , u32 buf_size); + +static const char *select_ucode(u32 ucode_index) +{ + enum ucode_type_e ucode = UCODE_GX; + switch (ucode_index) { + case UCODE_MODE_FULL: + if (get_cpu_type() >= MESON_CPU_MAJOR_ID_TXL) + ucode = UCODE_TXL; + else if (get_cpu_type() >= MESON_CPU_MAJOR_ID_GXL) + ucode = UCODE_GXL; + else if (get_cpu_type() >= MESON_CPU_MAJOR_ID_GXTVBB) + ucode = UCODE_GXTV; + else /* if (get_cpu_type() >= MESON_CPU_MAJOR_ID_GXBB) */ + ucode = UCODE_GX; + break; + break; + default: + break; + } + return (const char *)ucode_name[ucode]; +} + +static void hcodec_prog_qtbl(struct encode_wq_s *wq) +{ + WRITE_HREG(HCODEC_Q_QUANT_CONTROL, + (0 << 23) | /* quant_table_addr */ + (1 << 22)); /* quant_table_addr_update */ + + WRITE_HREG(HCODEC_QUANT_TABLE_DATA, + wq->quant_tbl_i4[0]); + WRITE_HREG(HCODEC_QUANT_TABLE_DATA, + wq->quant_tbl_i4[1]); + WRITE_HREG(HCODEC_QUANT_TABLE_DATA, + wq->quant_tbl_i4[2]); + WRITE_HREG(HCODEC_QUANT_TABLE_DATA, + wq->quant_tbl_i4[3]); + WRITE_HREG(HCODEC_QUANT_TABLE_DATA, + wq->quant_tbl_i4[4]); + WRITE_HREG(HCODEC_QUANT_TABLE_DATA, + wq->quant_tbl_i4[5]); + WRITE_HREG(HCODEC_QUANT_TABLE_DATA, + wq->quant_tbl_i4[6]); + WRITE_HREG(HCODEC_QUANT_TABLE_DATA, + wq->quant_tbl_i4[7]); + + WRITE_HREG(HCODEC_Q_QUANT_CONTROL, + (8 << 23) | /* quant_table_addr */ + (1 << 22)); /* quant_table_addr_update */ + + WRITE_HREG(HCODEC_QUANT_TABLE_DATA, + wq->quant_tbl_i16[0]); + WRITE_HREG(HCODEC_QUANT_TABLE_DATA, + wq->quant_tbl_i16[1]); + WRITE_HREG(HCODEC_QUANT_TABLE_DATA, + wq->quant_tbl_i16[2]); + WRITE_HREG(HCODEC_QUANT_TABLE_DATA, + wq->quant_tbl_i16[3]); + WRITE_HREG(HCODEC_QUANT_TABLE_DATA, + wq->quant_tbl_i16[4]); + WRITE_HREG(HCODEC_QUANT_TABLE_DATA, + wq->quant_tbl_i16[5]); + WRITE_HREG(HCODEC_QUANT_TABLE_DATA, + wq->quant_tbl_i16[6]); + WRITE_HREG(HCODEC_QUANT_TABLE_DATA, + wq->quant_tbl_i16[7]); + + WRITE_HREG(HCODEC_Q_QUANT_CONTROL, + (16 << 23) | /* quant_table_addr */ + (1 << 22)); /* quant_table_addr_update */ + + WRITE_HREG(HCODEC_QUANT_TABLE_DATA, + wq->quant_tbl_me[0]); + WRITE_HREG(HCODEC_QUANT_TABLE_DATA, + wq->quant_tbl_me[1]); + WRITE_HREG(HCODEC_QUANT_TABLE_DATA, + wq->quant_tbl_me[2]); + WRITE_HREG(HCODEC_QUANT_TABLE_DATA, + wq->quant_tbl_me[3]); + WRITE_HREG(HCODEC_QUANT_TABLE_DATA, + wq->quant_tbl_me[4]); + WRITE_HREG(HCODEC_QUANT_TABLE_DATA, + wq->quant_tbl_me[5]); + WRITE_HREG(HCODEC_QUANT_TABLE_DATA, + wq->quant_tbl_me[6]); + WRITE_HREG(HCODEC_QUANT_TABLE_DATA, + wq->quant_tbl_me[7]); + return; +} + +static void InitEncodeWeight(void) +{ + me_mv_merge_ctl = + (0x1 << 31) | /* [31] me_merge_mv_en_16 */ + (0x1 << 30) | /* [30] me_merge_small_mv_en_16 */ + (0x1 << 29) | /* [29] me_merge_flex_en_16 */ + (0x1 << 28) | /* [28] me_merge_sad_en_16 */ + (0x1 << 27) | /* [27] me_merge_mv_en_8 */ + (0x1 << 26) | /* [26] me_merge_small_mv_en_8 */ + (0x1 << 25) | /* [25] me_merge_flex_en_8 */ + (0x1 << 24) | /* [24] me_merge_sad_en_8 */ + (0x12 << 18) | + /* [23:18] me_merge_mv_diff_16 - MV diff + <= n pixel can be merged */ + (0x2b << 12) | + /* [17:12] me_merge_mv_diff_8 - MV diff + <= n pixel can be merged */ + (0x80 << 0); + /* [11:0] me_merge_min_sad - SAD + >= 0x180 can be merged with other MV */ + + me_mv_weight_01 = (ME_MV_STEP_WEIGHT_1 << 24) | + (ME_MV_PRE_WEIGHT_1 << 16) | + (ME_MV_STEP_WEIGHT_0 << 8) | + (ME_MV_PRE_WEIGHT_0 << 0); + + me_mv_weight_23 = (ME_MV_STEP_WEIGHT_3 << 24) | + (ME_MV_PRE_WEIGHT_3 << 16) | + (ME_MV_STEP_WEIGHT_2 << 8) | + (ME_MV_PRE_WEIGHT_2 << 0); + + me_sad_range_inc = (ME_SAD_RANGE_3 << 24) | + (ME_SAD_RANGE_2 << 16) | + (ME_SAD_RANGE_1 << 8) | + (ME_SAD_RANGE_0 << 0); + + me_step0_close_mv = (0x100 << 10) | + /* me_step0_big_sad -- two MV sad + diff bigger will use use 1 */ + (2 << 5) | /* me_step0_close_mv_y */ + (2 << 0); /* me_step0_close_mv_x */ + + me_f_skip_sad = (0x00 << 24) | /* force_skip_sad_3 */ + (STEP_2_SKIP_SAD << 16) | /* force_skip_sad_2 */ + (STEP_1_SKIP_SAD << 8) | /* force_skip_sad_1 */ + (STEP_0_SKIP_SAD << 0); /* force_skip_sad_0 */ + + me_f_skip_weight = (0x00 << 24) | /* force_skip_weight_3 */ + /* force_skip_weight_2 */ + (STEP_2_SKIP_WEIGHT << 16) | + /* force_skip_weight_1 */ + (STEP_1_SKIP_WEIGHT << 8) | + /* force_skip_weight_0 */ + (STEP_0_SKIP_WEIGHT << 0); + + if (get_cpu_type() >= MESON_CPU_MAJOR_ID_GXTVBB) { + me_f_skip_sad = 0; + me_f_skip_weight = 0; + me_mv_weight_01 = 0; + me_mv_weight_23 = 0; + } + + me_sad_enough_01 = (ME_SAD_ENOUGH_1_DATA << 12) | + /* me_sad_enough_1 */ + (ME_SAD_ENOUGH_0_DATA << 0) | + /* me_sad_enough_0 */ + (0 << 12) | /* me_sad_enough_1 */ + (0 << 0); /* me_sad_enough_0 */ + + me_sad_enough_23 = (ADV_MV_8x8_ENOUGH_DATA << 12) | + /* adv_mv_8x8_enough */ + (ME_SAD_ENOUGH_2_DATA << 0) | + /* me_sad_enough_2 */ + (0 << 12) | /* me_sad_enough_3 */ + (0 << 0); /* me_sad_enough_2 */ +} + +/*output stream buffer setting*/ +static void avc_init_output_buffer(struct encode_wq_s *wq) +{ + WRITE_HREG(HCODEC_VLC_VB_MEM_CTL, + ((1 << 31) | (0x3f << 24) | + (0x20 << 16) | (2 << 0))); + WRITE_HREG(HCODEC_VLC_VB_START_PTR, + wq->mem.BitstreamStart); + WRITE_HREG(HCODEC_VLC_VB_WR_PTR, + wq->mem.BitstreamStart); + WRITE_HREG(HCODEC_VLC_VB_SW_RD_PTR, + wq->mem.BitstreamStart); + WRITE_HREG(HCODEC_VLC_VB_END_PTR, + wq->mem.BitstreamEnd); + WRITE_HREG(HCODEC_VLC_VB_CONTROL, 1); + WRITE_HREG(HCODEC_VLC_VB_CONTROL, + ((0 << 14) | (7 << 3) | + (1 << 1) | (0 << 0))); +} + +/*input dct buffer setting*/ +static void avc_init_input_buffer(struct encode_wq_s *wq) +{ + WRITE_HREG(HCODEC_QDCT_MB_START_PTR, + wq->mem.dct_buff_start_addr); + WRITE_HREG(HCODEC_QDCT_MB_END_PTR, + wq->mem.dct_buff_end_addr); + WRITE_HREG(HCODEC_QDCT_MB_WR_PTR, + wq->mem.dct_buff_start_addr); + WRITE_HREG(HCODEC_QDCT_MB_RD_PTR, + wq->mem.dct_buff_start_addr); + WRITE_HREG(HCODEC_QDCT_MB_BUFF, 0); +} + +/*input reference buffer setting*/ +static void avc_init_reference_buffer(s32 canvas) +{ + WRITE_HREG(HCODEC_ANC0_CANVAS_ADDR, canvas); + WRITE_HREG(HCODEC_VLC_HCMD_CONFIG, 0); +} + +static void avc_init_assit_buffer(struct encode_wq_s *wq) +{ + WRITE_HREG(MEM_OFFSET_REG, wq->mem.assit_buffer_offset); +} + +/*deblock buffer setting, same as INI_CANVAS*/ +static void avc_init_dblk_buffer(s32 canvas) +{ + WRITE_HREG(HCODEC_REC_CANVAS_ADDR, canvas); + WRITE_HREG(HCODEC_DBKR_CANVAS_ADDR, canvas); + WRITE_HREG(HCODEC_DBKW_CANVAS_ADDR, canvas); +} + +static void avc_init_encoder(struct encode_wq_s *wq, bool idr) +{ + WRITE_HREG(HCODEC_VLC_TOTAL_BYTES, 0); + WRITE_HREG(HCODEC_VLC_CONFIG, 0x07); + WRITE_HREG(HCODEC_VLC_INT_CONTROL, 0); + + WRITE_HREG(HCODEC_ASSIST_AMR1_INT0, 0x15); + WRITE_HREG(HCODEC_ASSIST_AMR1_INT1, 0x8); + WRITE_HREG(HCODEC_ASSIST_AMR1_INT3, 0x14); + + WRITE_HREG(IDR_PIC_ID, wq->pic.idr_pic_id); + WRITE_HREG(FRAME_NUMBER, + (idr == true) ? 0 : wq->pic.frame_number); + WRITE_HREG(PIC_ORDER_CNT_LSB, + (idr == true) ? 0 : wq->pic.pic_order_cnt_lsb); + + WRITE_HREG(LOG2_MAX_PIC_ORDER_CNT_LSB, + wq->pic.log2_max_pic_order_cnt_lsb); + WRITE_HREG(LOG2_MAX_FRAME_NUM, + wq->pic.log2_max_frame_num); + WRITE_HREG(ANC0_BUFFER_ID, 0); + WRITE_HREG(QPPICTURE, wq->pic.init_qppicture); +} + +static void avc_canvas_init(struct encode_wq_s *wq) +{ + u32 canvas_width, canvas_height; + u32 start_addr = wq->mem.buf_start; + + canvas_width = ((wq->pic.encoder_width + 31) >> 5) << 5; + canvas_height = ((wq->pic.encoder_height + 15) >> 4) << 4; + + canvas_config(ENC_CANVAS_OFFSET, + start_addr + wq->mem.bufspec.dec0_y.buf_start, + canvas_width, canvas_height, + CANVAS_ADDR_NOWRAP, CANVAS_BLKMODE_LINEAR); + canvas_config(1 + ENC_CANVAS_OFFSET, + start_addr + wq->mem.bufspec.dec0_uv.buf_start, + canvas_width, canvas_height / 2, + CANVAS_ADDR_NOWRAP, CANVAS_BLKMODE_LINEAR); + /*here the third plane use the same address as the second plane*/ + canvas_config(2 + ENC_CANVAS_OFFSET, + start_addr + wq->mem.bufspec.dec0_uv.buf_start, + canvas_width, canvas_height / 2, + CANVAS_ADDR_NOWRAP, CANVAS_BLKMODE_LINEAR); + + canvas_config(3 + ENC_CANVAS_OFFSET, + start_addr + wq->mem.bufspec.dec1_y.buf_start, + canvas_width, canvas_height, + CANVAS_ADDR_NOWRAP, CANVAS_BLKMODE_LINEAR); + canvas_config(4 + ENC_CANVAS_OFFSET, + start_addr + wq->mem.bufspec.dec1_uv.buf_start, + canvas_width, canvas_height / 2, + CANVAS_ADDR_NOWRAP, CANVAS_BLKMODE_LINEAR); + /*here the third plane use the same address as the second plane*/ + canvas_config(5 + ENC_CANVAS_OFFSET, + start_addr + wq->mem.bufspec.dec1_uv.buf_start, + canvas_width, canvas_height / 2, + CANVAS_ADDR_NOWRAP, CANVAS_BLKMODE_LINEAR); +} + +static void avc_buffspec_init(struct encode_wq_s *wq) +{ + u32 canvas_width, canvas_height; + u32 start_addr = wq->mem.buf_start; + u32 mb_w = (wq->pic.encoder_width + 15) >> 4; + u32 mb_h = (wq->pic.encoder_height + 15) >> 4; + u32 mbs = mb_w * mb_h; + + canvas_width = ((wq->pic.encoder_width + 31) >> 5) << 5; + canvas_height = ((wq->pic.encoder_height + 15) >> 4) << 4; + + wq->mem.dct_buff_start_addr = start_addr + + wq->mem.bufspec.dct.buf_start; + wq->mem.dct_buff_end_addr = + wq->mem.dct_buff_start_addr + + wq->mem.bufspec.dct.buf_size - 1; + enc_pr(LOG_INFO, "dct_buff_start_addr is 0x%x, wq:%p.\n", + wq->mem.dct_buff_start_addr, (void *)wq); + + wq->mem.bufspec.dec0_uv.buf_start = + wq->mem.bufspec.dec0_y.buf_start + + canvas_width * canvas_height; + wq->mem.bufspec.dec0_uv.buf_size = canvas_width * canvas_height / 2; + wq->mem.bufspec.dec1_uv.buf_start = + wq->mem.bufspec.dec1_y.buf_start + + canvas_width * canvas_height; + wq->mem.bufspec.dec1_uv.buf_size = canvas_width * canvas_height / 2; + wq->mem.assit_buffer_offset = start_addr + + wq->mem.bufspec.assit.buf_start; + enc_pr(LOG_INFO, "assit_buffer_offset is 0x%x, wq: %p.\n", + wq->mem.assit_buffer_offset, (void *)wq); + /*output stream buffer config*/ + wq->mem.BitstreamStart = start_addr + + wq->mem.bufspec.bitstream.buf_start; + wq->mem.BitstreamEnd = + wq->mem.BitstreamStart + + wq->mem.bufspec.bitstream.buf_size - 1; + enc_pr(LOG_INFO, "BitstreamStart is 0x%x, wq: %p.\n", + wq->mem.BitstreamStart, (void *)wq); + + wq->mem.scaler_buff_start_addr = + wq->mem.buf_start + wq->mem.bufspec.scale_buff.buf_start; + wq->mem.dump_info_ddr_start_addr = + wq->mem.buf_start + wq->mem.bufspec.dump_info.buf_start; +enc_pr(LOG_INFO, "CBR: dump_info_ddr_start_addr:%x.\n", wq->mem.dump_info_ddr_start_addr); +enc_pr(LOG_INFO, "CBR: buf_start :%d.\n", wq->mem.buf_start); +enc_pr(LOG_INFO, "CBR: dump_info.buf_start :%d.\n", wq->mem.bufspec.dump_info.buf_start); + wq->mem.dump_info_ddr_size = + DUMP_INFO_BYTES_PER_MB * mbs; + wq->mem.dump_info_ddr_size = + (wq->mem.dump_info_ddr_size + PAGE_SIZE - 1) + & ~(PAGE_SIZE - 1); + wq->mem.cbr_info_ddr_start_addr = + wq->mem.buf_start + wq->mem.bufspec.cbr_info.buf_start; + wq->mem.cbr_info_ddr_size = + wq->mem.bufspec.cbr_info.buf_size; + + wq->mem.dblk_buf_canvas = + ((ENC_CANVAS_OFFSET + 2) << 16) | + ((ENC_CANVAS_OFFSET + 1) << 8) | + (ENC_CANVAS_OFFSET); + wq->mem.ref_buf_canvas = + ((ENC_CANVAS_OFFSET + 5) << 16) | + ((ENC_CANVAS_OFFSET + 4) << 8) | + (ENC_CANVAS_OFFSET + 3); +} + +static void avc_init_ie_me_parameter(struct encode_wq_s *wq, u32 quant) +{ + ie_cur_ref_sel = 0; + ie_pippeline_block = 12; + /* currently disable half and sub pixel */ + ie_me_mode = + (ie_pippeline_block & IE_PIPPELINE_BLOCK_MASK) << + IE_PIPPELINE_BLOCK_SHIFT; + + WRITE_HREG(IE_ME_MODE, ie_me_mode); + WRITE_HREG(IE_REF_SEL, ie_cur_ref_sel); + WRITE_HREG(IE_ME_MB_TYPE, ie_me_mb_type); +#ifdef MULTI_SLICE_MC + if (fixed_slice_cfg) + WRITE_HREG(FIXED_SLICE_CFG, fixed_slice_cfg); + else if (wq->pic.rows_per_slice != + (wq->pic.encoder_height + 15) >> 4) { + u32 mb_per_slice = (wq->pic.encoder_height + 15) >> 4; + mb_per_slice = mb_per_slice * wq->pic.rows_per_slice; + WRITE_HREG(FIXED_SLICE_CFG, mb_per_slice); + } else + WRITE_HREG(FIXED_SLICE_CFG, 0); +#else + WRITE_HREG(FIXED_SLICE_CFG, 0); +#endif +} + +/* for temp */ +#define HCODEC_MFDIN_REGC_MBLP (HCODEC_MFDIN_REGB_AMPC + 0x1) +#define HCODEC_MFDIN_REG0D (HCODEC_MFDIN_REGB_AMPC + 0x2) +#define HCODEC_MFDIN_REG0E (HCODEC_MFDIN_REGB_AMPC + 0x3) +#define HCODEC_MFDIN_REG0F (HCODEC_MFDIN_REGB_AMPC + 0x4) +#define HCODEC_MFDIN_REG10 (HCODEC_MFDIN_REGB_AMPC + 0x5) +#define HCODEC_MFDIN_REG11 (HCODEC_MFDIN_REGB_AMPC + 0x6) +#define HCODEC_MFDIN_REG12 (HCODEC_MFDIN_REGB_AMPC + 0x7) +#define HCODEC_MFDIN_REG13 (HCODEC_MFDIN_REGB_AMPC + 0x8) +#define HCODEC_MFDIN_REG14 (HCODEC_MFDIN_REGB_AMPC + 0x9) +#define HCODEC_MFDIN_REG15 (HCODEC_MFDIN_REGB_AMPC + 0xa) +#define HCODEC_MFDIN_REG16 (HCODEC_MFDIN_REGB_AMPC + 0xb) + +static void mfdin_basic(u32 input, u8 iformat, + u8 oformat, u32 picsize_x, u32 picsize_y, + u8 r2y_en, u8 nr, u8 ifmt_extra) +{ + u8 dsample_en; /* Downsample Enable */ + u8 interp_en; /* Interpolation Enable */ + u8 y_size; /* 0:16 Pixels for y direction pickup; 1:8 pixels */ + u8 r2y_mode; /* RGB2YUV Mode, range(0~3) */ + /* mfdin_reg3_canv[25:24]; + // bytes per pixel in x direction for index0, 0:half 1:1 2:2 3:3 */ + u8 canv_idx0_bppx; + /* mfdin_reg3_canv[27:26]; + // bytes per pixel in x direction for index1-2, 0:half 1:1 2:2 3:3 */ + u8 canv_idx1_bppx; + /* mfdin_reg3_canv[29:28]; + // bytes per pixel in y direction for index0, 0:half 1:1 2:2 3:3 */ + u8 canv_idx0_bppy; + /* mfdin_reg3_canv[31:30]; + // bytes per pixel in y direction for index1-2, 0:half 1:1 2:2 3:3 */ + u8 canv_idx1_bppy; + u8 ifmt444, ifmt422, ifmt420, linear_bytes4p; + u8 nr_enable; + u8 cfg_y_snr_en; + u8 cfg_y_tnr_en; + u8 cfg_c_snr_en; + u8 cfg_c_tnr_en; + u32 linear_bytesperline; + s32 reg_offset; + bool linear_enable = false; + bool format_err = false; + + if (get_cpu_type() >= MESON_CPU_MAJOR_ID_TXL) { + if ((iformat == 7) && (ifmt_extra > 2)) + format_err = true; + } else if (iformat == 7) + format_err = true; + + if (format_err) { + enc_pr(LOG_ERROR, + "mfdin format err, iformat:%d, ifmt_extra:%d\n", + iformat, ifmt_extra); + return; + } + if (iformat != 7) + ifmt_extra = 0; + + ifmt444 = ((iformat == 1) || (iformat == 5) || (iformat == 8) || + (iformat == 9) || (iformat == 12)) ? 1 : 0; + if (iformat == 7 && ifmt_extra == 1) + ifmt444 = 1; + ifmt422 = ((iformat == 0) || (iformat == 10)) ? 1 : 0; + if (iformat == 7 && ifmt_extra != 1) + ifmt422 = 1; + ifmt420 = ((iformat == 2) || (iformat == 3) || (iformat == 4) || + (iformat == 11)) ? 1 : 0; + dsample_en = ((ifmt444 && (oformat != 2)) || + (ifmt422 && (oformat == 0))) ? 1 : 0; + interp_en = ((ifmt422 && (oformat == 2)) || + (ifmt420 && (oformat != 0))) ? 1 : 0; + y_size = (oformat != 0) ? 1 : 0; + if (iformat == 12) + y_size = 0; + r2y_mode = (r2y_en == 1) ? 1 : 0; /* Fixed to 1 (TODO) */ + canv_idx0_bppx = (iformat == 1) ? 3 : (iformat == 0) ? 2 : 1; + canv_idx1_bppx = (iformat == 4) ? 0 : 1; + canv_idx0_bppy = 1; + canv_idx1_bppy = (iformat == 5) ? 1 : 0; + + if ((iformat == 8) || (iformat == 9) || (iformat == 12)) + linear_bytes4p = 3; + else if (iformat == 10) + linear_bytes4p = 2; + else if (iformat == 11) + linear_bytes4p = 1; + else + linear_bytes4p = 0; + if (iformat == 12) + linear_bytesperline = picsize_x * 4; + else + linear_bytesperline = picsize_x * linear_bytes4p; + + if (iformat < 8) + linear_enable = false; + else + linear_enable = true; + + if (get_cpu_type() >= MESON_CPU_MAJOR_ID_GXBB) { + reg_offset = -8; + /* nr_mode: 0:Disabled 1:SNR Only 2:TNR Only 3:3DNR */ + nr_enable = (nr) ? 1 : 0; + cfg_y_snr_en = ((nr == 1) || (nr == 3)) ? 1 : 0; + cfg_y_tnr_en = ((nr == 2) || (nr == 3)) ? 1 : 0; + cfg_c_snr_en = cfg_y_snr_en; + /* cfg_c_tnr_en = cfg_y_tnr_en; */ + cfg_c_tnr_en = 0; + + /* NR For Y */ + WRITE_HREG((HCODEC_MFDIN_REG0D + reg_offset), + ((cfg_y_snr_en << 0) | + (y_snr_err_norm << 1) | + (y_snr_gau_bld_core << 2) | + (((y_snr_gau_bld_ofst) & 0xff) << 6) | + (y_snr_gau_bld_rate << 14) | + (y_snr_gau_alp0_min << 20) | + (y_snr_gau_alp0_max << 26))); + WRITE_HREG((HCODEC_MFDIN_REG0E + reg_offset), + ((cfg_y_tnr_en << 0) | + (y_tnr_mc_en << 1) | + (y_tnr_txt_mode << 2) | + (y_tnr_mot_sad_margin << 3) | + (y_tnr_alpha_min << 7) | + (y_tnr_alpha_max << 13) | + (y_tnr_deghost_os << 19))); + WRITE_HREG((HCODEC_MFDIN_REG0F + reg_offset), + ((y_tnr_mot_cortxt_rate << 0) | + (y_tnr_mot_distxt_ofst << 8) | + (y_tnr_mot_distxt_rate << 4) | + (y_tnr_mot_dismot_ofst << 16) | + (y_tnr_mot_frcsad_lock << 24))); + WRITE_HREG((HCODEC_MFDIN_REG10 + reg_offset), + ((y_tnr_mot2alp_frc_gain << 0) | + (y_tnr_mot2alp_nrm_gain << 8) | + (y_tnr_mot2alp_dis_gain << 16) | + (y_tnr_mot2alp_dis_ofst << 24))); + WRITE_HREG((HCODEC_MFDIN_REG11 + reg_offset), + ((y_bld_beta2alp_rate << 0) | + (y_bld_beta_min << 8) | + (y_bld_beta_max << 14))); + + /* NR For C */ + WRITE_HREG((HCODEC_MFDIN_REG12 + reg_offset), + ((cfg_y_snr_en << 0) | + (c_snr_err_norm << 1) | + (c_snr_gau_bld_core << 2) | + (((c_snr_gau_bld_ofst) & 0xff) << 6) | + (c_snr_gau_bld_rate << 14) | + (c_snr_gau_alp0_min << 20) | + (c_snr_gau_alp0_max << 26))); + + WRITE_HREG((HCODEC_MFDIN_REG13 + reg_offset), + ((cfg_c_tnr_en << 0) | + (c_tnr_mc_en << 1) | + (c_tnr_txt_mode << 2) | + (c_tnr_mot_sad_margin << 3) | + (c_tnr_alpha_min << 7) | + (c_tnr_alpha_max << 13) | + (c_tnr_deghost_os << 19))); + WRITE_HREG((HCODEC_MFDIN_REG14 + reg_offset), + ((c_tnr_mot_cortxt_rate << 0) | + (c_tnr_mot_distxt_ofst << 8) | + (c_tnr_mot_distxt_rate << 4) | + (c_tnr_mot_dismot_ofst << 16) | + (c_tnr_mot_frcsad_lock << 24))); + WRITE_HREG((HCODEC_MFDIN_REG15 + reg_offset), + ((c_tnr_mot2alp_frc_gain << 0) | + (c_tnr_mot2alp_nrm_gain << 8) | + (c_tnr_mot2alp_dis_gain << 16) | + (c_tnr_mot2alp_dis_ofst << 24))); + + WRITE_HREG((HCODEC_MFDIN_REG16 + reg_offset), + ((c_bld_beta2alp_rate << 0) | + (c_bld_beta_min << 8) | + (c_bld_beta_max << 14))); + + WRITE_HREG((HCODEC_MFDIN_REG1_CTRL + reg_offset), + (iformat << 0) | (oformat << 4) | + (dsample_en << 6) | (y_size << 8) | + (interp_en << 9) | (r2y_en << 12) | + (r2y_mode << 13) | (ifmt_extra << 16) | + (nr_enable << 19)); + WRITE_HREG((HCODEC_MFDIN_REG8_DMBL + reg_offset), + (picsize_x << 14) | (picsize_y << 0)); + } else { + reg_offset = 0; + WRITE_HREG((HCODEC_MFDIN_REG1_CTRL + reg_offset), + (iformat << 0) | (oformat << 4) | + (dsample_en << 6) | (y_size << 8) | + (interp_en << 9) | (r2y_en << 12) | + (r2y_mode << 13)); + WRITE_HREG((HCODEC_MFDIN_REG8_DMBL + reg_offset), + (picsize_x << 12) | (picsize_y << 0)); + } + + if (linear_enable == false) { + WRITE_HREG((HCODEC_MFDIN_REG3_CANV + reg_offset), + (input & 0xffffff) | + (canv_idx1_bppy << 30) | + (canv_idx0_bppy << 28) | + (canv_idx1_bppx << 26) | + (canv_idx0_bppx << 24)); + WRITE_HREG((HCODEC_MFDIN_REG4_LNR0 + reg_offset), + (0 << 16) | (0 << 0)); + WRITE_HREG((HCODEC_MFDIN_REG5_LNR1 + reg_offset), 0); + } else { + WRITE_HREG((HCODEC_MFDIN_REG3_CANV + reg_offset), + (canv_idx1_bppy << 30) | + (canv_idx0_bppy << 28) | + (canv_idx1_bppx << 26) | + (canv_idx0_bppx << 24)); + WRITE_HREG((HCODEC_MFDIN_REG4_LNR0 + reg_offset), + (linear_bytes4p << 16) | (linear_bytesperline << 0)); + WRITE_HREG((HCODEC_MFDIN_REG5_LNR1 + reg_offset), input); + } + + if (iformat == 12) + WRITE_HREG((HCODEC_MFDIN_REG9_ENDN + reg_offset), + (2 << 0) | (1 << 3) | (0 << 6) | + (3 << 9) | (6 << 12) | (5 << 15) | + (4 << 18) | (7 << 21)); + else + WRITE_HREG((HCODEC_MFDIN_REG9_ENDN + reg_offset), + (7 << 0) | (6 << 3) | (5 << 6) | + (4 << 9) | (3 << 12) | (2 << 15) | + (1 << 18) | (0 << 21)); +} + +#ifdef CONFIG_AM_GE2D +static int scale_frame(struct encode_wq_s *wq, + struct encode_request_s *request, + struct config_para_ex_s *ge2d_config, + u32 src_addr, bool canvas) +{ + struct ge2d_context_s *context = encode_manager.context; + int src_top, src_left, src_width, src_height; + struct canvas_s cs0, cs1, cs2, cd; + u32 src_canvas, dst_canvas; + u32 src_canvas_w, dst_canvas_w; + u32 src_h = request->src_h; + u32 dst_w = ((wq->pic.encoder_width + 15) >> 4) << 4; + u32 dst_h = ((wq->pic.encoder_height + 15) >> 4) << 4; + int input_format = GE2D_FORMAT_M24_NV21; + src_top = request->crop_top; + src_left = request->crop_left; + src_width = request->src_w - src_left - request->crop_right; + src_height = request->src_h - src_top - request->crop_bottom; + if (canvas) { + if ((request->fmt == FMT_NV21) + || (request->fmt == FMT_NV12)) { + src_canvas = src_addr & 0xffff; + input_format = GE2D_FORMAT_M24_NV21; + } else { + src_canvas = src_addr & 0xffffff; + input_format = GE2D_FORMAT_M24_YUV420; + } + } else { + if ((request->fmt == FMT_NV21) + || (request->fmt == FMT_NV12)) { + src_canvas_w = + ((request->src_w + 31) >> 5) << 5; + canvas_config(ENC_CANVAS_OFFSET + 9, + src_addr, + src_canvas_w, src_h, + CANVAS_ADDR_NOWRAP, + CANVAS_BLKMODE_LINEAR); + canvas_config(ENC_CANVAS_OFFSET + 10, + src_addr + src_canvas_w * src_h, + src_canvas_w, src_h / 2, + CANVAS_ADDR_NOWRAP, + CANVAS_BLKMODE_LINEAR); + src_canvas = + ((ENC_CANVAS_OFFSET + 10) << 8) + | (ENC_CANVAS_OFFSET + 9); + input_format = GE2D_FORMAT_M24_NV21; + } else { + src_canvas_w = + ((request->src_w + 63) >> 6) << 6; + canvas_config(ENC_CANVAS_OFFSET + 9, + src_addr, + src_canvas_w, src_h, + CANVAS_ADDR_NOWRAP, + CANVAS_BLKMODE_LINEAR); + canvas_config(ENC_CANVAS_OFFSET + 10, + src_addr + src_canvas_w * src_h, + src_canvas_w / 2, src_h / 2, + CANVAS_ADDR_NOWRAP, + CANVAS_BLKMODE_LINEAR); + canvas_config(ENC_CANVAS_OFFSET + 11, + src_addr + src_canvas_w * src_h * 5 / 4, + src_canvas_w / 2, src_h / 2, + CANVAS_ADDR_NOWRAP, + CANVAS_BLKMODE_LINEAR); + src_canvas = + ((ENC_CANVAS_OFFSET + 11) << 16) | + ((ENC_CANVAS_OFFSET + 10) << 8) | + (ENC_CANVAS_OFFSET + 9); + input_format = GE2D_FORMAT_M24_YUV420; + } + } + dst_canvas_w = ((dst_w + 31) >> 5) << 5; + canvas_config(ENC_CANVAS_OFFSET + 6, + wq->mem.scaler_buff_start_addr, + dst_canvas_w, dst_h, + CANVAS_ADDR_NOWRAP, CANVAS_BLKMODE_LINEAR); + canvas_config(ENC_CANVAS_OFFSET + 7, + wq->mem.scaler_buff_start_addr + dst_canvas_w * dst_h, + dst_canvas_w, dst_h / 2, + CANVAS_ADDR_NOWRAP, CANVAS_BLKMODE_LINEAR); + dst_canvas = ((ENC_CANVAS_OFFSET + 7) << 8) | + (ENC_CANVAS_OFFSET + 6); + ge2d_config->alu_const_color = 0; + ge2d_config->bitmask_en = 0; + ge2d_config->src1_gb_alpha = 0; + ge2d_config->dst_xy_swap = 0; + canvas_read(src_canvas & 0xff, &cs0); + canvas_read((src_canvas >> 8) & 0xff, &cs1); + canvas_read((src_canvas >> 16) & 0xff, &cs2); + ge2d_config->src_planes[0].addr = cs0.addr; + ge2d_config->src_planes[0].w = cs0.width; + ge2d_config->src_planes[0].h = cs0.height; + ge2d_config->src_planes[1].addr = cs1.addr; + ge2d_config->src_planes[1].w = cs1.width; + ge2d_config->src_planes[1].h = cs1.height; + ge2d_config->src_planes[2].addr = cs2.addr; + ge2d_config->src_planes[2].w = cs2.width; + ge2d_config->src_planes[2].h = cs2.height; + canvas_read(dst_canvas & 0xff, &cd); + ge2d_config->dst_planes[0].addr = cd.addr; + ge2d_config->dst_planes[0].w = cd.width; + ge2d_config->dst_planes[0].h = cd.height; + ge2d_config->src_key.key_enable = 0; + ge2d_config->src_key.key_mask = 0; + ge2d_config->src_key.key_mode = 0; + ge2d_config->src_para.canvas_index = src_canvas; + ge2d_config->src_para.mem_type = CANVAS_TYPE_INVALID; + ge2d_config->src_para.format = input_format | GE2D_LITTLE_ENDIAN; + ge2d_config->src_para.fill_color_en = 0; + ge2d_config->src_para.fill_mode = 0; + ge2d_config->src_para.x_rev = 0; + ge2d_config->src_para.y_rev = 0; + ge2d_config->src_para.color = 0xffffffff; + ge2d_config->src_para.top = 0; + ge2d_config->src_para.left = 0; + ge2d_config->src_para.width = request->src_w; + ge2d_config->src_para.height = request->src_h; + ge2d_config->src2_para.mem_type = CANVAS_TYPE_INVALID; + ge2d_config->dst_para.canvas_index = dst_canvas; + ge2d_config->dst_para.mem_type = CANVAS_TYPE_INVALID; + ge2d_config->dst_para.format = + GE2D_FORMAT_M24_NV21 | GE2D_LITTLE_ENDIAN; + ge2d_config->dst_para.fill_color_en = 0; + ge2d_config->dst_para.fill_mode = 0; + ge2d_config->dst_para.x_rev = 0; + ge2d_config->dst_para.y_rev = 0; + ge2d_config->dst_para.color = 0; + ge2d_config->dst_para.top = 0; + ge2d_config->dst_para.left = 0; + ge2d_config->dst_para.width = dst_w; + ge2d_config->dst_para.height = dst_h; + ge2d_config->dst_para.x_rev = 0; + ge2d_config->dst_para.y_rev = 0; + if (ge2d_context_config_ex(context, ge2d_config) < 0) { + pr_err("++ge2d configing error.\n"); + return -1; + } + stretchblt_noalpha(context, src_left, src_top, src_width, src_height, + 0, 0, wq->pic.encoder_width, wq->pic.encoder_height); + return dst_canvas_w*dst_h * 3 / 2; +} +#endif + +static s32 set_input_format(struct encode_wq_s *wq, + struct encode_request_s *request) +{ + s32 ret = 0; + u8 iformat = MAX_FRAME_FMT, oformat = MAX_FRAME_FMT, r2y_en = 0; + u32 picsize_x, picsize_y, src_addr; + u32 canvas_w = 0; + u32 input = request->src; + u8 ifmt_extra = 0; + + if ((request->fmt == FMT_RGB565) || (request->fmt >= MAX_FRAME_FMT)) + return -1; + + picsize_x = ((wq->pic.encoder_width + 15) >> 4) << 4; + picsize_y = ((wq->pic.encoder_height + 15) >> 4) << 4; + oformat = 0; + if ((request->type == LOCAL_BUFF) + || (request->type == PHYSICAL_BUFF)) { + if ((request->type == LOCAL_BUFF) && + (request->flush_flag & AMVENC_FLUSH_FLAG_INPUT)) + dma_flush(wq->mem.dct_buff_start_addr, + request->framesize); + if (request->type == LOCAL_BUFF) { + input = wq->mem.dct_buff_start_addr; + src_addr = + wq->mem.dct_buff_start_addr; + } else { + src_addr = input; + picsize_y = wq->pic.encoder_height; + } + if (request->scale_enable) { +#ifdef CONFIG_AM_GE2D + struct config_para_ex_s ge2d_config; + memset(&ge2d_config, 0, + sizeof(struct config_para_ex_s)); + scale_frame( + wq, request, + &ge2d_config, + src_addr, + false); + iformat = 2; + r2y_en = 0; + input = ((ENC_CANVAS_OFFSET + 7) << 8) | + (ENC_CANVAS_OFFSET + 6); + ret = 0; + goto MFDIN; +#else + enc_pr(LOG_ERROR, + "Warning: need enable ge2d for scale frame!\n"); + return -1; +#endif + } + if ((request->fmt <= FMT_YUV444_PLANE) || + (request->fmt >= FMT_YUV422_12BIT)) + r2y_en = 0; + else + r2y_en = 1; + + if (request->fmt >= FMT_YUV422_12BIT) { + iformat = 7; + ifmt_extra = request->fmt - FMT_YUV422_12BIT; + if (request->fmt == FMT_YUV422_12BIT) + canvas_w = picsize_x * 24 / 8; + else if (request->fmt == FMT_YUV444_10BIT) + canvas_w = picsize_x * 32 / 8; + else + canvas_w = (picsize_x * 20 + 7) / 8; + canvas_w = ((canvas_w + 31) >> 5) << 5; + canvas_config(ENC_CANVAS_OFFSET + 6, + input, + canvas_w, picsize_y, + CANVAS_ADDR_NOWRAP, + CANVAS_BLKMODE_LINEAR); + input = ENC_CANVAS_OFFSET + 6; + input = input & 0xff; + } else if (request->fmt == FMT_YUV422_SINGLE) + iformat = 10; + else if ((request->fmt == FMT_YUV444_SINGLE) + || (request->fmt == FMT_RGB888)) { + iformat = 1; + if (request->fmt == FMT_RGB888) + r2y_en = 1; + canvas_w = picsize_x * 3; + canvas_w = ((canvas_w + 31) >> 5) << 5; + canvas_config(ENC_CANVAS_OFFSET + 6, + input, + canvas_w, picsize_y, + CANVAS_ADDR_NOWRAP, + CANVAS_BLKMODE_LINEAR); + input = ENC_CANVAS_OFFSET + 6; + } else if ((request->fmt == FMT_NV21) + || (request->fmt == FMT_NV12)) { + canvas_w = ((wq->pic.encoder_width + 31) >> 5) << 5; + iformat = (request->fmt == FMT_NV21) ? 2 : 3; + canvas_config(ENC_CANVAS_OFFSET + 6, + input, + canvas_w, picsize_y, + CANVAS_ADDR_NOWRAP, + CANVAS_BLKMODE_LINEAR); + canvas_config(ENC_CANVAS_OFFSET + 7, + input + canvas_w * picsize_y, + canvas_w, picsize_y / 2, + CANVAS_ADDR_NOWRAP, + CANVAS_BLKMODE_LINEAR); + input = ((ENC_CANVAS_OFFSET + 7) << 8) | + (ENC_CANVAS_OFFSET + 6); + } else if (request->fmt == FMT_YUV420) { + iformat = 4; + canvas_w = ((wq->pic.encoder_width + 63) >> 6) << 6; + canvas_config(ENC_CANVAS_OFFSET + 6, + input, + canvas_w, picsize_y, + CANVAS_ADDR_NOWRAP, + CANVAS_BLKMODE_LINEAR); + canvas_config(ENC_CANVAS_OFFSET + 7, + input + canvas_w * picsize_y, + canvas_w / 2, picsize_y / 2, + CANVAS_ADDR_NOWRAP, + CANVAS_BLKMODE_LINEAR); + canvas_config(ENC_CANVAS_OFFSET + 8, + input + canvas_w * picsize_y * 5 / 4, + canvas_w / 2, picsize_y / 2, + CANVAS_ADDR_NOWRAP, + CANVAS_BLKMODE_LINEAR); + input = ((ENC_CANVAS_OFFSET + 8) << 16) | + ((ENC_CANVAS_OFFSET + 7) << 8) | + (ENC_CANVAS_OFFSET + 6); + } else if ((request->fmt == FMT_YUV444_PLANE) + || (request->fmt == FMT_RGB888_PLANE)) { + if (request->fmt == FMT_RGB888_PLANE) + r2y_en = 1; + iformat = 5; + canvas_w = ((wq->pic.encoder_width + 31) >> 5) << 5; + canvas_config(ENC_CANVAS_OFFSET + 6, + input, + canvas_w, picsize_y, + CANVAS_ADDR_NOWRAP, + CANVAS_BLKMODE_LINEAR); + canvas_config(ENC_CANVAS_OFFSET + 7, + input + canvas_w * picsize_y, + canvas_w, picsize_y, + CANVAS_ADDR_NOWRAP, + CANVAS_BLKMODE_LINEAR); + canvas_config(ENC_CANVAS_OFFSET + 8, + input + canvas_w * picsize_y * 2, + canvas_w, picsize_y, + CANVAS_ADDR_NOWRAP, + CANVAS_BLKMODE_LINEAR); + input = ((ENC_CANVAS_OFFSET + 8) << 16) | + ((ENC_CANVAS_OFFSET + 7) << 8) | + (ENC_CANVAS_OFFSET + 6); + } else if (request->fmt == FMT_RGBA8888) { + r2y_en = 1; + iformat = 12; + } + ret = 0; + } else if (request->type == CANVAS_BUFF) { + r2y_en = 0; + if (request->scale_enable) { +#ifdef CONFIG_AM_GE2D + struct config_para_ex_s ge2d_config; + memset(&ge2d_config, 0, + sizeof(struct config_para_ex_s)); + scale_frame( + wq, request, + &ge2d_config, + input, true); + iformat = 2; + r2y_en = 0; + input = ((ENC_CANVAS_OFFSET + 7) << 8) | + (ENC_CANVAS_OFFSET + 6); + ret = 0; + goto MFDIN; +#else + enc_pr(LOG_ERROR, + "Warning: need enable ge2d for scale frame!\n"); + return -1; +#endif + } + if (request->fmt == FMT_YUV422_SINGLE) { + iformat = 0; + input = input & 0xff; + } else if (request->fmt == FMT_YUV444_SINGLE) { + iformat = 1; + input = input & 0xff; + } else if ((request->fmt == FMT_NV21) + || (request->fmt == FMT_NV12)) { + iformat = (request->fmt == FMT_NV21) ? 2 : 3; + input = input & 0xffff; + } else if (request->fmt == FMT_YUV420) { + iformat = 4; + input = input & 0xffffff; + } else if ((request->fmt == FMT_YUV444_PLANE) + || (request->fmt == FMT_RGB888_PLANE)) { + if (request->fmt == FMT_RGB888_PLANE) + r2y_en = 1; + iformat = 5; + input = input & 0xffffff; + } else if ((request->fmt == FMT_YUV422_12BIT) + || (request->fmt == FMT_YUV444_10BIT) + || (request->fmt == FMT_YUV422_10BIT)) { + iformat = 7; + ifmt_extra = request->fmt - FMT_YUV422_12BIT; + input = input & 0xff; + } else + ret = -1; + } + + if (ret == 0) + mfdin_basic(input, iformat, oformat, + picsize_x, picsize_y, r2y_en, + request->nr_mode, ifmt_extra); + return ret; +} + +#ifdef H264_ENC_CBR +static void ConvertTable2Risc(void *table, u32 len) +{ + u32 i, j; + u16 temp; + u16 *tbl = (u16 *)table; + if ((len < 8) || (len % 8) || (!table)) { + enc_pr(LOG_ERROR, "ConvertTable2Risc tbl %p, len %d error\n", + table, len); + return; + } + for (i = 0; i < len / 8; i++) { + j = i << 2; + temp = tbl[j]; + tbl[j] = tbl[j + 3]; + tbl[j + 3] = temp; + + temp = tbl[j + 1]; + tbl[j + 1] = tbl[j + 2]; + tbl[j + 2] = temp; + } + +} +#endif + +static void avc_prot_init(struct encode_wq_s *wq, + struct encode_request_s *request, u32 quant, bool IDR) +{ + u32 data32; + u32 pic_width, pic_height; + u32 pic_mb_nr; + u32 pic_mbx, pic_mby; + u32 i_pic_qp, p_pic_qp; + u32 i_pic_qp_c, p_pic_qp_c; + u32 pic_width_in_mb; + u32 slice_qp; + pic_width = wq->pic.encoder_width; + pic_height = wq->pic.encoder_height; + pic_mb_nr = 0; + pic_mbx = 0; + pic_mby = 0; + i_pic_qp = quant; + p_pic_qp = quant; + + pic_width_in_mb = (pic_width + 15) / 16; + WRITE_HREG(HCODEC_HDEC_MC_OMEM_AUTO, + (1 << 31) | /* use_omem_mb_xy */ + ((pic_width_in_mb - 1) << 16)); /* omem_max_mb_x */ + + WRITE_HREG(HCODEC_VLC_ADV_CONFIG, + /* early_mix_mc_hcmd -- will enable in P Picture */ + (0 << 10) | + (1 << 9) | /* update_top_left_mix */ + (1 << 8) | /* p_top_left_mix */ + /* mv_cal_mixed_type -- will enable in P Picture */ + (0 << 7) | + /* mc_hcmd_mixed_type -- will enable in P Picture */ + (0 << 6) | + (1 << 5) | /* use_seperate_int_control */ + (1 << 4) | /* hcmd_intra_use_q_info */ + (1 << 3) | /* hcmd_left_use_prev_info */ + (1 << 2) | /* hcmd_use_q_info */ + (1 << 1) | /* use_q_delta_quant */ + /* detect_I16_from_I4 use qdct detected mb_type */ + (0 << 0)); + + WRITE_HREG(HCODEC_QDCT_ADV_CONFIG, + (1 << 29) | /* mb_info_latch_no_I16_pred_mode */ + (1 << 28) | /* ie_dma_mbxy_use_i_pred */ + (1 << 27) | /* ie_dma_read_write_use_ip_idx */ + (1 << 26) | /* ie_start_use_top_dma_count */ + (1 << 25) | /* i_pred_top_dma_rd_mbbot */ + (1 << 24) | /* i_pred_top_dma_wr_disable */ + /* i_pred_mix -- will enable in P Picture */ + (0 << 23) | + (1 << 22) | /* me_ab_rd_when_intra_in_p */ + (1 << 21) | /* force_mb_skip_run_when_intra */ + /* mc_out_mixed_type -- will enable in P Picture */ + (0 << 20) | + (1 << 19) | /* ie_start_when_quant_not_full */ + (1 << 18) | /* mb_info_state_mix */ + /* mb_type_use_mix_result -- will enable in P Picture */ + (0 << 17) | + /* me_cb_ie_read_enable -- will enable in P Picture */ + (0 << 16) | + /* ie_cur_data_from_me -- will enable in P Picture */ + (0 << 15) | + (1 << 14) | /* rem_per_use_table */ + (0 << 13) | /* q_latch_int_enable */ + (1 << 12) | /* q_use_table */ + (0 << 11) | /* q_start_wait */ + (1 << 10) | /* LUMA_16_LEFT_use_cur */ + (1 << 9) | /* DC_16_LEFT_SUM_use_cur */ + (1 << 8) | /* c_ref_ie_sel_cur */ + (0 << 7) | /* c_ipred_perfect_mode */ + (1 << 6) | /* ref_ie_ul_sel */ + (1 << 5) | /* mb_type_use_ie_result */ + (1 << 4) | /* detect_I16_from_I4 */ + (1 << 3) | /* ie_not_wait_ref_busy */ + (1 << 2) | /* ie_I16_enable */ + (3 << 0)); /* ie_done_sel // fastest when waiting */ + + if (request != NULL) { + WRITE_HREG(HCODEC_IE_WEIGHT, + (request->i16_weight << 16) | + (request->i4_weight << 0)); + WRITE_HREG(HCODEC_ME_WEIGHT, + (request->me_weight << 0)); + WRITE_HREG(HCODEC_SAD_CONTROL_0, + /* ie_sad_offset_I16 */ + (request->i16_weight << 16) | + /* ie_sad_offset_I4 */ + (request->i4_weight << 0)); + WRITE_HREG(HCODEC_SAD_CONTROL_1, + /* ie_sad_shift_I16 */ + (IE_SAD_SHIFT_I16 << 24) | + /* ie_sad_shift_I4 */ + (IE_SAD_SHIFT_I4 << 20) | + /* me_sad_shift_INTER */ + (ME_SAD_SHIFT_INTER << 16) | + /* me_sad_offset_INTER */ + (request->me_weight << 0)); + wq->me_weight = request->me_weight; + wq->i4_weight = request->i4_weight; + wq->i16_weight = request->i16_weight; + } else { + WRITE_HREG(HCODEC_IE_WEIGHT, + (I16MB_WEIGHT_OFFSET << 16) | + (I4MB_WEIGHT_OFFSET << 0)); + WRITE_HREG(HCODEC_ME_WEIGHT, + (ME_WEIGHT_OFFSET << 0)); + WRITE_HREG(HCODEC_SAD_CONTROL_0, + /* ie_sad_offset_I16 */ + (I16MB_WEIGHT_OFFSET << 16) | + /* ie_sad_offset_I4 */ + (I4MB_WEIGHT_OFFSET << 0)); + WRITE_HREG(HCODEC_SAD_CONTROL_1, + /* ie_sad_shift_I16 */ + (IE_SAD_SHIFT_I16 << 24) | + /* ie_sad_shift_I4 */ + (IE_SAD_SHIFT_I4 << 20) | + /* me_sad_shift_INTER */ + (ME_SAD_SHIFT_INTER << 16) | + /* me_sad_offset_INTER */ + (ME_WEIGHT_OFFSET << 0)); + } + + WRITE_HREG(HCODEC_ADV_MV_CTL0, + (ADV_MV_LARGE_16x8 << 31) | + (ADV_MV_LARGE_8x16 << 30) | + (ADV_MV_8x8_WEIGHT << 16) | /* adv_mv_8x8_weight */ + /* adv_mv_4x4x4_weight should be set bigger */ + (ADV_MV_4x4x4_WEIGHT << 0)); + WRITE_HREG(HCODEC_ADV_MV_CTL1, + /* adv_mv_16x16_weight */ + (ADV_MV_16x16_WEIGHT << 16) | + (ADV_MV_LARGE_16x16 << 15) | + (ADV_MV_16_8_WEIGHT << 0)); /* adv_mv_16_8_weight */ + + hcodec_prog_qtbl(wq); + if (IDR) { + i_pic_qp = + wq->quant_tbl_i4[0] & 0xff; + i_pic_qp += + wq->quant_tbl_i16[0] & 0xff; + i_pic_qp /= 2; + p_pic_qp = i_pic_qp; + } else { + i_pic_qp = + wq->quant_tbl_i4[0] & 0xff; + i_pic_qp += + wq->quant_tbl_i16[0] & 0xff; + p_pic_qp = wq->quant_tbl_me[0] & 0xff; + slice_qp = (i_pic_qp + p_pic_qp) / 3; + i_pic_qp = slice_qp; + p_pic_qp = i_pic_qp; + } +#ifdef H264_ENC_CBR + if (get_cpu_type() >= MESON_CPU_MAJOR_ID_GXTVBB) { + data32 = READ_HREG(HCODEC_SAD_CONTROL_1); + data32 = data32 & 0xffff; /* remove sad shift */ + WRITE_HREG(HCODEC_SAD_CONTROL_1, data32); + WRITE_HREG(H264_ENC_CBR_TABLE_ADDR, + wq->mem.cbr_info_ddr_start_addr); + WRITE_HREG(H264_ENC_CBR_MB_SIZE_ADDR, + wq->mem.cbr_info_ddr_start_addr + + CBR_TABLE_SIZE); + WRITE_HREG(H264_ENC_CBR_CTL, + (wq->cbr_info.start_tbl_id << 28) | + (wq->cbr_info.short_shift << 24) | + (wq->cbr_info.long_mb_num << 16) | + (wq->cbr_info.long_th << 0)); + WRITE_HREG(H264_ENC_CBR_REGION_SIZE, + (wq->cbr_info.block_w << 16) | + (wq->cbr_info.block_h << 0)); + } +#endif + + WRITE_HREG(HCODEC_QDCT_VLC_QUANT_CTL_0, + (0 << 19) | /* vlc_delta_quant_1 */ + (i_pic_qp << 13) | /* vlc_quant_1 */ + (0 << 6) | /* vlc_delta_quant_0 */ + (i_pic_qp << 0)); /* vlc_quant_0 */ + WRITE_HREG(HCODEC_QDCT_VLC_QUANT_CTL_1, + (14 << 6) | /* vlc_max_delta_q_neg */ + (13 << 0)); /* vlc_max_delta_q_pos */ + WRITE_HREG(HCODEC_VLC_PIC_SIZE, + pic_width | (pic_height << 16)); + WRITE_HREG(HCODEC_VLC_PIC_POSITION, + (pic_mb_nr << 16) | + (pic_mby << 8) | + (pic_mbx << 0)); + + /* synopsys parallel_case full_case */ + switch (i_pic_qp) { + case 0: + i_pic_qp_c = 0; + break; + case 1: + i_pic_qp_c = 1; + break; + case 2: + i_pic_qp_c = 2; + break; + case 3: + i_pic_qp_c = 3; + break; + case 4: + i_pic_qp_c = 4; + break; + case 5: + i_pic_qp_c = 5; + break; + case 6: + i_pic_qp_c = 6; + break; + case 7: + i_pic_qp_c = 7; + break; + case 8: + i_pic_qp_c = 8; + break; + case 9: + i_pic_qp_c = 9; + break; + case 10: + i_pic_qp_c = 10; + break; + case 11: + i_pic_qp_c = 11; + break; + case 12: + i_pic_qp_c = 12; + break; + case 13: + i_pic_qp_c = 13; + break; + case 14: + i_pic_qp_c = 14; + break; + case 15: + i_pic_qp_c = 15; + break; + case 16: + i_pic_qp_c = 16; + break; + case 17: + i_pic_qp_c = 17; + break; + case 18: + i_pic_qp_c = 18; + break; + case 19: + i_pic_qp_c = 19; + break; + case 20: + i_pic_qp_c = 20; + break; + case 21: + i_pic_qp_c = 21; + break; + case 22: + i_pic_qp_c = 22; + break; + case 23: + i_pic_qp_c = 23; + break; + case 24: + i_pic_qp_c = 24; + break; + case 25: + i_pic_qp_c = 25; + break; + case 26: + i_pic_qp_c = 26; + break; + case 27: + i_pic_qp_c = 27; + break; + case 28: + i_pic_qp_c = 28; + break; + case 29: + i_pic_qp_c = 29; + break; + case 30: + i_pic_qp_c = 29; + break; + case 31: + i_pic_qp_c = 30; + break; + case 32: + i_pic_qp_c = 31; + break; + case 33: + i_pic_qp_c = 32; + break; + case 34: + i_pic_qp_c = 32; + break; + case 35: + i_pic_qp_c = 33; + break; + case 36: + i_pic_qp_c = 34; + break; + case 37: + i_pic_qp_c = 34; + break; + case 38: + i_pic_qp_c = 35; + break; + case 39: + i_pic_qp_c = 35; + break; + case 40: + i_pic_qp_c = 36; + break; + case 41: + i_pic_qp_c = 36; + break; + case 42: + i_pic_qp_c = 37; + break; + case 43: + i_pic_qp_c = 37; + break; + case 44: + i_pic_qp_c = 37; + break; + case 45: + i_pic_qp_c = 38; + break; + case 46: + i_pic_qp_c = 38; + break; + case 47: + i_pic_qp_c = 38; + break; + case 48: + i_pic_qp_c = 39; + break; + case 49: + i_pic_qp_c = 39; + break; + case 50: + i_pic_qp_c = 39; + break; + default: + i_pic_qp_c = 39; + break; + } + + /* synopsys parallel_case full_case */ + switch (p_pic_qp) { + case 0: + p_pic_qp_c = 0; + break; + case 1: + p_pic_qp_c = 1; + break; + case 2: + p_pic_qp_c = 2; + break; + case 3: + p_pic_qp_c = 3; + break; + case 4: + p_pic_qp_c = 4; + break; + case 5: + p_pic_qp_c = 5; + break; + case 6: + p_pic_qp_c = 6; + break; + case 7: + p_pic_qp_c = 7; + break; + case 8: + p_pic_qp_c = 8; + break; + case 9: + p_pic_qp_c = 9; + break; + case 10: + p_pic_qp_c = 10; + break; + case 11: + p_pic_qp_c = 11; + break; + case 12: + p_pic_qp_c = 12; + break; + case 13: + p_pic_qp_c = 13; + break; + case 14: + p_pic_qp_c = 14; + break; + case 15: + p_pic_qp_c = 15; + break; + case 16: + p_pic_qp_c = 16; + break; + case 17: + p_pic_qp_c = 17; + break; + case 18: + p_pic_qp_c = 18; + break; + case 19: + p_pic_qp_c = 19; + break; + case 20: + p_pic_qp_c = 20; + break; + case 21: + p_pic_qp_c = 21; + break; + case 22: + p_pic_qp_c = 22; + break; + case 23: + p_pic_qp_c = 23; + break; + case 24: + p_pic_qp_c = 24; + break; + case 25: + p_pic_qp_c = 25; + break; + case 26: + p_pic_qp_c = 26; + break; + case 27: + p_pic_qp_c = 27; + break; + case 28: + p_pic_qp_c = 28; + break; + case 29: + p_pic_qp_c = 29; + break; + case 30: + p_pic_qp_c = 29; + break; + case 31: + p_pic_qp_c = 30; + break; + case 32: + p_pic_qp_c = 31; + break; + case 33: + p_pic_qp_c = 32; + break; + case 34: + p_pic_qp_c = 32; + break; + case 35: + p_pic_qp_c = 33; + break; + case 36: + p_pic_qp_c = 34; + break; + case 37: + p_pic_qp_c = 34; + break; + case 38: + p_pic_qp_c = 35; + break; + case 39: + p_pic_qp_c = 35; + break; + case 40: + p_pic_qp_c = 36; + break; + case 41: + p_pic_qp_c = 36; + break; + case 42: + p_pic_qp_c = 37; + break; + case 43: + p_pic_qp_c = 37; + break; + case 44: + p_pic_qp_c = 37; + break; + case 45: + p_pic_qp_c = 38; + break; + case 46: + p_pic_qp_c = 38; + break; + case 47: + p_pic_qp_c = 38; + break; + case 48: + p_pic_qp_c = 39; + break; + case 49: + p_pic_qp_c = 39; + break; + case 50: + p_pic_qp_c = 39; + break; + default: + p_pic_qp_c = 39; + break; + } + WRITE_HREG(HCODEC_QDCT_Q_QUANT_I, + (i_pic_qp_c << 22) | + (i_pic_qp << 16) | + ((i_pic_qp_c % 6) << 12) | + ((i_pic_qp_c / 6) << 8) | + ((i_pic_qp % 6) << 4) | + ((i_pic_qp / 6) << 0)); + + WRITE_HREG(HCODEC_QDCT_Q_QUANT_P, + (p_pic_qp_c << 22) | + (p_pic_qp << 16) | + ((p_pic_qp_c % 6) << 12) | + ((p_pic_qp_c / 6) << 8) | + ((p_pic_qp % 6) << 4) | + ((p_pic_qp / 6) << 0)); + +#ifdef ENABLE_IGNORE_FUNCTION + WRITE_HREG(HCODEC_IGNORE_CONFIG, + (1 << 31) | /* ignore_lac_coeff_en */ + (1 << 26) | /* ignore_lac_coeff_else (<1) */ + (1 << 21) | /* ignore_lac_coeff_2 (<1) */ + (2 << 16) | /* ignore_lac_coeff_1 (<2) */ + (1 << 15) | /* ignore_cac_coeff_en */ + (1 << 10) | /* ignore_cac_coeff_else (<1) */ + (1 << 5) | /* ignore_cac_coeff_2 (<1) */ + (3 << 0)); /* ignore_cac_coeff_1 (<2) */ + + if (get_cpu_type() >= MESON_CPU_MAJOR_ID_GXTVBB) + WRITE_HREG(HCODEC_IGNORE_CONFIG_2, + (1 << 31) | /* ignore_t_lac_coeff_en */ + (1 << 26) | /* ignore_t_lac_coeff_else (<1) */ + (2 << 21) | /* ignore_t_lac_coeff_2 (<2) */ + (6 << 16) | /* ignore_t_lac_coeff_1 (<6) */ + (1<<15) | /* ignore_cdc_coeff_en */ + (0<<14) | /* ignore_t_lac_coeff_else_le_3 */ + (1<<13) | /* ignore_t_lac_coeff_else_le_4 */ + (1<<12) | /* ignore_cdc_only_when_empty_cac_inter */ + (1<<11) | /* ignore_cdc_only_when_one_empty_inter */ + /* ignore_cdc_range_max_inter 0-0, 1-1, 2-2, 3-3 */ + (2<<9) | + /* ignore_cdc_abs_max_inter 0-1, 1-2, 2-3, 3-4 */ + (0<<7) | + /* ignore_cdc_only_when_empty_cac_intra */ + (1<<5) | + /* ignore_cdc_only_when_one_empty_intra */ + (1<<4) | + /* ignore_cdc_range_max_intra 0-0, 1-1, 2-2, 3-3 */ + (1<<2) | + /* ignore_cdc_abs_max_intra 0-1, 1-2, 2-3, 3-4 */ + (0<<0)); + else + WRITE_HREG(HCODEC_IGNORE_CONFIG_2, + (1 << 31) | /* ignore_t_lac_coeff_en */ + (1 << 26) | /* ignore_t_lac_coeff_else (<1) */ + (1 << 21) | /* ignore_t_lac_coeff_2 (<1) */ + (5 << 16) | /* ignore_t_lac_coeff_1 (<5) */ + (0 << 0)); +#else + WRITE_HREG(HCODEC_IGNORE_CONFIG, 0); + WRITE_HREG(HCODEC_IGNORE_CONFIG_2, 0); +#endif + + WRITE_HREG(HCODEC_QDCT_MB_CONTROL, + (1 << 9) | /* mb_info_soft_reset */ + (1 << 0)); /* mb read buffer soft reset */ + + WRITE_HREG(HCODEC_QDCT_MB_CONTROL, + (1 << 28) | /* ignore_t_p8x8 */ + (0 << 27) | /* zero_mc_out_null_non_skipped_mb */ + (0 << 26) | /* no_mc_out_null_non_skipped_mb */ + (0 << 25) | /* mc_out_even_skipped_mb */ + (0 << 24) | /* mc_out_wait_cbp_ready */ + (0 << 23) | /* mc_out_wait_mb_type_ready */ + (1 << 29) | /* ie_start_int_enable */ + (1 << 19) | /* i_pred_enable */ + (1 << 20) | /* ie_sub_enable */ + (1 << 18) | /* iq_enable */ + (1 << 17) | /* idct_enable */ + (1 << 14) | /* mb_pause_enable */ + (1 << 13) | /* q_enable */ + (1 << 12) | /* dct_enable */ + (1 << 10) | /* mb_info_en */ + (0 << 3) | /* endian */ + (0 << 1) | /* mb_read_en */ + (0 << 0)); /* soft reset */ + + WRITE_HREG(HCODEC_SAD_CONTROL, + (0 << 3) | /* ie_result_buff_enable */ + (1 << 2) | /* ie_result_buff_soft_reset */ + (0 << 1) | /* sad_enable */ + (1 << 0)); /* sad soft reset */ + WRITE_HREG(HCODEC_IE_RESULT_BUFFER, 0); + + WRITE_HREG(HCODEC_SAD_CONTROL, + (1 << 3) | /* ie_result_buff_enable */ + (0 << 2) | /* ie_result_buff_soft_reset */ + (1 << 1) | /* sad_enable */ + (0 << 0)); /* sad soft reset */ + + WRITE_HREG(HCODEC_IE_CONTROL, + (1 << 30) | /* active_ul_block */ + (0 << 1) | /* ie_enable */ + (1 << 0)); /* ie soft reset */ + + WRITE_HREG(HCODEC_IE_CONTROL, + (1 << 30) | /* active_ul_block */ + (0 << 1) | /* ie_enable */ + (0 << 0)); /* ie soft reset */ + + WRITE_HREG(HCODEC_ME_SKIP_LINE, + (8 << 24) | /* step_3_skip_line */ + (8 << 18) | /* step_2_skip_line */ + (2 << 12) | /* step_1_skip_line */ + (0 << 6) | /* step_0_skip_line */ + (0 << 0)); + + WRITE_HREG(HCODEC_ME_MV_MERGE_CTL, me_mv_merge_ctl); + WRITE_HREG(HCODEC_ME_STEP0_CLOSE_MV, me_step0_close_mv); + WRITE_HREG(HCODEC_ME_SAD_ENOUGH_01, me_sad_enough_01); + WRITE_HREG(HCODEC_ME_SAD_ENOUGH_23, me_sad_enough_23); + WRITE_HREG(HCODEC_ME_F_SKIP_SAD, me_f_skip_sad); + WRITE_HREG(HCODEC_ME_F_SKIP_WEIGHT, me_f_skip_weight); + WRITE_HREG(HCODEC_ME_MV_WEIGHT_01, me_mv_weight_01); + WRITE_HREG(HCODEC_ME_MV_WEIGHT_23, me_mv_weight_23); + WRITE_HREG(HCODEC_ME_SAD_RANGE_INC, me_sad_range_inc); + + if (get_cpu_type() >= MESON_CPU_MAJOR_ID_TXL) { + WRITE_HREG(HCODEC_V5_SIMPLE_MB_CTL, 0); + WRITE_HREG(HCODEC_V5_SIMPLE_MB_CTL, + (v5_use_small_diff_cnt << 7) | + (v5_simple_mb_inter_all_en << 6) | + (v5_simple_mb_inter_8x8_en << 5) | + (v5_simple_mb_inter_16_8_en << 4) | + (v5_simple_mb_inter_16x16_en << 3) | + (v5_simple_mb_intra_en << 2) | + (v5_simple_mb_C_en << 1) | + (v5_simple_mb_Y_en << 0)); + WRITE_HREG(HCODEC_V5_MB_DIFF_SUM, 0); + WRITE_HREG(HCODEC_V5_SMALL_DIFF_CNT, + (v5_small_diff_C<<16) | + (v5_small_diff_Y<<0)); + WRITE_HREG(HCODEC_V5_SIMPLE_MB_DQUANT, + v5_simple_dq_setting); + WRITE_HREG(HCODEC_V5_SIMPLE_MB_ME_WEIGHT, + v5_simple_me_weight_setting); + /* txlx can remove it */ + WRITE_HREG(HCODEC_QDCT_CONFIG, 1 << 0); + } + + if (get_cpu_type() >= MESON_CPU_MAJOR_ID_GXL) { + WRITE_HREG(HCODEC_V4_FORCE_SKIP_CFG, + (i_pic_qp << 26) | /* v4_force_q_r_intra */ + (i_pic_qp << 20) | /* v4_force_q_r_inter */ + (0 << 19) | /* v4_force_q_y_enable */ + (5 << 16) | /* v4_force_qr_y */ + (6 << 12) | /* v4_force_qp_y */ + (0 << 0)); /* v4_force_skip_sad */ + + /* V3 Force skip */ + WRITE_HREG(HCODEC_V3_SKIP_CONTROL, + (1 << 31) | /* v3_skip_enable */ + (0 << 30) | /* v3_step_1_weight_enable */ + (1 << 28) | /* v3_mv_sad_weight_enable */ + (1 << 27) | /* v3_ipred_type_enable */ + (V3_FORCE_SKIP_SAD_1 << 12) | + (V3_FORCE_SKIP_SAD_0 << 0)); + WRITE_HREG(HCODEC_V3_SKIP_WEIGHT, + (V3_SKIP_WEIGHT_1 << 16) | + (V3_SKIP_WEIGHT_0 << 0)); + WRITE_HREG(HCODEC_V3_L1_SKIP_MAX_SAD, + (V3_LEVEL_1_F_SKIP_MAX_SAD << 16) | + (V3_LEVEL_1_SKIP_MAX_SAD << 0)); + WRITE_HREG(HCODEC_V3_L2_SKIP_WEIGHT, + (V3_FORCE_SKIP_SAD_2 << 16) | + (V3_SKIP_WEIGHT_2 << 0)); + if (request != NULL) { + unsigned int off1, off2; + off1 = V3_IE_F_ZERO_SAD_I4 - I4MB_WEIGHT_OFFSET; + off2 = V3_IE_F_ZERO_SAD_I16 + - I16MB_WEIGHT_OFFSET; + WRITE_HREG(HCODEC_V3_F_ZERO_CTL_0, + ((request->i16_weight + off2) << 16) | + ((request->i4_weight + off1) << 0)); + off1 = V3_ME_F_ZERO_SAD - ME_WEIGHT_OFFSET; + WRITE_HREG(HCODEC_V3_F_ZERO_CTL_1, + (0 << 25) | + /* v3_no_ver_when_top_zero_en */ + (0 << 24) | + /* v3_no_hor_when_left_zero_en */ + (3 << 16) | /* type_hor break */ + ((request->me_weight + off1) << 0)); + } else { + WRITE_HREG(HCODEC_V3_F_ZERO_CTL_0, + (V3_IE_F_ZERO_SAD_I16 << 16) | + (V3_IE_F_ZERO_SAD_I4 << 0)); + WRITE_HREG(HCODEC_V3_F_ZERO_CTL_1, + (0 << 25) | + /* v3_no_ver_when_top_zero_en */ + (0 << 24) | + /* v3_no_hor_when_left_zero_en */ + (3 << 16) | /* type_hor break */ + (V3_ME_F_ZERO_SAD << 0)); + } + } else if (get_cpu_type() >= MESON_CPU_MAJOR_ID_GXTVBB) { + /* V3 Force skip */ + WRITE_HREG(HCODEC_V3_SKIP_CONTROL, + (1 << 31) | /* v3_skip_enable */ + (0 << 30) | /* v3_step_1_weight_enable */ + (1 << 28) | /* v3_mv_sad_weight_enable */ + (1 << 27) | /* v3_ipred_type_enable */ + (0 << 12) | /* V3_FORCE_SKIP_SAD_1 */ + (0 << 0)); /* V3_FORCE_SKIP_SAD_0 */ + WRITE_HREG(HCODEC_V3_SKIP_WEIGHT, + (V3_SKIP_WEIGHT_1 << 16) | + (V3_SKIP_WEIGHT_0 << 0)); + WRITE_HREG(HCODEC_V3_L1_SKIP_MAX_SAD, + (V3_LEVEL_1_F_SKIP_MAX_SAD << 16) | + (V3_LEVEL_1_SKIP_MAX_SAD << 0)); + WRITE_HREG(HCODEC_V3_L2_SKIP_WEIGHT, + (0 << 16) | /* V3_FORCE_SKIP_SAD_2 */ + (V3_SKIP_WEIGHT_2 << 0)); + WRITE_HREG(HCODEC_V3_F_ZERO_CTL_0, + (0 << 16) | /* V3_IE_F_ZERO_SAD_I16 */ + (0 << 0)); /* V3_IE_F_ZERO_SAD_I4 */ + WRITE_HREG(HCODEC_V3_F_ZERO_CTL_1, + (0 << 25) | /* v3_no_ver_when_top_zero_en */ + (0 << 24) | /* v3_no_hor_when_left_zero_en */ + (3 << 16) | /* type_hor break */ + (0 << 0)); /* V3_ME_F_ZERO_SAD */ + } + if (get_cpu_type() >= MESON_CPU_MAJOR_ID_GXTVBB) { + int i; + /* MV SAD Table */ + for (i = 0; i < 64; i++) + WRITE_HREG(HCODEC_V3_MV_SAD_TABLE, + v3_mv_sad[i]); + + /* IE PRED SAD Table*/ + WRITE_HREG(HCODEC_V3_IPRED_TYPE_WEIGHT_0, + (C_ipred_weight_H << 24) | + (C_ipred_weight_V << 16) | + (I4_ipred_weight_else << 8) | + (I4_ipred_weight_most << 0)); + WRITE_HREG(HCODEC_V3_IPRED_TYPE_WEIGHT_1, + (I16_ipred_weight_DC << 24) | + (I16_ipred_weight_H << 16) | + (I16_ipred_weight_V << 8) | + (C_ipred_weight_DC << 0)); + WRITE_HREG(HCODEC_V3_LEFT_SMALL_MAX_SAD, + (v3_left_small_max_me_sad << 16) | + (v3_left_small_max_ie_sad << 0)); + } + WRITE_HREG(HCODEC_IE_DATA_FEED_BUFF_INFO, 0); + + WRITE_HREG(HCODEC_CURR_CANVAS_CTRL, 0); + data32 = READ_HREG(HCODEC_VLC_CONFIG); + data32 = data32 | (1 << 0); /* set pop_coeff_even_all_zero */ + WRITE_HREG(HCODEC_VLC_CONFIG, data32); + + WRITE_HREG(INFO_DUMP_START_ADDR, + wq->mem.dump_info_ddr_start_addr); + + /* clear mailbox interrupt */ + WRITE_HREG(HCODEC_IRQ_MBOX_CLR, 1); + + /* enable mailbox interrupt */ + WRITE_HREG(HCODEC_IRQ_MBOX_MASK, 1); +} + +void amvenc_reset(void) +{ + READ_VREG(DOS_SW_RESET1); + READ_VREG(DOS_SW_RESET1); + READ_VREG(DOS_SW_RESET1); + WRITE_VREG(DOS_SW_RESET1, + (1 << 2) | (1 << 6) | + (1 << 7) | (1 << 8) | + (1 << 14) | (1 << 16) | + (1 << 17)); + WRITE_VREG(DOS_SW_RESET1, 0); + READ_VREG(DOS_SW_RESET1); + READ_VREG(DOS_SW_RESET1); + READ_VREG(DOS_SW_RESET1); +} + +void amvenc_start(void) +{ + READ_VREG(DOS_SW_RESET1); + READ_VREG(DOS_SW_RESET1); + READ_VREG(DOS_SW_RESET1); + WRITE_VREG(DOS_SW_RESET1, + (1 << 12) | (1 << 11)); + WRITE_VREG(DOS_SW_RESET1, 0); + + READ_VREG(DOS_SW_RESET1); + READ_VREG(DOS_SW_RESET1); + READ_VREG(DOS_SW_RESET1); + + WRITE_HREG(HCODEC_MPSR, 0x0001); +} + +void amvenc_stop(void) +{ + ulong timeout = jiffies + HZ; + + WRITE_HREG(HCODEC_MPSR, 0); + WRITE_HREG(HCODEC_CPSR, 0); + while (READ_HREG(HCODEC_IMEM_DMA_CTRL) & 0x8000) { + if (time_after(jiffies, timeout)) + break; + } + READ_VREG(DOS_SW_RESET1); + READ_VREG(DOS_SW_RESET1); + READ_VREG(DOS_SW_RESET1); + + WRITE_VREG(DOS_SW_RESET1, + (1 << 12) | (1 << 11) | + (1 << 2) | (1 << 6) | + (1 << 7) | (1 << 8) | + (1 << 14) | (1 << 16) | + (1 << 17)); + + WRITE_VREG(DOS_SW_RESET1, 0); + + READ_VREG(DOS_SW_RESET1); + READ_VREG(DOS_SW_RESET1); + READ_VREG(DOS_SW_RESET1); +} + +static void __iomem *mc_addr; +static u32 mc_addr_map; +#define MC_SIZE (4096 * 8) +s32 amvenc_loadmc(const char *p, struct encode_wq_s *wq) +{ + ulong timeout; + s32 ret = 0; + + /* use static mempry*/ + if (mc_addr == NULL) { + mc_addr = kmalloc(MC_SIZE, GFP_KERNEL); + if (!mc_addr) { + enc_pr(LOG_ERROR, "avc loadmc iomap mc addr error.\n"); + return -ENOMEM; + } + } + + enc_pr(LOG_ALL, "avc encode ucode name is %s\n", p); + ret = get_decoder_firmware_data(VFORMAT_H264_ENC, p, + (u8 *)mc_addr, MC_SIZE); + if (ret < 0) { + enc_pr(LOG_ERROR, + "avc microcode fail ret=%d, name: %s, wq:%p.\n", + ret, p, (void *)wq); + } + + mc_addr_map = dma_map_single( + &encode_manager.this_pdev->dev, + mc_addr, MC_SIZE, DMA_TO_DEVICE); + + /* mc_addr_map = wq->mem.assit_buffer_offset; */ + /* mc_addr = ioremap_wc(mc_addr_map, MC_SIZE); */ + /* memcpy(mc_addr, p, MC_SIZE); */ + enc_pr(LOG_ALL, "address 0 is 0x%x\n", *((u32 *)mc_addr)); + enc_pr(LOG_ALL, "address 1 is 0x%x\n", *((u32 *)mc_addr + 1)); + enc_pr(LOG_ALL, "address 2 is 0x%x\n", *((u32 *)mc_addr + 2)); + enc_pr(LOG_ALL, "address 3 is 0x%x\n", *((u32 *)mc_addr + 3)); + WRITE_HREG(HCODEC_MPSR, 0); + WRITE_HREG(HCODEC_CPSR, 0); + + /* Read CBUS register for timing */ + timeout = READ_HREG(HCODEC_MPSR); + timeout = READ_HREG(HCODEC_MPSR); + + timeout = jiffies + HZ; + + WRITE_HREG(HCODEC_IMEM_DMA_ADR, mc_addr_map); + WRITE_HREG(HCODEC_IMEM_DMA_COUNT, 0x1000); + WRITE_HREG(HCODEC_IMEM_DMA_CTRL, (0x8000 | (7 << 16))); + + while (READ_HREG(HCODEC_IMEM_DMA_CTRL) & 0x8000) { + if (time_before(jiffies, timeout)) + schedule(); + else { + enc_pr(LOG_ERROR, "hcodec load mc error\n"); + ret = -EBUSY; + break; + } + } + dma_unmap_single( + &encode_manager.this_pdev->dev, + mc_addr_map, MC_SIZE, DMA_TO_DEVICE); + return ret; +} + +const u32 fix_mc[] __aligned(8) = { + 0x0809c05a, 0x06696000, 0x0c780000, 0x00000000 +}; + + +/* + * DOS top level register access fix. + * When hcodec is running, a protocol register HCODEC_CCPU_INTR_MSK + * is set to make hcodec access one CBUS out of DOS domain once + * to work around a HW bug for 4k2k dual decoder implementation. + * If hcodec is not running, then a ucode is loaded and executed + * instead. + */ +void amvenc_dos_top_reg_fix(void) +{ + bool hcodec_on; + ulong flags; + + spin_lock_irqsave(&lock, flags); + + hcodec_on = vdec_on(VDEC_HCODEC); + + if ((hcodec_on) && (READ_VREG(HCODEC_MPSR) & 1)) { + WRITE_HREG(HCODEC_CCPU_INTR_MSK, 1); + spin_unlock_irqrestore(&lock, flags); + return; + } + + if (!hcodec_on) + vdec_poweron(VDEC_HCODEC); + + amhcodec_loadmc(fix_mc); + + amhcodec_start(); + + udelay(1000); + + amhcodec_stop(); + + if (!hcodec_on) + vdec_poweroff(VDEC_HCODEC); + + spin_unlock_irqrestore(&lock, flags); +} + +bool amvenc_avc_on(void) +{ + bool hcodec_on; + ulong flags; + + spin_lock_irqsave(&lock, flags); + + hcodec_on = vdec_on(VDEC_HCODEC); + hcodec_on &= (encode_manager.wq_count > 0); + + spin_unlock_irqrestore(&lock, flags); + return hcodec_on; +} + +static s32 avc_poweron(u32 clock) +{ + ulong flags; + u32 data32; + + data32 = 0; + + amports_switch_gate("vdec", 1); + + spin_lock_irqsave(&lock, flags); + + WRITE_AOREG(AO_RTI_PWR_CNTL_REG0, + (READ_AOREG(AO_RTI_PWR_CNTL_REG0) & (~0x18))); + udelay(10); + /* Powerup HCODEC */ + /* [1:0] HCODEC */ + WRITE_AOREG(AO_RTI_GEN_PWR_SLEEP0, + (READ_AOREG(AO_RTI_GEN_PWR_SLEEP0) & (~0x3))); + udelay(10); + + WRITE_VREG(DOS_SW_RESET1, 0xffffffff); + WRITE_VREG(DOS_SW_RESET1, 0); + + /* Enable Dos internal clock gating */ + hvdec_clock_enable(clock); + + /* Powerup HCODEC memories */ + WRITE_VREG(DOS_MEM_PD_HCODEC, 0x0); + + /* Remove HCODEC ISO */ + WRITE_AOREG(AO_RTI_GEN_PWR_ISO0, + (READ_AOREG(AO_RTI_GEN_PWR_ISO0) & (~0x30))); + udelay(10); + /* Disable auto-clock gate */ + WRITE_VREG(DOS_GEN_CTRL0, + (READ_VREG(DOS_GEN_CTRL0) | 0x1)); + WRITE_VREG(DOS_GEN_CTRL0, + (READ_VREG(DOS_GEN_CTRL0) & 0xFFFFFFFE)); + + spin_unlock_irqrestore(&lock, flags); + + mdelay(10); + return 0; +} + +static s32 avc_poweroff(void) +{ + ulong flags; + + spin_lock_irqsave(&lock, flags); + + /* 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 */ + hvdec_clock_disable(); + + /* HCODEC power off */ + WRITE_AOREG(AO_RTI_GEN_PWR_SLEEP0, + READ_AOREG(AO_RTI_GEN_PWR_SLEEP0) | 0x3); + + spin_unlock_irqrestore(&lock, flags); + + /* release DOS clk81 clock gating */ + amports_switch_gate("vdec", 0); + return 0; +} + +static s32 reload_mc(struct encode_wq_s *wq) +{ + const char *p = select_ucode(encode_manager.ucode_index); + + amvenc_stop(); + + WRITE_VREG(DOS_SW_RESET1, 0xffffffff); + WRITE_VREG(DOS_SW_RESET1, 0); + + udelay(10); + + WRITE_HREG(HCODEC_ASSIST_MMC_CTRL1, 0x32); + enc_pr(LOG_INFO, "reload microcode\n"); + + if (amvenc_loadmc(p, wq) < 0) + return -EBUSY; + return 0; +} + +static void encode_isr_tasklet(ulong data) +{ + struct encode_manager_s *manager = (struct encode_manager_s *)data; + enc_pr(LOG_INFO, "encoder is done %d\n", manager->encode_hw_status); + if (((manager->encode_hw_status == ENCODER_IDR_DONE) + || (manager->encode_hw_status == ENCODER_NON_IDR_DONE) + || (manager->encode_hw_status == ENCODER_SEQUENCE_DONE) + || (manager->encode_hw_status == ENCODER_PICTURE_DONE)) + && (manager->process_irq)) { + wake_up_interruptible(&manager->event.hw_complete); + } +} + +/* irq function */ +static irqreturn_t enc_isr(s32 irq_number, void *para) +{ + struct encode_manager_s *manager = (struct encode_manager_s *)para; + WRITE_HREG(HCODEC_IRQ_MBOX_CLR, 1); + + manager->encode_hw_status = READ_HREG(ENCODER_STATUS); + if ((manager->encode_hw_status == ENCODER_IDR_DONE) + || (manager->encode_hw_status == ENCODER_NON_IDR_DONE) + || (manager->encode_hw_status == ENCODER_SEQUENCE_DONE) + || (manager->encode_hw_status == ENCODER_PICTURE_DONE)) { + enc_pr(LOG_ALL, "encoder stage is %d\n", + manager->encode_hw_status); + } + + if (((manager->encode_hw_status == ENCODER_IDR_DONE) + || (manager->encode_hw_status == ENCODER_NON_IDR_DONE) + || (manager->encode_hw_status == ENCODER_SEQUENCE_DONE) + || (manager->encode_hw_status == ENCODER_PICTURE_DONE)) + && (!manager->process_irq)) { + manager->process_irq = true; + if (manager->encode_hw_status != ENCODER_SEQUENCE_DONE) + manager->need_reset = true; + tasklet_schedule(&manager->encode_tasklet); + } + return IRQ_HANDLED; +} + +static s32 convert_request(struct encode_wq_s *wq, u32 *cmd_info) +{ + int i = 0; + u8 *ptr; + u32 data_offset; + u32 cmd = cmd_info[0]; + if (!wq) + return -1; + memset(&wq->request, 0, sizeof(struct encode_request_s)); + wq->request.me_weight = ME_WEIGHT_OFFSET; + wq->request.i4_weight = I4MB_WEIGHT_OFFSET; + wq->request.i16_weight = I16MB_WEIGHT_OFFSET; + + if (cmd == ENCODER_SEQUENCE) { + wq->request.cmd = cmd; + wq->request.ucode_mode = cmd_info[1]; + wq->request.quant = cmd_info[2]; + wq->request.flush_flag = cmd_info[3]; + wq->request.timeout = cmd_info[4]; + wq->request.timeout = 5000; /* 5000 ms */ + } else if ((cmd == ENCODER_IDR) || (cmd == ENCODER_NON_IDR)) { + wq->request.cmd = cmd; + wq->request.ucode_mode = cmd_info[1]; + wq->request.type = cmd_info[2]; + wq->request.fmt = cmd_info[3]; + wq->request.src = cmd_info[4]; + wq->request.framesize = cmd_info[5]; + wq->request.quant = cmd_info[6]; + wq->request.flush_flag = cmd_info[7]; + wq->request.timeout = cmd_info[8]; + wq->request.crop_top = cmd_info[9]; + wq->request.crop_bottom = cmd_info[10]; + wq->request.crop_left = cmd_info[11]; + wq->request.crop_right = cmd_info[12]; + wq->request.src_w = cmd_info[13]; + wq->request.src_h = cmd_info[14]; + wq->request.scale_enable = cmd_info[15]; + wq->request.nr_mode = + (nr_mode > 0) ? nr_mode : cmd_info[16]; + if (cmd == ENCODER_IDR) + wq->request.nr_mode = 0; + + data_offset = 17 + + (sizeof(wq->quant_tbl_i4) + + sizeof(wq->quant_tbl_i16) + + sizeof(wq->quant_tbl_me)) / 4; + + if (wq->request.quant == ADJUSTED_QP_FLAG) { + ptr = (u8 *) &cmd_info[17]; + memcpy(wq->quant_tbl_i4, ptr, + sizeof(wq->quant_tbl_i4)); + ptr += sizeof(wq->quant_tbl_i4); + memcpy(wq->quant_tbl_i16, ptr, + sizeof(wq->quant_tbl_i16)); + ptr += sizeof(wq->quant_tbl_i16); + memcpy(wq->quant_tbl_me, ptr, + sizeof(wq->quant_tbl_me)); + wq->request.i4_weight -= + cmd_info[data_offset++]; + wq->request.i16_weight -= + cmd_info[data_offset++]; + wq->request.me_weight -= + cmd_info[data_offset++]; + if (qp_table_debug) { + u8 *qp_tb = (u8 *)(&wq->quant_tbl_i4[0]); + for (i = 0; i < 32; i++) { + enc_pr(LOG_INFO, "%d ", *qp_tb); + qp_tb++; + } + enc_pr(LOG_INFO, "\n"); + + qp_tb = (u8 *)(&wq->quant_tbl_i16[0]); + for (i = 0; i < 32; i++) { + enc_pr(LOG_INFO, "%d ", *qp_tb); + qp_tb++; + } + enc_pr(LOG_INFO, "\n"); + + qp_tb = (u8 *)(&wq->quant_tbl_me[0]); + for (i = 0; i < 32; i++) { + enc_pr(LOG_INFO, "%d ", *qp_tb); + qp_tb++; + } + enc_pr(LOG_INFO, "\n"); + } + } else { + memset(wq->quant_tbl_me, wq->request.quant, + sizeof(wq->quant_tbl_me)); + memset(wq->quant_tbl_i4, wq->request.quant, + sizeof(wq->quant_tbl_i4)); + memset(wq->quant_tbl_i16, wq->request.quant, + sizeof(wq->quant_tbl_i16)); + data_offset += 3; + } +#ifdef H264_ENC_CBR + wq->cbr_info.block_w = cmd_info[data_offset++]; + wq->cbr_info.block_h = cmd_info[data_offset++]; + wq->cbr_info.long_th = cmd_info[data_offset++]; + wq->cbr_info.start_tbl_id = cmd_info[data_offset++]; + wq->cbr_info.short_shift = CBR_SHORT_SHIFT; + wq->cbr_info.long_mb_num = CBR_LONG_MB_NUM; +#endif + } else { + enc_pr(LOG_ERROR, "error cmd = %d, wq: %p.\n", + cmd, (void *)wq); + return -1; + } + wq->request.parent = wq; + return 0; +} + +void amvenc_avc_start_cmd(struct encode_wq_s *wq, + struct encode_request_s *request) +{ + u32 reload_flag = 0; + if (request->ucode_mode != encode_manager.ucode_index) { + encode_manager.ucode_index = request->ucode_mode; + if (reload_mc(wq)) { + enc_pr(LOG_ERROR, + "reload mc fail, wq:%p\n", (void *)wq); + return; + } + reload_flag = 1; + encode_manager.need_reset = true; + } + + wq->hw_status = 0; + wq->output_size = 0; + wq->ucode_index = encode_manager.ucode_index; + + ie_me_mode = (0 & ME_PIXEL_MODE_MASK) << ME_PIXEL_MODE_SHIFT; + if (encode_manager.need_reset) { + encode_manager.need_reset = false; + encode_manager.encode_hw_status = ENCODER_IDLE; + amvenc_reset(); + avc_canvas_init(wq); + avc_init_encoder(wq, + (request->cmd == ENCODER_IDR) ? true : false); + avc_init_input_buffer(wq); + avc_init_output_buffer(wq); + avc_prot_init(wq, request, request->quant, + (request->cmd == ENCODER_IDR) ? true : false); + avc_init_assit_buffer(wq); + enc_pr(LOG_INFO, + "begin to new frame, request->cmd: %d, ucode mode: %d, wq:%p.\n", + request->cmd, request->ucode_mode, (void *)wq); + } + if ((request->cmd == ENCODER_IDR) || + (request->cmd == ENCODER_NON_IDR)) { + avc_init_dblk_buffer(wq->mem.dblk_buf_canvas); + avc_init_reference_buffer(wq->mem.ref_buf_canvas); + } + if ((request->cmd == ENCODER_IDR) || + (request->cmd == ENCODER_NON_IDR)) + set_input_format(wq, request); + if (request->cmd == ENCODER_IDR) + ie_me_mb_type = HENC_MB_Type_I4MB; + else if (request->cmd == ENCODER_NON_IDR) + ie_me_mb_type = + (HENC_SKIP_RUN_AUTO << 16) | + (HENC_MB_Type_AUTO << 4) | + (HENC_MB_Type_AUTO << 0); + else + ie_me_mb_type = 0; + avc_init_ie_me_parameter(wq, request->quant); + +#ifdef MULTI_SLICE_MC + if (fixed_slice_cfg) + WRITE_HREG(FIXED_SLICE_CFG, fixed_slice_cfg); + else if (wq->pic.rows_per_slice != + (wq->pic.encoder_height + 15) >> 4) { + u32 mb_per_slice = (wq->pic.encoder_height + 15) >> 4; + mb_per_slice = mb_per_slice * wq->pic.rows_per_slice; + WRITE_HREG(FIXED_SLICE_CFG, mb_per_slice); + } else + WRITE_HREG(FIXED_SLICE_CFG, 0); +#else + WRITE_HREG(FIXED_SLICE_CFG, 0); +#endif + + encode_manager.encode_hw_status = request->cmd; + wq->hw_status = request->cmd; + WRITE_HREG(ENCODER_STATUS , request->cmd); + if ((request->cmd == ENCODER_IDR) + || (request->cmd == ENCODER_NON_IDR) + || (request->cmd == ENCODER_SEQUENCE) + || (request->cmd == ENCODER_PICTURE)) + encode_manager.process_irq = false; + + if (reload_flag) + amvenc_start(); + enc_pr(LOG_ALL, "amvenc_avc_start cmd, wq:%p.\n", (void *)wq); +} + +static void dma_flush(u32 buf_start , u32 buf_size) +{ + if ((buf_start == 0) || (buf_size == 0)) + return; + dma_sync_single_for_device( + &encode_manager.this_pdev->dev, buf_start, + buf_size, DMA_TO_DEVICE); +} + +static void cache_flush(u32 buf_start , u32 buf_size) +{ + if ((buf_start == 0) || (buf_size == 0)) + return; + dma_sync_single_for_cpu( + &encode_manager.this_pdev->dev, buf_start, + buf_size, DMA_FROM_DEVICE); +} + +static u32 getbuffer(struct encode_wq_s *wq, u32 type) +{ + u32 ret = 0; + + switch (type) { + case ENCODER_BUFFER_INPUT: + ret = wq->mem.dct_buff_start_addr; + break; + case ENCODER_BUFFER_REF0: + ret = wq->mem.dct_buff_start_addr + + wq->mem.bufspec.dec0_y.buf_start; + break; + case ENCODER_BUFFER_REF1: + ret = wq->mem.dct_buff_start_addr + + wq->mem.bufspec.dec1_y.buf_start; + break; + case ENCODER_BUFFER_OUTPUT: + ret = wq->mem.BitstreamStart; + break; + case ENCODER_BUFFER_DUMP: + ret = wq->mem.dump_info_ddr_start_addr; + break; + case ENCODER_BUFFER_CBR: + ret = wq->mem.cbr_info_ddr_start_addr; + break; + default: + break; + } + return ret; +} + +s32 amvenc_avc_start(struct encode_wq_s *wq, u32 clock) +{ + const char *p = select_ucode(encode_manager.ucode_index); + + avc_poweron(clock); + avc_canvas_init(wq); + + WRITE_HREG(HCODEC_ASSIST_MMC_CTRL1, 0x32); + + if (amvenc_loadmc(p, wq) < 0) + return -EBUSY; + + encode_manager.need_reset = true; + encode_manager.process_irq = false; + encode_manager.encode_hw_status = ENCODER_IDLE; + amvenc_reset(); + avc_init_encoder(wq, true); + avc_init_input_buffer(wq); /* dct buffer setting */ + avc_init_output_buffer(wq); /* output stream buffer */ + + ie_me_mode = (0 & ME_PIXEL_MODE_MASK) << ME_PIXEL_MODE_SHIFT; + avc_prot_init(wq, NULL, wq->pic.init_qppicture, true); + if (request_irq(encode_manager.irq_num, enc_isr, IRQF_SHARED, + "enc-irq", (void *)&encode_manager) == 0) + encode_manager.irq_requested = true; + else + encode_manager.irq_requested = false; + + /* decoder buffer , need set before each frame start */ + avc_init_dblk_buffer(wq->mem.dblk_buf_canvas); + /* reference buffer , need set before each frame start */ + avc_init_reference_buffer(wq->mem.ref_buf_canvas); + avc_init_assit_buffer(wq); /* assitant buffer for microcode */ + ie_me_mb_type = 0; + avc_init_ie_me_parameter(wq, wq->pic.init_qppicture); + WRITE_HREG(ENCODER_STATUS , ENCODER_IDLE); + +#ifdef MULTI_SLICE_MC + if (fixed_slice_cfg) + WRITE_HREG(FIXED_SLICE_CFG, fixed_slice_cfg); + else if (wq->pic.rows_per_slice != + (wq->pic.encoder_height + 15) >> 4) { + u32 mb_per_slice = (wq->pic.encoder_height + 15) >> 4; + mb_per_slice = mb_per_slice * wq->pic.rows_per_slice; + WRITE_HREG(FIXED_SLICE_CFG, mb_per_slice); + } else + WRITE_HREG(FIXED_SLICE_CFG, 0); +#else + WRITE_HREG(FIXED_SLICE_CFG, 0); +#endif + amvenc_start(); + return 0; +} + +void amvenc_avc_stop(void) +{ + if ((encode_manager.irq_num >= 0) && + (encode_manager.irq_requested == true)) { + free_irq(encode_manager.irq_num, &encode_manager); + encode_manager.irq_requested = false; + } + amvenc_stop(); + avc_poweroff(); +} + +static s32 avc_init(struct encode_wq_s *wq) +{ + s32 r = 0; + + encode_manager.ucode_index = wq->ucode_index; + r = amvenc_avc_start(wq, clock_level); + + enc_pr(LOG_DEBUG, + "init avc encode. microcode %d, ret=%d, wq:%p.\n", + encode_manager.ucode_index, r, (void *)wq); + return 0; +} + +static s32 amvenc_avc_light_reset(struct encode_wq_s *wq, u32 value) +{ + s32 r = 0; + + amvenc_avc_stop(); + + mdelay(value); + + encode_manager.ucode_index = UCODE_MODE_FULL; + r = amvenc_avc_start(wq, clock_level); + + enc_pr(LOG_DEBUG, + "amvenc_avc_light_reset finish, wq:%p. ret=%d\n", + (void *)wq, r); + return r; +} + +#ifdef CONFIG_CMA +static u32 checkCMA(void) +{ + u32 ret; + if (encode_manager.cma_pool_size > 0) { + ret = encode_manager.cma_pool_size; + ret = ret / MIN_SIZE; + } else + ret = 0; + return ret; +} +#endif + +/* file operation */ +static s32 amvenc_avc_open(struct inode *inode, struct file *file) +{ + s32 r = 0; + struct encode_wq_s *wq = NULL; + file->private_data = NULL; + enc_pr(LOG_DEBUG, "avc open\n"); +#ifdef CONFIG_AM_JPEG_ENCODER + if (jpegenc_on() == true) { + enc_pr(LOG_ERROR, + "hcodec in use for JPEG Encode now.\n"); + return -EBUSY; + } +#endif + +#ifdef CONFIG_CMA + if ((encode_manager.use_reserve == false) && + (encode_manager.check_cma == false)) { + encode_manager.max_instance = checkCMA(); + if (encode_manager.max_instance > 0) { + enc_pr(LOG_DEBUG, + "amvenc_avc check CMA pool sucess, max instance: %d.\n", + encode_manager.max_instance); + } else { + enc_pr(LOG_ERROR, + "amvenc_avc CMA pool too small.\n"); + } + encode_manager.check_cma = true; + } +#endif + + wq = create_encode_work_queue(); + if (wq == NULL) { + enc_pr(LOG_ERROR, "amvenc_avc create instance fail.\n"); + return -EBUSY; + } + +#ifdef CONFIG_CMA + if (encode_manager.use_reserve == false) { + wq->mem.buf_start = codec_mm_alloc_for_dma(ENCODE_NAME, + MIN_SIZE >> PAGE_SHIFT, 0, + CODEC_MM_FLAGS_CPU); + if (wq->mem.buf_start) { + wq->mem.buf_size = MIN_SIZE; + enc_pr(LOG_DEBUG, + "allocating phys 0x%x, size %dk, wq:%p.\n", + wq->mem.buf_start, + wq->mem.buf_size >> 10, (void *)wq); + } else { + enc_pr(LOG_ERROR, + "CMA failed to allocate dma buffer for %s, wq:%p.\n", + encode_manager.this_pdev->name, + (void *)wq); + destroy_encode_work_queue(wq); + return -ENOMEM; + } + } +#endif + + if (wq->mem.buf_start == 0 || + wq->mem.buf_size < MIN_SIZE) { + enc_pr(LOG_ERROR, + "alloc mem failed, start: 0x%x, size:0x%x, wq:%p.\n", + wq->mem.buf_start, + wq->mem.buf_size, (void *)wq); + destroy_encode_work_queue(wq); + return -ENOMEM; + } + + memcpy(&wq->mem.bufspec, &amvenc_buffspec[0], + sizeof(struct BuffInfo_s)); + + enc_pr(LOG_DEBUG, + "amvenc_avc memory config sucess, buff start:0x%x, size is 0x%x, wq:%p.\n", + wq->mem.buf_start, wq->mem.buf_size, (void *)wq); + + file->private_data = (void *) wq; + return r; +} + +static s32 amvenc_avc_release(struct inode *inode, struct file *file) +{ + struct encode_wq_s *wq = (struct encode_wq_s *)file->private_data; + if (wq) { + enc_pr(LOG_DEBUG, "avc release, wq:%p\n", (void *)wq); + destroy_encode_work_queue(wq); + } + return 0; +} + +static long amvenc_avc_ioctl(struct file *file, u32 cmd, ulong arg) +{ + long r = 0; + u32 amrisc_cmd = 0; + struct encode_wq_s *wq = (struct encode_wq_s *)file->private_data; +#define MAX_ADDR_INFO_SIZE 50 + u32 addr_info[MAX_ADDR_INFO_SIZE + 4]; + ulong argV; + u32 buf_start; + s32 canvas = -1; + struct canvas_s dst; + switch (cmd) { + case AMVENC_AVC_IOC_GET_ADDR: + if ((wq->mem.ref_buf_canvas & 0xff) == (ENC_CANVAS_OFFSET)) + put_user(1, (u32 *)arg); + else + put_user(2, (u32 *)arg); + break; + case AMVENC_AVC_IOC_INPUT_UPDATE: + break; + case AMVENC_AVC_IOC_NEW_CMD: + if (copy_from_user(addr_info, (void *)arg, + MAX_ADDR_INFO_SIZE * sizeof(u32))) { + enc_pr(LOG_ERROR, + "avc get new cmd error, wq:%p.\n", (void *)wq); + return -1; + } + r = convert_request(wq, addr_info); + if (r == 0) + r = encode_wq_add_request(wq); + if (r) { + enc_pr(LOG_ERROR, + "avc add new request error, wq:%p.\n", + (void *)wq); + } + break; + case AMVENC_AVC_IOC_GET_STAGE: + put_user(wq->hw_status, (u32 *)arg); + break; + case AMVENC_AVC_IOC_GET_OUTPUT_SIZE: + addr_info[0] = wq->output_size; + addr_info[1] = wq->me_weight; + addr_info[2] = wq->i4_weight; + addr_info[3] = wq->i16_weight; + r = copy_to_user((u32 *)arg, + addr_info , 4 * sizeof(u32)); + break; + case AMVENC_AVC_IOC_CONFIG_INIT: + if (copy_from_user(addr_info, (void *)arg, + MAX_ADDR_INFO_SIZE * sizeof(u32))) { + enc_pr(LOG_ERROR, + "avc config init error, wq:%p.\n", (void *)wq); + return -1; + } + wq->ucode_index = UCODE_MODE_FULL; +#ifdef MULTI_SLICE_MC + wq->pic.rows_per_slice = addr_info[1]; + enc_pr(LOG_DEBUG, + "avc init -- rows_per_slice: %d, wq: %p.\n", + wq->pic.rows_per_slice, (void *)wq); +#endif + enc_pr(LOG_DEBUG, + "avc init as mode %d, wq: %p.\n", + wq->ucode_index, (void *)wq); + + if (addr_info[2] > wq->mem.bufspec.max_width || + addr_info[3] > wq->mem.bufspec.max_height) { + enc_pr(LOG_ERROR, + "avc config init- encode size %dx%d is larger than supported (%dx%d). wq:%p.\n", + addr_info[2], addr_info[3], + wq->mem.bufspec.max_width, + wq->mem.bufspec.max_height, (void *)wq); + return -1; + } + wq->pic.encoder_width = addr_info[2]; + wq->pic.encoder_height = addr_info[3]; + if (wq->pic.encoder_width * + wq->pic.encoder_height >= 1280 * 720) + clock_level = 6; + else + clock_level = 5; + avc_buffspec_init(wq); + complete(&encode_manager.event.request_in_com); + addr_info[1] = wq->mem.bufspec.dct.buf_start; + addr_info[2] = wq->mem.bufspec.dct.buf_size; + addr_info[3] = wq->mem.bufspec.bitstream.buf_start; + addr_info[4] = wq->mem.bufspec.bitstream.buf_size; + addr_info[5] = wq->mem.bufspec.scale_buff.buf_start; + addr_info[6] = wq->mem.bufspec.scale_buff.buf_size; + addr_info[7] = wq->mem.bufspec.dump_info.buf_start; + addr_info[8] = wq->mem.bufspec.dump_info.buf_size; + addr_info[9] = wq->mem.bufspec.cbr_info.buf_start; + addr_info[10] = wq->mem.bufspec.cbr_info.buf_size; + r = copy_to_user((u32 *)arg, addr_info , 11*sizeof(u32)); + break; + case AMVENC_AVC_IOC_FLUSH_CACHE: + if (copy_from_user(addr_info, (void *)arg, + MAX_ADDR_INFO_SIZE * sizeof(u32))) { + enc_pr(LOG_ERROR, + "avc flush cache error, wq: %p.\n", (void *)wq); + return -1; + } + buf_start = getbuffer(wq, addr_info[0]); + dma_flush(buf_start + addr_info[1], + addr_info[2] - addr_info[1]); + break; + case AMVENC_AVC_IOC_FLUSH_DMA: + if (copy_from_user(addr_info, (void *)arg, + MAX_ADDR_INFO_SIZE * sizeof(u32))) { + enc_pr(LOG_ERROR, + "avc flush dma error, wq:%p.\n", (void *)wq); + return -1; + } + buf_start = getbuffer(wq, addr_info[0]); + cache_flush(buf_start + addr_info[1], + addr_info[2] - addr_info[1]); + break; + case AMVENC_AVC_IOC_GET_BUFFINFO: + put_user(wq->mem.buf_size, (u32 *)arg); + break; + case AMVENC_AVC_IOC_GET_DEVINFO: + if (get_cpu_type() >= MESON_CPU_MAJOR_ID_GXL) { + /* send the same id as GXTVBB to upper*/ + r = copy_to_user((s8 *)arg, AMVENC_DEVINFO_GXTVBB, + strlen(AMVENC_DEVINFO_GXTVBB)); + } else if (get_cpu_type() == MESON_CPU_MAJOR_ID_GXTVBB) { + r = copy_to_user((s8 *)arg, AMVENC_DEVINFO_GXTVBB, + strlen(AMVENC_DEVINFO_GXTVBB)); + } else if (get_cpu_type() == MESON_CPU_MAJOR_ID_GXBB) { + r = copy_to_user((s8 *)arg, AMVENC_DEVINFO_GXBB, + strlen(AMVENC_DEVINFO_GXBB)); + } else if (get_cpu_type() == MESON_CPU_MAJOR_ID_MG9TV) { + r = copy_to_user((s8 *)arg, AMVENC_DEVINFO_G9, + strlen(AMVENC_DEVINFO_G9)); + } else { + r = copy_to_user((s8 *)arg, AMVENC_DEVINFO_M8, + strlen(AMVENC_DEVINFO_M8)); + } + break; + case AMVENC_AVC_IOC_SUBMIT: + get_user(amrisc_cmd, ((u32 *)arg)); + if (amrisc_cmd == ENCODER_IDR) { + wq->pic.idr_pic_id++; + if (wq->pic.idr_pic_id > 65535) + wq->pic.idr_pic_id = 0; + wq->pic.pic_order_cnt_lsb = 2; + wq->pic.frame_number = 1; + } else if (amrisc_cmd == ENCODER_NON_IDR) { + wq->pic.frame_number++; + wq->pic.pic_order_cnt_lsb += 2; + if (wq->pic.frame_number > 65535) + wq->pic.frame_number = 0; + } + amrisc_cmd = wq->mem.dblk_buf_canvas; + wq->mem.dblk_buf_canvas = wq->mem.ref_buf_canvas; + /* current dblk buffer as next reference buffer */ + wq->mem.ref_buf_canvas = amrisc_cmd; + break; + case AMVENC_AVC_IOC_READ_CANVAS: + get_user(argV, ((u32 *)arg)); + canvas = argV; + if (canvas & 0xff) { + canvas_read(canvas & 0xff, &dst); + addr_info[0] = dst.addr; + if ((canvas & 0xff00) >> 8) + canvas_read((canvas & 0xff00) >> 8, &dst); + if ((canvas & 0xff0000) >> 16) + canvas_read((canvas & 0xff0000) >> 16, &dst); + addr_info[1] = dst.addr - addr_info[0] + + dst.width * dst.height; + } else { + addr_info[0] = 0; + addr_info[1] = 0; + } + dma_flush(dst.addr, dst.width * dst.height * 3 / 2); + r = copy_to_user((u32 *)arg, addr_info , 2 * sizeof(u32)); + break; + case AMVENC_AVC_IOC_MAX_INSTANCE: + put_user(encode_manager.max_instance, (u32 *)arg); + break; + default: + r = -1; + break; + } + return r; +} + +#ifdef CONFIG_COMPAT +static long amvenc_avc_compat_ioctl(struct file *filp, + unsigned int cmd, unsigned long args) +{ + unsigned long ret; + + args = (unsigned long)compat_ptr(args); + ret = amvenc_avc_ioctl(filp, cmd, args); + return ret; +} +#endif + +static s32 avc_mmap(struct file *filp, struct vm_area_struct *vma) +{ + struct encode_wq_s *wq = (struct encode_wq_s *)filp->private_data; + ulong off = vma->vm_pgoff << PAGE_SHIFT; + ulong vma_size = vma->vm_end - vma->vm_start; + + if (vma_size == 0) { + enc_pr(LOG_ERROR, "vma_size is 0, wq:%p.\n", (void *)wq); + return -EAGAIN; + } + if (!off) + off += wq->mem.buf_start; + enc_pr(LOG_ALL, + "vma_size is %ld , off is %ld, wq:%p.\n", + vma_size , off, (void *)wq); + vma->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP | VM_IO; + /* vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); */ + if (remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT, + vma->vm_end - vma->vm_start, vma->vm_page_prot)) { + enc_pr(LOG_ERROR, + "set_cached: failed remap_pfn_range, wq:%p.\n", + (void *)wq); + return -EAGAIN; + } + return 0; +} + +static u32 amvenc_avc_poll(struct file *file, poll_table *wait_table) +{ + struct encode_wq_s *wq = (struct encode_wq_s *)file->private_data; + poll_wait(file, &wq->request_complete, wait_table); + + if (atomic_read(&wq->request_ready)) { + atomic_dec(&wq->request_ready); + return POLLIN | POLLRDNORM; + } + return 0; +} + +static const struct file_operations amvenc_avc_fops = { + .owner = THIS_MODULE, + .open = amvenc_avc_open, + .mmap = avc_mmap, + .release = amvenc_avc_release, + .unlocked_ioctl = amvenc_avc_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = amvenc_avc_compat_ioctl, +#endif + .poll = amvenc_avc_poll, +}; + +/* work queue function */ +static s32 encode_process_request(struct encode_manager_s *manager, + struct encode_queue_item_s *pitem) +{ + s32 ret = 0; + struct encode_wq_s *wq = pitem->request.parent; + struct encode_request_s *request = &pitem->request; + u32 timeout = (request->timeout == 0) ? + 1 : msecs_to_jiffies(request->timeout); + u32 buf_start = 0; + u32 size = 0; + u32 flush_size = ((wq->pic.encoder_width + 31) >> 5 << 5) * + ((wq->pic.encoder_height + 15) >> 4 << 4) * 3 / 2; + +#ifdef H264_ENC_CBR + if (request->cmd == ENCODER_IDR || request->cmd == ENCODER_NON_IDR) { + if (request->flush_flag & AMVENC_FLUSH_FLAG_CBR + && get_cpu_type() >= MESON_CPU_MAJOR_ID_GXTVBB) { + void *vaddr = + phys_to_virt(wq->mem.cbr_info_ddr_start_addr); + ConvertTable2Risc(vaddr, 0xa00); + buf_start = getbuffer(wq, ENCODER_BUFFER_CBR); + dma_flush(buf_start, wq->mem.cbr_info_ddr_size); + } + } +#endif + +Again: + amvenc_avc_start_cmd(wq, request); + + if (no_timeout) { + wait_event_interruptible(manager->event.hw_complete, + (manager->encode_hw_status == ENCODER_IDR_DONE + || manager->encode_hw_status == ENCODER_NON_IDR_DONE + || manager->encode_hw_status == ENCODER_SEQUENCE_DONE + || manager->encode_hw_status == ENCODER_PICTURE_DONE)); + } else { + wait_event_interruptible_timeout(manager->event.hw_complete, + ((manager->encode_hw_status == ENCODER_IDR_DONE) + || (manager->encode_hw_status == ENCODER_NON_IDR_DONE) + || (manager->encode_hw_status == ENCODER_SEQUENCE_DONE) + || (manager->encode_hw_status == ENCODER_PICTURE_DONE)), + timeout); + } + + if ((request->cmd == ENCODER_SEQUENCE) && + (manager->encode_hw_status == ENCODER_SEQUENCE_DONE)) { + wq->sps_size = READ_HREG(HCODEC_VLC_TOTAL_BYTES); + wq->hw_status = manager->encode_hw_status; + request->cmd = ENCODER_PICTURE; + goto Again; + } else if ((request->cmd == ENCODER_PICTURE) && + (manager->encode_hw_status == ENCODER_PICTURE_DONE)) { + wq->pps_size = + READ_HREG(HCODEC_VLC_TOTAL_BYTES) - wq->sps_size; + wq->hw_status = manager->encode_hw_status; + if (request->flush_flag & AMVENC_FLUSH_FLAG_OUTPUT) { + buf_start = getbuffer(wq, ENCODER_BUFFER_OUTPUT); + cache_flush(buf_start, + wq->sps_size + wq->pps_size); + } + wq->output_size = (wq->sps_size << 16) | wq->pps_size; + } else { + wq->hw_status = manager->encode_hw_status; + if ((manager->encode_hw_status == ENCODER_IDR_DONE) || + (manager->encode_hw_status == ENCODER_NON_IDR_DONE)) { + wq->output_size = READ_HREG(HCODEC_VLC_TOTAL_BYTES); + if (request->flush_flag & AMVENC_FLUSH_FLAG_OUTPUT) { + buf_start = getbuffer(wq, + ENCODER_BUFFER_OUTPUT); + cache_flush(buf_start, wq->output_size); + } + if (request->flush_flag & + AMVENC_FLUSH_FLAG_DUMP) { + buf_start = getbuffer(wq, + ENCODER_BUFFER_DUMP); + size = wq->mem.dump_info_ddr_size; + cache_flush(buf_start, size); + enc_pr(LOG_DEBUG, "CBR flush dump_info done--- "); + } + if (request->flush_flag & + AMVENC_FLUSH_FLAG_REFERENCE) { + u32 ref_id = ENCODER_BUFFER_REF0; + if ((wq->mem.ref_buf_canvas & 0xff) == + (ENC_CANVAS_OFFSET)) + ref_id = ENCODER_BUFFER_REF0; + else + ref_id = ENCODER_BUFFER_REF1; + buf_start = getbuffer(wq, ref_id); + cache_flush(buf_start, flush_size); + } + } else { + manager->encode_hw_status = ENCODER_ERROR; + enc_pr(LOG_DEBUG, "avc encode light reset --- "); + enc_pr(LOG_DEBUG, + "frame type: %s, size: %dx%d, wq: %p\n", + (request->cmd == ENCODER_IDR) ? "IDR" : "P", + wq->pic.encoder_width, + wq->pic.encoder_height, (void *)wq); + enc_pr(LOG_DEBUG, + "mb info: 0x%x, encode status: 0x%x, dct status: 0x%x ", + READ_HREG(HCODEC_VLC_MB_INFO), + READ_HREG(ENCODER_STATUS), + READ_HREG(HCODEC_QDCT_STATUS_CTRL)); + enc_pr(LOG_DEBUG, + "vlc status: 0x%x, me status: 0x%x, risc pc:0x%x, debug:0x%x\n", + READ_HREG(HCODEC_VLC_STATUS_CTRL), + READ_HREG(HCODEC_ME_STATUS), + READ_HREG(HCODEC_MPC_E), + READ_HREG(DEBUG_REG)); + amvenc_avc_light_reset(wq, 30); + } + } + atomic_inc(&wq->request_ready); + wake_up_interruptible(&wq->request_complete); + return ret; +} + +s32 encode_wq_add_request(struct encode_wq_s *wq) +{ + struct encode_queue_item_s *pitem = NULL; + struct list_head *head = NULL; + struct encode_wq_s *tmp = NULL; + bool find = false; + + spin_lock(&encode_manager.event.sem_lock); + + head = &encode_manager.wq; + list_for_each_entry(tmp, head, list) { + if ((wq == tmp) && (wq != NULL)) { + find = true; + break; + } + } + + if (find == false) { + enc_pr(LOG_ERROR, "current wq (%p) doesn't register.\n", + (void *)wq); + goto error; + } + + if (list_empty(&encode_manager.free_queue)) { + enc_pr(LOG_ERROR, "work queue no space, wq:%p.\n", + (void *)wq); + goto error; + } + + pitem = list_entry(encode_manager.free_queue.next, + struct encode_queue_item_s, list); + if (IS_ERR(pitem)) + goto error; + + memcpy(&pitem->request, &wq->request, sizeof(struct encode_request_s)); + memset(&wq->request, 0, sizeof(struct encode_request_s)); + wq->hw_status = 0; + wq->output_size = 0; + pitem->request.parent = wq; + list_move_tail(&pitem->list, &encode_manager.process_queue); + spin_unlock(&encode_manager.event.sem_lock); + + enc_pr(LOG_INFO, + "add new work ok, cmd:%d, ucode mode: %d, wq:%p.\n", + pitem->request.cmd, pitem->request.ucode_mode, + (void *)wq); + complete(&encode_manager.event.request_in_com);/* new cmd come in */ + return 0; +error: + spin_unlock(&encode_manager.event.sem_lock); + return -1; +} + +struct encode_wq_s *create_encode_work_queue(void) +{ + struct encode_wq_s *encode_work_queue = NULL; + bool done = false; + u32 i, max_instance; + struct Buff_s *reserve_buff; + + encode_work_queue = kzalloc(sizeof(struct encode_wq_s), GFP_KERNEL); + if (IS_ERR(encode_work_queue)) { + enc_pr(LOG_ERROR, "can't create work queue\n"); + return NULL; + } + max_instance = encode_manager.max_instance; + encode_work_queue->pic.init_qppicture = 26; + encode_work_queue->pic.log2_max_frame_num = 4; + encode_work_queue->pic.log2_max_pic_order_cnt_lsb = 4; + encode_work_queue->pic.idr_pic_id = 0; + encode_work_queue->pic.frame_number = 0; + encode_work_queue->pic.pic_order_cnt_lsb = 0; + encode_work_queue->ucode_index = UCODE_MODE_FULL; + +#ifdef H264_ENC_CBR + encode_work_queue->cbr_info.block_w = 16; + encode_work_queue->cbr_info.block_h = 9; + encode_work_queue->cbr_info.long_th = CBR_LONG_THRESH; + encode_work_queue->cbr_info.start_tbl_id = START_TABLE_ID; + encode_work_queue->cbr_info.short_shift = CBR_SHORT_SHIFT; + encode_work_queue->cbr_info.long_mb_num = CBR_LONG_MB_NUM; +#endif + init_waitqueue_head(&encode_work_queue->request_complete); + atomic_set(&encode_work_queue->request_ready, 0); + spin_lock(&encode_manager.event.sem_lock); + if (encode_manager.wq_count < encode_manager.max_instance) { + list_add_tail(&encode_work_queue->list, &encode_manager.wq); + encode_manager.wq_count++; + if (encode_manager.use_reserve == true) { + for (i = 0; i < max_instance; i++) { + reserve_buff = &encode_manager.reserve_buff[i]; + if (reserve_buff->used == false) { + encode_work_queue->mem.buf_start = + reserve_buff->buf_start; + encode_work_queue->mem.buf_size = + reserve_buff->buf_size; + reserve_buff->used = true; + done = true; + break; + } + } + } else + done = true; + } + spin_unlock(&encode_manager.event.sem_lock); + if (done == false) { + kfree(encode_work_queue); + encode_work_queue = NULL; + enc_pr(LOG_ERROR, "too many work queue!\n"); + } + return encode_work_queue; /* find it */ +} + +static void _destroy_encode_work_queue(struct encode_manager_s *manager, + struct encode_wq_s **wq, + struct encode_wq_s *encode_work_queue, + bool *find) +{ + struct list_head *head; + struct encode_wq_s *wp_tmp = NULL; + u32 i, max_instance; + struct Buff_s *reserve_buff; + u32 buf_start = encode_work_queue->mem.buf_start; + + max_instance = manager->max_instance; + head = &manager->wq; + list_for_each_entry_safe((*wq), wp_tmp, head, list) { + if ((*wq) && (*wq == encode_work_queue)) { + list_del(&(*wq)->list); + if (manager->use_reserve == true) { + for (i = 0; i < max_instance; i++) { + reserve_buff = + &manager->reserve_buff[i]; + if (reserve_buff->used == true && + buf_start == + reserve_buff->buf_start) { + reserve_buff->used = false; + break; + } + } + } + *find = true; + manager->wq_count--; + enc_pr(LOG_DEBUG, + "remove encode_work_queue %p sucess, %s line %d.\n", + (void *)encode_work_queue, + __func__, __LINE__); + break; + } + } +} + +s32 destroy_encode_work_queue(struct encode_wq_s *encode_work_queue) +{ + struct encode_queue_item_s *pitem, *tmp; + struct encode_wq_s *wq = NULL; + bool find = false; + + struct list_head *head; + if (encode_work_queue) { + spin_lock(&encode_manager.event.sem_lock); + if (encode_manager.current_wq == encode_work_queue) { + encode_manager.remove_flag = true; + spin_unlock(&encode_manager.event.sem_lock); + enc_pr(LOG_DEBUG, + "warning--Destory the running queue, should not be here.\n"); + wait_for_completion( + &encode_manager.event.process_complete); + spin_lock(&encode_manager.event.sem_lock); + } /* else we can delete it safely. */ + + head = &encode_manager.process_queue; + list_for_each_entry_safe(pitem, tmp, head, list) { + if (pitem && pitem->request.parent == + encode_work_queue) { + pitem->request.parent = NULL; + enc_pr(LOG_DEBUG, + "warning--remove not process request, should not be here.\n"); + list_move_tail(&pitem->list, + &encode_manager.free_queue); + } + } + + _destroy_encode_work_queue(&encode_manager, &wq, + encode_work_queue, &find); + spin_unlock(&encode_manager.event.sem_lock); +#ifdef CONFIG_CMA + if (encode_work_queue->mem.buf_start) { + codec_mm_free_for_dma( + ENCODE_NAME, + encode_work_queue->mem.buf_start); + encode_work_queue->mem.buf_start = 0; + + } +#endif + kfree(encode_work_queue); + complete(&encode_manager.event.request_in_com); + } + return 0; +} + +static s32 encode_monitor_thread(void *data) +{ + struct encode_manager_s *manager = (struct encode_manager_s *)data; + struct encode_queue_item_s *pitem = NULL; + struct sched_param param = {.sched_priority = MAX_RT_PRIO - 1 }; + s32 ret = 0; + enc_pr(LOG_DEBUG, "encode workqueue monitor start.\n"); + sched_setscheduler(current, SCHED_FIFO, ¶m); + allow_signal(SIGTERM); + /* setup current_wq here. */ + while (manager->process_queue_state != ENCODE_PROCESS_QUEUE_STOP) { + if (kthread_should_stop()) + break; + + ret = wait_for_completion_interruptible( + &manager->event.request_in_com); + + if (ret == -ERESTARTSYS) + break; + + if (kthread_should_stop()) + break; + if (manager->inited == false) { + spin_lock(&manager->event.sem_lock); + if (!list_empty(&manager->wq)) { + struct encode_wq_s *first_wq = + list_entry(manager->wq.next, + struct encode_wq_s, list); + manager->current_wq = first_wq; + spin_unlock(&manager->event.sem_lock); + if (first_wq) { +#ifdef CONFIG_AM_GE2D + if (!manager->context) + manager->context = + create_ge2d_work_queue(); +#endif + avc_init(first_wq); + manager->inited = true; + } + spin_lock(&manager->event.sem_lock); + manager->current_wq = NULL; + spin_unlock(&manager->event.sem_lock); + if (manager->remove_flag) { + complete( + &manager + ->event.process_complete); + manager->remove_flag = false; + } + } else + spin_unlock(&manager->event.sem_lock); + continue; + } + + spin_lock(&manager->event.sem_lock); + pitem = NULL; + if (list_empty(&manager->wq)) { + spin_unlock(&manager->event.sem_lock); + manager->inited = false; + amvenc_avc_stop(); +#ifdef CONFIG_AM_GE2D + if (manager->context) { + destroy_ge2d_work_queue(manager->context); + manager->context = NULL; + } +#endif + enc_pr(LOG_DEBUG, "power off encode.\n"); + continue; + } else if (!list_empty(&manager->process_queue)) { + pitem = list_entry(manager->process_queue.next, + struct encode_queue_item_s, list); + list_del(&pitem->list); + manager->current_item = pitem; + manager->current_wq = pitem->request.parent; + } + spin_unlock(&manager->event.sem_lock); + + if (pitem) { + encode_process_request(manager, pitem); + spin_lock(&manager->event.sem_lock); + list_add_tail(&pitem->list, &manager->free_queue); + manager->current_item = NULL; + manager->last_wq = manager->current_wq; + manager->current_wq = NULL; + spin_unlock(&manager->event.sem_lock); + } + if (manager->remove_flag) { + complete(&manager->event.process_complete); + manager->remove_flag = false; + } + } + while (!kthread_should_stop()) + msleep(20); + + enc_pr(LOG_DEBUG, "exit encode_monitor_thread.\n"); + return 0; +} + +static s32 encode_start_monitor(void) +{ + s32 ret = 0; + + if (get_cpu_type() >= MESON_CPU_MAJOR_ID_GXTVBB) { + y_tnr_mot2alp_nrm_gain = 216; + y_tnr_mot2alp_dis_gain = 144; + c_tnr_mot2alp_nrm_gain = 216; + c_tnr_mot2alp_dis_gain = 144; + } else { + /* more tnr */ + y_tnr_mot2alp_nrm_gain = 144; + y_tnr_mot2alp_dis_gain = 96; + c_tnr_mot2alp_nrm_gain = 144; + c_tnr_mot2alp_dis_gain = 96; + } + + enc_pr(LOG_DEBUG, "encode start monitor.\n"); + encode_manager.process_queue_state = ENCODE_PROCESS_QUEUE_START; + encode_manager.encode_thread = kthread_run(encode_monitor_thread, + &encode_manager, "encode_monitor"); + if (IS_ERR(encode_manager.encode_thread)) { + ret = PTR_ERR(encode_manager.encode_thread); + encode_manager.process_queue_state = ENCODE_PROCESS_QUEUE_STOP; + enc_pr(LOG_ERROR, + "encode monitor : failed to start kthread (%d)\n", ret); + } + return ret; +} + +static s32 encode_stop_monitor(void) +{ + enc_pr(LOG_DEBUG, "stop encode monitor thread\n"); + if (encode_manager.encode_thread) { + spin_lock(&encode_manager.event.sem_lock); + if (!list_empty(&encode_manager.wq)) { + u32 count = encode_manager.wq_count; + spin_unlock(&encode_manager.event.sem_lock); + enc_pr(LOG_ERROR, + "stop encode monitor thread error, active wq (%d) is not 0.\n", + count); + return -1; + } + spin_unlock(&encode_manager.event.sem_lock); + encode_manager.process_queue_state = ENCODE_PROCESS_QUEUE_STOP; + send_sig(SIGTERM, encode_manager.encode_thread, 1); + complete(&encode_manager.event.request_in_com); + kthread_stop(encode_manager.encode_thread); + encode_manager.encode_thread = NULL; + kfree(mc_addr); + mc_addr = NULL; + } + return 0; +} + +static s32 encode_wq_init(void) +{ + u32 i = 0; + struct encode_queue_item_s *pitem = NULL; + + enc_pr(LOG_DEBUG, "encode_wq_init.\n"); + encode_manager.irq_requested = false; + + spin_lock_init(&encode_manager.event.sem_lock); + init_completion(&encode_manager.event.request_in_com); + init_waitqueue_head(&encode_manager.event.hw_complete); + init_completion(&encode_manager.event.process_complete); + INIT_LIST_HEAD(&encode_manager.process_queue); + INIT_LIST_HEAD(&encode_manager.free_queue); + INIT_LIST_HEAD(&encode_manager.wq); + + tasklet_init(&encode_manager.encode_tasklet, + encode_isr_tasklet, + (ulong)&encode_manager); + + for (i = 0; i < MAX_ENCODE_REQUEST; i++) { + pitem = kcalloc(1, + sizeof(struct encode_queue_item_s), + GFP_KERNEL); + if (IS_ERR(pitem)) { + enc_pr(LOG_ERROR, "can't request queue item memory.\n"); + return -1; + } + pitem->request.parent = NULL; + list_add_tail(&pitem->list, &encode_manager.free_queue); + } + encode_manager.current_wq = NULL; + encode_manager.last_wq = NULL; + encode_manager.encode_thread = NULL; + encode_manager.current_item = NULL; + encode_manager.wq_count = 0; + encode_manager.remove_flag = false; + InitEncodeWeight(); + if (encode_start_monitor()) { + enc_pr(LOG_ERROR, "encode create thread error.\n"); + return -1; + } + return 0; +} + +static s32 encode_wq_uninit(void) +{ + struct encode_queue_item_s *pitem, *tmp; + struct list_head *head; + u32 count = 0; + s32 r = -1; + enc_pr(LOG_DEBUG, "uninit encode wq.\n"); + if (encode_stop_monitor() == 0) { + if ((encode_manager.irq_num >= 0) && + (encode_manager.irq_requested == true)) { + free_irq(encode_manager.irq_num, &encode_manager); + encode_manager.irq_requested = false; + } + spin_lock(&encode_manager.event.sem_lock); + head = &encode_manager.process_queue; + list_for_each_entry_safe(pitem, tmp, head, list) { + if (pitem) { + list_del(&pitem->list); + kfree(pitem); + count++; + } + } + head = &encode_manager.free_queue; + list_for_each_entry_safe(pitem, tmp, head, list) { + if (pitem) { + list_del(&pitem->list); + kfree(pitem); + count++; + } + } + spin_unlock(&encode_manager.event.sem_lock); + if (count == MAX_ENCODE_REQUEST) + r = 0; + else { + enc_pr(LOG_ERROR, "lost some request item %d.\n", + MAX_ENCODE_REQUEST - count); + } + } + return r; +} + +static ssize_t encode_status_show(struct class *cla, + struct class_attribute *attr, char *buf) +{ + u32 process_count = 0; + u32 free_count = 0; + struct encode_queue_item_s *pitem = NULL; + struct encode_wq_s *current_wq = NULL; + struct encode_wq_s *last_wq = NULL; + struct list_head *head = NULL; + s32 irq_num = 0; + u32 hw_status = 0; + u32 process_queue_state = 0; + u32 wq_count = 0; + u32 ucode_index; + bool need_reset; + bool process_irq; + bool inited; + bool use_reserve; + struct Buff_s reserve_mem; + u32 max_instance; +#ifdef CONFIG_CMA + bool check_cma = false; +#endif + + spin_lock(&encode_manager.event.sem_lock); + head = &encode_manager.free_queue; + list_for_each_entry(pitem, head , list) { + free_count++; + if (free_count > MAX_ENCODE_REQUEST) + break; + } + + head = &encode_manager.process_queue; + list_for_each_entry(pitem, head , list) { + process_count++; + if (free_count > MAX_ENCODE_REQUEST) + break; + } + + current_wq = encode_manager.current_wq; + last_wq = encode_manager.last_wq; + pitem = encode_manager.current_item; + irq_num = encode_manager.irq_num; + hw_status = encode_manager.encode_hw_status; + process_queue_state = encode_manager.process_queue_state; + wq_count = encode_manager.wq_count; + ucode_index = encode_manager.ucode_index; + need_reset = encode_manager.need_reset; + process_irq = encode_manager.process_irq; + inited = encode_manager.inited; + use_reserve = encode_manager.use_reserve; + reserve_mem.buf_start = encode_manager.reserve_mem.buf_start; + reserve_mem.buf_size = encode_manager.reserve_mem.buf_size; + + max_instance = encode_manager.max_instance; +#ifdef CONFIG_CMA + check_cma = encode_manager.check_cma; +#endif + + spin_unlock(&encode_manager.event.sem_lock); + + enc_pr(LOG_DEBUG, + "encode process queue count: %d, free queue count: %d.\n", + process_count, free_count); + enc_pr(LOG_DEBUG, + "encode curent wq: %p, last wq: %p, wq count: %d, max_instance: %d.\n", + current_wq, last_wq, wq_count, max_instance); + if (current_wq) + enc_pr(LOG_DEBUG, + "encode curent wq -- encode width: %d, encode height: %d.\n", + current_wq->pic.encoder_width, + current_wq->pic.encoder_height); + enc_pr(LOG_DEBUG, + "encode curent pitem: %p, ucode_index: %d, hw_status: %d, need_reset: %s, process_irq: %s.\n", + pitem, ucode_index, hw_status, need_reset ? "true" : "false", + process_irq ? "true" : "false"); + enc_pr(LOG_DEBUG, + "encode irq num: %d, inited: %s, process_queue_state: %d.\n", + irq_num, inited ? "true" : "false", process_queue_state); + if (use_reserve) { + enc_pr(LOG_DEBUG, + "encode use reserve memory, buffer start: 0x%x, size: %d MB.\n", + reserve_mem.buf_start, + reserve_mem.buf_size / SZ_1M); + } else { +#ifdef CONFIG_CMA + enc_pr(LOG_DEBUG, "encode check cma: %s.\n", + check_cma ? "true" : "false"); +#endif + } + return snprintf(buf, 40, "encode max instance: %d\n", max_instance); +} + +static struct class_attribute amvenc_class_attrs[] = { + __ATTR(encode_status, + S_IRUGO | S_IWUSR, + encode_status_show, + NULL), + __ATTR_NULL +}; + +static struct class amvenc_avc_class = { + .name = CLASS_NAME, + .class_attrs = amvenc_class_attrs, +}; + +s32 init_avc_device(void) +{ + s32 r = 0; + r = register_chrdev(0, DEVICE_NAME, &amvenc_avc_fops); + if (r <= 0) { + enc_pr(LOG_ERROR, "register amvenc_avc device error.\n"); + return r; + } + avc_device_major = r; + + r = class_register(&amvenc_avc_class); + if (r < 0) { + enc_pr(LOG_ERROR, "error create amvenc_avc class.\n"); + return r; + } + + amvenc_avc_dev = device_create(&amvenc_avc_class, NULL, + MKDEV(avc_device_major, 0), NULL, + DEVICE_NAME); + + if (IS_ERR(amvenc_avc_dev)) { + enc_pr(LOG_ERROR, "create amvenc_avc device error.\n"); + class_unregister(&amvenc_avc_class); + return -1; + } + return r; +} + +s32 uninit_avc_device(void) +{ + if (amvenc_avc_dev) + device_destroy(&amvenc_avc_class, MKDEV(avc_device_major, 0)); + + class_destroy(&amvenc_avc_class); + + unregister_chrdev(avc_device_major, DEVICE_NAME); + return 0; +} + +static s32 avc_mem_device_init(struct reserved_mem *rmem, struct device *dev) +{ + s32 r; + struct resource res; + if (!rmem) { + enc_pr(LOG_ERROR, + "Can not obtain I/O memory, and will allocate avc buffer!\n"); + r = -EFAULT; + return r; + } + res.start = (phys_addr_t)rmem->base; + res.end = res.start + (phys_addr_t)rmem->size - 1; + encode_manager.reserve_mem.buf_start = res.start; + encode_manager.reserve_mem.buf_size = res.end - res.start + 1; + + if (encode_manager.reserve_mem.buf_size >= + amvenc_buffspec[0].min_buffsize) { + encode_manager.max_instance = + encode_manager.reserve_mem.buf_size / + amvenc_buffspec[0].min_buffsize; + if (encode_manager.max_instance > MAX_ENCODE_INSTANCE) + encode_manager.max_instance = MAX_ENCODE_INSTANCE; + encode_manager.reserve_buff = kzalloc( + encode_manager.max_instance * + sizeof(struct Buff_s), GFP_KERNEL); + if (encode_manager.reserve_buff) { + u32 i; + struct Buff_s *reserve_buff; + u32 max_instance = encode_manager.max_instance; + for (i = 0; i < max_instance; i++) { + reserve_buff = &encode_manager.reserve_buff[i]; + reserve_buff->buf_start = + i * + amvenc_buffspec[0] + .min_buffsize + + encode_manager.reserve_mem.buf_start; + reserve_buff->buf_size = + encode_manager.reserve_mem.buf_start; + reserve_buff->used = false; + } + encode_manager.use_reserve = true; + r = 0; + enc_pr(LOG_DEBUG, + "amvenc_avc use reserve memory, buff start: 0x%x, size: 0x%x, max instance is %d\n", + encode_manager.reserve_mem.buf_start, + encode_manager.reserve_mem.buf_size, + encode_manager.max_instance); + } else { + enc_pr(LOG_ERROR, + "amvenc_avc alloc reserve buffer pointer fail. max instance is %d.\n", + encode_manager.max_instance); + encode_manager.max_instance = 0; + encode_manager.reserve_mem.buf_start = 0; + encode_manager.reserve_mem.buf_size = 0; + r = -ENOMEM; + } + } else { + enc_pr(LOG_ERROR, + "amvenc_avc memory resource too small, size is 0x%x. Need 0x%x bytes at least.\n", + encode_manager.reserve_mem.buf_size, + amvenc_buffspec[0] + .min_buffsize); + encode_manager.reserve_mem.buf_start = 0; + encode_manager.reserve_mem.buf_size = 0; + r = -ENOMEM; + } + return r; +} + +static s32 amvenc_avc_probe(struct platform_device *pdev) +{ + /* struct resource mem; */ + s32 res_irq; + s32 idx; + s32 r; + + enc_pr(LOG_INFO, "amvenc_avc probe start.\n"); + + encode_manager.this_pdev = pdev; +#ifdef CONFIG_CMA + encode_manager.check_cma = false; +#endif + encode_manager.reserve_mem.buf_start = 0; + encode_manager.reserve_mem.buf_size = 0; + encode_manager.use_reserve = false; + encode_manager.max_instance = 0; + encode_manager.reserve_buff = NULL; + + idx = of_reserved_mem_device_init(&pdev->dev); + if (idx != 0) { + enc_pr(LOG_DEBUG, + "amvenc_avc_probe -- reserved memory config fail.\n"); + } + + if (encode_manager.use_reserve == false) { +#ifndef CONFIG_CMA + enc_pr(LOG_ERROR, + "amvenc_avc memory is invaild, probe fail!\n"); + return -EFAULT; +#else + encode_manager.cma_pool_size = + (codec_mm_get_total_size() > (MIN_SIZE * 2)) ? + (MIN_SIZE * 2) : codec_mm_get_total_size(); + enc_pr(LOG_DEBUG, + "amvenc_avc - cma memory pool size: %d MB\n", + (u32)encode_manager.cma_pool_size / SZ_1M); +#endif + } + + res_irq = platform_get_irq(pdev, 0); + if (res_irq < 0) { + enc_pr(LOG_ERROR, "[%s] get irq error!", __func__); + return -EINVAL; + } + + encode_manager.irq_num = res_irq; + if (encode_wq_init()) { + kfree(encode_manager.reserve_buff); + encode_manager.reserve_buff = NULL; + enc_pr(LOG_ERROR, "encode work queue init error.\n"); + return -EFAULT; + } + + r = init_avc_device(); + enc_pr(LOG_INFO, "amvenc_avc probe end.\n"); + return r; +} + +static s32 amvenc_avc_remove(struct platform_device *pdev) +{ + kfree(encode_manager.reserve_buff); + encode_manager.reserve_buff = NULL; + if (encode_wq_uninit()) { + enc_pr(LOG_ERROR, "encode work queue uninit error.\n"); + } + uninit_avc_device(); + enc_pr(LOG_INFO, "amvenc_avc remove.\n"); + return 0; +} + +static const struct of_device_id amlogic_avcenc_dt_match[] = { + { + .compatible = "amlogic, amvenc_avc", + }, + {}, +}; + +static struct platform_driver amvenc_avc_driver = { + .probe = amvenc_avc_probe, + .remove = amvenc_avc_remove, + .driver = { + .name = DRIVER_NAME, + .of_match_table = amlogic_avcenc_dt_match, + } +}; + +static struct codec_profile_t amvenc_avc_profile = { + .name = "avc", + .profile = "" +}; + +static s32 __init amvenc_avc_driver_init_module(void) +{ + enc_pr(LOG_INFO, "amvenc_avc module init\n"); + + if (platform_driver_register(&amvenc_avc_driver)) { + enc_pr(LOG_ERROR, + "failed to register amvenc_avc driver\n"); + return -ENODEV; + } + vcodec_profile_register(&amvenc_avc_profile); + return 0; +} + +static void __exit amvenc_avc_driver_remove_module(void) +{ + enc_pr(LOG_INFO, "amvenc_avc module remove.\n"); + + platform_driver_unregister(&amvenc_avc_driver); +} + +static const struct reserved_mem_ops rmem_avc_ops = { + .device_init = avc_mem_device_init, +}; + +static s32 __init avc_mem_setup(struct reserved_mem *rmem) +{ + rmem->ops = &rmem_avc_ops; + enc_pr(LOG_DEBUG, "amvenc_avc reserved mem setup.\n"); + return 0; +} + +module_param(fixed_slice_cfg, uint, 0664); +MODULE_PARM_DESC(fixed_slice_cfg, "\n fixed_slice_cfg\n"); + +module_param(clock_level, uint, 0664); +MODULE_PARM_DESC(clock_level, "\n clock_level\n"); + +module_param(encode_print_level, uint, 0664); +MODULE_PARM_DESC(encode_print_level, "\n encode_print_level\n"); + +module_param(no_timeout, uint, 0664); +MODULE_PARM_DESC(no_timeout, "\n no_timeout flag for process request\n"); + +module_param(nr_mode, int, 0664); +MODULE_PARM_DESC(nr_mode, "\n nr_mode option\n"); + +module_param(qp_table_debug, uint, 0664); +MODULE_PARM_DESC(qp_table_debug, "\n print qp table\n"); + +#ifdef MORE_MODULE_PARAM +module_param(me_mv_merge_ctl, uint, 0664); +MODULE_PARM_DESC(me_mv_merge_ctl, "\n me_mv_merge_ctl\n"); + +module_param(me_step0_close_mv, uint, 0664); +MODULE_PARM_DESC(me_step0_close_mv, "\n me_step0_close_mv\n"); + +module_param(me_f_skip_sad, uint, 0664); +MODULE_PARM_DESC(me_f_skip_sad, "\n me_f_skip_sad\n"); + +module_param(me_f_skip_weight, uint, 0664); +MODULE_PARM_DESC(me_f_skip_weight, "\n me_f_skip_weight\n"); + +module_param(me_mv_weight_01, uint, 0664); +MODULE_PARM_DESC(me_mv_weight_01, "\n me_mv_weight_01\n"); + +module_param(me_mv_weight_23, uint, 0664); +MODULE_PARM_DESC(me_mv_weight_23, "\n me_mv_weight_23\n"); + +module_param(me_sad_range_inc, uint, 0664); +MODULE_PARM_DESC(me_sad_range_inc, "\n me_sad_range_inc\n"); + +module_param(me_sad_enough_01, uint, 0664); +MODULE_PARM_DESC(me_sad_enough_01, "\n me_sad_enough_01\n"); + +module_param(me_sad_enough_23, uint, 0664); +MODULE_PARM_DESC(me_sad_enough_23, "\n me_sad_enough_23\n"); + +module_param(y_tnr_mc_en, uint, 0664); +MODULE_PARM_DESC(y_tnr_mc_en, "\n y_tnr_mc_en option\n"); +module_param(y_tnr_txt_mode, uint, 0664); +MODULE_PARM_DESC(y_tnr_txt_mode, "\n y_tnr_txt_mode option\n"); +module_param(y_tnr_mot_sad_margin, uint, 0664); +MODULE_PARM_DESC(y_tnr_mot_sad_margin, "\n y_tnr_mot_sad_margin option\n"); +module_param(y_tnr_mot_cortxt_rate, uint, 0664); +MODULE_PARM_DESC(y_tnr_mot_cortxt_rate, "\n y_tnr_mot_cortxt_rate option\n"); +module_param(y_tnr_mot_distxt_ofst, uint, 0664); +MODULE_PARM_DESC(y_tnr_mot_distxt_ofst, "\n y_tnr_mot_distxt_ofst option\n"); +module_param(y_tnr_mot_distxt_rate, uint, 0664); +MODULE_PARM_DESC(y_tnr_mot_distxt_rate, "\n y_tnr_mot_distxt_rate option\n"); +module_param(y_tnr_mot_dismot_ofst, uint, 0664); +MODULE_PARM_DESC(y_tnr_mot_dismot_ofst, "\n y_tnr_mot_dismot_ofst option\n"); +module_param(y_tnr_mot_frcsad_lock, uint, 0664); +MODULE_PARM_DESC(y_tnr_mot_frcsad_lock, "\n y_tnr_mot_frcsad_lock option\n"); +module_param(y_tnr_mot2alp_frc_gain, uint, 0664); +MODULE_PARM_DESC(y_tnr_mot2alp_frc_gain, "\n y_tnr_mot2alp_frc_gain option\n"); +module_param(y_tnr_mot2alp_nrm_gain, uint, 0664); +MODULE_PARM_DESC(y_tnr_mot2alp_nrm_gain, "\n y_tnr_mot2alp_nrm_gain option\n"); +module_param(y_tnr_mot2alp_dis_gain, uint, 0664); +MODULE_PARM_DESC(y_tnr_mot2alp_dis_gain, "\n y_tnr_mot2alp_dis_gain option\n"); +module_param(y_tnr_mot2alp_dis_ofst, uint, 0664); +MODULE_PARM_DESC(y_tnr_mot2alp_dis_ofst, "\n y_tnr_mot2alp_dis_ofst option\n"); +module_param(y_tnr_alpha_min, uint, 0664); +MODULE_PARM_DESC(y_tnr_alpha_min, "\n y_tnr_alpha_min option\n"); +module_param(y_tnr_alpha_max, uint, 0664); +MODULE_PARM_DESC(y_tnr_alpha_max, "\n y_tnr_alpha_max option\n"); +module_param(y_tnr_deghost_os, uint, 0664); +MODULE_PARM_DESC(y_tnr_deghost_os, "\n y_tnr_deghost_os option\n"); + +module_param(c_tnr_mc_en, uint, 0664); +MODULE_PARM_DESC(c_tnr_mc_en, "\n c_tnr_mc_en option\n"); +module_param(c_tnr_txt_mode, uint, 0664); +MODULE_PARM_DESC(c_tnr_txt_mode, "\n c_tnr_txt_mode option\n"); +module_param(c_tnr_mot_sad_margin, uint, 0664); +MODULE_PARM_DESC(c_tnr_mot_sad_margin, "\n c_tnr_mot_sad_margin option\n"); +module_param(c_tnr_mot_cortxt_rate, uint, 0664); +MODULE_PARM_DESC(c_tnr_mot_cortxt_rate, "\n c_tnr_mot_cortxt_rate option\n"); +module_param(c_tnr_mot_distxt_ofst, uint, 0664); +MODULE_PARM_DESC(c_tnr_mot_distxt_ofst, "\n c_tnr_mot_distxt_ofst option\n"); +module_param(c_tnr_mot_distxt_rate, uint, 0664); +MODULE_PARM_DESC(c_tnr_mot_distxt_rate, "\n c_tnr_mot_distxt_rate option\n"); +module_param(c_tnr_mot_dismot_ofst, uint, 0664); +MODULE_PARM_DESC(c_tnr_mot_dismot_ofst, "\n c_tnr_mot_dismot_ofst option\n"); +module_param(c_tnr_mot_frcsad_lock, uint, 0664); +MODULE_PARM_DESC(c_tnr_mot_frcsad_lock, "\n c_tnr_mot_frcsad_lock option\n"); +module_param(c_tnr_mot2alp_frc_gain, uint, 0664); +MODULE_PARM_DESC(c_tnr_mot2alp_frc_gain, "\n c_tnr_mot2alp_frc_gain option\n"); +module_param(c_tnr_mot2alp_nrm_gain, uint, 0664); +MODULE_PARM_DESC(c_tnr_mot2alp_nrm_gain, "\n c_tnr_mot2alp_nrm_gain option\n"); +module_param(c_tnr_mot2alp_dis_gain, uint, 0664); +MODULE_PARM_DESC(c_tnr_mot2alp_dis_gain, "\n c_tnr_mot2alp_dis_gain option\n"); +module_param(c_tnr_mot2alp_dis_ofst, uint, 0664); +MODULE_PARM_DESC(c_tnr_mot2alp_dis_ofst, "\n c_tnr_mot2alp_dis_ofst option\n"); +module_param(c_tnr_alpha_min, uint, 0664); +MODULE_PARM_DESC(c_tnr_alpha_min, "\n c_tnr_alpha_min option\n"); +module_param(c_tnr_alpha_max, uint, 0664); +MODULE_PARM_DESC(c_tnr_alpha_max, "\n c_tnr_alpha_max option\n"); +module_param(c_tnr_deghost_os, uint, 0664); +MODULE_PARM_DESC(c_tnr_deghost_os, "\n c_tnr_deghost_os option\n"); + +module_param(y_snr_err_norm, uint, 0664); +MODULE_PARM_DESC(y_snr_err_norm, "\n y_snr_err_norm option\n"); +module_param(y_snr_gau_bld_core, uint, 0664); +MODULE_PARM_DESC(y_snr_gau_bld_core, "\n y_snr_gau_bld_core option\n"); +module_param(y_snr_gau_bld_ofst, int, 0664); +MODULE_PARM_DESC(y_snr_gau_bld_ofst, "\n y_snr_gau_bld_ofst option\n"); +module_param(y_snr_gau_bld_rate, uint, 0664); +MODULE_PARM_DESC(y_snr_gau_bld_rate, "\n y_snr_gau_bld_rate option\n"); +module_param(y_snr_gau_alp0_min, uint, 0664); +MODULE_PARM_DESC(y_snr_gau_alp0_min, "\n y_snr_gau_alp0_min option\n"); +module_param(y_snr_gau_alp0_max, uint, 0664); +MODULE_PARM_DESC(y_snr_gau_alp0_max, "\n y_snr_gau_alp0_max option\n"); +module_param(y_bld_beta2alp_rate, uint, 0664); +MODULE_PARM_DESC(y_bld_beta2alp_rate, "\n y_bld_beta2alp_rate option\n"); +module_param(y_bld_beta_min, uint, 0664); +MODULE_PARM_DESC(y_bld_beta_min, "\n y_bld_beta_min option\n"); +module_param(y_bld_beta_max, uint, 0664); +MODULE_PARM_DESC(y_bld_beta_max, "\n y_bld_beta_max option\n"); + +module_param(c_snr_err_norm, uint, 0664); +MODULE_PARM_DESC(c_snr_err_norm, "\n c_snr_err_norm option\n"); +module_param(c_snr_gau_bld_core, uint, 0664); +MODULE_PARM_DESC(c_snr_gau_bld_core, "\n c_snr_gau_bld_core option\n"); +module_param(c_snr_gau_bld_ofst, int, 0664); +MODULE_PARM_DESC(c_snr_gau_bld_ofst, "\n c_snr_gau_bld_ofst option\n"); +module_param(c_snr_gau_bld_rate, uint, 0664); +MODULE_PARM_DESC(c_snr_gau_bld_rate, "\n c_snr_gau_bld_rate option\n"); +module_param(c_snr_gau_alp0_min, uint, 0664); +MODULE_PARM_DESC(c_snr_gau_alp0_min, "\n c_snr_gau_alp0_min option\n"); +module_param(c_snr_gau_alp0_max, uint, 0664); +MODULE_PARM_DESC(c_snr_gau_alp0_max, "\n c_snr_gau_alp0_max option\n"); +module_param(c_bld_beta2alp_rate, uint, 0664); +MODULE_PARM_DESC(c_bld_beta2alp_rate, "\n c_bld_beta2alp_rate option\n"); +module_param(c_bld_beta_min, uint, 0664); +MODULE_PARM_DESC(c_bld_beta_min, "\n c_bld_beta_min option\n"); +module_param(c_bld_beta_max, uint, 0664); +MODULE_PARM_DESC(c_bld_beta_max, "\n c_bld_beta_max option\n"); +#endif + +module_init(amvenc_avc_driver_init_module); +module_exit(amvenc_avc_driver_remove_module); +RESERVEDMEM_OF_DECLARE(amvenc_avc, "amlogic, amvenc-memory", avc_mem_setup); + +MODULE_DESCRIPTION("AMLOGIC AVC Video Encoder Driver"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("simon.zheng <simon.zheng@amlogic.com>"); diff --git a/drivers/frame_sink/encoder/h264/encoder.h b/drivers/frame_sink/encoder/h264/encoder.h new file mode 100644 index 0000000..db4f255 --- a/dev/null +++ b/drivers/frame_sink/encoder/h264/encoder.h @@ -0,0 +1,465 @@ +/* + * drivers/amlogic/amports/encoder.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 __H264_H__ +#define __H264_H__ + +#include <linux/mutex.h> +#include <linux/semaphore.h> +#include <linux/list.h> +#include <linux/interrupt.h> +#include <linux/sched.h> +#include <linux/spinlock.h> +#include <linux/wait.h> +#include <linux/slab.h> +#ifdef CONFIG_AM_GE2D +#include <linux/amlogic/ge2d/ge2d.h> +#endif + +#define AMVENC_DEVINFO_M8 "AML-M8" +#define AMVENC_DEVINFO_G9 "AML-G9" +#define AMVENC_DEVINFO_GXBB "AML-GXBB" +#define AMVENC_DEVINFO_GXTVBB "AML-GXTVBB" +#define AMVENC_DEVINFO_GXL "AML-GXL" + +#define HCODEC_IRQ_MBOX_CLR HCODEC_ASSIST_MBOX2_CLR_REG +#define HCODEC_IRQ_MBOX_MASK HCODEC_ASSIST_MBOX2_MASK + +/* M8: 2550/10 = 255M GX: 2000/10 = 200M */ +#define HDEC_L0() WRITE_HHI_REG(HHI_VDEC_CLK_CNTL, \ + (2 << 25) | (1 << 16) | (1 << 24) | \ + (0xffff & READ_HHI_REG(HHI_VDEC_CLK_CNTL))) +/* M8: 2550/8 = 319M GX: 2000/8 = 250M */ +#define HDEC_L1() WRITE_HHI_REG(HHI_VDEC_CLK_CNTL, \ + (0 << 25) | (1 << 16) | (1 << 24) | \ + (0xffff & READ_HHI_REG(HHI_VDEC_CLK_CNTL))) +/* M8: 2550/7 = 364M GX: 2000/7 = 285M */ +#define HDEC_L2() WRITE_HHI_REG(HHI_VDEC_CLK_CNTL, \ + (3 << 25) | (0 << 16) | (1 << 24) | \ + (0xffff & READ_HHI_REG(HHI_VDEC_CLK_CNTL))) +/* M8: 2550/6 = 425M GX: 2000/6 = 333M */ +#define HDEC_L3() WRITE_HHI_REG(HHI_VDEC_CLK_CNTL, \ + (1 << 25) | (1 << 16) | (1 << 24) | \ + (0xffff & READ_HHI_REG(HHI_VDEC_CLK_CNTL))) +/* M8: 2550/5 = 510M GX: 2000/5 = 400M */ +#define HDEC_L4() WRITE_HHI_REG(HHI_VDEC_CLK_CNTL, \ + (2 << 25) | (0 << 16) | (1 << 24) | \ + (0xffff & READ_HHI_REG(HHI_VDEC_CLK_CNTL))) +/* M8: 2550/4 = 638M GX: 2000/4 = 500M */ +#define HDEC_L5() WRITE_HHI_REG(HHI_VDEC_CLK_CNTL, \ + (0 << 25) | (0 << 16) | (1 << 24) | \ + (0xffff & READ_HHI_REG(HHI_VDEC_CLK_CNTL))) +/* M8: 2550/3 = 850M GX: 2000/3 = 667M */ +#define HDEC_L6() WRITE_HHI_REG(HHI_VDEC_CLK_CNTL, \ + (1 << 25) | (0 << 16) | (1 << 24) | \ + (0xffff & READ_HHI_REG(HHI_VDEC_CLK_CNTL))) + +#define hvdec_clock_enable(level) \ + do { \ + if (level == 0) \ + HDEC_L0(); \ + else if (level == 1) \ + HDEC_L1(); \ + else if (level == 2) \ + HDEC_L2(); \ + else if (level == 3) \ + HDEC_L3(); \ + else if (level == 4) \ + HDEC_L4(); \ + else if (level == 5) \ + HDEC_L5(); \ + else if (level == 6) \ + HDEC_L6(); \ + WRITE_VREG_BITS(DOS_GCLK_EN0, 0x7fff, 12, 15); \ + } while (0) + +#define hvdec_clock_disable() \ + do { \ + WRITE_VREG_BITS(DOS_GCLK_EN0, 0, 12, 15); \ + WRITE_HHI_REG_BITS(HHI_VDEC_CLK_CNTL, 0, 24, 1); \ + } while (0) + +#define LOG_ALL 0 +#define LOG_INFO 1 +#define LOG_DEBUG 2 +#define LOG_ERROR 3 + +#define enc_pr(level, x...) \ + do { \ + if (level >= encode_print_level) \ + printk(x); \ + } while (0) + +#define AMVENC_AVC_IOC_MAGIC 'E' + +#define AMVENC_AVC_IOC_GET_DEVINFO _IOW(AMVENC_AVC_IOC_MAGIC, 0xf0, u32) +#define AMVENC_AVC_IOC_MAX_INSTANCE _IOW(AMVENC_AVC_IOC_MAGIC, 0xf1, u32) + +#define AMVENC_AVC_IOC_GET_ADDR _IOW(AMVENC_AVC_IOC_MAGIC, 0x00, u32) +#define AMVENC_AVC_IOC_INPUT_UPDATE _IOW(AMVENC_AVC_IOC_MAGIC, 0x01, u32) +#define AMVENC_AVC_IOC_NEW_CMD _IOW(AMVENC_AVC_IOC_MAGIC, 0x02, u32) +#define AMVENC_AVC_IOC_GET_STAGE _IOW(AMVENC_AVC_IOC_MAGIC, 0x03, u32) +#define AMVENC_AVC_IOC_GET_OUTPUT_SIZE _IOW(AMVENC_AVC_IOC_MAGIC, 0x04, u32) +#define AMVENC_AVC_IOC_CONFIG_INIT _IOW(AMVENC_AVC_IOC_MAGIC, 0x05, u32) +#define AMVENC_AVC_IOC_FLUSH_CACHE _IOW(AMVENC_AVC_IOC_MAGIC, 0x06, u32) +#define AMVENC_AVC_IOC_FLUSH_DMA _IOW(AMVENC_AVC_IOC_MAGIC, 0x07, u32) +#define AMVENC_AVC_IOC_GET_BUFFINFO _IOW(AMVENC_AVC_IOC_MAGIC, 0x08, u32) +#define AMVENC_AVC_IOC_SUBMIT _IOW(AMVENC_AVC_IOC_MAGIC, 0x09, u32) +#define AMVENC_AVC_IOC_READ_CANVAS _IOW(AMVENC_AVC_IOC_MAGIC, 0x0a, u32) + + +#define IE_PIPPELINE_BLOCK_SHIFT 0 +#define IE_PIPPELINE_BLOCK_MASK 0x1f +#define ME_PIXEL_MODE_SHIFT 5 +#define ME_PIXEL_MODE_MASK 0x3 + +enum amvenc_mem_type_e { + LOCAL_BUFF = 0, + CANVAS_BUFF, + PHYSICAL_BUFF, + MAX_BUFF_TYPE +}; + +enum amvenc_frame_fmt_e { + FMT_YUV422_SINGLE = 0, + FMT_YUV444_SINGLE, + FMT_NV21, + FMT_NV12, + FMT_YUV420, + FMT_YUV444_PLANE, + FMT_RGB888, + FMT_RGB888_PLANE, + FMT_RGB565, + FMT_RGBA8888, + FMT_YUV422_12BIT, + FMT_YUV444_10BIT, + FMT_YUV422_10BIT, + MAX_FRAME_FMT +}; + +#define MAX_ENCODE_REQUEST 8 /* 64 */ + +#define MAX_ENCODE_INSTANCE 8 /* 64 */ + +#define ENCODE_PROCESS_QUEUE_START 0 +#define ENCODE_PROCESS_QUEUE_STOP 1 + +#define AMVENC_FLUSH_FLAG_INPUT 0x1 +#define AMVENC_FLUSH_FLAG_OUTPUT 0x2 +#define AMVENC_FLUSH_FLAG_REFERENCE 0x4 +#define AMVENC_FLUSH_FLAG_INTRA_INFO 0x8 +#define AMVENC_FLUSH_FLAG_INTER_INFO 0x10 +#define AMVENC_FLUSH_FLAG_QP 0x20 +#define AMVENC_FLUSH_FLAG_DUMP 0x40 +#define AMVENC_FLUSH_FLAG_CBR 0x80 + +#define ENCODER_BUFFER_INPUT 0 +#define ENCODER_BUFFER_REF0 1 +#define ENCODER_BUFFER_REF1 2 +#define ENCODER_BUFFER_OUTPUT 3 +#define ENCODER_BUFFER_INTER_INFO 4 +#define ENCODER_BUFFER_INTRA_INFO 5 +#define ENCODER_BUFFER_QP 6 +#define ENCODER_BUFFER_DUMP 7 +#define ENCODER_BUFFER_CBR 8 + +struct encode_wq_s; + +struct encode_request_s { + u32 quant; + u32 cmd; + u32 ucode_mode; + + u32 src; + + u32 framesize; + + u32 me_weight; + u32 i4_weight; + u32 i16_weight; + + u32 crop_top; + u32 crop_bottom; + u32 crop_left; + u32 crop_right; + u32 src_w; + u32 src_h; + u32 scale_enable; + + u32 nr_mode; + u32 flush_flag; + u32 timeout; + enum amvenc_mem_type_e type; + enum amvenc_frame_fmt_e fmt; + struct encode_wq_s *parent; +}; + +struct encode_queue_item_s { + struct list_head list; + struct encode_request_s request; +}; + +struct Buff_s { + u32 buf_start; + u32 buf_size; + bool used; +}; + +struct BuffInfo_s { + u32 lev_id; + u32 min_buffsize; + u32 max_width; + u32 max_height; + struct Buff_s dct; + struct Buff_s dec0_y; + struct Buff_s dec0_uv; + struct Buff_s dec1_y; + struct Buff_s dec1_uv; + struct Buff_s assit; + struct Buff_s bitstream; + struct Buff_s scale_buff; + struct Buff_s dump_info; + struct Buff_s cbr_info; +}; + +struct encode_meminfo_s { + u32 buf_start; + u32 buf_size; + + u32 BitstreamStart; + u32 BitstreamEnd; + + /*input buffer define*/ + u32 dct_buff_start_addr; + u32 dct_buff_end_addr; + + /*microcode assitant buffer*/ + u32 assit_buffer_offset; + + u32 scaler_buff_start_addr; + + u32 dump_info_ddr_start_addr; + u32 dump_info_ddr_size; + + u32 cbr_info_ddr_start_addr; + u32 cbr_info_ddr_size; + + s32 dblk_buf_canvas; + s32 ref_buf_canvas; + struct BuffInfo_s bufspec; +#ifdef CONFIG_CMA + struct page *venc_pages; +#endif +}; + +struct encode_picinfo_s { + u32 encoder_width; + u32 encoder_height; + + u32 rows_per_slice; + + u32 idr_pic_id; /* need reset as 0 for IDR */ + u32 frame_number; /* need plus each frame */ + /* need reset as 0 for IDR and plus 2 for NON-IDR */ + u32 pic_order_cnt_lsb; + + u32 log2_max_pic_order_cnt_lsb; + u32 log2_max_frame_num; + u32 init_qppicture; +}; + +struct encode_cbr_s { + u16 block_w; + u16 block_h; + u16 long_th; + u8 start_tbl_id; + u8 short_shift; + u8 long_mb_num; +}; + +struct encode_wq_s { + struct list_head list; + + /* dev info */ + u32 ucode_index; + u32 hw_status; + u32 output_size; + + u32 sps_size; + u32 pps_size; + + u32 me_weight; + u32 i4_weight; + u32 i16_weight; + + u32 quant_tbl_i4[8]; + u32 quant_tbl_i16[8]; + u32 quant_tbl_me[8]; + + struct encode_meminfo_s mem; + struct encode_picinfo_s pic; + struct encode_request_s request; + struct encode_cbr_s cbr_info; + atomic_t request_ready; + wait_queue_head_t request_complete; +}; + +struct encode_event_s { + wait_queue_head_t hw_complete; + struct completion process_complete; + spinlock_t sem_lock; /* for queue switch and create destroy queue. */ + struct completion request_in_com; +}; + +struct encode_manager_s { + struct list_head wq; + struct list_head process_queue; + struct list_head free_queue; + + u32 encode_hw_status; + u32 process_queue_state; + s32 irq_num; + u32 wq_count; + u32 ucode_index; + u32 max_instance; +#ifdef CONFIG_AM_GE2D + struct ge2d_context_s *context; +#endif + bool irq_requested; + bool need_reset; + bool process_irq; + bool inited; /* power on encode */ + bool remove_flag; /* remove wq; */ + bool uninit_flag; /* power off encode */ + bool use_reserve; + +#ifdef CONFIG_CMA + bool check_cma; + ulong cma_pool_size; +#endif + struct platform_device *this_pdev; + struct Buff_s *reserve_buff; + struct encode_wq_s *current_wq; + struct encode_wq_s *last_wq; + struct encode_queue_item_s *current_item; + struct task_struct *encode_thread; + struct Buff_s reserve_mem; + struct encode_event_s event; + struct tasklet_struct encode_tasklet; +}; + +extern s32 encode_wq_add_request(struct encode_wq_s *wq); +extern struct encode_wq_s *create_encode_work_queue(void); +extern s32 destroy_encode_work_queue(struct encode_wq_s *encode_work_queue); + +/******************************************** + * AV Scratch Register Re-Define +********************************************/ +#define ENCODER_STATUS HCODEC_HENC_SCRATCH_0 +#define MEM_OFFSET_REG HCODEC_HENC_SCRATCH_1 +#define DEBUG_REG HCODEC_HENC_SCRATCH_2 +#define IDR_PIC_ID HCODEC_HENC_SCRATCH_5 +#define FRAME_NUMBER HCODEC_HENC_SCRATCH_6 +#define PIC_ORDER_CNT_LSB HCODEC_HENC_SCRATCH_7 +#define LOG2_MAX_PIC_ORDER_CNT_LSB HCODEC_HENC_SCRATCH_8 +#define LOG2_MAX_FRAME_NUM HCODEC_HENC_SCRATCH_9 +#define ANC0_BUFFER_ID HCODEC_HENC_SCRATCH_A +#define QPPICTURE HCODEC_HENC_SCRATCH_B + +#define IE_ME_MB_TYPE HCODEC_HENC_SCRATCH_D + +/* bit 0-4, IE_PIPPELINE_BLOCK + * bit 5 me half pixel in m8 + * disable i4x4 in gxbb + * bit 6 me step2 sub pixel in m8 + * disable i16x16 in gxbb + */ +#define IE_ME_MODE HCODEC_HENC_SCRATCH_E +#define IE_REF_SEL HCODEC_HENC_SCRATCH_F + +/* [31:0] NUM_ROWS_PER_SLICE_P */ +/* [15:0] NUM_ROWS_PER_SLICE_I */ +#define FIXED_SLICE_CFG HCODEC_HENC_SCRATCH_L + +/* For GX */ +#define INFO_DUMP_START_ADDR HCODEC_HENC_SCRATCH_I + +/* For CBR */ +#define H264_ENC_CBR_TABLE_ADDR HCODEC_HENC_SCRATCH_3 +#define H264_ENC_CBR_MB_SIZE_ADDR HCODEC_HENC_SCRATCH_4 +/* Bytes(Float) * 256 */ +#define H264_ENC_CBR_CTL HCODEC_HENC_SCRATCH_G +/* [31:28] : init qp table idx */ +/* [27:24] : short_term adjust shift */ +/* [23:16] : Long_term MB_Number between adjust, */ +/* [15:0] Long_term adjust threshold(Bytes) */ +#define H264_ENC_CBR_TARGET_SIZE HCODEC_HENC_SCRATCH_H +/* Bytes(Float) * 256 */ +#define H264_ENC_CBR_PREV_BYTES HCODEC_HENC_SCRATCH_J +#define H264_ENC_CBR_REGION_SIZE HCODEC_HENC_SCRATCH_J + +/* --------------------------------------------------- */ +/* ENCODER_STATUS define */ +/* --------------------------------------------------- */ +#define ENCODER_IDLE 0 +#define ENCODER_SEQUENCE 1 +#define ENCODER_PICTURE 2 +#define ENCODER_IDR 3 +#define ENCODER_NON_IDR 4 +#define ENCODER_MB_HEADER 5 +#define ENCODER_MB_DATA 6 + +#define ENCODER_SEQUENCE_DONE 7 +#define ENCODER_PICTURE_DONE 8 +#define ENCODER_IDR_DONE 9 +#define ENCODER_NON_IDR_DONE 10 +#define ENCODER_MB_HEADER_DONE 11 +#define ENCODER_MB_DATA_DONE 12 + +#define ENCODER_NON_IDR_INTRA 13 +#define ENCODER_NON_IDR_INTER 14 + +#define ENCODER_ERROR 0xff + +/******************************************** +* defines for H.264 mb_type +********************************************/ +#define HENC_MB_Type_PBSKIP 0x0 +#define HENC_MB_Type_PSKIP 0x0 +#define HENC_MB_Type_BSKIP_DIRECT 0x0 +#define HENC_MB_Type_P16x16 0x1 +#define HENC_MB_Type_P16x8 0x2 +#define HENC_MB_Type_P8x16 0x3 +#define HENC_MB_Type_SMB8x8 0x4 +#define HENC_MB_Type_SMB8x4 0x5 +#define HENC_MB_Type_SMB4x8 0x6 +#define HENC_MB_Type_SMB4x4 0x7 +#define HENC_MB_Type_P8x8 0x8 +#define HENC_MB_Type_I4MB 0x9 +#define HENC_MB_Type_I16MB 0xa +#define HENC_MB_Type_IBLOCK 0xb +#define HENC_MB_Type_SI4MB 0xc +#define HENC_MB_Type_I8MB 0xd +#define HENC_MB_Type_IPCM 0xe +#define HENC_MB_Type_AUTO 0xf + +#define HENC_MB_CBP_AUTO 0xff +#define HENC_SKIP_RUN_AUTO 0xffff + + +extern bool amvenc_avc_on(void); +#endif diff --git a/drivers/frame_sink/encoder/h265/Makefile b/drivers/frame_sink/encoder/h265/Makefile new file mode 100644 index 0000000..e7414bf --- a/dev/null +++ b/drivers/frame_sink/encoder/h265/Makefile @@ -0,0 +1 @@ +obj-m += vpu.o diff --git a/drivers/frame_sink/encoder/h265/vmm.h b/drivers/frame_sink/encoder/h265/vmm.h new file mode 100644 index 0000000..cb0112e --- a/dev/null +++ b/drivers/frame_sink/encoder/h265/vmm.h @@ -0,0 +1,661 @@ +/* + * vmm.h + * + * memory allocator for VPU + * + * Copyright (C) 2006 - 2013 CHIPS&MEDIA INC. + * + * 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 __CNM_VIDEO_MEMORY_MANAGEMENT_H__ +#define __CNM_VIDEO_MEMORY_MANAGEMENT_H__ + +#define VMEM_PAGE_SIZE (16 * 1024) +#define MAKE_KEY(_a, _b) (((vmem_key_t)_a) << 32 | _b) +#define KEY_TO_VALUE(_key) (_key >> 32) + +#define VMEM_P_ALLOC(_x) vmalloc(_x) +#define VMEM_P_FREE(_x) vfree(_x) + +#define VMEM_ASSERT \ + pr_info("VMEM_ASSERT at %s:%d\n", __FILE__, __LINE__) + + +#define VMEM_HEIGHT(_tree) (_tree == NULL ? -1 : _tree->height) + +#define MAX(_a, _b) (_a >= _b ? _a : _b) + +struct avl_node_t; +#define vmem_key_t unsigned long long + +struct vmem_info_t { + ulong total_pages; + ulong alloc_pages; + ulong free_pages; + ulong page_size; +}; + +struct page_t { + s32 pageno; + ulong addr; + s32 used; + s32 alloc_pages; + s32 first_pageno; +}; + +struct avl_node_t { + vmem_key_t key; + s32 height; + struct page_t *page; + struct avl_node_t *left; + struct avl_node_t *right; +}; + +struct video_mm_t { + struct avl_node_t *free_tree; + struct avl_node_t *alloc_tree; + struct page_t *page_list; + s32 num_pages; + ulong base_addr; + ulong mem_size; + s32 free_page_count; + s32 alloc_page_count; +}; + +enum rotation_dir_t { + LEFT, + RIGHT +}; + +struct avl_node_data_t { + s32 key; + struct page_t *page; +}; + +static struct avl_node_t *make_avl_node( + vmem_key_t key, + struct page_t *page) +{ + struct avl_node_t *node = + (struct avl_node_t *)VMEM_P_ALLOC(sizeof(struct avl_node_t)); + node->key = key; + node->page = page; + node->height = 0; + node->left = NULL; + node->right = NULL; + return node; +} + +static s32 get_balance_factor(struct avl_node_t *tree) +{ + s32 factor = 0; + if (tree) + factor = VMEM_HEIGHT(tree->right) - VMEM_HEIGHT(tree->left); + return factor; +} + +/* + * Left Rotation + * + * A B + * \ / \ + * B => A C + * / \ \ + * D C D + * + */ +static struct avl_node_t *rotation_left(struct avl_node_t *tree) +{ + struct avl_node_t *rchild; + struct avl_node_t *lchild; + + if (tree == NULL) + return NULL; + + rchild = tree->right; + if (rchild == NULL) + return tree; + + lchild = rchild->left; + rchild->left = tree; + tree->right = lchild; + + tree->height = + MAX(VMEM_HEIGHT(tree->left), VMEM_HEIGHT(tree->right)) + 1; + rchild->height = + MAX(VMEM_HEIGHT(rchild->left), VMEM_HEIGHT(rchild->right)) + 1; + return rchild; +} + + +/* + * Reft Rotation + * + * A B + * \ / \ + * B => D A + * / \ / + * D C C + * + */ +static struct avl_node_t *rotation_right(struct avl_node_t *tree) +{ + struct avl_node_t *rchild; + struct avl_node_t *lchild; + + if (tree == NULL) + return NULL; + + lchild = tree->left; + if (lchild == NULL) + return NULL; + + rchild = lchild->right; + lchild->right = tree; + tree->left = rchild; + + tree->height = + MAX(VMEM_HEIGHT(tree->left), + VMEM_HEIGHT(tree->right)) + 1; + lchild->height = + MAX(VMEM_HEIGHT(lchild->left), + VMEM_HEIGHT(lchild->right)) + 1; + return lchild; +} + +static struct avl_node_t *do_balance(struct avl_node_t *tree) +{ + s32 bfactor = 0, child_bfactor; + bfactor = get_balance_factor(tree); + if (bfactor >= 2) { + child_bfactor = get_balance_factor(tree->right); + if (child_bfactor == 1 || child_bfactor == 0) { + tree = rotation_left(tree); + } else if (child_bfactor == -1) { + tree->right = rotation_right(tree->right); + tree = rotation_left(tree); + } else { + pr_info( + "invalid balancing factor: %d\n", + child_bfactor); + VMEM_ASSERT; + return NULL; + } + } else if (bfactor <= -2) { + child_bfactor = get_balance_factor(tree->left); + if (child_bfactor == -1 || child_bfactor == 0) { + tree = rotation_right(tree); + } else if (child_bfactor == 1) { + tree->left = rotation_left(tree->left); + tree = rotation_right(tree); + } else { + pr_info( + "invalid balancing factor: %d\n", + child_bfactor); + VMEM_ASSERT; + return NULL; + } + } + return tree; +} + +static struct avl_node_t *unlink_end_node( + struct avl_node_t *tree, + s32 dir, + struct avl_node_t **found_node) +{ + struct avl_node_t *node; + *found_node = NULL; + + if (tree == NULL) + return NULL; + + if (dir == LEFT) { + if (tree->left == NULL) { + *found_node = tree; + return NULL; + } + } else { + if (tree->right == NULL) { + *found_node = tree; + return NULL; + } + } + + if (dir == LEFT) { + node = tree->left; + tree->left = unlink_end_node(tree->left, LEFT, found_node); + if (tree->left == NULL) { + tree->left = (*found_node)->right; + (*found_node)->left = NULL; + (*found_node)->right = NULL; + } + } else { + node = tree->right; + tree->right = unlink_end_node(tree->right, RIGHT, found_node); + if (tree->right == NULL) { + tree->right = (*found_node)->left; + (*found_node)->left = NULL; + (*found_node)->right = NULL; + } + } + tree->height = + MAX(VMEM_HEIGHT(tree->left), VMEM_HEIGHT(tree->right)) + 1; + return do_balance(tree); +} + + +static struct avl_node_t *avltree_insert( + struct avl_node_t *tree, + vmem_key_t key, + struct page_t *page) +{ + if (tree == NULL) { + tree = make_avl_node(key, page); + } else { + if (key >= tree->key) + tree->right = + avltree_insert(tree->right, key, page); + else + tree->left = + avltree_insert(tree->left, key, page); + } + tree = do_balance(tree); + tree->height = + MAX(VMEM_HEIGHT(tree->left), VMEM_HEIGHT(tree->right)) + 1; + return tree; +} + +static struct avl_node_t *do_unlink(struct avl_node_t *tree) +{ + struct avl_node_t *node; + struct avl_node_t *end_node; + node = unlink_end_node(tree->right, LEFT, &end_node); + if (node) { + tree->right = node; + } else { + node = + unlink_end_node(tree->left, RIGHT, &end_node); + if (node) + tree->left = node; + } + + if (node == NULL) { + node = tree->right ? tree->right : tree->left; + end_node = node; + } + + if (end_node) { + end_node->left = + (tree->left != end_node) ? + tree->left : end_node->left; + end_node->right = + (tree->right != end_node) ? + tree->right : end_node->right; + end_node->height = + MAX(VMEM_HEIGHT(end_node->left), + VMEM_HEIGHT(end_node->right)) + 1; + } + tree = end_node; + return tree; +} + +static struct avl_node_t *avltree_remove( + struct avl_node_t *tree, + struct avl_node_t **found_node, + vmem_key_t key) +{ + *found_node = NULL; + if (tree == NULL) { + pr_info("failed to find key %d\n", (s32)key); + return NULL; + } + + if (key == tree->key) { + *found_node = tree; + tree = do_unlink(tree); + } else if (key > tree->key) { + tree->right = + avltree_remove(tree->right, found_node, key); + } else { + tree->left = + avltree_remove(tree->left, found_node, key); + } + + if (tree) + tree->height = + MAX(VMEM_HEIGHT(tree->left), + VMEM_HEIGHT(tree->right)) + 1; + + tree = do_balance(tree); + return tree; +} + +void avltree_free(struct avl_node_t *tree) +{ + if (tree == NULL) + return; + if (tree->left == NULL && tree->right == NULL) { + VMEM_P_FREE(tree); + return; + } + + avltree_free(tree->left); + tree->left = NULL; + avltree_free(tree->right); + tree->right = NULL; + VMEM_P_FREE(tree); +} + +static struct avl_node_t *remove_approx_value( + struct avl_node_t *tree, + struct avl_node_t **found, + vmem_key_t key) +{ + *found = NULL; + if (tree == NULL) + return NULL; + + if (key == tree->key) { + *found = tree; + tree = do_unlink(tree); + } else if (key > tree->key) { + tree->right = remove_approx_value(tree->right, found, key); + } else { + tree->left = remove_approx_value(tree->left, found, key); + if (*found == NULL) { + *found = tree; + tree = do_unlink(tree); + } + } + if (tree) + tree->height = + MAX(VMEM_HEIGHT(tree->left), + VMEM_HEIGHT(tree->right)) + 1; + tree = do_balance(tree); + return tree; +} + +static void set_blocks_free( + struct video_mm_t *mm, + s32 pageno, + s32 npages) +{ + s32 last_pageno = pageno + npages - 1; + s32 i; + struct page_t *page; + struct page_t *last_page; + + if (npages == 0) + VMEM_ASSERT; + + if (last_pageno >= mm->num_pages) { + pr_info( + "set_blocks_free: invalid last page number: %d\n", + last_pageno); + VMEM_ASSERT; + return; + } + + for (i = pageno; i <= last_pageno; i++) { + mm->page_list[i].used = 0; + mm->page_list[i].alloc_pages = 0; + mm->page_list[i].first_pageno = -1; + } + + page = &mm->page_list[pageno]; + page->alloc_pages = npages; + last_page = &mm->page_list[last_pageno]; + last_page->first_pageno = pageno; + mm->free_tree = + avltree_insert(mm->free_tree, MAKE_KEY(npages, pageno), page); +} + +static void set_blocks_alloc( + struct video_mm_t *mm, + s32 pageno, + s32 npages) +{ + s32 last_pageno = pageno + npages - 1; + s32 i; + struct page_t *page; + struct page_t *last_page; + + if (last_pageno >= mm->num_pages) { + pr_info( + "set_blocks_free: invalid last page number: %d\n", + last_pageno); + VMEM_ASSERT; + return; + } + + for (i = pageno; i <= last_pageno; i++) { + mm->page_list[i].used = 1; + mm->page_list[i].alloc_pages = 0; + mm->page_list[i].first_pageno = -1; + } + + page = &mm->page_list[pageno]; + page->alloc_pages = npages; + last_page = &mm->page_list[last_pageno]; + last_page->first_pageno = pageno; + mm->alloc_tree = + avltree_insert(mm->alloc_tree, MAKE_KEY(page->addr, 0), page); +} + + +s32 vmem_init(struct video_mm_t *mm, ulong addr, ulong size) +{ + s32 i; + + if (NULL == mm) + return -1; + + mm->base_addr = (addr + (VMEM_PAGE_SIZE - 1)) + & ~(VMEM_PAGE_SIZE - 1); + mm->mem_size = size & ~VMEM_PAGE_SIZE; + mm->num_pages = mm->mem_size / VMEM_PAGE_SIZE; + mm->free_tree = NULL; + mm->alloc_tree = NULL; + mm->free_page_count = mm->num_pages; + mm->alloc_page_count = 0; + mm->page_list = + (struct page_t *)VMEM_P_ALLOC( + mm->num_pages * sizeof(struct page_t)); + if (mm->page_list == NULL) { + pr_err("%s:%d failed to kmalloc(%ld)\n", + __func__, __LINE__, + mm->num_pages * sizeof(struct page_t)); + return -1; + } + + for (i = 0; i < mm->num_pages; i++) { + mm->page_list[i].pageno = i; + mm->page_list[i].addr = + mm->base_addr + i * VMEM_PAGE_SIZE; + mm->page_list[i].alloc_pages = 0; + mm->page_list[i].used = 0; + mm->page_list[i].first_pageno = -1; + } + set_blocks_free(mm, 0, mm->num_pages); + return 0; +} + +s32 vmem_exit(struct video_mm_t *mm) +{ + if (mm == NULL) { + pr_info("vmem_exit: invalid handle\n"); + return -1; + } + + if (mm->free_tree) + avltree_free(mm->free_tree); + if (mm->alloc_tree) + avltree_free(mm->alloc_tree); + + if (mm->page_list) { + VMEM_P_FREE(mm->page_list); + mm->page_list = NULL; + } + + mm->base_addr = 0; + mm->mem_size = 0; + mm->num_pages = 0; + mm->page_list = NULL; + mm->free_tree = NULL; + mm->alloc_tree = NULL; + mm->free_page_count = 0; + mm->alloc_page_count = 0; + return 0; +} + +ulong vmem_alloc(struct video_mm_t *mm, s32 size, ulong pid) +{ + struct avl_node_t *node; + struct page_t *free_page; + s32 npages, free_size; + s32 alloc_pageno; + ulong ptr; + + if (mm == NULL) { + pr_info("vmem_alloc: invalid handle\n"); + return -1; + } + + if (size <= 0) + return -1; + + npages = (size + VMEM_PAGE_SIZE - 1) / VMEM_PAGE_SIZE; + mm->free_tree = remove_approx_value(mm->free_tree, + &node, MAKE_KEY(npages, 0)); + + if (node == NULL) + return -1; + + free_page = node->page; + free_size = KEY_TO_VALUE(node->key); + alloc_pageno = free_page->pageno; + set_blocks_alloc(mm, alloc_pageno, npages); + if (npages != free_size) { + s32 free_pageno = alloc_pageno + npages; + set_blocks_free(mm, free_pageno, (free_size-npages)); + } + VMEM_P_FREE(node); + + ptr = mm->page_list[alloc_pageno].addr; + mm->alloc_page_count += npages; + mm->free_page_count -= npages; + return ptr; +} + +s32 vmem_free(struct video_mm_t *mm, ulong ptr, ulong pid) +{ + ulong addr; + struct avl_node_t *found; + struct page_t *page; + s32 pageno, prev_free_pageno, next_free_pageno; + s32 prev_size, next_size; + s32 merge_page_no, merge_page_size, free_page_size; + + if (mm == NULL) { + pr_info("vmem_free: invalid handle\n"); + return -1; + } + + addr = ptr; + mm->alloc_tree = avltree_remove(mm->alloc_tree, &found, + MAKE_KEY(addr, 0)); + + if (found == NULL) { + pr_info("vmem_free: 0x%08x not found\n", (s32)addr); + VMEM_ASSERT; + return -1; + } + + /* find previous free block */ + page = found->page; + pageno = page->pageno; + free_page_size = page->alloc_pages; + prev_free_pageno = pageno - 1; + prev_size = -1; + if (prev_free_pageno >= 0) { + if (mm->page_list[prev_free_pageno].used == 0) { + prev_free_pageno = + mm->page_list[prev_free_pageno].first_pageno; + prev_size = + mm->page_list[prev_free_pageno].alloc_pages; + } + } + + /* find next free block */ + next_free_pageno = pageno + page->alloc_pages; + next_free_pageno = + (next_free_pageno == mm->num_pages) ? -1 : next_free_pageno; + next_size = -1; + if (next_free_pageno >= 0) { + if (mm->page_list[next_free_pageno].used == 0) { + next_size = + mm->page_list[next_free_pageno].alloc_pages; + } + } + VMEM_P_FREE(found); + + /* merge */ + merge_page_no = page->pageno; + merge_page_size = page->alloc_pages; + if (prev_size >= 0) { + mm->free_tree = avltree_remove(mm->free_tree, &found, + MAKE_KEY(prev_size, prev_free_pageno)); + if (found == NULL) { + VMEM_ASSERT; + return -1; + } + merge_page_no = found->page->pageno; + merge_page_size += found->page->alloc_pages; + VMEM_P_FREE(found); + } + if (next_size >= 0) { + mm->free_tree = avltree_remove(mm->free_tree, &found, + MAKE_KEY(next_size, next_free_pageno)); + if (found == NULL) { + VMEM_ASSERT; + return -1; + } + merge_page_size += found->page->alloc_pages; + VMEM_P_FREE(found); + } + page->alloc_pages = 0; + page->first_pageno = -1; + set_blocks_free(mm, merge_page_no, merge_page_size); + mm->alloc_page_count -= free_page_size; + mm->free_page_count += free_page_size; + return 0; +} + +s32 vmem_get_info(struct video_mm_t *mm, struct vmem_info_t *info) +{ + if (mm == NULL) { + pr_info("vmem_get_info: invalid handle\n"); + return -1; + } + + if (info == NULL) + return -1; + + info->total_pages = mm->num_pages; + info->alloc_pages = mm->alloc_page_count; + info->free_pages = mm->free_page_count; + info->page_size = VMEM_PAGE_SIZE; + return 0; +} +#endif /* __CNM_VIDEO_MEMORY_MANAGEMENT_H__ */ diff --git a/drivers/frame_sink/encoder/h265/vpu.c b/drivers/frame_sink/encoder/h265/vpu.c new file mode 100644 index 0000000..26c40fd --- a/dev/null +++ b/drivers/frame_sink/encoder/h265/vpu.c @@ -0,0 +1,1997 @@ +/* + * vpu.c + * + * linux device driver for VPU. + * + * Copyright (C) 2006 - 2013 CHIPS&MEDIA INC. + * + * 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/mm.h> +#include <linux/interrupt.h> +#include <linux/ioport.h> +#include <linux/module.h> +#include <linux/dma-mapping.h> +#include <linux/wait.h> +#include <linux/list.h> +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/uaccess.h> +#include <linux/cdev.h> +#include <linux/slab.h> +#include <linux/sched.h> +#include <linux/platform_device.h> +#include <linux/of.h> +#include <linux/of_fdt.h> +#include <linux/reset.h> +#include <linux/clk.h> +#include <linux/compat.h> +#include <linux/of_reserved_mem.h> +#include <linux/of_address.h> +#include <linux/amlogic/media/codec_mm/codec_mm.h> + +#include <linux/amlogic/media/utils/vdec_reg.h> +#include "../../../common/media_clock/switch/amports_gate.h" + +#include "vpu.h" +#include "vmm.h" + +/* definitions to be changed as customer configuration */ +/* if you want to have clock gating scheme frame by frame */ +/* #define VPU_SUPPORT_CLOCK_CONTROL */ + +#define VPU_PLATFORM_DEVICE_NAME "HevcEnc" +#define VPU_DEV_NAME "HevcEnc" +#define VPU_CLASS_NAME "HevcEnc" + +#ifndef VM_RESERVED /*for kernel up to 3.7.0 version*/ +#define VM_RESERVED (VM_DONTEXPAND | VM_DONTDUMP) +#endif + +#define VPU_INIT_VIDEO_MEMORY_SIZE_IN_BYTE (64 * SZ_1M) + +#define LOG_ALL 0 +#define LOG_INFO 1 +#define LOG_DEBUG 2 +#define LOG_ERROR 3 + +#define enc_pr(level, x...) \ + do { \ + if (level >= print_level) \ + printk(x); \ + } while (0) + +static s32 print_level = LOG_DEBUG; +static s32 clock_level = 4; + +static struct video_mm_t s_vmem; +static struct vpudrv_buffer_t s_video_memory = {0}; +static bool use_reserve; +static ulong cma_pool_size; + +/* end customer definition */ +static struct vpudrv_buffer_t s_instance_pool = {0}; +static struct vpudrv_buffer_t s_common_memory = {0}; +static struct vpu_drv_context_t s_vpu_drv_context; +static s32 s_vpu_major; +static struct device *hevcenc_dev; + +static s32 s_vpu_open_ref_count; +static s32 s_vpu_irq; +static bool s_vpu_irq_requested; + +static struct vpudrv_buffer_t s_vpu_register = {0}; + +static s32 s_interrupt_flag; +static wait_queue_head_t s_interrupt_wait_q; + +static spinlock_t s_vpu_lock = __SPIN_LOCK_UNLOCKED(s_vpu_lock); +static DEFINE_SEMAPHORE(s_vpu_sem); +static struct list_head s_vbp_head = LIST_HEAD_INIT(s_vbp_head); +static struct list_head s_inst_list_head = LIST_HEAD_INIT(s_inst_list_head); +static struct tasklet_struct hevc_tasklet; +static struct platform_device *hevc_pdev; + +static struct vpu_bit_firmware_info_t s_bit_firmware_info[MAX_NUM_VPU_CORE]; + +static void dma_flush(u32 buf_start , u32 buf_size) +{ + if (hevc_pdev) + dma_sync_single_for_device( + &hevc_pdev->dev, buf_start, + buf_size, DMA_TO_DEVICE); +} + +static void cache_flush(u32 buf_start , u32 buf_size) +{ + if (hevc_pdev) + dma_sync_single_for_cpu( + &hevc_pdev->dev, buf_start, + buf_size, DMA_FROM_DEVICE); +} + +s32 vpu_hw_reset(void) +{ + enc_pr(LOG_DEBUG, "request vpu reset from application.\n"); + return 0; +} + +s32 vpu_clk_config(u32 enable) +{ + if (enable) + HevcEnc_clock_enable(clock_level); + else + HevcEnc_clock_disable(); + return 0; +} + +static s32 vpu_alloc_dma_buffer(struct vpudrv_buffer_t *vb) +{ + if (!vb) + return -1; + + vb->phys_addr = (ulong)vmem_alloc(&s_vmem, vb->size, 0); + if ((ulong)vb->phys_addr == (ulong)-1) { + enc_pr(LOG_ERROR, + "Physical memory allocation error size=%d\n", vb->size); + return -1; + } + + vb->base = (ulong)(s_video_memory.base + + (vb->phys_addr - s_video_memory.phys_addr)); + return 0; +} + +static void vpu_free_dma_buffer(struct vpudrv_buffer_t *vb) +{ + if (!vb) + return; + + if (vb->base) + vmem_free(&s_vmem, vb->phys_addr, 0); +} + +static s32 vpu_free_instances(struct file *filp) +{ + struct vpudrv_instanace_list_t *vil, *n; + struct vpudrv_instance_pool_t *vip; + void *vip_base; + + enc_pr(LOG_DEBUG, "vpu_free_instances\n"); + + list_for_each_entry_safe(vil, n, &s_inst_list_head, list) + { + if (vil->filp == filp) { + vip_base = (void *)s_instance_pool.base; + enc_pr(LOG_INFO, + "free_instances instIdx=%d, coreIdx=%d, vip_base=%p\n", + (s32)vil->inst_idx, + (s32)vil->core_idx, + vip_base); + vip = (struct vpudrv_instance_pool_t *)vip_base; + if (vip) { + /* only first 4 byte is key point + (inUse of CodecInst in vpuapi) + to free the corresponding instance. */ + memset(&vip->codecInstPool[vil->inst_idx], + 0x00, 4); + } + s_vpu_open_ref_count--; + list_del(&vil->list); + kfree(vil); + } + } + return 1; +} + +static s32 vpu_free_buffers(struct file *filp) +{ + struct vpudrv_buffer_pool_t *pool, *n; + struct vpudrv_buffer_t vb; + + enc_pr(LOG_DEBUG, "vpu_free_buffers\n"); + + list_for_each_entry_safe(pool, n, &s_vbp_head, list) + { + if (pool->filp == filp) { + vb = pool->vb; + if (vb.base) { + vpu_free_dma_buffer(&vb); + list_del(&pool->list); + kfree(pool); + } + } + } + return 0; +} + +static u32 vpu_is_buffer_cached(struct file *filp, ulong vm_pgoff) +{ + struct vpudrv_buffer_pool_t *pool, *n; + struct vpudrv_buffer_t vb; + bool find = false; + u32 cached = 0; + + enc_pr(LOG_ALL, "[+]vpu_is_buffer_cached\n"); + spin_lock(&s_vpu_lock); + list_for_each_entry_safe(pool, n, &s_vbp_head, list) + { + if (pool->filp == filp) { + vb = pool->vb; + if (((vb.phys_addr >> PAGE_SHIFT) == vm_pgoff) + && find == false){ + cached = vb.cached; + find = true; + } + } + } + spin_unlock(&s_vpu_lock); + enc_pr(LOG_ALL, "[-]vpu_is_buffer_cached, ret:%d\n", cached); + return cached; +} + +static void hevcenc_isr_tasklet(ulong data) +{ + struct vpu_drv_context_t *dev = (struct vpu_drv_context_t *)data; + enc_pr(LOG_INFO, "hevcenc_isr_tasklet interruput:0x%08lx\n", + dev->interrupt_reason); + if (dev->interrupt_reason) { + /* notify the interrupt to user space */ + if (dev->async_queue) { + enc_pr(LOG_ALL, "kill_fasync e %s\n", __func__); + kill_fasync(&dev->async_queue, SIGIO, POLL_IN); + } + s_interrupt_flag = 1; + wake_up_interruptible(&s_interrupt_wait_q); + } + enc_pr(LOG_ALL, "[-]%s\n", __func__); +} + +static irqreturn_t vpu_irq_handler(s32 irq, void *dev_id) +{ + struct vpu_drv_context_t *dev = (struct vpu_drv_context_t *)dev_id; + /* this can be removed. + it also work in VPU_WaitInterrupt of API function */ + u32 core; + ulong interrupt_reason = 0; + enc_pr(LOG_ALL, "[+]%s\n", __func__); + + for (core = 0; core < MAX_NUM_VPU_CORE; core++) { + if (s_bit_firmware_info[core].size == 0) { + /* it means that we didn't get an information + the current core from API layer. + No core activated.*/ + enc_pr(LOG_ERROR, + "s_bit_firmware_info[core].size is zero\n"); + continue; + } + if (ReadVpuRegister(W4_VPU_VPU_INT_STS)) { + interrupt_reason = ReadVpuRegister(W4_VPU_INT_REASON); + WriteVpuRegister(W4_VPU_INT_REASON_CLEAR, + interrupt_reason); + WriteVpuRegister(W4_VPU_VINT_CLEAR, 0x1); + dev->interrupt_reason |= interrupt_reason; + } + enc_pr(LOG_INFO, + "intr_reason: 0x%08lx\n", dev->interrupt_reason); + } + if (dev->interrupt_reason) + tasklet_schedule(&hevc_tasklet); + enc_pr(LOG_ALL, "[-]%s\n", __func__); + return IRQ_HANDLED; +} + +static s32 vpu_open(struct inode *inode, struct file *filp) +{ + bool alloc_buffer = false; + s32 r = 0; + enc_pr(LOG_DEBUG, "[+] %s\n", __func__); + spin_lock(&s_vpu_lock); + s_vpu_drv_context.open_count++; + if (s_vpu_drv_context.open_count == 1) { + alloc_buffer = true; + } else { + r = -EBUSY; + s_vpu_drv_context.open_count--; + spin_unlock(&s_vpu_lock); + goto Err; + } + filp->private_data = (void *)(&s_vpu_drv_context); + spin_unlock(&s_vpu_lock); + if (alloc_buffer && !use_reserve) { +#ifdef CONFIG_CMA + s_video_memory.size = VPU_INIT_VIDEO_MEMORY_SIZE_IN_BYTE; + s_video_memory.phys_addr = + (ulong)codec_mm_alloc_for_dma(VPU_DEV_NAME, + VPU_INIT_VIDEO_MEMORY_SIZE_IN_BYTE >> PAGE_SHIFT, 0, + CODEC_MM_FLAGS_CPU); + if (s_video_memory.phys_addr) + s_video_memory.base = + (ulong)phys_to_virt(s_video_memory.phys_addr); + else + s_video_memory.base = 0; + if (s_video_memory.base) { + enc_pr(LOG_DEBUG, + "allocating phys 0x%lx, virt addr 0x%lx, size %dk\n", + s_video_memory.phys_addr, + s_video_memory.base, + s_video_memory.size >> 10); + if (vmem_init(&s_vmem, + s_video_memory.phys_addr, + s_video_memory.size) < 0) { + enc_pr(LOG_ERROR, "fail to init vmem system\n"); + r = -ENOMEM; + codec_mm_free_for_dma( + VPU_DEV_NAME, + (u32)s_video_memory.phys_addr); + vmem_exit(&s_vmem); + memset(&s_video_memory, 0, + sizeof(struct vpudrv_buffer_t)); + memset(&s_vmem, 0, + sizeof(struct video_mm_t)); + } + } else { + enc_pr(LOG_ERROR, + "CMA failed to allocate dma buffer for %s, phys: 0x%lx\n", + VPU_DEV_NAME, s_video_memory.phys_addr); + if (s_video_memory.phys_addr) + codec_mm_free_for_dma( + VPU_DEV_NAME, + (u32)s_video_memory.phys_addr); + s_video_memory.phys_addr = 0; + r = -ENOMEM; + } +#else + enc_pr(LOG_ERROR, + "No CMA and reserved memory for HevcEnc!!!\n"); + r = -ENOMEM; +#endif + } else if (!s_video_memory.base) { + enc_pr(LOG_ERROR, + "HevcEnc memory is not malloced!!!\n"); + r = -ENOMEM; + } + if (alloc_buffer) { + ulong flags; + u32 data32; + if ((s_vpu_irq >= 0) && (s_vpu_irq_requested == false)) { + s32 err; + err = request_irq(s_vpu_irq, vpu_irq_handler, 0, + "HevcEnc-irq", (void *)(&s_vpu_drv_context)); + if (err) { + enc_pr(LOG_ERROR, + "fail to register interrupt handler\n"); + return -EFAULT; + } + s_vpu_irq_requested = true; + } + amports_switch_gate("vdec", 1); + spin_lock_irqsave(&s_vpu_lock, flags); + WRITE_AOREG(AO_RTI_GEN_PWR_SLEEP0, + READ_AOREG(AO_RTI_GEN_PWR_SLEEP0) & ~(0x3<<24)); + udelay(10); + + data32 = 0x700; + data32 |= READ_VREG(DOS_SW_RESET4); + WRITE_VREG(DOS_SW_RESET4, data32); + data32 &= ~0x700; + WRITE_VREG(DOS_SW_RESET4, data32); + + WRITE_MPEG_REG(RESET0_REGISTER, data32 & ~(1<<21)); + WRITE_MPEG_REG(RESET0_REGISTER, data32 | (1<<21)); + READ_MPEG_REG(RESET0_REGISTER); + READ_MPEG_REG(RESET0_REGISTER); + READ_MPEG_REG(RESET0_REGISTER); + READ_MPEG_REG(RESET0_REGISTER); +#ifndef VPU_SUPPORT_CLOCK_CONTROL + vpu_clk_config(1); +#endif + /* Enable wave420l_vpu_idle_rise_irq, + Disable wave420l_vpu_idle_fall_irq */ + WRITE_VREG(DOS_WAVE420L_CNTL_STAT, 0x1); + WRITE_VREG(DOS_MEM_PD_WAVE420L, 0x0); + + WRITE_AOREG(AO_RTI_GEN_PWR_ISO0, + READ_AOREG(AO_RTI_GEN_PWR_ISO0) & ~(0x3<<12)); + udelay(10); + + spin_unlock_irqrestore(&s_vpu_lock, flags); + } +Err: + enc_pr(LOG_DEBUG, "[-] %s, ret: %d\n", __func__, r); + return r; +} + +static long vpu_ioctl(struct file *filp, u32 cmd, ulong arg) +{ + s32 ret = 0; + struct vpu_drv_context_t *dev = + (struct vpu_drv_context_t *)filp->private_data; + + switch (cmd) { + case VDI_IOCTL_ALLOCATE_PHYSICAL_MEMORY: + { + struct vpudrv_buffer_pool_t *vbp; + enc_pr(LOG_ALL, + "[+]VDI_IOCTL_ALLOCATE_PHYSICAL_MEMORY\n"); + ret = down_interruptible(&s_vpu_sem); + if (ret == 0) { + vbp = kzalloc(sizeof(*vbp), GFP_KERNEL); + if (!vbp) { + up(&s_vpu_sem); + return -ENOMEM; + } + + ret = copy_from_user(&(vbp->vb), + (struct vpudrv_buffer_t *)arg, + sizeof(struct vpudrv_buffer_t)); + if (ret) { + kfree(vbp); + up(&s_vpu_sem); + return -EFAULT; + } + + ret = vpu_alloc_dma_buffer(&(vbp->vb)); + if (ret == -1) { + ret = -ENOMEM; + kfree(vbp); + up(&s_vpu_sem); + break; + } + ret = copy_to_user((void __user *)arg, + &(vbp->vb), + sizeof(struct vpudrv_buffer_t)); + if (ret) { + kfree(vbp); + ret = -EFAULT; + up(&s_vpu_sem); + break; + } + + vbp->filp = filp; + spin_lock(&s_vpu_lock); + list_add(&vbp->list, &s_vbp_head); + spin_unlock(&s_vpu_lock); + + up(&s_vpu_sem); + } + enc_pr(LOG_ALL, + "[-]VDI_IOCTL_ALLOCATE_PHYSICAL_MEMORY\n"); + } + break; + case VDI_IOCTL_ALLOCATE_PHYSICAL_MEMORY32: + { + struct vpudrv_buffer_pool_t *vbp; + struct compat_vpudrv_buffer_t buf32; + enc_pr(LOG_ALL, + "[+]VDI_IOCTL_ALLOCATE_PHYSICAL_MEMORY32\n"); + ret = down_interruptible(&s_vpu_sem); + if (ret == 0) { + vbp = kzalloc(sizeof(*vbp), GFP_KERNEL); + if (!vbp) { + up(&s_vpu_sem); + return -ENOMEM; + } + + ret = copy_from_user(&buf32, + (struct compat_vpudrv_buffer_t *)arg, + sizeof(struct compat_vpudrv_buffer_t)); + if (ret) { + kfree(vbp); + up(&s_vpu_sem); + return -EFAULT; + } + + vbp->vb.size = buf32.size; + vbp->vb.cached = buf32.cached; + vbp->vb.phys_addr = + (ulong)buf32.phys_addr; + vbp->vb.base = + (ulong)buf32.base; + vbp->vb.virt_addr = + (ulong)buf32.virt_addr; + ret = vpu_alloc_dma_buffer(&(vbp->vb)); + if (ret == -1) { + ret = -ENOMEM; + kfree(vbp); + up(&s_vpu_sem); + break; + } + + buf32.size = vbp->vb.size; + buf32.phys_addr = + (compat_ulong_t)vbp->vb.phys_addr; + buf32.base = + (compat_ulong_t)vbp->vb.base; + buf32.virt_addr = + (compat_ulong_t)vbp->vb.virt_addr; + + ret = copy_to_user((void __user *)arg, + &buf32, + sizeof(struct compat_vpudrv_buffer_t)); + if (ret) { + kfree(vbp); + ret = -EFAULT; + up(&s_vpu_sem); + break; + } + + vbp->filp = filp; + spin_lock(&s_vpu_lock); + list_add(&vbp->list, &s_vbp_head); + spin_unlock(&s_vpu_lock); + + up(&s_vpu_sem); + } + enc_pr(LOG_ALL, + "[-]VDI_IOCTL_ALLOCATE_PHYSICAL_MEMORY32\n"); + } + break; + case VDI_IOCTL_FREE_PHYSICALMEMORY: + { + struct vpudrv_buffer_pool_t *vbp, *n; + struct vpudrv_buffer_t vb; + enc_pr(LOG_ALL, + "[+]VDI_IOCTL_FREE_PHYSICALMEMORY\n"); + ret = down_interruptible(&s_vpu_sem); + if (ret == 0) { + ret = copy_from_user(&vb, + (struct vpudrv_buffer_t *)arg, + sizeof(struct vpudrv_buffer_t)); + if (ret) { + up(&s_vpu_sem); + return -EACCES; + } + + if (vb.base) + vpu_free_dma_buffer(&vb); + + spin_lock(&s_vpu_lock); + list_for_each_entry_safe(vbp, n, + &s_vbp_head, list) + { + if (vbp->vb.base == vb.base) { + list_del(&vbp->list); + kfree(vbp); + break; + } + } + spin_unlock(&s_vpu_lock); + up(&s_vpu_sem); + } + enc_pr(LOG_ALL, + "[-]VDI_IOCTL_FREE_PHYSICALMEMORY\n"); + } + break; + case VDI_IOCTL_FREE_PHYSICALMEMORY32: + { + struct vpudrv_buffer_pool_t *vbp, *n; + struct compat_vpudrv_buffer_t buf32; + struct vpudrv_buffer_t vb; + enc_pr(LOG_ALL, + "[+]VDI_IOCTL_FREE_PHYSICALMEMORY32\n"); + ret = down_interruptible(&s_vpu_sem); + if (ret == 0) { + ret = copy_from_user(&buf32, + (struct compat_vpudrv_buffer_t *)arg, + sizeof(struct compat_vpudrv_buffer_t)); + if (ret) { + up(&s_vpu_sem); + return -EACCES; + } + + vb.size = buf32.size; + vb.phys_addr = + (ulong)buf32.phys_addr; + vb.base = + (ulong)buf32.base; + vb.virt_addr = + (ulong)buf32.virt_addr; + + if (vb.base) + vpu_free_dma_buffer(&vb); + + spin_lock(&s_vpu_lock); + list_for_each_entry_safe(vbp, n, + &s_vbp_head, list) + { + if ((compat_ulong_t)vbp->vb.base + == buf32.base) { + list_del(&vbp->list); + kfree(vbp); + break; + } + } + spin_unlock(&s_vpu_lock); + up(&s_vpu_sem); + } + enc_pr(LOG_ALL, + "[-]VDI_IOCTL_FREE_PHYSICALMEMORY32\n"); + } + break; + case VDI_IOCTL_GET_RESERVED_VIDEO_MEMORY_INFO: + { + enc_pr(LOG_ALL, + "[+]VDI_IOCTL_GET_RESERVED_VIDEO_MEMORY_INFO\n"); + if (s_video_memory.base != 0) { + ret = copy_to_user((void __user *)arg, + &s_video_memory, + sizeof(struct vpudrv_buffer_t)); + if (ret != 0) + ret = -EFAULT; + } else { + ret = -EFAULT; + } + enc_pr(LOG_ALL, + "[-]VDI_IOCTL_GET_RESERVED_VIDEO_MEMORY_INFO\n"); + } + break; + case VDI_IOCTL_GET_RESERVED_VIDEO_MEMORY_INFO32: + { + struct compat_vpudrv_buffer_t buf32; + enc_pr(LOG_ALL, + "[+]VDI_IOCTL_GET_RESERVED_VIDEO_MEMORY_INFO32\n"); + + buf32.size = s_video_memory.size; + buf32.phys_addr = + (compat_ulong_t)s_video_memory.phys_addr; + buf32.base = + (compat_ulong_t)s_video_memory.base; + buf32.virt_addr = + (compat_ulong_t)s_video_memory.virt_addr; + if (s_video_memory.base != 0) { + ret = copy_to_user((void __user *)arg, + &buf32, + sizeof(struct compat_vpudrv_buffer_t)); + if (ret != 0) + ret = -EFAULT; + } else { + ret = -EFAULT; + } + enc_pr(LOG_ALL, + "[-]VDI_IOCTL_GET_RESERVED_VIDEO_MEMORY_INFO32\n"); + } + break; + case VDI_IOCTL_WAIT_INTERRUPT: + { + struct vpudrv_intr_info_t info; + enc_pr(LOG_ALL, + "[+]VDI_IOCTL_WAIT_INTERRUPT\n"); + ret = copy_from_user(&info, + (struct vpudrv_intr_info_t *)arg, + sizeof(struct vpudrv_intr_info_t)); + if (ret != 0) + return -EFAULT; + + ret = wait_event_interruptible_timeout( + s_interrupt_wait_q, + s_interrupt_flag != 0, + msecs_to_jiffies(info.timeout)); + if (!ret) { + ret = -ETIME; + break; + } + if (dev->interrupt_reason & (1 << W4_INT_ENC_PIC)) { + u32 start, end, size, core = 0; + start = ReadVpuRegister(W4_BS_RD_PTR); + end = ReadVpuRegister(W4_BS_WR_PTR); + size = ReadVpuRegister(W4_RET_ENC_PIC_BYTE); + enc_pr(LOG_INFO, "flush output buffer, "); + enc_pr(LOG_INFO, + "start:0x%x, end:0x%x, size:0x%x\n", + start, end, size); + if (end - start > size && end > start) + size = end - start; + if (size > 0) + cache_flush(start, size); + } + + if (signal_pending(current)) { + ret = -ERESTARTSYS; + break; + } + + enc_pr(LOG_INFO, + "s_interrupt_flag(%d), reason(0x%08lx)\n", + s_interrupt_flag, dev->interrupt_reason); + + info.intr_reason = dev->interrupt_reason; + s_interrupt_flag = 0; + dev->interrupt_reason = 0; + ret = copy_to_user((void __user *)arg, + &info, sizeof(struct vpudrv_intr_info_t)); + enc_pr(LOG_ALL, + "[-]VDI_IOCTL_WAIT_INTERRUPT\n"); + if (ret != 0) + return -EFAULT; + } + break; + case VDI_IOCTL_SET_CLOCK_GATE: + { + u32 clkgate; + enc_pr(LOG_ALL, + "[+]VDI_IOCTL_SET_CLOCK_GATE\n"); + if (get_user(clkgate, (u32 __user *) arg)) + return -EFAULT; +#ifdef VPU_SUPPORT_CLOCK_CONTROL + vpu_clk_config(clkgate); +#endif + enc_pr(LOG_ALL, + "[-]VDI_IOCTL_SET_CLOCK_GATE\n"); + } + break; + case VDI_IOCTL_GET_INSTANCE_POOL: + { + enc_pr(LOG_ALL, + "[+]VDI_IOCTL_GET_INSTANCE_POOL\n"); + ret = down_interruptible(&s_vpu_sem); + if (ret != 0) + break; + + if (s_instance_pool.base != 0) { + ret = copy_to_user((void __user *)arg, + &s_instance_pool, + sizeof(struct vpudrv_buffer_t)); + ret = (ret != 0) ? -EFAULT : 0; + } else { + ret = copy_from_user(&s_instance_pool, + (struct vpudrv_buffer_t *)arg, + sizeof(struct vpudrv_buffer_t)); + if (ret == 0) { + s_instance_pool.size = + PAGE_ALIGN( + s_instance_pool.size); + s_instance_pool.base = + (ulong)vmalloc( + s_instance_pool.size); + s_instance_pool.phys_addr = + s_instance_pool.base; + if (s_instance_pool.base == 0) { + ret = -EFAULT; + up(&s_vpu_sem); + break; + } + /*clearing memory*/ + memset((void *)s_instance_pool.base, + 0, s_instance_pool.size); + ret = copy_to_user((void __user *)arg, + &s_instance_pool, + sizeof(struct vpudrv_buffer_t)); + if (ret != 0) + ret = -EFAULT; + } else + ret = -EFAULT; + } + up(&s_vpu_sem); + enc_pr(LOG_ALL, + "[-]VDI_IOCTL_GET_INSTANCE_POOL\n"); + } + break; + case VDI_IOCTL_GET_INSTANCE_POOL32: + { + struct compat_vpudrv_buffer_t buf32; + enc_pr(LOG_ALL, + "[+]VDI_IOCTL_GET_INSTANCE_POOL32\n"); + ret = down_interruptible(&s_vpu_sem); + if (ret != 0) + break; + if (s_instance_pool.base != 0) { + buf32.size = s_instance_pool.size; + buf32.phys_addr = + (compat_ulong_t) + s_instance_pool.phys_addr; + buf32.base = + (compat_ulong_t) + s_instance_pool.base; + buf32.virt_addr = + (compat_ulong_t) + s_instance_pool.virt_addr; + ret = copy_to_user((void __user *)arg, + &buf32, + sizeof(struct compat_vpudrv_buffer_t)); + ret = (ret != 0) ? -EFAULT : 0; + } else { + ret = copy_from_user(&buf32, + (struct compat_vpudrv_buffer_t *)arg, + sizeof(struct compat_vpudrv_buffer_t)); + if (ret == 0) { + s_instance_pool.size = buf32.size; + s_instance_pool.size = + PAGE_ALIGN( + s_instance_pool.size); + s_instance_pool.base = + (ulong)vmalloc( + s_instance_pool.size); + s_instance_pool.phys_addr = + s_instance_pool.base; + buf32.size = + s_instance_pool.size; + buf32.phys_addr = + (compat_ulong_t) + s_instance_pool.phys_addr; + buf32.base = + (compat_ulong_t) + s_instance_pool.base; + buf32.virt_addr = + (compat_ulong_t) + s_instance_pool.virt_addr; + if (s_instance_pool.base == 0) { + ret = -EFAULT; + up(&s_vpu_sem); + break; + } + /*clearing memory*/ + memset((void *)s_instance_pool.base, + 0x0, s_instance_pool.size); + ret = copy_to_user((void __user *)arg, + &buf32, + sizeof( + struct compat_vpudrv_buffer_t)); + if (ret != 0) + ret = -EFAULT; + } else + ret = -EFAULT; + } + up(&s_vpu_sem); + enc_pr(LOG_ALL, + "[-]VDI_IOCTL_GET_INSTANCE_POOL32\n"); + } + break; + case VDI_IOCTL_GET_COMMON_MEMORY: + { + enc_pr(LOG_ALL, + "[+]VDI_IOCTL_GET_COMMON_MEMORY\n"); + if (s_common_memory.base != 0) { + ret = copy_to_user((void __user *)arg, + &s_common_memory, + sizeof(struct vpudrv_buffer_t)); + if (ret != 0) + ret = -EFAULT; + } else { + ret = copy_from_user(&s_common_memory, + (struct vpudrv_buffer_t *)arg, + sizeof(struct vpudrv_buffer_t)); + if (ret != 0) { + ret = -EFAULT; + break; + } + if (vpu_alloc_dma_buffer( + &s_common_memory) != -1) { + ret = copy_to_user((void __user *)arg, + &s_common_memory, + sizeof(struct vpudrv_buffer_t)); + if (ret != 0) + ret = -EFAULT; + } else + ret = -EFAULT; + } + enc_pr(LOG_ALL, + "[-]VDI_IOCTL_GET_COMMON_MEMORY\n"); + } + break; + case VDI_IOCTL_GET_COMMON_MEMORY32: + { + struct compat_vpudrv_buffer_t buf32; + enc_pr(LOG_ALL, + "[+]VDI_IOCTL_GET_COMMON_MEMORY32\n"); + + buf32.size = s_common_memory.size; + buf32.phys_addr = + (compat_ulong_t) + s_common_memory.phys_addr; + buf32.base = + (compat_ulong_t) + s_common_memory.base; + buf32.virt_addr = + (compat_ulong_t) + s_common_memory.virt_addr; + if (s_common_memory.base != 0) { + ret = copy_to_user((void __user *)arg, + &buf32, + sizeof(struct compat_vpudrv_buffer_t)); + if (ret != 0) + ret = -EFAULT; + } else { + ret = copy_from_user(&buf32, + (struct compat_vpudrv_buffer_t *)arg, + sizeof(struct compat_vpudrv_buffer_t)); + if (ret != 0) { + ret = -EFAULT; + break; + } + s_common_memory.size = buf32.size; + if (vpu_alloc_dma_buffer( + &s_common_memory) != -1) { + buf32.size = + s_common_memory.size; + buf32.phys_addr = + (compat_ulong_t) + s_common_memory.phys_addr; + buf32.base = + (compat_ulong_t) + s_common_memory.base; + buf32.virt_addr = + (compat_ulong_t) + s_common_memory.virt_addr; + ret = copy_to_user((void __user *)arg, + &buf32, + sizeof( + struct compat_vpudrv_buffer_t)); + if (ret != 0) + ret = -EFAULT; + } else + ret = -EFAULT; + } + enc_pr(LOG_ALL, + "[-]VDI_IOCTL_GET_COMMON_MEMORY32\n"); + } + break; + case VDI_IOCTL_OPEN_INSTANCE: + { + struct vpudrv_inst_info_t inst_info; + struct vpudrv_instanace_list_t *vil, *n; + + vil = kzalloc(sizeof(*vil), GFP_KERNEL); + if (!vil) + return -ENOMEM; + + if (copy_from_user(&inst_info, + (struct vpudrv_inst_info_t *)arg, + sizeof(struct vpudrv_inst_info_t))) + return -EFAULT; + + vil->inst_idx = inst_info.inst_idx; + vil->core_idx = inst_info.core_idx; + vil->filp = filp; + + spin_lock(&s_vpu_lock); + list_add(&vil->list, &s_inst_list_head); + + /* counting the current open instance number */ + inst_info.inst_open_count = 0; + list_for_each_entry_safe(vil, n, + &s_inst_list_head, list) + { + if (vil->core_idx == inst_info.core_idx) + inst_info.inst_open_count++; + } + + /* flag just for that vpu is in opened or closed */ + s_vpu_open_ref_count++; + spin_unlock(&s_vpu_lock); + + if (copy_to_user((void __user *)arg, + &inst_info, + sizeof(struct vpudrv_inst_info_t))) { + kfree(vil); + return -EFAULT; + } + + enc_pr(LOG_DEBUG, + "VDI_IOCTL_OPEN_INSTANCE "); + enc_pr(LOG_DEBUG, + "core_idx=%d, inst_idx=%d, ", + (u32)inst_info.core_idx, + (u32)inst_info.inst_idx); + enc_pr(LOG_DEBUG, + "s_vpu_open_ref_count=%d, inst_open_count=%d\n", + s_vpu_open_ref_count, + inst_info.inst_open_count); + } + break; + case VDI_IOCTL_CLOSE_INSTANCE: + { + struct vpudrv_inst_info_t inst_info; + struct vpudrv_instanace_list_t *vil, *n; + + enc_pr(LOG_ALL, + "[+]VDI_IOCTL_CLOSE_INSTANCE\n"); + if (copy_from_user(&inst_info, + (struct vpudrv_inst_info_t *)arg, + sizeof(struct vpudrv_inst_info_t))) + return -EFAULT; + + spin_lock(&s_vpu_lock); + list_for_each_entry_safe(vil, n, + &s_inst_list_head, list) + { + if (vil->inst_idx == inst_info.inst_idx && + vil->core_idx == inst_info.core_idx) { + list_del(&vil->list); + kfree(vil); + break; + } + } + + /* counting the current open instance number */ + inst_info.inst_open_count = 0; + list_for_each_entry_safe(vil, n, + &s_inst_list_head, list) + { + if (vil->core_idx == inst_info.core_idx) + inst_info.inst_open_count++; + } + + /* flag just for that vpu is in opened or closed */ + s_vpu_open_ref_count--; + spin_unlock(&s_vpu_lock); + + if (copy_to_user((void __user *)arg, + &inst_info, + sizeof(struct vpudrv_inst_info_t))) + return -EFAULT; + + enc_pr(LOG_DEBUG, + "VDI_IOCTL_CLOSE_INSTANCE "); + enc_pr(LOG_DEBUG, + "core_idx=%d, inst_idx=%d, ", + (u32)inst_info.core_idx, + (u32)inst_info.inst_idx); + enc_pr(LOG_DEBUG, + "s_vpu_open_ref_count=%d, inst_open_count=%d\n", + s_vpu_open_ref_count, + inst_info.inst_open_count); + } + break; + case VDI_IOCTL_GET_INSTANCE_NUM: + { + struct vpudrv_inst_info_t inst_info; + struct vpudrv_instanace_list_t *vil, *n; + enc_pr(LOG_ALL, + "[+]VDI_IOCTL_GET_INSTANCE_NUM\n"); + + ret = copy_from_user(&inst_info, + (struct vpudrv_inst_info_t *)arg, + sizeof(struct vpudrv_inst_info_t)); + if (ret != 0) + break; + + inst_info.inst_open_count = 0; + + spin_lock(&s_vpu_lock); + list_for_each_entry_safe(vil, n, + &s_inst_list_head, list) + { + if (vil->core_idx == inst_info.core_idx) + inst_info.inst_open_count++; + } + spin_unlock(&s_vpu_lock); + + ret = copy_to_user((void __user *)arg, + &inst_info, + sizeof(struct vpudrv_inst_info_t)); + + enc_pr(LOG_DEBUG, + "VDI_IOCTL_GET_INSTANCE_NUM "); + enc_pr(LOG_DEBUG, + "core_idx=%d, inst_idx=%d, open_count=%d\n", + (u32)inst_info.core_idx, + (u32)inst_info.inst_idx, + inst_info.inst_open_count); + } + break; + case VDI_IOCTL_RESET: + { + vpu_hw_reset(); + } + break; + case VDI_IOCTL_GET_REGISTER_INFO: + { + enc_pr(LOG_ALL, + "[+]VDI_IOCTL_GET_REGISTER_INFO\n"); + ret = copy_to_user((void __user *)arg, + &s_vpu_register, + sizeof(struct vpudrv_buffer_t)); + if (ret != 0) + ret = -EFAULT; + enc_pr(LOG_ALL, + "[-]VDI_IOCTL_GET_REGISTER_INFO "); + enc_pr(LOG_ALL, + "s_vpu_register.phys_addr=0x%lx, ", + s_vpu_register.phys_addr); + enc_pr(LOG_ALL, + "s_vpu_register.virt_addr=0x%lx, ", + s_vpu_register.virt_addr); + enc_pr(LOG_ALL, + "s_vpu_register.size=0x%x\n", + s_vpu_register.size); + } + break; + case VDI_IOCTL_GET_REGISTER_INFO32: + { + struct compat_vpudrv_buffer_t buf32; + enc_pr(LOG_ALL, + "[+]VDI_IOCTL_GET_REGISTER_INFO32\n"); + + buf32.size = s_vpu_register.size; + buf32.phys_addr = + (compat_ulong_t) + s_vpu_register.phys_addr; + buf32.base = + (compat_ulong_t) + s_vpu_register.base; + buf32.virt_addr = + (compat_ulong_t) + s_vpu_register.virt_addr; + ret = copy_to_user((void __user *)arg, + &buf32, + sizeof( + struct compat_vpudrv_buffer_t)); + if (ret != 0) + ret = -EFAULT; + enc_pr(LOG_ALL, + "[-]VDI_IOCTL_GET_REGISTER_INFO32 "); + enc_pr(LOG_ALL, + "s_vpu_register.phys_addr=0x%lx, ", + s_vpu_register.phys_addr); + enc_pr(LOG_ALL, + "s_vpu_register.virt_addr=0x%lx, ", + s_vpu_register.virt_addr); + enc_pr(LOG_ALL, + "s_vpu_register.size=0x%x\n", + s_vpu_register.size); + } + break; + case VDI_IOCTL_FLUSH_BUFFER32: + { + struct vpudrv_buffer_pool_t *pool, *n; + struct compat_vpudrv_buffer_t buf32; + struct vpudrv_buffer_t vb; + bool find = false; + u32 cached = 0; + enc_pr(LOG_ALL, + "[+]VDI_IOCTL_FLUSH_BUFFER32\n"); + + ret = copy_from_user(&buf32, + (struct compat_vpudrv_buffer_t *)arg, + sizeof(struct compat_vpudrv_buffer_t)); + if (ret) + return -EFAULT; + spin_lock(&s_vpu_lock); + list_for_each_entry_safe(pool, n, + &s_vbp_head, list) + { + if (pool->filp == filp) { + vb = pool->vb; + if (((compat_ulong_t)vb.phys_addr + == buf32.phys_addr) + && find == false){ + cached = vb.cached; + find = true; + } + } + } + spin_unlock(&s_vpu_lock); + if (find && cached) + dma_flush( + (u32)buf32.phys_addr, + (u32)buf32.size); + enc_pr(LOG_ALL, + "[-]VDI_IOCTL_FLUSH_BUFFER32\n"); + } + break; + case VDI_IOCTL_FLUSH_BUFFER: + { + struct vpudrv_buffer_pool_t *pool, *n; + struct vpudrv_buffer_t vb, buf; + bool find = false; + u32 cached = 0; + enc_pr(LOG_ALL, + "[+]VDI_IOCTL_FLUSH_BUFFER\n"); + + ret = copy_from_user(&buf, + (struct vpudrv_buffer_t *)arg, + sizeof(struct vpudrv_buffer_t)); + if (ret) + return -EFAULT; + spin_lock(&s_vpu_lock); + list_for_each_entry_safe(pool, n, + &s_vbp_head, list) + { + if (pool->filp == filp) { + vb = pool->vb; + if ((vb.phys_addr + == buf.phys_addr) + && find == false){ + cached = vb.cached; + find = true; + } + } + } + spin_unlock(&s_vpu_lock); + if (find && cached) + dma_flush( + (u32)buf.phys_addr, + (u32)buf.size); + enc_pr(LOG_ALL, + "[-]VDI_IOCTL_FLUSH_BUFFER\n"); + } + break; + default: + { + enc_pr(LOG_ERROR, + "No such IOCTL, cmd is %d\n", cmd); + } + break; + } + return ret; +} + +#ifdef CONFIG_COMPAT +static long vpu_compat_ioctl(struct file *filp, u32 cmd, ulong arg) +{ + long ret; + + arg = (ulong)compat_ptr(arg); + ret = vpu_ioctl(filp, cmd, arg); + return ret; +} +#endif + +static ssize_t vpu_write(struct file *filp, + const char *buf, + size_t len, + loff_t *ppos) +{ + enc_pr(LOG_INFO, "vpu_write len=%d\n", (int)len); + + if (!buf) { + enc_pr(LOG_ERROR, "vpu_write buf = NULL error\n"); + return -EFAULT; + } + + if (len == sizeof(struct vpu_bit_firmware_info_t)) { + struct vpu_bit_firmware_info_t *bit_firmware_info; + bit_firmware_info = + kmalloc(sizeof(struct vpu_bit_firmware_info_t), + GFP_KERNEL); + if (!bit_firmware_info) { + enc_pr(LOG_ERROR, + "vpu_write bit_firmware_info allocation error\n"); + return -EFAULT; + } + + if (copy_from_user(bit_firmware_info, buf, len)) { + enc_pr(LOG_ERROR, + "vpu_write copy_from_user error for bit_firmware_info\n"); + return -EFAULT; + } + + if (bit_firmware_info->size == + sizeof(struct vpu_bit_firmware_info_t)) { + enc_pr(LOG_INFO, + "vpu_write set bit_firmware_info coreIdx=0x%x, ", + bit_firmware_info->core_idx); + enc_pr(LOG_INFO, + "reg_base_offset=0x%x size=0x%x, bit_code[0]=0x%x\n", + bit_firmware_info->reg_base_offset, + bit_firmware_info->size, + bit_firmware_info->bit_code[0]); + + if (bit_firmware_info->core_idx + > MAX_NUM_VPU_CORE) { + enc_pr(LOG_ERROR, + "vpu_write coreIdx[%d] is ", + bit_firmware_info->core_idx); + enc_pr(LOG_ERROR, + "exceeded than MAX_NUM_VPU_CORE[%d]\n", + MAX_NUM_VPU_CORE); + return -ENODEV; + } + + memcpy((void *)&s_bit_firmware_info + [bit_firmware_info->core_idx], + bit_firmware_info, + sizeof(struct vpu_bit_firmware_info_t)); + kfree(bit_firmware_info); + return len; + } + kfree(bit_firmware_info); + } + return -1; +} + +static s32 vpu_release(struct inode *inode, struct file *filp) +{ + s32 ret = 0; + ulong flags; + enc_pr(LOG_DEBUG, "vpu_release\n"); + ret = down_interruptible(&s_vpu_sem); + if (ret == 0) { + vpu_free_buffers(filp); + vpu_free_instances(filp); + s_vpu_drv_context.open_count--; + if (s_vpu_drv_context.open_count == 0) { + if (s_instance_pool.base) { + enc_pr(LOG_DEBUG, "free instance pool\n"); + vfree((const void *)s_instance_pool.base); + s_instance_pool.base = 0; + } + if (s_common_memory.base) { + enc_pr(LOG_DEBUG, "free common memory\n"); + vpu_free_dma_buffer(&s_common_memory); + s_common_memory.base = 0; + } + + if (s_video_memory.base && !use_reserve) { + codec_mm_free_for_dma( + VPU_DEV_NAME, + (u32)s_video_memory.phys_addr); + vmem_exit(&s_vmem); + memset(&s_video_memory, + 0, sizeof(struct vpudrv_buffer_t)); + memset(&s_vmem, + 0, sizeof(struct video_mm_t)); + } + if ((s_vpu_irq >= 0) && (s_vpu_irq_requested == true)) { + free_irq(s_vpu_irq, &s_vpu_drv_context); + s_vpu_irq_requested = false; + } + spin_lock_irqsave(&s_vpu_lock, flags); + WRITE_AOREG(AO_RTI_GEN_PWR_ISO0, + READ_AOREG(AO_RTI_GEN_PWR_ISO0) | (0x3<<12)); + udelay(10); + + WRITE_VREG(DOS_MEM_PD_WAVE420L, 0xffffffff); +#ifndef VPU_SUPPORT_CLOCK_CONTROL + vpu_clk_config(0); +#endif + WRITE_AOREG(AO_RTI_GEN_PWR_SLEEP0, + READ_AOREG(AO_RTI_GEN_PWR_SLEEP0) | (0x3<<24)); + udelay(10); + spin_unlock_irqrestore(&s_vpu_lock, flags); + amports_switch_gate("vdec", 0); + } + } + up(&s_vpu_sem); + return 0; +} + +static s32 vpu_fasync(s32 fd, struct file *filp, s32 mode) +{ + struct vpu_drv_context_t *dev = + (struct vpu_drv_context_t *)filp->private_data; + return fasync_helper(fd, filp, mode, &dev->async_queue); +} + +static s32 vpu_map_to_register(struct file *fp, struct vm_area_struct *vm) +{ + ulong pfn; + vm->vm_flags |= VM_IO | VM_RESERVED; + vm->vm_page_prot = + pgprot_noncached(vm->vm_page_prot); + pfn = s_vpu_register.phys_addr >> PAGE_SHIFT; + return remap_pfn_range(vm, vm->vm_start, pfn, + vm->vm_end - vm->vm_start, + vm->vm_page_prot) ? -EAGAIN : 0; +} + +static s32 vpu_map_to_physical_memory( + struct file *fp, struct vm_area_struct *vm) +{ + vm->vm_flags |= VM_IO | VM_RESERVED; + if (vm->vm_pgoff == + (s_common_memory.phys_addr >> PAGE_SHIFT)) { + vm->vm_page_prot = + pgprot_noncached(vm->vm_page_prot); + } else { + if (vpu_is_buffer_cached(fp, vm->vm_pgoff) == 0) + vm->vm_page_prot = + pgprot_noncached(vm->vm_page_prot); + } + /* vm->vm_page_prot = pgprot_writecombine(vm->vm_page_prot); */ + return remap_pfn_range(vm, vm->vm_start, vm->vm_pgoff, + vm->vm_end - vm->vm_start, vm->vm_page_prot) ? -EAGAIN : 0; +} + +static s32 vpu_map_to_instance_pool_memory( + struct file *fp, struct vm_area_struct *vm) +{ + s32 ret; + long length = vm->vm_end - vm->vm_start; + ulong start = vm->vm_start; + s8 *vmalloc_area_ptr = (s8 *)s_instance_pool.base; + ulong pfn; + + vm->vm_flags |= VM_RESERVED; + + /* loop over all pages, map it page individually */ + while (length > 0) { + pfn = vmalloc_to_pfn(vmalloc_area_ptr); + ret = remap_pfn_range(vm, start, pfn, + PAGE_SIZE, PAGE_SHARED); + if (ret < 0) + return ret; + start += PAGE_SIZE; + vmalloc_area_ptr += PAGE_SIZE; + length -= PAGE_SIZE; + } + return 0; +} + +/* + * @brief memory map interface for vpu file operation + * @return 0 on success or negative error code on error + */ +static s32 vpu_mmap(struct file *fp, struct vm_area_struct *vm) +{ + /* if (vm->vm_pgoff == (s_vpu_register.phys_addr >> PAGE_SHIFT)) */ + if ((vm->vm_end - vm->vm_start == s_vpu_register.size + 1) && + (vm->vm_pgoff == 0)) { + vm->vm_pgoff = (s_vpu_register.phys_addr >> PAGE_SHIFT); + return vpu_map_to_register(fp, vm); + } + + if (vm->vm_pgoff == 0) + return vpu_map_to_instance_pool_memory(fp, vm); + + return vpu_map_to_physical_memory(fp, vm); +} + +static const struct file_operations vpu_fops = { + .owner = THIS_MODULE, + .open = vpu_open, + .release = vpu_release, + .write = vpu_write, + .unlocked_ioctl = vpu_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = vpu_compat_ioctl, +#endif + .fasync = vpu_fasync, + .mmap = vpu_mmap, +}; + +static ssize_t hevcenc_status_show(struct class *cla, + struct class_attribute *attr, char *buf) +{ + return snprintf(buf, 40, "hevcenc_status_show\n"); +} + +static struct class_attribute hevcenc_class_attrs[] = { + __ATTR(encode_status, + S_IRUGO | S_IWUSR, + hevcenc_status_show, + NULL), + __ATTR_NULL +}; + +static struct class hevcenc_class = { + .name = VPU_CLASS_NAME, + .class_attrs = hevcenc_class_attrs, +}; + +s32 init_HevcEnc_device(void) +{ + s32 r = 0; + r = register_chrdev(0, VPU_DEV_NAME, &vpu_fops); + if (r <= 0) { + enc_pr(LOG_ERROR, "register hevcenc device error.\n"); + return r; + } + s_vpu_major = r; + + r = class_register(&hevcenc_class); + if (r < 0) { + enc_pr(LOG_ERROR, "error create hevcenc class.\n"); + return r; + } + + hevcenc_dev = device_create(&hevcenc_class, NULL, + MKDEV(s_vpu_major, 0), NULL, + VPU_DEV_NAME); + + if (IS_ERR(hevcenc_dev)) { + enc_pr(LOG_ERROR, "create hevcenc device error.\n"); + class_unregister(&hevcenc_class); + return -1; + } + return r; +} + +s32 uninit_HevcEnc_device(void) +{ + if (hevcenc_dev) + device_destroy(&hevcenc_class, MKDEV(s_vpu_major, 0)); + + class_destroy(&hevcenc_class); + + unregister_chrdev(s_vpu_major, VPU_DEV_NAME); + return 0; +} + +static s32 hevc_mem_device_init( + struct reserved_mem *rmem, struct device *dev) +{ + s32 r; + if (!rmem) { + enc_pr(LOG_ERROR, + "Can not obtain I/O memory, will allocate hevc buffer!\n"); + r = -EFAULT; + return r; + } + + if ((!rmem->base) || + (rmem->size < VPU_INIT_VIDEO_MEMORY_SIZE_IN_BYTE)) { + enc_pr(LOG_ERROR, + "memory range error, 0x%lx - 0x%lx\n", + (ulong)rmem->base, (ulong)rmem->size); + r = -EFAULT; + return r; + } + r = 0; + s_video_memory.size = rmem->size; + s_video_memory.phys_addr = (ulong)rmem->base; + s_video_memory.base = + (ulong)phys_to_virt(s_video_memory.phys_addr); + if (!s_video_memory.base) { + enc_pr(LOG_ERROR, "fail to remap video memory "); + enc_pr(LOG_ERROR, + "physical phys_addr=0x%lx, base=0x%lx, size=0x%x\n", + (ulong)s_video_memory.phys_addr, + (ulong)s_video_memory.base, + (u32)s_video_memory.size); + s_video_memory.phys_addr = 0; + r = -EFAULT; + } + return r; +} + +static s32 vpu_probe(struct platform_device *pdev) +{ + s32 err = 0, irq, reg_count, idx; + struct resource res; + struct device_node *np, *child; + + enc_pr(LOG_DEBUG, "vpu_probe\n"); + + s_vpu_major = 0; + use_reserve = false; + s_vpu_irq = -1; + cma_pool_size = 0; + s_vpu_irq_requested = false; + s_vpu_open_ref_count = 0; + hevcenc_dev = NULL; + hevc_pdev = NULL; + memset(&s_video_memory, 0, sizeof(struct vpudrv_buffer_t)); + memset(&s_vpu_register, 0, sizeof(struct vpudrv_buffer_t)); + memset(&s_vmem, 0, sizeof(struct video_mm_t)); + memset(&s_bit_firmware_info[0], 0, sizeof(s_bit_firmware_info)); + memset(&res, 0, sizeof(struct resource)); + + idx = of_reserved_mem_device_init(&pdev->dev); + if (idx != 0) { + enc_pr(LOG_DEBUG, + "HevcEnc reserved memory config fail.\n"); + } else if (s_video_memory.phys_addr) { + use_reserve = true; + } + + if (use_reserve == false) { +#ifndef CONFIG_CMA + enc_pr(LOG_ERROR, + "HevcEnc reserved memory is invaild, probe fail!\n"); + err = -EFAULT; + goto ERROR_PROVE_DEVICE; +#else + cma_pool_size = + (codec_mm_get_total_size() > + (VPU_INIT_VIDEO_MEMORY_SIZE_IN_BYTE)) ? + (VPU_INIT_VIDEO_MEMORY_SIZE_IN_BYTE) : + codec_mm_get_total_size(); + enc_pr(LOG_DEBUG, + "HevcEnc - cma memory pool size: %d MB\n", + (u32)cma_pool_size / SZ_1M); +#endif + } + + /* get interrupt resource */ + irq = platform_get_irq_byname(pdev, "wave420l_irq"); + if (irq < 0) { + enc_pr(LOG_ERROR, "get HevcEnc irq resource error\n"); + err = -ENXIO; + goto ERROR_PROVE_DEVICE; + } + s_vpu_irq = irq; + enc_pr(LOG_DEBUG, "HevcEnc - wave420l_irq: %d\n", s_vpu_irq); +#if 0 + rstc = devm_reset_control_get(&pdev->dev, "HevcEnc"); + if (IS_ERR(rstc)) { + enc_pr(LOG_ERROR, + "get HevcEnc rstc error: %lx\n", PTR_ERR(rstc)); + rstc = NULL; + err = -ENOENT; + goto ERROR_PROVE_DEVICE; + } + reset_control_assert(rstc); + s_vpu_rstc = rstc; + + clk = clk_get(&pdev->dev, "clk_HevcEnc"); + if (IS_ERR(clk)) { + enc_pr(LOG_ERROR, "cannot get clock\n"); + clk = NULL; + err = -ENOENT; + goto ERROR_PROVE_DEVICE; + } + s_vpu_clk = clk; +#endif + +#ifdef VPU_SUPPORT_CLOCK_CONTROL +#else + vpu_clk_config(1); +#endif + + np = pdev->dev.of_node; + reg_count = 0; + for_each_child_of_node(np, child) { + if (of_address_to_resource(child, 0, &res) + || (reg_count > 1)) { + enc_pr(LOG_ERROR, + "no reg ranges or more reg ranges %d\n", + reg_count); + err = -ENXIO; + goto ERROR_PROVE_DEVICE; + } + /* if platform driver is implemented */ + if (res.start != 0) { + s_vpu_register.phys_addr = res.start; + s_vpu_register.virt_addr = + (ulong)ioremap_nocache( + res.start, resource_size(&res)); + s_vpu_register.size = res.end - res.start; + enc_pr(LOG_DEBUG, + "vpu base address get from platform driver "); + enc_pr(LOG_DEBUG, + "physical base addr=0x%lx, virtual base=0x%lx\n", + s_vpu_register.phys_addr, + s_vpu_register.virt_addr); + } else { + s_vpu_register.phys_addr = VPU_REG_BASE_ADDR; + s_vpu_register.virt_addr = + (ulong)ioremap_nocache( + s_vpu_register.phys_addr, VPU_REG_SIZE); + s_vpu_register.size = VPU_REG_SIZE; + enc_pr(LOG_DEBUG, + "vpu base address get from defined value "); + enc_pr(LOG_DEBUG, + "physical base addr=0x%lx, virtual base=0x%lx\n", + s_vpu_register.phys_addr, + s_vpu_register.virt_addr); + } + reg_count++; + } + + /* get the major number of the character device */ + if (init_HevcEnc_device()) { + err = -EBUSY; + enc_pr(LOG_ERROR, "could not allocate major number\n"); + goto ERROR_PROVE_DEVICE; + } + enc_pr(LOG_INFO, "SUCCESS alloc_chrdev_region\n"); + + init_waitqueue_head(&s_interrupt_wait_q); + tasklet_init(&hevc_tasklet, + hevcenc_isr_tasklet, + (ulong)&s_vpu_drv_context); + s_common_memory.base = 0; + s_instance_pool.base = 0; + + if (use_reserve == true) { + if (vmem_init(&s_vmem, s_video_memory.phys_addr, + s_video_memory.size) < 0) { + enc_pr(LOG_ERROR, "fail to init vmem system\n"); + goto ERROR_PROVE_DEVICE; + } + enc_pr(LOG_DEBUG, + "success to probe vpu device with video memory "); + enc_pr(LOG_DEBUG, + "phys_addr=0x%lx, base = 0x%lx\n", + (ulong)s_video_memory.phys_addr, + (ulong)s_video_memory.base); + } else + enc_pr(LOG_DEBUG, + "success to probe vpu device with video memory from cma\n"); + hevc_pdev = pdev; + return 0; + +ERROR_PROVE_DEVICE: + if (s_vpu_register.virt_addr) { + iounmap((void *)s_vpu_register.virt_addr); + memset(&s_vpu_register, 0, sizeof(struct vpudrv_buffer_t)); + } + + if (s_video_memory.base) { + vmem_exit(&s_vmem); + memset(&s_video_memory, 0, sizeof(struct vpudrv_buffer_t)); + memset(&s_vmem, 0, sizeof(struct video_mm_t)); + } + + vpu_clk_config(0); + + if (s_vpu_irq_requested == true) { + if (s_vpu_irq >= 0) { + free_irq(s_vpu_irq, &s_vpu_drv_context); + s_vpu_irq = -1; + } + s_vpu_irq_requested = false; + } + uninit_HevcEnc_device(); + return err; +} + +static s32 vpu_remove(struct platform_device *pdev) +{ + enc_pr(LOG_DEBUG, "vpu_remove\n"); + + if (s_instance_pool.base) { + vfree((const void *)s_instance_pool.base); + s_instance_pool.base = 0; + } + + if (s_common_memory.base) { + vpu_free_dma_buffer(&s_common_memory); + s_common_memory.base = 0; + } + + if (s_video_memory.base) { + if (!use_reserve) + codec_mm_free_for_dma( + VPU_DEV_NAME, + (u32)s_video_memory.phys_addr); + vmem_exit(&s_vmem); + memset(&s_video_memory, + 0, sizeof(struct vpudrv_buffer_t)); + memset(&s_vmem, + 0, sizeof(struct video_mm_t)); + } + + if (s_vpu_irq_requested == true) { + if (s_vpu_irq >= 0) { + free_irq(s_vpu_irq, &s_vpu_drv_context); + s_vpu_irq = -1; + } + s_vpu_irq_requested = false; + } + + if (s_vpu_register.virt_addr) { + iounmap((void *)s_vpu_register.virt_addr); + memset(&s_vpu_register, + 0, sizeof(struct vpudrv_buffer_t)); + } + hevc_pdev = NULL; + vpu_clk_config(0); + + uninit_HevcEnc_device(); + return 0; +} + +#ifdef CONFIG_PM +static void Wave4BitIssueCommand(u32 core, u32 cmd) +{ + WriteVpuRegister(W4_VPU_BUSY_STATUS, 1); + WriteVpuRegister(W4_CORE_INDEX, 0); + /* coreIdx = ReadVpuRegister(W4_VPU_BUSY_STATUS); */ + /* coreIdx = 0; */ + /* WriteVpuRegister(W4_INST_INDEX, + (instanceIndex & 0xffff) | (codecMode << 16)); */ + WriteVpuRegister(W4_COMMAND, cmd); + WriteVpuRegister(W4_VPU_HOST_INT_REQ, 1); + return; +} + +static s32 vpu_suspend(struct platform_device *pdev, pm_message_t state) +{ + u32 core; + ulong timeout = jiffies + HZ; /* vpu wait timeout to 1sec */ + enc_pr(LOG_DEBUG, "vpu_suspend\n"); + + vpu_clk_config(1); + + if (s_vpu_open_ref_count > 0) { + for (core = 0; core < MAX_NUM_VPU_CORE; core++) { + if (s_bit_firmware_info[core].size == 0) + continue; + while (ReadVpuRegister(W4_VPU_BUSY_STATUS)) { + if (time_after(jiffies, timeout)) { + enc_pr(LOG_ERROR, + "SLEEP_VPU BUSY timeout"); + goto DONE_SUSPEND; + } + } + Wave4BitIssueCommand(core, W4_CMD_SLEEP_VPU); + + while (ReadVpuRegister(W4_VPU_BUSY_STATUS)) { + if (time_after(jiffies, timeout)) { + enc_pr(LOG_ERROR, + "SLEEP_VPU BUSY timeout"); + goto DONE_SUSPEND; + } + } + if (ReadVpuRegister(W4_RET_SUCCESS) == 0) { + enc_pr(LOG_ERROR, + "SLEEP_VPU failed [0x%x]", + ReadVpuRegister(W4_RET_FAIL_REASON)); + goto DONE_SUSPEND; + } + } + } + + vpu_clk_config(0); + return 0; + +DONE_SUSPEND: + vpu_clk_config(0); + return -EAGAIN; +} +static s32 vpu_resume(struct platform_device *pdev) +{ + u32 i; + u32 core; + u32 val; + ulong timeout = jiffies + HZ; /* vpu wait timeout to 1sec */ + ulong code_base; + u32 code_size; + u32 remap_size; + u32 regVal; + u32 hwOption = 0; + + enc_pr(LOG_DEBUG, "vpu_resume\n"); + + vpu_clk_config(1); + + for (core = 0; core < MAX_NUM_VPU_CORE; core++) { + if (s_bit_firmware_info[core].size == 0) + continue; + code_base = s_common_memory.phys_addr; + /* ALIGN TO 4KB */ + code_size = (s_common_memory.size & ~0xfff); + if (code_size < s_bit_firmware_info[core].size * 2) + goto DONE_WAKEUP; + + /*---- LOAD BOOT CODE */ + for (i = 0; i < 512; i += 2) { + val = s_bit_firmware_info[core].bit_code[i]; + val |= (s_bit_firmware_info[core].bit_code[i+1] << 16); + WriteVpu(code_base+(i*2), val); + } + + regVal = 0; + WriteVpuRegister(W4_PO_CONF, regVal); + + /* Reset All blocks */ + regVal = 0x7ffffff; + WriteVpuRegister(W4_VPU_RESET_REQ, regVal); + + /* Waiting reset done */ + while (ReadVpuRegister(W4_VPU_RESET_STATUS)) { + if (time_after(jiffies, timeout)) + goto DONE_WAKEUP; + } + + WriteVpuRegister(W4_VPU_RESET_REQ, 0); + + /* remap page size */ + remap_size = (code_size >> 12) & 0x1ff; + regVal = 0x80000000 | (W4_REMAP_CODE_INDEX<<12) + | (0 << 16) | (1<<11) | remap_size; + WriteVpuRegister(W4_VPU_REMAP_CTRL, regVal); + /* DO NOT CHANGE! */ + WriteVpuRegister(W4_VPU_REMAP_VADDR, 0x00000000); + WriteVpuRegister(W4_VPU_REMAP_PADDR, code_base); + WriteVpuRegister(W4_ADDR_CODE_BASE, code_base); + WriteVpuRegister(W4_CODE_SIZE, code_size); + WriteVpuRegister(W4_CODE_PARAM, 0); + WriteVpuRegister(W4_INIT_VPU_TIME_OUT_CNT, timeout); + WriteVpuRegister(W4_HW_OPTION, hwOption); + + /* Interrupt */ + regVal = (1 << W4_INT_DEC_PIC_HDR); + regVal |= (1 << W4_INT_DEC_PIC); + regVal |= (1 << W4_INT_QUERY_DEC); + regVal |= (1 << W4_INT_SLEEP_VPU); + regVal |= (1 << W4_INT_BSBUF_EMPTY); + regVal = 0xfffffefe; + WriteVpuRegister(W4_VPU_VINT_ENABLE, regVal); + Wave4BitIssueCommand(core, W4_CMD_INIT_VPU); + WriteVpuRegister(W4_VPU_REMAP_CORE_START, 1); + while (ReadVpuRegister(W4_VPU_BUSY_STATUS)) { + if (time_after(jiffies, timeout)) + goto DONE_WAKEUP; + } + + if (ReadVpuRegister(W4_RET_SUCCESS) == 0) { + enc_pr(LOG_ERROR, + "WAKEUP_VPU failed [0x%x]", + ReadVpuRegister(W4_RET_FAIL_REASON)); + goto DONE_WAKEUP; + } + } + + if (s_vpu_open_ref_count == 0) + vpu_clk_config(0); +DONE_WAKEUP: + if (s_vpu_open_ref_count > 0) + vpu_clk_config(1); + return 0; +} +#else +#define vpu_suspend NULL +#define vpu_resume NULL +#endif /* !CONFIG_PM */ + +static const struct of_device_id cnm_hevcenc_dt_match[] = { + { + .compatible = "cnm, HevcEnc", + }, + {}, +}; + +static struct platform_driver vpu_driver = { + .driver = { + .name = VPU_PLATFORM_DEVICE_NAME, + .of_match_table = cnm_hevcenc_dt_match, + }, + .probe = vpu_probe, + .remove = vpu_remove, + .suspend = vpu_suspend, + .resume = vpu_resume, +}; + +static s32 __init vpu_init(void) +{ + s32 res; + enc_pr(LOG_DEBUG, "vpu_init\n"); + if (get_cpu_type() != MESON_CPU_MAJOR_ID_GXM) { + enc_pr(LOG_DEBUG, + "The chip is not support hevc encoder\n"); + return -1; + } + res = platform_driver_register(&vpu_driver); + enc_pr(LOG_INFO, + "end vpu_init result=0x%x\n", res); + return res; +} + +static void __exit vpu_exit(void) +{ + enc_pr(LOG_DEBUG, "vpu_exit\n"); + if (get_cpu_type() == MESON_CPU_MAJOR_ID_GXM) + platform_driver_unregister(&vpu_driver); + return; +} + +static const struct reserved_mem_ops rmem_hevc_ops = { + .device_init = hevc_mem_device_init, +}; + +static s32 __init hevc_mem_setup(struct reserved_mem *rmem) +{ + rmem->ops = &rmem_hevc_ops; + enc_pr(LOG_DEBUG, "HevcEnc reserved mem setup.\n"); + return 0; +} + +module_param(print_level, uint, 0664); +MODULE_PARM_DESC(print_level, "\n print_level\n"); + +module_param(clock_level, uint, 0664); +MODULE_PARM_DESC(clock_level, "\n clock_level\n"); + +MODULE_AUTHOR("Amlogic using C&M VPU, Inc."); +MODULE_DESCRIPTION("VPU linux driver"); +MODULE_LICENSE("GPL"); + +module_init(vpu_init); +module_exit(vpu_exit); +RESERVEDMEM_OF_DECLARE(cnm_hevc, "cnm, HevcEnc-memory", hevc_mem_setup); diff --git a/drivers/frame_sink/encoder/h265/vpu.h b/drivers/frame_sink/encoder/h265/vpu.h new file mode 100644 index 0000000..eaed0b7 --- a/dev/null +++ b/drivers/frame_sink/encoder/h265/vpu.h @@ -0,0 +1,288 @@ +/* + * vpu.h + * + * linux device driver for VPU. + * + * Copyright (C) 2006 - 2013 CHIPS&MEDIA INC. + * + * 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 __VPU_DRV_H__ +#define __VPU_DRV_H__ + +#include <linux/fs.h> +#include <linux/types.h> +#include <linux/compat.h> + +#define MAX_INST_HANDLE_SIZE (32*1024) +#define MAX_NUM_INSTANCE 4 +#define MAX_NUM_VPU_CORE 1 + +#define W4_CMD_INIT_VPU (0x0001) +#define W4_CMD_SLEEP_VPU (0x0400) +#define W4_CMD_WAKEUP_VPU (0x0800) + +/* GXM: 2000/10 = 200M */ +#define HevcEnc_L0() WRITE_HHI_REG(HHI_WAVE420L_CLK_CNTL, \ + (3 << 25) | (1 << 16) | (3 << 9) | (1 << 0)) +/* GXM: 2000/8 = 250M */ +#define HevcEnc_L1() WRITE_HHI_REG(HHI_WAVE420L_CLK_CNTL, \ + (1 << 25) | (1 << 16) | (1 << 9) | (1 << 0)) +/* GXM: 2000/7 = 285M */ +#define HevcEnc_L2() WRITE_HHI_REG(HHI_WAVE420L_CLK_CNTL, \ + (4 << 25) | (0 << 16) | (4 << 9) | (0 << 0)) +/*GXM: 2000/6 = 333M */ +#define HevcEnc_L3() WRITE_HHI_REG(HHI_WAVE420L_CLK_CNTL, \ + (2 << 25) | (1 << 16) | (2 << 9) | (1 << 0)) +/* GXM: 2000/5 = 400M */ +#define HevcEnc_L4() WRITE_HHI_REG(HHI_WAVE420L_CLK_CNTL, \ + (3 << 25) | (0 << 16) | (3 << 9) | (0 << 0)) +/* GXM: 2000/4 = 500M */ +#define HevcEnc_L5() WRITE_HHI_REG(HHI_WAVE420L_CLK_CNTL, \ + (1 << 25) | (0 << 16) | (1 << 9) | (0 << 0)) +/* GXM: 2000/3 = 667M */ +#define HevcEnc_L6() WRITE_HHI_REG(HHI_WAVE420L_CLK_CNTL, \ + (2 << 25) | (0 << 16) | (2 << 9) | (0 << 0)) + +#define HevcEnc_clock_enable(level) \ + do { \ + WRITE_HHI_REG(HHI_WAVE420L_CLK_CNTL, \ + READ_HHI_REG(HHI_WAVE420L_CLK_CNTL) \ + & (~(1 << 8)) & (~(1 << 24))); \ + if (level == 0) \ + HevcEnc_L0(); \ + else if (level == 1) \ + HevcEnc_L1(); \ + else if (level == 2) \ + HevcEnc_L2(); \ + else if (level == 3) \ + HevcEnc_L3(); \ + else if (level == 4) \ + HevcEnc_L4(); \ + else if (level == 5) \ + HevcEnc_L5(); \ + else if (level == 6) \ + HevcEnc_L6(); \ + WRITE_HHI_REG(HHI_WAVE420L_CLK_CNTL, \ + READ_HHI_REG(HHI_WAVE420L_CLK_CNTL) \ + | (1 << 8) | (1 << 24)); \ + } while (0) + +#define HevcEnc_clock_disable() \ + WRITE_HHI_REG(HHI_WAVE420L_CLK_CNTL, \ + READ_HHI_REG(HHI_WAVE420L_CLK_CNTL) \ + & (~(1 << 8)) & (~(1 << 24))); + +struct compat_vpudrv_buffer_t { + u32 size; + u32 cached; + compat_ulong_t phys_addr; + compat_ulong_t base; /* kernel logical address in use kernel */ + compat_ulong_t virt_addr; /* virtual user space address */ +}; + +struct vpudrv_buffer_t { + u32 size; + u32 cached; + ulong phys_addr; + ulong base; /* kernel logical address in use kernel */ + ulong virt_addr; /* virtual user space address */ +}; + +struct vpu_bit_firmware_info_t { + u32 size; /* size of this structure*/ + u32 core_idx; + u32 reg_base_offset; + u16 bit_code[512]; +}; + +struct vpudrv_inst_info_t { + u32 core_idx; + u32 inst_idx; + s32 inst_open_count; /* for output only*/ +}; + +struct vpudrv_intr_info_t { + u32 timeout; + s32 intr_reason; +}; + +struct vpu_drv_context_t { + struct fasync_struct *async_queue; + ulong interrupt_reason; + u32 open_count; /*!<< device reference count. Not instance count */ +}; + +/* To track the allocated memory buffer */ +struct vpudrv_buffer_pool_t { + struct list_head list; + struct vpudrv_buffer_t vb; + struct file *filp; +}; + +/* To track the instance index and buffer in instance pool */ +struct vpudrv_instanace_list_t { + struct list_head list; + ulong inst_idx; + ulong core_idx; + struct file *filp; +}; + +struct vpudrv_instance_pool_t { + u8 codecInstPool[MAX_NUM_INSTANCE][MAX_INST_HANDLE_SIZE]; +}; + +#define VPUDRV_BUF_LEN struct vpudrv_buffer_t +#define VPUDRV_BUF_LEN32 struct compat_vpudrv_buffer_t +#define VPUDRV_INST_LEN struct vpudrv_inst_info_t + +#define VDI_MAGIC 'V' +#define VDI_IOCTL_ALLOCATE_PHYSICAL_MEMORY \ + _IOW(VDI_MAGIC, 0, VPUDRV_BUF_LEN) + +#define VDI_IOCTL_FREE_PHYSICALMEMORY \ + _IOW(VDI_MAGIC, 1, VPUDRV_BUF_LEN) + +#define VDI_IOCTL_WAIT_INTERRUPT \ + _IOW(VDI_MAGIC, 2, struct vpudrv_intr_info_t) + +#define VDI_IOCTL_SET_CLOCK_GATE \ + _IOW(VDI_MAGIC, 3, u32) + +#define VDI_IOCTL_RESET \ + _IOW(VDI_MAGIC, 4, u32) + +#define VDI_IOCTL_GET_INSTANCE_POOL \ + _IOW(VDI_MAGIC, 5, VPUDRV_BUF_LEN) + +#define VDI_IOCTL_GET_COMMON_MEMORY \ + _IOW(VDI_MAGIC, 6, VPUDRV_BUF_LEN) + +#define VDI_IOCTL_GET_RESERVED_VIDEO_MEMORY_INFO \ + _IOW(VDI_MAGIC, 8, VPUDRV_BUF_LEN) + +#define VDI_IOCTL_OPEN_INSTANCE \ + _IOW(VDI_MAGIC, 9, VPUDRV_INST_LEN) + +#define VDI_IOCTL_CLOSE_INSTANCE \ + _IOW(VDI_MAGIC, 10, VPUDRV_INST_LEN) + +#define VDI_IOCTL_GET_INSTANCE_NUM \ + _IOW(VDI_MAGIC, 11, VPUDRV_INST_LEN) + +#define VDI_IOCTL_GET_REGISTER_INFO \ + _IOW(VDI_MAGIC, 12, VPUDRV_BUF_LEN) + +#define VDI_IOCTL_FLUSH_BUFFER \ + _IOW(VDI_MAGIC, 13, VPUDRV_BUF_LEN) + +#define VDI_IOCTL_ALLOCATE_PHYSICAL_MEMORY32 \ + _IOW(VDI_MAGIC, 0, VPUDRV_BUF_LEN32) + +#define VDI_IOCTL_FREE_PHYSICALMEMORY32 \ + _IOW(VDI_MAGIC, 1, VPUDRV_BUF_LEN32) + +#define VDI_IOCTL_GET_INSTANCE_POOL32 \ + _IOW(VDI_MAGIC, 5, VPUDRV_BUF_LEN32) + +#define VDI_IOCTL_GET_COMMON_MEMORY32 \ + _IOW(VDI_MAGIC, 6, VPUDRV_BUF_LEN32) + +#define VDI_IOCTL_GET_RESERVED_VIDEO_MEMORY_INFO32 \ + _IOW(VDI_MAGIC, 8, VPUDRV_BUF_LEN32) + +#define VDI_IOCTL_GET_REGISTER_INFO32 \ + _IOW(VDI_MAGIC, 12, VPUDRV_BUF_LEN32) + +#define VDI_IOCTL_FLUSH_BUFFER32 \ + _IOW(VDI_MAGIC, 13, VPUDRV_BUF_LEN32) + +enum { + W4_INT_INIT_VPU = 0, + W4_INT_DEC_PIC_HDR = 1, + W4_INT_SET_PARAM = 1, + W4_INT_ENC_INIT_SEQ = 1, + W4_INT_FINI_SEQ = 2, + W4_INT_DEC_PIC = 3, + W4_INT_ENC_PIC = 3, + W4_INT_SET_FRAMEBUF = 4, + W4_INT_FLUSH_DEC = 5, + W4_INT_ENC_SLICE_INT = 7, + W4_INT_GET_FW_VERSION = 8, + W4_INT_QUERY_DEC = 9, + W4_INT_SLEEP_VPU = 10, + W4_INT_WAKEUP_VPU = 11, + W4_INT_CHANGE_INT = 12, + W4_INT_CREATE_INSTANCE = 14, + W4_INT_BSBUF_EMPTY = 15, + /*!<< Bitstream buffer empty[dec]/full[enc] */ +}; + +/* WAVE4 registers */ +#define VPU_REG_BASE_ADDR 0xc8810000 +#define VPU_REG_SIZE (0x4000 * MAX_NUM_VPU_CORE) + +#define W4_REG_BASE 0x0000 +#define W4_VPU_BUSY_STATUS (W4_REG_BASE + 0x0070) +#define W4_VPU_INT_REASON_CLEAR (W4_REG_BASE + 0x0034) +#define W4_VPU_VINT_CLEAR (W4_REG_BASE + 0x003C) +#define W4_VPU_VPU_INT_STS (W4_REG_BASE + 0x0044) +#define W4_VPU_INT_REASON (W4_REG_BASE + 0x004c) + +#define W4_RET_SUCCESS (W4_REG_BASE + 0x0110) +#define W4_RET_FAIL_REASON (W4_REG_BASE + 0x0114) + +/* WAVE4 INIT, WAKEUP */ +#define W4_PO_CONF (W4_REG_BASE + 0x0000) +#define W4_VCPU_CUR_PC (W4_REG_BASE + 0x0004) + +#define W4_VPU_VINT_ENABLE (W4_REG_BASE + 0x0048) + +#define W4_VPU_RESET_REQ (W4_REG_BASE + 0x0050) +#define W4_VPU_RESET_STATUS (W4_REG_BASE + 0x0054) + +#define W4_VPU_REMAP_CTRL (W4_REG_BASE + 0x0060) +#define W4_VPU_REMAP_VADDR (W4_REG_BASE + 0x0064) +#define W4_VPU_REMAP_PADDR (W4_REG_BASE + 0x0068) +#define W4_VPU_REMAP_CORE_START (W4_REG_BASE + 0x006C) +#define W4_VPU_BUSY_STATUS (W4_REG_BASE + 0x0070) + +#define W4_HW_OPTION (W4_REG_BASE + 0x0124) +#define W4_CODE_SIZE (W4_REG_BASE + 0x011C) +/* Note: W4_INIT_CODE_BASE_ADDR should be aligned to 4KB */ +#define W4_ADDR_CODE_BASE (W4_REG_BASE + 0x0118) +#define W4_CODE_PARAM (W4_REG_BASE + 0x0120) +#define W4_INIT_VPU_TIME_OUT_CNT (W4_REG_BASE + 0x0134) + +/* WAVE4 Wave4BitIssueCommand */ +#define W4_CORE_INDEX (W4_REG_BASE + 0x0104) +#define W4_INST_INDEX (W4_REG_BASE + 0x0108) +#define W4_COMMAND (W4_REG_BASE + 0x0100) +#define W4_VPU_HOST_INT_REQ (W4_REG_BASE + 0x0038) + +#define W4_BS_RD_PTR (W4_REG_BASE + 0x0130) +#define W4_BS_WR_PTR (W4_REG_BASE + 0x0134) +#define W4_RET_ENC_PIC_BYTE (W4_REG_BASE + 0x01C8) + +#define W4_REMAP_CODE_INDEX 0 + +#define ReadVpuRegister(addr) \ + readl((void __iomem *)(s_vpu_register.virt_addr \ + + s_bit_firmware_info[core].reg_base_offset + addr)) + +#define WriteVpuRegister(addr, val) \ + writel((u32)val, (void __iomem *)(s_vpu_register.virt_addr \ + + s_bit_firmware_info[core].reg_base_offset + addr)) + +#define WriteVpu(addr, val) writel((u32)val, (void __iomem *)addr) +#endif diff --git a/drivers/include/dummy-for-git-empty-dir b/drivers/include/dummy-for-git-empty-dir new file mode 100644 index 0000000..e69de29 --- a/dev/null +++ b/drivers/include/dummy-for-git-empty-dir diff --git a/drivers/stream_input/Makefile b/drivers/stream_input/Makefile new file mode 100644 index 0000000..7d6a2c0 --- a/dev/null +++ b/drivers/stream_input/Makefile @@ -0,0 +1,10 @@ +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 +stream_input-objs += parser/rmparser.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..f1d2c74 --- a/dev/null +++ b/drivers/stream_input/amports/amstream.c @@ -0,0 +1,3651 @@ +/* + * 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" +#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 CONFIG_AM_VDEC_REAL //DEBUG_TMP + +#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 DEFAULT_VIDEO_BUFFER_SIZE (1024 * 1024 * 3) +#define DEFAULT_VIDEO_BUFFER_SIZE_4K (1024 * 1024 * 6) +#define DEFAULT_VIDEO_BUFFER_SIZE_TVP (1024 * 1024 * 10) +#define DEFAULT_VIDEO_BUFFER_SIZE_4K_TVP (1024 * 1024 * 15) + + +#define DEFAULT_AUDIO_BUFFER_SIZE (1024*768*2) +#define DEFAULT_SUBTITLE_BUFFER_SIZE (1024*256) + +static int def_4k_vstreambuf_sizeM = + (DEFAULT_VIDEO_BUFFER_SIZE_4K >> 20); +static int def_vstreambuf_sizeM = + (DEFAULT_VIDEO_BUFFER_SIZE >> 20); +static int debugflags; +static int slow_input; + + + + +/* #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 "/sdcard/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 + + + +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[] = { +#ifdef CONFIG_MULTI_DEC + { + .name = "amstream_vbuf", + .type = PORT_TYPE_ES | PORT_TYPE_VIDEO, + .fops = &vbuf_fops, + }, + { + .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, + }, +#else + { + .name = "amstream_vbuf", + .type = PORT_TYPE_ES | PORT_TYPE_VIDEO, + .fops = &vbuf_fops, + }, +#endif + { + .name = "amstream_abuf", + .type = PORT_TYPE_ES | PORT_TYPE_AUDIO, + .fops = &abuf_fops, + }, +#ifdef CONFIG_MULTI_DEC + { + .name = "amstream_mpts", + .type = PORT_TYPE_MPTS | PORT_TYPE_VIDEO | + PORT_TYPE_AUDIO | PORT_TYPE_SUB, + .fops = &mpts_fops, + }, + { + .name = "amstream_mpts_sched", + .type = PORT_TYPE_MPTS | PORT_TYPE_VIDEO | + PORT_TYPE_AUDIO | PORT_TYPE_SUB | + PORT_TYPE_DECODER_SCHED, + .fops = &mpts_fops, + }, +#else + { + .name = "amstream_mpts", + .type = PORT_TYPE_MPTS | PORT_TYPE_VIDEO | + PORT_TYPE_AUDIO | PORT_TYPE_SUB, + .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, + }, +#ifdef CONFIG_MULTI_DEC + { + .name = "amstream_hevc", +#ifdef CONFIG_AM_VDEC_DV +/*test dobly vision, remove later*/ + .type = PORT_TYPE_ES | PORT_TYPE_VIDEO | PORT_TYPE_HEVC | + PORT_TYPE_DECODER_SCHED | PORT_TYPE_DUALDEC, + .fops = &vbuf_fops, + .vformat = VFORMAT_HEVC, +#else + .type = PORT_TYPE_ES | PORT_TYPE_VIDEO | PORT_TYPE_HEVC, + .fops = &vbuf_fops, + .vformat = VFORMAT_HEVC, +#endif + }, + { + .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, + } +#ifdef CONFIG_AM_VDEC_DV + , + { + .name = "amstream_dves_avc", + .type = PORT_TYPE_ES | PORT_TYPE_VIDEO | + PORT_TYPE_DECODER_SCHED | PORT_TYPE_DUALDEC, + .fops = &vbuf_fops, + }, + { + .name = "amstream_dves_hevc", + .type = PORT_TYPE_ES | PORT_TYPE_VIDEO | PORT_TYPE_HEVC | + PORT_TYPE_DECODER_SCHED | PORT_TYPE_DUALDEC, + .fops = &vbuf_fops, + .vformat = VFORMAT_HEVC, + } +#endif +#else + { + .name = "amstream_hevc", + .type = PORT_TYPE_ES | PORT_TYPE_VIDEO | PORT_TYPE_HEVC, + .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 (PTS_TYPE_VIDEO == type) + return &bufs[BUF_TYPE_VIDEO]; + if (PTS_TYPE_AUDIO == type) + return &bufs[BUF_TYPE_AUDIO]; + if (has_hevc_vdec()) { + if (PTS_TYPE_HEVC == type) + 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 = def_4k_vstreambuf_sizeM * SZ_1M; + if (codec_mm_video_tvp_enabled()) + pvbuf->buf_size = DEFAULT_VIDEO_BUFFER_SIZE_4K_TVP; + 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 > def_vstreambuf_sizeM * SZ_1M) { + if (codec_mm_video_tvp_enabled()) + pvbuf->buf_size = DEFAULT_VIDEO_BUFFER_SIZE_TVP; + } else { + pvbuf->buf_size = def_vstreambuf_sizeM * SZ_1M; + if (codec_mm_video_tvp_enabled()) + pvbuf->buf_size = DEFAULT_VIDEO_BUFFER_SIZE_TVP; + } + 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: + if (vdec->slave) + vdec_release(vdec->slave); + 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; + } else { + pbuf->for_4k = 0; + } + + 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 (vdec_dual(vdec)) { + r = vdec_init(vdec->slave, + (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; + pvbuf->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); + if (slow_input) { + pr_info("slow_input: es codec write size %x\n", r); + msleep(3000); + } +#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; +#ifdef DATA_DEBUG + debug_file_write(buf, count); +#endif + 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); + if (slow_input) { + pr_info("slow_input: ts codec write size %x\n", r); + msleep(3000); + } + 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; + } else { + 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; + } + } else { + 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_DECODER_SCHED) == 0) && + (s->type & PORT_TYPE_VIDEO) && + (s->flag & PORT_FLAG_IN_USE)) { + 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) { + /* 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 (port->type & PORT_TYPE_VIDEO) { + /* TODO: mod gate */ + /* switch_mod_gate_by_name("vdec", 1); */ + amports_switch_gate("vdec", 1); + amports_switch_gate("clk_vdec_mux", 1); + amports_switch_gate("clk_hcodec_mux", 1); + + if (has_hevc_vdec()) { + amports_switch_gate("clk_hevc_mux", 1); + 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) { + priv->vdec = vdec_create(port, NULL); + + if (priv->vdec == NULL) { + port->flag = 0; + kfree(priv); + pr_err("amstream: vdec creation failed\n"); + return -ENOMEM; + } + + if (port->type & PORT_TYPE_DUALDEC) { + priv->vdec->slave = vdec_create(port, priv->vdec); + + if (priv->vdec->slave == NULL) { + vdec_release(priv->vdec); + port->flag = 0; + kfree(priv); + pr_err("amstream: sub 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) { + if (priv->vdec->slave) + vdec_release(priv->vdec->slave); + + 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 + if ((port->type & PORT_TYPE_MPTS) && + ((port->flag & PORT_FLAG_VFORMAT) == 0)) { + vdec_poweroff(VDEC_1); + vdec_poweroff(VDEC_HEVC); + } else if ((port->vformat == VFORMAT_HEVC + || port->vformat == VFORMAT_VP9)) { + vdec_poweroff(VDEC_HEVC); + } else { + vdec_poweroff(VDEC_1); + } +#endif + } + /* TODO: mod gate */ + /* switch_mod_gate_by_name("vdec", 0); */ + amports_switch_gate("vdec", 0); + amports_switch_gate("clk_hcodec_mux", 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) { + /* 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; + } + case AMSTREAM_SET_FRAME_BASE_PATH: + if ((this->type & PORT_TYPE_DECODER_SCHED) && + (parm.frame_base_video_path < FRAME_BASE_PATH_MAX)) { + vdec_set_video_path(priv->vdec, parm.data_32); + } else + r = -EINVAL; + 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; + } else { + 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_FIFO_NUM == userdata_poc_ri) + 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; + case AMSTREAM_SET_PTR_CONFIGS: + if (this->type & PORT_TYPE_VIDEO) { + if (!parm.pointer || (parm.len <= 0) || + (parm.len > PAGE_SIZE)) { + r = -EINVAL; + } else { + r = copy_from_user(priv->vdec->config, + parm.pointer, parm.len); + if (r) + r = -EINVAL; + else + priv->vdec->config_len = parm.len; + } + } 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 = ¶ + 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 = ¶ + 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 = ¶ + + 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; + else { + struct adec_status astatus; + struct am_io_param para; + struct am_io_param *p = ¶ + + 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_FIFO_NUM == userdata_poc_ri) + 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; + u32 len; +}; + +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) || + put_user(data32->len, &data->len)) + 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); + + /*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, "amstream"); + + class_unregister(&amstream_class); + + 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, + } +}; + +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); + return; +} + +module_init(amstream_module_init); +module_exit(amstream_module_exit); + +module_param(debugflags, uint, 0664); +MODULE_PARM_DESC(debugflags, "\n amstream debugflags\n"); + +module_param(def_4k_vstreambuf_sizeM, uint, 0664); +MODULE_PARM_DESC(def_4k_vstreambuf_sizeM, + "\nDefault video Stream buf size for 4K MByptes\n"); + +module_param(def_vstreambuf_sizeM, uint, 0664); +MODULE_PARM_DESC(def_vstreambuf_sizeM, + "\nDefault video Stream buf size for < 1080p MByptes\n"); + +module_param(slow_input, uint, 0664); +MODULE_PARM_DESC(slow_input, "\n amstream slow_input\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/esparser.c b/drivers/stream_input/parser/esparser.c new file mode 100644 index 0000000..a9b8e29 --- a/dev/null +++ b/drivers/stream_input/parser/esparser.c @@ -0,0 +1,940 @@ +/* + * 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_single(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 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_single(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 : PAGE_SIZE << 4; + int buf_num = (buf->type == BUF_TYPE_AUDIO) ? + 20 : (2 * SZ_1M)/(PAGE_SIZE << 4); + 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) { + ssize_t ret; + ret = threadrw_write(file, stbuf, buf, count); + if (ret == -EAGAIN) { + u32 a, b; + int vdelay, adelay; + if ((stbuf->type != BUF_TYPE_VIDEO) && + (stbuf->type != BUF_TYPE_HEVC)) + return ret; + if (stbuf->buf_size > (SZ_1M * 30) || + (threadrw_buffer_size(stbuf) > SZ_1M * 10) || + !threadrw_support_more_buffers(stbuf)) + return ret; + /*only chang buffer for video.*/ + vdelay = calculation_stream_delayed_ms( + PTS_TYPE_VIDEO, &a, &b); + adelay = calculation_stream_delayed_ms( + PTS_TYPE_AUDIO, &a, &b); + if ((vdelay > 100 && vdelay < 2000) && /*vdelay valid.*/ + ((vdelay < 500) ||/*video delay is short!*/ + (adelay > 0 && adelay < 1000))/*audio is low.*/ + ) { + /*on buffer fulled. + if delay is less than 100ms we think errors, + And we add more buffer on delay < 2s. + */ + int new_size = 2 * 1024 * 1024; + threadrw_alloc_more_buffer_size( + stbuf, new_size); + } + } + return ret; + } + 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..5c8a8b1 --- a/dev/null +++ b/drivers/stream_input/parser/psparser.c @@ -0,0 +1,1160 @@ +/* + * 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_single(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)); + + if (vdec_single(vdec)) { + 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/rmparser.c b/drivers/stream_input/parser/rmparser.c new file mode 100644 index 0000000..63d7a63 --- a/dev/null +++ b/drivers/stream_input/parser/rmparser.c @@ -0,0 +1,337 @@ +/* + * drivers/amlogic/amports/rmparser.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/types.h> +#include <linux/errno.h> +#include <linux/interrupt.h> +#include <linux/timer.h> +#include <linux/sched.h> +#include <linux/fs.h> +#include <linux/dma-mapping.h> +#include <linux/platform_device.h> +#include <linux/amlogic/media/utils/amstream.h> +#include <linux/amlogic/media/frame_sync/ptsserv.h> +#include <linux/uaccess.h> + +#include <linux/amlogic/media/utils/vdec_reg.h> +#include "../amports/amports_priv.h" +#include "streambuf.h" +#include "streambuf_reg.h" +#include <linux/delay.h> +#include "rmparser.h" + +#define MANAGE_PTS + +static u32 fetch_done; +static u32 parse_halt; + +static DECLARE_WAIT_QUEUE_HEAD(rm_wq); +static const char rmparser_id[] = "rmparser-id"; + +static irqreturn_t rm_parser_isr(int irq, void *dev_id) +{ + u32 int_status = READ_MPEG_REG(PARSER_INT_STATUS); + + if (int_status & PARSER_INTSTAT_FETCH_CMD) { + WRITE_MPEG_REG(PARSER_INT_STATUS, PARSER_INTSTAT_FETCH_CMD); + fetch_done = 1; + + wake_up_interruptible(&rm_wq); + } + + return IRQ_HANDLED; +} + +s32 rmparser_init(struct vdec_s *vdec) +{ + s32 r; + parse_halt = 0; + 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); + + CLEAR_MPEG_REG_MASK(PARSER_ES_CONTROL, ES_VID_MAN_RD_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_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(PFIFO_RD_PTR, 0); + WRITE_MPEG_REG(PFIFO_WR_PTR, 0); + + WRITE_MPEG_REG(PARSER_SEARCH_MASK, 0); + WRITE_MPEG_REG(PARSER_CONTROL, (ES_SEARCH | ES_PARSER_START)); + +#ifdef MANAGE_PTS + if (pts_start(PTS_TYPE_VIDEO) < 0) + goto Err_1; + + if (pts_start(PTS_TYPE_AUDIO) < 0) + goto Err_2; +#endif + /*TODO irq */ + + /* enable interrupt */ + + r = vdec_request_irq(PARSER_IRQ, rm_parser_isr, + "rmparser", (void *)rmparser_id); + + if (r) { + pr_info("RM parser irq register failed.\n"); + goto Err_3; + } + + WRITE_MPEG_REG(PARSER_INT_STATUS, 0xffff); + WRITE_MPEG_REG(PARSER_INT_ENABLE, + ((PARSER_INT_ALL & (~PARSER_INTSTAT_FETCH_CMD)) << + PARSER_INT_AMRISC_EN_BIT) + | (PARSER_INTSTAT_FETCH_CMD << PARSER_INT_HOST_EN_BIT)); + + return 0; + +Err_3: + pts_stop(PTS_TYPE_AUDIO); +Err_2: + pts_stop(PTS_TYPE_VIDEO); +Err_1: + return -ENOENT; +} +EXPORT_SYMBOL(rmparser_init); + +void rmparser_release(void) +{ + WRITE_MPEG_REG(PARSER_INT_ENABLE, 0); + /*TODO irq */ + + vdec_free_irq(PARSER_IRQ, (void *)rmparser_id); + +#ifdef MANAGE_PTS + pts_stop(PTS_TYPE_VIDEO); + pts_stop(PTS_TYPE_AUDIO); +#endif + + return; +} +EXPORT_SYMBOL(rmparser_release); + +static inline u32 buf_wp(u32 type) +{ + return (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); +} + +static ssize_t _rmparser_write(const char __user *buf, size_t count) +{ + size_t r = count; + const char __user *p = buf; + u32 len; + int ret; + static int halt_droped_len; + u32 vwp, awp; + 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 */ + vwp = buf_wp(BUF_TYPE_VIDEO); + awp = buf_wp(BUF_TYPE_AUDIO); + 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(rm_wq, fetch_done != 0, + HZ / 10); + if (ret == 0) { + WRITE_MPEG_REG(PARSER_FETCH_CMD, 0); + parse_halt++; + pr_info + ("write timeout,retry,halt_count=%d parse_control=%x\n", + parse_halt, READ_MPEG_REG(PARSER_CONTROL)); + + //vreal_set_fatal_flag(1);//DEBUG_TMP + + if (parse_halt > 10) { + WRITE_MPEG_REG(PARSER_CONTROL, + (ES_SEARCH | ES_PARSER_START)); + pr_info("reset parse_control=%x\n", + READ_MPEG_REG(PARSER_CONTROL)); + } + return -EAGAIN; + } else if (ret < 0) + return -ERESTARTSYS; + + if (vwp == buf_wp(BUF_TYPE_VIDEO) + && awp == buf_wp(BUF_TYPE_AUDIO)) { + struct stream_buf_s *v_buf_t = + get_buf_by_type(BUF_TYPE_VIDEO); + struct stream_buf_s *a_buf_t = + get_buf_by_type(BUF_TYPE_AUDIO); + int v_st_lv = stbuf_level(v_buf_t); + int a_st_lv = stbuf_level(a_buf_t); + if ((parse_halt + 1) % 10 == 1) { + pr_info("V&A WP not changed after write"); + pr_info(",video %x->%x", vwp, + buf_wp(BUF_TYPE_VIDEO)); + pr_info(",Audio:%x-->%x,parse_halt=%d\n", + awp, buf_wp(BUF_TYPE_AUDIO), + parse_halt); + } + parse_halt++; /*wp not changed , + we think have bugs on parser now. */ + if (parse_halt > 10 && + (v_st_lv < 1000 || a_st_lv < 100)) { + /*reset while at least one is underflow. */ + WRITE_MPEG_REG(PARSER_CONTROL, + (ES_SEARCH | ES_PARSER_START)); + pr_info("reset parse_control=%x\n", + READ_MPEG_REG(PARSER_CONTROL)); + } + if (parse_halt <= 10 || + halt_droped_len < 100 * 1024) { + /*drops first 10 pkt , + some times maybe no av data */ + pr_info("drop this pkt=%d,len=%d\n", parse_halt, + len); + p += len; + r -= len; + halt_droped_len += len; + } else + return -EAGAIN; + } else { + halt_droped_len = 0; + parse_halt = 0; + p += len; + r -= len; + } + } + return count - r; +} + +ssize_t rmparser_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 towrite = count; + if ((stbuf_space(vbuf) < count) || (stbuf_space(abuf) < count)) { + if (file->f_flags & O_NONBLOCK) { + towrite = min(stbuf_space(vbuf), stbuf_space(abuf)); + if (towrite < 1024) /*? can write small? */ + return -EAGAIN; + } else { + 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; + } + } + } + towrite = min(towrite, count); + return _rmparser_write(buf, towrite); +} +EXPORT_SYMBOL(rmparser_write); + +void rm_set_vasid(u32 vid, u32 aid) +{ + pr_info("rm_set_vasid aid %d, vid %d\n", aid, vid); + WRITE_MPEG_REG(VAS_STREAM_ID, (aid << 8) | vid); + + return; +} +EXPORT_SYMBOL(rm_set_vasid); + +void rm_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); + + return; +} +EXPORT_SYMBOL(rm_audio_reset); diff --git a/drivers/stream_input/parser/rmparser.h b/drivers/stream_input/parser/rmparser.h new file mode 100644 index 0000000..5d258ee --- a/dev/null +++ b/drivers/stream_input/parser/rmparser.h @@ -0,0 +1,136 @@ +/* + * drivers/amlogic/amports/rmparser.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 RMPARSER_H +#define RMPARSER_H + +#include "../../frame_provider/decoder/utils/vdec.h" + +extern void rm_set_vasid(u32 vid, u32 aid); + +extern ssize_t rmparser_write(struct file *file, + struct stream_buf_s *vbuf, + struct stream_buf_s *abuf, + const char __user *buf, size_t count); + +s32 rmparser_init(struct vdec_s *vdec); + +extern void rmparser_release(void); + +extern void rm_audio_reset(void); + +extern void vreal_set_fatal_flag(int flag); + +#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 /* RMPARSER_H */ diff --git a/drivers/stream_input/parser/streambuf.c b/drivers/stream_input/parser/streambuf.c new file mode 100644 index 0000000..8b9aa33 --- a/dev/null +++ b/drivers/stream_input/parser/streambuf.c @@ -0,0 +1,418 @@ +/* + * 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; + } + + 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); + } + if (buf->buf_size < buf->canusebuf_size) + buf->canusebuf_size = 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 (NULL != fetchbuf) + 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..deeaf19 --- a/dev/null +++ b/drivers/stream_input/parser/thread_rw.c @@ -0,0 +1,606 @@ +/* + * 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; + int from_cma; +}; + +#define MAX_MM_BUFFER_NUM 16 +struct threadrw_write_task { + struct file *file; + struct delayed_work write_work; + DECLARE_KFIFO_PTR(datafifo, void *); + DECLARE_KFIFO_PTR(freefifo, void *); + int bufs_num; + int max_bufs; + int errors; + spinlock_t lock; + struct mutex mutex; + struct stream_buf_s *sbuf; + int buffered_data_size; + int passed_data_len; + int buffer_size; + int def_block_size; + int data_offset; + int writework_on; + unsigned long codec_mm_buffer[MAX_MM_BUFFER_NUM]; + int manual_write; + int failed_onmore; + 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->manual_write && + rwbuf->from_cma && + !rwbuf->write_off) + codec_mm_dma_flush(rwbuf->vbuffer, + rwbuf->buffer_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); + int need_retry = 1; + task->writework_on = 1; + while (need_retry) { + mutex_lock(&task->mutex); + need_retry = do_write_work_in(task); + mutex_unlock(&task->mutex); + } + threadrw_schedule_delayed_work(task, HZ / 10); + task->writework_on = 0; +} + +static int alloc_task_buffers_inlock(struct threadrw_write_task *task, + int new_bubffers, + int block_size) +{ + struct threadrw_buf *rwbuf; + int i; + int used_codec_mm = task->manual_write ? 0 : 1; + int new_num = new_bubffers; + int mm_slot = -1; + int start_idx = task->bufs_num; + int total_mm = 0; + unsigned long addr; + + if (codec_mm_get_total_size() < 80 || + codec_mm_get_free_size() < 40) + used_codec_mm = 0; + if (task->bufs_num + new_num > task->max_bufs) + new_num = task->max_bufs - task->bufs_num; + for (i = 0; i < MAX_MM_BUFFER_NUM; i++) { + if (task->codec_mm_buffer[i] == 0) { + mm_slot = i; + break; + } + } + if (mm_slot < 0) + used_codec_mm = 0; + if (block_size <= 0) + block_size = DEFAULT_BLOCK_SIZE; + + if (used_codec_mm && (block_size * new_num) >= 128 * 1024) { + total_mm = ALIGN(block_size * new_num, (1 << 17)); + addr = + codec_mm_alloc_for_dma(BUF_NAME, + total_mm / PAGE_SIZE, 0, + CODEC_MM_FLAGS_DMA_CPU); + if (addr != 0) { + task->codec_mm_buffer[mm_slot] = addr; + task->buffer_size += total_mm; + } else { + used_codec_mm = 0; + } + } + for (i = 0; i < new_num; i++) { + int bufidx = start_idx + i; + rwbuf = &task->buf[bufidx]; + rwbuf->buffer_size = block_size; + if (used_codec_mm) { + unsigned long start_addr = + task->codec_mm_buffer[mm_slot]; + if (i == new_num - 1) + rwbuf->buffer_size = total_mm - + block_size * i; + rwbuf->dma_handle = (dma_addr_t) start_addr + + block_size * i; + rwbuf->vbuffer = codec_mm_phys_to_virt( + rwbuf->dma_handle); + rwbuf->from_cma = 1; + + } 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->bufs_num = bufidx; + break; + } + rwbuf->from_cma = 0; + task->buffer_size += rwbuf->buffer_size; + } + + kfifo_put(&task->freefifo, (const void *)rwbuf); + task->bufs_num = bufidx + 1; + } + if (start_idx > 0 ||/*have buffers before*/ + task->bufs_num >= 3 || + task->bufs_num == new_num) { + if (!task->def_block_size) + task->def_block_size = task->buf[0].buffer_size; + return 0; /*must >=3 for swap buffers. */ + } + if (task->bufs_num > 0) + free_task_buffers(task); + return -1; +} + +static int free_task_buffers(struct threadrw_write_task *task) +{ + int i; + for (i = 0; i < MAX_MM_BUFFER_NUM; i++) { + if (task->codec_mm_buffer[i]) + codec_mm_free_for_dma(BUF_NAME, + task->codec_mm_buffer[i]); + } + for (i = 0; i < task->bufs_num; i++) { + if (task->buf[i].vbuffer && task->buf[i].from_cma == 0) + 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_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 max_bufs = num; + int task_buffer_size; + struct threadrw_write_task *task; + int ret; + + if (!(flags & 1)) /*not audio*/ + max_bufs = 300; /*can great for video bufs.*/ + task_buffer_size = sizeof(struct threadrw_write_task) + + sizeof(struct threadrw_buf) * max_bufs; + task = vmalloc(task_buffer_size); + + if (!task) + return NULL; + memset(task, 0, task_buffer_size); + + spin_lock_init(&task->lock); + mutex_init(&task->mutex); + INIT_DELAYED_WORK(&task->write_work, do_write_work); + init_waitqueue_head(&task->wq); + ret = kfifo_alloc(&task->datafifo, max_bufs, GFP_KERNEL); + if (ret) + goto err1; + ret = kfifo_alloc(&task->freefifo, max_bufs, GFP_KERNEL); + if (ret) + goto err2; + task->write = write; + task->file = NULL; + task->buffer_size = 0; + task->manual_write = flags & 1; + task->max_bufs = max_bufs; + mutex_lock(&task->mutex); + ret = alloc_task_buffers_inlock(task, num, block_size); + mutex_unlock(&task->mutex); + 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; +} +int threadrw_support_more_buffers(struct stream_buf_s *stbuf) +{ + struct threadrw_write_task *task = stbuf->write_thread; + if (!task) + return 0; + if (task->failed_onmore) + return 0; + return task->max_bufs - task->bufs_num; +} + +/* +*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; + ssize_t size; + if (!task->file) { + task->file = file; + task->sbuf = stbuf; + } + mutex_lock(&task->mutex); + size = threadrw_write_in(task, stbuf, buf, count); + mutex_unlock(&task->mutex); + return size; +} + +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; +} +int threadrw_alloc_more_buffer_size( + struct stream_buf_s *stbuf, + int size) +{ + struct threadrw_write_task *task = stbuf->write_thread; + int block_size; + int new_num; + int ret = -1; + int old_num; + + if (!task) + return -1; + mutex_lock(&task->mutex); + block_size = task->def_block_size; + if (block_size == 0) + block_size = 32 * 1024; + new_num = size / block_size; + old_num = task->bufs_num; + if (new_num == 0) + new_num = 1; + else if (new_num > task->max_bufs - task->bufs_num) + new_num = task->max_bufs - task->bufs_num; + if (new_num != 0) + ret = alloc_task_buffers_inlock(task, new_num, + block_size); + mutex_unlock(&task->mutex); + pr_info("threadrw add more buffer from %d -> %d for size %d\n", + old_num, task->bufs_num, + size); + if (ret < 0 || old_num == task->bufs_num) + task->failed_onmore = 1; + return ret; +} + +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_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); + mutex_lock(&task->mutex); + free_task_buffers(task); + mutex_unlock(&task->mutex); + 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..f03a6e6 --- a/dev/null +++ b/drivers/stream_input/parser/thread_rw.h @@ -0,0 +1,52 @@ +/* + * 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); +int threadrw_alloc_more_buffer_size( + struct stream_buf_s *stbuf, + int size); +int threadrw_support_more_buffers(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..ba9a590 --- a/dev/null +++ b/drivers/stream_input/parser/tsdemux.c @@ -0,0 +1,1131 @@ +/* + * 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 (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_single(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_single(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); + +#ifdef CONFIG_MULTI_DEC + SET_MPEG_REG_MASK(PARSER_ES_CONTROL, ES_VID_MAN_RD_PTR); + WRITE_MPEG_REG(PARSER_VIDEO_WP, 0); + WRITE_MPEG_REG(PARSER_VIDEO_RP, 0); +#endif + + /*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..5e11c44 --- a/dev/null +++ b/drivers/stream_input/parser/tsdemux.h @@ -0,0 +1,95 @@ +/* + * 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 */ diff --git a/firmware/video_ucode.bin b/firmware/video_ucode.bin new file mode 100644 index 0000000..e495e37 --- a/dev/null +++ b/firmware/video_ucode.bin @@ -0,0 +1,8294 @@ +KCAP + + + +B< + + +?@ + +ɑ +GL @x + + +Ox +PxLx + + + + + +) + +J@ +
J +.8 + +GI , +O +I@ +I + +o +I@ +=Lʀ +I + bp + + + + +Q RPPCL + +@J +A +A +& +% +( +\ +!p! + +J + + + + +LJ + +a +A + +xJ e +B +a +@ xH + + + H G +G@ +` +xM` + +x
+ +xoG` +@x@xy + QL +FL@LBLȂ@ +G + + +q@ + + + + + + t + + + + +Iʁ + + + + +B@Ȃ + + + + + + + + +G + +GI +I +d + +` +@x +$ + + + +@x$@ +W(Ǥ +*e@-UU +\!@xU@-U +¢V + +@TeP W| NB +EE@ +BV +@@xAՀe +&N U +TeP W| NB +U@%$U@x$&) +bV +`aV0 +&N U'$ + + +$` + + + + + u V,E +@ + + +B +@A
Ǡ + +A + + +@@x +NNN@ + + +@@ II24 A" + + Iɐ ɐ @ I + +@I@ +I + +K +K +< +` +xJ + +xIǀ! +< +J + +xJ +xJ +G2 +H" + + +2 +xJ +3@ +G4 + + +~ $ +H +! + +N,@x + +@x + +c + x.I,J) +` +,@ +I +,@Ȅ@ -,
- +@ǠA+,- + +@ +G +;H -% +< + I + + + H +H@a + + +;I + + + + +GǑ@ʡ + +GH;ȡ@ʡ + + +G @@@ + +G + +G @@ + +G @ +I; +B + J +J + +J9 +@b +; + + + + +!sstG +@b + +GuuG + +@b + + +~ + + +o + + +H + $ɀ +@Ip p QIs Q #G)!D G)d + +P;! + + + + + + + + + +G,",@a +QI + + +Q +? +R + + + +ȼo + +@ +G + +G# +# +q@)#q s +s s +' +L! o +!*! +G) +))Wx@Ix +))@%xx +G) +))OxAx +))x +G$) + + +$ +@o + + + +'x 'LI! &'()) a ' 'LI! H$ a ' CI#x%>?L> @ +#LGB0 +@ +@ +* + + + @o +I@ +((#LB0 o + + A R K +(@xGB +%B +G +H + +` +x + + + + +S@& NI HB +@ +N @o +@N @n +$m + +G$) + +' +'x 'LI! &') a ' 'LI! H$ a '#%$ +o + +!L"L%'Le! ,a +* +$ʀ + + + +( +%&&&r !G%@ +G + +S& +] + +* + +$ @o +&I@b&@x* H"P !&% + + +, + +-xB + +G + +-J +Fa + + + ++ + + +f +J+J + + +Q +? +RP +@#\ a +L+L +@4 +x, +I@ ` + @I,Db +@x +-J + + +-G% +! + +-J+ + +x +! + + + @a + +x + b@ʢ c +$"L2@ + + + +CALc +AA K)` +! + + +@ +a $a ' + N, +?G)á +,@ǠA+ ++?aǠA+PCG + + + +ߏN LJ +J2x +@Ȇ + +0 +` + + + + 1 +@ +M*@I +G + *,@ +@@ +M*@I + *,@ +@ +M*@I + +ql +@0 +G + ,@ +@@0 + ,@ +@0 +qp + +qp +` + +qIq + + +J +O+ +a
` +z + +."@ +o@l + +J + +O+ + +a
` +z + +."@ +o@l + +G@@ + + + + + +H +Hs + + +@r + +@ + +@ + +x + +@ +x +@q~ +
@e#LJ + + + + +r + +
I + +@B#G@H
Q + +Q +Q + + + + +GG@Q +@ + / +B + / + +@*#
+;p ; + +GС@ʡ + +G@@!x + + +G +!I@ +R B@h + H +H:
B G) +@!D # +G +G +GB G +G + +xQ + +;Gp ; + +GБ@ʡ + + ;R` G)"j ! (! )I + + +@ + +Ϡ +Aaa + +@G1 +N + +I + + +@ +G +Ϡ +Aaa +@ +@ +} + + + +} +bPL +xG + + +Gǡ@ʡ +@}#+#L +G@ + + +/R + + +2@ +#@ +x +GpqG,H:Q 9qp # ~ =!^ HL <" !Z H=a ==a >H>a >>a a 9
ȁP G + +@x + + + +H + +t@^#sRP @]#tR` G)"j ! + + +Ѐ)` + +B @V#BH U#BP B @ s@ + tx +s + +B L#BH K#BP B @ + H + + +a +G@GA +I +I + + @ +>#"@ +@x "@ + + +G +@ + +G"@ @6#7 +x +@` +x + +@x +` +9x + + + + + + + + + +@ +EFQ@ + + + + + +1 1 +N +O +N! +N + + + + + + + +A@A` + + + + +@`AA0@ +A + + + + + + I @xp + + +CL + + + + + +Š +x +I +I + b + +H +H +` +x +ȃ +` +Lx@cx + + + + + + + +` + + +H@ + + + + + + + + +@ + I +B + I + + +IJ + IHrR + +FLAKȂ + + +N + +IJ + IH@ +@ +Q +HR + +@ +H +I a + + +b +H@ n +b +HrR b + H2 + +a + + +IJ2 + + +H2 + + @a + H2 + + +@7 + + +H +H + x + +H) +B +@ + +Ϡ +Aaa + +M@ +2 + + + +Ϡ +Aaa + +M@ + +
+@< +
+ +M@a +x + @ +Ȁ Ȁ ) + + +@ + + + + + +` + +ɂP + +J + + + + +@ + +@ +` +@ + +@ +` + + +8 +J + + +8 +R +IJ +QP PCII + + +X +L^ J@ +I +K@ ɂp ɂ` +Aɂ@ a + +IK! +Hb + + K + +@ + + +H + xI + + +@ + + +"@ # +1 +@x +s +!@ + +Hr + H@I + + + B @#EBH #EBP B @ +@IH @ + + + + @#E"@ ! +@l +!@ + +Hr + H@I + +H +!@ + +HHr + H@I +V +@ +S + + B @#EBH #EBP B @ +@ +I@I` +H@
I + )I! +@ + + +H@H`
+# @x + + +N +C @ + + + + +I + + +@P + + +I @n + +QRa +II@ + +b +O + +x +xd + + + + +K + + +O +@l + + @L" Il + + + +@`J +2 +@M + +@`J + + + +8 + @b + + +
@
`@ + +# +K# + @ `H +II@ + +E + + +@`2@ʢ +R +I + +@ + +@`<# +E + + + +@ + + +2@ʤ +5 + + + +!Hr@ + +@ + + +T +$xR +x P + + + + I +@`J + +S +@I +@`3@ + @@ + +@ + +@`J + +L! + + + +L + +@`3@ +
Ѐ @Q + + + +L +L +I@&l + +@ + D @ + + + +CL + + + + +@NH2 + +$ + + + + +@Hx +1xox + + +I + + +Jʀ +@I Ȁ QIȃ Q ' +"D d + +B@ H + +"x Q IH I +$KH
+ + +ȃ +I& +L" o +[) + + + +) +) + + +) +) + + + +) +) + + +) +) +)) +(@o + + +'L! !"#)Ja ' +'L! )Ja ' +CJ+@x%LLLL'L! !c +I) + +o +@ +*Q !ʂp L+H'*#LHB0 o +H + B R +H'@x, +e +l@b + + LB ¤"@BY +x + +L2 K +" +x + +%B +H +I + +` +x + +((o + +(&&& ( + +@%x +(% +U@( +N B +@ +N @o +M@N @n +(m + +( +)) + +'L! !)Ja ' +'L! )Ja '+%(o + +*Q !ʂp @xLL'L! !a +I) + +(@o + +*Q !ʂp H'*#LHB0 o +B H' +%&&& !$@ + H + +CJ +f +U@& +* +K( +( +@o +(@ +o + +"P +!& + + + +I +Iy +!% + +%G +"K +Fa + +d +#K +I +! +H +H +"J@ J +JI %b +@ +x + +x@ +J#"H' + +" +"K+ + +x + +` + +" +" +J# + + +a I + + +=LȀ +@a + +x + c@c +$"L3@ + + +IA IσA I A
3@e + + +AJA
+ +%e +]a a ' +O +O +?H# +H#?aǠAH#PCH + + + +I +# +I + +@ + H + +"P +!&J +@,x' + +@x& +i*% +&#i@("(h +(\(!xY@("h +'¢h +'') +'@iP | '& +'zB'zB'@i9@(I'z'h$(@ +'Bh +'@@x'Ad +((N( + &J +'iP | ' +@''&@x&'%%f%% a '@i()'z'% ! g%9@(I'z'h$(@ +'bh +'`'a*( +((N( + &j%(%%h% +( +' +&' +@g"&&( + + + + y h +&z&J + &b + + a +&B' +&@&A&
%Ǡ + +a & + +N J + + +JG + +I + ʠ + + ʠ + I +D + +E +2 + QIKbp I Q + IJ + +@I@ +I + ʠ + + ʠ + + + +@@ +ߏN LJ +J2x +@Ȇ + +0 +` + +-x +@` +5x + +@7x +` +Tx +L L + + +GI˂ !KO +@ +I + + + + P + +o +@=Lˢˀ + +N J + + + + + + + + + + + +@ +EFQ@ + + + + + +1 1 +N +O +N! +N + + + + + + + +A@A` + + + + +@`AA0@ +A + + + + + + I @xp + + +CL + + + + + +Š +x +I +I + b + +H +H +` +x +ȃ +` +Lx@cx + + + + + + + +` + + +H@ + + + + + + + + +@ + I +B + I + + +IJ + IHrR + +FLAKȂ + + +N + +IJ + IH@ + +Q +HB +I + +H +I a + + + + +b +H@ n +b +HrR b + H2 + +a + + +IJ2 + + +H2 + + @a + H2 + + +@7 + + +H +H + x + +H) +B +@ + +Ϡ +Aaa + +M@ +2 + + + +Ϡ +Aaa + +M@ + +
+@; +
+ +M@a +x + @ +Ȁ Ȁ ) + + +@ + + + + + +` + +ɂP + +J + + + + +@ + +@ +` +@ + +@ +` + + +8 +J + + +8 +R +IJ +QP PCII + + +X +L^ J@ +I +K@ ɂp ɂ` +Aɂ@ a + +IK! A +Hb + + K + +@ + + +H + xI + + + + + +"@ +1~ H +@x +t +!@ + +Hr + H@I + + + B #EBH +@IH @ + + + + #E"@ ! + +!@ + +Hr + H@I + +H +!@ + +HHr + H@I +V + +S + + B #EBH +@ +I@I` +H@
I + )I! + + + +H@H`
+# @x + + +N +C @ + + + + +I + + +@P + + +I @n + +QRa +II@ + +b +O + +x +xd + + + + +K + + +O +@l + + @L" Il + + + +@`J +2 +@M + +@`J + + + +8 + @b + + +
@
`@ + +# +K# + @ `H +II@ + +E + + +@`2@ʢ +R +I + +@ + +@`V#@ +E + + + + + + +2@ʤ +5 + + + +!Hr@ + +@ + + +T +$xR +x P + + + + I +@`J + +S +@I +@`3@ + @@ + +@ + +@`J + +L! + + + +L + +@`3@ +
Ѐ @Q + +@ + +L +L +I@&l + +@ + D @ + + + +CL + + + +@ +@NH2 + +$ + + + + +@Hx +1xox + + +I + + +Jʀ +@I Ȁ QIȃ Q ' +"D d + +B@ H + +"x Q IH I +䉁dKH
+ + +ȃ @ +I& +L" o +[) + + + +) +) + + +) +) + + + +) +) + + +) +) +)) +(@o + + +'L! !"#)Ja ' +'L! )Ja ' +CJ+@x%LLLL'L! !c +I) + +o +@ +*Q !ʂp L+H'*#LHB0 o +H + B R +H'@x, +e +l@b + + LB ¤"@BY +x + +L2 K +" +x + +%B +H +I + +` +x + +((o + +(&&& ( + +@%x +(% +U@( +N B +@ +N @o +M@N @n +(m + +( +)) + +'L! !)Ja ' +'L! )Ja '+%(o + +*Q !ʂp @xLL'L! !a +I) + +(@o + +*Q !ʂp H'*#LHB0 o +B H' +%&&& !$@ + H + +CJ +f +U@& +* +K( +( +@o +(@ +o + +"P +!& + + + +I +Iy +!% + +%G +"K +Fa + +d +#K +I +! +H +H +"J@ J +JI %b +@ +x + +x@ +J#"H' + +" +"K+ + +x + +` + +" +" +J# + + +a I + + +=LȀ +@a + +x + c@c +$"L3@ + + +IA IσA I A
3@e + + +AJA
+ +%e +]a a ' +O +O +?H# +H#?aǠAH#PCH + + + +I +# +I + +@ + H + +"P +!&J +@,x' + +@x& +i*% +&#i@("(h +(\(!xY@("h +'¢h +'') +'@iP | '& +'zB'zB'@i9@(I'z'h$(@ +'Bh +'@@x'Ad +((N( + &J +'iP | ' +@''&@x&'%%f%% a '@i()'z'% ! g%9@(I'z'h$(@ +'bh +'`'a*( +((N( + &j%(%%h% +( +' +&' +@g"&&( + + + + y h +&z&J + &b + + a +&B' +&@&A&
%Ǡ + +a & + +N J + + +JG + +I + ʠ + + ʠ + I +D + +E +2 + QIKbp I Q + IJ + +@I@ +I + ʠ + + ʠ + + + +@@ +ߏN LJ +J2x +@Ȇ + +0 +` + + + + +@z + + +@s@ +JAHR0 AH! IAH! AHB8 AH0 @ + +A@ +J> +AAAAJ< +A"@@@AH> ` + + + + + +AH> +I +H +s@@y +x + +H22 IH0 H + +@ys@ +J +s@AHR0 AH! AH! IAAH0 K@ + +A@ +J> +AAAAJ< +A"@@@ + +p s@ + FI> o + + @I@ @ @I AH> K ` + + + + +IIIb IIbI@ +IIIR IIRI +II +I + +IAH> + + + + + +@I +AH0 H. @AH> H< H: +M +.s@ + + + +x
GK` + + + +@A +C +C @ +C O +C C + +` + + +J +@ +ʀ + + +xNCh @xNC@ xNCH @xNCP +K@ + x
@ x
x
+ +K@ + +` +JB@Jb@H + +@xNb@ +N@@I + + +\ + +A> + +@b + + +xM@Q +S$S$AL +S + +@@AH> + + + + + + +@ P + +AH! AH! AH> +H< AH> @ +H$ H IAH6 @ + +J +A ` + +A ` +xAH! AH! AH! IAH! AH> +@=I +J +` +x +A> @ +AH0 AHR6 H4 HR* AHB8 HB0 +AHB8 AHR6 HR, HR" + + + + + +@ +s@ +HB +J +s@AH> H< H26 H4 IH2 % + + + +x@H@ HHAH> @ +{ + + + + + !I!!@x!AH> !H< a +I!@! +!AH> b +H +H + + +x + + + + + + + + + +` + +xI +Ib + + + + +H +H + +@Jo + + + +x + + +x +@x +@|x + +I< + HR +g + +s@ +J@k +s@ + @j s@ +HB +Jb +s@# +#@x#@ + +J@ɑ ## + +ȁ +I + a + +J@ɑ $AH> @a +
@ +xJ +@ + +x +HH +J@ɑ AH> AH> +@ + +H% + +AAH> b +bT %` + + + + -I + +Al +I& @ + +@a +I + + &%I + +I { + + +( + +I!(! )!xI)AH> a +( + +%@a +( +@x + ` +@xz +y + +@ AH +@" +@v + +F> o + + + + +H7# ` + $ + + + + + + +HH++ +HH+,x#Hb +' + + + ++@ + + + +` + +I I L F +ɗ ɗ H a * + a + , a @II@ a H +ER + + +E "` "d * + ` ~ + ,` +ȇ J,,` @ + ++J` a + ++B@ +I,*" +@x@H +@ + + +,H +@@@ + + @K +H01DH +HR n + +H,@a + + + + + +@ + + + +$ +@a + @ +&} +> ~ @M@
@LÀ +J +> ~ @M@
@LÀL + + +@ +@ + b +H + + + + +"Ab +!H! +@Y +_ + +x + + + +@ + +A + + + ++H2 + + +x +x+I ++@ +,+ +! + +! +J J +@xBX H` +xBP ` + +! +JH H@ +JH +JH JH +A1 + + +v} +xI + +@ + ` +x +xI + +@ + ` +x +@@ +@ +@@ +@"@I +@ +K + +K + + + + + +K + +3@H + + + + + +3@H + + + +I@J@ +B` H "| +I@J@ +Bh H %GI @ B D I
"H G "@ "H "P D @ B D "@ "D +HHH-HI@HHIHIHIIJHJHJJK HK!K!L)HLH)LLM#HMNHNHNN BH OXHHXXXYHYHOOHY + H +H I H + H +!ɐ H +H! H +(I H +( H +H%ɑ H +% H +)I H + +P. + + + + +2@@I + + + + +@ +I- + +I-I I- +J@ɑ A H@M +@ + + + +H,^a Ѐ +$@ +Y@hB&i + +%A#$AdIA +# +@#A + +$$ + + + + + + + + + +a @樀 +@ +@V + + + +$$f +$"x + +e;` + x%` + +d +% +@x +$$$$$ + + +$A$> $ +#Dc/ + +c +d@! + +Q f9@ +@xe<&a +d)e +xIa +de +@ x$@@$) a +c7 @c@ +c% +fH + + +#$ +d@ +# +f!x& +c +@ +e + + +%` + + +i2 $$$$$ +d + + +f!$e +$< +d&I@ +i2 +d + + +f $$< + + +hr +# + +C@ + +`b + +`b + + + + + + +) + +'@) g@a + + + + + + +z + + +s@ +JAHR0 AH! IAH! AHB8 AH0 @ + +A@ +J> +AAAAJ< +A"@@@AH> ` + + + + + +AH> + +H + +x +@y +H22 IH0 H + +}s@ +J +s@AHR0 AH! AH! IAAH0 K@ + +A@ +J> +AAAAJ< +A"@@@@I + + + FI> o + + I + + + + +IIIb IIbI@ +IIIR IIRI +II +I + +IAH> + + + + + +I +AH0 H. @AH> H< H: +M + + + + +x
GK` + + + +@A +C +C @ +C O +C C + +` + + +J +@ +ʀ + + +xNCh @xNC@ xNCH @xNCP +K@ + x
@ x
x
@I + +K@ + +` +JB@Jb@H + +@xNb@ +N@I + + +\ + +A> + +@b + + +xM@Q +S$S$AL +S + +@@AH> + + + + + + +@ P + +AH! AH! AH> +H< AH> @ +H$ H IAH6 @ + +N +A ` + +A ` +xAH! AH! AH! IAH! AH> +AI +J +` +x +A> @ +AH0 AHR6 H4 HR* AHB8 HB0 +AHB8 AHR6 HR, HR" + +@3y + +@ s@0Ib + + +s@ +HB +J +s@AH> H< H26 H4 IH2 + +@} + +xH HHAH> @ + + + + + + !I!!@x!AH> !H< a +I!! +H +H + +@s@@y +x + + + + + + + + + +` + +xI +Ib + + + + +H +H + +@Jo + + + +x + + +x +@x +@|x + +I< + HR +g + +s@ +Jo +s@ + n s@ +HB +J +s@# +#@x#@ + +J@ɑ ## + +ȁ @ +I + a + +J@ɑ $AH> @a +
@ +xJ +@ + +x +HH +J@ɑ AH> AH> + + +H% + +AAH> b +bT %` + +@ + + -I + +Al +I& @ + +@a +I + + &%I + +I { + +@ +( + +I!(! )!xI)AH> a +( + +%@a +( +@x + ` +@x + + +@ AH +@" +z + +F> o + + + + +H7# ` + $ + + + +Kx + + +HH++ +HH+,x#Hb +'#_x + + + +@ ++@ + + + +` + +I I L F +ɗ ɗ H a * + a + , a @II@ a H +ER + + +E "` "d * + ` ~ + ,` +ȇ J,,` @ + ++J` a + ++B@ +I,*" +@x@H +@ + + +,H +@@@ + + @K +H01DH +HR n + +H,@a + +@ + + + + + + + +$ +@a + @ +&} +> ~ @M@
@LÀ +J +> ~ @M@
@LÀL + + + + + b +H + + + + +"Ab +!H! +@Y +_ + +x + + + +@ + +A + + + ++H2 + + +x +x+I ++@ +,+ +! + +! +J J +@xBX H` +xBP ` + +! +JH H@ +JH +JH JH +A1 + + +r} +xI + +@ + ` +x +xI + +@ + ` +x +@@ +@ +@@ +@"@I +@ +K + +K + + + + + +K + +3@H + + + + + +3@H + + + +I@J@ +B` H "| +I@J@ +Bh H %GI @ B D I
"H G "@ "H "P D @ B D "@ "D +HHH-HI@HHIHIHIIJHJHJJK HK!K!L)HLH)LLM#HMNHNHNN BH OXHHXXXYHYHOOHY + H +H I H + H +!ɐ H +H! H +(I H +( H +H%ɑ H +% H +)I H + +P. + + + + +2@@I + + + + +@ +I- + +I-I I- +J@ɑ A H@M +@ + + + +H,^a Ѐ +$@ +Y@hB&i + +8 +bI@@ +%A#$AdIA +# +@#A + +$$ + + +@ + + + + + + +a @樀 +@ +@V + + + +$$f +$"x + +e;` + x%` + +d +% +@x +$$$$$ + + +$A$> $ +#Dc/ + +c +d@! + +Q f9@ +@xe<&a +d)e +xIa +de +@ x$@@$) a +c7 @c@ +c% +fH + + +#$ +d@ +# +f!x& +c +@ +e + + +%` + + +i2 $$$$$ +d + + +f!$e +$< +d&I@ +i2 +d + + +f $$< + + +hr +# + +C@ + +`b + +`b + + + + + + +) + +'@) g@a + + + +@x +@` +@x + + +` +@7x + + + + + + + + + + +EFQ@ + + + + + +1 1 +N +O +N! +N + + + + + + + +A@A` + + + +@`AA0@ + + +CL + + + + + +Š +x +I +I + b + +H +H +` + +ȃ +` +@Fx@^x + + + + + + + + + + H
@ + + + + + + + + +@ +
+B @ +
+ + +IJ + IHrR @ + +FLHAȂ + + +H + +IJ + IH@ z +Q + + +!` + + +H +H + +ɿ +H2@ + +b +H@ p +b +HrR @d + + +H + + +J2 + + +IH2 + +@e +H +@a +H2 + + +@7 +H` + + +H +H + x + +) +B +@ + +Ϡ +Aaa + +M@H +2 + + + +Ϡ +Aaa + +M@ +A +
+? +
+ +M@a +x + @ +Ȁ Ȁ ) + + +H@ + + + + + +J + + + + +@ + +@ +` +@ + +@ +` + + +8 + + +8 +R +IJ +QP PCII + + +X +I +K@ɂp +ɂ` + +IK! @H +b + +@X + + +@ + + +H +@? + x I + + + + + +"@ #@ +1~ H + + + @ + +HHr +IH H@I +@ + +J + B #EBH + + + + #E"@ !@ + + @ + +HHr +IH H@I +@ + + + @ + +Hr +H H@I +HѕH@H +HѕH@H +J + + B #EBH #EBP B @ !@ +I@I` +H@
I +H + +ʀ +ˀ +K + + +K + +H@H`
+ & + +
@xK@@ +N +C @ + + +H@H`
+@x +N +C @ + + + + + + +I + +I> + +@P + + +@b +I m + +QRa +II@ + +Ob + + +x +xd + + +P + +P + + +K + + +O +@l + + @L" Il + + + +@`J +2 +@M +@`J + + + +8 + @b + b@ + + + + + +@@ +
@
` +@ +# +K# + + @ `H +II@ + + +E + + +@`J +2@ʢ +I + +@ + +U +Q +@ +@`#@ +@ +E + + + + +URAU@2@AP + + + +2@ʤ +5 + + 2@ʤ +5 + +URAU@PPA# + + + + +` +P +O +$xR +x P + +$` + + + + +@`J + + + + +@`3@ + @@ + +@ + +@`J + + + + + + +@`3@ +
Ѐ @Q + +@ + +L +L + +@ D @ * + + +CL + + + + +@NH2 + +$ + + +H` +x + + + +H +@Hx +1xix + +I + +I +ʀ +@I Ȁ QIȃ Q & +"D c + +B@ + +y Q IH H +dL + + +ȃ + % +L" o +[( + + + +( +( + + +( +( + + + +( +( + + +( +( +(( +'@o + + +'L! !"#(Ja ' +'L! (Ja ' +CJ*@x%LLLL'L! H!c +I( + +o +@ +)Q J!ʂp L*&)#LHB0 o +H + B R +&@xHB +%B +H +I + +` +x + +''o + +'&&& ' + +@%x +'% +U@' +N B +@ +N @o +M@N @n +'m + +' +(( + +'L! !(Ja ' +'L! (Ja '*%'o + +)Q J!ʂp @xLL'L! H!a +I( + +'@o + +)Q J!ʂp &)#LHB0 o +B & +%&&& !#@ + H + +CJ f +U@& +) +K' +' +@o +'@@m +"P +!& + + + +I +K!% + +%G +!K +Fa + +d +#K + + +H! +JI %b +@ +x + +x@ +"!H' + +" +!K+ + +x + +` + +" +" +" + + +a I + + +=LȀ +%e +]a a ' +O +?" +"?aǠA"PCH + + +I +@K# + +I + +@ + H + +"P +!&J +' +*x' +V +@x% +i)%G +"i@!(' +(\(!xY@!' +'¢h +'') +'@iP | '& +'zB'zBf&& a @i9@'#(@ +'Bh +'@@x'Ad +'(N( + &j +'iP | ' +@%%@
x%g$$f&& a @i(('zg$9@'#(@ +'bh +'`'a)( +'(N( + &J$ +' +' +%' +@g"%&' + +V + + y (&j&: + +V +'$@ +&B' +&@&A&
%Ǡ + +a & + +N J + + +JG + +I + ʠ + + ʠ + + +D + +E + IJ + +@I@ +I + ʠ + + ʠ + + + +@@ +ߏN LJ +J2x +@Ȇ + +0 +` + + y +G@, +% +I` + + +@x +@:La + + + + +@-y +o +c +HCc +I +x +*L6Lǁ Q ;LH2 +H +L ` + +@@I + +x + + +@x + +2 + +(GGCG + +<L + + +B + +@x +BD HAd + +`H + + +` + +X ȃ +, + +q@BD HH@d + +x + + +@ +HA + + + +ȃ X + +x!GG +G$Lx0!$L@$N +@ +" +N"HD 2P @x!xA! +@< +`@ + + +` + + +Hzo + + + +@x + + +GI +O +I@ +I + +o +I@ +I + Rp + + + I`O + + + + + +LJ + + +` +1xG` +G +G + + + +] +G IGa @ +a +G + + + +` +x + +@ + +P
+ + + IG@ +@ + +P
+ + + +GG +b + + + +H` +` + + + +*L6L @˂@ +@` +I +@b$ r +@I2@ +C +Br@@xr@ + + Gr +Gr + + + + + +`` + + +G +b + + +o + +H + + + + + +G +` +x@ +I +x +Br +@ +G +I@ + + +x + + +HIa $ +HJIIa " +IJa ! + G + +A Q@ +G + ` +@x I IB@HB@ + + + + + + + + + + +J + +xGGP !@$N +@ +" +N"HD 2P @x!xA! +GG +G +G` + +@ + +@ +H!" + + +I +xF!x +@ + +N + r + + ! RGIba IGa +a + + + @@ + + + +"B @x@@ o + +@ +T~ + +( +! (@( hQ +( +* +kè +냨 ++B + +* +* + + +! ( 銂( ( +@$("Lh +! ( 銂( hЈ0D x( D *k +Q +* +* +k +p +* + +! h (( m-^ ( ( +@$("L( ( +! h (( -^ ( (X0D ( (0D ( ( +$/"L/ / +! h (( -^ ( ( +P$("L( ( +! h (( --^ ( h؎0D ( $$("L./( + + + +! ( 銂( @$("Lh +! ( 銂( hи0D @x( * +Q x+kA +k +p +0D ( ) ( +! h (( -^ ( @$("L( ( +! h (( ( (X0D ( (0D ( ( +! h (( m-^ ( P$("L( ( +! h (( 胂 ( hؾ0D ( $$("L./(( + + +@ x +@@x U@U5 +`a@ + +U@ + +@V + + +E NNN$"N + +xAd +V U@U5 + +E + +G +G +c + +NNN + + +@ +! +G + +ߏN Lȃ JB + +` + +, + *C(I( +,J@($,*F ( ) ("L(h! ( 튂( ( +,J@ ($("Lh! ( 튂( , +("Lh! ( 튂( + +G+ +% + @ +I` +@y` +@y$ +x +@:La + + + + +*y + + + + + +, + + +B I + + + + +@ + +o +c +HCc +I +x +*L6Lǁ Q ;LH2 +H +L ` + +@@I + +xyo + + +@x + +2 + +(GG$$"$" + +<L + + +@B + +@x +BD HAd + +`H + + +` + +X ȃ +, + +q@BD HH@d + +x + + +@ +HA + + + +ȃ X + +x!GG +G$Lx0!$L@$N +@ +" +N"HD 2P @x!xA! + +`@ + + +` + + +HRo + + + +@x + + +GI +O +I@ +I + +o +I@ +I + Rp + + + I`O + + + + + +LJ + + +1xG` +G +G + + + + +G IGa @ +a +G + + + +` +x + +@ + +P
+ + + IG@ +@ + +P
+ + + +GG +B + + + +H` +% + + + + +G +` +x@ +x +Br +@ + + + +x + + +HIa $ +HJIIa " +IJa ! + G +A Q@ +G + ` +I IB@HB@ + + + + + + + + + + +J + +@xGGP !@$N +@ +" +N"HD 2P @x!xA! +GG +G +G` + +@ + +@ +H!" + + +I +xF!x +@ + +N + r + + ! RGIba IGa +a + + + @@ + + + +"B @x@@ o + +@ + + +( +! (@( hQ2x( (o +( +* +kè +냨 ++B + +* +* + + +! ( 銂( ( +@$("Lh +! ( 銂( hЈ0D +,J@($,*F ( ) ("L(h! ( 튂( ( +,J@ ($("Lh! ( 튂( , +("Lh! ( 튂( +* +* +k +p +* + +! h (( m-^ ( ( +@$("L( ( +! h (( -^ ( (X0D ( (0D ( ( +$/"L/ / +! h (( -^ ( ( +P$("L( ( +! h (( --^ ( h؎0D ( $$("L./( + + + +! ( 銂( @$("Lh +! ( 銂( hи0D x( * +k +p +0D ( ) ( +! h (( -^ ( @$("L( ( +! h (( ( (X0D ( (0D ( ( +! h (( m-^ ( P$("L( ( +! h (( 胂 ( hؾ0D ( $$("L./(( +"xIU + +@ x +@@x U@U5 +`a@ + +U@ + +@V + + +E NNN$"N + xAd +V U@U5 +x +E + +G +G +c + +NNN + + +@ +! +G + + y +G@, +% +I` + + +@x +@:La + + + + +@-y +o +c +HCc +I +x +*L6Lǁ Q ;LH2 +H +L ` + +@@I + +x + + +@x + +2 + +(GGCG + +<L + + +B + +@x +BD HAd + +`H + + +` + +X ȃ +, + +q@BD HH@d + +x + + +@ +HA + + + +ȃ X + +x!GG +G$Lx0!$L@$N +@ +" +N"HD 2P @x!xA! +@< +`@ + + +` + + +Hzo + + + +@x + + +GI +O +I@ +I + +o +I@ +I + Rp + + + I`O + + + + + +LJ + + +` +1xG` +G +G + + + +] +G IGa @ +a +G + + + +` +x + +@ + +P
+ + + IG@ +@ + +P
+ + + +GG +b + + + +H` +` + + + +*L6L @˂@ +@` +I +@b$ r +@I2@ +C +Br@@xr@ + + Gr +Gr + + + + + +`` + + +G +b + + +o + +H + + + + + +G +` +x@ +I +x +Br +@ +G +I@ + + +x + + +HIa $ +HJIIa " +IJa ! + G + +A Q@ +G + ` +@x I IB@HB@ + + + + + + + + + + +J + +xGGP !@$N +@ +" +N"HD 2P @x!xA! +GG +G +G` + +@ + +@ +H!" + + +I +xF!x +@ + +N + r + + ! RGIba +a + + + @@ + + + +"B @x@@ o + +@ +T~ + +( +! (@( hQ +( +* +kè +냨 ++B + +* +* + + +! ( 銂( ( +@$("Lh +! ( 銂( hЈ0D x( D *k +Q +* +* +k +p +* + +! h (( m-^ ( ( +@$("L( ( +! h (( -^ ( (X0D ( (0D ( ( +$/"L/ / +! h (( -^ ( ( +P$("L( ( +! h (( --^ ( h؎0D ( $$("L./( + + + +! ( 銂( @$("Lh +! ( 銂( hи0D @x( * +Q x+kA +k +p +0D ( ) ( +! h (( -^ ( @$("L( ( +! h (( ( (X0D ( (0D ( ( +! h (( m-^ ( P$("L( ( +! h (( 胂 ( hؾ0D ( $$("L./(( + + +@ x +@@x U@U5 +`a@ + +U@ + +@V + + +E NNN$"N + +xAd +V U@U5 + +E + +G +G +c + +NNN + + +@ +! +G + +ߏN Lȃ JB + +` + +, + *C(I( +,J@($,*F ( ) ("L(h! ( 튂( ( +,J@ ($("Lh! ( 튂( , +("Lh! ( 튂( +I` +x` +x x + + + + + + +G + + +@ +(G% + +GG +x((LG/ +I +@ + + +a + + + +H@ + +x + % +(((LG/ + Gb +Gb I +Gb + +aB + +IB HJ , +` + + +`x +H +GH + xȑ@@ ëȁ +Q +Ffe@@ ȉ +Bc + + + +NR` Rj +@ +I I $$2@HbF HT *G@d +ߏNȁG + + + +G@ + +à +_ + + 9I9LJ!!l +` + +a +@a +& + + + + +G + + +o + + + % +H + + + +x@ +IA + + +x + + +./a # +HJ//a " +/0a `! +G + + A Q@ +G + +@x I IB@HB@ + + + +IAII + +IAI + +A + +N@@@ N + + +Α@ +Q@ + + +A@ + + +
+GG +@x + +O +O +O@x + + +O +G +S@ + +MSI@ +G + +xOÀ
+6HC
+ +H +Bx
` + + x6HC
a + + +H! +BxDBx
b +H! +@x + +
b +H! +DxBBx
Db +` +B +6HC
@ +
b +H! +DxB@x
a +Bx
` + +6HC
+
` +
a +" + + +x6HC
` + +@
b +H! + + +x6HC
a +H% + +H! +BBxD +
` + +6HC
+ + +
` + + + +I +x! x +IB +IW¦ +IC +IՌGå +SC +Ӌä +IRD +IҊGģ + QD + щĢ + E + PGš + ψE + Š +INI@x +Ƣ +F +Ĭǡ +ɋGG +IǠ +ʅI@Aa@ǁ@@xȁ@ G +A +\ +Y + + +~ + ++ +*` +* +kC + ++£ + +* +* + +( @ +节( ( + +( +* +k ++¢ + +* +* +@ +k +h +(@( x +(@( (( (@(( m-^ ( ( +k + +(@( x((@( (( (@(( -^ ( (X( (( ( +k +(@( x((@( h( (@(( -^ ( ( +k +h(@( x(@( h( (@(( --^ ( h؎ + + +"xU + x +@@x U@U5 +`a + + + +@xV U@U5 + +x + +E NNN
V
N:@x: +@xd +V UU@U5 +x +E +` +@x + +GG +G +I& + + I ! + `iI@ C + @ +I +.x +(x +@x + + +@` + +` + +Go + +~x + + +6L k + +K +ˠ +@yxTi +Q"N + + + + + +rxa +@ +@ @qx + +@@ II24 A" + + Iɐ ɐ @ I SIH@! + +@I@ + +x@ +Iɀ +ɡ +IG# + +I +H +IA" + +I! + + +G
A Q@ +G + + + G + + Ĩ +H(H(BL Q IGv v P +GHLȁP GL G A +q@ + q@I +
Go + + +QI Q +@I + +H +H + +G@xGMG +)Lx +q@ + + + +G@ + + +d + +H + + +Gȁ ` + + + +,((LG/ + + +o + +GG + +y + +"& + + + +.@n +I + +@ #`d` + +T + + + + +H &(G` +GMG(LG@. +CG((LG/ +G +@ + +@Aa +x + + +B + +G + +Ba +G +? + +"& +G +G +` +@x; +@x, + +x + + +G + + +ǀ +2@ +EE@ +G +ǀ` +
x + +@ + + + + +GG + +@ +@ +BX J +BP +BH J + +(`ih@(C( +(( +HH +G@ +R + +x +@@ +@ +O + +MNC@ +@ xLM +O + + +@@
M@ +xMMM@
NNNNNNNQUNY +
+M@ +B +͂ +@Ixx
+ +
O +
+
O +
M@
+OO +x +MC@
+
` +@ +x
P3@@$ +x +́ +
+@x + @
NMӀ
M3@M@
X
IM
tr M# + + +G@b + +IGB + + + + +@ + +!L@ +"L @ + +£ +J +@ +A + +@xo +r +@xJ JM` + + + + +@" + + +@ + +@ +@ +xV JM` + + +@ + + + + + + + +J JY" !L MJ" !\ @ + + +J + +c +@xK` +JAb +x +ˁ +` + + +J + + + + +pIrIB + + +I @x + ,IB +ɱ@ + + @ +II @ + +x ,IB +ɱ@ + + @ +II @ +,C + +C +A + +2@ + +@} + +@ + @ x +x hi! V hi! T +U@e)hiqiU +TqiU5@ + +o)Q `) + +e)hi% V + +ր` +xhi%@ + + U + +@!~ + +H +(G@ +, +<@ +P +XGA + + + + G@ + ! + $@ + < + HGA + x@@Gr1 + -G@ + Z + + +Gb@ +GB@ + +@x# +xI +x +$ +$Y@@ +$ +$)@S +d + +H +@ +p r +r + ÿ + + + +H + + + s +q u ILI +@I@HP U SI +I UIǁ O + +K<K4T + + + +!`ia@!C! + + + + + + I +A + + + ¥ +I + +A + +Di + + + + +G` + +x B I D` + +EG@ +ŀ +EŁx + +EG +ł +EŃ " K` +@" ` + +G` + + +EG +ń +EŅ " +EG +ņ +EŇ " + I ! + `iI@ C + @ +I + + +@x + + +@@` +RxL +` + +Go + + + +6L Kl + +K +ˠ + +QBN + + + + + +@xa +@ +@ + +@@ II24 A" + + Iɐ ɐ @ I SIH@! + +@I@ + +x@ +Iɀ +ɡ +IG# + +I +H +IA" + +I! + + +G
A Q@ +G + + + G + + +GHLȁP GL G A +q@ + +
Go + + +@I + + +H + +GxGMG +)L +q@ + + + +G@ + + +@F + + @a + +Gȁ ` + + + +B J@ +B +B B +K,@xBX I` +xBP ` + +,((LG/ + + +o + +GG + +d + +"& + + + +. +I + % +GMG +@ +G + + + +G@ + +@ +H + + +@; + + + + + +@Aa +x + + +B + +G + +Ba +G + + +"& +G +@x + +x + + +G + + +ǀ +2@ + +G +ǀ` +
x + +@ + + + + +GG + +@ +@ +BX J +BP +BH J + +(`ih@(C( +(( +x +@@ +@ +O + +MNC@ +@ xLM +O + + +@@
M@ +xMMM@
NNNNNNNQUNY +
+M@ +B +͂ +@Ixx
+ +
O +
+
O +
M@
+OO +x +MC@
+
` +@ +x
P3@@$ +x +́ +
+@x + @
NMӀ
M3@M@
X
IM
tr M# + + +G@b + +IGB + + + + +@ + +!L@ +"L @ + +£ +J +@ +A + +@xo +r +@xJ JM` + + + + +@" + + +@ + +@ +@ +xV JM` + + +@ + + + + + + + +J JY" !L MJ" !\ @ + + +J + +c +@xK` +JAb +x +ˁ +` + + +J + + + + +pIrIB + + +I @x + +@} + +@ + @ x +x hi! V hi! T +U@e)hiqiU +TqiU5@ + +o)Q `) + +e)hi% V + +ր` +xhi%@ + + U + + + @ +@x# +xI +x +$ +$Y@@ +$ +$)@S +d +
" !@ " !D MH + +H + +p r +r ]B + + ÿ + + + +H +@ + + s +q L + u HRJ RT v I@ +@I@ +@HP U SI +I UIǁ O + +@ +HP SI + + + +!`ia@!C! + + + + + + I +A + + + ¥ +I + +A + +Di + + + + +G` + +x B I D` + +EG@ +ŀ +EŁx + +EG +ł +EŃ " @" ` + +G` + + + +EG +ń@ +EŅ@x +EG +ŀ +EŁ + +EG +ņ@ +EŇ@x +EG +ł +EŃ@ +` + ++@+@ + + + +y + +I + +@ + + +H +AH2: @ +h OI +@x + +H + II + IAH2: ` + + +AH0 AH! I + +J +AH> ` +xAH> +0 + +Hb2 + +0 + +Hb2 + +0 + +Hb2 + +0 + +Hb2 + +0 + +Hb2 + +0 + +Hb2 + +4 + +HB6 + +4 + +HB6 + +4 + +HB6 + + + a + @a + +` + +I + +OAI + n + p + r t O + + +@K2 + + +@ +AH> J +H +` +` + + +A + +ʣ @x + MA + + + +H0 + +I + + + +H0 + + +H + +a + @a +H ` + + +" +AAAH o +H J +2X O +b +HAAAH o + + +H @ +Hb +"| AAAH @m + +B@ + + +xHAAAH o + +HAAAH o + + +OAHH +O JB@ + + + +H + + +H +J +OA +"T O +HAAAH o + +H"HAAAH o + + +HAAAH o + + + +AAAH o + + + +
@ +
+ +9A + + +9J2@ɗ :H + + + + +H2@ 2 + +J` JJ` +I +I + +OAH + +O + +H @ + ȇ $H
+
` + + + + + +@ +h O@a + + AH0 AH( I b + +
OAJb@ O +# + + + +H": ` +2` O + +*CAf +#G + + +I +
@| +@ + + KKa
AK@/ + +IH2 + +xJ
" +$ + + +A1 +@~ +((+@Q R PPCH + + +2@@I +@ F> +o + + +xJ +- + +?AH0 ˂@ɂ@ ʀ +` + +-AH> + + + +?AH0 ˂@ɂ@ +
x + + +V AHb4 b@ bH H2 P T B + +@AH> +F AH"< "B AH> @ 2l ˷ aF + + + + + +? + +? + + +? + +? +@ + +` + + + + + +H @H + I +@xI + +@a + I +b +IB +@ + +` + + + + + +H2@"@H +` + +P/ +H@ + +H0 + + +H0 + +H? "P +J +bI@@ +@bA˷ aF + +b ++@o + +a .
AB+@+ + + +!+A/@ + + + +,@l@! + +o + + + + + + +1 +/@+ o@a + + +J` JJ` + ++@+@ + + + +y + +I + +@ + + +H +AH2: +h OI +x + +H + II + IAH2: ` + + +AH0 AH! I + +J + +AH> ` +xAH> +0 + +Hb2 + +0 + +Hb2 + +0 + +Hb2 + +0 + +Hb2 + +0 + +Hb2 + +0 + +Hb2 + +4 + +HB6 + +4 + +HB6 + +4 + +HB6 + + + a + @a + +` + +I + +OAI + n + p + r t O@ + + +@K2 + + +@ +AH> J +H +` +` + + +A + +ʣ @x + MA@ + + + +H0 + +I + + + +H0 + + +H + +a + @a +H ` + + +" +AAAH o +H J +2X O +b +HAAAH o + + +H @ +Hb +"| AAAH @m + +B@ + + +xHAAAH o + +HAAAH o + + +OAHH +O JB@ + + + +H + + +H +J +OA +"T O +HAAAH o + +H"HAAAH o + + +HAAAH o + + + +AAAH o + + + +
+
+ +9A + + +9J2@ɗ :H + + + + +H2@ 2 + +J` JJ` +I +I + +OAH + +O + +H @ + ȇ $H
@d +
c + + + + + + +h O@a + +AH0 AH( I b + +
OAJb@ O +# + + + +H": ` +2` O@ + +*CAf +#G + + +I +
@| +@ + + KKa
AK@/ + +IH2 + +xJ
" +$ + + +A1 +~ +((+@Q R PPCH + + +2@@I +@ F> +o + + +xJ +- + +?AH0 ˂@ɂ@ ʀ +` + +-AH> + + + +?AH0 ˂@ɂ@ +
x + + +V AHb4 b@ bH H2 P T B + +@AH> +F AH"< "B AH> @ 2l ˷ aF + + + + + +? + +? + + +? + +? +@ + +` + + + + + +H @H + I +@xI + +@a + I +b +IB +@ + +` + + + + + +H2@"@H +` + +P/ +H@ + +H0 + + +H0 + +H? "P +J +bI@@ +@bA˷ aF + +b ++@o + +a .
AB+@+ + + +!+A/@ + + + +,@l@! + +o + + + + + + +1 +/@+ o@a + + +J` JJ` + FL CL R@ +I` + +xH! + +G
@a + +@x + + + +, + + + +@ + +! + + +J + + +,! + +G$ +` + + + +@I + +GIǁ +I@ +o +I@Ip v Gv v Gw p v Gv v Gw + +kWRk@4P`gP"k@IPrk\P + + +r +H + +@I + ` + +G
` +x + G`HR@ +`"@ + + +b + +2x +B + + + + + + +ǁ +c + + + + + +J` + + + + @I
^ G \ G Z +I + + + +I +@ +QGbɑ@I2 +QHbIb P PCIIb + +QRIc P PCII?Hba t +x@ + ɀ + + +ɱ + + + +GIǁ a +O +I@I + + + +@Lr@ǁ +I + +@a + + +L +H2X + + + + Ɂ x + + + + Gx +H + + & + +a +% + + +$ +a# +HJ!" +! + G + +A Q@ +G + +@xGH ` +xGP ` +xGX ` +@xG@ H` +xGH ` + + + G +@x I IB@HB@ + + + + + + + + + +I +I + +AL"CLh @ + + +( +x(h +@ +i +(A +h +A + +߿ +( +( )CL): + + + + + + + +@xa + +I +A@_` + +A@ + + + + + + +s s Eg + +a + + +L@a + + + + +E@a + +L + +D + ! +<Ȁa +E<B + +B + + = +E=== E DL>L +b + +D@ @ + +xHR& b@I"@J@Hb + @ +I` +x` +x" +@}x + +)L G@c +G" + + +)` + +` +.y +&&& +H@o@ +@ + +@J +@ + + +@Aɑ +G +G +G + + + +!`%#LB0 +! I +&&Hb + + +# +H + + + +&&&& + +H +! +H + +! +&x +H + + + + + + +GI +O +I@ +I + +o +I@ +=Lʀ +I + Rp + + + P$ + + + + + +LJ + + + + ȁ +ȁ +ȁ +ȁ +ȁ +h +G@G@ M +b +G + + + +ì +Ja + + +b + + +x + + +x + + + + + + + +AG + +J@ + + + +@ + + +mx +QLFLAH +p @ȁ +CL@ +@L@HG +cx +@`x + +a +Ix@b + +Vx +@ +9x8 + +Ȉ +@+x + +x@ +I +xI@ +r + +H + + + +H + +G +G + +A Q@ +G +@H +ȁ@ +I@@x@4@ + + + I + +` + + + + + +G + + + ` + + +GGb +m6 +m\ +-mNLǁ@4 + G` + +
x" + +G. + x + r@" + +@ +%G + + +x@H + + + + +x +$` +~ +* Ij@ +ꠂ )(` +*A꧂ * *I* * * * j +) +(5) + + + + +Fx + a + +J* J* h + +* )J + ) + +
` +:x + +G +@x +x +! 1 +" +!
1 +! 1 +! 1 +@ +@x*H*& + +x +@ +Q +G + +@ +Q +Q +Q +G +$G@ +L +L + + +G@ +G +H + +\ PPCG + +J@" +` + + +K! @ +` + + +K! @ + +@x +` + + +I +@ + + + + +( ( +G +xU +@x +@@#L + +W +@@!NU + + +@V +@c +c + +Gx + + +2@ +b +I Gp G` NG +G +x 2@ I2@ @a + + +xI Gp G` K"\ GX NI + + +I + +@ +x +x + @ + +x + + +K + + +x + + B@r +%"!1@F! + +H + +
x + +x" +H" +# +" +G + +Ga + + + +" + + +G@c +G + +`@ +@x +, +1 c +A +H + ` + + + +@x) + + +H + +- + +@ +&H&H + +GG@c + +, + +@b + +G +` +Rx) + + +H + + + + + +H +GG + + + +e + + +H + + + + + + +H + + + + + + + + +ax +a*aG*G@ +` +x +@x@x + +x!hJ + + + + + + + +@b + + +H + + +H + + + + + + + + +G$ + + +G +Ap &AX H'AP ! + + +, + +,I + +H$a + + + +` + + + +` + + + + +` + + +` + + +I + + + + +H + +H +G + +(#@ + +ǂ + +H&ȁ +Gt Gt +(I( *SI + + + + + + ++ @x, + +` + +HH@f +@ + + +G@ +HPx +HH + +H%H +HPG +HH +HP +@ + +HC@
r +HCH
C +HCP
HCX
"@Ir +C +C 3 +C C @ Ir +C +C 3 +C C B LCH + @ +I` +@*y` +y# +@~x + + +" + +Cc +)L + + +cy + +GGG>%&&&C +De +&Lo + + + + +G + + + +@o +@? + + + + +/G +J +Fa + + +; + + +Gd +J + + +Q +? +RP + +C. + + +? NI HB +@GA +GRA@HH +Da +C + + + + + +o +G#LB0 o + + A R LI + +%£ +H +c +C + +G +@c +&Lo + + +G + + +&Lo + + +Z` + + + +@ + +x +h + + + + +LJ +@j +D` +` + +I + + +L + + + +I +, + ++ +x +@ + + +r@b +D +C +@~ +q + + + +G + +A +@#G +\ + + + +H + + + + + +r@b +D +C +@e@a +q!G` +xZ +@x + + +G + + +A + +\ + + + + + +H + + + +@GG + +@ + +@@x + +1@ @H + +1@ @H + +1 +GA` +@x +G + + + + x +I@D#D + Hx !#(HH5a + @x +7#@8 +I@ + + +QLFLAIH +p "@ȁ +CL@ +@L@ G +H +a @r + + + G + + + +ID ` + + +@x@ +I@ + + + +@ + +@a +G +H + + +@ +J +@ʢ + +A Q@ +G + Z` +@ +@ +I@@x @@ + + + +@x + +aH + +@ + +mg + + +b +D +C + +J + +@J + b + + +GB + + +@x +G + +@ +C` +@x +s s + +L! o + +H + + + + + + + +x +xDL + + + +P +!x + + +/ǣ +Pd@QQ +¢R +@ +BR +@@xAc +N +Q@Q@ +bR +`a +N D +@ +Q@Q& + + +x/Ǡ + + t 4T + + + +B +@A
/Ǡ +P@PP4@a + + + +I| + I +"p "h + +bp + +@@ I ɐ ɐ +SI + +@I@ +I + +o +I@ +=Lʀ +I + bp + + + :Lb + LL8L + 8 +/$ +@ +!a "a # Λ +? +?aǠAPCG + + + +ȁ + + +Q +? +RP#LG +q@ + +. + +. + +. + + +gZ + +. + +Lx + ` + + + +x@@Hbd 0H@@HHbd 0r@G@r@@GR
Hbd 0Hbd 0 + +@@x + +@ + +@H@x +@H +@ +@` + +@ +@ +@` + +@ + +x +x
@
# + + + + +I + +@@ +L@ + +I@I@ +L@ +I + @x +Z + +Xш +PH Ȉ +X +H + +H + + + + + + +I +v +G +2x +Ha + +@2xU@ +IL + + + +x + +@
xZ@ + +G2 + + +A + + +x + + + + +H +. +ta +- +_x + +G7 +mWBl + +HR + II + + + + +@Lr@ǁ + +@x +I + + + + @} + + + +x +H@ + +@ +H + +@} + +@_ + +
A +E +Q +, +D +FA + + +A *1@1 +$A +4X +Q +@ + +! +Q +
F +F@q@xF
F +F@FF @xF
F + +
J` +F" I +F" I +@xD!H EP +
H + +D +@@D$L +M3 +M
AH a t + + + + +2a +-Iȁ +@L3 + +3 + + + +ɑ zI ++ @ +I` +zɀ` + +&xP +Ex +Ex + + + + + +) + +J@ +
J +.8 + +GI , +O +I@ +I + +o +I@ +=Lʀ +I + bp + + + + +Q RPPCL + +@J +A +A +& +% +( +\ +!p! + +J + + + + +LJ +@ +I +P! +Ga + +
x + H G +G@ +` +@x
ׁ` +x
+xMuG` +@x + QL +FLHAȂ + t +x +G + + +q@ + + + + + +B + +<B/L' @ 0@ + + +G + +GI +@LBLAQ +` + + +$ + + + +@x$@ +W(Ǥ +*e@-UU +\!@xU@-U +¢V + +@TeP W| NB +EE@ +BV +@@xAՀe +&N U +TeP W| NB +U@%$U@x$&) +bV +`aV0 +&N U'$ + + +$` + + + + + u V,E +@ + + +B +@A
Ǡ + +A + + +@@x +NNN@ + + +@@ II24 A" + + Iɐ ɐ @ I + +@I@ +I + +K +K +@ +@ +G + +<G@S; +< +` +xJ + +xIǀ! +< +J + +xJ +xJ +G2 +H" + + +2 +xJ +3@ +G4 + + +~ $ +H +! + +N,x + + +sx +@ + +S + x.I,J) +` +,@ +I +,@Ȅ@ -,
- +@ǠA+,- + +G +;H -% +< +@ I + +@ H +H@a + +H +;I + + + + +GǑ@ʡ +;GR N;3 +xH;;G + +G @ + +GH;ȡ@ʡ + +N; + + +G @@ + +G + +G @ + +G @ +I; + + J +J + +J9 + + +J + + + +!sstG +@b + +GuuG + +@b + +!x2 + +< + +x@ +x " +J +@b + +. + + + +J + + + + + + + + + +@~; + + +~ + + +o + + + +H + $ɀ +@Ip p QIs Q #G)!D G)d + +Q;! + + + + +@~ + + + +G,",@a +QI + + +Q +? +R+yP + + + +N" $ +>?L ?LIB +A + + á +H! + + à +G +x + + + +~ + +~ +@ +A +bPL +xG + + +Gǡ@ʡ +|~x#+#L +G@ + + ++ +/R + + +G# +# +q@)#q s +s s +' +L! o +!*! +G) +))YxJx +))%xx +G) +))Qx +)) +G$) + + +$ +@o + + + +'x 'LI! &'()) a ' 'LI! H$ a ' CI#G D +#LGB0 +@ +@ +* + + + @o +I@ +(@(`} + A R K +(@xGB +%B +G +H + +` +x + + + +'xI* +S@& NI HB +@ +N @o +@N @n +$m + +G$) + +' +'x 'LI! &') a ' 'LI! H$ a '#G D %$ +o + +!L"L%'Le! ,a +* +$ʀ + + + +( +%&&&r !G%@ +G + +S& +O + +* + +$ @o +&I@ + + +, + +-xB + +G + +-J +Fa + + + ++ + + +g +J+J + + +Q +? +RP +@#\ a +L+L +@$ +, + + +I@ ` + ~I,Db +@x +-J + + +-G% +! + +-J+ + +x +! + + + @a + +x + b@ʢ c +$"L2@ + + + +CALc +AA K)` +! + + +@ +a $a ' + N, +?G)á +,@ǠA+ ++?aǠA+PCG + + + +@ +G + +ߏN LJ +J2x +@Ȇ + +0 +` + + + C!?C` + + 1 +@ +M*@I +G + *,@ +@@ +M*@I + *,@ +@ +M*@I + +ql +@0 +G + ,@ +@@0 + ,@ +@0 +qp + +qp +` + +qIq + + +J +O+ +a
` +z + +."@ +o@l + +J + +O+ + +a
` +z + +."@ +o@l + +G@@ + + + + + +H +Hs + + +@r + +@ + +@ + +x + +@ +x +@^~ +
+ + + + +r + +
I +>~ + + + +xH@B H@ +Q +Q + + + + +G@Q +@ + / +B + / + +@#
+;p ; + +GС@ʡ + +G@!x + + +G +!I@ +R B@h + ?@C +H:
B G) +@!D # +G +G +GB G +G +} +xQ + +;Gp ; + +GБ@ʡ + + ;R` G)"j ! (! )I + + +@ + +Ϡ +Aaa + +@G1 +N + +I + + +@ +G +Ϡ +Aaa +@ +@ + +I,J:9>>>G@ +@ +s@ +c@ +T@ + +G$@ +@&x + + + + + + + +G @c@J + I + +CJ +G:G@c +q@1@ɑ + I + + + + + +@x +@x@9 +xjst +r,s-nuua u +qs@! + + +@@ + +@@ + +} + + + a +I +0020ȁ + + + +zJa CG + + +H + +t@#sRP @#tR` G)"j ! + + + +B @#BH #BP B @ @s@ @ tx +s + +B #BH #BP B @ s@IGr @ G + H + + +a +G@GA +I +I + + @ +#"@ + I + +@*x +@ +O + +" +x$r$ +O +, +" + + + +G @@# + +G"@ #@ + +L + +A +pA + +B@ +p3@ + + + + +1@A @@N @@ + +@x + + + +
+ +P + + + + +x@ +C + + + + +@x + + + + + + + + + C!?C` + +q + + + +K +@x +PH NL +xqMЀ @LF +PJ N, +
+@xM@
Ҁ +
+@ +0q\ +J@ +@K<@Oq + +q +L@ +A@ +A@ +c@ +0Aa ,@ + +`! + + +A@ +0 + +Db +DD0J 0!0 + + + p T p0X +02p0 +K@ + +1L3 + + + + +0 +p +q + +q
@ + +@ + +@ + +pF<@Op +a@ + +Q + + 2@ + +A + + + + 2@ + +A + + + + 2@ + +A + + + 2@ + +A + + + +q +1@ +@@P1I + + 0P +0@ + + + + + +@%1 + @ + +@M +@"1 |