author | Nanxin Qin <nanxin.qin@amlogic.com> | 2017-01-08 13:04:11 (GMT) |
---|---|---|
committer | Zhi Zhou <zhi.zhou@amlogic.com> | 2017-02-10 07:15:14 (GMT) |
commit | 111584911714906d40678d158b9dc2947c59af1c (patch) | |
tree | 0795e40455aee9a9ff8841375becc4c742d859cc | |
parent | 1e7a5a7cc36c276eb90666765d760991ec8dc723 (diff) | |
download | media-111584911714906d40678d158b9dc2947c59af1c.zip media-111584911714906d40678d158b9dc2947c59af1c.tar.gz media-111584911714906d40678d158b9dc2947c59af1c.tar.bz2 |
adds drivers of the multimedia.
vh264 decoder checkout out pts out.
Change-Id: I86861da91beeff56c6e7c614799f0726e9e4a542
54 files changed, 27889 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a9a7f44 --- a/dev/null +++ b/.gitignore @@ -0,0 +1,101 @@ +# +# NOTE! Don't add files that are generated in specific +# subdirectories here. Add them in the ".gitignore" file +# in that subdirectory instead. +# +# NOTE! Please use 'git ls-files -i --exclude-standard' +# command after changing this file, to see if there are +# any tracked files which get ignored after the change. +# +# Normal rules +# +.* +*.o +*.o.* +*.a +*.s +*.ko +*.so +*.so.dbg +*.mod.c +*.i +*.lst +*.symtypes +*.order +modules.builtin +*.elf +*.bin +*.gz +*.bz2 +*.lzma +*.xz +*.lz4 +*.lzo +*.patch +*.gcno + +# +# Top-level generic files +# +/tags +/TAGS +/linux +/vmlinux +/vmlinuz +/System.map +/Module.markers +Module.symvers + +# +# Debian directory (make deb-pkg) +# +/debian/ + +# +# git files that we don't want to ignore even it they are dot-files +# +!.gitignore +!.mailmap + +# +# Generated include files +# +include/config +include/generated +arch/*/include/generated + +# stgit generated dirs +patches-* + +# quilt's files +patches +series + +# cscope files +cscope.* +ncscope.* + +# gnu global files +GPATH +GRTAGS +GSYMS +GTAGS + +*.orig +*~ +\#*# + +# +# Leavings from module signing +# +extra_certificates +signing_key.priv +signing_key.x509 +x509.genkey + +# Kconfig presets +all.config + +# customer folder +customer + diff --git a/Media.mk b/Media.mk new file mode 100644 index 0000000..048c31a --- a/dev/null +++ b/Media.mk @@ -0,0 +1,64 @@ + +ARCH ?= arm64 +TOOLS := aarch64-linux-gnu- +CONFIGS := CONFIG_AMLOGIC_MEDIA_VDEC_H264=m \ + CONFIG_AMLOGIC_MEDIA_VDEC_H265=m + +define copy-media-modules +$(foreach m, $(shell find $(strip $(1)) -name "*.ko"),\ + $(shell cp $(m) $(strip $(2)) -rfa)) +endef + +ifneq (,$(ANDROID_BUILD_TOP)) +KDIR := $(OUT)/obj/KERNEL_OBJ/ + +MEDIA_DRIVERS := $(ANDROID_BUILD_TOP)/hardware/amlogic/media/drivers +ifeq (,$(wildcard $(MEDIA_DRIVERS))) +$(error No find the dir of drivers.) +endif + +INCLUDE := $(MEDIA_DRIVERS)/include +ifeq (,$(wildcard $(INCLUDE))) +$(error No find the dir of include.) +endif + +define media-modules + $(MAKE) -C $(KDIR) M=$(MEDIA_DRIVERS) ARCH=$(ARCH) \ + CROSS_COMPILE=$(TOOLS) $(CONFIGS) \ + EXTRA_CFLAGS+=-I$(INCLUDE) +endef + +else +KDIR := $(PWD)/kernel +ifeq (,$(wildcard $(KDIR))) +$(error No find the dir of kernel.) +endif + +MEDIA_DRIVERS := $(PWD)/media/drivers +ifeq (,$(wildcard $(MEDIA_DRIVERS))) +$(error No find the dir of drivers.) +endif + +INCLUDE := $(MEDIA_DRIVERS)/include +ifeq (,$(wildcard $(INCLUDE))) +$(error No find the dir of include.) +endif + +MODS_OUT ?= $(MEDIA_DRIVERS)/../modules +TOOLS := /opt/gcc-linaro-5.3-2016.02-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu- + +modules: + $(MAKE) -C $(KDIR) M=$(MEDIA_DRIVERS) ARCH=$(ARCH) \ + CROSS_COMPILE=$(TOOLS) $(CONFIGS) \ + EXTRA_CFLAGS+=-I$(INCLUDE) + +copy-modules: + @echo "start copying media modules." + $(call copy-media-modules, $(MEDIA_DRIVERS), $(MODS_OUT)) + +all: modules copy-modules + +clean: + $(MAKE) -C $(KDIR) M=$(MEDIA_DRIVERS) ARCH=$(ARCH) clean + +endif diff --git a/drivers/Makefile b/drivers/Makefile new file mode 100644 index 0000000..5d0d782 --- a/dev/null +++ b/drivers/Makefile @@ -0,0 +1,3 @@ +obj-y += common/ +obj-y += frame_provider/ +obj-y += stream_input/ diff --git a/drivers/common/Makefile b/drivers/common/Makefile new file mode 100644 index 0000000..77ce080 --- a/dev/null +++ b/drivers/common/Makefile @@ -0,0 +1,2 @@ +obj-y += media_clock/ +obj-y += firmware/ diff --git a/drivers/common/chips/chips.c b/drivers/common/chips/chips.c new file mode 100644 index 0000000..f2e7fa6 --- a/dev/null +++ b/drivers/common/chips/chips.c @@ -0,0 +1,158 @@ +/* + * drivers/amlogic/media/common/arch/chips/chips.c + * + * Copyright (C) 2016 Amlogic, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * +*/ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/types.h> +#include <linux/fs.h> +#include <linux/init.h> +#include <linux/device.h> +#include <linux/vmalloc.h> +#include <linux/mm.h> + +#include <linux/amlogic/media/utils/vformat.h> +#include <linux/amlogic/media/old_cpu_version.h> +#include "../../stream_input/amports/amports_priv.h" +#include "../../frame_provider/decoder/utils/vdec.h" +#include "chips.h" +#include <linux/amlogic/media/utils/log.h> + +#define VIDEO_FIRMWARE_FATHER_NAME "video" + +/* +*#define MESON_CPU_MAJOR_ID_M6 0x16 +*#define MESON_CPU_MAJOR_ID_M6TV 0x17 +*#define MESON_CPU_MAJOR_ID_M6TVL 0x18 +*#define MESON_CPU_MAJOR_ID_M8 0x19 +*#define MESON_CPU_MAJOR_ID_MTVD 0x1A +*#define MESON_CPU_MAJOR_ID_M8B 0x1B +*#define MESON_CPU_MAJOR_ID_MG9TV 0x1C +*#define MESON_CPU_MAJOR_ID_M8M2 0x1D +*#define MESON_CPU_MAJOR_ID_GXBB 0x1F +*#define MESON_CPU_MAJOR_ID_GXTVBB 0x20 +*#define MESON_CPU_MAJOR_ID_GXL 0x21 +*#define MESON_CPU_MAJOR_ID_GXM 0x22 +*#define MESON_CPU_MAJOR_ID_TXL 0x23 +*/ +struct type_name { + + int type; + + const char *name; +}; +static const struct type_name cpu_type_name[] = { + {MESON_CPU_MAJOR_ID_M6, "m6"}, + {MESON_CPU_MAJOR_ID_M6TV, "m6tv"}, + {MESON_CPU_MAJOR_ID_M6TVL, "m6tvl"}, + {MESON_CPU_MAJOR_ID_M8, "m8"}, + {MESON_CPU_MAJOR_ID_MTVD, "mtvd"}, + {MESON_CPU_MAJOR_ID_M8B, "m8b"}, + {MESON_CPU_MAJOR_ID_MG9TV, "mg9tv"}, + {MESON_CPU_MAJOR_ID_M8M2, "m8"}, + {MESON_CPU_MAJOR_ID_GXBB, "gxbb"}, + {MESON_CPU_MAJOR_ID_GXTVBB, "gxtvbb"}, + {MESON_CPU_MAJOR_ID_GXL, "gxl"}, + {MESON_CPU_MAJOR_ID_GXM, "gxm"}, + {MESON_CPU_MAJOR_ID_TXL, "txl"}, + {0, NULL}, +}; + +static const char *get_type_name(const struct type_name *typename, int size, + int type) +{ + + const char *name = "unknown"; + + int i; + + for (i = 0; i < size; i++) { + + if (type == typename[i].type) + + name = typename[i].name; + + } + + return name; +} + +const char *get_cpu_type_name(void) +{ + + return get_type_name(cpu_type_name, + sizeof(cpu_type_name) / sizeof(struct type_name), + get_cpu_type()); +} +EXPORT_SYMBOL(get_cpu_type_name); + +/* +*enum vformat_e { +* VFORMAT_MPEG12 = 0, +* VFORMAT_MPEG4, +* VFORMAT_H264, +* VFORMAT_MJPEG, +* VFORMAT_REAL, +* VFORMAT_JPEG, +* VFORMAT_VC1, +* VFORMAT_AVS, +* VFORMAT_YUV, +* VFORMAT_H264MVC, +* VFORMAT_H264_4K2K, +* VFORMAT_HEVC, +* VFORMAT_H264_ENC, +* VFORMAT_JPEG_ENC, +* VFORMAT_VP9, +* VFORMAT_MAX +*}; +*/ +static const struct type_name vformat_type_name[] = { + {VFORMAT_MPEG12, "mpeg12"}, + {VFORMAT_MPEG4, "mpeg4"}, + {VFORMAT_H264, "h264"}, + {VFORMAT_MJPEG, "mjpeg"}, + {VFORMAT_REAL, "real"}, + {VFORMAT_JPEG, "jpeg"}, + {VFORMAT_VC1, "vc1"}, + {VFORMAT_AVS, "avs"}, + {VFORMAT_YUV, "yuv"}, + {VFORMAT_H264MVC, "h264mvc"}, + {VFORMAT_H264_4K2K, "h264_4k"}, + {VFORMAT_HEVC, "hevc"}, + {VFORMAT_H264_ENC, "h264_enc"}, + {VFORMAT_JPEG_ENC, "jpeg_enc"}, + {VFORMAT_VP9, "vp9"}, + {VFORMAT_YUV, "yuv"}, + {0, NULL}, +}; + +const char *get_video_format_name(enum vformat_e type) +{ + + return get_type_name(vformat_type_name, + sizeof(vformat_type_name) / sizeof(struct type_name), type); +} +EXPORT_SYMBOL(get_video_format_name); + +static struct chip_vdec_info_s current_chip_info; + +struct chip_vdec_info_s *get_current_vdec_chip(void) +{ + + return ¤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..d2957a8 --- a/dev/null +++ b/drivers/common/firmware/Makefile @@ -0,0 +1 @@ +obj-m += firmware.o diff --git a/drivers/common/firmware/firmware.c b/drivers/common/firmware/firmware.c new file mode 100644 index 0000000..fc31c22 --- a/dev/null +++ b/drivers/common/firmware/firmware.c @@ -0,0 +1,753 @@ +/* + * drivers/amlogic/media/common/firmware/firmware.c + * + * Copyright (C) 2016 Amlogic, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * +*/ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/types.h> +#include <linux/fs.h> +#include <linux/init.h> +#include <linux/device.h> +#include <linux/vmalloc.h> +#include <linux/mm.h> +#include <linux/vmalloc.h> +#include <linux/slab.h> + +#include <linux/amlogic/media/utils/vformat.h> +#include <linux/amlogic/media/old_cpu_version.h> +#include "../../stream_input/amports/amports_priv.h" +#include "../../frame_provider/decoder/utils/vdec.h" +#include "firmware.h" +#include "../chips/chips.h" +#include "linux/string.h" +#include <linux/amlogic/media/utils/log.h> +#include <linux/firmware.h> +#include <linux/amlogic/major.h> +#include <linux/cdev.h> +#include <linux/crc32.h> + +#define CLASS_NAME "firmware_codec" +#define DEV_NAME "firmware_vdec" +#define DIR "video" +#define FRIMWARE_SIZE (30*1024) /*30k*/ +#define PACK_SIZE (512*1024) +#define BUFF_SIZE (512*1024) + +#define PACK ('P' << 24 | 'A' << 16 | 'C' << 8 | 'K') +#define CODE ('C' << 24 | 'O' << 16 | 'D' << 8 | 'E') + +static DEFINE_MUTEX(mutex); + +struct firmware_mgr_s { + struct list_head head; + spinlock_t lock; +}; + +struct firmware_info_s { + struct list_head node; + char path[64]; + char firmware_name[32]; + struct firmware_s *data; +}; + +struct ucode_info_s { + int cpu_version; + const char *name; +}; + +struct firmware_header_s { + int magic; + int checksum; + char version[32]; + char author[32]; + char date[32]; + char commit[16]; + int data_size; + unsigned int time; + char reserved[32]; +}; + +struct firmware_s { + union { + struct firmware_header_s header; + char buf[256]; + }; + char data[0]; +}; + +struct package_header_s { + int magic; + int size; + int checksum; + char reserved[128]; +}; + +struct package_s { + union { + struct package_header_s header; + char buf[256]; + }; + char data[0]; +}; + +struct info_header_s { + char name[32]; + char format[32]; + char cpu[32]; + int length; +}; + +struct package_info_s { + union { + struct info_header_s header; + char buf[256]; + }; + char data[0]; +}; + +static struct ucode_info_s ucode_info[] = { +#include "firmware_info.h" +}; + +struct firmware_dev_s { + struct cdev cdev; + struct device *dev; + dev_t dev_no; +}; + +static const struct file_operations firmware_fops = { + .owner = THIS_MODULE +}; + +struct firmware_mgr_s *g_mgr; +struct firmware_dev_s *g_dev; + +static u32 debug = 0; + +static int request_firmware_from_sys(const char *file_name, + char *buf, int size) +{ + int ret = -1; + const struct firmware *firmware; + + pr_info("Try load %s ...\n", file_name); + + ret = request_firmware(&firmware, file_name, g_dev->dev); + if (ret < 0) { + pr_info("Error : %d can't load the %s.\n", ret, file_name); + goto err; + } + + if (firmware->size > size) { + pr_info("Not enough memory size for ucode.\n"); + ret = -ENOMEM; + goto release; + } + + memcpy(buf, (char *)firmware->data, firmware->size); + + pr_info("Load mcode size : %zd, Name : %s.\n", + firmware->size, file_name); + ret = firmware->size; +release: + release_firmware(firmware); +err: + return ret; +} + +int request_decoder_firmware_on_sys(enum vformat_e type, + const char *file_name, char *buf, int size) +{ + int ret; + + ret = get_firmware_data(file_name, buf); + if (ret < 0) + pr_info("Get firmware fail.\n"); + + if (ret > size) { + pr_info("Not enough memory.\n"); + return -ENOMEM; + } + + return ret; +} +int get_decoder_firmware_data(enum vformat_e type, + const char *file_name, char *buf, int size) +{ + int ret; + + ret = request_decoder_firmware_on_sys(type, file_name, buf, size); + if (ret < 0) + pr_info("get_decoder_firmware_data %s for format %d failed!\n", + file_name, type); + + return ret; +} +EXPORT_SYMBOL(get_decoder_firmware_data); + +static unsigned long firmware_mgr_lock(struct firmware_mgr_s *mgr) +{ + unsigned long flags; + + spin_lock_irqsave(&mgr->lock, flags); + return flags; +} + +static void firmware_mgr_unlock(struct firmware_mgr_s *mgr, unsigned long flags) +{ + spin_unlock_irqrestore(&mgr->lock, flags); +} + +static void add_info(struct firmware_info_s *info) +{ + unsigned long flags; + struct firmware_mgr_s *mgr = g_mgr; + + flags = firmware_mgr_lock(mgr); + list_add(&info->node, &mgr->head); + firmware_mgr_unlock(mgr, flags); +} + +static void del_info(struct firmware_info_s *info) +{ + unsigned long flags; + struct firmware_mgr_s *mgr = g_mgr; + + flags = firmware_mgr_lock(mgr); + list_del(&info->node); + kfree(info); + firmware_mgr_unlock(mgr, flags); +} + +static void walk_firmware_info(void) +{ + struct firmware_mgr_s *mgr = g_mgr; + struct firmware_info_s *info; + + if (list_empty(&mgr->head)) { + pr_info("the info list is empty.\n"); + return ; + } + + list_for_each_entry(info, &mgr->head, node) { + if (IS_ERR_OR_NULL(info->data)) + continue; + + pr_info("path : %s.\n", info->path); + pr_info("name : %s.\n", info->firmware_name); + pr_info("version : %s.\n", info->data->header.version); + pr_info("checksum : 0x%x.\n", info->data->header.checksum); + pr_info("data size : %d.\n", info->data->header.data_size); + pr_info("author : %s.\n", info->data->header.author); + pr_info("date : %s.\n", info->data->header.date); + pr_info("commit : %s.\n\n", info->data->header.commit); + } +} + +static ssize_t info_show(struct class *class, + struct class_attribute *attr, char *buf) +{ + char *pbuf = buf; + struct firmware_mgr_s *mgr = g_mgr; + struct firmware_info_s *info; + + if (list_empty(&mgr->head)) { + pbuf += sprintf(pbuf, "No firmware.\n"); + goto out; + } + + list_for_each_entry(info, &mgr->head, node) { + if (IS_ERR_OR_NULL(info->data)) + continue; + + pr_info( "%10s : %s\n", "name", + info->firmware_name); + pr_info( "%10s : %d\n", "size", + info->data->header.data_size); + pr_info( "%10s : %s\n", "version", + info->data->header.version); + pr_info( "%10s : 0x%x\n", "checksum", + info->data->header.checksum); + pr_info( "%10s : %s\n", "commit", + info->data->header.commit); + pr_info( "%10s : %s\n", "author", + info->data->header.author); + pr_info( "%10s : %s\n\n", "date", + info->data->header.date); + } +out: + return pbuf - buf; +} + +static int set_firmware_info(void) +{ + int ret = 0, i, len; + struct firmware_info_s *info; + int info_size = ARRAY_SIZE(ucode_info); + int cpu_version = get_cpu_type(); + const char *name; + char *path = __getname(); + + if (IS_ERR_OR_NULL(path)) + return -ENOMEM; + + for (i = 0; i < info_size; i++) { + if (cpu_version != ucode_info[i].cpu_version) + continue; + + name = ucode_info[i].name; + if (IS_ERR_OR_NULL(name)) + break; + + len = snprintf(path, PATH_MAX, "%s/%s", DIR, + ucode_info[i].name); + if (len >= PATH_MAX) + continue; + + info = kzalloc(sizeof(struct firmware_info_s), GFP_KERNEL); + if (IS_ERR_OR_NULL(info)) { + __putname(path); + return -ENOMEM; + } + + strcpy(info->path, path); + strcpy(info->firmware_name, name); + info->data = NULL; + + add_info(info); + } + + __putname(path); + + return ret; +} + +static int firmware_probe(char *buf) +{ + int magic = 0; + + memcpy(&magic, buf, sizeof(int)); + return magic; +} + +static int checksum(struct firmware_s *firmware) +{ + unsigned int crc; + + crc = crc32_le(~0U, firmware->data, firmware->header.data_size); + + if (debug) + pr_info("firmware crc result : 0x%x\n", crc ^ ~0U); + + return firmware->header.checksum != (crc ^ ~0U) ? 0 : 1; +} + +static int check_repeat(struct firmware_s *data, const char *name) +{ + struct firmware_mgr_s *mgr = g_mgr; + struct firmware_info_s *info; + + if (list_empty(&mgr->head)) { + pr_info("the info list is empty.\n"); + return 0; + } + + list_for_each_entry(info, &mgr->head, node) { + struct firmware_s *tmp; + + if (strcmp(info->firmware_name, name)) + continue; + + if (IS_ERR_OR_NULL(info->data)) { + pr_info("the data is null.\n"); + info->data = data; + + return 1; + } + + if (info->data->header.time >= data->header.time) { + pr_info("the data is old.\n"); + kfree(data); + + return 1; + } + + pr_info("the data is new.\n"); + tmp = info->data; + info->data = data; + kfree(tmp); + + return 1; + } + + return 0; +} + +static int firmware_parse_package(char *buf, int size) +{ + int ret = 0; + struct package_s *pack; + struct package_info_s *pack_info; + struct firmware_info_s *info; + struct firmware_s *data; + char *pack_data; + const char *cpu; + int info_len, len; + char *path = __getname(); + + if (IS_ERR_OR_NULL(path)) + return -ENOMEM; + + pack = vmalloc(PACK_SIZE); + if (IS_ERR_OR_NULL(pack)) { + __putname(path); + return -ENOMEM; + } + + memset(pack, 0, PACK_SIZE); + memcpy(pack, buf, size); + + pack_data = pack->data; + pack_info = (struct package_info_s *)pack_data; + info_len = sizeof(struct package_info_s); + + for (;;) { + if (!pack_info->header.length) + break; + + cpu = get_cpu_type_name(); + if (strcmp(cpu, pack_info->header.cpu)) + continue; + + len = snprintf(path, PATH_MAX, "%s/%s", DIR, + pack_info->header.name); + if (len >= PATH_MAX) + continue; + + info = kzalloc(sizeof(struct firmware_info_s), GFP_KERNEL); + if (IS_ERR_OR_NULL(info)) { + ret = -ENOMEM; + goto out; + } + + data = kzalloc(FRIMWARE_SIZE, GFP_KERNEL); + if (IS_ERR_OR_NULL(data)) { + kfree(info); + ret = -ENOMEM; + goto out; + } + + strcpy(info->path, path); + strcpy(info->firmware_name, pack_info->header.name); + + len = pack_info->header.length; + memcpy(data, pack_info->data, len); + + pack_data += (pack_info->header.length + info_len); + pack_info = (struct package_info_s *)pack_data; + + if (!checksum(data)) { + pr_info("check sum fail !\n"); + kfree(data); + kfree(info); + goto out; + } + + if (check_repeat(data, info->firmware_name)) { + kfree(info); + continue; + } + + info->data = data; + add_info(info); + } +out: + __putname(path); + vfree(pack); + + return ret; +} + +static int firmware_parse_code(struct firmware_info_s *info, + char *buf, int size) +{ + info->data = kzalloc(FRIMWARE_SIZE, GFP_KERNEL); + if (IS_ERR_OR_NULL(info->data)) + return -ENOMEM; + + memcpy(info->data, buf, size); + + if (!checksum(info->data)) { + pr_info("check sum fail !\n"); + kfree(info->data); + return -1; + } + + return 0; +} + +static int get_firmware_from_sys(const char *path, + char *buf, int size) +{ + int len = 0; + + len = request_firmware_from_sys(path, buf, size); + if (len < 0) + pr_info("get data from fsys fail.\n"); + + return len; +} + +static int set_firmware_data(void) +{ + int magic = 0; + struct firmware_mgr_s *mgr = g_mgr; + struct firmware_info_s *info, *temp; + char *buf; + int size; + + if (list_empty(&mgr->head)) { + pr_info("the info list is empty.\n"); + return 0; + } + + buf = vmalloc(BUFF_SIZE); + if (IS_ERR_OR_NULL(buf)) + return -ENOMEM; + + list_for_each_entry_safe(info, temp, &mgr->head, node) { + size = get_firmware_from_sys(info->path, buf, BUFF_SIZE); + magic = firmware_probe(buf); + + switch (magic) { + case PACK: + firmware_parse_package(buf, size); + del_info(info); + break; + + case CODE: + firmware_parse_code(info, buf, size); + break; + + default: + del_info(info); + pr_info("invaild type.\n"); + } + + memset(buf, 0, BUFF_SIZE); + } + + if (debug) + walk_firmware_info(); + + vfree(buf); + + return 0; +} + +int get_firmware_data(const char *name, char *buf) +{ + int data_len, ret = -1; + struct firmware_mgr_s *mgr = g_mgr; + struct firmware_info_s *info; + char *firmware_name = __getname(); + + if (IS_ERR_OR_NULL(firmware_name)) + return -ENOMEM; + + strcat(firmware_name, name); + strcat(firmware_name, ".bin"); + + if (list_empty(&mgr->head)) { + pr_info("the info list is empty.\n"); + return 0; + } + + list_for_each_entry(info, &mgr->head, node) { + if (strcmp(firmware_name, info->firmware_name)) + continue; + + data_len = info->data->header.data_size; + memcpy(buf, info->data->data, data_len); + ret = data_len; + + break; + } + + __putname(firmware_name); + + return ret; +} +EXPORT_SYMBOL(get_firmware_data); + +static int firmware_pre_load(void) +{ + int ret = -1; + + ret = set_firmware_info(); + if (ret < 0) { + pr_info("Get path fail.\n"); + goto err; + } + + ret = set_firmware_data(); + if (ret < 0) { + pr_info("Set data fail.\n"); + goto err; + } +err: + return ret; +} + +static int firmware_mgr_init(void) +{ + g_mgr = kzalloc(sizeof(struct firmware_mgr_s), GFP_KERNEL); + if (IS_ERR_OR_NULL(g_mgr)) + return -ENOMEM; + + INIT_LIST_HEAD(&g_mgr->head); + spin_lock_init(&g_mgr->lock); + + return 0; +} + +static struct class_attribute firmware_class_attrs[] = { + __ATTR_RO(info), + __ATTR_NULL +}; + +static struct class firmware_class = { + .name = CLASS_NAME, + .class_attrs = firmware_class_attrs, +}; + +static int firmware_driver_init(void) +{ + int ret = -1; + + g_dev = kzalloc(sizeof(struct firmware_dev_s), GFP_KERNEL); + if (IS_ERR_OR_NULL(g_dev)) + return -ENOMEM; + + g_dev->dev_no = MKDEV(AMSTREAM_MAJOR, 100); + + ret = register_chrdev_region(g_dev->dev_no, 1, DEV_NAME); + if (ret < 0) { + pr_info("Can't get major number %d.\n", AMSTREAM_MAJOR); + goto err; + } + + cdev_init(&g_dev->cdev, &firmware_fops); + g_dev->cdev.owner = THIS_MODULE; + + ret = cdev_add(&g_dev->cdev, g_dev->dev_no, 1); + if (ret) { + pr_info("Error %d adding cdev fail.\n", ret); + goto err; + } + + ret = class_register(&firmware_class); + if (ret < 0) { + pr_info("Failed in creating class.\n"); + goto err; + } + + g_dev->dev = device_create(&firmware_class, NULL, + g_dev->dev_no, NULL, DEV_NAME); + if (IS_ERR_OR_NULL(g_dev->dev)) { + pr_info("Create device failed.\n"); + ret = -ENODEV; + goto err; + } + + pr_info("Registered firmware driver success.\n"); +err: + return ret; +} + +static void firmware_info_clean(void) +{ + struct firmware_mgr_s *mgr = g_mgr; + struct firmware_info_s *info; + unsigned long flags; + + flags = firmware_mgr_lock(mgr); + while (!list_empty(&mgr->head)) { + info = list_entry(mgr->head.next, + struct firmware_info_s, node); + list_del(&info->node); + kfree(info->data); + kfree(info); + } + firmware_mgr_unlock(mgr, flags); +} + +static void firmware_mgr_clean(void) +{ + struct firmware_mgr_s *mgr = g_mgr; + + kfree(mgr); +} + +static void firmware_driver_exit(void) +{ + cdev_del(&g_dev->cdev); + device_destroy(&firmware_class, g_dev->dev_no); + class_unregister(&firmware_class); + unregister_chrdev_region(g_dev->dev_no, 1); + kfree(g_dev); + + pr_info("Firmware driver cleaned up.\n"); +} + +static int __init firmware_module_init(void) +{ + int ret = -1; + + ret = firmware_driver_init(); + if (ret) { + pr_info("Error %d firmware driver init fail.\n", ret); + goto err; + } + + ret = firmware_mgr_init(); + if (ret) { + pr_info("Error %d firmware mgr init fail.\n", ret); + goto err; + } + + ret = firmware_pre_load(); + if (ret) { + pr_info("Error %d firmware pre load fail.\n", ret); + goto err; + } +err: + return ret; +} + +static void __exit firmware_module_exit(void) +{ + firmware_info_clean(); + firmware_mgr_clean(); + firmware_driver_exit(); +} + +module_param(debug, uint, 0664); + +module_init(firmware_module_init); +module_exit(firmware_module_exit); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Nanxin Qin <nanxin.qin@amlogic.com>"); diff --git a/drivers/common/firmware/firmware.h b/drivers/common/firmware/firmware.h new file mode 100644 index 0000000..d7d34ea --- a/dev/null +++ b/drivers/common/firmware/firmware.h @@ -0,0 +1,118 @@ +/* + * drivers/amlogic/media/common/firmware/firmware.h + * + * Copyright (C) 2016 Amlogic, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * +*/ + +#ifndef __VIDEO_FIRMWARE_HEADER_ +#define __VIDEO_FIRMWARE_HEADER_ +#include <linux/types.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/amlogic/media/old_cpu_version.h> +#include <linux/amlogic/media/utils/vformat.h> + +struct video_firmware_s { + + int cpu_type; + + enum vformat_e type; + + const char *name; + + int size; + + int ref_cnt; + + const char *version; + + struct video_firmware_s *next; + + const char ucode[1]; /*malloced more for ucode. */ +}; +int register_video_firamre_per_cpu(int cputype, enum vformat_e type, + const char *name, const char *code, int size, const char *ver); +int register_video_firamre(int cpus[], enum vformat_e type, const char *name, + const char *code, int size, const char *ver); +int get_decoder_firmware_data(enum vformat_e type, const char *file_name, + char *buf, int size); + +int show_all_buildin_firmwares(void); +int get_firmware_data(const char *name, char *buf); + + +#define F_VERSION "0.0.0.0" + +#define REGISTER_FIRMARE_PER_CPU_VER(cpu, type, name, ver)\ + register_video_firamre_per_cpu(cpu, type,\ + #name, (const char *)name, sizeof(name), ver) + +#define REGISTER_FIRMARE_PER_CPU(cpu, type, name)\ + REGISTER_FIRMARE_PER_CPU_VER(cpu, type, name, F_VERSION) + +#define REGISTER_FIRMARE_IN(cpus, type, name, ver)\ + register_video_firamre(cpus, type,\ + #name, (const char *)name, sizeof(name), ver) + +#define DEF_FIRMWARE_FOR_CPUS_TYPE_VER(cpus, type, name, ver)\ + do {\ + int t_cpus[] = cpus;\ + REGISTER_FIRMARE_IN(t_cpus, type, name, ver);\ + } while (0) + +#define DEF_FIRMWARE_FOR_CPUS_TYPE(cpus, type, name)\ + DEF_FIRMWARE_FOR_CPUS_TYPE_VER(cpus, type, name, F_VERSION) + +#define DEF_FIRMWARE_FOR_CPUS_VER(cpus, name, ver)\ + DEF_FIRMWARE_FOR_CPUS_TYPE_VER(cpus, FOR_VFORMAT, name, ver) + +#define DEF_FIRMWARE_FOR_CPUS(cpus, name)\ + DEF_FIRMWARE_FOR_CPUS_TYPE(cpus, FOR_VFORMAT, name) + +#define DEF_FIRMWARE_VER(name, ver)\ + DEF_FIRMWARE_FOR_CPUS_TYPE_VER(FOR_CPUS, FOR_VFORMAT, name, ver) + +#define DEF_FIRMWARE(name)\ + DEF_FIRMWARE_FOR_CPUS_TYPE_VER(FOR_CPUS, \ + FOR_VFORMAT, name, F_VERSION) + +/* +*#define INIT_DEF_FIRMWARE()\ +*static int __init init_ucode_per_cpu(void)\ +* {\ +* REG_FIRMWARE_ALL();\ +* return 0;\ +* } \ +*module_init(init_ucode_per_cpu); +*/ + + +/* +*sample: +* +*#define PER_CPUS {MESON_CPU_MAJOR_ID_M8,MESON_CPU_MAJOR_ID_M8M2,0} +*#define FOR_VFORMAT VFORMAT_H264 +* +*#define REG_FIRMWARE_ALL()\ +* DEF_FIRMWARE(vh264_mc);\ +* DEF_FIRMWARE(vh264_header_mc);\ +* DEF_FIRMWARE(vh264_data_mc);\ +* DEF_FIRMWARE(vh264_mmco_mc);\ +* DEF_FIRMWARE(vh264_list_mc);\ +* DEF_FIRMWARE(vh264_slice_mc);\ +* DEF_FIRMWARE_VER(vh264_slice_mc,"1.0");\ +* +*INIT_DEF_FIRMWARE(); +*/ +#endif diff --git a/drivers/common/firmware/firmware_info.h b/drivers/common/firmware/firmware_info.h new file mode 100644 index 0000000..3d2cdd9 --- a/dev/null +++ b/drivers/common/firmware/firmware_info.h @@ -0,0 +1,23 @@ +/* + * drivers/amlogic/media/common/firmware/firmware_info.h + * + * Copyright (C) 2016 Amlogic, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * +*/ + +{MESON_CPU_MAJOR_ID_GXL, "gxl_vh265.bin"}, + +{MESON_CPU_MAJOR_ID_GXL, "gxl_ucode.bin"}, + +{MESON_CPU_MAJOR_ID_GXL, "gxl_h264_all.bin"}, + diff --git a/drivers/common/media_clock/Makefile b/drivers/common/media_clock/Makefile new file mode 100644 index 0000000..25c9a10 --- a/dev/null +++ b/drivers/common/media_clock/Makefile @@ -0,0 +1,5 @@ +obj-m += media_clock.o +media_clock-objs += ../chips/chips.o +media_clock-objs += clk/clkgx.o +media_clock-objs += clk/clk.o +media_clock-objs += switch/amports_gate.o diff --git a/drivers/common/media_clock/clk/clk.c b/drivers/common/media_clock/clk/clk.c new file mode 100644 index 0000000..a32c8d0 --- a/dev/null +++ b/drivers/common/media_clock/clk/clk.c @@ -0,0 +1,389 @@ +/* + * drivers/amlogic/media/common/arch/clk/clk.c + * + * Copyright (C) 2016 Amlogic, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * +*/ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/types.h> +#include <linux/fs.h> +#include <linux/init.h> +#include <linux/device.h> +#include <linux/vmalloc.h> +#include <linux/mm.h> +#include <linux/vmalloc.h> +#include <linux/slab.h> + +#include <linux/amlogic/media/utils/vformat.h> +#include <linux/amlogic/media/old_cpu_version.h> +#include "../../../stream_input/amports/amports_priv.h" +#include "../../../frame_provider/decoder/utils/vdec.h" +#include "../../chips/chips.h" +#include "clk_priv.h" +#include <linux/amlogic/media/utils/log.h> + +#define p_vdec() (get_current_vdec_chip()->clk_mgr[VDEC_1]) +#define p_vdec2() (get_current_vdec_chip()->clk_mgr[VDEC_2]) +#define p_vdec_hcodec() (get_current_vdec_chip()->clk_mgr[VDEC_HCODEC]) +#define p_vdec_hevc() (get_current_vdec_chip()->clk_mgr[VDEC_HEVC]) + +static int clock_source_wxhxfps_saved[VDEC_MAX + 1]; + +#define IF_HAVE_RUN(p, fn)\ + do {\ + if (p && p->fn)\ + p->fn();\ + } while (0) +/* +*#define IF_HAVE_RUN_P1_RET(p, fn, p1)\ +* do {\ +* pr_debug("%s-----%d\n", __func__, clk);\ +* if (p && p->fn)\ +* return p->fn(p1);\ +* else\ +* return -1;\ +* } while (0) +* +*#define IF_HAVE_RUN_RET(p, fn)\ +* do {\ +* if (p && p->fn)\ +* return p->fn();\ +* else\ +* return 0;\ +* } while (0) +*/ + +int vdec_clock_init(void) +{ + if (p_vdec() && p_vdec()->clock_init) + return p_vdec()->clock_init(); + else + return 0; +} +EXPORT_SYMBOL(vdec_clock_init); + +/* +*clk ==0 : +* to be release. +* released shared clk, +*clk ==1 :default low clk +*clk ==2 :default high clk +*/ +int vdec_clock_set(int clk) +{ + pr_debug("%s-----%d\n", __func__, clk); + if (p_vdec() && p_vdec()->clock_set) + return p_vdec()->clock_set(clk); + else + return -1; +} +EXPORT_SYMBOL(vdec_clock_set); + +void vdec_clock_enable(void) +{ + vdec_clock_set(1); +} +EXPORT_SYMBOL(vdec_clock_enable); + +void vdec_clock_hi_enable(void) +{ + vdec_clock_set(2); +} +EXPORT_SYMBOL(vdec_clock_hi_enable); + +void vdec_clock_on(void) +{ + IF_HAVE_RUN(p_vdec(), clock_on); +} +EXPORT_SYMBOL(vdec_clock_on); + +void vdec_clock_off(void) +{ + IF_HAVE_RUN(p_vdec(), clock_off); + clock_source_wxhxfps_saved[VDEC_1] = 0; +} +EXPORT_SYMBOL(vdec_clock_off); + +int vdec2_clock_set(int clk) +{ + pr_debug("%s-----%d\n", __func__, clk); + if (p_vdec2() && p_vdec2()->clock_set) + return p_vdec2()->clock_set(clk); + else + return -1; +} +EXPORT_SYMBOL(vdec2_clock_set); + +void vdec2_clock_enable(void) +{ + vdec2_clock_set(1); +} +EXPORT_SYMBOL(vdec2_clock_enable); + +void vdec2_clock_hi_enable(void) +{ + vdec2_clock_set(2); +} +EXPORT_SYMBOL(vdec2_clock_hi_enable); + +void vdec2_clock_on(void) +{ + IF_HAVE_RUN(p_vdec2(), clock_on); +} +EXPORT_SYMBOL(vdec2_clock_on); + +void vdec2_clock_off(void) +{ + IF_HAVE_RUN(p_vdec2(), clock_off); + clock_source_wxhxfps_saved[VDEC_2] = 0; +} +EXPORT_SYMBOL(vdec2_clock_off); + +int hcodec_clock_set(int clk) +{ + pr_debug("%s-----%d\n", __func__, clk); + if (p_vdec_hcodec() && p_vdec_hcodec()->clock_set) + return p_vdec_hcodec()->clock_set(clk); + else + return -1; +} +EXPORT_SYMBOL(hcodec_clock_set); + +void hcodec_clock_enable(void) +{ + hcodec_clock_set(1); +} +EXPORT_SYMBOL(hcodec_clock_enable); + +void hcodec_clock_hi_enable(void) +{ + hcodec_clock_set(2); +} +EXPORT_SYMBOL(hcodec_clock_hi_enable); + +void hcodec_clock_on(void) +{ + IF_HAVE_RUN(p_vdec_hcodec(), clock_on); +} +EXPORT_SYMBOL(hcodec_clock_on); + +void hcodec_clock_off(void) +{ + IF_HAVE_RUN(p_vdec_hcodec(), clock_off); + clock_source_wxhxfps_saved[VDEC_HCODEC] = 0; +} +EXPORT_SYMBOL(hcodec_clock_off); + +int hevc_clock_init(void) +{ + if (p_vdec_hevc() && p_vdec_hevc()->clock_init) + return p_vdec_hevc()->clock_init(); + else + return 0; +} +EXPORT_SYMBOL(hevc_clock_init); + +int hevc_clock_set(int clk) +{ + pr_debug("%s-----%d\n", __func__, clk); + if (p_vdec_hevc() && p_vdec_hevc()->clock_set) + return p_vdec_hevc()->clock_set(clk); + else + return -1; +} +EXPORT_SYMBOL(hevc_clock_set); + +void hevc_clock_enable(void) +{ + hevc_clock_set(1); +} +EXPORT_SYMBOL(hevc_clock_enable); + +void hevc_clock_hi_enable(void) +{ + hevc_clock_set(2); +} +EXPORT_SYMBOL(hevc_clock_hi_enable); + +void hevc_clock_on(void) +{ + IF_HAVE_RUN(p_vdec_hevc(), clock_on); +} +EXPORT_SYMBOL(hevc_clock_on); + +void hevc_clock_off(void) +{ + IF_HAVE_RUN(p_vdec_hevc(), clock_off); + clock_source_wxhxfps_saved[VDEC_HEVC] = 0; +} +EXPORT_SYMBOL(hevc_clock_off); + +int vdec_source_get(enum vdec_type_e core) +{ + return clock_source_wxhxfps_saved[core]; +} +EXPORT_SYMBOL(vdec_source_get); + +int vdec_clk_get(enum vdec_type_e core) +{ + return get_current_vdec_chip()->clk_mgr[core]->clock_get(core); +} +EXPORT_SYMBOL(vdec_clk_get); + +int get_clk_with_source(int format, int w_x_h_fps) +{ + struct clk_set_setting *p_setting; + int i; + int clk = -2; + + p_setting = get_current_vdec_chip()->clk_setting_array; + if (!p_setting || format < 0 || format > VFORMAT_MAX) { + pr_info("error on get_clk_with_source ,%p,%d\n", + p_setting, format); + return -1; /*no setting found. */ + } + p_setting = &p_setting[format]; + for (i = 0; i < MAX_CLK_SET; i++) { + if (p_setting->set[i].wh_X_fps > w_x_h_fps) { + clk = p_setting->set[i].clk_Mhz; + break; + } + } + return clk; +} +EXPORT_SYMBOL(get_clk_with_source); + +int vdec_source_changed_for_clk_set(int format, int width, int height, int fps) +{ + int clk = get_clk_with_source(format, width * height * fps); + int ret_clk; + + if (clk < 0) { + pr_info("can't get valid clk for source ,%d,%d,%d\n", + width, height, fps); + if (format >= 1920 && width >= 1080 && fps >= 30) + clk = 2; /*default high clk */ + else + clk = 0; /*default clk. */ + } + if (width * height * fps == 0) + clk = 0; + /* + *clk == 0 + *is used for set default clk; + *if used supper clk. + *changed to default min clk. + */ + + if (format == VFORMAT_HEVC || format == VFORMAT_VP9) { + ret_clk = hevc_clock_set(clk); + clock_source_wxhxfps_saved[VDEC_HEVC] = width * height * fps; + } else if (format == VFORMAT_H264_ENC && format == VFORMAT_JPEG_ENC) { + ret_clk = hcodec_clock_set(clk); + clock_source_wxhxfps_saved[VDEC_HCODEC] = width * height * fps; + } else if (format == VFORMAT_H264_4K2K && + get_cpu_type() == MESON_CPU_MAJOR_ID_M8) { + ret_clk = vdec2_clock_set(clk); + clock_source_wxhxfps_saved[VDEC_2] = width * height * fps; + ret_clk = vdec_clock_set(clk); + clock_source_wxhxfps_saved[VDEC_1] = width * height * fps; + } else { + ret_clk = vdec_clock_set(clk); + clock_source_wxhxfps_saved[VDEC_1] = width * height * fps; + } + return ret_clk; +} +EXPORT_SYMBOL(vdec_source_changed_for_clk_set); + +static int register_vdec_clk_mgr_per_cpu(int cputype, + enum vdec_type_e vdec_type, struct chip_vdec_clk_s *t_mgr) +{ + + struct chip_vdec_clk_s *mgr; + + if (cputype != get_cpu_type() || vdec_type >= VDEC_MAX) { + /* + *pr_info("ignore vdec clk mgr for vdec[%d] cpu=%d\n", + *vdec_type, cputype); + */ + return 0; /* ignore don't needed firmare. */ + } + mgr = kmalloc(sizeof(struct chip_vdec_clk_s), GFP_KERNEL); + if (!mgr) + return -ENOMEM; + *mgr = *t_mgr; + /* + *pr_info("register vdec clk mgr for vdec[%d]\n", vdec_type); + */ + if (mgr->clock_init) { + if (mgr->clock_init()) { + kfree(mgr); + return -ENOMEM; + } + } + get_current_vdec_chip()->clk_mgr[vdec_type] = mgr; + return 0; +} + +int register_vdec_clk_mgr(int cputype[], enum vdec_type_e vdec_type, + struct chip_vdec_clk_s *t_mgr) +{ + int i = 0; + + while (cputype[i] > 0) { + register_vdec_clk_mgr_per_cpu(cputype[i], vdec_type, t_mgr); + i++; + } + return 0; +} +EXPORT_SYMBOL(register_vdec_clk_mgr); + +static int register_vdec_clk_setting_per_cpu(int cputype, + struct clk_set_setting *setting, int size) +{ + + struct clk_set_setting *p_setting; + + if (cputype != get_cpu_type()) { + /* + *pr_info("ignore clk_set_setting for cpu=%d\n", + *cputype); + */ + return 0; /* ignore don't needed this setting . */ + } + p_setting = kmalloc(size, GFP_KERNEL); + if (!p_setting) + return -ENOMEM; + memcpy(p_setting, setting, size); + + pr_info("register clk_set_setting cpu[%d]\n", cputype); + + get_current_vdec_chip()->clk_setting_array = p_setting; + return 0; +} + +int register_vdec_clk_setting(int cputype[], + struct clk_set_setting *p_seting, int size) +{ + int i = 0; + + while (cputype[i] > 0) { + register_vdec_clk_setting_per_cpu(cputype[i], p_seting, size); + i++; + } + return 0; +} +EXPORT_SYMBOL(register_vdec_clk_setting); + diff --git a/drivers/common/media_clock/clk/clk.h b/drivers/common/media_clock/clk/clk.h new file mode 100644 index 0000000..64cdd73 --- a/dev/null +++ b/drivers/common/media_clock/clk/clk.h @@ -0,0 +1,128 @@ +/* + * drivers/amlogic/media/common/arch/clk/clk.h + * + * Copyright (C) 2016 Amlogic, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * +*/ + +#ifndef VDEC_CHIP_CLK_HEADER +#define VDEC_CHIP_CLK_HEADER +#include <linux/types.h> +#include <linux/init.h> +#include <linux/module.h> +#include "clk_priv.h" +#include <linux/amlogic/media/clk/gp_pll.h> + +#ifndef INCLUDE_FROM_ARCH_CLK_MGR +int vdec_clock_init(void); +int vdec_clock_set(int clk); +int vdec2_clock_set(int clk); + +int hcodec_clock_set(int clk); +int hevc_clock_init(void); +int hevc_clock_set(int clk); + +void vdec_clock_on(void); +void vdec_clock_off(void); +void vdec2_clock_on(void); + +void vdec2_clock_off(void); +void hcodec_clock_on(void); +void hcodec_clock_off(void); +void hevc_clock_on(void); +void hevc_clock_off(void); + +int vdec_source_get(enum vdec_type_e core); +int vdec_clk_get(enum vdec_type_e core); + +int vdec_source_changed_for_clk_set(int format, int width, int height, int fps); +int get_clk_with_source(int format, int w_x_h_fps); + +void vdec_clock_enable(void); +void vdec_clock_hi_enable(void); +void hcodec_clock_enable(void); +void hcodec_clock_hi_enable(void); +void hevc_clock_enable(void); +void hevc_clock_hi_enable(void); +void vdec2_clock_enable(void); +void vdec2_clock_hi_enable(void); + +#endif +int register_vdec_clk_mgr(int cputype[], + enum vdec_type_e vdec_type, struct chip_vdec_clk_s *t_mgr); + +int register_vdec_clk_setting(int cputype[], + struct clk_set_setting *p_seting, int size); + +#ifdef INCLUDE_FROM_ARCH_CLK_MGR +static struct chip_vdec_clk_s vdec_clk_mgr __initdata = { + .clock_init = vdec_clock_init, + .clock_set = vdec_clock_set, + .clock_on = vdec_clock_on, + .clock_off = vdec_clock_off, + .clock_get = vdec_clock_get, +}; + +#ifdef VDEC_HAS_VDEC2 +static struct chip_vdec_clk_s vdec2_clk_mgr __initdata = { + .clock_set = vdec2_clock_set, + .clock_on = vdec2_clock_on, + .clock_off = vdec2_clock_off, + .clock_get = vdec_clock_get, +}; +#endif + +#ifdef VDEC_HAS_HEVC +static struct chip_vdec_clk_s vdec_hevc_clk_mgr __initdata = { + .clock_init = hevc_clock_init, + .clock_set = hevc_clock_set, + .clock_on = hevc_clock_on, + .clock_off = hevc_clock_off, + .clock_get = vdec_clock_get, +}; +#endif + +#ifdef VDEC_HAS_VDEC_HCODEC +static struct chip_vdec_clk_s vdec_hcodec_clk_mgr __initdata = { + .clock_set = hcodec_clock_set, + .clock_on = hcodec_clock_on, + .clock_off = hcodec_clock_off, + .clock_get = vdec_clock_get, +}; +#endif + +static int __init vdec_init_clk(void) +{ + int cpus[] = CLK_FOR_CPU; + register_vdec_clk_mgr(cpus, VDEC_1, &vdec_clk_mgr); +#ifdef VDEC_HAS_VDEC2 + register_vdec_clk_mgr(cpus, VDEC_2, &vdec2_clk_mgr); +#endif +#ifdef VDEC_HAS_HEVC + register_vdec_clk_mgr(cpus, VDEC_HEVC, &vdec_hevc_clk_mgr); +#endif +#ifdef VDEC_HAS_VDEC_HCODEC + register_vdec_clk_mgr(cpus, VDEC_HCODEC, &vdec_hcodec_clk_mgr); +#endif + +#ifdef VDEC_HAS_CLK_SETTINGS + register_vdec_clk_setting(cpus, + clks_for_formats, sizeof(clks_for_formats)); +#endif + return 0; +} + +#define ARCH_VDEC_CLK_INIT()\ + module_init(vdec_init_clk) +#endif +#endif diff --git a/drivers/common/media_clock/clk/clk_priv.h b/drivers/common/media_clock/clk/clk_priv.h new file mode 100644 index 0000000..1898e6d --- a/dev/null +++ b/drivers/common/media_clock/clk/clk_priv.h @@ -0,0 +1,38 @@ +/* + * drivers/amlogic/media/common/arch/clk/clk_priv.h + * + * Copyright (C) 2016 Amlogic, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * +*/ + +#ifndef AMPORTS_CLK_PRIV_HEADER +#define AMPORTS_CLK_PRIV_HEADER + +struct clk_set { + u32 wh_X_fps; /* [x*y*fps */ + u32 clk_Mhz; /*min MHZ */ +}; +#define MAX_CLK_SET 6 +struct clk_set_setting { + struct clk_set set[MAX_CLK_SET]; +}; + +struct chip_vdec_clk_s { + int (*clock_get)(enum vdec_type_e core); + int (*clock_init)(void); + int (*clock_set)(int clk); + void (*clock_on)(void); + void (*clock_off)(void); + void (*clock_prepare_switch)(void); +}; +#endif diff --git a/drivers/common/media_clock/clk/clkgx.c b/drivers/common/media_clock/clk/clkgx.c new file mode 100644 index 0000000..a32dceb --- a/dev/null +++ b/drivers/common/media_clock/clk/clkgx.c @@ -0,0 +1,668 @@ +/* + * drivers/amlogic/media/common/arch/clk/clkgx.c + * + * Copyright (C) 2016 Amlogic, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * +*/ + +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/amlogic/media/clk/gp_pll.h> +#include <linux/amlogic/media/utils/vdec_reg.h> +#include <linux/amlogic/media/utils/amports_config.h> +#include "../../../frame_provider/decoder/utils/vdec.h" +#include <linux/amlogic/media/registers/register.h> +#include "clk_priv.h" +#include <linux/amlogic/media/utils/log.h> + +#include <linux/amlogic/media/registers/register_ops.h> +#define debug_print pr_info + +#define MHz (1000000) + +struct clk_mux_s { + struct clk *vdec_clk; + struct clk *hcodec_clk; + struct clk *hevc_clk; +}; + +struct clk_mux_s gclk; +/* +*HHI_VDEC_CLK_CNTL +*0x1078[11:9] (fclk = 2000MHz) + *0: fclk_div4 + *1: fclk_div3 + *2: fclk_div5 + *3: fclk_div7 + *4: mpll_clk_out1 + *5: mpll_clk_out2 +*0x1078[6:0] + *divider +*0x1078[8] + *enable +*/ + +void vdec1_set_clk(int source, int div) +{ + debug_print("vdec1_set_clk %d, %d\n", source, div); + WRITE_HHI_REG_BITS(HHI_VDEC_CLK_CNTL, (source << 9) | (div - 1), 0, 16); +} +EXPORT_SYMBOL(vdec1_set_clk); + +void hcodec_set_clk(int source, int div) +{ + WRITE_HHI_REG_BITS(HHI_VDEC_CLK_CNTL, + (source << 9) | (div - 1), 16, 16); +} +EXPORT_SYMBOL(hcodec_set_clk); + +void vdec2_set_clk(int source, int div) +{ + WRITE_HHI_REG_BITS(HHI_VDEC2_CLK_CNTL, + (source << 9) | (div - 1), 0, 16); +} +EXPORT_SYMBOL(vdec2_set_clk); + +void hevc_set_clk(int source, int div) +{ + debug_print("hevc_set_clk %d, %d\n", source, div); + WRITE_HHI_REG_BITS(HHI_VDEC2_CLK_CNTL, + (source << 9) | (div - 1), 16, 16); +} +EXPORT_SYMBOL(hevc_set_clk); + +void clock_set_init(struct device *dev) +{ + gclk.vdec_clk = devm_clk_get(dev, "clk_vdec_mux"); + if (IS_ERR(gclk.vdec_clk)) { + printk("get vdec clk err.\n"); + } + + gclk.hcodec_clk = devm_clk_get(dev, "clk_hcodec_mux"); + if (IS_ERR(gclk.hcodec_clk)) { + printk("get hcodec clk err.\n"); + } + + gclk.hevc_clk = devm_clk_get(dev, "clk_hevc_mux"); + if (IS_ERR(gclk.hevc_clk)) { + printk("get hevc clk err.\n"); + } +} +EXPORT_SYMBOL(clock_set_init); + +void vdec_get_clk_source(int clk, int *source, int *div, int *rclk) +{ +#define source_div4 (0) +#define source_div3 (1) +#define source_div5 (2) +#define source_div7 (3) + if (clk > 500) { + *source = source_div3; + *div = 1; + *rclk = 667; + } else if (clk >= 500) { + *source = source_div4; + *div = 1; + *rclk = 500; + } else if (clk >= 400) { + *source = source_div5; + *div = 1; + *rclk = 400; + } else if (clk >= 333) { + *source = source_div3; + *div = 2; + *rclk = 333; + } else if (clk >= 200) { + *source = source_div5; + *div = 2; + *rclk = 200; + } else if (clk >= 166) { + *source = source_div4; + *div = 3; + *rclk = 166; + } else if (clk >= 133) { + *source = source_div5; + *div = 3; + *rclk = 133; + } else if (clk >= 100) { + *source = source_div5; + *div = 4; + *rclk = 100; + } else if (clk >= 50) { + *source = source_div5; + *div = 8; + *rclk = 50; + } else { + *source = source_div5; + *div = 20; + *rclk = 10; + } +} +EXPORT_SYMBOL(vdec_get_clk_source); + +static int vdec_set_clk(int dec, int rate) +{ + struct clk *clk; + + switch (dec) { + case VDEC_1: + clk = gclk.vdec_clk; + break; + + case VDEC_HCODEC: + clk = gclk.hcodec_clk; + break; + + case VDEC_2: + clk = gclk.vdec_clk; + break; + + case VDEC_HEVC: + clk = gclk.hevc_clk; + break; + + case VDEC_MAX: + break; + + default: + pr_info("invaild vdec type."); + } + + clk_set_rate(clk, rate); + WRITE_VREG_BITS(DOS_GCLK_EN0, 0x3ff, 0, 10); + + return 0; +} + +static bool is_gp0_div2 = true; + +/* set gp0 648M vdec use gp0 clk*/ +#define VDEC1_648M() \ + WRITE_HHI_REG_BITS(HHI_VDEC_CLK_CNTL, (6 << 9) | (0), 0, 16) + +#define HEVC_648M() \ + WRITE_HHI_REG_BITS(HHI_VDEC2_CLK_CNTL, (6 << 9) | (0), 16, 16) + +/*set gp0 1296M vdec use gp0 clk div2*/ +#define VDEC1_648M_DIV() \ + WRITE_HHI_REG_BITS(HHI_VDEC_CLK_CNTL, (6 << 9) | (1), 0, 16) + +#define HEVC_648M_DIV() \ + WRITE_HHI_REG_BITS(HHI_VDEC2_CLK_CNTL, (6 << 9) | (1), 16, 16) + +#define VDEC1_WITH_GP_PLL() \ + ((READ_HHI_REG(HHI_VDEC_CLK_CNTL) & 0xe00) == 0xc00) +#define HEVC_WITH_GP_PLL() \ + ((READ_HHI_REG(HHI_VDEC2_CLK_CNTL) & 0xe000000) == 0xc000000) + +#define VDEC1_CLOCK_ON() \ + do { if (is_meson_m8_cpu()) { \ + WRITE_HHI_REG_BITS(HHI_VDEC_CLK_CNTL, 1, 8, 1); \ + WRITE_VREG_BITS(DOS_GCLK_EN0, 0x3ff, 0, 10); \ + } else { \ + WRITE_HHI_REG_BITS(HHI_VDEC_CLK_CNTL, 1, 8, 1); \ + WRITE_HHI_REG_BITS(HHI_VDEC3_CLK_CNTL, 0, 15, 1); \ + WRITE_HHI_REG_BITS(HHI_VDEC3_CLK_CNTL, 0, 8, 1); \ + WRITE_VREG_BITS(DOS_GCLK_EN0, 0x3ff, 0, 10); \ + } \ + } while (0) + +#define VDEC2_CLOCK_ON() do {\ + WRITE_HHI_REG_BITS(HHI_VDEC2_CLK_CNTL, 1, 8, 1); \ + WRITE_VREG(DOS_GCLK_EN1, 0x3ff);\ + } while (0) + +#define HCODEC_CLOCK_ON() do {\ + WRITE_HHI_REG_BITS(HHI_VDEC_CLK_CNTL, 1, 24, 1); \ + WRITE_VREG_BITS(DOS_GCLK_EN0, 0x7fff, 12, 15);\ + } while (0) +#define HEVC_CLOCK_ON() do {\ + WRITE_HHI_REG_BITS(HHI_VDEC2_CLK_CNTL, 1, 24, 1); \ + WRITE_HHI_REG_BITS(HHI_VDEC4_CLK_CNTL, 0, 31, 1); \ + WRITE_HHI_REG_BITS(HHI_VDEC4_CLK_CNTL, 0, 24, 1); \ + WRITE_VREG(DOS_GCLK_EN3, 0xffffffff);\ + } while (0) +#define VDEC1_SAFE_CLOCK() do {\ + WRITE_HHI_REG_BITS(HHI_VDEC3_CLK_CNTL, \ + READ_HHI_REG(HHI_VDEC_CLK_CNTL) & 0x7f, 0, 7); \ + WRITE_HHI_REG_BITS(HHI_VDEC3_CLK_CNTL, 1, 8, 1); \ + WRITE_HHI_REG_BITS(HHI_VDEC3_CLK_CNTL, 1, 15, 1);\ + } while (0) + +#define VDEC1_CLOCK_OFF() \ + WRITE_HHI_REG_BITS(HHI_VDEC_CLK_CNTL, 0, 8, 1) +#define VDEC2_CLOCK_OFF() \ + WRITE_HHI_REG_BITS(HHI_VDEC2_CLK_CNTL, 0, 8, 1) +#define HCODEC_CLOCK_OFF() \ + WRITE_HHI_REG_BITS(HHI_VDEC_CLK_CNTL, 0, 24, 1) +#define HEVC_SAFE_CLOCK() do { \ + WRITE_HHI_REG_BITS(HHI_VDEC4_CLK_CNTL, \ + (READ_HHI_REG(HHI_VDEC2_CLK_CNTL) >> 16) & 0x7f, 16, 7);\ + WRITE_HHI_REG_BITS(HHI_VDEC4_CLK_CNTL, \ + (READ_HHI_REG(HHI_VDEC2_CLK_CNTL) >> 25) & 0x7f, 25, 7);\ + WRITE_HHI_REG_BITS(HHI_VDEC4_CLK_CNTL, 1, 24, 1); \ + WRITE_HHI_REG_BITS(HHI_VDEC4_CLK_CNTL, 1, 31, 1);\ + } while (0) + +#define HEVC_CLOCK_OFF() WRITE_HHI_REG_BITS(HHI_VDEC2_CLK_CNTL, 0, 24, 1) + +static int clock_real_clk[VDEC_MAX + 1]; +static struct gp_pll_user_handle_s *gp_pll_user_vdec, *gp_pll_user_hevc; + +static int gp_pll_user_cb_vdec(struct gp_pll_user_handle_s *user, int event) +{ + pr_info("gp_pll_user_cb_vdec call\n"); + if (event == GP_PLL_USER_EVENT_GRANT) { + if (!IS_ERR(gclk.vdec_clk)) { + vdec_set_clk(VDEC_1, 648 * MHz); + pr_info("get clock : %lu\n", + clk_get_rate(gclk.vdec_clk)); + } + } + return 0; +} + +/* +*enum vformat_e { +* VFORMAT_MPEG12 = 0, +* VFORMAT_MPEG4, +* VFORMAT_H264, +* VFORMAT_MJPEG, +* VFORMAT_REAL, +* VFORMAT_JPEG, +* VFORMAT_VC1, +* VFORMAT_AVS, +* VFORMAT_YUV, +* VFORMAT_H264MVC, +* VFORMAT_H264_4K2K, +* VFORMAT_HEVC, +* VFORMAT_H264_ENC, +* VFORMAT_JPEG_ENC, +* VFORMAT_VP9, +* VFORMAT_MAX +*}; +*sample: +*{{1280*720*30, 100}, {1920*1080*30, 166}, {1920*1080*60, 333}, +* {4096*2048*30, 600}, {4096*2048*60, 600}, {INT_MAX, 600},} +*mean: +*width * height * fps +*<720p30fps clk=100MHZ +*>=720p30fps & < 1080p30fps clk=166MHZ +*>=1080p 30fps & < 1080p60fps clk=333MHZ +*/ +static struct clk_set_setting clks_for_formats[] = { + { /*[VFORMAT_MPEG12] */ + {{1280 * 720 * 30, 100}, {1920 * 1080 * 30, 166}, + {1920 * 1080 * 60, 333}, + {4096 * 2048 * 30, 600}, {4096 * 2048 * 60, + 600}, {INT_MAX, 600}, + } + }, + { /*[VFORMAT_MPEG4] */ + {{1280 * 720 * 30, 100}, {1920 * 1080 * 30, 166}, + {1920 * 1080 * 60, 333}, + {4096 * 2048 * 30, 600}, {4096 * 2048 * 60, + 600}, {INT_MAX, 600}, + } + }, + { /*[VFORMAT_H264] */ + {{1280 * 720 * 30, 100}, {1920 * 1080 * 21, 166}, + {1920 * 1080 * 30, 333}, + {1920 * 1080 * 60, 600}, {4096 * 2048 * 60, + 600}, {INT_MAX, 600}, + } + }, + { /*[VFORMAT_MJPEG] */ + {{1280 * 720 * 30, 200}, {1920 * 1080 * 30, 200}, + {1920 * 1080 * 60, 333}, + {4096 * 2048 * 30, 600}, {4096 * 2048 * 60, + 600}, {INT_MAX, 600}, + } + }, + { /*[VFORMAT_REAL] */ + {{1280 * 720 * 20, 200}, {1920 * 1080 * 30, 500}, + {1920 * 1080 * 60, 500}, + {4096 * 2048 * 30, 600}, {4096 * 2048 * 60, + 600}, {INT_MAX, 600}, + } + }, + { /*[VFORMAT_JPEG] */ + {{1280 * 720 * 30, 100}, {1920 * 1080 * 30, 166}, + {1920 * 1080 * 60, 333}, + {4096 * 2048 * 30, 600}, {4096 * 2048 * 60, + 600}, {INT_MAX, 600}, + } + }, + { /*[VFORMAT_VC1] */ + {{1280 * 720 * 30, 100}, {1920 * 1080 * 30, 166}, + {1920 * 1080 * 60, 333}, + {4096 * 2048 * 30, 600}, {4096 * 2048 * 60, + 600}, {INT_MAX, 600}, + } + }, + { /*[VFORMAT_AVS] */ + {{1280 * 720 * 30, 100}, {1920 * 1080 * 30, 166}, + {1920 * 1080 * 60, 333}, + {4096 * 2048 * 30, 600}, {4096 * 2048 * 60, + 600}, {INT_MAX, 600}, + } + }, + { /*[VFORMAT_YUV] */ + {{1280 * 720 * 30, 100}, {INT_MAX, 100}, + {0, 0}, {0, 0}, {0, 0}, {0, 0}, + } + }, + { /*VFORMAT_H264MVC */ + {{1280 * 720 * 30, 333}, {1920 * 1080 * 30, 333}, + {4096 * 2048 * 60, 600}, + {INT_MAX, 630}, {0, 0}, {0, 0}, + } + }, + { /*VFORMAT_H264_4K2K */ + {{1280 * 720 * 30, 600}, {4096 * 2048 * 60, 630}, + {INT_MAX, 630}, + {0, 0}, {0, 0}, {0, 0}, + } + }, + { /*VFORMAT_HEVC */ + {{1280 * 720 * 30, 100}, {1920 * 1080 * 60, 600}, + {4096 * 2048 * 25, 630}, + {4096 * 2048 * 30, 630}, {4096 * 2048 * 60, + 630}, {INT_MAX, 630}, + } + }, + { /*VFORMAT_H264_ENC */ + {{1280 * 720 * 30, 0}, {INT_MAX, 0}, + {0, 0}, {0, 0}, {0, 0}, {0, 0}, + } + }, + { /*VFORMAT_JPEG_ENC */ + {{1280 * 720 * 30, 0}, {INT_MAX, 0}, + {0, 0}, {0, 0}, {0, 0}, {0, 0}, + } + }, + { /*VFORMAT_VP9 */ + {{1280 * 720 * 30, 100}, {1920 * 1080 * 30, 100}, + {1920 * 1080 * 60, 166}, + {4096 * 2048 * 30, 333}, {4096 * 2048 * 60, + 630}, {INT_MAX, 630}, + } + }, + +}; + +static int vdec_clock_init(void) +{ + gp_pll_user_vdec = gp_pll_user_register("vdec", 0, gp_pll_user_cb_vdec); + if (get_cpu_type() >= MESON_CPU_MAJOR_ID_GXL) + is_gp0_div2 = false; + else + is_gp0_div2 = true; + + if (get_cpu_type() >= MESON_CPU_MAJOR_ID_GXL) { + pr_info("used fix clk for vdec clk source!\n"); + /*update_vdec_clk_config_settings(1);*//*mask*/ + } + return (gp_pll_user_vdec) ? 0 : -ENOMEM; +} + +static void update_clk_with_clk_configs(int clk, int *source, int *div, + int *rclk) +{ + unsigned int config = 0;/*get_vdec_clk_config_settings();*//*mask*/ + + if (!config) + return; + if (config >= 10) { + int wantclk; + + wantclk = config; + vdec_get_clk_source(wantclk, source, div, rclk); + } +} + +#define NO_GP0_PLL 0/*(get_vdec_clk_config_settings() == 1)*//*mask*/ +#define ALWAYS_GP0_PLL 0/*(get_vdec_clk_config_settings() == 2)*//*mask*/ + +static int vdec_clock_set(int clk) +{ + int use_gpll = 0; + int clk_seted = 0; + int pll_wait = 0; + + if (clk == 1) + clk = 200; + else if (clk == 2) { + if (clock_real_clk[VDEC_1] != 648) + clk = 500; + else + clk = 648; + } else if (clk == 0) { + /* + *used for release gp pull. + *if used, release it. + *if not used gp pll + *do nothing. + */ + if (clock_real_clk[VDEC_1] == 667 || + (clock_real_clk[VDEC_1] == 648) || + clock_real_clk[VDEC_1] <= 0) + clk = 200; + else + clk = clock_real_clk[VDEC_1]; + } + + if ((clk > 500 && clk != 667)) { + if (clock_real_clk[VDEC_1] == 648) + return 648; + + gp_pll_request(gp_pll_user_vdec); + + while (pll_wait++ < 1000000) { + if (clk_get_rate(gclk.vdec_clk) == 648) { + clk_seted = 1; + break; + } + udelay(1); + } + + if (!clk_seted) { + use_gpll = 0; + clk = 667; + pr_info("get pll failed used fix pll\n"); + } + } + + if (!clk_seted) {/*if 648 not set, */ + vdec_set_clk(VDEC_1, clk * MHz); + pr_info("get clock : %lu", clk_get_rate(gclk.vdec_clk)); + } + + if (!use_gpll) + gp_pll_release(gp_pll_user_vdec); + + clock_real_clk[VDEC_1] = clk; + pr_info("vdec_clock_set to %d\n", clk); + + return clk; +} + +static void vdec_clock_on(void) +{ + VDEC1_CLOCK_ON(); +} + +static void vdec_clock_off(void) +{ + VDEC1_CLOCK_OFF(); + clock_real_clk[VDEC_1] = 0; + gp_pll_release(gp_pll_user_vdec); +} + +static int hcodec_clock_set(int clk) +{ + clk_set_rate(gclk.hcodec_clk, clk * MHz); + clock_real_clk[VDEC_HCODEC] = clk; + return clk; +} + +static void hcodec_clock_on(void) +{ + HCODEC_CLOCK_ON(); +} + +static void hcodec_clock_off(void) +{ + HCODEC_CLOCK_OFF(); +} + +static int gp_pll_user_cb_hevc(struct gp_pll_user_handle_s *user, int event) +{ + debug_print("gp_pll_user_cb_hevc callback\n"); + if (event == GP_PLL_USER_EVENT_GRANT) { + struct clk *clk = clk_get(NULL, "gp0_pll"); + + if (!IS_ERR(clk)) { + if (is_gp0_div2) + clk_set_rate(clk, 1296000000UL); + else + clk_set_rate(clk, 648000000UL); + HEVC_SAFE_CLOCK(); + HEVC_CLOCK_OFF(); + if (is_gp0_div2) + HEVC_648M_DIV(); + else + HEVC_648M(); + HEVC_CLOCK_ON(); + debug_print("gp_pll_user_cb_hevc callback2\n"); + } + } + + return 0; +} + +static int hevc_clock_init(void) +{ + gp_pll_user_hevc = gp_pll_user_register("hevc", 0, gp_pll_user_cb_hevc); + + return (gp_pll_user_hevc) ? 0 : -ENOMEM; +} + +static int hevc_clock_set(int clk) +{ + int use_gpll = 0; + int source, div, rclk; + int gp_pll_wait = 0; + int clk_seted = 0; + + debug_print("hevc_clock_set 1 to clk %d\n", clk); + if (clk == 1) + clk = 200; + else if (clk == 2) { + if (clock_real_clk[VDEC_HEVC] != 648) + clk = 500; + else + clk = 648; + } else if (clk == 0) { + /* + *used for release gp pull. + *if used, release it. + *if not used gp pll + *do nothing. + */ + if ((clock_real_clk[VDEC_HEVC] == 667) || + (clock_real_clk[VDEC_HEVC] == 648) || + (clock_real_clk[VDEC_HEVC] <= 0)) + clk = 200; + else + clk = clock_real_clk[VDEC_HEVC]; + } + vdec_get_clk_source(clk, &source, &div, &rclk); + update_clk_with_clk_configs(clk, &source, &div, &rclk); + + if (rclk == clock_real_clk[VDEC_HEVC]) + return rclk; /*clk not changed, */ + if (NO_GP0_PLL) { + use_gpll = 0; + clk_seted = 0; + } else if ((rclk > 500 && clk != 667) || ALWAYS_GP0_PLL) { + if (clock_real_clk[VDEC_HEVC] == 648) + return 648; + use_gpll = 1; + gp_pll_request(gp_pll_user_hevc); + while (!HEVC_WITH_GP_PLL() && gp_pll_wait++ < 1000000) + udelay(1); + if (HEVC_WITH_GP_PLL()) { + clk_seted = 1; + rclk = 648; + } else { + rclk = 667; + /*gp_pull request failed,used default 500Mhz */ + pr_info("get gp pll failed used fix pull\n"); + } + } + if (!clk_seted) { /*if 648 not set, */ + //HEVC_SAFE_CLOCK(); + //HEVC_CLOCK_OFF(); + //vdec_set_clk(VDEC_HEVC, source, div); + //HEVC_CLOCK_ON(); + } + if (!use_gpll) + gp_pll_release(gp_pll_user_hevc); + clock_real_clk[VDEC_HEVC] = rclk; +/* +* debug_print("hevc_clock_set 2 to rclk=%d, configs=%d\n", +* rclk, get_vdec_clk_config_settings()); +*/ + return rclk; +} + +static void hevc_clock_on(void) +{ + HEVC_CLOCK_ON(); +} + +static void hevc_clock_off(void) +{ + HEVC_CLOCK_OFF(); + gp_pll_release(gp_pll_user_hevc); + clock_real_clk[VDEC_HEVC] = 0; +} + +static int vdec_clock_get(enum vdec_type_e core) +{ + if (core >= VDEC_MAX) + return 0; + + return clock_real_clk[core]; +} + +#define INCLUDE_FROM_ARCH_CLK_MGR + +/*#define VDEC_HAS_VDEC2*/ +#define VDEC_HAS_HEVC +#define VDEC_HAS_VDEC_HCODEC +#define VDEC_HAS_CLK_SETTINGS +#define CLK_FOR_CPU {\ + MESON_CPU_MAJOR_ID_GXBB,\ + MESON_CPU_MAJOR_ID_GXTVBB,\ + MESON_CPU_MAJOR_ID_GXL,\ + MESON_CPU_MAJOR_ID_GXM,\ + MESON_CPU_MAJOR_ID_TXL,\ + 0} +#include "clk.h" +ARCH_VDEC_CLK_INIT(); +MODULE_LICENSE("GPL"); diff --git a/drivers/common/media_clock/switch/amports_gate.c b/drivers/common/media_clock/switch/amports_gate.c new file mode 100644 index 0000000..51e606c --- a/dev/null +++ b/drivers/common/media_clock/switch/amports_gate.c @@ -0,0 +1,195 @@ +/* + * drivers/amlogic/media/common/arch/switch/amports_gate.c + * + * Copyright (C) 2016 Amlogic, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * +*/ +#include <linux/compiler.h> +#include <linux/clk-provider.h> +#include <linux/err.h> +#include <linux/kernel.h> +#include <linux/spinlock.h> +#include <linux/clk.h> +#include "amports_gate.h" +#include <linux/amlogic/media/utils/vdec_reg.h> +#include "../../../stream_input/amports/amports_priv.h" + +#define DEBUG_REF 0 +#define GATE_RESET_OK + +struct gate_swtch_node { + struct clk *clk; + const char *name; + spinlock_t lock; + unsigned long flags; + int ref_count; +}; +#ifdef GATE_RESET_OK + +struct gate_swtch_node gates[] = { + { + .name = "demux", + }, + { + .name = "parser_top", + }, + { + .name = "vpu_intr", + }, + { + .name = "vdec", + }, + { + .name = "clk_vdec_mux", + }, + { + .name = "clk_hcodec_mux", + }, + { + .name = "clk_hevc_mux", + }, +}; + + +/* +mesonstream { + compatible = "amlogic, codec, streambuf"; + dev_name = "mesonstream"; + status = "okay"; + clocks = <&clkc CLKID_DOS_PARSER + &clkc CLKID_VPU_INTR + &clkc CLKID_DEMUX + &clkc CLKID_DOS + &clkc CLKID_VDEC_MUX + &clkc CLKID_HCODEC_MUX + &clkc CLKID_HEVC_MUX>; + clock-names = "parser_top", + "vpu_intr", + "demux", + "vdec", + "clk_vdec_mux", + "clk_hcodec_mux", + "clk_hevc_mux"; +}; +*/ + +int amports_clock_gate_init(struct device *dev) +{ + int i; + + for (i = 0; i < sizeof(gates) / sizeof(struct gate_swtch_node); i++) { + gates[i].clk = devm_clk_get(dev, gates[i].name); + if (IS_ERR_OR_NULL(gates[i].clk)) { + gates[i].clk = NULL; + pr_info("get gate %s control failed %p\n", + gates[i].name, + gates[i].clk); + } else { + pr_info("get gate %s control ok %p\n", + gates[i].name, + gates[i].clk); + } + gates[i].ref_count = 0; + spin_lock_init(&gates[i].lock); + } + return 0; +} +EXPORT_SYMBOL(amports_clock_gate_init); + +static int amports_gate_clk(struct gate_swtch_node *gate_node, int enable) +{ + spin_lock_irqsave(&gate_node->lock, gate_node->flags); + if (enable) { + if (DEBUG_REF) + pr_info("amports_gate_reset,count: %d\n", + gate_node->ref_count); + if (gate_node->ref_count == 0) + clk_prepare_enable(gate_node->clk); + + gate_node->ref_count++; + } else { + gate_node->ref_count--; + if (DEBUG_REF) + pr_info("amports_gate_reset,count: %d\n", + gate_node->ref_count); + + if (gate_node->ref_count == 0) + clk_disable_unprepare(gate_node->clk); + } + spin_unlock_irqrestore(&gate_node->lock, gate_node->flags); + return 0; +} + +int amports_switch_gate(const char *name, int enable) +{ + int i; + + for (i = 0; i < sizeof(gates) / sizeof(struct gate_swtch_node); i++) { + if (!strcmp(name, gates[i].name)) { + + /*pr_info("openclose:%d gate %s control\n", enable, + gates[i].name);*/ + + if (gates[i].clk) + amports_gate_clk(&gates[i], enable); + } + } + return 0; +} +EXPORT_SYMBOL(amports_switch_gate); + +#else +/* +*can used for debug. +*on chip bringup. +*/ +int amports_clock_gate_init(struct device *dev) +{ + static int gate_inited; + + if (gate_inited) + return 0; +/* +*#define HHI_GCLK_MPEG0 0x1050 +*#define HHI_GCLK_MPEG1 0x1051 +*#define HHI_GCLK_MPEG2 0x1052 +*#define HHI_GCLK_OTHER 0x1054 +*#define HHI_GCLK_AO 0x1055 +*/ + WRITE_HHI_REG_BITS(HHI_GCLK_MPEG0, 1, 1, 1);/*dos*/ + WRITE_HHI_REG_BITS(HHI_GCLK_MPEG1, 1, 25, 1);/*U_parser_top()*/ + WRITE_HHI_REG_BITS(HHI_GCLK_MPEG1, 0xff, 6, 8);/*aiu()*/ + WRITE_HHI_REG_BITS(HHI_GCLK_MPEG1, 1, 4, 1);/*demux()*/ + WRITE_HHI_REG_BITS(HHI_GCLK_MPEG1, 1, 2, 1);/*audio in()*/ + WRITE_HHI_REG_BITS(HHI_GCLK_MPEG2, 1, 25, 1);/*VPU Interrupt*/ + gate_inited++; + + + + return 0; +} +EXPORT_SYMBOL(amports_clock_gate_init); + +static int amports_switch_gate(struct gate_swtch_node *gate_node, int enable) +{ + return 0; +} + +int amports_switch_gate(const char *name, int enable) +{ + amports_switch_gate(0, 0); + return 0; +} +EXPORT_SYMBOL(amports_switch_gate); + +#endif diff --git a/drivers/common/media_clock/switch/amports_gate.h b/drivers/common/media_clock/switch/amports_gate.h new file mode 100644 index 0000000..53e1960 --- a/dev/null +++ b/drivers/common/media_clock/switch/amports_gate.h @@ -0,0 +1,25 @@ +/* + * drivers/amlogic/media/common/arch/switch/amports_gate.h + * + * Copyright (C) 2016 Amlogic, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * +*/ + +#ifndef AMPORT_GATE_H +#define AMPORT_GATE_H +#include <linux/device.h> + +extern int amports_clock_gate_init(struct device *dev); +extern int amports_switch_gate(const char *name, int enable); + +#endif diff --git a/drivers/frame_provider/Makefile b/drivers/frame_provider/Makefile new file mode 100644 index 0000000..371e088 --- a/dev/null +++ b/drivers/frame_provider/Makefile @@ -0,0 +1 @@ +obj-y += decoder/ diff --git a/drivers/frame_provider/decoder/Makefile b/drivers/frame_provider/decoder/Makefile new file mode 100644 index 0000000..2010cbb --- a/dev/null +++ b/drivers/frame_provider/decoder/Makefile @@ -0,0 +1,13 @@ +obj-y += utils/ +obj-$(CONFIG_AMLOGIC_MEDIA_VDEC_MPEG12) += mpeg12/ +obj-$(CONFIG_AMLOGIC_MEDIA_VDEC_MPEG4) += mpeg4/ +obj-$(CONFIG_AMLOGIC_MEDIA_VDEC_MMPEG4) += mmpeg4/ +obj-$(CONFIG_AMLOGIC_MEDIA_VDEC_VC1) += vc1/ +obj-$(CONFIG_AMLOGIC_MEDIA_VDEC_H264) += h264/ +obj-$(CONFIG_AMLOGIC_MEDIA_VDEC_MH264) += mh264/ +obj-$(CONFIG_AMLOGIC_MEDIA_VDEC_H264MVC) += h264_mvc/ +obj-$(CONFIG_AMLOGIC_MEDIA_VDEC_H265) += h265/ +obj-$(CONFIG_AMLOGIC_MEDIA_VDEC_VP9) += vp9/ +obj-$(CONFIG_AMLOGIC_MEDIA_VDEC_MJPEG) += mjpeg/ +obj-$(CONFIG_AMLOGIC_MEDIA_VDEC_REAL) += real/ +obj-$(CONFIG_AMLOGIC_MEDIA_VDEC_AVS) += avs/ diff --git a/drivers/frame_provider/decoder/h264/Makefile b/drivers/frame_provider/decoder/h264/Makefile new file mode 100644 index 0000000..b7a7ce5 --- a/dev/null +++ b/drivers/frame_provider/decoder/h264/Makefile @@ -0,0 +1 @@ +obj-m += vh264.o diff --git a/drivers/frame_provider/decoder/h264/vh264.c b/drivers/frame_provider/decoder/h264/vh264.c new file mode 100644 index 0000000..08bb598 --- a/dev/null +++ b/drivers/frame_provider/decoder/h264/vh264.c @@ -0,0 +1,2925 @@ +/* + * drivers/amlogic/media/frame_provider/decoder/h264/vh264.c + * + * Copyright (C) 2016 Amlogic, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * +*/ + +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/interrupt.h> +#include <linux/timer.h> +#include <linux/kfifo.h> +#include <linux/platform_device.h> + +#include <linux/amlogic/media/utils/amstream.h> +#include <linux/amlogic/media/frame_sync/ptsserv.h> +#include <linux/amlogic/media/vfm/vframe.h> +#include <linux/amlogic/media/vfm/vframe_provider.h> +#include <linux/amlogic/media/vfm/vframe_receiver.h> +#include <linux/amlogic/media/utils/vformat.h> +#include <linux/amlogic/media/frame_sync/tsync.h> +#include <linux/workqueue.h> +#include <linux/dma-mapping.h> +#include <linux/atomic.h> +#include <linux/module.h> +#include <linux/slab.h> +#include "../../../stream_input/amports/amports_priv.h" +#include <linux/amlogic/media/canvas/canvas.h> +#include <linux/amlogic/media/codec_mm/codec_mm.h> + +#include "../utils/vdec.h" +#include <linux/amlogic/media/utils/vdec_reg.h> +#include "../utils/amvdec.h" +#include "vh264.h" +#include "../../../stream_input/parser/streambuf.h" +#include <linux/delay.h> + +/*#include <linux/amlogic/ge2d/ge2d.h>*/ + +#define DRIVER_NAME "amvdec_h264" +#define MODULE_NAME "amvdec_h264" +#define MEM_NAME "codec_264" +#define HANDLE_H264_IRQ +/* #define DEBUG_PTS */ +#if 0 /* MESON_CPU_TYPE <= MESON_CPU_TYPE_MESON6TV */ +#define DROP_B_FRAME_FOR_1080P_50_60FPS +#endif +#define RATE_MEASURE_NUM 8 +#define RATE_CORRECTION_THRESHOLD 5 +#define RATE_24_FPS 4004 /* 23.97 */ +#define RATE_25_FPS 3840 /* 25 */ +#define DUR2PTS(x) ((x)*90/96) +#define PTS2DUR(x) ((x)*96/90) +#define DUR2PTS_REM(x) (x*90 - DUR2PTS(x)*96) +#define FIX_FRAME_RATE_CHECK_IDRFRAME_NUM 2 +#define VDEC_CLOCK_ADJUST_FRAME 50 + +static inline bool close_to(int a, int b, int m) +{ + return (abs(a - b) < m) ? true : false; +} + +static DEFINE_MUTEX(vh264_mutex); +/* 12M for L41 */ +#define MAX_DPB_BUFF_SIZE (12*1024*1024) +#define DEFAULT_MEM_SIZE (32*1024*1024) +#define AVIL_DPB_BUFF_SIZE 0x01ec2000 + +#define DEF_BUF_START_ADDR 0x1000000 +#define V_BUF_ADDR_OFFSET_NEW (0x1ee000) +#define V_BUF_ADDR_OFFSET (0x13e000) + +#define PIC_SINGLE_FRAME 0 +#define PIC_TOP_BOT_TOP 1 +#define PIC_BOT_TOP_BOT 2 +#define PIC_DOUBLE_FRAME 3 +#define PIC_TRIPLE_FRAME 4 +#define PIC_TOP_BOT 5 +#define PIC_BOT_TOP 6 +#define PIC_INVALID 7 + +#define EXTEND_SAR 0xff + +#define VF_POOL_SIZE 64 +#define VF_BUF_NUM 24 +#define PUT_INTERVAL (HZ/100) +#define NO_DISP_WD_COUNT (3 * HZ / PUT_INTERVAL) + +#define SWITCHING_STATE_OFF 0 +#define SWITCHING_STATE_ON_CMD3 1 +#define SWITCHING_STATE_ON_CMD1 2 +#define SWITCHING_STATE_ON_CMD1_PENDING 3 + + +#define DEC_CONTROL_FLAG_FORCE_2997_1080P_INTERLACE 0x0001 +#define DEC_CONTROL_FLAG_FORCE_2500_576P_INTERLACE 0x0002 +#define DEC_CONTROL_FLAG_DISABLE_FAST_POC 0x0004 + +#define INCPTR(p) ptr_atomic_wrap_inc(&p) + +#define SLICE_TYPE_I 2 +#define SLICE_TYPE_P 5 +#define SLICE_TYPE_B 6 + +struct buffer_spec_s { + unsigned int y_addr; + unsigned int u_addr; + unsigned int v_addr; + + int y_canvas_index; + int u_canvas_index; + int v_canvas_index; + + unsigned int y_canvas_width; + unsigned int u_canvas_width; + unsigned int v_canvas_width; + + unsigned int y_canvas_height; + unsigned int u_canvas_height; + unsigned int v_canvas_height; + + unsigned long phy_addr; + int alloc_count; +}; + +#define spec2canvas(x) \ + (((x)->v_canvas_index << 16) | \ + ((x)->u_canvas_index << 8) | \ + ((x)->y_canvas_index << 0)) + +static struct vframe_s *vh264_vf_peek(void *); +static struct vframe_s *vh264_vf_get(void *); +static void vh264_vf_put(struct vframe_s *, void *); +static int vh264_vf_states(struct vframe_states *states, void *); +static int vh264_event_cb(int type, void *data, void *private_data); + +static void vh264_prot_init(void); +static void vh264_local_init(void); +static void vh264_put_timer_func(unsigned long arg); +static void stream_switching_done(void); + +static const char vh264_dec_id[] = "vh264-dev"; + +#define PROVIDER_NAME "decoder.h264" + +static const struct vframe_operations_s vh264_vf_provider_ops = { + .peek = vh264_vf_peek, + .get = vh264_vf_get, + .put = vh264_vf_put, + .event_cb = vh264_event_cb, + .vf_states = vh264_vf_states, +}; + +static struct vframe_provider_s vh264_vf_prov; +/*TODO irq*/ +#if 1 +static u32 frame_buffer_size; +static u32 frame_width, frame_height, frame_dur, frame_prog, frame_packing_type, + last_duration; +static u32 saved_resolution; +static u32 last_mb_width, last_mb_height; +#else +static u32 frame_buffer_size; +static u32 frame_width, frame_height, frame_dur, frame_prog, last_duration; +static u32 last_mb_width, last_mb_height; +static u32 frame_packing_type; +#endif +static DECLARE_KFIFO(newframe_q, struct vframe_s *, VF_POOL_SIZE); +static DECLARE_KFIFO(display_q, struct vframe_s *, VF_POOL_SIZE); +static DECLARE_KFIFO(recycle_q, struct vframe_s *, VF_POOL_SIZE); +static DECLARE_KFIFO(delay_display_q, struct vframe_s *, VF_POOL_SIZE); + +static struct vframe_s vfpool[VF_POOL_SIZE]; +static s32 vfbuf_use[VF_BUF_NUM]; +static struct buffer_spec_s buffer_spec[VF_BUF_NUM]; +static struct buffer_spec_s fense_buffer_spec[2]; +static struct vframe_s fense_vf[2]; + +static struct timer_list recycle_timer; +static u32 stat; +static unsigned long buf_start, buf_end; +static u32 buf_size; +static s32 buf_offset; +static u32 ucode_map_start; +static u32 pts_outside; +static u32 sync_outside; +static u32 dec_control; +static u32 vh264_ratio; +static u32 vh264_rotation; +static u32 use_idr_framerate; + +static u32 seq_info; +static u32 timing_info_present_flag; +static u32 fixed_frame_rate_flag; +static u32 fixed_frame_rate_check_count; +static u32 aspect_ratio_info; +static u32 num_units_in_tick; +static u32 time_scale; +static u32 h264_ar; +static u32 decoder_debug_flag; +#ifdef DROP_B_FRAME_FOR_1080P_50_60FPS +static u32 last_interlaced; +#endif +static bool is_4k; +static unsigned char h264_first_pts_ready; +static bool h264_first_valid_pts_ready; +static u32 h264pts1, h264pts2; +static u32 h264_pts_count, duration_from_pts_done, duration_on_correcting; +static u32 vh264_error_count; +static u32 vh264_no_disp_count; +static u32 fatal_error_flag; +static u32 fatal_error_reset; +static u32 max_refer_buf = 1; +static u32 decoder_force_reset; +static unsigned int no_idr_error_count; +static unsigned int no_idr_error_max = 60; + +#if 0 +static u32 vh264_no_disp_wd_count; +#endif +static u32 vh264_running; +static s32 vh264_stream_switching_state; +static s32 vh264_eos; +static struct vframe_s *p_last_vf; +static s32 iponly_early_mode; + +/*TODO irq*/ +#if 1 +static u32 last_pts, last_pts_remainder; +#else +static u32 last_pts; +#endif +static bool check_pts_discontinue; +static u32 wait_buffer_counter; +static u32 video_signal_from_vui; + +static uint error_recovery_mode; +static uint error_recovery_mode_in = 3; +static uint error_recovery_mode_use = 3; + +static uint mb_total = 0, mb_width = 0, mb_height; +#define UCODE_IP_ONLY 2 +#define UCODE_IP_ONLY_PARAM 1 +static uint ucode_type; + +#ifdef DEBUG_PTS +static unsigned long pts_missed, pts_hit; +#endif +static uint debugfirmware; + +static atomic_t vh264_active = ATOMIC_INIT(0); +static int vh264_reset; +static struct work_struct error_wd_work; +static struct work_struct stream_switching_work; +static struct work_struct set_parameter_work; + +static struct dec_sysinfo vh264_amstream_dec_info; +static dma_addr_t mc_dma_handle; +static void *mc_cpu_addr; +static u32 first_offset; +static u32 first_pts; +static u64 first_pts64; +static bool first_pts_cached; +static void *sei_data_buffer; +static dma_addr_t sei_data_buffer_phys; +static int clk_adj_frame_count; + +#define MC_OFFSET_HEADER 0x0000 +#define MC_OFFSET_DATA 0x1000 +#define MC_OFFSET_MMCO 0x2000 +#define MC_OFFSET_LIST 0x3000 +#define MC_OFFSET_SLICE 0x4000 + +#define MC_TOTAL_SIZE (20*SZ_1K) +#define MC_SWAP_SIZE (4*SZ_1K) + +#define MODE_ERROR 0 +#define MODE_FULL 1 + +static DEFINE_SPINLOCK(lock); +static DEFINE_SPINLOCK(prepare_lock); +static DEFINE_SPINLOCK(recycle_lock); + +static bool block_display_q; +static int vh264_stop(int mode); +static s32 vh264_init(void); + +#define DFS_HIGH_THEASHOLD 3 + +static bool pts_discontinue; +#if 0 + +static struct ge2d_context_s *ge2d_videoh264_context; + +static int ge2d_videoh264task_init(void) +{ + if (ge2d_videoh264_context == NULL) + ge2d_videoh264_context = create_ge2d_work_queue(); + + if (ge2d_videoh264_context == NULL) { + pr_info("create_ge2d_work_queue video task failed\n"); + return -1; + } + return 0; +} + +static int ge2d_videoh264task_release(void) +{ + if (ge2d_videoh264_context) { + destroy_ge2d_work_queue(ge2d_videoh264_context); + ge2d_videoh264_context = NULL; + } + return 0; +} + +static int ge2d_canvas_dup(struct canvas_s *srcy, struct canvas_s *srcu, + struct canvas_s *des, int format, u32 srcindex, + u32 desindex) +{ + + struct config_para_ex_s ge2d_config; + /* pr_info("[%s]h264 ADDR srcy[0x%lx] srcu[0x%lx] des[0x%lx]\n", + * __func__, srcy->addr, srcu->addr, des->addr); + */ + memset(&ge2d_config, 0, sizeof(struct config_para_ex_s)); + + ge2d_config.alu_const_color = 0; + ge2d_config.bitmask_en = 0; + ge2d_config.src1_gb_alpha = 0; + + ge2d_config.src_planes[0].addr = srcy->addr; + ge2d_config.src_planes[0].w = srcy->width; + ge2d_config.src_planes[0].h = srcy->height; + + ge2d_config.src_planes[1].addr = srcu->addr; + ge2d_config.src_planes[1].w = srcu->width; + ge2d_config.src_planes[1].h = srcu->height; + + ge2d_config.dst_planes[0].addr = des->addr; + ge2d_config.dst_planes[0].w = des->width; + ge2d_config.dst_planes[0].h = des->height; + + ge2d_config.src_para.canvas_index = srcindex; + ge2d_config.src_para.mem_type = CANVAS_TYPE_INVALID; + ge2d_config.src_para.format = format; + ge2d_config.src_para.fill_color_en = 0; + ge2d_config.src_para.fill_mode = 0; + ge2d_config.src_para.color = 0; + ge2d_config.src_para.top = 0; + ge2d_config.src_para.left = 0; + ge2d_config.src_para.width = srcy->width; + ge2d_config.src_para.height = srcy->height; + + ge2d_config.dst_para.canvas_index = desindex; + ge2d_config.dst_para.mem_type = CANVAS_TYPE_INVALID; + ge2d_config.dst_para.format = format; + ge2d_config.dst_para.fill_color_en = 0; + ge2d_config.dst_para.fill_mode = 0; + ge2d_config.dst_para.color = 0; + ge2d_config.dst_para.top = 0; + ge2d_config.dst_para.left = 0; + ge2d_config.dst_para.width = srcy->width; + ge2d_config.dst_para.height = srcy->height; + + if (ge2d_context_config_ex(ge2d_videoh264_context, &ge2d_config) < 0) { + pr_info("ge2d_context_config_ex failed\n"); + return -1; + } + + stretchblt_noalpha(ge2d_videoh264_context, 0, 0, srcy->width, + srcy->height, 0, 0, srcy->width, srcy->height); + + return 0; +} +#endif/*mask*/ + +static inline int fifo_level(void) +{ + return VF_POOL_SIZE - kfifo_len(&newframe_q); +} + + +void spec_set_canvas(struct buffer_spec_s *spec, + unsigned width, unsigned height) +{ + canvas_config(spec->y_canvas_index, + spec->y_addr, + width, height, + CANVAS_ADDR_NOWRAP, CANVAS_BLKMODE_32X32); + + canvas_config(spec->u_canvas_index, + spec->u_addr, + width, height / 2, + CANVAS_ADDR_NOWRAP, CANVAS_BLKMODE_32X32); +} + +static void prepare_display_q(void) +{ + unsigned long flags; + int count; + + spin_lock_irqsave(&prepare_lock, flags); + + if (block_display_q) { + spin_unlock_irqrestore(&prepare_lock, flags); + return; + } + + spin_unlock_irqrestore(&prepare_lock, flags); + + count = (int)VF_POOL_SIZE - + kfifo_len(&delay_display_q) - + kfifo_len(&display_q) - + kfifo_len(&recycle_q) - + kfifo_len(&newframe_q); + + if ((vh264_stream_switching_state != SWITCHING_STATE_OFF) + || is_4k) + count = 0; + else + count = (count < 2) ? 0 : 2; + + while (kfifo_len(&delay_display_q) > count) { + struct vframe_s *vf; + + if (kfifo_get(&delay_display_q, &vf)) { + kfifo_put(&display_q, + (const struct vframe_s *)vf); + vf_notify_receiver(PROVIDER_NAME, + VFRAME_EVENT_PROVIDER_VFRAME_READY, NULL); + } + } +} + +static struct vframe_s *vh264_vf_peek(void *op_arg) +{ + struct vframe_s *vf; + + if (kfifo_peek(&display_q, &vf)) + return vf; + + return NULL; +} + +static struct vframe_s *vh264_vf_get(void *op_arg) +{ + struct vframe_s *vf; + + if (kfifo_get(&display_q, &vf)) + return vf; + + return NULL; +} + +static void vh264_vf_put(struct vframe_s *vf, void *op_arg) +{ + unsigned long flags; + + spin_lock_irqsave(&recycle_lock, flags); + + if ((vf != &fense_vf[0]) && (vf != &fense_vf[1])) + kfifo_put(&recycle_q, (const struct vframe_s *)vf); + + spin_unlock_irqrestore(&recycle_lock, flags); +} + +static int vh264_event_cb(int type, void *data, void *private_data) +{ + if (type & VFRAME_EVENT_RECEIVER_RESET) { + unsigned long flags; + + amvdec_stop(); +#ifndef CONFIG_POST_PROCESS_MANAGER + vf_light_unreg_provider(&vh264_vf_prov); +#endif + spin_lock_irqsave(&lock, flags); + vh264_local_init(); + vh264_prot_init(); + spin_unlock_irqrestore(&lock, flags); +#ifndef CONFIG_POST_PROCESS_MANAGER + vf_reg_provider(&vh264_vf_prov); +#endif + amvdec_start(); + } + return 0; +} + +static int vh264_vf_states(struct vframe_states *states, void *op_arg) +{ + unsigned long flags; + + spin_lock_irqsave(&lock, flags); + + states->vf_pool_size = VF_POOL_SIZE; + states->buf_free_num = kfifo_len(&newframe_q); + states->buf_avail_num = kfifo_len(&display_q) + + kfifo_len(&delay_display_q); + states->buf_recycle_num = kfifo_len(&recycle_q); + + spin_unlock_irqrestore(&lock, flags); + + return 0; +} + +#if 0 +static tvin_trans_fmt_t convert_3d_format(u32 type) +{ + const tvin_trans_fmt_t conv_tab[] = { + 0, /* checkerboard */ + 0, /* column alternation */ + TVIN_TFMT_3D_LA, /* row alternation */ + TVIN_TFMT_3D_LRH_OLER, /* side by side */ + TVIN_TFMT_3D_FA /* top bottom */ + }; + + return (type <= 4) ? conv_tab[type] : 0; +} +#endif + +static void set_frame_info(struct vframe_s *vf) +{ + vf->width = frame_width; + vf->height = frame_height; + vf->duration = frame_dur; + vf->ratio_control = + (min(h264_ar, (u32) DISP_RATIO_ASPECT_RATIO_MAX)) << + DISP_RATIO_ASPECT_RATIO_BIT; + vf->orientation = vh264_rotation; + vf->flag = 0; + +#ifdef CONFIG_POST_PROCESS_MANAGER_3D_PROCESS + vf->trans_fmt = 0; + if ((vf->trans_fmt == TVIN_TFMT_3D_LRF) || + (vf->trans_fmt == TVIN_TFMT_3D_LA)) { + vf->left_eye.start_x = 0; + vf->left_eye.start_y = 0; + vf->left_eye.width = frame_width / 2; + vf->left_eye.height = frame_height; + + vf->right_eye.start_x = 0; + vf->right_eye.start_y = 0; + vf->right_eye.width = frame_width / 2; + vf->right_eye.height = frame_height; + } else if ((vf->trans_fmt == TVIN_TFMT_3D_LRH_OLER) || + (vf->trans_fmt == TVIN_TFMT_3D_TB)) { + vf->left_eye.start_x = 0; + vf->left_eye.start_y = 0; + vf->left_eye.width = frame_width / 2; + vf->left_eye.height = frame_height; + + vf->right_eye.start_x = 0; + vf->right_eye.start_y = 0; + vf->right_eye.width = frame_width / 2; + vf->right_eye.height = frame_height; + } +#endif + +} + +#ifdef CONFIG_POST_PROCESS_MANAGER +static void vh264_ppmgr_reset(void) +{ + vf_notify_receiver(PROVIDER_NAME, VFRAME_EVENT_PROVIDER_RESET, NULL); + + vh264_local_init(); + + pr_info("vh264dec: vf_ppmgr_reset\n"); +} +#endif + +static int get_max_dpb_size(int level_idc, int mb_width, int mb_height) +{ + int size, r; + + switch (level_idc) { + case 10: + r = 1485; + break; + case 11: + r = 3375; + break; + case 12: + case 13: + case 20: + r = 8910; + break; + case 21: + r = 17820; + break; + case 22: + case 30: + r = 30375; + break; + case 31: + r = 67500; + break; + case 32: + r = 76800; + break; + case 40: + case 41: + case 42: + r = 122880; + break; + case 50: + r = 414000; + break; + case 51: + case 52: + r = 691200; + break; + default: + return 0; + } + size = (mb_width * mb_height + + (mb_width * mb_height / 2)) * 256 * 10; + r = (r * 1024 + size-1) / size; + r = min(r, 16); + /*pr_info("max_dpb %d size:%d\n", r, size);*/ + return r; +} +static void vh264_set_params(struct work_struct *work) +{ + int aspect_ratio_info_present_flag, aspect_ratio_idc; + int max_dpb_size, actual_dpb_size, max_reference_size; + int i, mb_mv_byte, start_addr; + unsigned long addr; + unsigned int post_canvas; + unsigned int frame_mbs_only_flag; + unsigned int chroma_format_idc, chroma444, video_signal; + unsigned int crop_infor, crop_bottom, crop_right, level_idc; + u32 disp_addr = 0xffffffff; + //struct canvas_s cur_canvas; + + if (!atomic_read(&vh264_active)) + return; + mutex_lock(&vh264_mutex); + if (vh264_stream_switching_state == SWITCHING_STATE_ON_CMD1) + vh264_stream_switching_state = SWITCHING_STATE_ON_CMD1_PENDING; + post_canvas = get_post_canvas(); + clk_adj_frame_count = 0; + /* set to max decoder clock rate at the beginning */ + vdec_source_changed(VFORMAT_H264, 3840, 2160, 60); + timing_info_present_flag = 0; + mb_width = READ_VREG(AV_SCRATCH_1); + seq_info = READ_VREG(AV_SCRATCH_2); + aspect_ratio_info = READ_VREG(AV_SCRATCH_3); + num_units_in_tick = READ_VREG(AV_SCRATCH_4); + time_scale = READ_VREG(AV_SCRATCH_5); + level_idc = READ_VREG(AV_SCRATCH_A); + video_signal = READ_VREG(AV_SCRATCH_H); + video_signal_from_vui = + ((video_signal & 0xffff) << 8) | + ((video_signal & 0xff0000) >> 16) | + ((video_signal & 0x3f000000)); +/* +* pr_info("video_signal_type_present_flag 0x%x\n", +* (video_signal_from_vui >> 29) & 1); +* pr_info("video_format 0x%x\n", +* (video_signal_from_vui >> 26) & 7); +* pr_info("video_full_range_flag 0x%x\n", +* (video_signal_from_vui >> 25) & 1); +* pr_info("color_description_present_flag 0x%x\n", +* (video_signal_from_vui >> 24) & 1); +* pr_info("color_primaries 0x%x\n", +* (video_signal_from_vui >> 16) & 0xff); +* pr_info("transfer_characteristic 0x%x\n", +* (video_signal_from_vui >> 8) & 0xff); +* pr_info("matrix_coefficient 0x%x\n", +* video_signal_from_vui & 0xff); +*/ + + mb_total = (mb_width >> 8) & 0xffff; + max_reference_size = (mb_width >> 24) & 0x7f; + mb_mv_byte = (mb_width & 0x80000000) ? 24 : 96; + if (ucode_type == UCODE_IP_ONLY_PARAM) + mb_mv_byte = 96; + mb_width = mb_width & 0xff; + if (get_cpu_type() >= MESON_CPU_MAJOR_ID_GXTVBB) { + if (!mb_width && mb_total) + mb_width = 256; + } + mb_height = mb_total / mb_width; + last_duration = 0; + /* AV_SCRATCH_2 + * bit 15: frame_mbs_only_flag + *bit 13-14: chroma_format_idc + */ + frame_mbs_only_flag = (seq_info >> 15) & 0x01; + chroma_format_idc = (seq_info >> 13) & 0x03; + chroma444 = (chroma_format_idc == 3) ? 1 : 0; + + /* @AV_SCRATCH_6.31-16 = (left << 8 | right ) << 1 + * @AV_SCRATCH_6.15-0 = (top << 8 | bottom ) << + * (2 - frame_mbs_only_flag) + */ + crop_infor = READ_VREG(AV_SCRATCH_6); + crop_bottom = (crop_infor & 0xff) >> (2 - frame_mbs_only_flag); + crop_right = ((crop_infor >> 16) & 0xff) >> (2 - frame_mbs_only_flag); + + /* if width or height from outside is not equal to mb, then use mb */ + /* add: for seeking stream with other resolution */ + if ((last_mb_width && (last_mb_width != mb_width)) + || (mb_width != ((frame_width + 15) >> 4))) + frame_width = 0; + if ((last_mb_height && (last_mb_height != mb_height)) + || (mb_height != ((frame_height + 15) >> 4))) + frame_height = 0; + last_mb_width = mb_width; + last_mb_height = mb_height; + + if ((frame_width == 0) || (frame_height == 0) || crop_infor) { + frame_width = mb_width << 4; + frame_height = mb_height << 4; + if (frame_mbs_only_flag) { + frame_height = + frame_height - (2 >> chroma444) * + min(crop_bottom, + (unsigned int)((8 << chroma444) - 1)); + frame_width = + frame_width - (2 >> chroma444) * min(crop_right, + (unsigned + int)((8 << chroma444) - 1)); + } else { + frame_height = + frame_height - (4 >> chroma444) * + min(crop_bottom, + (unsigned int)((8 << chroma444) + - 1)); + frame_width = + frame_width - (4 >> chroma444) * min(crop_right, + (unsigned + int)((8 << + chroma444) + - 1)); + } +#if 0 + pr_info + ("frame_mbs_only_flag %d, crop_bottom %d, frame_height %d, ", + frame_mbs_only_flag, crop_bottom, frame_height); + pr_info + ("mb_height %d,crop_right %d, frame_width %d, mb_width %d\n", + mb_height, crop_right, frame_width, mb_width); +#endif + if (frame_height == 1088) + frame_height = 1080; + } + + mb_width = (mb_width + 3) & 0xfffffffc; + mb_height = (mb_height + 3) & 0xfffffffc; + mb_total = mb_width * mb_height; + + /*max_reference_size <= max_dpb_size <= actual_dpb_size*/ + is_4k = (mb_total > 8160) ? true:false; + if (is_4k) { + /*4k2k*/ + if (get_cpu_type() >= MESON_CPU_MAJOR_ID_GXTVBB) { + max_dpb_size = get_max_dpb_size( + level_idc, mb_width, mb_height); + actual_dpb_size = max_dpb_size + 4; + if (actual_dpb_size > VF_BUF_NUM) + actual_dpb_size = VF_BUF_NUM; + } else { + vh264_running = 0; + fatal_error_flag = + DECODER_FATAL_ERROR_SIZE_OVERFLOW; + mutex_unlock(&vh264_mutex); + pr_err("oversize ! mb_total %d,\n", mb_total); + return; + } + } else { + actual_dpb_size = (frame_buffer_size - mb_total * mb_mv_byte * + max_reference_size) / (mb_total * 384); + actual_dpb_size = min(actual_dpb_size, VF_BUF_NUM); + max_dpb_size = get_max_dpb_size(level_idc, mb_width, mb_height); + if (max_reference_size > 1) + max_dpb_size = max_reference_size - 1; + else + max_dpb_size = max_reference_size; + if (actual_dpb_size < (max_dpb_size + 4)) { + actual_dpb_size = max_dpb_size + 4; + if (actual_dpb_size > VF_BUF_NUM) + actual_dpb_size = VF_BUF_NUM; + } + pr_info("actual_dpb_size %d max_dpb_size %d\n", + actual_dpb_size, max_dpb_size); + } + if (max_dpb_size == 0) + max_dpb_size = actual_dpb_size; + else + max_dpb_size = min(max_dpb_size, actual_dpb_size); + max_reference_size = min(max_reference_size, actual_dpb_size-1); + max_dpb_size = max(max_reference_size, max_dpb_size); + max_reference_size++; + + start_addr = addr = buf_start; + if (is_4k) + addr += ((mb_total << 8) + (mb_total << 7));/*keep last frame */ + WRITE_VREG(AV_SCRATCH_1, addr); + WRITE_VREG(AV_SCRATCH_3, post_canvas); /* should be modified later */ + /*canvas_read((READ_VCBUS_REG(VD1_IF0_CANVAS0) & 0xff), &cur_canvas); + disp_addr = (cur_canvas.addr + 7) >> 3;*//*mask*/ + if ((addr + mb_total * mb_mv_byte * max_reference_size) + >= buf_end) { + fatal_error_flag = + DECODER_FATAL_ERROR_NO_MEM; + vh264_running = 0; + mutex_unlock(&vh264_mutex); + pr_err("mv buf not enough!\n"); + return; + } + addr += mb_total * mb_mv_byte * max_reference_size; + WRITE_VREG(AV_SCRATCH_4, addr); + if (!(READ_VREG(AV_SCRATCH_F) & 0x1)) { + bool use_alloc = is_4k ? true:false; + int alloc_count = 0; + + for (i = 0; i < actual_dpb_size; i++) { + if (((addr + (mb_total << 8) + (mb_total << 7)) + >= buf_end) && (!use_alloc)) { + pr_info("start alloc for %d\n", i); + use_alloc = true; + } + if (use_alloc) { +#ifdef DOUBLE_WRITE + int page_count = + PAGE_ALIGN((mb_total << 8) + (mb_total + << 7) + (mb_total << 6) + + (mb_total << 5)) / PAGE_SIZE; +#else + int page_count = + PAGE_ALIGN((mb_total << 8) + + (mb_total << 7)) / PAGE_SIZE; +#endif + if (buffer_spec[i].phy_addr) { + if (page_count != + buffer_spec[i].alloc_count) { + pr_info("Delay release cma buf %d\n", + i); + codec_mm_free_for_dma(MEM_NAME, + buffer_spec[i].phy_addr); + buffer_spec[i].phy_addr = 0; + buffer_spec[i].alloc_count = 0; + } else + pr_info("Re-use CMA buffer %d\n", i); + } + if (!buffer_spec[i].phy_addr) { + if (!codec_mm_enough_for_size( + page_count * PAGE_SIZE, 1)) { + buffer_spec[i].alloc_count = 0; + fatal_error_flag = + DECODER_FATAL_ERROR_NO_MEM; + vh264_running = 0; + mutex_unlock(&vh264_mutex); + pr_err("CMA not enough mem! %d\n", + i); + return; + } + buffer_spec[i].alloc_count = page_count; + buffer_spec[i].phy_addr = + codec_mm_alloc_for_dma(MEM_NAME, + buffer_spec[i].alloc_count, + 4 + PAGE_SHIFT, + CODEC_MM_FLAGS_CMA_CLEAR | + CODEC_MM_FLAGS_FOR_VDECODER); + pr_info("CMA malloc ok %d\n", i); + } + alloc_count++; + if (!buffer_spec[i].phy_addr) { + buffer_spec[i].alloc_count = 0; + pr_err("264-4k mem alloc failed %d\n", + i); + vh264_running = 0; + mutex_unlock(&vh264_mutex); + return; + } + addr = buffer_spec[i].phy_addr; + } else { + if (buffer_spec[i].phy_addr) { + codec_mm_free_for_dma(MEM_NAME, + buffer_spec[i].phy_addr); + buffer_spec[i].phy_addr = 0; + buffer_spec[i].alloc_count = 0; + } + } + /*4k keep last frame */ + if (is_4k && ((addr + 7) >> 3) == disp_addr) + addr = start_addr; + if (i <= 21) { + buffer_spec[i].y_addr = addr; + addr += mb_total << 8; + buffer_spec[i].u_addr = addr; + buffer_spec[i].v_addr = addr; + addr += mb_total << 7; + vfbuf_use[i] = 0; + + buffer_spec[i].y_canvas_index = 128 + i * 2; + buffer_spec[i].u_canvas_index = 128 + i * 2 + 1; + buffer_spec[i].v_canvas_index = 128 + i * 2 + 1; + + buffer_spec[i].y_canvas_width = mb_width << 4; + buffer_spec[i].y_canvas_height = mb_height << 4; + buffer_spec[i].u_canvas_width = mb_width << 4; + buffer_spec[i].u_canvas_height = mb_height << 4; + buffer_spec[i].v_canvas_width = mb_width << 4; + buffer_spec[i].v_canvas_height = mb_height << 4; + + canvas_config(128 + i * 2, + buffer_spec[i].y_addr, + mb_width << 4, mb_height << 4, + CANVAS_ADDR_NOWRAP, + CANVAS_BLKMODE_32X32); + canvas_config(128 + i * 2 + 1, + buffer_spec[i].u_addr, + mb_width << 4, mb_height << 3, + CANVAS_ADDR_NOWRAP, + CANVAS_BLKMODE_32X32); + WRITE_VREG(ANC0_CANVAS_ADDR + i, + spec2canvas(&buffer_spec[i])); + } else { + buffer_spec[i].y_canvas_index = + 2 * (i - 21) + 4; + buffer_spec[i].y_addr = addr; + addr += mb_total << 8; + buffer_spec[i].u_canvas_index = + 2 * (i - 21) + 5; + buffer_spec[i].v_canvas_index = + 2 * (i - 21) + 5; + buffer_spec[i].u_addr = addr; + addr += mb_total << 7; + vfbuf_use[i] = 0; + + buffer_spec[i].y_canvas_width = mb_width << 4; + buffer_spec[i].y_canvas_height = mb_height << 4; + buffer_spec[i].u_canvas_width = mb_width << 4; + buffer_spec[i].u_canvas_height = mb_height << 4; + buffer_spec[i].v_canvas_width = mb_width << 4; + buffer_spec[i].v_canvas_height = mb_height << 4; + + spec_set_canvas(&buffer_spec[i] + , mb_width << 4, mb_height << 4); + WRITE_VREG(ANC0_CANVAS_ADDR + i + , spec2canvas(&buffer_spec[i])); + } + } + } else + addr = buf_start + mb_total * 384 * actual_dpb_size; + + timing_info_present_flag = seq_info & 0x2; + fixed_frame_rate_flag = 0; + aspect_ratio_info_present_flag = seq_info & 0x1; + aspect_ratio_idc = (seq_info >> 16) & 0xff; + + if (timing_info_present_flag) { + fixed_frame_rate_flag = seq_info & 0x40; + + if (((num_units_in_tick * 120) >= time_scale + && ((!sync_outside) || (!frame_dur))) && + num_units_in_tick + && time_scale) { + if (use_idr_framerate || !frame_dur + || !duration_from_pts_done || vh264_running) { + u32 frame_dur_es = + div_u64(96000ULL * 2 * + num_units_in_tick, + time_scale); + + /* hack to avoid use ES frame duration + * when it's half of the rate from + * system info + */ + /* sometimes the encoder is given a wrong + * frame rate but the system side information + *is more reliable + */ + if ((frame_dur * 2) != frame_dur_es) + frame_dur = frame_dur_es; + } + } + } else + pr_info("H.264: timing_info not present\n"); + + if (aspect_ratio_info_present_flag) { + if (aspect_ratio_idc == EXTEND_SAR) { + h264_ar = + div_u64(256ULL * (aspect_ratio_info >> 16) * + frame_height, + (aspect_ratio_info & 0xffff) * + frame_width); + } else { + /* pr_info("v264dec: aspect_ratio_idc = %d\n", + * aspect_ratio_idc); + */ + + switch (aspect_ratio_idc) { + case 1: + h264_ar = 0x100 * frame_height / frame_width; + break; + case 2: + h264_ar = 0x100 * frame_height * 11 / + (frame_width * 12); + break; + case 3: + h264_ar = 0x100 * frame_height * 11 / + (frame_width * 10); + break; + case 4: + h264_ar = 0x100 * frame_height * 11 / + (frame_width * 16); + break; + case 5: + h264_ar = 0x100 * frame_height * 33 / + (frame_width * 40); + break; + case 6: + h264_ar = 0x100 * frame_height * 11 / + (frame_width * 24); + break; + case 7: + h264_ar = 0x100 * frame_height * 11 / + (frame_width * 20); + break; + case 8: + h264_ar = 0x100 * frame_height * 11 / + (frame_width * 32); + break; + case 9: + h264_ar = 0x100 * frame_height * 33 / + (frame_width * 80); + break; + case 10: + h264_ar = 0x100 * frame_height * 11 / + (frame_width * 18); + break; + case 11: + h264_ar = 0x100 * frame_height * 11 / + (frame_width * 15); + break; + case 12: + h264_ar = 0x100 * frame_height * 33 / + (frame_width * 64); + break; + case 13: + h264_ar = 0x100 * frame_height * 99 / + (frame_width * 160); + break; + case 14: + h264_ar = 0x100 * frame_height * 3 / + (frame_width * 4); + break; + case 15: + h264_ar = 0x100 * frame_height * 2 / + (frame_width * 3); + break; + case 16: + h264_ar = 0x100 * frame_height * 1 / + (frame_width * 2); + break; + default: + if (vh264_ratio >> 16) { + h264_ar = (frame_height * + (vh264_ratio & 0xffff) * + 0x100 + + ((vh264_ratio >> 16) * + frame_width / 2)) / + ((vh264_ratio >> 16) * + frame_width); + } else { + h264_ar = frame_height * 0x100 / + frame_width; + } + break; + } + } + } else { + pr_info("v264dec: aspect_ratio not available from source\n"); + if (vh264_ratio >> 16) { + /* high 16 bit is width, low 16 bit is height */ + h264_ar = + ((vh264_ratio & 0xffff) * frame_height * 0x100 + + (vh264_ratio >> 16) * frame_width / 2) / + ((vh264_ratio >> 16) * frame_width); + } else + h264_ar = frame_height * 0x100 / frame_width; + } + + WRITE_VREG(AV_SCRATCH_0, + (max_reference_size << 24) | (actual_dpb_size << 16) | + (max_dpb_size << 8)); + if (vh264_stream_switching_state != SWITCHING_STATE_OFF) { + vh264_stream_switching_state = SWITCHING_STATE_OFF; + pr_info("Leaving switching mode.\n"); + } + mutex_unlock(&vh264_mutex); +} + +static unsigned pts_inc_by_duration(unsigned *new_pts, unsigned *new_pts_rem) +{ + unsigned r, rem; + + r = last_pts + DUR2PTS(frame_dur); + rem = last_pts_remainder + DUR2PTS_REM(frame_dur); + + if (rem >= 96) { + r++; + rem -= 96; + } + + if (new_pts) + *new_pts = r; + if (new_pts_rem) + *new_pts_rem = rem; + + return r; +} +static inline bool vh264_isr_parser(struct vframe_s *vf, + unsigned int pts_valid, unsigned int buffer_index, + unsigned int pts) +{ + unsigned int pts_duration = 0; + + if (h264_first_pts_ready == 0) { + if (pts_valid == 0) { + vfbuf_use[buffer_index]++; + vf->index = buffer_index; + kfifo_put(&recycle_q, + (const struct vframe_s *)vf); + return false; + } + + h264pts1 = pts; + h264_pts_count = 0; + h264_first_pts_ready = 1; + } else { + + if (pts_valid && (pts > h264pts1) && (h264_pts_count > 24) + && (duration_from_pts_done == 0)) { + unsigned int + old_duration = frame_dur; + h264pts2 = pts; + + pts_duration = (h264pts2 - h264pts1) * 16 / + (h264_pts_count * 15); + + if ((pts_duration != frame_dur) + && (!pts_outside)) { + if (use_idr_framerate) { + bool pts_c_24 = close_to(pts_duration, + RATE_24_FPS, + RATE_CORRECTION_THRESHOLD); + bool frm_c_25 = close_to(frame_dur, + RATE_25_FPS, + RATE_CORRECTION_THRESHOLD); + bool pts_c_25 = close_to(pts_duration, + RATE_25_FPS, + RATE_CORRECTION_THRESHOLD); + bool frm_c_24 = close_to(frame_dur, + RATE_24_FPS, + RATE_CORRECTION_THRESHOLD); + if ((pts_c_24 && frm_c_25) + || (pts_c_25 && frm_c_24)) { + pr_info + ("H.264:Correct frame dur "); + pr_info + (" from %d to duration based ", + frame_dur); + pr_info + ("on PTS %d ---\n", + pts_duration); + frame_dur = pts_duration; + duration_from_pts_done = 1; + } else if (((frame_dur < 96000 / 240) + && (pts_duration > 96000 / 240)) + || (!duration_on_correcting && + !frm_c_25 && !frm_c_24)) { + /* fft: if the frame rate is + * not regular, use the + * calculate rate insteadof. + */ + pr_info + ("H.264:Correct frame dur "); + pr_info + (" from %d to duration based ", + frame_dur); + pr_info + ("on PTS %d ---\n", + pts_duration); + frame_dur = pts_duration; + duration_on_correcting = 1; + } + } else { + if (close_to(pts_duration, + frame_dur, 2000)) { + frame_dur = pts_duration; + pr_info + ("used calculate frame rate,"); + pr_info("on duration =%d\n", + frame_dur); + } else { + pr_info + ("don't use calculate frame "); + pr_info + ("rate pts_duration =%d\n", + pts_duration); + } + } + } + + if (duration_from_pts_done == 0) { + if (close_to + (pts_duration, + old_duration, + RATE_CORRECTION_THRESHOLD)) { + pr_info + ("finished correct frame dur"); + pr_info + (" new=%d,old_duration=%d,cnt=%d\n", + pts_duration, + old_duration, + h264_pts_count); + duration_from_pts_done = 1; + } else { /*not the same,redo it. */ + if (!close_to(pts_duration, + old_duration, 1000) && + !close_to(pts_duration, + frame_dur, 1000) && + close_to(pts_duration, + last_duration, 200)) { + /* yangle: frame_dur must + * wrong,recover it. + */ + frame_dur = pts_duration; + } + + pr_info + ("restart correct frame duration "); + pr_info + ("new=%d,old_duration=%d,cnt=%d\n", + pts_duration, + old_duration, + h264_pts_count); + h264pts1 = h264pts2; + h264_pts_count = 0; + duration_from_pts_done = 0; + } + } + last_duration = pts_duration; + } + } + return true; +} +#ifdef HANDLE_H264_IRQ +static irqreturn_t vh264_isr(int irq, void *dev_id) +#else +static void vh264_isr(void) +#endif +{ + unsigned int buffer_index; + struct vframe_s *vf; + unsigned int cpu_cmd; + unsigned int pts, pts_lookup_save, pts_valid_save, pts_valid = 0; + unsigned int pts_us64_valid = 0; + u64 pts_us64; + bool force_interlaced_frame = false; + unsigned int sei_itu35_flags; + static const unsigned int idr_num = + FIX_FRAME_RATE_CHECK_IDRFRAME_NUM; + static const unsigned int flg_1080_itl = + DEC_CONTROL_FLAG_FORCE_2997_1080P_INTERLACE; + static const unsigned int flg_576_itl = + DEC_CONTROL_FLAG_FORCE_2500_576P_INTERLACE; + + WRITE_VREG(ASSIST_MBOX1_CLR_REG, 1); + + if (0 == (stat & STAT_VDEC_RUN)) { + pr_info("decoder is not running\n"); +#ifdef HANDLE_H264_IRQ + return IRQ_HANDLED; +#else + return; +#endif + } + + cpu_cmd = READ_VREG(AV_SCRATCH_0); + +#ifdef DROP_B_FRAME_FOR_1080P_50_60FPS + if ((frame_dur < 2004) && + (frame_width >= 1400) && + (frame_height >= 1000) && (last_interlaced == 0)) + SET_VREG_MASK(AV_SCRATCH_F, 0x8); +#endif + if ((decoder_force_reset == 1) + || ((error_recovery_mode != 1) + && (no_idr_error_count >= no_idr_error_max) + && (ucode_type != UCODE_IP_ONLY_PARAM))) { + vh264_running = 0; + pr_info("force reset decoder %d!!!\n", no_idr_error_count); + schedule_work(&error_wd_work); + decoder_force_reset = 0; + no_idr_error_count = 0; + } else if ((cpu_cmd & 0xff) == 1) { + if (unlikely + (vh264_running + && (kfifo_len(&newframe_q) != VF_POOL_SIZE))) { + /* a cmd 1 sent during decoding w/o getting a cmd 3. */ + /* should not happen but the original code has such + * case, do the same process + */ + if ((READ_VREG(AV_SCRATCH_1) & 0xff) + == 1) {/*invalid mb_width*/ + vh264_running = 0; + fatal_error_flag = DECODER_FATAL_ERROR_UNKNOWN; + /* this is fatal error, need restart */ + pr_info("cmd 1 fatal error happened\n"); + schedule_work(&error_wd_work); + } else { + vh264_stream_switching_state = SWITCHING_STATE_ON_CMD1; + pr_info("Enter switching mode cmd1.\n"); + schedule_work(&stream_switching_work); + } + return IRQ_HANDLED; + } + pr_info("Enter set parameter cmd1.\n"); + schedule_work(&set_parameter_work); + return IRQ_HANDLED; + } else if ((cpu_cmd & 0xff) == 2) { + int frame_mb_only, pic_struct_present, pic_struct, prog_frame, + poc_sel, idr_flag, eos, error; + int i, status, num_frame, b_offset; + int current_error_count, slice_type; + + vh264_running = 1; + vh264_no_disp_count = 0; + num_frame = (cpu_cmd >> 8) & 0xff; + frame_mb_only = seq_info & 0x8000; + pic_struct_present = seq_info & 0x10; + + current_error_count = READ_VREG(AV_SCRATCH_D); + if (vh264_error_count != current_error_count) { + /* pr_info("decoder error happened, count %d\n", + * current_error_count); + */ + vh264_error_count = current_error_count; + } + + for (i = 0; (i < num_frame) && (!vh264_eos); i++) { + status = READ_VREG(AV_SCRATCH_1 + i); + buffer_index = status & 0x1f; + error = status & 0x200; + slice_type = (READ_VREG(AV_SCRATCH_H) >> (i * 4)) & 0xf; + + if ((error_recovery_mode_use & 2) && error) + check_pts_discontinue = true; + if (ucode_type == UCODE_IP_ONLY_PARAM + && iponly_early_mode) + continue; + if ((p_last_vf != NULL) + && (p_last_vf->index == buffer_index)) + continue; + + if (buffer_index >= VF_BUF_NUM) + continue; + + pic_struct = (status >> 5) & 0x7; + prog_frame = status & 0x100; + poc_sel = status & 0x200; + idr_flag = status & 0x400; + frame_packing_type = (status >> 12) & 0x7; + eos = (status >> 15) & 1; + + if (eos) + vh264_eos = 1; + + b_offset = (status >> 16) & 0xffff; + + if (error) + no_idr_error_count++; + if (idr_flag || + (!error && (slice_type != SLICE_TYPE_I))) + no_idr_error_count = 0; + + if (decoder_debug_flag) { + pr_info + ("slice_type %x idr %x error %x count %d", + slice_type, idr_flag, error, + no_idr_error_count); + pr_info(" prog %x pic_struct %x offset %x\n", + prog_frame, pic_struct, b_offset); + } +#ifdef DROP_B_FRAME_FOR_1080P_50_60FPS + last_interlaced = prog_frame ? 0 : 1; +#endif + if (kfifo_get(&newframe_q, &vf) == 0) { + pr_info + ("fatal error, no available buffer slot."); + return IRQ_HANDLED; + } + + if (clk_adj_frame_count < VDEC_CLOCK_ADJUST_FRAME) + clk_adj_frame_count++; + + set_frame_info(vf); + + switch (i) { + case 0: + b_offset |= + (READ_VREG(AV_SCRATCH_A) & 0xffff) + << 16; + break; + case 1: + b_offset |= + READ_VREG(AV_SCRATCH_A) & 0xffff0000; + break; + case 2: + b_offset |= + (READ_VREG(AV_SCRATCH_B) & 0xffff) + << 16; + break; + case 3: + b_offset |= + READ_VREG(AV_SCRATCH_B) & 0xffff0000; + break; + case 4: + b_offset |= + (READ_VREG(AV_SCRATCH_C) & 0xffff) + << 16; + break; + case 5: + b_offset |= + READ_VREG(AV_SCRATCH_C) & 0xffff0000; + break; + default: + break; + } + + /* add 64bit pts us ; */ + if (unlikely + ((b_offset == first_offset) + && (first_pts_cached))) { + pts = first_pts; + pts_us64 = first_pts64; + first_pts_cached = false; + pts_valid = 1; + pts_us64_valid = 1; +#ifdef DEBUG_PTS + pts_hit++; +#endif + } else if (pts_lookup_offset_us64 + (PTS_TYPE_VIDEO, b_offset, &pts, 0, + &pts_us64) == 0) { + pts_valid = 1; + pts_us64_valid = 1; +#ifdef DEBUG_PTS + pts_hit++; +#endif + } else { + pts_valid = 0; + pts_us64_valid = 0; +#ifdef DEBUG_PTS + pts_missed++; +#endif + } + + /* on second IDR frame,check the diff between pts + * compute from duration and pts from lookup , + * if large than frame_dur,we think it is uncorrect. + */ + pts_lookup_save = pts; + pts_valid_save = pts_valid; + if (fixed_frame_rate_flag + && (fixed_frame_rate_check_count <= + idr_num)) { + if (idr_flag && pts_valid) { + fixed_frame_rate_check_count++; + /* pr_info("diff:%d\n", + * last_pts - pts_lookup_save); + */ + if ((fixed_frame_rate_check_count == + idr_num) && + (abs(pts - (last_pts + + DUR2PTS(frame_dur))) > + DUR2PTS(frame_dur))) { + fixed_frame_rate_flag = 0; + pr_info("pts sync mode play\n"); + } + + if (fixed_frame_rate_flag + && (fixed_frame_rate_check_count + > idr_num)) { + pr_info + ("fix_frame_rate mode play\n"); + } + } + } + + if (READ_VREG(AV_SCRATCH_F) & 2) { + /* for I only mode, ignore the PTS information + * and only uses frame duration for each I + * frame decoded + */ + if (p_last_vf) + pts_valid = 0; + /* also skip frame duration calculation + * based on PTS + */ + duration_from_pts_done = 1; + /* and add a default duration for 1/30 second + * if there is no valid frame + * duration available + */ + if (frame_dur == 0) + frame_dur = 96000 / 30; + } + + if (sync_outside == 0) { + if (!vh264_isr_parser(vf, + pts_valid, buffer_index, pts)) + continue; + + h264_pts_count++; + } else { + if (!idr_flag) + pts_valid = 0; + } + + if (pts_valid && !pts_discontinue) { + pts_discontinue = + (abs(last_pts - pts) >= + tsync_vpts_discontinuity_margin()); + } + /* if use_idr_framerate or fixed frame rate, only + * use PTS for IDR frames except for pts discontinue + */ + if (timing_info_present_flag && + frame_dur && + (use_idr_framerate || + (fixed_frame_rate_flag != 0)) + && pts_valid && h264_first_valid_pts_ready + && (!pts_discontinue)) { + pts_valid = + (slice_type == SLICE_TYPE_I) ? 1 : 0; + } + + if (!h264_first_valid_pts_ready && pts_valid) { + h264_first_valid_pts_ready = true; + last_pts = pts - DUR2PTS(frame_dur); + last_pts_remainder = 0; + } + /* calculate PTS of next frame and smooth + * PTS for fixed rate source + */ + if (pts_valid) { + if ((fixed_frame_rate_flag) && + (!pts_discontinue) && + (abs(pts_inc_by_duration(NULL, NULL) + - pts) + < DUR2PTS(frame_dur))) { + pts = pts_inc_by_duration(&pts, + &last_pts_remainder); + } else + last_pts_remainder = 0; + + } else { + if (fixed_frame_rate_flag && !pts_discontinue && + (fixed_frame_rate_check_count > idr_num) && + pts_valid_save && (sync_outside == 0) && + (abs(pts_inc_by_duration(NULL, NULL) - pts) + > DUR2PTS(frame_dur))) { + duration_from_pts_done = 0; + pr_info("recalc frame_dur\n"); + } else + pts = pts_inc_by_duration(&pts, + &last_pts_remainder); + pts_valid = 1; + } + + if ((dec_control & + flg_1080_itl) + && (frame_width == 1920) + && (frame_height >= 1080) + && (vf->duration == 3203)) + force_interlaced_frame = true; + else if ((dec_control & + flg_576_itl) + && (frame_width == 720) + && (frame_height == 576) + && (vf->duration == 3840)) + force_interlaced_frame = true; + + /* for frames with PTS, check if there is PTS + * discontinue based on previous frames + * (including error frames), + * force no VPTS discontinue reporting if we saw + *errors earlier but only once. + */ + if ((pts_valid) && (check_pts_discontinue) + && (!error)) { + if (pts_discontinue) { + vf->flag = 0; + check_pts_discontinue = false; + } else if ((pts - last_pts) < 90000) { + vf->flag = VFRAME_FLAG_NO_DISCONTINUE; + check_pts_discontinue = false; + } + } + + last_pts = pts; + + if (fixed_frame_rate_flag + && (fixed_frame_rate_check_count <= + idr_num) + && (sync_outside == 0) + && pts_valid_save) + pts = pts_lookup_save; + + if (pic_struct_present) { + if ((pic_struct == PIC_TOP_BOT) + || (pic_struct == PIC_BOT_TOP)) + prog_frame = 0; + } + + if ((!force_interlaced_frame) + && (prog_frame + || (pic_struct_present + && pic_struct + <= PIC_TRIPLE_FRAME))) { + if (pic_struct_present) { + if (pic_struct == PIC_TOP_BOT_TOP + || pic_struct + == PIC_BOT_TOP_BOT) { + vf->duration += + vf->duration >> 1; + } else if (pic_struct == + PIC_DOUBLE_FRAME) + vf->duration += vf->duration; + else if (pic_struct == + PIC_TRIPLE_FRAME) { + vf->duration += + vf->duration << 1; + } + } + + last_pts = + last_pts + DUR2PTS(vf->duration - + frame_dur); + + vf->index = buffer_index; + vf->type = + VIDTYPE_PROGRESSIVE | + VIDTYPE_VIU_FIELD | + VIDTYPE_VIU_NV21; + vf->duration_pulldown = 0; + vf->signal_type = video_signal_from_vui; + vf->index = buffer_index; + vf->pts = (pts_valid) ? pts : 0; + if (pts_us64_valid == 1) + vf->pts_us64 = pts_us64; + else + vf->pts_us64 = div64_u64(((u64)vf->pts)*100, 9); + vf->canvas0Addr = vf->canvas1Addr = + spec2canvas(&buffer_spec[buffer_index]); + vf->type_original = vf->type; + vfbuf_use[buffer_index]++; + + if ((error_recovery_mode_use & 2) && error) { + kfifo_put(&recycle_q, + (const struct vframe_s *)vf); + } else { + p_last_vf = vf; + pts_discontinue = false; + kfifo_put(&delay_display_q, + (const struct vframe_s *)vf); + } + } else { + if (pic_struct_present + && pic_struct == PIC_TOP_BOT) + vf->type = VIDTYPE_INTERLACE_TOP; + else if (pic_struct_present + && pic_struct == PIC_BOT_TOP) + vf->type = VIDTYPE_INTERLACE_BOTTOM; + else { + vf->type = + poc_sel ? + VIDTYPE_INTERLACE_BOTTOM : + VIDTYPE_INTERLACE_TOP; + } + vf->type |= VIDTYPE_VIU_NV21; + vf->type |= VIDTYPE_INTERLACE_FIRST; + + vf->duration >>= 1; + vf->duration_pulldown = 0; + vf->signal_type = video_signal_from_vui; + vf->index = buffer_index; + vf->pts = (pts_valid) ? pts : 0; + if (pts_us64_valid == 1) + vf->pts_us64 = pts_us64; + else + vf->pts_us64 = div64_u64(((u64)vf->pts)*100, 9); + vf->canvas0Addr = vf->canvas1Addr = + spec2canvas(&buffer_spec[buffer_index]); + vf->type_original = vf->type; + vfbuf_use[buffer_index]++; + vf->ready_jiffies64 = jiffies_64; + + if ((error_recovery_mode_use & 2) && error) { + kfifo_put(&recycle_q, + (const struct vframe_s *)vf); + continue; + } else { + pts_discontinue = false; + kfifo_put(&delay_display_q, + (const struct vframe_s *)vf); + } + + if (READ_VREG(AV_SCRATCH_F) & 2) + continue; + + if (kfifo_get(&newframe_q, &vf) == 0) { + pr_info + ("fatal error, no avail buffer slot."); + return IRQ_HANDLED; + } + + set_frame_info(vf); + + if (pic_struct_present + && pic_struct == PIC_TOP_BOT) + vf->type = VIDTYPE_INTERLACE_BOTTOM; + else if (pic_struct_present + && pic_struct == PIC_BOT_TOP) + vf->type = VIDTYPE_INTERLACE_TOP; + else { + vf->type = + poc_sel ? + VIDTYPE_INTERLACE_TOP : + VIDTYPE_INTERLACE_BOTTOM; + } + + vf->type |= VIDTYPE_VIU_NV21; + vf->duration >>= 1; + vf->duration_pulldown = 0; + vf->signal_type = video_signal_from_vui; + vf->index = buffer_index; + vf->pts = 0; + vf->pts_us64 = 0; + vf->canvas0Addr = vf->canvas1Addr = + spec2canvas(&buffer_spec[buffer_index]); + vf->type_original = vf->type; + vfbuf_use[buffer_index]++; + + p_last_vf = vf; + vf->ready_jiffies64 = jiffies_64; + + kfifo_put(&delay_display_q, + (const struct vframe_s *)vf); + } + } + + WRITE_VREG(AV_SCRATCH_0, 0); + } else if ((cpu_cmd & 0xff) == 3) { + vh264_running = 1; + vh264_stream_switching_state = SWITCHING_STATE_ON_CMD3; + + pr_info("Enter switching mode cmd3.\n"); + schedule_work(&stream_switching_work); + + } else if ((cpu_cmd & 0xff) == 4) { + vh264_running = 1; + /* reserved for slice group */ + WRITE_VREG(AV_SCRATCH_0, 0); + } else if ((cpu_cmd & 0xff) == 5) { + vh264_running = 1; + /* reserved for slice group */ + WRITE_VREG(AV_SCRATCH_0, 0); + } else if ((cpu_cmd & 0xff) == 6) { + vh264_running = 0; + fatal_error_flag = DECODER_FATAL_ERROR_UNKNOWN; + /* this is fatal error, need restart */ + pr_info("fatal error happened\n"); + if (!fatal_error_reset) + schedule_work(&error_wd_work); + } else if ((cpu_cmd & 0xff) == 7) { + vh264_running = 0; + frame_width = (READ_VREG(AV_SCRATCH_1) + 1) * 16; + pr_info("Over decoder supported size, width = %d\n", + frame_width); + fatal_error_flag = DECODER_FATAL_ERROR_SIZE_OVERFLOW; + } else if ((cpu_cmd & 0xff) == 8) { + vh264_running = 0; + frame_height = (READ_VREG(AV_SCRATCH_1) + 1) * 16; + pr_info("Over decoder supported size, height = %d\n", + frame_height); + fatal_error_flag = DECODER_FATAL_ERROR_SIZE_OVERFLOW; + } else if ((cpu_cmd & 0xff) == 9) { + first_offset = READ_VREG(AV_SCRATCH_1); + if (pts_lookup_offset_us64 + (PTS_TYPE_VIDEO, first_offset, &first_pts, 0, + &first_pts64) == 0) + first_pts_cached = true; + WRITE_VREG(AV_SCRATCH_0, 0); + + } else if ((cpu_cmd & 0xff) == 0xa) { + int b_offset = READ_VREG(AV_SCRATCH_2); + + buffer_index = READ_VREG(AV_SCRATCH_1); + /*pr_info("iponly output %d b_offset %x\n", + * buffer_index,b_offset); + */ + if (kfifo_get(&newframe_q, &vf) == 0) { + WRITE_VREG(AV_SCRATCH_0, 0); + pr_info + ("fatal error, no available buffer slot."); + return IRQ_HANDLED; + } + if (pts_lookup_offset_us64 (PTS_TYPE_VIDEO, b_offset, + &pts, 0, &pts_us64) != 0) + vf->pts_us64 = vf->pts = 0; + else { + vf->pts_us64 = pts_us64; + vf->pts = pts; + } + + set_frame_info(vf); + vf->type = VIDTYPE_PROGRESSIVE | + VIDTYPE_VIU_FIELD | + VIDTYPE_VIU_NV21; + vf->duration_pulldown = 0; + vf->signal_type = video_signal_from_vui; + vf->index = buffer_index; + vf->canvas0Addr = vf->canvas1Addr = + spec2canvas(&buffer_spec[buffer_index]); + vf->type_original = vf->type; + vfbuf_use[buffer_index]++; + p_last_vf = vf; + pts_discontinue = false; + iponly_early_mode = 1; + kfifo_put(&delay_display_q, + (const struct vframe_s *)vf); + WRITE_VREG(AV_SCRATCH_0, 0); + } + + sei_itu35_flags = READ_VREG(AV_SCRATCH_J); + if (sei_itu35_flags & (1 << 15)) { /* data ready */ + /* int ltemp; */ + /* unsigned char *daddr; */ + unsigned int sei_itu35_wp = (sei_itu35_flags >> 16) & 0xffff; + unsigned int sei_itu35_data_length = sei_itu35_flags & 0x7fff; + struct userdata_poc_info_t user_data_poc; + +#if 0 + /* dump lmem for debug */ + WRITE_VREG(0x301, 0x8000); + WRITE_VREG(0x31d, 0x2); + for (ltemp = 0; ltemp < 64; ltemp++) { + laddr = 0x20 + ltemp; + WRITE_VREG(0x31b, laddr); + pr_info("mem 0x%x data 0x%x\n", laddr, + READ_VREG(0x31c) & 0xffff); + } +#endif +#if 0 + for (ltemp = 0; ltemp < sei_itu35_wp; ltemp++) { + daddr = + (unsigned char *)phys_to_virt( + sei_data_buffer_phys + + ltemp); + /* daddr = (unsigned char *)(sei_data_buffer + + * ltemp); + */ + pr_info("0x%x\n", *daddr); + } +#endif + /* pr_info("pocinfo 0x%x, top poc %d, wp 0x%x, length %d\n", + * READ_VREG(AV_SCRATCH_L), READ_VREG(AV_SCRATCH_M), + * sei_itu35_wp, sei_itu35_data_length); + */ + user_data_poc.poc_info = READ_VREG(AV_SCRATCH_L); + user_data_poc.poc_number = READ_VREG(AV_SCRATCH_M); + set_userdata_poc(user_data_poc); + WRITE_VREG(AV_SCRATCH_J, 0); + wakeup_userdata_poll(sei_itu35_wp, + (unsigned long)sei_data_buffer, + USER_DATA_SIZE, sei_itu35_data_length); + } +#ifdef HANDLE_H264_IRQ + return IRQ_HANDLED; +#else + return; +#endif +} + +static void vh264_put_timer_func(unsigned long arg) +{ + struct timer_list *timer = (struct timer_list *)arg; + unsigned int wait_buffer_status; + unsigned int wait_i_pass_frames; + unsigned int reg_val; + + enum receviver_start_e state = RECEIVER_INACTIVE; + + if (vh264_reset) { + pr_info("operation forbidden in timer !\n"); + goto exit; + } + + prepare_display_q(); + + if (vf_get_receiver(PROVIDER_NAME)) { + state = + vf_notify_receiver(PROVIDER_NAME, + VFRAME_EVENT_PROVIDER_QUREY_STATE, + NULL); + if ((state == RECEIVER_STATE_NULL) + || (state == RECEIVER_STATE_NONE)) { + /* receiver has no event_cb or receiver's + * event_cb does not process this event + */ + state = RECEIVER_INACTIVE; + } + } else + state = RECEIVER_INACTIVE; +#ifndef HANDLE_H264_IRQ + vh264_isr(); +#endif + + if (vh264_stream_switching_state != SWITCHING_STATE_OFF) + wait_buffer_counter = 0; + else { + reg_val = READ_VREG(AV_SCRATCH_9); + wait_buffer_status = reg_val & (1 << 31); + wait_i_pass_frames = reg_val & 0xff; + if (wait_buffer_status) { + if (kfifo_is_empty(&display_q) && + kfifo_is_empty(&delay_display_q) && + kfifo_is_empty(&recycle_q) && + (state == RECEIVER_INACTIVE)) { + pr_info("$$$$decoder is waiting for buffer\n"); + if (++wait_buffer_counter > 4) { + amvdec_stop(); + +#ifdef CONFIG_POST_PROCESS_MANAGER + vh264_ppmgr_reset(); +#else + vf_light_unreg_provider(&vh264_vf_prov); + vh264_local_init(); + vf_reg_provider(&vh264_vf_prov); +#endif + vh264_prot_init(); + amvdec_start(); + } + } else + wait_buffer_counter = 0; + } else if (wait_i_pass_frames > 1000) { + pr_info("i passed frames > 1000\n"); + amvdec_stop(); +#ifdef CONFIG_POST_PROCESS_MANAGER + vh264_ppmgr_reset(); +#else + vf_light_unreg_provider(&vh264_vf_prov); + vh264_local_init(); + vf_reg_provider(&vh264_vf_prov); +#endif + vh264_prot_init(); + amvdec_start(); + } + } + +#if 0 + if (!wait_buffer_status) { + if (vh264_no_disp_count++ > NO_DISP_WD_COUNT) { + pr_info("$$$decoder did not send frame out\n"); + amvdec_stop(); +#ifdef CONFIG_POST_PROCESS_MANAGER + vh264_ppmgr_reset(); +#else + vf_light_unreg_provider(PROVIDER_NAME); + vh264_local_init(); + vf_reg_provider(vh264_vf_prov); +#endif + vh264_prot_init(); + amvdec_start(); + + vh264_no_disp_count = 0; + vh264_no_disp_wd_count++; + } + } +#endif + + while (!kfifo_is_empty(&recycle_q) && + ((READ_VREG(AV_SCRATCH_7) == 0) + || (READ_VREG(AV_SCRATCH_8) == 0)) + && (vh264_stream_switching_state == SWITCHING_STATE_OFF)) { + struct vframe_s *vf; + + if (kfifo_get(&recycle_q, &vf)) { + if ((vf->index >= 0) && (vf->index < VF_BUF_NUM)) { + if (--vfbuf_use[vf->index] == 0) { + if (READ_VREG(AV_SCRATCH_7) == 0) { + WRITE_VREG(AV_SCRATCH_7, + vf->index + 1); + } else { + WRITE_VREG(AV_SCRATCH_8, + vf->index + 1); + } + } + + vf->index = VF_BUF_NUM; + kfifo_put(&newframe_q, + (const struct vframe_s *)vf); + } + } + } + + if (vh264_stream_switching_state != SWITCHING_STATE_OFF) { + while (!kfifo_is_empty(&recycle_q)) { + struct vframe_s *vf; + + if (kfifo_get(&recycle_q, &vf)) { + if ((vf->index >= 0 && + (vf->index < VF_BUF_NUM))) { + vf->index = VF_BUF_NUM; + kfifo_put(&newframe_q, + (const struct vframe_s *)vf); + } + } + } + + WRITE_VREG(AV_SCRATCH_7, 0); + WRITE_VREG(AV_SCRATCH_8, 0); + + if (kfifo_len(&newframe_q) == VF_POOL_SIZE) + stream_switching_done(); + } + + if (ucode_type != UCODE_IP_ONLY_PARAM && + (clk_adj_frame_count > VDEC_CLOCK_ADJUST_FRAME) && + frame_dur > 0 && saved_resolution != + frame_width * frame_height * (96000 / frame_dur)) { + int fps = 96000 / frame_dur; + + if (frame_dur < 10) /*dur is too small ,think it errors fps*/ + fps = 60; + saved_resolution = frame_width * frame_height * fps; + vdec_source_changed(VFORMAT_H264, + frame_width, frame_height, fps); + } +exit: + timer->expires = jiffies + PUT_INTERVAL; + + add_timer(timer); +} + +int vh264_dec_status(struct vdec_s *vdec, struct vdec_status *vstatus) +{ + vstatus->width = frame_width; + vstatus->height = frame_height; + if (frame_dur != 0) + vstatus->fps = 96000 / frame_dur; + else + vstatus->fps = -1; + vstatus->error_count = READ_VREG(AV_SCRATCH_D); + vstatus->status = stat; + if (fatal_error_reset) + vstatus->status |= fatal_error_flag; + return 0; +} + +int vh264_set_trickmode(struct vdec_s *vdec, unsigned long trickmode) +{ + if (trickmode == TRICKMODE_I) { + WRITE_VREG(AV_SCRATCH_F, + (READ_VREG(AV_SCRATCH_F) & 0xfffffffc) | 2); + trickmode_i = 1; + } else if (trickmode == TRICKMODE_NONE) { + WRITE_VREG(AV_SCRATCH_F, READ_VREG(AV_SCRATCH_F) & 0xfffffffc); + trickmode_i = 0; + } + + return 0; +} + +static void vh264_prot_init(void) +{ + + while (READ_VREG(DCAC_DMA_CTRL) & 0x8000) + ; + while (READ_VREG(LMEM_DMA_CTRL) & 0x8000) + ; /* reg address is 0x350 */ + +#if 1 /* MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON6 */ + WRITE_VREG(DOS_SW_RESET0, (1 << 7) | (1 << 6) | (1 << 4)); + WRITE_VREG(DOS_SW_RESET0, 0); + + READ_VREG(DOS_SW_RESET0); + READ_VREG(DOS_SW_RESET0); + READ_VREG(DOS_SW_RESET0); + + WRITE_VREG(DOS_SW_RESET0, (1 << 7) | (1 << 6) | (1 << 4)); + WRITE_VREG(DOS_SW_RESET0, 0); + + WRITE_VREG(DOS_SW_RESET0, (1 << 9) | (1 << 8)); + WRITE_VREG(DOS_SW_RESET0, 0); + + READ_VREG(DOS_SW_RESET0); + READ_VREG(DOS_SW_RESET0); + READ_VREG(DOS_SW_RESET0); + +#else + WRITE_MPEG_REG(RESET0_REGISTER, + RESET_IQIDCT | RESET_MC | RESET_VLD_PART); + READ_MPEG_REG(RESET0_REGISTER); + WRITE_MPEG_REG(RESET0_REGISTER, + RESET_IQIDCT | RESET_MC | RESET_VLD_PART); + + WRITE_MPEG_REG(RESET2_REGISTER, RESET_PIC_DC | RESET_DBLK); +#endif + + WRITE_VREG(POWER_CTL_VLD, + READ_VREG(POWER_CTL_VLD) | + (0 << 10) | (1 << 9) | (1 << 6)); + + /* disable PSCALE for hardware sharing */ + WRITE_VREG(PSCALE_CTRL, 0); + + WRITE_VREG(AV_SCRATCH_0, 0); + WRITE_VREG(AV_SCRATCH_1, buf_offset); + WRITE_VREG(AV_SCRATCH_G, mc_dma_handle); + WRITE_VREG(AV_SCRATCH_7, 0); + WRITE_VREG(AV_SCRATCH_8, 0); + WRITE_VREG(AV_SCRATCH_9, 0); + + error_recovery_mode_use = + (error_recovery_mode != + 0) ? error_recovery_mode : error_recovery_mode_in; + WRITE_VREG(AV_SCRATCH_F, + (READ_VREG(AV_SCRATCH_F) & 0xffffffc3) | + (READ_VREG(AV_SCRATCH_F) & 0xffffff43) | + ((error_recovery_mode_use & 0x1) << 4)); + if (dec_control & DEC_CONTROL_FLAG_DISABLE_FAST_POC) + SET_VREG_MASK(AV_SCRATCH_F, 1 << 7); + /* clear mailbox interrupt */ + WRITE_VREG(ASSIST_MBOX1_CLR_REG, 1); + + /* enable mailbox interrupt */ + WRITE_VREG(ASSIST_MBOX1_MASK, 1); + + SET_VREG_MASK(MDEC_PIC_DC_CTRL, 1 << 17); + if (ucode_type == UCODE_IP_ONLY_PARAM) + SET_VREG_MASK(AV_SCRATCH_F, 1 << 6); + else + CLEAR_VREG_MASK(AV_SCRATCH_F, 1 << 6); + + WRITE_VREG(AV_SCRATCH_I, (u32)(sei_data_buffer_phys - buf_offset)); + WRITE_VREG(AV_SCRATCH_J, 0); + /* #if MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON8 */ + if ((get_cpu_type() >= MESON_CPU_MAJOR_ID_M8) && !is_meson_mtvd_cpu()) { + /* pr_info("vh264 meson8 prot init\n"); */ + WRITE_VREG(MDEC_PIC_DC_THRESH, 0x404038aa); + } + /* #endif */ +} + +static void vh264_local_init(void) +{ + int i; + + vh264_ratio = vh264_amstream_dec_info.ratio; + /* vh264_ratio = 0x100; */ + + vh264_rotation = (((unsigned long) vh264_amstream_dec_info.param) + >> 16) & 0xffff; + + frame_buffer_size = AVIL_DPB_BUFF_SIZE + buf_size - DEFAULT_MEM_SIZE; + frame_prog = 0; + frame_width = vh264_amstream_dec_info.width; + frame_height = vh264_amstream_dec_info.height; + frame_dur = vh264_amstream_dec_info.rate; + pts_outside = ((unsigned long) vh264_amstream_dec_info.param) & 0x01; + sync_outside = ((unsigned long) vh264_amstream_dec_info.param & 0x02) + >> 1; + use_idr_framerate = ((unsigned long) vh264_amstream_dec_info.param + & 0x04) >> 2; + max_refer_buf = !(((unsigned long) vh264_amstream_dec_info.param + & 0x10) >> 4); + + pr_info + ("H264 sysinfo: %dx%d duration=%d, pts_outside=%d, ", + frame_width, frame_height, frame_dur, pts_outside); + pr_debug("sync_outside=%d, use_idr_framerate=%d\n", + sync_outside, use_idr_framerate); + + if ((unsigned long) vh264_amstream_dec_info.param & 0x08) + ucode_type = UCODE_IP_ONLY_PARAM; + else + ucode_type = 0; + + if ((unsigned long) vh264_amstream_dec_info.param & 0x20) + error_recovery_mode_in = 1; + else + error_recovery_mode_in = 3; + + if (!vh264_running) { + last_mb_width = 0; + last_mb_height = 0; + } + + for (i = 0; i < VF_BUF_NUM; i++) + vfbuf_use[i] = 0; + + INIT_KFIFO(display_q); + INIT_KFIFO(delay_display_q); + INIT_KFIFO(recycle_q); + INIT_KFIFO(newframe_q); + + for (i = 0; i < VF_POOL_SIZE; i++) { + const struct vframe_s *vf = &vfpool[i]; + + vfpool[i].index = VF_BUF_NUM; + vfpool[i].bufWidth = 1920; + kfifo_put(&newframe_q, vf); + } + +#ifdef DROP_B_FRAME_FOR_1080P_50_60FPS + last_interlaced = 1; +#endif + h264_first_pts_ready = 0; + h264_first_valid_pts_ready = false; + h264pts1 = 0; + h264pts2 = 0; + h264_pts_count = 0; + duration_from_pts_done = 0; + vh264_error_count = READ_VREG(AV_SCRATCH_D); + + p_last_vf = NULL; + check_pts_discontinue = false; + last_pts = 0; + wait_buffer_counter = 0; + vh264_no_disp_count = 0; + fatal_error_flag = 0; + vh264_stream_switching_state = SWITCHING_STATE_OFF; +#ifdef DEBUG_PTS + pts_missed = 0; + pts_hit = 0; +#endif + pts_discontinue = false; + no_idr_error_count = 0; +} + +static s32 vh264_init(void) +{ + int trickmode_fffb = 0; + int firmwareloaded = 0; + int i; + + /* pr_info("\nvh264_init\n"); */ + init_timer(&recycle_timer); + + stat |= STAT_TIMER_INIT; + + vh264_running = 0;/* init here to reset last_mb_width&last_mb_height */ + vh264_eos = 0; + duration_on_correcting = 0; + first_pts = 0; + first_pts64 = 0; + first_offset = 0; + first_pts_cached = false; + fixed_frame_rate_check_count = 0; + saved_resolution = 0; + iponly_early_mode = 0; + vh264_local_init(); + + query_video_status(0, &trickmode_fffb); + +#if 0 + if (!trickmode_fffb) { + void __iomem *p = + ioremap_nocache(ucode_map_start, V_BUF_ADDR_OFFSET); + if (p != NULL) { + memset(p, 0, V_BUF_ADDR_OFFSET); + iounmap(p); + } + } +#endif + + amvdec_enable(); + + /* -- ucode loading (amrisc and swap code) */ + mc_cpu_addr = + dma_alloc_coherent(amports_get_dma_device(), MC_TOTAL_SIZE, + &mc_dma_handle, GFP_KERNEL); + if (!mc_cpu_addr) { + amvdec_disable(); + + pr_err("vh264_init: Can not allocate mc memory.\n"); + return -ENOMEM; + } + + pr_debug("264 ucode swap area: phyaddr %p, cpu vaddr %p\n", + (void *)mc_dma_handle, mc_cpu_addr); + if (debugfirmware) { + int r0, r1, r2, r3, r4, r5; + char firmwarename[32]; + + pr_debug("start load debug %d firmware ...\n", debugfirmware); + + snprintf(firmwarename, 32, "%s%d", "vh264_mc", debugfirmware); + r0 = amvdec_loadmc_ex(VFORMAT_H264, firmwarename, NULL); + +#define DEBUGGET_FW(t, name, buf, size, ret)\ + do {\ + snprintf(firmwarename, 32, "%s%d", name,\ + debugfirmware);\ + ret = get_decoder_firmware_data(t,\ + firmwarename, buf, size);\ + } while (0) + /*memcpy((u8 *) mc_cpu_addr + MC_OFFSET_HEADER, vh264_header_mc, + *MC_SWAP_SIZE); + */ + DEBUGGET_FW(VFORMAT_H264, "vh264_header_mc", + (u8 *) mc_cpu_addr + MC_OFFSET_HEADER, + MC_SWAP_SIZE, r1); + + /*memcpy((u8 *) mc_cpu_addr + MC_OFFSET_DATA, vh264_data_mc, + *MC_SWAP_SIZE); + */ + DEBUGGET_FW(VFORMAT_H264, "vh264_data_mc", + (u8 *) mc_cpu_addr + MC_OFFSET_DATA, MC_SWAP_SIZE, r2); + /*memcpy((u8 *) mc_cpu_addr + MC_OFFSET_MMCO, vh264_mmco_mc, + *MC_SWAP_SIZE); + */ + DEBUGGET_FW(VFORMAT_H264, "vh264_mmco_mc", + (u8 *) mc_cpu_addr + MC_OFFSET_MMCO, MC_SWAP_SIZE, r3); + /*memcpy((u8 *) mc_cpu_addr + MC_OFFSET_LIST, vh264_list_mc, + *MC_SWAP_SIZE); + */ + DEBUGGET_FW(VFORMAT_H264, "vh264_list_mc", + (u8 *) mc_cpu_addr + MC_OFFSET_LIST, MC_SWAP_SIZE, r4); + /*memcpy((u8 *) mc_cpu_addr + MC_OFFSET_SLICE, vh264_slice_mc, + *MC_SWAP_SIZE); + */ + DEBUGGET_FW(VFORMAT_H264, "vh264_slice_mc", + (u8 *) mc_cpu_addr + MC_OFFSET_SLICE, MC_SWAP_SIZE, r5); + + if (r0 < 0 || r1 < 0 || r2 < 0 || r3 < 0 || r4 < 0 || r5 < 0) { + pr_err("264 load debugfirmware err %d,%d,%d,%d,%d,%d\n", + r0, r1, r2, r3, r4, r5); + amvdec_disable(); + if (mc_cpu_addr) { + dma_free_coherent(amports_get_dma_device(), + MC_TOTAL_SIZE, mc_cpu_addr, + mc_dma_handle); + mc_cpu_addr = NULL; + } + return -EBUSY; + } + firmwareloaded = 1; + } else { + int ret = -1, size = -1; + unsigned int cpu = get_cpu_type(); + char *buf = vmalloc(0x1000 * 8); + + switch (cpu) { + case MESON_CPU_MAJOR_ID_GXTVBB: + case MESON_CPU_MAJOR_ID_GXL: + size = get_firmware_data("gxl_h264_all", buf); + if (size < 0) { + pr_err("get firmware fail."); + vfree(buf); + return -1; + } + + ret = amvdec_loadmc_ex(VFORMAT_H264, + "gxl_h264_all", buf); + memcpy((u8 *) mc_cpu_addr + MC_OFFSET_HEADER, + buf + 0x4000, MC_SWAP_SIZE); + memcpy((u8 *) mc_cpu_addr + MC_OFFSET_DATA, + buf + 0x2000, MC_SWAP_SIZE); + memcpy((u8 *) mc_cpu_addr + MC_OFFSET_MMCO, + buf + 0x6000, MC_SWAP_SIZE); + memcpy((u8 *) mc_cpu_addr + MC_OFFSET_LIST, + buf + 0x3000, MC_SWAP_SIZE); + memcpy((u8 *) mc_cpu_addr + MC_OFFSET_SLICE, + buf + 0x5000, MC_SWAP_SIZE); + break; + + default: + pr_err("invalid cpu type 0x%x.\n", cpu); + vfree(buf); + return -1; + } + + vfree(buf); + + if (ret < 0) { + pr_err("h264 load orignal firmware error %d.\n", ret); + amvdec_disable(); + if (mc_cpu_addr) { + dma_free_coherent(amports_get_dma_device(), + MC_TOTAL_SIZE, mc_cpu_addr, + mc_dma_handle); + mc_cpu_addr = NULL; + } + return -EBUSY; + } + } + + stat |= STAT_MC_LOAD; + + for (i = 0; i < ARRAY_SIZE(fense_buffer_spec); i++) { + struct buffer_spec_s *s = &fense_buffer_spec[i]; + + if (!codec_mm_enough_for_size(3 * SZ_1M, 1)) + return -ENOMEM; + + s->alloc_count = 3 * SZ_1M / PAGE_SIZE; + s->phy_addr = codec_mm_alloc_for_dma(MEM_NAME, + s->alloc_count, + 4 + PAGE_SHIFT, + CODEC_MM_FLAGS_CMA_CLEAR | CODEC_MM_FLAGS_FOR_VDECODER); + s->y_canvas_index = 2 * i; + s->u_canvas_index = 2 * i + 1; + s->v_canvas_index = 2 * i + 1; + } + + /* enable AMRISC side protocol */ + vh264_prot_init(); + +#ifdef HANDLE_H264_IRQ + /*TODO irq */ + + if (vdec_request_irq(VDEC_IRQ_1, vh264_isr, + "vh264-irq", (void *)vh264_dec_id)) { + pr_err("vh264 irq register error.\n"); + amvdec_disable(); + return -ENOENT; + } +#endif + + stat |= STAT_ISR_REG; + +#ifdef CONFIG_POST_PROCESS_MANAGER + vf_provider_init(&vh264_vf_prov, PROVIDER_NAME, &vh264_vf_provider_ops, + NULL); + vf_reg_provider(&vh264_vf_prov); + vf_notify_receiver(PROVIDER_NAME, VFRAME_EVENT_PROVIDER_START, NULL); +#else + vf_provider_init(&vh264_vf_prov, PROVIDER_NAME, &vh264_vf_provider_ops, + NULL); + vf_reg_provider(&vh264_vf_prov); +#endif + + vf_notify_receiver(PROVIDER_NAME, VFRAME_EVENT_PROVIDER_FR_HINT, + (void *)((unsigned long)frame_dur)); + + stat |= STAT_VF_HOOK; + + recycle_timer.data = (ulong) &recycle_timer; + recycle_timer.function = vh264_put_timer_func; + recycle_timer.expires = jiffies + PUT_INTERVAL; + + add_timer(&recycle_timer); + + stat |= STAT_TIMER_ARM; + + vh264_stream_switching_state = SWITCHING_STATE_OFF; + + stat |= STAT_VDEC_RUN; + wmb(); /* Ensure fetchbuf contents visible */ + + /* -- start decoder */ + amvdec_start(); + + init_userdata_fifo(); + + return 0; +} + +static int vh264_stop(int mode) +{ + int i; + + if (stat & STAT_VDEC_RUN) { + amvdec_stop(); + stat &= ~STAT_VDEC_RUN; + } + + if (stat & STAT_ISR_REG) { + WRITE_VREG(ASSIST_MBOX1_MASK, 0); + /*TODO irq */ + + vdec_free_irq(VDEC_IRQ_1, (void *)vh264_dec_id); + + stat &= ~STAT_ISR_REG; + } + + if (stat & STAT_TIMER_ARM) { + del_timer_sync(&recycle_timer); + stat &= ~STAT_TIMER_ARM; + } + + if (stat & STAT_VF_HOOK) { + if (mode == MODE_FULL) { + vf_notify_receiver(PROVIDER_NAME, + VFRAME_EVENT_PROVIDER_FR_END_HINT, + NULL); + } + + vf_unreg_provider(&vh264_vf_prov); + stat &= ~STAT_VF_HOOK; + } + + if (stat & STAT_MC_LOAD) { + if (mc_cpu_addr != NULL) { + dma_free_coherent(amports_get_dma_device(), + MC_TOTAL_SIZE, mc_cpu_addr, + mc_dma_handle); + mc_cpu_addr = NULL; + } + } + if (sei_data_buffer != NULL) { + dma_free_coherent( + amports_get_dma_device(), + USER_DATA_SIZE, + sei_data_buffer, + sei_data_buffer_phys); + sei_data_buffer = NULL; + sei_data_buffer_phys = 0; + } + amvdec_disable(); + + for (i = 0; i < ARRAY_SIZE(fense_buffer_spec); i++) { + if (fense_buffer_spec[i].phy_addr) { + codec_mm_free_for_dma(MEM_NAME, + fense_buffer_spec[i].phy_addr); + fense_buffer_spec[i].phy_addr = 0; + fense_buffer_spec[i].alloc_count = 0; + } + } + + for (i = 0; i < ARRAY_SIZE(buffer_spec); i++) { + if (buffer_spec[i].phy_addr) { + if (is_4k && !get_blackout_policy()) + pr_info("Skip releasing CMA buffer %d\n", i); + else { + codec_mm_free_for_dma(MEM_NAME, + buffer_spec[i].phy_addr); + buffer_spec[i].phy_addr = 0; + buffer_spec[i].alloc_count = 0; + } + } + } + return 0; +} + +static void error_do_work(struct work_struct *work) +{ + mutex_lock(&vh264_mutex); + + /* + * we need to lock vh264_stop/vh264_init. + * because we will call amvdec_h264_remove on this step; + * then we may call more than once on + * free_irq/deltimer/..and some other. + */ + if (atomic_read(&vh264_active)) { + amvdec_stop(); + vh264_reset = 1; +#ifdef CONFIG_POST_PROCESS_MANAGER + vh264_ppmgr_reset(); +#else + vf_light_unreg_provider(&vh264_vf_prov); + + vh264_local_init(); + + vf_reg_provider(&vh264_vf_prov); +#endif + msleep(30); + vh264_local_init(); + vh264_prot_init(); + + amvdec_start(); + vh264_reset = 0; + } + + mutex_unlock(&vh264_mutex); +} + +static void stream_switching_done(void) +{ + int state = vh264_stream_switching_state; + + WRITE_VREG(AV_SCRATCH_7, 0); + WRITE_VREG(AV_SCRATCH_8, 0); + WRITE_VREG(AV_SCRATCH_9, 0); + + if (state == SWITCHING_STATE_ON_CMD1) { + pr_info("Enter set parameter cmd1 switching_state %x.\n", + vh264_stream_switching_state); + schedule_work(&set_parameter_work); + return; + } else if (state == SWITCHING_STATE_ON_CMD1_PENDING) + return; + + vh264_stream_switching_state = SWITCHING_STATE_OFF; + + wmb(); /* Ensure fetchbuf contents visible */ + + if (state == SWITCHING_STATE_ON_CMD3) + WRITE_VREG(AV_SCRATCH_0, 0); + + pr_info("Leaving switching mode.\n"); +} + +/* construt a new frame as a copy of last frame so frame receiver can + * release all buffer resources to decoder. + */ +static void stream_switching_do(struct work_struct *work) +{ + int mb_total_num, mb_width_num, mb_height_num, i = 0; + struct vframe_s *vf = NULL; + u32 y_index, u_index, src_index, des_index, y_desindex, u_desindex; + struct canvas_s csy, csu, cyd; + unsigned long flags; + bool delay = true; + + if (!atomic_read(&vh264_active)) + return; + + if (vh264_stream_switching_state == SWITCHING_STATE_OFF) + return; + + spin_lock_irqsave(&prepare_lock, flags); + + block_display_q = true; + + spin_unlock_irqrestore(&prepare_lock, flags); + + mb_total_num = mb_total; + mb_width_num = mb_width; + mb_height_num = mb_height; + + while (is_4k || kfifo_len(&delay_display_q) > 2) { + if (kfifo_get(&delay_display_q, &vf)) { + kfifo_put(&display_q, + (const struct vframe_s *)vf); + vf_notify_receiver(PROVIDER_NAME, + VFRAME_EVENT_PROVIDER_VFRAME_READY, NULL); + } else + break; + } + + if (!kfifo_get(&delay_display_q, &vf)) { + vf = p_last_vf; + delay = false; + } + + while (vf) { + int buffer_index; + + buffer_index = vf->index & 0xff; + + /* construct a clone of the frame from last frame */ +#if 0 + pr_info("src yaddr[0x%x] index[%d] width[%d] heigth[%d]\n", + buffer_spec[buffer_index].y_addr, + buffer_spec[buffer_index].y_canvas_index, + buffer_spec[buffer_index].y_canvas_width, + buffer_spec[buffer_index].y_canvas_height); + + pr_info("src uaddr[0x%x] index[%d] width[%d] heigth[%d]\n", + buffer_spec[buffer_index].u_addr, + buffer_spec[buffer_index].u_canvas_index, + buffer_spec[buffer_index].u_canvas_width, + buffer_spec[buffer_index].u_canvas_height); +#endif + if (!is_4k) { + y_index = buffer_spec[buffer_index].y_canvas_index; + u_index = buffer_spec[buffer_index].u_canvas_index; + + canvas_read(y_index, &csy); + canvas_read(u_index, &csu); + + canvas_config(fense_buffer_spec[i].y_canvas_index, + fense_buffer_spec[i].phy_addr, + mb_width_num << 4, mb_height_num << 4, + CANVAS_ADDR_NOWRAP, + CANVAS_BLKMODE_LINEAR); + canvas_config(fense_buffer_spec[i].u_canvas_index, + fense_buffer_spec[i].phy_addr + + (mb_total_num << 8), + mb_width_num << 4, mb_height_num << 3, + CANVAS_ADDR_NOWRAP, + CANVAS_BLKMODE_LINEAR); + + y_desindex = fense_buffer_spec[i].y_canvas_index; + u_desindex = fense_buffer_spec[i].u_canvas_index; + + canvas_read(y_desindex, &cyd); + + src_index = ((y_index & 0xff) | + ((u_index << 8) & 0x0000ff00)); + des_index = ((y_desindex & 0xff) | + ((u_desindex << 8) & 0x0000ff00)); +#if 0 + ge2d_canvas_dup(&csy, &csu, &cyd, + GE2D_FORMAT_M24_NV21, + src_index, + des_index); +#endif/*mask*/ + } + fense_vf[i] = *vf; + fense_vf[i].index = -1; + + if (!is_4k) + fense_vf[i].canvas0Addr = + spec2canvas(&fense_buffer_spec[i]); + else + fense_vf[i].flag |= VFRAME_FLAG_SWITCHING_FENSE; + + /* send clone to receiver */ + kfifo_put(&display_q, + (const struct vframe_s *)&fense_vf[i]); + + /* early recycle frames for last session */ + if (delay) + vh264_vf_put(vf, NULL); + + vf_notify_receiver(PROVIDER_NAME, + VFRAME_EVENT_PROVIDER_VFRAME_READY, NULL); + + i++; + + if (!kfifo_get(&delay_display_q, &vf)) + break; + } + + block_display_q = false; + + pr_info("Switching fense frame post\n"); +} + +static int amvdec_h264_probe(struct platform_device *pdev) +{ + struct vdec_s *pdata = *(struct vdec_s **)pdev->dev.platform_data; + + mutex_lock(&vh264_mutex); + + if (pdata == NULL) { + pr_info("\namvdec_h264 memory resource undefined.\n"); + mutex_unlock(&vh264_mutex); + return -EFAULT; + } + + ucode_map_start = pdata->mem_start; + buf_size = pdata->mem_end - pdata->mem_start + 1; + if (buf_size < DEFAULT_MEM_SIZE) { + pr_info("\namvdec_h264 memory size not enough.\n"); + mutex_unlock(&vh264_mutex); + return -ENOMEM; + } + + buf_offset = pdata->mem_start - DEF_BUF_START_ADDR; + if (get_cpu_type() >= MESON_CPU_MAJOR_ID_GXTVBB) + buf_start = V_BUF_ADDR_OFFSET_NEW + pdata->mem_start; + else + buf_start = V_BUF_ADDR_OFFSET + pdata->mem_start; + buf_end = pdata->mem_end; + if (pdata->sys_info) + vh264_amstream_dec_info = *pdata->sys_info; + if (sei_data_buffer == NULL) { + sei_data_buffer = + dma_alloc_coherent(amports_get_dma_device(), + USER_DATA_SIZE, + &sei_data_buffer_phys, GFP_KERNEL); + if (!sei_data_buffer) { + pr_info("%s: Can not allocate sei_data_buffer\n", + __func__); + mutex_unlock(&vh264_mutex); + return -ENOMEM; + } + /* pr_info("buffer 0x%x, phys 0x%x, remap 0x%x\n", + * sei_data_buffer, sei_data_buffer_phys, + * (u32)sei_data_buffer_remap); + */ + } + pr_debug("amvdec_h264 mem-addr=%lx,buff_offset=%x,buf_start=%lx buf_size %x\n", + pdata->mem_start, buf_offset, buf_start, buf_size); + pdata->dec_status = vh264_dec_status; + pdata->set_trickmode = vh264_set_trickmode; + + if (vh264_init() < 0) { + pr_info("\namvdec_h264 init failed.\n"); + mutex_unlock(&vh264_mutex); + return -ENODEV; + } + + INIT_WORK(&error_wd_work, error_do_work); + INIT_WORK(&stream_switching_work, stream_switching_do); + INIT_WORK(&set_parameter_work, vh264_set_params); + + atomic_set(&vh264_active, 1); + + mutex_unlock(&vh264_mutex); + + return 0; +} + +static int amvdec_h264_remove(struct platform_device *pdev) +{ + atomic_set(&vh264_active, 0); + cancel_work_sync(&set_parameter_work); + cancel_work_sync(&error_wd_work); + cancel_work_sync(&stream_switching_work); + mutex_lock(&vh264_mutex); + vh264_stop(MODE_FULL); + vdec_source_changed(VFORMAT_H264, 0, 0, 0); + atomic_set(&vh264_active, 0); +#ifdef DEBUG_PTS + pr_info + ("pts missed %ld, pts hit %ld, pts_outside %d, duration %d, ", + pts_missed, pts_hit, pts_outside, frame_dur); + pr_info("sync_outside %d, use_idr_framerate %d\n", + sync_outside, use_idr_framerate); +#endif + mutex_unlock(&vh264_mutex); + return 0; +} + +/****************************************/ + +static struct platform_driver amvdec_h264_driver = { + .probe = amvdec_h264_probe, + .remove = amvdec_h264_remove, +#ifdef CONFIG_PM + .suspend = amvdec_suspend, + .resume = amvdec_resume, +#endif + .driver = { + .name = DRIVER_NAME, + } +}; + +static struct codec_profile_t amvdec_h264_profile = { + .name = "h264", + .profile = "" +}; + +static int __init amvdec_h264_driver_init_module(void) +{ + pr_debug("amvdec_h264 module init\n"); + + /*ge2d_videoh264task_init();mask*/ + + if (platform_driver_register(&amvdec_h264_driver)) { + pr_err("failed to register amvdec_h264 driver\n"); + return -ENODEV; + } + if (get_cpu_type() >= MESON_CPU_MAJOR_ID_GXTVBB) + amvdec_h264_profile.profile = "4k"; + vcodec_profile_register(&amvdec_h264_profile); + return 0; +} + +static void __exit amvdec_h264_driver_remove_module(void) +{ + pr_debug("amvdec_h264 module remove.\n"); + + platform_driver_unregister(&amvdec_h264_driver); + + /*ge2d_videoh264task_release();mask*/ +} + +/****************************************/ + +module_param(stat, uint, 0664); +MODULE_PARM_DESC(stat, "\n amvdec_h264 stat\n"); +module_param(error_recovery_mode, uint, 0664); +MODULE_PARM_DESC(error_recovery_mode, "\n amvdec_h264 error_recovery_mode\n"); +module_param(sync_outside, uint, 0664); +MODULE_PARM_DESC(sync_outside, "\n amvdec_h264 sync_outside\n"); +module_param(dec_control, uint, 0664); +MODULE_PARM_DESC(dec_control, "\n amvdec_h264 decoder control\n"); +module_param(fatal_error_reset, uint, 0664); +MODULE_PARM_DESC(fatal_error_reset, + "\n amvdec_h264 decoder reset when fatal error happens\n"); +module_param(max_refer_buf, uint, 0664); +MODULE_PARM_DESC(max_refer_buf, + "\n amvdec_h264 dec buffering or not for reference frame\n"); +module_param(ucode_type, uint, 0664); +MODULE_PARM_DESC(ucode_type, + "\n amvdec_h264 dec buffering or not for reference frame\n"); +module_param(debugfirmware, uint, 0664); +MODULE_PARM_DESC(debugfirmware, "\n amvdec_h264 debug load firmware\n"); +module_param(fixed_frame_rate_flag, uint, 0664); +MODULE_PARM_DESC(fixed_frame_rate_flag, + "\n amvdec_h264 fixed_frame_rate_flag\n"); +module_param(decoder_debug_flag, uint, 0664); +MODULE_PARM_DESC(decoder_debug_flag, + "\n amvdec_h264 decoder_debug_flag\n"); + +module_param(decoder_force_reset, uint, 0664); +MODULE_PARM_DESC(decoder_force_reset, + "\n amvdec_h264 decoder force reset\n"); +module_param(no_idr_error_max, uint, 0664); +MODULE_PARM_DESC(no_idr_error_max, + "\n print no_idr_error_max\n"); + + +module_init(amvdec_h264_driver_init_module); +module_exit(amvdec_h264_driver_remove_module); + +MODULE_DESCRIPTION("AMLOGIC H264 Video Decoder Driver"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Chen Zhang <chen.zhang@amlogic.com>"); diff --git a/drivers/frame_provider/decoder/h264/vh264.h b/drivers/frame_provider/decoder/h264/vh264.h new file mode 100644 index 0000000..a8b0c62 --- a/dev/null +++ b/drivers/frame_provider/decoder/h264/vh264.h @@ -0,0 +1,27 @@ +/* + * drivers/amlogic/media/frame_provider/decoder/h264/vh264.h + * + * Copyright (C) 2016 Amlogic, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * +*/ + +#ifndef VH264_H +#define VH264_H + +extern int query_video_status(int type, int *value); + +/* extern s32 vh264_init(void); */ + +extern s32 vh264_release(void); + +#endif /* VMPEG4_H */ diff --git a/drivers/frame_provider/decoder/h265/Makefile b/drivers/frame_provider/decoder/h265/Makefile new file mode 100644 index 0000000..cc2cdc1 --- a/dev/null +++ b/drivers/frame_provider/decoder/h265/Makefile @@ -0,0 +1 @@ +obj-m += vh265.o diff --git a/drivers/frame_provider/decoder/h265/vh265.c b/drivers/frame_provider/decoder/h265/vh265.c new file mode 100644 index 0000000..dea958e --- a/dev/null +++ b/drivers/frame_provider/decoder/h265/vh265.c @@ -0,0 +1,8155 @@ +/* + * drivers/amlogic/amports/vh265.c + * + * Copyright (C) 2015 Amlogic, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * +*/ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/interrupt.h> +#include <linux/semaphore.h> +#include <linux/delay.h> +#include <linux/timer.h> +#include <linux/kfifo.h> +#include <linux/kthread.h> +#include <linux/platform_device.h> +#include <linux/amlogic/media/vfm/vframe.h> +#include <linux/amlogic/media/utils/amstream.h> +#include <linux/amlogic/media/utils/vformat.h> +#include <linux/amlogic/media/frame_sync/ptsserv.h> +#include <linux/amlogic/media/canvas/canvas.h> +#include <linux/amlogic/media/vfm/vframe.h> +#include <linux/amlogic/media/vfm/vframe_provider.h> +#include <linux/amlogic/media/vfm/vframe_receiver.h> +#include <linux/dma-mapping.h> +#include <linux/dma-contiguous.h> +#include <linux/slab.h> +#include "../../../stream_input/amports/amports_priv.h" +#include <linux/amlogic/media/codec_mm/codec_mm.h> +#include "../utils/decoder_mmu_box.h" + +/*#define HEVC_PIC_STRUCT_SUPPORT*/ +#define MULTI_INSTANCE_SUPPORT + + + +#define MMU_COMPRESS_HEADER_SIZE 0x48000 +#define MAX_FRAME_4K_NUM 0x1200 +#define FRAME_MMU_MAP_SIZE (MAX_FRAME_4K_NUM * 4) +#define H265_MMU_MAP_BUFFER HEVC_ASSIST_SCRATCH_7 +#define HEVC_CM_HEADER_START_ADDR 0x3628 +#define HEVC_SAO_MMU_VH1_ADDR 0x363b +#define HEVC_SAO_MMU_VH0_ADDR 0x363a +#define HEVC_SAO_MMU_STATUS 0x3639 + + + +#define MEM_NAME "codec_265" +/* #include <mach/am_regs.h> */ +#include <linux/amlogic/media/utils/vdec_reg.h> + +#include "../utils/vdec.h" +#include "../utils/amvdec.h" +#include <linux/amlogic/media/video_sink/video/video.h> + +#define SUPPORT_10BIT +/* #define ERROR_HANDLE_DEBUG */ +#if 0/*MESON_CPU_TYPE == MESON_CPU_TYPE_MESON8B*/ +#undef SUPPORT_4K2K +#else +#define SUPPORT_4K2K +#endif + +#ifndef STAT_KTHREAD +#define STAT_KTHREAD 0x40 +#endif + +#ifdef MULTI_INSTANCE_SUPPORT +#define MULTI_DRIVER_NAME "ammvdec_h265" +#endif +#define DRIVER_NAME "amvdec_h265" +#define MODULE_NAME "amvdec_h265" + +#define PUT_INTERVAL (HZ/100) +#define ERROR_SYSTEM_RESET_COUNT 200 + +#define PTS_NORMAL 0 +#define PTS_NONE_REF_USE_DURATION 1 + +#define PTS_MODE_SWITCHING_THRESHOLD 3 +#define PTS_MODE_SWITCHING_RECOVERY_THREASHOLD 3 + +#define DUR2PTS(x) ((x)*90/96) +#define HEVC_SIZE (4096*2304) + +static atomic_t cma_allocate; + +struct hevc_state_s; +static int hevc_print(struct hevc_state_s *hevc, + int debug_flag, const char *fmt, ...); +static int vh265_vf_states(struct vframe_states *states, void *); +static struct vframe_s *vh265_vf_peek(void *); +static struct vframe_s *vh265_vf_get(void *); +static void vh265_vf_put(struct vframe_s *, void *); +static int vh265_event_cb(int type, void *data, void *private_data); + +static int vh265_stop(struct hevc_state_s *hevc); +#ifdef MULTI_INSTANCE_SUPPORT +static int vmh265_stop(struct hevc_state_s *hevc); +static s32 vh265_init(struct vdec_s *vdec); +#else +static s32 vh265_init(struct hevc_state_s *hevc); +#endif +static void vh265_prot_init(struct hevc_state_s *hevc); +static int vh265_local_init(struct hevc_state_s *hevc); +static void vh265_put_timer_func(unsigned long arg); + +static const char vh265_dec_id[] = "vh265-dev"; + +#define PROVIDER_NAME "decoder.h265" +#define MULTI_INSTANCE_PROVIDER_NAME "vdec.h265" + +static const struct vframe_operations_s vh265_vf_provider = { + .peek = vh265_vf_peek, + .get = vh265_vf_get, + .put = vh265_vf_put, + .event_cb = vh265_event_cb, + .vf_states = vh265_vf_states, +}; + +static struct vframe_provider_s vh265_vf_prov; + +static u32 bit_depth_luma; +static u32 bit_depth_chroma; +static u32 video_signal_type; + + +#define VIDEO_SIGNAL_TYPE_AVAILABLE_MASK 0x20000000 + +static const char * const video_format_names[] = { + "component", "PAL", "NTSC", "SECAM", + "MAC", "unspecified", "unspecified", "unspecified" +}; + +static const char * const color_primaries_names[] = { + "unknown", "bt709", "undef", "unknown", + "bt470m", "bt470bg", "smpte170m", "smpte240m", + "film", "bt2020" +}; + +static const char * const transfer_characteristics_names[] = { + "unknown", "bt709", "undef", "unknown", + "bt470m", "bt470bg", "smpte170m", "smpte240m", + "linear", "log100", "log316", "iec61966-2-4", + "bt1361e", "iec61966-2-1", "bt2020-10", "bt2020-12", + "smpte-st-2084", "smpte-st-428" +}; + +static const char * const matrix_coeffs_names[] = { + "GBR", "bt709", "undef", "unknown", + "fcc", "bt470bg", "smpte170m", "smpte240m", + "YCgCo", "bt2020nc", "bt2020c" +}; + +#ifdef SUPPORT_10BIT +#define HEVC_CM_BODY_START_ADDR 0x3626 +#define HEVC_CM_BODY_LENGTH 0x3627 +#define HEVC_CM_HEADER_LENGTH 0x3629 +#define HEVC_CM_HEADER_OFFSET 0x362b +#define HEVC_SAO_CTRL9 0x362d +#define LOSLESS_COMPRESS_MODE +/* DOUBLE_WRITE_MODE is enabled only when NV21 8 bit output is needed */ +/* +*double_write_mode: 0, no double write; 1, 1:1 ratio; 2, (1/4):(1/4) ratio; +* 3, (1/4):(1/4) ratio, with both compressed frame included +* 0x10, double write only +*/ +static u32 double_write_mode; + +/*#define DECOMP_HEADR_SURGENT*/ + +static u32 mem_map_mode; /* 0:linear 1:32x32 2:64x32 ; m8baby test1902 */ +static u32 enable_mem_saving = 1; +static u32 workaround_enable; +static u32 force_w_h; +#endif +static u32 force_fps; +static u32 pts_unstable; +#define H265_DEBUG_BUFMGR 0x01 +#define H265_DEBUG_BUFMGR_MORE 0x02 +#define H265_DEBUG_UCODE 0x04 +#define H265_DEBUG_REG 0x08 +#define H265_DEBUG_MAN_SEARCH_NAL 0x10 +#define H265_DEBUG_MAN_SKIP_NAL 0x20 +#define H265_DEBUG_DISPLAY_CUR_FRAME 0x40 +#define H265_DEBUG_FORCE_CLK 0x80 +#define H265_DEBUG_SEND_PARAM_WITH_REG 0x100 +#define H265_DEBUG_NO_DISPLAY 0x200 +#define H265_DEBUG_DISCARD_NAL 0x400 +#define H265_DEBUG_OUT_PTS 0x800 +#define H265_DEBUG_PRINT_DECODE_STATUS 0x1000 +#define H265_DEBUG_PRINT_SEI 0x2000 +#define H265_DEBUG_PIC_STRUCT 0x4000 +#define H265_DEBUG_DIS_LOC_ERROR_PROC 0x10000 +#define H265_DEBUG_DIS_SYS_ERROR_PROC 0x20000 +#define H265_DEBUG_DUMP_PIC_LIST 0x40000 +#define H265_DEBUG_TRIG_SLICE_SEGMENT_PROC 0x80000 +#define H265_DEBUG_HW_RESET 0x100000 +#define H265_DEBUG_LOAD_UCODE_FROM_FILE 0x200000 +#define H265_DEBUG_ERROR_TRIG 0x400000 +#define H265_DEBUG_NO_EOS_SEARCH_DONE 0x800000 +#define H265_DEBUG_NOT_USE_LAST_DISPBUF 0x1000000 +#define H265_DEBUG_IGNORE_CONFORMANCE_WINDOW 0x2000000 +#define H265_DEBUG_WAIT_DECODE_DONE_WHEN_STOP 0x4000000 +#ifdef MULTI_INSTANCE_SUPPORT +#define USE_OLD_PROVIDER 0x10000000 +#define PRINT_FLAG_VDEC_STATUS 0x20000000 +#define PRINT_FLAG_VDEC_DETAIL 0x40000000 +#define ONLY_RESET_AT_START 0x80000000 +#endif +#define MAX_BUF_NUM 24 +#define MAX_REF_PIC_NUM 24 +#define MAX_REF_ACTIVE 16 + +const u32 h265_version = 201602101; +static u32 debug; +static u32 radr; +static u32 rval; +static u32 dbg_cmd; +static u32 dbg_skip_decode_index; +static u32 endian = 0xff0; +#ifdef ERROR_HANDLE_DEBUG +static u32 dbg_nal_skip_flag; + /* bit[0], skip vps; bit[1], skip sps; bit[2], skip pps */ +static u32 dbg_nal_skip_count; +#endif +/*for debug*/ +static u32 decode_stop_pos; +static u32 decode_stop_pos_pre; +static u32 decode_pic_begin; +static uint slice_parse_begin; +static u32 step; +#ifdef MIX_STREAM_SUPPORT +#ifdef SUPPORT_4K2K +static u32 buf_alloc_width = 4096; +static u32 buf_alloc_height = 2304; +#else +static u32 buf_alloc_width = 1920; +static u32 buf_alloc_height = 1088; +#endif +static u32 dynamic_buf_num_margin; +#else +static u32 buf_alloc_width; +static u32 buf_alloc_height; +static u32 dynamic_buf_num_margin = 8; +#endif +static u32 buf_alloc_size; +static u32 re_config_pic_flag; +/* +*bit[0]: 0, + *bit[1]: 0, always release cma buffer when stop + *bit[1]: 1, never release cma buffer when stop +*bit[0]: 1, when stop, release cma buffer if blackout is 1; +*do not release cma buffer is blackout is not 1 +* +*bit[2]: 0, when start decoding, check current displayed buffer +* (only for buffer decoded by h265) if blackout is 0 +* 1, do not check current displayed buffer +* +*bit[3]: 1, if blackout is not 1, do not release current +* displayed cma buffer always. +*/ +/* set to 1 for fast play; +* set to 8 for other case of "keep last frame" +*/ +static u32 buffer_mode = 1; +/* buffer_mode_dbg: debug only*/ +static u32 buffer_mode_dbg = 0xffff0000; +/**/ +/* +*bit[1:0]PB_skip_mode: 0, start decoding at begin; +*1, start decoding after first I; +*2, only decode and display none error picture; +*3, start decoding and display after IDR,etc +*bit[31:16] PB_skip_count_after_decoding (decoding but not display), +*only for mode 0 and 1. + */ +static u32 nal_skip_policy = 2; + +/* +*bit 0, 1: only display I picture; +*bit 1, 1: only decode I picture; +*/ +static u32 i_only_flag; + +/* +*use_cma: 1, use both reserver memory and cma for buffers +*2, only use cma for buffers +*/ +static u32 use_cma = 2; + +static u32 max_decoding_time; +/* +*error handling +*/ +/* +*error_handle_policy: +*bit 0: 0, auto skip error_skip_nal_count nals before error recovery; +*1, skip error_skip_nal_count nals before error recovery; +*bit 1 (valid only when bit0 == 1): +*1, wait vps/sps/pps after error recovery; +*bit 2 (valid only when bit0 == 0): +*0, auto search after error recovery (hevc_recover() called); +*1, manual search after error recovery +*(change to auto search after get IDR: WRITE_VREG(NAL_SEARCH_CTL, 0x2)) +* +*bit 4: 0, set error_mark after reset/recover + *1, do not set error_mark after reset/recover +*bit 5: 0, check total lcu for every picture + *1, do not check total lcu +* +*/ + +static u32 error_handle_policy; +static u32 error_skip_nal_count = 6; +static u32 error_handle_threshold = 30; +static u32 error_handle_nal_skip_threshold = 10; +static u32 error_handle_system_threshold = 30; +static u32 interlace_enable = 1; +static u32 parser_sei_enable; +/* this is only for h265 mmu enable */ +static u32 mmu_enable = 1; + + + + +static u32 max_buf_num = 16; + +#ifdef MULTI_INSTANCE_SUPPORT +static unsigned int m_hevc_count; +#define MAX_DECODE_INSTANCE_NUM 4 +static unsigned int max_decode_instance_num + = MAX_DECODE_INSTANCE_NUM; +static unsigned int decode_frame_count[MAX_DECODE_INSTANCE_NUM]; +static unsigned int max_process_time[MAX_DECODE_INSTANCE_NUM]; +static unsigned int max_get_frame_interval[MAX_DECODE_INSTANCE_NUM]; +static unsigned int debug_flag[MAX_DECODE_INSTANCE_NUM]; +#endif + +DEFINE_SPINLOCK(lock); +struct task_struct *h265_task; +#undef DEBUG_REG +#ifdef DEBUG_REG +void WRITE_VREG_DBG(unsigned adr, unsigned val) +{ + if (debug & H265_DEBUG_REG) + pr_info("%s(%x, %x)\n", __func__, adr, val); + WRITE_VREG(adr, val); +} + +#undef WRITE_VREG +#define WRITE_VREG WRITE_VREG_DBG +#endif + +static DEFINE_MUTEX(vh265_mutex); + + +/* +************************************************* +* +h265 buffer management include +* +************************************************** +*/ +enum NalUnitType { + NAL_UNIT_CODED_SLICE_TRAIL_N = 0, /* 0 */ + NAL_UNIT_CODED_SLICE_TRAIL_R, /* 1 */ + + NAL_UNIT_CODED_SLICE_TSA_N, /* 2 */ + /* Current name in the spec: TSA_R */ + NAL_UNIT_CODED_SLICE_TLA, /* 3 */ + + NAL_UNIT_CODED_SLICE_STSA_N, /* 4 */ + NAL_UNIT_CODED_SLICE_STSA_R, /* 5 */ + + NAL_UNIT_CODED_SLICE_RADL_N, /* 6 */ + /* Current name in the spec: RADL_R */ + NAL_UNIT_CODED_SLICE_DLP, /* 7 */ + + NAL_UNIT_CODED_SLICE_RASL_N, /* 8 */ + /* Current name in the spec: RASL_R */ + NAL_UNIT_CODED_SLICE_TFD, /* 9 */ + + NAL_UNIT_RESERVED_10, + NAL_UNIT_RESERVED_11, + NAL_UNIT_RESERVED_12, + NAL_UNIT_RESERVED_13, + NAL_UNIT_RESERVED_14, + NAL_UNIT_RESERVED_15, + + /* Current name in the spec: BLA_W_LP */ + NAL_UNIT_CODED_SLICE_BLA, /* 16 */ + /* Current name in the spec: BLA_W_DLP */ + NAL_UNIT_CODED_SLICE_BLANT, /* 17 */ + NAL_UNIT_CODED_SLICE_BLA_N_LP, /* 18 */ + /* Current name in the spec: IDR_W_DLP */ + NAL_UNIT_CODED_SLICE_IDR, /* 19 */ + NAL_UNIT_CODED_SLICE_IDR_N_LP, /* 20 */ + NAL_UNIT_CODED_SLICE_CRA, /* 21 */ + NAL_UNIT_RESERVED_22, + NAL_UNIT_RESERVED_23, + + NAL_UNIT_RESERVED_24, + NAL_UNIT_RESERVED_25, + NAL_UNIT_RESERVED_26, + NAL_UNIT_RESERVED_27, + NAL_UNIT_RESERVED_28, + NAL_UNIT_RESERVED_29, + NAL_UNIT_RESERVED_30, + NAL_UNIT_RESERVED_31, + + NAL_UNIT_VPS, /* 32 */ + NAL_UNIT_SPS, /* 33 */ + NAL_UNIT_PPS, /* 34 */ + NAL_UNIT_ACCESS_UNIT_DELIMITER, /* 35 */ + NAL_UNIT_EOS, /* 36 */ + NAL_UNIT_EOB, /* 37 */ + NAL_UNIT_FILLER_DATA, /* 38 */ + NAL_UNIT_SEI, /* 39 Prefix SEI */ + NAL_UNIT_SEI_SUFFIX, /* 40 Suffix SEI */ + NAL_UNIT_RESERVED_41, + NAL_UNIT_RESERVED_42, + NAL_UNIT_RESERVED_43, + NAL_UNIT_RESERVED_44, + NAL_UNIT_RESERVED_45, + NAL_UNIT_RESERVED_46, + NAL_UNIT_RESERVED_47, + NAL_UNIT_UNSPECIFIED_48, + NAL_UNIT_UNSPECIFIED_49, + NAL_UNIT_UNSPECIFIED_50, + NAL_UNIT_UNSPECIFIED_51, + NAL_UNIT_UNSPECIFIED_52, + NAL_UNIT_UNSPECIFIED_53, + NAL_UNIT_UNSPECIFIED_54, + NAL_UNIT_UNSPECIFIED_55, + NAL_UNIT_UNSPECIFIED_56, + NAL_UNIT_UNSPECIFIED_57, + NAL_UNIT_UNSPECIFIED_58, + NAL_UNIT_UNSPECIFIED_59, + NAL_UNIT_UNSPECIFIED_60, + NAL_UNIT_UNSPECIFIED_61, + NAL_UNIT_UNSPECIFIED_62, + NAL_UNIT_UNSPECIFIED_63, + NAL_UNIT_INVALID, +}; + +/* --------------------------------------------------- */ +/* Amrisc Software Interrupt */ +/* --------------------------------------------------- */ +#define AMRISC_STREAM_EMPTY_REQ 0x01 +#define AMRISC_PARSER_REQ 0x02 +#define AMRISC_MAIN_REQ 0x04 + +/* --------------------------------------------------- */ +/* HEVC_DEC_STATUS define */ +/* --------------------------------------------------- */ +#define HEVC_DEC_IDLE 0x0 +#define HEVC_NAL_UNIT_VPS 0x1 +#define HEVC_NAL_UNIT_SPS 0x2 +#define HEVC_NAL_UNIT_PPS 0x3 +#define HEVC_NAL_UNIT_CODED_SLICE_SEGMENT 0x4 +#define HEVC_CODED_SLICE_SEGMENT_DAT 0x5 +#define HEVC_SLICE_DECODING 0x6 +#define HEVC_NAL_UNIT_SEI 0x7 +#define HEVC_SLICE_SEGMENT_DONE 0x8 +#define HEVC_NAL_SEARCH_DONE 0x9 +#define HEVC_DECPIC_DATA_DONE 0xa +#define HEVC_DECPIC_DATA_ERROR 0xb +#define HEVC_SEI_DAT 0xc +#define HEVC_SEI_DAT_DONE 0xd +#define HEVC_NAL_DECODE_DONE 0xe + +#define HEVC_DUMP_LMEM 0x20 + +#define HEVC_4k2k_60HZ_NOT_SUPPORT 0x80 +#define HEVC_DISCARD_NAL 0xf0 +#define HEVC_ACTION_ERROR 0xfe +#define HEVC_ACTION_DONE 0xff + +/* --------------------------------------------------- */ +/* Include "parser_cmd.h" */ +/* --------------------------------------------------- */ +#define PARSER_CMD_SKIP_CFG_0 0x0000090b + +#define PARSER_CMD_SKIP_CFG_1 0x1b14140f + +#define PARSER_CMD_SKIP_CFG_2 0x001b1910 + +#define PARSER_CMD_NUMBER 37 + +static unsigned short parser_cmd[PARSER_CMD_NUMBER] = { + 0x0401, + 0x8401, + 0x0800, + 0x0402, + 0x9002, + 0x1423, + 0x8CC3, + 0x1423, + 0x8804, + 0x9825, + 0x0800, + 0x04FE, + 0x8406, + 0x8411, + 0x1800, + 0x8408, + 0x8409, + 0x8C2A, + 0x9C2B, + 0x1C00, + 0x840F, + 0x8407, + 0x8000, + 0x8408, + 0x2000, + 0xA800, + 0x8410, + 0x04DE, + 0x840C, + 0x840D, + 0xAC00, + 0xA000, + 0x08C0, + 0x08E0, + 0xA40E, + 0xFC00, + 0x7C00 +}; + +/* +************************************************* +* +*h265 buffer management +* +************************************************** +*/ +/* #define BUFFER_MGR_ONLY */ +/* #define CONFIG_HEVC_CLK_FORCED_ON */ +/* #define ENABLE_SWAP_TEST */ +#define MCRCC_ENABLE +#define INVALID_POC 0x80000000 + +#define HEVC_DEC_STATUS_REG HEVC_ASSIST_SCRATCH_0 +#define HEVC_RPM_BUFFER HEVC_ASSIST_SCRATCH_1 +#define HEVC_SHORT_TERM_RPS HEVC_ASSIST_SCRATCH_2 +#define HEVC_VPS_BUFFER HEVC_ASSIST_SCRATCH_3 +#define HEVC_SPS_BUFFER HEVC_ASSIST_SCRATCH_4 +#define HEVC_PPS_BUFFER HEVC_ASSIST_SCRATCH_5 +#define HEVC_SAO_UP HEVC_ASSIST_SCRATCH_6 +#define HEVC_STREAM_SWAP_BUFFER HEVC_ASSIST_SCRATCH_7 +#define HEVC_STREAM_SWAP_BUFFER2 HEVC_ASSIST_SCRATCH_8 +#define HEVC_sao_mem_unit HEVC_ASSIST_SCRATCH_9 +#define HEVC_SAO_ABV HEVC_ASSIST_SCRATCH_A +#define HEVC_sao_vb_size HEVC_ASSIST_SCRATCH_B +#define HEVC_SAO_VB HEVC_ASSIST_SCRATCH_C +#define HEVC_SCALELUT HEVC_ASSIST_SCRATCH_D +#define HEVC_WAIT_FLAG HEVC_ASSIST_SCRATCH_E +#define RPM_CMD_REG HEVC_ASSIST_SCRATCH_F +#define LMEM_DUMP_ADR HEVC_ASSIST_SCRATCH_F +#define HEVC_STREAM_SWAP_TEST HEVC_ASSIST_SCRATCH_L +#define HEVC_DECODE_PIC_BEGIN_REG HEVC_ASSIST_SCRATCH_M +/*#define HEVC_DECODE_PIC_NUM_REG HEVC_ASSIST_SCRATCH_N*/ +#define HEVC_DECODE_SIZE HEVC_ASSIST_SCRATCH_N + +#define DEBUG_REG1 HEVC_ASSIST_SCRATCH_G +#define DEBUG_REG2 HEVC_ASSIST_SCRATCH_H +/* +*ucode parser/search control +*bit 0: 0, header auto parse; 1, header manual parse +*bit 1: 0, auto skip for noneseamless stream; 1, no skip +*bit [3:2]: valid when bit1==0; +*0, auto skip nal before first vps/sps/pps/idr; +*1, auto skip nal before first vps/sps/pps +*2, auto skip nal before first vps/sps/pps, +* and not decode until the first I slice (with slice address of 0) +* +*3, auto skip before first I slice (nal_type >=16 && nal_type<=21) +*bit [15:4] nal skip count (valid when bit0 == 1 (manual mode) ) +*bit [16]: for NAL_UNIT_EOS when bit0 is 0: +* 0, send SEARCH_DONE to arm ; 1, do not send SEARCH_DONE to arm +*bit [17]: for NAL_SEI when bit0 is 0: +* 0, do not parse SEI in ucode; 1, parse SEI in ucode +* +*bit [31:20]: used by ucode for debug purpose +*/ +#define NAL_SEARCH_CTL HEVC_ASSIST_SCRATCH_I +#define CUR_NAL_UNIT_TYPE HEVC_ASSIST_SCRATCH_J +#define DECODE_STOP_POS HEVC_ASSIST_SCRATCH_K + +#define MAX_INT 0x7FFFFFFF + +#define RPM_BEGIN 0x100 +#define modification_list_cur 0x140 +#define RPM_END 0x180 + +#define RPS_USED_BIT 14 +/* MISC_FLAG0 */ +#define PCM_LOOP_FILTER_DISABLED_FLAG_BIT 0 +#define PCM_ENABLE_FLAG_BIT 1 +#define LOOP_FILER_ACROSS_TILES_ENABLED_FLAG_BIT 2 +#define PPS_LOOP_FILTER_ACROSS_SLICES_ENABLED_FLAG_BIT 3 +#define DEBLOCKING_FILTER_OVERRIDE_ENABLED_FLAG_BIT 4 +#define PPS_DEBLOCKING_FILTER_DISABLED_FLAG_BIT 5 +#define DEBLOCKING_FILTER_OVERRIDE_FLAG_BIT 6 +#define SLICE_DEBLOCKING_FILTER_DISABLED_FLAG_BIT 7 +#define SLICE_SAO_LUMA_FLAG_BIT 8 +#define SLICE_SAO_CHROMA_FLAG_BIT 9 +#define SLICE_LOOP_FILTER_ACROSS_SLICES_ENABLED_FLAG_BIT 10 + +union param_u { + struct { + unsigned short data[RPM_END - RPM_BEGIN]; + } l; + struct { + /* from ucode lmem, do not change this struct */ + unsigned short CUR_RPS[0x10]; + unsigned short num_ref_idx_l0_active; + unsigned short num_ref_idx_l1_active; + unsigned short slice_type; + unsigned short slice_temporal_mvp_enable_flag; + unsigned short dependent_slice_segment_flag; + unsigned short slice_segment_address; + unsigned short num_title_rows_minus1; + unsigned short pic_width_in_luma_samples; + unsigned short pic_height_in_luma_samples; + unsigned short log2_min_coding_block_size_minus3; + unsigned short log2_diff_max_min_coding_block_size; + unsigned short log2_max_pic_order_cnt_lsb_minus4; + unsigned short POClsb; + unsigned short collocated_from_l0_flag; + unsigned short collocated_ref_idx; + unsigned short log2_parallel_merge_level; + unsigned short five_minus_max_num_merge_cand; + unsigned short sps_num_reorder_pics_0; + unsigned short modification_flag; + unsigned short tiles_enabled_flag; + unsigned short num_tile_columns_minus1; + unsigned short num_tile_rows_minus1; + unsigned short tile_width[4]; + unsigned short tile_height[4]; + unsigned short misc_flag0; + unsigned short pps_beta_offset_div2; + unsigned short pps_tc_offset_div2; + unsigned short slice_beta_offset_div2; + unsigned short slice_tc_offset_div2; + unsigned short pps_cb_qp_offset; + unsigned short pps_cr_qp_offset; + unsigned short first_slice_segment_in_pic_flag; + unsigned short m_temporalId; + unsigned short m_nalUnitType; + + unsigned short vui_num_units_in_tick_hi; + unsigned short vui_num_units_in_tick_lo; + unsigned short vui_time_scale_hi; + unsigned short vui_time_scale_lo; + unsigned short bit_depth; + unsigned short profile_etc; + unsigned short sei_frame_field_info; + unsigned short video_signal_type; + unsigned short modification_list[0x20]; + unsigned short conformance_window_flag; + unsigned short conf_win_left_offset; + unsigned short conf_win_right_offset; + unsigned short conf_win_top_offset; + unsigned short conf_win_bottom_offset; + unsigned short chroma_format_idc; + unsigned short color_description; + } p; +}; + +#define RPM_BUF_SIZE (0x80*2) +/* non mmu mode lmem size : 0x400, mmu mode : 0x500*/ +#define LMEM_BUF_SIZE (0x500 * 2) + +struct buff_s { + u32 buf_start; + u32 buf_size; + u32 buf_end; +}; + +struct BuffInfo_s { + u32 max_width; + u32 max_height; + unsigned int start_adr; + unsigned int end_adr; + struct buff_s ipp; + struct buff_s sao_abv; + struct buff_s sao_vb; + struct buff_s short_term_rps; + struct buff_s vps; + struct buff_s sps; + struct buff_s pps; + struct buff_s sao_up; + struct buff_s swap_buf; + struct buff_s swap_buf2; + struct buff_s scalelut; + struct buff_s dblk_para; + struct buff_s dblk_data; + struct buff_s mmu_vbh; + struct buff_s cm_header; + struct buff_s mpred_above; + struct buff_s mpred_mv; + struct buff_s rpm; + struct buff_s lmem; +}; +#define WORK_BUF_SPEC_NUM 2 +static struct BuffInfo_s amvh265_workbuff_spec[WORK_BUF_SPEC_NUM] = { + { + /* 8M bytes */ + .max_width = 1920, + .max_height = 1088, + .ipp = { + /* + *IPP work space calculation : + *4096 * (Y+CbCr+Flags) = 12k, round to 16k + */ + .buf_size = 0x4000, + }, + .sao_abv = { + .buf_size = 0x30000, + }, + .sao_vb = { + .buf_size = 0x30000, + }, + .short_term_rps = { + /* + *SHORT_TERM_RPS - Max 64 set, 16 entry every set, + *total 64x16x2 = 2048 bytes (0x800) + */ + .buf_size = 0x800, + }, + .vps = { + /* + *VPS STORE AREA - Max 16 VPS, each has 0x80 bytes, + *total 0x0800 bytes + */ + .buf_size = 0x800, + }, + .sps = { + /* + *SPS STORE AREA - Max 16 SPS, each has 0x80 bytes, + *total 0x0800 bytes + */ + .buf_size = 0x800, + }, + .pps = { + /* + *PPS STORE AREA - Max 64 PPS, each has 0x80 bytes, + *total 0x2000 bytes + */ + .buf_size = 0x2000, + }, + .sao_up = { + /* + *SAO UP STORE AREA - Max 640(10240/16) LCU, + *each has 16 bytes total 0x2800 bytes + */ + .buf_size = 0x2800, + }, + .swap_buf = { + /* + *256cyclex64bit = 2K bytes 0x800 + *(only 144 cycles valid) + */ + .buf_size = 0x800, + }, + .swap_buf2 = { + .buf_size = 0x800, + }, + .scalelut = { + /* + *support up to 32 SCALELUT 1024x32 = + *32Kbytes (0x8000) + */ + .buf_size = 0x8000, + }, + .dblk_para = { +#ifdef SUPPORT_10BIT + .buf_size = 0x40000, +#else + /* + *DBLK -> Max 256(4096/16) LCU, each para + *512bytes(total:0x20000), data 1024bytes(total:0x40000) + */ + .buf_size = 0x20000, +#endif + }, + .dblk_data = { + .buf_size = 0x40000, + }, + .mmu_vbh = { + .buf_size = 0x5000, /*2*16*2304/4, 4K*/ + }, + .cm_header = {/* 0x44000 = ((1088*2*1024*4)/32/4)*(32/8)*/ + .buf_size = MMU_COMPRESS_HEADER_SIZE*(16+1), + }, + .mpred_above = { + .buf_size = 0x8000, + }, + .mpred_mv = {/* 1080p, 0x40000 per buffer */ + .buf_size = 0x40000 * MAX_REF_PIC_NUM, + }, + .rpm = { + .buf_size = RPM_BUF_SIZE, + }, + .lmem = { + .buf_size = 0x500 * 2, + } + }, + { + .max_width = 4096, + .max_height = 2048, + .ipp = { + /* + *IPP work space calculation : + *4096 * (Y+CbCr+Flags) = 12k, round to 16k + */ + .buf_size = 0x4000, + }, + .sao_abv = { + .buf_size = 0x30000, + }, + .sao_vb = { + .buf_size = 0x30000, + }, + .short_term_rps = { + /* + *SHORT_TERM_RPS - Max 64 set, 16 entry every set, + *total 64x16x2 = 2048 bytes (0x800) + */ + .buf_size = 0x800, + }, + .vps = { + /* + *VPS STORE AREA - Max 16 VPS, each has 0x80 bytes, + *total 0x0800 bytes + */ + .buf_size = 0x800, + }, + .sps = { + /* + *SPS STORE AREA - Max 16 SPS, each has 0x80 bytes, + *total 0x0800 bytes + */ + .buf_size = 0x800, + }, + .pps = { + /* + *PPS STORE AREA - Max 64 PPS, each has 0x80 bytes, + *total 0x2000 bytes + */ + .buf_size = 0x2000, + }, + .sao_up = { + /* + *SAO UP STORE AREA - Max 640(10240/16) LCU, + *each has 16 bytes total 0x2800 bytes + */ + .buf_size = 0x2800, + }, + .swap_buf = { + /* + *256cyclex64bit = 2K bytes 0x800 + *(only 144 cycles valid) + */ + .buf_size = 0x800, + }, + .swap_buf2 = { + .buf_size = 0x800, + }, + .scalelut = { + /* + *support up to 32 SCALELUT 1024x32 = 32Kbytes + *(0x8000) + */ + .buf_size = 0x8000, + }, + .dblk_para = { + /* + *DBLK -> Max 256(4096/16) LCU, each para + *512bytes(total:0x20000), + *data 1024bytes(total:0x40000) + */ + .buf_size = 0x20000, + }, + .dblk_data = { + .buf_size = 0x40000, + }, + .mmu_vbh = { + .buf_size = 0x5000, /*2*16*2304/4, 4K*/ + }, + .cm_header = {/*0x44000 = ((1088*2*1024*4)/32/4)*(32/8)*/ + .buf_size = MMU_COMPRESS_HEADER_SIZE * (16+1), + }, + .mpred_above = { + .buf_size = 0x8000, + }, + .mpred_mv = { + /* + *.buf_size = 0x100000*16, + *4k2k , 0x100000 per buffer + */ + /* 4096x2304 , 0x120000 per buffer */ + .buf_size = 0x120000 * MAX_REF_PIC_NUM, + }, + .rpm = { + .buf_size = RPM_BUF_SIZE, + }, + .lmem = { + .buf_size = 0x500 * 2, + } + } +}; + +unsigned int get_mmu_mode(void) +{ + return mmu_enable; +} + +#ifdef SUPPORT_10BIT +/* Losless compression body buffer size 4K per 64x32 (jt) */ +static int compute_losless_comp_body_size(int width, int height, + int mem_saving_mode) +{ + int width_x64; + int height_x32; + int bsize; + + width_x64 = width + 63; + width_x64 >>= 6; + + height_x32 = height + 31; + height_x32 >>= 5; + if (mem_saving_mode == 1 && mmu_enable) + bsize = 3200 * width_x64 * height_x32; + else if (mem_saving_mode == 1) + bsize = 3072 * width_x64 * height_x32; + else + bsize = 4096 * width_x64 * height_x32; + + return bsize; +} + +/* Losless compression header buffer size 32bytes per 128x64 (jt) */ +static int compute_losless_comp_header_size(int width, int height) +{ + int width_x128; + int height_x64; + int hsize; + + width_x128 = width + 127; + width_x128 >>= 7; + + height_x64 = height + 63; + height_x64 >>= 6; + + hsize = 32*width_x128*height_x64; + + return hsize; +} + +#endif + +static void init_buff_spec(struct hevc_state_s *hevc, + struct BuffInfo_s *buf_spec) +{ + buf_spec->ipp.buf_start = buf_spec->start_adr; + buf_spec->sao_abv.buf_start = + buf_spec->ipp.buf_start + buf_spec->ipp.buf_size; + + buf_spec->sao_vb.buf_start = + buf_spec->sao_abv.buf_start + buf_spec->sao_abv.buf_size; + buf_spec->short_term_rps.buf_start = + buf_spec->sao_vb.buf_start + buf_spec->sao_vb.buf_size; + buf_spec->vps.buf_start = + buf_spec->short_term_rps.buf_start + + buf_spec->short_term_rps.buf_size; + buf_spec->sps.buf_start = + buf_spec->vps.buf_start + buf_spec->vps.buf_size; + buf_spec->pps.buf_start = + buf_spec->sps.buf_start + buf_spec->sps.buf_size; + buf_spec->sao_up.buf_start = + buf_spec->pps.buf_start + buf_spec->pps.buf_size; + buf_spec->swap_buf.buf_start = + buf_spec->sao_up.buf_start + buf_spec->sao_up.buf_size; + buf_spec->swap_buf2.buf_start = + buf_spec->swap_buf.buf_start + buf_spec->swap_buf.buf_size; + buf_spec->scalelut.buf_start = + buf_spec->swap_buf2.buf_start + buf_spec->swap_buf2.buf_size; + buf_spec->dblk_para.buf_start = + buf_spec->scalelut.buf_start + buf_spec->scalelut.buf_size; + buf_spec->dblk_data.buf_start = + buf_spec->dblk_para.buf_start + buf_spec->dblk_para.buf_size; + buf_spec->mmu_vbh.buf_start = + buf_spec->dblk_data.buf_start + buf_spec->dblk_data.buf_size; + buf_spec->cm_header.buf_start = + buf_spec->mmu_vbh.buf_start + buf_spec->mmu_vbh.buf_size; + buf_spec->mpred_above.buf_start = + buf_spec->cm_header.buf_start + buf_spec->cm_header.buf_size; + buf_spec->mpred_mv.buf_start = + buf_spec->mpred_above.buf_start + + buf_spec->mpred_above.buf_size; + if (debug & H265_DEBUG_SEND_PARAM_WITH_REG) { + buf_spec->end_adr = + buf_spec->mpred_mv.buf_start + + buf_spec->mpred_mv.buf_size; + } else { + buf_spec->rpm.buf_start = + buf_spec->mpred_mv.buf_start + + buf_spec->mpred_mv.buf_size; + if (debug & H265_DEBUG_UCODE) { + buf_spec->lmem.buf_start = + buf_spec->rpm.buf_start + + buf_spec->rpm.buf_size; + buf_spec->end_adr = + buf_spec->lmem.buf_start + + buf_spec->lmem.buf_size; + } else { + buf_spec->end_adr = + buf_spec->rpm.buf_start + + buf_spec->rpm.buf_size; + } + } + + if (debug) { + hevc_print(hevc, 0, + "%s workspace (%x %x) size = %x\n", __func__, + buf_spec->start_adr, buf_spec->end_adr, + buf_spec->end_adr - buf_spec->start_adr); + } + if (debug) { + hevc_print(hevc, 0, + "ipp.buf_start :%x\n", + buf_spec->ipp.buf_start); + hevc_print(hevc, 0, + "sao_abv.buf_start :%x\n", + buf_spec->sao_abv.buf_start); + hevc_print(hevc, 0, + "sao_vb.buf_start :%x\n", + buf_spec->sao_vb.buf_start); + hevc_print(hevc, 0, + "short_term_rps.buf_start :%x\n", + buf_spec->short_term_rps.buf_start); + hevc_print(hevc, 0, + "vps.buf_start :%x\n", + buf_spec->vps.buf_start); + hevc_print(hevc, 0, + "sps.buf_start :%x\n", + buf_spec->sps.buf_start); + hevc_print(hevc, 0, + "pps.buf_start :%x\n", + buf_spec->pps.buf_start); + hevc_print(hevc, 0, + "sao_up.buf_start :%x\n", + buf_spec->sao_up.buf_start); + hevc_print(hevc, 0, + "swap_buf.buf_start :%x\n", + buf_spec->swap_buf.buf_start); + hevc_print(hevc, 0, + "swap_buf2.buf_start :%x\n", + buf_spec->swap_buf2.buf_start); + hevc_print(hevc, 0, + "scalelut.buf_start :%x\n", + buf_spec->scalelut.buf_start); + hevc_print(hevc, 0, + "dblk_para.buf_start :%x\n", + buf_spec->dblk_para.buf_start); + hevc_print(hevc, 0, + "dblk_data.buf_start :%x\n", + buf_spec->dblk_data.buf_start); + hevc_print(hevc, 0, + "mpred_above.buf_start :%x\n", + buf_spec->mpred_above.buf_start); + hevc_print(hevc, 0, + "mpred_mv.buf_start :%x\n", + buf_spec->mpred_mv.buf_start); + if ((debug & H265_DEBUG_SEND_PARAM_WITH_REG) == 0) { + hevc_print(hevc, 0, + "rpm.buf_start :%x\n", + buf_spec->rpm.buf_start); + } + } + +} + +enum SliceType { + B_SLICE, + P_SLICE, + I_SLICE +}; + +/*USE_BUF_BLOCK*/ +struct BUF_s { + int index; + unsigned int alloc_flag; + /*buffer */ + unsigned int cma_page_count; + unsigned long alloc_addr; + unsigned long start_adr; + unsigned int size; + + unsigned int free_start_adr; +} /*BUF_t */; + +/* level 6, 6.1 maximum slice number is 800; other is 200 */ +#define MAX_SLICE_NUM 800 +struct PIC_s { + int index; + int BUF_index; + int POC; + int decode_idx; + int slice_type; + int RefNum_L0; + int RefNum_L1; + int num_reorder_pic; + int stream_offset; + unsigned char referenced; + unsigned char output_mark; + unsigned char recon_mark; + unsigned char output_ready; + unsigned char error_mark; + unsigned char used_by_display; + /**/ int slice_idx; + int m_aiRefPOCList0[MAX_SLICE_NUM][16]; + int m_aiRefPOCList1[MAX_SLICE_NUM][16]; + /*buffer */ + unsigned int header_adr; + unsigned long cma_alloc_addr; + unsigned int cma_page_count; + struct page *alloc_pages; + unsigned int mpred_mv_wr_start_addr; + unsigned int mc_y_adr; + unsigned int mc_u_v_adr; +#ifdef SUPPORT_10BIT + unsigned int comp_body_size; + unsigned int dw_y_adr; + unsigned int dw_u_v_adr; +#endif + unsigned int buf_size; + int mc_canvas_y; + int mc_canvas_u_v; + int width; + int height; + + int y_canvas_index; + int uv_canvas_index; +#ifdef MULTI_INSTANCE_SUPPORT + struct canvas_config_s canvas_config[2]; +#endif +#ifdef LOSLESS_COMPRESS_MODE + unsigned int losless_comp_body_size; +#endif + unsigned char pic_struct; + int vf_ref; +} /*PIC_t */; + +#define MAX_TILE_COL_NUM 5 +#define MAX_TILE_ROW_NUM 5 +struct tile_s { + int width; + int height; + int start_cu_x; + int start_cu_y; + + unsigned int sao_vb_start_addr; + unsigned int sao_abv_start_addr; +}; + +#define SEI_MASTER_DISPLAY_COLOR_MASK 0x00000001 + +#define VF_POOL_SIZE 32 + +#ifdef MULTI_INSTANCE_SUPPORT +#define DEC_RESULT_NONE 0 +#define DEC_RESULT_DONE 1 +#define DEC_RESULT_AGAIN 2 +#define DEC_RESULT_CONFIG_PARAM 3 +#define DEC_RESULT_ERROR 4 +#define DEC_INIT_PICLIST 5 +#define DEC_UNINIT_PICLIST 6 +static void vh265_work(struct work_struct *work); +#endif +struct hevc_state_s { +#ifdef MULTI_INSTANCE_SUPPORT + struct platform_device *platform_dev; + void (*vdec_cb)(struct vdec_s *, void *); + void *vdec_cb_arg; + struct vframe_chunk_s *chunk; + int dec_result; + struct work_struct work; +#endif + char *provider_name; + unsigned char index; + struct device *cma_dev; + unsigned char m_ins_flag; + unsigned long buf_start; + u32 buf_size; + + struct BuffInfo_s work_space_buf_store; + struct BuffInfo_s *work_space_buf; + struct buff_s *mc_buf; + + void *rpm_addr; + void *lmem_addr; + dma_addr_t rpm_phy_addr; + dma_addr_t lmem_phy_addr; + + unsigned int pic_list_init_flag; + unsigned int use_cma_flag; + + unsigned short *rpm_ptr; + unsigned short *lmem_ptr; + unsigned short *debug_ptr; + int debug_ptr_size; + int pic_w; + int pic_h; + int lcu_x_num; + int lcu_y_num; + int lcu_total; + int lcu_size; + int lcu_size_log2; + int lcu_x_num_pre; + int lcu_y_num_pre; + int first_pic_after_recover; + + int num_tile_col; + int num_tile_row; + int tile_enabled; + int tile_x; + int tile_y; + int tile_y_x; + int tile_start_lcu_x; + int tile_start_lcu_y; + int tile_width_lcu; + int tile_height_lcu; + + int slice_type; + unsigned int slice_addr; + unsigned int slice_segment_addr; + + unsigned char interlace_flag; + unsigned char curr_pic_struct; + + unsigned short sps_num_reorder_pics_0; + unsigned short misc_flag0; + int m_temporalId; + int m_nalUnitType; + int TMVPFlag; + int isNextSliceSegment; + int LDCFlag; + int m_pocRandomAccess; + int plevel; + int MaxNumMergeCand; + + int new_pic; + int new_tile; + int curr_POC; + int iPrevPOC; + int iPrevTid0POC; + int list_no; + int RefNum_L0; + int RefNum_L1; + int ColFromL0Flag; + int LongTerm_Curr; + int LongTerm_Col; + int Col_POC; + int LongTerm_Ref; + + struct PIC_s *cur_pic; + struct PIC_s *col_pic; + int skip_flag; + int decode_idx; + int slice_idx; + unsigned char have_vps; + unsigned char have_sps; + unsigned char have_pps; + unsigned char have_valid_start_slice; + unsigned char wait_buf; + unsigned char error_flag; + unsigned int error_skip_nal_count; + + unsigned char + ignore_bufmgr_error; /* bit 0, for decoding; bit 1, for displaying */ + int PB_skip_mode; + int PB_skip_count_after_decoding; +#ifdef SUPPORT_10BIT + int mem_saving_mode; +#endif +#ifdef LOSLESS_COMPRESS_MODE + unsigned int losless_comp_body_size; +#endif + int pts_mode; + int last_lookup_pts; + int last_pts; + u64 last_lookup_pts_us64; + u64 last_pts_us64; + u32 shift_byte_count_lo; + u32 shift_byte_count_hi; + int pts_mode_switching_count; + int pts_mode_recovery_count; + + int buf_num; + int pic_num; + + /**/ + struct buff_s mc_buf_spec; + union param_u param; + + struct tile_s m_tile[MAX_TILE_ROW_NUM][MAX_TILE_COL_NUM]; + + struct timer_list timer; + struct BUF_s m_BUF[MAX_BUF_NUM]; + u32 used_buf_num; + struct PIC_s m_PIC[MAX_REF_PIC_NUM]; + + DECLARE_KFIFO(newframe_q, struct vframe_s *, VF_POOL_SIZE); + DECLARE_KFIFO(display_q, struct vframe_s *, VF_POOL_SIZE); + DECLARE_KFIFO(pending_q, struct vframe_s *, VF_POOL_SIZE); + struct vframe_s vfpool[VF_POOL_SIZE]; + + u32 stat; + u32 frame_width; + u32 frame_height; + u32 frame_dur; + u32 frame_ar; + u32 bit_depth_luma; + u32 bit_depth_chroma; + u32 video_signal_type; + u32 saved_resolution; + bool get_frame_dur; + u32 error_watchdog_count; + u32 error_skip_nal_wt_cnt; + u32 error_system_watchdog_count; + +#ifdef DEBUG_PTS + unsigned long pts_missed; + unsigned long pts_hit; +#endif + struct dec_sysinfo vh265_amstream_dec_info; + unsigned char init_flag; + unsigned char uninit_list; + u32 start_decoding_time; + + int show_frame_num; + struct semaphore h265_sema; +#ifdef USE_UNINIT_SEMA + struct semaphore h265_uninit_done_sema; +#endif + int fatal_error; + + unsigned long pre_last_frame_alloc_addr; + unsigned long pre_last_frame_alloc_size; + u32 predisp_addr; + u32 predisp_size; + + u32 sei_present_flag; + void *frame_mmu_map_addr; + dma_addr_t frame_mmu_map_phy_addr; + unsigned int mmu_mc_buf_start; + unsigned int mmu_mc_buf_end; + unsigned int mmu_mc_start_4k_adr; + void *mmu_box; + + unsigned int last_put_idx_a; + unsigned int last_put_idx_b; + + unsigned int dec_status; + + + /* data for SEI_MASTER_DISPLAY_COLOR */ + unsigned int primaries[3][2]; + unsigned int white_point[2]; + unsigned int luminance[2]; + + struct PIC_s *pre_top_pic; + struct PIC_s *pre_bot_pic; + +} /*hevc_stru_t */; + +static int hevc_print(struct hevc_state_s *hevc, + int flag, const char *fmt, ...) +{ +#define HEVC_PRINT_BUF 128 + unsigned char buf[HEVC_PRINT_BUF]; + int len = 0; +#ifdef MULTI_INSTANCE_SUPPORT + if (hevc == NULL || + (debug_flag[hevc->index] && flag == 0) || + (debug_flag[hevc->index] & flag)) { +#endif + va_list args; + + va_start(args, fmt); + if (hevc) + len = sprintf(buf, "%d: ", hevc->index); + vsnprintf(buf + len, HEVC_PRINT_BUF - len - 1, fmt, args); + pr_info("%s", buf); + va_end(args); +#ifdef MULTI_INSTANCE_SUPPORT + } +#endif + return 0; +} + +#undef pr_info + +/*#define pr_info(format, ...) hevc_print(hevc, 0, format)*/ +#define pr_info printk + +static void set_canvas(struct PIC_s *pic); + +static void hevc_init_stru(struct hevc_state_s *hevc, + struct BuffInfo_s *buf_spec_i, + struct buff_s *mc_buf_i) +{ + int i; + + hevc->work_space_buf = buf_spec_i; + hevc->mc_buf = mc_buf_i; + hevc->rpm_addr = NULL; + hevc->lmem_addr = NULL; + + hevc->curr_POC = INVALID_POC; + + hevc->pic_list_init_flag = 0; + hevc->use_cma_flag = 0; + hevc->decode_idx = 0; + hevc->slice_idx = 0; + hevc->new_pic = 0; + hevc->new_tile = 0; + hevc->iPrevPOC = 0; + hevc->list_no = 0; + /* int m_uiMaxCUWidth = 1<<7; */ + /* int m_uiMaxCUHeight = 1<<7; */ + hevc->m_pocRandomAccess = MAX_INT; + hevc->tile_enabled = 0; + hevc->tile_x = 0; + hevc->tile_y = 0; + hevc->iPrevTid0POC = 0; + hevc->slice_addr = 0; + hevc->slice_segment_addr = 0; + hevc->skip_flag = 0; + hevc->misc_flag0 = 0; + + hevc->cur_pic = NULL; + hevc->col_pic = NULL; + hevc->wait_buf = 0; + hevc->error_flag = 0; + hevc->error_skip_nal_count = 0; + hevc->have_vps = 0; + hevc->have_sps = 0; + hevc->have_pps = 0; + hevc->have_valid_start_slice = 0; + + hevc->pts_mode = PTS_NORMAL; + hevc->last_pts = 0; + hevc->last_lookup_pts = 0; + hevc->last_pts_us64 = 0; + hevc->last_lookup_pts_us64 = 0; + hevc->shift_byte_count_lo = 0; + hevc->shift_byte_count_hi = 0; + hevc->pts_mode_switching_count = 0; + hevc->pts_mode_recovery_count = 0; + + hevc->PB_skip_mode = nal_skip_policy & 0x3; + hevc->PB_skip_count_after_decoding = (nal_skip_policy >> 16) & 0xffff; + if (hevc->PB_skip_mode == 0) + hevc->ignore_bufmgr_error = 0x1; + else + hevc->ignore_bufmgr_error = 0x0; + + for (i = 0; i < MAX_REF_PIC_NUM; i++) + hevc->m_PIC[i].index = -1; + hevc->buf_num = 0; + hevc->pic_num = 0; + hevc->lcu_x_num_pre = 0; + hevc->lcu_y_num_pre = 0; + hevc->first_pic_after_recover = 0; + + hevc->pre_top_pic = NULL; + hevc->pre_bot_pic = NULL; + + hevc->sei_present_flag = 0; +} + +static int prepare_display_buf(struct hevc_state_s *hevc, struct PIC_s *pic); +static int H265_alloc_mmu(struct hevc_state_s *hevc, + struct PIC_s *new_pic, unsigned short bit_depth, + unsigned int *mmu_index_adr); + +static void get_rpm_param(union param_u *params) +{ + int i; + unsigned int data32; + + for (i = 0; i < 128; i++) { + do { + data32 = READ_VREG(RPM_CMD_REG); + /* pr_info("%x\n", data32); */ + } while ((data32 & 0x10000) == 0); + params->l.data[i] = data32 & 0xffff; + /* pr_info("%x\n", data32); */ + WRITE_VREG(RPM_CMD_REG, 0); + } +} + +static struct PIC_s *get_pic_by_POC(struct hevc_state_s *hevc, int POC) +{ + int i; + struct PIC_s *pic; + struct PIC_s *ret_pic = NULL; + + for (i = 0; i < MAX_REF_PIC_NUM; i++) { + pic = &hevc->m_PIC[i]; + if (pic->index == -1) + continue; + if (pic->POC == POC) { + if (ret_pic == NULL) + ret_pic = pic; + else { + if (pic->decode_idx > ret_pic->decode_idx) + ret_pic = pic; + } + } + } + return ret_pic; +} + +static struct PIC_s *get_ref_pic_by_POC(struct hevc_state_s *hevc, int POC) +{ + int i; + struct PIC_s *pic; + struct PIC_s *ret_pic = NULL; + + for (i = 0; i < MAX_REF_PIC_NUM; i++) { + pic = &hevc->m_PIC[i]; + if (pic->index == -1) + continue; + if ((pic->POC == POC) && (pic->referenced)) { + if (ret_pic == NULL) + ret_pic = pic; + else { + if (pic->decode_idx > ret_pic->decode_idx) + ret_pic = pic; + } + } + } + + if (ret_pic == NULL) { + if (debug) { + pr_info("Wrong, POC of %d is not in referenced list\n", + POC); + } + ret_pic = get_pic_by_POC(hevc, POC); + } + return ret_pic; +} + +static unsigned int log2i(unsigned int val) +{ + unsigned int ret = -1; + + while (val != 0) { + val >>= 1; + ret++; + } + return ret; +} + +static int init_buf_spec(struct hevc_state_s *hevc); + +static void uninit_mmu_buffers(struct hevc_state_s *hevc) +{ + int i; + + decoder_mmu_box_free(hevc->mmu_box); + hevc->mmu_box = NULL; + + for (i = 0; i < MAX_REF_PIC_NUM; i++) { + struct PIC_s *pic = &hevc->m_PIC[i]; + + if (pic->cma_alloc_addr) { + codec_mm_free_for_dma(MEM_NAME, + pic->cma_alloc_addr); + pic->cma_alloc_addr = 0; + } + } +} + +/*USE_BUF_BLOCK*/ +static void uninit_buf_list(struct hevc_state_s *hevc, bool force_free) +{ + int i; + unsigned char release_cma_flag = 0; + unsigned char blackout = get_blackout_policy(); + u32 buffer_mode_real = + (buffer_mode & ((buffer_mode_dbg >> 16) & 0xfff)) | + (buffer_mode_dbg & 0xfff); + + blackout &= ((buffer_mode_dbg >> 28) & 0xf); + blackout |= ((buffer_mode_dbg >> 12) & 0xf); + + hevc->predisp_addr = 0; + + if (force_free) { + blackout = 0; + buffer_mode_real = 0; + pr_info("maybe reuinit buf_list, free cma buffer\n"); + } + + if (buffer_mode_real & 1) { + if (blackout == 1) + release_cma_flag = 1; + } else { + if (buffer_mode_real & 2) + ; + else + release_cma_flag = 1; + } + + if (blackout != 1) { + struct PIC_s *pic; + + if ((release_cma_flag == 1) && + (buffer_mode_real & 8)) { + release_cma_flag = 2; + } + + msleep(50); /* ensure RDMA for display is done */ + if (get_cpu_type() >= MESON_CPU_MAJOR_ID_GXBB && + ((double_write_mode == 0) || + (double_write_mode == 3))) { + hevc->predisp_addr = + READ_VCBUS_REG(AFBC_BODY_BADDR) << 4; + } else { + struct canvas_s cur_canvas; + + canvas_read((READ_VCBUS_REG(VD1_IF0_CANVAS0) & 0xff), + &cur_canvas); + hevc->predisp_addr = cur_canvas.addr; + } + + for (i = 0; i < MAX_REF_PIC_NUM; i++) { + pic = &hevc->m_PIC[i]; + if (pic->index == -1) + continue; + if (hevc->predisp_addr == pic->mc_y_adr) { + hevc->predisp_size = pic->buf_size; + pr_info("%s, set hevc->predisp_size = %d\n", + __func__, pic->buf_size); + break; + } + } + } + + if (hevc->pre_last_frame_alloc_addr) { + if (blackout == 1 || hevc->predisp_addr == 0 + || hevc->predisp_addr < hevc->pre_last_frame_alloc_addr + || hevc->predisp_addr >= + (hevc->pre_last_frame_alloc_addr + + hevc->pre_last_frame_alloc_size) + ) { + codec_mm_free_for_dma(MEM_NAME, + hevc->pre_last_frame_alloc_addr); + pr_info("release pre_last_frame cma buffer %ld\n", + hevc->pre_last_frame_alloc_addr); + hevc->pre_last_frame_alloc_addr = 0; + hevc->pre_last_frame_alloc_size = 0; + } + } + + if (release_cma_flag) { + pr_info("release cma begin\n"); + for (i = 0; i < hevc->used_buf_num; i++) { + if (hevc->m_BUF[i].alloc_addr != 0 + && hevc->m_BUF[i].cma_page_count > 0) { + if ((release_cma_flag == 2) + && (hevc->predisp_addr >= + hevc->m_BUF[i].start_adr) + && (hevc->predisp_addr < + (hevc->m_BUF[i].start_adr + + hevc->m_BUF[i].size))) { + if (hevc->pre_last_frame_alloc_addr) + pr_info("last buf not free\n"); + else { + hevc->pre_last_frame_alloc_addr + = + hevc->m_BUF[i].alloc_addr; + hevc->pre_last_frame_alloc_size + = hevc->m_BUF[i].size; + hevc->m_BUF[i].alloc_addr = 0; + hevc->m_BUF[i]. + cma_page_count = 0; + continue; + } + } + + pr_debug("release cma buffer[%d] (%d %ld)\n", i, + hevc->m_BUF[i].cma_page_count, + hevc->m_BUF[i].alloc_addr); + codec_mm_free_for_dma(MEM_NAME, + hevc->m_BUF[i].alloc_addr); + hevc->m_BUF[i].alloc_addr = 0; + hevc->m_BUF[i].cma_page_count = 0; + + } + } + pr_info("release cma end\n"); + } + pr_info("%s, blackout %x r%x buf_mode %x r%x rel_cma_flag %x hevc->predisp_addr %d pre_alloc_addr(%ld, %ld)\n", + __func__, get_blackout_policy(), blackout, + buffer_mode, buffer_mode_real, release_cma_flag, + hevc->predisp_addr, hevc->pre_last_frame_alloc_addr, + hevc->pre_last_frame_alloc_size); + hevc->pic_list_init_flag = 0; + hevc->buf_num = 0; +} + +static void init_buf_list(struct hevc_state_s *hevc) +{ + int i; + int buf_size; + int mc_buffer_end = hevc->mc_buf->buf_start + hevc->mc_buf->buf_size; + + if (dynamic_buf_num_margin > 0) + hevc->used_buf_num = hevc->sps_num_reorder_pics_0 + + dynamic_buf_num_margin; + else + hevc->used_buf_num = max_buf_num; + + if (hevc->used_buf_num > MAX_BUF_NUM) + hevc->used_buf_num = MAX_BUF_NUM; + if (buf_alloc_size > 0) { + buf_size = buf_alloc_size; + if (debug) + pr_info("[Buffer Management] init_buf_list:\n"); + } else { + int pic_width = buf_alloc_width ? buf_alloc_width : hevc->pic_w; + int pic_height = + buf_alloc_height ? buf_alloc_height : hevc->pic_h; +#ifdef LOSLESS_COMPRESS_MODE +/*SUPPORT_10BIT*/ + int losless_comp_header_size = compute_losless_comp_header_size + (pic_width, pic_height); + int losless_comp_body_size = compute_losless_comp_body_size + (pic_width, pic_height, hevc->mem_saving_mode); + int mc_buffer_size = losless_comp_header_size + + losless_comp_body_size; + int mc_buffer_size_h = (mc_buffer_size + 0xffff)>>16; + + if (double_write_mode) { + int pic_width_dw = ((double_write_mode == 2) || + (double_write_mode == 3)) ? + pic_width / 2 : pic_width; + int pic_height_dw = ((double_write_mode == 2) || + (double_write_mode == 3)) ? + pic_height / 2 : pic_height; + int lcu_size = hevc->lcu_size; + int pic_width_lcu = (pic_width_dw % lcu_size) + ? pic_width_dw / lcu_size + + 1 : pic_width_dw / lcu_size; + int pic_height_lcu = (pic_height_dw % lcu_size) + ? pic_height_dw / lcu_size + + 1 : pic_height_dw / lcu_size; + int lcu_total = pic_width_lcu * pic_height_lcu; + int mc_buffer_size_u_v = lcu_total * lcu_size * lcu_size / 2; + int mc_buffer_size_u_v_h = (mc_buffer_size_u_v + 0xffff) >> 16; + /*64k alignment*/ + buf_size = ((mc_buffer_size_u_v_h << 16) * 3); + } else + buf_size = 0; + + if (mc_buffer_size & 0xffff) { /*64k alignment*/ + mc_buffer_size_h += 1; + } + if ((double_write_mode & 0x10) == 0) + buf_size += (mc_buffer_size_h << 16); +#else + int lcu_size = hevc->lcu_size; + int pic_width_lcu = + (pic_width % lcu_size) ? pic_width / lcu_size + + 1 : pic_width / lcu_size; + int pic_height_lcu = + (pic_height % lcu_size) ? pic_height / lcu_size + + 1 : pic_height / lcu_size; + int lcu_total = pic_width_lcu * pic_height_lcu; + int mc_buffer_size_u_v = lcu_total * lcu_size * lcu_size / 2; + int mc_buffer_size_u_v_h = + (mc_buffer_size_u_v + 0xffff) >> 16; + /*64k alignment*/ + buf_size = (mc_buffer_size_u_v_h << 16) * 3; +#endif + if (debug) { + pr_info + ("init_buf_list num %d (width %d height %d):\n", + hevc->used_buf_num, pic_width, pic_height); + } + } + + pr_info("allocate begin\n"); + //get_cma_alloc_ref(); + atomic_inc(&cma_allocate); + + for (i = 0; i < hevc->used_buf_num; i++) { + if (((i + 1) * buf_size) > hevc->mc_buf->buf_size) { + if (use_cma) + hevc->use_cma_flag = 1; + else { + if (debug) { + pr_info("%s maximum buf size is used\n", + __func__); + } + break; + } + } + + + if (!mmu_enable) { + hevc->m_BUF[i].alloc_flag = 0; + hevc->m_BUF[i].index = i; + + if (use_cma == 2) + hevc->use_cma_flag = 1; + if (hevc->use_cma_flag) { + if ((hevc->m_BUF[i].cma_page_count != 0) + && (hevc->m_BUF[i].alloc_addr != 0) + && (hevc->m_BUF[i].size != buf_size)) { + if ((hevc->predisp_addr >= + hevc->m_BUF[i].alloc_addr) + && (hevc->predisp_addr < + (hevc->m_BUF[i].alloc_addr + + hevc->m_BUF[i].size))) { + hevc->pre_last_frame_alloc_addr = + hevc->m_BUF[i].alloc_addr; + hevc->pre_last_frame_alloc_size = + hevc->m_BUF[i].size; + } else { + codec_mm_free_for_dma(MEM_NAME, + hevc->m_BUF[i].alloc_addr); + pr_info("release cma buffer[%d] (%d %ld)\n", + i, hevc->m_BUF[i].cma_page_count, + hevc->m_BUF[i].alloc_addr); + } + hevc->m_BUF[i].alloc_addr = 0; + hevc->m_BUF[i].cma_page_count = 0; + } + if (hevc->m_BUF[i].alloc_addr == 0) { + if (!codec_mm_enough_for_size(buf_size, 1)) { + /* + *not enough mem for buffer. + */ + pr_info("not enought buffer for [%d],%d\n", + i, buf_size); + hevc->m_BUF[i].cma_page_count = 0; + if (i <= 8) { + /* + *if alloced (i+1)>=9 + *don't send errors. + */ + hevc->fatal_error |= + DECODER_FATAL_ERROR_NO_MEM; + } + break; + } + hevc->m_BUF[i].cma_page_count = + PAGE_ALIGN(buf_size) / PAGE_SIZE; + hevc->m_BUF[i].alloc_addr = + codec_mm_alloc_for_dma( + MEM_NAME, hevc->m_BUF[i].cma_page_count, + 4 + PAGE_SHIFT, + CODEC_MM_FLAGS_FOR_VDECODER); + if (hevc->m_BUF[i].alloc_addr == 0) { + pr_info("alloc cma buffer[%d] fail\n", + i); + hevc->m_BUF[i].cma_page_count = 0; + break; + } + pr_debug("allocate cma buffer[%d] (%d,%ld,%ld)\n", + i, + hevc->m_BUF[i].cma_page_count, + hevc->m_BUF[i].alloc_addr, + hevc->m_BUF[i].start_adr); + } else { + pr_info("reuse cma buffer[%d] (%d,%ld,%ld)\n", + i, + hevc->m_BUF[i].cma_page_count, + hevc->m_BUF[i].alloc_addr, + hevc->m_BUF[i].start_adr); + } + hevc->m_BUF[i].start_adr = hevc->m_BUF[i].alloc_addr; + } else { + hevc->m_BUF[i].cma_page_count = 0; + hevc->m_BUF[i].alloc_addr = 0; + hevc->m_BUF[i].start_adr = + hevc->mc_buf->buf_start + i * buf_size; + } + hevc->m_BUF[i].size = buf_size; + hevc->m_BUF[i].free_start_adr = hevc->m_BUF[i].start_adr; + + if (((hevc->m_BUF[i].start_adr + buf_size) > mc_buffer_end) + && (hevc->m_BUF[i].alloc_addr == 0)) { + if (debug) { + pr_info + ("Max mc buffer or mpred_mv buffer is used\n"); + } + break; + } + + if (debug) { + pr_info("Buffer %d: start_adr %p size %x\n", i, + (void *)hevc->m_BUF[i].start_adr, + hevc->m_BUF[i].size); + } + } + } + //put_cma_alloc_ref(); + atomic_dec(&cma_allocate); + + pr_info("allocate end\n"); + hevc->buf_num = i; + +} + +static int config_pic(struct hevc_state_s *hevc, struct PIC_s *pic, + unsigned int last_disp_addr) +{ + int ret = -1; + int i; + int pic_width = ((re_config_pic_flag == 0) && buf_alloc_width) ? + buf_alloc_width : hevc->pic_w; + int pic_height = ((re_config_pic_flag == 0) && buf_alloc_height) ? + buf_alloc_height : hevc->pic_h; + int lcu_size = hevc->lcu_size; + int pic_width_lcu = (pic_width % lcu_size) ? pic_width / lcu_size + + 1 : pic_width / lcu_size; + int pic_height_lcu = (pic_height % lcu_size) ? pic_height / lcu_size + + 1 : pic_height / lcu_size; + int lcu_total = pic_width_lcu * pic_height_lcu; + int lcu_size_log2 = hevc->lcu_size_log2; + /* + *int MV_MEM_UNIT=lcu_size_log2== + * 6 ? 0x100 : lcu_size_log2==5 ? 0x40 : 0x10; + */ + int MV_MEM_UNIT = lcu_size_log2 == 6 ? 0x200 : lcu_size_log2 == + 5 ? 0x80 : 0x20; + int mpred_mv_end = hevc->work_space_buf->mpred_mv.buf_start + + hevc->work_space_buf->mpred_mv.buf_size; + unsigned int y_adr = 0; + int buf_size = 0; +#ifdef LOSLESS_COMPRESS_MODE +/*SUPPORT_10BIT*/ + int losless_comp_header_size = + compute_losless_comp_header_size(pic_width, + pic_height); + int losless_comp_body_size = compute_losless_comp_body_size(pic_width, + pic_height, hevc->mem_saving_mode); + int mc_buffer_size = losless_comp_header_size + losless_comp_body_size; + int mc_buffer_size_h = (mc_buffer_size + 0xffff)>>16; + int mc_buffer_size_u_v = 0; + int mc_buffer_size_u_v_h = 0; + + if (double_write_mode) { + int pic_width_dw = ((double_write_mode == 2) || + (double_write_mode == 3)) ? + pic_width / 2 : pic_width; + int pic_height_dw = ((double_write_mode == 2) || + (double_write_mode == 3)) ? + pic_height / 2 : pic_height; + int pic_width_lcu_dw = (pic_width_dw % lcu_size) ? + pic_width_dw / lcu_size + 1 : + pic_width_dw / lcu_size; + int pic_height_lcu_dw = (pic_height_dw % lcu_size) ? + pic_height_dw / lcu_size + 1 : + pic_height_dw / lcu_size; + int lcu_total_dw = pic_width_lcu_dw * pic_height_lcu_dw; + + mc_buffer_size_u_v = lcu_total_dw * lcu_size * lcu_size / 2; + mc_buffer_size_u_v_h = (mc_buffer_size_u_v + 0xffff) >> 16; + /*64k alignment*/ + buf_size = ((mc_buffer_size_u_v_h << 16) * 3); + } + if (mc_buffer_size & 0xffff) { /*64k alignment*/ + mc_buffer_size_h += 1; + } + if ((double_write_mode & 0x10) == 0) + buf_size += (mc_buffer_size_h << 16); +#else + int mc_buffer_size_u_v = lcu_total * lcu_size * lcu_size / 2; + int mc_buffer_size_u_v_h = (mc_buffer_size_u_v + 0xffff) >> 16; + /*64k alignment*/ + buf_size = ((mc_buffer_size_u_v_h << 16) * 3); +#endif + + + if (mmu_enable) { + if ((hevc->work_space_buf->cm_header.buf_start + + ((pic->index + 1) + * MMU_COMPRESS_HEADER_SIZE)) + > (hevc->work_space_buf->cm_header.buf_start + + hevc->work_space_buf->cm_header.buf_size)) { + pr_info("MMU header_adr allocate fail\n"); + return -1; + } + + pic->header_adr = hevc->work_space_buf->cm_header.buf_start + + (pic->index * MMU_COMPRESS_HEADER_SIZE); + if (last_disp_addr && pic->header_adr == last_disp_addr) { + /*if same as disp add used last one.*/ + pr_info("same as disp %d: %d\n", + pic->index, pic->header_adr); + pic->header_adr = + hevc->work_space_buf->cm_header.buf_start + + (16 * MMU_COMPRESS_HEADER_SIZE); + } + if (debug&H265_DEBUG_BUFMGR) { + pr_info("MMU header_adr %d: %x\n", + pic->index, pic->header_adr); + } + } + + + if ((hevc->work_space_buf->mpred_mv.buf_start + (((pic->index + 1) + * lcu_total) * MV_MEM_UNIT)) + <= mpred_mv_end) { + + if (!mmu_enable) { + for (i = 0; i < hevc->buf_num; i++) { + y_adr = ((hevc->m_BUF[i].free_start_adr + + 0xffff) >> 16) << 16; + /*64k alignment*/ + if ((y_adr+buf_size) <= + (hevc->m_BUF[i].start_adr+ + hevc->m_BUF[i].size)) { + hevc->m_BUF[i].free_start_adr = + y_adr + buf_size; + break; + } + } + } else + i = pic->index; + if (i < hevc->buf_num) { + pic->POC = INVALID_POC; + /* + *ensure get_pic_by_POC() + *not get the buffer not decoded + */ + pic->BUF_index = i; +#ifdef LOSLESS_COMPRESS_MODE +/*SUPPORT_10BIT*/ + pic->comp_body_size = losless_comp_body_size; + pic->buf_size = buf_size; + + if (!mmu_enable) + pic->mc_y_adr = y_adr; + else if (double_write_mode) { + if ((hevc->mc_buf->buf_start + + (i + 1) * buf_size) + < hevc->mc_buf->buf_end) + y_adr = hevc->mc_buf->buf_start + + i * buf_size; + else { + pic->cma_alloc_addr + = codec_mm_alloc_for_dma( + MEM_NAME, buf_size/PAGE_SIZE, + 4 + PAGE_SHIFT, + CODEC_MM_FLAGS_FOR_VDECODER); + if (pic->cma_alloc_addr) + y_adr = pic->cma_alloc_addr; + else + return -1; + } + } + pic->mc_canvas_y = pic->index; + pic->mc_canvas_u_v = pic->index; + if (!mmu_enable && double_write_mode & 0x10) { + pic->mc_u_v_adr = y_adr + + ((mc_buffer_size_u_v_h << 16) << 1); + + pic->mc_canvas_y = (pic->index << 1); + pic->mc_canvas_u_v = (pic->index << 1) + 1; + + pic->dw_y_adr = y_adr; + pic->dw_u_v_adr = pic->mc_u_v_adr; + } else if (double_write_mode) { + if (mmu_enable) + pic->dw_y_adr = y_adr; + else + pic->dw_y_adr = y_adr + + (mc_buffer_size_h << 16); + pic->dw_u_v_adr = pic->dw_y_adr + + ((mc_buffer_size_u_v_h << 16) << 1); + } +#else + pic->buf_size = (mc_buffer_size_u_v_h << 16) * 3; + pic->mc_y_adr = y_adr; + pic->mc_u_v_adr = y_adr + + ((mc_buffer_size_u_v_h << 16) << 1); + + pic->mc_canvas_y = (pic->index << 1); + pic->mc_canvas_u_v = (pic->index << 1) + 1; +#endif + pic->mpred_mv_wr_start_addr = + hevc->work_space_buf->mpred_mv.buf_start + + ((pic->index * lcu_total) + * MV_MEM_UNIT); + + + if (!mmu_enable && (hevc->predisp_addr != 0) && + (hevc->predisp_size != 0) && + (buffer_mode & 0x4) == 0) { + if ((pic->mc_y_adr >= + (hevc->predisp_addr + + hevc->predisp_size)) || + ((pic->mc_y_adr + pic->buf_size) <= + hevc->predisp_addr)) { + pic->used_by_display = 0; + } else { + pic->used_by_display = 1; + pr_info("%s, pic[%d] is displayed now, do not use it before it is not displayed\n", + __func__, i); + } + } else + pic->used_by_display = 0; + + if (debug) { + pr_info + ("%s index %d BUF_index %d mc_y_adr %x ", + __func__, pic->index, + pic->BUF_index, pic->mc_y_adr); +#ifdef LOSLESS_COMPRESS_MODE + pr_info + ("comp_body_size %x comp_buf_size %x ", + pic->comp_body_size, pic->buf_size); + pr_info + ("mpred_mv_wr_start_adr %x\n", + pic->mpred_mv_wr_start_addr); + if (mmu_enable && double_write_mode) + pr_info + ("mmu double write adr %ld\n", + pic->cma_alloc_addr); + +#else + pr_info + ("mc_u_v_adr %x mpred_mv_wr_start_adr %x\n", + pic->mc_u_v_adr, pic->mpred_mv_wr_start_addr); +#endif + } + ret = 0; + } + } + return ret; +} + +/* +*free hevc->m_BUF[..] for all free hevc->m_PIC[..] +* with the different size of hevc->pic_w,hevc->pic_h +*/ +static int recycle_buf(struct hevc_state_s *hevc) +{ + int i, j; + + for (i = 0; i < MAX_REF_PIC_NUM; i++) { + struct PIC_s *pic = &hevc->m_PIC[i]; + + if (pic->index == -1) + continue; + if (pic->width != hevc->pic_w || pic->height != hevc->pic_h) { + if (pic->output_mark == 0 && pic->referenced == 0 + && pic->output_ready == 0) { + if (mmu_enable) { + decoder_mmu_box_free_idx(hevc->mmu_box, + pic->index); + } + pic->BUF_index = -1; + if (debug) { + pr_info("%s: %d\n", __func__, + pic->index); + } + } + } + } + + for (i = 0; i < hevc->buf_num; i++) { + if (hevc->m_BUF[i].free_start_adr != hevc->m_BUF[i].start_adr) { + for (j = 0; j < MAX_REF_PIC_NUM; j++) { + struct PIC_s *pic = &hevc->m_PIC[j]; + + if (pic->index == -1) + continue; + if (pic->BUF_index == i) + break; + } + if (j == MAX_REF_PIC_NUM) + hevc->m_BUF[i].free_start_adr = + hevc->m_BUF[i].start_adr; + } + } + return 0; +} + +//need export.. +extern int get_video0_frame_info(struct vframe_s *vf); + +static void init_pic_list(struct hevc_state_s *hevc) +{ + int i; + struct vframe_s vf; + unsigned long flags; + unsigned long disp_addr = 0; + + if (!get_video0_frame_info(&vf)) { + spin_lock_irqsave(&lock, flags); + if (vf.type & VIDTYPE_SCATTER) { + /*sc only used header.*/ + disp_addr = (VSYNC_RD_MPEG_REG(AFBC_HEAD_BADDR) << 4); + } else if (vf.type & VIDTYPE_COMPRESS) { + /*sc checked body.*/ + disp_addr = (VSYNC_RD_MPEG_REG(AFBC_BODY_BADDR) << 4); + } else { + struct canvas_s cur_canvas; + + canvas_read(vf.canvas0Addr & 0xff, &cur_canvas); + disp_addr = cur_canvas.addr; + } + spin_unlock_irqrestore(&lock, flags); + } + for (i = 0; i < MAX_REF_PIC_NUM; i++) { + struct PIC_s *pic = &hevc->m_PIC[i]; + + memset(pic, 0, sizeof(struct PIC_s)); + pic->index = i; + pic->BUF_index = -1; + if (config_pic(hevc, pic, disp_addr) < 0) { + if (debug) + pr_info("Config_pic %d fail\n", pic->index); + pic->index = -1; + break; + } + pic->width = hevc->pic_w; + pic->height = hevc->pic_h; + if (double_write_mode) + set_canvas(pic); + } + + for (; i < MAX_REF_PIC_NUM; i++) { + struct PIC_s *pic = &hevc->m_PIC[i]; + + memset(pic, 0, sizeof(struct PIC_s)); + pic->index = -1; + pic->BUF_index = -1; + } + +} + +#ifdef LOSLESS_COMPRESS_MODE +static void init_decode_head_hw(struct hevc_state_s *hevc) +{ + + struct BuffInfo_s *buf_spec = hevc->work_space_buf; + unsigned int data32; + + int losless_comp_header_size = + compute_losless_comp_header_size(hevc->pic_w, + hevc->pic_h); + int losless_comp_body_size = compute_losless_comp_body_size(hevc->pic_w, + hevc->pic_h, hevc->mem_saving_mode); + + hevc->losless_comp_body_size = losless_comp_body_size; + + + if (mmu_enable) { + WRITE_VREG(HEVCD_MPP_DECOMP_CTL1, (0x1 << 4)); + WRITE_VREG(HEVCD_MPP_DECOMP_CTL2, 0x0); + } else { + if (hevc->mem_saving_mode == 1) + WRITE_VREG(HEVCD_MPP_DECOMP_CTL1, + (1 << 3) | ((workaround_enable & 2) ? 1 : 0)); + else + WRITE_VREG(HEVCD_MPP_DECOMP_CTL1, + ((workaround_enable & 2) ? 1 : 0)); + WRITE_VREG(HEVCD_MPP_DECOMP_CTL2, (losless_comp_body_size >> 5)); + /* + *WRITE_VREG(HEVCD_MPP_DECOMP_CTL3,(0xff<<20) | (0xff<<10) | 0xff); + * //8-bit mode + */ + } + WRITE_VREG(HEVC_CM_BODY_LENGTH, losless_comp_body_size); + WRITE_VREG(HEVC_CM_HEADER_OFFSET, losless_comp_body_size); + WRITE_VREG(HEVC_CM_HEADER_LENGTH, losless_comp_header_size); + + if (mmu_enable) { + WRITE_VREG(HEVC_SAO_MMU_VH0_ADDR, buf_spec->mmu_vbh.buf_start); + WRITE_VREG(HEVC_SAO_MMU_VH1_ADDR, + buf_spec->mmu_vbh.buf_start + + buf_spec->mmu_vbh.buf_size/2); + data32 = READ_VREG(HEVC_SAO_CTRL9); + data32 |= 0x1; + WRITE_VREG(HEVC_SAO_CTRL9, data32); + + /* use HEVC_CM_HEADER_START_ADDR */ + data32 = READ_VREG(HEVC_SAO_CTRL5); + data32 |= (1<<10); + WRITE_VREG(HEVC_SAO_CTRL5, data32); + } + + + pr_info("%s: (%d, %d) body_size 0x%x header_size 0x%x\n", + __func__, hevc->pic_w, hevc->pic_h, + losless_comp_body_size, losless_comp_header_size); + +} +#endif +#define HEVCD_MPP_ANC2AXI_TBL_DATA 0x3464 + +static void init_pic_list_hw(struct hevc_state_s *hevc) +{ + int i; + int cur_pic_num = MAX_REF_PIC_NUM; + + if (get_cpu_type() >= MESON_CPU_MAJOR_ID_GXL) + WRITE_VREG(HEVCD_MPP_ANC2AXI_TBL_CONF_ADDR, + (0x1 << 1) | (0x1 << 2)); + else + WRITE_VREG(HEVCD_MPP_ANC2AXI_TBL_CONF_ADDR, 0x0); + + for (i = 0; i < MAX_REF_PIC_NUM; i++) { + if (hevc->m_PIC[i].index == -1) { + cur_pic_num = i; + break; + } + if (get_cpu_type() >= MESON_CPU_MAJOR_ID_GXL) { + if (mmu_enable) + WRITE_VREG(HEVCD_MPP_ANC2AXI_TBL_DATA, + hevc->m_PIC[i].header_adr>>5); + else + WRITE_VREG(HEVCD_MPP_ANC2AXI_TBL_DATA, + hevc->m_PIC[i].mc_y_adr >> 5); + } else + WRITE_VREG(HEVCD_MPP_ANC2AXI_TBL_CMD_ADDR, + hevc->m_PIC[i].mc_y_adr | + (hevc->m_PIC[i].mc_canvas_y << 8) | 0x1); + if (double_write_mode & 0x10) { + if (get_cpu_type() >= MESON_CPU_MAJOR_ID_GXL) { + if (mmu_enable) + WRITE_VREG(HEVCD_MPP_ANC2AXI_TBL_DATA, + hevc->m_PIC[i].header_adr>>5); + else + WRITE_VREG(HEVCD_MPP_ANC2AXI_TBL_DATA, + hevc->m_PIC[i].mc_u_v_adr >> 5); + } + else + WRITE_VREG(HEVCD_MPP_ANC2AXI_TBL_CMD_ADDR, + hevc->m_PIC[i].mc_u_v_adr | + (hevc->m_PIC[i].mc_canvas_u_v << 8) + | 0x1); + } + } + + for (; i < MAX_REF_PIC_NUM; i++) { + if (get_cpu_type() >= MESON_CPU_MAJOR_ID_GXL) { + if (mmu_enable) + WRITE_VREG(HEVCD_MPP_ANC2AXI_TBL_DATA, + hevc->m_PIC[cur_pic_num-1].header_adr>>5); + else + WRITE_VREG(HEVCD_MPP_ANC2AXI_TBL_DATA, + hevc->m_PIC[cur_pic_num-1].mc_y_adr >> 5); +#ifndef LOSLESS_COMPRESS_MODE + WRITE_VREG(HEVCD_MPP_ANC2AXI_TBL_DATA, + hevc->m_PIC[cur_pic_num-1].mc_u_v_adr >> 5); +#endif + } else { + WRITE_VREG(HEVCD_MPP_ANC2AXI_TBL_CMD_ADDR, + hevc->m_PIC[cur_pic_num-1].mc_y_adr| + (hevc->m_PIC[cur_pic_num-1].mc_canvas_y<<8) + | 0x1); +#ifndef LOSLESS_COMPRESS_MODE + WRITE_VREG(HEVCD_MPP_ANC2AXI_TBL_CMD_ADDR, + hevc->m_PIC[cur_pic_num-1].mc_u_v_adr| + (hevc->m_PIC[cur_pic_num-1].mc_canvas_u_v<<8) + | 0x1); +#endif + } + } + + WRITE_VREG(HEVCD_MPP_ANC2AXI_TBL_CONF_ADDR, 0x1); + + /* Zero out canvas registers in IPP -- avoid simulation X */ + WRITE_VREG(HEVCD_MPP_ANC_CANVAS_ACCCONFIG_ADDR, + (0 << 8) | (0 << 1) | 1); + for (i = 0; i < 32; i++) + WRITE_VREG(HEVCD_MPP_ANC_CANVAS_DATA_ADDR, 0); + +#ifdef LOSLESS_COMPRESS_MODE + if ((double_write_mode & 0x10) == 0) + init_decode_head_hw(hevc); +#endif + +} + + +static void dump_pic_list(struct hevc_state_s *hevc) +{ + int i; + struct PIC_s *pic; + + pr_info("pic_list_init_flag is %d\r\n", hevc->pic_list_init_flag); + for (i = 0; i < MAX_REF_PIC_NUM; i++) { + pic = &hevc->m_PIC[i]; + if (pic->index == -1) + continue; + pr_info + ("index %d decode_idx:%d, POC:%d, referenced:%d, ", + pic->index, pic->decode_idx, pic->POC, pic->referenced); + pr_info("num_reorder_pic:%d, output_mark:%d, w/h %d,%d", + pic->num_reorder_pic, pic->output_mark, + pic->width, pic->height); + pr_info("output_ready:%d, mv_wr_start %x vf_ref %d\n", + pic->output_ready, pic->mpred_mv_wr_start_addr, + pic->vf_ref); + } +} + +static struct PIC_s *output_pic(struct hevc_state_s *hevc, + unsigned char flush_flag) +{ + int num_pic_not_yet_display = 0; + int i; + struct PIC_s *pic; + struct PIC_s *pic_display = NULL; + + if (i_only_flag & 0x4) { + for (i = 0; i < MAX_REF_PIC_NUM; i++) { + pic = &hevc->m_PIC[i]; + if ((pic->index == -1) || (pic->POC == INVALID_POC)) + continue; + if (pic->output_mark) { + if (pic_display) { + if (pic->decode_idx < + pic_display->decode_idx) + pic_display = pic; + + } else + pic_display = pic; + + } + } + if (pic_display) { + pic_display->output_mark = 0; + pic_display->recon_mark = 0; + pic_display->output_ready = 1; + pic_display->referenced = 0; + } + } else { + for (i = 0; i < MAX_REF_PIC_NUM; i++) { + pic = &hevc->m_PIC[i]; + if ((pic->index == -1) || (pic->POC == INVALID_POC)) + continue; + if (pic->output_mark) + num_pic_not_yet_display++; + } + + for (i = 0; i < MAX_REF_PIC_NUM; i++) { + pic = &hevc->m_PIC[i]; + if ((pic->index == -1) || (pic->POC == INVALID_POC)) + continue; + if (pic->output_mark) { + if (pic_display) { + if (pic->POC < pic_display->POC) + pic_display = pic; + else if ((pic->POC == pic_display->POC) + && (pic->decode_idx < + pic_display-> + decode_idx)) + pic_display + = pic; + } else + pic_display = pic; + } + } + if (pic_display) { + if ((num_pic_not_yet_display > + pic_display->num_reorder_pic) + || flush_flag) { + pic_display->output_mark = 0; + pic_display->recon_mark = 0; + pic_display->output_ready = 1; + } else if (num_pic_not_yet_display >= + (MAX_REF_PIC_NUM - 1)) { + pic_display->output_mark = 0; + pic_display->recon_mark = 0; + pic_display->output_ready = 1; + pr_info("Warning, num_reorder_pic %d is byeond buf num\n", + pic_display->num_reorder_pic); + } else + pic_display = NULL; + } + } + return pic_display; +} + +static int config_mc_buffer(struct hevc_state_s *hevc, struct PIC_s *cur_pic) +{ + int i; + struct PIC_s *pic; + + if (debug & H265_DEBUG_BUFMGR) + pr_info("config_mc_buffer entered .....\n"); + if (cur_pic->slice_type != 2) { /* P and B pic */ + WRITE_VREG(HEVCD_MPP_ANC_CANVAS_ACCCONFIG_ADDR, + (0 << 8) | (0 << 1) | 1); + for (i = 0; i < cur_pic->RefNum_L0; i++) { + pic = + get_ref_pic_by_POC(hevc, + cur_pic-> + m_aiRefPOCList0[cur_pic-> + slice_idx][i]); + if (pic) { + if (pic->error_mark) + cur_pic->error_mark = 1; + WRITE_VREG(HEVCD_MPP_ANC_CANVAS_DATA_ADDR, + (pic->mc_canvas_u_v << 16) + | (pic->mc_canvas_u_v + << 8) | + pic->mc_canvas_y); + if (debug & H265_DEBUG_BUFMGR) { + pr_info + ("refid %x mc_canvas_u_v %x", + i, pic->mc_canvas_u_v); + pr_info(" mc_canvas_y %x\n", + pic->mc_canvas_y); + } + } else { + if (debug) { + pr_info + ("Error %s, %dth poc (%d)", + __func__, i, + cur_pic->m_aiRefPOCList0[cur_pic-> + slice_idx][i]); + pr_info + (" of RPS is not in the pic list0\n"); + } + cur_pic->error_mark = 1; + /* dump_lmem(); */ + } + } + } + if (cur_pic->slice_type == 0) { /* B pic */ + if (debug & H265_DEBUG_BUFMGR) + pr_info("config_mc_buffer RefNum_L1\n"); + WRITE_VREG(HEVCD_MPP_ANC_CANVAS_ACCCONFIG_ADDR, + (16 << 8) | (0 << 1) | 1); + for (i = 0; i < cur_pic->RefNum_L1; i++) { + pic = + get_ref_pic_by_POC(hevc, + cur_pic-> + m_aiRefPOCList1[cur_pic-> + slice_idx][i]); + if (pic) { + if (pic->error_mark) + cur_pic->error_mark = 1; + WRITE_VREG(HEVCD_MPP_ANC_CANVAS_DATA_ADDR, + (pic->mc_canvas_u_v << 16) + | (pic->mc_canvas_u_v + << 8) | + pic->mc_canvas_y); + if (debug & H265_DEBUG_BUFMGR) { + pr_info + ("refid %x mc_canvas_u_v %x", + i, pic->mc_canvas_u_v); + pr_info(" mc_canvas_y %x\n", + pic->mc_canvas_y); + } + } else { + if (debug) { + pr_info + ("Error %s, %dth poc (%d)", + __func__, i, + cur_pic->m_aiRefPOCList1[cur_pic-> + slice_idx][i]); + pr_info + (" of RPS is not in the pic list1\n"); + } + cur_pic->error_mark = 1; + /* dump_lmem(); */ + } + } + } + return 0; +} + +static void apply_ref_pic_set(struct hevc_state_s *hevc, int cur_poc, + union param_u *params) +{ + int ii, i; + int poc_tmp; + struct PIC_s *pic; + unsigned char is_referenced; + + /* pr_info("%s cur_poc %d\n", __func__, cur_poc); */ + for (ii = 0; ii < MAX_REF_PIC_NUM; ii++) { + pic = &hevc->m_PIC[ii]; + if (pic->index == -1) + continue; + + if ((pic->referenced == 0 || pic->POC == cur_poc)) + continue; + is_referenced = 0; + for (i = 0; i < 16; i++) { + int delt; + + if (params->p.CUR_RPS[i] & 0x8000) + break; + delt = + params->p.CUR_RPS[i] & + ((1 << (RPS_USED_BIT - 1)) - 1); + if (params->p.CUR_RPS[i] & (1 << (RPS_USED_BIT - 1))) { + poc_tmp = + cur_poc - ((1 << (RPS_USED_BIT - 1)) - + delt); + } else + poc_tmp = cur_poc + delt; + if (poc_tmp == pic->POC) { + is_referenced = 1; + /* pr_info("i is %d\n", i); */ + break; + } + } + if (is_referenced == 0) { + pic->referenced = 0; + /* pr_info("set poc %d reference to 0\n", pic->POC); */ + } + } + +} + +static void set_ref_pic_list(struct hevc_state_s *hevc, union param_u *params) +{ + struct PIC_s *pic = hevc->cur_pic; + int i, rIdx; + int num_neg = 0; + int num_pos = 0; + int total_num; + int num_ref_idx_l0_active = + (params->p.num_ref_idx_l0_active > + MAX_REF_ACTIVE) ? MAX_REF_ACTIVE : + params->p.num_ref_idx_l0_active; + int num_ref_idx_l1_active = + (params->p.num_ref_idx_l1_active > + MAX_REF_ACTIVE) ? MAX_REF_ACTIVE : + params->p.num_ref_idx_l1_active; + + int RefPicSetStCurr0[16]; + int RefPicSetStCurr1[16]; + + for (i = 0; i < 16; i++) { + RefPicSetStCurr0[i] = 0; + RefPicSetStCurr1[i] = 0; + pic->m_aiRefPOCList0[pic->slice_idx][i] = 0; + pic->m_aiRefPOCList1[pic->slice_idx][i] = 0; + } + for (i = 0; i < 16; i++) { + if (params->p.CUR_RPS[i] & 0x8000) + break; + if ((params->p.CUR_RPS[i] >> RPS_USED_BIT) & 1) { + int delt = + params->p.CUR_RPS[i] & + ((1 << (RPS_USED_BIT - 1)) - 1); + + if ((params->p.CUR_RPS[i] >> (RPS_USED_BIT - 1)) & 1) { + RefPicSetStCurr0[num_neg] = + pic->POC - ((1 << (RPS_USED_BIT - 1)) - + delt); + /* + *pr_info("RefPicSetStCurr0 %x %x %x\n", + *RefPicSetStCurr0[num_neg], pic->POC, + *(0x800-(params[i]&0x7ff))); + */ + num_neg++; + } else { + RefPicSetStCurr1[num_pos] = pic->POC + delt; + /* + *pr_info("RefPicSetStCurr1 %d\n", + *RefPicSetStCurr1[num_pos]); + */ + num_pos++; + } + } + } + total_num = num_neg + num_pos; + if (debug & H265_DEBUG_BUFMGR) { + pr_info + ("%s: curpoc %d slice_type %d, total %d ", + __func__, pic->POC, params->p.slice_type, total_num); + pr_info("num_neg %d num_list0 %d num_list1 %d\n", + num_neg, num_ref_idx_l0_active, num_ref_idx_l1_active); + } + + if (debug & H265_DEBUG_BUFMGR) { + pr_info + ("HEVC Stream buf start "); + pr_info("%x end %x wr %x rd %x lev %x ctl %x intctl %x\n", + READ_VREG(HEVC_STREAM_START_ADDR), + READ_VREG(HEVC_STREAM_END_ADDR), + READ_VREG(HEVC_STREAM_WR_PTR), + READ_VREG(HEVC_STREAM_RD_PTR), + READ_VREG(HEVC_STREAM_LEVEL), + READ_VREG(HEVC_STREAM_FIFO_CTL), + READ_VREG(HEVC_PARSER_INT_CONTROL)); + } + + if (total_num > 0) { + if (params->p.modification_flag & 0x1) { + if (debug & H265_DEBUG_BUFMGR) + pr_info("ref0 POC (modification):"); + for (rIdx = 0; rIdx < num_ref_idx_l0_active; rIdx++) { + int cIdx = params->p.modification_list[rIdx]; + + pic->m_aiRefPOCList0[pic->slice_idx][rIdx] = + cIdx >= + num_neg ? RefPicSetStCurr1[cIdx - + num_neg] : + RefPicSetStCurr0[cIdx]; + if (debug & H265_DEBUG_BUFMGR) { + pr_info("%d ", + pic->m_aiRefPOCList0[pic-> + slice_idx] + [rIdx]); + } + } + } else { + if (debug & H265_DEBUG_BUFMGR) + pr_info("ref0 POC:"); + for (rIdx = 0; rIdx < num_ref_idx_l0_active; rIdx++) { + int cIdx = rIdx % total_num; + + pic->m_aiRefPOCList0[pic->slice_idx][rIdx] = + cIdx >= + num_neg ? RefPicSetStCurr1[cIdx - + num_neg] : + RefPicSetStCurr0[cIdx]; + if (debug & H265_DEBUG_BUFMGR) { + pr_info("%d ", + pic->m_aiRefPOCList0[pic-> + slice_idx] + [rIdx]); + } + } + } + if (debug & H265_DEBUG_BUFMGR) + pr_info("\n"); + if (params->p.slice_type == B_SLICE) { + if (params->p.modification_flag & 0x2) { + if (debug & H265_DEBUG_BUFMGR) + pr_info("ref1 POC (modification):"); + for (rIdx = 0; rIdx < num_ref_idx_l1_active; + rIdx++) { + int cIdx; + + if (params->p.modification_flag & 0x1) { + cIdx = + params->p. + modification_list + [num_ref_idx_l0_active + + rIdx]; + } else { + cIdx = + params->p. + modification_list[rIdx]; + } + pic->m_aiRefPOCList1[pic-> + slice_idx][rIdx] = + cIdx >= + num_pos ? + RefPicSetStCurr0[cIdx - num_pos] + : RefPicSetStCurr1[cIdx]; + if (debug & H265_DEBUG_BUFMGR) { + pr_info("%d ", + pic-> + m_aiRefPOCList1[pic-> + slice_idx] + [rIdx]); + } + } + } else { + if (debug & H265_DEBUG_BUFMGR) + pr_info("ref1 POC:"); + for (rIdx = 0; rIdx < num_ref_idx_l1_active; + rIdx++) { + int cIdx = rIdx % total_num; + + pic->m_aiRefPOCList1[pic-> + slice_idx][rIdx] = + cIdx >= + num_pos ? + RefPicSetStCurr0[cIdx - + num_pos] + : RefPicSetStCurr1[cIdx]; + if (debug & H265_DEBUG_BUFMGR) { + pr_info("%d ", + pic-> + m_aiRefPOCList1[pic-> + slice_idx] + [rIdx]); + } + } + } + if (debug & H265_DEBUG_BUFMGR) + pr_info("\n"); + } + } + /*set m_PIC */ + pic->slice_type = (params->p.slice_type == I_SLICE) ? 2 : + (params->p.slice_type == P_SLICE) ? 1 : + (params->p.slice_type == B_SLICE) ? 0 : 3; + pic->RefNum_L0 = num_ref_idx_l0_active; + pic->RefNum_L1 = num_ref_idx_l1_active; +} + +static void update_tile_info(struct hevc_state_s *hevc, int pic_width_cu, + int pic_height_cu, int sao_mem_unit, + union param_u *params) +{ + int i, j; + int start_cu_x, start_cu_y; + int sao_vb_size = (sao_mem_unit + (2 << 4)) * pic_height_cu; + int sao_abv_size = sao_mem_unit * pic_width_cu; + + hevc->tile_enabled = params->p.tiles_enabled_flag & 1; + if (params->p.tiles_enabled_flag & 1) { + hevc->num_tile_col = params->p.num_tile_columns_minus1 + 1; + hevc->num_tile_row = params->p.num_tile_rows_minus1 + 1; + + if (hevc->num_tile_row > MAX_TILE_ROW_NUM + || hevc->num_tile_row <= 0) { + hevc->num_tile_row = 1; + pr_info("%s: num_tile_rows_minus1 (%d) error!!\n", + __func__, params->p.num_tile_rows_minus1); + } + if (hevc->num_tile_col > MAX_TILE_COL_NUM + || hevc->num_tile_col <= 0) { + hevc->num_tile_col = 1; + pr_info("%s: num_tile_columns_minus1 (%d) error!!\n", + __func__, params->p.num_tile_columns_minus1); + } + if (debug & H265_DEBUG_BUFMGR) { + pr_info + ("%s pic_w_cu %d pic_h_cu %d tile_enabled ", + __func__, pic_width_cu, pic_height_cu); + pr_info("num_tile_col %d num_tile_row %d:\n", + hevc->num_tile_col, hevc->num_tile_row); + } + + if (params->p.tiles_enabled_flag & 2) { /* uniform flag */ + int w = pic_width_cu / hevc->num_tile_col; + int h = pic_height_cu / hevc->num_tile_row; + + start_cu_y = 0; + for (i = 0; i < hevc->num_tile_row; i++) { + start_cu_x = 0; + for (j = 0; j < hevc->num_tile_col; j++) { + if (j == (hevc->num_tile_col - 1)) { + hevc->m_tile[i][j].width = + pic_width_cu - + start_cu_x; + } else + hevc->m_tile[i][j].width = w; + if (i == (hevc->num_tile_row - 1)) { + hevc->m_tile[i][j].height = + pic_height_cu - + start_cu_y; + } else + hevc->m_tile[i][j].height = h; + hevc->m_tile[i][j].start_cu_x + = start_cu_x; + hevc->m_tile[i][j].start_cu_y + = start_cu_y; + hevc->m_tile[i][j].sao_vb_start_addr = + hevc->work_space_buf->sao_vb. + buf_start + j * sao_vb_size; + hevc->m_tile[i][j].sao_abv_start_addr = + hevc->work_space_buf->sao_abv. + buf_start + i * sao_abv_size; + if (debug & H265_DEBUG_BUFMGR) { + pr_info + ("{y=%d, x=%d w %d h %d ", + i, j, hevc->m_tile[i][j].width, + hevc->m_tile[i][j].height); + pr_info + ("start_x %d start_y %d ", + hevc->m_tile[i][j].start_cu_x, + hevc->m_tile[i][j].start_cu_y); + pr_info + ("sao_vb_start 0x%x ", + hevc->m_tile[i][j]. + sao_vb_start_addr); + pr_info + ("sao_abv_start 0x%x}\n", + hevc->m_tile[i][j]. + sao_abv_start_addr); + } + start_cu_x += hevc->m_tile[i][j].width; + + } + start_cu_y += hevc->m_tile[i][0].height; + } + } else { + start_cu_y = 0; + for (i = 0; i < hevc->num_tile_row; i++) { + start_cu_x = 0; + for (j = 0; j < hevc->num_tile_col; j++) { + if (j == (hevc->num_tile_col - 1)) { + hevc->m_tile[i][j].width = + pic_width_cu - + start_cu_x; + } else { + hevc->m_tile[i][j].width = + params->p.tile_width[j]; + } + if (i == (hevc->num_tile_row - 1)) { + hevc->m_tile[i][j].height = + pic_height_cu - + start_cu_y; + } else { + hevc->m_tile[i][j].height = + params-> + p.tile_height[i]; + } + hevc->m_tile[i][j].start_cu_x + = start_cu_x; + hevc->m_tile[i][j].start_cu_y + = start_cu_y; + hevc->m_tile[i][j].sao_vb_start_addr = + hevc->work_space_buf->sao_vb. + buf_start + j * sao_vb_size; + hevc->m_tile[i][j].sao_abv_start_addr = + hevc->work_space_buf->sao_abv. + buf_start + i * sao_abv_size; + if (debug & H265_DEBUG_BUFMGR) { + pr_info + ("{y=%d, x=%d w %d h %d ", + i, j, hevc->m_tile[i][j].width, + hevc->m_tile[i][j].height); + pr_info + ("start_x %d start_y %d ", + hevc->m_tile[i][j].start_cu_x, + hevc->m_tile[i][j].start_cu_y); + pr_info + ("sao_vb_start 0x%x ", + hevc->m_tile[i][j]. + sao_vb_start_addr); + pr_info + ("sao_abv_start 0x%x}\n", + hevc->m_tile[i][j]. + sao_abv_start_addr); + + } + start_cu_x += hevc->m_tile[i][j].width; + } + start_cu_y += hevc->m_tile[i][0].height; + } + } + } else { + hevc->num_tile_col = 1; + hevc->num_tile_row = 1; + hevc->m_tile[0][0].width = pic_width_cu; + hevc->m_tile[0][0].height = pic_height_cu; + hevc->m_tile[0][0].start_cu_x = 0; + hevc->m_tile[0][0].start_cu_y = 0; + hevc->m_tile[0][0].sao_vb_start_addr = + hevc->work_space_buf->sao_vb.buf_start; + hevc->m_tile[0][0].sao_abv_start_addr = + hevc->work_space_buf->sao_abv.buf_start; + } +} + +static int get_tile_index(struct hevc_state_s *hevc, int cu_adr, + int pic_width_lcu) +{ + int cu_x; + int cu_y; + int tile_x = 0; + int tile_y = 0; + int i; + + if (pic_width_lcu == 0) { + if (debug) { + pr_info + ("%s Error, pic_width_lcu is 0, pic_w %d, pic_h %d\n", + __func__, hevc->pic_w, hevc->pic_h); + } + return -1; + } + cu_x = cu_adr % pic_width_lcu; + cu_y = cu_adr / pic_width_lcu; + if (hevc->tile_enabled) { + for (i = 0; i < hevc->num_tile_col; i++) { + if (cu_x >= hevc->m_tile[0][i].start_cu_x) + tile_x = i; + else + break; + } + for (i = 0; i < hevc->num_tile_row; i++) { + if (cu_y >= hevc->m_tile[i][0].start_cu_y) + tile_y = i; + else + break; + } + } + return (tile_x) | (tile_y << 8); +} + +static void print_scratch_error(int error_num) +{ +#if 0 + if (debug) { + pr_info(" ERROR : HEVC_ASSIST_SCRATCH_TEST Error : %d\n", + error_num); + } +#endif +} + +static void hevc_config_work_space_hw(struct hevc_state_s *hevc) +{ + struct BuffInfo_s *buf_spec = hevc->work_space_buf; + + if (debug) + pr_info("%s %x %x %x %x %x %x %x %x %x %x %x %x\n", + __func__, + buf_spec->ipp.buf_start, + buf_spec->start_adr, + buf_spec->short_term_rps.buf_start, + buf_spec->vps.buf_start, + buf_spec->sps.buf_start, + buf_spec->pps.buf_start, + buf_spec->sao_up.buf_start, + buf_spec->swap_buf.buf_start, + buf_spec->swap_buf2.buf_start, + buf_spec->scalelut.buf_start, + buf_spec->dblk_para.buf_start, + buf_spec->dblk_data.buf_start); + WRITE_VREG(HEVCD_IPP_LINEBUFF_BASE, buf_spec->ipp.buf_start); + if ((debug & H265_DEBUG_SEND_PARAM_WITH_REG) == 0) + WRITE_VREG(HEVC_RPM_BUFFER, (u32)hevc->rpm_phy_addr); + WRITE_VREG(HEVC_SHORT_TERM_RPS, buf_spec->short_term_rps.buf_start); + WRITE_VREG(HEVC_VPS_BUFFER, buf_spec->vps.buf_start); + WRITE_VREG(HEVC_SPS_BUFFER, buf_spec->sps.buf_start); + WRITE_VREG(HEVC_PPS_BUFFER, buf_spec->pps.buf_start); + WRITE_VREG(HEVC_SAO_UP, buf_spec->sao_up.buf_start); + if (mmu_enable) + WRITE_VREG(H265_MMU_MAP_BUFFER, hevc->frame_mmu_map_phy_addr); + else + WRITE_VREG(HEVC_STREAM_SWAP_BUFFER, + buf_spec->swap_buf.buf_start); + WRITE_VREG(HEVC_STREAM_SWAP_BUFFER2, buf_spec->swap_buf2.buf_start); + WRITE_VREG(HEVC_SCALELUT, buf_spec->scalelut.buf_start); + /* cfg_p_addr */ + WRITE_VREG(HEVC_DBLK_CFG4, buf_spec->dblk_para.buf_start); + /* cfg_d_addr */ + WRITE_VREG(HEVC_DBLK_CFG5, buf_spec->dblk_data.buf_start); + + if (debug & H265_DEBUG_UCODE) + WRITE_VREG(LMEM_DUMP_ADR, (u32)hevc->lmem_phy_addr); + +} + +static void hevc_init_decoder_hw(struct hevc_state_s *hevc, + int decode_pic_begin, int decode_pic_num) +{ + unsigned int data32; + int i; + +#if 1 + /* m8baby test1902 */ + if (debug & H265_DEBUG_BUFMGR) + pr_info("[test.c] Test Parser Register Read/Write\n"); + data32 = READ_VREG(HEVC_PARSER_VERSION); + if (data32 != 0x00010001) { + print_scratch_error(25); + return; + } + WRITE_VREG(HEVC_PARSER_VERSION, 0x5a5a55aa); + data32 = READ_VREG(HEVC_PARSER_VERSION); + if (data32 != 0x5a5a55aa) { + print_scratch_error(26); + return; + } +#if 0 + /* test Parser Reset */ + /* reset iqit to start mem init again */ + WRITE_VREG(DOS_SW_RESET3, (1 << 14) | + (1 << 3) /* reset_whole parser */ + ); + WRITE_VREG(DOS_SW_RESET3, 0); /* clear reset_whole parser */ + data32 = READ_VREG(HEVC_PARSER_VERSION); + if (data32 != 0x00010001) + pr_info("Test Parser Fatal Error\n"); +#endif + /* reset iqit to start mem init again */ + WRITE_VREG(DOS_SW_RESET3, (1 << 14) + ); + CLEAR_VREG_MASK(HEVC_CABAC_CONTROL, 1); + CLEAR_VREG_MASK(HEVC_PARSER_CORE_CONTROL, 1); + +#endif + + if (debug & H265_DEBUG_BUFMGR) + pr_info("[test.c] Enable BitStream Fetch\n"); + data32 = READ_VREG(HEVC_STREAM_CONTROL); + data32 = data32 | (1 << 0) /* stream_fetch_enable */ + ; + if (hevc->m_ins_flag) + data32 |= (7 << 4); /*endian control*/ + WRITE_VREG(HEVC_STREAM_CONTROL, data32); + + data32 = READ_VREG(HEVC_SHIFT_STARTCODE); + if (data32 != 0x00000100) { + print_scratch_error(29); + return; + } + data32 = READ_VREG(HEVC_SHIFT_EMULATECODE); + if (data32 != 0x00000300) { + print_scratch_error(30); + return; + } + WRITE_VREG(HEVC_SHIFT_STARTCODE, 0x12345678); + WRITE_VREG(HEVC_SHIFT_EMULATECODE, 0x9abcdef0); + data32 = READ_VREG(HEVC_SHIFT_STARTCODE); + if (data32 != 0x12345678) { + print_scratch_error(31); + return; + } + data32 = READ_VREG(HEVC_SHIFT_EMULATECODE); + if (data32 != 0x9abcdef0) { + print_scratch_error(32); + return; + } + WRITE_VREG(HEVC_SHIFT_STARTCODE, 0x00000100); + WRITE_VREG(HEVC_SHIFT_EMULATECODE, 0x00000300); + + if (debug & H265_DEBUG_BUFMGR) + pr_info("[test.c] Enable HEVC Parser Interrupt\n"); + data32 = READ_VREG(HEVC_PARSER_INT_CONTROL); + data32 &= 0x03ffffff; + data32 = data32 | (3 << 29) | (2 << 26) | (1 << 24) + | /* stream_buffer_empty_int_amrisc_enable */ + (1 << 22) | /* stream_fifo_empty_int_amrisc_enable*/ + (1 << 7) | /* dec_done_int_cpu_enable */ + (1 << 4) | /* startcode_found_int_cpu_enable */ + (0 << 3) | /* startcode_found_int_amrisc_enable */ + (1 << 0) /* parser_int_enable */ + ; + WRITE_VREG(HEVC_PARSER_INT_CONTROL, data32); + + if (debug & H265_DEBUG_BUFMGR) + pr_info("[test.c] Enable HEVC Parser Shift\n"); + + data32 = READ_VREG(HEVC_SHIFT_STATUS); + data32 = data32 | (1 << 1) | /* emulation_check_on */ + (1 << 0) /* startcode_check_on */ + ; + WRITE_VREG(HEVC_SHIFT_STATUS, data32); + + WRITE_VREG(HEVC_SHIFT_CONTROL, (3 << 6) |/* sft_valid_wr_position */ + (2 << 4) | /* emulate_code_length_sub_1 */ + (2 << 1) | /* start_code_length_sub_1 */ + (1 << 0) /* stream_shift_enable */ + ); + + WRITE_VREG(HEVC_CABAC_CONTROL, (1 << 0) /* cabac_enable */ + ); + /* hevc_parser_core_clk_en */ + WRITE_VREG(HEVC_PARSER_CORE_CONTROL, (1 << 0) + ); + + WRITE_VREG(HEVC_DEC_STATUS_REG, 0); + + /* Initial IQIT_SCALELUT memory -- just to avoid X in simulation */ + if (debug & H265_DEBUG_BUFMGR) { + pr_info + ("[test.c] Initial IQIT_SCALELUT memory --"); + pr_info + (" just to avoid X in simulation...\n"); + } + WRITE_VREG(HEVC_IQIT_SCALELUT_WR_ADDR, 0); /* cfg_p_addr */ + for (i = 0; i < 1024; i++) + WRITE_VREG(HEVC_IQIT_SCALELUT_DATA, 0); + +#ifdef ENABLE_SWAP_TEST + WRITE_VREG(HEVC_STREAM_SWAP_TEST, 100); +#else + WRITE_VREG(HEVC_STREAM_SWAP_TEST, 0); +#endif + + WRITE_VREG(HEVC_DECODE_PIC_BEGIN_REG, 0); + /*WRITE_VREG(HEVC_DECODE_PIC_NUM_REG, 0xffffffff);*/ + /*WRITE_VREG(HEVC_DECODE_SIZE, 0);*/ + WRITE_VREG(HEVC_DECODE_SIZE, 0); + /* Send parser_cmd */ + if (debug) + pr_info("[test.c] SEND Parser Command ...\n"); + WRITE_VREG(HEVC_PARSER_CMD_WRITE, (1 << 16) | (0 << 0)); + for (i = 0; i < PARSER_CMD_NUMBER; i++) + WRITE_VREG(HEVC_PARSER_CMD_WRITE, parser_cmd[i]); + + WRITE_VREG(HEVC_PARSER_CMD_SKIP_0, PARSER_CMD_SKIP_CFG_0); + WRITE_VREG(HEVC_PARSER_CMD_SKIP_1, PARSER_CMD_SKIP_CFG_1); + WRITE_VREG(HEVC_PARSER_CMD_SKIP_2, PARSER_CMD_SKIP_CFG_2); + + WRITE_VREG(HEVC_PARSER_IF_CONTROL, + /* (1 << 8) | // sao_sw_pred_enable */ + (1 << 5) | /* parser_sao_if_en */ + (1 << 2) | /* parser_mpred_if_en */ + (1 << 0) /* parser_scaler_if_en */ + ); + + /* Changed to Start MPRED in microcode */ + /* + *pr_info("[test.c] Start MPRED\n"); + *WRITE_VREG(HEVC_MPRED_INT_STATUS, + *(1<<31) + *); + */ + + if (debug) + pr_info("[test.c] Reset IPP\n"); + WRITE_VREG(HEVCD_IPP_TOP_CNTL, (0 << 1) | /* enable ipp */ + (1 << 0) /* software reset ipp and mpp */ + ); + WRITE_VREG(HEVCD_IPP_TOP_CNTL, (1 << 1) | /* enable ipp */ + (0 << 0) /* software reset ipp and mpp */ + ); + + if (double_write_mode & 0x10) + WRITE_VREG(HEVCD_MPP_DECOMP_CTL1, + 0x1 << 31 /*/Enable NV21 reference read mode for MC*/ + ); + +} + +static void decoder_hw_reset(void) +{ + int i; + unsigned int data32; + /* reset iqit to start mem init again */ + WRITE_VREG(DOS_SW_RESET3, (1 << 14) + ); + CLEAR_VREG_MASK(HEVC_CABAC_CONTROL, 1); + CLEAR_VREG_MASK(HEVC_PARSER_CORE_CONTROL, 1); + + data32 = READ_VREG(HEVC_STREAM_CONTROL); + data32 = data32 | (1 << 0) /* stream_fetch_enable */ + ; + WRITE_VREG(HEVC_STREAM_CONTROL, data32); + + data32 = READ_VREG(HEVC_SHIFT_STARTCODE); + if (data32 != 0x00000100) { + print_scratch_error(29); + return; + } + data32 = READ_VREG(HEVC_SHIFT_EMULATECODE); + if (data32 != 0x00000300) { + print_scratch_error(30); + return; + } + WRITE_VREG(HEVC_SHIFT_STARTCODE, 0x12345678); + WRITE_VREG(HEVC_SHIFT_EMULATECODE, 0x9abcdef0); + data32 = READ_VREG(HEVC_SHIFT_STARTCODE); + if (data32 != 0x12345678) { + print_scratch_error(31); + return; + } + data32 = READ_VREG(HEVC_SHIFT_EMULATECODE); + if (data32 != 0x9abcdef0) { + print_scratch_error(32); + return; + } + WRITE_VREG(HEVC_SHIFT_STARTCODE, 0x00000100); + WRITE_VREG(HEVC_SHIFT_EMULATECODE, 0x00000300); + + data32 = READ_VREG(HEVC_PARSER_INT_CONTROL); + data32 &= 0x03ffffff; + data32 = data32 | (3 << 29) | (2 << 26) | (1 << 24) + | /* stream_buffer_empty_int_amrisc_enable */ + (1 << 22) | /*stream_fifo_empty_int_amrisc_enable */ + (1 << 7) | /* dec_done_int_cpu_enable */ + (1 << 4) | /* startcode_found_int_cpu_enable */ + (0 << 3) | /* startcode_found_int_amrisc_enable */ + (1 << 0) /* parser_int_enable */ + ; + WRITE_VREG(HEVC_PARSER_INT_CONTROL, data32); + + data32 = READ_VREG(HEVC_SHIFT_STATUS); + data32 = data32 | (1 << 1) | /* emulation_check_on */ + (1 << 0) /* startcode_check_on */ + ; + WRITE_VREG(HEVC_SHIFT_STATUS, data32); + + WRITE_VREG(HEVC_SHIFT_CONTROL, (3 << 6) |/* sft_valid_wr_position */ + (2 << 4) | /* emulate_code_length_sub_1 */ + (2 << 1) | /* start_code_length_sub_1 */ + (1 << 0) /* stream_shift_enable */ + ); + + WRITE_VREG(HEVC_CABAC_CONTROL, (1 << 0) /* cabac_enable */ + ); + /* hevc_parser_core_clk_en */ + WRITE_VREG(HEVC_PARSER_CORE_CONTROL, (1 << 0) + ); + + /* Initial IQIT_SCALELUT memory -- just to avoid X in simulation */ + WRITE_VREG(HEVC_IQIT_SCALELUT_WR_ADDR, 0); /* cfg_p_addr */ + for (i = 0; i < 1024; i++) + WRITE_VREG(HEVC_IQIT_SCALELUT_DATA, 0); + + /* Send parser_cmd */ + WRITE_VREG(HEVC_PARSER_CMD_WRITE, (1 << 16) | (0 << 0)); + for (i = 0; i < PARSER_CMD_NUMBER; i++) + WRITE_VREG(HEVC_PARSER_CMD_WRITE, parser_cmd[i]); + + WRITE_VREG(HEVC_PARSER_CMD_SKIP_0, PARSER_CMD_SKIP_CFG_0); + WRITE_VREG(HEVC_PARSER_CMD_SKIP_1, PARSER_CMD_SKIP_CFG_1); + WRITE_VREG(HEVC_PARSER_CMD_SKIP_2, PARSER_CMD_SKIP_CFG_2); + + WRITE_VREG(HEVC_PARSER_IF_CONTROL, + /* (1 << 8) | // sao_sw_pred_enable */ + (1 << 5) | /* parser_sao_if_en */ + (1 << 2) | /* parser_mpred_if_en */ + (1 << 0) /* parser_scaler_if_en */ + ); + + WRITE_VREG(HEVCD_IPP_TOP_CNTL, (0 << 1) | /* enable ipp */ + (1 << 0) /* software reset ipp and mpp */ + ); + WRITE_VREG(HEVCD_IPP_TOP_CNTL, (1 << 1) | /* enable ipp */ + (0 << 0) /* software reset ipp and mpp */ + ); +} + +#ifdef CONFIG_HEVC_CLK_FORCED_ON +static void config_hevc_clk_forced_on(void) +{ + unsigned int rdata32; + /* IQIT */ + rdata32 = READ_VREG(HEVC_IQIT_CLK_RST_CTRL); + WRITE_VREG(HEVC_IQIT_CLK_RST_CTRL, rdata32 | (0x1 << 2)); + + /* DBLK */ + rdata32 = READ_VREG(HEVC_DBLK_CFG0); + WRITE_VREG(HEVC_DBLK_CFG0, rdata32 | (0x1 << 2)); + + /* SAO */ + rdata32 = READ_VREG(HEVC_SAO_CTRL1); + WRITE_VREG(HEVC_SAO_CTRL1, rdata32 | (0x1 << 2)); + + /* MPRED */ + rdata32 = READ_VREG(HEVC_MPRED_CTRL1); + WRITE_VREG(HEVC_MPRED_CTRL1, rdata32 | (0x1 << 24)); + + /* PARSER */ + rdata32 = READ_VREG(HEVC_STREAM_CONTROL); + WRITE_VREG(HEVC_STREAM_CONTROL, rdata32 | (0x1 << 15)); + rdata32 = READ_VREG(HEVC_SHIFT_CONTROL); + WRITE_VREG(HEVC_SHIFT_CONTROL, rdata32 | (0x1 << 15)); + rdata32 = READ_VREG(HEVC_CABAC_CONTROL); + WRITE_VREG(HEVC_CABAC_CONTROL, rdata32 | (0x1 << 13)); + rdata32 = READ_VREG(HEVC_PARSER_CORE_CONTROL); + WRITE_VREG(HEVC_PARSER_CORE_CONTROL, rdata32 | (0x1 << 15)); + rdata32 = READ_VREG(HEVC_PARSER_INT_CONTROL); + WRITE_VREG(HEVC_PARSER_INT_CONTROL, rdata32 | (0x1 << 15)); + rdata32 = READ_VREG(HEVC_PARSER_IF_CONTROL); + WRITE_VREG(HEVC_PARSER_IF_CONTROL, + rdata32 | (0x3 << 5) | (0x3 << 2) | (0x3 << 0)); + + /* IPP */ + rdata32 = READ_VREG(HEVCD_IPP_DYNCLKGATE_CONFIG); + WRITE_VREG(HEVCD_IPP_DYNCLKGATE_CONFIG, rdata32 | 0xffffffff); + + /* MCRCC */ + rdata32 = READ_VREG(HEVCD_MCRCC_CTL1); + WRITE_VREG(HEVCD_MCRCC_CTL1, rdata32 | (0x1 << 3)); +} +#endif + +#ifdef MCRCC_ENABLE +static void config_mcrcc_axi_hw(struct hevc_state_s *hevc, int slice_type) +{ + unsigned int rdata32; + unsigned int rdata32_2; + int l0_cnt = 0; + int l1_cnt = 0x7fff; + + if (double_write_mode & 0x10) { + l0_cnt = hevc->cur_pic->RefNum_L0; + l1_cnt = hevc->cur_pic->RefNum_L1; + } + + WRITE_VREG(HEVCD_MCRCC_CTL1, 0x2); /* reset mcrcc */ + + if (slice_type == 2) { /* I-PIC */ + /* remove reset -- disables clock */ + WRITE_VREG(HEVCD_MCRCC_CTL1, 0x0); + return; + } + + if (slice_type == 0) { /* B-PIC */ + /* Programme canvas0 */ + WRITE_VREG(HEVCD_MPP_ANC_CANVAS_ACCCONFIG_ADDR, + (0 << 8) | (0 << 1) | 0); + rdata32 = READ_VREG(HEVCD_MPP_ANC_CANVAS_DATA_ADDR); + rdata32 = rdata32 & 0xffff; + rdata32 = rdata32 | (rdata32 << 16); + WRITE_VREG(HEVCD_MCRCC_CTL2, rdata32); + + /* Programme canvas1 */ + WRITE_VREG(HEVCD_MPP_ANC_CANVAS_ACCCONFIG_ADDR, + (16 << 8) | (1 << 1) | 0); + rdata32_2 = READ_VREG(HEVCD_MPP_ANC_CANVAS_DATA_ADDR); + rdata32_2 = rdata32_2 & 0xffff; + rdata32_2 = rdata32_2 | (rdata32_2 << 16); + if (rdata32 == rdata32_2 && l1_cnt > 1) { + rdata32_2 = READ_VREG(HEVCD_MPP_ANC_CANVAS_DATA_ADDR); + rdata32_2 = rdata32_2 & 0xffff; + rdata32_2 = rdata32_2 | (rdata32_2 << 16); + } + WRITE_VREG(HEVCD_MCRCC_CTL3, rdata32_2); + } else { /* P-PIC */ + WRITE_VREG(HEVCD_MPP_ANC_CANVAS_ACCCONFIG_ADDR, + (0 << 8) | (1 << 1) | 0); + rdata32 = READ_VREG(HEVCD_MPP_ANC_CANVAS_DATA_ADDR); + rdata32 = rdata32 & 0xffff; + rdata32 = rdata32 | (rdata32 << 16); + WRITE_VREG(HEVCD_MCRCC_CTL2, rdata32); + + if (l0_cnt == 1) { + WRITE_VREG(HEVCD_MCRCC_CTL3, rdata32); + } else { + /* Programme canvas1 */ + rdata32 = READ_VREG(HEVCD_MPP_ANC_CANVAS_DATA_ADDR); + rdata32 = rdata32 & 0xffff; + rdata32 = rdata32 | (rdata32 << 16); + WRITE_VREG(HEVCD_MCRCC_CTL3, rdata32); + } + } + /* enable mcrcc progressive-mode */ + WRITE_VREG(HEVCD_MCRCC_CTL1, 0xff0); +} +#endif + +static void config_title_hw(struct hevc_state_s *hevc, int sao_vb_size, + int sao_mem_unit) +{ + WRITE_VREG(HEVC_sao_mem_unit, sao_mem_unit); + WRITE_VREG(HEVC_SAO_ABV, hevc->work_space_buf->sao_abv.buf_start); + WRITE_VREG(HEVC_sao_vb_size, sao_vb_size); + WRITE_VREG(HEVC_SAO_VB, hevc->work_space_buf->sao_vb.buf_start); +} + +static void config_mpred_hw(struct hevc_state_s *hevc) +{ + int i; + unsigned int data32; + struct PIC_s *cur_pic = hevc->cur_pic; + struct PIC_s *col_pic = hevc->col_pic; + int AMVP_MAX_NUM_CANDS_MEM = 3; + int AMVP_MAX_NUM_CANDS = 2; + int NUM_CHROMA_MODE = 5; + int DM_CHROMA_IDX = 36; + int above_ptr_ctrl = 0; + int buffer_linear = 1; + int cu_size_log2 = 3; + + int mpred_mv_rd_start_addr; + int mpred_curr_lcu_x; + int mpred_curr_lcu_y; + int mpred_above_buf_start; + int mpred_mv_rd_ptr; + int mpred_mv_rd_ptr_p1; + int mpred_mv_rd_end_addr; + int MV_MEM_UNIT; + int mpred_mv_wr_ptr; + int *ref_poc_L0, *ref_poc_L1; + + int above_en; + int mv_wr_en; + int mv_rd_en; + int col_isIntra; + + if (hevc->slice_type != 2) { + above_en = 1; + mv_wr_en = 1; + mv_rd_en = 1; + col_isIntra = 0; + } else { + above_en = 1; + mv_wr_en = 1; + mv_rd_en = 0; + col_isIntra = 0; + } + + mpred_mv_rd_start_addr = col_pic->mpred_mv_wr_start_addr; + data32 = READ_VREG(HEVC_MPRED_CURR_LCU); + mpred_curr_lcu_x = data32 & 0xffff; + mpred_curr_lcu_y = (data32 >> 16) & 0xffff; + + MV_MEM_UNIT = + hevc->lcu_size_log2 == 6 ? 0x200 : hevc->lcu_size_log2 == + 5 ? 0x80 : 0x20; + mpred_mv_rd_ptr = + mpred_mv_rd_start_addr + (hevc->slice_addr * MV_MEM_UNIT); + + mpred_mv_rd_ptr_p1 = mpred_mv_rd_ptr + MV_MEM_UNIT; + mpred_mv_rd_end_addr = + mpred_mv_rd_start_addr + + ((hevc->lcu_x_num * hevc->lcu_y_num) * MV_MEM_UNIT); + + mpred_above_buf_start = hevc->work_space_buf->mpred_above.buf_start; + + mpred_mv_wr_ptr = + cur_pic->mpred_mv_wr_start_addr + + (hevc->slice_addr * MV_MEM_UNIT); + + if (debug & H265_DEBUG_BUFMGR) { + pr_info("cur pic index %d col pic index %d\n", cur_pic->index, + col_pic->index); + } + + WRITE_VREG(HEVC_MPRED_MV_WR_START_ADDR, + cur_pic->mpred_mv_wr_start_addr); + WRITE_VREG(HEVC_MPRED_MV_RD_START_ADDR, mpred_mv_rd_start_addr); + + data32 = ((hevc->lcu_x_num - hevc->tile_width_lcu) * MV_MEM_UNIT); + WRITE_VREG(HEVC_MPRED_MV_WR_ROW_JUMP, data32); + WRITE_VREG(HEVC_MPRED_MV_RD_ROW_JUMP, data32); + + data32 = READ_VREG(HEVC_MPRED_CTRL0); + data32 = (hevc->slice_type | + hevc->new_pic << 2 | + hevc->new_tile << 3 | + hevc->isNextSliceSegment << 4 | + hevc->TMVPFlag << 5 | + hevc->LDCFlag << 6 | + hevc->ColFromL0Flag << 7 | + above_ptr_ctrl << 8 | + above_en << 9 | + mv_wr_en << 10 | + mv_rd_en << 11 | + col_isIntra << 12 | + buffer_linear << 13 | + hevc->LongTerm_Curr << 14 | + hevc->LongTerm_Col << 15 | + hevc->lcu_size_log2 << 16 | + cu_size_log2 << 20 | hevc->plevel << 24); + WRITE_VREG(HEVC_MPRED_CTRL0, data32); + + data32 = READ_VREG(HEVC_MPRED_CTRL1); + data32 = ( +#if 0 + /* no set in m8baby test1902 */ + /* Don't override clk_forced_on , */ + (data32 & (0x1 << 24)) | +#endif + hevc->MaxNumMergeCand | + AMVP_MAX_NUM_CANDS << 4 | + AMVP_MAX_NUM_CANDS_MEM << 8 | + NUM_CHROMA_MODE << 12 | DM_CHROMA_IDX << 16); + WRITE_VREG(HEVC_MPRED_CTRL1, data32); + + data32 = (hevc->pic_w | hevc->pic_h << 16); + WRITE_VREG(HEVC_MPRED_PIC_SIZE, data32); + + data32 = ((hevc->lcu_x_num - 1) | (hevc->lcu_y_num - 1) << 16); + WRITE_VREG(HEVC_MPRED_PIC_SIZE_LCU, data32); + + data32 = (hevc->tile_start_lcu_x | hevc->tile_start_lcu_y << 16); + WRITE_VREG(HEVC_MPRED_TILE_START, data32); + + data32 = (hevc->tile_width_lcu | hevc->tile_height_lcu << 16); + WRITE_VREG(HEVC_MPRED_TILE_SIZE_LCU, data32); + + data32 = (hevc->RefNum_L0 | hevc->RefNum_L1 << 8 | 0 + /* col_RefNum_L0<<16| */ + /* col_RefNum_L1<<24 */ + ); + WRITE_VREG(HEVC_MPRED_REF_NUM, data32); + + data32 = (hevc->LongTerm_Ref); + WRITE_VREG(HEVC_MPRED_LT_REF, data32); + + data32 = 0; + for (i = 0; i < hevc->RefNum_L0; i++) + data32 = data32 | (1 << i); + WRITE_VREG(HEVC_MPRED_REF_EN_L0, data32); + + data32 = 0; + for (i = 0; i < hevc->RefNum_L1; i++) + data32 = data32 | (1 << i); + WRITE_VREG(HEVC_MPRED_REF_EN_L1, data32); + + WRITE_VREG(HEVC_MPRED_CUR_POC, hevc->curr_POC); + WRITE_VREG(HEVC_MPRED_COL_POC, hevc->Col_POC); + + /* + *below MPRED Ref_POC_xx_Lx registers must follow Ref_POC_xx_L0 -> + *Ref_POC_xx_L1 in pair write order!!! + */ + ref_poc_L0 = &(cur_pic->m_aiRefPOCList0[cur_pic->slice_idx][0]); + ref_poc_L1 = &(cur_pic->m_aiRefPOCList1[cur_pic->slice_idx][0]); + + WRITE_VREG(HEVC_MPRED_L0_REF00_POC, ref_poc_L0[0]); + WRITE_VREG(HEVC_MPRED_L1_REF00_POC, ref_poc_L1[0]); + + WRITE_VREG(HEVC_MPRED_L0_REF01_POC, ref_poc_L0[1]); + WRITE_VREG(HEVC_MPRED_L1_REF01_POC, ref_poc_L1[1]); + + WRITE_VREG(HEVC_MPRED_L0_REF02_POC, ref_poc_L0[2]); + WRITE_VREG(HEVC_MPRED_L1_REF02_POC, ref_poc_L1[2]); + + WRITE_VREG(HEVC_MPRED_L0_REF03_POC, ref_poc_L0[3]); + WRITE_VREG(HEVC_MPRED_L1_REF03_POC, ref_poc_L1[3]); + + WRITE_VREG(HEVC_MPRED_L0_REF04_POC, ref_poc_L0[4]); + WRITE_VREG(HEVC_MPRED_L1_REF04_POC, ref_poc_L1[4]); + + WRITE_VREG(HEVC_MPRED_L0_REF05_POC, ref_poc_L0[5]); + WRITE_VREG(HEVC_MPRED_L1_REF05_POC, ref_poc_L1[5]); + + WRITE_VREG(HEVC_MPRED_L0_REF06_POC, ref_poc_L0[6]); + WRITE_VREG(HEVC_MPRED_L1_REF06_POC, ref_poc_L1[6]); + + WRITE_VREG(HEVC_MPRED_L0_REF07_POC, ref_poc_L0[7]); + WRITE_VREG(HEVC_MPRED_L1_REF07_POC, ref_poc_L1[7]); + + WRITE_VREG(HEVC_MPRED_L0_REF08_POC, ref_poc_L0[8]); + WRITE_VREG(HEVC_MPRED_L1_REF08_POC, ref_poc_L1[8]); + + WRITE_VREG(HEVC_MPRED_L0_REF09_POC, ref_poc_L0[9]); + WRITE_VREG(HEVC_MPRED_L1_REF09_POC, ref_poc_L1[9]); + + WRITE_VREG(HEVC_MPRED_L0_REF10_POC, ref_poc_L0[10]); + WRITE_VREG(HEVC_MPRED_L1_REF10_POC, ref_poc_L1[10]); + + WRITE_VREG(HEVC_MPRED_L0_REF11_POC, ref_poc_L0[11]); + WRITE_VREG(HEVC_MPRED_L1_REF11_POC, ref_poc_L1[11]); + + WRITE_VREG(HEVC_MPRED_L0_REF12_POC, ref_poc_L0[12]); + WRITE_VREG(HEVC_MPRED_L1_REF12_POC, ref_poc_L1[12]); + + WRITE_VREG(HEVC_MPRED_L0_REF13_POC, ref_poc_L0[13]); + WRITE_VREG(HEVC_MPRED_L1_REF13_POC, ref_poc_L1[13]); + + WRITE_VREG(HEVC_MPRED_L0_REF14_POC, ref_poc_L0[14]); + WRITE_VREG(HEVC_MPRED_L1_REF14_POC, ref_poc_L1[14]); + + WRITE_VREG(HEVC_MPRED_L0_REF15_POC, ref_poc_L0[15]); + WRITE_VREG(HEVC_MPRED_L1_REF15_POC, ref_poc_L1[15]); + + if (hevc->new_pic) { + WRITE_VREG(HEVC_MPRED_ABV_START_ADDR, mpred_above_buf_start); + WRITE_VREG(HEVC_MPRED_MV_WPTR, mpred_mv_wr_ptr); + /* WRITE_VREG(HEVC_MPRED_MV_RPTR,mpred_mv_rd_ptr); */ + WRITE_VREG(HEVC_MPRED_MV_RPTR, mpred_mv_rd_start_addr); + } else if (!hevc->isNextSliceSegment) { + /* WRITE_VREG(HEVC_MPRED_MV_RPTR,mpred_mv_rd_ptr_p1); */ + WRITE_VREG(HEVC_MPRED_MV_RPTR, mpred_mv_rd_ptr); + } + + WRITE_VREG(HEVC_MPRED_MV_RD_END_ADDR, mpred_mv_rd_end_addr); +} + +static void config_sao_hw(struct hevc_state_s *hevc, union param_u *params) +{ + unsigned int data32, data32_2; + int misc_flag0 = hevc->misc_flag0; + int slice_deblocking_filter_disabled_flag = 0; + + int mc_buffer_size_u_v = + hevc->lcu_total * hevc->lcu_size * hevc->lcu_size / 2; + int mc_buffer_size_u_v_h = (mc_buffer_size_u_v + 0xffff) >> 16; + struct PIC_s *cur_pic = hevc->cur_pic; + + data32 = READ_VREG(HEVC_SAO_CTRL0); + data32 &= (~0xf); + data32 |= hevc->lcu_size_log2; + WRITE_VREG(HEVC_SAO_CTRL0, data32); + + data32 = (hevc->pic_w | hevc->pic_h << 16); + WRITE_VREG(HEVC_SAO_PIC_SIZE, data32); + + data32 = ((hevc->lcu_x_num - 1) | (hevc->lcu_y_num - 1) << 16); + WRITE_VREG(HEVC_SAO_PIC_SIZE_LCU, data32); + + if (hevc->new_pic) + WRITE_VREG(HEVC_SAO_Y_START_ADDR, 0xffffffff); +#ifdef LOSLESS_COMPRESS_MODE +/*SUPPORT_10BIT*/ + if ((double_write_mode & 0x10) == 0) { + data32 = READ_VREG(HEVC_SAO_CTRL5); + data32 &= (~(0xff << 16)); + if (double_write_mode != 1) + data32 |= (0xff<<16); + if (hevc->mem_saving_mode == 1) + data32 |= (1 << 9); + else + data32 &= ~(1 << 9); + if (workaround_enable & 1) + data32 |= (1 << 7); + WRITE_VREG(HEVC_SAO_CTRL5, data32); + } + data32 = cur_pic->mc_y_adr; + if (double_write_mode) + WRITE_VREG(HEVC_SAO_Y_START_ADDR, cur_pic->dw_y_adr); + + if ((double_write_mode & 0x10) == 0) + WRITE_VREG(HEVC_CM_BODY_START_ADDR, data32); + + if (mmu_enable) + WRITE_VREG(HEVC_CM_HEADER_START_ADDR, cur_pic->header_adr); +#else + data32 = cur_pic->mc_y_adr; + WRITE_VREG(HEVC_SAO_Y_START_ADDR, data32); +#endif + data32 = (mc_buffer_size_u_v_h << 16) << 1; + WRITE_VREG(HEVC_SAO_Y_LENGTH, data32); + +#ifdef LOSLESS_COMPRESS_MODE +/*SUPPORT_10BIT*/ + if (double_write_mode) + WRITE_VREG(HEVC_SAO_C_START_ADDR, cur_pic->dw_u_v_adr); +#else + data32 = cur_pic->mc_u_v_adr; + WRITE_VREG(HEVC_SAO_C_START_ADDR, data32); +#endif + data32 = (mc_buffer_size_u_v_h << 16); + WRITE_VREG(HEVC_SAO_C_LENGTH, data32); + +#ifdef LOSLESS_COMPRESS_MODE +/*SUPPORT_10BIT*/ + if (double_write_mode) { + WRITE_VREG(HEVC_SAO_Y_WPTR, cur_pic->dw_y_adr); + WRITE_VREG(HEVC_SAO_C_WPTR, cur_pic->dw_u_v_adr); + } +#else + /* multi tile to do... */ + data32 = cur_pic->mc_y_adr; + WRITE_VREG(HEVC_SAO_Y_WPTR, data32); + + data32 = cur_pic->mc_u_v_adr; + WRITE_VREG(HEVC_SAO_C_WPTR, data32); +#endif + /* DBLK CONFIG HERE */ + if (hevc->new_pic) { + data32 = (hevc->pic_w | hevc->pic_h << 16); + WRITE_VREG(HEVC_DBLK_CFG2, data32); + + if ((misc_flag0 >> PCM_ENABLE_FLAG_BIT) & 0x1) { + data32 = + ((misc_flag0 >> + PCM_LOOP_FILTER_DISABLED_FLAG_BIT) & + 0x1) << 3; + } else + data32 = 0; + data32 |= + (((params->p.pps_cb_qp_offset & 0x1f) << 4) | + ((params->p.pps_cr_qp_offset + & 0x1f) << + 9)); + data32 |= + (hevc->lcu_size == + 64) ? 0 : ((hevc->lcu_size == 32) ? 1 : 2); + + WRITE_VREG(HEVC_DBLK_CFG1, data32); + } +#if 0 + data32 = READ_VREG(HEVC_SAO_CTRL1); + data32 &= (~0x3000); + data32 |= (mem_map_mode << + 12);/* + *[13:12] axi_aformat, + *0-Linear, 1-32x32, 2-64x32 + */ + WRITE_VREG(HEVC_SAO_CTRL1, data32); + + data32 = READ_VREG(HEVCD_IPP_AXIIF_CONFIG); + data32 &= (~0x30); + data32 |= (mem_map_mode << + 4); /* + *[5:4] -- address_format + *00:linear 01:32x32 10:64x32 + */ + WRITE_VREG(HEVCD_IPP_AXIIF_CONFIG, data32); +#else + /* m8baby test1902 */ + data32 = READ_VREG(HEVC_SAO_CTRL1); + data32 &= (~0x3000); + data32 |= (mem_map_mode << + 12); /* + *[13:12] axi_aformat, 0-Linear, + *1-32x32, 2-64x32 + */ + data32 &= (~0xff0); + /* data32 |= 0x670; // Big-Endian per 64-bit */ + data32 |= endian; /* Big-Endian per 64-bit */ + data32 &= (~0x3); /*[1]:dw_disable [0]:cm_disable*/ + if (double_write_mode == 0) + data32 |= 0x2; /*disable double write*/ + else if (!mmu_enable && (double_write_mode & 0x10)) + data32 |= 0x1; /*disable cm*/ + WRITE_VREG(HEVC_SAO_CTRL1, data32); + + if (double_write_mode & 0x10) { + /* + *[23:22] dw_v1_ctrl + *[21:20] dw_v0_ctrl + *[19:18] dw_h1_ctrl + *[17:16] dw_h0_ctrl + */ + data32 = READ_VREG(HEVC_SAO_CTRL5); + /*set them all 0 for H265_NV21 (no down-scale)*/ + data32 &= ~(0xff << 16); + WRITE_VREG(HEVC_SAO_CTRL5, data32); + } + + data32 = READ_VREG(HEVCD_IPP_AXIIF_CONFIG); + data32 &= (~0x30); + /* [5:4] -- address_format 00:linear 01:32x32 10:64x32 */ + data32 |= (mem_map_mode << + 4); + data32 &= (~0xF); + data32 |= 0xf; /* valid only when double write only */ + /*data32 |= 0x8;*/ /* Big-Endian per 64-bit */ + WRITE_VREG(HEVCD_IPP_AXIIF_CONFIG, data32); +#endif + data32 = 0; + data32_2 = READ_VREG(HEVC_SAO_CTRL0); + data32_2 &= (~0x300); + /* + *slice_deblocking_filter_disabled_flag = 0; + * ucode has handle it , so read it from ucode directly + */ + if (hevc->tile_enabled) { + data32 |= + ((misc_flag0 >> + LOOP_FILER_ACROSS_TILES_ENABLED_FLAG_BIT) & + 0x1) << 0; + data32_2 |= + ((misc_flag0 >> + LOOP_FILER_ACROSS_TILES_ENABLED_FLAG_BIT) & + 0x1) << 8; + } + slice_deblocking_filter_disabled_flag = (misc_flag0 >> + SLICE_DEBLOCKING_FILTER_DISABLED_FLAG_BIT) & + 0x1; /* ucode has handle it,so read it from ucode directly */ + if ((misc_flag0 & (1 << DEBLOCKING_FILTER_OVERRIDE_ENABLED_FLAG_BIT)) + && (misc_flag0 & (1 << DEBLOCKING_FILTER_OVERRIDE_FLAG_BIT))) { + /* + *slice_deblocking_filter_disabled_flag = + *(misc_flag0>>SLICE_DEBLOCKING_FILTER_DISABLED_FLAG_BIT)&0x1; + * //ucode has handle it , so read it from ucode directly + */ + data32 |= slice_deblocking_filter_disabled_flag << 2; + if (debug & H265_DEBUG_BUFMGR) + pr_info("(1,%x)", data32); + if (!slice_deblocking_filter_disabled_flag) { + data32 |= (params->p.slice_beta_offset_div2 & 0xf) << 3; + data32 |= (params->p.slice_tc_offset_div2 & 0xf) << 7; + if (debug & H265_DEBUG_BUFMGR) + pr_info("(2,%x)", data32); + } + } else { + data32 |= + ((misc_flag0 >> + PPS_DEBLOCKING_FILTER_DISABLED_FLAG_BIT) & + 0x1) << 2; + if (debug & H265_DEBUG_BUFMGR) + pr_info("(3,%x)", data32); + if (((misc_flag0 >> PPS_DEBLOCKING_FILTER_DISABLED_FLAG_BIT) & + 0x1) == 0) { + data32 |= (params->p.pps_beta_offset_div2 & 0xf) << 3; + data32 |= (params->p.pps_tc_offset_div2 & 0xf) << 7; + if (debug & H265_DEBUG_BUFMGR) + pr_info("(4,%x)", data32); + } + } + if ((misc_flag0 & (1 << PPS_LOOP_FILTER_ACROSS_SLICES_ENABLED_FLAG_BIT)) + && ((misc_flag0 & (1 << SLICE_SAO_LUMA_FLAG_BIT)) + || (misc_flag0 & (1 << SLICE_SAO_CHROMA_FLAG_BIT)) + || (!slice_deblocking_filter_disabled_flag))) { + data32 |= + ((misc_flag0 >> + SLICE_LOOP_FILTER_ACROSS_SLICES_ENABLED_FLAG_BIT) + & 0x1) << 1; + data32_2 |= + ((misc_flag0 >> + SLICE_LOOP_FILTER_ACROSS_SLICES_ENABLED_FLAG_BIT) + & 0x1) << 9; + if (debug & H265_DEBUG_BUFMGR) + pr_info("(5,%x)\n", data32); + } else { + data32 |= + ((misc_flag0 >> + PPS_LOOP_FILTER_ACROSS_SLICES_ENABLED_FLAG_BIT) + & 0x1) << 1; + data32_2 |= + ((misc_flag0 >> + PPS_LOOP_FILTER_ACROSS_SLICES_ENABLED_FLAG_BIT) + & 0x1) << 9; + if (debug & H265_DEBUG_BUFMGR) + pr_info("(6,%x)\n", data32); + } + WRITE_VREG(HEVC_DBLK_CFG9, data32); + WRITE_VREG(HEVC_SAO_CTRL0, data32_2); +} + +static void clear_used_by_display_flag(struct hevc_state_s *hevc) +{ + struct PIC_s *pic; + int i; + + if (debug & H265_DEBUG_NOT_USE_LAST_DISPBUF) + return; + + for (i = 0; i < MAX_REF_PIC_NUM; i++) { + pic = &hevc->m_PIC[i]; + pic->used_by_display = 0; + } +} + +static struct PIC_s *get_new_pic(struct hevc_state_s *hevc, + union param_u *rpm_param) +{ + struct PIC_s *new_pic = NULL; + struct PIC_s *pic; + /* recycle un-used pic */ + int i; + int ret; + + for (i = 0; i < MAX_REF_PIC_NUM; i++) { + pic = &hevc->m_PIC[i]; + if (pic->index == -1) + continue; + if ((pic->used_by_display) && !mmu_enable + && ((READ_VCBUS_REG(AFBC_BODY_BADDR) << 4) != + pic->mc_y_adr)) + pic->used_by_display = 0; + if (pic->output_mark == 0 && pic->referenced == 0 + && pic->output_ready == 0 + && pic->used_by_display == 0) { + if (new_pic) { + if (pic->POC < new_pic->POC) + new_pic = pic; + } else + new_pic = pic; + } + } + + /*try to allocate more pic for new resolution*/ + if (re_config_pic_flag && new_pic == NULL) { + int ii; + + for (ii = 0; ii < MAX_REF_PIC_NUM; ii++) { + if (hevc->m_PIC[ii].index == -1) + break; + } + if (ii < MAX_REF_PIC_NUM) { + new_pic = &hevc->m_PIC[ii]; + memset(new_pic, 0, sizeof(struct PIC_s)); + new_pic->index = ii; + new_pic->BUF_index = -1; + } + } + /**/ + + if (new_pic == NULL) { + /* pr_info("Error: Buffer management, no free buffer\n"); */ + return new_pic; + } + + new_pic->referenced = 1; + if (new_pic->width != hevc->pic_w || new_pic->height != hevc->pic_h) { + if (re_config_pic_flag) { + /* re config pic for new resolution */ + recycle_buf(hevc); + /* if(new_pic->BUF_index == -1){ */ + if (config_pic(hevc, new_pic, 0) < 0) { + if (debug & H265_DEBUG_BUFMGR_MORE) { + pr_info("Config_pic %d fail\n", + new_pic->index); + dump_pic_list(hevc); + } + new_pic->index = -1; + new_pic = NULL; + } else + init_pic_list_hw(hevc); + } + if (new_pic) { + new_pic->width = hevc->pic_w; + new_pic->height = hevc->pic_h; + set_canvas(new_pic); + } + } + if (new_pic) { + new_pic->decode_idx = hevc->decode_idx; + new_pic->slice_idx = 0; + new_pic->referenced = 1; + new_pic->output_mark = 0; + new_pic->recon_mark = 0; + new_pic->error_mark = 0; + /* new_pic->output_ready = 0; */ + new_pic->num_reorder_pic = rpm_param->p.sps_num_reorder_pics_0; + new_pic->losless_comp_body_size = hevc->losless_comp_body_size; + new_pic->POC = hevc->curr_POC; + new_pic->pic_struct = hevc->curr_pic_struct; + } + + if (mmu_enable) { + ret = H265_alloc_mmu(hevc, new_pic, rpm_param->p.bit_depth, + hevc->frame_mmu_map_addr); + /*pr_info("get pic index %x\n",new_pic->index);*/ + if (ret != 0) { + pr_err("can't alloc need mmu1,idx %d ret =%d\n", + new_pic->decode_idx, + ret); + return NULL; + } + + } + + return new_pic; +} + +static int get_display_pic_num(struct hevc_state_s *hevc) +{ + int i; + struct PIC_s *pic; + int num = 0; + + for (i = 0; i < MAX_REF_PIC_NUM; i++) { + pic = &hevc->m_PIC[i]; + if (pic->index == -1) + continue; + + if (pic->output_ready == 1) + num++; + } + return num; +} + +static void flush_output(struct hevc_state_s *hevc, struct PIC_s *pic) +{ + struct PIC_s *pic_display; + + if (pic) { + /*PB skip control */ + if (pic->error_mark == 0 && hevc->PB_skip_mode == 1) { + /* start decoding after first I */ + hevc->ignore_bufmgr_error |= 0x1; + } + if (hevc->ignore_bufmgr_error & 1) { + if (hevc->PB_skip_count_after_decoding > 0) + hevc->PB_skip_count_after_decoding--; + else { + /* start displaying */ + hevc->ignore_bufmgr_error |= 0x2; + } + } + /**/ + if (pic->POC != INVALID_POC) { + pic->output_mark = 1; + pic->recon_mark = 1; + } + pic->recon_mark = 1; + } + do { + pic_display = output_pic(hevc, 1); + + if (pic_display) { + pic_display->referenced = 0; + if ((pic_display->error_mark + && ((hevc->ignore_bufmgr_error & 0x2) == 0)) + || (debug & H265_DEBUG_DISPLAY_CUR_FRAME) + || (debug & H265_DEBUG_NO_DISPLAY)) { + pic_display->output_ready = 0; + if (debug & H265_DEBUG_BUFMGR) { + pr_info + ("[BM] Display: POC %d, ", + pic_display->POC); + pr_info + ("decoding index %d ==> ", + pic_display->decode_idx); + pr_info + ("Debug mode or error, recycle it\n"); + } + } else { + if (i_only_flag & 0x1 + && pic_display->slice_type != 2) + pic_display->output_ready = 0; + else { + prepare_display_buf(hevc, pic_display); + if (debug & H265_DEBUG_BUFMGR) { + pr_info + ("[BM] flush Display: POC %d, ", + pic_display->POC); + pr_info + ("decoding index %d\n", + pic_display->decode_idx); + } + } + } + } + } while (pic_display); +} + +static inline void hevc_pre_pic(struct hevc_state_s *hevc, + struct PIC_s *pic) +{ + + /* prev pic */ + /*if (hevc->curr_POC != 0) {*/ + if (hevc->m_nalUnitType != NAL_UNIT_CODED_SLICE_IDR + && hevc->m_nalUnitType != + NAL_UNIT_CODED_SLICE_IDR_N_LP) { + struct PIC_s *pic_display; + + pic = get_pic_by_POC(hevc, hevc->iPrevPOC); + if (pic && (pic->POC != INVALID_POC)) { + /*PB skip control */ + if (pic->error_mark == 0 + && hevc->PB_skip_mode == 1) { + /* + *start decoding after + *first I + */ + hevc->ignore_bufmgr_error |= 0x1; + } + if (hevc->ignore_bufmgr_error & 1) { + if (hevc->PB_skip_count_after_decoding > 0) { + hevc->PB_skip_count_after_decoding--; + } else { + /* start displaying */ + hevc->ignore_bufmgr_error |= 0x2; + } + } + pic->output_mark = 1; + pic->recon_mark = 1; + } + do { + pic_display = output_pic(hevc, 0); + + if (pic_display) { + if ((pic_display->error_mark && + ((hevc->ignore_bufmgr_error & + 0x2) == 0)) + || (debug & + H265_DEBUG_DISPLAY_CUR_FRAME) + || (debug & + H265_DEBUG_NO_DISPLAY)) { + pic_display->output_ready = 0; + if (debug & H265_DEBUG_BUFMGR) { + pr_info + ("[BM] Display: POC %d, ", + pic_display->POC); + pr_info + ("decoding index %d ==> ", + pic_display-> + decode_idx); + pr_info + ("Debug or err,recycle it\n"); + } + } else { + if (i_only_flag & 0x1 + && pic_display-> + slice_type != 2) { + pic_display->output_ready = 0; + } else { + prepare_display_buf + (hevc, + pic_display); + if (debug & H265_DEBUG_BUFMGR) { + pr_info + ("[BM] Display: POC %d, ", + pic_display->POC); + pr_info + ("decoding index %d\n", + pic_display-> + decode_idx); + } + } + } + } + } while (pic_display); + } else { + if (debug & H265_DEBUG_BUFMGR) { + pr_info + ("[BM] current pic is IDR, "); + pr_info + ("clear referenced flag of all buffers\n"); + } + if (debug & H265_DEBUG_BUFMGR) + dump_pic_list(hevc); + pic = get_pic_by_POC(hevc, hevc->iPrevPOC); + flush_output(hevc, pic); + } + +} + +static void check_pic_decoded_lcu_count(struct hevc_state_s *hevc) +{ + int current_lcu_idx = READ_VREG(HEVC_PARSER_LCU_START)&0xffffff; + + if (debug & H265_DEBUG_BUFMGR) { + pr_info("cur lcu idx = %d, (total %d)\n", + current_lcu_idx, hevc->lcu_total); + } + if ((error_handle_policy & 0x20) == 0 && hevc->cur_pic != NULL) { + if (hevc->first_pic_after_recover) { + if (current_lcu_idx != + ((hevc->lcu_x_num_pre*hevc->lcu_y_num_pre) - 1)) + hevc->cur_pic->error_mark = 1; + } else { + if (hevc->lcu_x_num_pre != 0 + && hevc->lcu_y_num_pre != 0 + && current_lcu_idx != 0 + && current_lcu_idx < + ((hevc->lcu_x_num_pre*hevc->lcu_y_num_pre) - 1)) + hevc->cur_pic->error_mark = 1; + } + if (hevc->cur_pic->error_mark) + pr_info("cur lcu idx = %d, (total %d), set error_mark\n", + current_lcu_idx, + hevc->lcu_x_num_pre*hevc->lcu_y_num_pre); + + } + hevc->lcu_x_num_pre = hevc->lcu_x_num; + hevc->lcu_y_num_pre = hevc->lcu_y_num; +} + +static int hevc_slice_segment_header_process(struct hevc_state_s *hevc, + union param_u *rpm_param, + int decode_pic_begin) +{ + int i; + int lcu_x_num_div; + int lcu_y_num_div; + int Col_ref; + int dbg_skip_flag = 0; + + if (hevc->wait_buf == 0) { + hevc->sps_num_reorder_pics_0 = + rpm_param->p.sps_num_reorder_pics_0; + hevc->m_temporalId = rpm_param->p.m_temporalId; + hevc->m_nalUnitType = rpm_param->p.m_nalUnitType; + hevc->interlace_flag = + (rpm_param->p.profile_etc >> 2) & 0x1; + hevc->curr_pic_struct = + (rpm_param->p.sei_frame_field_info >> 3) & 0xf; + + if (interlace_enable == 0) + hevc->interlace_flag = 0; + if (interlace_enable & 0x100) + hevc->interlace_flag = interlace_enable & 0x1; + if (hevc->interlace_flag == 0) + hevc->curr_pic_struct = 0; + /* if(hevc->m_nalUnitType == NAL_UNIT_EOS){ */ + /* + *hevc->m_pocRandomAccess = MAX_INT; + * //add to fix RAP_B_Bossen_1 + */ + /* } */ + hevc->misc_flag0 = rpm_param->p.misc_flag0; + if (rpm_param->p.first_slice_segment_in_pic_flag == 0) { + hevc->slice_segment_addr = + rpm_param->p.slice_segment_address; + if (!rpm_param->p.dependent_slice_segment_flag) + hevc->slice_addr = hevc->slice_segment_addr; + } else { + hevc->slice_segment_addr = 0; + hevc->slice_addr = 0; + } + + hevc->iPrevPOC = hevc->curr_POC; + hevc->slice_type = (rpm_param->p.slice_type == I_SLICE) ? 2 : + (rpm_param->p.slice_type == P_SLICE) ? 1 : + (rpm_param->p.slice_type == B_SLICE) ? 0 : 3; + /* hevc->curr_predFlag_L0=(hevc->slice_type==2) ? 0:1; */ + /* hevc->curr_predFlag_L1=(hevc->slice_type==0) ? 1:0; */ + hevc->TMVPFlag = rpm_param->p.slice_temporal_mvp_enable_flag; + hevc->isNextSliceSegment = + rpm_param->p.dependent_slice_segment_flag ? 1 : 0; + if (hevc->pic_w != rpm_param->p.pic_width_in_luma_samples + || hevc->pic_h != + rpm_param->p.pic_height_in_luma_samples) { + pr_info("Pic Width/Height Change (%d,%d)=>(%d,%d), interlace %d\n", + hevc->pic_w, hevc->pic_h, + rpm_param->p.pic_width_in_luma_samples, + rpm_param->p.pic_height_in_luma_samples, + hevc->interlace_flag); + + hevc->pic_w = rpm_param->p.pic_width_in_luma_samples; + hevc->pic_h = rpm_param->p.pic_height_in_luma_samples; + hevc->frame_width = hevc->pic_w; + hevc->frame_height = hevc->pic_h; +#ifdef LOSLESS_COMPRESS_MODE + if (re_config_pic_flag == 0 && + (double_write_mode & 0x10) == 0) + init_decode_head_hw(hevc); +#endif + } + + if (hevc->pic_w * hevc->pic_h > HEVC_SIZE) { + pr_info("over size : %u x %u.\n", + hevc->pic_w, hevc->pic_h); + debug |= (H265_DEBUG_DIS_LOC_ERROR_PROC | + H265_DEBUG_DIS_SYS_ERROR_PROC); + hevc->fatal_error |= DECODER_FATAL_ERROR_SIZE_OVERFLOW; + return -1; + } + + /* it will cause divide 0 error */ + if (hevc->pic_w == 0 || hevc->pic_h == 0) { + if (debug) { + pr_info("Fatal Error, pic_w = %d, pic_h = %d\n", + hevc->pic_w, hevc->pic_h); + } + return 3; + } + hevc->lcu_size = + 1 << (rpm_param->p.log2_min_coding_block_size_minus3 + + 3 + rpm_param-> + p.log2_diff_max_min_coding_block_size); + if (hevc->lcu_size == 0) { + pr_info("Error, lcu_size = 0 (%d,%d)\n", + rpm_param->p. + log2_min_coding_block_size_minus3, + rpm_param->p. + log2_diff_max_min_coding_block_size); + return 3; + } + hevc->lcu_size_log2 = log2i(hevc->lcu_size); + lcu_x_num_div = (hevc->pic_w / hevc->lcu_size); + lcu_y_num_div = (hevc->pic_h / hevc->lcu_size); + hevc->lcu_x_num = + ((hevc->pic_w % hevc->lcu_size) == + 0) ? lcu_x_num_div : lcu_x_num_div + 1; + hevc->lcu_y_num = + ((hevc->pic_h % hevc->lcu_size) == + 0) ? lcu_y_num_div : lcu_y_num_div + 1; + hevc->lcu_total = hevc->lcu_x_num * hevc->lcu_y_num; + + if (hevc->m_nalUnitType == NAL_UNIT_CODED_SLICE_IDR + || hevc->m_nalUnitType == + NAL_UNIT_CODED_SLICE_IDR_N_LP) { + hevc->curr_POC = 0; + if ((hevc->m_temporalId - 1) == 0) + hevc->iPrevTid0POC = hevc->curr_POC; + } else { + int iMaxPOClsb = + 1 << (rpm_param->p. + log2_max_pic_order_cnt_lsb_minus4 + 4); + int iPrevPOClsb; + int iPrevPOCmsb; + int iPOCmsb; + int iPOClsb = rpm_param->p.POClsb; + + if (iMaxPOClsb == 0) { + pr_info("error iMaxPOClsb is 0\n"); + return 3; + } + + iPrevPOClsb = hevc->iPrevTid0POC % iMaxPOClsb; + iPrevPOCmsb = hevc->iPrevTid0POC - iPrevPOClsb; + + if ((iPOClsb < iPrevPOClsb) + && ((iPrevPOClsb - iPOClsb) >= + (iMaxPOClsb / 2))) + iPOCmsb = iPrevPOCmsb + iMaxPOClsb; + else if ((iPOClsb > iPrevPOClsb) + && ((iPOClsb - iPrevPOClsb) > + (iMaxPOClsb / 2))) + iPOCmsb = iPrevPOCmsb - iMaxPOClsb; + else + iPOCmsb = iPrevPOCmsb; + if (debug & H265_DEBUG_BUFMGR) { + pr_info + ("iPrePOC%d iMaxPOClsb%d iPOCmsb%d iPOClsb%d\n", + hevc->iPrevTid0POC, iMaxPOClsb, iPOCmsb, + iPOClsb); + } + if (hevc->m_nalUnitType == NAL_UNIT_CODED_SLICE_BLA + || hevc->m_nalUnitType == + NAL_UNIT_CODED_SLICE_BLANT + || hevc->m_nalUnitType == + NAL_UNIT_CODED_SLICE_BLA_N_LP) { + /* For BLA picture types, POCmsb is set to 0. */ + iPOCmsb = 0; + } + hevc->curr_POC = (iPOCmsb + iPOClsb); + if ((hevc->m_temporalId - 1) == 0) + hevc->iPrevTid0POC = hevc->curr_POC; + else { + if (debug & H265_DEBUG_BUFMGR) { + pr_info("m_temporalID is %d\n", + hevc->m_temporalId); + } + } + } + hevc->RefNum_L0 = + (rpm_param->p.num_ref_idx_l0_active > + MAX_REF_ACTIVE) ? MAX_REF_ACTIVE : rpm_param->p. + num_ref_idx_l0_active; + hevc->RefNum_L1 = + (rpm_param->p.num_ref_idx_l1_active > + MAX_REF_ACTIVE) ? MAX_REF_ACTIVE : rpm_param->p. + num_ref_idx_l1_active; + + /* if(curr_POC==0x10) dump_lmem(); */ + + /* skip RASL pictures after CRA/BLA pictures */ + if (hevc->m_pocRandomAccess == MAX_INT) {/* first picture */ + if (hevc->m_nalUnitType == NAL_UNIT_CODED_SLICE_CRA || + hevc->m_nalUnitType == NAL_UNIT_CODED_SLICE_BLA + || hevc->m_nalUnitType == + NAL_UNIT_CODED_SLICE_BLANT + || hevc->m_nalUnitType == + NAL_UNIT_CODED_SLICE_BLA_N_LP) + hevc->m_pocRandomAccess = hevc->curr_POC; + else + hevc->m_pocRandomAccess = -MAX_INT; + } else if (hevc->m_nalUnitType == NAL_UNIT_CODED_SLICE_BLA + || hevc->m_nalUnitType == + NAL_UNIT_CODED_SLICE_BLANT + || hevc->m_nalUnitType == + NAL_UNIT_CODED_SLICE_BLA_N_LP) + hevc->m_pocRandomAccess = hevc->curr_POC; + else if ((hevc->curr_POC < hevc->m_pocRandomAccess) && + (nal_skip_policy >= 3) && + (hevc->m_nalUnitType == + NAL_UNIT_CODED_SLICE_RASL_N || + hevc->m_nalUnitType == + NAL_UNIT_CODED_SLICE_TFD)) { /* skip */ + if (debug) { + pr_info + ("RASL picture with POC %d < %d ", + hevc->curr_POC, hevc->m_pocRandomAccess); + pr_info("RandomAccess point POC), skip it\n"); + } + return 1; + } + + WRITE_VREG(HEVC_WAIT_FLAG, READ_VREG(HEVC_WAIT_FLAG) | 0x2); + hevc->skip_flag = 0; + /**/ + /* if((iPrevPOC != curr_POC)){ */ + if (rpm_param->p.slice_segment_address == 0) { + struct PIC_s *pic; + + hevc->new_pic = 1; + check_pic_decoded_lcu_count(hevc); + /**/ if (use_cma == 0) { + if (hevc->pic_list_init_flag == 0) { + /*USE_BUF_BLOCK*/ + init_buf_list(hevc); + /**/ + init_pic_list(hevc); + init_pic_list_hw(hevc); + init_buf_spec(hevc); + hevc->pic_list_init_flag = 3; + } + } + hevc->first_pic_after_recover = 0; + if (debug & H265_DEBUG_BUFMGR_MORE) + dump_pic_list(hevc); + /* prev pic */ + hevc_pre_pic(hevc, pic); + /* + *update referenced of old pictures + *(cur_pic->referenced is 1 and not updated) + */ + apply_ref_pic_set(hevc, hevc->curr_POC, + rpm_param); + if (mmu_enable && hevc->cur_pic != NULL) { + if (!(hevc->cur_pic->error_mark + && ((hevc->ignore_bufmgr_error & 0x1) == 0))) { + long used_4k_num = + (READ_VREG(HEVC_SAO_MMU_STATUS) >> 16); + decoder_mmu_box_free_idx_tail(hevc->mmu_box, + hevc->cur_pic->index, used_4k_num); + } + + } + + /* new pic */ + hevc->cur_pic = get_new_pic(hevc, rpm_param); + if (hevc->cur_pic == NULL) { + if (debug & H265_DEBUG_BUFMGR) + dump_pic_list(hevc); + hevc->wait_buf = 1; + return -1; + } + if (debug & H265_DEBUG_DISPLAY_CUR_FRAME) { + hevc->cur_pic->output_ready = 1; + hevc->cur_pic->stream_offset = + READ_VREG(HEVC_SHIFT_BYTE_COUNT); + prepare_display_buf(hevc, hevc->cur_pic); + hevc->wait_buf = 2; + return -1; + } + } else { + if (hevc->pic_list_init_flag != 3 + || hevc->cur_pic == NULL) { + /* make it dec from the first slice segment */ + return 3; + } + hevc->cur_pic->slice_idx++; + hevc->new_pic = 0; + } + } else { + if (hevc->wait_buf == 1) { + /* + *if (mmu_enable && hevc->cur_pic != NULL) { + * long used_4k_num = + * (READ_VREG(HEVC_SAO_MMU_STATUS) >> 16); + * decoder_mmu_box_free_idx_tail(hevc->mmu_box, + * hevc->cur_pic->index, used_4k_num); + * + * } + */ + hevc->cur_pic = get_new_pic(hevc, rpm_param); + if (hevc->cur_pic == NULL) + return -1; + hevc->wait_buf = 0; + } else if (hevc->wait_buf == + 2) { + if (get_display_pic_num(hevc) > + 1) + return -1; + hevc->wait_buf = 0; + } + if (debug & H265_DEBUG_BUFMGR_MORE) + dump_pic_list(hevc); + } + + if (hevc->new_pic) { +#if 1 + /*SUPPORT_10BIT*/ + int sao_mem_unit = + (hevc->lcu_size == 16 ? 9 : + hevc->lcu_size == + 32 ? 14 : 24) << 4; +#else + int sao_mem_unit = ((hevc->lcu_size / 8) * 2 + 4) << 4; +#endif + int pic_height_cu = + (hevc->pic_h + hevc->lcu_size - 1) / hevc->lcu_size; + int pic_width_cu = + (hevc->pic_w + hevc->lcu_size - 1) / hevc->lcu_size; + int sao_vb_size = (sao_mem_unit + (2 << 4)) * pic_height_cu; + + /* int sao_abv_size = sao_mem_unit*pic_width_cu; */ + if (debug & H265_DEBUG_BUFMGR) { + pr_info("==>%s dec idx %d, struct %d interlace %d\n", + __func__, + hevc->decode_idx, + hevc->curr_pic_struct, + hevc->interlace_flag); + } + if (dbg_skip_decode_index != 0 && + hevc->decode_idx == dbg_skip_decode_index) + dbg_skip_flag = 1; + + hevc->decode_idx++; + update_tile_info(hevc, pic_width_cu, pic_height_cu, + sao_mem_unit, rpm_param); + + config_title_hw(hevc, sao_vb_size, sao_mem_unit); + } + + if (hevc->iPrevPOC != hevc->curr_POC) { + hevc->new_tile = 1; + hevc->tile_x = 0; + hevc->tile_y = 0; + hevc->tile_y_x = 0; + if (debug & H265_DEBUG_BUFMGR) { + pr_info("new_tile (new_pic) tile_x=%d, tile_y=%d\n", + hevc->tile_x, hevc->tile_y); + } + } else if (hevc->tile_enabled) { + if (debug & H265_DEBUG_BUFMGR) { + pr_info("slice_segment_address is %d\n", + rpm_param->p.slice_segment_address); + } + hevc->tile_y_x = + get_tile_index(hevc, rpm_param->p.slice_segment_address, + (hevc->pic_w + + hevc->lcu_size - + 1) / hevc->lcu_size); + if (hevc->tile_y_x != (hevc->tile_x | (hevc->tile_y << 8))) { + hevc->new_tile = 1; + hevc->tile_x = hevc->tile_y_x & 0xff; + hevc->tile_y = (hevc->tile_y_x >> 8) & 0xff; + if (debug & H265_DEBUG_BUFMGR) { + pr_info + ("new_tile seg adr %d tile_x=%d, tile_y=%d\n", + rpm_param->p.slice_segment_address, + hevc->tile_x, hevc->tile_y); + } + } else + hevc->new_tile = 0; + } else + hevc->new_tile = 0; + + if (hevc->new_tile) { + hevc->tile_start_lcu_x = + hevc->m_tile[hevc->tile_y][hevc->tile_x].start_cu_x; + hevc->tile_start_lcu_y = + hevc->m_tile[hevc->tile_y][hevc->tile_x].start_cu_y; + hevc->tile_width_lcu = + hevc->m_tile[hevc->tile_y][hevc->tile_x].width; + hevc->tile_height_lcu = + hevc->m_tile[hevc->tile_y][hevc->tile_x].height; + } + + set_ref_pic_list(hevc, rpm_param); + + Col_ref = rpm_param->p.collocated_ref_idx; + + hevc->LDCFlag = 0; + if (rpm_param->p.slice_type != I_SLICE) { + hevc->LDCFlag = 1; + for (i = 0; (i < hevc->RefNum_L0) && hevc->LDCFlag; i++) { + if (hevc->cur_pic-> + m_aiRefPOCList0[hevc->cur_pic->slice_idx][i] > + hevc->curr_POC) + hevc->LDCFlag = 0; + } + if (rpm_param->p.slice_type == B_SLICE) { + for (i = 0; (i < hevc->RefNum_L1) + && hevc->LDCFlag; i++) { + if (hevc->cur_pic-> + m_aiRefPOCList1[hevc->cur_pic-> + slice_idx][i] > + hevc->curr_POC) + hevc->LDCFlag = 0; + } + } + } + + hevc->ColFromL0Flag = rpm_param->p.collocated_from_l0_flag; + + hevc->plevel = + rpm_param->p.log2_parallel_merge_level; + hevc->MaxNumMergeCand = 5 - rpm_param->p.five_minus_max_num_merge_cand; + + hevc->LongTerm_Curr = 0; /* to do ... */ + hevc->LongTerm_Col = 0; /* to do ... */ + + hevc->list_no = 0; + if (rpm_param->p.slice_type == B_SLICE) + hevc->list_no = 1 - hevc->ColFromL0Flag; + if (hevc->list_no == 0) { + if (Col_ref < hevc->RefNum_L0) { + hevc->Col_POC = + hevc->cur_pic->m_aiRefPOCList0[hevc->cur_pic-> + slice_idx][Col_ref]; + } else + hevc->Col_POC = INVALID_POC; + } else { + if (Col_ref < hevc->RefNum_L1) { + hevc->Col_POC = + hevc->cur_pic->m_aiRefPOCList1[hevc->cur_pic-> + slice_idx][Col_ref]; + } else + hevc->Col_POC = INVALID_POC; + } + + hevc->LongTerm_Ref = 0; /* to do ... */ + + if (hevc->slice_type != 2) { + /* if(i_only_flag==1){ */ + /* return 0xf; */ + /* } */ + + if (hevc->Col_POC != INVALID_POC) { + hevc->col_pic = get_ref_pic_by_POC(hevc, hevc->Col_POC); + if (hevc->col_pic == NULL) { + hevc->cur_pic->error_mark = 1; + if (debug) { + pr_info + ("WRONG,fail to get the pic Col_POC\n"); + } + } else if (hevc->col_pic->error_mark) { + hevc->cur_pic->error_mark = 1; + if (debug) { + pr_info + ("WRONG, Col_POC error_mark is 1\n"); + } + } + + if (hevc->cur_pic->error_mark + && ((hevc->ignore_bufmgr_error & 0x1) == 0)) { + return 2; + } + } else + hevc->col_pic = hevc->cur_pic; + } /* */ + if (hevc->col_pic == NULL) + hevc->col_pic = hevc->cur_pic; +#ifdef BUFFER_MGR_ONLY + return 0xf; +#else + if ((decode_pic_begin > 0 && hevc->decode_idx <= decode_pic_begin) + || (dbg_skip_flag)) + return 0xf; +#endif + + config_mc_buffer(hevc, hevc->cur_pic); + + if (hevc->cur_pic->error_mark + && ((hevc->ignore_bufmgr_error & 0x1) == 0)) { + if (debug) + pr_info("Discard this picture index %d\n", + hevc->cur_pic->index); + return 2; + } +#ifdef MCRCC_ENABLE + config_mcrcc_axi_hw(hevc, hevc->cur_pic->slice_type); +#endif + config_mpred_hw(hevc); + + config_sao_hw(hevc, rpm_param); + + if ((hevc->slice_type != 2) && (i_only_flag & 0x2)) + return 0xf; + + return 0; +} + + + +static int H265_alloc_mmu(struct hevc_state_s *hevc, struct PIC_s *new_pic, + unsigned short bit_depth, unsigned int *mmu_index_adr) { + int cur_buf_idx = new_pic->index; + int bit_depth_10 = (bit_depth != 0x00); + int picture_size; + int cur_mmu_4k_number; + + picture_size = compute_losless_comp_body_size(new_pic->width, + new_pic->height, !bit_depth_10); + cur_mmu_4k_number = ((picture_size+(1<<12)-1) >> 12); + + /* + *pr_info("alloc_mmu cur_idx : %d picture_size : %d mmu_4k_number : %d\r\n", +* cur_buf_idx, picture_size, cur_mmu_4k_number); +*/ + return decoder_mmu_box_alloc_idx( + hevc->mmu_box, + cur_buf_idx, + cur_mmu_4k_number, + mmu_index_adr); +} + + + + + + +/* +************************************************* +* +*h265 buffer management end +* +************************************************** +*/ +static struct hevc_state_s gHevc; + +static void hevc_local_uninit(struct hevc_state_s *hevc) +{ + hevc->rpm_ptr = NULL; + hevc->lmem_ptr = NULL; + + if (hevc->rpm_addr) { + dma_unmap_single(amports_get_dma_device(), + hevc->rpm_phy_addr, RPM_BUF_SIZE, DMA_FROM_DEVICE); + kfree(hevc->rpm_addr); + hevc->rpm_addr = NULL; + } + if (hevc->lmem_addr) { + dma_unmap_single(amports_get_dma_device(), + hevc->lmem_phy_addr, LMEM_BUF_SIZE, DMA_FROM_DEVICE); + kfree(hevc->lmem_addr); + hevc->lmem_addr = NULL; + } + + if (mmu_enable && hevc->frame_mmu_map_addr) { + if (hevc->frame_mmu_map_phy_addr) + dma_free_coherent(amports_get_dma_device(), + FRAME_MMU_MAP_SIZE, hevc->frame_mmu_map_addr, + hevc->frame_mmu_map_phy_addr); + hevc->frame_mmu_map_addr = NULL; + } + + +} + +static int hevc_local_init(struct hevc_state_s *hevc) +{ + int ret = -1; + struct BuffInfo_s *cur_buf_info = NULL; + + memset(&hevc->param, 0, sizeof(union param_u)); + + cur_buf_info = &hevc->work_space_buf_store; +#ifdef SUPPORT_4K2K + memcpy(cur_buf_info, &amvh265_workbuff_spec[1], /* 4k2k work space */ + sizeof(struct BuffInfo_s)); +#else + memcpy(cur_buf_info, &amvh265_workbuff_spec[0], /* 1080p work space */ + sizeof(struct BuffInfo_s)); +#endif + cur_buf_info->start_adr = hevc->buf_start; + hevc->mc_buf_spec.buf_end = hevc->buf_start + hevc->buf_size; + init_buff_spec(hevc, cur_buf_info); + + if (mmu_enable) { + hevc->mmu_box = decoder_mmu_box_alloc_box(DRIVER_NAME, + 0, MAX_REF_PIC_NUM); + if (!hevc->mmu_box) { + pr_err("h265 alloc mmu box failed!!\n"); + return -1; + } + } + + + hevc->mc_buf_spec.buf_start = (cur_buf_info->end_adr + 0xffff) + & (~0xffff); + hevc->mc_buf_spec.buf_size = (hevc->mc_buf_spec.buf_end + - hevc->mc_buf_spec.buf_start); + + hevc_init_stru(hevc, cur_buf_info, &hevc->mc_buf_spec); + + hevc->bit_depth_luma = 8; + hevc->bit_depth_chroma = 8; + hevc->video_signal_type = 0; + bit_depth_luma = hevc->bit_depth_luma; + bit_depth_chroma = hevc->bit_depth_chroma; + video_signal_type = hevc->video_signal_type; + + if ((debug & H265_DEBUG_SEND_PARAM_WITH_REG) == 0) { + hevc->rpm_addr = kmalloc(RPM_BUF_SIZE, GFP_KERNEL); + if (hevc->rpm_addr == NULL) { + pr_err("%s: failed to alloc rpm buffer\n", __func__); + return -1; + } + + hevc->rpm_phy_addr = dma_map_single(amports_get_dma_device(), + hevc->rpm_addr, RPM_BUF_SIZE, DMA_FROM_DEVICE); + if (dma_mapping_error(amports_get_dma_device(), + hevc->rpm_phy_addr)) { + pr_err("%s: failed to map rpm buffer\n", __func__); + kfree(hevc->rpm_addr); + hevc->rpm_addr = NULL; + return -1; + } + + hevc->rpm_ptr = hevc->rpm_addr; + } + + if (debug & H265_DEBUG_UCODE) { + hevc->lmem_addr = kmalloc(LMEM_BUF_SIZE, GFP_KERNEL); + if (hevc->lmem_addr == NULL) { + pr_err("%s: failed to alloc lmem buffer\n", __func__); + return -1; + } + + hevc->lmem_phy_addr = dma_map_single(amports_get_dma_device(), + hevc->lmem_addr, LMEM_BUF_SIZE, DMA_FROM_DEVICE); + if (dma_mapping_error(amports_get_dma_device(), + hevc->lmem_phy_addr)) { + pr_err("%s: failed to map lmem buffer\n", __func__); + kfree(hevc->lmem_addr); + hevc->lmem_addr = NULL; + return -1; + } + + hevc->lmem_ptr = hevc->lmem_addr; + } + + if (mmu_enable) { + hevc->frame_mmu_map_addr = + dma_alloc_coherent(amports_get_dma_device(), + FRAME_MMU_MAP_SIZE, + &hevc->frame_mmu_map_phy_addr, GFP_KERNEL); + if (hevc->frame_mmu_map_addr == NULL) { + pr_err("%s: failed to alloc count_buffer\n", __func__); + return -1; + } + memset(hevc->frame_mmu_map_addr, 0, FRAME_MMU_MAP_SIZE); + } + ret = 0; + return ret; +} + +/* +******************************************* + * Mailbox command + ******************************************* + */ +#define CMD_FINISHED 0 +#define CMD_ALLOC_VIEW 1 +#define CMD_FRAME_DISPLAY 3 +#define CMD_DEBUG 10 + + +#define DECODE_BUFFER_NUM_MAX 32 +#define DISPLAY_BUFFER_NUM 6 + +#define video_domain_addr(adr) (adr&0x7fffffff) +#define DECODER_WORK_SPACE_SIZE 0x800000 + +#define spec2canvas(x) \ + (((x)->uv_canvas_index << 16) | \ + ((x)->uv_canvas_index << 8) | \ + ((x)->y_canvas_index << 0)) + + +static void set_canvas(struct PIC_s *pic) +{ + int canvas_w = ALIGN(pic->width, 64)/4; + int canvas_h = ALIGN(pic->height, 32)/4; + int blkmode = mem_map_mode; + + /*CANVAS_BLKMODE_64X32*/ +#ifdef SUPPORT_10BIT + if (double_write_mode) { + canvas_w = pic->width; + canvas_h = pic->height; + if ((double_write_mode == 2) || + (double_write_mode == 3)) { + canvas_w >>= 2; + canvas_h >>= 2; + } + + if (mem_map_mode == 0) + canvas_w = ALIGN(canvas_w, 32); + else + canvas_w = ALIGN(canvas_w, 64); + canvas_h = ALIGN(canvas_h, 32); + + pic->y_canvas_index = 128 + pic->index * 2; + pic->uv_canvas_index = 128 + pic->index * 2 + 1; + + canvas_config_ex(pic->y_canvas_index, + pic->dw_y_adr, canvas_w, canvas_h, + CANVAS_ADDR_NOWRAP, blkmode, 0x7); + canvas_config_ex(pic->uv_canvas_index, pic->dw_u_v_adr, + canvas_w, canvas_h, + CANVAS_ADDR_NOWRAP, blkmode, 0x7); +#ifdef MULTI_INSTANCE_SUPPORT + pic->canvas_config[0].phy_addr = + pic->dw_y_adr; + pic->canvas_config[0].width = + canvas_w; + pic->canvas_config[0].height = + canvas_h; + pic->canvas_config[0].block_mode = + blkmode; + pic->canvas_config[0].endian = 7; + + pic->canvas_config[1].phy_addr = + pic->dw_u_v_adr; + pic->canvas_config[1].width = + canvas_w; + pic->canvas_config[1].height = + canvas_h; + pic->canvas_config[1].block_mode = + blkmode; + pic->canvas_config[1].endian = 7; +#endif + } else { + if (!mmu_enable) { + /* to change after 10bit VPU is ready ... */ + pic->y_canvas_index = 128 + pic->index; + pic->uv_canvas_index = 128 + pic->index; + + canvas_config_ex(pic->y_canvas_index, + pic->mc_y_adr, canvas_w, canvas_h, + CANVAS_ADDR_NOWRAP, blkmode, 0x7); + canvas_config_ex(pic->uv_canvas_index, pic->mc_u_v_adr, + canvas_w, canvas_h, + CANVAS_ADDR_NOWRAP, blkmode, 0x7); + } + } +#else + pic->y_canvas_index = 128 + pic->index * 2; + pic->uv_canvas_index = 128 + pic->index * 2 + 1; + + canvas_config_ex(pic->y_canvas_index, pic->mc_y_adr, canvas_w, canvas_h, + CANVAS_ADDR_NOWRAP, blkmode, 0x7); + canvas_config_ex(pic->uv_canvas_index, pic->mc_u_v_adr, + canvas_w, canvas_h, + CANVAS_ADDR_NOWRAP, blkmode, 0x7); +#endif +} + +static int init_buf_spec(struct hevc_state_s *hevc) +{ + int pic_width = hevc->pic_w; + int pic_height = hevc->pic_h; + + /* pr_info("%s1: %d %d\n", __func__, hevc->pic_w, hevc->pic_h); */ + pr_info("%s2 %d %d\n", __func__, pic_width, pic_height); + /* pic_width = hevc->pic_w; */ + /* pic_height = hevc->pic_h; */ + + if (hevc->frame_width == 0 || hevc->frame_height == 0) { + hevc->frame_width = pic_width; + hevc->frame_height = pic_height; + + } + + return 0; +} + +static void set_frame_info(struct hevc_state_s *hevc, struct vframe_s *vf) +{ + unsigned int ar; + int i, j; + + if ((double_write_mode == 2) || + (double_write_mode == 3)) { + vf->width = hevc->frame_width/4; + vf->height = hevc->frame_height/4; + } else { + vf->width = hevc->frame_width; + vf->height = hevc->frame_height; + } + vf->duration = hevc->frame_dur; + vf->duration_pulldown = 0; + vf->flag = 0; + + ar = min_t(u32, hevc->frame_ar, DISP_RATIO_ASPECT_RATIO_MAX); + vf->ratio_control = (ar << DISP_RATIO_ASPECT_RATIO_BIT); + + /* signal_type */ + if (hevc->video_signal_type & VIDEO_SIGNAL_TYPE_AVAILABLE_MASK) + vf->signal_type = hevc->video_signal_type; + else + vf->signal_type = 0; + + /* master_display_colour */ + if (hevc->sei_present_flag & SEI_MASTER_DISPLAY_COLOR_MASK) { + for (i = 0; i < 3; i++) + for (j = 0; j < 2; j++) + vf->prop.master_display_colour.primaries[i][j] + = hevc->primaries[i][j]; + for (i = 0; i < 2; i++) { + vf->prop.master_display_colour.white_point[i] + = hevc->white_point[i]; + vf->prop.master_display_colour.luminance[i] + = hevc->luminance[i]; + } + vf->prop.master_display_colour.present_flag = 1; + } else + vf->prop.master_display_colour.present_flag = 0; + +} + +static int vh265_vf_states(struct vframe_states *states, void *op_arg) +{ + unsigned long flags; +#ifdef MULTI_INSTANCE_SUPPORT + struct vdec_s *vdec = op_arg; + struct hevc_state_s *hevc = (struct hevc_state_s *)vdec->private; +#else + struct hevc_state_s *hevc = (struct hevc_state_s *)op_arg; +#endif + + spin_lock_irqsave(&lock, flags); + + states->vf_pool_size = VF_POOL_SIZE; + states->buf_free_num = kfifo_len(&hevc->newframe_q); + states->buf_avail_num = kfifo_len(&hevc->display_q); + + if (step == 2) + states->buf_avail_num = 0; + spin_unlock_irqrestore(&lock, flags); + return 0; +} + +static struct vframe_s *vh265_vf_peek(void *op_arg) +{ + struct vframe_s *vf; +#ifdef MULTI_INSTANCE_SUPPORT + struct vdec_s *vdec = op_arg; + struct hevc_state_s *hevc = (struct hevc_state_s *)vdec->private; +#else + struct hevc_state_s *hevc = (struct hevc_state_s *)op_arg; +#endif + + if (step == 2) + return NULL; + + if (kfifo_peek(&hevc->display_q, &vf)) + return vf; + + return NULL; +} + +static struct vframe_s *vh265_vf_get(void *op_arg) +{ + struct vframe_s *vf; +#ifdef MULTI_INSTANCE_SUPPORT + struct vdec_s *vdec = op_arg; + struct hevc_state_s *hevc = (struct hevc_state_s *)vdec->private; +#else + struct hevc_state_s *hevc = (struct hevc_state_s *)op_arg; +#endif + + if (step == 2) + return NULL; + else if (step == 1) + step = 2; + + if (kfifo_get(&hevc->display_q, &vf)) { + if (debug & H265_DEBUG_PIC_STRUCT) + pr_info("%s(type %d index 0x%x)\n", + __func__, vf->type, vf->index); + + hevc->show_frame_num++; + return vf; + } + + return NULL; +} + +static void vh265_vf_put(struct vframe_s *vf, void *op_arg) +{ + unsigned long flags; +#ifdef MULTI_INSTANCE_SUPPORT + struct vdec_s *vdec = op_arg; + struct hevc_state_s *hevc = (struct hevc_state_s *)vdec->private; +#else + struct hevc_state_s *hevc = (struct hevc_state_s *)op_arg; +#endif + unsigned char index_top = vf->index & 0xff; + unsigned char index_bot = (vf->index >> 8) & 0xff; + + if (debug & H265_DEBUG_PIC_STRUCT) + pr_info("%s(type %d index 0x%x)\n", + __func__, vf->type, vf->index); + + kfifo_put(&hevc->newframe_q, (const struct vframe_s *)vf); + spin_lock_irqsave(&lock, flags); + + if (index_top != 0xff && index_top >= 0 + && index_top < MAX_REF_PIC_NUM) { + if (hevc->m_PIC[index_top].vf_ref > 0) { + hevc->m_PIC[index_top].vf_ref--; + + if (hevc->m_PIC[index_top].vf_ref == 0) { + hevc->m_PIC[index_top].output_ready = 0; + if (mmu_enable) + hevc->m_PIC[index_top]. + used_by_display = 0; + hevc->last_put_idx_a = index_top; + if (hevc->wait_buf != 0) + WRITE_VREG(HEVC_ASSIST_MBOX1_IRQ_REG, + 0x1); + } + } + } + + if (index_bot != 0xff && index_bot >= 0 + && index_bot < MAX_REF_PIC_NUM) { + if (hevc->m_PIC[index_bot].vf_ref > 0) { + hevc->m_PIC[index_bot].vf_ref--; + + if (hevc->m_PIC[index_bot].vf_ref == 0) { + clear_used_by_display_flag(hevc); + hevc->m_PIC[index_bot].output_ready = 0; + hevc->last_put_idx_b = index_bot; + if (hevc->wait_buf != 0) + WRITE_VREG(HEVC_ASSIST_MBOX1_IRQ_REG, + 0x1); + } + } + } + spin_unlock_irqrestore(&lock, flags); +} + +static int vh265_event_cb(int type, void *data, void *private_data) +{ + if (type & VFRAME_EVENT_RECEIVER_RESET) { +#if 0 + unsigned long flags; + + amhevc_stop(); +#ifndef CONFIG_POST_PROCESS_MANAGER + vf_light_unreg_provider(&vh265_vf_prov); +#endif + spin_lock_irqsave(&hevc->lock, flags); + vh265_local_init(); + vh265_prot_init(); + spin_unlock_irqrestore(&hevc->lock, flags); +#ifndef CONFIG_POST_PROCESS_MANAGER + vf_reg_provider(&vh265_vf_prov); +#endif + amhevc_start(); +#endif + } + + return 0; +} + +#ifdef HEVC_PIC_STRUCT_SUPPORT +static int process_pending_vframe(struct hevc_state_s *hevc, + struct PIC_s *pair_pic, unsigned char pair_frame_top_flag) +{ + struct vframe_s *vf; + + if (debug & H265_DEBUG_PIC_STRUCT) + pr_info("%s: pair_pic index 0x%x %s\n", + __func__, pair_pic->index, + pair_frame_top_flag ? + "top" : "bot"); + + if (kfifo_len(&hevc->pending_q) > 1) { + /* do not pending more than 1 frame */ + if (kfifo_get(&hevc->pending_q, &vf) == 0) { + pr_info("fatal error, no available buffer slot."); + return -1; + } + if (debug & H265_DEBUG_PIC_STRUCT) + pr_info("%s warning(1), vf=>display_q: (index 0x%x)\n", + __func__, vf->index); + kfifo_put(&hevc->display_q, (const struct vframe_s *)vf); + } + + if (kfifo_peek(&hevc->pending_q, &vf)) { + if (pair_pic == NULL || pair_pic->vf_ref <= 0) { + /* + *if pair_pic is recycled (pair_pic->vf_ref <= 0), + *do not use it + */ + if (kfifo_get(&hevc->pending_q, &vf) == 0) { + pr_info("fatal error, no available buffer slot."); + return -1; + } + if (debug & H265_DEBUG_PIC_STRUCT) + pr_info("%s warning(2), vf=>display_q: (index 0x%x)\n", + __func__, vf->index); + if (vf) + kfifo_put(&hevc->display_q, + (const struct vframe_s *)vf); + } else if ((!pair_frame_top_flag) && + (((vf->index >> 8) & 0xff) == 0xff)) { + if (kfifo_get(&hevc->pending_q, &vf) == 0) { + pr_info("fatal error, no available buffer slot."); + return -1; + } + if (vf) { + vf->type = VIDTYPE_PROGRESSIVE + | VIDTYPE_VIU_NV21; + vf->index &= 0xff; + vf->index |= (pair_pic->index << 8); + vf->canvas1Addr = spec2canvas(pair_pic); + pair_pic->vf_ref++; + kfifo_put(&hevc->display_q, + (const struct vframe_s *)vf); + if (debug & H265_DEBUG_PIC_STRUCT) + pr_info("%s vf => display_q: (index 0x%x)\n", + __func__, vf->index); + } + } else if (pair_frame_top_flag && + ((vf->index & 0xff) == 0xff)) { + if (kfifo_get(&hevc->pending_q, &vf) == 0) { + pr_info("fatal error, no available buffer slot."); + return -1; + } + if (vf) { + vf->type = VIDTYPE_PROGRESSIVE + | VIDTYPE_VIU_NV21; + vf->index &= 0xff00; + vf->index |= pair_pic->index; + vf->canvas0Addr = spec2canvas(pair_pic); + pair_pic->vf_ref++; + kfifo_put(&hevc->display_q, + (const struct vframe_s *)vf); + if (debug & H265_DEBUG_PIC_STRUCT) + pr_info("%s vf => display_q: (index 0x%x)\n", + __func__, vf->index); + } + } + } + return 0; +} +#endif + +static int prepare_display_buf(struct hevc_state_s *hevc, struct PIC_s *pic) +{ + struct vframe_s *vf = NULL; + int stream_offset = pic->stream_offset; + unsigned short slice_type = pic->slice_type; + + if (kfifo_get(&hevc->newframe_q, &vf) == 0) { + pr_info("fatal error, no available buffer slot."); + return -1; + } + + if (vf) { + /* + *if (pts_lookup_offset(PTS_TYPE_VIDEO, + *stream_offset, &vf->pts, 0) != 0) { + */ + if (pts_lookup_offset_us64 + (PTS_TYPE_VIDEO, stream_offset, &vf->pts, 0, + &vf->pts_us64) != 0) { +#ifdef DEBUG_PTS + hevc->pts_missed++; +#endif + vf->pts = 0; + vf->pts_us64 = 0; + } +#ifdef DEBUG_PTS + else + hevc->pts_hit++; +#endif + if (pts_unstable && (hevc->frame_dur > 0)) + hevc->pts_mode = PTS_NONE_REF_USE_DURATION; + + if ((hevc->pts_mode == PTS_NORMAL) && (vf->pts != 0) + && hevc->get_frame_dur) { + int pts_diff = (int)vf->pts - hevc->last_lookup_pts; + + if (pts_diff < 0) { + hevc->pts_mode_switching_count++; + hevc->pts_mode_recovery_count = 0; + + if (hevc->pts_mode_switching_count >= + PTS_MODE_SWITCHING_THRESHOLD) { + hevc->pts_mode = + PTS_NONE_REF_USE_DURATION; + pr_info + ("HEVC: switch to n_d mode.\n"); + } + + } else { + int p = PTS_MODE_SWITCHING_RECOVERY_THREASHOLD; + + hevc->pts_mode_recovery_count++; + if (hevc->pts_mode_recovery_count > p) { + hevc->pts_mode_switching_count = 0; + hevc->pts_mode_recovery_count = 0; + } + } + } + + if (vf->pts != 0) + hevc->last_lookup_pts = vf->pts; + + if ((hevc->pts_mode == PTS_NONE_REF_USE_DURATION) + && (slice_type != 2)) + vf->pts = hevc->last_pts + DUR2PTS(hevc->frame_dur); + hevc->last_pts = vf->pts; + + if (vf->pts_us64 != 0) + hevc->last_lookup_pts_us64 = vf->pts_us64; + + if ((hevc->pts_mode == PTS_NONE_REF_USE_DURATION) + && (slice_type != 2)) { + vf->pts_us64 = + hevc->last_pts_us64 + + (DUR2PTS(hevc->frame_dur) * 100 / 9); + } + hevc->last_pts_us64 = vf->pts_us64; + if ((debug & H265_DEBUG_OUT_PTS) != 0) { + pr_info + ("H265 dec out pts: vf->pts=%d, vf->pts_us64 = %lld\n", + vf->pts, vf->pts_us64); + } + + /* + *vf->index: + *(1) vf->type is VIDTYPE_PROGRESSIVE + * and vf->canvas0Addr != vf->canvas1Addr, + * vf->index[7:0] is the index of top pic + * vf->index[15:8] is the index of bot pic + *(2) other cases, + * only vf->index[7:0] is used + * vf->index[15:8] == 0xff + */ + vf->index = 0xff00 | pic->index; +#if 1 +/*SUPPORT_10BIT*/ + if (double_write_mode & 0x10) { + /* double write only */ + vf->compBodyAddr = 0; + vf->compHeadAddr = 0; + } else { + + if (mmu_enable) { + vf->compBodyAddr = 0; + vf->compHeadAddr = pic->header_adr; + } else { + vf->compBodyAddr = pic->mc_y_adr; /*body adr*/ + vf->compHeadAddr = pic->mc_y_adr + + pic->losless_comp_body_size; + } + + /*head adr*/ + vf->canvas0Addr = vf->canvas1Addr = 0; + } + if (double_write_mode) { + vf->type = VIDTYPE_PROGRESSIVE | VIDTYPE_VIU_FIELD; + vf->type |= VIDTYPE_VIU_NV21; + if (double_write_mode == 3) + vf->type |= VIDTYPE_COMPRESS; +#ifdef MULTI_INSTANCE_SUPPORT + if (hevc->m_ins_flag && + (debug & USE_OLD_PROVIDER) == 0) { + vf->canvas0Addr = vf->canvas1Addr = -1; + vf->canvas0_config[0] = + pic->canvas_config[0]; + vf->canvas0_config[1] = + pic->canvas_config[1]; + + vf->canvas1_config[0] = + pic->canvas_config[0]; + vf->canvas1_config[1] = + pic->canvas_config[1]; + + } else +#endif + vf->canvas0Addr = vf->canvas1Addr + = spec2canvas(pic); + } else { + vf->canvas0Addr = vf->canvas1Addr = 0; + vf->type = VIDTYPE_COMPRESS | VIDTYPE_VIU_FIELD; + if (mmu_enable) + vf->type |= VIDTYPE_SCATTER; + + } + vf->compWidth = pic->width; + vf->compHeight = pic->height; + + switch (hevc->bit_depth_luma) { + case 9: + vf->bitdepth = BITDEPTH_Y9; + break; + case 10: + vf->bitdepth = BITDEPTH_Y10; + break; + default: + vf->bitdepth = BITDEPTH_Y8; + break; + } + switch (hevc->bit_depth_chroma) { + case 9: + vf->bitdepth |= (BITDEPTH_U9 | BITDEPTH_V9); + break; + case 10: + vf->bitdepth |= (BITDEPTH_U10 | BITDEPTH_V10); + break; + default: + vf->bitdepth |= (BITDEPTH_U8 | BITDEPTH_V8); + break; + } + if (hevc->mem_saving_mode == 1) + vf->bitdepth |= BITDEPTH_SAVING_MODE; +#else + vf->type = VIDTYPE_PROGRESSIVE | VIDTYPE_VIU_FIELD; + vf->type |= VIDTYPE_VIU_NV21; + vf->canvas0Addr = vf->canvas1Addr = spec2canvas(pic); +#endif + set_frame_info(hevc, vf); + /* + *if((vf->width!=pic->width)||(vf->height!=pic->height)) + */ + /* + *pr_info("aaa: %d/%d, %d/%d\n", + *vf->width,vf->height, pic->width, pic->height); + */ + if ((double_write_mode == 2) || + (double_write_mode == 3)) { + vf->width = pic->width/4; + vf->height = pic->height/4; + } else { + vf->width = pic->width; + vf->height = pic->height; + } + if (force_w_h != 0) { + vf->width = (force_w_h >> 16) & 0xffff; + vf->height = force_w_h & 0xffff; + } + if (force_fps & 0x100) { + u32 rate = force_fps & 0xff; + + if (rate) + vf->duration = 96000/rate; + else + vf->duration = 0; + } + + /* + * !!! to do ... + * need move below code to get_new_pic(), + * hevc->xxx can only be used by current decoded pic + */ + if (hevc->param.p.conformance_window_flag && + (debug & H265_DEBUG_IGNORE_CONFORMANCE_WINDOW) == 0) { + unsigned SubWidthC, SubHeightC; + + switch (hevc->param.p.chroma_format_idc) { + case 1: + SubWidthC = 2; + SubHeightC = 2; + break; + case 2: + SubWidthC = 2; + SubHeightC = 1; + break; + default: + SubWidthC = 1; + SubHeightC = 1; + break; + } + vf->width -= SubWidthC * + (hevc->param.p.conf_win_left_offset + + hevc->param.p.conf_win_right_offset); + vf->height -= SubHeightC * + (hevc->param.p.conf_win_top_offset + + hevc->param.p.conf_win_bottom_offset); + if (debug & H265_DEBUG_BUFMGR) + pr_info("conformance_window %d, %d, %d, %d, %d => cropped width %d, height %d\n", + hevc->param.p.chroma_format_idc, + hevc->param.p.conf_win_left_offset, + hevc->param.p.conf_win_right_offset, + hevc->param.p.conf_win_top_offset, + hevc->param.p.conf_win_bottom_offset, + vf->width, vf->height); + } + +#ifdef HEVC_PIC_STRUCT_SUPPORT + if (pic->pic_struct == 3 || pic->pic_struct == 4) { + struct vframe_s *vf2; + + if (debug & H265_DEBUG_PIC_STRUCT) + pr_info("pic_struct = %d index 0x%x\n", + pic->pic_struct, + pic->index); + + if (kfifo_get(&hevc->newframe_q, &vf2) == 0) { + pr_info("fatal error, no available buffer slot."); + return -1; + } + pic->vf_ref = 2; + vf->duration = vf->duration>>1; + memcpy(vf2, vf, sizeof(struct vframe_s)); + + if (pic->pic_struct == 3) { + vf->type = VIDTYPE_INTERLACE_TOP + | VIDTYPE_VIU_NV21; + vf2->type = VIDTYPE_INTERLACE_BOTTOM + | VIDTYPE_VIU_NV21; + } else { + vf->type = VIDTYPE_INTERLACE_BOTTOM + | VIDTYPE_VIU_NV21; + vf2->type = VIDTYPE_INTERLACE_TOP + | VIDTYPE_VIU_NV21; + } + kfifo_put(&hevc->display_q, + (const struct vframe_s *)vf); + kfifo_put(&hevc->display_q, + (const struct vframe_s *)vf2); + } else if (pic->pic_struct == 5 + || pic->pic_struct == 6) { + struct vframe_s *vf2, *vf3; + + if (debug & H265_DEBUG_PIC_STRUCT) + pr_info("pic_struct = %d index 0x%x\n", + pic->pic_struct, + pic->index); + + if (kfifo_get(&hevc->newframe_q, &vf2) == 0) { + pr_info("fatal error, no available buffer slot."); + return -1; + } + if (kfifo_get(&hevc->newframe_q, &vf3) == 0) { + pr_info("fatal error, no available buffer slot."); + return -1; + } + pic->vf_ref = 3; + vf->duration = vf->duration/3; + memcpy(vf2, vf, sizeof(struct vframe_s)); + memcpy(vf3, vf, sizeof(struct vframe_s)); + + if (pic->pic_struct == 5) { + vf->type = VIDTYPE_INTERLACE_TOP + | VIDTYPE_VIU_NV21; + vf2->type = VIDTYPE_INTERLACE_BOTTOM + | VIDTYPE_VIU_NV21; + vf3->type = VIDTYPE_INTERLACE_TOP + | VIDTYPE_VIU_NV21; + } else { + vf->type = VIDTYPE_INTERLACE_BOTTOM + | VIDTYPE_VIU_NV21; + vf2->type = VIDTYPE_INTERLACE_TOP + | VIDTYPE_VIU_NV21; + vf3->type = VIDTYPE_INTERLACE_BOTTOM + | VIDTYPE_VIU_NV21; + } + kfifo_put(&hevc->display_q, + (const struct vframe_s *)vf); + kfifo_put(&hevc->display_q, + (const struct vframe_s *)vf2); + kfifo_put(&hevc->display_q, + (const struct vframe_s *)vf3); + + } else if (pic->pic_struct == 9 + || pic->pic_struct == 10) { + if (debug & H265_DEBUG_PIC_STRUCT) + pr_info("pic_struct = %d index 0x%x\n", + pic->pic_struct, + pic->index); + + pic->vf_ref = 1; + /* process previous pending vf*/ + process_pending_vframe(hevc, + pic, (pic->pic_struct == 9)); + + /* process current vf */ + kfifo_put(&hevc->pending_q, + (const struct vframe_s *)vf); + vf->height <<= 1; + if (pic->pic_struct == 9) { + vf->type = VIDTYPE_INTERLACE_TOP + | VIDTYPE_VIU_NV21 | VIDTYPE_VIU_FIELD; + process_pending_vframe(hevc, + hevc->pre_bot_pic, 0); + } else { + vf->type = VIDTYPE_INTERLACE_BOTTOM | + VIDTYPE_VIU_NV21 | VIDTYPE_VIU_FIELD; + vf->index = (pic->index << 8) | 0xff; + process_pending_vframe(hevc, + hevc->pre_top_pic, 1); + } + + /**/ + if (pic->pic_struct == 9) + hevc->pre_top_pic = pic; + else + hevc->pre_bot_pic = pic; + + } else if (pic->pic_struct == 11 + || pic->pic_struct == 12) { + if (debug & H265_DEBUG_PIC_STRUCT) + pr_info("pic_struct = %d index 0x%x\n", + pic->pic_struct, + pic->index); + pic->vf_ref = 1; + /* process previous pending vf*/ + process_pending_vframe(hevc, pic, + (pic->pic_struct == 11)); + + /* put current into pending q */ + vf->height <<= 1; + if (pic->pic_struct == 11) + vf->type = VIDTYPE_INTERLACE_TOP | + VIDTYPE_VIU_NV21 | VIDTYPE_VIU_FIELD; + else { + vf->type = VIDTYPE_INTERLACE_BOTTOM | + VIDTYPE_VIU_NV21 | VIDTYPE_VIU_FIELD; + vf->index = (pic->index << 8) | 0xff; + } + kfifo_put(&hevc->pending_q, + (const struct vframe_s *)vf); + + /**/ + if (pic->pic_struct == 11) + hevc->pre_top_pic = pic; + else + hevc->pre_bot_pic = pic; + + } else { + pic->vf_ref = 1; + + if (debug & H265_DEBUG_PIC_STRUCT) + pr_info("pic_struct = %d index 0x%x\n", + pic->pic_struct, + pic->index); + + switch (pic->pic_struct) { + case 7: + vf->duration <<= 1; + break; + case 8: + vf->duration = vf->duration * 3; + break; + case 1: + vf->height <<= 1; + vf->type = VIDTYPE_INTERLACE_TOP | + VIDTYPE_VIU_NV21 | VIDTYPE_VIU_FIELD; + process_pending_vframe(hevc, pic, 1); + hevc->pre_top_pic = pic; + break; + case 2: + vf->height <<= 1; + vf->type = VIDTYPE_INTERLACE_BOTTOM + | VIDTYPE_VIU_NV21 + | VIDTYPE_VIU_FIELD; + process_pending_vframe(hevc, pic, 0); + hevc->pre_bot_pic = pic; + break; + } + kfifo_put(&hevc->display_q, + (const struct vframe_s *)vf); + } +#else + vf->type_original = vf->type; + + if (mmu_enable) + vf->mem_handle = + decoder_mmu_box_get_mem_handle( + hevc->mmu_box, pic->index); + pic->vf_ref = 1; + kfifo_put(&hevc->display_q, (const struct vframe_s *)vf); +#endif + + vf_notify_receiver(hevc->provider_name, + VFRAME_EVENT_PROVIDER_VFRAME_READY, NULL); + } + + return 0; +} + +static void process_nal_sei(struct hevc_state_s *hevc, + int payload_type, int payload_size) +{ + unsigned short data; + + if (debug & H265_DEBUG_PRINT_SEI) + pr_info("\tsei message: payload_type = 0x%02x, payload_size = 0x%02x\n", + payload_type, payload_size); + + if (payload_type == 137) { + int i, j; + /* MASTERING_DISPLAY_COLOUR_VOLUME */ + if (payload_size >= 24) { + if (debug & H265_DEBUG_PRINT_SEI) + pr_info("\tsei MASTERING_DISPLAY_COLOUR_VOLUME available\n"); + for (i = 0; i < 3; i++) { + for (j = 0; j < 2; j++) { + data = + (READ_HREG(HEVC_SHIFTED_DATA) >> 16); + hevc->primaries[i][j] = data; + WRITE_HREG(HEVC_SHIFT_COMMAND, + (1<<7)|16); + if (debug & H265_DEBUG_PRINT_SEI) + pr_info("\t\tprimaries[%1d][%1d] = %04x\n", + i, j, hevc->primaries[i][j]); + } + } + for (i = 0; i < 2; i++) { + data = (READ_HREG(HEVC_SHIFTED_DATA) >> 16); + hevc->white_point[i] = data; + WRITE_HREG(HEVC_SHIFT_COMMAND, (1<<7)|16); + if (debug & H265_DEBUG_PRINT_SEI) + pr_info("\t\twhite_point[%1d] = %04x\n", + i, hevc->white_point[i]); + } + for (i = 0; i < 2; i++) { + data = + (READ_HREG(HEVC_SHIFTED_DATA) >> 16); + hevc->luminance[i] = data << 16; + WRITE_HREG(HEVC_SHIFT_COMMAND, + (1<<7)|16); + data = + (READ_HREG(HEVC_SHIFTED_DATA) >> 16); + hevc->luminance[i] |= data; + WRITE_HREG(HEVC_SHIFT_COMMAND, + (1<<7)|16); + if (debug & H265_DEBUG_PRINT_SEI) + pr_info("\t\tluminance[%1d] = %08x\n", + i, hevc->luminance[i]); + } + hevc->sei_present_flag |= SEI_MASTER_DISPLAY_COLOR_MASK; + } + payload_size -= 24; + while (payload_size > 0) { + data = (READ_HREG(HEVC_SHIFTED_DATA) >> 24); + payload_size--; + WRITE_HREG(HEVC_SHIFT_COMMAND, (1<<7)|8); + pr_info("\t\tskip byte %02x\n", data); + } + } +} + +static void hevc_recover(struct hevc_state_s *hevc) +{ + u32 rem; + u64 shift_byte_count64; + unsigned hevc_shift_byte_count; + unsigned hevc_stream_start_addr; + unsigned hevc_stream_end_addr; + unsigned hevc_stream_rd_ptr; + unsigned hevc_stream_wr_ptr; + unsigned hevc_stream_control; + unsigned hevc_stream_fifo_ctl; + unsigned hevc_stream_buf_size; +#if 0 + for (i = 0; i < (hevc->debug_ptr_size / 2); i += 4) { + int ii; + + for (ii = 0; ii < 4; ii++) + pr_info("%04x ", hevc->debug_ptr[i + 3 - ii]); + if (((i + ii) & 0xf) == 0) + pr_info("\n"); + } +#endif +#define ES_VID_MAN_RD_PTR (1<<0) + + amhevc_stop(); + + /* reset */ + WRITE_MPEG_REG(PARSER_VIDEO_RP, READ_VREG(HEVC_STREAM_RD_PTR)); + SET_MPEG_REG_MASK(PARSER_ES_CONTROL, ES_VID_MAN_RD_PTR); + + hevc_stream_start_addr = READ_VREG(HEVC_STREAM_START_ADDR); + hevc_stream_end_addr = READ_VREG(HEVC_STREAM_END_ADDR); + hevc_stream_rd_ptr = READ_VREG(HEVC_STREAM_RD_PTR); + hevc_stream_wr_ptr = READ_VREG(HEVC_STREAM_WR_PTR); + hevc_stream_control = READ_VREG(HEVC_STREAM_CONTROL); + hevc_stream_fifo_ctl = READ_VREG(HEVC_STREAM_FIFO_CTL); + hevc_stream_buf_size = hevc_stream_end_addr - hevc_stream_start_addr; + + /* + *HEVC streaming buffer will reset and restart + *from current hevc_stream_rd_ptr position + */ + /* calculate HEVC_SHIFT_BYTE_COUNT value with the new position. */ + hevc_shift_byte_count = READ_VREG(HEVC_SHIFT_BYTE_COUNT); + if ((hevc->shift_byte_count_lo & (1 << 31)) + && ((hevc_shift_byte_count & (1 << 31)) == 0)) + hevc->shift_byte_count_hi++; + + hevc->shift_byte_count_lo = hevc_shift_byte_count; + shift_byte_count64 = ((u64)(hevc->shift_byte_count_hi) << 32) | + hevc->shift_byte_count_lo; + div_u64_rem(shift_byte_count64, hevc_stream_buf_size, &rem); + shift_byte_count64 -= rem; + shift_byte_count64 += hevc_stream_rd_ptr - hevc_stream_start_addr; + + if (rem > (hevc_stream_rd_ptr - hevc_stream_start_addr)) + shift_byte_count64 += hevc_stream_buf_size; + + hevc->shift_byte_count_lo = (u32)shift_byte_count64; + hevc->shift_byte_count_hi = (u32)(shift_byte_count64 >> 32); + + WRITE_VREG(DOS_SW_RESET3, + /* (1<<2)| */ + (1 << 3) | (1 << 4) | (1 << 8) | + (1 << 11) | (1 << 12) | (1 << 14) + | (1 << 15) | (1 << 17) | (1 << 18) | (1 << 19)); + WRITE_VREG(DOS_SW_RESET3, 0); + + WRITE_VREG(HEVC_STREAM_START_ADDR, hevc_stream_start_addr); + WRITE_VREG(HEVC_STREAM_END_ADDR, hevc_stream_end_addr); + WRITE_VREG(HEVC_STREAM_RD_PTR, hevc_stream_rd_ptr); + WRITE_VREG(HEVC_STREAM_WR_PTR, hevc_stream_wr_ptr); + WRITE_VREG(HEVC_STREAM_CONTROL, hevc_stream_control); + WRITE_VREG(HEVC_SHIFT_BYTE_COUNT, hevc->shift_byte_count_lo); + WRITE_VREG(HEVC_STREAM_FIFO_CTL, hevc_stream_fifo_ctl); + + hevc_config_work_space_hw(hevc); + decoder_hw_reset(); + + hevc->have_vps = 0; + hevc->have_sps = 0; + hevc->have_pps = 0; + + hevc->have_valid_start_slice = 0; + + if (double_write_mode & 0x10) + WRITE_VREG(HEVCD_MPP_DECOMP_CTL1, + 0x1 << 31 /*/Enable NV21 reference read mode for MC*/ + ); + + WRITE_VREG(HEVC_WAIT_FLAG, 1); + /* clear mailbox interrupt */ + WRITE_VREG(HEVC_ASSIST_MBOX1_CLR_REG, 1); + /* enable mailbox interrupt */ + WRITE_VREG(HEVC_ASSIST_MBOX1_MASK, 1); + /* disable PSCALE for hardware sharing */ + WRITE_VREG(HEVC_PSCALE_CTRL, 0); + + CLEAR_MPEG_REG_MASK(PARSER_ES_CONTROL, ES_VID_MAN_RD_PTR); + + if (debug & H265_DEBUG_UCODE) + WRITE_VREG(DEBUG_REG1, 0x1); + else + WRITE_VREG(DEBUG_REG1, 0x0); + + if ((error_handle_policy & 1) == 0) { + if ((error_handle_policy & 4) == 0) { + /* ucode auto mode, and do not check vps/sps/pps/idr */ + WRITE_VREG(NAL_SEARCH_CTL, + 0xc); + } else { + WRITE_VREG(NAL_SEARCH_CTL, 0x1);/* manual parser NAL */ + } + } else { + WRITE_VREG(NAL_SEARCH_CTL, 0x1);/* manual parser NAL */ + } + + if (debug & H265_DEBUG_NO_EOS_SEARCH_DONE) + WRITE_VREG(NAL_SEARCH_CTL, READ_VREG(NAL_SEARCH_CTL) | 0x10000); + if (parser_sei_enable & 0x1) + WRITE_VREG(NAL_SEARCH_CTL, + READ_VREG(NAL_SEARCH_CTL) | 0x20000); + WRITE_VREG(DECODE_STOP_POS, decode_stop_pos); + + /* if (amhevc_loadmc(vh265_mc) < 0) { */ + /* amhevc_disable(); */ + /* return -EBUSY; */ + /* } */ +#if 0 + for (i = 0; i < (hevc->debug_ptr_size / 2); i += 4) { + int ii; + + for (ii = 0; ii < 4; ii++) { + /* hevc->debug_ptr[i+3-ii]=ttt++; */ + pr_info("%04x ", hevc->debug_ptr[i + 3 - ii]); + } + if (((i + ii) & 0xf) == 0) + pr_info("\n"); + } +#endif + init_pic_list_hw(hevc); + + pr_info("%s HEVC_SHIFT_BYTE_COUNT=%x\n", __func__, + READ_VREG(HEVC_SHIFT_BYTE_COUNT)); + + amhevc_start(); + + /* skip, search next start code */ + WRITE_VREG(HEVC_WAIT_FLAG, READ_VREG(HEVC_WAIT_FLAG) & (~0x2)); + hevc->skip_flag = 1; +#ifdef ERROR_HANDLE_DEBUG + if (dbg_nal_skip_count & 0x20000) { + dbg_nal_skip_count &= ~0x20000; + return; + } +#endif + WRITE_VREG(HEVC_DEC_STATUS_REG, HEVC_ACTION_DONE); + /* Interrupt Amrisc to excute */ + WRITE_VREG(HEVC_MCPU_INTR_REQ, AMRISC_MAIN_REQ); +#ifdef MULTI_INSTANCE_SUPPORT + if (!hevc->m_ins_flag) +#endif + hevc->first_pic_after_recover = 1; +} + + +static irqreturn_t vh265_isr_thread_fn(int irq, void *data) +{ + struct hevc_state_s *hevc = (struct hevc_state_s *) data; + unsigned int dec_status = hevc->dec_status; + int i, ret; + + + + if (dec_status == HEVC_SEI_DAT) { + int payload_type = READ_HREG(CUR_NAL_UNIT_TYPE) & 0xffff; + int payload_size = + (READ_HREG(CUR_NAL_UNIT_TYPE) >> 16) & 0xffff; + process_nal_sei(hevc, payload_type, payload_size); + + WRITE_VREG(HEVC_DEC_STATUS_REG, HEVC_SEI_DAT_DONE); + } else if (dec_status == HEVC_NAL_SEARCH_DONE) { + int naltype = READ_HREG(CUR_NAL_UNIT_TYPE); + int parse_type = HEVC_DISCARD_NAL; + + hevc->error_watchdog_count = 0; + hevc->error_skip_nal_wt_cnt = 0; + if (slice_parse_begin > 0 && debug & H265_DEBUG_DISCARD_NAL) { + pr_info("nal type %d, discard %d\n", naltype, + slice_parse_begin); + if (naltype <= NAL_UNIT_CODED_SLICE_CRA) + slice_parse_begin--; + } + if (naltype == NAL_UNIT_EOS) { + struct PIC_s *pic; + + pr_info("get NAL_UNIT_EOS, flush output"); + pic = get_pic_by_POC(hevc, hevc->curr_POC); + hevc->curr_POC = INVALID_POC; + /* add to fix RAP_B_Bossen_1 */ + hevc->m_pocRandomAccess = MAX_INT; + flush_output(hevc, pic); + WRITE_VREG(HEVC_DEC_STATUS_REG, HEVC_DISCARD_NAL); + /* Interrupt Amrisc to excute */ + WRITE_VREG(HEVC_MCPU_INTR_REQ, AMRISC_MAIN_REQ); + return IRQ_HANDLED; + } + + if (hevc->error_skip_nal_count > 0) { + pr_info("nal type %d, discard %d\n", naltype, + hevc->error_skip_nal_count); + hevc->error_skip_nal_count--; + if (hevc->error_skip_nal_count == 0) { + hevc_recover(hevc); + hevc->error_flag = 0; + if ((error_handle_policy & 0x2) == 0) { + hevc->have_vps = 1; + hevc->have_sps = 1; + hevc->have_pps = 1; + } + return IRQ_HANDLED; + } + } else if (naltype == NAL_UNIT_VPS) { + parse_type = HEVC_NAL_UNIT_VPS; + hevc->have_vps = 1; +#ifdef ERROR_HANDLE_DEBUG + if (dbg_nal_skip_flag & 1) + parse_type = HEVC_DISCARD_NAL; +#endif + } else if (hevc->have_vps) { + if (naltype == NAL_UNIT_SPS) { + parse_type = HEVC_NAL_UNIT_SPS; + hevc->have_sps = 1; +#ifdef ERROR_HANDLE_DEBUG + if (dbg_nal_skip_flag & 2) + parse_type = HEVC_DISCARD_NAL; +#endif + } else if (naltype == NAL_UNIT_PPS) { + parse_type = HEVC_NAL_UNIT_PPS; + hevc->have_pps = 1; +#ifdef ERROR_HANDLE_DEBUG + if (dbg_nal_skip_flag & 4) + parse_type = HEVC_DISCARD_NAL; +#endif + } else if (hevc->have_sps && hevc->have_pps) { + int seg = HEVC_NAL_UNIT_CODED_SLICE_SEGMENT; + + if ((naltype == NAL_UNIT_CODED_SLICE_IDR) || + (naltype == + NAL_UNIT_CODED_SLICE_IDR_N_LP) + || (naltype == + NAL_UNIT_CODED_SLICE_CRA) + || (naltype == + NAL_UNIT_CODED_SLICE_BLA) + || (naltype == + NAL_UNIT_CODED_SLICE_BLANT) + || (naltype == + NAL_UNIT_CODED_SLICE_BLA_N_LP) + ) { + if (slice_parse_begin > 0) { + pr_info + ("discard %d, for debugging\n", + slice_parse_begin); + slice_parse_begin--; + } else { + parse_type = seg; + } + hevc->have_valid_start_slice = 1; + } else if (naltype <= + NAL_UNIT_CODED_SLICE_CRA + && (hevc->have_valid_start_slice + || (hevc->PB_skip_mode != 3))) { + if (slice_parse_begin > 0) { + pr_info + ("discard %d, dd\n", + slice_parse_begin); + slice_parse_begin--; + } else + parse_type = seg; + + } + } + } + if (hevc->have_vps && hevc->have_sps && hevc->have_pps + && hevc->have_valid_start_slice && + hevc->error_flag == 0) { + if ((debug & H265_DEBUG_MAN_SEARCH_NAL) == 0 && + (!hevc->m_ins_flag)) { + /* + *auot parser NAL; do not check + *vps/sps/pps/idr + */ + WRITE_VREG(NAL_SEARCH_CTL, 0x2); + } + + if (debug & H265_DEBUG_NO_EOS_SEARCH_DONE) { + WRITE_VREG(NAL_SEARCH_CTL, + READ_VREG(NAL_SEARCH_CTL) | + 0x10000); + } + if (parser_sei_enable & 0x1) + WRITE_VREG(NAL_SEARCH_CTL, + READ_VREG(NAL_SEARCH_CTL) | 0x20000); + } + + if (debug & H265_DEBUG_BUFMGR) { + pr_info("naltype = %d parse_type %d\n %d %d %d %d\n", + naltype, parse_type, hevc->have_vps, + hevc->have_sps, hevc->have_pps, + hevc->have_valid_start_slice); + } + + WRITE_VREG(HEVC_DEC_STATUS_REG, parse_type); + /* Interrupt Amrisc to excute */ + WRITE_VREG(HEVC_MCPU_INTR_REQ, AMRISC_MAIN_REQ); + + } else if (dec_status == HEVC_SLICE_SEGMENT_DONE) { + if (hevc->start_decoding_time > 0) { + u32 process_time = 1000* + (jiffies - hevc->start_decoding_time)/HZ; + if (process_time > max_decoding_time) + max_decoding_time = process_time; + } + + hevc->error_watchdog_count = 0; + if (hevc->pic_list_init_flag == 2) { + hevc->pic_list_init_flag = 3; + pr_info("set pic_list_init_flag to 3\n"); + } else if (hevc->wait_buf == 0) { + u32 vui_time_scale; + u32 vui_num_units_in_tick; + + if (debug & H265_DEBUG_SEND_PARAM_WITH_REG) + get_rpm_param(&hevc->param); + else { + dma_sync_single_for_cpu( + amports_get_dma_device(), + hevc->rpm_phy_addr, + RPM_BUF_SIZE, + DMA_FROM_DEVICE); + + for (i = 0; i < (RPM_END - RPM_BEGIN); i += 4) { + int ii; + + for (ii = 0; ii < 4; ii++) { + hevc->param.l.data[i + ii] = + hevc->rpm_ptr[i + 3 + - ii]; + } + } + } + if (debug & H265_DEBUG_BUFMGR_MORE) { + pr_info("rpm_param: (%d)\n", hevc->slice_idx); + hevc->slice_idx++; + for (i = 0; i < (RPM_END - RPM_BEGIN); i++) { + pr_info("%04x ", hevc->param.l.data[i]); + if (((i + 1) & 0xf) == 0) + pr_info("\n"); + } + + pr_info("vui_timing_info: %x, %x, %x, %x\n", + hevc->param.p.vui_num_units_in_tick_hi, + hevc->param.p.vui_num_units_in_tick_lo, + hevc->param.p.vui_time_scale_hi, + hevc->param.p.vui_time_scale_lo); + } + + vui_time_scale = + (u32)(hevc->param.p.vui_time_scale_hi << 16) | + hevc->param.p.vui_time_scale_lo; + vui_num_units_in_tick = + (u32)(hevc->param. + p.vui_num_units_in_tick_hi << 16) | + hevc->param. + p.vui_num_units_in_tick_lo; + if (hevc->bit_depth_luma != + ((hevc->param.p.bit_depth & 0xf) + 8)) { + pr_info("Bit depth luma = %d\n", + (hevc->param.p.bit_depth & 0xf) + 8); + } + if (hevc->bit_depth_chroma != + (((hevc->param.p.bit_depth >> 4) & 0xf) + 8)) { + pr_info("Bit depth chroma = %d\n", + ((hevc->param.p.bit_depth >> 4) & + 0xf) + 8); + } + hevc->bit_depth_luma = + (hevc->param.p.bit_depth & 0xf) + 8; + hevc->bit_depth_chroma = + ((hevc->param.p.bit_depth >> 4) & 0xf) + 8; + bit_depth_luma = hevc->bit_depth_luma; + bit_depth_chroma = hevc->bit_depth_chroma; +#ifdef SUPPORT_10BIT + if (hevc->bit_depth_luma == 8 && + hevc->bit_depth_chroma == 8 && + enable_mem_saving) + hevc->mem_saving_mode = 1; + else + hevc->mem_saving_mode = 0; +#endif + if ((vui_time_scale != 0) + && (vui_num_units_in_tick != 0)) { + hevc->frame_dur = + div_u64(96000ULL * + vui_num_units_in_tick, + vui_time_scale); + hevc->get_frame_dur = true; + } + + if (hevc->video_signal_type != + ((hevc->param.p.video_signal_type << 16) + | hevc->param.p.color_description)) { + u32 v = hevc->param.p.video_signal_type; + u32 c = hevc->param.p.color_description; +#if 0 + if (v & 0x2000) { + pr_info("video_signal_type present:\n"); + pr_info(" %s %s\n", + video_format_names[(v >> 10) & 7], + ((v >> 9) & 1) ? + "full_range" : "limited"); + if (v & 0x100) { + pr_info(" color_description present:\n"); + pr_info(" color_primarie = %s\n", + color_primaries_names + [v & 0xff]); + pr_info(" transfer_characteristic = %s\n", + transfer_characteristics_names + [(c >> 8) & 0xff]); + pr_info(" matrix_coefficient = %s\n", + matrix_coeffs_names[c & 0xff]); + } + } +#endif + hevc->video_signal_type = (v << 16) | c; + video_signal_type = hevc->video_signal_type; + } + + if (use_cma && + (hevc->param.p.slice_segment_address == 0) + && (hevc->pic_list_init_flag == 0)) { + int log = hevc->param.p.log2_min_coding_block_size_minus3; + int log_s = hevc->param.p.log2_diff_max_min_coding_block_size; + + hevc->pic_w = hevc->param.p.pic_width_in_luma_samples; + hevc->pic_h = hevc->param.p.pic_height_in_luma_samples; + hevc->lcu_size = 1 << (log + 3 + log_s); + hevc->lcu_size_log2 = log2i(hevc->lcu_size); + if (hevc->pic_w == 0 || hevc->pic_h == 0 + || hevc->lcu_size == 0) { + /* skip search next start code */ + WRITE_VREG(HEVC_WAIT_FLAG, READ_VREG(HEVC_WAIT_FLAG) + & (~0x2)); + hevc->skip_flag = 1; + WRITE_VREG(HEVC_DEC_STATUS_REG, HEVC_ACTION_DONE); + /* Interrupt Amrisc to excute */ + WRITE_VREG(HEVC_MCPU_INTR_REQ, AMRISC_MAIN_REQ); + + } else { + hevc->sps_num_reorder_pics_0 = + hevc->param.p.sps_num_reorder_pics_0; + hevc->pic_list_init_flag = 1; +#ifdef MULTI_INSTANCE_SUPPORT + if (hevc->m_ins_flag) + schedule_work(&hevc->work); + else +#endif + up(&hevc->h265_sema); + pr_info("set pic_list_init_flag 1\n"); + } + return IRQ_HANDLED; + } + +} + ret = + hevc_slice_segment_header_process(hevc, + &hevc->param, decode_pic_begin); + if (ret < 0) + ; + else if (ret == 0) { + if ((hevc->new_pic) && (hevc->cur_pic)) { + hevc->cur_pic->stream_offset = + READ_VREG(HEVC_SHIFT_BYTE_COUNT); + } + + WRITE_VREG(HEVC_DEC_STATUS_REG, + HEVC_CODED_SLICE_SEGMENT_DAT); + /* Interrupt Amrisc to excute */ + WRITE_VREG(HEVC_MCPU_INTR_REQ, AMRISC_MAIN_REQ); + + hevc->start_decoding_time = jiffies; + } else { + /* skip, search next start code */ + WRITE_VREG(HEVC_WAIT_FLAG, READ_VREG(HEVC_WAIT_FLAG) & (~0x2)); + hevc->skip_flag = 1; + WRITE_VREG(HEVC_DEC_STATUS_REG, HEVC_ACTION_DONE); + /* Interrupt Amrisc to excute */ + WRITE_VREG(HEVC_MCPU_INTR_REQ, AMRISC_MAIN_REQ); + } + + } + + if (mmu_enable) { + if (hevc->last_put_idx_a >= 0 + && hevc->last_put_idx_a < MAX_REF_PIC_NUM) { + int i = hevc->last_put_idx_a; + struct PIC_s *pic = &hevc->m_PIC[i]; + + /*free not used buffers.*/ + if (pic->output_mark == 0 && pic->referenced == 0 + && pic->output_ready == 0 + && pic->used_by_display == 0 + && (pic[i].index != -1)) { + decoder_mmu_box_free_idx(hevc->mmu_box, i); + hevc->last_put_idx_a = -1; + /* pr_info("release pic buf %x\n",i);*/ + } + } + if (hevc->last_put_idx_b >= 0 + && hevc->last_put_idx_b < MAX_REF_PIC_NUM) { + int i = hevc->last_put_idx_b; + struct PIC_s *pic = &hevc->m_PIC[i]; + + /*free not used buffers.*/ + if (pic->output_mark == 0 && pic->referenced == 0 + && pic->output_ready == 0 + && pic->used_by_display == 0 + && (pic[i].index != -1)) { + decoder_mmu_box_free_idx(hevc->mmu_box, i); + hevc->last_put_idx_b = -1; + } + } + } + + return IRQ_HANDLED; +} + +static irqreturn_t vh265_isr(int irq, void *data) +{ + int i, temp; + unsigned int dec_status; + struct hevc_state_s *hevc = (struct hevc_state_s *)data; + + dec_status = READ_VREG(HEVC_DEC_STATUS_REG); + if (hevc->init_flag == 0) + return IRQ_HANDLED; + hevc->dec_status = dec_status; + if (debug & H265_DEBUG_BUFMGR) + pr_info("265 isr dec status = %d\n", dec_status); + + if (debug & H265_DEBUG_UCODE) { + if (READ_HREG(DEBUG_REG1) & 0x10000) { + dma_sync_single_for_cpu( + amports_get_dma_device(), + hevc->lmem_phy_addr, + LMEM_BUF_SIZE, + DMA_FROM_DEVICE); + + pr_info("LMEM<tag %x>:\n", READ_HREG(DEBUG_REG1)); + + if (mmu_enable) + temp = 0x500; + else + temp = 0x400; + for (i = 0; i < temp; i += 4) { + int ii; + + if ((i & 0xf) == 0) + pr_info("%03x: ", i); + for (ii = 0; ii < 4; ii++) { + pr_info("%04x ", + hevc->lmem_ptr[i + 3 - ii]); + } + if (((i + ii) & 0xf) == 0) + pr_info("\n"); + } + WRITE_HREG(DEBUG_REG1, 0); + } else if (READ_HREG(DEBUG_REG1) != 0) { + pr_info("dbg%x: %x\n", READ_HREG(DEBUG_REG1), + READ_HREG(DEBUG_REG2)); + WRITE_HREG(DEBUG_REG1, 0); + return IRQ_HANDLED; + } + + } + + if (hevc->pic_list_init_flag == 1) + return IRQ_HANDLED; + + if (hevc->error_flag == 1) { + if ((error_handle_policy & 0x10) == 0) { + if (hevc->cur_pic) { + int current_lcu_idx = + READ_VREG(HEVC_PARSER_LCU_START) + & 0xffffff; + if (current_lcu_idx < + ((hevc->lcu_x_num*hevc->lcu_y_num)-1)) + hevc->cur_pic->error_mark = 1; + + } + } + if ((error_handle_policy & 1) == 0) { + hevc->error_skip_nal_count = 1; + /* + *manual search nal, skip error_skip_nal_count + *of nal and trigger the HEVC_NAL_SEARCH_DONE irq + */ + WRITE_VREG(NAL_SEARCH_CTL, + (error_skip_nal_count << 4) | 0x1); + } else { + hevc->error_skip_nal_count = error_skip_nal_count; + WRITE_VREG(NAL_SEARCH_CTL, 0x1);/* manual parser NAL */ + } + if (debug & H265_DEBUG_NO_EOS_SEARCH_DONE) { + WRITE_VREG(NAL_SEARCH_CTL, + READ_VREG(NAL_SEARCH_CTL) | 0x10000); + } + if (parser_sei_enable & 0x1) + WRITE_VREG(NAL_SEARCH_CTL, + READ_VREG(NAL_SEARCH_CTL) | 0x20000); + /* search new nal */ + WRITE_VREG(HEVC_DEC_STATUS_REG, HEVC_ACTION_DONE); + /* Interrupt Amrisc to excute */ + WRITE_VREG(HEVC_MCPU_INTR_REQ, AMRISC_MAIN_REQ); + + /* pr_info("%s: error handle\n", __func__); */ + hevc->error_flag = 2; + return IRQ_HANDLED; + } else if (hevc->error_flag == 3) { + pr_info("error_flag=3, hevc_recover"); + hevc_recover(hevc); + hevc->error_flag = 0; + + if ((error_handle_policy & 0x10) == 0) { + if (hevc->cur_pic) { + int current_lcu_idx = + READ_VREG(HEVC_PARSER_LCU_START) + & 0xffffff; + if (current_lcu_idx < + ((hevc->lcu_x_num*hevc->lcu_y_num)-1)) + hevc->cur_pic->error_mark = 1; + + } + } + if ((error_handle_policy & 1) == 0) { + /* + *need skip some data when + *error_flag of 3 is triggered, + */ + /* + *to avoid hevc_recover() being called + *for many times at the same bitstream position + */ + hevc->error_skip_nal_count = 1; + /* + *manual search nal, skip error_skip_nal_count + *of nal and trigger the HEVC_NAL_SEARCH_DONE irq + */ + WRITE_VREG(NAL_SEARCH_CTL, + (error_skip_nal_count << 4) | 0x1); + } + + if ((error_handle_policy & 0x2) == 0) { + hevc->have_vps = 1; + hevc->have_sps = 1; + hevc->have_pps = 1; + } + return IRQ_HANDLED; + } + + i = READ_VREG(HEVC_SHIFT_BYTE_COUNT); + if ((hevc->shift_byte_count_lo & (1 << 31)) && ((i & (1 << 31)) == 0)) + hevc->shift_byte_count_hi++; + hevc->shift_byte_count_lo = i; + +#ifdef MULTI_INSTANCE_SUPPORT + if (dec_status == HEVC_NAL_DECODE_DONE || + dec_status == HEVC_DECPIC_DATA_DONE) { + if (hevc->m_ins_flag) { + hevc->dec_result = DEC_RESULT_DONE; + schedule_work(&hevc->work); + } + + return IRQ_HANDLED; + } +#endif +#if 0 + if (dec_status == HEVC_SEI_DAT) { + int payload_type = READ_HREG(CUR_NAL_UNIT_TYPE) & 0xffff; + int payload_size = + (READ_HREG(CUR_NAL_UNIT_TYPE) >> 16) & 0xffff; + process_nal_sei(hevc, payload_type, payload_size); + + WRITE_VREG(HEVC_DEC_STATUS_REG, HEVC_SEI_DAT_DONE); + } else if (dec_status == HEVC_NAL_SEARCH_DONE) { + int naltype = READ_HREG(CUR_NAL_UNIT_TYPE); + int parse_type = HEVC_DISCARD_NAL; + + hevc->error_watchdog_count = 0; + hevc->error_skip_nal_wt_cnt = 0; + if (slice_parse_begin > 0 && debug & H265_DEBUG_DISCARD_NAL) { + pr_info("nal type %d, discard %d\n", naltype, + slice_parse_begin); + if (naltype <= NAL_UNIT_CODED_SLICE_CRA) + slice_parse_begin--; + } + if (naltype == NAL_UNIT_EOS) { + struct PIC_s *pic; + + pr_info("get NAL_UNIT_EOS, flush output"); + pic = get_pic_by_POC(hevc, hevc->curr_POC); + hevc->curr_POC = INVALID_POC; + /* add to fix RAP_B_Bossen_1 */ + hevc->m_pocRandomAccess = MAX_INT; + flush_output(hevc, pic); + WRITE_VREG(HEVC_DEC_STATUS_REG, HEVC_DISCARD_NAL); + /* Interrupt Amrisc to excute */ + WRITE_VREG(HEVC_MCPU_INTR_REQ, AMRISC_MAIN_REQ); + return IRQ_HANDLED; + } + + if (hevc->error_skip_nal_count > 0) { + pr_info("nal type %d, discard %d\n", naltype, + hevc->error_skip_nal_count); + hevc->error_skip_nal_count--; + if (hevc->error_skip_nal_count == 0) { + hevc_recover(hevc); + hevc->error_flag = 0; + if ((error_handle_policy & 0x2) == 0) { + hevc->have_vps = 1; + hevc->have_sps = 1; + hevc->have_pps = 1; + } + return IRQ_HANDLED; + } + } else if (naltype == NAL_UNIT_VPS) { + parse_type = HEVC_NAL_UNIT_VPS; + hevc->have_vps = 1; +#ifdef ERROR_HANDLE_DEBUG + if (dbg_nal_skip_flag & 1) + parse_type = HEVC_DISCARD_NAL; +#endif + } else if (hevc->have_vps) { + if (naltype == NAL_UNIT_SPS) { + parse_type = HEVC_NAL_UNIT_SPS; + hevc->have_sps = 1; +#ifdef ERROR_HANDLE_DEBUG + if (dbg_nal_skip_flag & 2) + parse_type = HEVC_DISCARD_NAL; +#endif + } else if (naltype == NAL_UNIT_PPS) { + parse_type = HEVC_NAL_UNIT_PPS; + hevc->have_pps = 1; +#ifdef ERROR_HANDLE_DEBUG + if (dbg_nal_skip_flag & 4) + parse_type = HEVC_DISCARD_NAL; +#endif + } else if (hevc->have_sps && hevc->have_pps) { + int seg = HEVC_NAL_UNIT_CODED_SLICE_SEGMENT; + + if ((naltype == NAL_UNIT_CODED_SLICE_IDR) || + (naltype == + NAL_UNIT_CODED_SLICE_IDR_N_LP) + || (naltype == + NAL_UNIT_CODED_SLICE_CRA) + || (naltype == + NAL_UNIT_CODED_SLICE_BLA) + || (naltype == + NAL_UNIT_CODED_SLICE_BLANT) + || (naltype == + NAL_UNIT_CODED_SLICE_BLA_N_LP) + ) { + if (slice_parse_begin > 0) { + pr_info + ("discard %d, for debugging\n", + slice_parse_begin); + slice_parse_begin--; + } else { + parse_type = seg; + } + hevc->have_valid_start_slice = 1; + } else if (naltype <= + NAL_UNIT_CODED_SLICE_CRA + && (hevc->have_valid_start_slice + || (hevc->PB_skip_mode != 3))) { + if (slice_parse_begin > 0) { + pr_info + ("discard %d, dd\n", + slice_parse_begin); + slice_parse_begin--; + } else + parse_type = seg; + + } + } + } + if (hevc->have_vps && hevc->have_sps && hevc->have_pps + && hevc->have_valid_start_slice && + hevc->error_flag == 0) { + if ((debug & H265_DEBUG_MAN_SEARCH_NAL) == 0 && + (!hevc->m_ins_flag)) { + /* + *auot parser NAL; do not check + *vps/sps/pps/idr + */ + WRITE_VREG(NAL_SEARCH_CTL, 0x2); + } + + if (debug & H265_DEBUG_NO_EOS_SEARCH_DONE) { + WRITE_VREG(NAL_SEARCH_CTL, + READ_VREG(NAL_SEARCH_CTL) | + 0x10000); + } + if (parser_sei_enable & 0x1) + WRITE_VREG(NAL_SEARCH_CTL, + READ_VREG(NAL_SEARCH_CTL) | 0x20000); + } + + if (debug & H265_DEBUG_BUFMGR) { + pr_info("naltype = %d parse_type %d\n %d %d %d %d\n", + naltype, parse_type, hevc->have_vps, + hevc->have_sps, hevc->have_pps, + hevc->have_valid_start_slice); + } + + WRITE_VREG(HEVC_DEC_STATUS_REG, parse_type); + /* Interrupt Amrisc to excute */ + WRITE_VREG(HEVC_MCPU_INTR_REQ, AMRISC_MAIN_REQ); + + } else if (dec_status == HEVC_SLICE_SEGMENT_DONE) { + if (hevc->start_decoding_time > 0) { + u32 process_time = 1000* + (jiffies - hevc->start_decoding_time)/HZ; + if (process_time > max_decoding_time) + max_decoding_time = process_time; + } + + hevc->error_watchdog_count = 0; + if (hevc->pic_list_init_flag == 2) { + hevc->pic_list_init_flag = 3; + pr_info("set pic_list_init_flag to 3\n"); + } else if (hevc->wait_buf == 0) { + u32 vui_time_scale; + u32 vui_num_units_in_tick; + + if (debug & H265_DEBUG_SEND_PARAM_WITH_REG) + get_rpm_param(&hevc->param); + else { + dma_sync_single_for_cpu( + amports_get_dma_device(), + hevc->rpm_phy_addr, + RPM_BUF_SIZE, + DMA_FROM_DEVICE); + + for (i = 0; i < (RPM_END - RPM_BEGIN); i += 4) { + int ii; + + for (ii = 0; ii < 4; ii++) { + hevc->param.l.data[i + ii] = + hevc->rpm_ptr[i + 3 + - ii]; + } + } + } + if (debug & H265_DEBUG_BUFMGR_MORE) { + pr_info("rpm_param: (%d)\n", hevc->slice_idx); + hevc->slice_idx++; + for (i = 0; i < (RPM_END - RPM_BEGIN); i++) { + pr_info("%04x ", hevc->param.l.data[i]); + if (((i + 1) & 0xf) == 0) + pr_info("\n"); + } + + pr_info("vui_timing_info: %x, %x, %x, %x\n", + hevc-> + param.p.vui_num_units_in_tick_hi, + hevc-> + param.p.vui_num_units_in_tick_lo, + hevc->param.p.vui_time_scale_hi, + hevc->param.p.vui_time_scale_lo); + } + + vui_time_scale = + (u32)(hevc->param.p. + vui_time_scale_hi << 16) | + hevc->param.p.vui_time_scale_lo; + vui_num_units_in_tick = + (u32)(hevc->param.p. + vui_num_units_in_tick_hi << 16) | + hevc->param. + p.vui_num_units_in_tick_lo; + if (hevc->bit_depth_luma != + ((hevc->param.p.bit_depth & 0xf) + 8)) { + pr_info("Bit depth luma = %d\n", + (hevc->param.p.bit_depth & 0xf) + 8); + } + if (hevc->bit_depth_chroma != + (((hevc->param.p.bit_depth >> 4) & 0xf) + 8)) { + pr_info("Bit depth chroma = %d\n", + ((hevc->param.p.bit_depth >> 4) & + 0xf) + 8); + } + hevc->bit_depth_luma = + (hevc->param.p.bit_depth & 0xf) + 8; + hevc->bit_depth_chroma = + ((hevc->param.p.bit_depth >> 4) & 0xf) + 8; + bit_depth_luma = hevc->bit_depth_luma; + bit_depth_chroma = hevc->bit_depth_chroma; +#ifdef SUPPORT_10BIT + if (hevc->bit_depth_luma == 8 && + hevc->bit_depth_chroma == 8 && + enable_mem_saving) + hevc->mem_saving_mode = 1; + else + hevc->mem_saving_mode = 0; +#endif + if ((vui_time_scale != 0) + && (vui_num_units_in_tick != 0)) { + hevc->frame_dur = + div_u64(96000ULL * + vui_num_units_in_tick, + vui_time_scale); + hevc->get_frame_dur = true; + } + + if (hevc->video_signal_type != + ((hevc->param.p.video_signal_type << 16) + | hevc->param.p.color_description)) { + u32 v = hevc->param.p.video_signal_type; + u32 c = hevc->param.p.color_description; +#if 0 + if (v & 0x2000) { + pr_info("video_signal_type present:\n"); + pr_info(" %s %s\n", + video_format_names[(v >> 10) & 7], + ((v >> 9) & 1) ? + "full_range" : "limited"); + if (v & 0x100) { + pr_info(" color_description present:\n"); + pr_info(" color_primarie = %s\n", + color_primaries_names + [v & 0xff]); + pr_info(" transfer_characteristic = %s\n", + transfer_characteristics_names + [(c >> 8) & 0xff]); + pr_info(" matrix_coefficient = %s\n", + matrix_coeffs_names[c & 0xff]); + } + } +#endif + hevc->video_signal_type = (v << 16) | c; + video_signal_type = hevc->video_signal_type; + } + + if (use_cma && + (hevc->param.p.slice_segment_address == 0) + && (hevc->pic_list_init_flag == 0)) { + int log = hevc->param.p. + log2_min_coding_block_size_minus3; + int log_s = hevc->param.p. + log2_diff_max_min_coding_block_size; + hevc->pic_w = + hevc->param.p.pic_width_in_luma_samples; + hevc->pic_h = + hevc->param.p.pic_height_in_luma_samples; + hevc->lcu_size = + 1 << (log + 3 + log_s); + hevc->lcu_size_log2 = log2i(hevc->lcu_size); + if (hevc->pic_w == 0 || hevc->pic_h == 0 + || hevc->lcu_size == 0) { + /* skip, search next start code */ + WRITE_VREG(HEVC_WAIT_FLAG, + READ_VREG(HEVC_WAIT_FLAG) & + (~0x2)); + hevc->skip_flag = 1; + WRITE_VREG(HEVC_DEC_STATUS_REG, + HEVC_ACTION_DONE); + /* Interrupt Amrisc to excute */ + WRITE_VREG(HEVC_MCPU_INTR_REQ, + AMRISC_MAIN_REQ); + + } else { + hevc->sps_num_reorder_pics_0 = + hevc->param.p.sps_num_reorder_pics_0; + hevc->pic_list_init_flag = 1; +#ifdef MULTI_INSTANCE_SUPPORT + if (hevc->m_ins_flag) + schedule_work(&hevc->work); + else +#endif + up(&hevc->h265_sema); + pr_info("set pic_list_init_flag 1\n"); + } + return IRQ_HANDLED; + } + + } + ret = + hevc_slice_segment_header_process(hevc, &hevc->param, + decode_pic_begin); + if (ret < 0) + ; + else if (ret == 0) { + if ((hevc->new_pic) && (hevc->cur_pic)) { + hevc->cur_pic->stream_offset = + READ_VREG(HEVC_SHIFT_BYTE_COUNT); + } + + WRITE_VREG(HEVC_DEC_STATUS_REG, + HEVC_CODED_SLICE_SEGMENT_DAT); + /* Interrupt Amrisc to excute */ + WRITE_VREG(HEVC_MCPU_INTR_REQ, AMRISC_MAIN_REQ); + + hevc->start_decoding_time = jiffies; + } else { + /* skip, search next start code */ + WRITE_VREG(HEVC_WAIT_FLAG, + READ_VREG(HEVC_WAIT_FLAG) & (~0x2)); + hevc->skip_flag = 1; + WRITE_VREG(HEVC_DEC_STATUS_REG, HEVC_ACTION_DONE); + /* Interrupt Amrisc to excute */ + WRITE_VREG(HEVC_MCPU_INTR_REQ, AMRISC_MAIN_REQ); + } + + } + return IRQ_HANDLED; + +#else + return IRQ_WAKE_THREAD; +#endif + +} + +static void vh265_put_timer_func(unsigned long arg) +{ + struct hevc_state_s *hevc = (struct hevc_state_s *)arg; + struct timer_list *timer = &hevc->timer; + unsigned char empty_flag; + unsigned int buf_level; + + enum receviver_start_e state = RECEIVER_INACTIVE; + + if (hevc->init_flag == 0) { + if (hevc->stat & STAT_TIMER_ARM) { + timer->expires = jiffies + PUT_INTERVAL; + add_timer(&hevc->timer); + } + return; + } + + if (hevc->m_ins_flag == 0 && + vf_get_receiver(hevc->provider_name)) { + state = + vf_notify_receiver(hevc->provider_name, + VFRAME_EVENT_PROVIDER_QUREY_STATE, NULL); + if ((state == RECEIVER_STATE_NULL) + || (state == RECEIVER_STATE_NONE)) + state = RECEIVER_INACTIVE; + } else + state = RECEIVER_INACTIVE; + + empty_flag = (READ_VREG(HEVC_PARSER_INT_STATUS) >> 6) & 0x1; + /* error watchdog */ + if (hevc->m_ins_flag == 0 && + (empty_flag == 0) + && (hevc->pic_list_init_flag == 0 + || hevc->pic_list_init_flag == 3)) { + /* decoder has input */ + if ((debug & H265_DEBUG_DIS_LOC_ERROR_PROC) == 0) { + + buf_level = READ_VREG(HEVC_STREAM_LEVEL); + /* receiver has no buffer to recycle */ + if ((state == RECEIVER_INACTIVE) && + (kfifo_is_empty(&hevc->display_q) && + buf_level > 0x200) + ) { + if (hevc->error_flag == 0) { + hevc->error_watchdog_count++; + if (hevc->error_watchdog_count == + error_handle_threshold) { + pr_info + ("H265 dec err local reset.\n"); + hevc->error_flag = 1; + hevc->error_watchdog_count = 0; + hevc->error_skip_nal_wt_cnt = 0; + hevc-> + error_system_watchdog_count++; + WRITE_VREG + (HEVC_ASSIST_MBOX1_IRQ_REG, + 0x1); + } + } else if (hevc->error_flag == 2) { + int th = + error_handle_nal_skip_threshold; + hevc->error_skip_nal_wt_cnt++; + if (hevc->error_skip_nal_wt_cnt + == th) { + hevc->error_flag = 3; + hevc->error_watchdog_count = 0; + hevc-> + error_skip_nal_wt_cnt = 0; + WRITE_VREG + (HEVC_ASSIST_MBOX1_IRQ_REG, + 0x1); + } + } + } + } + + if ((debug & H265_DEBUG_DIS_SYS_ERROR_PROC) == 0) + /* receiver has no buffer to recycle */ + if ((state == RECEIVER_INACTIVE) && + (kfifo_is_empty(&hevc->display_q)) + ) { /* no buffer to recycle */ + if ((debug & H265_DEBUG_DIS_LOC_ERROR_PROC) != + 0) + hevc->error_system_watchdog_count++; + if (hevc->error_system_watchdog_count == + error_handle_system_threshold) { + /* and it lasts for a while */ + pr_info + ("H265 dec fatal error watchdog.\n"); + hevc->error_system_watchdog_count = 0; + hevc->fatal_error = + DECODER_FATAL_ERROR_UNKNOWN; + } + } + } else { + hevc->error_watchdog_count = 0; + hevc->error_system_watchdog_count = 0; + } + + if (decode_stop_pos != decode_stop_pos_pre) { + WRITE_VREG(DECODE_STOP_POS, decode_stop_pos); + decode_stop_pos_pre = decode_stop_pos; + } + + if (debug & H265_DEBUG_DUMP_PIC_LIST) { + dump_pic_list(hevc); + debug &= ~H265_DEBUG_DUMP_PIC_LIST; + } + if (debug & H265_DEBUG_TRIG_SLICE_SEGMENT_PROC) { + WRITE_VREG(HEVC_ASSIST_MBOX1_IRQ_REG, 0x1); + debug &= ~H265_DEBUG_TRIG_SLICE_SEGMENT_PROC; + } + if (debug & H265_DEBUG_HW_RESET) { + hevc->error_skip_nal_count = error_skip_nal_count; + WRITE_VREG(HEVC_DEC_STATUS_REG, HEVC_ACTION_DONE); + + debug &= ~H265_DEBUG_HW_RESET; + } + if (debug & H265_DEBUG_ERROR_TRIG) { + WRITE_VREG(DECODE_STOP_POS, 1); + debug &= ~H265_DEBUG_ERROR_TRIG; + } +#ifdef ERROR_HANDLE_DEBUG + if ((dbg_nal_skip_count > 0) && ((dbg_nal_skip_count & 0x10000) != 0)) { + hevc->error_skip_nal_count = dbg_nal_skip_count & 0xffff; + dbg_nal_skip_count &= ~0x10000; + WRITE_VREG(HEVC_DEC_STATUS_REG, HEVC_ACTION_DONE); + } +#endif + + if (radr != 0) { + if (rval != 0) { + WRITE_VREG(radr, rval); + pr_info("WRITE_VREG(%x,%x)\n", radr, rval); + } else + pr_info("READ_VREG(%x)=%x\n", radr, READ_VREG(radr)); + rval = 0; + radr = 0; + } + if (dbg_cmd != 0) { + if (dbg_cmd == 1) { + u32 disp_laddr; + + if (get_cpu_type() >= MESON_CPU_MAJOR_ID_GXBB && + double_write_mode == 0) { + disp_laddr = + READ_VCBUS_REG(AFBC_BODY_BADDR) << 4; + } else { + struct canvas_s cur_canvas; + + canvas_read((READ_VCBUS_REG(VD1_IF0_CANVAS0) + & 0xff), &cur_canvas); + disp_laddr = cur_canvas.addr; + } + pr_info("current displayed buffer address %x\r\n", + disp_laddr); + } + dbg_cmd = 0; + } + /*don't changed at start.*/ + if (hevc->m_ins_flag == 0 && + hevc->get_frame_dur && hevc->show_frame_num > 60 && + hevc->frame_dur > 0 && hevc->saved_resolution != + hevc->frame_width * hevc->frame_height * + (96000 / hevc->frame_dur)) { + int fps = 96000 / hevc->frame_dur; + + if (hevc_source_changed(VFORMAT_HEVC, + hevc->frame_width, hevc->frame_height, fps) > 0) + hevc->saved_resolution = hevc->frame_width * + hevc->frame_height * fps; + } + + + timer->expires = jiffies + PUT_INTERVAL; + add_timer(timer); +} + +static int h265_task_handle(void *data) +{ + int ret = 0; + struct hevc_state_s *hevc = (struct hevc_state_s *)data; + + set_user_nice(current, -10); + while (1) { + if (use_cma == 0) { + pr_info + ("ERROR: use_cma can not be changed dynamically\n"); + } + ret = down_interruptible(&hevc->h265_sema); + if ((hevc->init_flag != 0) && (hevc->pic_list_init_flag == 1)) { + /*USE_BUF_BLOCK*/ + init_buf_list(hevc); + /**/ + init_pic_list(hevc); + init_pic_list_hw(hevc); + init_buf_spec(hevc); + hevc->pic_list_init_flag = 2; + pr_info("set pic_list_init_flag to 2\n"); + + WRITE_VREG(HEVC_ASSIST_MBOX1_IRQ_REG, 0x1); + + } + + if (hevc->uninit_list) { + /*USE_BUF_BLOCK*/ + uninit_buf_list(hevc, false); + pr_info("uninit list\n"); + hevc->uninit_list = 0; +#ifdef USE_UNINIT_SEMA + up(&hevc->h265_uninit_done_sema); +#endif + } + + } + + return 0; + +} + +void vh265_free_cmabuf(void) +{ + struct hevc_state_s *hevc = &gHevc; + + mutex_lock(&vh265_mutex); + + if (hevc->init_flag) { + mutex_unlock(&vh265_mutex); + return; + } + + if (use_cma) { + pr_info("force uninit_buf_list\n"); + uninit_buf_list(hevc, true); + } + + mutex_unlock(&vh265_mutex); +} + +#ifdef MULTI_INSTANCE_SUPPORT +int vh265_dec_status(struct vdec_s *vdec, struct vdec_status *vstatus) +#else +int vh265_dec_status(struct vdec_status *vstatus) +#endif +{ + struct hevc_state_s *hevc = &gHevc; + + vstatus->width = hevc->frame_width; + vstatus->height = hevc->frame_height; + if (hevc->frame_dur != 0) + vstatus->fps = 96000 / hevc->frame_dur; + else + vstatus->fps = -1; + vstatus->error_count = 0; + vstatus->status = hevc->stat | hevc->fatal_error; + return 0; +} + +#if 0 +static void H265_DECODE_INIT(void) +{ + /* enable hevc clocks */ + WRITE_VREG(DOS_GCLK_EN3, 0xffffffff); + /* *************************************************************** */ + /* Power ON HEVC */ + /* *************************************************************** */ + /* Powerup HEVC */ + WRITE_VREG(P_AO_RTI_GEN_PWR_SLEEP0, + READ_VREG(P_AO_RTI_GEN_PWR_SLEEP0) & (~(0x3 << 6))); + WRITE_VREG(DOS_MEM_PD_HEVC, 0x0); + WRITE_VREG(DOS_SW_RESET3, READ_VREG(DOS_SW_RESET3) | (0x3ffff << 2)); + WRITE_VREG(DOS_SW_RESET3, READ_VREG(DOS_SW_RESET3) & (~(0x3ffff << 2))); + /* remove isolations */ + WRITE_VREG(AO_RTI_GEN_PWR_ISO0, + READ_VREG(AO_RTI_GEN_PWR_ISO0) & (~(0x3 << 10))); + +} +#endif + +static void vh265_prot_init(struct hevc_state_s *hevc) +{ + /* H265_DECODE_INIT(); */ + + hevc_config_work_space_hw(hevc); + + hevc_init_decoder_hw(hevc, 0, 0xffffffff); + + WRITE_VREG(HEVC_WAIT_FLAG, 1); + + /* WRITE_VREG(P_HEVC_MPSR, 1); */ + + /* clear mailbox interrupt */ + WRITE_VREG(HEVC_ASSIST_MBOX1_CLR_REG, 1); + + /* enable mailbox interrupt */ + WRITE_VREG(HEVC_ASSIST_MBOX1_MASK, 1); + + /* disable PSCALE for hardware sharing */ + WRITE_VREG(HEVC_PSCALE_CTRL, 0); + + if (debug & H265_DEBUG_UCODE) + WRITE_VREG(DEBUG_REG1, 0x1); + else + WRITE_VREG(DEBUG_REG1, 0x0); + + if ((debug & (H265_DEBUG_MAN_SKIP_NAL | H265_DEBUG_MAN_SEARCH_NAL)) || + hevc->m_ins_flag) { + WRITE_VREG(NAL_SEARCH_CTL, 0x1); /* manual parser NAL */ + } else { + unsigned ctl_val = 0x8; /* check vps/sps/pps/i-slice in ucode */ + + if (hevc->PB_skip_mode == 0) + ctl_val = 0x4; /* check vps/sps/pps only in ucode */ + else if (hevc->PB_skip_mode == 3) + ctl_val = 0x0; /* check vps/sps/pps/idr in ucode */ + WRITE_VREG(NAL_SEARCH_CTL, ctl_val); + } + if (debug & H265_DEBUG_NO_EOS_SEARCH_DONE) + WRITE_VREG(NAL_SEARCH_CTL, READ_VREG(NAL_SEARCH_CTL) | 0x10000); + + if (parser_sei_enable & 0x1) + WRITE_VREG(NAL_SEARCH_CTL, READ_VREG(NAL_SEARCH_CTL) | 0x20000); + WRITE_VREG(DECODE_STOP_POS, decode_stop_pos); + +} + +static int vh265_local_init(struct hevc_state_s *hevc) +{ + int i; + int ret = -1; + +#ifdef DEBUG_PTS + hevc->pts_missed = 0; + hevc->pts_hit = 0; +#endif + + hevc->last_put_idx_a = -1; + hevc->last_put_idx_b = -1; + hevc->saved_resolution = 0; + hevc->get_frame_dur = false; + hevc->frame_width = hevc->vh265_amstream_dec_info.width; + hevc->frame_height = hevc->vh265_amstream_dec_info.height; + if (hevc->frame_width * hevc->frame_height > HEVC_SIZE) { + pr_info("over size : %u x %u.\n", + hevc->frame_width, hevc->frame_height); + hevc->fatal_error |= DECODER_FATAL_ERROR_SIZE_OVERFLOW; + return ret; + } + hevc->frame_dur = + (hevc->vh265_amstream_dec_info.rate == + 0) ? 3600 : hevc->vh265_amstream_dec_info.rate; + if (hevc->frame_width && hevc->frame_height) + hevc->frame_ar = hevc->frame_height * 0x100 / hevc->frame_width; + hevc->error_watchdog_count = 0; + hevc->sei_present_flag = 0; + pts_unstable = ((unsigned long)hevc->vh265_amstream_dec_info.param + & 0x40) >> 6; + pr_info("h265:pts_unstable=%d\n", pts_unstable); +/* +*TODO:FOR VERSION +*/ + pr_info("h265: ver (%d,%d) decinfo: %dx%d rate=%d\n", h265_version, + 0, hevc->frame_width, hevc->frame_height, hevc->frame_dur); + + if (hevc->frame_dur == 0) + hevc->frame_dur = 96000 / 24; + + INIT_KFIFO(hevc->display_q); + INIT_KFIFO(hevc->newframe_q); + + + for (i = 0; i < VF_POOL_SIZE; i++) { + const struct vframe_s *vf = &hevc->vfpool[i]; + + hevc->vfpool[i].index = -1; + kfifo_put(&hevc->newframe_q, vf); + } + + + ret = hevc_local_init(hevc); + + return ret; +} +#ifdef MULTI_INSTANCE_SUPPORT +static s32 vh265_init(struct vdec_s *vdec) +{ + struct hevc_state_s *hevc = (struct hevc_state_s *)vdec->private; +#else +static s32 vh265_init(struct hevc_state_s *hevc) +{ +#endif + init_timer(&hevc->timer); + + hevc->stat |= STAT_TIMER_INIT; + if (vh265_local_init(hevc) < 0) + return -EBUSY; + +#ifdef MULTI_INSTANCE_SUPPORT + if (hevc->m_ins_flag) { + hevc->timer.data = (ulong) hevc; + hevc->timer.function = vh265_put_timer_func; + hevc->timer.expires = jiffies + PUT_INTERVAL; + + add_timer(&hevc->timer); + + hevc->stat |= STAT_TIMER_ARM; + + INIT_WORK(&hevc->work, vh265_work); + return 0; + } +#endif + amhevc_enable(); + if (debug & H265_DEBUG_LOAD_UCODE_FROM_FILE) { + pr_info("load ucode from file of vh265_mc_debug\n"); + if (amhevc_loadmc_ex(VFORMAT_HEVC, + "vh265_mc_debug", NULL) < 0) { + amhevc_disable(); + return -EBUSY; + } +#if 0 + } else if (double_write_mode & 0x10) { + pr_info("load ucode from file of vh265_mc_dw\n"); + if (amhevc_loadmc_ex(VFORMAT_HEVC, + "vh265_mc_dw", NULL) < 0) { + amhevc_disable(); + return -EBUSY; + } +#endif + } else if (mmu_enable && (get_cpu_type() >= MESON_CPU_MAJOR_ID_GXL)) { + if (amhevc_loadmc_ex(VFORMAT_HEVC, "vh265_mc_mmu", NULL) + < 0) { + amhevc_disable(); + return -EBUSY; + } + pr_info("vh265 mmu ucode loaded!\n"); + } else if (amhevc_loadmc_ex(VFORMAT_HEVC, "vh265_mc", NULL) < 0) { + amhevc_disable(); + return -EBUSY; + } + hevc->stat |= STAT_MC_LOAD; + + /* enable AMRISC side protocol */ + vh265_prot_init(hevc); + + if (vdec_request_threaded_irq(VDEC_IRQ_1, vh265_isr, + vh265_isr_thread_fn, + IRQF_ONESHOT,/*run thread on this irq disabled*/ + "vh265-irq", (void *)hevc)) { + pr_info("vh265 irq register error.\n"); + amhevc_disable(); + return -ENOENT; + } + + hevc->stat |= STAT_ISR_REG; + hevc->provider_name = PROVIDER_NAME; + +#ifdef MULTI_INSTANCE_SUPPORT + vf_provider_init(&vh265_vf_prov, hevc->provider_name, + &vh265_vf_provider, vdec); + vf_reg_provider(&vh265_vf_prov); + vf_notify_receiver(hevc->provider_name, VFRAME_EVENT_PROVIDER_START, + NULL); + vf_notify_receiver(hevc->provider_name, VFRAME_EVENT_PROVIDER_FR_HINT, + (void *)((unsigned long)hevc->frame_dur)); +#else + vf_provider_init(&vh265_vf_prov, PROVIDER_NAME, &vh265_vf_provider, + hevc); + vf_reg_provider(&vh265_vf_prov); + vf_notify_receiver(PROVIDER_NAME, VFRAME_EVENT_PROVIDER_START, NULL); + vf_notify_receiver(PROVIDER_NAME, VFRAME_EVENT_PROVIDER_FR_HINT, + (void *)((unsigned long)hevc->frame_dur)); +#endif + hevc->stat |= STAT_VF_HOOK; + + hevc->timer.data = (ulong) hevc; + hevc->timer.function = vh265_put_timer_func; + hevc->timer.expires = jiffies + PUT_INTERVAL; + + add_timer(&hevc->timer); + + hevc->stat |= STAT_TIMER_ARM; + + if (use_cma) { + if (h265_task == NULL) { + sema_init(&hevc->h265_sema, 1); +#ifdef USE_UNINIT_SEMA + sema_init( + &hevc->h265_uninit_done_sema, 0); +#endif + h265_task = + kthread_run(h265_task_handle, hevc, + "kthread_h265"); + } + } + /* hevc->stat |= STAT_KTHREAD; */ + + if (debug & H265_DEBUG_FORCE_CLK) { + pr_info("%s force clk\n", __func__); + WRITE_VREG(HEVC_IQIT_CLK_RST_CTRL, + READ_VREG(HEVC_IQIT_CLK_RST_CTRL) | + ((1 << 2) | (1 << 1))); + WRITE_VREG(HEVC_DBLK_CFG0, + READ_VREG(HEVC_DBLK_CFG0) | ((1 << 2) | + (1 << 1) | 0x3fff0000));/* 2,29:16 */ + WRITE_VREG(HEVC_SAO_CTRL1, READ_VREG(HEVC_SAO_CTRL1) | + (1 << 2)); /* 2 */ + WRITE_VREG(HEVC_MPRED_CTRL1, READ_VREG(HEVC_MPRED_CTRL1) | + (1 << 24)); /* 24 */ + WRITE_VREG(HEVC_STREAM_CONTROL, + READ_VREG(HEVC_STREAM_CONTROL) | + (1 << 15)); /* 15 */ + WRITE_VREG(HEVC_CABAC_CONTROL, READ_VREG(HEVC_CABAC_CONTROL) | + (1 << 13)); /* 13 */ + WRITE_VREG(HEVC_PARSER_CORE_CONTROL, + READ_VREG(HEVC_PARSER_CORE_CONTROL) | + (1 << 15)); /* 15 */ + WRITE_VREG(HEVC_PARSER_INT_CONTROL, + READ_VREG(HEVC_PARSER_INT_CONTROL) | + (1 << 15)); /* 15 */ + WRITE_VREG(HEVC_PARSER_IF_CONTROL, + READ_VREG(HEVC_PARSER_IF_CONTROL) | ((1 << 6) | + (1 << 3) | (1 << 1))); /* 6, 3, 1 */ + WRITE_VREG(HEVCD_IPP_DYNCLKGATE_CONFIG, + READ_VREG(HEVCD_IPP_DYNCLKGATE_CONFIG) | + 0xffffffff); /* 31:0 */ + WRITE_VREG(HEVCD_MCRCC_CTL1, READ_VREG(HEVCD_MCRCC_CTL1) | + (1 << 3)); /* 3 */ + } + + amhevc_start(); + + hevc->stat |= STAT_VDEC_RUN; +#ifndef MULTI_INSTANCE_SUPPORT + set_vdec_func(&vh265_dec_status); +#endif + hevc->init_flag = 1; + if (mmu_enable) + error_handle_threshold = 300; + else + error_handle_threshold = 30; + /* + *pr_info("%d, vh265_init, RP=0x%x\n", + *__LINE__, READ_VREG(HEVC_STREAM_RD_PTR)); + */ + + return 0; +} + +static int vh265_stop(struct hevc_state_s *hevc) +{ + + hevc->init_flag = 0; + + if (debug & H265_DEBUG_WAIT_DECODE_DONE_WHEN_STOP) { + int wait_timeout_count = 0; + + while (READ_VREG(HEVC_DEC_STATUS_REG) == + HEVC_CODED_SLICE_SEGMENT_DAT && + wait_timeout_count < 10){ + wait_timeout_count++; + msleep(20); + } + } + if (hevc->stat & STAT_VDEC_RUN) { + amhevc_stop(); + hevc->stat &= ~STAT_VDEC_RUN; + } + + if (hevc->stat & STAT_ISR_REG) { + WRITE_VREG(HEVC_ASSIST_MBOX1_MASK, 0); + vdec_free_irq(VDEC_IRQ_1, (void *)hevc); + hevc->stat &= ~STAT_ISR_REG; + } + + hevc->stat &= ~STAT_TIMER_INIT; + if (hevc->stat & STAT_TIMER_ARM) { + del_timer_sync(&hevc->timer); + hevc->stat &= ~STAT_TIMER_ARM; + } + + if (hevc->stat & STAT_VF_HOOK) { + vf_notify_receiver(hevc->provider_name, + VFRAME_EVENT_PROVIDER_FR_END_HINT, NULL); + + vf_unreg_provider(&vh265_vf_prov); + hevc->stat &= ~STAT_VF_HOOK; + } + + hevc_local_uninit(hevc); + + if (use_cma) { +#ifdef USE_UNINIT_SEMA + int ret; +#endif + hevc->uninit_list = 1; + up(&hevc->h265_sema); +#ifdef USE_UNINIT_SEMA + ret = down_interruptible( + &hevc->h265_uninit_done_sema); +#else + while (hevc->uninit_list) /* wait uninit complete */ + msleep(20); +#endif + if (hevc->mmu_box) + uninit_mmu_buffers(hevc); + } + + amhevc_disable(); + + return 0; +} + +#ifdef MULTI_INSTANCE_SUPPORT +static int vmh265_stop(struct hevc_state_s *hevc) +{ + + hevc->init_flag = 0; + + if (hevc->stat & STAT_TIMER_ARM) { + del_timer_sync(&hevc->timer); + hevc->stat &= ~STAT_TIMER_ARM; + } + + if (hevc->stat & STAT_VF_HOOK) { + vf_notify_receiver(hevc->provider_name, + VFRAME_EVENT_PROVIDER_FR_END_HINT, NULL); + + vf_unreg_provider(&vh265_vf_prov); + hevc->stat &= ~STAT_VF_HOOK; + } + + hevc_local_uninit(hevc); + + if (use_cma) { +#ifdef USE_UNINIT_SEMA + int ret; +#endif + hevc->uninit_list = 1; + schedule_work(&hevc->work); +#ifdef USE_UNINIT_SEMA + ret = down_interruptible( + &hevc->h265_uninit_done_sema); +#else + while (hevc->uninit_list) /* wait uninit complete */ + msleep(20); +#endif + } + cancel_work_sync(&hevc->work); + + return 0; +} + +static unsigned int start_decode_buf_level; /* = 0x80000;*/ + +static void vh265_work(struct work_struct *work) +{ + struct hevc_state_s *hevc = container_of(work, + struct hevc_state_s, work); + + /* + *finished decoding one frame or error, + * notify vdec core to switch context + */ + if ((hevc->init_flag != 0) && (hevc->pic_list_init_flag == 1)) { + /*USE_BUF_BLOCK*/ + init_buf_list(hevc); + /**/ + init_pic_list(hevc); + init_pic_list_hw(hevc); + init_buf_spec(hevc); + hevc->pic_list_init_flag = 2; + pr_info("set pic_list_init_flag to 2\n"); + + WRITE_VREG(HEVC_ASSIST_MBOX1_IRQ_REG, 0x1); + return; + } + + if (hevc->uninit_list) { + /*USE_BUF_BLOCK*/ + uninit_buf_list(hevc, false); + pr_info("uninit list\n"); + hevc->uninit_list = 0; +#ifdef USE_UNINIT_SEMA + up(&hevc->h265_uninit_done_sema); +#endif + return; + } + + if (hevc->dec_result == DEC_RESULT_DONE) { + /* + *if (!hevc->ctx_valid) + * hevc->ctx_valid = 1; + */ + hevc_print(hevc, PRINT_FLAG_VDEC_STATUS, + "%s dec_result %d %x %x %x\n", + __func__, + hevc->dec_result, + READ_VREG(HEVC_STREAM_LEVEL), + READ_VREG(HEVC_STREAM_WR_PTR), + READ_VREG(HEVC_STREAM_RD_PTR)); + vdec_vframe_dirty(hw_to_vdec(hevc), hevc->chunk); + } else { + hevc_print(hevc, PRINT_FLAG_VDEC_DETAIL, + "%s dec_result %d %x %x %x\n", + __func__, + hevc->dec_result, + READ_VREG(HEVC_STREAM_LEVEL), + READ_VREG(HEVC_STREAM_WR_PTR), + READ_VREG(HEVC_STREAM_RD_PTR)); + } + + /* mark itself has all HW resource released and input released */ + vdec_set_status(hw_to_vdec(hevc), VDEC_STATUS_CONNECTED); + + if (hevc->vdec_cb) + hevc->vdec_cb(hw_to_vdec(hevc), hevc->vdec_cb_arg); +} + +static int vh265_hw_ctx_restore(struct hevc_state_s *hevc) +{ + /* new to do ... */ + vh265_prot_init(hevc); + return 0; +} + +static bool is_buffer_available(struct vdec_s *vdec) +{ + /* new to do ... */ + return 1; +} + +static bool run_ready(struct vdec_s *vdec) +{ + struct hevc_state_s *hevc = + (struct hevc_state_s *)vdec->private; + + hevc_print(hevc, + PRINT_FLAG_VDEC_DETAIL, "%s\r\n", __func__); + + return is_buffer_available(vdec); +} + +static void run(struct vdec_s *vdec, + void (*callback)(struct vdec_s *, void *), void *arg) +{ + struct hevc_state_s *hevc = + (struct hevc_state_s *)vdec->private; + int r; + + hevc->vdec_cb_arg = arg; + hevc->vdec_cb = callback; + /* hw->chunk = vdec_prepare_input(vdec); */ + r = vdec_prepare_input(vdec, &hevc->chunk); + if (r < 0) { + hevc->dec_result = DEC_RESULT_AGAIN; + + hevc_print(hevc, PRINT_FLAG_VDEC_DETAIL, + "ammvdec_vh265: Insufficient data\n"); + + schedule_work(&hevc->work); + return; + } + + hevc->dec_result = DEC_RESULT_NONE; + + if (/*(!input_frame_based(vdec)) && */(start_decode_buf_level > 0)) { + if (READ_VREG(HEVC_STREAM_LEVEL) < + start_decode_buf_level) { + hevc_print(hevc, + PRINT_FLAG_VDEC_DETAIL, + "%s: VIFIFO_LEVEL %x is low (<%x)\n", + __func__, + READ_VREG(HEVC_STREAM_LEVEL), + start_decode_buf_level); + + hevc->dec_result = DEC_RESULT_AGAIN; + schedule_work(&hevc->work); + return; + } + } + + hevc_print(hevc, PRINT_FLAG_VDEC_STATUS, + "%s: %x %x %x\n", + __func__, + READ_VREG(HEVC_STREAM_LEVEL), + READ_VREG(HEVC_STREAM_WR_PTR), + READ_VREG(HEVC_STREAM_RD_PTR)); + + if (((debug & ONLY_RESET_AT_START) == 0) || + (hevc->init_flag == 0)) { + if (amhevc_loadmc_ex(VFORMAT_HEVC, "vh265_mc", NULL) < 0) { + amhevc_disable(); + pr_info("%s: Error amvdec_loadmc fail\n", __func__); + return; + } + + if (vh265_hw_ctx_restore(hevc) < 0) { + schedule_work(&hevc->work); + return; + } + + vdec_enable_input(vdec); + + WRITE_VREG(HEVC_DEC_STATUS_REG, HEVC_ACTION_DONE); + + WRITE_VREG(HEVC_SHIFT_BYTE_COUNT, 0); + WRITE_VREG(HEVC_DECODE_SIZE, hevc->chunk->size); + hevc->init_flag = 1; + + if (hevc->pic_list_init_flag == 3) + init_pic_list_hw(hevc); + amhevc_start(); + } else { + WRITE_VREG(HEVC_SHIFT_BYTE_COUNT, 0); + WRITE_VREG(HEVC_DECODE_SIZE, hevc->chunk->size); + + hevc_recover(hevc); + + vdec_enable_input(vdec); + + WRITE_VREG(HEVC_DEC_STATUS_REG, HEVC_ACTION_DONE); + } + +} + +static void reset(struct vdec_s *vdec) +{ + + struct hevc_state_s *hevc = + (struct hevc_state_s *)vdec->private; + + hevc_print(hevc, + PRINT_FLAG_VDEC_DETAIL, "%s\r\n", __func__); + +} + +static irqreturn_t vh265_irq_cb(struct vdec_s *vdec) +{ + struct hevc_state_s *hevc = + (struct hevc_state_s *)vdec->private; + + return vh265_isr(0, hevc); +} + +static irqreturn_t vh265_threaded_irq_cb(struct vdec_s *vdec) +{ + struct hevc_state_s *hevc = + (struct hevc_state_s *)vdec->private; + + return vh265_isr_thread_fn(0, hevc); +} +#endif + +static int amvdec_h265_probe(struct platform_device *pdev) +{ +#ifdef MULTI_INSTANCE_SUPPORT + struct vdec_s *pdata = *(struct vdec_s **)pdev->dev.platform_data; +#else + struct vdec_dev_reg_s *pdata = + (struct vdec_dev_reg_s *)pdev->dev.platform_data; +#endif + struct hevc_state_s *hevc = &gHevc; + + if (debug) + pr_info("%s\r\n", __func__); + mutex_lock(&vh265_mutex); + + if ((get_cpu_type() >= MESON_CPU_MAJOR_ID_GXTVBB) && + (parser_sei_enable & 0x100) == 0) + parser_sei_enable = 1; + hevc->m_ins_flag = 0; + hevc->init_flag = 0; + hevc->uninit_list = 0; + hevc->fatal_error = 0; + hevc->show_frame_num = 0; + if (pdata == NULL) { + pr_info("\namvdec_h265 memory resource undefined.\n"); + mutex_unlock(&vh265_mutex); + return -EFAULT; + } + + hevc->buf_start = pdata->mem_start; + hevc->buf_size = pdata->mem_end - pdata->mem_start + 1; + /* + *hevc->mc_buf_spec.buf_end = pdata->mem_end + 1; + *for (i = 0; i < WORK_BUF_SPEC_NUM; i++) + * amvh265_workbuff_spec[i].start_adr = pdata->mem_start; + */ + if (debug) { + pr_info("===H.265 decoder mem resource 0x%lx -- 0x%lx\n", + pdata->mem_start, pdata->mem_end + 1); + } + + if (pdata->sys_info) + hevc->vh265_amstream_dec_info = *pdata->sys_info; + else { + hevc->vh265_amstream_dec_info.width = 0; + hevc->vh265_amstream_dec_info.height = 0; + hevc->vh265_amstream_dec_info.rate = 30; + } +#ifndef MULTI_INSTANCE_SUPPORT + if (pdata->flag & DEC_FLAG_HEVC_WORKAROUND) { + workaround_enable |= 3; + pr_info("amvdec_h265 HEVC_WORKAROUND flag set.\n"); + } else + workaround_enable &= ~3; +#endif + hevc->cma_dev = pdata->cma_dev; +#ifdef MULTI_INSTANCE_SUPPORT + pdata->private = hevc; + pdata->dec_status = vh265_dec_status; + if (vh265_init(pdata) < 0) { +#else + if (vh265_init(hevc) < 0) { +#endif + pr_info("\namvdec_h265 init failed.\n"); + hevc_local_uninit(hevc); + mutex_unlock(&vh265_mutex); + return -ENODEV; + } + /*set the max clk for smooth playing...*/ + hevc_source_changed(VFORMAT_HEVC, + 4096, 2048, 30); + mutex_unlock(&vh265_mutex); + + return 0; +} + +static int amvdec_h265_remove(struct platform_device *pdev) +{ + struct hevc_state_s *hevc = &gHevc; + + if (debug) + pr_info("%s\r\n", __func__); + + mutex_lock(&vh265_mutex); + + vh265_stop(hevc); + + hevc_source_changed(VFORMAT_HEVC, 0, 0, 0); + + +#ifdef DEBUG_PTS + pr_info("pts missed %ld, pts hit %ld, duration %d\n", + hevc->pts_missed, hevc->pts_hit, hevc->frame_dur); +#endif + + mutex_unlock(&vh265_mutex); + + return 0; +} +/****************************************/ + +static struct platform_driver amvdec_h265_driver = { + .probe = amvdec_h265_probe, + .remove = amvdec_h265_remove, +#ifdef CONFIG_PM + .suspend = amhevc_suspend, + .resume = amhevc_resume, +#endif + .driver = { + .name = DRIVER_NAME, + } +}; + +#ifdef MULTI_INSTANCE_SUPPORT +static int ammvdec_h265_probe(struct platform_device *pdev) +{ + + struct vdec_s *pdata = *(struct vdec_s **)pdev->dev.platform_data; + struct hevc_state_s *hevc = NULL; + + if (debug) + pr_info("%s\r\n", __func__); + if (pdata == NULL) { + pr_info("\nammvdec_h265 memory resource undefined.\n"); + return -EFAULT; + } + + hevc = (struct hevc_state_s *)devm_kzalloc(&pdev->dev, + sizeof(struct hevc_state_s), GFP_KERNEL); + if (hevc == NULL) { + pr_info("\nammvdec_h265 device data allocation failed\n"); + return -ENOMEM; + } + hevc->index = m_hevc_count % max_decode_instance_num; + m_hevc_count++; + pdata->private = hevc; + pdata->dec_status = vh265_dec_status; + /* pdata->set_trickmode = set_trickmode; */ + pdata->run_ready = run_ready; + pdata->run = run; + pdata->reset = reset; + pdata->irq_handler = vh265_irq_cb; + pdata->threaded_irq_handler = vh265_threaded_irq_cb; + + pdata->id = pdev->id; + + if (pdata->use_vfm_path) + snprintf(pdata->vf_provider_name, VDEC_PROVIDER_NAME_SIZE, + VFM_DEC_PROVIDER_NAME); + else if (debug & USE_OLD_PROVIDER) + snprintf(pdata->vf_provider_name, VDEC_PROVIDER_NAME_SIZE, + PROVIDER_NAME); + else + snprintf(pdata->vf_provider_name, VDEC_PROVIDER_NAME_SIZE, + MULTI_INSTANCE_PROVIDER_NAME ".%02x", pdev->id & 0xff); + + vf_provider_init(&pdata->vframe_provider, pdata->vf_provider_name, + &vh265_vf_provider, pdata); + + hevc->provider_name = pdata->vf_provider_name; + platform_set_drvdata(pdev, pdata); + + hevc->platform_dev = pdev; + + hevc->buf_start = pdata->mem_start; + hevc->buf_size = pdata->mem_end - pdata->mem_start + 1; + if (get_cpu_type() < MESON_CPU_MAJOR_ID_GXL + || double_write_mode == 0x10) + mmu_enable = 0; + if ((get_cpu_type() >= MESON_CPU_MAJOR_ID_GXTVBB) && + (parser_sei_enable & 0x100) == 0) + parser_sei_enable = 1; + hevc->m_ins_flag = 1; + hevc->init_flag = 0; + hevc->uninit_list = 0; + hevc->fatal_error = 0; + hevc->show_frame_num = 0; + if (pdata == NULL) { + pr_info("\namvdec_h265 memory resource undefined.\n"); + return -EFAULT; + } + /* + *hevc->mc_buf_spec.buf_end = pdata->mem_end + 1; + *for (i = 0; i < WORK_BUF_SPEC_NUM; i++) + * amvh265_workbuff_spec[i].start_adr = pdata->mem_start; + */ + if (debug) { + pr_info("===H.265 decoder mem resource 0x%lx -- 0x%lx\n", + pdata->mem_start, pdata->mem_end + 1); + } + + if (pdata->sys_info) + hevc->vh265_amstream_dec_info = *pdata->sys_info; + else { + hevc->vh265_amstream_dec_info.width = 0; + hevc->vh265_amstream_dec_info.height = 0; + hevc->vh265_amstream_dec_info.rate = 30; + } + + hevc->cma_dev = pdata->cma_dev; + + if (vh265_init(pdata) < 0) { + pr_info("\namvdec_h265 init failed.\n"); + hevc_local_uninit(hevc); + return -ENODEV; + } + + return 0; +} + +static int ammvdec_h265_remove(struct platform_device *pdev) +{ + struct hevc_state_s *hevc = + (struct hevc_state_s *) + (((struct vdec_s *)(platform_get_drvdata(pdev)))->private); + + if (debug) + pr_info("%s\r\n", __func__); + + vmh265_stop(hevc); + + /* vdec_source_changed(VFORMAT_H264, 0, 0, 0); */ + + vdec_set_status(hw_to_vdec(hevc), VDEC_STATUS_DISCONNECTED); + + return 0; +} + +static struct platform_driver ammvdec_h265_driver = { + .probe = ammvdec_h265_probe, + .remove = ammvdec_h265_remove, +#ifdef CONFIG_PM + .suspend = amvdec_suspend, + .resume = amvdec_resume, +#endif + .driver = { + .name = MULTI_DRIVER_NAME, + } +}; +#endif + +static struct codec_profile_t amvdec_h265_profile = { + .name = "hevc", + .profile = "" +}; + +static int __init amvdec_h265_driver_init_module(void) +{ + pr_debug("amvdec_h265 module init\n"); + error_handle_policy = 0; + +#ifdef ERROR_HANDLE_DEBUG + dbg_nal_skip_flag = 0; + dbg_nal_skip_count = 0; +#endif + decode_stop_pos = 0; + decode_stop_pos_pre = 0; + decode_pic_begin = 0; + slice_parse_begin = 0; + step = 0; + buf_alloc_size = 0; + +#ifdef MULTI_INSTANCE_SUPPORT + if (platform_driver_register(&ammvdec_h265_driver)) + pr_err("failed to register ammvdec_h265 driver\n"); + +#endif + if (platform_driver_register(&amvdec_h265_driver)) { + pr_err("failed to register amvdec_h265 driver\n"); + return -ENODEV; + } +#if 1/*MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON8*/ + if (!has_hevc_vdec()) { + /* not support hevc */ + amvdec_h265_profile.name = "hevc_unsupport"; + } else if (is_meson_m8m2_cpu()) { + /* m8m2 support 4k */ + amvdec_h265_profile.profile = "4k"; + } else if (get_cpu_type() >= MESON_CPU_MAJOR_ID_GXBB) { + amvdec_h265_profile.profile = + "4k, 9bit, 10bit, dwrite, compressed"; + } else if (get_cpu_type() >= MESON_CPU_MAJOR_ID_MG9TV) + amvdec_h265_profile.profile = "4k"; +#endif + if (codec_mm_get_total_size() < 80 * SZ_1M) { + pr_info("amvdec_h265 default mmu enabled.\n"); + mmu_enable = 1; + } + + vcodec_profile_register(&amvdec_h265_profile); + + return 0; +} + +static void __exit amvdec_h265_driver_remove_module(void) +{ + pr_debug("amvdec_h265 module remove.\n"); + +#ifdef MULTI_INSTANCE_SUPPORT + platform_driver_unregister(&ammvdec_h265_driver); +#endif + platform_driver_unregister(&amvdec_h265_driver); +} + +/****************************************/ +/* +*module_param(stat, uint, 0664); +*MODULE_PARM_DESC(stat, "\n amvdec_h265 stat\n"); +*/ +module_param(use_cma, uint, 0664); +MODULE_PARM_DESC(use_cma, "\n amvdec_h265 use_cma\n"); + +module_param(bit_depth_luma, uint, 0664); +MODULE_PARM_DESC(bit_depth_luma, "\n amvdec_h265 bit_depth_luma\n"); + +module_param(bit_depth_chroma, uint, 0664); +MODULE_PARM_DESC(bit_depth_chroma, "\n amvdec_h265 bit_depth_chroma\n"); + +module_param(video_signal_type, uint, 0664); +MODULE_PARM_DESC(video_signal_type, "\n amvdec_h265 video_signal_type\n"); + +module_param(debug, uint, 0664); +MODULE_PARM_DESC(debug, "\n amvdec_h265 debug\n"); + +#ifdef ERROR_HANDLE_DEBUG +module_param(dbg_nal_skip_flag, uint, 0664); +MODULE_PARM_DESC(dbg_nal_skip_flag, "\n amvdec_h265 dbg_nal_skip_flag\n"); + +module_param(dbg_nal_skip_count, uint, 0664); +MODULE_PARM_DESC(dbg_nal_skip_count, "\n amvdec_h265 dbg_nal_skip_count\n"); +#endif + +module_param(radr, uint, 0664); +MODULE_PARM_DESC(radr, "\nradr\n"); + +module_param(rval, uint, 0664); +MODULE_PARM_DESC(rval, "\nrval\n"); + +module_param(dbg_cmd, uint, 0664); +MODULE_PARM_DESC(dbg_cmd, "\ndbg_cmd\n"); + +module_param(dbg_skip_decode_index, uint, 0664); +MODULE_PARM_DESC(dbg_skip_decode_index, "\ndbg_skip_decode_index\n"); + +module_param(endian, uint, 0664); +MODULE_PARM_DESC(endian, "\nrval\n"); + +module_param(step, uint, 0664); +MODULE_PARM_DESC(step, "\n amvdec_h265 step\n"); + +module_param(decode_stop_pos, uint, 0664); +MODULE_PARM_DESC(decode_stop_pos, "\n amvdec_h265 decode_stop_pos\n"); + +module_param(decode_pic_begin, uint, 0664); +MODULE_PARM_DESC(decode_pic_begin, "\n amvdec_h265 decode_pic_begin\n"); + +module_param(slice_parse_begin, uint, 0664); +MODULE_PARM_DESC(slice_parse_begin, "\n amvdec_h265 slice_parse_begin\n"); + +module_param(nal_skip_policy, uint, 0664); +MODULE_PARM_DESC(nal_skip_policy, "\n amvdec_h265 nal_skip_policy\n"); + +module_param(i_only_flag, uint, 0664); +MODULE_PARM_DESC(i_only_flag, "\n amvdec_h265 i_only_flag\n"); + +module_param(error_handle_policy, uint, 0664); +MODULE_PARM_DESC(error_handle_policy, "\n amvdec_h265 error_handle_policy\n"); + +module_param(error_handle_threshold, uint, 0664); +MODULE_PARM_DESC(error_handle_threshold, + "\n amvdec_h265 error_handle_threshold\n"); + +module_param(error_handle_nal_skip_threshold, uint, 0664); +MODULE_PARM_DESC(error_handle_nal_skip_threshold, + "\n amvdec_h265 error_handle_nal_skip_threshold\n"); + +module_param(error_handle_system_threshold, uint, 0664); +MODULE_PARM_DESC(error_handle_system_threshold, + "\n amvdec_h265 error_handle_system_threshold\n"); + +module_param(error_skip_nal_count, uint, 0664); +MODULE_PARM_DESC(error_skip_nal_count, + "\n amvdec_h265 error_skip_nal_count\n"); + +module_param(buf_alloc_width, uint, 0664); +MODULE_PARM_DESC(buf_alloc_width, "\n buf_alloc_width\n"); + +module_param(buf_alloc_height, uint, 0664); +MODULE_PARM_DESC(buf_alloc_height, "\n buf_alloc_height\n"); + +module_param(buf_alloc_size, uint, 0664); +MODULE_PARM_DESC(buf_alloc_size, "\n buf_alloc_size\n"); + +module_param(re_config_pic_flag, uint, 0664); +MODULE_PARM_DESC(re_config_pic_flag, "\n re_config_pic_flag\n"); + +module_param(buffer_mode, uint, 0664); +MODULE_PARM_DESC(buffer_mode, "\n buffer_mode\n"); + +module_param(buffer_mode_dbg, uint, 0664); +MODULE_PARM_DESC(buffer_mode_dbg, "\n buffer_mode_dbg\n"); +/*USE_BUF_BLOCK*/ +module_param(max_buf_num, uint, 0664); +MODULE_PARM_DESC(max_buf_num, "\n max_buf_num\n"); + +module_param(dynamic_buf_num_margin, uint, 0664); +MODULE_PARM_DESC(dynamic_buf_num_margin, "\n dynamic_buf_num_margin\n"); +/**/ + +module_param(mem_map_mode, uint, 0664); +MODULE_PARM_DESC(mem_map_mode, "\n mem_map_mode\n"); + +#ifdef SUPPORT_10BIT +module_param(double_write_mode, uint, 0664); +MODULE_PARM_DESC(double_write_mode, "\n double_write_mode\n"); + +module_param(enable_mem_saving, uint, 0664); +MODULE_PARM_DESC(enable_mem_saving, "\n enable_mem_saving\n"); + +module_param(force_w_h, uint, 0664); +MODULE_PARM_DESC(force_w_h, "\n force_w_h\n"); +#endif + +module_param(force_fps, uint, 0664); +MODULE_PARM_DESC(force_fps, "\n force_fps\n"); + +module_param(max_decoding_time, uint, 0664); +MODULE_PARM_DESC(max_decoding_time, "\n max_decoding_time\n"); + +module_param(interlace_enable, uint, 0664); +MODULE_PARM_DESC(interlace_enable, "\n interlace_enable\n"); +module_param(pts_unstable, uint, 0664); +MODULE_PARM_DESC(pts_unstable, "\n amvdec_h265 pts_unstable\n"); +module_param(parser_sei_enable, uint, 0664); +MODULE_PARM_DESC(parser_sei_enable, "\n parser_sei_enable\n"); +module_param(mmu_enable, uint, 0664); +MODULE_PARM_DESC(mmu_enable, "\n mmu_enable\n"); + +#ifdef MULTI_INSTANCE_SUPPORT +module_param(start_decode_buf_level, uint, 0664); +MODULE_PARM_DESC(start_decode_buf_level, + "\n ammvdec_h264 start_decode_buf_level\n"); + +module_param_array(decode_frame_count, uint, + &max_decode_instance_num, 0664); + +module_param_array(max_process_time, uint, + &max_decode_instance_num, 0664); + +module_param_array(max_get_frame_interval, + uint, &max_decode_instance_num, 0664); + +module_param_array(debug_flag, uint, &max_decode_instance_num, 0664); +#endif + +module_init(amvdec_h265_driver_init_module); +module_exit(amvdec_h265_driver_remove_module); + +MODULE_DESCRIPTION("AMLOGIC h265 Video Decoder Driver"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Tim Yao <tim.yao@amlogic.com>"); diff --git a/drivers/frame_provider/decoder/h265/vh265.h b/drivers/frame_provider/decoder/h265/vh265.h new file mode 100644 index 0000000..8b10541 --- a/dev/null +++ b/drivers/frame_provider/decoder/h265/vh265.h @@ -0,0 +1,27 @@ +/* + * drivers/amlogic/amports/vh265.h + * + * Copyright (C) 2015 Amlogic, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * +*/ + +#ifndef VH265_H +#define VH265_H + +extern u32 get_blackout_policy(void); + +extern s32 vh265_init(void); + +extern s32 vh265_release(void); + +#endif /* VMPEG4_H */ diff --git a/drivers/frame_provider/decoder/utils/Makefile b/drivers/frame_provider/decoder/utils/Makefile new file mode 100644 index 0000000..97e47f4 --- a/dev/null +++ b/drivers/frame_provider/decoder/utils/Makefile @@ -0,0 +1,2 @@ +obj-m += decoder_common.o +decoder_common-objs += utils.o vdec.o vdec_input.o amvdec.o decoder_mmu_box.o diff --git a/drivers/frame_provider/decoder/utils/amvdec.c b/drivers/frame_provider/decoder/utils/amvdec.c new file mode 100644 index 0000000..f9b8662 --- a/dev/null +++ b/drivers/frame_provider/decoder/utils/amvdec.c @@ -0,0 +1,936 @@ +/* + * drivers/amlogic/media/frame_provider/decoder/utils/amvdec.c + * + * Copyright (C) 2016 Amlogic, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * +*/ + +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/platform_device.h> +#include <linux/sched.h> +#include <linux/slab.h> +#include <linux/dma-mapping.h> +#include <linux/amlogic/media/utils/vformat.h> +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/vmalloc.h> +#include "vdec.h" + +#ifdef CONFIG_PM +#include <linux/pm.h> +#endif + +#ifdef CONFIG_WAKELOCK +#include <linux/wakelock.h> +#endif +#include "../../../stream_input/amports/amports_priv.h" + +/* #include <mach/am_regs.h> */ +/* #include <mach/power_gate.h> */ +#include <linux/amlogic/media/utils/vdec_reg.h> +#include "amvdec.h" +#include <linux/amlogic/media/utils/amports_config.h> +#include "../../../common/firmware/firmware.h" + +#define MC_SIZE (4096 * 4) + +#ifdef CONFIG_WAKELOCK +static struct wake_lock amvdec_lock; +struct timer_list amvdevtimer; +#define WAKE_CHECK_INTERVAL (100*HZ/100) +#endif +#define AMVDEC_USE_STATIC_MEMORY +static void *mc_addr; +static dma_addr_t mc_addr_map; + +#ifdef CONFIG_WAKELOCK +static int video_running; +static int video_stated_changed = 1; +#endif + +static void amvdec_pg_enable(bool enable) +{ + ulong timeout; + + if (enable) { + AMVDEC_CLK_GATE_ON(MDEC_CLK_PIC_DC); + AMVDEC_CLK_GATE_ON(MDEC_CLK_DBLK); + AMVDEC_CLK_GATE_ON(MC_CLK); + AMVDEC_CLK_GATE_ON(IQIDCT_CLK); + /* AMVDEC_CLK_GATE_ON(VLD_CLK); */ + AMVDEC_CLK_GATE_ON(AMRISC); + /* #if MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON6TVD */ + if (get_cpu_type() >= MESON_CPU_MAJOR_ID_M8) + WRITE_VREG(GCLK_EN, 0x3ff); + /* #endif */ + CLEAR_VREG_MASK(MDEC_PIC_DC_CTRL, 1 << 31); + } else { + + AMVDEC_CLK_GATE_OFF(AMRISC); + timeout = jiffies + HZ / 10; + + while (READ_VREG(MDEC_PIC_DC_STATUS) != 0) { + if (time_after(jiffies, timeout)) { + WRITE_VREG_BITS(MDEC_PIC_DC_CTRL, 1, 0, 1); + WRITE_VREG_BITS(MDEC_PIC_DC_CTRL, 0, 0, 1); + READ_VREG(MDEC_PIC_DC_STATUS); + READ_VREG(MDEC_PIC_DC_STATUS); + READ_VREG(MDEC_PIC_DC_STATUS); + break; + } + } + + AMVDEC_CLK_GATE_OFF(MDEC_CLK_PIC_DC); + timeout = jiffies + HZ / 10; + + while (READ_VREG(DBLK_STATUS) & 1) { + if (time_after(jiffies, timeout)) { + WRITE_VREG(DBLK_CTRL, 3); + WRITE_VREG(DBLK_CTRL, 0); + READ_VREG(DBLK_STATUS); + READ_VREG(DBLK_STATUS); + READ_VREG(DBLK_STATUS); + break; + } + } + AMVDEC_CLK_GATE_OFF(MDEC_CLK_DBLK); + timeout = jiffies + HZ / 10; + + while (READ_VREG(MC_STATUS0) & 1) { + if (time_after(jiffies, timeout)) { + SET_VREG_MASK(MC_CTRL1, 0x9); + CLEAR_VREG_MASK(MC_CTRL1, 0x9); + READ_VREG(MC_STATUS0); + READ_VREG(MC_STATUS0); + READ_VREG(MC_STATUS0); + break; + } + } + AMVDEC_CLK_GATE_OFF(MC_CLK); + timeout = jiffies + HZ / 10; + while (READ_VREG(DCAC_DMA_CTRL) & 0x8000) { + if (time_after(jiffies, timeout)) + break; + } + AMVDEC_CLK_GATE_OFF(IQIDCT_CLK); + /* AMVDEC_CLK_GATE_OFF(VLD_CLK); */ + } +} + +static void amvdec2_pg_enable(bool enable) +{ + if (has_vdec2()) { + ulong timeout; + + if (!vdec_on(VDEC_2)) + return; + if (enable) { + /* WRITE_VREG(VDEC2_GCLK_EN, 0x3ff); */ + } else { + timeout = jiffies + HZ / 10; + + while (READ_VREG(VDEC2_MDEC_PIC_DC_STATUS) != 0) { + if (time_after(jiffies, timeout)) { + WRITE_VREG_BITS(VDEC2_MDEC_PIC_DC_CTRL, + 1, 0, 1); + WRITE_VREG_BITS(VDEC2_MDEC_PIC_DC_CTRL, + 0, 0, 1); + READ_VREG(VDEC2_MDEC_PIC_DC_STATUS); + READ_VREG(VDEC2_MDEC_PIC_DC_STATUS); + READ_VREG(VDEC2_MDEC_PIC_DC_STATUS); + break; + } + } + + timeout = jiffies + HZ / 10; + + while (READ_VREG(VDEC2_DBLK_STATUS) & 1) { + if (time_after(jiffies, timeout)) { + WRITE_VREG(VDEC2_DBLK_CTRL, 3); + WRITE_VREG(VDEC2_DBLK_CTRL, 0); + READ_VREG(VDEC2_DBLK_STATUS); + READ_VREG(VDEC2_DBLK_STATUS); + READ_VREG(VDEC2_DBLK_STATUS); + break; + } + } + + timeout = jiffies + HZ / 10; + + while (READ_VREG(VDEC2_DCAC_DMA_CTRL) & 0x8000) { + if (time_after(jiffies, timeout)) + break; + } + } + } +} + +static void amhevc_pg_enable(bool enable) +{ + if (has_hevc_vdec()) { + ulong timeout; + + if (!vdec_on(VDEC_HEVC)) + return; + if (enable) { + /* WRITE_VREG(VDEC2_GCLK_EN, 0x3ff); */ + } else { + timeout = jiffies + HZ / 10; + + while (READ_VREG(HEVC_MDEC_PIC_DC_STATUS) != 0) { + if (time_after(jiffies, timeout)) { + WRITE_VREG_BITS(HEVC_MDEC_PIC_DC_CTRL, + 1, 0, 1); + WRITE_VREG_BITS(HEVC_MDEC_PIC_DC_CTRL, + 0, 0, 1); + READ_VREG(HEVC_MDEC_PIC_DC_STATUS); + READ_VREG(HEVC_MDEC_PIC_DC_STATUS); + READ_VREG(HEVC_MDEC_PIC_DC_STATUS); + break; + } + } + + timeout = jiffies + HZ / 10; + + while (READ_VREG(HEVC_DBLK_STATUS) & 1) { + if (time_after(jiffies, timeout)) { + WRITE_VREG(HEVC_DBLK_CTRL, 3); + WRITE_VREG(HEVC_DBLK_CTRL, 0); + READ_VREG(HEVC_DBLK_STATUS); + READ_VREG(HEVC_DBLK_STATUS); + READ_VREG(HEVC_DBLK_STATUS); + break; + } + } + + timeout = jiffies + HZ / 10; + + while (READ_VREG(HEVC_DCAC_DMA_CTRL) & 0x8000) { + if (time_after(jiffies, timeout)) + break; + } + } + } +} + +#ifdef CONFIG_WAKELOCK +int amvdec_wake_lock(void) +{ + wake_lock(&amvdec_lock); + return 0; +} + +int amvdec_wake_unlock(void) +{ + wake_unlock(&amvdec_lock); + return 0; +} +#else +#define amvdec_wake_lock() +#define amvdec_wake_unlock() +#endif + +static s32 am_loadmc_ex(enum vformat_e type, + const char *name, char *def, s32(*load)(const u32 *)) +{ + char *mc_addr = vmalloc(4096 * 4); + char *pmc_addr = def; + int err; + + if (!def && mc_addr) { + int loaded; + + loaded = get_decoder_firmware_data(type, + name, mc_addr, (4096 * 4)); + if (loaded > 0) + pmc_addr = mc_addr; + } + if (!pmc_addr) { + vfree(mc_addr); + return -1; + } + err = (*load)((u32 *) pmc_addr); + if (err < 0) { + pr_err("loading firmware %s to vdec ram failed!\n", name); + return err; + } + vfree(mc_addr); + pr_debug("loading firmware %s to vdec ram ok!\n", name); + return err; +} + +static s32 amvdec_loadmc(const u32 *p) +{ + ulong timeout; + s32 ret = 0; + +#ifdef AMVDEC_USE_STATIC_MEMORY + if (mc_addr == NULL) { +#else + { +#endif + mc_addr = kmalloc(MC_SIZE, GFP_KERNEL); + } + + if (!mc_addr) + return -ENOMEM; + + memcpy(mc_addr, p, MC_SIZE); + + mc_addr_map = dma_map_single(get_vdec_device(), + mc_addr, MC_SIZE, DMA_TO_DEVICE); + + WRITE_VREG(MPSR, 0); + WRITE_VREG(CPSR, 0); + + /* Read CBUS register for timing */ + timeout = READ_VREG(MPSR); + timeout = READ_VREG(MPSR); + + timeout = jiffies + HZ; + + WRITE_VREG(IMEM_DMA_ADR, mc_addr_map); + WRITE_VREG(IMEM_DMA_COUNT, 0x1000); + WRITE_VREG(IMEM_DMA_CTRL, (0x8000 | (7 << 16))); + + while (READ_VREG(IMEM_DMA_CTRL) & 0x8000) { + if (time_before(jiffies, timeout)) + schedule(); + else { + pr_err("vdec load mc error\n"); + ret = -EBUSY; + break; + } + } + + dma_unmap_single(get_vdec_device(), + mc_addr_map, MC_SIZE, DMA_TO_DEVICE); + +#ifndef AMVDEC_USE_STATIC_MEMORY + kfree(mc_addr); + mc_addr = NULL; +#endif + + return ret; +} + +s32 amvdec_loadmc_ex(enum vformat_e type, const char *name, char *def) +{ + return am_loadmc_ex(type, name, def, &amvdec_loadmc); +} +EXPORT_SYMBOL(amvdec_loadmc_ex); + +static s32 amvdec2_loadmc(const u32 *p) +{ + if (has_vdec2()) { + ulong timeout; + s32 ret = 0; + +#ifdef AMVDEC_USE_STATIC_MEMORY + if (mc_addr == NULL) { +#else + { +#endif + mc_addr = kmalloc(MC_SIZE, GFP_KERNEL); + } + + if (!mc_addr) + return -ENOMEM; + + memcpy(mc_addr, p, MC_SIZE); + + mc_addr_map = dma_map_single(get_vdec_device(), + mc_addr, MC_SIZE, DMA_TO_DEVICE); + + WRITE_VREG(VDEC2_MPSR, 0); + WRITE_VREG(VDEC2_CPSR, 0); + + /* Read CBUS register for timing */ + timeout = READ_VREG(VDEC2_MPSR); + timeout = READ_VREG(VDEC2_MPSR); + + timeout = jiffies + HZ; + + WRITE_VREG(VDEC2_IMEM_DMA_ADR, mc_addr_map); + WRITE_VREG(VDEC2_IMEM_DMA_COUNT, 0x1000); + WRITE_VREG(VDEC2_IMEM_DMA_CTRL, (0x8000 | (7 << 16))); + + while (READ_VREG(VDEC2_IMEM_DMA_CTRL) & 0x8000) { + if (time_before(jiffies, timeout)) + schedule(); + else { + pr_err("vdec2 load mc error\n"); + ret = -EBUSY; + break; + } + } + + dma_unmap_single(get_vdec_device(), + mc_addr_map, MC_SIZE, DMA_TO_DEVICE); + +#ifndef AMVDEC_USE_STATIC_MEMORY + kfree(mc_addr); + mc_addr = NULL; +#endif + + return ret; + } else + return 0; +} + +s32 amvdec2_loadmc_ex(enum vformat_e type, const char *name, char *def) +{ + if (has_vdec2()) + return am_loadmc_ex(type, name, def, &amvdec2_loadmc); + else + return 0; +} +EXPORT_SYMBOL(amvdec2_loadmc_ex); + +s32 amhcodec_loadmc(const u32 *p) +{ +#ifdef AMVDEC_USE_STATIC_MEMORY + if (mc_addr == NULL) { +#else + { +#endif + mc_addr = kmalloc(MC_SIZE, GFP_KERNEL); + } + + if (!mc_addr) + return -ENOMEM; + + memcpy(mc_addr, p, MC_SIZE); + + mc_addr_map = dma_map_single(get_vdec_device(), + mc_addr, MC_SIZE, DMA_TO_DEVICE); + + WRITE_VREG(HCODEC_IMEM_DMA_ADR, mc_addr_map); + WRITE_VREG(HCODEC_IMEM_DMA_COUNT, 0x100); + WRITE_VREG(HCODEC_IMEM_DMA_CTRL, (0x8000 | (7 << 16))); + + while (READ_VREG(HCODEC_IMEM_DMA_CTRL) & 0x8000) + udelay(1000); + + dma_unmap_single(get_vdec_device(), + mc_addr_map, MC_SIZE, DMA_TO_DEVICE); + +#ifndef AMVDEC_USE_STATIC_MEMORY + kfree(mc_addr); +#endif + + return 0; +} +EXPORT_SYMBOL(amhcodec_loadmc); + +s32 amhcodec_loadmc_ex(enum vformat_e type, const char *name, char *def) +{ + return am_loadmc_ex(type, name, def, &amhcodec_loadmc); +} +EXPORT_SYMBOL(amhcodec_loadmc_ex); + +static s32 amhevc_loadmc(const u32 *p) +{ + ulong timeout; + s32 ret = 0; + + if (has_hevc_vdec()) { +#ifdef AMVDEC_USE_STATIC_MEMORY + if (mc_addr == NULL) { +#else + { +#endif + mc_addr = kmalloc(MC_SIZE, GFP_KERNEL); + } + + if (!mc_addr) + return -ENOMEM; + + memcpy(mc_addr, p, MC_SIZE); + + mc_addr_map = + dma_map_single(get_vdec_device(), + mc_addr, MC_SIZE, DMA_TO_DEVICE); + + WRITE_VREG(HEVC_MPSR, 0); + WRITE_VREG(HEVC_CPSR, 0); + + /* Read CBUS register for timing */ + timeout = READ_VREG(HEVC_MPSR); + timeout = READ_VREG(HEVC_MPSR); + + timeout = jiffies + HZ; + + WRITE_VREG(HEVC_IMEM_DMA_ADR, mc_addr_map); + WRITE_VREG(HEVC_IMEM_DMA_COUNT, 0x1000); + WRITE_VREG(HEVC_IMEM_DMA_CTRL, (0x8000 | (7 << 16))); + + while (READ_VREG(HEVC_IMEM_DMA_CTRL) & 0x8000) { + if (time_before(jiffies, timeout)) + schedule(); + else { + pr_err("vdec2 load mc error\n"); + ret = -EBUSY; + break; + } + } + + dma_unmap_single(get_vdec_device(), + mc_addr_map, MC_SIZE, DMA_TO_DEVICE); + +#ifndef AMVDEC_USE_STATIC_MEMORY + kfree(mc_addr); + mc_addr = NULL; +#endif + } + + return ret; +} + +s32 amhevc_loadmc_ex(enum vformat_e type, const char *name, char *def) +{ + if (has_hevc_vdec()) + return am_loadmc_ex(type, name, def, &amhevc_loadmc); + else + return 0; +} +EXPORT_SYMBOL(amhevc_loadmc_ex); + +void amvdec_start(void) +{ +#ifdef CONFIG_WAKELOCK + amvdec_wake_lock(); +#endif + + /* #if MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON6 */ + if (get_cpu_type() >= MESON_CPU_MAJOR_ID_M6) { + READ_VREG(DOS_SW_RESET0); + READ_VREG(DOS_SW_RESET0); + READ_VREG(DOS_SW_RESET0); + + WRITE_VREG(DOS_SW_RESET0, (1 << 12) | (1 << 11)); + WRITE_VREG(DOS_SW_RESET0, 0); + + READ_VREG(DOS_SW_RESET0); + READ_VREG(DOS_SW_RESET0); + READ_VREG(DOS_SW_RESET0); + } else { + /* #else */ + /* additional cbus dummy register reading for timing control */ + READ_MPEG_REG(RESET0_REGISTER); + READ_MPEG_REG(RESET0_REGISTER); + READ_MPEG_REG(RESET0_REGISTER); + READ_MPEG_REG(RESET0_REGISTER); + + WRITE_MPEG_REG(RESET0_REGISTER, RESET_VCPU | RESET_CCPU); + + READ_MPEG_REG(RESET0_REGISTER); + READ_MPEG_REG(RESET0_REGISTER); + READ_MPEG_REG(RESET0_REGISTER); + } + /* #endif */ + + WRITE_VREG(MPSR, 0x0001); +} +EXPORT_SYMBOL(amvdec_start); + +void amvdec2_start(void) +{ + if (has_vdec2()) { +#ifdef CONFIG_WAKELOCK + amvdec_wake_lock(); +#endif + + READ_VREG(DOS_SW_RESET2); + READ_VREG(DOS_SW_RESET2); + READ_VREG(DOS_SW_RESET2); + + WRITE_VREG(DOS_SW_RESET2, (1 << 12) | (1 << 11)); + WRITE_VREG(DOS_SW_RESET2, 0); + + READ_VREG(DOS_SW_RESET2); + READ_VREG(DOS_SW_RESET2); + READ_VREG(DOS_SW_RESET2); + + WRITE_VREG(VDEC2_MPSR, 0x0001); + } +} +EXPORT_SYMBOL(amvdec2_start); + +void amhcodec_start(void) +{ + WRITE_VREG(HCODEC_MPSR, 0x0001); +} +EXPORT_SYMBOL(amhcodec_start); + +void amhevc_start(void) +{ + + if (has_hevc_vdec()) { +#ifdef CONFIG_WAKELOCK + amvdec_wake_lock(); +#endif + + READ_VREG(DOS_SW_RESET3); + READ_VREG(DOS_SW_RESET3); + READ_VREG(DOS_SW_RESET3); + + WRITE_VREG(DOS_SW_RESET3, (1 << 12) | (1 << 11)); + WRITE_VREG(DOS_SW_RESET3, 0); + + READ_VREG(DOS_SW_RESET3); + READ_VREG(DOS_SW_RESET3); + READ_VREG(DOS_SW_RESET3); + + WRITE_VREG(HEVC_MPSR, 0x0001); + } +} +EXPORT_SYMBOL(amhevc_start); + +void amvdec_stop(void) +{ + ulong timeout = jiffies + HZ; + + WRITE_VREG(MPSR, 0); + WRITE_VREG(CPSR, 0); + + while (READ_VREG(IMEM_DMA_CTRL) & 0x8000) { + if (time_after(jiffies, timeout)) + break; + } + + /* #if MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON6 */ + if (get_cpu_type() >= MESON_CPU_MAJOR_ID_M6) { + READ_VREG(DOS_SW_RESET0); + READ_VREG(DOS_SW_RESET0); + READ_VREG(DOS_SW_RESET0); + + WRITE_VREG(DOS_SW_RESET0, (1 << 12) | (1 << 11)); + WRITE_VREG(DOS_SW_RESET0, 0); + + READ_VREG(DOS_SW_RESET0); + READ_VREG(DOS_SW_RESET0); + READ_VREG(DOS_SW_RESET0); + } else { + /* #else */ + WRITE_MPEG_REG(RESET0_REGISTER, RESET_VCPU | RESET_CCPU); + + /* additional cbus dummy register reading for timing control */ + READ_MPEG_REG(RESET0_REGISTER); + READ_MPEG_REG(RESET0_REGISTER); + READ_MPEG_REG(RESET0_REGISTER); + READ_MPEG_REG(RESET0_REGISTER); + } + /* #endif */ + +#ifdef CONFIG_WAKELOCK + amvdec_wake_unlock(); +#endif +} +EXPORT_SYMBOL(amvdec_stop); + +void amvdec2_stop(void) +{ + if (has_vdec2()) { + ulong timeout = jiffies + HZ; + + WRITE_VREG(VDEC2_MPSR, 0); + WRITE_VREG(VDEC2_CPSR, 0); + + while (READ_VREG(VDEC2_IMEM_DMA_CTRL) & 0x8000) { + if (time_after(jiffies, timeout)) + break; + } + + READ_VREG(DOS_SW_RESET2); + READ_VREG(DOS_SW_RESET2); + READ_VREG(DOS_SW_RESET2); + +#ifdef CONFIG_WAKELOCK + amvdec_wake_unlock(); +#endif + } +} +EXPORT_SYMBOL(amvdec2_stop); + +void amhcodec_stop(void) +{ + WRITE_VREG(HCODEC_MPSR, 0); +} +EXPORT_SYMBOL(amhcodec_stop); + +void amhevc_stop(void) +{ + if (has_hevc_vdec()) { + ulong timeout = jiffies + HZ; + + WRITE_VREG(HEVC_MPSR, 0); + WRITE_VREG(HEVC_CPSR, 0); + + while (READ_VREG(HEVC_IMEM_DMA_CTRL) & 0x8000) { + if (time_after(jiffies, timeout)) + break; + } + + READ_VREG(DOS_SW_RESET3); + READ_VREG(DOS_SW_RESET3); + READ_VREG(DOS_SW_RESET3); + +#ifdef CONFIG_WAKELOCK + amvdec_wake_unlock(); +#endif + } +} +EXPORT_SYMBOL(amhevc_stop); + +void amvdec_enable(void) +{ + amvdec_pg_enable(true); +} +EXPORT_SYMBOL(amvdec_enable); + +void amvdec_disable(void) +{ + amvdec_pg_enable(false); +} +EXPORT_SYMBOL(amvdec_disable); + +void amvdec2_enable(void) +{ + if (has_vdec2()) + amvdec2_pg_enable(true); +} +EXPORT_SYMBOL(amvdec2_enable); + +void amvdec2_disable(void) +{ + if (has_vdec2()) + amvdec2_pg_enable(false); +} +EXPORT_SYMBOL(amvdec2_disable); + +void amhevc_enable(void) +{ + if (has_hevc_vdec()) + amhevc_pg_enable(true); +} +EXPORT_SYMBOL(amhevc_enable); + +void amhevc_disable(void) +{ + if (has_hevc_vdec()) + amhevc_pg_enable(false); +} +EXPORT_SYMBOL(amhevc_disable); + +#ifdef CONFIG_PM +int amvdec_suspend(struct platform_device *dev, pm_message_t event) +{ + amvdec_pg_enable(false); + + /* #if MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON6TVD */ + if (has_vdec2()) + amvdec2_pg_enable(false); + /* #endif */ + + if (has_hevc_vdec()) + amhevc_pg_enable(false); + + return 0; +} +EXPORT_SYMBOL(amvdec_suspend); + +int amvdec_resume(struct platform_device *dev) +{ + amvdec_pg_enable(true); + + /* #if MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON6TVD */ + if (has_vdec2()) + amvdec2_pg_enable(true); + /* #endif */ + + /* #if MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON8 */ + if (has_hevc_vdec()) + amhevc_pg_enable(true); + /* #endif */ + + return 0; +} +EXPORT_SYMBOL(amvdec_resume); + +int amhevc_suspend(struct platform_device *dev, pm_message_t event) +{ + if (has_hevc_vdec()) + amhevc_pg_enable(false); + return 0; +} +EXPORT_SYMBOL(amhevc_suspend); + +int amhevc_resume(struct platform_device *dev) +{ + if (has_hevc_vdec()) + amhevc_pg_enable(true); + return 0; +} +EXPORT_SYMBOL(amhevc_resume); + + +#endif + +#ifdef CONFIG_WAKELOCK + +static int vdec_is_paused(void) +{ + static unsigned long old_wp = -1, old_rp = -1, old_level = -1; + unsigned long wp, rp, level; + static int paused_time; + + /* #if MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON8 */ + if (has_hevc_vdec()) { + if ((vdec_on(VDEC_HEVC)) + && (READ_VREG(HEVC_STREAM_CONTROL) & 1)) { + wp = READ_VREG(HEVC_STREAM_WR_PTR); + rp = READ_VREG(HEVC_STREAM_RD_PTR); + level = READ_VREG(HEVC_STREAM_LEVEL); + } else { + wp = READ_VREG(VLD_MEM_VIFIFO_WP); + rp = READ_VREG(VLD_MEM_VIFIFO_RP); + level = READ_VREG(VLD_MEM_VIFIFO_LEVEL); + } + } else + /* #endif */ + { + wp = READ_VREG(VLD_MEM_VIFIFO_WP); + rp = READ_VREG(VLD_MEM_VIFIFO_RP); + level = READ_VREG(VLD_MEM_VIFIFO_LEVEL); + } + /*have data,but output buffer is full */ + if ((rp == old_rp && level > 1024) || + (rp == old_rp && wp == old_wp && level == level)) { + /*no write && not read */ + paused_time++; + } else { + paused_time = 0; + } + old_wp = wp; old_rp = rp; old_level = level; + if (paused_time > 10) + return 1; + return 0; +} + +int amvdev_pause(void) +{ + video_running = 0; + video_stated_changed = 1; + return 0; +} +EXPORT_SYMBOL(amvdev_pause); + +int amvdev_resume(void) +{ + video_running = 1; + video_stated_changed = 1; + return 0; +} +EXPORT_SYMBOL(amvdev_resume); + +static void vdec_paused_check_timer(unsigned long arg) +{ + if (video_stated_changed) { + if (!video_running) { + if (vdec_is_paused()) { + pr_info("vdec paused and release wakelock now\n"); + amvdec_wake_unlock(); + video_stated_changed = 0; + } + } else { + amvdec_wake_lock(); + video_stated_changed = 0; + } + } + mod_timer(&amvdevtimer, jiffies + WAKE_CHECK_INTERVAL); +} +#else +int amvdev_pause(void) +{ + return 0; +} + +int amvdev_resume(void) +{ + return 0; +} +#endif + +int amvdec_init(void) +{ +#ifdef CONFIG_WAKELOCK + /* + *wake_lock_init(&amvdec_lock, WAKE_LOCK_IDLE, "amvdec_lock"); + *tmp mark for compile, no "WAKE_LOCK_IDLE" definition in kernel 3.8 + */ + wake_lock_init(&amvdec_lock, /*WAKE_LOCK_IDLE */ WAKE_LOCK_SUSPEND, + "amvdec_lock"); + + init_timer(&amvdevtimer); + + amvdevtimer.data = (ulong) &amvdevtimer; + amvdevtimer.function = vdec_paused_check_timer; +#endif + return 0; +} +EXPORT_SYMBOL(amvdec_init); + +void amvdec_exit(void) +{ +#ifdef CONFIG_WAKELOCK + del_timer_sync(&amvdevtimer); +#endif +} +EXPORT_SYMBOL(amvdec_exit); + +#if 0 +int __init amvdec_init(void) +{ +#ifdef CONFIG_WAKELOCK + /* + *wake_lock_init(&amvdec_lock, WAKE_LOCK_IDLE, "amvdec_lock"); + *tmp mark for compile, no "WAKE_LOCK_IDLE" definition in kernel 3.8 + */ + wake_lock_init(&amvdec_lock, /*WAKE_LOCK_IDLE */ WAKE_LOCK_SUSPEND, + "amvdec_lock"); + + init_timer(&amvdevtimer); + + amvdevtimer.data = (ulong) &amvdevtimer; + amvdevtimer.function = vdec_paused_check_timer; +#endif + return 0; +} + +static void __exit amvdec_exit(void) +{ +#ifdef CONFIG_WAKELOCK + del_timer_sync(&amvdevtimer); +#endif +} + +module_init(amvdec_init); +module_exit(amvdec_exit); +#endif + +MODULE_DESCRIPTION("Amlogic Video Decoder Utility Driver"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Tim Yao <timyao@amlogic.com>"); diff --git a/drivers/frame_provider/decoder/utils/amvdec.h b/drivers/frame_provider/decoder/utils/amvdec.h new file mode 100644 index 0000000..82aceef --- a/dev/null +++ b/drivers/frame_provider/decoder/utils/amvdec.h @@ -0,0 +1,82 @@ +/* + * drivers/amlogic/media/frame_provider/decoder/utils/amvdec.h + * + * Copyright (C) 2016 Amlogic, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * +*/ + +#ifndef AMVDEC_H +#define AMVDEC_H +#include <linux/amlogic/media/utils/amports_config.h> +#include <linux/amlogic/media/utils/vformat.h> + +#define UCODE_ALIGN 8 +#define UCODE_ALIGN_MASK 7UL + +struct amvdec_dec_reg_s { + unsigned long mem_start; + unsigned long mem_end; + struct device *cma_dev; + struct dec_sysinfo *dec_sysinfo; +}; /*amvdec_dec_reg_t */ + +extern void amvdec_start(void); +extern void amvdec_stop(void); +extern void amvdec_enable(void); +extern void amvdec_disable(void); +s32 amvdec_loadmc_ex(enum vformat_e type, const char *name, char *def); + +extern void amvdec2_start(void); +extern void amvdec2_stop(void); +extern void amvdec2_enable(void); +extern void amvdec2_disable(void); +s32 amvdec2_loadmc_ex(enum vformat_e type, const char *name, char *def); + +extern void amhevc_start(void); +extern void amhevc_stop(void); +extern void amhevc_enable(void); +extern void amhevc_disable(void); +s32 amhevc_loadmc_ex(enum vformat_e type, const char *name, char *def); + +extern void amhcodec_start(void); +extern void amhcodec_stop(void); +s32 amhcodec_loadmc(const u32 *p); +s32 amhcodec_loadmc_ex(enum vformat_e type, const char *name, char *def); + +extern int amvdev_pause(void); +extern int amvdev_resume(void); + +#ifdef CONFIG_PM +extern int amvdec_suspend(struct platform_device *dev, pm_message_t event); +extern int amvdec_resume(struct platform_device *dec); +extern int amhevc_suspend(struct platform_device *dev, pm_message_t event); +extern int amhevc_resume(struct platform_device *dec); + +#endif + +int amvdec_init(void); +void amvdec_exit(void); + +#if 1 /* MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON6 */ +#define AMVDEC_CLK_GATE_ON(a) +#define AMVDEC_CLK_GATE_OFF(a) +#else +#define AMVDEC_CLK_GATE_ON(a) CLK_GATE_ON(a) +#define AMVDEC_CLK_GATE_OFF(a) CLK_GATE_OFF(a) +#endif + +/* TODO: move to register headers */ +#define RESET_VCPU (1<<7) +#define RESET_CCPU (1<<8) + +#endif /* AMVDEC_H */ diff --git a/drivers/frame_provider/decoder/utils/decoder_mmu_box.c b/drivers/frame_provider/decoder/utils/decoder_mmu_box.c new file mode 100644 index 0000000..941829a --- a/dev/null +++ b/drivers/frame_provider/decoder/utils/decoder_mmu_box.c @@ -0,0 +1,373 @@ +/* + * drivers/amlogic/media/frame_provider/decoder/utils/decoder_mmu_box.c + * + * Copyright (C) 2016 Amlogic, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * +*/ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/interrupt.h> +#include <linux/semaphore.h> +#include <linux/delay.h> +#include <linux/timer.h> +#include <linux/kfifo.h> +#include <linux/kthread.h> +#include <linux/slab.h> +#include <linux/amlogic/media/codec_mm/codec_mm_scatter.h> +#include <linux/platform_device.h> +struct decoder_mmu_box { + int max_sc_num; + const char *name; + int channel_id; + struct mutex mutex; + struct list_head list; + struct codec_mm_scatter *sc_list[1]; +}; +#define MAX_KEEP_FRAME 4 +#define START_KEEP_ID 0x9 +#define MAX_KEEP_ID (INT_MAX - 1) +struct decoder_mmu_box_mgr { + int num; + struct mutex mutex; + struct codec_mm_scatter *keep_sc[MAX_KEEP_FRAME]; + int keep_id[MAX_KEEP_FRAME]; + int next_id;/*id for keep & free.*/ + struct list_head box_list; +}; +static struct decoder_mmu_box_mgr global_mgr; +static struct decoder_mmu_box_mgr *get_decoder_mmu_box_mgr(void) +{ + return &global_mgr; +} + +static int decoder_mmu_box_mgr_add_box(struct decoder_mmu_box *box) +{ + struct decoder_mmu_box_mgr *mgr = get_decoder_mmu_box_mgr(); + + mutex_lock(&mgr->mutex); + list_add_tail(&box->list, &mgr->box_list); + mutex_unlock(&mgr->mutex); + return 0; +} + +static int decoder_mmu_box_mgr_del_box(struct decoder_mmu_box *box) +{ + struct decoder_mmu_box_mgr *mgr = get_decoder_mmu_box_mgr(); + + mutex_lock(&mgr->mutex); + list_del(&box->list); + mutex_unlock(&mgr->mutex); + return 0; +} + + + +void *decoder_mmu_box_alloc_box(const char *name, int channel_id, int max_num) +{ + struct decoder_mmu_box *box; + int size; + + size = sizeof(struct decoder_mmu_box) + + sizeof(struct codec_mm_scatter *) * + max_num; + box = kmalloc(size, GFP_KERNEL); + if (!box) { + pr_err("can't alloc decoder buffers box!!!\n"); + return NULL; + } + memset(box, 0, size); + box->max_sc_num = max_num; + box->name = name; + box->channel_id = channel_id; + mutex_init(&box->mutex); + INIT_LIST_HEAD(&box->list); + decoder_mmu_box_mgr_add_box(box); + codec_mm_scatter_mgt_delay_free_swith(1, 0); + return (void *)box; +} +EXPORT_SYMBOL(decoder_mmu_box_alloc_box); + +int decoder_mmu_box_alloc_idx( + void *handle, int idx, int num_pages, + unsigned int *mmu_index_adr) +{ + struct decoder_mmu_box *box = handle; + struct codec_mm_scatter *sc; + int ret; + int i; + + if (!box || idx < 0 || idx >= box->max_sc_num) { + pr_err("can't alloc mmu box(%p),idx:%d in (%d-%d)\n", + box, idx, 0, + box->max_sc_num - 1); + return -1; + } + mutex_lock(&box->mutex); + sc = box->sc_list[idx]; + if (sc) { + if (sc->page_max_cnt >= num_pages) + ret = codec_mm_scatter_alloc_want_pages(sc, + num_pages); + else { + codec_mm_scatter_dec_owner_user(sc, 0); + box->sc_list[idx] = NULL; + sc = NULL; + } + + } + if (!sc) { + sc = codec_mm_scatter_alloc(num_pages + 64, num_pages); + if (!sc) { + mutex_unlock(&box->mutex); + pr_err("alloc mmu failed, need pages=%d\n", + num_pages); + return -1; + } + box->sc_list[idx] = sc; + } + + for (i = 0; i < num_pages; i++) + mmu_index_adr[i] = PAGE_INDEX(sc->pages_list[i]); + mmu_index_adr[num_pages] = 0; + + mutex_unlock(&box->mutex); + + return 0; +} +EXPORT_SYMBOL(decoder_mmu_box_alloc_idx); + +int decoder_mmu_box_free_idx_tail( + void *handle, int idx, + int start_release_index) +{ + struct decoder_mmu_box *box = handle; + struct codec_mm_scatter *sc; + + if (!box || idx < 0 || idx >= box->max_sc_num) { + pr_err("can't free tail mmu box(%p),idx:%d in (%d-%d)\n", + box, idx, 0, + box->max_sc_num - 1); + return -1; + } + mutex_lock(&box->mutex); + sc = box->sc_list[idx]; + if (sc && start_release_index < sc->page_cnt) + codec_mm_scatter_free_tail_pages_fast(sc, + start_release_index); + mutex_unlock(&box->mutex); + return 0; +} +EXPORT_SYMBOL(decoder_mmu_box_free_idx_tail); + +int decoder_mmu_box_free_idx(void *handle, int idx) +{ + struct decoder_mmu_box *box = handle; + struct codec_mm_scatter *sc; + + if (!box || idx < 0 || idx >= box->max_sc_num) { + pr_err("can't free idx of box(%p),idx:%d in (%d-%d)\n", + box, idx, 0, + box->max_sc_num - 1); + return -1; + } + mutex_lock(&box->mutex); + sc = box->sc_list[idx]; + if (sc && sc->page_cnt > 0) { + codec_mm_scatter_dec_owner_user(sc, 0); + box->sc_list[idx] = NULL; + } mutex_unlock(&box->mutex); + return 0; +} +EXPORT_SYMBOL(decoder_mmu_box_free_idx); + +int decoder_mmu_box_free(void *handle) +{ + struct decoder_mmu_box *box = handle; + struct codec_mm_scatter *sc; + int i; + + if (!box) { + pr_err("can't free box of NULL box!\n"); + return -1; + } + mutex_lock(&box->mutex); + for (i = 0; i < box->max_sc_num; i++) { + sc = box->sc_list[i]; + if (sc) { + codec_mm_scatter_dec_owner_user(sc, 200); + box->sc_list[i] = NULL; + } + } + mutex_unlock(&box->mutex); + decoder_mmu_box_mgr_del_box(box); + kfree(box); + codec_mm_scatter_mgt_delay_free_swith(0, 2000); + return 0; +} +EXPORT_SYMBOL(decoder_mmu_box_free); + +void *decoder_mmu_box_get_mem_handle(void *box_handle, int idx) +{ + struct decoder_mmu_box *box = box_handle; + + if (!box || idx < 0 || idx >= box->max_sc_num) + return NULL; + return box->sc_list[idx]; +} +EXPORT_SYMBOL(decoder_mmu_box_get_mem_handle); + +static int decoder_mmu_box_dump(struct decoder_mmu_box *box, + void *buf, int size) +{ + char *pbuf = buf; + char sbuf[512]; + int tsize = 0; + int s; + int i; + + if (!pbuf) + pbuf = sbuf; + + #define BUFPRINT(args...) \ + do {\ + s = sprintf(pbuf, args);\ + tsize += s;\ + pbuf += s; \ + } while (0) + + for (i = 0; i < box->max_sc_num; i++) { + struct codec_mm_scatter *sc = box->sc_list[i]; + + if (sc) { + BUFPRINT("sc mem[%d]:%p, size=%d\n", + i, sc, + sc->page_cnt << PAGE_SHIFT); + } + } +#undef BUFPRINT + if (!buf) + pr_info("%s", sbuf); + + return tsize; +} + +static int decoder_mmu_box_dump_all(void *buf, int size) +{ + struct decoder_mmu_box_mgr *mgr = get_decoder_mmu_box_mgr(); + char *pbuf = buf; + char sbuf[512]; + int tsize = 0; + int s; + int i; + struct list_head *head, *list; + + if (!pbuf) + pbuf = sbuf; + + #define BUFPRINT(args...) \ + do {\ + s = sprintf(pbuf, args);\ + tsize += s;\ + pbuf += s; \ + } while (0) + + mutex_lock(&mgr->mutex); + head = &mgr->box_list; + list = head->next; + i = 0; + while (list != head) { + struct decoder_mmu_box *box; + + box = list_entry(list, struct decoder_mmu_box, + list); + BUFPRINT("box[%d]: %s, player_id:%d, max_num:%d\n", + i, + box->name, + box->channel_id, + box->max_sc_num); + if (buf) { + tsize += decoder_mmu_box_dump(box, pbuf, size - tsize); + if (tsize > 0) + pbuf += tsize; + } else { + pr_info("%s", sbuf); + pbuf = sbuf; + tsize += decoder_mmu_box_dump(box, NULL, 0); + } + list = list->next; + i++; + } + mutex_unlock(&mgr->mutex); + + +#undef BUFPRINT + if (!buf) + pr_info("%s", sbuf); + return tsize; +} + + + +static ssize_t +box_dump_show(struct class *class, + struct class_attribute *attr, char *buf) +{ + ssize_t ret = 0; + + ret = decoder_mmu_box_dump_all(buf, PAGE_SIZE); + return ret; +} + + + +static struct class_attribute decoder_mmu_box_class_attrs[] = { + __ATTR_RO(box_dump), + __ATTR_NULL +}; + +static struct class decoder_mmu_box_class = { + .name = "decoder_mmu_box", + .class_attrs = decoder_mmu_box_class_attrs, +}; + +int decoder_mmu_box_init(void) +{ + int r; + + memset(&global_mgr, 0, sizeof(global_mgr)); + INIT_LIST_HEAD(&global_mgr.box_list); + mutex_init(&global_mgr.mutex); + global_mgr.next_id = START_KEEP_ID; + r = class_register(&decoder_mmu_box_class); + return r; +} +EXPORT_SYMBOL(decoder_mmu_box_init); + +#if 0 +static int __init decoder_mmu_box_init(void) +{ + int r; + + memset(&global_mgr, 0, sizeof(global_mgr)); + INIT_LIST_HEAD(&global_mgr.box_list); + mutex_init(&global_mgr.mutex); + global_mgr.next_id = START_KEEP_ID; + r = class_register(&decoder_mmu_box_class); + return r; +} + +module_init(decoder_mmu_box_init); +#endif diff --git a/drivers/frame_provider/decoder/utils/decoder_mmu_box.h b/drivers/frame_provider/decoder/utils/decoder_mmu_box.h new file mode 100644 index 0000000..7b3fb43 --- a/dev/null +++ b/drivers/frame_provider/decoder/utils/decoder_mmu_box.h @@ -0,0 +1,38 @@ +/* + * drivers/amlogic/media/frame_provider/decoder/utils/decoder_mmu_box.h + * + * Copyright (C) 2016 Amlogic, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * +*/ + +#ifndef DECODER_BUFFER_BOX +#define DECODER_BUFFER_BOX +void *decoder_mmu_box_alloc_box(const char *name, int player_id, int max_num); + +int decoder_mmu_box_alloc_idx(void *handle, int idx, int num_pages, + unsigned int *mmu_index_adr); + +int decoder_mmu_box_free_idx_tail(void *handle, int idx, + int start_release_index); + +int decoder_mmu_box_free_idx(void *handle, int idx); + +int decoder_mmu_box_free(void *handle); + +int decoder_mmu_box_move_keep_idx(void *box_handle, int keep_idx); +int decoder_mmu_box_free_keep(int keep_id); +int decoder_mmu_box_free_all_keep(void); +void *decoder_mmu_box_get_mem_handle(void *box_handle, int idx); +int decoder_mmu_box_init(void); + +#endif diff --git a/drivers/frame_provider/decoder/utils/utils.c b/drivers/frame_provider/decoder/utils/utils.c new file mode 100644 index 0000000..3455330 --- a/dev/null +++ b/drivers/frame_provider/decoder/utils/utils.c @@ -0,0 +1,61 @@ +/* + * drivers/amlogic/media/frame_provider/decoder/utils/utils.c + * + * Copyright (C) 2016 Amlogic, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * +*/ + +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/delay.h> +#include <linux/kthread.h> +#include <linux/platform_device.h> +#include <linux/uaccess.h> +#include <linux/semaphore.h> +#include <linux/sched/rt.h> +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/slab.h> + +#include "vdec.h" +#include "vdec_input.h" +#include <linux/amlogic/media/utils/vdec_reg.h> +#include "amvdec.h" +#include "decoder_mmu_box.h" + +static int __init decoder_common_init(void) +{ + /*vdec init.*/ + vdec_module_init(); + + /*amvdec init.*/ + amvdec_init(); + + /*mmu box init.*/ + decoder_mmu_box_init();/*exit?*/ + + return 0; +} + +static void __exit decoder_common_exit(void) +{ + /*vdec exit.*/ + vdec_module_exit(); + + /*amvdec exit.*/ + amvdec_exit(); +} + +module_init(decoder_common_init); +module_exit(decoder_common_exit); diff --git a/drivers/frame_provider/decoder/utils/vdec.c b/drivers/frame_provider/decoder/utils/vdec.c new file mode 100644 index 0000000..8088965 --- a/dev/null +++ b/drivers/frame_provider/decoder/utils/vdec.c @@ -0,0 +1,2688 @@ +/* + * drivers/amlogic/media/frame_provider/decoder/utils/vdec.c + * + * Copyright (C) 2016 Amlogic, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * +*/ + +#include <linux/kernel.h> +#include <linux/spinlock.h> +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/delay.h> +#include <linux/kthread.h> +#include <linux/platform_device.h> +#include <linux/uaccess.h> +#include <linux/semaphore.h> +#include <linux/sched/rt.h> +#include <linux/interrupt.h> +#include <linux/amlogic/media/utils/vformat.h> +#include <linux/amlogic/iomap.h> +#include <linux/amlogic/media/canvas/canvas.h> +#include <linux/amlogic/media/vfm/vframe.h> +#include <linux/amlogic/media/vfm/vframe_provider.h> +#include <linux/amlogic/media/vfm/vframe_receiver.h> +#include <linux/amlogic/media/video_sink/video/ionvideo_ext.h> +#include <linux/amlogic/media/vfm/vfm_ext.h> + +#include <linux/amlogic/media/utils/vdec_reg.h> +#include "vdec.h" +#include <linux/of.h> +#include <linux/of_fdt.h> +#include <linux/libfdt_env.h> +#include <linux/of_reserved_mem.h> +#include <linux/dma-contiguous.h> +#include <linux/cma.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/dma-mapping.h> +#include <linux/dma-contiguous.h> +#include "../../../stream_input/amports/amports_priv.h" + +#include <linux/amlogic/media/utils/amports_config.h> +#include "../utils/amvdec.h" +/*#include "../vp9/vvp9.h"*//*mask*/ +#include "vdec_input.h" + +#include "../../../common/media_clock/clk/clk.h" +#include <linux/reset.h> +#include <linux/amlogic/media/old_cpu_version.h> +#include <linux/amlogic/media/codec_mm/codec_mm.h> + +static DEFINE_MUTEX(vdec_mutex); + +#define MC_SIZE (4096 * 4) +#define CMA_ALLOC_SIZE SZ_64M +#define MEM_NAME "vdec_prealloc" +static int inited_vcodec_num; +static int poweron_clock_level; +static int keep_vdec_mem; +static unsigned int debug_trace_num = 16 * 20; +static unsigned int clk_config; + +static struct page *vdec_cma_page; +int vdec_mem_alloced_from_codec, delay_release; +static unsigned long reserved_mem_start, reserved_mem_end; +static int hevc_max_reset_count; +static DEFINE_SPINLOCK(vdec_spin_lock); + +#define HEVC_TEST_LIMIT 100 +#define GXBB_REV_A_MINOR 0xA + +struct am_reg { + char *name; + int offset; +}; + +struct vdec_isr_context_s { + int index; + int irq; + irq_handler_t dev_isr; + irq_handler_t dev_threaded_isr; + void *dev_id; +}; + +struct vdec_core_s { + struct list_head connected_vdec_list; + spinlock_t lock; + + atomic_t vdec_nr; + struct vdec_s *vfm_vdec; + struct vdec_s *active_vdec; + struct platform_device *vdec_core_platform_device; + struct device *cma_dev; + unsigned long mem_start; + unsigned long mem_end; + + struct semaphore sem; + struct task_struct *thread; + + struct vdec_isr_context_s isr_context[VDEC_IRQ_MAX]; +}; + +static struct vdec_core_s *vdec_core; + +unsigned long vdec_core_lock(struct vdec_core_s *core) +{ + unsigned long flags; + + spin_lock_irqsave(&core->lock, flags); + + return flags; +} + +void vdec_core_unlock(struct vdec_core_s *core, unsigned long flags) +{ + spin_unlock_irqrestore(&core->lock, flags); +} + +static int get_canvas(unsigned int index, unsigned int base) +{ + int start; + int canvas_index = index * base; + + if ((base > 4) || (base == 0)) + return -1; + + if ((AMVDEC_CANVAS_START_INDEX + canvas_index + base - 1) + <= AMVDEC_CANVAS_MAX1) { + start = AMVDEC_CANVAS_START_INDEX + base * index; + } else { + canvas_index -= (AMVDEC_CANVAS_MAX1 - + AMVDEC_CANVAS_START_INDEX + 1) / base * base; + if (canvas_index <= AMVDEC_CANVAS_MAX2) + start = canvas_index / base; + else + return -1; + } + + if (base == 1) { + return start; + } else if (base == 2) { + return ((start + 1) << 16) | ((start + 1) << 8) | start; + } else if (base == 3) { + return ((start + 2) << 16) | ((start + 1) << 8) | start; + } else if (base == 4) { + return (((start + 3) << 24) | (start + 2) << 16) | + ((start + 1) << 8) | start; + } + + return -1; +} + + +int vdec_status(struct vdec_s *vdec, struct vdec_status *vstatus) +{ + if (vdec->dec_status) + return vdec->dec_status(vdec, vstatus); + + return -1; +} +EXPORT_SYMBOL(vdec_status); + +int vdec_set_trickmode(struct vdec_s *vdec, unsigned long trickmode) +{ + if (vdec->set_trickmode) + return vdec->set_trickmode(vdec, trickmode); + + return -1; +} +EXPORT_SYMBOL(vdec_set_trickmode); + +/* +* clk_config: + *0:default + *1:no gp0_pll; + *2:always used gp0_pll; + *>=10:fixed n M clk; + *== 100 , 100M clks; +*/ +unsigned int get_vdec_clk_config_settings(void) +{ + return clk_config; +} +void update_vdec_clk_config_settings(unsigned int config) +{ + clk_config = config; +} +EXPORT_SYMBOL(update_vdec_clk_config_settings); + +static bool hevc_workaround_needed(void) +{ + return (get_cpu_type() == MESON_CPU_MAJOR_ID_GXBB) && + (get_meson_cpu_version(MESON_CPU_VERSION_LVL_MINOR) + == GXBB_REV_A_MINOR); +} + +struct device *get_codec_cma_device(void) +{ + return vdec_core->cma_dev; +} + +static unsigned int get_mmu_mode(void) +{ + return 1;/*mask temp*/ +} + +#ifdef CONFIG_MULTI_DEC +static const char * const vdec_device_name[] = { + "amvdec_mpeg12", "ammvdec_mpeg12", + "amvdec_mpeg4", "ammvdec_mpeg4", + "amvdec_h264", "ammvdec_h264", + "amvdec_mjpeg", "ammvdec_mjpeg", + "amvdec_real", "ammvdec_real", + "amjpegdec", "ammjpegdec", + "amvdec_vc1", "ammvdec_vc1", + "amvdec_avs", "ammvdec_avs", + "amvdec_yuv", "ammvdec_yuv", + "amvdec_h264mvc", "ammvdec_h264mvc", + "amvdec_h264_4k2k", "ammvdec_h264_4k2k", + "amvdec_h265", "ammvdec_h265", + "amvenc_avc", "amvenc_avc", + "jpegenc", "jpegenc", + "amvdec_vp9", "amvdec_vp9" +}; + +static int vdec_default_buf_size[] = { + 32, 32, /*"amvdec_mpeg12",*/ + 32, 32, /*"amvdec_mpeg4",*/ + 48, 0, /*"amvdec_h264",*/ + 32, 32, /*"amvdec_mjpeg",*/ + 32, 32, /*"amvdec_real",*/ + 32, 32, /*"amjpegdec",*/ + 32, 32, /*"amvdec_vc1",*/ + 32, 32, /*"amvdec_avs",*/ + 32, 32, /*"amvdec_yuv",*/ + 64, 64, /*"amvdec_h264mvc",*/ + 64, 64, /*"amvdec_h264_4k2k", else alloc on decoder*/ + 48, 48, /*"amvdec_h265", else alloc on decoder*/ + 0, 0, /* avs encoder */ + 0, 0, /* jpg encoder */ +#ifdef VP9_10B_MMU + 24, 24, /*"amvdec_vp9", else alloc on decoder*/ +#else + 32, 32, +#endif + 0 +}; + +#else + +static const char * const vdec_device_name[] = { + "amvdec_mpeg12", + "amvdec_mpeg4", + "amvdec_h264", + "amvdec_mjpeg", + "amvdec_real", + "amjpegdec", + "amvdec_vc1", + "amvdec_avs", + "amvdec_yuv", + "amvdec_h264mvc", + "amvdec_h264_4k2k", + "amvdec_h265", + "amvenc_avc", + "jpegenc", + "amvdec_vp9" +}; + +static int vdec_default_buf_size[] = { + 32, /*"amvdec_mpeg12",*/ + 32, /*"amvdec_mpeg4",*/ + 48, /*"amvdec_h264",*/ + 32, /*"amvdec_mjpeg",*/ + 32, /*"amvdec_real",*/ + 32, /*"amjpegdec",*/ + 32, /*"amvdec_vc1",*/ + 32, /*"amvdec_avs",*/ + 32, /*"amvdec_yuv",*/ + 64, /*"amvdec_h264mvc",*/ + 64, /*"amvdec_h264_4k2k", else alloc on decoder*/ + 48, /*"amvdec_h265", else alloc on decoder*/ + 0, /* avs encoder */ + 0, /* jpg encoder */ +#ifdef VP9_10B_MMU + 24, /*"amvdec_vp9", else alloc on decoder*/ +#else + 32, +#endif +}; +#endif + +int vdec_set_decinfo(struct vdec_s *vdec, struct dec_sysinfo *p) +{ + if (copy_from_user((void *)&vdec->sys_info_store, (void *)p, + sizeof(struct dec_sysinfo))) + return -EFAULT; + + return 0; +} +EXPORT_SYMBOL(vdec_set_decinfo); + +/* construct vdec strcture */ +struct vdec_s *vdec_create(int type) +{ + struct vdec_s *vdec; + + vdec = kzalloc(sizeof(struct vdec_s), GFP_KERNEL); + + /* TBD */ + if (vdec) { + vdec->magic = 0x43454456; + vdec->id = 0; + vdec->type = type; + vdec->sys_info = &vdec->sys_info_store; + + INIT_LIST_HEAD(&vdec->list); + + vdec_input_init(&vdec->input, vdec); + + atomic_inc(&vdec_core->vdec_nr); + } + + pr_info("vdec_create instance %p, total %d\n", vdec, + atomic_read(&vdec_core->vdec_nr)); + + return vdec; +} +EXPORT_SYMBOL(vdec_create); + +int vdec_set_format(struct vdec_s *vdec, int format) +{ + vdec->format = format; + return 0; +} +EXPORT_SYMBOL(vdec_set_format); + +int vdec_set_pts(struct vdec_s *vdec, u32 pts) +{ + vdec->pts = pts; + vdec->pts_valid = true; + return 0; +} +EXPORT_SYMBOL(vdec_set_pts); + +int vdec_set_pts64(struct vdec_s *vdec, u64 pts64) +{ + vdec->pts64 = pts64; + vdec->pts_valid = true; + return 0; +} +EXPORT_SYMBOL(vdec_set_pts64); + +void vdec_set_status(struct vdec_s *vdec, int status) +{ + vdec->status = status; +} +EXPORT_SYMBOL(vdec_set_status); + +void vdec_set_next_status(struct vdec_s *vdec, int status) +{ + vdec->next_status = status; +} +EXPORT_SYMBOL(vdec_set_next_status); + +/* add frame data to input chain */ +int vdec_write_vframe(struct vdec_s *vdec, const char *buf, size_t count) +{ + return vdec_input_add_frame(&vdec->input, buf, count); +} +EXPORT_SYMBOL(vdec_write_vframe); + +/* +*get next frame from input chain +*/ +/* +*THE VLD_FIFO is 512 bytes and Video buffer level + * empty interrupt is set to 0x80 bytes threshold + */ +#define VLD_PADDING_SIZE 1024 +#define HEVC_PADDING_SIZE (1024*16) +#define FIFO_ALIGN 8 +int vdec_prepare_input(struct vdec_s *vdec, struct vframe_chunk_s **p) +{ + struct vdec_input_s *input = &vdec->input; + struct vframe_chunk_s *chunk = NULL; + struct vframe_block_list_s *block = NULL; + int dummy; + + /* full reset to HW input */ + if (input->target == VDEC_INPUT_TARGET_VLD) { + WRITE_VREG(VLD_MEM_VIFIFO_CONTROL, 0); + + /* reset VLD fifo for all vdec */ + WRITE_VREG(DOS_SW_RESET0, (1<<5) | (1<<4) | (1<<3)); + WRITE_VREG(DOS_SW_RESET0, 0); + + dummy = READ_MPEG_REG(RESET0_REGISTER); + WRITE_VREG(POWER_CTL_VLD, 1 << 4); + } else if (input->target == VDEC_INPUT_TARGET_HEVC) { + WRITE_VREG(HEVC_STREAM_CONTROL, 0); + + WRITE_VREG(DOS_SW_RESET3, + (1<<3)|(1<<4)|(1<<8)|(1<<11)|(1<<12)|(1<<14)|(1<<15)| + (1<<17)|(1<<18)|(1<<19)); + WRITE_VREG(DOS_SW_RESET3, 0); + } + + /* + *setup HW decoder input buffer (VLD context) + * based on input->type and input->target + */ + if (input_frame_based(input)) { + chunk = vdec_input_next_chunk(&vdec->input); + + if (chunk == NULL) { + *p = NULL; + return -1; + } + + block = chunk->block; + + if (input->target == VDEC_INPUT_TARGET_VLD) { + WRITE_VREG(VLD_MEM_VIFIFO_START_PTR, block->start); + WRITE_VREG(VLD_MEM_VIFIFO_END_PTR, block->start + + block->size - 8); + WRITE_VREG(VLD_MEM_VIFIFO_CURR_PTR, + round_down(block->start + chunk->offset, + FIFO_ALIGN)); + + WRITE_VREG(VLD_MEM_VIFIFO_CONTROL, 1); + WRITE_VREG(VLD_MEM_VIFIFO_CONTROL, 0); + + /* set to manual mode */ + WRITE_VREG(VLD_MEM_VIFIFO_BUF_CNTL, 2); + WRITE_VREG(VLD_MEM_VIFIFO_RP, + round_down(block->start + chunk->offset, + FIFO_ALIGN)); + dummy = chunk->offset + chunk->size + + VLD_PADDING_SIZE; + if (dummy >= block->size) + dummy -= block->size; + WRITE_VREG(VLD_MEM_VIFIFO_WP, + round_down(block->start + dummy, FIFO_ALIGN)); + + WRITE_VREG(VLD_MEM_VIFIFO_BUF_CNTL, 3); + WRITE_VREG(VLD_MEM_VIFIFO_BUF_CNTL, 2); + + WRITE_VREG(VLD_MEM_VIFIFO_CONTROL, + (0x11 << 16) | (1<<10) | (7<<3)); + + } else if (input->target == VDEC_INPUT_TARGET_HEVC) { + WRITE_VREG(HEVC_STREAM_START_ADDR, block->start); + WRITE_VREG(HEVC_STREAM_END_ADDR, block->start + + block->size); + WRITE_VREG(HEVC_STREAM_RD_PTR, block->start + + chunk->offset); + dummy = chunk->offset + chunk->size + + HEVC_PADDING_SIZE; + if (dummy >= block->size) + dummy -= block->size; + WRITE_VREG(HEVC_STREAM_WR_PTR, + round_down(block->start + dummy, FIFO_ALIGN)); + } + + *p = chunk; + return chunk->size; + + } else { + u32 rp = 0, wp = 0, fifo_len = 0; + int size; + /* stream based */ + if (input->swap_valid) { + if (input->target == VDEC_INPUT_TARGET_VLD) { + WRITE_VREG(VLD_MEM_VIFIFO_CONTROL, 0); + + /* restore read side */ + WRITE_VREG(VLD_MEM_SWAP_ADDR, + page_to_phys(input->swap_page)); + WRITE_VREG(VLD_MEM_SWAP_CTL, 1); + + while (READ_VREG(VLD_MEM_SWAP_CTL) & (1<<7)) + ; + WRITE_VREG(VLD_MEM_SWAP_CTL, 0); + + /* restore wrap count */ + WRITE_VREG(VLD_MEM_VIFIFO_WRAP_COUNT, + input->stream_cookie); + + rp = READ_VREG(VLD_MEM_VIFIFO_RP); + fifo_len = READ_VREG(VLD_MEM_VIFIFO_LEVEL); + + /* enable */ + WRITE_VREG(VLD_MEM_VIFIFO_CONTROL, + (0x11 << 16) | (1<<10)); + + /* update write side */ + WRITE_VREG(VLD_MEM_VIFIFO_WP, + READ_MPEG_REG(PARSER_VIDEO_WP)); + + wp = READ_VREG(VLD_MEM_VIFIFO_WP); + } else if (input->target == VDEC_INPUT_TARGET_HEVC) { + /* restore read side */ + WRITE_VREG(HEVC_STREAM_SWAP_ADDR, + page_to_phys(input->swap_page)); + WRITE_VREG(HEVC_STREAM_SWAP_CTRL, 1); + + while (READ_VREG(HEVC_STREAM_SWAP_CTRL) + & (1<<7)) + ; + WRITE_VREG(HEVC_STREAM_SWAP_CTRL, 0); + + /* restore stream offset */ + WRITE_VREG(HEVC_SHIFT_BYTE_COUNT, + input->stream_cookie); + + rp = READ_VREG(HEVC_STREAM_RD_PTR); + fifo_len = (READ_VREG(HEVC_STREAM_FIFO_CTL) + >> 16) & 0x7f; + + + /* enable */ + + /* update write side */ + WRITE_VREG(HEVC_STREAM_WR_PTR, + READ_MPEG_REG(PARSER_VIDEO_WP)); + + wp = READ_VREG(HEVC_STREAM_WR_PTR); + } + + } else { + if (input->target == VDEC_INPUT_TARGET_VLD) { + WRITE_VREG(VLD_MEM_VIFIFO_START_PTR, + input->start); + WRITE_VREG(VLD_MEM_VIFIFO_END_PTR, + input->start + input->size - 8); + WRITE_VREG(VLD_MEM_VIFIFO_CURR_PTR, + input->start); + + WRITE_VREG(VLD_MEM_VIFIFO_CONTROL, 1); + WRITE_VREG(VLD_MEM_VIFIFO_CONTROL, 0); + + /* set to manual mode */ + WRITE_VREG(VLD_MEM_VIFIFO_BUF_CNTL, 2); + WRITE_VREG(VLD_MEM_VIFIFO_RP, input->start); + WRITE_VREG(VLD_MEM_VIFIFO_WP, + READ_MPEG_REG(PARSER_VIDEO_WP)); + + rp = READ_VREG(VLD_MEM_VIFIFO_RP); + + /* enable */ + WRITE_VREG(VLD_MEM_VIFIFO_CONTROL, + (0x11 << 16) | (1<<10)); + + wp = READ_VREG(VLD_MEM_VIFIFO_WP); + + } else if (input->target == VDEC_INPUT_TARGET_HEVC) { + WRITE_VREG(HEVC_STREAM_START_ADDR, + input->start); + WRITE_VREG(HEVC_STREAM_END_ADDR, + input->start + input->size); + WRITE_VREG(HEVC_STREAM_RD_PTR, + input->start); + WRITE_VREG(HEVC_STREAM_WR_PTR, + READ_MPEG_REG(PARSER_VIDEO_WP)); + + rp = READ_VREG(HEVC_STREAM_RD_PTR); + wp = READ_VREG(HEVC_STREAM_WR_PTR); + fifo_len = (READ_VREG(HEVC_STREAM_FIFO_CTL) + >> 16) & 0x7f; + + /* enable */ + } + } + *p = NULL; + if (wp >= rp) + size = wp - rp + fifo_len; + else + size = wp + input->size - rp + fifo_len; + if (size < 0) { + pr_info("%s error: input->size %x wp %x rp %x fifo_len %x => size %x\r\n", + __func__, input->size, wp, rp, fifo_len, size); + size = 0; + } + return size; + } +} +EXPORT_SYMBOL(vdec_prepare_input); + +void vdec_enable_input(struct vdec_s *vdec) +{ + struct vdec_input_s *input = &vdec->input; + + if (vdec->status != VDEC_STATUS_ACTIVE) + return; + + if (input->target == VDEC_INPUT_TARGET_VLD) + SET_VREG_MASK(VLD_MEM_VIFIFO_CONTROL, (1<<2) | (1<<1)); + else if (input->target == VDEC_INPUT_TARGET_HEVC) { + SET_VREG_MASK(HEVC_STREAM_CONTROL, 1); + SET_VREG_MASK(HEVC_STREAM_FIFO_CTL, (1<<29)); + } +} +EXPORT_SYMBOL(vdec_enable_input); + +void vdec_vframe_dirty(struct vdec_s *vdec, struct vframe_chunk_s *chunk) +{ + if (chunk) + chunk->flag |= VFRAME_CHUNK_FLAG_CONSUMED; + + if (vdec_stream_based(vdec)) { + vdec->input.swap_needed = true; + if (vdec->input.target == VDEC_INPUT_TARGET_VLD) { + WRITE_MPEG_REG(PARSER_VIDEO_RP, + READ_VREG(VLD_MEM_VIFIFO_RP)); + WRITE_VREG(VLD_MEM_VIFIFO_WP, + READ_MPEG_REG(PARSER_VIDEO_WP)); + } else if (vdec->input.target == VDEC_INPUT_TARGET_HEVC) { + WRITE_MPEG_REG(PARSER_VIDEO_RP, + READ_VREG(HEVC_STREAM_RD_PTR)); + WRITE_VREG(HEVC_STREAM_WR_PTR, + READ_MPEG_REG(PARSER_VIDEO_WP)); + } + } +} +EXPORT_SYMBOL(vdec_vframe_dirty); + +void vdec_save_input_context(struct vdec_s *vdec) +{ + struct vdec_input_s *input = &vdec->input; + + if (input->target == VDEC_INPUT_TARGET_VLD) + WRITE_VREG(VLD_MEM_VIFIFO_CONTROL, 1<<15); + else if (input->target == VDEC_INPUT_TARGET_HEVC) + WRITE_VREG(HEVC_STREAM_CONTROL, 0); + + if (input_stream_based(input) && (input->swap_needed)) { + if (input->target == VDEC_INPUT_TARGET_VLD) { + WRITE_VREG(VLD_MEM_SWAP_ADDR, + page_to_phys(input->swap_page)); + WRITE_VREG(VLD_MEM_SWAP_CTL, 3); + while (READ_VREG(VLD_MEM_SWAP_CTL) & (1<<7)) + ; + WRITE_VREG(VLD_MEM_SWAP_CTL, 0); + vdec->input.stream_cookie = + READ_VREG(VLD_MEM_VIFIFO_WRAP_COUNT); + } else if (input->target == VDEC_INPUT_TARGET_HEVC) { + WRITE_VREG(HEVC_STREAM_SWAP_ADDR, + page_to_phys(input->swap_page)); + WRITE_VREG(HEVC_STREAM_SWAP_CTRL, 3); + + while (READ_VREG(HEVC_STREAM_SWAP_CTRL) & (1<<7)) + ; + WRITE_VREG(HEVC_STREAM_SWAP_CTRL, 0); + + vdec->input.stream_cookie = + READ_VREG(HEVC_SHIFT_BYTE_COUNT); + } + + input->swap_valid = true; + + WRITE_MPEG_REG(PARSER_VIDEO_RP, READ_VREG(VLD_MEM_VIFIFO_RP)); + } +} +EXPORT_SYMBOL(vdec_save_input_context); + +void vdec_clean_input(struct vdec_s *vdec) +{ + struct vdec_input_s *input = &vdec->input; + + while (!list_empty(&input->vframe_chunk_list)) { + struct vframe_chunk_s *chunk = + vdec_input_next_chunk(input); + if (chunk->flag & VFRAME_CHUNK_FLAG_CONSUMED) + vdec_input_release_chunk(input, chunk); + else + break; + } + vdec_save_input_context(vdec); +} +EXPORT_SYMBOL(vdec_clean_input); + +static const char *vdec_status_str(struct vdec_s *vdec) +{ + switch (vdec->status) { + case VDEC_STATUS_UNINITIALIZED: + return "VDEC_STATUS_UNINITIALIZED"; + case VDEC_STATUS_DISCONNECTED: + return "VDEC_STATUS_DISCONNECTED"; + case VDEC_STATUS_CONNECTED: + return "VDEC_STATUS_CONNECTED"; + case VDEC_STATUS_ACTIVE: + return "VDEC_STATUS_ACTIVE"; + default: + return "invalid status"; + } +} + +static const char *vdec_type_str(struct vdec_s *vdec) +{ + switch (vdec->type) { + case VDEC_TYPE_SINGLE: + return "VDEC_TYPE_SINGLE"; + case VDEC_TYPE_STREAM_PARSER: + return "VDEC_TYPE_STREAM_PARSER"; + case VDEC_TYPE_FRAME_BLOCK: + return "VDEC_TYPE_FRAME_BLOCK"; + case VDEC_TYPE_FRAME_CIRCULAR: + return "VDEC_TYPE_FRAME_CIRCULAR"; + default: + return "VDEC_TYPE_INVALID"; + } +} + +void walk_vdec_core_list(char *s) +{ + struct vdec_s *vdec; + struct vdec_core_s *core = vdec_core; + unsigned long flags; + + pr_info("%s --->\n", s); + + flags = vdec_core_lock(vdec_core); + + if (list_empty(&core->connected_vdec_list)) { + pr_info("connected vdec list empty\n"); + } else { + list_for_each_entry(vdec, &core->connected_vdec_list, list) { + pr_info("\tvdec (%p), status = %s\n", vdec, + vdec_status_str(vdec)); + } + } + + vdec_core_unlock(vdec_core, flags); +} +EXPORT_SYMBOL(walk_vdec_core_list); + +/* insert vdec to vdec_core for scheduling */ +int vdec_connect(struct vdec_s *vdec) +{ + unsigned long flags; + + if (vdec->status != VDEC_STATUS_DISCONNECTED) + return 0; + + vdec_set_status(vdec, VDEC_STATUS_CONNECTED); + vdec_set_next_status(vdec, VDEC_STATUS_CONNECTED); + + init_completion(&vdec->inactive_done); + + flags = vdec_core_lock(vdec_core); + + list_add_tail(&vdec->list, &vdec_core->connected_vdec_list); + + vdec_core_unlock(vdec_core, flags); + + up(&vdec_core->sem); + + return 0; +} +EXPORT_SYMBOL(vdec_connect); + +/* remove vdec from vdec_core scheduling */ +int vdec_disconnect(struct vdec_s *vdec) +{ + if ((vdec->status != VDEC_STATUS_CONNECTED) && + (vdec->status != VDEC_STATUS_ACTIVE)) { + return 0; + } + + /* + *when a vdec is under the management of scheduler + * the status change will only be from vdec_core_thread + */ + vdec_set_next_status(vdec, VDEC_STATUS_DISCONNECTED); + + up(&vdec_core->sem); + + wait_for_completion(&vdec->inactive_done); + + return 0; +} +EXPORT_SYMBOL(vdec_disconnect); + +/* release vdec structure */ +int vdec_destroy(struct vdec_s *vdec) +{ + vdec_input_release(&vdec->input); + + kfree(vdec); + + atomic_dec(&vdec_core->vdec_nr); + + return 0; +} +EXPORT_SYMBOL(vdec_destroy); + +/* + * Only support time sliced decoding for frame based input, + * so legacy decoder can exist with time sliced decoder. + */ +static const char *get_dev_name(bool use_legacy_vdec, int format) +{ +#ifdef CONFIG_MULTI_DEC + if (use_legacy_vdec) + return vdec_device_name[format * 2]; + else + return vdec_device_name[format * 2 + 1]; +#else + return vdec_device_name[format]; +#endif +} + +/* +*register vdec_device + * create output, vfm or create ionvideo output + */ +s32 vdec_init(struct vdec_s *vdec, int is_4k) +{ + int r = 0; + struct vdec_s *p = vdec; + int retry_num = 0; + int more_buffers = 0; + const char *dev_name; + + if (is_4k && vdec->format < VFORMAT_H264) { + /* + *old decoder don't support 4k + * but size is bigger; + * clear 4k flag, and used more buffers; + */ + more_buffers = 1; + is_4k = 0; + } + + dev_name = get_dev_name((vdec->type == VDEC_TYPE_SINGLE), + vdec->format); + + if (dev_name == NULL) + return -ENODEV; + + pr_info("vdec_init, dev_name:%s, vdec_type=%s\n", + dev_name, vdec_type_str(vdec)); + + /* + *todo: VFM patch control should be configurable, + * for now all stream based input uses default VFM path. + */ + if (vdec_stream_based(vdec)) { + if (vdec_core->vfm_vdec == NULL) { + pr_info("vdec_init set vfm decoder %p\n", vdec); + vdec_core->vfm_vdec = vdec; + } else { + pr_info("vdec_init vfm path busy.\n"); + return -EBUSY; + } + } + + if (vdec->format == VFORMAT_H264_4K2K || + (vdec->format == VFORMAT_HEVC && is_4k)) { + //try_free_keep_video(0);/*mask*/ + } + + /* + *when blackout_policy was set, vdec would not free cma buffer, if + * current vformat require larger buffer size than current + * buf size, reallocated it + */ + if (vdec_core->mem_start != vdec_core->mem_end && + vdec_core->mem_end - vdec_core->mem_start + 1 < + vdec_default_buf_size[vdec->format] * SZ_1M) { +#ifdef CONFIG_MULTI_DEC + pr_info("current vdec size %ld, vformat %d need size %d\n", + vdec_core->mem_end - vdec_core->mem_start, + vdec->format, + vdec_default_buf_size[vdec->format * 2] * SZ_1M); +#else + pr_info("current vdec size %ld, vformat %d need size %d\n", + vdec_core->mem_end - vdec_core->mem_start, + vdec->format, + vdec_default_buf_size[vdec->format] * SZ_1M); +#endif + //try_free_keep_video(0);/*mask*/ + vdec_free_cmabuf(); + } + + mutex_lock(&vdec_mutex); + inited_vcodec_num++; + mutex_unlock(&vdec_mutex); + + vdec_input_set_type(&vdec->input, vdec->type, + (vdec->format == VFORMAT_HEVC || + vdec->format == VFORMAT_VP9) ? + VDEC_INPUT_TARGET_HEVC : + VDEC_INPUT_TARGET_VLD); + + p->cma_dev = vdec_core->cma_dev; + p->get_canvas = get_canvas; + /* todo */ + p->use_vfm_path = vdec_stream_based(vdec); + + if (p->use_vfm_path) { + pr_info("vdec_dev_reg.mem[0x%lx -- 0x%lx]\n", + vdec_core->mem_start, + vdec_core->mem_end); + p->mem_start = vdec_core->mem_start; + p->mem_end = vdec_core->mem_end; + } + + /* allocate base memory for decoder instance */ + while (p->mem_start == p->mem_end) { + int alloc_size; + +#ifdef CONFIG_MULTI_DEC + if (p->use_vfm_path) + alloc_size = + vdec_default_buf_size[vdec->format * 2] + * SZ_1M; + else + alloc_size = + vdec_default_buf_size[vdec->format * 2 + 1] + * SZ_1M; +#else + alloc_size = vdec_default_buf_size[vdec->format] * SZ_1M; +#endif + if (alloc_size == 0) + break;/*alloc end*/ + if (is_4k) { + /*used 264 4k's setting for 265.*/ +#ifdef CONFIG_MULTI_DEC + int m4k_size = + vdec_default_buf_size[VFORMAT_H264_4K2K * 2] * + SZ_1M; +#else + int m4k_size = + vdec_default_buf_size[VFORMAT_H264_4K2K] * + SZ_1M; +#endif + if (get_cpu_type() >= MESON_CPU_MAJOR_ID_GXTVBB) + m4k_size = 32 * SZ_1M; + if ((m4k_size > 0) && (m4k_size < 200 * SZ_1M)) + alloc_size = m4k_size; + +#ifdef VP9_10B_MMU + if ((vdec->format == VFORMAT_VP9) && + (get_cpu_type() >= MESON_CPU_MAJOR_ID_GXL)) { +#ifdef CONFIG_MULTI_DEC + if (p->use_vfm_path) + alloc_size = + vdec_default_buf_size[VFORMAT_VP9 * 2] + * SZ_1M; + else + alloc_size = + vdec_default_buf_size[VFORMAT_VP9 + * 2 + 1] * SZ_1M; + +#else + alloc_size = + vdec_default_buf_size[VFORMAT_VP9] * SZ_1M; +#endif + } +#endif + } else if (more_buffers) { + alloc_size = alloc_size + 16 * SZ_1M; + } + + if ((vdec->format == VFORMAT_HEVC) + && get_mmu_mode() + && (get_cpu_type() >= MESON_CPU_MAJOR_ID_GXL)) { +#ifdef CONFIG_MULTI_DEC + if (p->use_vfm_path) + alloc_size = 33 * SZ_1M; + else + alloc_size = 33 * SZ_1M; +#else + alloc_size = 33 * SZ_1M; +#endif + } + + p->mem_start = codec_mm_alloc_for_dma(MEM_NAME, + alloc_size / PAGE_SIZE, 4 + PAGE_SHIFT, + CODEC_MM_FLAGS_CMA_CLEAR | CODEC_MM_FLAGS_CPU | + CODEC_MM_FLAGS_FOR_VDECODER); + if (!p->mem_start) { + if (retry_num < 1) { + pr_err("vdec base CMA allocation failed,try again\\n"); + retry_num++; + //try_free_keep_video(0);/*mask*/ + continue;/*retry alloc*/ + } + pr_err("vdec base CMA allocation failed.\n"); + + mutex_lock(&vdec_mutex); + inited_vcodec_num--; + mutex_unlock(&vdec_mutex); + + return -ENOMEM; + } + + p->mem_end = p->mem_start + alloc_size - 1; + pr_info("vdec base memory alloced [%p -- %p]\n", + (void *)p->mem_start, + (void *)p->mem_end); + + break;/*alloc end*/ + } + + if (p->use_vfm_path) { + vdec_core->mem_start = p->mem_start; + vdec_core->mem_end = p->mem_end; + vdec_mem_alloced_from_codec = 1; + } + +/*alloc end:*/ + /* vdec_dev_reg.flag = 0; */ + + p->dev = + platform_device_register_data( + &vdec_core->vdec_core_platform_device->dev, + dev_name, + PLATFORM_DEVID_AUTO, + &p, sizeof(struct vdec_s *)); + + if (IS_ERR(p->dev)) { + r = PTR_ERR(p->dev); + pr_err("vdec: Decoder device %s register failed (%d)\n", + dev_name, r); + + mutex_lock(&vdec_mutex); + inited_vcodec_num--; + mutex_unlock(&vdec_mutex); + + goto error; + } + + if ((p->type == VDEC_TYPE_FRAME_BLOCK) && (p->run == NULL)) { + r = -ENODEV; + pr_err("vdec: Decoder device not handled (%s)\n", dev_name); + + mutex_lock(&vdec_mutex); + inited_vcodec_num--; + mutex_unlock(&vdec_mutex); + + goto error; + } +#if 0 + if (p->use_vfm_path) { + vdec->vf_receiver_inst = -1; + } else { + /* + *create IONVIDEO instance and connect decoder's + * vf_provider interface to it + */ + if (p->type != VDEC_TYPE_FRAME_BLOCK) { + r = -ENODEV; + pr_err("vdec: Incorrect decoder type\n"); + + mutex_lock(&vdec_mutex); + inited_vcodec_num--; + mutex_unlock(&vdec_mutex); + + goto error; + } +#if 1 + r = ionvideo_alloc_map(&vdec->vf_receiver_name, + &vdec->vf_receiver_inst); +#else + /* + * temporarily just use decoder instance ID as iondriver ID + * to solve OMX iondriver instance number check time sequence + * only the limitation is we can NOT mix different video + * decoders since same ID will be used for different decoder + * formats. + */ + vdec->vf_receiver_inst = p->dev->id; + r = ionvideo_assign_map(&vdec->vf_receiver_name, + &vdec->vf_receiver_inst); +#endif + if (r < 0) { + pr_err("IonVideo frame receiver allocation failed.\n"); + + mutex_lock(&vdec_mutex); + inited_vcodec_num--; + mutex_unlock(&vdec_mutex); + + goto error; + } + + snprintf(vdec->vfm_map_chain, VDEC_MAP_NAME_SIZE, + "%s %s", vdec->vf_provider_name, + vdec->vf_receiver_name); + snprintf(vdec->vfm_map_id, VDEC_MAP_NAME_SIZE, + "%s-%s", vdec->vf_provider_name, + vdec->vf_receiver_name); + + if (vfm_map_add(vdec->vfm_map_id, vdec->vfm_map_chain) < 0) { + r = -ENOMEM; + pr_err("Decoder pipeline map creation failed %s.\n", + vdec->vfm_map_id); + vdec->vfm_map_id[0] = 0; + + mutex_lock(&vdec_mutex); + inited_vcodec_num--; + mutex_unlock(&vdec_mutex); + + goto error; + } + + pr_info("vfm map %s created\n", vdec->vfm_map_id); + + /* + *assume IONVIDEO driver already have a few vframe_receiver + * registered. + * 1. Call iondriver function to allocate a IONVIDEO path and + * provide receiver's name and receiver op. + * 2. Get decoder driver's provider name from driver instance + * 3. vfm_map_add(name, "<decoder provider name> + * <iondriver receiver name>"), e.g. + * vfm_map_add("vdec_ion_map_0", "mpeg4_0 iondriver_1"); + * 4. vf_reg_provider and vf_reg_receiver + * Note: the decoder provider's op uses vdec as op_arg + * the iondriver receiver's op uses iondev device as + * op_arg + */ + + } + +#endif/*mask*/ + if (vdec->type != VDEC_TYPE_SINGLE) { + vf_reg_provider(&p->vframe_provider); + + vf_notify_receiver(p->vf_provider_name, + VFRAME_EVENT_PROVIDER_START, + vdec); + } + + pr_info("vdec_init, vf_provider_name = %s\n", p->vf_provider_name); + + /* vdec is now ready to be active */ + vdec_set_status(vdec, VDEC_STATUS_DISCONNECTED); + + return 0; + +error: + return r; +} +EXPORT_SYMBOL(vdec_init); + +void vdec_release(struct vdec_s *vdec) +{ + vdec_disconnect(vdec); + + if (vdec->vframe_provider.name) + vf_unreg_provider(&vdec->vframe_provider); + + if (vdec_core->vfm_vdec == vdec) + vdec_core->vfm_vdec = NULL; +#if 0 + if (vdec->vf_receiver_inst >= 0) { + if (vdec->vfm_map_id[0]) { + vfm_map_remove(vdec->vfm_map_id); + vdec->vfm_map_id[0] = 0; + } + + /* + *vf_receiver_inst should be > 0 since 0 is + * for either un-initialized vdec or a ionvideo + * instance reserved for legacy path. + */ + ionvideo_release_map(vdec->vf_receiver_inst); + } +#endif/*mask*/ + platform_device_unregister(vdec->dev); + + if (!vdec->use_vfm_path) { + if (vdec->mem_start) { + codec_mm_free_for_dma(MEM_NAME, vdec->mem_start); + vdec->mem_start = 0; + vdec->mem_end = 0; + } + } else if (delay_release-- <= 0 && + !keep_vdec_mem && + vdec_mem_alloced_from_codec && + vdec_core->mem_start /*&& + get_blackout_policy()*//*mask*/) { + codec_mm_free_for_dma(MEM_NAME, vdec_core->mem_start); + vdec_cma_page = NULL; + vdec_core->mem_start = reserved_mem_start; + vdec_core->mem_end = reserved_mem_end; + } + + vdec_destroy(vdec); + + mutex_lock(&vdec_mutex); + inited_vcodec_num--; + mutex_unlock(&vdec_mutex); +} +EXPORT_SYMBOL(vdec_release); + +int vdec_reset(struct vdec_s *vdec) +{ + vdec_disconnect(vdec); + + if (vdec->vframe_provider.name) + vf_unreg_provider(&vdec->vframe_provider); + + if (vdec->reset) + vdec->reset(vdec); + + vdec_input_release(&vdec->input); + + vf_reg_provider(&vdec->vframe_provider); + vf_notify_receiver(vdec->vf_provider_name, + VFRAME_EVENT_PROVIDER_START, vdec); + + vdec_connect(vdec); + + return 0; +} +EXPORT_SYMBOL(vdec_reset); + +void vdec_free_cmabuf(void) +{ + mutex_lock(&vdec_mutex); + + if (inited_vcodec_num > 0) { + mutex_unlock(&vdec_mutex); + return; + } + + if (vdec_mem_alloced_from_codec && vdec_core->mem_start) { + codec_mm_free_for_dma(MEM_NAME, vdec_core->mem_start); + vdec_cma_page = NULL; + vdec_core->mem_start = reserved_mem_start; + vdec_core->mem_end = reserved_mem_end; + pr_info("force free vdec memory\n"); + } + + mutex_unlock(&vdec_mutex); +} +EXPORT_SYMBOL(vdec_free_cmabuf); + +static struct vdec_s *active_vdec(struct vdec_core_s *core) +{ + struct vdec_s *vdec; + struct list_head *p; + + list_for_each(p, &core->connected_vdec_list) { + vdec = list_entry(p, struct vdec_s, list); + if (vdec->status == VDEC_STATUS_ACTIVE) + return vdec; + } + + return NULL; +} + +/* +*Decoder callback + * Each decoder instance uses this callback to notify status change, e.g. when + * decoder finished using HW resource. + * a sample callback from decoder's driver is following: + * + * if (hw->vdec_cb) { + * vdec_set_next_status(vdec, VDEC_STATUS_CONNECTED); + * hw->vdec_cb(vdec, hw->vdec_cb_arg); + * } + */ +static void vdec_callback(struct vdec_s *vdec, void *data) +{ + struct vdec_core_s *core = (struct vdec_core_s *)data; + + up(&core->sem); +} + +static irqreturn_t vdec_isr(int irq, void *dev_id) +{ + struct vdec_isr_context_s *c = + (struct vdec_isr_context_s *)dev_id; + struct vdec_s *vdec = vdec_core->active_vdec; + + if (c->dev_isr) + return c->dev_isr(irq, c->dev_id); + + if (c != &vdec_core->isr_context[VDEC_IRQ_1]) { +#if 0 + pr_warn("vdec interrupt w/o a valid receiver\n"); +#endif + return IRQ_HANDLED; + } + + if (!vdec) { +#if 0 + pr_warn("vdec interrupt w/o an active instance running. core = %p\n", + core); +#endif + return IRQ_HANDLED; + } + + if (!vdec->irq_handler) { +#if 0 + pr_warn("vdec instance has no irq handle.\n"); +#endif + return IRQ_HANDLED; + } + + return vdec->irq_handler(vdec); +} + +static irqreturn_t vdec_thread_isr(int irq, void *dev_id) +{ + struct vdec_isr_context_s *c = + (struct vdec_isr_context_s *)dev_id; + struct vdec_s *vdec = vdec_core->active_vdec; + + if (c->dev_threaded_isr) + return c->dev_threaded_isr(irq, c->dev_id); + + if (!vdec) + return IRQ_HANDLED; + + if (!vdec->threaded_irq_handler) + return IRQ_HANDLED; + + return vdec->threaded_irq_handler(vdec); +} + +static inline bool vdec_ready_to_run(struct vdec_s *vdec) +{ + if (vdec->status != VDEC_STATUS_CONNECTED) + return false; + + if (!vdec->run_ready) + return false; + + return vdec->run_ready(vdec); +} + +/* +*struct vdec_core_shread manages all decoder instance in active list. When + * a vdec is added into the active list, it can onlt be in two status: + * VDEC_STATUS_CONNECTED(the decoder does not own HW resource and ready to run) + * VDEC_STATUS_ACTIVE(the decoder owns HW resources and is running). + * Removing a decoder from active list is only performed within core thread. + * Adding a decoder into active list is performed from user thread. + */ +static int vdec_core_thread(void *data) +{ + unsigned long flags; + struct vdec_core_s *core = (struct vdec_core_s *)data; + + struct sched_param param = {.sched_priority = MAX_RT_PRIO - 1}; + + sched_setscheduler(current, SCHED_FIFO, ¶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; + vdec->run(vdec, vdec_callback, core); + } + + /* remove disconnected decoder from active list */ + list_for_each_entry_safe(vdec, tmp, &disconnecting_list, list) { + list_del(&vdec->list); + vdec_set_status(vdec, VDEC_STATUS_DISCONNECTED); + complete(&vdec->inactive_done); + } + + if (!core->active_vdec) { + msleep(20); + up(&core->sem); + } + } + + return 0; +} + +#if 1 /* MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON8 */ +static bool test_hevc(u32 decomp_addr, u32 us_delay) +{ + int i; + + /* SW_RESET IPP */ + WRITE_VREG(HEVCD_IPP_TOP_CNTL, 1); + WRITE_VREG(HEVCD_IPP_TOP_CNTL, 0); + + /* initialize all canvas table */ + WRITE_VREG(HEVCD_MPP_ANC2AXI_TBL_CONF_ADDR, 0); + for (i = 0; i < 32; i++) + WRITE_VREG(HEVCD_MPP_ANC2AXI_TBL_CMD_ADDR, + 0x1 | (i << 8) | decomp_addr); + WRITE_VREG(HEVCD_MPP_ANC2AXI_TBL_CONF_ADDR, 1); + WRITE_VREG(HEVCD_MPP_ANC_CANVAS_ACCCONFIG_ADDR, (0 << 8) | (0<<1) | 1); + for (i = 0; i < 32; i++) + WRITE_VREG(HEVCD_MPP_ANC_CANVAS_DATA_ADDR, 0); + + /* Initialize mcrcc */ + WRITE_VREG(HEVCD_MCRCC_CTL1, 0x2); + WRITE_VREG(HEVCD_MCRCC_CTL2, 0x0); + WRITE_VREG(HEVCD_MCRCC_CTL3, 0x0); + WRITE_VREG(HEVCD_MCRCC_CTL1, 0xff0); + + /* Decomp initialize */ + WRITE_VREG(HEVCD_MPP_DECOMP_CTL1, 0x0); + WRITE_VREG(HEVCD_MPP_DECOMP_CTL2, 0x0); + + /* Frame level initialization */ + WRITE_VREG(HEVCD_IPP_TOP_FRMCONFIG, 0x100 | (0x100 << 16)); + WRITE_VREG(HEVCD_IPP_TOP_TILECONFIG3, 0x0); + WRITE_VREG(HEVCD_IPP_TOP_LCUCONFIG, 0x1 << 5); + WRITE_VREG(HEVCD_IPP_BITDEPTH_CONFIG, 0x2 | (0x2 << 2)); + + WRITE_VREG(HEVCD_IPP_CONFIG, 0x0); + WRITE_VREG(HEVCD_IPP_LINEBUFF_BASE, 0x0); + + /* Enable SWIMP mode */ + WRITE_VREG(HEVCD_IPP_SWMPREDIF_CONFIG, 0x1); + + /* Enable frame */ + WRITE_VREG(HEVCD_IPP_TOP_CNTL, 0x2); + WRITE_VREG(HEVCD_IPP_TOP_FRMCTL, 0x1); + + /* Send SW-command CTB info */ + WRITE_VREG(HEVCD_IPP_SWMPREDIF_CTBINFO, 0x1 << 31); + + /* Send PU_command */ + WRITE_VREG(HEVCD_IPP_SWMPREDIF_PUINFO0, (0x4 << 9) | (0x4 << 16)); + WRITE_VREG(HEVCD_IPP_SWMPREDIF_PUINFO1, 0x1 << 3); + WRITE_VREG(HEVCD_IPP_SWMPREDIF_PUINFO2, 0x0); + WRITE_VREG(HEVCD_IPP_SWMPREDIF_PUINFO3, 0x0); + + udelay(us_delay); + + WRITE_VREG(HEVCD_IPP_DBG_SEL, 0x2 << 4); + + return (READ_VREG(HEVCD_IPP_DBG_DATA) & 3) == 1; +} + +void vdec_poweron(enum vdec_type_e core) +{ + void *decomp_addr = NULL; + dma_addr_t decomp_dma_addr; + u32 decomp_addr_aligned = 0; + int hevc_loop = 0; + + mutex_lock(&vdec_mutex); + + if (vdec_on(core)) { + mutex_unlock(&vdec_mutex); + return; + } + + if (hevc_workaround_needed() && + (core == VDEC_HEVC)) { + decomp_addr = codec_mm_dma_alloc_coherent(MEM_NAME, + SZ_64K + SZ_4K, &decomp_dma_addr, GFP_KERNEL, 0); + + if (decomp_addr) { + decomp_addr_aligned = ALIGN(decomp_dma_addr, SZ_64K); + memset((u8 *)decomp_addr + + (decomp_addr_aligned - decomp_dma_addr), + 0xff, SZ_4K); + } else + pr_err("vdec: alloc HEVC gxbb decomp buffer failed.\n"); + } + + if (core == VDEC_1) { + /* vdec1 power on */ + WRITE_AOREG(AO_RTI_GEN_PWR_SLEEP0, + READ_AOREG(AO_RTI_GEN_PWR_SLEEP0) & ~0xc); + /* wait 10uS */ + udelay(10); + /* vdec1 soft reset */ + WRITE_VREG(DOS_SW_RESET0, 0xfffffffc); + WRITE_VREG(DOS_SW_RESET0, 0); + /* enable vdec1 clock */ + /* + *add power on vdec clock level setting,only for m8 chip, + * m8baby and m8m2 can dynamic adjust vdec clock, + * power on with default clock level + */ + vdec_clock_hi_enable(); + /* power up vdec memories */ + WRITE_VREG(DOS_MEM_PD_VDEC, 0); + /* remove vdec1 isolation */ + WRITE_AOREG(AO_RTI_GEN_PWR_ISO0, + READ_AOREG(AO_RTI_GEN_PWR_ISO0) & ~0xC0); + /* reset DOS top registers */ + WRITE_VREG(DOS_VDEC_MCRCC_STALL_CTRL, 0); + if (get_cpu_type() >= + MESON_CPU_MAJOR_ID_GXBB) { + /* + *enable VDEC_1 DMC request + */ + unsigned long flags; + + spin_lock_irqsave(&vdec_spin_lock, flags); + codec_dmcbus_write(DMC_REQ_CTRL, + codec_dmcbus_read(DMC_REQ_CTRL) | (1 << 13)); + spin_unlock_irqrestore(&vdec_spin_lock, flags); + } + } else if (core == VDEC_2) { + if (has_vdec2()) { + /* vdec2 power on */ + WRITE_AOREG(AO_RTI_GEN_PWR_SLEEP0, + READ_AOREG(AO_RTI_GEN_PWR_SLEEP0) & + ~0x30); + /* wait 10uS */ + udelay(10); + /* vdec2 soft reset */ + WRITE_VREG(DOS_SW_RESET2, 0xffffffff); + WRITE_VREG(DOS_SW_RESET2, 0); + /* enable vdec1 clock */ + vdec2_clock_hi_enable(); + /* power up vdec memories */ + WRITE_VREG(DOS_MEM_PD_VDEC2, 0); + /* remove vdec2 isolation */ + WRITE_AOREG(AO_RTI_GEN_PWR_ISO0, + READ_AOREG(AO_RTI_GEN_PWR_ISO0) & + ~0x300); + /* reset DOS top registers */ + WRITE_VREG(DOS_VDEC2_MCRCC_STALL_CTRL, 0); + } + } else if (core == VDEC_HCODEC) { + if (has_hdec()) { + /* hcodec power on */ + WRITE_AOREG(AO_RTI_GEN_PWR_SLEEP0, + READ_AOREG(AO_RTI_GEN_PWR_SLEEP0) & + ~0x3); + /* wait 10uS */ + udelay(10); + /* hcodec soft reset */ + WRITE_VREG(DOS_SW_RESET1, 0xffffffff); + WRITE_VREG(DOS_SW_RESET1, 0); + /* enable hcodec clock */ + hcodec_clock_enable(); + /* power up hcodec memories */ + WRITE_VREG(DOS_MEM_PD_HCODEC, 0); + /* remove hcodec isolation */ + WRITE_AOREG(AO_RTI_GEN_PWR_ISO0, + READ_AOREG(AO_RTI_GEN_PWR_ISO0) & + ~0x30); + } + } else if (core == VDEC_HEVC) { + if (has_hevc_vdec()) { + bool hevc_fixed = false; + + while (!hevc_fixed) { + /* hevc power on */ + WRITE_AOREG(AO_RTI_GEN_PWR_SLEEP0, + READ_AOREG(AO_RTI_GEN_PWR_SLEEP0) & + ~0xc0); + /* wait 10uS */ + udelay(10); + /* hevc soft reset */ + WRITE_VREG(DOS_SW_RESET3, 0xffffffff); + WRITE_VREG(DOS_SW_RESET3, 0); + /* enable hevc clock */ + hevc_clock_hi_enable(); + /* power up hevc memories */ + WRITE_VREG(DOS_MEM_PD_HEVC, 0); + /* remove hevc isolation */ + WRITE_AOREG(AO_RTI_GEN_PWR_ISO0, + READ_AOREG(AO_RTI_GEN_PWR_ISO0) & + ~0xc00); + + if (!hevc_workaround_needed()) + break; + + if (decomp_addr) + hevc_fixed = test_hevc( + decomp_addr_aligned, 20); + + if (!hevc_fixed) { + hevc_loop++; + + mutex_unlock(&vdec_mutex); + + if (hevc_loop >= HEVC_TEST_LIMIT) { + pr_warn("hevc power sequence over limit\n"); + pr_warn("=====================================================\n"); + pr_warn(" This chip is identified to have HW failure.\n"); + pr_warn(" Please contact sqa-platform to replace the platform.\n"); + pr_warn("=====================================================\n"); + + panic("Force panic for chip detection !!!\n"); + + break; + } + + vdec_poweroff(VDEC_HEVC); + + mdelay(10); + + mutex_lock(&vdec_mutex); + } + } + + if (hevc_loop > hevc_max_reset_count) + hevc_max_reset_count = hevc_loop; + + WRITE_VREG(DOS_SW_RESET3, 0xffffffff); + udelay(10); + WRITE_VREG(DOS_SW_RESET3, 0); + } + } + + if (decomp_addr) + codec_mm_dma_free_coherent(MEM_NAME, + SZ_64K + SZ_4K, decomp_addr, decomp_dma_addr, 0); + + mutex_unlock(&vdec_mutex); +} +EXPORT_SYMBOL(vdec_poweron); + +void vdec_poweroff(enum vdec_type_e core) +{ + mutex_lock(&vdec_mutex); + if (core == VDEC_1) { + if (get_cpu_type() >= + MESON_CPU_MAJOR_ID_GXBB) { + /* disable VDEC_1 DMC REQ*/ + unsigned long flags; + + spin_lock_irqsave(&vdec_spin_lock, flags); + codec_dmcbus_write(DMC_REQ_CTRL, + codec_dmcbus_read(DMC_REQ_CTRL) & (~(1 << 13))); + spin_unlock_irqrestore(&vdec_spin_lock, flags); + udelay(10); + } + /* enable vdec1 isolation */ + WRITE_AOREG(AO_RTI_GEN_PWR_ISO0, + READ_AOREG(AO_RTI_GEN_PWR_ISO0) | 0xc0); + /* power off vdec1 memories */ + WRITE_VREG(DOS_MEM_PD_VDEC, 0xffffffffUL); + /* disable vdec1 clock */ + vdec_clock_off(); + /* vdec1 power off */ + WRITE_AOREG(AO_RTI_GEN_PWR_SLEEP0, + READ_AOREG(AO_RTI_GEN_PWR_SLEEP0) | 0xc); + } else if (core == VDEC_2) { + if (has_vdec2()) { + /* enable vdec2 isolation */ + WRITE_AOREG(AO_RTI_GEN_PWR_ISO0, + READ_AOREG(AO_RTI_GEN_PWR_ISO0) | + 0x300); + /* power off vdec2 memories */ + WRITE_VREG(DOS_MEM_PD_VDEC2, 0xffffffffUL); + /* disable vdec2 clock */ + vdec2_clock_off(); + /* vdec2 power off */ + WRITE_AOREG(AO_RTI_GEN_PWR_SLEEP0, + READ_AOREG(AO_RTI_GEN_PWR_SLEEP0) | + 0x30); + } + } else if (core == VDEC_HCODEC) { + if (has_hdec()) { + /* enable hcodec isolation */ + WRITE_AOREG(AO_RTI_GEN_PWR_ISO0, + READ_AOREG(AO_RTI_GEN_PWR_ISO0) | + 0x30); + /* power off hcodec memories */ + WRITE_VREG(DOS_MEM_PD_HCODEC, 0xffffffffUL); + /* disable hcodec clock */ + hcodec_clock_off(); + /* hcodec power off */ + WRITE_AOREG(AO_RTI_GEN_PWR_SLEEP0, + READ_AOREG(AO_RTI_GEN_PWR_SLEEP0) | 3); + } + } else if (core == VDEC_HEVC) { + if (has_hevc_vdec()) { + /* enable hevc isolation */ + WRITE_AOREG(AO_RTI_GEN_PWR_ISO0, + READ_AOREG(AO_RTI_GEN_PWR_ISO0) | + 0xc00); + /* power off hevc memories */ + WRITE_VREG(DOS_MEM_PD_HEVC, 0xffffffffUL); + /* disable hevc clock */ + hevc_clock_off(); + /* hevc power off */ + WRITE_AOREG(AO_RTI_GEN_PWR_SLEEP0, + READ_AOREG(AO_RTI_GEN_PWR_SLEEP0) | + 0xc0); + } + } + mutex_unlock(&vdec_mutex); +} +EXPORT_SYMBOL(vdec_poweroff); + +bool vdec_on(enum vdec_type_e core) +{ + bool ret = false; + + if (core == VDEC_1) { + if (((READ_AOREG(AO_RTI_GEN_PWR_SLEEP0) & 0xc) == 0) && + (READ_HHI_REG(HHI_VDEC_CLK_CNTL) & 0x100)) + ret = true; + } else if (core == VDEC_2) { + if (has_vdec2()) { + if (((READ_AOREG(AO_RTI_GEN_PWR_SLEEP0) & 0x30) == 0) && + (READ_HHI_REG(HHI_VDEC2_CLK_CNTL) & 0x100)) + ret = true; + } + } else if (core == VDEC_HCODEC) { + if (has_hdec()) { + if (((READ_AOREG(AO_RTI_GEN_PWR_SLEEP0) & 0x3) == 0) && + (READ_HHI_REG(HHI_VDEC_CLK_CNTL) & 0x1000000)) + ret = true; + } + } else if (core == VDEC_HEVC) { + if (has_hevc_vdec()) { + if (((READ_AOREG(AO_RTI_GEN_PWR_SLEEP0) & 0xc0) == 0) && + (READ_HHI_REG(HHI_VDEC2_CLK_CNTL) & 0x1000000)) + ret = true; + } + } + + return ret; +} +EXPORT_SYMBOL(vdec_on); + +#elif 0 /* MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON6TVD */ +void vdec_poweron(enum vdec_type_e core) +{ + ulong flags; + + spin_lock_irqsave(&lock, flags); + + if (core == VDEC_1) { + /* vdec1 soft reset */ + WRITE_VREG(DOS_SW_RESET0, 0xfffffffc); + WRITE_VREG(DOS_SW_RESET0, 0); + /* enable vdec1 clock */ + vdec_clock_enable(); + /* reset DOS top registers */ + WRITE_VREG(DOS_VDEC_MCRCC_STALL_CTRL, 0); + } else if (core == VDEC_2) { + /* vdec2 soft reset */ + WRITE_VREG(DOS_SW_RESET2, 0xffffffff); + WRITE_VREG(DOS_SW_RESET2, 0); + /* enable vdec2 clock */ + vdec2_clock_enable(); + /* reset DOS top registers */ + WRITE_VREG(DOS_VDEC2_MCRCC_STALL_CTRL, 0); + } else if (core == VDEC_HCODEC) { + /* hcodec soft reset */ + WRITE_VREG(DOS_SW_RESET1, 0xffffffff); + WRITE_VREG(DOS_SW_RESET1, 0); + /* enable hcodec clock */ + hcodec_clock_enable(); + } + + spin_unlock_irqrestore(&lock, flags); +} + +void vdec_poweroff(enum vdec_type_e core) +{ + ulong flags; + + spin_lock_irqsave(&lock, flags); + + if (core == VDEC_1) { + /* disable vdec1 clock */ + vdec_clock_off(); + } else if (core == VDEC_2) { + /* disable vdec2 clock */ + vdec2_clock_off(); + } else if (core == VDEC_HCODEC) { + /* disable hcodec clock */ + hcodec_clock_off(); + } + + spin_unlock_irqrestore(&lock, flags); +} + +bool vdec_on(enum vdec_type_e core) +{ + bool ret = false; + + if (core == VDEC_1) { + if (READ_HHI_REG(HHI_VDEC_CLK_CNTL) & 0x100) + ret = true; + } else if (core == VDEC_2) { + if (READ_HHI_REG(HHI_VDEC2_CLK_CNTL) & 0x100) + ret = true; + } else if (core == VDEC_HCODEC) { + if (READ_HHI_REG(HHI_VDEC_CLK_CNTL) & 0x1000000) + ret = true; + } + + return ret; +} +#endif + +int vdec_source_changed(int format, int width, int height, int fps) +{ + /* todo: add level routines for clock adjustment per chips */ + int ret = -1; + static int on_setting; + + if (on_setting > 0) + return ret;/*on changing clk,ignore this change*/ + + if (vdec_source_get(VDEC_1) == width * height * fps) + return ret; + + + on_setting = 1; + ret = vdec_source_changed_for_clk_set(format, width, height, fps); + pr_info("vdec1 video changed to %d x %d %d fps clk->%dMHZ\n", + width, height, fps, vdec_clk_get(VDEC_1)); + on_setting = 0; + return ret; + +} +EXPORT_SYMBOL(vdec_source_changed); + +int vdec2_source_changed(int format, int width, int height, int fps) +{ + int ret = -1; + static int on_setting; + + if (has_vdec2()) { + /* todo: add level routines for clock adjustment per chips */ + if (on_setting != 0) + return ret;/*on changing clk,ignore this change*/ + + if (vdec_source_get(VDEC_2) == width * height * fps) + return ret; + + on_setting = 1; + ret = vdec_source_changed_for_clk_set(format, + width, height, fps); + pr_info("vdec2 video changed to %d x %d %d fps clk->%dMHZ\n", + width, height, fps, vdec_clk_get(VDEC_2)); + on_setting = 0; + return ret; + } + return 0; +} +EXPORT_SYMBOL(vdec2_source_changed); + +int hevc_source_changed(int format, int width, int height, int fps) +{ + /* todo: add level routines for clock adjustment per chips */ + int ret = -1; + static int on_setting; + + if (on_setting != 0) + return ret;/*on changing clk,ignore this change*/ + + if (vdec_source_get(VDEC_HEVC) == width * height * fps) + return ret; + + on_setting = 1; + ret = vdec_source_changed_for_clk_set(format, width, height, fps); + pr_info("hevc video changed to %d x %d %d fps clk->%dMHZ\n", + width, height, fps, vdec_clk_get(VDEC_HEVC)); + on_setting = 0; + + return ret; +} +EXPORT_SYMBOL(hevc_source_changed); + +static enum vdec2_usage_e vdec2_usage = USAGE_NONE; +void set_vdec2_usage(enum vdec2_usage_e usage) +{ + if (has_vdec2()) { + mutex_lock(&vdec_mutex); + vdec2_usage = usage; + mutex_unlock(&vdec_mutex); + } +} +EXPORT_SYMBOL(set_vdec2_usage); + +enum vdec2_usage_e get_vdec2_usage(void) +{ + if (has_vdec2()) + return vdec2_usage; + else + return 0; +} +EXPORT_SYMBOL(get_vdec2_usage); + +static struct am_reg am_risc[] = { + {"MSP", 0x300}, + {"MPSR", 0x301}, + {"MCPU_INT_BASE", 0x302}, + {"MCPU_INTR_GRP", 0x303}, + {"MCPU_INTR_MSK", 0x304}, + {"MCPU_INTR_REQ", 0x305}, + {"MPC-P", 0x306}, + {"MPC-D", 0x307}, + {"MPC_E", 0x308}, + {"MPC_W", 0x309}, + {"CSP", 0x320}, + {"CPSR", 0x321}, + {"CCPU_INT_BASE", 0x322}, + {"CCPU_INTR_GRP", 0x323}, + {"CCPU_INTR_MSK", 0x324}, + {"CCPU_INTR_REQ", 0x325}, + {"CPC-P", 0x326}, + {"CPC-D", 0x327}, + {"CPC_E", 0x328}, + {"CPC_W", 0x329}, + {"AV_SCRATCH_0", 0x09c0}, + {"AV_SCRATCH_1", 0x09c1}, + {"AV_SCRATCH_2", 0x09c2}, + {"AV_SCRATCH_3", 0x09c3}, + {"AV_SCRATCH_4", 0x09c4}, + {"AV_SCRATCH_5", 0x09c5}, + {"AV_SCRATCH_6", 0x09c6}, + {"AV_SCRATCH_7", 0x09c7}, + {"AV_SCRATCH_8", 0x09c8}, + {"AV_SCRATCH_9", 0x09c9}, + {"AV_SCRATCH_A", 0x09ca}, + {"AV_SCRATCH_B", 0x09cb}, + {"AV_SCRATCH_C", 0x09cc}, + {"AV_SCRATCH_D", 0x09cd}, + {"AV_SCRATCH_E", 0x09ce}, + {"AV_SCRATCH_F", 0x09cf}, + {"AV_SCRATCH_G", 0x09d0}, + {"AV_SCRATCH_H", 0x09d1}, + {"AV_SCRATCH_I", 0x09d2}, + {"AV_SCRATCH_J", 0x09d3}, + {"AV_SCRATCH_K", 0x09d4}, + {"AV_SCRATCH_L", 0x09d5}, + {"AV_SCRATCH_M", 0x09d6}, + {"AV_SCRATCH_N", 0x09d7}, +}; + +static ssize_t amrisc_regs_show(struct class *class, + struct class_attribute *attr, char *buf) +{ + char *pbuf = buf; + struct am_reg *regs = am_risc; + int rsize = sizeof(am_risc) / sizeof(struct am_reg); + int i; + unsigned val; + ssize_t ret; + + if (get_cpu_type() >= MESON_CPU_MAJOR_ID_M8) { + mutex_lock(&vdec_mutex); + if (!vdec_on(VDEC_1)) { + mutex_unlock(&vdec_mutex); + pbuf += sprintf(pbuf, "amrisc is power off\n"); + ret = pbuf - buf; + return ret; + } + } else if (get_cpu_type() >= MESON_CPU_MAJOR_ID_M6) { + /*TODO:M6 define */ + /* + * switch_mod_gate_by_type(MOD_VDEC, 1); + */ + amports_switch_gate("vdec", 1); + } + pbuf += sprintf(pbuf, "amrisc registers show:\n"); + for (i = 0; i < rsize; i++) { + val = READ_VREG(regs[i].offset); + pbuf += sprintf(pbuf, "%s(%#x)\t:%#x(%d)\n", + regs[i].name, regs[i].offset, val, val); + } + if (get_cpu_type() >= MESON_CPU_MAJOR_ID_M8) + mutex_unlock(&vdec_mutex); + else if (get_cpu_type() >= MESON_CPU_MAJOR_ID_M6) { + /*TODO:M6 define */ + /* + * switch_mod_gate_by_type(MOD_VDEC, 0); + */ + amports_switch_gate("vdec", 0); + } + ret = pbuf - buf; + return ret; +} + +static ssize_t dump_trace_show(struct class *class, + struct class_attribute *attr, char *buf) +{ + int i; + char *pbuf = buf; + ssize_t ret; + u16 *trace_buf = kmalloc(debug_trace_num * 2, GFP_KERNEL); + + if (!trace_buf) { + pbuf += sprintf(pbuf, "No Memory bug\n"); + ret = pbuf - buf; + return ret; + } + if (get_cpu_type() >= MESON_CPU_MAJOR_ID_M8) { + mutex_lock(&vdec_mutex); + if (!vdec_on(VDEC_1)) { + mutex_unlock(&vdec_mutex); + kfree(trace_buf); + pbuf += sprintf(pbuf, "amrisc is power off\n"); + ret = pbuf - buf; + return ret; + } + } else if (get_cpu_type() >= MESON_CPU_MAJOR_ID_M6) { + /*TODO:M6 define */ + /* + * switch_mod_gate_by_type(MOD_VDEC, 1); + */ + amports_switch_gate("vdec", 1); + } + pr_info("dump trace steps:%d start\n", debug_trace_num); + i = 0; + while (i <= debug_trace_num - 16) { + trace_buf[i] = READ_VREG(MPC_E); + trace_buf[i + 1] = READ_VREG(MPC_E); + trace_buf[i + 2] = READ_VREG(MPC_E); + trace_buf[i + 3] = READ_VREG(MPC_E); + trace_buf[i + 4] = READ_VREG(MPC_E); + trace_buf[i + 5] = READ_VREG(MPC_E); + trace_buf[i + 6] = READ_VREG(MPC_E); + trace_buf[i + 7] = READ_VREG(MPC_E); + trace_buf[i + 8] = READ_VREG(MPC_E); + trace_buf[i + 9] = READ_VREG(MPC_E); + trace_buf[i + 10] = READ_VREG(MPC_E); + trace_buf[i + 11] = READ_VREG(MPC_E); + trace_buf[i + 12] = READ_VREG(MPC_E); + trace_buf[i + 13] = READ_VREG(MPC_E); + trace_buf[i + 14] = READ_VREG(MPC_E); + trace_buf[i + 15] = READ_VREG(MPC_E); + i += 16; + }; + pr_info("dump trace steps:%d finished\n", debug_trace_num); + if (get_cpu_type() >= MESON_CPU_MAJOR_ID_M8) + mutex_unlock(&vdec_mutex); + else if (get_cpu_type() >= MESON_CPU_MAJOR_ID_M6) { + /*TODO:M6 define */ + /* + * switch_mod_gate_by_type(MOD_VDEC, 0); + */ + amports_switch_gate("vdec", 0); + } + for (i = 0; i < debug_trace_num; i++) { + if (i % 4 == 0) { + if (i % 16 == 0) + pbuf += sprintf(pbuf, "\n"); + else if (i % 8 == 0) + pbuf += sprintf(pbuf, " "); + else /* 4 */ + pbuf += sprintf(pbuf, " "); + } + pbuf += sprintf(pbuf, "%04x:", trace_buf[i]); + } + while (i < debug_trace_num) + ; + kfree(trace_buf); + pbuf += sprintf(pbuf, "\n"); + ret = pbuf - buf; + return ret; +} + +static ssize_t clock_level_show(struct class *class, + struct class_attribute *attr, char *buf) +{ + char *pbuf = buf; + size_t ret; + + pbuf += sprintf(pbuf, "%dMHZ\n", vdec_clk_get(VDEC_1)); + + if (has_vdec2()) + pbuf += sprintf(pbuf, "%dMHZ\n", vdec_clk_get(VDEC_2)); + + if (has_hevc_vdec()) + pbuf += sprintf(pbuf, "%dMHZ\n", vdec_clk_get(VDEC_HEVC)); + + ret = pbuf - buf; + return ret; +} + +static ssize_t store_poweron_clock_level(struct class *class, + struct class_attribute *attr, + const char *buf, size_t size) +{ + unsigned val; + ssize_t ret; + + /*ret = sscanf(buf, "%d", &val);*/ + ret = kstrtoint(buf, 0, &val); + + if (ret != 0) + return -EINVAL; + poweron_clock_level = val; + return size; +} + +static ssize_t show_poweron_clock_level(struct class *class, + struct class_attribute *attr, char *buf) +{ + return sprintf(buf, "%d\n", poweron_clock_level); +} + +/* +*if keep_vdec_mem == 1 +*always don't release +*vdec 64 memory for fast play. +*/ +static ssize_t store_keep_vdec_mem(struct class *class, + struct class_attribute *attr, + const char *buf, size_t size) +{ + unsigned val; + ssize_t ret; + + /*ret = sscanf(buf, "%d", &val);*/ + ret = kstrtoint(buf, 0, &val); + if (ret != 0) + return -EINVAL; + keep_vdec_mem = val; + return size; +} + +static ssize_t show_keep_vdec_mem(struct class *class, + struct class_attribute *attr, char *buf) +{ + return sprintf(buf, "%d\n", keep_vdec_mem); +} + + +/*irq num as same as .dts*/ +/* +* interrupts = <0 3 1 +* 0 23 1 +* 0 32 1 +* 0 43 1 +* 0 44 1 +* 0 45 1>; +* interrupt-names = "vsync", +* "demux", +* "parser", +* "mailbox_0", +* "mailbox_1", +* "mailbox_2"; +*/ +s32 vdec_request_threaded_irq(enum vdec_irq_num num, + irq_handler_t handler, + irq_handler_t thread_fn, + unsigned long irqflags, + const char *devname, void *dev) +{ + s32 res_irq; + s32 ret = 0; + + if (num >= VDEC_IRQ_MAX) { + pr_err("[%s] request irq error, irq num too big!", __func__); + return -EINVAL; + } + + if (vdec_core->isr_context[num].irq < 0) { + res_irq = platform_get_irq( + vdec_core->vdec_core_platform_device, num); + if (res_irq < 0) { + pr_err("[%s] get irq error!", __func__); + return -EINVAL; + } + + vdec_core->isr_context[num].irq = res_irq; + vdec_core->isr_context[num].dev_isr = handler; + vdec_core->isr_context[num].dev_threaded_isr = thread_fn; + vdec_core->isr_context[num].dev_id = dev; + + ret = request_threaded_irq(res_irq, + vdec_isr, + vdec_thread_isr, + (thread_fn) ? IRQF_ONESHOT : irqflags, + devname, + &vdec_core->isr_context[num]); + + if (ret) { + vdec_core->isr_context[num].irq = -1; + vdec_core->isr_context[num].dev_isr = NULL; + vdec_core->isr_context[num].dev_threaded_isr = NULL; + vdec_core->isr_context[num].dev_id = NULL; + + pr_err("vdec irq register error for %s.\n", devname); + return -EIO; + } + } else { + vdec_core->isr_context[num].dev_isr = handler; + vdec_core->isr_context[num].dev_threaded_isr = thread_fn; + vdec_core->isr_context[num].dev_id = dev; + } + + return ret; +} +EXPORT_SYMBOL(vdec_request_threaded_irq); + +s32 vdec_request_irq(enum vdec_irq_num num, irq_handler_t handler, + const char *devname, void *dev) +{ + pr_info("vdec_request_irq %p, %s\n", handler, devname); + + return vdec_request_threaded_irq(num, + handler, + NULL,/*no thread_fn*/ + IRQF_SHARED, + devname, + dev); +} +EXPORT_SYMBOL(vdec_request_irq); + +void vdec_free_irq(enum vdec_irq_num num, void *dev) +{ + if (num >= VDEC_IRQ_MAX) { + pr_err("[%s] request irq error, irq num too big!", __func__); + return; + } + + synchronize_irq(vdec_core->isr_context[num].irq); + + /* + *assume amrisc is stopped already and there is no mailbox interrupt + * when we reset pointers here. + */ + vdec_core->isr_context[num].dev_isr = NULL; + vdec_core->isr_context[num].dev_threaded_isr = NULL; + vdec_core->isr_context[num].dev_id = NULL; +} +EXPORT_SYMBOL(vdec_free_irq); + +static int dump_mode; +static ssize_t dump_risc_mem_store(struct class *class, + struct class_attribute *attr, + const char *buf, size_t size)/*set*/ +{ + unsigned val; + ssize_t ret; + char dump_mode_str[4] = "PRL"; + + /*ret = sscanf(buf, "%d", &val);*/ + ret = kstrtoint(buf, 0, &val); + + if (ret != 0) + return -EINVAL; + dump_mode = val & 0x3; + pr_info("set dump mode to %d,%c_mem\n", + dump_mode, dump_mode_str[dump_mode]); + return size; +} +static u32 read_amrisc_reg(int reg) +{ + WRITE_VREG(0x31b, reg); + return READ_VREG(0x31c); +} + +static void dump_pmem(void) +{ + int i; + + WRITE_VREG(0x301, 0x8000); + WRITE_VREG(0x31d, 0); + pr_info("start dump amrisc pmem of risc\n"); + for (i = 0; i < 0xfff; i++) { + /*same as .o format*/ + pr_info("%08x // 0x%04x:\n", read_amrisc_reg(i), i); + } +} + +static void dump_lmem(void) +{ + int i; + + WRITE_VREG(0x301, 0x8000); + WRITE_VREG(0x31d, 2); + pr_info("start dump amrisc lmem\n"); + for (i = 0; i < 0x3ff; i++) { + /*same as */ + pr_info("[%04x] = 0x%08x:\n", i, read_amrisc_reg(i)); + } +} + +static ssize_t dump_risc_mem_show(struct class *class, + struct class_attribute *attr, char *buf) +{ + char *pbuf = buf; + int ret; + + if (get_cpu_type() >= MESON_CPU_MAJOR_ID_M8) { + mutex_lock(&vdec_mutex); + if (!vdec_on(VDEC_1)) { + mutex_unlock(&vdec_mutex); + pbuf += sprintf(pbuf, "amrisc is power off\n"); + ret = pbuf - buf; + return ret; + } + } else if (get_cpu_type() >= MESON_CPU_MAJOR_ID_M6) { + /*TODO:M6 define */ + /* + * switch_mod_gate_by_type(MOD_VDEC, 1); + */ + amports_switch_gate("vdec", 1); + } + /*start do**/ + switch (dump_mode) { + case 0: + dump_pmem(); + break; + case 2: + dump_lmem(); + break; + default: + break; + } + + /*done*/ + if (get_cpu_type() >= MESON_CPU_MAJOR_ID_M8) + mutex_unlock(&vdec_mutex); + else if (get_cpu_type() >= MESON_CPU_MAJOR_ID_M6) { + /*TODO:M6 define */ + /* + * switch_mod_gate_by_type(MOD_VDEC, 0); + */ + amports_switch_gate("vdec", 0); + } + return sprintf(buf, "done\n"); +} + +static ssize_t core_show(struct class *class, struct class_attribute *attr, + char *buf) +{ + struct vdec_core_s *core = vdec_core; + char *pbuf = buf; + + if (list_empty(&core->connected_vdec_list)) + pbuf += sprintf(pbuf, "connected vdec list empty\n"); + else { + struct vdec_s *vdec; + + list_for_each_entry(vdec, &core->connected_vdec_list, list) { + pbuf += sprintf(pbuf, + "\tvdec (%p (%s)), status = %s\n", + vdec, vdec_device_name[vdec->format * 2], + vdec_status_str(vdec)); + } + } + + return pbuf - buf; +} + +static struct class_attribute vdec_class_attrs[] = { + __ATTR_RO(amrisc_regs), + __ATTR_RO(dump_trace), + __ATTR_RO(clock_level), + __ATTR(poweron_clock_level, S_IRUGO | S_IWUSR | S_IWGRP, + show_poweron_clock_level, store_poweron_clock_level), + __ATTR(dump_risc_mem, S_IRUGO | S_IWUSR | S_IWGRP, + dump_risc_mem_show, dump_risc_mem_store), + __ATTR(keep_vdec_mem, S_IRUGO | S_IWUSR | S_IWGRP, + show_keep_vdec_mem, store_keep_vdec_mem), + __ATTR_RO(core), + __ATTR_NULL +}; + +static struct class vdec_class = { + .name = "vdec", + .class_attrs = vdec_class_attrs, + }; + + +/* +*pre alloced enough memory for decoder +*fast start. +*/ +void pre_alloc_vdec_memory(void) +{ + if (!keep_vdec_mem || vdec_core->mem_start) + return; + + vdec_core->mem_start = codec_mm_alloc_for_dma(MEM_NAME, + CMA_ALLOC_SIZE / PAGE_SIZE, 4 + PAGE_SHIFT, + CODEC_MM_FLAGS_CMA_CLEAR | + CODEC_MM_FLAGS_FOR_VDECODER); + if (!vdec_core->mem_start) + return; + pr_debug("vdec base memory alloced %p\n", + (void *)vdec_core->mem_start); + + vdec_core->mem_end = vdec_core->mem_start + CMA_ALLOC_SIZE - 1; + vdec_mem_alloced_from_codec = 1; + delay_release = 3; +} +EXPORT_SYMBOL(pre_alloc_vdec_memory); + +struct device *get_vdec_device(void) +{ + return &vdec_core->vdec_core_platform_device->dev; +} +EXPORT_SYMBOL(get_vdec_device); + +static int vdec_probe(struct platform_device *pdev) +{ + s32 i, r; + + vdec_core = (struct vdec_core_s *)devm_kzalloc(&pdev->dev, + sizeof(struct vdec_core_s), GFP_KERNEL); + if (vdec_core == NULL) { + pr_err("vdec core allocation failed.\n"); + return -ENOMEM; + } + + atomic_set(&vdec_core->vdec_nr, 0); + sema_init(&vdec_core->sem, 1); + + r = class_register(&vdec_class); + if (r) { + pr_info("vdec class create fail.\n"); + return r; + } + + vdec_core->vdec_core_platform_device = pdev; + + platform_set_drvdata(pdev, vdec_core); + + for (i = 0; i < VDEC_IRQ_MAX; i++) { + vdec_core->isr_context[i].index = i; + vdec_core->isr_context[i].irq = -1; + } + + r = vdec_request_threaded_irq(VDEC_IRQ_1, NULL, NULL, + IRQF_ONESHOT, "vdec-1", NULL); + if (r < 0) { + pr_err("vdec interrupt request failed\n"); + return r; + } + + r = of_reserved_mem_device_init(&pdev->dev); + if (r == 0) + pr_info("vdec_probe done\n"); + + vdec_core->cma_dev = &pdev->dev; + + if (get_cpu_type() < MESON_CPU_MAJOR_ID_M8) { + /* default to 250MHz */ + vdec_clock_hi_enable(); + } + + if (get_cpu_type() == MESON_CPU_MAJOR_ID_GXBB) { + /* set vdec dmc request to urgent */ + WRITE_DMCREG(DMC_AM5_CHAN_CTRL, 0x3f203cf); + } + if (codec_mm_get_reserved_size() >= 48 * SZ_1M + && codec_mm_get_reserved_size() <= 96 * SZ_1M) { +#ifdef CONFIG_MULTI_DEC + vdec_default_buf_size[VFORMAT_H264_4K2K * 2] = + codec_mm_get_reserved_size() / SZ_1M; +#else + vdec_default_buf_size[VFORMAT_H264_4K2K] = + codec_mm_get_reserved_size() / SZ_1M; +#endif + + /*all reserved size for prealloc*/ + } + pre_alloc_vdec_memory(); + + INIT_LIST_HEAD(&vdec_core->connected_vdec_list); + spin_lock_init(&vdec_core->lock); + + vdec_core->thread = kthread_run(vdec_core_thread, vdec_core, + "vdec-core"); + + return 0; +} + +static int vdec_remove(struct platform_device *pdev) +{ + int i; + + for (i = 0; i < VDEC_IRQ_MAX; i++) { + if (vdec_core->isr_context[i].irq >= 0) { + free_irq(vdec_core->isr_context[i].irq, + &vdec_core->isr_context[i]); + vdec_core->isr_context[i].irq = -1; + vdec_core->isr_context[i].dev_isr = NULL; + vdec_core->isr_context[i].dev_threaded_isr = NULL; + vdec_core->isr_context[i].dev_id = NULL; + } + } + + kthread_stop(vdec_core->thread); + + class_unregister(&vdec_class); + + return 0; +} + +static const struct of_device_id amlogic_vdec_dt_match[] = { + { + .compatible = "amlogic, vdec", + }, + {}, +}; + +static struct platform_driver vdec_driver = { + .probe = vdec_probe, + .remove = vdec_remove, + .driver = { + .name = "vdec", + .of_match_table = amlogic_vdec_dt_match, + } +}; + +int vdec_module_init(void) +{ + if (platform_driver_register(&vdec_driver)) { + pr_info("failed to register vdec module\n"); + return -ENODEV; + } + + return 0; +} +EXPORT_SYMBOL(vdec_module_init); + +void vdec_module_exit(void) +{ + platform_driver_unregister(&vdec_driver); +} +EXPORT_SYMBOL(vdec_module_exit); + +#if 0 +static int __init vdec_module_init(void) +{ + if (platform_driver_register(&vdec_driver)) { + pr_info("failed to register vdec module\n"); + return -ENODEV; + } + + return 0; +} + +static void __exit vdec_module_exit(void) +{ + platform_driver_unregister(&vdec_driver); +} +#endif + +static int vdec_mem_device_init(struct reserved_mem *rmem, struct device *dev) +{ + unsigned long start, end; + + start = rmem->base; + end = rmem->base + rmem->size - 1; + pr_info("init vdec memsource %lx->%lx\n", start, end); + + vdec_core->mem_start = start; + vdec_core->mem_end = end; + vdec_core->cma_dev = dev; + + return 0; +} + +static const struct reserved_mem_ops rmem_vdec_ops = { + .device_init = vdec_mem_device_init, +}; + +static int __init vdec_mem_setup(struct reserved_mem *rmem) +{ + rmem->ops = &rmem_vdec_ops; + pr_info("vdec: reserved mem setup\n"); + + return 0; +} + +RESERVEDMEM_OF_DECLARE(vdec, "amlogic, vdec-memory", vdec_mem_setup); + +module_param(debug_trace_num, uint, 0664); +module_param(hevc_max_reset_count, int, 0664); +module_param(clk_config, uint, 0664); +/* +*module_init(vdec_module_init); +*module_exit(vdec_module_exit); +*/ +MODULE_DESCRIPTION("AMLOGIC vdec driver"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Tim Yao <timyao@amlogic.com>"); diff --git a/drivers/frame_provider/decoder/utils/vdec.h b/drivers/frame_provider/decoder/utils/vdec.h new file mode 100644 index 0000000..26c4ced --- a/dev/null +++ b/drivers/frame_provider/decoder/utils/vdec.h @@ -0,0 +1,273 @@ +/* + * drivers/amlogic/media/frame_provider/decoder/utils/vdec.h + * + * Copyright (C) 2016 Amlogic, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * +*/ + +#ifndef VDEC_H +#define VDEC_H +#include <linux/amlogic/media/utils/amports_config.h> +#include <linux/interrupt.h> +#include <linux/platform_device.h> +#include <linux/list.h> +#include <linux/completion.h> +#include <linux/irqreturn.h> + +#include <linux/amlogic/media/utils/amstream.h> +#include <linux/amlogic/media/vfm/vframe.h> +#include <linux/amlogic/media/vfm/vframe_provider.h> +#include <linux/amlogic/media/vfm/vframe_receiver.h> + +#include "vdec_input.h" + +s32 vdec_dev_register(void); +s32 vdec_dev_unregister(void); + +int vdec_source_changed(int format, int width, int height, int fps); +int vdec2_source_changed(int format, int width, int height, int fps); +int hevc_source_changed(int format, int width, int height, int fps); +struct device *get_vdec_device(void); +int vdec_module_init(void); +void vdec_module_exit(void); + +#define DEC_FLAG_HEVC_WORKAROUND 0x01 + +enum vdec_type_e { + VDEC_1 = 0, + VDEC_HCODEC, + VDEC_2, + VDEC_HEVC, + VDEC_MAX +}; + +extern void vdec2_power_mode(int level); +extern void vdec_poweron(enum vdec_type_e core); +extern void vdec_poweroff(enum vdec_type_e core); +extern bool vdec_on(enum vdec_type_e core); + +/*irq num as same as .dts*/ + +/* +* interrupts = <0 3 1 +* 0 23 1 +* 0 32 1 +* 0 43 1 +* 0 44 1 +* 0 45 1>; +* interrupt-names = "vsync", +* "demux", +* "parser", +* "mailbox_0", +* "mailbox_1", +* "mailbox_2"; +*/ +enum vdec_irq_num { + VSYNC_IRQ = 0, + DEMUX_IRQ, + PARSER_IRQ, + VDEC_IRQ_0, + VDEC_IRQ_1, + VDEC_IRQ_2, + VDEC_IRQ_MAX, +}; +extern s32 vdec_request_threaded_irq(enum vdec_irq_num num, + irq_handler_t handler, + irq_handler_t thread_fn, + unsigned long irqflags, const char *devname, void *dev); +extern s32 vdec_request_irq(enum vdec_irq_num num, irq_handler_t handler, + const char *devname, void *dev); +extern void vdec_free_irq(enum vdec_irq_num num, void *dev); + +enum vdec2_usage_e { + USAGE_NONE, + USAGE_DEC_4K2K, + USAGE_ENCODE, +}; + +extern void set_vdec2_usage(enum vdec2_usage_e usage); +extern enum vdec2_usage_e get_vdec2_usage(void); + +extern void dma_contiguous_early_fixup(phys_addr_t base, unsigned long size); +unsigned int get_vdec_clk_config_settings(void); +void update_vdec_clk_config_settings(unsigned int config); +/*unsigned int get_mmu_mode(void);*//*mask*/ + +struct vdec_s; +enum vformat_t; + +/* stream based with single instance decoder driver */ +#define VDEC_TYPE_SINGLE 0 + +/* stream based with multi-instance decoder with HW resouce sharing */ +#define VDEC_TYPE_STREAM_PARSER 1 + +/* frame based with multi-instance decoder, input block list based */ +#define VDEC_TYPE_FRAME_BLOCK 2 + +/* frame based with multi-instance decoder, single circular input block */ +#define VDEC_TYPE_FRAME_CIRCULAR 3 + +/* decoder status: uninitialized */ +#define VDEC_STATUS_UNINITIALIZED 0 + +/* decoder status: before the decoder can start consuming data */ +#define VDEC_STATUS_DISCONNECTED 1 + +/* decoder status: decoder should become disconnected once it's not active */ +#define VDEC_STATUS_CONNECTED 2 + +/* decoder status: decoder owns HW resource and is running */ +#define VDEC_STATUS_ACTIVE 3 + +#define VDEC_PROVIDER_NAME_SIZE 16 +#define VDEC_RECEIVER_NAME_SIZE 16 +#define VDEC_MAP_NAME_SIZE 40 + +struct vdec_s { + u32 magic; + struct list_head list; + int id; + + int status; + int next_status; + int type; + int port_flag; + int format; + u32 pts; + u64 pts64; + bool pts_valid; + + struct completion inactive_done; + + /* config (temp) */ + unsigned long mem_start; + unsigned long mem_end; + + struct device *cma_dev; + struct platform_device *dev; + struct dec_sysinfo sys_info_store; + struct dec_sysinfo *sys_info; + + /* input */ + struct vdec_input_s input; + + /* frame provider/receiver interface */ + char vf_provider_name[VDEC_PROVIDER_NAME_SIZE]; + struct vframe_provider_s vframe_provider; + char *vf_receiver_name; + char vfm_map_id[VDEC_MAP_NAME_SIZE]; + char vfm_map_chain[VDEC_MAP_NAME_SIZE]; + int vf_receiver_inst; + bool use_vfm_path; + + /* canvas */ + int (*get_canvas)(unsigned int index, unsigned int base); + + int (*dec_status)(struct vdec_s *vdec, struct vdec_status *vstatus); + int (*set_trickmode)(struct vdec_s *vdec, unsigned long trickmode); + + bool (*run_ready)(struct vdec_s *vdec); + void (*run)(struct vdec_s *vdec, + void (*callback)(struct vdec_s *, void *), void *); + void (*reset)(struct vdec_s *vdec); + irqreturn_t (*irq_handler)(struct vdec_s *); + irqreturn_t (*threaded_irq_handler)(struct vdec_s *); + + /* private */ + void *private; /* decoder per instance specific data */ +}; + +/* common decoder vframe provider name to use default vfm path */ +#define VFM_DEC_PROVIDER_NAME "decoder" + +#define hw_to_vdec(hw) ((struct vdec_s *) \ + (platform_get_drvdata(hw->platform_dev))) + +#define canvas_y(canvas) ((canvas) & 0xff) +#define canvas_u(canvas) (((canvas) >> 8) & 0xff) +#define canvas_v(canvas) (((canvas) >> 16) & 0xff) +#define canvas_y2(canvas) (((canvas) >> 16) & 0xff) +#define canvas_u2(canvas) (((canvas) >> 24) & 0xff) + +#define vdec_frame_based(vdec) \ + (((vdec)->type == VDEC_TYPE_FRAME_BLOCK) || \ + ((vdec)->type == VDEC_TYPE_FRAME_CIRCULAR)) +#define vdec_stream_based(vdec) \ + (((vdec)->type == VDEC_TYPE_STREAM_PARSER) || \ + ((vdec)->type == VDEC_TYPE_SINGLE)) +#define vdec_stream_auto(vdec) \ + (vdec->type == VDEC_TYPE_SINGLE) + +/* construct vdec strcture */ +extern struct vdec_s *vdec_create(int type); + +/* set video format */ +extern int vdec_set_format(struct vdec_s *vdec, int format); + +/* set PTS */ +extern int vdec_set_pts(struct vdec_s *vdec, u32 pts); + +extern int vdec_set_pts64(struct vdec_s *vdec, u64 pts64); + +/* add frame data to input chain */ +extern int vdec_write_vframe(struct vdec_s *vdec, const char *buf, + size_t count); + +/* mark the vframe_chunk as consumed */ +extern void vdec_vframe_dirty(struct vdec_s *vdec, + struct vframe_chunk_s *chunk); + +/* prepare decoder input */ +extern int vdec_prepare_input(struct vdec_s *vdec, struct vframe_chunk_s **p); + +/* clean decoder input */ +extern void vdec_clean_input(struct vdec_s *vdec); + +/* enable decoder input */ +extern void vdec_enable_input(struct vdec_s *vdec); + +/* allocate input chain + * register vdec_device + * create output, vfm or create ionvideo output + * insert vdec to vdec_manager for scheduling + */ +extern int vdec_connect(struct vdec_s *vdec); + +/* remove vdec from vdec_manager scheduling + * release input chain + * disconnect video output from ionvideo + */ +extern int vdec_disconnect(struct vdec_s *vdec); + +/* release vdec structure */ +extern int vdec_destroy(struct vdec_s *vdec); + +/* reset vdec */ +extern int vdec_reset(struct vdec_s *vdec); + +extern void vdec_set_status(struct vdec_s *vdec, int status); + +extern void vdec_set_next_status(struct vdec_s *vdec, int status); + +extern int vdec_set_decinfo(struct vdec_s *vdec, struct dec_sysinfo *p); + +extern int vdec_init(struct vdec_s *vdec, int is_4k); + +extern void vdec_release(struct vdec_s *vdec); + +extern int vdec_status(struct vdec_s *vdec, struct vdec_status *vstatus); + +extern int vdec_set_trickmode(struct vdec_s *vdec, unsigned long trickmode); + +#endif /* VDEC_H */ diff --git a/drivers/frame_provider/decoder/utils/vdec_input.c b/drivers/frame_provider/decoder/utils/vdec_input.c new file mode 100644 index 0000000..e0c3ce0 --- a/dev/null +++ b/drivers/frame_provider/decoder/utils/vdec_input.c @@ -0,0 +1,542 @@ +/* + * drivers/amlogic/media/frame_provider/decoder/utils/vdec_input.c + * + * Copyright (C) 2016 Amlogic, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * +*/ + +#include <linux/uaccess.h> +#include <linux/list.h> +#include <linux/slab.h> +#include <linux/dma-mapping.h> +#include <linux/amlogic/media/codec_mm/codec_mm.h> + +#include "../../../stream_input/amports/amports_priv.h" +#include "vdec.h" +#include "vdec_input.h" + +#define VFRAME_BLOCK_SIZE (4*SZ_1M) +#define VFRAME_BLOCK_PAGESIZE (PAGE_ALIGN(VFRAME_BLOCK_SIZE)/PAGE_SIZE) +#define VFRAME_BLOCK_PAGEALIGN 4 +#define VFRAME_BLOCK_MAX_LEVEL (8*SZ_1M) +#define VFRAME_BLOCK_HOLE (SZ_64K) + +#define FRAME_PADDING_SIZE 1024U +#define MEM_NAME "VFRAME_INPUT" + +static int vframe_chunk_fill(struct vdec_input_s *input, + struct vframe_chunk_s *chunk, const char *buf, + size_t count, struct vframe_block_list_s *block) +{ + u8 *p = (u8 *)block->start_virt + block->wp; + + if (block->type == VDEC_TYPE_FRAME_BLOCK) { + if (copy_from_user(p, buf, count)) + return -EFAULT; + + p += count; + + memset(p, 0, FRAME_PADDING_SIZE); + + dma_sync_single_for_device(get_vdec_device(), + block->start + block->wp, + count + FRAME_PADDING_SIZE, DMA_TO_DEVICE); + + } else if (block->type == VDEC_TYPE_FRAME_CIRCULAR) { + size_t len = min((size_t)(block->size - block->wp), count); + u32 wp; + + if (copy_from_user(p, buf, len)) + return -EFAULT; + + dma_sync_single_for_device(get_vdec_device(), + block->start + block->wp, + len, DMA_TO_DEVICE); + + p += len; + + if (count > len) { + p = (u8 *)block->start_virt; + if (copy_from_user(p, buf, count - len)) + return -EFAULT; + + dma_sync_single_for_device(get_vdec_device(), + block->start, + count-len, DMA_TO_DEVICE); + + p += count - len; + } + + wp = block->wp + count; + if (wp >= block->size) + wp -= block->size; + + len = min(block->size - wp, FRAME_PADDING_SIZE); + + memset(p, 0, len); + + dma_sync_single_for_device(get_vdec_device(), + block->start + wp, + len, DMA_TO_DEVICE); + + if (len < FRAME_PADDING_SIZE) { + p = (u8 *)block->start_virt; + + memset(p, 0, count - len); + + dma_sync_single_for_device(get_vdec_device(), + block->start, + count - len, DMA_TO_DEVICE); + } + } + + return 0; +} + +static inline u32 vframe_block_space(struct vframe_block_list_s *block) +{ + if (block->type == VDEC_TYPE_FRAME_BLOCK) { + return block->size - block->wp; + } else { + return (block->rp >= block->wp) ? + (block->rp - block->wp) : + (block->rp - block->wp + block->size); + } +} + +static void vframe_block_add_chunk(struct vframe_block_list_s *block, + struct vframe_chunk_s *chunk) +{ + block->wp += chunk->size + FRAME_PADDING_SIZE; + if (block->wp >= block->size) + block->wp -= block->size; + block->chunk_count++; + chunk->block = block; + block->input->wr_block = block; + chunk->sequence = block->input->sequence; + block->input->sequence++; +} + +static void vframe_block_free_storage(struct vframe_block_list_s *block) +{ + struct vdec_input_s *input = block->input; + + if (block->addr) { + dma_unmap_single( + get_vdec_device(), + block->start, + VFRAME_BLOCK_PAGESIZE, + DMA_TO_DEVICE); + + codec_mm_free_for_dma(MEM_NAME, block->addr); + + block->addr = 0; + block->start_virt = NULL; + block->start = 0; + } + + if (input->swap_page) { + __free_page(input->swap_page); + input->swap_page = NULL; + input->swap_valid = false; + } + + block->size = 0; +} + +static int vframe_block_init_alloc_storage(struct vdec_input_s *input, + struct vframe_block_list_s *block) +{ + block->magic = 0x4b434c42; + block->input = input; + block->type = input->type; + + /* + * todo: for different type use different size + */ + block->addr = codec_mm_alloc_for_dma( + MEM_NAME, + VFRAME_BLOCK_PAGESIZE, + VFRAME_BLOCK_PAGEALIGN, + CODEC_MM_FLAGS_DMA_CPU | CODEC_MM_FLAGS_FOR_VDECODER); + + if (!block->addr) { + pr_err("Input block allocation failed\n"); + return -ENOMEM; + } + + block->start_virt = (void *)codec_mm_phys_to_virt(block->addr); + block->start = dma_map_single( + get_vdec_device(), + block->start_virt, + VFRAME_BLOCK_PAGESIZE, + DMA_TO_DEVICE); + block->size = VFRAME_BLOCK_PAGESIZE * PAGE_SIZE; + + return 0; +} + +void vdec_input_init(struct vdec_input_s *input, struct vdec_s *vdec) +{ + INIT_LIST_HEAD(&input->vframe_block_list); + INIT_LIST_HEAD(&input->vframe_chunk_list); + spin_lock_init(&input->lock); + + input->vdec = vdec; +} +EXPORT_SYMBOL(vdec_input_init); + +int vdec_input_set_buffer(struct vdec_input_s *input, u32 start, u32 size) +{ + if (input_frame_based(input)) + return -EINVAL; + + input->start = start; + input->size = size; + input->swap_page = alloc_page(GFP_KERNEL); + + if (input->swap_page == NULL) + return -ENOMEM; + + return 0; +} +EXPORT_SYMBOL(vdec_input_set_buffer); + +void vdec_input_set_type(struct vdec_input_s *input, int type, int target) +{ + input->type = type; + input->target = target; +} +EXPORT_SYMBOL(vdec_input_set_type); + +int vdec_input_get_status(struct vdec_input_s *input, + struct vdec_input_status_s *status) +{ + unsigned long flags; + + if (input->vdec == NULL) + return -EINVAL; + + flags = vdec_input_lock(input); + + if (list_empty(&input->vframe_block_list)) { + status->size = VFRAME_BLOCK_SIZE; + status->data_len = 0; + status->free_len = VFRAME_BLOCK_SIZE; + status->read_pointer = 0; + } else { + int r = VFRAME_BLOCK_MAX_LEVEL - vdec_input_level(input) + - VFRAME_BLOCK_HOLE; + status->size = input->size; + status->data_len = vdec_input_level(input); + status->free_len = (r > 0) ? r : 0; + status->read_pointer = input->total_rd_count; + } + + vdec_input_unlock(input, flags); + + return 0; +} +EXPORT_SYMBOL(vdec_input_get_status); + +static void vdec_input_add_block(struct vdec_input_s *input, + struct vframe_block_list_s *block) +{ + unsigned long flags; + + flags = vdec_input_lock(input); + + list_add_tail(&block->list, &input->vframe_block_list); + input->size += block->size; + + vdec_input_unlock(input, flags); +} + +static void vdec_input_remove_block(struct vdec_input_s *input, + struct vframe_block_list_s *block) +{ + unsigned long flags; + + flags = vdec_input_lock(input); + + list_del(&block->list); + input->size -= block->size; + + vdec_input_unlock(input, flags); + + vframe_block_free_storage(block); + + kfree(block); + + pr_info("block %p removed\n", block); +} + +int vdec_input_level(struct vdec_input_s *input) +{ + return input->total_wr_count - input->total_rd_count; +} +EXPORT_SYMBOL(vdec_input_level); + +int vdec_input_add_frame(struct vdec_input_s *input, const char *buf, + size_t count) +{ + unsigned long flags; + struct vframe_chunk_s *chunk; + struct vdec_s *vdec = input->vdec; + struct vframe_block_list_s *block = input->wr_block; + +#if 0 + if (add_count == 0) { + add_count++; + memcpy(sps, buf, 30); + return 30; + } else if (add_count == 1) { + add_count++; + memcpy(pps, buf, 8); + return 8; + } + add_count++; +#endif + +#if 0 + pr_info("vdec_input_add_frame add %p, count=%d\n", buf, (int)count); + + if (count >= 8) { + pr_info("%02x %02x %02x %02x %02x %02x %02x %02x\n", + buf[0], buf[1], buf[2], buf[3], + buf[4], buf[5], buf[6], buf[7]); + } + if (count >= 16) { + pr_info("%02x %02x %02x %02x %02x %02x %02x %02x\n", + buf[8], buf[9], buf[10], buf[11], + buf[12], buf[13], buf[14], buf[15]); + } + if (count >= 24) { + pr_info("%02x %02x %02x %02x %02x %02x %02x %02x\n", + buf[16], buf[17], buf[18], buf[19], + buf[20], buf[21], buf[22], buf[23]); + } + if (count >= 32) { + pr_info("%02x %02x %02x %02x %02x %02x %02x %02x\n", + buf[24], buf[25], buf[26], buf[27], + buf[28], buf[29], buf[30], buf[31]); + } +#endif + if (input_stream_based(input)) + return -EINVAL; + + /* prepare block to write */ + if ((!block) || (block && + (vframe_block_space(block) < (count + FRAME_PADDING_SIZE)))) { + /* switch to another block for the added chunk */ + struct vframe_block_list_s *new_block; + +#if 0 + pr_info("Adding new block, total level = %d, total_wr_count=%d, total_rd_count=%d\n", + vdec_input_level(input), + (int)input->total_wr_count, + (int)input->total_rd_count); +#endif + + if ((!list_empty(&input->vframe_block_list)) && + (block->type == VDEC_TYPE_FRAME_CIRCULAR)) + return -EAGAIN; + /* + *todo: add input limit check for + * VDEC_TYPE_FRAME_BLOCK + */ + if (vdec_input_level(input) > + VFRAME_BLOCK_MAX_LEVEL) { + pr_info("vdec_input %p reaching max size\n", + input); + return -EAGAIN; + } + + /* + *check next block of current wr_block, it should be an empty + * block to use + */ + if ((block) && (!list_is_last( + &block->list, &input->vframe_block_list))) { + block = list_next_entry(block, list); + + if (vframe_block_space(block) != VFRAME_BLOCK_SIZE) + /* should never happen */ + pr_err("next writing block not empty.\n"); + } else { + /* add a new block into input list */ + new_block = kzalloc(sizeof(struct vframe_block_list_s), + GFP_KERNEL); + if (new_block == NULL) + return -EAGAIN; + + if (vframe_block_init_alloc_storage(input, + new_block) != 0) { + kfree(new_block); + pr_err("vframe_block storage allocation failed\n"); + return -EAGAIN; + } + + INIT_LIST_HEAD(&new_block->list); + + vdec_input_add_block(input, new_block); + + /* pr_info("added new block %p\n", new_block); */ + + block = new_block; + } + } + + chunk = kzalloc(sizeof(struct vframe_chunk_s), GFP_KERNEL); + + if (!chunk) + /*pr_err("vframe_chunk structure allocation failed\n");*/ + return -ENOMEM; + + chunk->magic = 0x4b554843; + if (vdec->pts_valid) { + chunk->pts = vdec->pts; + chunk->pts64 = vdec->pts64; + } + chunk->pts_valid = vdec->pts_valid; + vdec->pts_valid = false; + chunk->offset = block->wp; + chunk->size = count; + + INIT_LIST_HEAD(&chunk->list); + + if (vframe_chunk_fill(input, chunk, buf, count, block)) { + pr_err("vframe_chunk_fill failed\n"); + kfree(chunk); + return -EFAULT; + } + + flags = vdec_input_lock(input); + + vframe_block_add_chunk(block, chunk); + + list_add_tail(&chunk->list, &input->vframe_chunk_list); + + vdec_input_unlock(input, flags); + + input->total_wr_count += count; + +#if 0 + if (add_count == 2) + input->total_wr_count += 38; +#endif + + return count; +} +EXPORT_SYMBOL(vdec_input_add_frame); + +struct vframe_chunk_s *vdec_input_next_chunk(struct vdec_input_s *input) +{ + if (list_empty(&input->vframe_chunk_list)) + return NULL; + + return list_first_entry(&input->vframe_chunk_list, + struct vframe_chunk_s, list); +} +EXPORT_SYMBOL(vdec_input_next_chunk); + +struct vframe_chunk_s *vdec_input_next_input_chunk( + struct vdec_input_s *input) +{ + struct vframe_chunk_s *chunk = NULL; + struct list_head *p; + + list_for_each(p, &input->vframe_chunk_list) { + struct vframe_chunk_s *c = list_entry( + p, struct vframe_chunk_s, list); + if ((c->flag & VFRAME_CHUNK_FLAG_CONSUMED) == 0) { + chunk = c; + break; + } + } + + return chunk; +} +EXPORT_SYMBOL(vdec_input_next_input_chunk); + +void vdec_input_release_chunk(struct vdec_input_s *input, + struct vframe_chunk_s *chunk) +{ + unsigned long flags; + struct vframe_block_list_s *block = chunk->block; + + flags = vdec_input_lock(input); + + list_del(&chunk->list); + + block->rp += chunk->size; + if (block->rp >= block->size) + block->rp -= block->size; + block->chunk_count--; + input->total_rd_count += chunk->size; + + if (block->chunk_count == 0) { + /* reuse the block */ + block->wp = 0; + block->rp = 0; + + if ((input->wr_block == block) && + (!list_is_last(&block->list, + &input->vframe_block_list))) + input->wr_block = list_next_entry(block, list); + + list_move_tail(&block->list, &input->vframe_block_list); + } + + vdec_input_unlock(input, flags); + + kfree(chunk); +} +EXPORT_SYMBOL(vdec_input_release_chunk); + +unsigned long vdec_input_lock(struct vdec_input_s *input) +{ + unsigned long flags; + + spin_lock_irqsave(&input->lock, flags); + + return flags; +} +EXPORT_SYMBOL(vdec_input_lock); + +void vdec_input_unlock(struct vdec_input_s *input, unsigned long flags) +{ + spin_unlock_irqrestore(&input->lock, flags); +} +EXPORT_SYMBOL(vdec_input_unlock); + +void vdec_input_release(struct vdec_input_s *input) +{ + struct list_head *p, *tmp; + + /* release chunk data */ + list_for_each_safe(p, tmp, &input->vframe_chunk_list) { + struct vframe_chunk_s *chunk = list_entry( + p, struct vframe_chunk_s, list); + vdec_input_release_chunk(input, chunk); + } + + /* release input blocks */ + list_for_each_safe(p, tmp, &input->vframe_block_list) { + struct vframe_block_list_s *block = list_entry( + p, struct vframe_block_list_s, list); + vdec_input_remove_block(input, block); + } +} +EXPORT_SYMBOL(vdec_input_release); + diff --git a/drivers/frame_provider/decoder/utils/vdec_input.h b/drivers/frame_provider/decoder/utils/vdec_input.h new file mode 100644 index 0000000..2b952a7 --- a/dev/null +++ b/drivers/frame_provider/decoder/utils/vdec_input.h @@ -0,0 +1,131 @@ +/* + * drivers/amlogic/media/frame_provider/decoder/utils/vdec_input.h + * + * Copyright (C) 2016 Amlogic, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * +*/ + +#ifndef VDEC_INPUT_H +#define VDEC_INPUT_H + +struct vdec_s; +struct vdec_input_s; + +struct vframe_block_list_s { + u32 magic; + struct list_head list; + ulong start; + void *start_virt; + ulong addr; + int type; + u32 size; + u32 wp; + u32 rp; + int chunk_count; + struct vdec_input_s *input; +}; + +#define VFRAME_CHUNK_FLAG_CONSUMED 0x0001 + +struct vframe_chunk_s { + u32 magic; + struct list_head list; + int flag; + u32 offset; + u32 size; + u32 pts; + u64 pts64; + bool pts_valid; + u64 sequence; + struct vframe_block_list_s *block; +}; + +#define VDEC_INPUT_TARGET_VLD 0 +#define VDEC_INPUT_TARGET_HEVC 1 + +struct vdec_input_s { + struct list_head vframe_block_list; + struct list_head vframe_chunk_list; + struct vframe_block_list_s *wr_block; + spinlock_t lock; + int type; + int target; + struct vdec_s *vdec; + bool swap_valid; + bool swap_needed; + struct page *swap_page; + int total_wr_count; + int total_rd_count; + u64 sequence; + unsigned start; + unsigned size; + int stream_cookie; /* wrap count for vld_mem and*/ + /*HEVC_SHIFT_BYTE_COUNT for hevc */ +}; + +struct vdec_input_status_s { + int size; + int data_len; + int free_len; + int read_pointer; +}; + +#define input_frame_based(input) \ + (((input)->type == VDEC_TYPE_FRAME_BLOCK) || \ + ((input)->type == VDEC_TYPE_FRAME_CIRCULAR)) +#define input_stream_based(input) \ + (((input)->type == VDEC_TYPE_STREAM_PARSER) || \ + ((input)->type == VDEC_TYPE_SINGLE)) + +/* Initialize vdec_input structure */ +extern void vdec_input_init(struct vdec_input_s *input, struct vdec_s *vdec); + +/* Get available input data size */ +extern int vdec_input_level(struct vdec_input_s *input); + +/* Set input type and target */ +extern void vdec_input_set_type(struct vdec_input_s *input, int type, + int target); + +/* Set stream buffer information for stream based input */ +extern int vdec_input_set_buffer(struct vdec_input_s *input, u32 start, + u32 size); + +/* Add enqueue video data into decoder's input */ +extern int vdec_input_add_frame(struct vdec_input_s *input, const char *buf, + size_t count); + +/* Peek next frame data from decoder's input */ +extern struct vframe_chunk_s + *vdec_input_next_chunk(struct vdec_input_s *input); + +/* Peek next frame data from decoder's input, not marked as consumed */ +extern struct vframe_chunk_s *vdec_input_next_input_chunk(struct vdec_input_s + *input); + +/* Consume next frame data from decoder's input */ +extern void vdec_input_release_chunk(struct vdec_input_s *input, + struct vframe_chunk_s *chunk); + +/* Get decoder input buffer status */ +extern int vdec_input_get_status(struct vdec_input_s *input, + struct vdec_input_status_s *status); + +extern unsigned long vdec_input_lock(struct vdec_input_s *input); + +extern void vdec_input_unlock(struct vdec_input_s *input, unsigned long lock); + +/* release all resource for decoder's input */ +extern void vdec_input_release(struct vdec_input_s *input); + +#endif /* VDEC_INPUT_H */ diff --git a/drivers/stream_input/Makefile b/drivers/stream_input/Makefile new file mode 100644 index 0000000..fa6ea32 --- a/dev/null +++ b/drivers/stream_input/Makefile @@ -0,0 +1,9 @@ +obj-m += stream_input.o +stream_input-objs += amports/amstream.o +stream_input-objs += amports/amstream_profile.o +stream_input-objs += amports/adec.o +stream_input-objs += parser/thread_rw.o +stream_input-objs += parser/streambuf.o +stream_input-objs += parser/esparser.o +stream_input-objs += parser/tsdemux.o +stream_input-objs += parser/psparser.o diff --git a/drivers/stream_input/amports/Makefile b/drivers/stream_input/amports/Makefile new file mode 100644 index 0000000..55fbdce --- a/dev/null +++ b/drivers/stream_input/amports/Makefile @@ -0,0 +1,2 @@ +obj-y += amports.o +amports-objs += amstream.o amstream_profile.o adec.o diff --git a/drivers/stream_input/amports/adec.c b/drivers/stream_input/amports/adec.c new file mode 100644 index 0000000..220c888 --- a/dev/null +++ b/drivers/stream_input/amports/adec.c @@ -0,0 +1,295 @@ +/* + * drivers/amlogic/media/stream_input/amports/adec.c + * + * Copyright (C) 2016 Amlogic, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * +*/ + +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/uio_driver.h> + +#include <linux/amlogic/media/utils/aformat.h> +#include <linux/amlogic/media/frame_sync/ptsserv.h> + +#include <linux/amlogic/media/registers/register.h> + +#include "../parser/streambuf.h" +#include <linux/module.h> +#include "amports_priv.h" + +#define INFO_VALID ((astream_dev) && (astream_dev->format)) + +struct astream_device_s { + char *name; + char *format; + s32 channum; + s32 samplerate; + s32 datawidth; + + struct device dev; +}; + +static char *astream_format[] = { + "amadec_mpeg", + "amadec_pcm_s16le", + "amadec_aac", + "amadec_ac3", + "amadec_alaw", + "amadec_mulaw", + "amadec_dts", + "amadec_pcm_s16be", + "amadec_flac", + "amadec_cook", + "amadec_pcm_u8", + "amadec_adpcm", + "amadec_amr", + "amadec_raac", + "amadec_wma", + "amadec_wmapro", + "amadec_pcm_bluray", + "amadec_alac", + "amadec_vorbis", + "amadec_aac_latm", + "amadec_ape", + "amadec_eac3", + "amadec_pcm_widi", + "amadec_wmavoi" +}; + +static const char *na_string = "NA"; +static struct astream_device_s *astream_dev; + +static ssize_t format_show(struct class *class, struct class_attribute *attr, + char *buf) +{ + if (INFO_VALID && astream_dev->format) + return sprintf(buf, "%s\n", astream_dev->format); + else + return sprintf(buf, "%s\n", na_string); +} + +static ssize_t channum_show(struct class *class, struct class_attribute *attr, + char *buf) +{ + if (INFO_VALID) + return sprintf(buf, "%u\n", astream_dev->channum); + else + return sprintf(buf, "%s\n", na_string); +} + +static ssize_t samplerate_show(struct class *class, + struct class_attribute *attr, char *buf) +{ + if (INFO_VALID) + return sprintf(buf, "%u\n", astream_dev->samplerate); + else + return sprintf(buf, "%s\n", na_string); +} + +static ssize_t datawidth_show(struct class *class, + struct class_attribute *attr, + char *buf) +{ + if (INFO_VALID) + return sprintf(buf, "%u\n", astream_dev->datawidth); + else + return sprintf(buf, "%s\n", na_string); +} + +static ssize_t pts_show(struct class *class, struct class_attribute *attr, + char *buf) +{ + u32 pts; + u32 pts_margin = 0; + + if (astream_dev->samplerate <= 12000) + pts_margin = 512; + + if (INFO_VALID && (pts_lookup(PTS_TYPE_AUDIO, &pts, pts_margin) >= 0)) + return sprintf(buf, "0x%x\n", pts); + else + return sprintf(buf, "%s\n", na_string); +} + +static struct class_attribute astream_class_attrs[] = { + __ATTR_RO(format), + __ATTR_RO(samplerate), + __ATTR_RO(channum), + __ATTR_RO(datawidth), + __ATTR_RO(pts), + __ATTR_NULL +}; + +static struct class astream_class = { + .name = "astream", + .class_attrs = astream_class_attrs, + }; + +#if 1 +#define IO_CBUS_PHY_BASE 0xc1100000 +#define CBUS_REG_OFFSET(reg) ((reg) << 2) +#define IO_SECBUS_PHY_BASE 0xda000000 + +static struct uio_info astream_uio_info = { + .name = "astream_uio", + .version = "0.1", + .irq = UIO_IRQ_NONE, + + .mem = { + [0] = { + .name = "AIFIFO", + .memtype = UIO_MEM_PHYS, + .addr = + (IO_CBUS_PHY_BASE + CBUS_REG_OFFSET(AIU_AIFIFO_CTRL)) + &(PAGE_MASK), + .size = PAGE_SIZE, + }, + [1] = { + .memtype = UIO_MEM_PHYS, + .addr = + (IO_CBUS_PHY_BASE + CBUS_REG_OFFSET(VCOP_CTRL_REG)), + .size = PAGE_SIZE, + }, + [2] = { + .name = "SECBUS", + .memtype = UIO_MEM_PHYS, + .addr = (IO_SECBUS_PHY_BASE), + .size = PAGE_SIZE, + }, + [3] = { + .name = "CBUS", + .memtype = UIO_MEM_PHYS, + .addr = + (IO_CBUS_PHY_BASE + CBUS_REG_OFFSET(ASSIST_HW_REV)) + &(PAGE_MASK), + .size = PAGE_SIZE, + }, + [4] = { + .name = "CBUS-START", + .memtype = UIO_MEM_PHYS, + .addr = (IO_CBUS_PHY_BASE + CBUS_REG_OFFSET(0x1000)), + .size = PAGE_SIZE * 4, + }, + }, +}; +#endif + +static void astream_release(struct device *dev) +{ + kfree(astream_dev); + + astream_dev = NULL; +} + +s32 adec_init(struct stream_port_s *port) +{ + enum aformat_e af; + + if (!astream_dev) + return -ENODEV; + + af = port->aformat; + + astream_dev->channum = port->achanl; + astream_dev->samplerate = port->asamprate; + astream_dev->datawidth = port->adatawidth; + + /*wmb();don't need it...*/ + if (af <= ARRAY_SIZE(astream_format)) + astream_dev->format = astream_format[af]; + else + astream_dev->format = NULL; + return 0; +} + +s32 adec_release(enum aformat_e vf) +{ + pr_info("adec_release\n"); + + if (!astream_dev) + return -ENODEV; + + astream_dev->format = NULL; + + return 0; +} + +s32 astream_dev_register(void) +{ + s32 r; + + r = class_register(&astream_class); + if (r) { + pr_info("astream class create fail.\n"); + return r; + } + + astream_dev = kzalloc(sizeof(struct astream_device_s), GFP_KERNEL); + + if (!astream_dev) { + r = -ENOMEM; + goto err_3; + } + + astream_dev->dev.class = &astream_class; + astream_dev->dev.release = astream_release; + + dev_set_name(&astream_dev->dev, "astream-dev"); + + dev_set_drvdata(&astream_dev->dev, astream_dev); + + r = device_register(&astream_dev->dev); + if (r) { + pr_info("astream device register fail.\n"); + goto err_2; + } + +#if 1 + if (uio_register_device(&astream_dev->dev, &astream_uio_info)) { + pr_info("astream UIO device register fail.\n"); + r = -ENODEV; + goto err_1; + } +#endif + + return 0; + +err_1: + device_unregister(&astream_dev->dev); + +err_2: + kfree(astream_dev); + astream_dev = NULL; + +err_3: + class_unregister(&astream_class); + + return r; +} + +void astream_dev_unregister(void) +{ + if (astream_dev) { +#if 1 + uio_unregister_device(&astream_uio_info); +#endif + + device_unregister(&astream_dev->dev); + + class_unregister(&astream_class); + } +} diff --git a/drivers/stream_input/amports/adec.h b/drivers/stream_input/amports/adec.h new file mode 100644 index 0000000..545ac76 --- a/dev/null +++ b/drivers/stream_input/amports/adec.h @@ -0,0 +1,32 @@ +/* + * drivers/amlogic/media/stream_input/amports/adec.h + * + * Copyright (C) 2016 Amlogic, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * +*/ + +#ifndef ADEC_H +#define ADEC_H + +#include "../parser/streambuf.h" +#include <linux/amlogic/media/utils/aformat.h> + +extern s32 adec_init(struct stream_port_s *port); + +extern s32 adec_release(enum aformat_e af); + +extern s32 astream_dev_register(void); + +extern s32 astream_dev_unregister(void); + +#endif /* ADEC_H */ diff --git a/drivers/stream_input/amports/amports_priv.h b/drivers/stream_input/amports/amports_priv.h new file mode 100644 index 0000000..4b6f96e --- a/dev/null +++ b/drivers/stream_input/amports/amports_priv.h @@ -0,0 +1,56 @@ +/* + * drivers/amlogic/media/stream_input/amports/amports_priv.h + * + * Copyright (C) 2016 Amlogic, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * +*/ + +#ifndef AMPORTS_PRIV_HEAD_HH +#define AMPORTS_PRIV_HEAD_HH +#include "../parser/streambuf.h" +#include "../../common/media_clock/switch/amports_gate.h" +#include <linux/amlogic/media/vfm/vframe.h> +#include "../../common/firmware/firmware.h" +#include <linux/amlogic/media/registers/register.h> +#include <linux/amlogic/media/utils/log.h> + +struct port_priv_s { + struct vdec_s *vdec; + struct stream_port_s *port; +}; + +struct stream_buf_s *get_buf_by_type(u32 type); + +extern void amvenc_dos_top_reg_fix(void); + +/*video.c provide*/ +extern u32 trickmode_i; +struct amvideocap_req; +extern u32 set_blackout_policy(int policy); +extern u32 get_blackout_policy(void); +int calculation_stream_ext_delayed_ms(u8 type); +int ext_get_cur_video_frame(struct vframe_s **vf, int *canvas_index); +int ext_put_video_frame(struct vframe_s *vf); +int ext_register_end_frame_callback(struct amvideocap_req *req); +int amstream_request_firmware_from_sys(const char *file_name, + char *buf, int size); +void set_vsync_pts_inc_mode(int inc); + +void set_real_audio_info(void *arg); +#define dbg() pr_info("on %s,line %d\n", __func__, __LINE__) + +struct device *amports_get_dma_device(void); +struct device *get_codec_cma_device(void); +int amports_get_debug_flags(void); + +#endif diff --git a/drivers/stream_input/amports/amstream.c b/drivers/stream_input/amports/amstream.c new file mode 100644 index 0000000..3f4459d --- a/dev/null +++ b/drivers/stream_input/amports/amstream.c @@ -0,0 +1,3648 @@ +/* + * drivers/amlogic/media/stream_input/amports/amstream.c + * + * Copyright (C) 2016 Amlogic, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * +*/ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/types.h> +#include <linux/fs.h> +#include <linux/init.h> +#include <linux/device.h> +#include <linux/slab.h> +#include <linux/vmalloc.h> +#include <linux/mm.h> +#include <linux/amlogic/major.h> +#include <linux/sched.h> +#include <linux/interrupt.h> +#include <linux/delay.h> +#include <linux/kthread.h> + +#include <linux/amlogic/media/utils/amstream.h> +#include <linux/amlogic/media/utils/vformat.h> +#include <linux/amlogic/media/utils/aformat.h> + +#include <linux/amlogic/media/frame_sync/tsync.h> +#include <linux/amlogic/media/frame_sync/ptsserv.h> +#include <linux/amlogic/media/frame_sync/timestamp.h> + +#include <linux/types.h> +#include <linux/uaccess.h> +#include <linux/io.h> +/* #include <mach/am_regs.h> */ + +#include <linux/platform_device.h> +#include <linux/mutex.h> +#include <linux/poll.h> +#include <linux/dma-mapping.h> +#include <linux/dma-contiguous.h> +#include <linux/uaccess.h> +#include <linux/clk.h> +#if 1 /* MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON6 */ +/* #include <mach/mod_gate.h> */ +/* #include <mach/power_gate.h> */ +#endif +#include "../parser/streambuf.h" +#include "../parser/streambuf_reg.h" +#include "../parser/tsdemux.h" +#include "../parser/psparser.h" +#include "../parser/esparser.h" +#include "../../frame_provider/decoder/utils/vdec.h" +#include "adec.h" +/*#include "../parser/rmparser.h"*//*mask*/ +#include "amports_priv.h" +#include <linux/amlogic/media/utils/amports_config.h> +#include <linux/amlogic/media/frame_sync/tsync_pcr.h> +#include "../parser/thread_rw.h" + + +#include <linux/firmware.h> + +#include <linux/of.h> +#include <linux/of_fdt.h> +#include <linux/libfdt_env.h> +#include <linux/of_reserved_mem.h> +#include <linux/reset.h> +#include "../../common/firmware/firmware.h" +#ifdef CONFIG_COMPAT +#include <linux/compat.h> +#endif +#include <linux/amlogic/media/codec_mm/codec_mm.h> + + +#define DEVICE_NAME "amstream-dev" +#define DRIVER_NAME "amstream" +#define MODULE_NAME "amstream" + +#define MAX_AMSTREAM_PORT_NUM ARRAY_SIZE(ports) +u32 amstream_port_num; +u32 amstream_buf_num; + +#if 0 +#if MESON_CPU_TYPE == MESON_CPU_TYPE_MESONG9TV +#define NO_VDEC2_INIT 1 +#elif MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON6TVD +#define NO_VDEC2_INIT IS_MESON_M8M2_CPU +#endif +#endif +#define NO_VDEC2_INIT 1 + + +static int debugflags; + +/* #define DATA_DEBUG */ +static int use_bufferlevelx10000 = 10000; +static int reset_canuse_buferlevel(int level); +static struct platform_device *amstream_pdev; +struct device *amports_get_dma_device(void) +{ + return &amstream_pdev->dev; +} +EXPORT_SYMBOL(amports_get_dma_device); + +/* +*bit0:no threadrw +*/ +int amports_get_debug_flags(void) +{ + return debugflags; +} + +#ifdef DATA_DEBUG +#include <linux/fs.h> +#define DEBUG_FILE_NAME "/tmp/debug.tmp" +static struct file *debug_filp; +static loff_t debug_file_pos; + +void debug_file_write(const char __user *buf, size_t count) +{ + mm_segment_t old_fs; + + if (!debug_filp) + return; + + old_fs = get_fs(); + set_fs(KERNEL_DS); + + if (count != vfs_write(debug_filp, buf, count, &debug_file_pos)) + pr_err("Failed to write debug file\n"); + + set_fs(old_fs); +} +#endif + +#define DEFAULT_VIDEO_BUFFER_SIZE (1024*1024*15) +#define DEFAULT_VIDEO_BUFFER_SIZE_4K (1024*1024*15) + +#define DEFAULT_AUDIO_BUFFER_SIZE (1024*768*2) +#define DEFAULT_SUBTITLE_BUFFER_SIZE (1024*256) + +static int amstream_open(struct inode *inode, struct file *file); +static int amstream_release(struct inode *inode, struct file *file); +static long amstream_ioctl(struct file *file, unsigned int cmd, ulong arg); +#ifdef CONFIG_COMPAT +static long amstream_compat_ioctl + (struct file *file, unsigned int cmd, ulong arg); +#endif +static ssize_t amstream_vbuf_write +(struct file *file, const char *buf, size_t count, loff_t *ppos); +static ssize_t amstream_vframe_write +(struct file *file, const char *buf, size_t count, loff_t *ppos); +static ssize_t amstream_abuf_write +(struct file *file, const char *buf, size_t count, loff_t *ppos); +static ssize_t amstream_mpts_write +(struct file *file, const char *buf, size_t count, loff_t *ppos); +static ssize_t amstream_mpps_write +(struct file *file, const char *buf, size_t count, loff_t *ppos); +static ssize_t amstream_sub_read +(struct file *file, char *buf, size_t count, loff_t *ppos); +static ssize_t amstream_sub_write +(struct file *file, const char *buf, size_t count, loff_t *ppos); +static unsigned int amstream_sub_poll +(struct file *file, poll_table *wait_table); +static unsigned int amstream_userdata_poll +(struct file *file, poll_table *wait_table); +static ssize_t amstream_userdata_read +(struct file *file, char *buf, size_t count, loff_t *ppos); +static int (*amstream_adec_status) +(struct adec_status *astatus); +#ifdef CONFIG_AM_VDEC_REAL +static ssize_t amstream_mprm_write +(struct file *file, const char *buf, size_t count, loff_t *ppos); +#endif + +static const struct file_operations vbuf_fops = { + .owner = THIS_MODULE, + .open = amstream_open, + .release = amstream_release, + .write = amstream_vbuf_write, + .unlocked_ioctl = amstream_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = amstream_compat_ioctl, +#endif +}; + +static const struct file_operations vframe_fops = { + .owner = THIS_MODULE, + .open = amstream_open, + .release = amstream_release, + .write = amstream_vframe_write, + .unlocked_ioctl = amstream_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = amstream_compat_ioctl, +#endif +}; + +static const struct file_operations abuf_fops = { + .owner = THIS_MODULE, + .open = amstream_open, + .release = amstream_release, + .write = amstream_abuf_write, + .unlocked_ioctl = amstream_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = amstream_compat_ioctl, +#endif +}; + +static const struct file_operations mpts_fops = { + .owner = THIS_MODULE, + .open = amstream_open, + .release = amstream_release, + .write = amstream_mpts_write, + .unlocked_ioctl = amstream_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = amstream_compat_ioctl, +#endif +}; + +static const struct file_operations mpps_fops = { + .owner = THIS_MODULE, + .open = amstream_open, + .release = amstream_release, + .write = amstream_mpps_write, + .unlocked_ioctl = amstream_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = amstream_compat_ioctl, +#endif +}; + +static const struct file_operations mprm_fops = { + .owner = THIS_MODULE, + .open = amstream_open, + .release = amstream_release, +#ifdef CONFIG_AM_VDEC_REAL + .write = amstream_mprm_write, +#endif + .unlocked_ioctl = amstream_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = amstream_compat_ioctl, +#endif +}; + +static const struct file_operations sub_fops = { + .owner = THIS_MODULE, + .open = amstream_open, + .release = amstream_release, + .write = amstream_sub_write, + .unlocked_ioctl = amstream_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = amstream_compat_ioctl, +#endif +}; + +static const struct file_operations sub_read_fops = { + .owner = THIS_MODULE, + .open = amstream_open, + .release = amstream_release, + .read = amstream_sub_read, + .poll = amstream_sub_poll, + .unlocked_ioctl = amstream_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = amstream_compat_ioctl, +#endif +}; + +static const struct file_operations userdata_fops = { + .owner = THIS_MODULE, + .open = amstream_open, + .release = amstream_release, + .read = amstream_userdata_read, + .poll = amstream_userdata_poll, + .unlocked_ioctl = amstream_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = amstream_compat_ioctl, +#endif +}; + +static const struct file_operations amstream_fops = { + .owner = THIS_MODULE, + .open = amstream_open, + .release = amstream_release, + .unlocked_ioctl = amstream_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = amstream_compat_ioctl, +#endif +}; + +/**************************************************/ +static struct audio_info audio_dec_info; +static struct class *amstream_dev_class; +static DEFINE_MUTEX(amstream_mutex); + +atomic_t subdata_ready = ATOMIC_INIT(0); +static int sub_type; +static int sub_port_inited; +/* wait queue for poll */ +static wait_queue_head_t amstream_sub_wait; +atomic_t userdata_ready = ATOMIC_INIT(0); +static int userdata_length; +static wait_queue_head_t amstream_userdata_wait; +#define USERDATA_FIFO_NUM 1024 +static struct userdata_poc_info_t userdata_poc_info[USERDATA_FIFO_NUM]; +static int userdata_poc_ri = 0, userdata_poc_wi; + +static struct stream_port_s ports[] = { + { + .name = "amstream_vbuf", + .type = PORT_TYPE_ES | PORT_TYPE_VIDEO, + .fops = &vbuf_fops, + }, +#ifdef CONFIG_MULTI_DEC + { + .name = "amstream_vbuf_sched", + .type = PORT_TYPE_ES | PORT_TYPE_VIDEO | + PORT_TYPE_DECODER_SCHED, + .fops = &vbuf_fops, + }, + { + .name = "amstream_vframe", + .type = PORT_TYPE_ES | PORT_TYPE_VIDEO | + PORT_TYPE_FRAME | PORT_TYPE_DECODER_SCHED, + .fops = &vframe_fops, + }, +#endif + { + .name = "amstream_abuf", + .type = PORT_TYPE_ES | PORT_TYPE_AUDIO, + .fops = &abuf_fops, + }, + { + .name = "amstream_mpts", + .type = PORT_TYPE_MPTS | PORT_TYPE_VIDEO | + PORT_TYPE_AUDIO | PORT_TYPE_SUB, + .fops = &mpts_fops, + }, +#ifdef CONFIG_MULTI_DEC + { + .name = "amstream_mpts_sched", + .type = PORT_TYPE_MPTS | PORT_TYPE_VIDEO | + PORT_TYPE_AUDIO | PORT_TYPE_SUB | + PORT_TYPE_DECODER_SCHED, + .fops = &mpts_fops, + }, +#endif + { + .name = "amstream_mpps", + .type = PORT_TYPE_MPPS | PORT_TYPE_VIDEO | + PORT_TYPE_AUDIO | PORT_TYPE_SUB, + .fops = &mpps_fops, + }, + { + .name = "amstream_rm", + .type = PORT_TYPE_RM | PORT_TYPE_VIDEO | PORT_TYPE_AUDIO, + .fops = &mprm_fops, + }, + { + .name = "amstream_sub", + .type = PORT_TYPE_SUB, + .fops = &sub_fops, + }, + { + .name = "amstream_sub_read", + .type = PORT_TYPE_SUB_RD, + .fops = &sub_read_fops, + }, + { + .name = "amstream_userdata", + .type = PORT_TYPE_USERDATA, + .fops = &userdata_fops, + }, + { + .name = "amstream_hevc", + .type = PORT_TYPE_ES | PORT_TYPE_VIDEO | PORT_TYPE_HEVC, + .fops = &vbuf_fops, + .vformat = VFORMAT_HEVC, + } +#ifdef CONFIG_MULTI_DEC + , + { + .name = "amstream_hevc_frame", + .type = PORT_TYPE_ES | PORT_TYPE_VIDEO | PORT_TYPE_HEVC | + PORT_TYPE_FRAME | PORT_TYPE_DECODER_SCHED, + .fops = &vframe_fops, + .vformat = VFORMAT_HEVC, + }, + { + .name = "amstream_hevc_sched", + .type = PORT_TYPE_ES | PORT_TYPE_VIDEO | PORT_TYPE_HEVC | + PORT_TYPE_DECODER_SCHED, + .fops = &vbuf_fops, + .vformat = VFORMAT_HEVC, + } +#endif +}; + +static struct stream_buf_s bufs[BUF_MAX_NUM] = { + { + .reg_base = VLD_MEM_VIFIFO_REG_BASE, + .type = BUF_TYPE_VIDEO, + .buf_start = 0, + .buf_size = DEFAULT_VIDEO_BUFFER_SIZE, + .default_buf_size = DEFAULT_VIDEO_BUFFER_SIZE, + .first_tstamp = INVALID_PTS + }, + { + .reg_base = AIU_MEM_AIFIFO_REG_BASE, + .type = BUF_TYPE_AUDIO, + .buf_start = 0, + .buf_size = DEFAULT_AUDIO_BUFFER_SIZE, + .default_buf_size = DEFAULT_AUDIO_BUFFER_SIZE, + .first_tstamp = INVALID_PTS + }, + { + .reg_base = 0, + .type = BUF_TYPE_SUBTITLE, + .buf_start = 0, + .buf_size = DEFAULT_SUBTITLE_BUFFER_SIZE, + .default_buf_size = DEFAULT_SUBTITLE_BUFFER_SIZE, + .first_tstamp = INVALID_PTS + }, + { + .reg_base = 0, + .type = BUF_TYPE_USERDATA, + .buf_start = 0, + .buf_size = 0, + .first_tstamp = INVALID_PTS + }, + { + .reg_base = HEVC_STREAM_REG_BASE, + .type = BUF_TYPE_HEVC, + .buf_start = 0, + .buf_size = DEFAULT_VIDEO_BUFFER_SIZE_4K, + .default_buf_size = DEFAULT_VIDEO_BUFFER_SIZE_4K, + .first_tstamp = INVALID_PTS + }, +}; + +struct stream_buf_s *get_buf_by_type(u32 type) +{ + if (type == PTS_TYPE_VIDEO) + return &bufs[BUF_TYPE_VIDEO]; + if (type == PTS_TYPE_AUDIO) + return &bufs[BUF_TYPE_AUDIO]; + if (has_hevc_vdec()) { + if (type == PTS_TYPE_HEVC) + return &bufs[BUF_TYPE_HEVC]; + } + + return NULL; +} + +void set_sample_rate_info(int arg) +{ + audio_dec_info.sample_rate = arg; + audio_dec_info.valid = 1; +} + +void set_ch_num_info(int arg) +{ + audio_dec_info.channels = arg; +} + +struct audio_info *get_audio_info(void) +{ + return &audio_dec_info; +} +EXPORT_SYMBOL(get_audio_info); + +static void amstream_change_vbufsize(struct port_priv_s *priv, + struct stream_buf_s *pvbuf) +{ + if (pvbuf->buf_start != 0) { + pr_info("streambuf is alloced before\n"); + return; + } + if (pvbuf->for_4k) { + pvbuf->buf_size = DEFAULT_VIDEO_BUFFER_SIZE_4K; + if ((pvbuf->buf_size > 30 * SZ_1M) && + (codec_mm_get_total_size() < 220 * SZ_1M)) { + /*if less than 250M, used 20M for 4K & 265*/ + pvbuf->buf_size = pvbuf->buf_size >> 1; + } + } else if (pvbuf->buf_size > DEFAULT_VIDEO_BUFFER_SIZE) { + pvbuf->buf_size = DEFAULT_VIDEO_BUFFER_SIZE; + } else { + pvbuf->buf_size = DEFAULT_VIDEO_BUFFER_SIZE; + } + reset_canuse_buferlevel(10000); +} + +static bool port_get_inited(struct port_priv_s *priv) +{ + struct stream_port_s *port = priv->port; + + if (port->type & PORT_TYPE_VIDEO) { + struct vdec_s *vdec = priv->vdec; + + return vdec->port_flag & PORT_FLAG_INITED; + } + + return port->flag & PORT_FLAG_INITED; +} + +static void port_set_inited(struct port_priv_s *priv) +{ + struct stream_port_s *port = priv->port; + + if (port->type & PORT_TYPE_VIDEO) { + struct vdec_s *vdec = priv->vdec; + + vdec->port_flag |= PORT_FLAG_INITED; + } else + port->flag |= PORT_FLAG_INITED; +} + +static void video_port_release(struct port_priv_s *priv, + struct stream_buf_s *pbuf, int release_num) +{ + struct stream_port_s *port = priv->port; + struct vdec_s *vdec = priv->vdec; + + switch (release_num) { + default: + /*fallthrough*/ + case 0: /*release all */ + /*fallthrough*/ + case 4: + if ((port->type & PORT_TYPE_FRAME) == 0) + esparser_release(pbuf); + /*fallthrough*/ + case 3: + vdec_release(vdec); + priv->vdec = NULL; + /*fallthrough*/ + case 2: + if ((port->type & PORT_TYPE_FRAME) == 0) + stbuf_release(pbuf); + /*fallthrough*/ + case 1: + ; + } +} + +static int video_port_init(struct port_priv_s *priv, + struct stream_buf_s *pbuf) +{ + int r; + struct stream_port_s *port = priv->port; + struct vdec_s *vdec = priv->vdec; + + if ((port->flag & PORT_FLAG_VFORMAT) == 0) { + pr_err("vformat not set\n"); + return -EPERM; + } + + if (port->vformat == VFORMAT_H264_4K2K || + (priv->vdec->sys_info->height * + priv->vdec->sys_info->width) > 1920*1088) { + pbuf->for_4k = 1; + } + + if (port->type & PORT_TYPE_FRAME) { + r = vdec_init(vdec, + (priv->vdec->sys_info->height * + priv->vdec->sys_info->width) > 1920*1088); + if (r < 0) { + pr_err("video_port_init %d, vdec_init failed\n", + __LINE__); + video_port_release(priv, pbuf, 2); + return r; + } + + return 0; + } + + amstream_change_vbufsize(priv, pbuf); + + if (has_hevc_vdec()) { + if (port->type & PORT_TYPE_MPTS) { + if (pbuf->type == BUF_TYPE_HEVC) + vdec_poweroff(VDEC_1); + else + vdec_poweroff(VDEC_HEVC); + } + } + + r = stbuf_init(pbuf, vdec); + if (r < 0) { + pr_err("video_port_init %d, stbuf_init failed\n", __LINE__); + return r; + } + + /* todo: set path based on port flag */ + r = vdec_init(vdec, + (priv->vdec->sys_info->height * + priv->vdec->sys_info->width) > 1920*1088); + + if (r < 0) { + pr_err("video_port_init %d, vdec_init failed\n", __LINE__); + video_port_release(priv, pbuf, 2); + return r; + } + + if (port->type & PORT_TYPE_ES) { + r = esparser_init(pbuf, vdec); + if (r < 0) { + video_port_release(priv, pbuf, 3); + pr_err("esparser_init() failed\n"); + return r; + } + } + + pbuf->flag |= BUF_FLAG_IN_USE; + + vdec_connect(priv->vdec); + + return 0; +} + +static void audio_port_release(struct stream_port_s *port, + struct stream_buf_s *pbuf, int release_num) +{ + switch (release_num) { + default: + /*fallthrough*/ + case 0: /*release all */ + /*fallthrough*/ + case 4: + esparser_release(pbuf); + /*fallthrough*/ + case 3: + adec_release(port->vformat); + /*fallthrough*/ + case 2: + stbuf_release(pbuf); + /*fallthrough*/ + case 1: + ; + } +} + +static int audio_port_reset(struct stream_port_s *port, + struct stream_buf_s *pbuf) +{ + int r; + + if ((port->flag & PORT_FLAG_AFORMAT) == 0) { + pr_err("aformat not set\n"); + return 0; + } + + pts_stop(PTS_TYPE_AUDIO); + + stbuf_release(pbuf); + + r = stbuf_init(pbuf, NULL); + if (r < 0) + return r; + + r = adec_init(port); + if (r < 0) { + audio_port_release(port, pbuf, 2); + return r; + } + + if (port->type & PORT_TYPE_ES) + esparser_audio_reset_s(pbuf); + + if (port->type & PORT_TYPE_MPTS) + tsdemux_audio_reset(); + + if (port->type & PORT_TYPE_MPPS) + psparser_audio_reset(); + +#ifdef CONFIG_AM_VDEC_REAL + if (port->type & PORT_TYPE_RM) + rm_audio_reset(); +#endif + + pbuf->flag |= BUF_FLAG_IN_USE; + + pts_start(PTS_TYPE_AUDIO); + + return 0; +} + +static int sub_port_reset(struct stream_port_s *port, + struct stream_buf_s *pbuf) +{ + int r; + + port->flag &= (~PORT_FLAG_INITED); + + stbuf_release(pbuf); + + r = stbuf_init(pbuf, NULL); + if (r < 0) + return r; + + if (port->type & PORT_TYPE_MPTS) + tsdemux_sub_reset(); + + if (port->type & PORT_TYPE_MPPS) + psparser_sub_reset(); + + if (port->sid == 0xffff) { /* es sub */ + esparser_sub_reset(); + pbuf->flag |= BUF_FLAG_PARSER; + } + + pbuf->flag |= BUF_FLAG_IN_USE; + + port->flag |= PORT_FLAG_INITED; + + return 0; +} + +static int audio_port_init(struct stream_port_s *port, + struct stream_buf_s *pbuf) +{ + int r; + + if ((port->flag & PORT_FLAG_AFORMAT) == 0) { + pr_err("aformat not set\n"); + return 0; + } + + r = stbuf_init(pbuf, NULL); + if (r < 0) + return r; + r = adec_init(port); + if (r < 0) { + audio_port_release(port, pbuf, 2); + return r; + } + if (port->type & PORT_TYPE_ES) { + r = esparser_init(pbuf, NULL); + if (r < 0) { + audio_port_release(port, pbuf, 3); + return r; + } + } + pbuf->flag |= BUF_FLAG_IN_USE; + return 0; +} + +static void sub_port_release(struct stream_port_s *port, + struct stream_buf_s *pbuf) +{ + if ((port->sid == 0xffff) && + ((port->type & (PORT_TYPE_MPPS | PORT_TYPE_MPTS)) == 0)) { + /* this is es sub */ + esparser_release(pbuf); + } + stbuf_release(pbuf); + sub_port_inited = 0; +} + +static int sub_port_init(struct stream_port_s *port, struct stream_buf_s *pbuf) +{ + int r; + + if ((port->flag & PORT_FLAG_SID) == 0) { + pr_err("subtitle id not set\n"); + return 0; + } + + r = stbuf_init(pbuf, NULL); + if (r < 0) + return r; + + if ((port->sid == 0xffff) && + ((port->type & (PORT_TYPE_MPPS | PORT_TYPE_MPTS)) == 0)) { + /* es sub */ + r = esparser_init(pbuf, NULL); + if (r < 0) { + sub_port_release(port, pbuf); + return r; + } + } + + sub_port_inited = 1; + return 0; +} + +static int amstream_port_init(struct port_priv_s *priv) +{ + int r; + struct stream_buf_s *pvbuf = &bufs[BUF_TYPE_VIDEO]; + struct stream_buf_s *pabuf = &bufs[BUF_TYPE_AUDIO]; + struct stream_buf_s *psbuf = &bufs[BUF_TYPE_SUBTITLE]; + struct stream_buf_s *pubuf = &bufs[BUF_TYPE_USERDATA]; + struct stream_port_s *port = priv->port; + struct vdec_s *vdec = priv->vdec; + + mutex_lock(&amstream_mutex); + + stbuf_fetch_init(); + + if (port_get_inited(priv)) { + mutex_unlock(&amstream_mutex); + return 0; + } + + if ((port->type & PORT_TYPE_AUDIO) && + (port->flag & PORT_FLAG_AFORMAT)) { + r = audio_port_init(port, pabuf); + if (r < 0) { + pr_err("audio_port_init failed\n"); + goto error1; + } + } + + if ((port->type & PORT_TYPE_VIDEO) && + (port->flag & PORT_FLAG_VFORMAT)) { + pubuf->buf_size = 0; + pubuf->buf_start = 0; + pubuf->buf_wp = 0; + pubuf->buf_rp = 0; + pubuf->for_4k = 0; + if (has_hevc_vdec()) { + if (port->vformat == VFORMAT_HEVC || + port->vformat == VFORMAT_VP9) + pvbuf = &bufs[BUF_TYPE_HEVC]; + } + r = video_port_init(priv, pvbuf); + if (r < 0) { + pr_err("video_port_init failed\n"); + goto error2; + } + } + + if ((port->type & PORT_TYPE_SUB) && (port->flag & PORT_FLAG_SID)) { + r = sub_port_init(port, psbuf); + if (r < 0) { + pr_err("sub_port_init failed\n"); + goto error3; + } + } + + if (port->type & PORT_TYPE_MPTS) { + if (has_hevc_vdec()) { + r = tsdemux_init( + (port->flag & PORT_FLAG_VID) ? port->vid : 0xffff, + (port->flag & PORT_FLAG_AID) ? port->aid : 0xffff, + (port->flag & PORT_FLAG_SID) ? port->sid : 0xffff, + (port->pcr_inited == 1) ? port->pcrid : 0xffff, + (port->vformat == VFORMAT_HEVC) || + (port->vformat == VFORMAT_VP9), + vdec); + } else { + r = tsdemux_init( + (port->flag & PORT_FLAG_VID) ? port->vid : 0xffff, + (port->flag & PORT_FLAG_AID) ? port->aid : 0xffff, + (port->flag & PORT_FLAG_SID) ? port->sid : 0xffff, + (port->pcr_inited == 1) ? port->pcrid : 0xffff, + 0, + vdec); + } + + if (r < 0) { + pr_err("tsdemux_init failed\n"); + goto error4; + } + tsync_pcr_start(); + } + if (port->type & PORT_TYPE_MPPS) { + r = psparser_init( + (port->flag & PORT_FLAG_VID) ? port->vid : 0xffff, + (port->flag & PORT_FLAG_AID) ? port->aid : 0xffff, + (port->flag & PORT_FLAG_SID) ? port->sid : 0xffff, + priv->vdec); + if (r < 0) { + pr_err("psparser_init failed\n"); + goto error5; + } + } +#ifdef CONFIG_AM_VDEC_REAL + if (port->type & PORT_TYPE_RM) { + rm_set_vasid( + (port->flag & PORT_FLAG_VID) ? port->vid : 0xffff, + (port->flag & PORT_FLAG_AID) ? port->aid : 0xffff); + } +#endif +#if 1 /* MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON6TVD */ + if (!NO_VDEC2_INIT) { + if ((port->type & PORT_TYPE_VIDEO) + && (port->vformat == VFORMAT_H264_4K2K)) + stbuf_vdec2_init(pvbuf); + } +#endif + + if ((port->type & PORT_TYPE_VIDEO) && + (port->flag & PORT_FLAG_VFORMAT)) + /* connect vdec at the end after all HW initialization */ + vdec_connect(vdec); + + tsync_audio_break(0); /* clear audio break */ + set_vsync_pts_inc_mode(0); /* clear video inc */ + + port_set_inited(priv); + + mutex_unlock(&amstream_mutex); + return 0; + /*errors follow here */ +error5: + tsdemux_release(); +error4: + sub_port_release(port, psbuf); +error3: + video_port_release(priv, pvbuf, 0); +error2: + audio_port_release(port, pabuf, 0); +error1: + mutex_unlock(&amstream_mutex); + return r; +} + +static int amstream_port_release(struct port_priv_s *priv) +{ + struct stream_port_s *port = priv->port; + struct stream_buf_s *pvbuf = &bufs[BUF_TYPE_VIDEO]; + struct stream_buf_s *pabuf = &bufs[BUF_TYPE_AUDIO]; + struct stream_buf_s *psbuf = &bufs[BUF_TYPE_SUBTITLE]; + + if (has_hevc_vdec()) { + if (port->vformat == VFORMAT_HEVC + || port->vformat == VFORMAT_VP9) + pvbuf = &bufs[BUF_TYPE_HEVC]; + } + + if (port->type & PORT_TYPE_MPTS) { + tsync_pcr_stop(); + tsdemux_release(); + } + + if (port->type & PORT_TYPE_MPPS) + psparser_release(); + + if (port->type & PORT_TYPE_VIDEO) + video_port_release(priv, pvbuf, 0); + + if (port->type & PORT_TYPE_AUDIO) + audio_port_release(port, pabuf, 0); + + if (port->type & PORT_TYPE_SUB) + sub_port_release(port, psbuf); + + port->pcr_inited = 0; + port->flag = 0; + return 0; +} + +static void amstream_change_avid(struct stream_port_s *port) +{ + if (port->type & PORT_TYPE_MPTS) { + tsdemux_change_avid( + (port->flag & PORT_FLAG_VID) ? port->vid : 0xffff, + (port->flag & PORT_FLAG_AID) ? port->aid : 0xffff); + } + + if (port->type & PORT_TYPE_MPPS) { + psparser_change_avid( + (port->flag & PORT_FLAG_VID) ? port->vid : 0xffff, + (port->flag & PORT_FLAG_AID) ? port->aid : 0xffff); + } + +#ifdef CONFIG_AM_VDEC_REAL + if (port->type & PORT_TYPE_RM) { + rm_set_vasid( + (port->flag & PORT_FLAG_VID) ? port->vid : 0xffff, + (port->flag & PORT_FLAG_AID) ? port->aid : 0xffff); + } +#endif +} + +static void amstream_change_sid(struct stream_port_s *port) +{ + if (port->type & PORT_TYPE_MPTS) { + tsdemux_change_sid( + (port->flag & PORT_FLAG_SID) ? port->sid : 0xffff); + } + + if (port->type & PORT_TYPE_MPPS) { + psparser_change_sid( + (port->flag & PORT_FLAG_SID) ? port->sid : 0xffff); + } +} + +/**************************************************/ +static ssize_t amstream_vbuf_write(struct file *file, const char *buf, + size_t count, loff_t *ppos) +{ + struct port_priv_s *priv = (struct port_priv_s *)file->private_data; + struct stream_port_s *port = priv->port; + struct stream_buf_s *pbuf = NULL; + int r; + + + if (has_hevc_vdec()) { + pbuf = (port->type & PORT_TYPE_HEVC) ? &bufs[BUF_TYPE_HEVC] : + &bufs[BUF_TYPE_VIDEO]; + } else + pbuf = &bufs[BUF_TYPE_VIDEO]; + + if (!(port_get_inited(priv))) { + r = amstream_port_init(priv); + if (r < 0) + return r; + } + + if (port->flag & PORT_FLAG_DRM) + r = drm_write(file, pbuf, buf, count); + else + r = esparser_write(file, pbuf, buf, count); +#ifdef DATA_DEBUG + debug_file_write(buf, r); +#endif + + return r; +} + +static ssize_t amstream_vframe_write(struct file *file, const char *buf, + size_t count, loff_t *ppos) +{ + struct port_priv_s *priv = (struct port_priv_s *)file->private_data; + + return vdec_write_vframe(priv->vdec, buf, count); +} + +static ssize_t amstream_abuf_write(struct file *file, const char *buf, + size_t count, loff_t *ppos) +{ + struct port_priv_s *priv = (struct port_priv_s *)file->private_data; + struct stream_port_s *port = priv->port; + struct stream_buf_s *pbuf = &bufs[BUF_TYPE_AUDIO]; + int r; + + if (!(port_get_inited(priv))) { + r = amstream_port_init(priv); + if (r < 0) + return r; + } + + if (port->flag & PORT_FLAG_DRM) + r = drm_write(file, pbuf, buf, count); + else + r = esparser_write(file, pbuf, buf, count); + + return r; +} + +static ssize_t amstream_mpts_write(struct file *file, const char *buf, + size_t count, loff_t *ppos) +{ + struct port_priv_s *priv = (struct port_priv_s *)file->private_data; + struct stream_port_s *port = priv->port; + struct stream_buf_s *pabuf = &bufs[BUF_TYPE_AUDIO]; + struct stream_buf_s *pvbuf = NULL; + int r = 0; + + if (has_hevc_vdec()) { + pvbuf = (port->vformat == VFORMAT_HEVC || + port->vformat == VFORMAT_VP9) ? + &bufs[BUF_TYPE_HEVC] : &bufs[BUF_TYPE_VIDEO]; + } else + pvbuf = &bufs[BUF_TYPE_VIDEO]; + + if (!(port_get_inited(priv))) { + r = amstream_port_init(priv); + if (r < 0) + return r; + } +#ifdef DATA_DEBUG + debug_file_write(buf, count); +#endif + if (port->flag & PORT_FLAG_DRM) + r = drm_tswrite(file, pvbuf, pabuf, buf, count); + else + r = tsdemux_write(file, pvbuf, pabuf, buf, count); + return r; +} + +static ssize_t amstream_mpps_write(struct file *file, const char *buf, + size_t count, loff_t *ppos) +{ + struct port_priv_s *priv = (struct port_priv_s *)file->private_data; + struct stream_buf_s *pvbuf = &bufs[BUF_TYPE_VIDEO]; + struct stream_buf_s *pabuf = &bufs[BUF_TYPE_AUDIO]; + int r; + + if (!(port_get_inited(priv))) { + r = amstream_port_init(priv); + if (r < 0) + return r; + } + return psparser_write(file, pvbuf, pabuf, buf, count); +} + +#ifdef CONFIG_AM_VDEC_REAL +static ssize_t amstream_mprm_write(struct file *file, const char *buf, + size_t count, loff_t *ppos) +{ + struct port_priv_s *priv = (struct port_priv_s *)file->private_data; + struct stream_buf_s *pvbuf = &bufs[BUF_TYPE_VIDEO]; + struct stream_buf_s *pabuf = &bufs[BUF_TYPE_AUDIO]; + int r; + + if (!(port_get_inited(priv))) { + r = amstream_port_init(priv); + if (r < 0) + return r; + } + return rmparser_write(file, pvbuf, pabuf, buf, count); +} +#endif + +static ssize_t amstream_sub_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + u32 sub_rp, sub_wp, sub_start, data_size, res; + struct stream_buf_s *s_buf = &bufs[BUF_TYPE_SUBTITLE]; + + if (sub_port_inited == 0) + return 0; + + sub_rp = stbuf_sub_rp_get(); + sub_wp = stbuf_sub_wp_get(); + sub_start = stbuf_sub_start_get(); + + if (sub_wp == sub_rp || sub_rp == 0) + return 0; + + if (sub_wp > sub_rp) + data_size = sub_wp - sub_rp; + else + data_size = s_buf->buf_size - sub_rp + sub_wp; + + if (data_size > count) + data_size = count; + + if (sub_wp < sub_rp) { + int first_num = s_buf->buf_size - (sub_rp - sub_start); + + if (data_size <= first_num) { + res = copy_to_user((void *)buf, + (void *)(codec_mm_phys_to_virt(sub_rp)), + data_size); + if (res >= 0) + stbuf_sub_rp_set(sub_rp + data_size - res); + + return data_size - res; + } + + if (first_num > 0) { + res = copy_to_user((void *)buf, + (void *)(codec_mm_phys_to_virt(sub_rp)), + first_num); + if (res >= 0) { + stbuf_sub_rp_set(sub_rp + first_num - + res); + } + + return first_num - res; + } + + res = copy_to_user((void *)buf, + (void *)(codec_mm_phys_to_virt(sub_start)), + data_size - first_num); + + if (res >= 0) { + stbuf_sub_rp_set(sub_start + data_size - + first_num - res); + } + + return data_size - first_num - res; + } + res = + copy_to_user((void *)buf, + (void *)(codec_mm_phys_to_virt(sub_rp)), + data_size); + + if (res >= 0) + stbuf_sub_rp_set(sub_rp + data_size - res); + + return data_size - res; +} + +static ssize_t amstream_sub_write(struct file *file, const char *buf, + size_t count, loff_t *ppos) +{ + struct port_priv_s *priv = (struct port_priv_s *)file->private_data; + struct stream_buf_s *pbuf = &bufs[BUF_TYPE_SUBTITLE]; + int r; + + if (!(port_get_inited(priv))) { + r = amstream_port_init(priv); + if (r < 0) + return r; + } + r = esparser_write(file, pbuf, buf, count); + if (r < 0) + return r; + + wakeup_sub_poll(); + + return r; +} + +static unsigned int amstream_sub_poll(struct file *file, + poll_table *wait_table) +{ + poll_wait(file, &amstream_sub_wait, wait_table); + + if (atomic_read(&subdata_ready)) { + atomic_dec(&subdata_ready); + return POLLOUT | POLLWRNORM; + } + + return 0; +} + +void set_userdata_poc(struct userdata_poc_info_t poc) +{ + /* + *pr_err("id %d, slicetype %d\n", + * userdata_slicetype_wi, slicetype); + */ + userdata_poc_info[userdata_poc_wi] = poc; + userdata_poc_wi++; + if (userdata_poc_wi == USERDATA_FIFO_NUM) + userdata_poc_wi = 0; +} +EXPORT_SYMBOL(set_userdata_poc); + +void init_userdata_fifo(void) +{ + userdata_poc_ri = 0; + userdata_poc_wi = 0; +} +EXPORT_SYMBOL(init_userdata_fifo); + +int wakeup_userdata_poll(int wp, unsigned long start_phyaddr, int buf_size, + int data_length) +{ + struct stream_buf_s *userdata_buf = &bufs[BUF_TYPE_USERDATA]; + + userdata_buf->buf_start = start_phyaddr; + userdata_buf->buf_wp = wp; + userdata_buf->buf_size = buf_size; + atomic_set(&userdata_ready, 1); + userdata_length += data_length; + wake_up_interruptible(&amstream_userdata_wait); + return userdata_buf->buf_rp; +} +EXPORT_SYMBOL(wakeup_userdata_poll); + +static unsigned int amstream_userdata_poll(struct file *file, + poll_table *wait_table) +{ + poll_wait(file, &amstream_userdata_wait, wait_table); + if (atomic_read(&userdata_ready)) { + atomic_set(&userdata_ready, 0); + return POLLIN | POLLRDNORM; + } + return 0; +} + +static ssize_t amstream_userdata_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + u32 data_size, res, retVal = 0, buf_wp; + struct stream_buf_s *userdata_buf = &bufs[BUF_TYPE_USERDATA]; + + buf_wp = userdata_buf->buf_wp; + if (userdata_buf->buf_start == 0 || userdata_buf->buf_size == 0) + return 0; + if (buf_wp == userdata_buf->buf_rp) + return 0; + if (buf_wp > userdata_buf->buf_rp) + data_size = buf_wp - userdata_buf->buf_rp; + else { + data_size = + userdata_buf->buf_size - userdata_buf->buf_rp + buf_wp; + } + if (data_size > count) + data_size = count; + if (buf_wp < userdata_buf->buf_rp) { + int first_num = userdata_buf->buf_size - userdata_buf->buf_rp; + + if (data_size <= first_num) { + res = copy_to_user((void *)buf, + (void *)((userdata_buf->buf_rp + + userdata_buf->buf_start)), data_size); + userdata_buf->buf_rp += data_size - res; + retVal = data_size - res; + } else { + if (first_num > 0) { + res = copy_to_user((void *)buf, + (void *)((userdata_buf->buf_rp + + userdata_buf->buf_start)), first_num); + userdata_buf->buf_rp += first_num - res; + retVal = first_num - res; + } else { + res = copy_to_user((void *)buf, + (void *)((userdata_buf->buf_start)), + data_size - first_num); + userdata_buf->buf_rp = + data_size - first_num - res; + retVal = data_size - first_num - res; + } + } + } else { + res = copy_to_user((void *)buf, + (void *)((userdata_buf->buf_rp + + userdata_buf->buf_start)), + data_size); + userdata_buf->buf_rp += data_size - res; + retVal = data_size - res; + } + return retVal; +} + +static int amstream_open(struct inode *inode, struct file *file) +{ + s32 i; + struct stream_port_s *s; + struct stream_port_s *port = &ports[iminor(inode)]; + struct port_priv_s *priv; + + if (iminor(inode) >= amstream_port_num) + return -ENODEV; + + mutex_lock(&amstream_mutex); + + if (port->type & PORT_TYPE_VIDEO) { + for (s = &ports[0], i = 0; i < amstream_port_num; i++, s++) { + if (((s->type & PORT_TYPE_VIDEO) == 0) || + ((s->flag & PORT_FLAG_IN_USE) == 0)) + continue; + + if (((s->type & PORT_TYPE_DECODER_SCHED) == 0) || + ((s->type & PORT_TYPE_FRAME) == 0)) { + mutex_unlock(&amstream_mutex); + return -EBUSY; + } + } + } + + if ((port->flag & PORT_FLAG_IN_USE) && + ((port->type & PORT_TYPE_FRAME) == 0)) { + mutex_unlock(&amstream_mutex); + return -EBUSY; + } + + /* check other ports conflicts for audio */ + for (s = &ports[0], i = 0; i < amstream_port_num; i++, s++) { + if ((s->flag & PORT_FLAG_IN_USE) && + ((port->type) & (s->type) & PORT_TYPE_AUDIO)) { + mutex_unlock(&amstream_mutex); + return -EBUSY; + } + } + + priv = kzalloc(sizeof(struct port_priv_s), GFP_KERNEL); + if (priv == NULL) + return -ENOMEM; + + priv->port = port; + + if (get_cpu_type() >= MESON_CPU_MAJOR_ID_M6) { + + amports_switch_gate("clk_vdec_mux", 1); + amports_switch_gate("clk_hcodec_mux", 1); + amports_switch_gate("clk_hevc_mux", 1); + + /* TODO: mod gate */ + /* switch_mod_gate_by_name("demux", 1); */ + amports_switch_gate("demux", 1); + if (get_cpu_type() >= MESON_CPU_MAJOR_ID_M8) { + /* TODO: clc gate */ + /* CLK_GATE_ON(HIU_PARSER_TOP); */ + amports_switch_gate("parser_top", 1); + } + + if ((get_cpu_type() >= MESON_CPU_MAJOR_ID_M8) + && !is_meson_mtvd_cpu()) { + /* TODO: clc gate */ + /* CLK_GATE_ON(VPU_INTR); */ + amports_switch_gate("vpu_intr", 1); + } + + if (port->type & PORT_TYPE_VIDEO) { + /* TODO: mod gate */ + /* switch_mod_gate_by_name("vdec", 1); */ + amports_switch_gate("vdec", 1); + if (has_hevc_vdec()) { + if (port->type & + (PORT_TYPE_MPTS | PORT_TYPE_HEVC)) + vdec_poweron(VDEC_HEVC); + + if ((port->type & PORT_TYPE_HEVC) == 0) + vdec_poweron(VDEC_1); + } else { + if (get_cpu_type() >= MESON_CPU_MAJOR_ID_M8) + vdec_poweron(VDEC_1); + } + } + + if (port->type & PORT_TYPE_AUDIO) { + /* TODO: mod gate */ + /* switch_mod_gate_by_name("audio", 1); */ + amports_switch_gate("audio", 1); + } + } + + port->vid = 0; + port->aid = 0; + port->sid = 0; + port->pcrid = 0xffff; + file->f_op = port->fops; + file->private_data = priv; + + port->flag = PORT_FLAG_IN_USE; + port->pcr_inited = 0; +#ifdef DATA_DEBUG + debug_filp = filp_open(DEBUG_FILE_NAME, O_WRONLY, 0); + if (IS_ERR(debug_filp)) { + pr_err("amstream: open debug file failed\n"); + debug_filp = NULL; + } +#endif + mutex_unlock(&amstream_mutex); + + if (port->type & PORT_TYPE_VIDEO) { + int type = VDEC_TYPE_SINGLE; + + if (port->type & PORT_TYPE_DECODER_SCHED) + type = (port->type & PORT_TYPE_FRAME) ? + VDEC_TYPE_FRAME_BLOCK : + VDEC_TYPE_STREAM_PARSER; + + priv->vdec = vdec_create(type); + + if (priv->vdec == NULL) { + kfree(priv); + pr_err("amstream: vdec creation failed\n"); + return -ENOMEM; + } + } + + return 0; +} + +static int amstream_release(struct inode *inode, struct file *file) +{ + struct port_priv_s *priv = file->private_data; + struct stream_port_s *port = priv->port; + + if (iminor(inode) >= amstream_port_num) + return -ENODEV; + + mutex_lock(&amstream_mutex); + + if (port_get_inited(priv)) + amstream_port_release(priv); + + if (priv->vdec) { + vdec_release(priv->vdec); + priv->vdec = NULL; + } + + if ((port->type & (PORT_TYPE_AUDIO | PORT_TYPE_VIDEO)) == + PORT_TYPE_AUDIO) { + s32 i; + struct stream_port_s *s; + + for (s = &ports[0], i = 0; i < amstream_port_num; i++, s++) { + if ((s->flag & PORT_FLAG_IN_USE) + && (s->type & PORT_TYPE_VIDEO)) + break; + } + if (i == amstream_port_num) + timestamp_firstvpts_set(0); + } + port->flag = 0; + + /* /timestamp_pcrscr_set(0); */ + +#ifdef DATA_DEBUG + if (debug_filp) { + filp_close(debug_filp, current->files); + debug_filp = NULL; + debug_file_pos = 0; + } +#endif + + if (get_cpu_type() >= MESON_CPU_MAJOR_ID_M6) { + if (port->type & PORT_TYPE_VIDEO) { + if (get_cpu_type() >= MESON_CPU_MAJOR_ID_M8) { +#ifndef CONFIG_MULTI_DEC + if (has_hevc_vdec()) + vdec_poweroff(VDEC_HEVC); + + vdec_poweroff(VDEC_1); +#else + /* + *todo: power control based on + * active decoder instances + */ +#endif + } + /* TODO: mod gate */ + /* switch_mod_gate_by_name("vdec", 0); */ + amports_switch_gate("vdec", 0); + } + + if (port->type & PORT_TYPE_AUDIO) { + /* TODO: mod gate */ + /* switch_mod_gate_by_name("audio", 0); */ + /* amports_switch_gate("audio", 0); */ + } + + if (get_cpu_type() >= MESON_CPU_MAJOR_ID_M8 + && !is_meson_mtvd_cpu()) { + /* TODO: clc gate */ + /* /CLK_GATE_OFF(VPU_INTR); */ + amports_switch_gate("vpu_intr", 0); + } + + if (get_cpu_type() >= MESON_CPU_MAJOR_ID_M8) { + /* TODO: clc gate */ + /* CLK_GATE_OFF(HIU_PARSER_TOP); */ + amports_switch_gate("parser_top", 0); + } + /* TODO: mod gate */ + /* switch_mod_gate_by_name("demux", 0); */ + amports_switch_gate("demux", 0); + } + + kfree(priv); + + mutex_unlock(&amstream_mutex); + return 0; +} + +static long amstream_ioctl_get_version(struct port_priv_s *priv, + ulong arg) +{ + int version = (AMSTREAM_IOC_VERSION_FIRST & 0xffff) << 16 + | (AMSTREAM_IOC_VERSION_SECOND & 0xffff); + put_user(version, (u32 __user *)arg); + + return 0; +} +static long amstream_ioctl_get(struct port_priv_s *priv, ulong arg) +{ + struct stream_port_s *this = priv->port; + long r = 0; + + struct am_ioctl_parm parm; + + if (copy_from_user + ((void *)&parm, (void *)arg, + sizeof(parm))) + r = -EFAULT; + + switch (parm.cmd) { + case AMSTREAM_GET_SUB_LENGTH: + if ((this->type & PORT_TYPE_SUB) || + (this->type & PORT_TYPE_SUB_RD)) { + u32 sub_wp, sub_rp; + struct stream_buf_s *psbuf = &bufs[BUF_TYPE_SUBTITLE]; + int val; + + sub_wp = stbuf_sub_wp_get(); + sub_rp = stbuf_sub_rp_get(); + + if (sub_wp == sub_rp) + val = 0; + else if (sub_wp > sub_rp) + val = sub_wp - sub_rp; + else + val = psbuf->buf_size - (sub_rp - sub_wp); + parm.data_32 = val; + } else + r = -EINVAL; + break; + case AMSTREAM_GET_UD_LENGTH: + if (this->type & PORT_TYPE_USERDATA) { + parm.data_32 = userdata_length; + userdata_length = 0; + } else + r = -EINVAL; + break; + case AMSTREAM_GET_APTS_LOOKUP: + if (this->type & PORT_TYPE_AUDIO) { + u32 pts = 0, offset; + + offset = parm.data_32; + pts_lookup_offset(PTS_TYPE_AUDIO, offset, &pts, 300); + parm.data_32 = pts; + } + break; + case AMSTREAM_GET_FIRST_APTS_FLAG: + if (this->type & PORT_TYPE_AUDIO) { + parm.data_32 = first_pts_checkin_complete( + PTS_TYPE_AUDIO); + } + break; + case AMSTREAM_GET_APTS: + parm.data_32 = timestamp_apts_get(); + break; + case AMSTREAM_GET_VPTS: + parm.data_32 = timestamp_vpts_get(); + break; + case AMSTREAM_GET_PCRSCR: + parm.data_32 = timestamp_pcrscr_get(); + break; + case AMSTREAM_GET_LAST_CHECKIN_APTS: + parm.data_32 = get_last_checkin_pts(PTS_TYPE_AUDIO); + break; + case AMSTREAM_GET_LAST_CHECKIN_VPTS: + parm.data_32 = get_last_checkin_pts(PTS_TYPE_VIDEO); + break; + case AMSTREAM_GET_LAST_CHECKOUT_APTS: + parm.data_32 = get_last_checkout_pts(PTS_TYPE_AUDIO); + break; + case AMSTREAM_GET_LAST_CHECKOUT_VPTS: + parm.data_32 = get_last_checkout_pts(PTS_TYPE_VIDEO); + break; + case AMSTREAM_GET_SUB_NUM: + parm.data_32 = psparser_get_sub_found_num(); + break; + case AMSTREAM_GET_VIDEO_DELAY_LIMIT_MS: + parm.data_32 = bufs[BUF_TYPE_VIDEO].max_buffer_delay_ms; + break; + case AMSTREAM_GET_AUDIO_DELAY_LIMIT_MS: + parm.data_32 = bufs[BUF_TYPE_AUDIO].max_buffer_delay_ms; + break; + case AMSTREAM_GET_VIDEO_CUR_DELAY_MS: { + int delay; + + delay = calculation_stream_delayed_ms( + PTS_TYPE_VIDEO, NULL, NULL); + if (delay >= 0) + parm.data_32 = delay; + else + parm.data_32 = 0; + } + break; + + case AMSTREAM_GET_AUDIO_CUR_DELAY_MS: { + int delay; + + delay = calculation_stream_delayed_ms( + PTS_TYPE_AUDIO, NULL, NULL); + if (delay >= 0) + parm.data_32 = delay; + else + parm.data_32 = 0; + } + break; + case AMSTREAM_GET_AUDIO_AVG_BITRATE_BPS: { + int delay; + u32 avgbps; + + delay = calculation_stream_delayed_ms( + PTS_TYPE_AUDIO, NULL, &avgbps); + if (delay >= 0) + parm.data_32 = avgbps; + else + parm.data_32 = 0; + } + break; + case AMSTREAM_GET_VIDEO_AVG_BITRATE_BPS: { + int delay; + u32 avgbps; + + delay = calculation_stream_delayed_ms( + PTS_TYPE_VIDEO, NULL, &avgbps); + if (delay >= 0) + parm.data_32 = avgbps; + else + parm.data_32 = 0; + } + break; + case AMSTREAM_GET_ION_ID: + parm.data_32 = priv->vdec->vf_receiver_inst; + break; + default: + r = -ENOIOCTLCMD; + break; + } + /* pr_info("parm size:%d\n", sizeof(parm)); */ + if (r == 0) { + if (copy_to_user((void *)arg, &parm, sizeof(parm))) + r = -EFAULT; + } + + return r; + +} +static long amstream_ioctl_set(struct port_priv_s *priv, ulong arg) +{ + struct stream_port_s *this = priv->port; + struct am_ioctl_parm parm; + long r = 0; + + if (copy_from_user + ((void *)&parm, (void *)arg, + sizeof(parm))) + r = -EFAULT; + + + switch (parm.cmd) { + case AMSTREAM_SET_VB_START: + if ((this->type & PORT_TYPE_VIDEO) && + ((bufs[BUF_TYPE_VIDEO].flag & BUF_FLAG_IN_USE) == 0)) { + if (has_hevc_vdec()) + bufs[BUF_TYPE_HEVC].buf_start = parm.data_32; + bufs[BUF_TYPE_VIDEO].buf_start = parm.data_32; + } else + r = -EINVAL; + break; + case AMSTREAM_SET_VB_SIZE: + if ((this->type & PORT_TYPE_VIDEO) && + ((bufs[BUF_TYPE_VIDEO].flag & BUF_FLAG_IN_USE) == 0)) { + if (bufs[BUF_TYPE_VIDEO].flag & BUF_FLAG_ALLOC) { + if (has_hevc_vdec()) { + r = stbuf_change_size( + &bufs[BUF_TYPE_HEVC], + parm.data_32); + } + r = stbuf_change_size( + &bufs[BUF_TYPE_VIDEO], + parm.data_32); + } + } else if (this->type & PORT_TYPE_FRAME) { + /* todo: frame based set max buffer size */ + r = 0; + } else + r = -EINVAL; + break; + case AMSTREAM_SET_AB_START: + if ((this->type & PORT_TYPE_AUDIO) && + ((bufs[BUF_TYPE_AUDIO].flag & BUF_FLAG_IN_USE) == 0)) + bufs[BUF_TYPE_AUDIO].buf_start = parm.data_32; + else + r = -EINVAL; + break; + case AMSTREAM_SET_AB_SIZE: + if ((this->type & PORT_TYPE_AUDIO) && + ((bufs[BUF_TYPE_AUDIO].flag & BUF_FLAG_IN_USE) == 0)) { + if (bufs[BUF_TYPE_AUDIO].flag & BUF_FLAG_ALLOC) { + r = stbuf_change_size( + &bufs[BUF_TYPE_AUDIO], parm.data_32); + } + } else + r = -EINVAL; + break; + case AMSTREAM_SET_VFORMAT: + if ((this->type & PORT_TYPE_VIDEO) && + (parm.data_vformat < VFORMAT_MAX)) { + this->vformat = parm.data_vformat; + this->flag |= PORT_FLAG_VFORMAT; + + vdec_set_format(priv->vdec, this->vformat); + } else + r = -EINVAL; + break; + case AMSTREAM_SET_AFORMAT: + if ((this->type & PORT_TYPE_AUDIO) && + (parm.data_aformat < AFORMAT_MAX)) { + memset(&audio_dec_info, 0, + sizeof(struct audio_info)); + /* for new format,reset the audio info. */ + this->aformat = parm.data_aformat; + this->flag |= PORT_FLAG_AFORMAT; + } else + r = -EINVAL; + break; + case AMSTREAM_SET_VID: + if (this->type & PORT_TYPE_VIDEO) { + this->vid = parm.data_32; + this->flag |= PORT_FLAG_VID; + } else + r = -EINVAL; + + break; + case AMSTREAM_SET_AID: + if (this->type & PORT_TYPE_AUDIO) { + this->aid = parm.data_32; + this->flag |= PORT_FLAG_AID; + + if (port_get_inited(priv)) { + tsync_audio_break(1); + amstream_change_avid(this); + } + } else + r = -EINVAL; + break; + case AMSTREAM_SET_SID: + if (this->type & PORT_TYPE_SUB) { + this->sid = parm.data_32; + this->flag |= PORT_FLAG_SID; + + if (port_get_inited(priv)) + amstream_change_sid(this); + } else + r = -EINVAL; + + break; + case AMSTREAM_IOC_PCRID: + this->pcrid = parm.data_32; + this->pcr_inited = 1; + pr_err("set pcrid = 0x%x\n", this->pcrid); + break; + case AMSTREAM_SET_ACHANNEL: + if (this->type & PORT_TYPE_AUDIO) { + this->achanl = parm.data_32; + set_ch_num_info(parm.data_32); + } else + r = -EINVAL; + break; + case AMSTREAM_SET_SAMPLERATE: + if (this->type & PORT_TYPE_AUDIO) { + this->asamprate = parm.data_32; + set_sample_rate_info(parm.data_32); + } else + r = -EINVAL; + break; + case AMSTREAM_SET_DATAWIDTH: + if (this->type & PORT_TYPE_AUDIO) + this->adatawidth = parm.data_32; + else + r = -EINVAL; + break; + case AMSTREAM_SET_TSTAMP: + if ((this->type & (PORT_TYPE_AUDIO | PORT_TYPE_VIDEO)) == + ((PORT_TYPE_AUDIO | PORT_TYPE_VIDEO))) + r = -EINVAL; + else if (this->type & PORT_TYPE_FRAME) + r = vdec_set_pts(priv->vdec, parm.data_32); + else if (has_hevc_vdec() && this->type & PORT_TYPE_HEVC) + r = es_vpts_checkin(&bufs[BUF_TYPE_HEVC], + parm.data_32); + else if (this->type & PORT_TYPE_VIDEO) + r = es_vpts_checkin(&bufs[BUF_TYPE_VIDEO], + parm.data_32); + else if (this->type & PORT_TYPE_AUDIO) + r = es_apts_checkin(&bufs[BUF_TYPE_AUDIO], + parm.data_32); + break; + case AMSTREAM_SET_TSTAMP_US64: + if ((this->type & (PORT_TYPE_AUDIO | PORT_TYPE_VIDEO)) == + ((PORT_TYPE_AUDIO | PORT_TYPE_VIDEO))) + r = -EINVAL; + else { + u64 pts = parm.data_64; + + if (this->type & PORT_TYPE_FRAME) { + /* + *todo: check upper layer for decoder handler + * life sequence or multi-tasking management + */ + r = vdec_set_pts64(priv->vdec, pts); + } else if (has_hevc_vdec()) { + if (this->type & PORT_TYPE_HEVC) { + r = es_vpts_checkin_us64( + &bufs[BUF_TYPE_HEVC], pts); + } else if (this->type & PORT_TYPE_VIDEO) { + r = es_vpts_checkin_us64( + &bufs[BUF_TYPE_VIDEO], pts); + } else if (this->type & PORT_TYPE_AUDIO) { + r = es_vpts_checkin_us64( + &bufs[BUF_TYPE_AUDIO], pts); + } + } else { + if (this->type & PORT_TYPE_VIDEO) { + r = es_vpts_checkin_us64( + &bufs[BUF_TYPE_VIDEO], pts); + } else if (this->type & PORT_TYPE_AUDIO) { + r = es_vpts_checkin_us64( + &bufs[BUF_TYPE_AUDIO], pts); + } + } + } + break; + case AMSTREAM_PORT_INIT: + r = amstream_port_init(priv); + break; + case AMSTREAM_SET_TRICKMODE: + if ((this->type & PORT_TYPE_VIDEO) == 0) + return -EINVAL; + r = vdec_set_trickmode(priv->vdec, parm.data_32); + if (r == -1) + return -ENODEV; + break; + + case AMSTREAM_AUDIO_RESET: + if (this->type & PORT_TYPE_AUDIO) { + struct stream_buf_s *pabuf = &bufs[BUF_TYPE_AUDIO]; + + r = audio_port_reset(this, pabuf); + } else + r = -EINVAL; + + break; + case AMSTREAM_SUB_RESET: + if (this->type & PORT_TYPE_SUB) { + struct stream_buf_s *psbuf = &bufs[BUF_TYPE_SUBTITLE]; + + r = sub_port_reset(this, psbuf); + } else + r = -EINVAL; + break; + case AMSTREAM_DEC_RESET: + tsync_set_dec_reset(); + break; + case AMSTREAM_SET_TS_SKIPBYTE: + if (parm.data_32 >= 0) + tsdemux_set_skipbyte(parm.data_32); + else + r = -EINVAL; + break; + case AMSTREAM_SET_SUB_TYPE: + sub_type = parm.data_32; + break; + case AMSTREAM_SET_PCRSCR: + timestamp_pcrscr_set(parm.data_32); + break; + case AMSTREAM_SET_DEMUX: + tsdemux_set_demux(parm.data_32); + break; + case AMSTREAM_SET_VIDEO_DELAY_LIMIT_MS: + if (has_hevc_vdec()) + bufs[BUF_TYPE_HEVC].max_buffer_delay_ms = parm.data_32; + bufs[BUF_TYPE_VIDEO].max_buffer_delay_ms = parm.data_32; + break; + case AMSTREAM_SET_AUDIO_DELAY_LIMIT_MS: + bufs[BUF_TYPE_AUDIO].max_buffer_delay_ms = parm.data_32; + break; + case AMSTREAM_SET_DRMMODE: + if (parm.data_32 == 1) { + pr_err("set drmmode\n"); + this->flag |= PORT_FLAG_DRM; + } else { + this->flag &= (~PORT_FLAG_DRM); + pr_err("no drmmode\n"); + } + break; + case AMSTREAM_SET_APTS: { + unsigned int pts; + + pts = parm.data_32; + if (tsync_get_mode() == TSYNC_MODE_PCRMASTER) + tsync_pcr_set_apts(pts); + else + tsync_set_apts(pts); + break; + } + default: + r = -ENOIOCTLCMD; + break; + } + return r; +} +static long amstream_ioctl_get_ex(struct port_priv_s *priv, ulong arg) +{ + struct stream_port_s *this = priv->port; + long r = 0; + struct am_ioctl_parm_ex parm; + + if (copy_from_user + ((void *)&parm, (void *)arg, + sizeof(parm))) + r = -EFAULT; + + switch (parm.cmd) { + case AMSTREAM_GET_EX_VB_STATUS: + if (this->type & PORT_TYPE_VIDEO) { + struct am_ioctl_parm_ex *p = &parm; + struct stream_buf_s *buf = NULL; + + buf = (this->vformat == VFORMAT_HEVC || + this->vformat == VFORMAT_VP9) ? + &bufs[BUF_TYPE_HEVC] : + &bufs[BUF_TYPE_VIDEO]; + + if (p == NULL) { + r = -EINVAL; + break; + } + + if (this->type & PORT_TYPE_FRAME) { + struct vdec_input_status_s status; + + /* + *todo: check upper layer for decoder + * handler lifecycle + */ + if (priv->vdec == NULL) { + r = -EINVAL; + break; + } + + r = vdec_input_get_status(&priv->vdec->input, + &status); + if (r == 0) { + p->status.size = status.size; + p->status.data_len = status.data_len; + p->status.free_len = status.free_len; + p->status.read_pointer = + status.read_pointer; + } + break; + } + + p->status.size = stbuf_canusesize(buf); + p->status.data_len = stbuf_level(buf); + p->status.free_len = stbuf_space(buf); + p->status.read_pointer = stbuf_rp(buf); + } else + r = -EINVAL; + break; + case AMSTREAM_GET_EX_AB_STATUS: + if (this->type & PORT_TYPE_AUDIO) { + struct am_ioctl_parm_ex *p = &parm; + struct stream_buf_s *buf = &bufs[BUF_TYPE_AUDIO]; + + if (p == NULL) + r = -EINVAL; + + p->status.size = stbuf_canusesize(buf); + p->status.data_len = stbuf_level(buf); + p->status.free_len = stbuf_space(buf); + p->status.read_pointer = stbuf_rp(buf); + + } else + r = -EINVAL; + break; + case AMSTREAM_GET_EX_VDECSTAT: + if ((this->type & PORT_TYPE_VIDEO) == 0) { + pr_err("no video\n"); + return -EINVAL; + } + { + struct vdec_status vstatus; + struct am_ioctl_parm_ex *p = &parm; + + if (p == NULL) + return -EINVAL; + if (vdec_status(priv->vdec, &vstatus) == -1) + return -ENODEV; + p->vstatus.width = vstatus.width; + p->vstatus.height = vstatus.height; + p->vstatus.fps = vstatus.fps; + p->vstatus.error_count = vstatus.error_count; + p->vstatus.status = vstatus.status; + } + break; + case AMSTREAM_GET_EX_ADECSTAT: + if ((this->type & PORT_TYPE_AUDIO) == 0) { + pr_err("no audio\n"); + return -EINVAL; + } + if (amstream_adec_status == NULL) { + /* + *pr_err("no amstream_adec_status\n"); + *return -ENODEV; + */ + memset(&parm.astatus, 0, sizeof(parm.astatus)); + } else { + struct adec_status astatus; + struct am_ioctl_parm_ex *p = &parm; + + if (p == NULL) + return -EINVAL; + amstream_adec_status(&astatus); + p->astatus.channels = astatus.channels; + p->astatus.sample_rate = astatus.sample_rate; + p->astatus.resolution = astatus.resolution; + p->astatus.error_count = astatus.error_count; + p->astatus.status = astatus.status; + } + break; + + case AMSTREAM_GET_EX_UD_POC: + if (this->type & PORT_TYPE_USERDATA) { + struct userdata_poc_info_t userdata_poc = + userdata_poc_info[userdata_poc_ri]; + memcpy(&parm.data_userdata_info, + &userdata_poc, + sizeof(struct userdata_poc_info_t)); + + userdata_poc_ri++; + if (userdata_poc_ri == USERDATA_FIFO_NUM) + userdata_poc_ri = 0; + } else + r = -EINVAL; + break; + default: + r = -ENOIOCTLCMD; + break; + } + /* pr_info("parm size:%zx\n", sizeof(parm)); */ + if (r == 0) { + if (copy_to_user((void *)arg, &parm, sizeof(parm))) + r = -EFAULT; + } + return r; + +} +static long amstream_ioctl_set_ex(struct port_priv_s *priv, ulong arg) +{ + long r = 0; + return r; +} +static long amstream_ioctl_get_ptr(struct port_priv_s *priv, ulong arg) +{ + long r = 0; + + struct am_ioctl_parm_ptr parm; + + if (copy_from_user + ((void *)&parm, (void *)arg, + sizeof(parm))) + r = -EFAULT; + + switch (parm.cmd) { + case AMSTREAM_GET_PTR_SUB_INFO: + { + struct subtitle_info msub_info[MAX_SUB_NUM]; + struct subtitle_info *psub_info[MAX_SUB_NUM]; + int i; + + for (i = 0; i < MAX_SUB_NUM; i++) + psub_info[i] = &msub_info[i]; + + r = psparser_get_sub_info(psub_info); + + if (r == 0) { + memcpy(parm.pdata_sub_info, msub_info, + sizeof(struct subtitle_info) + * MAX_SUB_NUM); + } + } + break; + default: + r = -ENOIOCTLCMD; + break; + } + /* pr_info("parm size:%d\n", sizeof(parm)); */ + if (r == 0) { + if (copy_to_user((void *)arg, &parm, sizeof(parm))) + r = -EFAULT; + } + + return r; + +} +static long amstream_ioctl_set_ptr(struct port_priv_s *priv, ulong arg) +{ + struct stream_port_s *this = priv->port; + struct am_ioctl_parm_ptr parm; + long r = 0; + + if (copy_from_user + ((void *)&parm, (void *)arg, + sizeof(parm))) { + pr_err("[%s]%d, arg err\n", __func__, __LINE__); + r = -EFAULT; + } + switch (parm.cmd) { + case AMSTREAM_SET_PTR_AUDIO_INFO: + if ((this->type & PORT_TYPE_VIDEO) + || (this->type & PORT_TYPE_AUDIO)) { + if (parm.pdata_audio_info != NULL) + memcpy((void *)&audio_dec_info, + (void *)parm.pdata_audio_info, + sizeof(audio_dec_info)); + } else + r = -EINVAL; + break; + default: + r = -ENOIOCTLCMD; + break; + } + return r; +} + +static long amstream_do_ioctl_new(struct port_priv_s *priv, + unsigned int cmd, ulong arg) +{ + long r = 0; + struct stream_port_s *this = priv->port; + + switch (cmd) { + case AMSTREAM_IOC_GET_VERSION: + r = amstream_ioctl_get_version(priv, arg); + break; + case AMSTREAM_IOC_GET: + r = amstream_ioctl_get(priv, arg); + break; + case AMSTREAM_IOC_SET: + r = amstream_ioctl_set(priv, arg); + break; + case AMSTREAM_IOC_GET_EX: + r = amstream_ioctl_get_ex(priv, arg); + break; + case AMSTREAM_IOC_SET_EX: + r = amstream_ioctl_set_ex(priv, arg); + break; + case AMSTREAM_IOC_GET_PTR: + r = amstream_ioctl_get_ptr(priv, arg); + break; + case AMSTREAM_IOC_SET_PTR: + r = amstream_ioctl_set_ptr(priv, arg); + break; + case AMSTREAM_IOC_SYSINFO: + if (this->type & PORT_TYPE_VIDEO) + r = vdec_set_decinfo(priv->vdec, (void *)arg); + else + r = -EINVAL; + break; + default: + r = -ENOIOCTLCMD; + break; + } + + return r; +} + +static long amstream_do_ioctl_old(struct port_priv_s *priv, + unsigned int cmd, ulong arg) +{ + struct stream_port_s *this = priv->port; + long r = 0; + + switch (cmd) { + + case AMSTREAM_IOC_VB_START: + if ((this->type & PORT_TYPE_VIDEO) && + ((bufs[BUF_TYPE_VIDEO].flag & BUF_FLAG_IN_USE) == 0)) { + if (has_hevc_vdec()) + bufs[BUF_TYPE_HEVC].buf_start = arg; + bufs[BUF_TYPE_VIDEO].buf_start = arg; + } else + r = -EINVAL; + break; + + case AMSTREAM_IOC_VB_SIZE: + if ((this->type & PORT_TYPE_VIDEO) && + ((bufs[BUF_TYPE_VIDEO].flag & BUF_FLAG_IN_USE) == 0)) { + if (bufs[BUF_TYPE_VIDEO].flag & BUF_FLAG_ALLOC) { + if (has_hevc_vdec()) { + r = stbuf_change_size( + &bufs[BUF_TYPE_HEVC], arg); + } + r = stbuf_change_size( + &bufs[BUF_TYPE_VIDEO], arg); + } + } else + r = -EINVAL; + break; + + case AMSTREAM_IOC_AB_START: + if ((this->type & PORT_TYPE_AUDIO) && + ((bufs[BUF_TYPE_AUDIO].flag & BUF_FLAG_IN_USE) == 0)) + bufs[BUF_TYPE_AUDIO].buf_start = arg; + else + r = -EINVAL; + break; + + case AMSTREAM_IOC_AB_SIZE: + if ((this->type & PORT_TYPE_AUDIO) && + ((bufs[BUF_TYPE_AUDIO].flag & BUF_FLAG_IN_USE) == 0)) { + if (bufs[BUF_TYPE_AUDIO].flag & BUF_FLAG_ALLOC) { + r = stbuf_change_size( + &bufs[BUF_TYPE_AUDIO], arg); + } + } else + r = -EINVAL; + break; + + case AMSTREAM_IOC_VFORMAT: + if ((this->type & PORT_TYPE_VIDEO) && (arg < VFORMAT_MAX)) { + this->vformat = (enum vformat_e)arg; + this->flag |= PORT_FLAG_VFORMAT; + + vdec_set_format(priv->vdec, this->vformat); + } else + r = -EINVAL; + break; + + case AMSTREAM_IOC_AFORMAT: + if ((this->type & PORT_TYPE_AUDIO) && (arg < AFORMAT_MAX)) { + memset(&audio_dec_info, 0, + sizeof(struct audio_info)); + /* for new format,reset the audio info. */ + this->aformat = (enum aformat_e)arg; + this->flag |= PORT_FLAG_AFORMAT; + } else + r = -EINVAL; + break; + + case AMSTREAM_IOC_VID: + if (this->type & PORT_TYPE_VIDEO) { + this->vid = (u32) arg; + this->flag |= PORT_FLAG_VID; + } else + r = -EINVAL; + + break; + + case AMSTREAM_IOC_AID: + if (this->type & PORT_TYPE_AUDIO) { + this->aid = (u32) arg; + this->flag |= PORT_FLAG_AID; + + if (port_get_inited(priv)) { + tsync_audio_break(1); + amstream_change_avid(this); + } + } else + r = -EINVAL; + break; + + case AMSTREAM_IOC_SID: + if (this->type & PORT_TYPE_SUB) { + this->sid = (u32) arg; + this->flag |= PORT_FLAG_SID; + + if (port_get_inited(priv)) + amstream_change_sid(this); + } else + r = -EINVAL; + + break; + + case AMSTREAM_IOC_PCRID: + this->pcrid = (u32) arg; + this->pcr_inited = 1; + pr_err("set pcrid = 0x%x\n", this->pcrid); + break; + + case AMSTREAM_IOC_VB_STATUS: + if (this->type & PORT_TYPE_VIDEO) { + struct am_io_param para; + struct am_io_param *p = ¶ + 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; + { + 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_poc_ri == USERDATA_FIFO_NUM) + userdata_poc_ri = 0; + } else + r = -EINVAL; + break; + + case AMSTREAM_IOC_SET_DEC_RESET: + tsync_set_dec_reset(); + break; + + case AMSTREAM_IOC_TS_SKIPBYTE: + if ((int)arg >= 0) + tsdemux_set_skipbyte(arg); + else + r = -EINVAL; + break; + + case AMSTREAM_IOC_SUB_TYPE: + sub_type = (int)arg; + break; + + case AMSTREAM_IOC_APTS_LOOKUP: + if (this->type & PORT_TYPE_AUDIO) { + u32 pts = 0, offset; + + get_user(offset, (unsigned long __user *)arg); + pts_lookup_offset(PTS_TYPE_AUDIO, offset, &pts, 300); + put_user(pts, (int __user *)arg); + } + return 0; + case GET_FIRST_APTS_FLAG: + if (this->type & PORT_TYPE_AUDIO) { + put_user(first_pts_checkin_complete(PTS_TYPE_AUDIO), + (int __user *)arg); + } + break; + + case AMSTREAM_IOC_APTS: + put_user(timestamp_apts_get(), (int __user *)arg); + break; + + case AMSTREAM_IOC_VPTS: + put_user(timestamp_vpts_get(), (int __user *)arg); + break; + + case AMSTREAM_IOC_PCRSCR: + put_user(timestamp_pcrscr_get(), (int __user *)arg); + break; + + case AMSTREAM_IOC_SET_PCRSCR: + timestamp_pcrscr_set(arg); + break; + case AMSTREAM_IOC_GET_LAST_CHECKIN_APTS: + put_user(get_last_checkin_pts(PTS_TYPE_AUDIO), (int *)arg); + break; + case AMSTREAM_IOC_GET_LAST_CHECKIN_VPTS: + put_user(get_last_checkin_pts(PTS_TYPE_VIDEO), (int *)arg); + break; + case AMSTREAM_IOC_GET_LAST_CHECKOUT_APTS: + put_user(get_last_checkout_pts(PTS_TYPE_AUDIO), (int *)arg); + break; + case AMSTREAM_IOC_GET_LAST_CHECKOUT_VPTS: + put_user(get_last_checkout_pts(PTS_TYPE_VIDEO), (int *)arg); + break; + case AMSTREAM_IOC_SUB_NUM: + put_user(psparser_get_sub_found_num(), (int *)arg); + break; + + case AMSTREAM_IOC_SUB_INFO: + if (arg > 0) { + struct subtitle_info msub_info[MAX_SUB_NUM]; + struct subtitle_info *psub_info[MAX_SUB_NUM]; + int i; + + for (i = 0; i < MAX_SUB_NUM; i++) + psub_info[i] = &msub_info[i]; + + r = psparser_get_sub_info(psub_info); + + if (r == 0) { + if (copy_to_user((void __user *)arg, msub_info, + sizeof(struct subtitle_info) * MAX_SUB_NUM)) + r = -EFAULT; + } + } + break; + case AMSTREAM_IOC_SET_DEMUX: + tsdemux_set_demux((int)arg); + break; + case AMSTREAM_IOC_SET_VIDEO_DELAY_LIMIT_MS: + if (has_hevc_vdec()) + bufs[BUF_TYPE_HEVC].max_buffer_delay_ms = (int)arg; + bufs[BUF_TYPE_VIDEO].max_buffer_delay_ms = (int)arg; + break; + case AMSTREAM_IOC_SET_AUDIO_DELAY_LIMIT_MS: + bufs[BUF_TYPE_AUDIO].max_buffer_delay_ms = (int)arg; + break; + case AMSTREAM_IOC_GET_VIDEO_DELAY_LIMIT_MS: + put_user(bufs[BUF_TYPE_VIDEO].max_buffer_delay_ms, (int *)arg); + break; + case AMSTREAM_IOC_GET_AUDIO_DELAY_LIMIT_MS: + put_user(bufs[BUF_TYPE_AUDIO].max_buffer_delay_ms, (int *)arg); + break; + case AMSTREAM_IOC_GET_VIDEO_CUR_DELAY_MS: { + int delay; + + delay = calculation_stream_delayed_ms( + PTS_TYPE_VIDEO, NULL, NULL); + if (delay >= 0) + put_user(delay, (int *)arg); + else + put_user(0, (int *)arg); + } + break; + + case AMSTREAM_IOC_GET_AUDIO_CUR_DELAY_MS: { + int delay; + + delay = calculation_stream_delayed_ms(PTS_TYPE_AUDIO, NULL, + NULL); + if (delay >= 0) + put_user(delay, (int *)arg); + else + put_user(0, (int *)arg); + } + break; + case AMSTREAM_IOC_GET_AUDIO_AVG_BITRATE_BPS: { + int delay; + u32 avgbps; + + delay = calculation_stream_delayed_ms(PTS_TYPE_AUDIO, NULL, + &avgbps); + if (delay >= 0) + put_user(avgbps, (int *)arg); + else + put_user(0, (int *)arg); + break; + } + case AMSTREAM_IOC_GET_VIDEO_AVG_BITRATE_BPS: { + int delay; + u32 avgbps; + + delay = calculation_stream_delayed_ms(PTS_TYPE_VIDEO, NULL, + &avgbps); + if (delay >= 0) + put_user(avgbps, (int *)arg); + else + put_user(0, (int *)arg); + break; + } + case AMSTREAM_IOC_SET_DRMMODE: + if ((u32) arg == 1) { + pr_err("set drmmode\n"); + this->flag |= PORT_FLAG_DRM; + } else { + this->flag &= (~PORT_FLAG_DRM); + pr_err("no drmmode\n"); + } + break; + case AMSTREAM_IOC_SET_APTS: { + unsigned long pts; + + if (get_user(pts, (unsigned long __user *)arg)) { + pr_err + ("Get audio pts from user space fault!\n"); + return -EFAULT; + } + if (tsync_get_mode() == TSYNC_MODE_PCRMASTER) + tsync_pcr_set_apts(pts); + else + tsync_set_apts(pts); + break; + } + default: + r = -ENOIOCTLCMD; + break; + } + + return r; +} + +static long amstream_do_ioctl(struct port_priv_s *priv, + unsigned int cmd, ulong arg) +{ + long r = 0; + + switch (cmd) { + case AMSTREAM_IOC_GET_VERSION: + case AMSTREAM_IOC_GET: + case AMSTREAM_IOC_SET: + case AMSTREAM_IOC_GET_EX: + case AMSTREAM_IOC_SET_EX: + case AMSTREAM_IOC_GET_PTR: + case AMSTREAM_IOC_SET_PTR: + case AMSTREAM_IOC_SYSINFO: + r = amstream_do_ioctl_new(priv, cmd, arg); + break; + default: + r = amstream_do_ioctl_old(priv, cmd, arg); + break; + } + if (r != 0) + pr_err("amstream_do_ioctl error :%lx, %x\n", r, cmd); + + return r; +} +static long amstream_ioctl(struct file *file, unsigned int cmd, ulong arg) +{ + struct port_priv_s *priv = (struct port_priv_s *)file->private_data; + struct stream_port_s *this = priv->port; + + if (!this) + return -ENODEV; + + return amstream_do_ioctl(priv, cmd, arg); +} + +#ifdef CONFIG_COMPAT +struct dec_sysinfo32 { + + u32 format; + + u32 width; + + u32 height; + + u32 rate; + + u32 extra; + + u32 status; + + u32 ratio; + + compat_uptr_t param; + + u64 ratio64; +}; + +struct am_ioctl_parm_ptr32 { + union { + compat_uptr_t pdata_audio_info; + compat_uptr_t pdata_sub_info; + compat_uptr_t pointer; + char data[8]; + }; + u32 cmd; + char reserved[4]; +}; + +static long amstream_ioc_setget_ptr(struct port_priv_s *priv, + unsigned int cmd, struct am_ioctl_parm_ptr32 __user *arg) +{ + struct am_ioctl_parm_ptr __user *data; + struct am_ioctl_parm_ptr32 __user *data32 = arg; + int ret; + + data = compat_alloc_user_space(sizeof(*data)); + if (!access_ok(VERIFY_WRITE, data, sizeof(*data))) + return -EFAULT; + + if (put_user(data32->cmd, &data->cmd) || + put_user(compat_ptr(data32->pointer), &data->pointer)) + return -EFAULT; + + + ret = amstream_do_ioctl(priv, cmd, (unsigned long)data); + if (ret < 0) + return ret; + return 0; + +} + +static long amstream_set_sysinfo(struct port_priv_s *priv, + struct dec_sysinfo32 __user *arg) +{ + struct dec_sysinfo __user *data; + struct dec_sysinfo32 __user *data32 = arg; + int ret; + + data = compat_alloc_user_space(sizeof(*data)); + if (!access_ok(VERIFY_WRITE, data, sizeof(*data))) + return -EFAULT; + if (copy_in_user(data, data32, 7 * sizeof(u32))) + return -EFAULT; + if (put_user(compat_ptr(data32->param), &data->param)) + return -EFAULT; + if (copy_in_user(&data->ratio64, &data32->ratio64, + sizeof(data->ratio64))) + return -EFAULT; + + ret = amstream_do_ioctl(priv, AMSTREAM_IOC_SYSINFO, + (unsigned long)data); + if (ret < 0) + return ret; + + if (copy_in_user(&arg->format, &data->format, 7 * sizeof(u32)) || + copy_in_user(&arg->ratio64, &data->ratio64, + sizeof(arg->ratio64))) + return -EFAULT; + + return 0; +} +static long amstream_compat_ioctl(struct file *file, + unsigned int cmd, ulong arg) +{ + s32 r = 0; + struct port_priv_s *priv = (struct port_priv_s *)file->private_data; + + switch (cmd) { + case AMSTREAM_IOC_GET_VERSION: + case AMSTREAM_IOC_GET: + case AMSTREAM_IOC_SET: + case AMSTREAM_IOC_GET_EX: + case AMSTREAM_IOC_SET_EX: + return amstream_do_ioctl(priv, cmd, (ulong)compat_ptr(arg)); + case AMSTREAM_IOC_GET_PTR: + case AMSTREAM_IOC_SET_PTR: + return amstream_ioc_setget_ptr(priv, cmd, compat_ptr(arg)); + case AMSTREAM_IOC_SYSINFO: + return amstream_set_sysinfo(priv, compat_ptr(arg)); + default: + return amstream_do_ioctl(priv, cmd, (ulong)compat_ptr(arg)); + } + + return r; +} +#endif + +static ssize_t ports_show(struct class *class, struct class_attribute *attr, + char *buf) +{ + int i; + char *pbuf = buf; + struct stream_port_s *p = NULL; + + for (i = 0; i < amstream_port_num; i++) { + p = &ports[i]; + /*name */ + pbuf += sprintf(pbuf, "%s\t:\n", p->name); + /*type */ + pbuf += sprintf(pbuf, "\ttype:%d( ", p->type); + if (p->type & PORT_TYPE_VIDEO) + pbuf += sprintf(pbuf, "%s ", "Video"); + if (p->type & PORT_TYPE_AUDIO) + pbuf += sprintf(pbuf, "%s ", "Audio"); + if (p->type & PORT_TYPE_MPTS) + pbuf += sprintf(pbuf, "%s ", "TS"); + if (p->type & PORT_TYPE_MPPS) + pbuf += sprintf(pbuf, "%s ", "PS"); + if (p->type & PORT_TYPE_ES) + pbuf += sprintf(pbuf, "%s ", "ES"); + if (p->type & PORT_TYPE_RM) + pbuf += sprintf(pbuf, "%s ", "RM"); + if (p->type & PORT_TYPE_SUB) + pbuf += sprintf(pbuf, "%s ", "Subtitle"); + if (p->type & PORT_TYPE_SUB_RD) + pbuf += sprintf(pbuf, "%s ", "Subtitle_Read"); + if (p->type & PORT_TYPE_USERDATA) + pbuf += sprintf(pbuf, "%s ", "userdata"); + pbuf += sprintf(pbuf, ")\n"); + /*flag */ + pbuf += sprintf(pbuf, "\tflag:%d( ", p->flag); + if (p->flag & PORT_FLAG_IN_USE) + pbuf += sprintf(pbuf, "%s ", "Used"); + else + pbuf += sprintf(pbuf, "%s ", "Unused"); + if ((p->type & PORT_TYPE_VIDEO) == 0) { + if (p->flag & PORT_FLAG_INITED) + pbuf += sprintf(pbuf, "%s ", "inited"); + else + pbuf += sprintf(pbuf, "%s ", "uninited"); + } + pbuf += sprintf(pbuf, ")\n"); + /*others */ + pbuf += sprintf(pbuf, "\tVformat:%d\n", + (p->flag & PORT_FLAG_VFORMAT) ? p->vformat : -1); + pbuf += sprintf(pbuf, "\tAformat:%d\n", + (p->flag & PORT_FLAG_AFORMAT) ? p->aformat : -1); + pbuf += sprintf(pbuf, "\tVid:%d\n", + (p->flag & PORT_FLAG_VID) ? p->vid : -1); + pbuf += sprintf(pbuf, "\tAid:%d\n", + (p->flag & PORT_FLAG_AID) ? p->aid : -1); + pbuf += sprintf(pbuf, "\tSid:%d\n", + (p->flag & PORT_FLAG_SID) ? p->sid : -1); + pbuf += sprintf(pbuf, "\tPCRid:%d\n", + (p->pcr_inited == 1) ? p->pcrid : -1); + pbuf += sprintf(pbuf, "\tachannel:%d\n", p->achanl); + pbuf += sprintf(pbuf, "\tasamprate:%d\n", p->asamprate); + pbuf += sprintf(pbuf, "\tadatawidth:%d\n\n", p->adatawidth); + } + return pbuf - buf; +} + +static ssize_t bufs_show(struct class *class, struct class_attribute *attr, + char *buf) +{ + int i; + char *pbuf = buf; + struct stream_buf_s *p = NULL; + char buf_type[][12] = { "Video", "Audio", "Subtitle", + "UserData", "HEVC" }; + + for (i = 0; i < amstream_buf_num; i++) { + p = &bufs[i]; + /*type */ + pbuf += sprintf(pbuf, "%s buffer:", buf_type[p->type]); + /*flag */ + pbuf += sprintf(pbuf, "\tflag:%d( ", p->flag); + if (p->flag & BUF_FLAG_ALLOC) + pbuf += sprintf(pbuf, "%s ", "Alloc"); + else + pbuf += sprintf(pbuf, "%s ", "Unalloc"); + if (p->flag & BUF_FLAG_IN_USE) + pbuf += sprintf(pbuf, "%s ", "Used"); + else + pbuf += sprintf(pbuf, "%s ", "Noused"); + if (p->flag & BUF_FLAG_PARSER) + pbuf += sprintf(pbuf, "%s ", "Parser"); + else + pbuf += sprintf(pbuf, "%s ", "noParser"); + if (p->flag & BUF_FLAG_FIRST_TSTAMP) + pbuf += sprintf(pbuf, "%s ", "firststamp"); + else + pbuf += sprintf(pbuf, "%s ", "nofirststamp"); + pbuf += sprintf(pbuf, ")\n"); + /*buf stats */ + + pbuf += sprintf(pbuf, "\tbuf addr:%p\n", (void *)p->buf_start); + + if (p->type != BUF_TYPE_SUBTITLE) { + pbuf += sprintf(pbuf, "\tbuf size:%#x\n", p->buf_size); + pbuf += sprintf(pbuf, + "\tbuf canusesize:%#x\n", + p->canusebuf_size); + pbuf += sprintf(pbuf, + "\tbuf regbase:%#lx\n", p->reg_base); + + if (p->reg_base && p->flag & BUF_FLAG_IN_USE) { + if (get_cpu_type() >= MESON_CPU_MAJOR_ID_M6) { + /* TODO: mod gate */ + /* switch_mod_gate_by_name("vdec", 1);*/ + amports_switch_gate("vdec", 1); + } + pbuf += sprintf(pbuf, "\tbuf level:%#x\n", + stbuf_level(p)); + pbuf += sprintf(pbuf, "\tbuf space:%#x\n", + stbuf_space(p)); + pbuf += sprintf(pbuf, + "\tbuf read pointer:%#x\n", + stbuf_rp(p)); + if (get_cpu_type() >= MESON_CPU_MAJOR_ID_M6) { + /* TODO: mod gate */ + /* switch_mod_gate_by_name("vdec", 0);*/ + amports_switch_gate("vdec", 0); + } + } else + pbuf += sprintf(pbuf, "\tbuf no used.\n"); + + if (p->type == BUF_TYPE_USERDATA) { + pbuf += sprintf(pbuf, + "\tbuf write pointer:%#x\n", + p->buf_wp); + pbuf += sprintf(pbuf, + "\tbuf read pointer:%#x\n", + p->buf_rp); + } + } else { + u32 sub_wp, sub_rp, data_size; + + sub_wp = stbuf_sub_wp_get(); + sub_rp = stbuf_sub_rp_get(); + if (sub_wp >= sub_rp) + data_size = sub_wp - sub_rp; + else + data_size = p->buf_size - sub_rp + sub_wp; + pbuf += sprintf(pbuf, "\tbuf size:%#x\n", p->buf_size); + pbuf += + sprintf(pbuf, "\tbuf canusesize:%#x\n", + p->canusebuf_size); + pbuf += + sprintf(pbuf, "\tbuf start:%#x\n", + stbuf_sub_start_get()); + pbuf += sprintf(pbuf, + "\tbuf write pointer:%#x\n", sub_wp); + pbuf += sprintf(pbuf, + "\tbuf read pointer:%#x\n", sub_rp); + pbuf += sprintf(pbuf, "\tbuf level:%#x\n", data_size); + } + + pbuf += sprintf(pbuf, "\tbuf first_stamp:%#x\n", + p->first_tstamp); + pbuf += sprintf(pbuf, "\tbuf wcnt:%#x\n\n", p->wcnt); + pbuf += sprintf(pbuf, "\tbuf max_buffer_delay_ms:%dms\n", + p->max_buffer_delay_ms); + + if (p->reg_base && p->flag & BUF_FLAG_IN_USE) { + int calc_delayms = 0; + u32 bitrate = 0, avg_bitrate = 0; + + calc_delayms = calculation_stream_delayed_ms( + (p->type == BUF_TYPE_AUDIO) ? PTS_TYPE_AUDIO : + PTS_TYPE_VIDEO, + &bitrate, + &avg_bitrate); + + if (calc_delayms >= 0) { + pbuf += sprintf(pbuf, + "\tbuf current delay:%dms\n", + calc_delayms); + pbuf += sprintf(pbuf, + "\tbuf bitrate latest:%dbps,avg:%dbps\n", + bitrate, avg_bitrate); + pbuf += sprintf(pbuf, + "\tbuf time after last pts:%d ms\n", + calculation_stream_ext_delayed_ms + ((p->type == BUF_TYPE_AUDIO) ? PTS_TYPE_AUDIO : + PTS_TYPE_VIDEO)); + + pbuf += sprintf(pbuf, + "\tbuf time after last write data :%d ms\n", + (int)(jiffies_64 - + p->last_write_jiffies64) * 1000 / HZ); + } + } + if (p->write_thread) { + pbuf += sprintf(pbuf, + "\twrite thread:%d/%d,fifo %d:%d,passed:%d\n", + threadrw_buffer_level(p), + threadrw_buffer_size(p), + threadrw_datafifo_len(p), + threadrw_freefifo_len(p), + threadrw_passed_len(p) + ); + } + } + + return pbuf - buf; +} + +static ssize_t videobufused_show(struct class *class, + struct class_attribute *attr, char *buf) +{ + char *pbuf = buf; + struct stream_buf_s *p = NULL; + struct stream_buf_s *p_hevc = NULL; + + p = &bufs[0]; + if (has_hevc_vdec()) + p_hevc = &bufs[BUF_TYPE_HEVC]; + + if (p->flag & BUF_FLAG_IN_USE) + pbuf += sprintf(pbuf, "%d ", 1); + else if (has_hevc_vdec() && (p_hevc->flag & BUF_FLAG_IN_USE)) + pbuf += sprintf(pbuf, "%d ", 1); + else + pbuf += sprintf(pbuf, "%d ", 0); + return 1; +} + +static ssize_t vcodec_profile_show(struct class *class, + struct class_attribute *attr, char *buf) +{ + return vcodec_profile_read(buf); +} + +static int reset_canuse_buferlevel(int levelx10000) +{ + int i; + struct stream_buf_s *p = NULL; + + if (levelx10000 >= 0 && levelx10000 <= 10000) + use_bufferlevelx10000 = levelx10000; + else + use_bufferlevelx10000 = 10000; + for (i = 0; i < amstream_buf_num; i++) { + p = &bufs[i]; + p->canusebuf_size = ((p->buf_size / 1024) * + use_bufferlevelx10000 / 10000) * 1024; + p->canusebuf_size += 1023; + p->canusebuf_size &= ~1023; + if (p->canusebuf_size > p->buf_size) + p->canusebuf_size = p->buf_size; + } + return 0; +} + +static ssize_t show_canuse_buferlevel(struct class *class, + struct class_attribute *attr, char *buf) +{ + ssize_t size = sprintf(buf, + "use_bufferlevel=%d/10000[=(set range[ 0~10000])=\n", + use_bufferlevelx10000); + return size; +} + +static ssize_t store_canuse_buferlevel(struct class *class, + struct class_attribute *attr, + const char *buf, size_t size) +{ + unsigned val; + ssize_t ret; + + /*ret = sscanf(buf, "%d", &val);*/ + ret = kstrtoint(buf, 0, &val); + + if (ret != 0) + return -EINVAL; + val = val; + reset_canuse_buferlevel(val); + return size; +} + +static ssize_t store_maxdelay(struct class *class, + struct class_attribute *attr, + const char *buf, size_t size) +{ + unsigned val; + ssize_t ret; + int i; + + /*ret = sscanf(buf, "%d", &val);*/ + ret = kstrtoint(buf, 0, &val); + if (ret != 0) + return -EINVAL; + for (i = 0; i < amstream_buf_num; i++) + bufs[i].max_buffer_delay_ms = val; + return size; +} + +static ssize_t show_maxdelay(struct class *class, + struct class_attribute *attr, + char *buf) +{ + ssize_t size = 0; + + size += sprintf(buf, "%dms video max buffered data delay ms\n", + bufs[0].max_buffer_delay_ms); + size += sprintf(buf, "%dms audio max buffered data delay ms\n", + bufs[1].max_buffer_delay_ms); + return size; +} + +static struct class_attribute amstream_class_attrs[] = { + __ATTR_RO(ports), + __ATTR_RO(bufs), + __ATTR_RO(vcodec_profile), + __ATTR_RO(videobufused), + __ATTR(canuse_buferlevel, S_IRUGO | S_IWUSR | S_IWGRP, + show_canuse_buferlevel, store_canuse_buferlevel), + __ATTR(max_buffer_delay_ms, S_IRUGO | S_IWUSR | S_IWGRP, show_maxdelay, + store_maxdelay), + __ATTR_NULL +}; + +static struct class amstream_class = { + .name = "amstream", + .class_attrs = amstream_class_attrs, +}; + +int amstream_request_firmware_from_sys(const char *file_name, + char *buf, int size) +{ + const struct firmware *firmware; + int err = 0; + struct device *micro_dev; + + pr_info("try load %s ...", file_name); + micro_dev = device_create(&amstream_class, + NULL, MKDEV(AMSTREAM_MAJOR, 100), + NULL, "videodec"); + if (micro_dev == NULL) { + pr_err("device_create failed =%d\n", err); + return -1; + } + err = request_firmware(&firmware, file_name, micro_dev); + if (err < 0) { + pr_err("can't load the %s,err=%d\n", file_name, err); + goto error1; + } + if (firmware->size > size) { + pr_err("not enough memory size for audiodsp code\n"); + err = -ENOMEM; + goto release; + } + + memcpy(buf, (char *)firmware->data, firmware->size); + /*mb(); don't need it*/ + pr_err("load mcode size=%zd\n mcode name %s\n", firmware->size, + file_name); + err = firmware->size; +release: + release_firmware(firmware); +error1: + device_destroy(&amstream_class, MKDEV(AMSTREAM_MAJOR, 100)); + return err; +} + +/*static struct resource memobj;*/ +static int amstream_probe(struct platform_device *pdev) +{ + int i; + int r; + struct stream_port_s *st; + + pr_err("Amlogic A/V streaming port init\n"); + + amstream_port_num = MAX_AMSTREAM_PORT_NUM; + amstream_buf_num = BUF_MAX_NUM; +/* +* r = of_reserved_mem_device_init(&pdev->dev); +* if (r == 0) +* pr_info("of probe done"); +* else { +* r = -ENOMEM; +* return r; +* } +*/ + r = class_register(&amstream_class); + if (r) { + pr_err("amstream class create fail.\n"); + return r; + } + + r = astream_dev_register(); + if (r) + return r; + + r = register_chrdev(AMSTREAM_MAJOR, "amstream", &amstream_fops); + if (r < 0) { + pr_err("Can't allocate major for amstreaming device\n"); + + goto error2; + } + + amstream_dev_class = class_create(THIS_MODULE, DEVICE_NAME); + + for (st = &ports[0], i = 0; i < amstream_port_num; i++, st++) { + st->class_dev = device_create(amstream_dev_class, NULL, + MKDEV(AMSTREAM_MAJOR, i), NULL, + ports[i].name); + } + + amstream_adec_status = NULL; + if (tsdemux_class_register() != 0) { + r = (-EIO); + goto error3; + } + + init_waitqueue_head(&amstream_sub_wait); + init_waitqueue_head(&amstream_userdata_wait); + reset_canuse_buferlevel(10000); + amstream_pdev = pdev; + amports_clock_gate_init(&amstream_pdev->dev); + + clock_set_init(&amstream_pdev->dev); + + /*prealloc fetch buf to avoid no continue buffer later...*/ + stbuf_fetch_init(); + return 0; + + /* + * error4: + * tsdemux_class_unregister(); + */ +error3: + for (st = &ports[0], i = 0; i < amstream_port_num; i++, st++) + device_destroy(amstream_dev_class, MKDEV(AMSTREAM_MAJOR, i)); + class_destroy(amstream_dev_class); +error2: + unregister_chrdev(AMSTREAM_MAJOR, "amstream"); + /* error1: */ + astream_dev_unregister(); + return r; +} + +static int amstream_remove(struct platform_device *pdev) +{ + int i; + struct stream_port_s *st; + + if (bufs[BUF_TYPE_VIDEO].flag & BUF_FLAG_ALLOC) + stbuf_change_size(&bufs[BUF_TYPE_VIDEO], 0); + if (bufs[BUF_TYPE_AUDIO].flag & BUF_FLAG_ALLOC) + stbuf_change_size(&bufs[BUF_TYPE_AUDIO], 0); + stbuf_fetch_release(); + tsdemux_class_unregister(); + for (st = &ports[0], i = 0; i < amstream_port_num; i++, st++) + device_destroy(amstream_dev_class, MKDEV(AMSTREAM_MAJOR, i)); + + class_destroy(amstream_dev_class); + + unregister_chrdev(AMSTREAM_MAJOR, DEVICE_NAME); + + astream_dev_unregister(); + + amstream_adec_status = NULL; + + pr_err("Amlogic A/V streaming port release\n"); + + return 0; +} + +void set_adec_func(int (*adec_func)(struct adec_status *)) +{ + amstream_adec_status = adec_func; +} + +void wakeup_sub_poll(void) +{ + atomic_inc(&subdata_ready); + wake_up_interruptible(&amstream_sub_wait); +} + +int get_sub_type(void) +{ + return sub_type; +} + +/*get pes buffers */ + +struct stream_buf_s *get_stream_buffer(int id) +{ + if (id >= BUF_MAX_NUM) + return 0; + return &bufs[id]; +} + +static const struct of_device_id amlogic_mesonstream_dt_match[] = { + { + .compatible = "amlogic, codec, streambuf", + }, + {}, +}; + +static struct platform_driver amstream_driver = { + .probe = amstream_probe, + .remove = amstream_remove, + .driver = { + .owner = THIS_MODULE, + .name = "mesonstream", + .of_match_table = amlogic_mesonstream_dt_match, + } +}; +#if 0 +int amstream_module_init(void) +{ + if (platform_driver_register(&amstream_driver)) { + pr_err("failed to register amstream module\n"); + return -ENODEV; + } + return 0; +} +EXPORT_SYMBOL(amstream_module_init); + +void amstream_module_exit(void) +{ + platform_driver_unregister(&amstream_driver); +} +EXPORT_SYMBOL(amstream_module_exit); +#endif +#if 1 +static int __init amstream_module_init(void) +{ + if (platform_driver_register(&amstream_driver)) { + pr_err("failed to register amstream module\n"); + return -ENODEV; + } + return 0; +} + +static void __exit amstream_module_exit(void) +{ + platform_driver_unregister(&amstream_driver); +} +#endif + +#if 0 +static int amstream_mem_device_init(struct reserved_mem *rmem, + struct device *dev) +{ + struct resource *res; + int r; + + res = &memobj; + + res->start = (phys_addr_t) rmem->base; + res->end = res->start + (phys_addr_t) rmem->size - 1; + if (!res) { + pr_err( + "Can not get I/O memory, and will allocate stream buffer!\n"); + + if (stbuf_change_size(&bufs[BUF_TYPE_VIDEO], + DEFAULT_VIDEO_BUFFER_SIZE) != 0) { + r = (-ENOMEM); + goto error4; + } + if (stbuf_change_size + (&bufs[BUF_TYPE_AUDIO], + DEFAULT_AUDIO_BUFFER_SIZE) != 0) { + r = (-ENOMEM); + goto error5; + } + if (stbuf_change_size + (&bufs[BUF_TYPE_SUBTITLE], + DEFAULT_SUBTITLE_BUFFER_SIZE) != 0) { + r = (-ENOMEM); + goto error6; + } + } else { + bufs[BUF_TYPE_VIDEO].buf_start = res->start; + bufs[BUF_TYPE_VIDEO].buf_size = + resource_size(res) - DEFAULT_AUDIO_BUFFER_SIZE - + DEFAULT_SUBTITLE_BUFFER_SIZE; + bufs[BUF_TYPE_VIDEO].flag |= BUF_FLAG_IOMEM; + bufs[BUF_TYPE_VIDEO].default_buf_size = + bufs[BUF_TYPE_VIDEO].buf_size; + + bufs[BUF_TYPE_AUDIO].buf_start = + res->start + bufs[BUF_TYPE_VIDEO].buf_size; + bufs[BUF_TYPE_AUDIO].buf_size = DEFAULT_AUDIO_BUFFER_SIZE; + bufs[BUF_TYPE_AUDIO].flag |= BUF_FLAG_IOMEM; + + if (stbuf_change_size + (&bufs[BUF_TYPE_SUBTITLE], + DEFAULT_SUBTITLE_BUFFER_SIZE) != 0) { + r = (-ENOMEM); + goto error4; + } + } + + if (has_hevc_vdec()) { + bufs[BUF_TYPE_HEVC].buf_start = bufs[BUF_TYPE_VIDEO].buf_start; + bufs[BUF_TYPE_HEVC].buf_size = bufs[BUF_TYPE_VIDEO].buf_size; + + if (bufs[BUF_TYPE_VIDEO].flag & BUF_FLAG_IOMEM) + bufs[BUF_TYPE_HEVC].flag |= BUF_FLAG_IOMEM; + + bufs[BUF_TYPE_HEVC].default_buf_size = + bufs[BUF_TYPE_VIDEO].default_buf_size; + } + + if (stbuf_fetch_init() != 0) { + r = (-ENOMEM); + goto error7; + } + + return 0; + +error7: + if (bufs[BUF_TYPE_SUBTITLE].flag & BUF_FLAG_ALLOC) + stbuf_change_size(&bufs[BUF_TYPE_SUBTITLE], 0); +error6: + if (bufs[BUF_TYPE_AUDIO].flag & BUF_FLAG_ALLOC) + stbuf_change_size(&bufs[BUF_TYPE_AUDIO], 0); +error5: + if (bufs[BUF_TYPE_VIDEO].flag & BUF_FLAG_ALLOC) + stbuf_change_size(&bufs[BUF_TYPE_VIDEO], 0); +error4: + return 0; +} + +static const struct reserved_mem_ops rmem_amstream_ops = { + .device_init = amstream_mem_device_init, +}; + +static int __init amstream_mem_setup(struct reserved_mem *rmem) +{ + rmem->ops = &rmem_amstream_ops; + pr_err("share mem setup\n"); + + return 0; +} +RESERVEDMEM_OF_DECLARE(mesonstream, + "amlogic, stream-memory", amstream_mem_setup); + +#endif +#if 1 +module_init(amstream_module_init); +module_exit(amstream_module_exit); +#endif + +module_param(debugflags, uint, 0664); +MODULE_PARM_DESC(debugflags, "\n amstream debugflags\n"); + + +MODULE_DESCRIPTION("AMLOGIC streaming port driver"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Tim Yao <timyao@amlogic.com>"); diff --git a/drivers/stream_input/amports/amstream_profile.c b/drivers/stream_input/amports/amstream_profile.c new file mode 100644 index 0000000..d65ee5c --- a/dev/null +++ b/drivers/stream_input/amports/amstream_profile.c @@ -0,0 +1,54 @@ +/* + * drivers/amlogic/media/stream_input/amports/amstream_profile.c + * + * Copyright (C) 2016 Amlogic, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * +*/ + +#include <linux/kernel.h> +#include <linux/fs.h> +#include <linux/device.h> +#include <linux/interrupt.h> +#include <linux/amlogic/media/utils/amstream.h> +#include "amports_priv.h" + +static const struct codec_profile_t *vcodec_profile[SUPPORT_VDEC_NUM] = { 0 }; + +static int vcodec_profile_idx; + +ssize_t vcodec_profile_read(char *buf) +{ + char *pbuf = buf; + int i = 0; + + for (i = 0; i < vcodec_profile_idx; i++) { + pbuf += sprintf(pbuf, "%s:%s;\n", vcodec_profile[i]->name, + vcodec_profile[i]->profile); + } + + return pbuf - buf; +} + +int vcodec_profile_register(const struct codec_profile_t *vdec_profile) +{ + if (vcodec_profile_idx < SUPPORT_VDEC_NUM) { + vcodec_profile[vcodec_profile_idx] = vdec_profile; + vcodec_profile_idx++; + pr_debug("regist %s codec profile\n", vdec_profile->name); + + } + + return 0; +} +EXPORT_SYMBOL(vcodec_profile_register); + diff --git a/drivers/stream_input/parser/Makefile b/drivers/stream_input/parser/Makefile new file mode 100644 index 0000000..5fb0dce --- a/dev/null +++ b/drivers/stream_input/parser/Makefile @@ -0,0 +1,6 @@ +obj-y += thread_rw.o +obj-y += streambuf.o +obj-y += esparser.o +obj-y += tsdemux.o +obj-y += psparser.o +#obj-y += rmparser.o diff --git a/drivers/stream_input/parser/esparser.c b/drivers/stream_input/parser/esparser.c new file mode 100644 index 0000000..d9de02c --- a/dev/null +++ b/drivers/stream_input/parser/esparser.c @@ -0,0 +1,911 @@ +/* + * drivers/amlogic/media/stream_input/parser/esparser.c + * + * Copyright (C) 2016 Amlogic, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * +*/ + +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/interrupt.h> +#include <linux/wait.h> +#include <linux/sched.h> +#include <linux/fs.h> +#include <linux/mutex.h> +#include <linux/slab.h> +#include <linux/dma-mapping.h> +#include <linux/amlogic/media/frame_sync/ptsserv.h> + +#include <linux/uaccess.h> +#include <linux/atomic.h> + +/* #include <mach/am_regs.h> */ +#include <linux/delay.h> + +#include "../../frame_provider/decoder/utils/vdec.h" +#include <linux/amlogic/media/utils/vdec_reg.h> +#include "streambuf_reg.h" +#include "streambuf.h" +#include "esparser.h" +#include "../amports/amports_priv.h" +#include "thread_rw.h" + +#include <linux/amlogic/media/codec_mm/codec_mm.h> + + + +#define SAVE_SCR 0 + +#define ES_START_CODE_PATTERN 0x00000100 +#define ES_START_CODE_MASK 0xffffff00 +#define SEARCH_PATTERN_LEN 512 +#define ES_PARSER_POP READ_MPEG_REG(PFIFO_DATA) + +#define PARSER_WRITE (ES_WRITE | ES_PARSER_START) +#define PARSER_VIDEO (ES_TYPE_VIDEO) +#define PARSER_AUDIO (ES_TYPE_AUDIO) +#define PARSER_SUBPIC (ES_TYPE_SUBTITLE) +#define PARSER_PASSTHROUGH (ES_PASSTHROUGH | ES_PARSER_START) +#define PARSER_AUTOSEARCH (ES_SEARCH | ES_PARSER_START) +#define PARSER_DISCARD (ES_DISCARD | ES_PARSER_START) +#define PARSER_BUSY (ES_PARSER_BUSY) + +static unsigned char *search_pattern; +static dma_addr_t search_pattern_map; +static u32 audio_real_wp; +static u32 audio_buf_start; +static u32 audio_buf_end; + +static const char esparser_id[] = "esparser-id"; + +static DECLARE_WAIT_QUEUE_HEAD(wq); + + +static u32 search_done; +static u32 video_data_parsed; +static u32 audio_data_parsed; +static atomic_t esparser_use_count = ATOMIC_INIT(0); +static DEFINE_MUTEX(esparser_mutex); + +static inline u32 get_buf_wp(u32 type) +{ + if (type == BUF_TYPE_AUDIO) + return audio_real_wp; + else + return 0; +} +static inline u32 get_buf_start(u32 type) +{ + if (type == BUF_TYPE_AUDIO) + return audio_buf_start; + else + return 0; +} +static inline u32 get_buf_end(u32 type) +{ + if (type == BUF_TYPE_AUDIO) + return audio_buf_end; + else + return 0; +} +static void set_buf_wp(u32 type, u32 wp) +{ + if (type == BUF_TYPE_AUDIO) { + audio_real_wp = wp; + WRITE_MPEG_REG(AIU_MEM_AIFIFO_MAN_WP, wp/* & 0xffffff00*/); + } +} + +static irqreturn_t esparser_isr(int irq, void *dev_id) +{ + u32 int_status = READ_MPEG_REG(PARSER_INT_STATUS); + + WRITE_MPEG_REG(PARSER_INT_STATUS, int_status); + + if (int_status & PARSER_INTSTAT_SC_FOUND) { + WRITE_MPEG_REG(PFIFO_RD_PTR, 0); + WRITE_MPEG_REG(PFIFO_WR_PTR, 0); + search_done = 1; + wake_up_interruptible(&wq); + } + return IRQ_HANDLED; +} + +static inline u32 buf_wp(u32 type) +{ + u32 wp; + + if ((READ_MPEG_REG(PARSER_ES_CONTROL) & ES_VID_MAN_RD_PTR) == 0) { + wp = +#if 1/* MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON8 */ + (type == BUF_TYPE_HEVC) ? READ_VREG(HEVC_STREAM_WR_PTR) : +#endif + (type == BUF_TYPE_VIDEO) ? READ_VREG(VLD_MEM_VIFIFO_WP) : + (type == BUF_TYPE_AUDIO) ? + READ_MPEG_REG(AIU_MEM_AIFIFO_MAN_WP) : + READ_MPEG_REG(PARSER_SUB_START_PTR); + } else { + wp = +#if 1/* MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON8 */ + (type == BUF_TYPE_HEVC) ? READ_MPEG_REG(PARSER_VIDEO_WP) : +#endif + (type == BUF_TYPE_VIDEO) ? READ_MPEG_REG(PARSER_VIDEO_WP) : + (type == BUF_TYPE_AUDIO) ? + READ_MPEG_REG(AIU_MEM_AIFIFO_MAN_WP) : + READ_MPEG_REG(PARSER_SUB_START_PTR); + } + + return wp; +} + +static ssize_t _esparser_write(const char __user *buf, + size_t count, u32 type, int isphybuf) +{ + size_t r = count; + const char __user *p = buf; + + u32 len = 0; + u32 parser_type; + int ret; + u32 wp; + dma_addr_t dma_addr = 0; + + if (type == BUF_TYPE_HEVC) + parser_type = PARSER_VIDEO; + else if (type == BUF_TYPE_VIDEO) + parser_type = PARSER_VIDEO; + else if (type == BUF_TYPE_AUDIO) + parser_type = PARSER_AUDIO; + else + parser_type = PARSER_SUBPIC; + + wp = buf_wp(type); + + if (r > 0) { + if (isphybuf) + len = count; + else { + len = min_t(size_t, r, (size_t) FETCHBUF_SIZE); + + if (copy_from_user(fetchbuf, p, len)) + return -EFAULT; + dma_addr = dma_map_single( + amports_get_dma_device(), fetchbuf, + FETCHBUF_SIZE, DMA_TO_DEVICE); + if (dma_mapping_error(amports_get_dma_device(), + (dma_addr_t) dma_addr)) + return -EFAULT; + + } + + /* wmb(); don't need */ + /* reset the Write and read pointer to zero again */ + WRITE_MPEG_REG(PFIFO_RD_PTR, 0); + WRITE_MPEG_REG(PFIFO_WR_PTR, 0); + + WRITE_MPEG_REG_BITS(PARSER_CONTROL, len, ES_PACK_SIZE_BIT, + ES_PACK_SIZE_WID); + WRITE_MPEG_REG_BITS(PARSER_CONTROL, + parser_type | PARSER_WRITE | + PARSER_AUTOSEARCH, ES_CTRL_BIT, + ES_CTRL_WID); + + if (isphybuf) { + u32 buf_32 = (unsigned long)buf & 0xffffffff; + + WRITE_MPEG_REG(PARSER_FETCH_ADDR, buf_32); + } else { + WRITE_MPEG_REG(PARSER_FETCH_ADDR, dma_addr); + dma_unmap_single(amports_get_dma_device(), dma_addr, + FETCHBUF_SIZE, DMA_TO_DEVICE); + } + + search_done = 0; + if (!(isphybuf & TYPE_PATTERN)) { + WRITE_MPEG_REG(PARSER_FETCH_CMD, + (7 << FETCH_ENDIAN) | len); + WRITE_MPEG_REG(PARSER_FETCH_ADDR, search_pattern_map); + WRITE_MPEG_REG(PARSER_FETCH_CMD, + (7 << FETCH_ENDIAN) | SEARCH_PATTERN_LEN); + } else { + WRITE_MPEG_REG(PARSER_FETCH_CMD, + (7 << FETCH_ENDIAN) | (len + 512)); + } + ret = wait_event_interruptible_timeout(wq, search_done != 0, + HZ / 5); + if (ret == 0) { + WRITE_MPEG_REG(PARSER_FETCH_CMD, 0); + + if (wp == buf_wp(type)) + /*no data fetched */ + return -EAGAIN; + + pr_info("W Timeout, but fetch ok,"); + pr_info("type %d len=%d,wpdiff=%d, isphy %x\n", + type, len, wp - buf_wp(type), isphybuf); + + } else if (ret < 0) + return -ERESTARTSYS; + } + + if ((type == BUF_TYPE_VIDEO) + || (has_hevc_vdec() && (type == BUF_TYPE_HEVC))) + video_data_parsed += len; + else if (type == BUF_TYPE_AUDIO) + audio_data_parsed += len; + + return len; +} + +static ssize_t _esparser_write_s(const char __user *buf, + size_t count, u32 type) +{ + size_t r = count; + const char __user *p = buf; + u32 len = 0; + int ret; + u32 wp, buf_start, buf_end; + dma_addr_t buf_wp_map; + + if (type != BUF_TYPE_AUDIO) + WARN_ON(1);/*BUG();*/ + wp = get_buf_wp(type); + buf_end = get_buf_end(type) + 8; + buf_start = get_buf_start(type); + /*pr_info("write wp 0x%x, count %d, start 0x%x, end 0x%x\n",*/ + /* wp, (u32)count, buf_start, buf_end);*/ + if (wp + count > buf_end) { + ret = copy_from_user(codec_mm_phys_to_virt(wp), + p, buf_end - wp); + if (ret > 0) { + len += buf_end - wp - ret; + buf_wp_map = dma_map_single(amports_get_dma_device(), + codec_mm_phys_to_virt(wp), len, DMA_TO_DEVICE); + wp += len; + pr_info("copy from user not finished\n"); + dma_unmap_single(NULL, buf_wp_map, len, DMA_TO_DEVICE); + set_buf_wp(type, wp); + goto end_write; + } else if (ret == 0) { + len += buf_end - wp; + buf_wp_map = dma_map_single(amports_get_dma_device(), + codec_mm_phys_to_virt(wp), len, DMA_TO_DEVICE); + wp = buf_start; + r = count - len; + dma_unmap_single(NULL, buf_wp_map, len, DMA_TO_DEVICE); + set_buf_wp(type, wp); + } else { + pr_info("copy from user failed 1\n"); + pr_info("w wp 0x%x, count %d, start 0x%x end 0x%x\n", + wp, (u32)count, buf_start, buf_end); + return -EAGAIN; + } + } + ret = copy_from_user(codec_mm_phys_to_virt(wp), p + len, r); + if (ret >= 0) { + len += r - ret; + buf_wp_map = dma_map_single(amports_get_dma_device(), + codec_mm_phys_to_virt(wp), r - ret, DMA_TO_DEVICE); + + if (ret > 0) + pr_info("copy from user not finished 2\n"); + wp += r - ret; + dma_unmap_single(NULL, buf_wp_map, r - ret, DMA_TO_DEVICE); + set_buf_wp(type, wp); + } else { + pr_info("copy from user failed 2\n"); + return -EAGAIN; + } + +end_write: + if (type == BUF_TYPE_AUDIO) + audio_data_parsed += len; + + return len; +} + +s32 es_vpts_checkin_us64(struct stream_buf_s *buf, u64 us64) +{ + u32 passed; + + if (buf->write_thread) + passed = threadrw_dataoffset(buf); + else + passed = video_data_parsed; + return pts_checkin_offset_us64(PTS_TYPE_VIDEO, passed, us64); + +} + +s32 es_apts_checkin_us64(struct stream_buf_s *buf, u64 us64) +{ + u32 passed; + + if (buf->write_thread) + passed = threadrw_dataoffset(buf); + else + passed = audio_data_parsed; + return pts_checkin_offset_us64(PTS_TYPE_AUDIO, passed, us64); +} + +s32 es_vpts_checkin(struct stream_buf_s *buf, u32 pts) +{ +#if 0 + if (buf->first_tstamp == INVALID_PTS) { + buf->flag |= BUF_FLAG_FIRST_TSTAMP; + buf->first_tstamp = pts; + return 0; + } +#endif + u32 passed = video_data_parsed + threadrw_buffer_level(buf); + + return pts_checkin_offset(PTS_TYPE_VIDEO, passed, pts); + +} + +s32 es_apts_checkin(struct stream_buf_s *buf, u32 pts) +{ +#if 0 + if (buf->first_tstamp == INVALID_PTS) { + buf->flag |= BUF_FLAG_FIRST_TSTAMP; + buf->first_tstamp = pts; + + return 0; + } +#endif + u32 passed = audio_data_parsed + threadrw_buffer_level(buf); + + return pts_checkin_offset(PTS_TYPE_AUDIO, passed, pts); +} + +s32 esparser_init(struct stream_buf_s *buf, struct vdec_s *vdec) +{ + s32 r = 0; + u32 pts_type; + u32 parser_sub_start_ptr; + u32 parser_sub_end_ptr; + u32 parser_sub_rp; + bool first_use = false; + /* #if MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON8 */ + if (has_hevc_vdec() && (buf->type == BUF_TYPE_HEVC)) + pts_type = PTS_TYPE_HEVC; + else + /* #endif */ + if (buf->type == BUF_TYPE_VIDEO) + pts_type = PTS_TYPE_VIDEO; + else if (buf->type == BUF_TYPE_AUDIO) + pts_type = PTS_TYPE_AUDIO; + else if (buf->type == BUF_TYPE_SUBTITLE) + pts_type = PTS_TYPE_MAX; + else + return -EINVAL; + mutex_lock(&esparser_mutex); + parser_sub_start_ptr = READ_MPEG_REG(PARSER_SUB_START_PTR); + parser_sub_end_ptr = READ_MPEG_REG(PARSER_SUB_END_PTR); + parser_sub_rp = READ_MPEG_REG(PARSER_SUB_RP); + + buf->flag |= BUF_FLAG_PARSER; + + if (atomic_add_return(1, &esparser_use_count) == 1) { + first_use = true; + + if (fetchbuf == 0) { + pr_info("%s: no fetchbuf\n", __func__); + r = -ENOMEM; + goto Err_1; + } + + if (search_pattern == NULL) { + search_pattern = kcalloc(1, + SEARCH_PATTERN_LEN, + GFP_KERNEL); + + if (search_pattern == NULL) { + pr_err("%s: no search_pattern\n", __func__); + r = -ENOMEM; + goto Err_1; + } + + /* build a fake start code to get parser interrupt */ + search_pattern[0] = 0x00; + search_pattern[1] = 0x00; + search_pattern[2] = 0x01; + search_pattern[3] = 0xff; + + search_pattern_map = dma_map_single( + amports_get_dma_device(), + search_pattern, + SEARCH_PATTERN_LEN, + DMA_TO_DEVICE); + } + + /* reset PARSER with first esparser_init() call */ + WRITE_MPEG_REG(RESET1_REGISTER, RESET_PARSER); + + /* TS data path */ +#ifndef CONFIG_AM_DVB + WRITE_MPEG_REG(FEC_INPUT_CONTROL, 0); +#else + tsdemux_set_reset_flag(); +#endif + CLEAR_MPEG_REG_MASK(TS_HIU_CTL, 1 << USE_HI_BSF_INTERFACE); + CLEAR_MPEG_REG_MASK(TS_HIU_CTL_2, 1 << USE_HI_BSF_INTERFACE); + CLEAR_MPEG_REG_MASK(TS_HIU_CTL_3, 1 << USE_HI_BSF_INTERFACE); + + CLEAR_MPEG_REG_MASK(TS_FILE_CONFIG, (1 << TS_HIU_ENABLE)); + + WRITE_MPEG_REG(PARSER_CONFIG, + (10 << PS_CFG_PFIFO_EMPTY_CNT_BIT) | + (1 << PS_CFG_MAX_ES_WR_CYCLE_BIT) | + (16 << PS_CFG_MAX_FETCH_CYCLE_BIT)); + + WRITE_MPEG_REG(PFIFO_RD_PTR, 0); + WRITE_MPEG_REG(PFIFO_WR_PTR, 0); + + WRITE_MPEG_REG(PARSER_SEARCH_PATTERN, ES_START_CODE_PATTERN); + WRITE_MPEG_REG(PARSER_SEARCH_MASK, ES_START_CODE_MASK); + + WRITE_MPEG_REG(PARSER_CONFIG, + (10 << PS_CFG_PFIFO_EMPTY_CNT_BIT) | + (1 << PS_CFG_MAX_ES_WR_CYCLE_BIT) | + PS_CFG_STARTCODE_WID_24 | + PS_CFG_PFIFO_ACCESS_WID_8 | + /* single byte pop */ + (16 << PS_CFG_MAX_FETCH_CYCLE_BIT)); + + WRITE_MPEG_REG(PARSER_CONTROL, PARSER_AUTOSEARCH); + + } + /* #if MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON8 */ + /* hook stream buffer with PARSER */ + if (has_hevc_vdec() && (pts_type == PTS_TYPE_HEVC)) { + WRITE_MPEG_REG(PARSER_VIDEO_START_PTR, vdec->input.start); + WRITE_MPEG_REG(PARSER_VIDEO_END_PTR, vdec->input.start + + vdec->input.size - 8); + + if (vdec_stream_auto(vdec)) { + CLEAR_MPEG_REG_MASK(PARSER_ES_CONTROL, + ES_VID_MAN_RD_PTR); + + /* set vififo_vbuf_rp_sel=>hevc */ + WRITE_VREG(DOS_GEN_CTRL0, 3 << 1); + + /* set use_parser_vbuf_wp */ + SET_VREG_MASK(HEVC_STREAM_CONTROL, + (1 << 3) | (0 << 4)); + /* set stream_fetch_enable */ + SET_VREG_MASK(HEVC_STREAM_CONTROL, 1); + + /* set stream_buffer_hole with 256 bytes */ + SET_VREG_MASK(HEVC_STREAM_FIFO_CTL, + (1 << 29)); + } else { + SET_MPEG_REG_MASK(PARSER_ES_CONTROL, + ES_VID_MAN_RD_PTR); + WRITE_MPEG_REG(PARSER_VIDEO_WP, vdec->input.start); + WRITE_MPEG_REG(PARSER_VIDEO_RP, vdec->input.start); + } + video_data_parsed = 0; + } else + /* #endif */ + if (pts_type == PTS_TYPE_VIDEO) { + WRITE_MPEG_REG(PARSER_VIDEO_START_PTR, + vdec->input.start); + WRITE_MPEG_REG(PARSER_VIDEO_END_PTR, + vdec->input.start + vdec->input.size - 8); + if (vdec_stream_auto(vdec)) { + CLEAR_MPEG_REG_MASK(PARSER_ES_CONTROL, + ES_VID_MAN_RD_PTR); + WRITE_VREG(VLD_MEM_VIFIFO_BUF_CNTL, + MEM_BUFCTRL_INIT); + CLEAR_VREG_MASK(VLD_MEM_VIFIFO_BUF_CNTL, + MEM_BUFCTRL_INIT); + + if (has_hevc_vdec()) { + /* set vififo_vbuf_rp_sel=>vdec */ + WRITE_VREG(DOS_GEN_CTRL0, 0); + + } + } else { + SET_MPEG_REG_MASK(PARSER_ES_CONTROL, + ES_VID_MAN_RD_PTR); + WRITE_MPEG_REG(PARSER_VIDEO_WP, + vdec->input.start); + WRITE_MPEG_REG(PARSER_VIDEO_RP, + vdec->input.start); + } + video_data_parsed = 0; + } else if (pts_type == PTS_TYPE_AUDIO) { + /* set wp as buffer start */ + SET_MPEG_REG_MASK(AIU_MEM_AIFIFO_BUF_CNTL, + MEM_BUFCTRL_MANUAL); + WRITE_MPEG_REG(AIU_MEM_AIFIFO_MAN_RP, + READ_MPEG_REG(AIU_MEM_AIFIFO_START_PTR)); + WRITE_MPEG_REG_BITS(AIU_MEM_AIFIFO_CONTROL, 7, 3, 3); + SET_MPEG_REG_MASK(AIU_MEM_AIFIFO_BUF_CNTL, + MEM_BUFCTRL_INIT); + CLEAR_MPEG_REG_MASK(AIU_MEM_AIFIFO_BUF_CNTL, + MEM_BUFCTRL_INIT); + WRITE_MPEG_REG(AIU_MEM_AIFIFO_MAN_WP, + READ_MPEG_REG(AIU_MEM_AIFIFO_START_PTR)); + audio_data_parsed = 0; + audio_buf_start = + READ_MPEG_REG(AIU_MEM_AIFIFO_START_PTR); + audio_real_wp = audio_buf_start; + audio_buf_end = READ_MPEG_REG(AIU_MEM_AIFIFO_END_PTR); + } else if (buf->type == BUF_TYPE_SUBTITLE) { + WRITE_MPEG_REG(PARSER_SUB_START_PTR, + parser_sub_start_ptr); + WRITE_MPEG_REG(PARSER_SUB_END_PTR, + parser_sub_end_ptr); + WRITE_MPEG_REG(PARSER_SUB_RP, parser_sub_rp); + SET_MPEG_REG_MASK(PARSER_ES_CONTROL, + (7 << ES_SUB_WR_ENDIAN_BIT) | + ES_SUB_MAN_RD_PTR); + } + + if (pts_type < PTS_TYPE_MAX) { + r = pts_start(pts_type); + + if (r < 0) { + pr_info("esparser_init: pts_start failed\n"); + goto Err_1; + } + } +#if 0 + if (buf->flag & BUF_FLAG_FIRST_TSTAMP) { + if (buf->type == BUF_TYPE_VIDEO) + es_vpts_checkin(buf, buf->first_tstamp); + else if (buf->type == BUF_TYPE_AUDIO) + es_apts_checkin(buf, buf->first_tstamp); + + buf->flag &= ~BUF_FLAG_FIRST_TSTAMP; + } +#endif + + if (first_use) { + /*TODO irq */ + r = vdec_request_irq(PARSER_IRQ, esparser_isr, + "parser", (void *)esparser_id); + + if (r) { + pr_info("esparser_init: irq register failed.\n"); + goto Err_2; + } + + WRITE_MPEG_REG(PARSER_INT_STATUS, 0xffff); + WRITE_MPEG_REG(PARSER_INT_ENABLE, + PARSER_INTSTAT_SC_FOUND << + PARSER_INT_HOST_EN_BIT); + } + mutex_unlock(&esparser_mutex); + if (!(amports_get_debug_flags() & 1) && + !codec_mm_video_tvp_enabled()) { + int block_size = (buf->type == BUF_TYPE_AUDIO) ? + PAGE_SIZE << 2 : PAGE_SIZE << 4; + int buf_num = (buf->type == BUF_TYPE_AUDIO) ? + 5 : 10; + if (!(buf->type == BUF_TYPE_SUBTITLE)) + buf->write_thread = threadrw_alloc(buf_num, + block_size, + esparser_write_ex, + (buf->type == BUF_TYPE_AUDIO) ? 1 : 0); + /*manul mode for audio*/ + } + return 0; + +Err_2: + pts_stop(pts_type); + +Err_1: + atomic_dec(&esparser_use_count); + buf->flag &= ~BUF_FLAG_PARSER; + mutex_unlock(&esparser_mutex); + return r; +} + +void esparser_audio_reset_s(struct stream_buf_s *buf) +{ + ulong flags; + DEFINE_SPINLOCK(lock); + + spin_lock_irqsave(&lock, flags); + + SET_MPEG_REG_MASK(AIU_MEM_AIFIFO_BUF_CNTL, MEM_BUFCTRL_MANUAL); + WRITE_MPEG_REG(AIU_MEM_AIFIFO_MAN_RP, + READ_MPEG_REG(AIU_MEM_AIFIFO_START_PTR)); + WRITE_MPEG_REG_BITS(AIU_MEM_AIFIFO_CONTROL, 7, 3, 3); + SET_MPEG_REG_MASK(AIU_MEM_AIFIFO_BUF_CNTL, MEM_BUFCTRL_INIT); + CLEAR_MPEG_REG_MASK(AIU_MEM_AIFIFO_BUF_CNTL, MEM_BUFCTRL_INIT); + WRITE_MPEG_REG(AIU_MEM_AIFIFO_MAN_WP, + READ_MPEG_REG(AIU_MEM_AIFIFO_START_PTR)); + + buf->flag |= BUF_FLAG_PARSER; + + audio_data_parsed = 0; + audio_real_wp = READ_MPEG_REG(AIU_MEM_AIFIFO_START_PTR); + spin_unlock_irqrestore(&lock, flags); + +} + +void esparser_audio_reset(struct stream_buf_s *buf) +{ + ulong flags; + DEFINE_SPINLOCK(lock); + + spin_lock_irqsave(&lock, flags); + + WRITE_MPEG_REG(PARSER_AUDIO_WP, + READ_MPEG_REG(AIU_MEM_AIFIFO_START_PTR)); + WRITE_MPEG_REG(PARSER_AUDIO_RP, + READ_MPEG_REG(AIU_MEM_AIFIFO_START_PTR)); + + WRITE_MPEG_REG(PARSER_AUDIO_START_PTR, + READ_MPEG_REG(AIU_MEM_AIFIFO_START_PTR)); + WRITE_MPEG_REG(PARSER_AUDIO_END_PTR, + READ_MPEG_REG(AIU_MEM_AIFIFO_END_PTR)); + CLEAR_MPEG_REG_MASK(PARSER_ES_CONTROL, ES_AUD_MAN_RD_PTR); + + WRITE_MPEG_REG(AIU_MEM_AIFIFO_BUF_CNTL, MEM_BUFCTRL_INIT); + CLEAR_MPEG_REG_MASK(AIU_MEM_AIFIFO_BUF_CNTL, MEM_BUFCTRL_INIT); + + buf->flag |= BUF_FLAG_PARSER; + + audio_data_parsed = 0; + audio_real_wp = 0; + audio_buf_start = 0; + audio_buf_end = 0; + spin_unlock_irqrestore(&lock, flags); + +} + +void esparser_release(struct stream_buf_s *buf) +{ + u32 pts_type; + + /* check if esparser_init() is ever called */ + if ((buf->flag & BUF_FLAG_PARSER) == 0) + return; + + if (atomic_read(&esparser_use_count) == 0) { + pr_info + ("[%s:%d]###warning, esparser has been released already\n", + __func__, __LINE__); + return; + } + if (buf->write_thread) + threadrw_release(buf); + if (atomic_dec_and_test(&esparser_use_count)) { + WRITE_MPEG_REG(PARSER_INT_ENABLE, 0); + /*TODO irq */ + + vdec_free_irq(PARSER_IRQ, (void *)esparser_id); + + if (search_pattern) { + dma_unmap_single(amports_get_dma_device(), + search_pattern_map, + SEARCH_PATTERN_LEN, DMA_TO_DEVICE); + kfree(search_pattern); + search_pattern = NULL; + } + } + + if (has_hevc_vdec() && (buf->type == BUF_TYPE_HEVC)) + pts_type = PTS_TYPE_VIDEO; + else if (buf->type == BUF_TYPE_VIDEO) + pts_type = PTS_TYPE_VIDEO; + else if (buf->type == BUF_TYPE_AUDIO) + pts_type = PTS_TYPE_AUDIO; + else if (buf->type == BUF_TYPE_SUBTITLE) { + buf->flag &= ~BUF_FLAG_PARSER; + return; + } else + return; + + buf->flag &= ~BUF_FLAG_PARSER; + pts_stop(pts_type); +} + +ssize_t drm_write(struct file *file, struct stream_buf_s *stbuf, + const char __user *buf, size_t count) +{ + s32 r; + u32 len; + u32 realcount, totalcount; + u32 re_count = count; + u32 havewritebytes = 0; + u32 leftcount = 0; + + struct drm_info tmpmm; + struct drm_info *drm = &tmpmm; + u32 res = 0; + int isphybuf = 0; + unsigned long realbuf; + + if (buf == NULL || count == 0) + return -EINVAL; + if (stbuf->write_thread) { + r = threadrw_flush_buffers(stbuf); + if (r < 0) + pr_info("Warning. drm flush threadrw failed[%d]\n", r); + } + res = copy_from_user(drm, buf, sizeof(struct drm_info)); + if (res) { + pr_info("drm kmalloc failed res[%d]\n", res); + return -EFAULT; + } + + if ((drm->drm_flag & TYPE_DRMINFO) && (drm->drm_hasesdata == 0)) { + /* buf only has drminfo not have esdata; */ + realbuf = drm->drm_phy; + realcount = drm->drm_pktsize; + isphybuf = drm->drm_flag; + /* DRM_PRNT("drm_get_rawdata + *onlydrminfo drm->drm_hasesdata[0x%x] + * stbuf->type %d buf[0x%x]\n", + *drm->drm_hasesdata,stbuf->type,buf); + */ + } else if (drm->drm_hasesdata == 1) { /* buf is drminfo+es; */ + realcount = drm->drm_pktsize; + realbuf = (unsigned long)buf + sizeof(struct drm_info); + isphybuf = 0; + /* DRM_PRNT("drm_get_rawdata + * drminfo+es drm->drm_hasesdata[0x%x] + * stbuf->type %d\n",drm->drm_hasesdata,stbuf->type); + */ + } else { /* buf is hwhead; */ + realcount = count; + isphybuf = 0; + realbuf = (unsigned long)buf; + /* DRM_PRNT("drm_get_rawdata + * drm->drm_hasesdata[0x%x] + * len[%d] count[%d] realcout[%d]\n", + * drm->drm_hasesdata,len,count,realcount); + */ + } + + len = realcount; + count = realcount; + totalcount = realcount; + + while (len > 0) { + if (stbuf->type != BUF_TYPE_SUBTITLE + && stbuf_space(stbuf) < count) { + len = min(stbuf_canusesize(stbuf) / 8, len); + if (stbuf_space(stbuf) < len) { + r = stbuf_wait_space(stbuf, len); + /* write part data , not allow return ; */ + if ((r < leftcount) && (leftcount > 0)) + continue; + else if ((r < 0) && (leftcount == 0))/*full; */ + return -EAGAIN; + } + } + len = min_t(u32, len, count); + + mutex_lock(&esparser_mutex); + + if (stbuf->type != BUF_TYPE_AUDIO) + r = _esparser_write((const char __user *)realbuf, len, + stbuf->type, isphybuf); + else + r = _esparser_write_s((const char __user *)realbuf, len, + stbuf->type); + if (r < 0) { + pr_info("drm_write _esparser_write failed [%d]\n", r); + return r; + } + havewritebytes += r; + leftcount = totalcount - havewritebytes; + if (havewritebytes == totalcount) { + + mutex_unlock(&esparser_mutex); + break; /* write ok; */ + } else if ((len > 0) && (havewritebytes < totalcount)) { + DRM_PRNT + ("d writebytes[%d] want[%d] total[%d] real[%d]\n", + havewritebytes, len, totalcount, realcount); + len = len - r; /* write again; */ + realbuf = realbuf + r; + } else { + pr_info + ("e writebytes[%d] want[%d] total[%d] real[%d]\n", + havewritebytes, len, totalcount, realcount); + } + mutex_unlock(&esparser_mutex); + } + + return re_count; +} +/* +*flags: +*1:phy +*2:noblock +*/ +ssize_t esparser_write_ex(struct file *file, + struct stream_buf_s *stbuf, + const char __user *buf, size_t count, + int flags) +{ + + s32 r; + u32 len = count; + + if (buf == NULL || count == 0) + return -EINVAL; + + /*subtitle have no level to check, */ + if (stbuf->type != BUF_TYPE_SUBTITLE && stbuf_space(stbuf) < count) { + if ((flags & 2) || ((file != NULL) && + (file->f_flags & O_NONBLOCK))) { + len = stbuf_space(stbuf); + + if (len < 256) /* <1k.do eagain, */ + return -EAGAIN; + } else { + len = min(stbuf_canusesize(stbuf) / 8, len); + + if (stbuf_space(stbuf) < len) { + r = stbuf_wait_space(stbuf, len); + if (r < 0) + return r; + } + } + } + + stbuf->last_write_jiffies64 = jiffies_64; + + len = min_t(u32, len, count); + + mutex_lock(&esparser_mutex); + + if (stbuf->type == BUF_TYPE_AUDIO) + r = _esparser_write_s(buf, len, stbuf->type); + else + r = _esparser_write(buf, len, stbuf->type, flags & 1); + + mutex_unlock(&esparser_mutex); + + return r; +} +ssize_t esparser_write(struct file *file, + struct stream_buf_s *stbuf, + const char __user *buf, size_t count) +{ + if (stbuf->write_thread) + return threadrw_write(file, stbuf, buf, count); + return esparser_write_ex(file, stbuf, buf, count, 0); +} + + +void esparser_sub_reset(void) +{ + ulong flags; + DEFINE_SPINLOCK(lock); + u32 parser_sub_start_ptr; + u32 parser_sub_end_ptr; + + spin_lock_irqsave(&lock, flags); + + parser_sub_start_ptr = READ_MPEG_REG(PARSER_SUB_START_PTR); + parser_sub_end_ptr = READ_MPEG_REG(PARSER_SUB_END_PTR); + + WRITE_MPEG_REG(PARSER_SUB_START_PTR, parser_sub_start_ptr); + WRITE_MPEG_REG(PARSER_SUB_END_PTR, parser_sub_end_ptr); + WRITE_MPEG_REG(PARSER_SUB_RP, parser_sub_start_ptr); + WRITE_MPEG_REG(PARSER_SUB_WP, parser_sub_start_ptr); + SET_MPEG_REG_MASK(PARSER_ES_CONTROL, + (7 << ES_SUB_WR_ENDIAN_BIT) | ES_SUB_MAN_RD_PTR); + + spin_unlock_irqrestore(&lock, flags); +} diff --git a/drivers/stream_input/parser/esparser.h b/drivers/stream_input/parser/esparser.h new file mode 100644 index 0000000..62396a2 --- a/dev/null +++ b/drivers/stream_input/parser/esparser.h @@ -0,0 +1,149 @@ +/* + * drivers/amlogic/media/stream_input/parser/esparser.h + * + * Copyright (C) 2016 Amlogic, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * +*/ + +#ifndef ESPARSER_H +#define ESPARSER_H + +#include "../../frame_provider/decoder/utils/vdec.h" + +extern ssize_t drm_write(struct file *file, + struct stream_buf_s *stbuf, const char __user *buf, size_t count); + +extern s32 esparser_init(struct stream_buf_s *buf, struct vdec_s *vdec); +extern s32 esparser_init_s(struct stream_buf_s *buf); +extern void esparser_release(struct stream_buf_s *buf); +extern ssize_t esparser_write(struct file *file, + struct stream_buf_s *stbuf, const char __user *buf, size_t count); +extern ssize_t esparser_write_ex(struct file *file, + struct stream_buf_s *stbuf, + const char __user *buf, size_t count, int is_phy); + +extern s32 es_vpts_checkin_us64(struct stream_buf_s *buf, u64 us64); + +extern s32 es_apts_checkin_us64(struct stream_buf_s *buf, u64 us64); + +extern int es_vpts_checkin(struct stream_buf_s *buf, u32 pts); + +extern int es_apts_checkin(struct stream_buf_s *buf, u32 pts); + +extern void esparser_audio_reset(struct stream_buf_s *buf); +extern void esparser_audio_reset_s(struct stream_buf_s *buf); + +extern void esparser_sub_reset(void); + +#ifdef CONFIG_AM_DVB +extern int tsdemux_set_reset_flag(void); +#endif + +/* TODO: move to register headers */ +#define ES_PACK_SIZE_BIT 8 +#define ES_PACK_SIZE_WID 24 + +#define ES_CTRL_WID 8 +#define ES_CTRL_BIT 0 +#define ES_TYPE_MASK (3 << 6) +#define ES_TYPE_VIDEO (0 << 6) +#define ES_TYPE_AUDIO (1 << 6) +#define ES_TYPE_SUBTITLE (2 << 6) + +#define ES_WRITE (1<<5) +#define ES_PASSTHROUGH (1<<4) +#define ES_INSERT_BEFORE_ES_WRITE (1<<3) +#define ES_DISCARD (1<<2) +#define ES_SEARCH (1<<1) +#define ES_PARSER_START (1<<0) +#define ES_PARSER_BUSY (1<<0) + +#define PARSER_INTSTAT_FETCH_CMD (1<<7) +#define PARSER_INTSTAT_PARSE (1<<4) +#define PARSER_INTSTAT_DISCARD (1<<3) +#define PARSER_INTSTAT_INSZERO (1<<2) +#define PARSER_INTSTAT_ACT_NOSSC (1<<1) +#define PARSER_INTSTAT_SC_FOUND (1<<0) + +#define FETCH_CIR_BUF (1<<31) +#define FETCH_CHK_BUF_STOP (1<<30) +#define FETCH_PASSTHROUGH (1<<29) +#define FETCH_ENDIAN 27 +#define FETCH_PASSTHROUGH_TYPE_MASK (0x3<<27) +#define FETCH_ENDIAN_MASK (0x7<<27) +#define FETCH_BUF_SIZE_MASK (0x7ffffff) +#define FETCH_CMD_PTR_MASK 3 +#define FETCH_CMD_RD_PTR_BIT 5 +#define FETCH_CMD_WR_PTR_BIT 3 +#define FETCH_CMD_NUM_MASK 3 +#define FETCH_CMD_NUM_BIT 0 + +#define ES_COUNT_MASK 0xfff +#define ES_COUNT_BIT 20 +#define ES_REQ_PENDING (1<<19) +#define ES_PASSTHROUGH_EN (1<<18) +#define ES_PASSTHROUGH_TYPE_MASK (3<<16) +#define ES_PASSTHROUGH_TYPE_VIDEO (0<<16) +#define ES_PASSTHROUGH_TYPE_AUDIO (1<<16) +#define ES_PASSTHROUGH_TYPE_SUBTITLE (2<<16) +#define ES_WR_ENDIAN_MASK (0x7) +#define ES_SUB_WR_ENDIAN_BIT 9 +#define ES_SUB_MAN_RD_PTR (1<<8) +#define ES_AUD_WR_ENDIAN_BIT 5 +#define ES_AUD_MAN_RD_PTR (1<<4) +#define ES_VID_WR_ENDIAN_BIT 1 +#define ES_VID_MAN_RD_PTR (1<<0) + +#define PS_CFG_FETCH_DMA_URGENT (1<<31) +#define PS_CFG_STREAM_DMA_URGENT (1<<30) +#define PS_CFG_FORCE_PFIFO_REN (1<<29) +#define PS_CFG_PFIFO_PEAK_EN (1<<28) +#define PS_CFG_SRC_SEL_BIT 24 +#define PS_CFG_SRC_SEL_MASK (3<<PS_CFG_SRC_SEL_BIT) +#define PS_CFG_SRC_SEL_FETCH (0<<PS_CFG_SRC_SEL_BIT) +#define PS_CFG_SRC_SEL_AUX1 (1<<PS_CFG_SRC_SEL_BIT) /*from NDMA */ +#define PS_CFG_SRC_SEL_AUX2 (2<<PS_CFG_SRC_SEL_BIT) +#define PS_CFG_SRC_SEL_AUX3 (3<<PS_CFG_SRC_SEL_BIT) +#define PS_CFG_PFIFO_EMPTY_CNT_BIT 16 +#define PS_CFG_PFIFO_EMPTY_CNT_MASK 0xff +#define PS_CFG_MAX_ES_WR_CYCLE_BIT 12 +#define PS_CFG_MAX_ES_WR_CYCLE_MASK 0xf +#define PS_CFG_STARTCODE_WID_MASK (0x3<<10) +#define PS_CFG_STARTCODE_WID_8 (0x0<<10) +#define PS_CFG_STARTCODE_WID_16 (0x1<<10) +#define PS_CFG_STARTCODE_WID_24 (0x2<<10) +#define PS_CFG_STARTCODE_WID_32 (0x3<<10) +#define PS_CFG_PFIFO_ACCESS_WID_MASK (0x3<<8) +#define PS_CFG_PFIFO_ACCESS_WID_8 (0x0<<8) +#define PS_CFG_PFIFO_ACCESS_WID_16 (0x1<<8) +#define PS_CFG_PFIFO_ACCESS_WID_24 (0x2<<8) +#define PS_CFG_PFIFO_ACCESS_WID_32 (0x3<<8) +#define PS_CFG_MAX_FETCH_CYCLE_BIT 0 +#define PS_CFG_MAX_FETCH_CYCLE_MASK 0xff + +#define PARSER_INT_DISABLE_CNT_MASK 0xffff +#define PARSER_INT_DISABLE_CNT_BIT 16 +#define PARSER_INT_HOST_EN_MASK 0xff +#define PARSER_INT_HOST_EN_BIT 8 +#define PARSER_INT_AMRISC_EN_MASK 0xff +#define PARSER_INT_AMRISC_EN_BIT 0 +#define PARSER_INT_ALL 0xff + +#define RESET_PARSER (1<<8) +#define TS_HIU_ENABLE 5 +#define USE_HI_BSF_INTERFACE 7 + +#define DRM_PRNT(fmt, args...) +#define TRACE() pr_info("drm--[%s::%d]\n", __func__, __LINE__) + +#endif /* ESPARSER_H */ diff --git a/drivers/stream_input/parser/psparser.c b/drivers/stream_input/parser/psparser.c new file mode 100644 index 0000000..80e5001 --- a/dev/null +++ b/drivers/stream_input/parser/psparser.c @@ -0,0 +1,1158 @@ +/* + * drivers/amlogic/media/stream_input/parser/psparser.c + * + * Copyright (C) 2016 Amlogic, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * +*/ + +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/interrupt.h> +#include <linux/wait.h> +#include <linux/sched.h> +#include <linux/fs.h> +#include <linux/slab.h> +#include <linux/dma-mapping.h> +#include <linux/amlogic/media/frame_sync/ptsserv.h> +#include <linux/amlogic/media/utils/amstream.h> + +#include <linux/uaccess.h> +/* #include <mach/am_regs.h> */ +#include <linux/amlogic/media/utils/vdec_reg.h> +#include "streambuf_reg.h" +#include "streambuf.h" +#include "psparser.h" +#include "../amports/amports_priv.h" + + +#define TIMESTAMP_IONLY 1 +#define SAVE_SCR 0 + +#define MPEG_START_CODE_PATTERN (0x00000100L) +#define MPEG_START_CODE_MASK (0xffffff00L) +#define MAX_MPG_AUDIOPK_SIZE 0x1000 + +#define SUB_INSERT_START_CODE_HIGH 0x414d4c55 +#define SUB_INSERT_START_CODE_LOW 0xaa000000 + +#define PARSER_WRITE (ES_WRITE | ES_PARSER_START) +#define PARSER_VIDEO (ES_TYPE_VIDEO) +#define PARSER_AUDIO (ES_TYPE_AUDIO) +#define PARSER_SUBPIC (ES_TYPE_SUBTITLE) +#define PARSER_PASSTHROUGH (ES_PASSTHROUGH | ES_PARSER_START) +#define PARSER_AUTOSEARCH (ES_SEARCH | ES_PARSER_START) +#define PARSER_DISCARD (ES_DISCARD | ES_PARSER_START) +#define PARSER_BUSY (ES_PARSER_BUSY) + +#define PARSER_PARAMETER_LENGTH_BIT 16 +#define PARSER_PARAMETER_LOOP_BIT 24 + +#define PARSER_POP READ_MPEG_REG(PFIFO_DATA) +#define SET_BLOCK(size) \ +WRITE_MPEG_REG_BITS(PARSER_CONTROL, size, ES_PACK_SIZE_BIT, ES_PACK_SIZE_WID) +#define SET_DISCARD_SIZE(size) WRITE_MPEG_REG(PARSER_PARAMETER, size) + +#define VIDEO_AUTO_FLUSH +#ifdef VIDEO_AUTO_FLUSH +static u32 video_auto_flush_state; +#define VIDEO_AUTO_FLUSH_IDLE 0 +#define VIDEO_AUTO_FLUSH_MONITOR 1 +#define VIDEO_AUTO_FLUSH_TRIGGER 2 +#define VIDEO_AUTO_FLUSH_DONE 3 +#define VIDEO_AUTO_FLUSH_PTS_THRESHOLD 90000 +#define VIDEO_AUTO_FLUSH_BYTE_COUNT 1024 + +static s32 audio_last_pts; +static s32 audio_monitor_pts; +#endif + +enum { + SEARCH_START_CODE = 0, + SEND_VIDEO_SEARCH, + SEND_AUDIO_SEARCH, + SEND_SUBPIC_SEARCH, + DISCARD_SEARCH, + DISCARD_ONLY +#ifdef VIDEO_AUTO_FLUSH + , + SEARCH_START_CODE_VIDEO_FLUSH +#endif +}; + +enum { + AUDIO_FIRST_ACCESS_ARM = 0, + AUDIO_FIRST_ACCESS_POPING, + AUDIO_FIRST_ACCESS_DONE +}; + +static const char psparser_id[] = "psparser-id"; + +static DECLARE_WAIT_QUEUE_HEAD(wq); + +static struct tasklet_struct psparser_tasklet; +static u32 fetch_done; +static u8 audio_id, video_id, sub_id, sub_id_max; +static u32 audio_first_access; +static u32 packet_remaining; +static u32 video_data_parsed; +static u32 audio_data_parsed; +static u32 pts_equ_dts_flag; + +static unsigned first_apts, first_vpts; +static unsigned audio_got_first_pts, video_got_first_dts, sub_got_first_pts; +atomic_t sub_block_found = ATOMIC_INIT(0); + +#define DEBUG_VOB_SUB +#ifdef DEBUG_VOB_SUB +static u8 sub_found_num; +static struct subtitle_info *sub_info[MAX_SUB_NUM]; +#endif + +static bool ptsmgr_first_vpts_ready(void) +{ + return (video_got_first_dts != 0) ? true : false; +} + +static bool ptsmgr_first_apts_ready(void) +{ + return (audio_got_first_pts != 0) ? true : false; +} + +static void ptsmgr_vpts_checkin(u32 pts) +{ + if (video_got_first_dts == 0) { + video_got_first_dts = 1; + first_vpts = pts; + } + + pts_checkin_offset(PTS_TYPE_VIDEO, video_data_parsed, pts); +} + +static void ptsmgr_apts_checkin(u32 pts) +{ + if (audio_got_first_pts == 0) { + audio_got_first_pts = 1; + first_apts = pts; + } + /* apts_checkin(pts); */ + pts_checkin_offset(PTS_TYPE_AUDIO, audio_data_parsed, pts); + +#ifdef VIDEO_AUTO_FLUSH + audio_last_pts = pts; + + if ((video_auto_flush_state == VIDEO_AUTO_FLUSH_IDLE) + && ptsmgr_first_vpts_ready()) { + video_auto_flush_state = VIDEO_AUTO_FLUSH_MONITOR; + audio_monitor_pts = pts; + } + + if (video_auto_flush_state == VIDEO_AUTO_FLUSH_MONITOR) { + if ((audio_last_pts - audio_monitor_pts) > + VIDEO_AUTO_FLUSH_PTS_THRESHOLD) + video_auto_flush_state = VIDEO_AUTO_FLUSH_TRIGGER; + } +#endif +} + +static u32 parser_process(s32 type, s32 packet_len) +{ + s16 temp, header_len, misc_flags, i; + u32 pts = 0, dts = 0; + u32 pts_dts_flag = 0; + u16 invalid_pts = 0; + + temp = PARSER_POP; + packet_len--; + + if ((temp >> 6) == 0x02) { + /* mpeg-2 system */ + misc_flags = PARSER_POP; + header_len = PARSER_POP; + packet_len -= 2; + packet_len -= header_len; + + if ((misc_flags >> 6) > 1) { + /* PTS exist */ + pts = ((PARSER_POP >> 1) & 7) << 30; /* bit 32-30 */ + pts |= PARSER_POP << 22; /* bit 29-22 */ + pts |= (PARSER_POP >> 1) << 15; /* bit 21-15 */ + pts |= (PARSER_POP << 7); /* bit 14-07 */ + pts |= (PARSER_POP >> 1); /* bit 06-00 */ + header_len -= 5; + pts_dts_flag |= 2; + } + + if ((misc_flags >> 6) > 2) { + /* DTS exist */ + dts = ((PARSER_POP >> 1) & 7) << 30; /* bit 32-30 */ + dts |= PARSER_POP << 22; /* bit 29-22 */ + dts |= (PARSER_POP >> 1) << 15; /* bit 21-15 */ + dts |= (PARSER_POP << 7); /* bit 14-07 */ + dts |= (PARSER_POP >> 1); /* bit 06-00 */ + header_len -= 5; + pts_dts_flag |= 1; + } + + if (misc_flags & 0x20) { + /* ESCR_flag */ + PARSER_POP; + PARSER_POP; + PARSER_POP; + PARSER_POP; + PARSER_POP; + PARSER_POP; + header_len -= 5; + } + + if (misc_flags & 0x10) { + /* ES_rate_flag */ + PARSER_POP; + PARSER_POP; + PARSER_POP; + header_len -= 3; + } + + if (misc_flags & 0x08) { + /* DSM_trick_mode_flag */ + PARSER_POP; + header_len -= 1; + } + + if (misc_flags & 0x04) { + /* additional_copy_info_flag */ + PARSER_POP; + header_len -= 1; + } + + if (misc_flags & 0x02) { + /* PES_CRC_flag */ + PARSER_POP; + PARSER_POP; + header_len -= 2; + } + + if (misc_flags & 0x01) { + /* PES_extension_flag */ + misc_flags = PARSER_POP; + header_len--; + + if ((misc_flags & 0x80) && (header_len >= 128)) { + /* PES_private_data_flag */ + for (i = 0; i < 128; i++) + PARSER_POP; + + header_len -= 128; + } +#if 0 + if (misc_flags & 0x40) { + /* pack_header_field_flag */ + /* Invalid case */ + } +#endif + if (misc_flags & 0x20) { + /* program_packet_sequence_counter_flag */ + PARSER_POP; + PARSER_POP; + header_len -= 2; + } + + if (misc_flags & 0x10) { + /* PSTD_buffer_flag */ + PARSER_POP; + PARSER_POP; + header_len -= 2; + } + + if (misc_flags & 1) { + /* PES_extension_flag_2 */ + temp = PARSER_POP & 0x7f; + + while (temp) { + PARSER_POP; + temp--; + header_len--; + } + } + + while (header_len) { + PARSER_POP; + header_len--; + } + } + + while (header_len) { + PARSER_POP; + header_len--; + } + + } else { + /* mpeg-1 system */ + while (temp == 0xff) { + temp = PARSER_POP; + packet_len--; + } + + if ((temp >> 6) == 1) { + PARSER_POP; /* STD buffer size */ + temp = PARSER_POP; + packet_len -= 2; + } + + if (((temp >> 4) == 2) || ((temp >> 4) == 3)) { + pts = ((temp >> 1) & 7) << 30; /* bit 32-30 */ + pts |= PARSER_POP << 22; /* bit 29-22 */ + pts |= (PARSER_POP >> 1) << 15; /* bit 21-15 */ + pts |= (PARSER_POP << 7); /* bit 14-07 */ + pts |= (PARSER_POP >> 1); /* bit 06-00 */ + packet_len -= 4; + pts_dts_flag |= 2; + } + + if ((temp >> 4) == 3) { + dts = ((PARSER_POP >> 1) & 7) << 30; /* bit 32-30 */ + dts |= PARSER_POP << 22; /* bit 29-22 */ + dts |= (PARSER_POP >> 1) << 15; /* bit 21-15 */ + dts |= (PARSER_POP << 7); /* bit 14-07 */ + dts |= (PARSER_POP >> 1); /* bit 06-00 */ + packet_len -= 5; + pts_dts_flag |= 1; + } + } + + if ((pts == 0) && (dts == 0xffffffff)) { + invalid_pts = 1; + pr_info("invalid pts\n"); + } + + if (!packet_len) + return SEARCH_START_CODE; + + else if (type == 0) { +#ifdef VIDEO_AUTO_FLUSH + if (video_auto_flush_state == VIDEO_AUTO_FLUSH_MONITOR) + audio_monitor_pts = audio_last_pts; +#endif + + if ((pts_dts_flag) && (!invalid_pts)) { +#if TIMESTAMP_IONLY + if (!ptsmgr_first_vpts_ready()) { + if (pts_dts_flag & 2) + ptsmgr_vpts_checkin(pts); + else + ptsmgr_vpts_checkin(dts); + } else if ((pts_dts_flag & 3) == 3) { + if (pts_equ_dts_flag) { + if (dts == pts) + ptsmgr_vpts_checkin(pts); + } else { + if (dts == pts) + pts_equ_dts_flag = 1; + ptsmgr_vpts_checkin(pts); + } + } +#else + if (!ptsmgr_first_vpts_ready()) { + if (pts_dts_flag & 2) + ptsmgr_vpts_checkin(pts); + else + ptsmgr_vpts_checkin(dts); + } else if (pts_dts_flag & 2) + ptsmgr_vpts_checkin(pts); +#endif + } + + if (ptsmgr_first_vpts_ready() || invalid_pts) { + SET_BLOCK(packet_len); + video_data_parsed += packet_len; + return SEND_VIDEO_SEARCH; + + } else { + SET_DISCARD_SIZE(packet_len); + return DISCARD_SEARCH; + } + + } else if (type == 1) { + /* mpeg audio */ + if (pts_dts_flag & 2) + ptsmgr_apts_checkin(pts); + + if (ptsmgr_first_apts_ready()) { + SET_BLOCK(packet_len); + audio_data_parsed += packet_len; + return SEND_AUDIO_SEARCH; + + } else { + SET_DISCARD_SIZE(packet_len); + return DISCARD_SEARCH; + } + + } else if (type == 2) { + /* Private stream */ + temp = PARSER_POP; /* sub_stream_id */ + packet_len--; + + if (((temp & 0xf8) == 0xa0) && (temp == audio_id)) { + /* DVD_VIDEO Audio LPCM data */ + PARSER_POP; + temp = (PARSER_POP << 8) | PARSER_POP; + if (temp == 0) + temp = 4; + temp--; + packet_len -= 3; + + if (audio_first_access == AUDIO_FIRST_ACCESS_ARM) { + if (temp) { + packet_remaining = packet_len - temp; + SET_DISCARD_SIZE(temp); + audio_first_access = + AUDIO_FIRST_ACCESS_POPING; + return DISCARD_ONLY; + } + + audio_first_access = AUDIO_FIRST_ACCESS_DONE; + + if (packet_len) { + SET_BLOCK(packet_len); + audio_data_parsed += packet_len; + return SEND_AUDIO_SEARCH; + + } else + return SEARCH_START_CODE; + + } else { + PARSER_POP; + PARSER_POP; + PARSER_POP; + packet_len -= 3; + } + + if (pts_dts_flag & 2) + ptsmgr_apts_checkin(pts); + + if (ptsmgr_first_apts_ready()) { + SET_BLOCK(packet_len); + audio_data_parsed += packet_len; + return SEND_AUDIO_SEARCH; + + } else { + SET_DISCARD_SIZE(packet_len); + return DISCARD_SEARCH; + } + + } else if (((temp & 0xf8) == 0x80) && (temp == audio_id)) { + /* Audio AC3 data */ + PARSER_POP; + temp = (PARSER_POP << 8) | PARSER_POP; + packet_len -= 3; + + if (audio_first_access == AUDIO_FIRST_ACCESS_ARM) { + if (pts_dts_flag & 2) + ptsmgr_apts_checkin(pts); + + if ((temp > 2) && (packet_len > (temp - 2))) { + temp -= 2; + packet_remaining = packet_len - temp; + SET_DISCARD_SIZE(temp); + audio_first_access = + AUDIO_FIRST_ACCESS_POPING; + return DISCARD_ONLY; + } + + audio_first_access = AUDIO_FIRST_ACCESS_DONE; + + if (packet_len) { + SET_BLOCK(packet_len); + audio_data_parsed += packet_len; + return SEND_AUDIO_SEARCH; + + } else + return SEARCH_START_CODE; + } + + if (pts_dts_flag & 2) + ptsmgr_apts_checkin(pts); + + if (ptsmgr_first_apts_ready()) { + SET_BLOCK(packet_len); + audio_data_parsed += packet_len; + return SEND_AUDIO_SEARCH; + + } else { + SET_DISCARD_SIZE(packet_len); + return DISCARD_SEARCH; + } + + } else if (((temp & 0xf8) == 0x88) && (temp == audio_id)) { + /* Audio DTS data */ + PARSER_POP; + PARSER_POP; + PARSER_POP; + packet_len -= 3; + + if (audio_first_access == AUDIO_FIRST_ACCESS_ARM) + audio_first_access = AUDIO_FIRST_ACCESS_DONE; + + if (pts_dts_flag & 2) + ptsmgr_apts_checkin(pts); + + if (ptsmgr_first_apts_ready()) { + SET_BLOCK(packet_len); + audio_data_parsed += packet_len; + return SEND_AUDIO_SEARCH; + + } else { + SET_DISCARD_SIZE(packet_len); + return DISCARD_SEARCH; + } + } else if ((temp & 0xe0) == 0x20) { + if (temp > sub_id_max) + sub_id_max = temp; +#ifdef DEBUG_VOB_SUB + for (i = 0; i < sub_found_num; i++) { + if (!sub_info[i]) + break; + if (temp == sub_info[i]->id) + break; + } + if (i == sub_found_num && i < MAX_SUB_NUM) { + if (sub_info[sub_found_num]) { + sub_info[sub_found_num]->id = temp; + sub_found_num++; + pr_info + ("[%s]found new sub_id=0x%x (num %d)\n", + __func__, temp, sub_found_num); + } else { + pr_info + ("[%s]sub info NULL!\n", __func__); + } + } +#endif + + if (temp == sub_id) { + /* DVD sub-picture data */ + if (!packet_len) + return SEARCH_START_CODE; + + else { +#if 0 + if (pts_dts_flag & 2) + ptsmgr_spts_checkin(pts); + + if (ptsmgr_first_spts_ready()) { + SET_BLOCK(packet_len); + return SEND_SUBPIC_SEARCH; + + } else { + SET_DISCARD_SIZE(packet_len); + return DISCARD_SEARCH; + } +#else + if (pts_dts_flag & 2) + sub_got_first_pts = 1; + + if (sub_got_first_pts) { + pr_info + ("sub pts 0x%x, len %d\n", + pts, packet_len); + SET_BLOCK(packet_len); + WRITE_MPEG_REG + (PARSER_PARAMETER, + 16 << + PARSER_PARAMETER_LENGTH_BIT); + WRITE_MPEG_REG + (PARSER_INSERT_DATA, + SUB_INSERT_START_CODE_HIGH); + WRITE_MPEG_REG + (PARSER_INSERT_DATA, + SUB_INSERT_START_CODE_LOW | + get_sub_type()); + WRITE_MPEG_REG + (PARSER_INSERT_DATA, + packet_len); + WRITE_MPEG_REG + (PARSER_INSERT_DATA, pts); + atomic_set(&sub_block_found, 1); + return SEND_SUBPIC_SEARCH; + } + + SET_DISCARD_SIZE(packet_len); + return DISCARD_SEARCH; +#endif + } + } else { + SET_DISCARD_SIZE(packet_len); + return DISCARD_SEARCH; + } + } else { + SET_DISCARD_SIZE(packet_len); + return DISCARD_SEARCH; + } + + if (!packet_len) + return SEARCH_START_CODE; + + else { + SET_BLOCK(packet_len); + audio_data_parsed += packet_len; + return SEND_AUDIO_SEARCH; + } + } + + return SEARCH_START_CODE; +} + +static void on_start_code_found(int start_code) +{ + unsigned short packet_len; + unsigned short temp; + unsigned next_action; +#if SAVE_SCR + unsigned scr; +#endif + + if (atomic_read(&sub_block_found)) { + wakeup_sub_poll(); + atomic_set(&sub_block_found, 0); + } + + if (audio_first_access == AUDIO_FIRST_ACCESS_POPING) { + /* + *we are in the procedure of poping data for audio first + * access, continue with last packet + */ + audio_first_access = AUDIO_FIRST_ACCESS_DONE; + + if (packet_remaining) { + next_action = SEND_AUDIO_SEARCH; + SET_BLOCK(packet_remaining); + + } else + next_action = SEARCH_START_CODE; + + } else if (start_code == 0xba) { /* PACK_START_CODE */ + temp = PARSER_POP; + + if ((temp >> 6) == 0x01) { +#if SAVE_SCR + scr = ((temp >> 3) & 0x3) << 30; /* bit 31-30 */ + scr |= (temp & 0x3) << 28; /* bit 29-28 */ + scr |= (PARSER_POP) << 20; /* bit 27-20 */ + temp = PARSER_POP; + scr |= (temp >> 4) << 16; /* bit 19-16 */ + scr |= (temp & 7) << 13; /* bit 15-13 */ + scr |= (PARSER_POP) << 5; /* bit 12-05 */ + scr |= (PARSER_POP) >> 3; /* bit 04-00 */ +#else + PARSER_POP; + PARSER_POP; + PARSER_POP; + PARSER_POP; +#endif + PARSER_POP; + PARSER_POP; + PARSER_POP; + PARSER_POP; + temp = PARSER_POP & 7; + + while (temp) { /* stuff byte */ + PARSER_POP; + temp--; + } + + } else { + /* mpeg-1 Pack Header */ +#if SAVE_SCR + scr = ((temp >> 1) & 0x3) << 30; /* bit 31-30 */ + scr |= (PARSER_POP) << 22; /* bit 29-22 */ + scr |= (PARSER_POP >> 1) << 15; /* bit 21-15 */ + scr |= (PARSER_POP) << 7; /* bit 14-07 */ + scr |= (PARSER_POP >> 1); /* bit 06-00 */ +#else + PARSER_POP; + PARSER_POP; + PARSER_POP; + PARSER_POP; +#endif + } + +#ifdef VIDEO_AUTO_FLUSH + if (video_auto_flush_state == VIDEO_AUTO_FLUSH_TRIGGER) { + next_action = SEARCH_START_CODE_VIDEO_FLUSH; + video_auto_flush_state = VIDEO_AUTO_FLUSH_DONE; + } else +#endif + + next_action = SEARCH_START_CODE; + + } else { + packet_len = (PARSER_POP << 8) | PARSER_POP; + + if (start_code == video_id) + next_action = parser_process(0, packet_len); + + else if (start_code == audio_id) { + /* add mpeg audio packet length check */ + if (packet_len > MAX_MPG_AUDIOPK_SIZE) + next_action = SEARCH_START_CODE; + + else + next_action = parser_process(1, packet_len); + + } else if (start_code == 0xbb) { + SET_DISCARD_SIZE(packet_len); + next_action = DISCARD_SEARCH; + } else if (start_code == 0xbd) + next_action = parser_process(2, packet_len); + + else if (start_code == 0xbf) { + SET_DISCARD_SIZE(packet_len); + next_action = DISCARD_SEARCH; + } else if ((start_code < 0xc0) || (start_code > 0xc8)) + next_action = SEARCH_START_CODE; + + else if (packet_len) { + SET_DISCARD_SIZE(packet_len); + next_action = DISCARD_SEARCH; + + } else + next_action = SEARCH_START_CODE; + } + + switch (next_action) { + case SEARCH_START_CODE: + WRITE_MPEG_REG(PARSER_CONTROL, PARSER_AUTOSEARCH); + break; + + case SEND_VIDEO_SEARCH: + WRITE_MPEG_REG_BITS(PARSER_CONTROL, + PARSER_AUTOSEARCH | PARSER_VIDEO | + PARSER_WRITE, ES_CTRL_BIT, ES_CTRL_WID); + break; + + case SEND_AUDIO_SEARCH: + WRITE_MPEG_REG_BITS(PARSER_CONTROL, + PARSER_AUTOSEARCH | PARSER_AUDIO | + PARSER_WRITE, ES_CTRL_BIT, ES_CTRL_WID); + break; + + case SEND_SUBPIC_SEARCH: + WRITE_MPEG_REG_BITS(PARSER_CONTROL, + PARSER_AUTOSEARCH | PARSER_SUBPIC | + PARSER_WRITE | ES_INSERT_BEFORE_ES_WRITE, + ES_CTRL_BIT, ES_CTRL_WID); + break; + + case DISCARD_SEARCH: + WRITE_MPEG_REG_BITS(PARSER_CONTROL, + PARSER_AUTOSEARCH | PARSER_DISCARD, + ES_CTRL_BIT, ES_CTRL_WID); + break; + + case DISCARD_ONLY: + WRITE_MPEG_REG_BITS(PARSER_CONTROL, + PARSER_DISCARD, ES_CTRL_BIT, ES_CTRL_WID); + break; + +#ifdef VIDEO_AUTO_FLUSH + case SEARCH_START_CODE_VIDEO_FLUSH: + WRITE_MPEG_REG(PARSER_INSERT_DATA, 0xffffffff); + WRITE_MPEG_REG(PARSER_INSERT_DATA, 0xffffffff); + WRITE_MPEG_REG(PARSER_PARAMETER, + ((VIDEO_AUTO_FLUSH_BYTE_COUNT / + 8) << PARSER_PARAMETER_LOOP_BIT) | (8 << + PARSER_PARAMETER_LENGTH_BIT)); + WRITE_MPEG_REG(PARSER_CONTROL, + PARSER_AUTOSEARCH | PARSER_VIDEO | PARSER_WRITE | + ES_INSERT_BEFORE_ES_WRITE); + break; +#endif + } +} + +static void parser_tasklet(ulong data) +{ + s32 sc; + u32 int_status = READ_MPEG_REG(PARSER_INT_STATUS); + + WRITE_MPEG_REG(PARSER_INT_STATUS, int_status); + + if (int_status & PARSER_INTSTAT_FETCH_CMD) { + fetch_done = 1; + + wake_up_interruptible(&wq); + } + + if (int_status & PARSER_INTSTAT_SC_FOUND) { + sc = PARSER_POP; + + on_start_code_found(sc); + + } else if (int_status & PARSER_INTSTAT_DISCARD) + on_start_code_found(0); +} + +static irqreturn_t parser_isr(int irq, void *dev_id) +{ + tasklet_schedule(&psparser_tasklet); + + return IRQ_HANDLED; +} + +static ssize_t _psparser_write(const char __user *buf, size_t count) +{ + size_t r = count; + const char __user *p = buf; + u32 len; + int ret; + dma_addr_t dma_addr = 0; + + if (r > 0) { + len = min_t(size_t, r, FETCHBUF_SIZE); + if (copy_from_user(fetchbuf, p, len)) + return -EFAULT; + + dma_addr = + dma_map_single(amports_get_dma_device(), + fetchbuf, FETCHBUF_SIZE, DMA_TO_DEVICE); + if (dma_mapping_error(amports_get_dma_device(), dma_addr)) + return -EFAULT; + + + fetch_done = 0; + + wmb(); /* Ensure fetchbuf contents visible */ + + WRITE_MPEG_REG(PARSER_FETCH_ADDR, dma_addr); + + WRITE_MPEG_REG(PARSER_FETCH_CMD, (7 << FETCH_ENDIAN) | len); + dma_unmap_single(amports_get_dma_device(), dma_addr, + FETCHBUF_SIZE, DMA_TO_DEVICE); + ret = + wait_event_interruptible_timeout(wq, fetch_done != 0, + HZ / 10); + if (ret == 0) { + WRITE_MPEG_REG(PARSER_FETCH_CMD, 0); + pr_info("write timeout, retry\n"); + return -EAGAIN; + } else if (ret < 0) + return -ERESTARTSYS; + + p += len; + r -= len; + } + + return count - r; +} + +s32 psparser_init(u32 vid, u32 aid, u32 sid, struct vdec_s *vdec) +{ + s32 r; + u32 parser_sub_start_ptr; + u32 parser_sub_end_ptr; + u32 parser_sub_rp; + +#ifdef DEBUG_VOB_SUB + u8 i; + + for (i = 0; i < MAX_SUB_NUM; i++) { + sub_info[i] = kzalloc(sizeof(struct subtitle_info), GFP_KERNEL); + if (!sub_info[i]) { + pr_info + ("[psparser_init]alloc for subtitle info failed\n"); + } else + sub_info[i]->id = -1; + } + sub_found_num = 0; +#endif + parser_sub_start_ptr = READ_MPEG_REG(PARSER_SUB_START_PTR); + parser_sub_end_ptr = READ_MPEG_REG(PARSER_SUB_END_PTR); + parser_sub_rp = READ_MPEG_REG(PARSER_SUB_RP); + + video_id = vid; + audio_id = aid; + sub_id = sid; + audio_got_first_pts = 0; + video_got_first_dts = 0; + sub_got_first_pts = 0; + first_apts = 0; + first_vpts = 0; + pts_equ_dts_flag = 0; + +#ifdef VIDEO_AUTO_FLUSH + video_auto_flush_state = VIDEO_AUTO_FLUSH_IDLE; +#endif + + pr_info("video 0x%x, audio 0x%x, sub 0x%x\n", video_id, audio_id, + sub_id); + if (fetchbuf == 0) { + pr_info("%s: no fetchbuf\n", __func__); + return -ENOMEM; + } + + WRITE_MPEG_REG(RESET1_REGISTER, RESET_PARSER); + + /* TS data path */ +#ifndef CONFIG_AM_DVB + WRITE_MPEG_REG(FEC_INPUT_CONTROL, 0); +#else + tsdemux_set_reset_flag(); +#endif + CLEAR_MPEG_REG_MASK(TS_HIU_CTL, 1 << USE_HI_BSF_INTERFACE); + CLEAR_MPEG_REG_MASK(TS_HIU_CTL_2, 1 << USE_HI_BSF_INTERFACE); + CLEAR_MPEG_REG_MASK(TS_HIU_CTL_3, 1 << USE_HI_BSF_INTERFACE); + CLEAR_MPEG_REG_MASK(TS_FILE_CONFIG, (1 << TS_HIU_ENABLE)); + + /* hook stream buffer with PARSER */ + WRITE_MPEG_REG(PARSER_VIDEO_START_PTR, vdec->input.start); + WRITE_MPEG_REG(PARSER_VIDEO_END_PTR, + vdec->input.start + vdec->input.size - 8); + + if (vdec_stream_auto(vdec)) { + CLEAR_MPEG_REG_MASK(PARSER_ES_CONTROL, ES_VID_MAN_RD_PTR); + WRITE_VREG(VLD_MEM_VIFIFO_BUF_CNTL, MEM_BUFCTRL_INIT); + CLEAR_VREG_MASK(VLD_MEM_VIFIFO_BUF_CNTL, MEM_BUFCTRL_INIT); + } else { + SET_MPEG_REG_MASK(PARSER_ES_CONTROL, ES_VID_MAN_RD_PTR); + WRITE_MPEG_REG(PARSER_VIDEO_WP, vdec->input.start); + WRITE_MPEG_REG(PARSER_VIDEO_RP, vdec->input.start); + } + + WRITE_MPEG_REG(PARSER_AUDIO_START_PTR, + READ_MPEG_REG(AIU_MEM_AIFIFO_START_PTR)); + WRITE_MPEG_REG(PARSER_AUDIO_END_PTR, + READ_MPEG_REG(AIU_MEM_AIFIFO_END_PTR)); + CLEAR_MPEG_REG_MASK(PARSER_ES_CONTROL, ES_AUD_MAN_RD_PTR); + + WRITE_MPEG_REG(PARSER_CONFIG, + (10 << PS_CFG_PFIFO_EMPTY_CNT_BIT) | + (1 << PS_CFG_MAX_ES_WR_CYCLE_BIT) | + (16 << PS_CFG_MAX_FETCH_CYCLE_BIT)); + + WRITE_VREG(VLD_MEM_VIFIFO_BUF_CNTL, MEM_BUFCTRL_INIT); + CLEAR_VREG_MASK(VLD_MEM_VIFIFO_BUF_CNTL, MEM_BUFCTRL_INIT); + + WRITE_MPEG_REG(AIU_MEM_AIFIFO_BUF_CNTL, MEM_BUFCTRL_INIT); + CLEAR_MPEG_REG_MASK(AIU_MEM_AIFIFO_BUF_CNTL, MEM_BUFCTRL_INIT); + + WRITE_MPEG_REG(PARSER_SUB_START_PTR, parser_sub_start_ptr); + WRITE_MPEG_REG(PARSER_SUB_END_PTR, parser_sub_end_ptr); + WRITE_MPEG_REG(PARSER_SUB_RP, parser_sub_start_ptr); + WRITE_MPEG_REG(PARSER_SUB_WP, parser_sub_start_ptr); + SET_MPEG_REG_MASK(PARSER_ES_CONTROL, + (7 << ES_SUB_WR_ENDIAN_BIT) | ES_SUB_MAN_RD_PTR); + + WRITE_MPEG_REG(PFIFO_RD_PTR, 0); + WRITE_MPEG_REG(PFIFO_WR_PTR, 0); + + WRITE_MPEG_REG(PARSER_SEARCH_PATTERN, MPEG_START_CODE_PATTERN); + WRITE_MPEG_REG(PARSER_SEARCH_MASK, MPEG_START_CODE_MASK); + + WRITE_MPEG_REG(PARSER_CONFIG, + (10 << PS_CFG_PFIFO_EMPTY_CNT_BIT) | + (1 << PS_CFG_MAX_ES_WR_CYCLE_BIT) | + PS_CFG_STARTCODE_WID_24 | + PS_CFG_PFIFO_ACCESS_WID_8 | /* single byte pop */ + (16 << PS_CFG_MAX_FETCH_CYCLE_BIT)); + WRITE_MPEG_REG(PARSER_CONTROL, PARSER_AUTOSEARCH); + + tasklet_init(&psparser_tasklet, parser_tasklet, 0); + r = pts_start(PTS_TYPE_VIDEO); + if (r < 0) + goto Err_1; + r = pts_start(PTS_TYPE_AUDIO); + if (r < 0) + goto Err_2; + + video_data_parsed = 0; + audio_data_parsed = 0; + /*TODO irq */ + + r = vdec_request_irq(PARSER_IRQ, parser_isr, + "psparser", (void *)psparser_id); + + if (r) { + pr_info("PS Demux irq register failed.\n"); + + r = -ENOENT; + goto Err_3; + } + + WRITE_MPEG_REG(PARSER_INT_STATUS, 0xffff); + WRITE_MPEG_REG(PARSER_INT_ENABLE, + PARSER_INT_ALL << PARSER_INT_HOST_EN_BIT); + + return 0; + +Err_3: + pts_stop(PTS_TYPE_AUDIO); + +Err_2: + pts_stop(PTS_TYPE_VIDEO); + +Err_1: + return r; +} + +void psparser_release(void) +{ + u8 i; + + pr_info("psparser_release\n"); + + WRITE_MPEG_REG(PARSER_INT_ENABLE, 0); + /*TODO irq */ + + vdec_free_irq(PARSER_IRQ, (void *)psparser_id); + + pts_stop(PTS_TYPE_VIDEO); + pts_stop(PTS_TYPE_AUDIO); +#ifdef DEBUG_VOB_SUB + for (i = 0; i < MAX_SUB_NUM; i++) + kfree(sub_info[i]); + pr_info("psparser release subtitle info\n"); +#endif +} + +ssize_t psparser_write(struct file *file, + struct stream_buf_s *vbuf, + struct stream_buf_s *abuf, + const char __user *buf, size_t count) +{ + s32 r; + + struct port_priv_s *priv = (struct port_priv_s *)file->private_data; + struct stream_port_s *port = priv->port; + + if ((stbuf_space(vbuf) < count) || (stbuf_space(abuf) < count)) { + if (file->f_flags & O_NONBLOCK) + return -EAGAIN; + + if ((port->flag & PORT_FLAG_VID) + && (stbuf_space(vbuf) < count)) { + r = stbuf_wait_space(vbuf, count); + if (r < 0) + return r; + } + if ((port->flag & PORT_FLAG_AID) + && (stbuf_space(abuf) < count)) { + r = stbuf_wait_space(abuf, count); + if (r < 0) + return r; + } + } + + return _psparser_write(buf, count); +} + +void psparser_change_avid(unsigned int vid, unsigned int aid) +{ + video_id = vid; + audio_id = aid; +} + +void psparser_change_sid(unsigned int sid) +{ + sub_id = sid; +} + +void psparser_audio_reset(void) +{ + ulong flags; + + DEFINE_SPINLOCK(lock); + + spin_lock_irqsave(&lock, flags); + + WRITE_MPEG_REG(PARSER_AUDIO_WP, + READ_MPEG_REG(AIU_MEM_AIFIFO_START_PTR)); + WRITE_MPEG_REG(PARSER_AUDIO_RP, + READ_MPEG_REG(AIU_MEM_AIFIFO_START_PTR)); + + WRITE_MPEG_REG(PARSER_AUDIO_START_PTR, + READ_MPEG_REG(AIU_MEM_AIFIFO_START_PTR)); + WRITE_MPEG_REG(PARSER_AUDIO_END_PTR, + READ_MPEG_REG(AIU_MEM_AIFIFO_END_PTR)); + CLEAR_MPEG_REG_MASK(PARSER_ES_CONTROL, ES_AUD_MAN_RD_PTR); + + WRITE_MPEG_REG(AIU_MEM_AIFIFO_BUF_CNTL, MEM_BUFCTRL_INIT); + CLEAR_MPEG_REG_MASK(AIU_MEM_AIFIFO_BUF_CNTL, MEM_BUFCTRL_INIT); + + audio_data_parsed = 0; + + spin_unlock_irqrestore(&lock, flags); + +} + +void psparser_sub_reset(void) +{ + ulong flags; + + DEFINE_SPINLOCK(lock); + u32 parser_sub_start_ptr; + u32 parser_sub_end_ptr; + + spin_lock_irqsave(&lock, flags); + + parser_sub_start_ptr = READ_MPEG_REG(PARSER_SUB_START_PTR); + parser_sub_end_ptr = READ_MPEG_REG(PARSER_SUB_END_PTR); + + WRITE_MPEG_REG(PARSER_SUB_START_PTR, parser_sub_start_ptr); + WRITE_MPEG_REG(PARSER_SUB_END_PTR, parser_sub_end_ptr); + WRITE_MPEG_REG(PARSER_SUB_RP, parser_sub_start_ptr); + WRITE_MPEG_REG(PARSER_SUB_WP, parser_sub_start_ptr); + SET_MPEG_REG_MASK(PARSER_ES_CONTROL, + (7 << ES_SUB_WR_ENDIAN_BIT) | ES_SUB_MAN_RD_PTR); + + spin_unlock_irqrestore(&lock, flags); + +} + +u8 psparser_get_sub_found_num(void) +{ +#ifdef DEBUG_VOB_SUB + return sub_found_num; +#else + return 0; +#endif +} + +u8 psparser_get_sub_info(struct subtitle_info **sub_infos) +{ +#ifdef DEBUG_VOB_SUB + u8 i = 0; + int ret = 0; + u8 size = sizeof(struct subtitle_info); + + for (i = 0; i < sub_found_num; i++) { + if (!sub_info[i]) { + pr_info + ("[psparser_get_sub_info:%d] sub_info[%d] NULL\n", + __LINE__, i); + ret = -1; + break; + } + if (!sub_infos[i]) { + pr_info + ("[psparser_get_sub_info:%d] sub_infos[%d] NULL\n", + __LINE__, i); + ret = -2; + break; + } + memcpy(sub_infos[i], sub_info[i], size); + } + return ret; +#else + return 0; +#endif +} diff --git a/drivers/stream_input/parser/psparser.h b/drivers/stream_input/parser/psparser.h new file mode 100644 index 0000000..1280b6a --- a/dev/null +++ b/drivers/stream_input/parser/psparser.h @@ -0,0 +1,141 @@ +/* + * drivers/amlogic/media/stream_input/parser/psparser.h + * + * Copyright (C) 2016 Amlogic, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * +*/ + +#ifndef PSPARSER_H +#define PSPARSER_H + +#include "../../frame_provider/decoder/utils/vdec.h" + +extern s32 psparser_init(u32 vid, u32 aid, u32 sid, struct vdec_s *vdec); + +extern void psparser_release(void); + +extern ssize_t psparser_write(struct file *file, + struct stream_buf_s *vbuf, + struct stream_buf_s *abuf, const char __user *buf, size_t count); + +extern void psparser_change_avid(unsigned int vid, unsigned int aid); + +extern void psparser_change_sid(unsigned int sid); + +extern void psparser_audio_reset(void); + +extern void psparser_sub_reset(void); + +extern u8 psparser_get_sub_found_num(void); + +extern u8 psparser_get_sub_info(struct subtitle_info *sub_infos[]); + +#ifdef CONFIG_AM_DVB +extern int tsdemux_set_reset_flag(void); +#endif + +/* TODO: move to register headers */ +#define ES_PACK_SIZE_BIT 8 +#define ES_PACK_SIZE_WID 24 + +#define ES_CTRL_WID 8 +#define ES_CTRL_BIT 0 +#define ES_TYPE_MASK (3 << 6) +#define ES_TYPE_VIDEO (0 << 6) +#define ES_TYPE_AUDIO (1 << 6) +#define ES_TYPE_SUBTITLE (2 << 6) + +#define ES_WRITE (1<<5) +#define ES_PASSTHROUGH (1<<4) +#define ES_INSERT_BEFORE_ES_WRITE (1<<3) +#define ES_DISCARD (1<<2) +#define ES_SEARCH (1<<1) +#define ES_PARSER_START (1<<0) +#define ES_PARSER_BUSY (1<<0) + +#define PARSER_INTSTAT_FETCH_CMD (1<<7) +#define PARSER_INTSTAT_PARSE (1<<4) +#define PARSER_INTSTAT_DISCARD (1<<3) +#define PARSER_INTSTAT_INSZERO (1<<2) +#define PARSER_INTSTAT_ACT_NOSSC (1<<1) +#define PARSER_INTSTAT_SC_FOUND (1<<0) + +#define FETCH_CIR_BUF (1<<31) +#define FETCH_CHK_BUF_STOP (1<<30) +#define FETCH_PASSTHROUGH (1<<29) +#define FETCH_ENDIAN 27 +#define FETCH_PASSTHROUGH_TYPE_MASK (0x3<<27) +#define FETCH_ENDIAN_MASK (0x7<<27) +#define FETCH_BUF_SIZE_MASK (0x7ffffff) +#define FETCH_CMD_PTR_MASK 3 +#define FETCH_CMD_RD_PTR_BIT 5 +#define FETCH_CMD_WR_PTR_BIT 3 +#define FETCH_CMD_NUM_MASK 3 +#define FETCH_CMD_NUM_BIT 0 + +#define ES_COUNT_MASK 0xfff +#define ES_COUNT_BIT 20 +#define ES_REQ_PENDING (1<<19) +#define ES_PASSTHROUGH_EN (1<<18) +#define ES_PASSTHROUGH_TYPE_MASK (3<<16) +#define ES_PASSTHROUGH_TYPE_VIDEO (0<<16) +#define ES_PASSTHROUGH_TYPE_AUDIO (1<<16) +#define ES_PASSTHROUGH_TYPE_SUBTITLE (2<<16) +#define ES_WR_ENDIAN_MASK (0x7) +#define ES_SUB_WR_ENDIAN_BIT 9 +#define ES_SUB_MAN_RD_PTR (1<<8) +#define ES_AUD_WR_ENDIAN_BIT 5 +#define ES_AUD_MAN_RD_PTR (1<<4) +#define ES_VID_WR_ENDIAN_BIT 1 +#define ES_VID_MAN_RD_PTR (1<<0) + +#define PS_CFG_FETCH_DMA_URGENT (1<<31) +#define PS_CFG_STREAM_DMA_URGENT (1<<30) +#define PS_CFG_FORCE_PFIFO_REN (1<<29) +#define PS_CFG_PFIFO_PEAK_EN (1<<28) +#define PS_CFG_SRC_SEL_BIT 24 +#define PS_CFG_SRC_SEL_MASK (3<<PS_CFG_SRC_SEL_BIT) +#define PS_CFG_SRC_SEL_FETCH (0<<PS_CFG_SRC_SEL_BIT) +#define PS_CFG_SRC_SEL_AUX1 (1<<PS_CFG_SRC_SEL_BIT) /* from NDMA */ +#define PS_CFG_SRC_SEL_AUX2 (2<<PS_CFG_SRC_SEL_BIT) +#define PS_CFG_SRC_SEL_AUX3 (3<<PS_CFG_SRC_SEL_BIT) +#define PS_CFG_PFIFO_EMPTY_CNT_BIT 16 +#define PS_CFG_PFIFO_EMPTY_CNT_MASK 0xff +#define PS_CFG_MAX_ES_WR_CYCLE_BIT 12 +#define PS_CFG_MAX_ES_WR_CYCLE_MASK 0xf +#define PS_CFG_STARTCODE_WID_MASK (0x3<<10) +#define PS_CFG_STARTCODE_WID_8 (0x0<<10) +#define PS_CFG_STARTCODE_WID_16 (0x1<<10) +#define PS_CFG_STARTCODE_WID_24 (0x2<<10) +#define PS_CFG_STARTCODE_WID_32 (0x3<<10) +#define PS_CFG_PFIFO_ACCESS_WID_MASK (0x3<<8) +#define PS_CFG_PFIFO_ACCESS_WID_8 (0x0<<8) +#define PS_CFG_PFIFO_ACCESS_WID_16 (0x1<<8) +#define PS_CFG_PFIFO_ACCESS_WID_24 (0x2<<8) +#define PS_CFG_PFIFO_ACCESS_WID_32 (0x3<<8) +#define PS_CFG_MAX_FETCH_CYCLE_BIT 0 +#define PS_CFG_MAX_FETCH_CYCLE_MASK 0xff + +#define PARSER_INT_DISABLE_CNT_MASK 0xffff +#define PARSER_INT_DISABLE_CNT_BIT 16 +#define PARSER_INT_HOST_EN_MASK 0xff +#define PARSER_INT_HOST_EN_BIT 8 +#define PARSER_INT_AMRISC_EN_MASK 0xff +#define PARSER_INT_AMRISC_EN_BIT 0 +#define PARSER_INT_ALL 0xff + +#define RESET_PARSER (1<<8) +#define TS_HIU_ENABLE 5 +#define USE_HI_BSF_INTERFACE 7 + +#endif /* PSPARSER_H */ diff --git a/drivers/stream_input/parser/streambuf.c b/drivers/stream_input/parser/streambuf.c new file mode 100644 index 0000000..15056d6 --- a/dev/null +++ b/drivers/stream_input/parser/streambuf.c @@ -0,0 +1,426 @@ +/* + * drivers/amlogic/media/stream_input/parser/streambuf.c + * + * Copyright (C) 2016 Amlogic, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * +*/ + +#include <linux/kernel.h> +#include <linux/spinlock.h> +#include <linux/timer.h> +#include <linux/sched.h> +#include <linux/fs.h> +#include <linux/io.h> +#include <linux/amlogic/media/frame_sync/ptsserv.h> +#include <linux/amlogic/media/utils/vformat.h> +#include <linux/amlogic/iomap.h> +#include <asm/cacheflush.h> +#include <linux/uaccess.h> +#include <linux/vmalloc.h> +/* #include <mach/am_regs.h> */ + +#include <linux/amlogic/media/utils/vdec_reg.h> +#include "../../frame_provider/decoder/utils/vdec.h" +#include "streambuf_reg.h" +#include "streambuf.h" +#include <linux/amlogic/media/utils/amports_config.h> +#include "../amports/amports_priv.h" +#include <linux/dma-mapping.h> +#include <linux/dma-contiguous.h> +#include <linux/amlogic/media/codec_mm/codec_mm.h> + +#define STBUF_SIZE (64*1024) +#define STBUF_WAIT_INTERVAL (HZ/100) +#define MEM_NAME "streambuf" + +void *fetchbuf; + +static s32 _stbuf_alloc(struct stream_buf_s *buf) +{ + if (buf->buf_size == 0) + return -ENOBUFS; + + while (buf->buf_start == 0) { + int flags = CODEC_MM_FLAGS_DMA; + + buf->buf_page_num = PAGE_ALIGN(buf->buf_size) / PAGE_SIZE; + if (buf->type == BUF_TYPE_SUBTITLE) + flags = CODEC_MM_FLAGS_DMA_CPU; + + /* + *if 4k, + *used cma first,for less mem fragments. + */ + if (((buf->type == BUF_TYPE_HEVC) || + (buf->type == BUF_TYPE_VIDEO)) && + buf->for_4k) + flags |= CODEC_MM_FLAGS_CMA_FIRST; + if (buf->buf_size > 20 * 1024 * 1024) + flags |= CODEC_MM_FLAGS_CMA_FIRST; + + if ((buf->type == BUF_TYPE_HEVC) || + (buf->type == BUF_TYPE_VIDEO)) { + flags |= CODEC_MM_FLAGS_FOR_VDECODER; + } else if (buf->type == BUF_TYPE_AUDIO) { + flags |= CODEC_MM_FLAGS_FOR_ADECODER; + flags |= CODEC_MM_FLAGS_DMA_CPU; + } + + if (buf->type == BUF_TYPE_VIDEO + && codec_mm_get_total_size() <= 68 * SZ_1M) { + buf->buf_size = (1024*1024*10); + buf->buf_page_num = + PAGE_ALIGN(buf->buf_size) / PAGE_SIZE; + } + + buf->buf_start = codec_mm_alloc_for_dma(MEM_NAME, + buf->buf_page_num, 4+PAGE_SHIFT, flags); + if (!buf->buf_start) { + int is_video = (buf->type == BUF_TYPE_HEVC) || + (buf->type == BUF_TYPE_VIDEO); + if (is_video && buf->buf_size >= 9 * SZ_1M) {/*min 6M*/ + int old_size = buf->buf_size; + + buf->buf_size = + PAGE_ALIGN(buf->buf_size * 2/3); + pr_info("%s stbuf alloced size = %d failed try small %d size\n", + (buf->type == BUF_TYPE_HEVC) ? "HEVC" : + (buf->type == BUF_TYPE_VIDEO) ? "Video" : + (buf->type == BUF_TYPE_AUDIO) ? "Audio" : + "Subtitle", old_size, buf->buf_size); + continue; + } + pr_info("%s stbuf alloced size = %d failed\n", + (buf->type == BUF_TYPE_HEVC) ? "HEVC" : + (buf->type == BUF_TYPE_VIDEO) ? "Video" : + (buf->type == BUF_TYPE_AUDIO) ? "Audio" : + "Subtitle", buf->buf_size); + return -ENOMEM; + } + pr_info("%s stbuf alloced at %p, size = %d\n", + (buf->type == BUF_TYPE_HEVC) ? "HEVC" : + (buf->type == BUF_TYPE_VIDEO) ? "Video" : + (buf->type == BUF_TYPE_AUDIO) ? "Audio" : + "Subtitle", (void *)buf->buf_start, + buf->buf_size); + } + + buf->flag |= BUF_FLAG_ALLOC; + + return 0; +} + +int stbuf_change_size(struct stream_buf_s *buf, int size) +{ + unsigned long old_buf; + int old_size, old_pagenum; + int ret; + + pr_info("buffersize=%d,%d,start=%p\n", size, buf->buf_size, + (void *)buf->buf_start); + + if (buf->buf_size == size && buf->buf_start != 0) + return 0; + + old_buf = buf->buf_start; + old_size = buf->buf_size; + old_pagenum = buf->buf_page_num; + buf->buf_start = 0; + buf->buf_size = size; + ret = size; + + if (size == 0 || _stbuf_alloc(buf) == 0) { + /* + * size=0:We only free the old memory; + * alloc ok,changed to new buffer + */ + if (old_buf != 0) + codec_mm_free_for_dma(MEM_NAME, old_buf); + + pr_info("changed the (%d) buffer size from %d to %d\n", + buf->type, old_size, size); + return 0; + } + /* alloc failed */ + buf->buf_start = old_buf; + buf->buf_size = old_size; + buf->buf_page_num = old_pagenum; + pr_info("changed the (%d) buffer size from %d to %d,failed\n", + buf->type, old_size, size); + + return ret; +} + +int stbuf_fetch_init(void) +{ + if (fetchbuf != NULL) + return 0; + + fetchbuf = (void *)__get_free_pages(GFP_KERNEL, + get_order(FETCHBUF_SIZE)); + + if (!fetchbuf) { + pr_info("%s: Can not allocate fetch working buffer\n", + __func__); + return -ENOMEM; + } + return 0; +} + +void stbuf_fetch_release(void) +{ + if (0 && fetchbuf) { + /* always don't free.for safe alloc/free*/ + free_pages((unsigned long)fetchbuf, get_order(FETCHBUF_SIZE)); + fetchbuf = 0; + } +} + +static void _stbuf_timer_func(unsigned long arg) +{ + struct stream_buf_s *p = (struct stream_buf_s *)arg; + + if (stbuf_space(p) < p->wcnt) { + p->timer.expires = jiffies + STBUF_WAIT_INTERVAL; + + add_timer(&p->timer); + } else + wake_up_interruptible(&p->wq); + +} + +u32 stbuf_level(struct stream_buf_s *buf) +{ + if ((buf->type == BUF_TYPE_HEVC) || (buf->type == BUF_TYPE_VIDEO)) { + if (READ_MPEG_REG(PARSER_ES_CONTROL) & 1) { + int level = READ_MPEG_REG(PARSER_VIDEO_WP) - + READ_MPEG_REG(PARSER_VIDEO_RP); + if (level < 0) + level += READ_MPEG_REG(PARSER_VIDEO_END_PTR) - + READ_MPEG_REG(PARSER_VIDEO_START_PTR) + 8; + return (u32)level; + } else + return (buf->type == BUF_TYPE_HEVC) ? + READ_VREG(HEVC_STREAM_LEVEL) : + _READ_ST_REG(LEVEL); + } + + return _READ_ST_REG(LEVEL); +} + +u32 stbuf_rp(struct stream_buf_s *buf) +{ + if ((buf->type == BUF_TYPE_HEVC) || (buf->type == BUF_TYPE_VIDEO)) { + if (READ_MPEG_REG(PARSER_ES_CONTROL) & 1) + return READ_MPEG_REG(PARSER_VIDEO_RP); + else + return (buf->type == BUF_TYPE_HEVC) ? + READ_VREG(HEVC_STREAM_RD_PTR) : + _READ_ST_REG(RP); + } + + return _READ_ST_REG(RP); +} + +u32 stbuf_space(struct stream_buf_s *buf) +{ + /* + *reserved space for safe write, + * the parser fifo size is 1024byts, so reserve it + */ + int size; + + size = buf->canusebuf_size - stbuf_level(buf); + + if (buf->canusebuf_size >= buf->buf_size / 2) { + /* old reversed value,tobe full, reversed only... */ + size = size - 6 * 1024; + } + + if ((buf->type == BUF_TYPE_VIDEO) + || (has_hevc_vdec() && buf->type == BUF_TYPE_HEVC)) + size -= READ_MPEG_REG(PARSER_VIDEO_HOLE); + + return size > 0 ? size : 0; +} + +u32 stbuf_size(struct stream_buf_s *buf) +{ + return buf->buf_size; +} + +u32 stbuf_canusesize(struct stream_buf_s *buf) +{ + return buf->canusebuf_size; +} + +s32 stbuf_init(struct stream_buf_s *buf, struct vdec_s *vdec) +{ + s32 r; + u32 dummy; + u32 addr32; + + if (!buf->buf_start) { + r = _stbuf_alloc(buf); + if (r < 0) + return r; + } + addr32 = buf->buf_start & 0xffffffff; + init_waitqueue_head(&buf->wq); + + if ((buf->type == BUF_TYPE_VIDEO) || (buf->type == BUF_TYPE_HEVC)) { + if (vdec) { + if (vdec_stream_based(vdec)) + vdec_input_set_buffer(&vdec->input, addr32, + buf->buf_size); + else + return vdec_input_set_buffer(&vdec->input, + addr32, buf->buf_size); + } + } + + buf->write_thread = 0; + if (has_hevc_vdec() && buf->type == BUF_TYPE_HEVC) { + CLEAR_VREG_MASK(HEVC_STREAM_CONTROL, 1); + WRITE_VREG(HEVC_STREAM_START_ADDR, addr32); + WRITE_VREG(HEVC_STREAM_END_ADDR, addr32 + buf->buf_size); + WRITE_VREG(HEVC_STREAM_RD_PTR, addr32); + WRITE_VREG(HEVC_STREAM_WR_PTR, addr32); + + return 0; + } + + if (buf->type == BUF_TYPE_VIDEO) { + _WRITE_ST_REG(CONTROL, 0); + /* reset VLD before setting all pointers */ + WRITE_VREG(VLD_MEM_VIFIFO_WRAP_COUNT, 0); + /*TODO: only > m6*/ +#if 1/* MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON6 */ + WRITE_VREG(DOS_SW_RESET0, (1 << 4)); + WRITE_VREG(DOS_SW_RESET0, 0); +#else + WRITE_MPEG_REG(RESET0_REGISTER, RESET_VLD); +#endif + + dummy = READ_MPEG_REG(RESET0_REGISTER); + WRITE_VREG(POWER_CTL_VLD, 1 << 4); + } else if (buf->type == BUF_TYPE_AUDIO) { + _WRITE_ST_REG(CONTROL, 0); + + WRITE_MPEG_REG(AIU_AIFIFO_GBIT, 0x80); + } + + if (buf->type == BUF_TYPE_SUBTITLE) { + WRITE_MPEG_REG(PARSER_SUB_RP, addr32); + WRITE_MPEG_REG(PARSER_SUB_START_PTR, addr32); + WRITE_MPEG_REG(PARSER_SUB_END_PTR, + addr32 + buf->buf_size - 8); + + return 0; + } + + _WRITE_ST_REG(START_PTR, addr32); + _WRITE_ST_REG(CURR_PTR, addr32); + _WRITE_ST_REG(END_PTR, addr32 + buf->buf_size - 8); + + _SET_ST_REG_MASK(CONTROL, MEM_BUFCTRL_INIT); + _CLR_ST_REG_MASK(CONTROL, MEM_BUFCTRL_INIT); + + _WRITE_ST_REG(BUF_CTRL, MEM_BUFCTRL_MANUAL); + _WRITE_ST_REG(WP, addr32); + + _SET_ST_REG_MASK(BUF_CTRL, MEM_BUFCTRL_INIT); + _CLR_ST_REG_MASK(BUF_CTRL, MEM_BUFCTRL_INIT); + + _SET_ST_REG_MASK(CONTROL, + (0x11 << 16) | MEM_FILL_ON_LEVEL | MEM_CTRL_FILL_EN | + MEM_CTRL_EMPTY_EN); + return 0; +} + +void stbuf_vdec2_init(struct stream_buf_s *buf) +{ + + _WRITE_VDEC2_ST_REG(CONTROL, 0); + + _WRITE_VDEC2_ST_REG(START_PTR, _READ_ST_REG(START_PTR)); + _WRITE_VDEC2_ST_REG(END_PTR, _READ_ST_REG(END_PTR)); + _WRITE_VDEC2_ST_REG(CURR_PTR, _READ_ST_REG(CURR_PTR)); + + _WRITE_VDEC2_ST_REG(CONTROL, MEM_FILL_ON_LEVEL | MEM_BUFCTRL_INIT); + _WRITE_VDEC2_ST_REG(CONTROL, MEM_FILL_ON_LEVEL); + + _WRITE_VDEC2_ST_REG(BUF_CTRL, MEM_BUFCTRL_INIT); + _WRITE_VDEC2_ST_REG(BUF_CTRL, 0); + + _WRITE_VDEC2_ST_REG(CONTROL, + (0x11 << 16) | MEM_FILL_ON_LEVEL | MEM_CTRL_FILL_EN + | MEM_CTRL_EMPTY_EN); +} + +s32 stbuf_wait_space(struct stream_buf_s *stream_buf, size_t count) +{ + struct stream_buf_s *p = stream_buf; + long time_out = 20; + + p->wcnt = count; + + setup_timer(&p->timer, _stbuf_timer_func, (ulong) p); + + mod_timer(&p->timer, jiffies + STBUF_WAIT_INTERVAL); + + if (wait_event_interruptible_timeout + (p->wq, stbuf_space(p) >= count, time_out) == 0) { + del_timer_sync(&p->timer); + + return -EAGAIN; + } + + del_timer_sync(&p->timer); + + return 0; +} + +void stbuf_release(struct stream_buf_s *buf) +{ + buf->first_tstamp = INVALID_PTS; + + stbuf_init(buf, NULL); /* reinit buffer */ + + if (buf->flag & BUF_FLAG_ALLOC && buf->buf_start) { + codec_mm_free_for_dma(MEM_NAME, buf->buf_start); + buf->flag &= ~BUF_FLAG_ALLOC; + buf->buf_start = 0; + } + buf->flag &= ~BUF_FLAG_IN_USE; +} + +u32 stbuf_sub_rp_get(void) +{ + return READ_MPEG_REG(PARSER_SUB_RP); +} + +void stbuf_sub_rp_set(unsigned int sub_rp) +{ + WRITE_MPEG_REG(PARSER_SUB_RP, sub_rp); +} + +u32 stbuf_sub_wp_get(void) +{ + return READ_MPEG_REG(PARSER_SUB_WP); +} + +u32 stbuf_sub_start_get(void) +{ + return READ_MPEG_REG(PARSER_SUB_START_PTR); +} diff --git a/drivers/stream_input/parser/streambuf.h b/drivers/stream_input/parser/streambuf.h new file mode 100644 index 0000000..cdae0a8 --- a/dev/null +++ b/drivers/stream_input/parser/streambuf.h @@ -0,0 +1,136 @@ +/* + * drivers/amlogic/media/stream_input/parser/streambuf.h + * + * Copyright (C) 2016 Amlogic, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * +*/ + +#ifndef STREAMBUF_H +#define STREAMBUF_H +#include <linux/amlogic/media/utils/amports_config.h> + +#define BUF_FLAG_ALLOC 0x01 +#define BUF_FLAG_IN_USE 0x02 +#define BUF_FLAG_PARSER 0x04 +#define BUF_FLAG_FIRST_TSTAMP 0x08 +#define BUF_FLAG_IOMEM 0x10 + +#define BUF_TYPE_VIDEO 0 +#define BUF_TYPE_AUDIO 1 +#define BUF_TYPE_SUBTITLE 2 +#define BUF_TYPE_USERDATA 3 +#define BUF_TYPE_HEVC 4 +#define BUF_MAX_NUM 5 + +#define INVALID_PTS 0xffffffff + +#define FETCHBUF_SIZE (64*1024) +#define USER_DATA_SIZE (8*1024) + +struct vdec_s; + +struct stream_buf_s { + s32 flag; + u32 type; + unsigned long buf_start; + struct page *buf_pages; + int buf_page_num; + u32 buf_size; + u32 default_buf_size; + u32 canusebuf_size; + u32 first_tstamp; + const ulong reg_base; + wait_queue_head_t wq; + struct timer_list timer; + u32 wcnt; + u32 buf_wp; + u32 buf_rp; + u32 max_buffer_delay_ms; + u64 last_write_jiffies64; + void *write_thread; + int for_4k; +} /*stream_buf_t */; + +struct stream_port_s { + /* driver info */ + const char *name; + struct device *class_dev; + const struct file_operations *fops; + + /* ports control */ + s32 type; + s32 flag; + s32 pcr_inited; + + /* decoder info */ + s32 vformat; + s32 aformat; + s32 achanl; + s32 asamprate; + s32 adatawidth; + + /* parser info */ + u32 vid; + u32 aid; + u32 sid; + u32 pcrid; +} /*stream_port_t */; +enum drm_level_e { + DRM_LEVEL1 = 1, + DRM_LEVEL2 = 2, + DRM_LEVEL3 = 3, + DRM_NONE = 4, +}; + +struct drm_info { + enum drm_level_e drm_level; + u32 drm_flag; + u32 drm_hasesdata; + u32 drm_priv; + u32 drm_pktsize; + u32 drm_pktpts; + u32 drm_phy; + u32 drm_vir; + u32 drm_remap; + u32 data_offset; + u32 extpad[8]; +} /*drminfo_t */; + +#define TYPE_DRMINFO 0x80 +#define TYPE_PATTERN 0x40 + +struct vdec_s; + +extern void *fetchbuf; + +extern u32 stbuf_level(struct stream_buf_s *buf); +extern u32 stbuf_rp(struct stream_buf_s *buf); +extern u32 stbuf_space(struct stream_buf_s *buf); +extern u32 stbuf_size(struct stream_buf_s *buf); +extern u32 stbuf_canusesize(struct stream_buf_s *buf); +extern s32 stbuf_init(struct stream_buf_s *buf, struct vdec_s *vdec); +extern s32 stbuf_wait_space(struct stream_buf_s *stream_buf, size_t count); +extern void stbuf_release(struct stream_buf_s *buf); +extern int stbuf_change_size(struct stream_buf_s *buf, int size); +extern int stbuf_fetch_init(void); +extern void stbuf_fetch_release(void); +extern u32 stbuf_sub_rp_get(void); +extern void stbuf_sub_rp_set(unsigned int sub_rp); +extern u32 stbuf_sub_wp_get(void); +extern u32 stbuf_sub_start_get(void); +extern u32 stbuf_userdata_start_get(void); +extern struct stream_buf_s *get_stream_buffer(int id); + +extern void stbuf_vdec2_init(struct stream_buf_s *buf); + +#endif /* STREAMBUF_H */ diff --git a/drivers/stream_input/parser/streambuf_reg.h b/drivers/stream_input/parser/streambuf_reg.h new file mode 100644 index 0000000..f00c705 --- a/dev/null +++ b/drivers/stream_input/parser/streambuf_reg.h @@ -0,0 +1,111 @@ +/* + * drivers/amlogic/media/stream_input/parser/streambuf_reg.h + * + * Copyright (C) 2016 Amlogic, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * +*/ + +#ifndef STREAMBUF_REG_H +#define STREAMBUF_REG_H + +#define HEVC_STREAM_REG_BASE HEVC_STREAM_START_ADDR + +#define VLD_MEM_VIFIFO_REG_BASE VLD_MEM_VIFIFO_START_PTR +#define AIU_MEM_AIFIFO_REG_BASE AIU_MEM_AIFIFO_START_PTR + +#define START_PTR 0 +#define CURR_PTR 1 +#define END_PTR 2 +#define BYTES_AVAIL 3 +#define CONTROL 4 +#define WP 5 +#define RP 6 +#define LEVEL 7 +#define BUF_CTRL 8 + +/* +*#if MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON6 +*#define _WRITE_ST_REG(r, val) \ +* __raw_writel(val, (volatile void __iomem *)(buf->reg_base+(r<<2))) +*#define _WRITE_ST_REG_BITS(r, val, s, e) \ +* __raw_writel((((_READ_ST_REG(r) & \ +* (((1L<<(e)-1)<<(s))-1)<<(s)))|((unsigned)((val)&((1L<<(e))-1))<<(s))), \ +* (volatile void __iomem *)(buf->reg_base+(r<<2))) +*#define _SET_ST_REG_MASK(r, val) \ +* __raw_writel(_READ_ST_REG(r)| (val), \ +* (volatile void __iomem *)(buf->reg_base+(r<<2))) +*#define _CLR_ST_REG_MASK(r, val) \ +* __raw_writel(_READ_ST_REG(r)&~(val), \ +* (volatile void __iomem *)(buf->reg_base+(r<<2))) +*#define _READ_ST_REG(r) \ +* (__raw_readl((volatile void __iomem *)(buf->reg_base+(r<<2)))) +* +*#if MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON6TVD +*#define _READ_VDEC2_ST_REG(r) \ +* (__raw_readl((volatile void __iomem *)(buf->reg_base + \ +* DOS_REG_ADDR(VDEC2_VLD_MEM_VIFIFO_START_PTR) - \ +* DOS_REG_ADDR(VLD_MEM_VIFIFO_START_PTR) + (r<<2)))) +*#define _WRITE_VDEC2_ST_REG(r, val) \ +* __raw_writel(val, (volatile void __iomem *)(buf->reg_base + \ +* DOS_REG_ADDR(VDEC2_VLD_MEM_VIFIFO_START_PTR) - \ +* DOS_REG_ADDR(VLD_MEM_VIFIFO_START_PTR) + (r<<2))) +*#endif +* +*#define MEM_BUFCTRL_MANUAL (1<<1) +*#define MEM_BUFCTRL_INIT (1<<0) +*#define MEM_LEVEL_CNT_BIT 18 +*#define MEM_FIFO_CNT_BIT 16 +*#define MEM_FILL_ON_LEVEL (1<<10) +*#define MEM_CTRL_EMPTY_EN (1<<2) +*#define MEM_CTRL_FILL_EN (1<<1) +*#define MEM_CTRL_INIT (1<<0) +* +*#else +*#define _WRITE_ST_REG(r, val) WRITE_MPEG_REG(buf->reg_base + (r), \ +* (val)) +*#define _WRITE_ST_REG_BITS(r, val, s, e) WRITE_MPEG_REG(buf->reg_base + (r), \ +* (val), (s), (e)) +*#define _SET_ST_REG_MASK(r, val) SET_MPEG_REG_MASK(buf->reg_base + \ +* (r), (val)) +*#define _CLR_ST_REG_MASK(r, val) CLEAR_MPEG_REG_MASK(buf->reg_base + \ +* (r), (val)) +*#define _READ_ST_REG(r) READ_MPEG_REG(buf->reg_base + (r)) +*#endif +*/ + + /*TODO*/ +#define _WRITE_ST_REG(r, val) do { \ + if (buf->reg_base == VLD_MEM_VIFIFO_REG_BASE) \ + codec_dosbus_write((buf->reg_base+(r)), (val)); \ + else \ + codec_cbus_write((buf->reg_base+(r)), (val)); \ + } while (0) +#define _READ_ST_REG(r) \ + ((buf->reg_base == VLD_MEM_VIFIFO_REG_BASE) ? \ + codec_dosbus_read(buf->reg_base+(r)) : \ + codec_cbus_read(buf->reg_base+(r))) +#define _SET_ST_REG_MASK(r, val) _WRITE_ST_REG(r, _READ_ST_REG(r) | (val)) +#define _CLR_ST_REG_MASK(r, val) _WRITE_ST_REG(r, _READ_ST_REG(r)&~(val)) +#define _READ_VDEC2_ST_REG(r) (codec_dosbus_read(\ + (VDEC2_VLD_MEM_VIFIFO_START_PTR+(r)))) +#define _WRITE_VDEC2_ST_REG(r, val) codec_dosbus_write(\ + (VDEC2_VLD_MEM_VIFIFO_START_PTR+r), val) +#define MEM_BUFCTRL_MANUAL (1<<1) +#define MEM_BUFCTRL_INIT (1<<0) +#define MEM_LEVEL_CNT_BIT 18 +#define MEM_FIFO_CNT_BIT 16 +#define MEM_FILL_ON_LEVEL (1<<10) +#define MEM_CTRL_EMPTY_EN (1<<2) +#define MEM_CTRL_FILL_EN (1<<1) +#define MEM_CTRL_INIT (1<<0) +#endif /* STREAMBUF_REG_H */ diff --git a/drivers/stream_input/parser/thread_rw.c b/drivers/stream_input/parser/thread_rw.c new file mode 100644 index 0000000..e725407 --- a/dev/null +++ b/drivers/stream_input/parser/thread_rw.c @@ -0,0 +1,505 @@ +/* + * drivers/amlogic/media/stream_input/parser/thread_rw.c + * + * Copyright (C) 2016 Amlogic, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * +*/ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/interrupt.h> +#include <linux/timer.h> +#include <linux/kfifo.h> +#include <linux/workqueue.h> +#include <linux/dma-mapping.h> +#include <linux/dma-contiguous.h> +#include <linux/uaccess.h> +#include <linux/fs.h> +#include <linux/vmalloc.h> +#include <linux/amlogic/media/codec_mm/codec_mm.h> + +/* #include <mach/am_regs.h> */ +#include <linux/delay.h> + +#include "../../stream_input/parser/streambuf.h" +#include "../../stream_input/amports/amports_priv.h" +#include "thread_rw.h" + +#define BUF_NAME "fetchbuf" + +#define DEFAULT_BLOCK_SIZE (64*1024) + +struct threadrw_buf { + void *vbuffer; + dma_addr_t dma_handle; + int write_off; + int data_size; + int buffer_size; +}; + +struct threadrw_write_task { + struct file *file; + struct delayed_work write_work; + DECLARE_KFIFO_PTR(datafifo, void *); + DECLARE_KFIFO_PTR(freefifo, void *); + int max_buf; + int errors; + spinlock_t lock; + struct stream_buf_s *sbuf; + int buffered_data_size; + int passed_data_len; + int buffer_size; + int data_offset; + int writework_on; + unsigned long codec_mm_buffer; + int manual_write; + wait_queue_head_t wq; + ssize_t (*write)(struct file *, + struct stream_buf_s *, + const char __user *, + size_t, int); + struct threadrw_buf buf[1]; + /*don't add any after buf[] define */ +}; + +static int free_task_buffers(struct threadrw_write_task *task); + +static struct workqueue_struct *threadrw_wq_get(void) +{ + static struct workqueue_struct *threadrw_wq; + + if (!threadrw_wq) + threadrw_wq = create_singlethread_workqueue("threadrw"); + return threadrw_wq; +} + +static int threadrw_schedule_delayed_work( + struct threadrw_write_task *task, + unsigned long delay) +{ + bool ret; + + if (threadrw_wq_get()) { + ret = queue_delayed_work(threadrw_wq_get(), + &task->write_work, delay); + } else + ret = schedule_delayed_work(&task->write_work, delay); + if (!ret) { + cancel_delayed_work(&task->write_work); + if (threadrw_wq_get()) + ret = queue_delayed_work(threadrw_wq_get(), + &task->write_work, 0); + else + ret = schedule_delayed_work(&task->write_work, 0); + } + return 0; +} + +static ssize_t threadrw_write_onece( + struct threadrw_write_task *task, + struct file *file, + struct stream_buf_s *stbuf, + const char __user *buf, size_t count) +{ + struct threadrw_buf *rwbuf = NULL; + int ret = 0; + int to_write; + + if (!kfifo_get(&task->freefifo, (void *)&rwbuf)) { + if (task->errors) + return task->errors; + return -EAGAIN; + } + + to_write = min_t(u32, rwbuf->buffer_size, count); + if (copy_from_user(rwbuf->vbuffer, buf, to_write)) { + kfifo_put(&task->freefifo, (const void *)buf); + ret = -EFAULT; + goto err; + } + rwbuf->data_size = to_write; + rwbuf->write_off = 0; + kfifo_put(&task->datafifo, (const void *)rwbuf); + threadrw_schedule_delayed_work(task, 0); + return to_write; +err: + return ret; +} + +static ssize_t threadrw_write_in( + struct threadrw_write_task *task, + struct stream_buf_s *stbuf, + const char __user *buf, size_t count) +{ + int ret = 0; + int off = 0; + int left = count; + int wait_num = 0; + unsigned long flags; + + while (left > 0) { + ret = threadrw_write_onece(task, + task->file, + stbuf, buf + off, left); + if (ret >= left) { + off = count; + left = 0; + } else if (ret > 0) { + off += ret; + left -= ret; + + } else if (ret < 0) { + if (off > 0) { + break; /*have write ok some data. */ + } else if (ret == -EAGAIN) { + if (!(task->file->f_flags & O_NONBLOCK) && + (++wait_num < 10)) { + wait_event_interruptible_timeout( + task->wq, + !kfifo_is_empty( + &task->freefifo), + HZ / 100); + continue; /* write again. */ + } + ret = -EAGAIN; + break; + } + break; /*to end */ + } + } + + /*end: */ + spin_lock_irqsave(&task->lock, flags); + if (off > 0) { + task->buffered_data_size += off; + task->data_offset += off; + } + spin_unlock_irqrestore(&task->lock, flags); + if (off > 0) + return off; + else + return ret; +} + +static int do_write_work_in(struct threadrw_write_task *task) +{ + struct threadrw_buf *rwbuf = NULL; + int ret; + int need_re_write = 0; + int write_len = 0; + unsigned long flags; + + if (kfifo_is_empty(&task->datafifo)) + return 0; + if (!kfifo_peek(&task->datafifo, (void *)&rwbuf)) + return 0; + if (task->codec_mm_buffer && !rwbuf->write_off) + codec_mm_dma_flush(rwbuf->vbuffer, + rwbuf->data_size, + DMA_TO_DEVICE); + if (task->manual_write) { + ret = task->write(task->file, task->sbuf, + (const char __user *)rwbuf->vbuffer + rwbuf->write_off, + rwbuf->data_size, + 2); /* noblock,virtual addr */ + } else { + ret = task->write(task->file, task->sbuf, + (const char __user *)rwbuf->dma_handle + rwbuf->write_off, + rwbuf->data_size, + 3); /* noblock,phy addr */ + } + if (ret == -EAGAIN) { + need_re_write = 0; + /*do later retry. */ + } else if (ret >= rwbuf->data_size) { + write_len += rwbuf->data_size; + if (kfifo_get(&task->datafifo, (void *)&rwbuf)) { + rwbuf->data_size = 0; + kfifo_put(&task->freefifo, (const void *)rwbuf); + /*wakeup write thread. */ + wake_up_interruptible(&task->wq); + } else + pr_err("write ok,but kfifo_get data failed.!!!\n"); + need_re_write = 1; + } else if (ret > 0) { + rwbuf->data_size -= ret; /* half data write */ + rwbuf->write_off += ret; + write_len += ret; + need_re_write = 1; + } else { /*ret <=0 */ + pr_err("get errors ret=%d size=%d\n", ret, + rwbuf->data_size); + task->errors = ret; + } + if (write_len > 0) { + spin_lock_irqsave(&task->lock, flags); + task->passed_data_len += write_len; + task->buffered_data_size -= write_len; + spin_unlock_irqrestore(&task->lock, flags); + } + return need_re_write; + +} + +static void do_write_work(struct work_struct *work) +{ + struct threadrw_write_task *task = container_of(work, + struct threadrw_write_task, + write_work.work); + + task->writework_on = 1; + while (do_write_work_in(task)) + ; + threadrw_schedule_delayed_work(task, HZ / 10); + task->writework_on = 0; +} + +static int init_task_buffers(struct threadrw_write_task *task, int num, + int block_size) +{ + struct threadrw_buf *rwbuf; + int i; + int used_codec_mm = 1; + int buffers_num = num; + + if (used_codec_mm && (block_size * buffers_num) >= 128 * 1024) { + int total_mm = ALIGN(block_size * buffers_num, (1 << 17)); + + task->codec_mm_buffer = codec_mm_alloc_for_dma(BUF_NAME, + total_mm / PAGE_SIZE, 0, + CODEC_MM_FLAGS_DMA_CPU); + task->buffer_size = total_mm; + buffers_num = total_mm / block_size; + } + for (i = 0; i < buffers_num; i++) { + rwbuf = &task->buf[i]; + rwbuf->buffer_size = block_size > 0 ? + block_size : DEFAULT_BLOCK_SIZE; + if (task->codec_mm_buffer) { + rwbuf->buffer_size = block_size; + if (i == buffers_num - 1) + rwbuf->buffer_size = task->buffer_size - + block_size * i; + rwbuf->dma_handle = (dma_addr_t) task->codec_mm_buffer + + block_size * i; + rwbuf->vbuffer = codec_mm_phys_to_virt( + rwbuf->dma_handle); + + } else { + rwbuf->vbuffer = dma_alloc_coherent( + amports_get_dma_device(), + rwbuf->buffer_size, + &rwbuf->dma_handle, GFP_KERNEL); + if (!rwbuf->vbuffer) { + rwbuf->buffer_size = 0; + rwbuf->dma_handle = 0; + task->max_buf = i + 1; + break; + } + task->buffer_size += rwbuf->buffer_size; + } + + kfifo_put(&task->freefifo, (const void *)rwbuf); + task->max_buf = i + 1; + } + if (task->max_buf >= 3 || task->max_buf == num) + return 0; /*must >=3 for swap buffers. */ + if (task->max_buf > 0) + free_task_buffers(task); + return -1; +} + +static int free_task_buffers(struct threadrw_write_task *task) +{ + int i; + + if (task->codec_mm_buffer) + codec_mm_free_for_dma(BUF_NAME, task->codec_mm_buffer); + else { + for (i = 0; i < task->max_buf; i++) { + if (task->buf[i].vbuffer) + dma_free_coherent(amports_get_dma_device(), + task->buf[i].buffer_size, + task->buf[i].vbuffer, + task->buf[i].dma_handle); + } + } + return 0; +} + +static struct threadrw_write_task *threadrw_buf_alloc_in(int num, + int block_size, + ssize_t (*write)(struct file *, + struct stream_buf_s *, + const char __user *, size_t, int), + int flags) +{ + int task_buffer_size = sizeof(struct threadrw_write_task) + + sizeof(struct threadrw_buf) * (num - 1) + 4; + struct threadrw_write_task *task = vmalloc(task_buffer_size); + int ret; + + if (!task) + return NULL; + memset(task, 0, task_buffer_size); + + spin_lock_init(&task->lock); + INIT_DELAYED_WORK(&task->write_work, do_write_work); + init_waitqueue_head(&task->wq); + ret = kfifo_alloc(&task->datafifo, num, GFP_KERNEL); + if (ret) + goto err1; + ret = kfifo_alloc(&task->freefifo, num, GFP_KERNEL); + if (ret) + goto err2; + task->write = write; + task->file = NULL; + task->buffer_size = 0; + task->manual_write = flags & 1; + ret = init_task_buffers(task, num, block_size); + if (ret < 0) + goto err3; + threadrw_wq_get(); /*start thread. */ + return task; + +err3: + kfifo_free(&task->freefifo); +err2: + kfifo_free(&task->datafifo); +err1: + vfree(task); + pr_err("alloc threadrw failed num:%d,block:%d\n", num, block_size); + return NULL; +} + +/* +*fifo data size; +*/ + +int threadrw_buffer_level(struct stream_buf_s *stbuf) +{ + struct threadrw_write_task *task = stbuf->write_thread; + + if (task) + return task->buffered_data_size; + return 0; +} + +int threadrw_buffer_size(struct stream_buf_s *stbuf) +{ + struct threadrw_write_task *task = stbuf->write_thread; + + if (task) + return task->buffer_size; + return 0; +} + +int threadrw_datafifo_len(struct stream_buf_s *stbuf) +{ + struct threadrw_write_task *task = stbuf->write_thread; + + if (task) + return kfifo_len(&task->datafifo); + return 0; +} + +int threadrw_freefifo_len(struct stream_buf_s *stbuf) +{ + struct threadrw_write_task *task = stbuf->write_thread; + + if (task) + return kfifo_len(&task->freefifo); + return 0; +} + +/* +*data len out fifo; +*/ +int threadrw_passed_len(struct stream_buf_s *stbuf) +{ + struct threadrw_write_task *task = stbuf->write_thread; + + if (task) + return task->passed_data_len; + return 0; + +} +/* +*all data writed.; +*/ +int threadrw_dataoffset(struct stream_buf_s *stbuf) +{ + struct threadrw_write_task *task = stbuf->write_thread; + int offset = 0; + + if (task) + return task->data_offset; + return offset; + +} + +ssize_t threadrw_write(struct file *file, struct stream_buf_s *stbuf, + const char __user *buf, size_t count) +{ + struct threadrw_write_task *task = stbuf->write_thread; + + if (!task->file) { + task->file = file; + task->sbuf = stbuf; + } + return threadrw_write_in(task, stbuf, buf, count); +} + +int threadrw_flush_buffers(struct stream_buf_s *stbuf) +{ + struct threadrw_write_task *task = stbuf->write_thread; + int max_retry = 20; + + if (!task) + return 0; + while (!kfifo_is_empty(&task->datafifo) && max_retry-- > 0) { + threadrw_schedule_delayed_work(task, 0); + msleep(20); + } + if (!kfifo_is_empty(&task->datafifo)) + return -1;/*data not flushed*/ + return 0; +} + +void *threadrw_alloc(int num, + int block_size, + ssize_t (*write)(struct file *, + struct stream_buf_s *, + const char __user *, + size_t, int), + int flags) +{ + return threadrw_buf_alloc_in(num, block_size, write, flags); +} + +void threadrw_release(struct stream_buf_s *stbuf) +{ + struct threadrw_write_task *task = stbuf->write_thread; + + if (task) { + wake_up_interruptible(&task->wq); + cancel_delayed_work_sync(&task->write_work); + free_task_buffers(task); + kfifo_free(&task->freefifo); + kfifo_free(&task->datafifo); + vfree(task); + } + stbuf->write_thread = NULL; +} diff --git a/drivers/stream_input/parser/thread_rw.h b/drivers/stream_input/parser/thread_rw.h new file mode 100644 index 0000000..b16cf7b --- a/dev/null +++ b/drivers/stream_input/parser/thread_rw.h @@ -0,0 +1,42 @@ +/* + * drivers/amlogic/media/stream_input/parser/thread_rw.h + * + * Copyright (C) 2016 Amlogic, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * +*/ + +#ifndef THREAD_RW_H +#define THREAD_RW_H +#include "../../stream_input/parser/streambuf_reg.h" +#include "../../stream_input/parser/streambuf.h" +#include "../../stream_input/parser/esparser.h" +#include "../../stream_input/amports/amports_priv.h" + +ssize_t threadrw_write(struct file *file, + struct stream_buf_s *stbuf, const char __user *buf, size_t count); + +void *threadrw_alloc(int num, int block_size, ssize_t(*write) (struct file *, + struct stream_buf_s *, const char __user *, size_t, int), + int flags); /*flags &1: manual mode */ + +void threadrw_release(struct stream_buf_s *stbuf); + +int threadrw_buffer_level(struct stream_buf_s *stbuf); +int threadrw_buffer_size(struct stream_buf_s *stbuf); +int threadrw_datafifo_len(struct stream_buf_s *stbuf); +int threadrw_freefifo_len(struct stream_buf_s *stbuf); +int threadrw_passed_len(struct stream_buf_s *stbuf); +int threadrw_flush_buffers(struct stream_buf_s *stbuf); +int threadrw_dataoffset(struct stream_buf_s *stbuf); + +#endif diff --git a/drivers/stream_input/parser/tsdemux.c b/drivers/stream_input/parser/tsdemux.c new file mode 100644 index 0000000..a747b25 --- a/dev/null +++ b/drivers/stream_input/parser/tsdemux.c @@ -0,0 +1,1129 @@ +/* + * drivers/amlogic/media/stream_input/parser/tsdemux.c + * + * Copyright (C) 2016 Amlogic, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * +*/ + +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/interrupt.h> +#include <linux/wait.h> +#include <linux/sched.h> +#include <linux/fs.h> +#include <linux/dma-mapping.h> +#include <linux/amlogic/media/frame_sync/ptsserv.h> +#include <linux/amlogic/media/utils/amstream.h> +#include <linux/amlogic/media/vfm/vframe_provider.h> +#include <linux/device.h> +#include <linux/delay.h> + +#include <linux/uaccess.h> +/* #include <mach/am_regs.h> */ +#include <linux/clk.h> +/* #if MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON6 */ +/* #include <mach/mod_gate.h> */ +/* #endif */ + +#include "../../frame_provider/decoder/utils/vdec.h" +#include <linux/amlogic/media/utils/vdec_reg.h> +#include "streambuf_reg.h" +#include "streambuf.h" +#include <linux/amlogic/media/utils/amports_config.h> + +#include "tsdemux.h" +#include <linux/reset.h> +#include "../amports/amports_priv.h" + + +static const char tsdemux_fetch_id[] = "tsdemux-fetch-id"; +static const char tsdemux_irq_id[] = "tsdemux-irq-id"; + +static u32 curr_pcr_num = 0xffff; +static u32 curr_vid_id = 0xffff; +static u32 curr_aud_id = 0xffff; +static u32 curr_sub_id = 0xffff; +static u32 curr_pcr_id = 0xffff; + +static DECLARE_WAIT_QUEUE_HEAD(wq); +static u32 fetch_done; +static u32 discontinued_counter; +static u32 first_pcr; +static u8 pcrscr_valid; + +static int demux_skipbyte; + +static struct tsdemux_ops *demux_ops; +static DEFINE_SPINLOCK(demux_ops_lock); + +static int enable_demux_driver(void) +{ +#ifdef ENABLE_DEMUX_DRIVER + return demux_ops ? 1 : 0; +#else + return 0; +#endif +} + +void tsdemux_set_ops(struct tsdemux_ops *ops) +{ + unsigned long flags; + + spin_lock_irqsave(&demux_ops_lock, flags); + demux_ops = ops; + spin_unlock_irqrestore(&demux_ops_lock, flags); +} +EXPORT_SYMBOL(tsdemux_set_ops); + +int tsdemux_set_reset_flag_ext(void) +{ + int r = 0; + + if (demux_ops && demux_ops->set_reset_flag) + r = demux_ops->set_reset_flag(); + + return r; +} + +int tsdemux_set_reset_flag(void) +{ + unsigned long flags; + int r; + + spin_lock_irqsave(&demux_ops_lock, flags); + r = tsdemux_set_reset_flag_ext(); + spin_unlock_irqrestore(&demux_ops_lock, flags); + + return r; +} + +static int tsdemux_reset(void) +{ + unsigned long flags; + int r; + + spin_lock_irqsave(&demux_ops_lock, flags); + if (demux_ops && demux_ops->reset) { + tsdemux_set_reset_flag_ext(); + r = demux_ops->reset(); + } + spin_unlock_irqrestore(&demux_ops_lock, flags); + + return r; +} + +static int tsdemux_request_irq(irq_handler_t handler, void *data) +{ + unsigned long flags; + int r; + + spin_lock_irqsave(&demux_ops_lock, flags); + if (demux_ops && demux_ops->request_irq) + r = demux_ops->request_irq(handler, data); + spin_unlock_irqrestore(&demux_ops_lock, flags); + + return r; +} + +static int tsdemux_free_irq(void) +{ + unsigned long flags; + int r; + + spin_lock_irqsave(&demux_ops_lock, flags); + if (demux_ops && demux_ops->free_irq) + r = demux_ops->free_irq(); + spin_unlock_irqrestore(&demux_ops_lock, flags); + + return r; +} + +static int tsdemux_set_vid(int vpid) +{ + unsigned long flags; + int r = 0; + + spin_lock_irqsave(&demux_ops_lock, flags); + if (demux_ops && demux_ops->set_vid) + r = demux_ops->set_vid(vpid); + spin_unlock_irqrestore(&demux_ops_lock, flags); + + return r; +} + +static int tsdemux_set_aid(int apid) +{ + unsigned long flags; + int r = 0; + + spin_lock_irqsave(&demux_ops_lock, flags); + if (demux_ops && demux_ops->set_aid) + r = demux_ops->set_aid(apid); + spin_unlock_irqrestore(&demux_ops_lock, flags); + + return r; +} + +static int tsdemux_set_sid(int spid) +{ + unsigned long flags; + int r = 0; + + spin_lock_irqsave(&demux_ops_lock, flags); + if (demux_ops && demux_ops->set_sid) + r = demux_ops->set_sid(spid); + spin_unlock_irqrestore(&demux_ops_lock, flags); + + return r; +} + +static int tsdemux_set_pcrid(int pcrpid) +{ + unsigned long flags; + int r = 0; + + spin_lock_irqsave(&demux_ops_lock, flags); + if (demux_ops && demux_ops->set_pcrid) + r = demux_ops->set_pcrid(pcrpid); + spin_unlock_irqrestore(&demux_ops_lock, flags); + + return r; +} + +static int tsdemux_set_skip_byte(int skipbyte) +{ + unsigned long flags; + int r = 0; + + spin_lock_irqsave(&demux_ops_lock, flags); + if (demux_ops && demux_ops->set_skipbyte) + r = demux_ops->set_skipbyte(skipbyte); + spin_unlock_irqrestore(&demux_ops_lock, flags); + + return r; +} + +static int tsdemux_config(void) +{ + return 0; +} + +/*TODO irq*/ +static irqreturn_t tsdemux_isr(int irq, void *dev_id) +{ + u32 int_status = 0; + int id = (long)dev_id; + + if (!enable_demux_driver()) { + int_status = READ_MPEG_REG(STB_INT_STATUS); + } else { + if (id == 0) + int_status = READ_MPEG_REG(STB_INT_STATUS); + else if (id == 1) + int_status = READ_MPEG_REG(STB_INT_STATUS_2); + else if (id == 2) + int_status = READ_MPEG_REG(STB_INT_STATUS_3); + } + + if (int_status & (1 << NEW_PDTS_READY)) { + if (!enable_demux_driver()) { + u32 pdts_status = READ_MPEG_REG(STB_PTS_DTS_STATUS); + + if (pdts_status & (1 << VIDEO_PTS_READY)) + pts_checkin_wrptr(PTS_TYPE_VIDEO, + READ_MPEG_REG(VIDEO_PDTS_WR_PTR), + READ_MPEG_REG(VIDEO_PTS_DEMUX)); + + if (pdts_status & (1 << AUDIO_PTS_READY)) + pts_checkin_wrptr(PTS_TYPE_AUDIO, + READ_MPEG_REG(AUDIO_PDTS_WR_PTR), + READ_MPEG_REG(AUDIO_PTS_DEMUX)); + + WRITE_MPEG_REG(STB_PTS_DTS_STATUS, pdts_status); + } else { +#define DMX_READ_REG(i, r)\ + ((i) ? ((i == 1) ? READ_MPEG_REG(r##_2) : \ + READ_MPEG_REG(r##_3)) : READ_MPEG_REG(r)) + + u32 pdts_status = DMX_READ_REG(id, STB_PTS_DTS_STATUS); + + if (pdts_status & (1 << VIDEO_PTS_READY)) + pts_checkin_wrptr(PTS_TYPE_VIDEO, + DMX_READ_REG(id, VIDEO_PDTS_WR_PTR), + DMX_READ_REG(id, VIDEO_PTS_DEMUX)); + + if (pdts_status & (1 << AUDIO_PTS_READY)) + pts_checkin_wrptr(PTS_TYPE_AUDIO, + DMX_READ_REG(id, AUDIO_PDTS_WR_PTR), + DMX_READ_REG(id, AUDIO_PTS_DEMUX)); + + if (id == 1) + WRITE_MPEG_REG(STB_PTS_DTS_STATUS_2, + pdts_status); + else if (id == 2) + WRITE_MPEG_REG(STB_PTS_DTS_STATUS_3, + pdts_status); + else + WRITE_MPEG_REG(STB_PTS_DTS_STATUS, + pdts_status); + } + } + if (int_status & (1 << DIS_CONTINUITY_PACKET)) { + discontinued_counter++; + /* pr_info("discontinued counter=%d\n",discontinued_counter); */ + } + if (int_status & (1 << SUB_PES_READY)) { + /* TODO: put data to somewhere */ + /* pr_info("subtitle pes ready\n"); */ + wakeup_sub_poll(); + } + + if (!enable_demux_driver()) + WRITE_MPEG_REG(STB_INT_STATUS, int_status); + + return IRQ_HANDLED; +} + +static irqreturn_t parser_isr(int irq, void *dev_id) +{ + u32 int_status = READ_MPEG_REG(PARSER_INT_STATUS); + + WRITE_MPEG_REG(PARSER_INT_STATUS, int_status); + + if (int_status & PARSER_INTSTAT_FETCH_CMD) { + fetch_done = 1; + + wake_up_interruptible(&wq); + } + + return IRQ_HANDLED; +} + +static ssize_t _tsdemux_write(const char __user *buf, size_t count, + int isphybuf) +{ + size_t r = count; + const char __user *p = buf; + u32 len; + int ret; + dma_addr_t dma_addr = 0; + + if (r > 0) { + if (isphybuf) + len = count; + else { + len = min_t(size_t, r, FETCHBUF_SIZE); + if (copy_from_user(fetchbuf, p, len)) + return -EFAULT; + + dma_addr = + dma_map_single(amports_get_dma_device(), + fetchbuf, + FETCHBUF_SIZE, DMA_TO_DEVICE); + if (dma_mapping_error(amports_get_dma_device(), + dma_addr)) + return -EFAULT; + + + } + + fetch_done = 0; + + wmb(); /* Ensure fetchbuf contents visible */ + + if (isphybuf) { + u32 buf_32 = (unsigned long)buf & 0xffffffff; + + WRITE_MPEG_REG(PARSER_FETCH_ADDR, buf_32); + } else { + WRITE_MPEG_REG(PARSER_FETCH_ADDR, dma_addr); + dma_unmap_single(amports_get_dma_device(), dma_addr, + FETCHBUF_SIZE, DMA_TO_DEVICE); + } + + WRITE_MPEG_REG(PARSER_FETCH_CMD, (7 << FETCH_ENDIAN) | len); + + + ret = + wait_event_interruptible_timeout(wq, fetch_done != 0, + HZ / 2); + if (ret == 0) { + WRITE_MPEG_REG(PARSER_FETCH_CMD, 0); + pr_info("write timeout, retry\n"); + return -EAGAIN; + } else if (ret < 0) + return -ERESTARTSYS; + + p += len; + r -= len; + } + + return count - r; +} + +static int reset_pcr_regs(void) +{ + u32 pcr_num; + + if (curr_pcr_id >= 0x1FFF) + return 0; + + /* set parameter to fetch pcr */ + pcr_num = 0; + if (curr_pcr_id == curr_vid_id) + pcr_num = 0; + else if (curr_pcr_id == curr_aud_id) + pcr_num = 1; + else if (curr_pcr_id == curr_sub_id) + pcr_num = 2; + else + pcr_num = 3; + + if (pcr_num != curr_pcr_num) { + u32 clk_unit = 0; + u32 clk_81 = 0; + struct clk *clk; + + clk = clk_get_sys("clk81", "clk81"); + if (IS_ERR(clk) || clk == 0) { + pr_info("[%s:%d] error clock\n", __func__, + __LINE__); + return 0; + } + + clk_81 = clk_get_rate(clk); + clk_unit = clk_81 / 80000; + + pr_info("[%s:%d] clk_81 = %x clk_unit =%x\n", __func__, + __LINE__, clk_81, clk_unit); + + if (READ_MPEG_REG(TS_HIU_CTL_2) & 0x80) { + WRITE_MPEG_REG(PCR90K_CTL_2, (12 << 1) | clk_unit); + WRITE_MPEG_REG(ASSIGN_PID_NUMBER_2, pcr_num); + pr_info("[tsdemux_init] To use device 2,pcr_num=%d\n", + pcr_num); + } else if (READ_MPEG_REG(TS_HIU_CTL_3) & 0x80) { + WRITE_MPEG_REG(PCR90K_CTL_3, (12 << 1) | clk_unit); + WRITE_MPEG_REG(ASSIGN_PID_NUMBER_3, pcr_num); + pr_info("[tsdemux_init] To use device 3,pcr_num=%d\n", + pcr_num); + } else { + WRITE_MPEG_REG(PCR90K_CTL, (12 << 1) | clk_unit); + WRITE_MPEG_REG(ASSIGN_PID_NUMBER, pcr_num); + pr_info("[tsdemux_init] To use device 1,pcr_num=%d\n", + pcr_num); + } + + curr_pcr_num = pcr_num; + } + + return 1; +} + +s32 tsdemux_init(u32 vid, u32 aid, u32 sid, u32 pcrid, bool is_hevc, + struct vdec_s *vdec) +{ + s32 r; + u32 parser_sub_start_ptr; + u32 parser_sub_end_ptr; + u32 parser_sub_rp; + + /* #if MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON6 */ + /*TODO clk */ + /* + *switch_mod_gate_by_type(MOD_DEMUX, 1); + */ + /* #endif */ + + amports_switch_gate("demux", 1); + + parser_sub_start_ptr = READ_MPEG_REG(PARSER_SUB_START_PTR); + parser_sub_end_ptr = READ_MPEG_REG(PARSER_SUB_END_PTR); + parser_sub_rp = READ_MPEG_REG(PARSER_SUB_RP); + + WRITE_MPEG_REG(RESET1_REGISTER, RESET_PARSER); + + if (enable_demux_driver()) { + tsdemux_reset(); + } else { + WRITE_MPEG_REG(RESET1_REGISTER, RESET_PARSER | RESET_DEMUXSTB); + + WRITE_MPEG_REG(STB_TOP_CONFIG, 0); + WRITE_MPEG_REG(DEMUX_CONTROL, 0); + } + + /* set PID filter */ + pr_info + ("tsdemux video_pid = 0x%x, audio_pid = 0x%x,", + vid, aid); + pr_info + ("sub_pid = 0x%x, pcrid = 0x%x\n", + sid, pcrid); + + if (!enable_demux_driver()) { + WRITE_MPEG_REG(FM_WR_DATA, + (((vid < 0x1fff) + ? (vid & 0x1fff) | (VIDEO_PACKET << 13) + : 0xffff) << 16) + | ((aid < 0x1fff) + ? (aid & 0x1fff) | (AUDIO_PACKET << 13) + : 0xffff)); + WRITE_MPEG_REG(FM_WR_ADDR, 0x8000); + while (READ_MPEG_REG(FM_WR_ADDR) & 0x8000) + ; + + WRITE_MPEG_REG(FM_WR_DATA, + (((sid < 0x1fff) + ? (sid & 0x1fff) | (SUB_PACKET << 13) + : 0xffff) << 16) + | 0xffff); + WRITE_MPEG_REG(FM_WR_ADDR, 0x8001); + while (READ_MPEG_REG(FM_WR_ADDR) & 0x8000) + ; + + WRITE_MPEG_REG(MAX_FM_COMP_ADDR, 1); + + WRITE_MPEG_REG(STB_INT_MASK, 0); + WRITE_MPEG_REG(STB_INT_STATUS, 0xffff); + + /* TS data path */ + WRITE_MPEG_REG(FEC_INPUT_CONTROL, 0x7000); + WRITE_MPEG_REG(DEMUX_MEM_REQ_EN, + (1 << VIDEO_PACKET) | + (1 << AUDIO_PACKET) | (1 << SUB_PACKET)); + WRITE_MPEG_REG(DEMUX_ENDIAN, + (7 << OTHER_ENDIAN) | + (7 << BYPASS_ENDIAN) | (0 << SECTION_ENDIAN)); + WRITE_MPEG_REG(TS_HIU_CTL, 1 << USE_HI_BSF_INTERFACE); + WRITE_MPEG_REG(TS_FILE_CONFIG, + (demux_skipbyte << 16) | + (6 << DES_OUT_DLY) | + (3 << TRANSPORT_SCRAMBLING_CONTROL_ODD) | + (1 << TS_HIU_ENABLE) | (4 << FEC_FILE_CLK_DIV)); + + /* enable TS demux */ + WRITE_MPEG_REG(DEMUX_CONTROL, + (1 << STB_DEMUX_ENABLE) | + (1 << KEEP_DUPLICATE_PACKAGE)); + } + + if (fetchbuf == 0) { + pr_info("%s: no fetchbuf\n", __func__); + return -ENOMEM; + } + + /* hook stream buffer with PARSER */ + /* #if MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON8 */ + if (has_hevc_vdec() && is_hevc) { + WRITE_MPEG_REG(PARSER_VIDEO_START_PTR, vdec->input.start); + WRITE_MPEG_REG(PARSER_VIDEO_END_PTR, vdec->input.start + + vdec->input.size - 8); + + if (vdec_stream_based(vdec)) { + CLEAR_MPEG_REG_MASK(PARSER_ES_CONTROL, + ES_VID_MAN_RD_PTR); + /* set vififo_vbuf_rp_sel=>hevc */ + WRITE_VREG(DOS_GEN_CTRL0, 3 << 1); + /* set use_parser_vbuf_wp */ + SET_VREG_MASK(HEVC_STREAM_CONTROL, + (1 << 3) | (0 << 4)); + /* set stream_fetch_enable */ + SET_VREG_MASK(HEVC_STREAM_CONTROL, 1); + /* set stream_buffer_hole with 256 bytes */ + SET_VREG_MASK(HEVC_STREAM_FIFO_CTL, + (1 << 29)); + } else { + SET_MPEG_REG_MASK(PARSER_ES_CONTROL, ES_VID_MAN_RD_PTR); + WRITE_MPEG_REG(PARSER_VIDEO_WP, vdec->input.start); + WRITE_MPEG_REG(PARSER_VIDEO_RP, vdec->input.start); + } + } else + /* #endif */ + { + WRITE_MPEG_REG(PARSER_VIDEO_START_PTR, vdec->input.start); + WRITE_MPEG_REG(PARSER_VIDEO_END_PTR, vdec->input.start + + vdec->input.size - 8); + + if (vdec_stream_based(vdec)) { + CLEAR_MPEG_REG_MASK(PARSER_ES_CONTROL, + ES_VID_MAN_RD_PTR); + + WRITE_VREG(VLD_MEM_VIFIFO_BUF_CNTL, MEM_BUFCTRL_INIT); + CLEAR_VREG_MASK(VLD_MEM_VIFIFO_BUF_CNTL, + MEM_BUFCTRL_INIT); + /* set vififo_vbuf_rp_sel=>vdec */ + if (has_hevc_vdec()) + WRITE_VREG(DOS_GEN_CTRL0, 0); + } else { + SET_MPEG_REG_MASK(PARSER_ES_CONTROL, ES_VID_MAN_RD_PTR); + WRITE_MPEG_REG(PARSER_VIDEO_WP, vdec->input.start); + WRITE_MPEG_REG(PARSER_VIDEO_RP, vdec->input.start); + } + } + + WRITE_MPEG_REG(PARSER_AUDIO_START_PTR, + READ_MPEG_REG(AIU_MEM_AIFIFO_START_PTR)); + WRITE_MPEG_REG(PARSER_AUDIO_END_PTR, + READ_MPEG_REG(AIU_MEM_AIFIFO_END_PTR)); + CLEAR_MPEG_REG_MASK(PARSER_ES_CONTROL, ES_AUD_MAN_RD_PTR); + + WRITE_MPEG_REG(PARSER_CONFIG, + (10 << PS_CFG_PFIFO_EMPTY_CNT_BIT) | + (1 << PS_CFG_MAX_ES_WR_CYCLE_BIT) | + (16 << PS_CFG_MAX_FETCH_CYCLE_BIT)); + + WRITE_MPEG_REG(AIU_MEM_AIFIFO_BUF_CNTL, MEM_BUFCTRL_INIT); + CLEAR_MPEG_REG_MASK(AIU_MEM_AIFIFO_BUF_CNTL, MEM_BUFCTRL_INIT); + + WRITE_MPEG_REG(PARSER_SUB_START_PTR, parser_sub_start_ptr); + WRITE_MPEG_REG(PARSER_SUB_END_PTR, parser_sub_end_ptr); + WRITE_MPEG_REG(PARSER_SUB_RP, parser_sub_rp); + SET_MPEG_REG_MASK(PARSER_ES_CONTROL, + (7 << ES_SUB_WR_ENDIAN_BIT) | ES_SUB_MAN_RD_PTR); + + /* #if MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON8 */ + if (has_hevc_vdec()) + r = pts_start((is_hevc) ? PTS_TYPE_HEVC : PTS_TYPE_VIDEO); + else + /* #endif */ + r = pts_start(PTS_TYPE_VIDEO); + + if (r < 0) { + pr_info("Video pts start failed.(%d)\n", r); + goto err1; + } + r = pts_start(PTS_TYPE_AUDIO); + if (r < 0) { + pr_info("Audio pts start failed.(%d)\n", r); + goto err2; + } + /*TODO irq */ + + r = vdec_request_irq(PARSER_IRQ, parser_isr, + "tsdemux-fetch", (void *)tsdemux_fetch_id); + + if (r) + goto err3; + + WRITE_MPEG_REG(PARSER_INT_STATUS, 0xffff); + WRITE_MPEG_REG(PARSER_INT_ENABLE, + PARSER_INTSTAT_FETCH_CMD << PARSER_INT_HOST_EN_BIT); + + WRITE_MPEG_REG(PARSER_VIDEO_HOLE, 0x400); + WRITE_MPEG_REG(PARSER_AUDIO_HOLE, 0x400); + + discontinued_counter = 0; + + if (!enable_demux_driver()) { + /*TODO irq */ + + r = vdec_request_irq(DEMUX_IRQ, tsdemux_isr, + "tsdemux-irq", (void *)tsdemux_irq_id); + + WRITE_MPEG_REG(STB_INT_MASK, (1 << SUB_PES_READY) + | (1 << NEW_PDTS_READY) + | (1 << DIS_CONTINUITY_PACKET)); + if (r) + goto err4; + } else { + tsdemux_config(); + tsdemux_request_irq(tsdemux_isr, (void *)tsdemux_irq_id); + if (vid < 0x1FFF) { + curr_vid_id = vid; + tsdemux_set_vid(vid); + } + if (aid < 0x1FFF) { + curr_aud_id = aid; + tsdemux_set_aid(aid); + } + if (sid < 0x1FFF) { + curr_sub_id = sid; + tsdemux_set_sid(sid); + } + + curr_pcr_id = pcrid; + if ((pcrid < 0x1FFF) && (pcrid != vid) && (pcrid != aid) + && (pcrid != sid)) + tsdemux_set_pcrid(pcrid); + } + + pcrscr_valid = reset_pcr_regs(); + first_pcr = 0; + + return 0; + +err4: + /*TODO irq */ + + if (!enable_demux_driver()) + vdec_free_irq(PARSER_IRQ, (void *)tsdemux_fetch_id); + +err3: + pts_stop(PTS_TYPE_AUDIO); +err2: + /* #if MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON8 */ + if (has_hevc_vdec()) + pts_stop((is_hevc) ? PTS_TYPE_HEVC : PTS_TYPE_VIDEO); + else + /* #endif */ + pts_stop(PTS_TYPE_VIDEO); +err1: + pr_info("TS Demux init failed.\n"); + return -ENOENT; +} + +void tsdemux_release(void) +{ + pcrscr_valid = 0; + first_pcr = 0; + + WRITE_MPEG_REG(PARSER_INT_ENABLE, 0); + WRITE_MPEG_REG(PARSER_VIDEO_HOLE, 0); + WRITE_MPEG_REG(PARSER_AUDIO_HOLE, 0); + /*TODO irq */ + + vdec_free_irq(PARSER_IRQ, (void *)tsdemux_fetch_id); + + if (!enable_demux_driver()) { + WRITE_MPEG_REG(STB_INT_MASK, 0); + /*TODO irq */ + + vdec_free_irq(DEMUX_IRQ, (void *)tsdemux_irq_id); + } else { + + tsdemux_set_aid(0xffff); + tsdemux_set_vid(0xffff); + tsdemux_set_sid(0xffff); + tsdemux_set_pcrid(0xffff); + tsdemux_free_irq(); + + curr_vid_id = 0xffff; + curr_aud_id = 0xffff; + curr_sub_id = 0xffff; + curr_pcr_id = 0xffff; + curr_pcr_num = 0xffff; + } + + pts_stop(PTS_TYPE_VIDEO); + pts_stop(PTS_TYPE_AUDIO); + + WRITE_MPEG_REG(RESET1_REGISTER, RESET_PARSER); + + /* #if MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON6 */ + /*TODO clk */ + /* + *switch_mod_gate_by_type(MOD_DEMUX, 0); + */ + /* #endif */ + amports_switch_gate("demux", 0); + +} + +static int limited_delay_check(struct file *file, + struct stream_buf_s *vbuf, + struct stream_buf_s *abuf, + const char __user *buf, size_t count) +{ + int write_size; + + if (vbuf->max_buffer_delay_ms > 0 && abuf->max_buffer_delay_ms > 0 && + stbuf_level(vbuf) > 1024 && stbuf_level(abuf) > 256) { + int vdelay = + calculation_stream_delayed_ms(PTS_TYPE_VIDEO, + NULL, NULL); + int adelay = + calculation_stream_delayed_ms(PTS_TYPE_AUDIO, + NULL, NULL); + /*max wait 100ms,if timeout,try again top level. */ + int maxretry = 10; + /*too big delay,do wait now. */ + /*if noblock mode,don't do wait. */ + if (!(file->f_flags & O_NONBLOCK)) { + while (vdelay > vbuf->max_buffer_delay_ms + && adelay > abuf->max_buffer_delay_ms + && maxretry-- > 0) { + msleep(20); + vdelay = + calculation_stream_delayed_ms + (PTS_TYPE_VIDEO, NULL, NULL); + adelay = + calculation_stream_delayed_ms + (PTS_TYPE_AUDIO, NULL, NULL); + } + } + if (vdelay > vbuf->max_buffer_delay_ms + && adelay > abuf->max_buffer_delay_ms) + return 0; + } + write_size = min(stbuf_space(vbuf), stbuf_space(abuf)); + write_size = min_t(int, count, write_size); + return write_size; +} + +ssize_t drm_tswrite(struct file *file, + struct stream_buf_s *vbuf, + struct stream_buf_s *abuf, + const char __user *buf, size_t count) +{ + s32 r; + u32 realcount = count; + u32 re_count = count; + u32 havewritebytes = 0; + + struct drm_info tmpmm; + struct drm_info *drm = &tmpmm; + u32 res = 0; + int isphybuf = 0; + unsigned long realbuf; + + struct port_priv_s *priv = (struct port_priv_s *)file->private_data; + struct stream_port_s *port = priv->port; + size_t wait_size, write_size; + + if (buf == NULL || count == 0) + return -EINVAL; + + res = copy_from_user(drm, buf, sizeof(struct drm_info)); + if (res) { + pr_info("drm kmalloc failed res[%d]\n", res); + return -EFAULT; + } + + if (drm->drm_flag == TYPE_DRMINFO && drm->drm_level == DRM_LEVEL1) { + /* buf only has drminfo not have esdata; */ + realcount = drm->drm_pktsize; + realbuf = drm->drm_phy; + isphybuf = 1; + } else + realbuf = (unsigned long)buf; + /* pr_info("drm->drm_flag = 0x%x,realcount = %d , buf = 0x%x ",*/ + /*drm->drm_flag,realcount, buf); */ + + count = realcount; + + while (count > 0) { + if ((stbuf_space(vbuf) < count) || + (stbuf_space(abuf) < count)) { + if (file->f_flags & O_NONBLOCK) { + int v_stbuf_space = stbuf_space(vbuf); + int a_stbuf_space = stbuf_space(abuf); + + write_size = min(v_stbuf_space, a_stbuf_space); + /*have 188 bytes,write now., */ + if (write_size <= 188) + return -EAGAIN; + } else { + wait_size = + min(stbuf_canusesize(vbuf) / 8, + stbuf_canusesize(abuf) / 4); + if ((port->flag & PORT_FLAG_VID) + && (stbuf_space(vbuf) < wait_size)) { + r = stbuf_wait_space(vbuf, wait_size); + + if (r < 0) { + pr_info + ("write no space--- "); + pr_info + ("no space,%d--%d,r-%d\n", + stbuf_space(vbuf), + stbuf_space(abuf), r); + return r; + } + } + + if ((port->flag & PORT_FLAG_AID) + && (stbuf_space(abuf) < wait_size)) { + r = stbuf_wait_space(abuf, wait_size); + + if (r < 0) { + pr_info + ("write no stbuf_wait_space--"); + pr_info + ("no space,%d--%d,r-%d\n", + stbuf_space(vbuf), + stbuf_space(abuf), r); + return r; + } + } + } + } + + write_size = min(stbuf_space(vbuf), stbuf_space(abuf)); + write_size = min(count, write_size); + /* pr_info("write_size = %d,count = %d,\n",*/ + /*write_size, count); */ + if (write_size > 0) + r = _tsdemux_write((const char __user *)realbuf, + write_size, isphybuf); + else + return -EAGAIN; + + havewritebytes += r; + + /* pr_info("havewritebytes = %d, r = %d,\n",*/ + /*havewritebytes, r); */ + if (havewritebytes == realcount) + break; /* write ok; */ + else if (havewritebytes > realcount) + pr_info(" error ! write too much\n"); + + count -= r; + } + return re_count; +} + +ssize_t tsdemux_write(struct file *file, + struct stream_buf_s *vbuf, + struct stream_buf_s *abuf, + const char __user *buf, size_t count) +{ + s32 r; + struct port_priv_s *priv = (struct port_priv_s *)file->private_data; + struct stream_port_s *port = priv->port; + size_t wait_size, write_size; + + if ((stbuf_space(vbuf) < count) || (stbuf_space(abuf) < count)) { + if (file->f_flags & O_NONBLOCK) { + write_size = min(stbuf_space(vbuf), stbuf_space(abuf)); + if (write_size <= 188) /*have 188 bytes,write now., */ + return -EAGAIN; + } else { + wait_size = + min(stbuf_canusesize(vbuf) / 8, + stbuf_canusesize(abuf) / 4); + if ((port->flag & PORT_FLAG_VID) + && (stbuf_space(vbuf) < wait_size)) { + r = stbuf_wait_space(vbuf, wait_size); + + if (r < 0) { + /* + *pr_info("write no space--- "); + *pr_info("no space,%d--%d,r-%d\n", + *stbuf_space(vbuf), + *stbuf_space(abuf),r); + */ + return r; + } + } + + if ((port->flag & PORT_FLAG_AID) + && (stbuf_space(abuf) < wait_size)) { + r = stbuf_wait_space(abuf, wait_size); + + if (r < 0) { + /* + *pr_info("write no stbuf_wait_space")' + *pr_info{"--- no space,%d--%d,r-%d\n", + *stbuf_space(vbuf), + *stbuf_space(abuf),r); + */ + return r; + } + } + } + } + vbuf->last_write_jiffies64 = jiffies_64; + abuf->last_write_jiffies64 = jiffies_64; + write_size = limited_delay_check(file, vbuf, abuf, buf, count); + if (write_size > 0) + return _tsdemux_write(buf, write_size, 0); + else + return -EAGAIN; +} + +static ssize_t show_discontinue_counter(struct class *class, + struct class_attribute *attr, char *buf) +{ + return sprintf(buf, "%d\n", discontinued_counter); +} + +static struct class_attribute tsdemux_class_attrs[] = { + __ATTR(discontinue_counter, S_IRUGO, show_discontinue_counter, NULL), + __ATTR_NULL +}; + +static struct class tsdemux_class = { + .name = "tsdemux", + .class_attrs = tsdemux_class_attrs, + }; + +int tsdemux_class_register(void) +{ + int r = class_register(&tsdemux_class); + + if (r < 0) + pr_info("register tsdemux class error!\n"); + discontinued_counter = 0; + return r; +} + +void tsdemux_class_unregister(void) +{ + class_unregister(&tsdemux_class); +} + +void tsdemux_change_avid(unsigned int vid, unsigned int aid) +{ + if (!enable_demux_driver()) { + WRITE_MPEG_REG(FM_WR_DATA, + (((vid & 0x1fff) | (VIDEO_PACKET << 13)) << 16) + | ((aid & 0x1fff) | (AUDIO_PACKET << 13))); + WRITE_MPEG_REG(FM_WR_ADDR, 0x8000); + while (READ_MPEG_REG(FM_WR_ADDR) & 0x8000) + ; + } else { + curr_vid_id = vid; + curr_aud_id = aid; + + tsdemux_set_vid(vid); + tsdemux_set_aid(aid); + + reset_pcr_regs(); + } + +} + +void tsdemux_change_sid(unsigned int sid) +{ + if (!enable_demux_driver()) { + WRITE_MPEG_REG(FM_WR_DATA, + (((sid & 0x1fff) | (SUB_PACKET << 13)) << 16) + | 0xffff); + WRITE_MPEG_REG(FM_WR_ADDR, 0x8001); + while (READ_MPEG_REG(FM_WR_ADDR) & 0x8000) + ; + } else { + curr_sub_id = sid; + + tsdemux_set_sid(sid); + + reset_pcr_regs(); + } + +} + +void tsdemux_audio_reset(void) +{ + ulong flags; + + DEFINE_SPINLOCK(lock); + + spin_lock_irqsave(&lock, flags); + + WRITE_MPEG_REG(PARSER_AUDIO_WP, + READ_MPEG_REG(AIU_MEM_AIFIFO_START_PTR)); + WRITE_MPEG_REG(PARSER_AUDIO_RP, + READ_MPEG_REG(AIU_MEM_AIFIFO_START_PTR)); + + WRITE_MPEG_REG(PARSER_AUDIO_START_PTR, + READ_MPEG_REG(AIU_MEM_AIFIFO_START_PTR)); + WRITE_MPEG_REG(PARSER_AUDIO_END_PTR, + READ_MPEG_REG(AIU_MEM_AIFIFO_END_PTR)); + CLEAR_MPEG_REG_MASK(PARSER_ES_CONTROL, ES_AUD_MAN_RD_PTR); + + WRITE_MPEG_REG(AIU_MEM_AIFIFO_BUF_CNTL, MEM_BUFCTRL_INIT); + CLEAR_MPEG_REG_MASK(AIU_MEM_AIFIFO_BUF_CNTL, MEM_BUFCTRL_INIT); + + spin_unlock_irqrestore(&lock, flags); + +} + +void tsdemux_sub_reset(void) +{ + ulong flags; + DEFINE_SPINLOCK(lock); + u32 parser_sub_start_ptr; + u32 parser_sub_end_ptr; + + spin_lock_irqsave(&lock, flags); + + parser_sub_start_ptr = READ_MPEG_REG(PARSER_SUB_START_PTR); + parser_sub_end_ptr = READ_MPEG_REG(PARSER_SUB_END_PTR); + + WRITE_MPEG_REG(PARSER_SUB_START_PTR, parser_sub_start_ptr); + WRITE_MPEG_REG(PARSER_SUB_END_PTR, parser_sub_end_ptr); + WRITE_MPEG_REG(PARSER_SUB_RP, parser_sub_start_ptr); + WRITE_MPEG_REG(PARSER_SUB_WP, parser_sub_start_ptr); + SET_MPEG_REG_MASK(PARSER_ES_CONTROL, + (7 << ES_SUB_WR_ENDIAN_BIT) | ES_SUB_MAN_RD_PTR); + + spin_unlock_irqrestore(&lock, flags); + +} + +void tsdemux_set_skipbyte(int skipbyte) +{ + if (!enable_demux_driver()) + demux_skipbyte = skipbyte; + else + tsdemux_set_skip_byte(skipbyte); + +} + +void tsdemux_set_demux(int dev) +{ + if (enable_demux_driver()) { + unsigned long flags; + int r = 0; + + spin_lock_irqsave(&demux_ops_lock, flags); + if (demux_ops && demux_ops->set_demux) + r = demux_ops->set_demux(dev); + spin_unlock_irqrestore(&demux_ops_lock, flags); + } +} + +u32 tsdemux_pcrscr_get(void) +{ + u32 pcr = 0; + + if (pcrscr_valid == 0) + return 0; + + if (READ_MPEG_REG(TS_HIU_CTL_2) & 0x80) + pcr = READ_MPEG_REG(PCR_DEMUX_2); + else if (READ_MPEG_REG(TS_HIU_CTL_3) & 0x80) + pcr = READ_MPEG_REG(PCR_DEMUX_3); + else + pcr = READ_MPEG_REG(PCR_DEMUX); + if (first_pcr == 0) + first_pcr = pcr; + return pcr; +} + +u32 tsdemux_first_pcrscr_get(void) +{ + if (pcrscr_valid == 0) + return 0; + + if (first_pcr == 0) { + u32 pcr; + + if (READ_MPEG_REG(TS_HIU_CTL_2) & 0x80) + pcr = READ_MPEG_REG(PCR_DEMUX_2); + else if (READ_MPEG_REG(TS_HIU_CTL_3) & 0x80) + pcr = READ_MPEG_REG(PCR_DEMUX_3); + else + pcr = READ_MPEG_REG(PCR_DEMUX); + first_pcr = pcr; + /* pr_info("set first_pcr = 0x%x\n", pcr); */ + } + + return first_pcr; +} + +u8 tsdemux_pcrscr_valid(void) +{ + return pcrscr_valid; +} diff --git a/drivers/stream_input/parser/tsdemux.h b/drivers/stream_input/parser/tsdemux.h new file mode 100644 index 0000000..13ab4cd --- a/dev/null +++ b/drivers/stream_input/parser/tsdemux.h @@ -0,0 +1,93 @@ +/* + * drivers/amlogic/media/stream_input/parser/tsdemux.h + * + * Copyright (C) 2016 Amlogic, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * +*/ + +#ifndef TSDEMUX_H +#define TSDEMUX_H +#include <linux/amlogic/media/utils/amports_config.h> + +/* TODO: move to register headers */ +#define NEW_PDTS_READY 4 +#define AUDIO_PTS_READY 2 +#define VIDEO_PTS_READY 0 +#define DIS_CONTINUITY_PACKET 6 +#define SUB_PES_READY 7 + +#define PARSER_INTSTAT_FETCH_CMD (1<<7) + +#define FETCH_ENDIAN 27 +#define FETCH_ENDIAN_MASK (0x7<<27) + +#define RESET_DEMUXSTB (1<<1) +#define RESET_PARSER (1<<8) + +#define VIDEO_PACKET 0 +#define AUDIO_PACKET 1 +#define SUB_PACKET 2 + +#define OTHER_ENDIAN 6 +#define BYPASS_ENDIAN 3 +#define SECTION_ENDIAN 0 + +#define USE_HI_BSF_INTERFACE 7 +#define DES_OUT_DLY 8 +#define TRANSPORT_SCRAMBLING_CONTROL_ODD 6 +#define TS_HIU_ENABLE 5 +#define FEC_FILE_CLK_DIV 0 +#define STB_DEMUX_ENABLE 4 +#define KEEP_DUPLICATE_PACKAGE 6 + +#define ES_VID_MAN_RD_PTR (1<<0) +#define ES_AUD_MAN_RD_PTR (1<<4) + +#define PS_CFG_PFIFO_EMPTY_CNT_BIT 16 +#define PS_CFG_MAX_ES_WR_CYCLE_BIT 12 +#define PS_CFG_MAX_FETCH_CYCLE_BIT 0 + +#define ES_SUB_WR_ENDIAN_BIT 9 +#define ES_SUB_MAN_RD_PTR (1<<8) +#define PARSER_INTSTAT_FETCH_CMD (1<<7) + +#define PARSER_INT_HOST_EN_BIT 8 + +struct stream_buf_s; +struct vdec_s; + +extern s32 tsdemux_init(u32 vid, u32 aid, u32 sid, u32 pcrid, bool is_hevc, + struct vdec_s *vdec); + +extern void tsdemux_release(void); +extern ssize_t drm_tswrite(struct file *file, + struct stream_buf_s *vbuf, + struct stream_buf_s *abuf, const char __user *buf, size_t count); + +extern ssize_t tsdemux_write(struct file *file, + struct stream_buf_s *vbuf, + struct stream_buf_s *abuf, const char __user *buf, size_t count); + +extern u32 tsdemux_pcrscr_get(void); +extern u8 tsdemux_pcrscr_valid(void); +extern u32 tsdemux_first_pcrscr_get(void); + +int tsdemux_class_register(void); +void tsdemux_class_unregister(void); +void tsdemux_change_avid(unsigned int vid, unsigned int aid); +void tsdemux_change_sid(unsigned int sid); +void tsdemux_audio_reset(void); +void tsdemux_sub_reset(void); +void tsdemux_set_skipbyte(int skipbyte); +void tsdemux_set_demux(int dev); +#endif /* TSDEMUX_H */ |