ODROID-COMMON: slipstream Amlogic drivers

Change-Id: I96b0ed0cce029e85a12c1a04327f106b68ba7f43
This commit is contained in:
Mauro (mdrjr) Ribeiro
2018-11-26 15:38:14 +09:00
parent f64e727448
commit 5ef121eb61
156 changed files with 125347 additions and 7 deletions

View File

@@ -1502,6 +1502,22 @@ CONFIG_AMLOGIC_DEFENDKEY=y
#
# Meson SPI NOR Flash Support
#
CONFIG_AMLOGIC_MEDIA_VDEC_MPEG12=m
CONFIG_AMLOGIC_MEDIA_VDEC_MPEG4=m
CONFIG_AMLOGIC_MEDIA_VDEC_MPEG4_MULTI=m
CONFIG_AMLOGIC_MEDIA_VDEC_VC1=m
CONFIG_AMLOGIC_MEDIA_VDEC_H264=m
CONFIG_AMLOGIC_MEDIA_VDEC_H264_MULTI=m
CONFIG_AMLOGIC_MEDIA_VDEC_H264_MVC=m
CONFIG_AMLOGIC_MEDIA_VDEC_H265=m
CONFIG_AMLOGIC_MEDIA_VDEC_VP9=m
CONFIG_AMLOGIC_MEDIA_VDEC_MJPEG=m
CONFIG_AMLOGIC_MEDIA_VDEC_MJPEG_MULTI=m
CONFIG_AMLOGIC_MEDIA_VDEC_REAL=m
CONFIG_AMLOGIC_MEDIA_VDEC_AVS=m
CONFIG_AMLOGIC_MEDIA_VDEC_AVS2=m
CONFIG_AMLOGIC_MEDIA_VENC_H264=m
CONFIG_AMLOGIC_MEDIA_VENC_H265=m
CONFIG_ARM_AMBA=y
#
@@ -4447,6 +4463,29 @@ CONFIG_NVMEM=y
# CONFIG_FPGA is not set
# CONFIG_TEE is not set
#
# ARM GPU Configuration
#
CONFIG_MALI_MIDGARD=y
# CONFIG_MALI_GATOR_SUPPORT is not set
# CONFIG_MALI_MIDGARD_DVFS is not set
# CONFIG_MALI_MIDGARD_ENABLE_TRACE is not set
# CONFIG_MALI_DEVFREQ is not set
# CONFIG_MALI_DMA_FENCE is not set
CONFIG_MALI_PLATFORM_NAME="devicetree"
CONFIG_MALI_EXPERT=y
# CONFIG_MALI_CORESTACK is not set
# CONFIG_MALI_PRFCNT_SET_SECONDARY is not set
# CONFIG_MALI_DEBUG is not set
# CONFIG_MALI_FENCE_DEBUG is not set
# CONFIG_MALI_NO_MALI is not set
# CONFIG_MALI_TRACE_TIMELINE is not set
# CONFIG_MALI_SYSTEM_TRACE is not set
# CONFIG_MALI_JOB_DUMP is not set
# CONFIG_MALI_2MB_ALLOC is not set
# CONFIG_MALI_PWRSOFT_765 is not set
CONFIG_MALI_KUTF=m
#
# Firmware Drivers
#
@@ -4721,7 +4760,7 @@ CONFIG_DEBUG_INFO=y
# CONFIG_GDB_SCRIPTS is not set
CONFIG_ENABLE_WARN_DEPRECATED=y
CONFIG_ENABLE_MUST_CHECK=y
CONFIG_FRAME_WARN=2048
CONFIG_FRAME_WARN=0
# CONFIG_STRIP_ASM_SYMS is not set
# CONFIG_READABLE_ASM is not set
# CONFIG_UNUSED_SYMBOLS is not set
@@ -4836,6 +4875,7 @@ CONFIG_HAVE_C_RECORDMCOUNT=y
CONFIG_TRACE_CLOCK=y
CONFIG_RING_BUFFER=y
CONFIG_EVENT_TRACING=y
CONFIG_GPU_TRACEPOINTS=y
CONFIG_CONTEXT_SWITCH_TRACER=y
CONFIG_TRACING=y
CONFIG_GENERIC_TRACER=y

View File

@@ -135,5 +135,7 @@ source "drivers/amlogic/defendkey/Kconfig"
source "drivers/amlogic/battery/Kconfig"
source "drivers/amlogic/spi-nor/Kconfig"
source "drivers/amlogic/media_modules/Kconfig"
endmenu
endif

View File

@@ -7,14 +7,8 @@
## Do not change.
##########################################
ifndef CONFIG_KASAN
KBUILD_CFLAGS += -Wlarger-than=28792
KBUILD_CFLAGS += -Wstack-usage=1856 -Wno-bool-operation -Wno-maybe-uninitialized
else
ifeq ($(call cc-ifversion, -lt, 0500, y), y)
$(error -----GCC VERSION TOO SMALL FOR KASAN -----)
endif
endif
# These 2 marked sentence is just for generate warning messages
#KBUILD_CFLAGS += -Wno-error=larger-than=28792
@@ -133,3 +127,5 @@ obj-$(CONFIG_AMLOGIC_DEBUG) += debug/
obj-$(CONFIG_AMLOGIC_DEFENDKEY) += defendkey/
obj-$(CONFIG_MTD_SPI_NOR) += spi-nor/
obj-y += media_modules/

View File

@@ -0,0 +1,101 @@
config AMLOGIC_MEDIA_VDEC_MPEG12
tristate "Amlogic Video decoder"
default m
help
Enables amlogic video decoder
config AMLOGIC_MEDIA_VDEC_MPEG4
tristate "Amlogic Video decoder"
default m
help
Enables amlogic video decoder
config AMLOGIC_MEDIA_VDEC_MPEG4_MULTI
tristate "Amlogic Video decoder"
default m
help
Enables amlogic video decoder
config AMLOGIC_MEDIA_VDEC_VC1
tristate "Amlogic Video decoder"
default m
help
Enables amlogic video decoder
config AMLOGIC_MEDIA_VDEC_H264
tristate "Amlogic Video decoder"
default m
help
Enables amlogic video decoder
config AMLOGIC_MEDIA_VDEC_H264_MULTI
tristate "Amlogic Video decoder"
default m
help
Enables amlogic video decoder
config AMLOGIC_MEDIA_VDEC_H264_MVC
tristate "Amlogic Video decoder"
default m
help
Enables amlogic video decoder
config AMLOGIC_MEDIA_VDEC_H265
tristate "Amlogic Video decoder"
default m
help
Enables amlogic video decoder
config AMLOGIC_MEDIA_VDEC_VP9
tristate "Amlogic Video decoder"
default m
help
Enables amlogic video decoder
config AMLOGIC_MEDIA_VDEC_MJPEG
tristate "Amlogic Video decoder"
default m
help
Enables amlogic video decoder
config AMLOGIC_MEDIA_VDEC_MJPEG_MULTI
tristate "Amlogic Video decoder"
default m
help
Enables amlogic video decoder
config AMLOGIC_MEDIA_VDEC_REAL
tristate "Amlogic Video decoder"
default m
help
Enables amlogic video decoder
config AMLOGIC_MEDIA_VDEC_AVS
tristate "Amlogic Video decoder"
default m
help
Enables amlogic video decoder
config AMLOGIC_MEDIA_VDEC_AVS2
tristate "Amlogic Video decoder"
default m
help
Enables amlogic video decoder
config AMLOGIC_MEDIA_VENC_H264
tristate "Amlogic Video decoder"
default m
help
Enables amlogic video decoder
config AMLOGIC_MEDIA_VENC_H265
tristate "Amlogic Video decoder"
default m
help
Enables amlogic video decoder
config AMLOGIC_MEDIA_ENHANCEMENT_DOLBYVISION
tristate "Amlogic Video decoder"
default m
help
Enables amlogic video decoder

View File

@@ -0,0 +1,4 @@
obj-y += common/
obj-y += frame_provider/
obj-y += frame_sink/
obj-y += stream_input/

View File

@@ -0,0 +1,2 @@
obj-y += media_clock/
obj-y += firmware/

View File

@@ -0,0 +1,183 @@
/*
* 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/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>
#include <linux/amlogic/media/utils/vdec_reg.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"},
{MESON_CPU_MAJOR_ID_TXLX, "txlx"},
{MESON_CPU_MAJOR_ID_G12A, "g12a"},
{MESON_CPU_MAJOR_ID_G12B, "g12b"},
{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_AVS2,
* 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_AVS2, "avs2"},
{VFORMAT_YUV, "yuv"},
{0, NULL},
};
const char *get_video_format_name(enum vformat_e type)
{
return get_type_name(vformat_type_name,
sizeof(vformat_type_name) / sizeof(struct type_name), type);
}
EXPORT_SYMBOL(get_video_format_name);
static struct chip_vdec_info_s current_chip_info;
struct chip_vdec_info_s *get_current_vdec_chip(void)
{
return &current_chip_info;
}
EXPORT_SYMBOL(get_current_vdec_chip);
bool check_efuse_chip(int vformat)
{
unsigned int status, i = 0;
int type[] = {15, 14, 11, 2}; /* avs2, vp9, h265, h264 */
status = (READ_EFUSE_REG(EFUSE_LIC2) >> 8 & 0xf);
if (!status)
return false;
do {
if ((status & 1) && (type[i] == vformat))
return true;
i++;
} while (status >>= 1);
return false;
}
EXPORT_SYMBOL(check_efuse_chip);

View File

@@ -0,0 +1,40 @@
/*
* 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 "../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);
bool check_efuse_chip(int vformat);
#endif

View File

@@ -0,0 +1,3 @@
obj-m += firmware.o
firmware-objs += firmware_drv.o
firmware-objs += firmware_type.o

View File

@@ -0,0 +1,32 @@
/*
* drivers/amlogic/media/common/firmware/firmware_cfg.h
*
* Copyright (C) 2016 Amlogic, Inc. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
*/
/*all firmwares in one bin.*/
{VIDEO_MISC, VIDEO_PACKAGE, "video_ucode.bin"},
/* Note: if the addition of new package has the same name */
/* as the firmware in the video_ucode.bin, the firmware */
/* in the video_ucode.bin will be ignored yet, because the */
/* video_ucode.bin will always be processed in the end */
{VIDEO_ENCODE, VIDEO_PACKAGE, "h264_enc.bin"},
/*firmware for a special format, to replace the format in the package.*/
{VIDEO_DECODE, VIDEO_FW_FILE, "h265.bin"},
{VIDEO_DECODE, VIDEO_FW_FILE, "h264.bin"},
{VIDEO_DECODE, VIDEO_FW_FILE, "h264_multi.bin"},

View File

@@ -0,0 +1,927 @@
/*
* drivers/amlogic/media/common/firmware/firmware.c
*
* Copyright (C) 2016 Amlogic, Inc. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/vmalloc.h>
#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/amlogic/media/utils/vformat.h>
#include <linux/amlogic/cpu_version.h>
#include "../../stream_input/amports/amports_priv.h"
#include "../../frame_provider/decoder/utils/vdec.h"
#include "firmware_priv.h"
#include "../chips/chips.h"
#include <linux/string.h>
#include <linux/amlogic/media/utils/log.h>
#include <linux/firmware.h>
#include <linux/amlogic/tee.h>
#include <linux/amlogic/major.h>
#include <linux/cdev.h>
#include <linux/crc32.h>
/* major.minor.revision */
#define PACK_VERS "v0.0.1"
#define CLASS_NAME "firmware_codec"
#define DEV_NAME "firmware_vdec"
#define DIR "video"
#define FRIMWARE_SIZE (64 * 1024) /*64k*/
#define BUFF_SIZE (1024 * 1024 * 2)
#define FW_LOAD_FORCE (0x1)
#define FW_LOAD_TRY (0X2)
/*the first 256 bytes are signature data*/
#define SEC_OFFSET (256)
#define PACK ('P' << 24 | 'A' << 16 | 'C' << 8 | 'K')
#define CODE ('C' << 24 | 'O' << 16 | 'D' << 8 | 'E')
static DEFINE_MUTEX(mutex);
static struct ucode_file_info_s ucode_info[] = {
#include "firmware_cfg.h"
};
static const struct file_operations fw_fops = {
.owner = THIS_MODULE
};
struct fw_mgr_s *g_mgr;
struct fw_dev_s *g_dev;
static u32 debug;
static u32 detail;
int get_firmware_data(unsigned int format, char *buf)
{
int data_len, ret = -1;
struct fw_mgr_s *mgr = g_mgr;
struct fw_info_s *info;
if (tee_enabled()) {
pr_info ("tee load firmware fomat = %d\n",(u32)format);
ret = tee_load_video_fw((u32)format, 0);
if (ret == 0)
ret = 1;
else
ret = -1;
return ret;
}
mutex_lock(&mutex);
if (list_empty(&mgr->fw_head)) {
pr_info("the info list is empty.\n");
goto out;
}
list_for_each_entry(info, &mgr->fw_head, node) {
if (format != info->format)
continue;
data_len = info->data->head.data_size;
memcpy(buf, info->data->data, data_len);
ret = data_len;
break;
}
out:
mutex_unlock(&mutex);
return ret;
}
EXPORT_SYMBOL(get_firmware_data);
int get_data_from_name(const char *name, char *buf)
{
int data_len, ret = -1;
struct fw_mgr_s *mgr = g_mgr;
struct fw_info_s *info;
char *fw_name = __getname();
if (IS_ERR_OR_NULL(fw_name))
return -ENOMEM;
strcat(fw_name, name);
strcat(fw_name, ".bin");
mutex_lock(&mutex);
if (list_empty(&mgr->fw_head)) {
pr_info("the info list is empty.\n");
goto out;
}
list_for_each_entry(info, &mgr->fw_head, node) {
if (strcmp(fw_name, info->name))
continue;
data_len = info->data->head.data_size;
memcpy(buf, info->data->data, data_len);
ret = data_len;
break;
}
out:
mutex_unlock(&mutex);
__putname(fw_name);
return ret;
}
EXPORT_SYMBOL(get_data_from_name);
static int fw_probe(char *buf)
{
int magic = 0;
memcpy(&magic, buf, sizeof(int));
return magic;
}
static int request_firmware_from_sys(const char *file_name,
char *buf, int size)
{
int ret = -1;
const struct firmware *fw;
int magic, offset = 0;
pr_info("Try to load %s ...\n", file_name);
ret = request_firmware(&fw, file_name, g_dev->dev);
if (ret < 0) {
pr_info("Error : %d can't load the %s.\n", ret, file_name);
goto err;
}
if (fw->size > size) {
pr_info("Not enough memory size for ucode.\n");
ret = -ENOMEM;
goto release;
}
magic = fw_probe((char *)fw->data);
if (magic != PACK && magic != CODE) {
if (fw->size < SEC_OFFSET) {
pr_info("This is an invalid firmware file.\n");
goto release;
}
magic = fw_probe((char *)fw->data + SEC_OFFSET);
if (magic != PACK) {
pr_info("The firmware file is not packet.\n");
goto release;
}
offset = SEC_OFFSET;
}
memcpy(buf, (char *)fw->data + offset, fw->size - offset);
pr_info("load firmware size : %zd, Name : %s.\n",
fw->size, file_name);
ret = fw->size;
release:
release_firmware(fw);
err:
return ret;
}
int request_decoder_firmware_on_sys(enum vformat_e format,
const char *file_name, char *buf, int size)
{
int ret;
ret = get_data_from_name(file_name, buf);
if (ret < 0)
pr_info("Get firmware fail.\n");
if (ret > size) {
pr_info("Not enough memory.\n");
return -ENOMEM;
}
return ret;
}
int get_decoder_firmware_data(enum vformat_e format,
const char *file_name, char *buf, int size)
{
int ret;
ret = request_decoder_firmware_on_sys(format, file_name, buf, size);
if (ret < 0)
pr_info("get_decoder_firmware_data %s for format %d failed!\n",
file_name, format);
return ret;
}
EXPORT_SYMBOL(get_decoder_firmware_data);
static unsigned long fw_mgr_lock(struct fw_mgr_s *mgr)
{
unsigned long flags;
spin_lock_irqsave(&mgr->lock, flags);
return flags;
}
static void fw_mgr_unlock(struct fw_mgr_s *mgr, unsigned long flags)
{
spin_unlock_irqrestore(&mgr->lock, flags);
}
static void fw_add_info(struct fw_info_s *info)
{
unsigned long flags;
struct fw_mgr_s *mgr = g_mgr;
flags = fw_mgr_lock(mgr);
list_add(&info->node, &mgr->fw_head);
fw_mgr_unlock(mgr, flags);
}
static void fw_del_info(struct fw_info_s *info)
{
unsigned long flags;
struct fw_mgr_s *mgr = g_mgr;
flags = fw_mgr_lock(mgr);
list_del(&info->node);
kfree(info);
fw_mgr_unlock(mgr, flags);
}
static void fw_info_walk(void)
{
struct fw_mgr_s *mgr = g_mgr;
struct fw_info_s *info;
if (list_empty(&mgr->fw_head)) {
pr_info("the info list is empty.\n");
return;
}
list_for_each_entry(info, &mgr->fw_head, node) {
if (IS_ERR_OR_NULL(info->data))
continue;
pr_info("name : %s.\n", info->name);
pr_info("ver : %s.\n",
info->data->head.version);
pr_info("crc : 0x%x.\n",
info->data->head.checksum);
pr_info("size : %d.\n",
info->data->head.data_size);
pr_info("maker: %s.\n",
info->data->head.maker);
pr_info("from : %s.\n", info->src_from);
pr_info("date : %s.\n\n",
info->data->head.date);
}
}
static void fw_files_info_walk(void)
{
struct fw_mgr_s *mgr = g_mgr;
struct fw_files_s *files;
if (list_empty(&mgr->files_head)) {
pr_info("the file list is empty.\n");
return;
}
list_for_each_entry(files, &mgr->files_head, node) {
pr_info("type : %s.\n", !files->fw_type ?
"VIDEO_DECODE" : files->fw_type == 1 ?
"VIDEO_ENCODE" : "VIDEO_MISC");
pr_info("from : %s.\n", !files->file_type ?
"VIDEO_PACKAGE" : "VIDEO_FW_FILE");
pr_info("path : %s.\n", files->path);
pr_info("name : %s.\n\n", files->name);
}
}
static ssize_t info_show(struct class *class,
struct class_attribute *attr, char *buf)
{
char *pbuf = buf;
struct fw_mgr_s *mgr = g_mgr;
struct fw_info_s *info;
unsigned int secs = 0;
struct tm tm;
mutex_lock(&mutex);
if (list_empty(&mgr->fw_head)) {
pbuf += sprintf(pbuf, "No firmware.\n");
goto out;
}
list_for_each_entry(info, &mgr->fw_head, node) {
if (IS_ERR_OR_NULL(info->data))
continue;
if (detail) {
pr_info("%-5s: %s\n", "name", info->name);
pr_info("%-5s: %s\n", "ver",
info->data->head.version);
pr_info("%-5s: 0x%x\n", "sum",
info->data->head.checksum);
pr_info("%-5s: %d\n", "size",
info->data->head.data_size);
pr_info("%-5s: %s\n", "maker",
info->data->head.maker);
pr_info("%-5s: %s\n", "from",
info->src_from);
pr_info("%-5s: %s\n\n", "date",
info->data->head.date);
continue;
}
secs = info->data->head.time
- sys_tz.tz_minuteswest * 60;
time_to_tm(secs, 0, &tm);
pr_info("%s %-16s, %02d:%02d:%02d %d/%d/%ld, %s %-8s, %s %s\n",
"fmt:", info->data->head.format,
tm.tm_hour, tm.tm_min, tm.tm_sec,
tm.tm_mon + 1, tm.tm_mday, tm.tm_year + 1900,
"id:", info->data->head.commit,
"mk:", info->data->head.maker);
}
out:
mutex_unlock(&mutex);
return pbuf - buf;
}
static ssize_t info_store(struct class *cls,
struct class_attribute *attr, const char *buf, size_t count)
{
if (kstrtoint(buf, 0, &detail) < 0)
return -EINVAL;
return count;
}
static int fw_info_fill(void)
{
int ret = 0, i, len;
struct fw_mgr_s *mgr = g_mgr;
struct fw_files_s *files;
int info_size = ARRAY_SIZE(ucode_info);
char *path = __getname();
const char *name;
if (IS_ERR_OR_NULL(path))
return -ENOMEM;
for (i = 0; i < info_size; i++) {
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;
files = kzalloc(sizeof(struct fw_files_s), GFP_KERNEL);
if (IS_ERR_OR_NULL(files)) {
__putname(path);
return -ENOMEM;
}
files->file_type = ucode_info[i].file_type;
files->fw_type = ucode_info[i].fw_type;
strncpy(files->path, path, sizeof(files->path));
strncpy(files->name, name, sizeof(files->name));
list_add(&files->node, &mgr->files_head);
}
__putname(path);
if (debug)
fw_files_info_walk();
return ret;
}
static int fw_data_check_sum(struct firmware_s *fw)
{
unsigned int crc;
crc = crc32_le(~0U, fw->data, fw->head.data_size);
/*pr_info("firmware crc result : 0x%x\n", crc ^ ~0U);*/
return fw->head.checksum != (crc ^ ~0U) ? 0 : 1;
}
static int fw_data_filter(struct firmware_s *fw,
struct fw_info_s *fw_info)
{
struct fw_mgr_s *mgr = g_mgr;
struct fw_info_s *info, *tmp;
int cpu = fw_get_cpu(fw->head.cpu);
if (mgr->cur_cpu < cpu) {
pr_info("the fw %s is not match.\n", fw_info->name);
kfree(fw_info);
kfree(fw);
return -1;
}
/* the encode fw need to ignoring filtering rules. */
if (fw_info->format == FIRMWARE_MAX)
return 0;
list_for_each_entry_safe(info, tmp, &mgr->fw_head, node) {
if (info->format != fw_info->format)
continue;
if (IS_ERR_OR_NULL(info->data)) {
fw_del_info(info);
return 0;
}
/* high priority of VIDEO_FW_FILE */
if (info->file_type == VIDEO_FW_FILE) {
pr_info("the %s need to priority proc.\n",info->name);
kfree(fw_info);
kfree(fw);
return 1;
}
/* the cpu ver is lower and needs to be filtered */
if (cpu < fw_get_cpu(info->data->head.cpu)) {
pr_info("the fw %s is not match.\n",
fw_info->name);
kfree(fw_info);
kfree(fw);
return 1;
}
/* removes not match fw from info list */
pr_info("the fw %s is not match.\n", info->name);
kfree(info->data);
fw_del_info(info);
}
return 0;
}
static int fw_check_pack_version(char *buf)
{
struct package_s *pack = NULL;
int major, minor, rev, ver = 0;
pack = (struct package_s *) buf;
sscanf(PACK_VERS, "v%x.%x.%x", &major, &minor, &rev);
ver = (major << 24 | minor << 16 | rev);
pr_info("the package has %d fws totally.\n", pack->head.total);
major = pack->head.version >> 24;
minor = (pack->head.version >> 16) & 0xf;
rev = pack->head.version & 0xff;
if (ver < pack->head.version) {
pr_info("the pack ver v%d.%d.%d too higher to unsupport.\n",
major, minor, rev);
return -1;
}
if (ver != pack->head.version) {
pr_info("the fw pack ver v%d.%d.%d is too lower.\n", major, minor, rev);
pr_info("it may work abnormally so need to be update in time.\n");
}
return 0;
}
static int fw_package_parse(struct fw_files_s *files,
char *buf, int size)
{
int ret = 0;
struct package_info_s *pack_info;
struct fw_info_s *info;
struct firmware_s *data;
char *pack_data;
int info_len, len;
int try_cnt = 100;
char *path = __getname();
if (IS_ERR_OR_NULL(path))
return -ENOMEM;
pack_data = ((struct package_s *)buf)->data;
pack_info = (struct package_info_s *)pack_data;
info_len = sizeof(struct package_info_s);
do {
if (!pack_info->head.length)
break;
len = snprintf(path, PATH_MAX, "%s/%s", DIR,
pack_info->head.name);
if (len >= PATH_MAX)
continue;
info = kzalloc(sizeof(struct fw_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;
}
info->file_type = files->file_type;
strncpy(info->src_from, files->name,
sizeof(info->src_from));
strncpy(info->name, pack_info->head.name,
sizeof(info->name));
info->format = get_fw_format(pack_info->head.format);
len = pack_info->head.length;
memcpy(data, pack_info->data, len);
pack_data += (pack_info->head.length + info_len);
pack_info = (struct package_info_s *)pack_data;
if (!fw_data_check_sum(data)) {
pr_info("check sum fail !\n");
kfree(data);
kfree(info);
goto out;
}
if (fw_data_filter(data, info))
continue;
if (debug)
pr_info("adds %s to the fw list.\n", info->name);
info->data = data;
fw_add_info(info);
} while (try_cnt--);
out:
__putname(path);
return ret;
}
static int fw_code_parse(struct fw_files_s *files,
char *buf, int size)
{
struct fw_info_s *info;
info = kzalloc(sizeof(struct fw_info_s), GFP_KERNEL);
if (IS_ERR_OR_NULL(info))
return -ENOMEM;
info->data = kzalloc(FRIMWARE_SIZE, GFP_KERNEL);
if (IS_ERR_OR_NULL(info->data))
return -ENOMEM;
info->file_type = files->file_type;
strncpy(info->src_from, files->name,
sizeof(info->src_from));
memcpy(info->data, buf, size);
if (!fw_data_check_sum(info->data)) {
pr_info("check sum fail !\n");
kfree(info->data);
return -1;
}
if (debug)
pr_info("adds %s to the fw list.\n", info->name);
fw_add_info(info);
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 fw_data_binding(void)
{
int ret = 0, magic = 0;
struct fw_mgr_s *mgr = g_mgr;
struct fw_files_s *files, *tmp;
char *buf = vmalloc(BUFF_SIZE);
int size;
if (list_empty(&mgr->files_head)) {
pr_info("the file list is empty.\n");
return 0;
}
if (IS_ERR_OR_NULL(buf))
return -ENOMEM;
memset(buf, 0, BUFF_SIZE);
list_for_each_entry_safe(files, tmp, &mgr->files_head, node) {
size = get_firmware_from_sys(files->path, buf, BUFF_SIZE);
magic = fw_probe(buf);
if (files->file_type == VIDEO_PACKAGE && magic == PACK) {
pr_info("start to parse fw package.\n");
if (!fw_check_pack_version(buf))
ret = fw_package_parse(files, buf, size);
} else if (files->file_type == VIDEO_FW_FILE && magic == CODE) {
pr_info("start to parse fw code.\n");
ret = fw_code_parse(files, buf, size);
} else {
list_del(&files->node);
kfree(files);
pr_info("invaild file type.\n");
}
memset(buf, 0, BUFF_SIZE);
}
if (debug)
fw_info_walk();
vfree(buf);
return ret;
}
static int fw_pre_load(void)
{
if (fw_info_fill() < 0) {
pr_info("Get path fail.\n");
return -1;
}
if (fw_data_binding() < 0) {
pr_info("Set data fail.\n");
return -1;
}
return 0;
}
static int fw_mgr_init(void)
{
g_mgr = kzalloc(sizeof(struct fw_mgr_s), GFP_KERNEL);
if (IS_ERR_OR_NULL(g_mgr))
return -ENOMEM;
g_mgr->cur_cpu = get_cpu_type();
INIT_LIST_HEAD(&g_mgr->files_head);
INIT_LIST_HEAD(&g_mgr->fw_head);
spin_lock_init(&g_mgr->lock);
return 0;
}
static void fw_ctx_clean(void)
{
struct fw_mgr_s *mgr = g_mgr;
struct fw_files_s *files;
struct fw_info_s *info;
unsigned long flags;
flags = fw_mgr_lock(mgr);
while (!list_empty(&mgr->files_head)) {
files = list_entry(mgr->files_head.next,
struct fw_files_s, node);
list_del(&files->node);
kfree(files);
}
while (!list_empty(&mgr->fw_head)) {
info = list_entry(mgr->fw_head.next,
struct fw_info_s, node);
list_del(&info->node);
kfree(info->data);
kfree(info);
}
fw_mgr_unlock(mgr, flags);
}
int video_fw_reload(int mode)
{
int ret = 0;
struct fw_mgr_s *mgr = g_mgr;
if (tee_enabled())
return 0;
mutex_lock(&mutex);
if (mode & FW_LOAD_FORCE) {
fw_ctx_clean();
ret = fw_pre_load();
if (ret < 0)
pr_err("The fw reload fail.\n");
} else if (mode & FW_LOAD_TRY) {
if (!list_empty(&mgr->fw_head)) {
pr_info("The fw has been loaded.\n");
goto out;
}
ret = fw_pre_load();
if (ret < 0)
pr_err("The fw try to reload fail.\n");
}
out:
mutex_unlock(&mutex);
return ret;
}
EXPORT_SYMBOL(video_fw_reload);
static ssize_t reload_show(struct class *class,
struct class_attribute *attr, char *buf)
{
char *pbuf = buf;
pbuf += sprintf(pbuf, "The fw reload usage.\n");
pbuf += sprintf(pbuf, "> set 1 means that the fw is forced to update\n");
pbuf += sprintf(pbuf, "> set 2 means that the fw is try to reload\n");
return pbuf - buf;
}
static ssize_t reload_store(struct class *class,
struct class_attribute *attr,
const char *buf, size_t size)
{
int ret = -1;
unsigned int val;
ret = kstrtoint(buf, 0, &val);
if (ret != 0)
return -EINVAL;
ret = video_fw_reload(val);
if (ret < 0)
pr_err("fw reload fail.\n");
return size;
}
static ssize_t debug_show(struct class *cls,
struct class_attribute *attr, char *buf)
{
return sprintf(buf, "%x\n", debug);
}
static ssize_t debug_store(struct class *cls,
struct class_attribute *attr, const char *buf, size_t count)
{
if (kstrtoint(buf, 0, &debug) < 0)
return -EINVAL;
return count;
}
static struct class_attribute fw_class_attrs[] = {
__ATTR(info, 0664, info_show, info_store),
__ATTR(reload, 0664, reload_show, reload_store),
__ATTR(debug, 0664, debug_show, debug_store),
__ATTR_NULL
};
static struct class fw_class = {
.name = CLASS_NAME,
.class_attrs = fw_class_attrs,
};
static int fw_driver_init(void)
{
int ret = -1;
g_dev = kzalloc(sizeof(struct fw_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, &fw_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(&fw_class);
if (ret < 0) {
pr_info("Failed in creating class.\n");
goto err;
}
g_dev->dev = device_create(&fw_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 fw_driver_exit(void)
{
cdev_del(&g_dev->cdev);
device_destroy(&fw_class, g_dev->dev_no);
class_unregister(&fw_class);
unregister_chrdev_region(g_dev->dev_no, 1);
kfree(g_dev);
kfree(g_mgr);
}
static int __init fw_module_init(void)
{
int ret = -1;
ret = fw_driver_init();
if (ret) {
pr_info("Error %d firmware driver init fail.\n", ret);
goto err;
}
ret = fw_mgr_init();
if (ret) {
pr_info("Error %d firmware mgr init fail.\n", ret);
goto err;
}
ret = fw_pre_load();
if (ret) {
pr_info("Error %d firmware pre load fail.\n", ret);
goto err;
}
err:
return ret;
}
static void __exit fw_module_exit(void)
{
fw_ctx_clean();
fw_driver_exit();
pr_info("Firmware driver cleaned up.\n");
}
module_init(fw_module_init);
module_exit(fw_module_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Nanxin Qin <nanxin.qin@amlogic.com>");

View File

@@ -0,0 +1,117 @@
/*
* 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_PRIV_HEAD_
#define __VIDEO_FIRMWARE_PRIV_HEAD_
#include <linux/types.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/cdev.h>
#include "firmware_type.h"
struct fw_mgr_s {
struct list_head fw_head;
struct list_head files_head;
spinlock_t lock;
int cur_cpu;
};
struct fw_files_s {
struct list_head node;
int fw_type;
int file_type;
char name[32];
char path[64];
};
struct ucode_file_info_s {
int fw_type;
int file_type;
const char *name;
};
struct fw_info_s {
struct list_head node;
char name[32];
char src_from[32];
int file_type;
unsigned int format;
struct firmware_s *data;
};
struct fw_head_s {
int magic;
int checksum;
char name[32];
char cpu[16];
char format[32];
char version[32];
char maker[32];
char date[32];
char commit[16];
int data_size;
unsigned int time;
char reserved[128];
};
struct firmware_s {
union {
struct fw_head_s head;
char buf[512];
};
char data[0];
};
struct package_head_s {
int magic;
int size;
int checksum;
int total;
int version;
char reserved[128];
};
struct package_s {
union {
struct package_head_s head;
char buf[256];
};
char data[0];
};
struct info_head_s {
char name[32];
char format[32];
char cpu[32];
int length;
};
struct package_info_s {
union {
struct info_head_s head;
char buf[256];
};
char data[0];
};
struct fw_dev_s {
struct cdev cdev;
struct device *dev;
dev_t dev_no;
};
#endif

View File

@@ -0,0 +1,92 @@
#include "firmware_type.h"
#include <linux/amlogic/cpu_version.h>
static const struct format_name_s format_name[] = {
{VIDEO_DEC_MPEG12, "mpeg12"},
{VIDEO_DEC_MPEG12_MULTI, "mpeg12_multi"},
{VIDEO_DEC_MPEG4_3, "divx311"},
{VIDEO_DEC_MPEG4_4, "divx4x"},
{VIDEO_DEC_MPEG4_4_MULTI, "divx4x_multi"},
{VIDEO_DEC_MPEG4_5, "xvid"},
{VIDEO_DEC_MPEG4_5_MULTI, "xvid_multi"},
{VIDEO_DEC_H263, "h263"},
{VIDEO_DEC_H263_MULTI, "h263_multi"},
{VIDEO_DEC_MJPEG, "mjpeg"},
{VIDEO_DEC_MJPEG_MULTI, "mjpeg_multi"},
{VIDEO_DEC_REAL_V8, "real_v8"},
{VIDEO_DEC_REAL_V9, "real_v9"},
{VIDEO_DEC_VC1, "vc1"},
{VIDEO_DEC_VC1_G12A, "vc1_g12a"},
{VIDEO_DEC_AVS, "avs"},
{VIDEO_DEC_AVS_GXM, "avs_gxm"},
{VIDEO_DEC_AVS_NOCABAC, "avs_no_cabac"},
{VIDEO_DEC_H264, "h264"},
{VIDEO_DEC_H264_4k2K, "h264_4k2k"},
{VIDEO_DEC_H264_4k2K_SINGLE, "h264_4k2k_single"},
{VIDEO_DEC_H264_MVC, "h264_mvc"},
{VIDEO_DEC_H264_MVC_GXM, "h264_mvc_gxm"},
{VIDEO_DEC_H264_MULTI, "h264_multi"},
{VIDEO_DEC_H264_MULTI_MMU, "h264_multi_mmu"},
{VIDEO_DEC_H264_MULTI_GXM, "h264_multi_gxm"},
{VIDEO_DEC_HEVC, "hevc"},
{VIDEO_DEC_HEVC_MMU, "hevc_mmu"},
{VIDEO_DEC_HEVC_G12A, "hevc_g12a"},
{VIDEO_DEC_VP9, "vp9"},
{VIDEO_DEC_VP9_MMU, "vp9_mmu"},
{VIDEO_DEC_VP9_G12A, "vp9_g12a"},
{VIDEO_DEC_AVS2, "avs2"},
{VIDEO_DEC_AVS2_MMU, "avs2_mmu"},
{VIDEO_ENC_H264, "h264_enc"},
{VIDEO_ENC_JPEG, "jpeg_enc"},
{FIRMWARE_MAX, "unknown"},
};
static const struct cpu_type_s cpu_type[] = {
{MESON_CPU_MAJOR_ID_GXL, "gxl"},
{MESON_CPU_MAJOR_ID_GXM, "gxm"},
{MESON_CPU_MAJOR_ID_G12A, "g12a"},
{MESON_CPU_MAJOR_ID_G12B, "g12b"},
};
const char *get_fw_format_name(unsigned int format)
{
const char *name = "unknown";
int i, size = ARRAY_SIZE(format_name);
for (i = 0; i < size; i++) {
if (format == format_name[i].format)
name = format_name[i].name;
}
return name;
}
EXPORT_SYMBOL(get_fw_format_name);
unsigned int get_fw_format(const char *name)
{
unsigned int format = FIRMWARE_MAX;
int i, size = ARRAY_SIZE(format_name);
for (i = 0; i < size; i++) {
if (!strcmp(name, format_name[i].name))
format = format_name[i].format;
}
return format;
}
EXPORT_SYMBOL(get_fw_format);
int fw_get_cpu(const char *name)
{
int type = 0;
int i, size = ARRAY_SIZE(cpu_type);
for (i = 0; i < size; i++) {
if (!strcmp(name, cpu_type[i].name))
type = cpu_type[i].type;
}
return type;
}
EXPORT_SYMBOL(fw_get_cpu);

View File

@@ -0,0 +1,76 @@
#ifndef __VIDEO_FIRMWARE_FORMAT_
#define __VIDEO_FIRMWARE_FORMAT_
#include <linux/slab.h>
/* example: #define VIDEO_DEC_AV1 TAG('A', 'V', '1', '-')*/
#define TAG(a, b, c, d)\
((a << 24) | (b << 16) | (c << 8) | d)
/* fws define */
#define VIDEO_DEC_MPEG12 (0)
#define VIDEO_DEC_MPEG4_3 (1)
#define VIDEO_DEC_MPEG4_4 (2)
#define VIDEO_DEC_MPEG4_5 (3)
#define VIDEO_DEC_H263 (4)
#define VIDEO_DEC_MJPEG (5)
#define VIDEO_DEC_MJPEG_MULTI (6)
#define VIDEO_DEC_REAL_V8 (7)
#define VIDEO_DEC_REAL_V9 (8)
#define VIDEO_DEC_VC1 (9)
#define VIDEO_DEC_AVS (10)
#define VIDEO_DEC_H264 (11)
#define VIDEO_DEC_H264_4k2K (12)
#define VIDEO_DEC_H264_4k2K_SINGLE (13)
#define VIDEO_DEC_H264_MVC (14)
#define VIDEO_DEC_H264_MULTI (15)
#define VIDEO_DEC_HEVC (16)
#define VIDEO_DEC_HEVC_MMU (17)
#define VIDEO_DEC_VP9 (18)
#define VIDEO_DEC_VP9_MMU (19)
#define VIDEO_ENC_H264 (20)
#define VIDEO_ENC_JPEG (21)
#define VIDEO_DEC_H264_MULTI_MMU (23)
#define VIDEO_DEC_HEVC_G12A (24)
#define VIDEO_DEC_VP9_G12A (25)
#define VIDEO_DEC_AVS2 (26)
#define VIDEO_DEC_AVS2_MMU (27)
#define VIDEO_DEC_AVS_GXM (28)
#define VIDEO_DEC_AVS_NOCABAC (29)
#define VIDEO_DEC_H264_MULTI_GXM (30)
#define VIDEO_DEC_H264_MVC_GXM (31)
#define VIDEO_DEC_VC1_G12A (32)
#define VIDEO_DEC_MPEG12_MULTI TAG('M', '1', '2', 'M')
#define VIDEO_DEC_MPEG4_4_MULTI TAG('M', '4', '4', 'M')
#define VIDEO_DEC_MPEG4_5_MULTI TAG('M', '4', '5', 'M')
#define VIDEO_DEC_H263_MULTI TAG('2', '6', '3', 'M')
/* ... */
#define FIRMWARE_MAX (UINT_MAX)
#define VIDEO_PACKAGE (0)
#define VIDEO_FW_FILE (1)
#define VIDEO_DECODE (0)
#define VIDEO_ENCODE (1)
#define VIDEO_MISC (2)
#define OPTEE_VDEC_LEGENCY (0)
#define OPTEE_VDEC (1)
#define OPTEE_VDEC_HEVC (2)
struct format_name_s {
unsigned int format;
const char *name;
};
struct cpu_type_s {
int type;
const char *name;
};
const char *get_firmware_type_name(unsigned int format);
unsigned int get_fw_format(const char *name);
int fw_get_cpu(const char *name);
#endif

View File

@@ -0,0 +1,5 @@
obj-m += media_clock.o
media_clock-objs += ../chips/chips.o
media_clock-objs += clk/clkg12.o
media_clock-objs += clk/clk.o
media_clock-objs += switch/amports_gate.o

View File

@@ -0,0 +1,455 @@
/*
* 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/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])
#define p_vdec_hevc_back() (get_current_vdec_chip()->clk_mgr[VDEC_HEVCB])
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_back_clock_init(void)
{
if (p_vdec_hevc_back() && p_vdec_hevc_back()->clock_init)
return p_vdec_hevc_back()->clock_init();
else
return 0;
}
EXPORT_SYMBOL(hevc_back_clock_init);
int hevc_back_clock_set(int clk)
{
pr_debug("%s-----%d\n", __func__, clk);
if (p_vdec_hevc_back() && p_vdec_hevc_back()->clock_set)
return p_vdec_hevc_back()->clock_set(clk);
else
return -1;
}
EXPORT_SYMBOL(hevc_back_clock_set);
void hevc_back_clock_enable(void)
{
hevc_back_clock_set(1);
}
EXPORT_SYMBOL(hevc_back_clock_enable);
void hevc_back_clock_hi_enable(void)
{
hevc_back_clock_set(2);
}
EXPORT_SYMBOL(hevc_back_clock_hi_enable);
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_back_clock_on(void)
{
IF_HAVE_RUN(p_vdec_hevc_back(), clock_on);
}
EXPORT_SYMBOL(hevc_back_clock_on);
void hevc_back_clock_off(void)
{
IF_HAVE_RUN(p_vdec_hevc_back(), clock_off);
clock_source_wxhxfps_saved[VDEC_HEVCB] = 0;
}
EXPORT_SYMBOL(hevc_back_clock_off);
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
|| format == VFORMAT_AVS2) {
ret_clk = hevc_clock_set(clk);
clock_source_wxhxfps_saved[VDEC_HEVC] = width * height * fps;
if (get_cpu_type() >= MESON_CPU_MAJOR_ID_G12A) {
ret_clk = hevc_back_clock_set(clk);
clock_source_wxhxfps_saved[VDEC_HEVCB] = width * height * fps;
}
} else if (format == VFORMAT_H264_ENC || format == VFORMAT_JPEG_ENC) {
ret_clk = hcodec_clock_set(clk);
clock_source_wxhxfps_saved[VDEC_HCODEC] = width * height * fps;
} else if (format == VFORMAT_H264_4K2K &&
get_cpu_type() == MESON_CPU_MAJOR_ID_M8) {
ret_clk = vdec2_clock_set(clk);
clock_source_wxhxfps_saved[VDEC_2] = width * height * fps;
ret_clk = vdec_clock_set(clk);
clock_source_wxhxfps_saved[VDEC_1] = width * height * fps;
} else {
ret_clk = vdec_clock_set(clk);
clock_source_wxhxfps_saved[VDEC_1] = width * height * fps;
}
return ret_clk;
}
EXPORT_SYMBOL(vdec_source_changed_for_clk_set);
static int register_vdec_clk_mgr_per_cpu(int cputype,
enum vdec_type_e vdec_type, struct chip_vdec_clk_s *t_mgr)
{
struct chip_vdec_clk_s *mgr;
if (cputype != get_cpu_type() || vdec_type >= VDEC_MAX) {
/*
*pr_info("ignore vdec clk mgr for vdec[%d] cpu=%d\n",
*vdec_type, cputype);
*/
return 0; /* ignore don't needed firmare. */
}
mgr = kmalloc(sizeof(struct chip_vdec_clk_s), GFP_KERNEL);
if (!mgr)
return -ENOMEM;
*mgr = *t_mgr;
/*
*pr_info("register vdec clk mgr for vdec[%d]\n", vdec_type);
*/
if (mgr->clock_init) {
if (mgr->clock_init()) {
kfree(mgr);
return -ENOMEM;
}
}
get_current_vdec_chip()->clk_mgr[vdec_type] = mgr;
return 0;
}
int register_vdec_clk_mgr(int cputype[], enum vdec_type_e vdec_type,
struct chip_vdec_clk_s *t_mgr)
{
int i = 0;
while (cputype[i] > 0) {
register_vdec_clk_mgr_per_cpu(cputype[i], vdec_type, t_mgr);
i++;
}
return 0;
}
EXPORT_SYMBOL(register_vdec_clk_mgr);
int unregister_vdec_clk_mgr(enum vdec_type_e vdec_type)
{
kfree(get_current_vdec_chip()->clk_mgr[vdec_type]);
return 0;
}
EXPORT_SYMBOL(unregister_vdec_clk_mgr);
static int register_vdec_clk_setting_per_cpu(int cputype,
struct clk_set_setting *setting, int size)
{
struct clk_set_setting *p_setting;
if (cputype != get_cpu_type()) {
/*
*pr_info("ignore clk_set_setting for cpu=%d\n",
*cputype);
*/
return 0; /* ignore don't needed this setting . */
}
p_setting = kmalloc(size, GFP_KERNEL);
if (!p_setting)
return -ENOMEM;
memcpy(p_setting, setting, size);
pr_info("register clk_set_setting cpu[%d]\n", cputype);
get_current_vdec_chip()->clk_setting_array = p_setting;
return 0;
}
int register_vdec_clk_setting(int cputype[],
struct clk_set_setting *p_seting, int size)
{
int i = 0;
while (cputype[i] > 0) {
register_vdec_clk_setting_per_cpu(cputype[i], p_seting, size);
i++;
}
return 0;
}
EXPORT_SYMBOL(register_vdec_clk_setting);
int unregister_vdec_clk_setting(void)
{
kfree(get_current_vdec_chip()->clk_setting_array);
return 0;
}
EXPORT_SYMBOL(unregister_vdec_clk_setting);

View File

@@ -0,0 +1,172 @@
/*
* 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 hevc_back_clock_init(void);
void hevc_back_clock_on(void);
void hevc_back_clock_off(void);
int hevc_back_clock_set(int clk);
void hevc_back_clock_enable(void);
void hevc_back_clock_hi_enable(void);
int vdec_source_get(enum vdec_type_e core);
int vdec_clk_get(enum vdec_type_e core);
int vdec_source_changed_for_clk_set(int format, int width, int height, int fps);
int get_clk_with_source(int format, int w_x_h_fps);
void vdec_clock_enable(void);
void vdec_clock_hi_enable(void);
void hcodec_clock_enable(void);
void hcodec_clock_hi_enable(void);
void hevc_clock_enable(void);
void hevc_clock_hi_enable(void);
void vdec2_clock_enable(void);
void vdec2_clock_hi_enable(void);
void set_clock_gate(struct gate_switch_node *nodes, int num);
#endif
int register_vdec_clk_mgr(int cputype[],
enum vdec_type_e vdec_type, struct chip_vdec_clk_s *t_mgr);
int unregister_vdec_clk_mgr(enum vdec_type_e vdec_type);
int register_vdec_clk_setting(int cputype[],
struct clk_set_setting *p_seting, int size);
int unregister_vdec_clk_setting(void);
#ifdef INCLUDE_FROM_ARCH_CLK_MGR
static struct chip_vdec_clk_s vdec_clk_mgr __initdata = {
.clock_init = vdec_clock_init,
.clock_set = vdec_clock_set,
.clock_on = vdec_clock_on,
.clock_off = vdec_clock_off,
.clock_get = vdec_clock_get,
};
#ifdef VDEC_HAS_VDEC2
static struct chip_vdec_clk_s vdec2_clk_mgr __initdata = {
.clock_set = vdec2_clock_set,
.clock_on = vdec2_clock_on,
.clock_off = vdec2_clock_off,
.clock_get = vdec_clock_get,
};
#endif
#ifdef VDEC_HAS_HEVC
static struct chip_vdec_clk_s vdec_hevc_clk_mgr __initdata = {
.clock_init = hevc_clock_init,
.clock_set = hevc_clock_set,
.clock_on = hevc_clock_on,
.clock_off = hevc_clock_off,
.clock_get = vdec_clock_get,
};
static struct chip_vdec_clk_s vdec_hevc_back_clk_mgr __initdata = {
.clock_init = hevc_back_clock_init,
.clock_set = hevc_back_clock_set,
.clock_on = hevc_back_clock_on,
.clock_off = hevc_back_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);
if (get_cpu_type() >= MESON_CPU_MAJOR_ID_G12A)
register_vdec_clk_mgr(cpus, VDEC_HEVCB, &vdec_hevc_back_clk_mgr);
#endif
#ifdef VDEC_HAS_VDEC_HCODEC
register_vdec_clk_mgr(cpus, VDEC_HCODEC, &vdec_hcodec_clk_mgr);
#endif
#ifdef VDEC_HAS_CLK_SETTINGS
register_vdec_clk_setting(cpus,
clks_for_formats, sizeof(clks_for_formats));
#endif
return 0;
}
static void __exit vdec_clk_exit(void)
{
unregister_vdec_clk_mgr(VDEC_1);
#ifdef VDEC_HAS_VDEC2
unregister_vdec_clk_mgr(VDEC_2);
#endif
#ifdef VDEC_HAS_HEVC
unregister_vdec_clk_mgr(VDEC_HEVC);
#endif
#ifdef VDEC_HAS_VDEC_HCODEC
unregister_vdec_clk_mgr(VDEC_HCODEC);
#endif
#ifdef VDEC_HAS_CLK_SETTINGS
unregister_vdec_clk_setting();
#endif
pr_info("media clock exit.\n");
}
#define ARCH_VDEC_CLK_INIT()\
module_init(vdec_init_clk)
#define ARCH_VDEC_CLK_EXIT()\
module_exit(vdec_clk_exit)
#endif
#endif

View File

@@ -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

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,206 @@
/*
* 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.
*
*/
#define DEBUG
#include <linux/compiler.h>
#include <linux/clk-provider.h>
#include <linux/err.h>
#include <linux/kernel.h>
#include <linux/spinlock.h>
#include <linux/clk.h>
#include "amports_gate.h"
#include <linux/amlogic/media/utils/vdec_reg.h>
#include "../../../stream_input/amports/amports_priv.h"
#include "../../../frame_provider/decoder/utils/vdec.h"
#include "../clk/clk.h"
#define DEBUG_REF 1
#define GATE_RESET_OK
#ifdef GATE_RESET_OK
struct gate_switch_node gates[] = {
{
.name = "demux",
},
{
.name = "parser_top",
},
{
.name = "vdec",
},
{
.name = "clk_81",
},
{
.name = "clk_vdec_mux",
},
{
.name = "clk_hcodec_mux",
},
{
.name = "clk_hevc_mux",
},
{
.name = "clk_hevcb_mux",
},
{
.name = "ahbarb0",
},
{
.name = "asyncfifo",
},
};
/*
*mesonstream {
* compatible = "amlogic, codec, streambuf";
* dev_name = "mesonstream";
* status = "okay";
* clocks = <&clkc CLKID_DOS_PARSER
* &clkc CLKID_DEMUX
* &clkc CLKID_DOS
* &clkc CLKID_VDEC_MUX
* &clkc CLKID_HCODEC_MUX
* &clkc CLKID_HEVCF_MUX
* &clkc CLKID_HEVC_MUX>;
* clock-names = "parser_top",
* "demux",
* "vdec",
* "clk_vdec_mux",
* "clk_hcodec_mux",
* "clk_hevc_mux",
* "clk_hevcb_mux";
*};
*/
int amports_clock_gate_init(struct device *dev)
{
int i;
for (i = 0; i < sizeof(gates) / sizeof(struct gate_switch_node); i++) {
gates[i].clk = devm_clk_get(dev, gates[i].name);
if (IS_ERR_OR_NULL(gates[i].clk)) {
gates[i].clk = NULL;
pr_info("get gate %s control failed %p\n",
gates[i].name,
gates[i].clk);
} else {
pr_info("get gate %s control ok %p\n",
gates[i].name,
gates[i].clk);
}
gates[i].ref_count = 0;
mutex_init(&gates[i].mutex);
}
set_clock_gate(gates, ARRAY_SIZE(gates));
return 0;
}
EXPORT_SYMBOL(amports_clock_gate_init);
static int amports_gate_clk(struct gate_switch_node *gate_node, int enable)
{
mutex_lock(&gate_node->mutex);
if (enable) {
if (gate_node->ref_count == 0)
clk_prepare_enable(gate_node->clk);
gate_node->ref_count++;
if (DEBUG_REF)
pr_debug("the %-15s clock on, ref cnt: %d\n",
gate_node->name, gate_node->ref_count);
} else {
gate_node->ref_count--;
if (gate_node->ref_count == 0)
clk_disable_unprepare(gate_node->clk);
if (DEBUG_REF)
pr_debug("the %-15s clock off, ref cnt: %d\n",
gate_node->name, gate_node->ref_count);
}
mutex_unlock(&gate_node->mutex);
return 0;
}
int amports_switch_gate(const char *name, int enable)
{
int i;
for (i = 0; i < sizeof(gates) / sizeof(struct gate_switch_node); i++) {
if (!strcmp(name, gates[i].name)) {
/*pr_info("openclose:%d gate %s control\n", enable,
* gates[i].name);
*/
if (gates[i].clk)
amports_gate_clk(&gates[i], enable);
}
}
return 0;
}
EXPORT_SYMBOL(amports_switch_gate);
#else
/*
*can used for debug.
*on chip bringup.
*/
int amports_clock_gate_init(struct device *dev)
{
static int gate_inited;
if (gate_inited)
return 0;
/*
*#define HHI_GCLK_MPEG0 0x1050
*#define HHI_GCLK_MPEG1 0x1051
*#define HHI_GCLK_MPEG2 0x1052
*#define HHI_GCLK_OTHER 0x1054
*#define HHI_GCLK_AO 0x1055
*/
WRITE_HHI_REG_BITS(HHI_GCLK_MPEG0, 1, 1, 1);/*dos*/
WRITE_HHI_REG_BITS(HHI_GCLK_MPEG1, 1, 25, 1);/*U_parser_top()*/
WRITE_HHI_REG_BITS(HHI_GCLK_MPEG1, 0xff, 6, 8);/*aiu()*/
WRITE_HHI_REG_BITS(HHI_GCLK_MPEG1, 1, 4, 1);/*demux()*/
WRITE_HHI_REG_BITS(HHI_GCLK_MPEG1, 1, 2, 1);/*audio in()*/
WRITE_HHI_REG_BITS(HHI_GCLK_MPEG2, 1, 25, 1);/*VPU Interrupt*/
gate_inited++;
return 0;
}
EXPORT_SYMBOL(amports_clock_gate_init);
static int amports_switch_gate(struct gate_switch_node *gate_node, int enable)
{
return 0;
}
int amports_switch_gate(const char *name, int enable)
{
amports_switch_gate(0, 0);
return 0;
}
EXPORT_SYMBOL(amports_switch_gate);
#endif

View File

@@ -0,0 +1,32 @@
/*
* drivers/amlogic/media/common/arch/switch/amports_gate.h
*
* Copyright (C) 2016 Amlogic, Inc. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
*/
#ifndef AMPORT_GATE_H
#define AMPORT_GATE_H
#include <linux/device.h>
struct gate_switch_node {
struct clk *clk;
const char *name;
struct mutex mutex;
int ref_count;
};
extern int amports_clock_gate_init(struct device *dev);
extern int amports_switch_gate(const char *name, int enable);
#endif

View File

@@ -0,0 +1 @@
obj-y += decoder/

View File

@@ -0,0 +1,12 @@
obj-y += utils/
obj-y += mpeg12/
obj-y += mpeg4/
obj-y += vc1/
obj-y += h264/
obj-y += h264_multi/
obj-y += h265/
obj-y += vp9/
obj-y += mjpeg/
obj-y += real/
obj-y += avs/
obj-y += avs2/

View File

@@ -0,0 +1,2 @@
obj-$(CONFIG_AMLOGIC_MEDIA_VDEC_AVS) += amvdec_avs.o
amvdec_avs-objs += avs.o avsp_trans.o

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,72 @@
#ifndef AVS_H_
#define AVS_H_
#ifdef CONFIG_AMLOGIC_AVSP_LONG_CABAC
#define AVSP_LONG_CABAC
#endif
/*#define BITSTREAM_READ_TMP_NO_CACHE*/
#ifdef AVSP_LONG_CABAC
#define MAX_CODED_FRAME_SIZE 1500000 /*!< bytes for one frame*/
#define LOCAL_HEAP_SIZE (1024*1024*10)
/*
*#define MAX_CODED_FRAME_SIZE 240000
*#define MAX_CODED_FRAME_SIZE 700000
*/
#define SVA_STREAM_BUF_SIZE 1024
extern void *es_write_addr_virt;
extern dma_addr_t es_write_addr_phy;
extern void *bitstream_read_tmp;
extern dma_addr_t bitstream_read_tmp_phy;
extern void *avsp_heap_adr;
int avs_get_debug_flag(void);
int process_long_cabac(void);
/* bit [6] - skip_mode_flag
* bit [5:4] - picture_type
* bit [3] - picture_structure (0-Field, 1-Frame)
* bit [2] - fixed_picture_qp
* bit [1] - progressive_sequence
* bit [0] - active
*/
#define LONG_CABAC_REQ AV_SCRATCH_K
#define LONG_CABAC_SRC_ADDR AV_SCRATCH_H
#define LONG_CABAC_DES_ADDR AV_SCRATCH_I
/* bit[31:16] - vertical_size
* bit[15:0] - horizontal_size
*/
#define LONG_CABAC_PIC_SIZE AV_SCRATCH_J
#endif
/*
*#define PERFORMANCE_DEBUG
*#define DUMP_DEBUG
*/
#define AVS_DEBUG_PRINT 0x01
#define AVS_DEBUG_UCODE 0x02
#define AVS_DEBUG_OLD_ERROR_HANDLE 0x10
#define AVS_DEBUG_USE_FULL_SPEED 0x80
#define AEC_DUMP 0x100
#define STREAM_INFO_DUMP 0x200
#define SLICE_INFO_DUMP 0x400
#define MB_INFO_DUMP 0x800
#define MB_NUM_DUMP 0x1000
#define BLOCK_NUM_DUMP 0x2000
#define COEFF_DUMP 0x4000
#define ES_DUMP 0x8000
#define DQUANT_DUMP 0x10000
#define STREAM_INFO_DUMP_MORE 0x20000
#define STREAM_INFO_DUMP_MORE2 0x40000
extern void *es_write_addr_virt;
extern void *bitstream_read_tmp;
extern dma_addr_t bitstream_read_tmp_phy;
int read_bitstream(unsigned char *Buf, int size);
int u_v(int LenInBits, char *tracestring);
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,2 @@
obj-$(CONFIG_AMLOGIC_MEDIA_VDEC_AVS2) += amvdec_avs2.o
amvdec_avs2-objs += vavs2.o avs2_bufmgr.o

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,26 @@
/*
* drivers/amlogic/amports/vavs2.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 VAVS2_H
#define VAVS2_H
#define AVS2_10B_MMU
#define MV_USE_FIXED_BUF
void adapt_coef_probs(int pic_count, int prev_kf, int cur_kf, int pre_fc,
unsigned int *prev_prob, unsigned int *cur_prob, unsigned int *count);
#endif

View File

@@ -0,0 +1,6 @@
obj-$(CONFIG_AMLOGIC_MEDIA_VDEC_H264) += amvdec_h264.o
amvdec_h264-objs += vh264.o
obj-$(CONFIG_AMLOGIC_MEDIA_VDEC_H264_MVC) += amvdec_h264mvc.o
amvdec_h264mvc-objs += vh264_mvc.o

File diff suppressed because it is too large Load Diff

View File

@@ -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 */

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,2 @@
obj-$(CONFIG_AMLOGIC_MEDIA_VDEC_H264_MULTI) += amvdec_mh264.o
amvdec_mh264-objs += vmh264.o h264_dpb.o

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,860 @@
#ifndef H264_DPB_H_
#define H264_DPB_H_
#define ERROR_CHECK
#define OUTPUT_BUFFER_IN_C
#define PRINT_FLAG_ERROR 0x0
#define PRINT_FLAG_VDEC_STATUS 0X0001
#define PRINT_FLAG_UCODE_EVT 0x0002
#define PRINT_FLAG_MMU_DETAIL 0x0004
#define PRINT_FLAG_ERRORFLAG_DBG 0x0008
#define PRINT_FLAG_DPB_DETAIL 0x0010
#define PRINT_FLAG_DEC_DETAIL 0x0020
#define PRINT_FLAG_VDEC_DETAIL 0x0040
#define PRINT_FLAG_DUMP_DPB 0x0080
#define PRINT_FRAMEBASE_DATA 0x0100
#define PRINT_FLAG_DEBUG_POC 0x0200
#define RRINT_FLAG_RPM 0x0400
#define DEBUG_DISABLE_RUNREADY_RMBUF 0x0800
#define PRINT_FLAG_DUMP_BUFSPEC 0x1000
#define DISABLE_ERROR_HANDLE 0x10000
#define DEBUG_DUMP_STAT 0x80000
#define MVC_EXTENSION_ENABLE 0
#define PRINTREFLIST 0
#define MAX_LIST_SIZE 33
#define FALSE 0
#define H264_SLICE_HEAD_DONE 0x01
#define H264_PIC_DATA_DONE 0x02
/*#define H264_SPS_DONE 0x03*/
/*#define H264_PPS_DONE 0x04*/
/*#define H264_SLICE_DATA_DONE 0x05*/
/*#define H264_DATA_END 0x06*/
#define H264_CONFIG_REQUEST 0x11
#define H264_DATA_REQUEST 0x12
#define H264_WRRSP_REQUEST 0x13
#define H264_WRRSP_DONE 0x14
#define H264_DECODE_BUFEMPTY 0x20
#define H264_DECODE_TIMEOUT 0x21
#define H264_SEARCH_BUFEMPTY 0x22
#define H264_DECODE_OVER_SIZE 0x23
#define H264_FIND_NEXT_PIC_NAL 0x50
#define H264_FIND_NEXT_DVEL_NAL 0x51
#define H264_AUX_DATA_READY 0x52
/* 0x8x, search state*/
#define H264_STATE_SEARCH_AFTER_SPS 0x80
#define H264_STATE_SEARCH_AFTER_PPS 0x81
#define H264_STATE_PARSE_SLICE_HEAD 0x82
#define H264_STATE_SEARCH_HEAD 0x83
/**/
#define H264_ACTION_SEARCH_HEAD 0xf0
#define H264_ACTION_DECODE_SLICE 0xf1
#define H264_ACTION_CONFIG_DONE 0xf2
#define H264_ACTION_DECODE_NEWPIC 0xf3
#define H264_ACTION_DECODE_START 0xff
#define RPM_BEGIN 0x0
#define RPM_END 0x400
#define val(s) (s[0]|(s[1]<<16))
#define FRAME_IN_DPB 24
#define DPB_OFFSET 0x100
#define MMCO_OFFSET 0x200
union param {
#if 0
#define H_TIME_STAMP_START 0X00
#define H_TIME_STAMP_END 0X17
#define PTS_ZERO_0 0X18
#define PTS_ZERO_1 0X19
#endif
#define FIXED_FRAME_RATE_FLAG 0X21
#define OFFSET_DELIMITER_LO 0x2f
#define OFFSET_DELIMITER_HI 0x30
#define SLICE_IPONLY_BREAK 0X5C
#define PREV_MAX_REFERENCE_FRAME_NUM 0X5D
#define EOS 0X5E
#define FRAME_PACKING_TYPE 0X5F
#define OLD_POC_PAR_1 0X60
#define OLD_POC_PAR_2 0X61
#define PREV_MBX 0X62
#define PREV_MBY 0X63
#define ERROR_SKIP_MB_NUM 0X64
#define ERROR_MB_STATUS 0X65
#define L0_PIC0_STATUS 0X66
#define TIMEOUT_COUNTER 0X67
#define BUFFER_SIZE 0X68
#define BUFFER_SIZE_HI 0X69
#define CROPPING_LEFT_RIGHT 0X6A
#define CROPPING_TOP_BOTTOM 0X6B
#define POC_SELECT_NEED_SWAP 0X6C
#define POC_SELECT_SWAP 0X6D
#define MAX_BUFFER_FRAME 0X6E
#define NON_CONFORMING_STREAM 0X70
#define RECOVERY_POINT 0X71
#define POST_CANVAS 0X72
#define POST_CANVAS_H 0X73
#define SKIP_PIC_COUNT 0X74
#define TARGET_NUM_SCALING_LIST 0X75
#define FF_POST_ONE_FRAME 0X76
#define PREVIOUS_BIT_CNT 0X77
#define MB_NOT_SHIFT_COUNT 0X78
#define PIC_STATUS 0X79
#define FRAME_COUNTER 0X7A
#define NEW_SLICE_TYPE 0X7B
#define NEW_PICTURE_STRUCTURE 0X7C
#define NEW_FRAME_NUM 0X7D
#define NEW_IDR_PIC_ID 0X7E
#define IDR_PIC_ID 0X7F
/* h264 LOCAL */
#define NAL_UNIT_TYPE 0X80
#define NAL_REF_IDC 0X81
#define SLICE_TYPE 0X82
#define LOG2_MAX_FRAME_NUM 0X83
#define FRAME_MBS_ONLY_FLAG 0X84
#define PIC_ORDER_CNT_TYPE 0X85
#define LOG2_MAX_PIC_ORDER_CNT_LSB 0X86
#define PIC_ORDER_PRESENT_FLAG 0X87
#define REDUNDANT_PIC_CNT_PRESENT_FLAG 0X88
#define PIC_INIT_QP_MINUS26 0X89
#define DEBLOCKING_FILTER_CONTROL_PRESENT_FLAG 0X8A
#define NUM_SLICE_GROUPS_MINUS1 0X8B
#define MODE_8X8_FLAGS 0X8C
#define ENTROPY_CODING_MODE_FLAG 0X8D
#define SLICE_QUANT 0X8E
#define TOTAL_MB_HEIGHT 0X8F
#define PICTURE_STRUCTURE 0X90
#define TOP_INTRA_TYPE 0X91
#define RV_AI_STATUS 0X92
#define AI_READ_START 0X93
#define AI_WRITE_START 0X94
#define AI_CUR_BUFFER 0X95
#define AI_DMA_BUFFER 0X96
#define AI_READ_OFFSET 0X97
#define AI_WRITE_OFFSET 0X98
#define AI_WRITE_OFFSET_SAVE 0X99
#define RV_AI_BUFF_START 0X9A
#define I_PIC_MB_COUNT 0X9B
#define AI_WR_DCAC_DMA_CTRL 0X9C
#define SLICE_MB_COUNT 0X9D
#define PICTYPE 0X9E
#define SLICE_GROUP_MAP_TYPE 0X9F
#define MB_TYPE 0XA0
#define MB_AFF_ADDED_DMA 0XA1
#define PREVIOUS_MB_TYPE 0XA2
#define WEIGHTED_PRED_FLAG 0XA3
#define WEIGHTED_BIPRED_IDC 0XA4
/* bit 3:2 - PICTURE_STRUCTURE
* bit 1 - MB_ADAPTIVE_FRAME_FIELD_FLAG
* bit 0 - FRAME_MBS_ONLY_FLAG
*/
#define MBFF_INFO 0XA5
#define TOP_INTRA_TYPE_TOP 0XA6
#define RV_AI_BUFF_INC 0xa7
#define DEFAULT_MB_INFO_LO 0xa8
/* 0 -- no need to read
* 1 -- need to wait Left
* 2 -- need to read Intra
* 3 -- need to read back MV
*/
#define NEED_READ_TOP_INFO 0xa9
/* 0 -- idle
* 1 -- wait Left
* 2 -- reading top Intra
* 3 -- reading back MV
*/
#define READ_TOP_INFO_STATE 0xaa
#define DCAC_MBX 0xab
#define TOP_MB_INFO_OFFSET 0xac
#define TOP_MB_INFO_RD_IDX 0xad
#define TOP_MB_INFO_WR_IDX 0xae
#define VLD_NO_WAIT 0
#define VLD_WAIT_BUFFER 1
#define VLD_WAIT_HOST 2
#define VLD_WAIT_GAP 3
#define VLD_WAITING 0xaf
#define MB_X_NUM 0xb0
/* #define MB_WIDTH 0xb1 */
#define MB_HEIGHT 0xb2
#define MBX 0xb3
#define TOTAL_MBY 0xb4
#define INTR_MSK_SAVE 0xb5
/* #define has_time_stamp 0xb6 */
#define NEED_DISABLE_PPE 0xb6
#define IS_NEW_PICTURE 0XB7
#define PREV_NAL_REF_IDC 0XB8
#define PREV_NAL_UNIT_TYPE 0XB9
#define FRAME_MB_COUNT 0XBA
#define SLICE_GROUP_UCODE 0XBB
#define SLICE_GROUP_CHANGE_RATE 0XBC
#define SLICE_GROUP_CHANGE_CYCLE_LEN 0XBD
#define DELAY_LENGTH 0XBE
#define PICTURE_STRUCT 0XBF
/* #define pre_picture_struct 0xc0 */
#define DCAC_PREVIOUS_MB_TYPE 0xc1
#define TIME_STAMP 0XC2
#define H_TIME_STAMP 0XC3
#define VPTS_MAP_ADDR 0XC4
#define H_VPTS_MAP_ADDR 0XC5
/*#define MAX_DPB_SIZE 0XC6*/
#define PIC_INSERT_FLAG 0XC7
#define TIME_STAMP_START 0XC8
#define TIME_STAMP_END 0XDF
#define OFFSET_FOR_NON_REF_PIC 0XE0
#define OFFSET_FOR_TOP_TO_BOTTOM_FIELD 0XE2
#define MAX_REFERENCE_FRAME_NUM 0XE4
#define FRAME_NUM_GAP_ALLOWED 0XE5
#define NUM_REF_FRAMES_IN_PIC_ORDER_CNT_CYCLE 0XE6
#define PROFILE_IDC_MMCO 0XE7
#define LEVEL_IDC_MMCO 0XE8
#define FRAME_SIZE_IN_MB 0XE9
#define DELTA_PIC_ORDER_ALWAYS_ZERO_FLAG 0XEA
#define PPS_NUM_REF_IDX_L0_ACTIVE_MINUS1 0XEB
#define PPS_NUM_REF_IDX_L1_ACTIVE_MINUS1 0XEC
#define CURRENT_SPS_ID 0XED
#define CURRENT_PPS_ID 0XEE
/* bit 0 - sequence parameter set may change
* bit 1 - picture parameter set may change
* bit 2 - new dpb just inited
* bit 3 - IDR picture not decoded yet
* bit 5:4 - 0: mb level code loaded 1: picture
* level code loaded 2: slice level code loaded
*/
#define DECODE_STATUS 0XEF
#define FIRST_MB_IN_SLICE 0XF0
#define PREV_MB_WIDTH 0XF1
#define PREV_FRAME_SIZE_IN_MB 0XF2
/*#define MAX_REFERENCE_FRAME_NUM_IN_MEM 0XF3*/
/* bit 0 - aspect_ratio_info_present_flag
* bit 1 - timing_info_present_flag
* bit 2 - nal_hrd_parameters_present_flag
* bit 3 - vcl_hrd_parameters_present_flag
* bit 4 - pic_struct_present_flag
* bit 5 - bitstream_restriction_flag
*/
#define VUI_STATUS 0XF4
#define ASPECT_RATIO_IDC 0XF5
#define ASPECT_RATIO_SAR_WIDTH 0XF6
#define ASPECT_RATIO_SAR_HEIGHT 0XF7
#define NUM_UNITS_IN_TICK 0XF8
#define TIME_SCALE 0XFA
#define CURRENT_PIC_INFO 0XFC
#define DPB_BUFFER_INFO 0XFD
#define REFERENCE_POOL_INFO 0XFE
#define REFERENCE_LIST_INFO 0XFF
struct{
unsigned short data[RPM_END-RPM_BEGIN];
} l;
struct{
unsigned short dump[DPB_OFFSET];
unsigned short dpb_base[FRAME_IN_DPB<<3];
unsigned short dpb_max_buffer_frame;
unsigned short actual_dpb_size;
unsigned short colocated_buf_status;
unsigned short num_forward_short_term_reference_pic;
unsigned short num_short_term_reference_pic;
unsigned short num_reference_pic;
unsigned short current_dpb_index;
unsigned short current_decoded_frame_num;
unsigned short current_reference_frame_num;
unsigned short l0_size;
unsigned short l1_size;
/* [6:5] : nal_ref_idc */
/* [4:0] : nal_unit_type */
unsigned short NAL_info_mmco;
/* [1:0] : 00 - top field, 01 - bottom field,
* 10 - frame, 11 - mbaff frame
*/
unsigned short picture_structure_mmco;
unsigned short frame_num;
unsigned short pic_order_cnt_lsb;
unsigned short num_ref_idx_l0_active_minus1;
unsigned short num_ref_idx_l1_active_minus1;
unsigned short PrevPicOrderCntLsb;
unsigned short PreviousFrameNum;
/* 32 bits variables */
unsigned short delta_pic_order_cnt_bottom[2];
unsigned short delta_pic_order_cnt_0[2];
unsigned short delta_pic_order_cnt_1[2];
unsigned short PrevPicOrderCntMsb[2];
unsigned short PrevFrameNumOffset[2];
unsigned short frame_pic_order_cnt[2];
unsigned short top_field_pic_order_cnt[2];
unsigned short bottom_field_pic_order_cnt[2];
unsigned short colocated_mv_addr_start[2];
unsigned short colocated_mv_addr_end[2];
unsigned short colocated_mv_wr_addr[2];
} dpb;
struct {
unsigned short dump[MMCO_OFFSET];
/* array base address for offset_for_ref_frame */
unsigned short offset_for_ref_frame_base[128];
/* 0 - Index in DPB
* 1 - Picture Flag
* [ 2] : 0 - short term reference,
* 1 - long term reference
* [ 1] : bottom field
* [ 0] : top field
* 2 - Picture Number (short term or long term) low 16 bits
* 3 - Picture Number (short term or long term) high 16 bits
*/
unsigned short reference_base[128];
/* command and parameter, until command is 3 */
unsigned short l0_reorder_cmd[66];
unsigned short l1_reorder_cmd[66];
/* command and parameter, until command is 0 */
unsigned short mmco_cmd[44];
unsigned short l0_base[40];
unsigned short l1_base[40];
} mmco;
struct {
/* from ucode lmem, do not change this struct */
} p;
};
struct StorablePicture;
struct VideoParameters;
struct DecodedPictureBuffer;
/* New enum for field processing */
enum PictureStructure {
FRAME,
TOP_FIELD,
BOTTOM_FIELD
};
#define I_Slice 2
#define P_Slice 5
#define B_Slice 6
#define P_Slice_0 0
#define B_Slice_1 1
#define I_Slice_7 7
enum SliceType {
P_SLICE = 0,
B_SLICE = 1,
I_SLICE = 2,
SP_SLICE = 3,
SI_SLICE = 4,
NUM_SLICE_TYPES = 5
};
enum ProfileIDC {
FREXT_CAVLC444 = 44, /*!< YUV 4:4:4/14 "CAVLC 4:4:4"*/
BASELINE = 66, /*!< YUV 4:2:0/8 "Baseline"*/
MAIN = 77, /*!< YUV 4:2:0/8 "Main"*/
EXTENDED = 88, /*!< YUV 4:2:0/8 "Extended"*/
FREXT_HP = 100, /*!< YUV 4:2:0/8 "High"*/
FREXT_Hi10P = 110, /*!< YUV 4:2:0/10 "High 10"*/
FREXT_Hi422 = 122, /*!< YUV 4:2:2/10 "High 4:2:2"*/
FREXT_Hi444 = 244, /*!< YUV 4:4:4/14 "High 4:4:4"*/
MVC_HIGH = 118, /*!< YUV 4:2:0/8 "Multiview High"*/
STEREO_HIGH = 128 /*!< YUV 4:2:0/8 "Stereo High"*/
};
struct SPSParameters {
unsigned int profile_idc;
int pic_order_cnt_type;
int log2_max_pic_order_cnt_lsb_minus4;
int num_ref_frames_in_pic_order_cnt_cycle;
short offset_for_ref_frame[128];
short offset_for_non_ref_pic;
short offset_for_top_to_bottom_field;
/**/
int frame_mbs_only_flag;
int num_ref_frames;
int max_dpb_size;
int log2_max_frame_num_minus4;
};
#define DEC_REF_PIC_MARKING_BUFFER_NUM_MAX 45
struct DecRefPicMarking_s {
int memory_management_control_operation;
int difference_of_pic_nums_minus1;
int long_term_pic_num;
int long_term_frame_idx;
int max_long_term_frame_idx_plus1;
struct DecRefPicMarking_s *Next;
};
#define REORDERING_COMMAND_MAX_SIZE 33
struct Slice {
int first_mb_in_slice;
int mode_8x8_flags;
int picture_structure_mmco;
int frame_num;
int idr_flag;
int toppoc;
int bottompoc;
int framepoc;
int pic_order_cnt_lsb;
int PicOrderCntMsb;
unsigned char field_pic_flag;
unsigned char bottom_field_flag;
int ThisPOC;
int nal_reference_idc;
int AbsFrameNum;
int delta_pic_order_cnt_bottom;
int delta_pic_order_cnt[2];
/**/
char listXsize[6];
struct StorablePicture *listX[6][MAX_LIST_SIZE * 2];
/**/
enum PictureStructure structure;
int long_term_reference_flag;
int no_output_of_prior_pics_flag;
int adaptive_ref_pic_buffering_flag;
struct VideoParameters *p_Vid;
struct DecodedPictureBuffer *p_Dpb;
int num_ref_idx_active[2]; /* number of available list references */
/*modification*/
int slice_type; /* slice type */
int ref_pic_list_reordering_flag[2];
int modification_of_pic_nums_idc[2][REORDERING_COMMAND_MAX_SIZE];
int abs_diff_pic_num_minus1[2][REORDERING_COMMAND_MAX_SIZE];
int long_term_pic_idx[2][REORDERING_COMMAND_MAX_SIZE];
/**/
unsigned char dec_ref_pic_marking_buffer_valid;
struct DecRefPicMarking_s
dec_ref_pic_marking_buffer[DEC_REF_PIC_MARKING_BUFFER_NUM_MAX];
};
struct OldSliceParams {
unsigned int field_pic_flag;
unsigned int frame_num;
int nal_ref_idc;
unsigned int pic_oder_cnt_lsb;
int delta_pic_oder_cnt_bottom;
int delta_pic_order_cnt[2];
unsigned char bottom_field_flag;
unsigned char idr_flag;
int idr_pic_id;
int pps_id;
#if (MVC_EXTENSION_ENABLE)
int view_id;
int inter_view_flag;
int anchor_pic_flag;
#endif
int layer_id;
};
struct VideoParameters {
int PrevPicOrderCntMsb;
int PrevPicOrderCntLsb;
unsigned char last_has_mmco_5;
unsigned char last_pic_bottom_field;
int ThisPOC;
int PreviousFrameNum;
int FrameNumOffset;
int PreviousFrameNumOffset;
int max_frame_num;
unsigned int pre_frame_num;
int ExpectedDeltaPerPicOrderCntCycle;
int PicOrderCntCycleCnt;
int FrameNumInPicOrderCntCycle;
int ExpectedPicOrderCnt;
/**/
struct SPSParameters *active_sps;
struct Slice **ppSliceList;
int iSliceNumOfCurrPic;
int conceal_mode;
int earlier_missing_poc;
int pocs_in_dpb[100];
struct OldSliceParams old_slice;
/**/
struct StorablePicture *dec_picture;
struct StorablePicture *no_reference_picture;
/*modification*/
int non_conforming_stream;
int recovery_point;
};
static inline int imin(int a, int b)
{
return ((a) < (b)) ? (a) : (b);
}
static inline int imax(int a, int b)
{
return ((a) > (b)) ? (a) : (b);
}
#define MAX_PIC_BUF_NUM 128
#define MAX_NUM_SLICES 50
struct StorablePicture {
/**/
int width;
int height;
int y_canvas_index;
int u_canvas_index;
int v_canvas_index;
/**/
int index;
unsigned char is_used;
enum PictureStructure structure;
int poc;
int top_poc;
int bottom_poc;
int frame_poc;
unsigned int frame_num;
unsigned int recovery_frame;
int pic_num;
int buf_spec_num;
int buf_spec_is_alloced;
int colocated_buf_index;
int long_term_pic_num;
int long_term_frame_idx;
unsigned char is_long_term;
int used_for_reference;
int is_output;
#if 1
/* rain */
int pre_output;
#endif
int non_existing;
int separate_colour_plane_flag;
short max_slice_id;
int size_x, size_y, size_x_cr, size_y_cr;
int size_x_m1, size_y_m1, size_x_cr_m1, size_y_cr_m1;
int coded_frame;
int mb_aff_frame_flag;
unsigned int PicWidthInMbs;
unsigned int PicSizeInMbs;
int iLumaPadY, iLumaPadX;
int iChromaPadY, iChromaPadX;
/* for mb aff, if frame for referencing the top field */
struct StorablePicture *top_field;
/* for mb aff, if frame for referencing the bottom field */
struct StorablePicture *bottom_field;
/* for mb aff, if field for referencing the combined frame */
struct StorablePicture *frame;
int slice_type;
int idr_flag;
int no_output_of_prior_pics_flag;
int long_term_reference_flag;
int adaptive_ref_pic_buffering_flag;
int chroma_format_idc;
int frame_mbs_only_flag;
int frame_cropping_flag;
int frame_crop_left_offset;
int frame_crop_right_offset;
int frame_crop_top_offset;
int frame_crop_bottom_offset;
int qp;
int chroma_qp_offset[2];
int slice_qp_delta;
/* stores the memory management control operations */
struct DecRefPicMarking_s *dec_ref_pic_marking_buffer;
/* picture error concealment */
/*indicates if this is a concealed picture */
int concealed_pic;
/* variables for tone mapping */
int seiHasTone_mapping;
int tone_mapping_model_id;
int tonemapped_bit_depth;
/* imgpel* tone_mapping_lut; tone mapping look up table */
int proc_flag;
#if (MVC_EXTENSION_ENABLE)
int view_id;
int inter_view_flag;
int anchor_pic_flag;
#endif
int iLumaStride;
int iChromaStride;
int iLumaExpandedHeight;
int iChromaExpandedHeight;
/* imgpel **cur_imgY; for more efficient get_block_luma */
int no_ref;
int iCodingType;
char listXsize[MAX_NUM_SLICES][2];
struct StorablePicture **listX[MAX_NUM_SLICES][2];
int layer_id;
int offset_delimiter_lo;
int offset_delimiter_hi;
u32 pts;
u64 pts64;
unsigned char data_flag;
};
struct FrameStore {
/* rain */
int buf_spec_num;
/* rain */
int colocated_buf_index;
/* 0=empty; 1=top; 2=bottom; 3=both fields (or frame) */
int is_used;
/* 0=not used for ref; 1=top used; 2=bottom used;
* 3=both fields (or frame) used
*/
int is_reference;
/* 0=not used for ref; 1=top used; 2=bottom used;
* 3=both fields (or frame) used
*/
int is_long_term;
/* original marking by nal_ref_idc: 0=not used for ref; 1=top used;
* 2=bottom used; 3=both fields (or frame) used
*/
int is_orig_reference;
int is_non_existent;
unsigned int frame_num;
unsigned int recovery_frame;
int frame_num_wrap;
int long_term_frame_idx;
int is_output;
#if 1
/* rain */
int pre_output;
/* index in gFrameStore */
int index;
#define I_FLAG 0x01
#define IDR_FLAG 0x02
#define ERROR_FLAG 0x10
#define NULL_FLAG 0x20
#define MAYBE_ERROR_FLAG 0x40
#define NODISP_FLAG 0x80
unsigned char data_flag;
#endif
int poc;
/* picture error concealment */
int concealment_reference;
struct StorablePicture *frame;
struct StorablePicture *top_field;
struct StorablePicture *bottom_field;
#if (MVC_EXTENSION_ENABLE)
int view_id;
int inter_view_flag[2];
int anchor_pic_flag[2];
#endif
int layer_id;
u32 pts;
u64 pts64;
};
/* #define DPB_SIZE_MAX 16 */
#define DPB_SIZE_MAX 32
struct DecodedPictureBuffer {
struct VideoParameters *p_Vid;
/* InputParameters *p_Inp; ??? */
struct FrameStore *fs[DPB_SIZE_MAX];
struct FrameStore *fs_ref[DPB_SIZE_MAX];
struct FrameStore *fs_ltref[DPB_SIZE_MAX];
/* inter-layer reference (for multi-layered codecs) */
struct FrameStore *fs_ilref[DPB_SIZE_MAX];
/**/
struct FrameStore *fs_list0[DPB_SIZE_MAX];
struct FrameStore *fs_list1[DPB_SIZE_MAX];
struct FrameStore *fs_listlt[DPB_SIZE_MAX];
/**/
unsigned int size;
unsigned int used_size;
unsigned int ref_frames_in_buffer;
unsigned int ltref_frames_in_buffer;
int last_output_poc;
#if (MVC_EXTENSION_ENABLE)
int last_output_view_id;
#endif
int max_long_term_pic_idx;
int init_done;
int first_pic_done; /*by rain*/
int num_ref_frames;
struct FrameStore *last_picture;
unsigned int used_size_il;
int layer_id;
/* DPB related function; */
};
struct h264_dpb_stru {
struct vdec_s *vdec;
int decoder_index;
union param dpb_param;
int decode_idx;
int buf_num;
int curr_POC;
int reorder_pic_num;
u8 fast_output_enable;
/*poc_even_flag:
0, init; 1, odd; 2, even*/
u8 poc_even_odd_flag;
u32 decode_pic_count;
/**/
unsigned int max_reference_size;
unsigned int colocated_buf_map;
unsigned int colocated_buf_count;
unsigned int colocated_mv_addr_start;
unsigned int colocated_mv_addr_end;
unsigned int colocated_buf_size;
struct DecodedPictureBuffer mDPB;
struct Slice mSlice;
struct VideoParameters mVideo;
struct SPSParameters mSPS;
struct StorablePicture m_PIC[MAX_PIC_BUF_NUM];
struct FrameStore mFrameStore[DPB_SIZE_MAX];
/*vui*/
unsigned int vui_status;
unsigned int num_units_in_tick;
unsigned int time_scale;
unsigned int fixed_frame_rate_flag;
unsigned int aspect_ratio_idc;
unsigned int aspect_ratio_sar_width;
unsigned int aspect_ratio_sar_height;
unsigned int dec_dpb_status;
unsigned char buf_alloc_fail;
unsigned int dpb_error_flag;
};
extern unsigned int h264_debug_flag;
extern unsigned int h264_debug_mask;
int dpb_print(int indext, int debug_flag, const char *fmt, ...);
int dpb_print_cont(int index, int debug_flag, const char *fmt, ...);
unsigned char dpb_is_debug(int index, int debug_flag);
int prepare_display_buf(struct vdec_s *vdec, struct FrameStore *frame);
int release_buf_spec_num(struct vdec_s *vdec, int buf_spec_num);
void set_frame_output_flag(struct h264_dpb_stru *p_H264_Dpb, int index);
int is_there_unused_frame_from_dpb(struct DecodedPictureBuffer *p_Dpb);
int h264_slice_header_process(struct h264_dpb_stru *p_H264_Dpb);
void dpb_init_global(struct h264_dpb_stru *p_H264_Dpb,
int id, int actual_dpb_size, int max_reference_size);
void init_colocate_buf(struct h264_dpb_stru *p_H264_Dpb, int count);
int release_colocate_buf(struct h264_dpb_stru *p_H264_Dpb, int index);
int get_free_buf_idx(struct vdec_s *vdec);
int store_picture_in_dpb(struct h264_dpb_stru *p_H264_Dpb,
struct StorablePicture *p, unsigned char data_flag);
int release_picture(struct h264_dpb_stru *p_H264_Dpb,
struct StorablePicture *pic);
void remove_dpb_pictures(struct h264_dpb_stru *p_H264_Dpb);
void bufmgr_post(struct h264_dpb_stru *p_H264_Dpb);
void bufmgr_force_recover(struct h264_dpb_stru *p_H264_Dpb);
int get_long_term_flag_by_buf_spec_num(struct h264_dpb_stru *p_H264_Dpb,
int buf_spec_num);
void bufmgr_h264_remove_unused_frame(struct h264_dpb_stru *p_H264_Dpb,
u8 force_flag);
void flush_dpb(struct h264_dpb_stru *p_H264_Dpb);
void print_pic_info(int decindex, const char *info,
struct StorablePicture *pic,
int slice_type);
void dump_dpb(struct DecodedPictureBuffer *p_Dpb, u8 force);
void dump_pic(struct h264_dpb_stru *p_H264_Dpb);
enum PictureStructure get_cur_slice_picture_struct(
struct h264_dpb_stru *p_H264_Dpb);
int dpb_check_ref_list_error(
struct h264_dpb_stru *p_H264_Dpb);
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,2 @@
obj-$(CONFIG_AMLOGIC_MEDIA_VDEC_H265) += amvdec_h265.o
amvdec_h265-objs += vh265.o

File diff suppressed because it is too large Load Diff

View File

@@ -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 */

View File

@@ -0,0 +1,5 @@
obj-$(CONFIG_AMLOGIC_MEDIA_VDEC_MJPEG) += amvdec_mjpeg.o
amvdec_mjpeg-objs += vmjpeg.o
obj-$(CONFIG_AMLOGIC_MEDIA_VDEC_MJPEG_MULTI) += amvdec_mmjpeg.o
amvdec_mmjpeg-objs += vmjpeg_multi.o

View File

@@ -0,0 +1,956 @@
/*
* drivers/amlogic/amports/vmjpeg.c
*
* Copyright (C) 2015 Amlogic, Inc. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
*/
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/interrupt.h>
#include <linux/timer.h>
#include <linux/kfifo.h>
#include <linux/platform_device.h>
#include <linux/amlogic/media/frame_sync/ptsserv.h>
#include <linux/amlogic/media/utils/amstream.h>
#include <linux/amlogic/media/canvas/canvas.h>
#include <linux/amlogic/media/vfm/vframe.h>
#include <linux/amlogic/media/vfm/vframe_provider.h>
#include <linux/amlogic/media/vfm/vframe_receiver.h>
#include <linux/amlogic/media/utils/vdec_reg.h>
#include "../../../stream_input/amports/amports_priv.h"
#include <linux/amlogic/media/registers/register.h>
#include "../utils/decoder_mmu_box.h"
#include "../utils/decoder_bmmu_box.h"
#include <linux/amlogic/media/codec_mm/codec_mm.h>
#include <linux/amlogic/media/codec_mm/configs.h>
#ifdef CONFIG_AM_VDEC_MJPEG_LOG
#define AMLOG
#define LOG_LEVEL_VAR amlog_level_vmjpeg
#define LOG_MASK_VAR amlog_mask_vmjpeg
#define LOG_LEVEL_ERROR 0
#define LOG_LEVEL_INFO 1
#define LOG_LEVEL_DESC "0:ERROR, 1:INFO"
#endif
#include <linux/amlogic/media/utils/amlog.h>
MODULE_AMLOG(LOG_LEVEL_ERROR, 0, LOG_LEVEL_DESC, LOG_DEFAULT_MASK_DESC);
#include "../utils/amvdec.h"
#include "../utils/firmware.h"
#define DRIVER_NAME "amvdec_mjpeg"
#define MODULE_NAME "amvdec_mjpeg"
/* protocol register usage
* AV_SCRATCH_0 - AV_SCRATCH_1 : initial display buffer fifo
* AV_SCRATCH_2 - AV_SCRATCH_3 : decoder settings
* AV_SCRATCH_4 - AV_SCRATCH_7 : display buffer spec
* AV_SCRATCH_8 - AV_SCRATCH_9 : amrisc/host display buffer management
* AV_SCRATCH_a : time stamp
*/
#define MREG_DECODE_PARAM AV_SCRATCH_2 /* bit 0-3: pico_addr_mode */
/* bit 15-4: reference height */
#define MREG_TO_AMRISC AV_SCRATCH_8
#define MREG_FROM_AMRISC AV_SCRATCH_9
#define MREG_FRAME_OFFSET AV_SCRATCH_A
#define PICINFO_BUF_IDX_MASK 0x0007
#define PICINFO_AVI1 0x0080
#define PICINFO_INTERLACE 0x0020
#define PICINFO_INTERLACE_AVI1_BOT 0x0010
#define PICINFO_INTERLACE_FIRST 0x0010
#define VF_POOL_SIZE 16
#define DECODE_BUFFER_NUM_MAX 4
#define MAX_BMMU_BUFFER_NUM DECODE_BUFFER_NUM_MAX
#define PUT_INTERVAL (HZ/100)
#if 1/*MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON6*/
/* #define NV21 */
#endif
static DEFINE_MUTEX(vmjpeg_mutex);
static struct dec_sysinfo vmjpeg_amstream_dec_info;
static struct vframe_s *vmjpeg_vf_peek(void *);
static struct vframe_s *vmjpeg_vf_get(void *);
static void vmjpeg_vf_put(struct vframe_s *, void *);
static int vmjpeg_vf_states(struct vframe_states *states, void *);
static int vmjpeg_event_cb(int type, void *data, void *private_data);
static int vmjpeg_prot_init(void);
static void vmjpeg_local_init(void);
static const char vmjpeg_dec_id[] = "vmjpeg-dev";
static struct vdec_info *gvs;
static struct work_struct set_clk_work;
#define PROVIDER_NAME "decoder.mjpeg"
static const struct vframe_operations_s vmjpeg_vf_provider = {
.peek = vmjpeg_vf_peek,
.get = vmjpeg_vf_get,
.put = vmjpeg_vf_put,
.event_cb = vmjpeg_event_cb,
.vf_states = vmjpeg_vf_states,
};
static void *mm_blk_handle;
static struct vframe_provider_s vmjpeg_vf_prov;
static DECLARE_KFIFO(newframe_q, struct vframe_s *, VF_POOL_SIZE);
static DECLARE_KFIFO(display_q, struct vframe_s *, VF_POOL_SIZE);
static DECLARE_KFIFO(recycle_q, struct vframe_s *, VF_POOL_SIZE);
static struct vframe_s vfpool[VF_POOL_SIZE];
static s32 vfbuf_use[DECODE_BUFFER_NUM_MAX];
static u32 frame_width, frame_height, frame_dur;
static u32 saved_resolution;
static struct timer_list recycle_timer;
static u32 stat;
static u32 buf_size = 32 * 1024 * 1024;
static DEFINE_SPINLOCK(lock);
static bool is_reset;
static inline u32 index2canvas0(u32 index)
{
const u32 canvas_tab[4] = {
#ifdef NV21
0x010100, 0x030302, 0x050504, 0x070706
#else
0x020100, 0x050403, 0x080706, 0x0b0a09
#endif
};
return canvas_tab[index];
}
static inline u32 index2canvas1(u32 index)
{
const u32 canvas_tab[4] = {
#ifdef NV21
0x0d0d0c, 0x0f0f0e, 0x171716, 0x191918
#else
0x0e0d0c, 0x181716, 0x222120, 0x252423
#endif
};
return canvas_tab[index];
}
static void set_frame_info(struct vframe_s *vf)
{
vf->width = frame_width;
vf->height = frame_height;
vf->duration = frame_dur;
vf->ratio_control = 0;
vf->duration_pulldown = 0;
vf->flag = 0;
}
static irqreturn_t vmjpeg_isr(int irq, void *dev_id)
{
u32 reg, offset, pts, pts_valid = 0;
struct vframe_s *vf = NULL;
u64 pts_us64;
WRITE_VREG(ASSIST_MBOX1_CLR_REG, 1);
reg = READ_VREG(MREG_FROM_AMRISC);
if (reg & PICINFO_BUF_IDX_MASK) {
offset = READ_VREG(MREG_FRAME_OFFSET);
if (pts_lookup_offset_us64
(PTS_TYPE_VIDEO, offset, &pts, 0, &pts_us64) == 0)
pts_valid = 1;
if ((reg & PICINFO_INTERLACE) == 0) {
u32 index = ((reg & PICINFO_BUF_IDX_MASK) - 1) & 3;
if (index >= DECODE_BUFFER_NUM_MAX) {
pr_err("fatal error, invalid buffer index.");
return IRQ_HANDLED;
}
if (kfifo_get(&newframe_q, &vf) == 0) {
pr_info(
"fatal error, no available buffer slot.");
return IRQ_HANDLED;
}
set_frame_info(vf);
vf->signal_type = 0;
vf->index = index;
#ifdef NV21
vf->type =
VIDTYPE_PROGRESSIVE | VIDTYPE_VIU_FIELD |
VIDTYPE_VIU_NV21;
#else
vf->type = VIDTYPE_PROGRESSIVE | VIDTYPE_VIU_FIELD;
#endif
vf->canvas0Addr = vf->canvas1Addr =
index2canvas0(index);
vf->pts = (pts_valid) ? pts : 0;
vf->pts_us64 = (pts_valid) ? pts_us64 : 0;
vf->orientation = 0;
vf->type_original = vf->type;
vfbuf_use[index]++;
vf->mem_handle =
decoder_bmmu_box_get_mem_handle(
mm_blk_handle,
index);
gvs->frame_dur = frame_dur;
vdec_count_info(gvs, 0, offset);
kfifo_put(&display_q, (const struct vframe_s *)vf);
vf_notify_receiver(PROVIDER_NAME,
VFRAME_EVENT_PROVIDER_VFRAME_READY,
NULL);
} else {
u32 index = ((reg & PICINFO_BUF_IDX_MASK) - 1) & 3;
if (index >= DECODE_BUFFER_NUM_MAX) {
pr_info("fatal error, invalid buffer index.");
return IRQ_HANDLED;
}
if (kfifo_get(&newframe_q, &vf) == 0) {
pr_info
("fatal error, no available buffer slot.");
return IRQ_HANDLED;
}
set_frame_info(vf);
vf->signal_type = 0;
vf->index = index;
#if 0
if (reg & PICINFO_AVI1) {
/* AVI1 format */
if (reg & PICINFO_INTERLACE_AVI1_BOT) {
vf->type =
VIDTYPE_INTERLACE_BOTTOM |
VIDTYPE_INTERLACE_FIRST;
} else
vf->type = VIDTYPE_INTERLACE_TOP;
} else {
if (reg & PICINFO_INTERLACE_FIRST) {
vf->type =
VIDTYPE_INTERLACE_TOP |
VIDTYPE_INTERLACE_FIRST;
} else
vf->type = VIDTYPE_INTERLACE_BOTTOM;
}
vf->type |= VIDTYPE_VIU_FIELD;
#ifdef NV21
vf->type |= VIDTYPE_VIU_NV21;
#endif
vf->duration >>= 1;
vf->canvas0Addr = vf->canvas1Addr =
index2canvas0(index);
vf->orientation = 0;
if ((vf->type & VIDTYPE_INTERLACE_FIRST) &&
(pts_valid))
vf->pts = pts;
else
vf->pts = 0;
vfbuf_use[index]++;
kfifo_put(&display_q, (const struct vframe_s *)vf);
#else
/* send whole frame by weaving top & bottom field */
#ifdef NV21
vf->type = VIDTYPE_PROGRESSIVE | VIDTYPE_VIU_NV21;
#else
vf->type = VIDTYPE_PROGRESSIVE;
#endif
vf->canvas0Addr = index2canvas0(index);
vf->canvas1Addr = index2canvas1(index);
vf->orientation = 0;
if (pts_valid) {
vf->pts = pts;
vf->pts_us64 = pts_us64;
} else {
vf->pts = 0;
vf->pts_us64 = 0;
}
vf->type_original = vf->type;
vfbuf_use[index]++;
vf->mem_handle =
decoder_bmmu_box_get_mem_handle(
mm_blk_handle,
index);
gvs->frame_dur = frame_dur;
vdec_count_info(gvs, 0, offset);
kfifo_put(&display_q, (const struct vframe_s *)vf);
vf_notify_receiver(PROVIDER_NAME,
VFRAME_EVENT_PROVIDER_VFRAME_READY,
NULL);
#endif
}
WRITE_VREG(MREG_FROM_AMRISC, 0);
}
return IRQ_HANDLED;
}
static struct vframe_s *vmjpeg_vf_peek(void *op_arg)
{
struct vframe_s *vf;
if (kfifo_peek(&display_q, &vf))
return vf;
return NULL;
}
static struct vframe_s *vmjpeg_vf_get(void *op_arg)
{
struct vframe_s *vf;
if (kfifo_get(&display_q, &vf))
return vf;
return NULL;
}
static void vmjpeg_vf_put(struct vframe_s *vf, void *op_arg)
{
kfifo_put(&recycle_q, (const struct vframe_s *)vf);
}
static int vmjpeg_event_cb(int type, void *data, void *private_data)
{
if (type & VFRAME_EVENT_RECEIVER_RESET) {
unsigned long flags;
amvdec_stop();
#ifndef CONFIG_AMLOGIC_POST_PROCESS_MANAGER
vf_light_unreg_provider(&vmjpeg_vf_prov);
#endif
spin_lock_irqsave(&lock, flags);
vmjpeg_local_init();
vmjpeg_prot_init();
spin_unlock_irqrestore(&lock, flags);
#ifndef CONFIG_AMLOGIC_POST_PROCESS_MANAGER
vf_reg_provider(&vmjpeg_vf_prov);
#endif
amvdec_start();
}
return 0;
}
static int vmjpeg_vf_states(struct vframe_states *states, void *op_arg)
{
unsigned long flags;
spin_lock_irqsave(&lock, flags);
states->vf_pool_size = VF_POOL_SIZE;
states->buf_free_num = kfifo_len(&newframe_q);
states->buf_avail_num = kfifo_len(&display_q);
states->buf_recycle_num = kfifo_len(&recycle_q);
spin_unlock_irqrestore(&lock, flags);
return 0;
}
static void mjpeg_set_clk(struct work_struct *work)
{
if (frame_dur > 0 && saved_resolution !=
frame_width * frame_height * (96000 / frame_dur)) {
int fps = 96000 / frame_dur;
saved_resolution = frame_width * frame_height * fps;
vdec_source_changed(VFORMAT_MJPEG,
frame_width, frame_height, fps);
}
}
static void vmjpeg_put_timer_func(unsigned long arg)
{
struct timer_list *timer = (struct timer_list *)arg;
while (!kfifo_is_empty(&recycle_q) &&
(READ_VREG(MREG_TO_AMRISC) == 0)) {
struct vframe_s *vf;
if (kfifo_get(&recycle_q, &vf)) {
if ((vf->index >= 0)
&& (vf->index < DECODE_BUFFER_NUM_MAX)
&& (--vfbuf_use[vf->index] == 0)) {
WRITE_VREG(MREG_TO_AMRISC, vf->index + 1);
vf->index = DECODE_BUFFER_NUM_MAX;
}
kfifo_put(&newframe_q, (const struct vframe_s *)vf);
}
}
schedule_work(&set_clk_work);
timer->expires = jiffies + PUT_INTERVAL;
add_timer(timer);
}
int vmjpeg_dec_status(struct vdec_s *vdec, struct vdec_info *vstatus)
{
vstatus->frame_width = frame_width;
vstatus->frame_height = frame_height;
if (0 != frame_dur)
vstatus->frame_rate = 96000 / frame_dur;
else
vstatus->frame_rate = 96000;
vstatus->error_count = 0;
vstatus->status = stat;
vstatus->bit_rate = gvs->bit_rate;
vstatus->frame_dur = frame_dur;
vstatus->frame_data = gvs->frame_data;
vstatus->total_data = gvs->total_data;
vstatus->frame_count = gvs->frame_count;
vstatus->error_frame_count = gvs->error_frame_count;
vstatus->drop_frame_count = gvs->drop_frame_count;
vstatus->total_data = gvs->total_data;
vstatus->samp_cnt = gvs->samp_cnt;
vstatus->offset = gvs->offset;
snprintf(vstatus->vdec_name, sizeof(vstatus->vdec_name),
"%s", DRIVER_NAME);
return 0;
}
int vmjpeg_set_isreset(struct vdec_s *vdec, int isreset)
{
is_reset = isreset;
return 0;
}
/****************************************/
static int vmjpeg_canvas_init(void)
{
int i, ret;
u32 canvas_width, canvas_height;
u32 decbuf_size, decbuf_y_size, decbuf_uv_size;
unsigned long buf_start;
if (buf_size <= 0x00400000) {
/* SD only */
canvas_width = 768;
canvas_height = 576;
decbuf_y_size = 0x80000;
decbuf_uv_size = 0x20000;
decbuf_size = 0x100000;
} else {
/* HD & SD */
canvas_width = 1920;
canvas_height = 1088;
decbuf_y_size = 0x200000;
decbuf_uv_size = 0x80000;
decbuf_size = 0x300000;
}
for (i = 0; i < MAX_BMMU_BUFFER_NUM; i++) {
ret = decoder_bmmu_box_alloc_buf_phy(mm_blk_handle, i,
decbuf_size, DRIVER_NAME, &buf_start);
if (ret < 0)
return ret;
#ifdef NV21
canvas_config(index2canvas0(i) & 0xff,
buf_start,
canvas_width, canvas_height,
CANVAS_ADDR_NOWRAP,
CANVAS_BLKMODE_LINEAR);
canvas_config((index2canvas0(i) >> 8) & 0xff,
buf_start +
decbuf_y_size, canvas_width,
canvas_height / 2, CANVAS_ADDR_NOWRAP,
CANVAS_BLKMODE_LINEAR);
canvas_config(index2canvas1(i) & 0xff,
buf_start +
decbuf_size / 2, canvas_width,
canvas_height, CANVAS_ADDR_NOWRAP,
CANVAS_BLKMODE_LINEAR);
canvas_config((index2canvas1(i) >> 8) & 0xff,
buf_start +
decbuf_y_size + decbuf_uv_size / 2,
canvas_width, canvas_height / 2,
CANVAS_ADDR_NOWRAP,
CANVAS_BLKMODE_LINEAR);
#else
canvas_config(index2canvas0(i) & 0xff,
buf_start,
canvas_width, canvas_height,
CANVAS_ADDR_NOWRAP,
CANVAS_BLKMODE_LINEAR);
canvas_config((index2canvas0(i) >> 8) & 0xff,
buf_start +
decbuf_y_size, canvas_width / 2,
canvas_height / 2, CANVAS_ADDR_NOWRAP,
CANVAS_BLKMODE_LINEAR);
canvas_config((index2canvas0(i) >> 16) & 0xff,
buf_start +
decbuf_y_size + decbuf_uv_size,
canvas_width / 2, canvas_height / 2,
CANVAS_ADDR_NOWRAP,
CANVAS_BLKMODE_LINEAR);
canvas_config(index2canvas1(i) & 0xff,
buf_start +
decbuf_size / 2, canvas_width,
canvas_height, CANVAS_ADDR_NOWRAP,
CANVAS_BLKMODE_LINEAR);
canvas_config((index2canvas1(i) >> 8) & 0xff,
buf_start +
decbuf_y_size + decbuf_uv_size / 2,
canvas_width / 2, canvas_height / 2,
CANVAS_ADDR_NOWRAP,
CANVAS_BLKMODE_LINEAR);
canvas_config((index2canvas1(i) >> 16) & 0xff,
buf_start +
decbuf_y_size + decbuf_uv_size +
decbuf_uv_size / 2, canvas_width / 2,
canvas_height / 2, CANVAS_ADDR_NOWRAP,
CANVAS_BLKMODE_LINEAR);
#endif
}
return 0;
}
static void init_scaler(void)
{
/* 4 point triangle */
const unsigned int filt_coef[] = {
0x20402000, 0x20402000, 0x1f3f2101, 0x1f3f2101,
0x1e3e2202, 0x1e3e2202, 0x1d3d2303, 0x1d3d2303,
0x1c3c2404, 0x1c3c2404, 0x1b3b2505, 0x1b3b2505,
0x1a3a2606, 0x1a3a2606, 0x19392707, 0x19392707,
0x18382808, 0x18382808, 0x17372909, 0x17372909,
0x16362a0a, 0x16362a0a, 0x15352b0b, 0x15352b0b,
0x14342c0c, 0x14342c0c, 0x13332d0d, 0x13332d0d,
0x12322e0e, 0x12322e0e, 0x11312f0f, 0x11312f0f,
0x10303010
};
int i;
/* pscale enable, PSCALE cbus bmem enable */
WRITE_VREG(PSCALE_CTRL, 0xc000);
/* write filter coefs */
WRITE_VREG(PSCALE_BMEM_ADDR, 0);
for (i = 0; i < 33; i++) {
WRITE_VREG(PSCALE_BMEM_DAT, 0);
WRITE_VREG(PSCALE_BMEM_DAT, filt_coef[i]);
}
/* Y horizontal initial info */
WRITE_VREG(PSCALE_BMEM_ADDR, 37 * 2);
/* [35]: buf repeat pix0,
* [34:29] => buf receive num,
* [28:16] => buf blk x,
* [15:0] => buf phase
*/
WRITE_VREG(PSCALE_BMEM_DAT, 0x0008);
WRITE_VREG(PSCALE_BMEM_DAT, 0x60000000);
/* C horizontal initial info */
WRITE_VREG(PSCALE_BMEM_ADDR, 41 * 2);
WRITE_VREG(PSCALE_BMEM_DAT, 0x0008);
WRITE_VREG(PSCALE_BMEM_DAT, 0x60000000);
/* Y vertical initial info */
WRITE_VREG(PSCALE_BMEM_ADDR, 39 * 2);
WRITE_VREG(PSCALE_BMEM_DAT, 0x0008);
WRITE_VREG(PSCALE_BMEM_DAT, 0x60000000);
/* C vertical initial info */
WRITE_VREG(PSCALE_BMEM_ADDR, 43 * 2);
WRITE_VREG(PSCALE_BMEM_DAT, 0x0008);
WRITE_VREG(PSCALE_BMEM_DAT, 0x60000000);
/* Y horizontal phase step */
WRITE_VREG(PSCALE_BMEM_ADDR, 36 * 2 + 1);
/* [19:0] => Y horizontal phase step */
WRITE_VREG(PSCALE_BMEM_DAT, 0x10000);
/* C horizontal phase step */
WRITE_VREG(PSCALE_BMEM_ADDR, 40 * 2 + 1);
/* [19:0] => C horizontal phase step */
WRITE_VREG(PSCALE_BMEM_DAT, 0x10000);
/* Y vertical phase step */
WRITE_VREG(PSCALE_BMEM_ADDR, 38 * 2 + 1);
/* [19:0] => Y vertical phase step */
WRITE_VREG(PSCALE_BMEM_DAT, 0x10000);
/* C vertical phase step */
WRITE_VREG(PSCALE_BMEM_ADDR, 42 * 2 + 1);
/* [19:0] => C horizontal phase step */
WRITE_VREG(PSCALE_BMEM_DAT, 0x10000);
/* reset pscaler */
#if 1/*MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON6*/
WRITE_VREG(DOS_SW_RESET0, (1 << 10));
WRITE_VREG(DOS_SW_RESET0, 0);
#else
WRITE_RESET_REG(RESET2_REGISTER, RESET_PSCALE);
#endif
READ_RESET_REG(RESET2_REGISTER);
READ_RESET_REG(RESET2_REGISTER);
READ_RESET_REG(RESET2_REGISTER);
WRITE_VREG(PSCALE_RST, 0x7);
WRITE_VREG(PSCALE_RST, 0x0);
}
static int vmjpeg_prot_init(void)
{
int r;
#if 1/*MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON6*/
WRITE_VREG(DOS_SW_RESET0, (1 << 7) | (1 << 6));
WRITE_VREG(DOS_SW_RESET0, 0);
#else
WRITE_RESET_REG(RESET0_REGISTER, RESET_IQIDCT | RESET_MC);
#endif
r = vmjpeg_canvas_init();
WRITE_VREG(AV_SCRATCH_0, 12);
WRITE_VREG(AV_SCRATCH_1, 0x031a);
#ifdef NV21
WRITE_VREG(AV_SCRATCH_4, 0x010100);
WRITE_VREG(AV_SCRATCH_5, 0x030302);
WRITE_VREG(AV_SCRATCH_6, 0x050504);
WRITE_VREG(AV_SCRATCH_7, 0x070706);
#else
WRITE_VREG(AV_SCRATCH_4, 0x020100);
WRITE_VREG(AV_SCRATCH_5, 0x050403);
WRITE_VREG(AV_SCRATCH_6, 0x080706);
WRITE_VREG(AV_SCRATCH_7, 0x0b0a09);
#endif
init_scaler();
/* clear buffer IN/OUT registers */
WRITE_VREG(MREG_TO_AMRISC, 0);
WRITE_VREG(MREG_FROM_AMRISC, 0);
WRITE_VREG(MCPU_INTR_MSK, 0xffff);
WRITE_VREG(MREG_DECODE_PARAM, (frame_height << 4) | 0x8000);
/* clear mailbox interrupt */
WRITE_VREG(ASSIST_MBOX1_CLR_REG, 1);
/* enable mailbox interrupt */
WRITE_VREG(ASSIST_MBOX1_MASK, 1);
/* set interrupt mapping for vld */
WRITE_VREG(ASSIST_AMR1_INT8, 8);
#if 1/*MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON6*/
#ifdef NV21
SET_VREG_MASK(MDEC_PIC_DC_CTRL, 1 << 17);
#else
CLEAR_VREG_MASK(MDEC_PIC_DC_CTRL, 1 << 17);
#endif
#endif
return r;
}
static int vmjpeg_vdec_info_init(void)
{
gvs = kzalloc(sizeof(struct vdec_info), GFP_KERNEL);
if (NULL == gvs) {
pr_info("the struct of vdec status malloc failed.\n");
return -ENOMEM;
}
return 0;
}
static void vmjpeg_local_init(void)
{
int i;
frame_width = vmjpeg_amstream_dec_info.width;
frame_height = vmjpeg_amstream_dec_info.height;
frame_dur = vmjpeg_amstream_dec_info.rate;
saved_resolution = 0;
amlog_level(LOG_LEVEL_INFO, "mjpegdec: w(%d), h(%d), dur(%d)\n",
frame_width, frame_height, frame_dur);
for (i = 0; i < DECODE_BUFFER_NUM_MAX; i++)
vfbuf_use[i] = 0;
INIT_KFIFO(display_q);
INIT_KFIFO(recycle_q);
INIT_KFIFO(newframe_q);
for (i = 0; i < VF_POOL_SIZE; i++) {
const struct vframe_s *vf = &vfpool[i];
vfpool[i].index = DECODE_BUFFER_NUM_MAX;
kfifo_put(&newframe_q, vf);
}
if (mm_blk_handle) {
decoder_bmmu_box_free(mm_blk_handle);
mm_blk_handle = NULL;
}
mm_blk_handle = decoder_bmmu_box_alloc_box(
DRIVER_NAME,
0,
MAX_BMMU_BUFFER_NUM,
4 + PAGE_SHIFT,
CODEC_MM_FLAGS_CMA_CLEAR |
CODEC_MM_FLAGS_FOR_VDECODER);
}
static s32 vmjpeg_init(void)
{
int ret = -1, size = -1;
char *buf = vmalloc(0x1000 * 16);
if (IS_ERR_OR_NULL(buf))
return -ENOMEM;
init_timer(&recycle_timer);
stat |= STAT_TIMER_INIT;
amvdec_enable();
vmjpeg_local_init();
size = get_firmware_data(VIDEO_DEC_MJPEG, buf);
if (size < 0) {
amvdec_disable();
pr_err("get firmware fail.");
vfree(buf);
return -1;
}
if (size == 1)
pr_info ("tee load ok");
else if (amvdec_loadmc_ex(VFORMAT_MJPEG, NULL, buf) < 0) {
amvdec_disable();
vfree(buf);
return -EBUSY;
}
vfree(buf);
stat |= STAT_MC_LOAD;
/* enable AMRISC side protocol */
ret = vmjpeg_prot_init();
if (ret < 0)
return ret;
ret = vdec_request_irq(VDEC_IRQ_1, vmjpeg_isr,
"vmjpeg-irq", (void *)vmjpeg_dec_id);
if (ret) {
amvdec_disable();
amlog_level(LOG_LEVEL_ERROR, "vmjpeg irq register error.\n");
return -ENOENT;
}
stat |= STAT_ISR_REG;
#ifdef CONFIG_AMLOGIC_POST_PROCESS_MANAGER
vf_provider_init(&vmjpeg_vf_prov, PROVIDER_NAME, &vmjpeg_vf_provider,
NULL);
vf_reg_provider(&vmjpeg_vf_prov);
vf_notify_receiver(PROVIDER_NAME, VFRAME_EVENT_PROVIDER_START, NULL);
#else
vf_provider_init(&vmjpeg_vf_prov, PROVIDER_NAME, &vmjpeg_vf_provider,
NULL);
vf_reg_provider(&vmjpeg_vf_prov);
#endif
if (!is_reset)
vf_notify_receiver(PROVIDER_NAME,
VFRAME_EVENT_PROVIDER_FR_HINT,
(void *)
((unsigned long)vmjpeg_amstream_dec_info.rate));
stat |= STAT_VF_HOOK;
recycle_timer.data = (ulong)&recycle_timer;
recycle_timer.function = vmjpeg_put_timer_func;
recycle_timer.expires = jiffies + PUT_INTERVAL;
add_timer(&recycle_timer);
stat |= STAT_TIMER_ARM;
amvdec_start();
stat |= STAT_VDEC_RUN;
return 0;
}
static int amvdec_mjpeg_probe(struct platform_device *pdev)
{
struct vdec_s *pdata = *(struct vdec_s **)pdev->dev.platform_data;
mutex_lock(&vmjpeg_mutex);
amlog_level(LOG_LEVEL_INFO, "amvdec_mjpeg probe start.\n");
INIT_WORK(&set_clk_work, mjpeg_set_clk);
if (pdata == NULL) {
amlog_level(LOG_LEVEL_ERROR,
"amvdec_mjpeg memory resource undefined.\n");
mutex_unlock(&vmjpeg_mutex);
return -EFAULT;
}
if (pdata->sys_info)
vmjpeg_amstream_dec_info = *pdata->sys_info;
pdata->dec_status = vmjpeg_dec_status;
pdata->set_isreset = vmjpeg_set_isreset;
is_reset = 0;
vmjpeg_vdec_info_init();
if (vmjpeg_init() < 0) {
amlog_level(LOG_LEVEL_ERROR, "amvdec_mjpeg init failed.\n");
mutex_unlock(&vmjpeg_mutex);
kfree(gvs);
gvs = NULL;
return -ENODEV;
}
mutex_unlock(&vmjpeg_mutex);
amlog_level(LOG_LEVEL_INFO, "amvdec_mjpeg probe end.\n");
return 0;
}
static int amvdec_mjpeg_remove(struct platform_device *pdev)
{
mutex_lock(&vmjpeg_mutex);
cancel_work_sync(&set_clk_work);
if (stat & STAT_VDEC_RUN) {
amvdec_stop();
stat &= ~STAT_VDEC_RUN;
}
if (stat & STAT_ISR_REG) {
vdec_free_irq(VDEC_IRQ_1, (void *)vmjpeg_dec_id);
stat &= ~STAT_ISR_REG;
}
if (stat & STAT_TIMER_ARM) {
del_timer_sync(&recycle_timer);
stat &= ~STAT_TIMER_ARM;
}
if (stat & STAT_VF_HOOK) {
if (!is_reset)
vf_notify_receiver(PROVIDER_NAME,
VFRAME_EVENT_PROVIDER_FR_END_HINT,
NULL);
vf_unreg_provider(&vmjpeg_vf_prov);
stat &= ~STAT_VF_HOOK;
}
amvdec_disable();
mutex_unlock(&vmjpeg_mutex);
kfree(gvs);
gvs = NULL;
if (mm_blk_handle) {
decoder_bmmu_box_free(mm_blk_handle);
mm_blk_handle = NULL;
}
amlog_level(LOG_LEVEL_INFO, "amvdec_mjpeg remove.\n");
return 0;
}
/****************************************/
static struct platform_driver amvdec_mjpeg_driver = {
.probe = amvdec_mjpeg_probe,
.remove = amvdec_mjpeg_remove,
#ifdef CONFIG_PM
.suspend = amvdec_suspend,
.resume = amvdec_resume,
#endif
.driver = {
.name = DRIVER_NAME,
}
};
static struct codec_profile_t amvdec_mjpeg_profile = {
.name = "mjpeg",
.profile = ""
};
static struct mconfig mjpeg_configs[] = {
MC_PU32("stat", &stat),
};
static struct mconfig_node mjpeg_node;
static int __init amvdec_mjpeg_driver_init_module(void)
{
amlog_level(LOG_LEVEL_INFO, "amvdec_mjpeg module init\n");
if (platform_driver_register(&amvdec_mjpeg_driver)) {
amlog_level(LOG_LEVEL_ERROR,
"failed to register amvdec_mjpeg driver\n");
return -ENODEV;
}
vcodec_profile_register(&amvdec_mjpeg_profile);
INIT_REG_NODE_CONFIGS("media.decoder", &mjpeg_node,
"mjpeg", mjpeg_configs, CONFIG_FOR_RW);
return 0;
}
static void __exit amvdec_mjpeg_driver_remove_module(void)
{
amlog_level(LOG_LEVEL_INFO, "amvdec_mjpeg module remove.\n");
platform_driver_unregister(&amvdec_mjpeg_driver);
}
/****************************************/
module_param(stat, uint, 0664);
MODULE_PARM_DESC(stat, "\n amvdec_mjpeg stat\n");
module_init(amvdec_mjpeg_driver_init_module);
module_exit(amvdec_mjpeg_driver_remove_module);
MODULE_DESCRIPTION("AMLOGIC MJMPEG Video Decoder Driver");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Tim Yao <timyao@amlogic.com>");

View File

@@ -0,0 +1,932 @@
/*
* drivers/amlogic/amports/vmjpeg.c
*
* Copyright (C) 2015 Amlogic, Inc. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
*/
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/interrupt.h>
#include <linux/timer.h>
#include <linux/kfifo.h>
#include <linux/platform_device.h>
#include <linux/amlogic/media/frame_sync/ptsserv.h>
#include <linux/amlogic/media/utils/amstream.h>
#include <linux/amlogic/media/canvas/canvas.h>
#include <linux/amlogic/media/vfm/vframe.h>
#include <linux/amlogic/media/vfm/vframe_provider.h>
#include <linux/amlogic/media/vfm/vframe_receiver.h>
#include <linux/amlogic/tee.h>
#include <linux/amlogic/media/utils/vdec_reg.h>
#include <linux/amlogic/media/registers/register.h>
#include "../../../stream_input/amports/amports_priv.h"
#include "../utils/vdec_input.h"
#include "../utils/vdec.h"
#include "../utils/amvdec.h"
#include "../utils/decoder_mmu_box.h"
#include "../utils/decoder_bmmu_box.h"
#include <linux/amlogic/media/codec_mm/codec_mm.h>
#include <linux/amlogic/media/codec_mm/configs.h>
#include "../utils/firmware.h"
#define MEM_NAME "codec_mmjpeg"
#define DRIVER_NAME "ammvdec_mjpeg"
#define MODULE_NAME "ammvdec_mjpeg"
#define CHECK_INTERVAL (HZ/100)
/* protocol register usage
* AV_SCRATCH_4 : decode buffer spec
* AV_SCRATCH_5 : decode buffer index
*/
#define MREG_DECODE_PARAM AV_SCRATCH_2 /* bit 0-3: pico_addr_mode */
/* bit 15-4: reference height */
#define MREG_TO_AMRISC AV_SCRATCH_8
#define MREG_FROM_AMRISC AV_SCRATCH_9
#define MREG_FRAME_OFFSET AV_SCRATCH_A
#define DEC_STATUS_REG AV_SCRATCH_J
#define PICINFO_BUF_IDX_MASK 0x0007
#define PICINFO_AVI1 0x0080
#define PICINFO_INTERLACE 0x0020
#define PICINFO_INTERLACE_AVI1_BOT 0x0010
#define PICINFO_INTERLACE_FIRST 0x0010
#define VF_POOL_SIZE 16
#define DECODE_BUFFER_NUM_MAX 4
#define MAX_BMMU_BUFFER_NUM DECODE_BUFFER_NUM_MAX
#define DEFAULT_MEM_SIZE (32*SZ_1M)
static int debug_enable;
#define DECODE_ID(hw) (hw_to_vdec(hw)->id)
static struct vframe_s *vmjpeg_vf_peek(void *);
static struct vframe_s *vmjpeg_vf_get(void *);
static void vmjpeg_vf_put(struct vframe_s *, void *);
static int vmjpeg_vf_states(struct vframe_states *states, void *);
static int vmjpeg_event_cb(int type, void *data, void *private_data);
static void vmjpeg_work(struct work_struct *work);
static int pre_decode_buf_level = 0x800;
static const char vmjpeg_dec_id[] = "vmmjpeg-dev";
#define PROVIDER_NAME "vdec.mjpeg"
static const struct vframe_operations_s vf_provider_ops = {
.peek = vmjpeg_vf_peek,
.get = vmjpeg_vf_get,
.put = vmjpeg_vf_put,
.event_cb = vmjpeg_event_cb,
.vf_states = vmjpeg_vf_states,
};
#define DEC_RESULT_NONE 0
#define DEC_RESULT_DONE 1
#define DEC_RESULT_AGAIN 2
#define DEC_RESULT_FORCE_EXIT 3
#define DEC_RESULT_EOS 4
#define DEC_DECODE_TIMEOUT 0x21
struct buffer_spec_s {
unsigned int y_addr;
unsigned int u_addr;
unsigned int v_addr;
int y_canvas_index;
int u_canvas_index;
int v_canvas_index;
struct canvas_config_s canvas_config[3];
unsigned long cma_alloc_addr;
int cma_alloc_count;
unsigned int buf_adr;
};
#define spec2canvas(x) \
(((x)->v_canvas_index << 16) | \
((x)->u_canvas_index << 8) | \
((x)->y_canvas_index << 0))
struct vdec_mjpeg_hw_s {
spinlock_t lock;
struct mutex vmjpeg_mutex;
struct platform_device *platform_dev;
DECLARE_KFIFO(newframe_q, struct vframe_s *, VF_POOL_SIZE);
DECLARE_KFIFO(display_q, struct vframe_s *, VF_POOL_SIZE);
struct vframe_s vfpool[VF_POOL_SIZE];
struct buffer_spec_s buffer_spec[DECODE_BUFFER_NUM_MAX];
s32 vfbuf_use[DECODE_BUFFER_NUM_MAX];
u32 frame_width;
u32 frame_height;
u32 frame_dur;
u32 saved_resolution;
u8 init_flag;
u32 stat;
u32 dec_result;
unsigned long buf_start;
u32 buf_size;
void *mm_blk_handle;
struct dec_sysinfo vmjpeg_amstream_dec_info;
struct vframe_chunk_s *chunk;
struct work_struct work;
void (*vdec_cb)(struct vdec_s *, void *);
void *vdec_cb_arg;
struct firmware_s *fw;
struct timer_list check_timer;
unsigned decode_timeout_count;
u8 eos;
u32 frame_num, put_num;
};
static void set_frame_info(struct vdec_mjpeg_hw_s *hw, struct vframe_s *vf)
{
vf->width = hw->frame_width;
vf->height = hw->frame_height;
vf->duration = hw->frame_dur;
vf->ratio_control = 0;
vf->duration_pulldown = 0;
vf->flag = 0;
vf->canvas0Addr = vf->canvas1Addr = -1;
vf->plane_num = 3;
vf->canvas0_config[0] = hw->buffer_spec[vf->index].canvas_config[0];
vf->canvas0_config[1] = hw->buffer_spec[vf->index].canvas_config[1];
vf->canvas0_config[2] = hw->buffer_spec[vf->index].canvas_config[2];
vf->canvas1_config[0] = hw->buffer_spec[vf->index].canvas_config[0];
vf->canvas1_config[1] = hw->buffer_spec[vf->index].canvas_config[1];
vf->canvas1_config[2] = hw->buffer_spec[vf->index].canvas_config[2];
}
static irqreturn_t vmjpeg_isr(struct vdec_s *vdec, int irq)
{
struct vdec_mjpeg_hw_s *hw = (struct vdec_mjpeg_hw_s *)(vdec->private);
u32 reg;
struct vframe_s *vf = NULL;
u32 index, offset = 0, pts;
u64 pts_us64;
if (debug_enable & 0x1)
pr_info("%s: %d\n", __func__, __LINE__);
WRITE_VREG(ASSIST_MBOX1_CLR_REG, 1);
if (!hw)
return IRQ_HANDLED;
if (hw->eos)
return IRQ_HANDLED;
reg = READ_VREG(MREG_FROM_AMRISC);
index = READ_VREG(AV_SCRATCH_5);
if (index >= DECODE_BUFFER_NUM_MAX) {
pr_err("fatal error, invalid buffer index.");
return IRQ_HANDLED;
}
if (kfifo_get(&hw->newframe_q, &vf) == 0) {
pr_info(
"fatal error, no available buffer slot.");
return IRQ_HANDLED;
}
vf->index = index;
set_frame_info(hw, vf);
vf->type = VIDTYPE_PROGRESSIVE | VIDTYPE_VIU_FIELD;
/* vf->pts = (pts_valid) ? pts : 0; */
/* vf->pts_us64 = (pts_valid) ? pts_us64 : 0; */
if (hw->chunk) {
vf->pts = hw->chunk->pts;
vf->pts_us64 = hw->chunk->pts64;
} else {
offset = READ_VREG(MREG_FRAME_OFFSET);
if (pts_lookup_offset_us64
(PTS_TYPE_VIDEO, offset, &pts, 3000,
&pts_us64) == 0) {
vf->pts = pts;
vf->pts_us64 = pts_us64;
} else {
vf->pts = 0;
vf->pts_us64 = 0;
}
}
vf->orientation = 0;
hw->vfbuf_use[index]++;
kfifo_put(&hw->display_q, (const struct vframe_s *)vf);
hw->frame_num++;
vf_notify_receiver(vdec->vf_provider_name,
VFRAME_EVENT_PROVIDER_VFRAME_READY,
NULL);
hw->dec_result = DEC_RESULT_DONE;
schedule_work(&hw->work);
return IRQ_HANDLED;
}
static struct vframe_s *vmjpeg_vf_peek(void *op_arg)
{
struct vframe_s *vf;
struct vdec_s *vdec = op_arg;
struct vdec_mjpeg_hw_s *hw = (struct vdec_mjpeg_hw_s *)vdec->private;
if (!hw)
return NULL;
if (kfifo_peek(&hw->display_q, &vf))
return vf;
return NULL;
}
static struct vframe_s *vmjpeg_vf_get(void *op_arg)
{
struct vframe_s *vf;
struct vdec_s *vdec = op_arg;
struct vdec_mjpeg_hw_s *hw = (struct vdec_mjpeg_hw_s *)vdec->private;
if (!hw)
return NULL;
if (kfifo_get(&hw->display_q, &vf))
return vf;
return NULL;
}
static void vmjpeg_vf_put(struct vframe_s *vf, void *op_arg)
{
struct vdec_s *vdec = op_arg;
struct vdec_mjpeg_hw_s *hw = (struct vdec_mjpeg_hw_s *)vdec->private;
hw->vfbuf_use[vf->index]--;
kfifo_put(&hw->newframe_q, (const struct vframe_s *)vf);
hw->put_num++;
}
static int vmjpeg_event_cb(int type, void *data, void *private_data)
{
return 0;
}
static int vmjpeg_vf_states(struct vframe_states *states, void *op_arg)
{
unsigned long flags;
struct vdec_s *vdec = op_arg;
struct vdec_mjpeg_hw_s *hw = (struct vdec_mjpeg_hw_s *)vdec->private;
spin_lock_irqsave(&hw->lock, flags);
states->vf_pool_size = VF_POOL_SIZE;
states->buf_free_num = kfifo_len(&hw->newframe_q);
states->buf_avail_num = kfifo_len(&hw->display_q);
states->buf_recycle_num = 0;
spin_unlock_irqrestore(&hw->lock, flags);
return 0;
}
static int vmjpeg_dec_status(struct vdec_s *vdec, struct vdec_info *vstatus)
{
struct vdec_mjpeg_hw_s *hw = (struct vdec_mjpeg_hw_s *)vdec->private;
vstatus->frame_width = hw->frame_width;
vstatus->frame_height = hw->frame_height;
if (0 != hw->frame_dur)
vstatus->frame_rate = 96000 / hw->frame_dur;
else
vstatus->frame_rate = 96000;
vstatus->error_count = 0;
vstatus->status = hw->stat;
return 0;
}
/****************************************/
static void vmjpeg_canvas_init(struct vdec_s *vdec)
{
int i, ret;
u32 canvas_width, canvas_height;
u32 decbuf_size, decbuf_y_size, decbuf_uv_size;
unsigned long buf_start, addr;
struct vdec_mjpeg_hw_s *hw =
(struct vdec_mjpeg_hw_s *)vdec->private;
canvas_width = 1920;
canvas_height = 1088;
decbuf_y_size = 0x200000;
decbuf_uv_size = 0x80000;
decbuf_size = 0x300000;
for (i = 0; i < DECODE_BUFFER_NUM_MAX; i++) {
int canvas;
canvas = vdec->get_canvas(i, 3);
ret = decoder_bmmu_box_alloc_buf_phy(hw->mm_blk_handle, i,
decbuf_size, DRIVER_NAME, &buf_start);
if (ret < 0) {
pr_err("CMA alloc failed! size 0x%d idx %d\n",
decbuf_size, i);
return;
}
hw->buffer_spec[i].buf_adr = buf_start;
addr = hw->buffer_spec[i].buf_adr;
hw->buffer_spec[i].y_addr = addr;
addr += decbuf_y_size;
hw->buffer_spec[i].u_addr = addr;
addr += decbuf_uv_size;
hw->buffer_spec[i].v_addr = addr;
hw->buffer_spec[i].y_canvas_index = canvas_y(canvas);
hw->buffer_spec[i].u_canvas_index = canvas_u(canvas);
hw->buffer_spec[i].v_canvas_index = canvas_v(canvas);
canvas_config(hw->buffer_spec[i].y_canvas_index,
hw->buffer_spec[i].y_addr,
canvas_width,
canvas_height,
CANVAS_ADDR_NOWRAP,
CANVAS_BLKMODE_LINEAR);
hw->buffer_spec[i].canvas_config[0].phy_addr =
hw->buffer_spec[i].y_addr;
hw->buffer_spec[i].canvas_config[0].width =
canvas_width;
hw->buffer_spec[i].canvas_config[0].height =
canvas_height;
hw->buffer_spec[i].canvas_config[0].block_mode =
CANVAS_BLKMODE_LINEAR;
canvas_config(hw->buffer_spec[i].u_canvas_index,
hw->buffer_spec[i].u_addr,
canvas_width / 2,
canvas_height / 2,
CANVAS_ADDR_NOWRAP,
CANVAS_BLKMODE_LINEAR);
hw->buffer_spec[i].canvas_config[1].phy_addr =
hw->buffer_spec[i].u_addr;
hw->buffer_spec[i].canvas_config[1].width =
canvas_width / 2;
hw->buffer_spec[i].canvas_config[1].height =
canvas_height / 2;
hw->buffer_spec[i].canvas_config[1].block_mode =
CANVAS_BLKMODE_LINEAR;
canvas_config(hw->buffer_spec[i].v_canvas_index,
hw->buffer_spec[i].v_addr,
canvas_width / 2,
canvas_height / 2,
CANVAS_ADDR_NOWRAP,
CANVAS_BLKMODE_LINEAR);
hw->buffer_spec[i].canvas_config[2].phy_addr =
hw->buffer_spec[i].v_addr;
hw->buffer_spec[i].canvas_config[2].width =
canvas_width / 2;
hw->buffer_spec[i].canvas_config[2].height =
canvas_height / 2;
hw->buffer_spec[i].canvas_config[2].block_mode =
CANVAS_BLKMODE_LINEAR;
}
}
static void init_scaler(void)
{
/* 4 point triangle */
const unsigned int filt_coef[] = {
0x20402000, 0x20402000, 0x1f3f2101, 0x1f3f2101,
0x1e3e2202, 0x1e3e2202, 0x1d3d2303, 0x1d3d2303,
0x1c3c2404, 0x1c3c2404, 0x1b3b2505, 0x1b3b2505,
0x1a3a2606, 0x1a3a2606, 0x19392707, 0x19392707,
0x18382808, 0x18382808, 0x17372909, 0x17372909,
0x16362a0a, 0x16362a0a, 0x15352b0b, 0x15352b0b,
0x14342c0c, 0x14342c0c, 0x13332d0d, 0x13332d0d,
0x12322e0e, 0x12322e0e, 0x11312f0f, 0x11312f0f,
0x10303010
};
int i;
/* pscale enable, PSCALE cbus bmem enable */
WRITE_VREG(PSCALE_CTRL, 0xc000);
/* write filter coefs */
WRITE_VREG(PSCALE_BMEM_ADDR, 0);
for (i = 0; i < 33; i++) {
WRITE_VREG(PSCALE_BMEM_DAT, 0);
WRITE_VREG(PSCALE_BMEM_DAT, filt_coef[i]);
}
/* Y horizontal initial info */
WRITE_VREG(PSCALE_BMEM_ADDR, 37 * 2);
/* [35]: buf repeat pix0,
* [34:29] => buf receive num,
* [28:16] => buf blk x,
* [15:0] => buf phase
*/
WRITE_VREG(PSCALE_BMEM_DAT, 0x0008);
WRITE_VREG(PSCALE_BMEM_DAT, 0x60000000);
/* C horizontal initial info */
WRITE_VREG(PSCALE_BMEM_ADDR, 41 * 2);
WRITE_VREG(PSCALE_BMEM_DAT, 0x0008);
WRITE_VREG(PSCALE_BMEM_DAT, 0x60000000);
/* Y vertical initial info */
WRITE_VREG(PSCALE_BMEM_ADDR, 39 * 2);
WRITE_VREG(PSCALE_BMEM_DAT, 0x0008);
WRITE_VREG(PSCALE_BMEM_DAT, 0x60000000);
/* C vertical initial info */
WRITE_VREG(PSCALE_BMEM_ADDR, 43 * 2);
WRITE_VREG(PSCALE_BMEM_DAT, 0x0008);
WRITE_VREG(PSCALE_BMEM_DAT, 0x60000000);
/* Y horizontal phase step */
WRITE_VREG(PSCALE_BMEM_ADDR, 36 * 2 + 1);
/* [19:0] => Y horizontal phase step */
WRITE_VREG(PSCALE_BMEM_DAT, 0x10000);
/* C horizontal phase step */
WRITE_VREG(PSCALE_BMEM_ADDR, 40 * 2 + 1);
/* [19:0] => C horizontal phase step */
WRITE_VREG(PSCALE_BMEM_DAT, 0x10000);
/* Y vertical phase step */
WRITE_VREG(PSCALE_BMEM_ADDR, 38 * 2 + 1);
/* [19:0] => Y vertical phase step */
WRITE_VREG(PSCALE_BMEM_DAT, 0x10000);
/* C vertical phase step */
WRITE_VREG(PSCALE_BMEM_ADDR, 42 * 2 + 1);
/* [19:0] => C horizontal phase step */
WRITE_VREG(PSCALE_BMEM_DAT, 0x10000);
/* reset pscaler */
#if 1/*MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON6*/
WRITE_VREG(DOS_SW_RESET0, (1 << 10));
WRITE_VREG(DOS_SW_RESET0, 0);
#else
WRITE_RESET_REG(RESET2_REGISTER, RESET_PSCALE);
#endif
READ_RESET_REG(RESET2_REGISTER);
READ_RESET_REG(RESET2_REGISTER);
READ_RESET_REG(RESET2_REGISTER);
WRITE_VREG(PSCALE_RST, 0x7);
WRITE_VREG(PSCALE_RST, 0x0);
}
static void timeout_process(struct vdec_mjpeg_hw_s *hw)
{
amvdec_stop();
pr_info("%s decoder timeout\n", __func__);
hw->dec_result = DEC_RESULT_DONE;
vdec_schedule_work(&hw->work);
}
static void check_timer_func(unsigned long arg)
{
struct vdec_mjpeg_hw_s *hw = (struct vdec_mjpeg_hw_s *)arg;
struct vdec_s *vdec = hw_to_vdec(hw);
if ((debug_enable & 0x2) != 0) {
pr_info("%s: status:nstatus=%d:%d\n",
__func__, vdec->status, vdec->next_status);
pr_info("%s: %d,buftl=%x:%x:%x:%x\n",
__func__, __LINE__,
READ_VREG(VLD_MEM_VIFIFO_BUF_CNTL),
READ_PARSER_REG(PARSER_VIDEO_WP),
READ_VREG(VLD_MEM_VIFIFO_LEVEL),
READ_VREG(VLD_MEM_VIFIFO_WP));
}
if ((debug_enable & 0x100) != 0) {
hw->dec_result = DEC_RESULT_DONE;
vdec_schedule_work(&hw->work);
pr_info("vdec %d is forced to be disconnected\n",
debug_enable & 0xff);
debug_enable = 0;
return;
}
if (input_stream_based(vdec)
&& READ_VREG(VLD_MEM_VIFIFO_LEVEL) <= 0x80) {
if (hw->decode_timeout_count > 0)
hw->decode_timeout_count--;
if (hw->decode_timeout_count == 0)
timeout_process(hw);
}
if (READ_VREG(DEC_STATUS_REG) == DEC_DECODE_TIMEOUT) {
pr_info("ucode DEC_DECODE_TIMEOUT\n");
if (hw->decode_timeout_count > 0)
hw->decode_timeout_count--;
if (hw->decode_timeout_count == 0)
timeout_process(hw);
WRITE_VREG(DEC_STATUS_REG, 0);
}
if (vdec->next_status == VDEC_STATUS_DISCONNECTED) {
hw->dec_result = DEC_RESULT_FORCE_EXIT;
vdec_schedule_work(&hw->work);
pr_info("vdec requested to be disconnected\n");
return;
}
mod_timer(&hw->check_timer, jiffies + CHECK_INTERVAL);
}
static void vmjpeg_hw_ctx_restore(struct vdec_s *vdec, int index)
{
struct vdec_mjpeg_hw_s *hw =
(struct vdec_mjpeg_hw_s *)vdec->private;
WRITE_VREG(DOS_SW_RESET0, (1 << 7) | (1 << 6));
WRITE_VREG(DOS_SW_RESET0, 0);
vmjpeg_canvas_init(vdec);
/* find next decode buffer index */
WRITE_VREG(AV_SCRATCH_4, spec2canvas(&hw->buffer_spec[index]));
WRITE_VREG(AV_SCRATCH_5, index);
init_scaler();
/* clear buffer IN/OUT registers */
WRITE_VREG(MREG_TO_AMRISC, 0);
WRITE_VREG(MREG_FROM_AMRISC, 0);
WRITE_VREG(MCPU_INTR_MSK, 0xffff);
WRITE_VREG(MREG_DECODE_PARAM, (hw->frame_height << 4) | 0x8000);
/* clear mailbox interrupt */
WRITE_VREG(ASSIST_MBOX1_CLR_REG, 1);
/* enable mailbox interrupt */
WRITE_VREG(ASSIST_MBOX1_MASK, 1);
/* set interrupt mapping for vld */
WRITE_VREG(ASSIST_AMR1_INT8, 8);
#if 1/*MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON6*/
CLEAR_VREG_MASK(MDEC_PIC_DC_CTRL, 1 << 17);
#endif
}
static s32 vmjpeg_init(struct vdec_s *vdec)
{
int i;
int size = -1, fw_size = 0x1000 * 16;
struct firmware_s *fw = NULL;
struct vdec_mjpeg_hw_s *hw =
(struct vdec_mjpeg_hw_s *)vdec->private;
fw = vmalloc(sizeof(struct firmware_s) + fw_size);
if (IS_ERR_OR_NULL(fw))
return -ENOMEM;
if (tee_enabled()) {
size = 1;
pr_debug (" tee load\n");
} else
size = get_firmware_data(VIDEO_DEC_MJPEG_MULTI, fw->data);
if (size < 0) {
pr_err("get firmware fail.");
vfree(fw);
return -1;
}
fw->len = size;
hw->fw = fw;
hw->frame_width = hw->vmjpeg_amstream_dec_info.width;
hw->frame_height = hw->vmjpeg_amstream_dec_info.height;
hw->frame_dur = hw->vmjpeg_amstream_dec_info.rate;
hw->saved_resolution = 0;
hw->eos = 0;
hw->init_flag = 0;
hw->frame_num = 0;
hw->put_num = 0;
for (i = 0; i < DECODE_BUFFER_NUM_MAX; i++)
hw->vfbuf_use[i] = 0;
INIT_KFIFO(hw->display_q);
INIT_KFIFO(hw->newframe_q);
for (i = 0; i < VF_POOL_SIZE; i++) {
const struct vframe_s *vf = &hw->vfpool[i];
hw->vfpool[i].index = -1;
kfifo_put(&hw->newframe_q, vf);
}
if (hw->mm_blk_handle) {
decoder_bmmu_box_free(hw->mm_blk_handle);
hw->mm_blk_handle = NULL;
}
hw->mm_blk_handle = decoder_bmmu_box_alloc_box(
DRIVER_NAME,
0,
MAX_BMMU_BUFFER_NUM,
4 + PAGE_SHIFT,
CODEC_MM_FLAGS_CMA_CLEAR |
CODEC_MM_FLAGS_FOR_VDECODER);
init_timer(&hw->check_timer);
hw->check_timer.data = (unsigned long)hw;
hw->check_timer.function = check_timer_func;
hw->check_timer.expires = jiffies + CHECK_INTERVAL;
/*add_timer(&hw->check_timer);*/
hw->stat |= STAT_TIMER_ARM;
INIT_WORK(&hw->work, vmjpeg_work);
return 0;
}
static unsigned long run_ready(struct vdec_s *vdec, unsigned long mask)
{
struct vdec_mjpeg_hw_s *hw =
(struct vdec_mjpeg_hw_s *)vdec->private;
if (hw->eos)
return 0;
if (vdec_stream_based(vdec) && (hw->init_flag == 0)
&& pre_decode_buf_level != 0) {
u32 rp, wp, level;
rp = READ_PARSER_REG(PARSER_VIDEO_RP);
wp = READ_PARSER_REG(PARSER_VIDEO_WP);
if (wp < rp)
level = vdec->input.size + wp - rp;
else
level = wp - rp;
if (level < pre_decode_buf_level)
return 0;
}
return CORE_MASK_VDEC_1 | CORE_MASK_HEVC;
}
static void run(struct vdec_s *vdec, unsigned long mask,
void (*callback)(struct vdec_s *, void *), void *arg)
{
struct vdec_mjpeg_hw_s *hw =
(struct vdec_mjpeg_hw_s *)vdec->private;
int i,ret = -1;
hw->vdec_cb_arg = arg;
hw->vdec_cb = callback;
for (i = 0; i < DECODE_BUFFER_NUM_MAX; i++) {
if (hw->vfbuf_use[i] == 0)
break;
}
if (i == DECODE_BUFFER_NUM_MAX) {
hw->dec_result = DEC_RESULT_AGAIN;
schedule_work(&hw->work);
return;
}
ret = vdec_prepare_input(vdec, &hw->chunk);
if (ret < 0) {
if (debug_enable & 0x1) {
pr_info("%s: %d,r=%d,buftl=%x:%x:%x\n",
__func__, __LINE__, ret,
READ_VREG(VLD_MEM_VIFIFO_BUF_CNTL),
READ_PARSER_REG(PARSER_VIDEO_WP),
READ_VREG(VLD_MEM_VIFIFO_WP));
}
hw->dec_result = DEC_RESULT_AGAIN;
schedule_work(&hw->work);
return;
}
hw->dec_result = DEC_RESULT_NONE;
if (amvdec_vdec_loadmc_ex(vdec, "vmmjpeg_mc",hw->fw->data) < 0) {
pr_err("%s: Error amvdec_loadmc fail\n", __func__);
return;
}
/* if (amvdec_vdec_loadmc_buf_ex(vdec, hw->fw->data, hw->fw->len) < 0) {
pr_err("%s: Error amvdec_loadmc fail\n", __func__);
return;
}*/
vmjpeg_hw_ctx_restore(vdec, i);
#if 0
vdec_enable_input(vdec);
mod_timer(&hw->check_timer, jiffies + CHECK_INTERVAL);
#endif
amvdec_start();
vdec_enable_input(vdec);
mod_timer(&hw->check_timer, jiffies + CHECK_INTERVAL);
hw->decode_timeout_count = 2;
hw->init_flag = 1;
if (debug_enable&0x1) {
pr_info("%s (0x%x 0x%x 0x%x) vldcrl 0x%x bitcnt 0x%x powerctl 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n",
__func__,
READ_VREG(VLD_MEM_VIFIFO_LEVEL),
READ_VREG(VLD_MEM_VIFIFO_WP),
READ_VREG(VLD_MEM_VIFIFO_RP),
READ_VREG(VLD_DECODE_CONTROL),
READ_VREG(VIFF_BIT_CNT),
READ_VREG(POWER_CTL_VLD),
READ_VREG(VLD_MEM_VIFIFO_START_PTR),
READ_VREG(VLD_MEM_VIFIFO_CURR_PTR),
READ_VREG(VLD_MEM_VIFIFO_CONTROL),
READ_VREG(VLD_MEM_VIFIFO_BUF_CNTL),
READ_VREG(VLD_MEM_VIFIFO_END_PTR));
}
}
static void vmjpeg_work(struct work_struct *work)
{
struct vdec_mjpeg_hw_s *hw = container_of(work,
struct vdec_mjpeg_hw_s, work);
if (debug_enable & 0x2)
pr_info("%s: result=%d,len=%d:%d\n",
__func__, hw->dec_result,
kfifo_len(&hw->newframe_q),
kfifo_len(&hw->display_q));
if (hw->dec_result == DEC_RESULT_DONE)
vdec_vframe_dirty(hw_to_vdec(hw), hw->chunk);
else if (hw->dec_result == DEC_RESULT_AGAIN) {
/*
stream base: stream buf empty or timeout
frame base: vdec_prepare_input fail
*/
if (!vdec_has_more_input(hw_to_vdec(hw))) {
hw->dec_result = DEC_RESULT_EOS;
vdec_schedule_work(&hw->work);
/*pr_info("%s: return\n",
__func__);*/
return;
}
} else if (hw->dec_result == DEC_RESULT_FORCE_EXIT) {
pr_info("%s: force exit\n",
__func__);
} else if (hw->dec_result == DEC_RESULT_EOS) {
/*pr_info("%s: end of stream\n",
__func__);*/
if (READ_VREG(VLD_MEM_VIFIFO_LEVEL) < 0x100)
hw->eos = 1;
vdec_vframe_dirty(hw_to_vdec(hw), hw->chunk);
}
amvdec_stop();
/* mark itself has all HW resource released and input released */
vdec_set_status(hw_to_vdec(hw), VDEC_STATUS_CONNECTED);
vdec_core_finish_run(hw_to_vdec(hw), CORE_MASK_VDEC_1
| CORE_MASK_HEVC);
del_timer_sync(&hw->check_timer);
hw->stat &= ~STAT_TIMER_ARM;
if (hw->vdec_cb)
hw->vdec_cb(hw_to_vdec(hw), hw->vdec_cb_arg);
}
static int amvdec_mjpeg_probe(struct platform_device *pdev)
{
struct vdec_s *pdata = *(struct vdec_s **)pdev->dev.platform_data;
struct vdec_mjpeg_hw_s *hw = NULL;
if (pdata == NULL) {
pr_info("amvdec_mjpeg memory resource undefined.\n");
return -EFAULT;
}
hw = (struct vdec_mjpeg_hw_s *)devm_kzalloc(&pdev->dev,
sizeof(struct vdec_mjpeg_hw_s), GFP_KERNEL);
if (hw == NULL) {
pr_info("\nammvdec_mjpeg device data allocation failed\n");
return -ENOMEM;
}
pdata->private = hw;
pdata->dec_status = vmjpeg_dec_status;
pdata->run = run;
pdata->run_ready = run_ready;
pdata->irq_handler = vmjpeg_isr;
if (pdata->use_vfm_path)
snprintf(pdata->vf_provider_name, VDEC_PROVIDER_NAME_SIZE,
VFM_DEC_PROVIDER_NAME);
else
snprintf(pdata->vf_provider_name, VDEC_PROVIDER_NAME_SIZE,
PROVIDER_NAME ".%02x", pdev->id & 0xff);
vf_provider_init(&pdata->vframe_provider, pdata->vf_provider_name,
&vf_provider_ops, pdata);
platform_set_drvdata(pdev, pdata);
hw->platform_dev = pdev;
if (pdata->sys_info)
hw->vmjpeg_amstream_dec_info = *pdata->sys_info;
if (vmjpeg_init(pdata) < 0) {
pr_info("amvdec_mjpeg init failed.\n");
return -ENODEV;
}
vdec_core_request(pdata, CORE_MASK_VDEC_1 | CORE_MASK_HEVC
| CORE_MASK_COMBINE);
return 0;
}
static int amvdec_mjpeg_remove(struct platform_device *pdev)
{
struct vdec_mjpeg_hw_s *hw =
(struct vdec_mjpeg_hw_s *)
(((struct vdec_s *)(platform_get_drvdata(pdev)))->private);
if (hw->stat & STAT_TIMER_ARM) {
del_timer_sync(&hw->check_timer);
hw->stat &= ~STAT_TIMER_ARM;
}
cancel_work_sync(&hw->work);
if (hw->mm_blk_handle) {
decoder_bmmu_box_free(hw->mm_blk_handle);
hw->mm_blk_handle = NULL;
}
vfree(hw->fw);
hw->fw = NULL;
vdec_core_release(hw_to_vdec(hw), CORE_MASK_VDEC_1 | CORE_MASK_HEVC);
vdec_set_status(hw_to_vdec(hw), VDEC_STATUS_DISCONNECTED);
return 0;
}
/****************************************/
static struct platform_driver amvdec_mjpeg_driver = {
.probe = amvdec_mjpeg_probe,
.remove = amvdec_mjpeg_remove,
#ifdef CONFIG_PM
.suspend = amvdec_suspend,
.resume = amvdec_resume,
#endif
.driver = {
.name = DRIVER_NAME,
}
};
static struct codec_profile_t amvdec_mjpeg_profile = {
.name = "mmjpeg",
.profile = ""
};
static int __init amvdec_mjpeg_driver_init_module(void)
{
if (platform_driver_register(&amvdec_mjpeg_driver)) {
pr_err("failed to register amvdec_mjpeg driver\n");
return -ENODEV;
}
vcodec_profile_register(&amvdec_mjpeg_profile);
return 0;
}
static void __exit amvdec_mjpeg_driver_remove_module(void)
{
platform_driver_unregister(&amvdec_mjpeg_driver);
}
/****************************************/
module_param(debug_enable, uint, 0664);
MODULE_PARM_DESC(debug_enable, "\n debug enable\n");
module_param(pre_decode_buf_level, int, 0664);
MODULE_PARM_DESC(pre_decode_buf_level,
"\n ammvdec_h264 pre_decode_buf_level\n");
module_init(amvdec_mjpeg_driver_init_module);
module_exit(amvdec_mjpeg_driver_remove_module);
MODULE_DESCRIPTION("AMLOGIC MJMPEG Video Decoder Driver");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Tim Yao <timyao@amlogic.com>");

View File

@@ -0,0 +1,2 @@
obj-$(CONFIG_AMLOGIC_MEDIA_VDEC_MPEG12) += amvdec_mpeg12.o
amvdec_mpeg12-objs += vmpeg12.o

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,26 @@
/*
* drivers/amlogic/amports/vmpeg12.h
*
* Copyright (C) 2015 Amlogic, Inc. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
*/
#ifndef VMPEG12_H
#define VMPEG12_H
/* /#if MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON6 */
/* TODO: move to register headers */
#define VPP_VD1_POSTBLEND (1 << 10)
/* /#endif */
#endif /* VMPEG12_H */

View File

@@ -0,0 +1,5 @@
obj-$(CONFIG_AMLOGIC_MEDIA_VDEC_MPEG4) += amvdec_mpeg4.o
amvdec_mpeg4-objs += vmpeg4.o
obj-$(CONFIG_AMLOGIC_MEDIA_VDEC_MPEG4_MULTI) += amvdec_mmpeg4.o
amvdec_mmpeg4-objs += vmpeg4_multi.o

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,26 @@
/*
* drivers/amlogic/amports/vmpeg4.h
*
* Copyright (C) 2015 Amlogic, Inc. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
*/
#ifndef VMPEG4_H
#define VMPEG4_H
/* /#if MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON6 */
/* TODO: move to register headers */
#define VPP_VD1_POSTBLEND (1 << 10)
/* /#endif */
#endif /* VMPEG4_H */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,2 @@
obj-$(CONFIG_AMLOGIC_MEDIA_VDEC_REAL) += amvdec_real.o
amvdec_real-objs += vreal.o

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,26 @@
/*
* drivers/amlogic/amports/vreal.h
*
* Copyright (C) 2015 Amlogic, Inc. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
*/
#ifndef VREAL_H
#define VREAL_H
#if 1 /* MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON6 */
/* TODO: move to register headers */
#define VPP_VD1_POSTBLEND (1 << 10)
#endif
#endif /* VREAL_H */

View File

@@ -0,0 +1,4 @@
obj-m += decoder_common.o
decoder_common-objs += utils.o vdec.o vdec_input.o amvdec.o
decoder_common-objs += decoder_mmu_box.o decoder_bmmu_box.o
decoder_common-objs += config_parser.o secprot.o vdec_profile.o

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,88 @@
/*
* drivers/amlogic/media/frame_provider/decoder/utils/amvdec.h
*
* Copyright (C) 2016 Amlogic, Inc. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
*/
#ifndef AMVDEC_H
#define AMVDEC_H
#include <linux/amlogic/media/utils/amports_config.h>
#include <linux/amlogic/media/utils/vformat.h>
#include "vdec.h"
#define UCODE_ALIGN 8
#define UCODE_ALIGN_MASK 7UL
struct amvdec_dec_reg_s {
unsigned long mem_start;
unsigned long mem_end;
struct device *cma_dev;
struct dec_sysinfo *dec_sysinfo;
}; /*amvdec_dec_reg_t */
struct vdec_s;
extern void amvdec_start(void);
extern void amvdec_stop(void);
extern void amvdec_enable(void);
extern void amvdec_disable(void);
s32 amvdec_loadmc_ex(enum vformat_e type, const char *name, char *def);
s32 amvdec_vdec_loadmc_ex(struct vdec_s *vdec, const char *name, 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);
s32 amhevc_vdec_loadmc_ex(struct vdec_s *vdec, const char *name, char *def);
s32 amvdec_vdec_loadmc_buf_ex(struct vdec_s *vdec, char *buf, int size);
extern void amhcodec_start(void);
extern void amhcodec_stop(void);
s32 amhcodec_loadmc(const u32 *p);
s32 amhcodec_loadmc_ex(enum vformat_e type, const char *name, char *def);
extern int amvdev_pause(void);
extern int amvdev_resume(void);
#ifdef CONFIG_PM
extern int amvdec_suspend(struct platform_device *dev, pm_message_t event);
extern int amvdec_resume(struct platform_device *dec);
extern int amhevc_suspend(struct platform_device *dev, pm_message_t event);
extern int amhevc_resume(struct platform_device *dec);
#endif
int amvdec_init(void);
void amvdec_exit(void);
#if 1 /* MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON6 */
#define AMVDEC_CLK_GATE_ON(a)
#define AMVDEC_CLK_GATE_OFF(a)
#else
#define AMVDEC_CLK_GATE_ON(a) CLK_GATE_ON(a)
#define AMVDEC_CLK_GATE_OFF(a) CLK_GATE_OFF(a)
#endif
/* TODO: move to register headers */
#define RESET_VCPU (1<<7)
#define RESET_CCPU (1<<8)
#endif /* AMVDEC_H */

View File

@@ -0,0 +1,64 @@
/*
* drivers/amlogic/amports/config_parser.c
*
* Copyright (C) 2015 Amlogic, Inc. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/errno.h>
#include "config_parser.h"
/*
*sample config:
*configs: width:1920;height:1080;
*need:width
*ok: return 0;
**val = value;
*/
int get_config_int(const char *configs, const char *need, int *val)
{
const char *str;
int ret;
int lval = 0;
*val = 0;
if (!configs || !need)
return -1;
str = strstr(configs, need);
if (str != NULL) {
if (str > configs && str[-1] != ';') {
/*
* if not the first config val.
* make sure before is ';'
* to recognize:
* ;crop_width:100
* ;width:100
*/
return -2;
}
str += strlen(need);
if (str[0] != ':' || str[1] == '\0')
return -3;
ret = sscanf(str, ":%d", &lval);
if (ret == 1) {
*val = lval;
return 0;
}
}
return -4;
}
EXPORT_SYMBOL(get_config_int);

View File

@@ -0,0 +1,21 @@
/*
* drivers/amlogic/amports/config_parser.c
*
* Copyright (C) 2015 Amlogic, Inc. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
*/
#ifndef CONFIG_PARSER_HHH_
#define CONFIG_PARSER_HHH_
int get_config_int(const char *configs, const char *need, int *val);
#endif/*CONFIG_PARSER_HHH_*/

View File

@@ -0,0 +1,536 @@
/*
* drivers/amlogic/amports/decoder/decoder_bmmu_box.c
*
* Copyright (C) 2015 Amlogic, Inc. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
*/
#define DEBUG
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/interrupt.h>
#include <linux/semaphore.h>
#include <linux/delay.h>
#include <linux/timer.h>
#include <linux/kfifo.h>
#include <linux/kthread.h>
#include <linux/slab.h>
#include <linux/amlogic/media/codec_mm/codec_mm_scatter.h>
#include <linux/platform_device.h>
#include <linux/amlogic/media/video_sink/video_keeper.h>
#include "decoder_bmmu_box.h"
#include <linux/amlogic/media/codec_mm/codec_mm.h>
struct decoder_bmmu_box {
int max_mm_num;
const char *name;
int channel_id;
struct mutex mutex;
struct list_head list;
int total_size;
int change_size_on_need_smaller;
int align2n; /*can overwite on idx alloc */
int mem_flags; /*can overwite on idx alloc */
struct codec_mm_s *mm_list[1];
};
struct decoder_bmmu_box_mgr {
int num;
struct mutex mutex;
struct list_head box_list;
};
static struct decoder_bmmu_box_mgr global_blk_mgr;
static struct decoder_bmmu_box_mgr *get_decoder_bmmu_box_mgr(void)
{
return &global_blk_mgr;
}
static int decoder_bmmu_box_mgr_add_box(struct decoder_bmmu_box *box)
{
struct decoder_bmmu_box_mgr *mgr = get_decoder_bmmu_box_mgr();
mutex_lock(&mgr->mutex);
list_add_tail(&box->list, &mgr->box_list);
mutex_unlock(&mgr->mutex);
return 0;
}
static int decoder_bmmu_box_mgr_del_box(struct decoder_bmmu_box *box)
{
struct decoder_bmmu_box_mgr *mgr = get_decoder_bmmu_box_mgr();
mutex_lock(&mgr->mutex);
list_del(&box->list);
mutex_unlock(&mgr->mutex);
return 0;
}
void *decoder_bmmu_box_alloc_box(const char *name,
int channel_id, int max_num,
int aligned, int mem_flags)
/*min_size_M:wait alloc this size*/
{
struct decoder_bmmu_box *box;
int size;
int tvp_flags;
tvp_flags = (mem_flags & CODEC_MM_FLAGS_TVP) ?
CODEC_MM_FLAGS_TVP : 0;
pr_debug("decoder_bmmu_box_alloc_box, tvp_flags = %x\n", tvp_flags);
size = sizeof(struct decoder_bmmu_box) + sizeof(struct codec_mm_s *) *
max_num;
box = kmalloc(size, GFP_KERNEL);
if (!box) {
pr_err("can't alloc decoder buffers box!!!\n");
return NULL;
}
memset(box, 0, size);
box->max_mm_num = max_num;
box->name = name;
box->channel_id = channel_id;
box->align2n = aligned;
box->mem_flags = mem_flags | tvp_flags;
mutex_init(&box->mutex);
INIT_LIST_HEAD(&box->list);
decoder_bmmu_box_mgr_add_box(box);
return (void *)box;
}
EXPORT_SYMBOL(decoder_bmmu_box_alloc_box);
int decoder_bmmu_box_alloc_idx(void *handle, int idx, int size, int aligned_2n,
int mem_flags)
/*align& flags if -1 user box default.*/
{
struct decoder_bmmu_box *box = handle;
struct codec_mm_s *mm;
int align = aligned_2n;
int memflags = mem_flags;
if (!box || idx < 0 || idx >= box->max_mm_num) {
pr_err("can't alloc mmu box(%p),idx:%d\n",
box, idx);
return -1;
}
if (align == -1)
align = box->align2n;
if (memflags == -1)
memflags = box->mem_flags;
mutex_lock(&box->mutex);
mm = box->mm_list[idx];
if (mm) {
int invalid = 0;
if (mm->page_count * PAGE_SIZE < size) {
/*size is small. */
invalid = 1;
} else if (box->change_size_on_need_smaller &&
(mm->buffer_size > (size << 1))) {
/*size is too large. */
invalid = 2;
} else if (mm->phy_addr & ((1 << align) - 1)) {
/*addr is not align */
invalid = 4;
}
if (invalid) {
box->total_size -= mm->buffer_size;
codec_mm_release(mm, box->name);
box->mm_list[idx] = NULL;
mm = NULL;
}
}
if (!mm) {
mm = codec_mm_alloc(box->name, size, align, memflags);
if (mm) {
box->mm_list[idx] = mm;
box->total_size += mm->buffer_size;
mm->ins_id = box->channel_id;
mm->ins_buffer_id = idx;
}
}
mutex_unlock(&box->mutex);
return mm ? 0 : -ENOMEM;
}
int decoder_bmmu_box_free_idx(void *handle, int idx)
{
struct decoder_bmmu_box *box = handle;
struct codec_mm_s *mm;
if (!box || idx < 0 || idx >= box->max_mm_num) {
pr_err("can't free idx of box(%p),idx:%d in (%d-%d)\n",
box, idx, 0,
box->max_mm_num - 1);
return -1;
}
mutex_lock(&box->mutex);
mm = box->mm_list[idx];
if (mm) {
box->total_size -= mm->buffer_size;
codec_mm_release(mm, box->name);
box->mm_list[idx] = NULL;
mm = NULL;
}
mutex_unlock(&box->mutex);
return 0;
}
EXPORT_SYMBOL(decoder_bmmu_box_free_idx);
int decoder_bmmu_box_free(void *handle)
{
struct decoder_bmmu_box *box = handle;
struct codec_mm_s *mm;
int i;
if (!box) {
pr_err("can't free box of NULL box!\n");
return -1;
}
mutex_lock(&box->mutex);
for (i = 0; i < box->max_mm_num; i++) {
mm = box->mm_list[i];
if (mm) {
codec_mm_release(mm, box->name);
box->mm_list[i] = NULL;
}
}
mutex_unlock(&box->mutex);
decoder_bmmu_box_mgr_del_box(box);
kfree(box);
return 0;
}
EXPORT_SYMBOL(decoder_bmmu_box_free);
void *decoder_bmmu_box_get_mem_handle(void *box_handle, int idx)
{
struct decoder_bmmu_box *box = box_handle;
if (!box || idx < 0 || idx >= box->max_mm_num)
return NULL;
return box->mm_list[idx];
}
EXPORT_SYMBOL(decoder_bmmu_box_get_mem_handle);
int decoder_bmmu_box_get_mem_size(void *box_handle, int idx)
{
struct decoder_bmmu_box *box = box_handle;
int size = 0;
if (!box || idx < 0 || idx >= box->max_mm_num)
return 0;
mutex_lock(&box->mutex);
if (box->mm_list[idx] != NULL)
size = box->mm_list[idx]->buffer_size;
mutex_unlock(&box->mutex);
return size;
}
unsigned long decoder_bmmu_box_get_phy_addr(void *box_handle, int idx)
{
struct decoder_bmmu_box *box = box_handle;
struct codec_mm_s *mm;
if (!box || idx < 0 || idx >= box->max_mm_num)
return 0;
mm = box->mm_list[idx];
if (!mm)
return 0;
return mm->phy_addr;
}
EXPORT_SYMBOL(decoder_bmmu_box_get_phy_addr);
void *decoder_bmmu_box_get_virt_addr(void *box_handle, int idx)
{
struct decoder_bmmu_box *box = box_handle;
struct codec_mm_s *mm;
if (!box || idx < 0 || idx >= box->max_mm_num)
return NULL;
mm = box->mm_list[idx];
if (!mm)
return 0;
return codec_mm_phys_to_virt(mm->phy_addr);
}
/*flags: &0x1 for wait,*/
int decoder_bmmu_box_check_and_wait_size(int size, int flags)
{
if ((flags & BMMU_ALLOC_FLAGS_CAN_CLEAR_KEEPER) &&
codec_mm_get_free_size() < size) {
pr_err("CMA force free keep,for size = %d\n", size);
/*need free others?
*/
try_free_keep_video(1);
}
return codec_mm_enough_for_size(size,
flags & BMMU_ALLOC_FLAGS_WAIT);
}
int decoder_bmmu_box_alloc_idx_wait(
void *handle, int idx,
int size, int aligned_2n,
int mem_flags,
int wait_flags)
{
int have_space;
int ret = -1;
if (decoder_bmmu_box_get_mem_size(handle, idx) >= size)
return 0;/*have alloced memery before.*/
have_space = decoder_bmmu_box_check_and_wait_size(
size,
wait_flags);
if (have_space) {
ret = decoder_bmmu_box_alloc_idx(handle,
idx, size, aligned_2n, mem_flags);
} else {
try_free_keep_video(1);
ret = -ENOMEM;
}
return ret;
}
EXPORT_SYMBOL(decoder_bmmu_box_alloc_idx_wait);
int decoder_bmmu_box_alloc_buf_phy(
void *handle, int idx,
int size, unsigned char *driver_name,
unsigned long *buf_phy_addr)
{
if (!decoder_bmmu_box_check_and_wait_size(
size,
1)) {
pr_info("%s not enough buf for buf_idx = %d\n",
driver_name, idx);
return -ENOMEM;
}
if (!decoder_bmmu_box_alloc_idx_wait(
handle,
idx,
size,
-1,
-1,
BMMU_ALLOC_FLAGS_WAITCLEAR
)) {
*buf_phy_addr =
decoder_bmmu_box_get_phy_addr(
handle,
idx);
/*
*pr_info("%s malloc buf_idx = %d addr = %ld size = %d\n",
* driver_name, idx, *buf_phy_addr, size);
*/
} else {
pr_info("%s malloc failed %d\n", driver_name, idx);
return -ENOMEM;
}
return 0;
}
EXPORT_SYMBOL(decoder_bmmu_box_alloc_buf_phy);
static int decoder_bmmu_box_dump(struct decoder_bmmu_box *box, void *buf,
int size)
{
char *pbuf = buf;
char sbuf[512];
int tsize = 0;
int s;
int i;
if (!buf) {
pbuf = sbuf;
size = 100000;
}
#define BUFPRINT(args...) \
do {\
s = snprintf(pbuf, size - tsize, args);\
tsize += s;\
pbuf += s; \
} while (0)
for (i = 0; i < box->max_mm_num; i++) {
struct codec_mm_s *mm = box->mm_list[i];
if (buf && (size - tsize) < 256) {
BUFPRINT("\n\t**NOT END**\n");
break;
}
if (mm) {
BUFPRINT("code mem[%d]:%p, addr=%p, size=%d,from=%d\n",
i,
(void *)mm,
(void *)mm->phy_addr,
mm->buffer_size,
mm->from_flags);
if (!buf) {
pr_info("%s", sbuf);
pbuf = sbuf;
}
}
}
#undef BUFPRINT
return tsize;
}
static int decoder_bmmu_box_dump_all(void *buf, int size)
{
struct decoder_bmmu_box_mgr *mgr = get_decoder_bmmu_box_mgr();
char *pbuf = buf;
char sbuf[512];
int tsize = 0;
int s;
int i;
struct list_head *head, *list;
if (!buf) {
pbuf = sbuf;
size = 100000;
}
#define BUFPRINT(args...) \
do {\
s = snprintf(pbuf, size - tsize, args);\
tsize += s;\
pbuf += s; \
} while (0)
mutex_lock(&mgr->mutex);
head = &mgr->box_list;
list = head->next;
i = 0;
while (list != head) {
struct decoder_bmmu_box *box;
box = list_entry(list, struct decoder_bmmu_box, list);
BUFPRINT("box[%d]: %s, %splayer_id:%d, max_num:%d, size:%d\n",
i, box->name,
(box->mem_flags & CODEC_MM_FLAGS_TVP) ?
"TVP mode " : "",
box->channel_id,
box->max_mm_num,
box->total_size);
if (buf) {
s = decoder_bmmu_box_dump(box, pbuf, size - tsize);
if (s > 0) {
tsize += s;
pbuf += s;
}
} else {
pr_info("%s", sbuf);
pbuf = sbuf;
tsize += decoder_bmmu_box_dump(box, NULL, 0);
}
list = list->next;
i++;
}
mutex_unlock(&mgr->mutex);
#undef BUFPRINT
if (!buf)
pr_info("%s", sbuf);
return tsize;
}
static ssize_t box_dump_show(struct class *class, struct class_attribute *attr,
char *buf)
{
ssize_t ret = 0;
ret = decoder_bmmu_box_dump_all(buf, PAGE_SIZE);
return ret;
}
static ssize_t box_debug_show(struct class *class,
struct class_attribute *attr,
char *buf)
{
ssize_t size = 0;
size += sprintf(buf, "box debug help:\n");
size += sprintf(buf + size, "echo n > debug\n");
size += sprintf(buf + size, "n==0: clear all debugs)\n");
size += sprintf(buf + size,
"n=1: dump all box\n");
return size;
}
static ssize_t box_debug_store(struct class *class,
struct class_attribute *attr,
const char *buf, size_t size)
{
unsigned val;
ssize_t ret;
val = -1;
ret = sscanf(buf, "%d", &val);
if (ret != 1)
return -EINVAL;
switch (val) {
case 1:
decoder_bmmu_box_dump_all(NULL , 0);
break;
default:
pr_err("unknow cmd! %d\n", val);
}
return size;
}
static struct class_attribute decoder_bmmu_box_class_attrs[] = {
__ATTR_RO(box_dump),
__ATTR(debug, S_IRUGO | S_IWUSR | S_IWGRP,
box_debug_show, box_debug_store),
__ATTR_NULL
};
static struct class decoder_bmmu_box_class = {
.name = "decoder_bmmu_box",
.class_attrs = decoder_bmmu_box_class_attrs,
};
int decoder_bmmu_box_init(void)
{
int r;
memset(&global_blk_mgr, 0, sizeof(global_blk_mgr));
INIT_LIST_HEAD(&global_blk_mgr.box_list);
mutex_init(&global_blk_mgr.mutex);
r = class_register(&decoder_bmmu_box_class);
return r;
}
EXPORT_SYMBOL(decoder_bmmu_box_init);
void decoder_bmmu_box_exit(void)
{
class_unregister(&decoder_bmmu_box_class);
pr_info("dec bmmu box exit.\n");
}
#if 0
static int __init decoder_bmmu_box_init(void)
{
int r;
memset(&global_blk_mgr, 0, sizeof(global_blk_mgr));
INIT_LIST_HEAD(&global_blk_mgr.box_list);
mutex_init(&global_blk_mgr.mutex);
r = class_register(&decoder_bmmu_box_class);
return r;
}
module_init(decoder_bmmu_box_init);
#endif

View File

@@ -0,0 +1,67 @@
/*
* drivers/amlogic/amports/decoder/decoder_bmmu_box.h
*
* Copyright (C) 2015 Amlogic, Inc. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
*/
#ifndef DECODER_BLOCK_BUFFER_BOX
#define DECODER_BLOCK_BUFFER_BOX
void *decoder_bmmu_box_alloc_box(const char *name,
int channel_id,
int max_num,
int aligned,
int mem_flags);
int decoder_bmmu_box_alloc_idx(
void *handle, int idx, int size,
int aligned_2n, int mem_flags);
int decoder_bmmu_box_free_idx(void *handle, int idx);
int decoder_bmmu_box_free(void *handle);
void *decoder_bmmu_box_get_mem_handle(
void *box_handle, int idx);
unsigned long decoder_bmmu_box_get_phy_addr(
void *box_handle, int idx);
void *decoder_bmmu_box_get_virt_addr(
void *box_handle, int idx);
/*flags: &0x1 for wait,*/
int decoder_bmmu_box_check_and_wait_size(
int size, int flags);
int decoder_bmmu_box_alloc_buf_phy(
void *handle, int idx,
int size, unsigned char *driver_name,
unsigned long *buf_phy_addr);
#define BMMU_ALLOC_FLAGS_WAIT (1 << 0)
#define BMMU_ALLOC_FLAGS_CAN_CLEAR_KEEPER (1 << 1)
#define BMMU_ALLOC_FLAGS_WAITCLEAR \
(BMMU_ALLOC_FLAGS_WAIT |\
BMMU_ALLOC_FLAGS_CAN_CLEAR_KEEPER)
int decoder_bmmu_box_alloc_idx_wait(
void *handle, int idx,
int size, int aligned_2n,
int mem_flags,
int wait_flags);
int decoder_bmmu_box_init(void);
void decoder_bmmu_box_exit(void);
#endif

View File

@@ -0,0 +1,395 @@
/*
* 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;
int tvp_mode;
struct mutex mutex;
struct list_head list;
struct codec_mm_scatter *sc_list[1];
};
#define MAX_KEEP_FRAME 4
#define START_KEEP_ID 0x9
#define MAX_KEEP_ID (INT_MAX - 1)
struct decoder_mmu_box_mgr {
int num;
struct mutex mutex;
struct codec_mm_scatter *keep_sc[MAX_KEEP_FRAME];
int keep_id[MAX_KEEP_FRAME];
int next_id;/*id for keep & free.*/
struct list_head box_list;
};
static struct decoder_mmu_box_mgr global_mgr;
static struct decoder_mmu_box_mgr *get_decoder_mmu_box_mgr(void)
{
return &global_mgr;
}
static int decoder_mmu_box_mgr_add_box(struct decoder_mmu_box *box)
{
struct decoder_mmu_box_mgr *mgr = get_decoder_mmu_box_mgr();
mutex_lock(&mgr->mutex);
list_add_tail(&box->list, &mgr->box_list);
mutex_unlock(&mgr->mutex);
return 0;
}
static int decoder_mmu_box_mgr_del_box(struct decoder_mmu_box *box)
{
struct decoder_mmu_box_mgr *mgr = get_decoder_mmu_box_mgr();
mutex_lock(&mgr->mutex);
list_del(&box->list);
mutex_unlock(&mgr->mutex);
return 0;
}
void *decoder_mmu_box_alloc_box(const char *name,
int channel_id,
int max_num,
int min_size_M,
int mem_flags)
/*min_size_M:wait alloc this size*/
{
struct decoder_mmu_box *box;
int size;
pr_debug("decoder_mmu_box_alloc_box, mem_flags = 0x%x\n", mem_flags);
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;
box->tvp_mode = mem_flags;
mutex_init(&box->mutex);
INIT_LIST_HEAD(&box->list);
decoder_mmu_box_mgr_add_box(box);
codec_mm_scatter_mgt_delay_free_swith(1, 2000,
min_size_M, box->tvp_mode);
return (void *)box;
}
EXPORT_SYMBOL(decoder_mmu_box_alloc_box);
int decoder_mmu_box_alloc_idx(
void *handle, int idx, int num_pages,
unsigned int *mmu_index_adr)
{
struct decoder_mmu_box *box = handle;
struct codec_mm_scatter *sc;
int ret;
int i;
if (!box || idx < 0 || idx >= box->max_sc_num) {
pr_err("can't alloc mmu box(%p),idx:%d\n",
box, idx);
return -1;
}
mutex_lock(&box->mutex);
sc = box->sc_list[idx];
if (sc) {
if (sc->page_max_cnt >= num_pages)
ret = codec_mm_scatter_alloc_want_pages(sc,
num_pages);
else {
codec_mm_scatter_dec_owner_user(sc, 0);
box->sc_list[idx] = NULL;
sc = NULL;
}
}
if (!sc) {
sc = codec_mm_scatter_alloc(num_pages + 64, num_pages,
box->tvp_mode);
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);
codec_mm_scatter_mgt_delay_free_swith(0, 2000, 0, box->tvp_mode);
kfree(box);
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 (!buf) {
pbuf = sbuf;
size = 100000;
}
#define BUFPRINT(args...) \
do {\
s = snprintf(pbuf, size - tsize, 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;
size = 100000;
}
#define BUFPRINT(args...) \
do {\
s = snprintf(pbuf, size - tsize, 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, %splayer_id:%d, max_num:%d\n",
i,
box->name,
box->tvp_mode ? "TVP mode " : "",
box->channel_id,
box->max_sc_num);
if (buf) {
s += decoder_mmu_box_dump(box, pbuf, size - tsize);
if (s > 0) {
tsize += s;
pbuf += s;
}
} else {
pr_info("%s", sbuf);
pbuf = sbuf;
tsize += decoder_mmu_box_dump(box, NULL, 0);
}
list = list->next;
i++;
}
mutex_unlock(&mgr->mutex);
#undef BUFPRINT
if (!buf)
pr_info("%s", sbuf);
return tsize;
}
static ssize_t
box_dump_show(struct class *class,
struct class_attribute *attr, char *buf)
{
ssize_t ret = 0;
ret = decoder_mmu_box_dump_all(buf, PAGE_SIZE);
return ret;
}
static struct class_attribute decoder_mmu_box_class_attrs[] = {
__ATTR_RO(box_dump),
__ATTR_NULL
};
static struct class decoder_mmu_box_class = {
.name = "decoder_mmu_box",
.class_attrs = decoder_mmu_box_class_attrs,
};
int decoder_mmu_box_init(void)
{
int r;
memset(&global_mgr, 0, sizeof(global_mgr));
INIT_LIST_HEAD(&global_mgr.box_list);
mutex_init(&global_mgr.mutex);
global_mgr.next_id = START_KEEP_ID;
r = class_register(&decoder_mmu_box_class);
return r;
}
EXPORT_SYMBOL(decoder_mmu_box_init);
void decoder_mmu_box_exit(void)
{
class_unregister(&decoder_mmu_box_class);
pr_info("dec mmu box exit.\n");
}
#if 0
static int __init decoder_mmu_box_init(void)
{
int r;
memset(&global_mgr, 0, sizeof(global_mgr));
INIT_LIST_HEAD(&global_mgr.box_list);
mutex_init(&global_mgr.mutex);
global_mgr.next_id = START_KEEP_ID;
r = class_register(&decoder_mmu_box_class);
return r;
}
module_init(decoder_mmu_box_init);
#endif

View File

@@ -0,0 +1,46 @@
/*
* drivers/amlogic/media/frame_provider/decoder/utils/decoder_mmu_box.h
*
* Copyright (C) 2016 Amlogic, Inc. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
*/
#ifndef DECODER_BUFFER_BOX
#define DECODER_BUFFER_BOX
void *decoder_mmu_box_alloc_box(const char *name,
int channel_id,
int max_num,
int min_size_M,
int mem_flags);
int decoder_mmu_box_alloc_idx(
void *handle, int idx, int num_pages,
unsigned int *mmu_index_adr);
int decoder_mmu_box_free_idx_tail(void *handle, int idx,
int start_release_index);
int decoder_mmu_box_free_idx(void *handle, int idx);
int decoder_mmu_box_free(void *handle);
int decoder_mmu_box_move_keep_idx(void *box_handle,
int keep_idx);
int decoder_mmu_box_free_keep(int keep_id);
int decoder_mmu_box_free_all_keep(void);
void *decoder_mmu_box_get_mem_handle(void *box_handle, int idx);
int decoder_mmu_box_init(void);
void decoder_mmu_box_exit(void);
#endif

View File

@@ -0,0 +1,21 @@
#ifndef __VIDEO_FIRMWARE_HEADER_
#define __VIDEO_FIRMWARE_HEADER_
#include "../../../common/firmware/firmware_type.h"
#include <linux/amlogic/media/utils/vformat.h>
#define FW_LOAD_FORCE (0x1)
#define FW_LOAD_TRY (0X2)
struct firmware_s {
unsigned int len;
char data[0];
};
extern int get_decoder_firmware_data(enum vformat_e type,
const char *file_name, char *buf, int size);
extern int get_data_from_name(const char *name, char *buf);
extern int get_firmware_data(unsigned int foramt, char *buf);
extern int video_fw_reload(int mode);
#endif

View File

@@ -0,0 +1,43 @@
/*
* drivers/amlogic/amports/arch/secprot.c
*
* Copyright (C) 2017 Amlogic, Inc. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
*/
#include "secprot.h"
int tee_config_device_secure(int dev_id, int secure)
{
int ret = 0;
register unsigned x0 asm("x0");
register unsigned x1 asm("x1");
register unsigned x2 asm("x2");
x0 = OPTEE_SMC_CONFIG_DEVICE_SECURE;
x1 = dev_id;
x2 = secure;
asm volatile(
__asmeq("%0", "x0")
__asmeq("%1", "x0")
__asmeq("%2", "x1")
__asmeq("%3", "x2")
"smc #0\n"
: "=r"(x0)
: "r"(x0), "r"(x1), "r"(x2));
ret = x0;
return ret;
}

View File

@@ -0,0 +1,39 @@
/*
* drivers/amlogic/amports/arch/secprot.h
*
* Copyright (C) 2017 Amlogic, Inc. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
*/
#ifndef __SECPROT_H_
#define __SECPROT_H_
#define DMC_DEV_TYPE_NON_SECURE 0
#define DMC_DEV_TYPE_SECURE 1
#define DMC_DEV_ID_GPU 1
#define DMC_DEV_ID_HEVC 4
#define DMC_DEV_ID_PARSER 7
#define DMC_DEV_ID_VPU 8
#define DMC_DEV_ID_VDEC 13
#define DMC_DEV_ID_HCODEC 14
#define DMC_DEV_ID_GE2D 15
#define OPTEE_SMC_CONFIG_DEVICE_SECURE 0xb200000e
#define __asmeq(x, y) ".ifnc " x "," y " ; .err ; .endif\n\t"
extern int tee_config_device_secure(int dev_id, int secure);
#endif /* __SECPROT_H_ */

View File

@@ -0,0 +1,71 @@
/*
* drivers/amlogic/media/frame_provider/decoder/utils/utils.c
*
* Copyright (C) 2016 Amlogic, Inc. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
*/
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/delay.h>
#include <linux/kthread.h>
#include <linux/platform_device.h>
#include <linux/uaccess.h>
#include <linux/semaphore.h>
#include <linux/sched/rt.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/slab.h>
#include "vdec.h"
#include "vdec_input.h"
#include <linux/amlogic/media/utils/vdec_reg.h>
#include "amvdec.h"
#include "decoder_mmu_box.h"
#include "decoder_bmmu_box.h"
#include "vdec_profile.h"
static int __init decoder_common_init(void)
{
/*vdec init.*/
vdec_module_init();
/*amvdec init.*/
amvdec_init();
/*mmu box init.*/
decoder_mmu_box_init();/*exit?*/
decoder_bmmu_box_init();
vdec_profile_init_debugfs();
return 0;
}
static void __exit decoder_common_exit(void)
{
/*vdec exit.*/
vdec_module_exit();
/*amvdec exit.*/
amvdec_exit();
decoder_mmu_box_exit();
decoder_bmmu_box_exit();
vdec_profile_exit_debugfs();
}
module_init(decoder_common_init);
module_exit(decoder_common_exit);

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,420 @@
/*
* 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>
/*#define CONFIG_AM_VDEC_DV*/
#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 VDEC_DEBUG_SUPPORT
#define DEC_FLAG_HEVC_WORKAROUND 0x01
#define VDEC_FIFO_ALIGN 8
enum vdec_type_e {
VDEC_1 = 0,
VDEC_HCODEC,
VDEC_2,
VDEC_HEVC,
VDEC_HEVCB,
VDEC_MAX
};
#define CORE_MASK_VDEC_1 (1 << VDEC_1)
#define CORE_MASK_HCODEC (1 << VDEC_HCODEC)
#define CORE_MASK_VDEC_2 (1 << VDEC_2)
#define CORE_MASK_HEVC (1 << VDEC_HEVC)
#define CORE_MASK_HEVC_FRONT (1 << VDEC_HEVC)
#define CORE_MASK_HEVC_BACK (1 << VDEC_HEVCB)
#define CORE_MASK_COMBINE (1UL << 31)
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);
extern void vdec_power_reset(void);
/*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_HEVC_BACK,
VDEC_IRQ_MAX,
};
enum vdec_fr_hint_state {
VDEC_NO_NEED_HINT = 0,
VDEC_NEED_HINT,
VDEC_HINTED,
};
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);
extern void dma_contiguous_early_fixup(phys_addr_t base, unsigned long size);
unsigned int get_vdec_clk_config_settings(void);
void update_vdec_clk_config_settings(unsigned int config);
//unsigned int get_mmu_mode(void);//DEBUG_TMP
struct vdec_s;
enum vformat_t;
/* stream based with single instance decoder driver */
#define VDEC_TYPE_SINGLE 0
/* stream based with multi-instance decoder with HW resouce sharing */
#define VDEC_TYPE_STREAM_PARSER 1
/* frame based with multi-instance decoder, input block list based */
#define VDEC_TYPE_FRAME_BLOCK 2
/* frame based with multi-instance decoder, single circular input block */
#define VDEC_TYPE_FRAME_CIRCULAR 3
/* decoder status: uninitialized */
#define VDEC_STATUS_UNINITIALIZED 0
/* decoder status: before the decoder can start consuming data */
#define VDEC_STATUS_DISCONNECTED 1
/* decoder status: decoder should become disconnected once it's not active */
#define VDEC_STATUS_CONNECTED 2
/* decoder status: decoder owns HW resource and is running */
#define VDEC_STATUS_ACTIVE 3
#define VDEC_PROVIDER_NAME_SIZE 16
#define VDEC_RECEIVER_NAME_SIZE 16
#define VDEC_MAP_NAME_SIZE 45
#define VDEC_FLAG_OTHER_INPUT_CONTEXT 0x0
#define VDEC_FLAG_SELF_INPUT_CONTEXT 0x01
#define VDEC_NEED_MORE_DATA_RUN 0x01
#define VDEC_NEED_MORE_DATA_DIRTY 0x02
#define VDEC_NEED_MORE_DATA 0x04
struct vdec_s {
u32 magic;
struct list_head list;
unsigned long core_mask;
unsigned long active_mask;
unsigned long sched_mask;
int id;
struct vdec_s *master;
struct vdec_s *slave;
struct stream_port_s *port;
int status;
int next_status;
int type;
int port_flag;
int format;
u32 pts;
u64 pts64;
bool pts_valid;
int flag;
int sched;
int need_more_data;
struct completion inactive_done;
/* config (temp) */
unsigned long mem_start;
unsigned long mem_end;
void *mm_blk_handle;
struct device *cma_dev;
struct platform_device *dev;
struct dec_sysinfo sys_info_store;
struct dec_sysinfo *sys_info;
/* input */
struct vdec_input_s input;
/* mc cache */
u32 mc[4096 * 4];
bool mc_loaded;
/* frame provider/receiver interface */
char vf_provider_name[VDEC_PROVIDER_NAME_SIZE];
struct vframe_provider_s vframe_provider;
char *vf_receiver_name;
char vfm_map_id[VDEC_MAP_NAME_SIZE];
char vfm_map_chain[VDEC_MAP_NAME_SIZE];
int vf_receiver_inst;
enum FRAME_BASE_VIDEO_PATH frame_base_video_path;
enum vdec_fr_hint_state fr_hint_state;
bool use_vfm_path;
char config[PAGE_SIZE];
int config_len;
bool is_reset;
bool dolby_meta_with_el;
/* canvas */
int (*get_canvas)(unsigned int index, unsigned int base);
int (*dec_status)(struct vdec_s *vdec, struct vdec_info *vstatus);
int (*set_trickmode)(struct vdec_s *vdec, unsigned long trickmode);
int (*set_isreset)(struct vdec_s *vdec, int isreset);
unsigned long (*run_ready)(struct vdec_s *vdec, unsigned long mask);
void (*run)(struct vdec_s *vdec, unsigned long mask,
void (*callback)(struct vdec_s *, void *), void *);
void (*reset)(struct vdec_s *vdec);
void (*dump_state)(struct vdec_s *vdec);
irqreturn_t (*irq_handler)(struct vdec_s *vdec, int irq);
irqreturn_t (*threaded_irq_handler)(struct vdec_s *vdec, int irq);
int (*user_data_read)(struct vdec_s *vdec,
struct userdata_param_t *puserdata_para);
void (*reset_userdata_fifo)(struct vdec_s *vdec, int bInit);
/* private */
void *private; /* decoder per instance specific data */
#ifdef VDEC_DEBUG_SUPPORT
u64 profile_start_clk[VDEC_MAX];
u64 total_clk[VDEC_MAX];
u32 check_count[VDEC_MAX];
u32 not_run_ready_count[VDEC_MAX];
u32 input_underrun_count[VDEC_MAX];
u32 run_count[VDEC_MAX];
u64 run_clk[VDEC_MAX];
u64 start_run_clk[VDEC_MAX];
#endif
atomic_t inirq_thread_flag;
atomic_t inirq_flag;
};
/* common decoder vframe provider name to use default vfm path */
#define VFM_DEC_PROVIDER_NAME "decoder"
#define VFM_DEC_DVBL_PROVIDER_NAME "dvbldec"
#define VFM_DEC_DVEL_PROVIDER_NAME "dveldec"
#define hw_to_vdec(hw) ((struct vdec_s *) \
(platform_get_drvdata(hw->platform_dev)))
#define canvas_y(canvas) ((canvas) & 0xff)
#define canvas_u(canvas) (((canvas) >> 8) & 0xff)
#define canvas_v(canvas) (((canvas) >> 16) & 0xff)
#define canvas_y2(canvas) (((canvas) >> 16) & 0xff)
#define canvas_u2(canvas) (((canvas) >> 24) & 0xff)
#define vdec_frame_based(vdec) \
(((vdec)->type == VDEC_TYPE_FRAME_BLOCK) || \
((vdec)->type == VDEC_TYPE_FRAME_CIRCULAR))
#define vdec_stream_based(vdec) \
(((vdec)->type == VDEC_TYPE_STREAM_PARSER) || \
((vdec)->type == VDEC_TYPE_SINGLE))
#define vdec_single(vdec) \
((vdec)->type == VDEC_TYPE_SINGLE)
#define vdec_dual(vdec) \
(((vdec)->port->type & PORT_TYPE_DUALDEC) ||\
(vdec_get_debug_flags() & 0x100))
#define vdec_secure(vdec) \
(((vdec)->port_flag & PORT_FLAG_DRM))
/* construct vdec strcture */
extern struct vdec_s *vdec_create(struct stream_port_s *port,
struct vdec_s *master);
/* set video format */
extern int vdec_set_format(struct vdec_s *vdec, int format);
/* set PTS */
extern int vdec_set_pts(struct vdec_s *vdec, u32 pts);
extern int vdec_set_pts64(struct vdec_s *vdec, u64 pts64);
/* set vfm map when use frame base decoder */
extern int vdec_set_video_path(struct vdec_s *vdec, int video_path);
/* set receive id when receive is ionvideo or amlvideo */
extern int vdec_set_receive_id(struct vdec_s *vdec, int receive_id);
/* 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);
/* sync decoder input */
extern int vdec_sync_input(struct vdec_s *vdec);
/* enable decoder input */
extern void vdec_enable_input(struct vdec_s *vdec);
/* set decoder input prepare level */
extern void vdec_set_prepare_level(struct vdec_s *vdec, int level);
/* set vdec input */
extern int vdec_set_input_buffer(struct vdec_s *vdec, u32 start, u32 size);
/* check if decoder can get more input */
extern bool vdec_has_more_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_info *vstatus);
extern int vdec_set_trickmode(struct vdec_s *vdec, unsigned long trickmode);
extern int vdec_set_isreset(struct vdec_s *vdec, int isreset);
extern int vdec_set_dv_metawithel(struct vdec_s *vdec, int isdvmetawithel);
extern void vdec_set_no_powerdown(int flag);
extern int vdec_is_support_4k(void);
extern void vdec_set_flag(struct vdec_s *vdec, u32 flag);
extern void vdec_set_eos(struct vdec_s *vdec, bool eos);
extern void vdec_set_next_sched(struct vdec_s *vdec, struct vdec_s *next_vdec);
extern const char *vdec_status_str(struct vdec_s *vdec);
extern const char *vdec_type_str(struct vdec_s *vdec);
extern const char *vdec_device_name_str(struct vdec_s *vdec);
extern void vdec_schedule_work(struct work_struct *work);
extern void vdec_count_info(struct vdec_info *vs, unsigned int err,
unsigned int offset);
extern bool vdec_need_more_data(struct vdec_s *vdec);
extern void hevc_reset_core(struct vdec_s *vdec);
extern void vdec_set_suspend_clk(int mode, int hevc);
extern unsigned long vdec_ready_to_run(struct vdec_s *vdec, unsigned long mask);
extern void vdec_prepare_run(struct vdec_s *vdec, unsigned long mask);
extern int vdec_core_request(struct vdec_s *vdec, unsigned long mask);
extern int vdec_core_release(struct vdec_s *vdec, unsigned long mask);
extern const bool vdec_core_with_input(unsigned long mask);
extern void vdec_core_finish_run(struct vdec_s *vdec, unsigned long mask);
#ifdef VDEC_DEBUG_SUPPORT
extern void vdec_set_step_mode(void);
#endif
int vdec_read_user_data(struct vdec_s *vdec,
struct userdata_param_t *p_userdata_param);
int vdec_wakeup_userdata_poll(struct vdec_s *vdec);
void vdec_reset_userdata_fifo(struct vdec_s *vdec, int bInit);
#ifdef VDEC_DEBUG_SUPPORT
extern void vdec_set_step_mode(void);
#endif
int vdec_get_debug_flags(void);
unsigned char is_mult_inc(unsigned int);
#endif /* VDEC_H */

View File

@@ -0,0 +1,940 @@
/*
* 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 (512 * SZ_1K)/*512 for 1080p default init.*/
#define VFRAME_BLOCK_SIZE_4K (2 * SZ_1M) /*2M for 4K default.*/
#define VFRAME_BLOCK_SIZE_MAX (4 * SZ_1M)
#define VFRAME_BLOCK_PAGEALIGN 4
#define VFRAME_BLOCK_MIN_LEVEL (2 * SZ_1M)
#define VFRAME_BLOCK_MAX_LEVEL (8 * SZ_1M)
#define VFRAME_BLOCK_MAX_TOTAL_SIZE (16 * SZ_1M)
/*
2s for OMX
*/
#define MAX_FRAME_DURATION_S 2
#define VFRAME_BLOCK_HOLE (SZ_64K)
#define MIN_FRAME_PADDING_SIZE ((u32)(L1_CACHE_BYTES))
#define EXTRA_PADDING_SIZE (16 * SZ_1K) /*HEVC_PADDING_SIZE*/
#define MEM_NAME "VFRAME_INPUT"
static int vdec_input_get_duration_u64(struct vdec_input_s *input);
static struct vframe_block_list_s *
vdec_input_alloc_new_block(struct vdec_input_s *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;
int total_size = count + chunk->pading_size;
if (block->type == VDEC_TYPE_FRAME_BLOCK) {
if (copy_from_user(p, buf, count))
return -EFAULT;
p += count;
memset(p, 0, chunk->pading_size);
dma_sync_single_for_device(get_vdec_device(),
block->start + block->wp,
total_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, chunk->pading_size);
memset(p, 0, len);
dma_sync_single_for_device(get_vdec_device(),
block->start + wp,
len, DMA_TO_DEVICE);
if (chunk->pading_size > len) {
p = (u8 *)block->start_virt;
memset(p, 0, count - len);
dma_sync_single_for_device(get_vdec_device(),
block->start,
chunk->pading_size - 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 + chunk->pading_size;
if (block->wp >= block->size)
block->wp -= block->size;
block->data_size += chunk->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_block(struct vframe_block_list_s *block)
{
if (block->addr) {
codec_mm_free_for_dma(MEM_NAME, block->addr);
}
/*
*pr_err("free block %d, size=%d\n", block->id, block->size);
*/
kfree(block);
}
static int vframe_block_init_alloc_storage(struct vdec_input_s *input,
struct vframe_block_list_s *block)
{
int alloc_size = input->default_block_size;
block->magic = 0x4b434c42;
block->input = input;
block->type = input->type;
/*
* todo: for different type use different size
*/
alloc_size = PAGE_ALIGN(alloc_size);
block->addr = codec_mm_alloc_for_dma_ex(
MEM_NAME,
alloc_size/PAGE_SIZE,
VFRAME_BLOCK_PAGEALIGN,
CODEC_MM_FLAGS_DMA_CPU | CODEC_MM_FLAGS_FOR_VDECODER,
input->id,
block->id);
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 = block->addr;
block->size = alloc_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_block_free_list);
INIT_LIST_HEAD(&input->vframe_chunk_list);
spin_lock_init(&input->lock);
input->id = vdec->id;
input->block_nums = 0;
input->vdec = vdec;
input->block_id_seq = 0;
input->size = 0;
input->default_block_size = VFRAME_BLOCK_SIZE;
}
int vdec_input_prepare_bufs(struct vdec_input_s *input,
int frame_width, int frame_height)
{
struct vframe_block_list_s *block;
int i;
unsigned long flags;
if (input->size > 0)
return 0;
if (frame_width * frame_height >= 1920 * 1088) {
/*have add data before. ignore prepare buffers.*/
input->default_block_size = VFRAME_BLOCK_SIZE_4K;
}
/*prepared 3 buffers for smooth start.*/
for (i = 0; i < 3; i++) {
block = vdec_input_alloc_new_block(input);
if (!block)
break;
flags = vdec_input_lock(input);
list_move_tail(&block->list,
&input->vframe_block_free_list);
input->wr_block = NULL;
vdec_input_unlock(input, flags);
}
return 0;
}
static int vdec_input_dump_block_locked(
struct vframe_block_list_s *block,
char *buf, int size)
{
char *pbuf = buf;
char sbuf[512];
int tsize = 0;
int s;
if (!pbuf) {
pbuf = sbuf;
size = 512;
}
#define BUFPRINT(args...) \
do {\
s = snprintf(pbuf, size - tsize, args);\
tsize += s;\
pbuf += s; \
} while (0)
BUFPRINT("\tblock:[%d:%p]-addr=%p,vstart=%p,type=%d\n",
block->id,
block,
(void *)block->addr,
(void *)block->start_virt,
block->type);
BUFPRINT("\t-blocksize=%d,data=%d,wp=%d,rp=%d,chunk_count=%d\n",
block->size,
block->data_size,
block->wp,
block->rp,
block->chunk_count);
/*
BUFPRINT("\tlist=%p,next=%p,prev=%p\n",
&block->list,
block->list.next,
block->list.prev);
*/
#undef BUFPRINT
if (!buf)
pr_info("%s", sbuf);
return tsize;
}
int vdec_input_dump_blocks(struct vdec_input_s *input,
char *bufs, int size)
{
struct list_head *p, *tmp;
unsigned long flags;
char *lbuf = bufs;
char sbuf[256];
int s = 0;
if (size <= 0)
return 0;
if (!bufs)
lbuf = sbuf;
s += snprintf(lbuf + s, size - s,
"blocks:vdec-%d id:%d,bufsize=%d,dsize=%d,frames:%d,dur:%dms\n",
input->id,
input->block_nums,
input->size,
input->data_size,
input->have_frame_num,
vdec_input_get_duration_u64(input)/1000);
if (bufs)
lbuf += s;
else {
pr_info("%s", sbuf);
lbuf = NULL;
}
flags = vdec_input_lock(input);
/* dump 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);
if (bufs != NULL) {
lbuf = bufs + s;
if (size - s < 128)
break;
}
s += vdec_input_dump_block_locked(block, lbuf, size - s);
}
list_for_each_safe(p, tmp, &input->vframe_block_free_list) {
struct vframe_block_list_s *block = list_entry(
p, struct vframe_block_list_s, list);
if (bufs != NULL) {
lbuf = bufs + s;
if (size - s < 128)
break;
}
s += vdec_input_dump_block_locked(block, lbuf, size - s);
}
vdec_input_unlock(input, flags);
return s;
}
static int vdec_input_dump_chunk_locked(
struct vframe_chunk_s *chunk,
char *buf, int size)
{
char *pbuf = buf;
char sbuf[512];
int tsize = 0;
int s;
if (!pbuf) {
pbuf = sbuf;
size = 512;
}
#define BUFPRINT(args...) \
do {\
s = snprintf(pbuf, size - tsize, args);\
tsize += s;\
pbuf += s; \
} while (0)
BUFPRINT(
"\t[%lld:%p]-off=%d,size:%d,p:%d,\tpts64=%lld,addr=%p\n",
chunk->sequence,
chunk->block,
chunk->offset,
chunk->size,
chunk->pading_size,
chunk->pts64,
(void *)(chunk->block->addr + chunk->offset));
/*
BUFPRINT("\tlist=%p,next=%p,prev=%p\n",
&chunk->list,
chunk->list.next,
chunk->list.prev);
*/
#undef BUFPRINT
if (!buf)
pr_info("%s", sbuf);
return tsize;
}
int vdec_input_dump_chunks(struct vdec_input_s *input,
char *bufs, int size)
{
struct list_head *p, *tmp;
unsigned long flags;
char *lbuf = bufs;
char sbuf[256];
int s = 0;
if (size <= 0)
return 0;
if (!bufs)
lbuf = sbuf;
snprintf(lbuf + s, size - s,
"blocks:vdec-%d id:%d,bufsize=%d,dsize=%d,frames:%d,maxframe:%d\n",
input->id,
input->block_nums,
input->size,
input->data_size,
input->have_frame_num,
input->frame_max_size);
if (bufs)
lbuf += s;
if (!bufs) {
pr_info("%s", sbuf);
lbuf = NULL;
}
flags = vdec_input_lock(input);
/*dump chunks list infos.*/
list_for_each_safe(p, tmp, &input->vframe_chunk_list) {
struct vframe_chunk_s *chunk = list_entry(
p, struct vframe_chunk_s, list);
if (bufs != NULL)
lbuf = bufs + s;
s += vdec_input_dump_chunk_locked(chunk, lbuf, size - s);
}
vdec_input_unlock(input, flags);
return s;
}
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_rp = start;
if (vdec_secure(input->vdec))
input->swap_page_phys = codec_mm_alloc_for_dma("SWAP",
1, 0, CODEC_MM_FLAGS_TVP);
else {
input->swap_page = alloc_page(GFP_KERNEL);
if (input->swap_page) {
input->swap_page_phys =
page_to_phys(input->swap_page);
}
}
if (input->swap_page_phys == 0)
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;
if (type == VDEC_TYPE_FRAME_CIRCULAR) {
/*alway used max block.*/
input->default_block_size = VFRAME_BLOCK_SIZE_MAX;
}
}
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);
block->wp = 0;
block->id = input->block_id_seq++;
list_add_tail(&block->list, &input->vframe_block_list);
input->size += block->size;
input->block_nums++;
input->wr_block = block;
vdec_input_unlock(input, flags);
}
static inline void vdec_input_del_block_locked(struct vdec_input_s *input,
struct vframe_block_list_s *block)
{
list_del(&block->list);
input->size -= block->size;
input->block_nums--;
}
int vdec_input_level(struct vdec_input_s *input)
{
return input->total_wr_count - input->total_rd_count;
}
EXPORT_SYMBOL(vdec_input_level);
static struct vframe_block_list_s *
vdec_input_alloc_new_block(struct vdec_input_s *input)
{
struct vframe_block_list_s *block;
block = kzalloc(sizeof(struct vframe_block_list_s),
GFP_KERNEL);
if (block == NULL) {
input->no_mem_err_cnt++;
pr_err("vframe_block structure allocation failed\n");
return NULL;
}
if (vframe_block_init_alloc_storage(input,
block) != 0) {
kfree(block);
pr_err("vframe_block storage allocation failed\n");
return NULL;
}
INIT_LIST_HEAD(&block->list);
vdec_input_add_block(input, block);
/*
*pr_info("vdec-%d:new block id=%d, total_blocks:%d, size=%d\n",
* input->id,
* block->id,
* input->block_nums,
* block->size);
*/
if (0 && input->size > VFRAME_BLOCK_MAX_LEVEL * 2) {
/*
used
*/
pr_info(
"input[%d] reach max: size:%d, blocks:%d",
input->id,
input->size,
input->block_nums);
pr_info("level:%d, wr:%lld,rd:%lld\n",
vdec_input_level(input),
input->total_wr_count,
input->total_rd_count);
vdec_input_dump_blocks(input, NULL, 0);
}
return block;
}
static int vdec_input_get_duration_u64(struct vdec_input_s *input)
{
int duration = (input->last_inpts_u64 - input->last_comsumed_pts_u64);
if (input->last_in_nopts_cnt > 0 &&
input->last_comsumed_pts_u64 > 0 &&
input->last_duration > 0) {
duration += (input->last_in_nopts_cnt -
input->last_comsumed_no_pts_cnt) *
input->last_duration;
}
if (duration > 1000 * 1000000)/*> 1000S,I think jumped.*/
duration = 0;
if (duration <= 0 && input->last_duration > 0) {
/*..*/
duration = input->last_duration * input->have_frame_num;
}
if (duration < 0)
duration = 0;
return duration;
}
/*
ret >= 13: have enough buffer, blocked add more buffers
*/
static int vdec_input_have_blocks_enough(struct vdec_input_s *input)
{
int ret = 0;
if (vdec_input_level(input) > VFRAME_BLOCK_MIN_LEVEL)
ret += 1;
if (vdec_input_level(input) >= VFRAME_BLOCK_MAX_LEVEL)
ret += 2;
if (vdec_input_get_duration_u64(input) > MAX_FRAME_DURATION_S)
ret += 4;
if (input->have_frame_num > 30)
ret += 8;
else
ret -= 8;/*not enough frames.*/
if (input->size >= VFRAME_BLOCK_MAX_TOTAL_SIZE)
ret += 100;/*always bloced add more buffers.*/
return ret;
}
static int vdec_input_get_free_block(
struct vdec_input_s *input,
int size,/*frame size + pading*/
struct vframe_block_list_s **block_ret)
{
struct vframe_block_list_s *to_freeblock = NULL;
struct vframe_block_list_s *block = NULL;
unsigned long flags;
flags = vdec_input_lock(input);
/*get from free list.*/
if (!list_empty(&input->vframe_block_free_list)) {
block = list_entry(input->vframe_block_free_list.next,
struct vframe_block_list_s, list);
if (block->size < (size)) {
vdec_input_del_block_locked(input, block);
to_freeblock = block;
block = NULL;
} else {
list_move_tail(&block->list,
&input->vframe_block_list);
input->wr_block = block;/*swith to new block*/
}
}
vdec_input_unlock(input, flags);
if (to_freeblock) {
/*free the small block.*/
vframe_block_free_block(to_freeblock);
}
if (block) {
*block_ret = block;
return 0;
}
if (vdec_input_have_blocks_enough(input) > 13) {
/*buf fulled */
return -EAGAIN;
}
if (input->no_mem_err_cnt > 3) {
/*alloced failed more times.
*/
return -EAGAIN;
}
if (input->default_block_size <=
size * 2) {
int def_size = input->default_block_size;
do {
def_size *= 2;
} while ((def_size <= 2 * size) &&
(def_size <= VFRAME_BLOCK_SIZE_MAX));
if (def_size < size)
def_size = ALIGN(size + 64, (1 << 17));
/*128k aligned,same as codec_mm*/
input->default_block_size = def_size;
}
block = vdec_input_alloc_new_block(input);
if (!block) {
input->no_mem_err_cnt++;
return -EAGAIN;
}
input->no_mem_err_cnt = 0;
*block_ret = block;
return 0;
}
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;
int need_pading_size = MIN_FRAME_PADDING_SIZE;
#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;
if (count < PAGE_SIZE) {
need_pading_size = PAGE_ALIGN(count + need_pading_size) -
count;
} else {
/*to 64 bytes aligned;*/
if (count & 0x3f)
need_pading_size += 64 - (count & 0x3f);
}
block = input->wr_block;
if (block &&
(vframe_block_space(block) > (count + need_pading_size))) {
/*this block have enough buffers.
do nothings.
*/
} else if (block && (block->type == VDEC_TYPE_FRAME_CIRCULAR)) {
/*in circular module.
only one block,.*/
return -EAGAIN;
} else if (block != NULL) {
/*have block but not enough space.
recycle the no enough blocks.*/
flags = vdec_input_lock(input);
if (input->wr_block == block &&
block->chunk_count == 0) {
block->rp = 0;
block->wp = 0;
/*block no data move to freelist*/
list_move_tail(&block->list,
&input->vframe_block_free_list);
input->wr_block = NULL;
}
vdec_input_unlock(input, flags);
block = NULL;
}
if (!block) {/*try new block.*/
int ret = vdec_input_get_free_block(input,
count + need_pading_size + EXTRA_PADDING_SIZE,
&block);
if (ret < 0)/*no enough block now.*/
return ret;
}
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;
}
if (vdec->pts_valid &&
input->last_inpts_u64 > 0 &&
input->last_in_nopts_cnt == 0) {
int d = (int)(chunk->pts64 - input->last_inpts_u64);
if (d > 0 && (d < input->last_duration))
input->last_duration = d;
/* alwasy: used the smallest duration;
if 60fps->30 fps.
maybe have warning value.
*/
}
chunk->pts_valid = vdec->pts_valid;
vdec->pts_valid = false;
chunk->offset = block->wp;
chunk->size = count;
chunk->pading_size = need_pading_size;
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);
input->data_size += chunk->size;
input->have_frame_num++;
if (chunk->pts_valid) {
input->last_inpts_u64 = chunk->pts64;
input->last_in_nopts_cnt = 0;
} else {
/*nopts*/
input->last_in_nopts_cnt++;
}
vdec_input_unlock(input, flags);
if (chunk->size > input->frame_max_size)
input->frame_max_size = chunk->size;
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)
{
struct vframe_chunk_s *chunk = NULL;
unsigned long flags;
flags = vdec_input_lock(input);
if (!list_empty(&input->vframe_chunk_list)) {
chunk = list_first_entry(&input->vframe_chunk_list,
struct vframe_chunk_s, list);
}
vdec_input_unlock(input, flags);
return chunk;
}
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;
unsigned long flags;
flags = vdec_input_lock(input);
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;
}
}
vdec_input_unlock(input, flags);
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;
struct vframe_block_list_s *tofreeblock = NULL;
flags = vdec_input_lock(input);
list_del(&chunk->list);
input->have_frame_num--;
if (chunk->pts_valid) {
input->last_comsumed_no_pts_cnt = 0;
input->last_comsumed_pts_u64 = chunk->pts64;
} else
input->last_comsumed_no_pts_cnt++;
block->rp += chunk->size;
if (block->rp >= block->size)
block->rp -= block->size;
block->data_size -= chunk->size;
block->chunk_count--;
input->data_size -= chunk->size;
input->total_rd_count += chunk->size;
if (block->chunk_count == 0 &&
input->wr_block != block) {/*don't free used block*/
if (block->size < input->default_block_size) {
vdec_input_del_block_locked(input, block);
tofreeblock = block;
} else {
block->rp = 0;
block->wp = 0;
list_move_tail(&block->list,
&input->vframe_block_free_list);
}
}
vdec_input_unlock(input, flags);
if (tofreeblock)
vframe_block_free_block(tofreeblock);
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);
}
list_for_each_safe(p, tmp, &input->vframe_block_list) {
/*should never here.*/
list_move_tail(p, &input->vframe_block_free_list);
}
/* release input blocks */
list_for_each_safe(p, tmp, &input->vframe_block_free_list) {
struct vframe_block_list_s *block = list_entry(
p, struct vframe_block_list_s, list);
vdec_input_del_block_locked(input, block);
vframe_block_free_block(block);
}
/* release swap pages */
if (input->swap_page_phys) {
if (vdec_secure(input->vdec))
codec_mm_free_for_dma("SWAP", input->swap_page_phys);
else
__free_page(input->swap_page);
input->swap_page = NULL;
input->swap_page_phys = 0;
}
input->swap_valid = false;
}
EXPORT_SYMBOL(vdec_input_release);

View File

@@ -0,0 +1,164 @@
/*
* 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;
int id;
struct list_head list;
ulong start;
void *start_virt;
ulong addr;
int type;
u32 size;
u32 wp;
u32 rp;
int data_size;
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;
u32 pading_size;
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 list_head vframe_block_free_list;
struct vframe_block_list_s *wr_block;
int have_free_blocks;
int no_mem_err_cnt;/*when alloc no mem cnt++*/
int block_nums;
int block_id_seq;
int id;
spinlock_t lock;
int type;
int target;
struct vdec_s *vdec;
bool swap_valid;
bool swap_needed;
bool eos;
struct page *swap_page;
unsigned long swap_page_phys;
u64 total_wr_count;
u64 total_rd_count;
u64 streaming_rp;
u32 swap_rp;
bool last_swap_slave;
int dirty_count;
u64 sequence;
unsigned start;
unsigned size;
int default_block_size;
int data_size;
int frame_max_size;
int prepare_level;
/*for check frame delay.*/
u64 last_inpts_u64;
u64 last_comsumed_pts_u64;
int last_in_nopts_cnt;
int last_comsumed_no_pts_cnt;
int last_duration;
/*for check frame delay.*/
int have_frame_num;
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);
extern int vdec_input_prepare_bufs(struct vdec_input_s *input,
int frame_width, int frame_height);
/* 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);
int vdec_input_dump_chunks(struct vdec_input_s *input,
char *bufs, int size);
int vdec_input_dump_blocks(struct vdec_input_s *input,
char *bufs, int size);
#endif /* VDEC_INPUT_H */

View File

@@ -0,0 +1,258 @@
/*
* drivers/amlogic/amports/vdec_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/mutex.h>
#include <linux/types.h>
#include <linux/debugfs.h>
#include <linux/amlogic/media/utils/vdec_reg.h>
#include "vdec_profile.h"
#include "vdec.h"
#define ISA_TIMERE 0x2662
#define ISA_TIMERE_HI 0x2663
#define PROFILE_REC_SIZE 40
static DEFINE_MUTEX(vdec_profile_mutex);
static int rec_wp;
static bool rec_wrapped;
struct dentry *root, *event;
struct vdec_profile_rec_s {
struct vdec_s *vdec;
u64 timestamp;
int event;
int para1;
int para2;
};
static struct vdec_profile_rec_s recs[PROFILE_REC_SIZE];
static const char *event_name[VDEC_PROFILE_MAX_EVENT] = {
"run",
"cb",
"save_input",
"check run ready",
"run ready",
"disconnect",
"dec_work",
"info"
};
#if 0 /* get time from hardware. */
static u64 get_us_time_hw(void)
{
u32 lo, hi1, hi2;
int offset = 0;
/* txlx, g12a isa register base is 0x3c00 */
if (get_cpu_type() >= MESON_CPU_MAJOR_ID_TXLX)
offset = 0x1600;
do {
hi1 = READ_MPEG_REG(ISA_TIMERE_HI + offset);
lo = READ_MPEG_REG(ISA_TIMERE + offset);
hi2 = READ_MPEG_REG(ISA_TIMERE_HI + offset);
} while (hi1 != hi2);
return (((u64)hi1) << 32) | lo;
}
#endif
static u64 get_us_time_system(void)
{
struct timeval tv;
do_gettimeofday(&tv);
return div64_u64(timeval_to_ns(&tv), 1000);
}
void vdec_profile_more(struct vdec_s *vdec, int event, int para1, int para2)
{
mutex_lock(&vdec_profile_mutex);
recs[rec_wp].vdec = vdec;
recs[rec_wp].timestamp = get_us_time_system();
recs[rec_wp].event = event;
recs[rec_wp].para1 = para1;
recs[rec_wp].para2 = para2;
rec_wp++;
if (rec_wp == PROFILE_REC_SIZE) {
rec_wrapped = true;
rec_wp = 0;
}
mutex_unlock(&vdec_profile_mutex);
}
EXPORT_SYMBOL(vdec_profile_more);
void vdec_profile(struct vdec_s *vdec, int event)
{
vdec_profile_more(vdec, event, 0 , 0);
}
EXPORT_SYMBOL(vdec_profile);
void vdec_profile_flush(struct vdec_s *vdec)
{
int i;
mutex_lock(&vdec_profile_mutex);
for (i = 0; i < PROFILE_REC_SIZE; i++) {
if (recs[i].vdec == vdec)
recs[i].vdec = NULL;
}
mutex_unlock(&vdec_profile_mutex);
}
static const char *event_str(int event)
{
if (event < VDEC_PROFILE_MAX_EVENT)
return event_name[event];
return "INVALID";
}
static int vdec_profile_dbg_show(struct seq_file *m, void *v)
{
int i, end;
u64 base_timestamp;
mutex_lock(&vdec_profile_mutex);
if (rec_wrapped) {
i = rec_wp;
end = rec_wp;
} else {
i = 0;
end = rec_wp;
}
base_timestamp = recs[i].timestamp;
while (1) {
if ((!rec_wrapped) && (i == end))
break;
if (recs[i].vdec) {
seq_printf(m, "[%s:%d] \t%016llu us : %s (%d,%d)\n",
vdec_device_name_str(recs[i].vdec),
recs[i].vdec->id,
recs[i].timestamp - base_timestamp,
event_str(recs[i].event),
recs[i].para1,
recs[i].para2
);
} else {
seq_printf(m, "[%s:%d] \t%016llu us : %s (%d,%d)\n",
"N/A",
0,
recs[i].timestamp - base_timestamp,
event_str(recs[i].event),
recs[i].para1,
recs[i].para2
);
}
if (++i == PROFILE_REC_SIZE)
i = 0;
if (rec_wrapped && (i == end))
break;
}
mutex_unlock(&vdec_profile_mutex);
return 0;
}
static int vdec_profile_dbg_open(struct inode *inode, struct file *file)
{
return single_open(file, vdec_profile_dbg_show, NULL);
}
static const struct file_operations event_dbg_fops = {
.open = vdec_profile_dbg_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
#if 0 /*DEBUG_TMP*/
static int __init vdec_profile_init_debugfs(void)
{
struct dentry *root, *event;
root = debugfs_create_dir("vdec_profile", NULL);
if (IS_ERR(root) || !root)
goto err;
event = debugfs_create_file("event", 0400, root, NULL,
&event_dbg_fops);
if (!event)
goto err_1;
mutex_init(&vdec_profile_mutex);
return 0;
err_1:
debugfs_remove(root);
err:
pr_err("Can not create debugfs for vdec_profile\n");
return 0;
}
#endif
int vdec_profile_init_debugfs(void)
{
struct dentry *root, *event;
root = debugfs_create_dir("vdec_profile", NULL);
if (IS_ERR(root) || !root)
goto err;
event = debugfs_create_file("event", 0400, root, NULL,
&event_dbg_fops);
if (!event)
goto err_1;
mutex_init(&vdec_profile_mutex);
return 0;
err_1:
debugfs_remove(root);
err:
pr_err("Can not create debugfs for vdec_profile\n");
return 0;
}
EXPORT_SYMBOL(vdec_profile_init_debugfs);
void vdec_profile_exit_debugfs(void)
{
debugfs_remove(event);
debugfs_remove(root);
}
EXPORT_SYMBOL(vdec_profile_exit_debugfs);
/*module_init(vdec_profile_init_debugfs);*/

View File

@@ -0,0 +1,40 @@
/*
* drivers/amlogic/amports/vdec_profile.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_PROFILE_H
#define VDEC_PROFILE_H
struct vdec_s;
#define VDEC_PROFILE_EVENT_RUN 0
#define VDEC_PROFILE_EVENT_CB 1
#define VDEC_PROFILE_EVENT_SAVE_INPUT 2
#define VDEC_PROFILE_EVENT_CHK_RUN_READY 3
#define VDEC_PROFILE_EVENT_RUN_READY 4
#define VDEC_PROFILE_EVENT_DISCONNECT 5
#define VDEC_PROFILE_EVENT_DEC_WORK 6
#define VDEC_PROFILE_EVENT_INFO 7
#define VDEC_PROFILE_MAX_EVENT 8
extern void vdec_profile(struct vdec_s *vdec, int event);
extern void vdec_profile_more(struct vdec_s *vdec, int event, int para1, int para2);
extern void vdec_profile_flush(struct vdec_s *vdec);
int vdec_profile_init_debugfs(void);
void vdec_profile_exit_debugfs(void);
#endif /* VDEC_PROFILE_H */

View File

@@ -0,0 +1,149 @@
/*
* drivers/amlogic/amports/vdec_trace.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.
*
*/
#undef TRACE_SYSTEM
#define TRACE_SYSTEM vdec
#if !defined(_VDEC_TRACE_H) || defined(TRACE_HEADER_MULTI_READ)
#define _VDEC_TRACE_H
#include <linux/tracepoint.h>
struct vdec_s;
/* single lifecycle events */
DECLARE_EVENT_CLASS(vdec_event_class,
TP_PROTO(struct vdec_s *vdec),
TP_ARGS(vdec),
TP_STRUCT__entry(
__field(struct vdec_s *, vdec)
),
TP_fast_assign(
__entry->vdec = vdec;
),
TP_printk("[%p]", __entry->vdec)
);
#define DEFINE_VDEC_EVENT(name) \
DEFINE_EVENT(vdec_event_class, name, \
TP_PROTO(struct vdec_s *vdec), \
TP_ARGS(vdec))
DEFINE_VDEC_EVENT(vdec_create);
DEFINE_VDEC_EVENT(vdec_connect);
DEFINE_VDEC_EVENT(vdec_disconnect);
DEFINE_VDEC_EVENT(vdec_destroy);
DEFINE_VDEC_EVENT(vdec_reset);
DEFINE_VDEC_EVENT(vdec_release);
/* set format event */
#define format_name(format) \
__print_symbolic(format, \
{0, "MPEG"}, \
{1, "MPEG4"}, \
{2, "H264"}, \
{3, "MJPEG"}, \
{4, "REAL"}, \
{5, "JPEG"}, \
{6, "VC1"}, \
{7, "AVS"}, \
{8, "YUV"}, \
{9, "H264MVC"}, \
{10, "H264_4K2K"}, \
{11, "H265"}, \
{12, "ENC_AVC"}, \
{13, "ENC_JPEG"}, \
{14, "VP9"})
TRACE_EVENT(vdec_set_format,
TP_PROTO(struct vdec_s *vdec, int format),
TP_ARGS(vdec, format),
TP_STRUCT__entry(
__field(struct vdec_s *, vdec)
__field(int, format)
),
TP_fast_assign(
__entry->vdec = vdec;
__entry->format = format;
),
TP_printk("[%p]:%s", __entry->vdec,
format_name(__entry->format))
);
/* status events */
#define status_name(status) \
__print_symbolic(status, \
{0, "UNINITIALIZED"}, \
{1, "DISCONNECTED"}, \
{2, "CONNECTED"}, \
{3, "ACTIVE"})
DECLARE_EVENT_CLASS(vdec_status_class,
TP_PROTO(struct vdec_s *vdec, int state),
TP_ARGS(vdec, state),
TP_STRUCT__entry(
__field(struct vdec_s *, vdec)
__field(int, state)
),
TP_fast_assign(
__entry->vdec = vdec;
__entry->state = state;
),
TP_printk("[%p]:%s", __entry->vdec, status_name(__entry->state))
);
#define DEFINE_STATUS_EVENT(name) \
DEFINE_EVENT(vdec_status_class, name, \
TP_PROTO(struct vdec_s *vdec, int status), \
TP_ARGS(vdec, status))
DEFINE_STATUS_EVENT(vdec_set_status);
DEFINE_STATUS_EVENT(vdec_set_next_status);
/* set pts events */
DECLARE_EVENT_CLASS(vdec_pts_class,
TP_PROTO(struct vdec_s *vdec, u64 pts),
TP_ARGS(vdec, pts),
TP_STRUCT__entry(
__field(struct vdec_s *, vdec)
__field(u64, pts)
),
TP_fast_assign(
__entry->vdec = vdec;
__entry->pts = pts;
),
TP_printk("[%p]%llu", __entry->vdec, __entry->pts)
);
#define DEFINE_PTS_EVENT(name) \
DEFINE_EVENT(vdec_pts_class, name, \
TP_PROTO(struct vdec_s *vdec, u64 pts), \
TP_ARGS(vdec, pts))
DEFINE_PTS_EVENT(vdec_set_pts);
DEFINE_PTS_EVENT(vdec_set_pts64);
#endif /* _VDEC_TRACE_H */
/*
#undef TRACE_INCLUDE_PATH
#undef TRACE_INCLUDE_FILE
#define TRACE_INCLUDE_PATH .
#define TRACE_INCLUDE_FILE vdec_trace
#include <trace/define_trace.h>
*/
/**/ //DEBUG_TMP

View File

@@ -0,0 +1,2 @@
obj-$(CONFIG_AMLOGIC_MEDIA_VDEC_VC1) += amvdec_vc1.o
amvdec_vc1-objs += vvc1.o

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,2 @@
obj-$(CONFIG_AMLOGIC_MEDIA_VDEC_VP9) += amvdec_vp9.o
amvdec_vp9-objs += vvp9.o

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,23 @@
/*
* drivers/amlogic/amports/vvp9.h
*
* Copyright (C) 2015 Amlogic, Inc. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
*/
#ifndef VVP9_H
#define VVP9_H
#define VP9_10B_MMU
void adapt_coef_probs(int pic_count, int prev_kf, int cur_kf, int pre_fc,
unsigned int *prev_prob, unsigned int *cur_prob, unsigned int *count);
#endif

View File

@@ -0,0 +1 @@
obj-y += encoder/

View File

@@ -0,0 +1,2 @@
obj-$(CONFIG_AMLOGIC_MEDIA_VENC_H264) += h264/
obj-$(CONFIG_AMLOGIC_MEDIA_VENC_H265) += h265/

View File

@@ -0,0 +1 @@
obj-m += encoder.o

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,468 @@
/*
* drivers/amlogic/amports/encoder.h
*
* Copyright (C) 2015 Amlogic, Inc. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
*/
#ifndef __H264_H__
#define __H264_H__
#include <linux/mutex.h>
#include <linux/semaphore.h>
#include <linux/list.h>
#include <linux/interrupt.h>
#include <linux/sched.h>
#include <linux/spinlock.h>
#include <linux/wait.h>
#include <linux/slab.h>
#ifdef CONFIG_AMLOGIC_MEDIA_GE2D
#include <linux/amlogic/media/ge2d/ge2d.h>
#endif
#define AMVENC_DEVINFO_M8 "AML-M8"
#define AMVENC_DEVINFO_G9 "AML-G9"
#define AMVENC_DEVINFO_GXBB "AML-GXBB"
#define AMVENC_DEVINFO_GXTVBB "AML-GXTVBB"
#define AMVENC_DEVINFO_GXL "AML-GXL"
#define HCODEC_IRQ_MBOX_CLR HCODEC_ASSIST_MBOX2_CLR_REG
#define HCODEC_IRQ_MBOX_MASK HCODEC_ASSIST_MBOX2_MASK
/* M8: 2550/10 = 255M GX: 2000/10 = 200M */
#define HDEC_L0() WRITE_HHI_REG(HHI_VDEC_CLK_CNTL, \
(2 << 25) | (1 << 16) | (1 << 24) | \
(0xffff & READ_HHI_REG(HHI_VDEC_CLK_CNTL)))
/* M8: 2550/8 = 319M GX: 2000/8 = 250M */
#define HDEC_L1() WRITE_HHI_REG(HHI_VDEC_CLK_CNTL, \
(0 << 25) | (1 << 16) | (1 << 24) | \
(0xffff & READ_HHI_REG(HHI_VDEC_CLK_CNTL)))
/* M8: 2550/7 = 364M GX: 2000/7 = 285M */
#define HDEC_L2() WRITE_HHI_REG(HHI_VDEC_CLK_CNTL, \
(3 << 25) | (0 << 16) | (1 << 24) | \
(0xffff & READ_HHI_REG(HHI_VDEC_CLK_CNTL)))
/* M8: 2550/6 = 425M GX: 2000/6 = 333M */
#define HDEC_L3() WRITE_HHI_REG(HHI_VDEC_CLK_CNTL, \
(1 << 25) | (1 << 16) | (1 << 24) | \
(0xffff & READ_HHI_REG(HHI_VDEC_CLK_CNTL)))
/* M8: 2550/5 = 510M GX: 2000/5 = 400M */
#define HDEC_L4() WRITE_HHI_REG(HHI_VDEC_CLK_CNTL, \
(2 << 25) | (0 << 16) | (1 << 24) | \
(0xffff & READ_HHI_REG(HHI_VDEC_CLK_CNTL)))
/* M8: 2550/4 = 638M GX: 2000/4 = 500M */
#define HDEC_L5() WRITE_HHI_REG(HHI_VDEC_CLK_CNTL, \
(0 << 25) | (0 << 16) | (1 << 24) | \
(0xffff & READ_HHI_REG(HHI_VDEC_CLK_CNTL)))
/* M8: 2550/3 = 850M GX: 2000/3 = 667M */
#define HDEC_L6() WRITE_HHI_REG(HHI_VDEC_CLK_CNTL, \
(1 << 25) | (0 << 16) | (1 << 24) | \
(0xffff & READ_HHI_REG(HHI_VDEC_CLK_CNTL)))
#define hvdec_clock_enable(level) \
do { \
if (level == 0) \
HDEC_L0(); \
else if (level == 1) \
HDEC_L1(); \
else if (level == 2) \
HDEC_L2(); \
else if (level == 3) \
HDEC_L3(); \
else if (level == 4) \
HDEC_L4(); \
else if (level == 5) \
HDEC_L5(); \
else if (level == 6) \
HDEC_L6(); \
WRITE_VREG_BITS(DOS_GCLK_EN0, 0x7fff, 12, 15); \
} while (0)
#define hvdec_clock_disable() \
do { \
WRITE_VREG_BITS(DOS_GCLK_EN0, 0, 12, 15); \
WRITE_HHI_REG_BITS(HHI_VDEC_CLK_CNTL, 0, 24, 1); \
} while (0)
#define LOG_ALL 0
#define LOG_INFO 1
#define LOG_DEBUG 2
#define LOG_ERROR 3
#define enc_pr(level, x...) \
do { \
if (level >= encode_print_level) \
printk(x); \
} while (0)
#define AMVENC_AVC_IOC_MAGIC 'E'
#define AMVENC_AVC_IOC_GET_DEVINFO _IOW(AMVENC_AVC_IOC_MAGIC, 0xf0, u32)
#define AMVENC_AVC_IOC_MAX_INSTANCE _IOW(AMVENC_AVC_IOC_MAGIC, 0xf1, u32)
#define AMVENC_AVC_IOC_GET_ADDR _IOW(AMVENC_AVC_IOC_MAGIC, 0x00, u32)
#define AMVENC_AVC_IOC_INPUT_UPDATE _IOW(AMVENC_AVC_IOC_MAGIC, 0x01, u32)
#define AMVENC_AVC_IOC_NEW_CMD _IOW(AMVENC_AVC_IOC_MAGIC, 0x02, u32)
#define AMVENC_AVC_IOC_GET_STAGE _IOW(AMVENC_AVC_IOC_MAGIC, 0x03, u32)
#define AMVENC_AVC_IOC_GET_OUTPUT_SIZE _IOW(AMVENC_AVC_IOC_MAGIC, 0x04, u32)
#define AMVENC_AVC_IOC_CONFIG_INIT _IOW(AMVENC_AVC_IOC_MAGIC, 0x05, u32)
#define AMVENC_AVC_IOC_FLUSH_CACHE _IOW(AMVENC_AVC_IOC_MAGIC, 0x06, u32)
#define AMVENC_AVC_IOC_FLUSH_DMA _IOW(AMVENC_AVC_IOC_MAGIC, 0x07, u32)
#define AMVENC_AVC_IOC_GET_BUFFINFO _IOW(AMVENC_AVC_IOC_MAGIC, 0x08, u32)
#define AMVENC_AVC_IOC_SUBMIT _IOW(AMVENC_AVC_IOC_MAGIC, 0x09, u32)
#define AMVENC_AVC_IOC_READ_CANVAS _IOW(AMVENC_AVC_IOC_MAGIC, 0x0a, u32)
#define IE_PIPPELINE_BLOCK_SHIFT 0
#define IE_PIPPELINE_BLOCK_MASK 0x1f
#define ME_PIXEL_MODE_SHIFT 5
#define ME_PIXEL_MODE_MASK 0x3
enum amvenc_mem_type_e {
LOCAL_BUFF = 0,
CANVAS_BUFF,
PHYSICAL_BUFF,
MAX_BUFF_TYPE
};
enum amvenc_frame_fmt_e {
FMT_YUV422_SINGLE = 0,
FMT_YUV444_SINGLE,
FMT_NV21,
FMT_NV12,
FMT_YUV420,
FMT_YUV444_PLANE,
FMT_RGB888,
FMT_RGB888_PLANE,
FMT_RGB565,
FMT_RGBA8888,
FMT_YUV422_12BIT,
FMT_YUV444_10BIT,
FMT_YUV422_10BIT,
MAX_FRAME_FMT
};
#define MAX_ENCODE_REQUEST 8 /* 64 */
#define MAX_ENCODE_INSTANCE 8 /* 64 */
#define ENCODE_PROCESS_QUEUE_START 0
#define ENCODE_PROCESS_QUEUE_STOP 1
#define AMVENC_FLUSH_FLAG_INPUT 0x1
#define AMVENC_FLUSH_FLAG_OUTPUT 0x2
#define AMVENC_FLUSH_FLAG_REFERENCE 0x4
#define AMVENC_FLUSH_FLAG_INTRA_INFO 0x8
#define AMVENC_FLUSH_FLAG_INTER_INFO 0x10
#define AMVENC_FLUSH_FLAG_QP 0x20
#define AMVENC_FLUSH_FLAG_DUMP 0x40
#define AMVENC_FLUSH_FLAG_CBR 0x80
#define ENCODER_BUFFER_INPUT 0
#define ENCODER_BUFFER_REF0 1
#define ENCODER_BUFFER_REF1 2
#define ENCODER_BUFFER_OUTPUT 3
#define ENCODER_BUFFER_INTER_INFO 4
#define ENCODER_BUFFER_INTRA_INFO 5
#define ENCODER_BUFFER_QP 6
#define ENCODER_BUFFER_DUMP 7
#define ENCODER_BUFFER_CBR 8
struct encode_wq_s;
struct encode_request_s {
u32 quant;
u32 cmd;
u32 ucode_mode;
u32 src;
u32 framesize;
u32 me_weight;
u32 i4_weight;
u32 i16_weight;
u32 crop_top;
u32 crop_bottom;
u32 crop_left;
u32 crop_right;
u32 src_w;
u32 src_h;
u32 scale_enable;
u32 nr_mode;
u32 flush_flag;
u32 timeout;
enum amvenc_mem_type_e type;
enum amvenc_frame_fmt_e fmt;
struct encode_wq_s *parent;
};
struct encode_queue_item_s {
struct list_head list;
struct encode_request_s request;
};
struct Buff_s {
u32 buf_start;
u32 buf_size;
bool used;
};
struct BuffInfo_s {
u32 lev_id;
u32 min_buffsize;
u32 max_width;
u32 max_height;
struct Buff_s dct;
struct Buff_s dec0_y;
struct Buff_s dec0_uv;
struct Buff_s dec1_y;
struct Buff_s dec1_uv;
struct Buff_s assit;
struct Buff_s bitstream;
struct Buff_s scale_buff;
struct Buff_s dump_info;
struct Buff_s cbr_info;
};
struct encode_meminfo_s {
u32 buf_start;
u32 buf_size;
u32 BitstreamStart;
u32 BitstreamEnd;
/*input buffer define*/
u32 dct_buff_start_addr;
u32 dct_buff_end_addr;
/*microcode assitant buffer*/
u32 assit_buffer_offset;
u32 scaler_buff_start_addr;
u32 dump_info_ddr_start_addr;
u32 dump_info_ddr_size;
u32 cbr_info_ddr_start_addr;
u32 cbr_info_ddr_size;
s32 dblk_buf_canvas;
s32 ref_buf_canvas;
struct BuffInfo_s bufspec;
#ifdef CONFIG_CMA
struct page *venc_pages;
#endif
};
struct encode_picinfo_s {
u32 encoder_width;
u32 encoder_height;
u32 rows_per_slice;
u32 idr_pic_id; /* need reset as 0 for IDR */
u32 frame_number; /* need plus each frame */
/* need reset as 0 for IDR and plus 2 for NON-IDR */
u32 pic_order_cnt_lsb;
u32 log2_max_pic_order_cnt_lsb;
u32 log2_max_frame_num;
u32 init_qppicture;
};
struct encode_cbr_s {
u16 block_w;
u16 block_h;
u16 long_th;
u8 start_tbl_id;
u8 short_shift;
u8 long_mb_num;
};
struct encode_wq_s {
struct list_head list;
/* dev info */
u32 ucode_index;
u32 hw_status;
u32 output_size;
u32 sps_size;
u32 pps_size;
u32 me_weight;
u32 i4_weight;
u32 i16_weight;
u32 quant_tbl_i4[8];
u32 quant_tbl_i16[8];
u32 quant_tbl_me[8];
struct encode_meminfo_s mem;
struct encode_picinfo_s pic;
struct encode_request_s request;
struct encode_cbr_s cbr_info;
atomic_t request_ready;
wait_queue_head_t request_complete;
};
struct encode_event_s {
wait_queue_head_t hw_complete;
struct completion process_complete;
spinlock_t sem_lock; /* for queue switch and create destroy queue. */
struct completion request_in_com;
};
struct encode_manager_s {
struct list_head wq;
struct list_head process_queue;
struct list_head free_queue;
u32 encode_hw_status;
u32 process_queue_state;
s32 irq_num;
u32 wq_count;
u32 ucode_index;
u32 max_instance;
#ifdef CONFIG_AMLOGIC_MEDIA_GE2D
struct ge2d_context_s *context;
#endif
bool irq_requested;
bool need_reset;
bool process_irq;
bool inited; /* power on encode */
bool remove_flag; /* remove wq; */
bool uninit_flag; /* power off encode */
bool use_reserve;
#ifdef CONFIG_CMA
bool check_cma;
ulong cma_pool_size;
#endif
struct platform_device *this_pdev;
struct Buff_s *reserve_buff;
struct encode_wq_s *current_wq;
struct encode_wq_s *last_wq;
struct encode_queue_item_s *current_item;
struct task_struct *encode_thread;
struct Buff_s reserve_mem;
struct encode_event_s event;
struct tasklet_struct encode_tasklet;
};
extern s32 encode_wq_add_request(struct encode_wq_s *wq);
extern struct encode_wq_s *create_encode_work_queue(void);
extern s32 destroy_encode_work_queue(struct encode_wq_s *encode_work_queue);
/********************************************
* AV Scratch Register Re-Define
****************************************** *
*/
#define ENCODER_STATUS HCODEC_HENC_SCRATCH_0
#define MEM_OFFSET_REG HCODEC_HENC_SCRATCH_1
#define DEBUG_REG HCODEC_HENC_SCRATCH_2
#define IDR_PIC_ID HCODEC_HENC_SCRATCH_5
#define FRAME_NUMBER HCODEC_HENC_SCRATCH_6
#define PIC_ORDER_CNT_LSB HCODEC_HENC_SCRATCH_7
#define LOG2_MAX_PIC_ORDER_CNT_LSB HCODEC_HENC_SCRATCH_8
#define LOG2_MAX_FRAME_NUM HCODEC_HENC_SCRATCH_9
#define ANC0_BUFFER_ID HCODEC_HENC_SCRATCH_A
#define QPPICTURE HCODEC_HENC_SCRATCH_B
#define IE_ME_MB_TYPE HCODEC_HENC_SCRATCH_D
/* bit 0-4, IE_PIPPELINE_BLOCK
* bit 5 me half pixel in m8
* disable i4x4 in gxbb
* bit 6 me step2 sub pixel in m8
* disable i16x16 in gxbb
*/
#define IE_ME_MODE HCODEC_HENC_SCRATCH_E
#define IE_REF_SEL HCODEC_HENC_SCRATCH_F
/* [31:0] NUM_ROWS_PER_SLICE_P */
/* [15:0] NUM_ROWS_PER_SLICE_I */
#define FIXED_SLICE_CFG HCODEC_HENC_SCRATCH_L
/* For GX */
#define INFO_DUMP_START_ADDR HCODEC_HENC_SCRATCH_I
/* For CBR */
#define H264_ENC_CBR_TABLE_ADDR HCODEC_HENC_SCRATCH_3
#define H264_ENC_CBR_MB_SIZE_ADDR HCODEC_HENC_SCRATCH_4
/* Bytes(Float) * 256 */
#define H264_ENC_CBR_CTL HCODEC_HENC_SCRATCH_G
/* [31:28] : init qp table idx */
/* [27:24] : short_term adjust shift */
/* [23:16] : Long_term MB_Number between adjust, */
/* [15:0] Long_term adjust threshold(Bytes) */
#define H264_ENC_CBR_TARGET_SIZE HCODEC_HENC_SCRATCH_H
/* Bytes(Float) * 256 */
#define H264_ENC_CBR_PREV_BYTES HCODEC_HENC_SCRATCH_J
#define H264_ENC_CBR_REGION_SIZE HCODEC_HENC_SCRATCH_J
/* --------------------------------------------------- */
/* ENCODER_STATUS define */
/* --------------------------------------------------- */
#define ENCODER_IDLE 0
#define ENCODER_SEQUENCE 1
#define ENCODER_PICTURE 2
#define ENCODER_IDR 3
#define ENCODER_NON_IDR 4
#define ENCODER_MB_HEADER 5
#define ENCODER_MB_DATA 6
#define ENCODER_SEQUENCE_DONE 7
#define ENCODER_PICTURE_DONE 8
#define ENCODER_IDR_DONE 9
#define ENCODER_NON_IDR_DONE 10
#define ENCODER_MB_HEADER_DONE 11
#define ENCODER_MB_DATA_DONE 12
#define ENCODER_NON_IDR_INTRA 13
#define ENCODER_NON_IDR_INTER 14
#define ENCODER_ERROR 0xff
/********************************************
* defines for H.264 mb_type
*******************************************
*/
#define HENC_MB_Type_PBSKIP 0x0
#define HENC_MB_Type_PSKIP 0x0
#define HENC_MB_Type_BSKIP_DIRECT 0x0
#define HENC_MB_Type_P16x16 0x1
#define HENC_MB_Type_P16x8 0x2
#define HENC_MB_Type_P8x16 0x3
#define HENC_MB_Type_SMB8x8 0x4
#define HENC_MB_Type_SMB8x4 0x5
#define HENC_MB_Type_SMB4x8 0x6
#define HENC_MB_Type_SMB4x4 0x7
#define HENC_MB_Type_P8x8 0x8
#define HENC_MB_Type_I4MB 0x9
#define HENC_MB_Type_I16MB 0xa
#define HENC_MB_Type_IBLOCK 0xb
#define HENC_MB_Type_SI4MB 0xc
#define HENC_MB_Type_I8MB 0xd
#define HENC_MB_Type_IPCM 0xe
#define HENC_MB_Type_AUTO 0xf
#define HENC_MB_CBP_AUTO 0xff
#define HENC_SKIP_RUN_AUTO 0xffff
extern bool amvenc_avc_on(void);
#endif

View File

@@ -0,0 +1 @@
obj-m += vpu.o

View File

@@ -0,0 +1,665 @@
/*
* vmm.h
*
* memory allocator for VPU
*
* Copyright (C) 2006 - 2013 CHIPS&MEDIA INC.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
*/
#ifndef __CNM_VIDEO_MEMORY_MANAGEMENT_H__
#define __CNM_VIDEO_MEMORY_MANAGEMENT_H__
#define VMEM_PAGE_SIZE (16 * 1024)
#define MAKE_KEY(_a, _b) (((vmem_key_t)_a) << 32 | _b)
#define KEY_TO_VALUE(_key) (_key >> 32)
#define VMEM_P_ALLOC(_x) vmalloc(_x)
#define VMEM_P_FREE(_x) vfree(_x)
#define VMEM_ASSERT \
pr_info("VMEM_ASSERT at %s:%d\n", __FILE__, __LINE__)
#define VMEM_HEIGHT(_tree) (_tree == NULL ? -1 : _tree->height)
#define MAX(_a, _b) (_a >= _b ? _a : _b)
struct avl_node_t;
#define vmem_key_t unsigned long long
struct vmem_info_t {
ulong total_pages;
ulong alloc_pages;
ulong free_pages;
ulong page_size;
};
struct page_t {
s32 pageno;
ulong addr;
s32 used;
s32 alloc_pages;
s32 first_pageno;
};
struct avl_node_t {
vmem_key_t key;
s32 height;
struct page_t *page;
struct avl_node_t *left;
struct avl_node_t *right;
};
struct video_mm_t {
struct avl_node_t *free_tree;
struct avl_node_t *alloc_tree;
struct page_t *page_list;
s32 num_pages;
ulong base_addr;
ulong mem_size;
s32 free_page_count;
s32 alloc_page_count;
};
enum rotation_dir_t {
LEFT,
RIGHT
};
struct avl_node_data_t {
s32 key;
struct page_t *page;
};
static struct avl_node_t *make_avl_node(
vmem_key_t key,
struct page_t *page)
{
struct avl_node_t *node =
(struct avl_node_t *)VMEM_P_ALLOC(sizeof(struct avl_node_t));
node->key = key;
node->page = page;
node->height = 0;
node->left = NULL;
node->right = NULL;
return node;
}
static s32 get_balance_factor(struct avl_node_t *tree)
{
s32 factor = 0;
if (tree)
factor = VMEM_HEIGHT(tree->right) - VMEM_HEIGHT(tree->left);
return factor;
}
/*
* Left Rotation
*
* A B
* \ / \
* B => A C
* / \ \
* D C D
*
*/
static struct avl_node_t *rotation_left(struct avl_node_t *tree)
{
struct avl_node_t *rchild;
struct avl_node_t *lchild;
if (tree == NULL)
return NULL;
rchild = tree->right;
if (rchild == NULL)
return tree;
lchild = rchild->left;
rchild->left = tree;
tree->right = lchild;
tree->height =
MAX(VMEM_HEIGHT(tree->left), VMEM_HEIGHT(tree->right)) + 1;
rchild->height =
MAX(VMEM_HEIGHT(rchild->left), VMEM_HEIGHT(rchild->right)) + 1;
return rchild;
}
/*
* Reft Rotation
*
* A B
* \ / \
* B => D A
* / \ /
* D C C
*
*/
static struct avl_node_t *rotation_right(struct avl_node_t *tree)
{
struct avl_node_t *rchild;
struct avl_node_t *lchild;
if (tree == NULL)
return NULL;
lchild = tree->left;
if (lchild == NULL)
return NULL;
rchild = lchild->right;
lchild->right = tree;
tree->left = rchild;
tree->height =
MAX(VMEM_HEIGHT(tree->left),
VMEM_HEIGHT(tree->right)) + 1;
lchild->height =
MAX(VMEM_HEIGHT(lchild->left),
VMEM_HEIGHT(lchild->right)) + 1;
return lchild;
}
static struct avl_node_t *do_balance(struct avl_node_t *tree)
{
s32 bfactor = 0, child_bfactor;
bfactor = get_balance_factor(tree);
if (bfactor >= 2) {
child_bfactor = get_balance_factor(tree->right);
if (child_bfactor == 1 || child_bfactor == 0) {
tree = rotation_left(tree);
} else if (child_bfactor == -1) {
tree->right = rotation_right(tree->right);
tree = rotation_left(tree);
} else {
pr_info(
"invalid balancing factor: %d\n",
child_bfactor);
VMEM_ASSERT;
return NULL;
}
} else if (bfactor <= -2) {
child_bfactor = get_balance_factor(tree->left);
if (child_bfactor == -1 || child_bfactor == 0) {
tree = rotation_right(tree);
} else if (child_bfactor == 1) {
tree->left = rotation_left(tree->left);
tree = rotation_right(tree);
} else {
pr_info(
"invalid balancing factor: %d\n",
child_bfactor);
VMEM_ASSERT;
return NULL;
}
}
return tree;
}
static struct avl_node_t *unlink_end_node(
struct avl_node_t *tree,
s32 dir,
struct avl_node_t **found_node)
{
struct avl_node_t *node;
*found_node = NULL;
if (tree == NULL)
return NULL;
if (dir == LEFT) {
if (tree->left == NULL) {
*found_node = tree;
return NULL;
}
} else {
if (tree->right == NULL) {
*found_node = tree;
return NULL;
}
}
if (dir == LEFT) {
node = tree->left;
tree->left = unlink_end_node(tree->left, LEFT, found_node);
if (tree->left == NULL) {
tree->left = (*found_node)->right;
(*found_node)->left = NULL;
(*found_node)->right = NULL;
}
} else {
node = tree->right;
tree->right = unlink_end_node(tree->right, RIGHT, found_node);
if (tree->right == NULL) {
tree->right = (*found_node)->left;
(*found_node)->left = NULL;
(*found_node)->right = NULL;
}
}
tree->height =
MAX(VMEM_HEIGHT(tree->left), VMEM_HEIGHT(tree->right)) + 1;
return do_balance(tree);
}
static struct avl_node_t *avltree_insert(
struct avl_node_t *tree,
vmem_key_t key,
struct page_t *page)
{
if (tree == NULL) {
tree = make_avl_node(key, page);
} else {
if (key >= tree->key)
tree->right =
avltree_insert(tree->right, key, page);
else
tree->left =
avltree_insert(tree->left, key, page);
}
tree = do_balance(tree);
tree->height =
MAX(VMEM_HEIGHT(tree->left), VMEM_HEIGHT(tree->right)) + 1;
return tree;
}
static struct avl_node_t *do_unlink(struct avl_node_t *tree)
{
struct avl_node_t *node;
struct avl_node_t *end_node;
node = unlink_end_node(tree->right, LEFT, &end_node);
if (node) {
tree->right = node;
} else {
node =
unlink_end_node(tree->left, RIGHT, &end_node);
if (node)
tree->left = node;
}
if (node == NULL) {
node = tree->right ? tree->right : tree->left;
end_node = node;
}
if (end_node) {
end_node->left =
(tree->left != end_node) ?
tree->left : end_node->left;
end_node->right =
(tree->right != end_node) ?
tree->right : end_node->right;
end_node->height =
MAX(VMEM_HEIGHT(end_node->left),
VMEM_HEIGHT(end_node->right)) + 1;
}
tree = end_node;
return tree;
}
static struct avl_node_t *avltree_remove(
struct avl_node_t *tree,
struct avl_node_t **found_node,
vmem_key_t key)
{
*found_node = NULL;
if (tree == NULL) {
pr_info("failed to find key %d\n", (s32)key);
return NULL;
}
if (key == tree->key) {
*found_node = tree;
tree = do_unlink(tree);
} else if (key > tree->key) {
tree->right =
avltree_remove(tree->right, found_node, key);
} else {
tree->left =
avltree_remove(tree->left, found_node, key);
}
if (tree)
tree->height =
MAX(VMEM_HEIGHT(tree->left),
VMEM_HEIGHT(tree->right)) + 1;
tree = do_balance(tree);
return tree;
}
void avltree_free(struct avl_node_t *tree)
{
if (tree == NULL)
return;
if (tree->left == NULL && tree->right == NULL) {
VMEM_P_FREE(tree);
return;
}
avltree_free(tree->left);
tree->left = NULL;
avltree_free(tree->right);
tree->right = NULL;
VMEM_P_FREE(tree);
}
static struct avl_node_t *remove_approx_value(
struct avl_node_t *tree,
struct avl_node_t **found,
vmem_key_t key)
{
*found = NULL;
if (tree == NULL)
return NULL;
if (key == tree->key) {
*found = tree;
tree = do_unlink(tree);
} else if (key > tree->key) {
tree->right = remove_approx_value(tree->right, found, key);
} else {
tree->left = remove_approx_value(tree->left, found, key);
if (*found == NULL) {
*found = tree;
tree = do_unlink(tree);
}
}
if (tree)
tree->height =
MAX(VMEM_HEIGHT(tree->left),
VMEM_HEIGHT(tree->right)) + 1;
tree = do_balance(tree);
return tree;
}
static void set_blocks_free(
struct video_mm_t *mm,
s32 pageno,
s32 npages)
{
s32 last_pageno = pageno + npages - 1;
s32 i;
struct page_t *page;
struct page_t *last_page;
if (npages == 0)
VMEM_ASSERT;
if (last_pageno >= mm->num_pages) {
pr_info(
"set_blocks_free: invalid last page number: %d\n",
last_pageno);
VMEM_ASSERT;
return;
}
for (i = pageno; i <= last_pageno; i++) {
mm->page_list[i].used = 0;
mm->page_list[i].alloc_pages = 0;
mm->page_list[i].first_pageno = -1;
}
page = &mm->page_list[pageno];
page->alloc_pages = npages;
last_page = &mm->page_list[last_pageno];
last_page->first_pageno = pageno;
mm->free_tree =
avltree_insert(mm->free_tree, MAKE_KEY(npages, pageno), page);
}
static void set_blocks_alloc(
struct video_mm_t *mm,
s32 pageno,
s32 npages)
{
s32 last_pageno = pageno + npages - 1;
s32 i;
struct page_t *page;
struct page_t *last_page;
if (last_pageno >= mm->num_pages) {
pr_info(
"set_blocks_free: invalid last page number: %d\n",
last_pageno);
VMEM_ASSERT;
return;
}
for (i = pageno; i <= last_pageno; i++) {
mm->page_list[i].used = 1;
mm->page_list[i].alloc_pages = 0;
mm->page_list[i].first_pageno = -1;
}
page = &mm->page_list[pageno];
page->alloc_pages = npages;
last_page = &mm->page_list[last_pageno];
last_page->first_pageno = pageno;
mm->alloc_tree =
avltree_insert(mm->alloc_tree, MAKE_KEY(page->addr, 0), page);
}
s32 vmem_init(struct video_mm_t *mm, ulong addr, ulong size)
{
s32 i;
if (mm == NULL)
return -1;
mm->base_addr = (addr + (VMEM_PAGE_SIZE - 1))
& ~(VMEM_PAGE_SIZE - 1);
mm->mem_size = size & ~VMEM_PAGE_SIZE;
mm->num_pages = mm->mem_size / VMEM_PAGE_SIZE;
mm->free_tree = NULL;
mm->alloc_tree = NULL;
mm->free_page_count = mm->num_pages;
mm->alloc_page_count = 0;
mm->page_list =
(struct page_t *)VMEM_P_ALLOC(
mm->num_pages * sizeof(struct page_t));
if (mm->page_list == NULL) {
pr_err("%s:%d failed to kmalloc(%ld)\n",
__func__, __LINE__,
mm->num_pages * sizeof(struct page_t));
return -1;
}
for (i = 0; i < mm->num_pages; i++) {
mm->page_list[i].pageno = i;
mm->page_list[i].addr =
mm->base_addr + i * VMEM_PAGE_SIZE;
mm->page_list[i].alloc_pages = 0;
mm->page_list[i].used = 0;
mm->page_list[i].first_pageno = -1;
}
set_blocks_free(mm, 0, mm->num_pages);
return 0;
}
s32 vmem_exit(struct video_mm_t *mm)
{
if (mm == NULL) {
pr_info("vmem_exit: invalid handle\n");
return -1;
}
if (mm->free_tree)
avltree_free(mm->free_tree);
if (mm->alloc_tree)
avltree_free(mm->alloc_tree);
if (mm->page_list) {
VMEM_P_FREE(mm->page_list);
mm->page_list = NULL;
}
mm->base_addr = 0;
mm->mem_size = 0;
mm->num_pages = 0;
mm->page_list = NULL;
mm->free_tree = NULL;
mm->alloc_tree = NULL;
mm->free_page_count = 0;
mm->alloc_page_count = 0;
return 0;
}
ulong vmem_alloc(struct video_mm_t *mm, s32 size, ulong pid)
{
struct avl_node_t *node;
struct page_t *free_page;
s32 npages, free_size;
s32 alloc_pageno;
ulong ptr;
if (mm == NULL) {
pr_info("vmem_alloc: invalid handle\n");
return -1;
}
if (size <= 0)
return -1;
npages = (size + VMEM_PAGE_SIZE - 1) / VMEM_PAGE_SIZE;
mm->free_tree = remove_approx_value(mm->free_tree,
&node, MAKE_KEY(npages, 0));
if (node == NULL)
return -1;
free_page = node->page;
free_size = KEY_TO_VALUE(node->key);
alloc_pageno = free_page->pageno;
set_blocks_alloc(mm, alloc_pageno, npages);
if (npages != free_size) {
s32 free_pageno = alloc_pageno + npages;
set_blocks_free(mm, free_pageno, (free_size-npages));
}
VMEM_P_FREE(node);
ptr = mm->page_list[alloc_pageno].addr;
mm->alloc_page_count += npages;
mm->free_page_count -= npages;
return ptr;
}
s32 vmem_free(struct video_mm_t *mm, ulong ptr, ulong pid)
{
ulong addr;
struct avl_node_t *found;
struct page_t *page;
s32 pageno, prev_free_pageno, next_free_pageno;
s32 prev_size, next_size;
s32 merge_page_no, merge_page_size, free_page_size;
if (mm == NULL) {
pr_info("vmem_free: invalid handle\n");
return -1;
}
addr = ptr;
mm->alloc_tree = avltree_remove(mm->alloc_tree, &found,
MAKE_KEY(addr, 0));
if (found == NULL) {
pr_info("vmem_free: 0x%08x not found\n", (s32)addr);
VMEM_ASSERT;
return -1;
}
/* find previous free block */
page = found->page;
pageno = page->pageno;
free_page_size = page->alloc_pages;
prev_free_pageno = pageno - 1;
prev_size = -1;
if (prev_free_pageno >= 0) {
if (mm->page_list[prev_free_pageno].used == 0) {
prev_free_pageno =
mm->page_list[prev_free_pageno].first_pageno;
prev_size =
mm->page_list[prev_free_pageno].alloc_pages;
}
}
/* find next free block */
next_free_pageno = pageno + page->alloc_pages;
next_free_pageno =
(next_free_pageno == mm->num_pages) ? -1 : next_free_pageno;
next_size = -1;
if (next_free_pageno >= 0) {
if (mm->page_list[next_free_pageno].used == 0) {
next_size =
mm->page_list[next_free_pageno].alloc_pages;
}
}
VMEM_P_FREE(found);
/* merge */
merge_page_no = page->pageno;
merge_page_size = page->alloc_pages;
if (prev_size >= 0) {
mm->free_tree = avltree_remove(mm->free_tree, &found,
MAKE_KEY(prev_size, prev_free_pageno));
if (found == NULL) {
VMEM_ASSERT;
return -1;
}
merge_page_no = found->page->pageno;
merge_page_size += found->page->alloc_pages;
VMEM_P_FREE(found);
}
if (next_size >= 0) {
mm->free_tree = avltree_remove(mm->free_tree, &found,
MAKE_KEY(next_size, next_free_pageno));
if (found == NULL) {
VMEM_ASSERT;
return -1;
}
merge_page_size += found->page->alloc_pages;
VMEM_P_FREE(found);
}
page->alloc_pages = 0;
page->first_pageno = -1;
set_blocks_free(mm, merge_page_no, merge_page_size);
mm->alloc_page_count -= free_page_size;
mm->free_page_count += free_page_size;
return 0;
}
s32 vmem_get_info(struct video_mm_t *mm, struct vmem_info_t *info)
{
if (mm == NULL) {
pr_info("vmem_get_info: invalid handle\n");
return -1;
}
if (info == NULL)
return -1;
info->total_pages = mm->num_pages;
info->alloc_pages = mm->alloc_page_count;
info->free_pages = mm->free_page_count;
info->page_size = VMEM_PAGE_SIZE;
return 0;
}
#endif /* __CNM_VIDEO_MEMORY_MANAGEMENT_H__ */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,306 @@
/*
* vpu.h
*
* linux device driver for VPU.
*
* Copyright (C) 2006 - 2013 CHIPS&MEDIA INC.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
*/
#ifndef __VPU_DRV_H__
#define __VPU_DRV_H__
#include <linux/fs.h>
#include <linux/types.h>
#include <linux/compat.h>
#define MAX_INST_HANDLE_SIZE (32*1024)
#define MAX_NUM_INSTANCE 4
#define MAX_NUM_VPU_CORE 1
#define W4_CMD_INIT_VPU (0x0001)
#define W4_CMD_SLEEP_VPU (0x0400)
#define W4_CMD_WAKEUP_VPU (0x0800)
/* GXM: 2000/10 = 200M */
#define HevcEnc_L0() WRITE_HHI_REG(HHI_WAVE420L_CLK_CNTL, \
(3 << 25) | (1 << 16) | (3 << 9) | (1 << 0))
/* GXM: 2000/8 = 250M */
#define HevcEnc_L1() WRITE_HHI_REG(HHI_WAVE420L_CLK_CNTL, \
(1 << 25) | (1 << 16) | (1 << 9) | (1 << 0))
/* GXM: 2000/7 = 285M */
#define HevcEnc_L2() WRITE_HHI_REG(HHI_WAVE420L_CLK_CNTL, \
(4 << 25) | (0 << 16) | (4 << 9) | (0 << 0))
/*GXM: 2000/6 = 333M */
#define HevcEnc_L3() WRITE_HHI_REG(HHI_WAVE420L_CLK_CNTL, \
(2 << 25) | (1 << 16) | (2 << 9) | (1 << 0))
/* GXM: 2000/5 = 400M */
#define HevcEnc_L4() WRITE_HHI_REG(HHI_WAVE420L_CLK_CNTL, \
(3 << 25) | (0 << 16) | (3 << 9) | (0 << 0))
/* GXM: 2000/4 = 500M */
#define HevcEnc_L5() WRITE_HHI_REG(HHI_WAVE420L_CLK_CNTL, \
(1 << 25) | (0 << 16) | (1 << 9) | (0 << 0))
/* GXM: 2000/3 = 667M */
#define HevcEnc_L6() WRITE_HHI_REG(HHI_WAVE420L_CLK_CNTL, \
(2 << 25) | (0 << 16) | (2 << 9) | (0 << 0))
#define HevcEnc_clock_enable(level) \
do { \
WRITE_HHI_REG(HHI_WAVE420L_CLK_CNTL, \
READ_HHI_REG(HHI_WAVE420L_CLK_CNTL) \
& (~(1 << 8)) & (~(1 << 24))); \
if (level == 0) \
HevcEnc_L0(); \
else if (level == 1) \
HevcEnc_L1(); \
else if (level == 2) \
HevcEnc_L2(); \
else if (level == 3) \
HevcEnc_L3(); \
else if (level == 4) \
HevcEnc_L4(); \
else if (level == 5) \
HevcEnc_L5(); \
else if (level == 6) \
HevcEnc_L6(); \
WRITE_HHI_REG(HHI_WAVE420L_CLK_CNTL, \
READ_HHI_REG(HHI_WAVE420L_CLK_CNTL) \
| (1 << 8) | (1 << 24)); \
} while (0)
#define HevcEnc_clock_disable() \
WRITE_HHI_REG(HHI_WAVE420L_CLK_CNTL, \
READ_HHI_REG(HHI_WAVE420L_CLK_CNTL) \
& (~(1 << 8)) & (~(1 << 24)))
/* ACLK 667MHZ */
#define HevcEnc_MoreClock_enable() \
do { \
WRITE_HHI_REG(HHI_WAVE420L_CLK_CNTL2, \
READ_HHI_REG(HHI_WAVE420L_CLK_CNTL2) \
& (~(1 << 8))); \
WRITE_HHI_REG(HHI_WAVE420L_CLK_CNTL2, \
(2 << 9) | (0 << 0)); \
WRITE_HHI_REG(HHI_WAVE420L_CLK_CNTL2, \
READ_HHI_REG(HHI_WAVE420L_CLK_CNTL2) \
| (1 << 8)); \
} while (0)
#define HevcEnc_MoreClock_disable() \
WRITE_HHI_REG(HHI_WAVE420L_CLK_CNTL2, \
READ_HHI_REG(HHI_WAVE420L_CLK_CNTL2) \
& (~(1 << 8)))
struct compat_vpudrv_buffer_t {
u32 size;
u32 cached;
compat_ulong_t phys_addr;
compat_ulong_t base; /* kernel logical address in use kernel */
compat_ulong_t virt_addr; /* virtual user space address */
};
struct vpudrv_buffer_t {
u32 size;
u32 cached;
ulong phys_addr;
ulong base; /* kernel logical address in use kernel */
ulong virt_addr; /* virtual user space address */
};
struct vpu_bit_firmware_info_t {
u32 size; /* size of this structure*/
u32 core_idx;
u32 reg_base_offset;
u16 bit_code[512];
};
struct vpudrv_inst_info_t {
u32 core_idx;
u32 inst_idx;
s32 inst_open_count; /* for output only*/
};
struct vpudrv_intr_info_t {
u32 timeout;
s32 intr_reason;
};
struct vpu_drv_context_t {
struct fasync_struct *async_queue;
ulong interrupt_reason;
u32 open_count; /*!<< device reference count. Not instance count */
};
/* To track the allocated memory buffer */
struct vpudrv_buffer_pool_t {
struct list_head list;
struct vpudrv_buffer_t vb;
struct file *filp;
};
/* To track the instance index and buffer in instance pool */
struct vpudrv_instanace_list_t {
struct list_head list;
ulong inst_idx;
ulong core_idx;
struct file *filp;
};
struct vpudrv_instance_pool_t {
u8 codecInstPool[MAX_NUM_INSTANCE][MAX_INST_HANDLE_SIZE];
};
#define VPUDRV_BUF_LEN struct vpudrv_buffer_t
#define VPUDRV_BUF_LEN32 struct compat_vpudrv_buffer_t
#define VPUDRV_INST_LEN struct vpudrv_inst_info_t
#define VDI_MAGIC 'V'
#define VDI_IOCTL_ALLOCATE_PHYSICAL_MEMORY \
_IOW(VDI_MAGIC, 0, VPUDRV_BUF_LEN)
#define VDI_IOCTL_FREE_PHYSICALMEMORY \
_IOW(VDI_MAGIC, 1, VPUDRV_BUF_LEN)
#define VDI_IOCTL_WAIT_INTERRUPT \
_IOW(VDI_MAGIC, 2, struct vpudrv_intr_info_t)
#define VDI_IOCTL_SET_CLOCK_GATE \
_IOW(VDI_MAGIC, 3, u32)
#define VDI_IOCTL_RESET \
_IOW(VDI_MAGIC, 4, u32)
#define VDI_IOCTL_GET_INSTANCE_POOL \
_IOW(VDI_MAGIC, 5, VPUDRV_BUF_LEN)
#define VDI_IOCTL_GET_COMMON_MEMORY \
_IOW(VDI_MAGIC, 6, VPUDRV_BUF_LEN)
#define VDI_IOCTL_GET_RESERVED_VIDEO_MEMORY_INFO \
_IOW(VDI_MAGIC, 8, VPUDRV_BUF_LEN)
#define VDI_IOCTL_OPEN_INSTANCE \
_IOW(VDI_MAGIC, 9, VPUDRV_INST_LEN)
#define VDI_IOCTL_CLOSE_INSTANCE \
_IOW(VDI_MAGIC, 10, VPUDRV_INST_LEN)
#define VDI_IOCTL_GET_INSTANCE_NUM \
_IOW(VDI_MAGIC, 11, VPUDRV_INST_LEN)
#define VDI_IOCTL_GET_REGISTER_INFO \
_IOW(VDI_MAGIC, 12, VPUDRV_BUF_LEN)
#define VDI_IOCTL_FLUSH_BUFFER \
_IOW(VDI_MAGIC, 13, VPUDRV_BUF_LEN)
#define VDI_IOCTL_ALLOCATE_PHYSICAL_MEMORY32 \
_IOW(VDI_MAGIC, 0, VPUDRV_BUF_LEN32)
#define VDI_IOCTL_FREE_PHYSICALMEMORY32 \
_IOW(VDI_MAGIC, 1, VPUDRV_BUF_LEN32)
#define VDI_IOCTL_GET_INSTANCE_POOL32 \
_IOW(VDI_MAGIC, 5, VPUDRV_BUF_LEN32)
#define VDI_IOCTL_GET_COMMON_MEMORY32 \
_IOW(VDI_MAGIC, 6, VPUDRV_BUF_LEN32)
#define VDI_IOCTL_GET_RESERVED_VIDEO_MEMORY_INFO32 \
_IOW(VDI_MAGIC, 8, VPUDRV_BUF_LEN32)
#define VDI_IOCTL_GET_REGISTER_INFO32 \
_IOW(VDI_MAGIC, 12, VPUDRV_BUF_LEN32)
#define VDI_IOCTL_FLUSH_BUFFER32 \
_IOW(VDI_MAGIC, 13, VPUDRV_BUF_LEN32)
enum {
W4_INT_INIT_VPU = 0,
W4_INT_DEC_PIC_HDR = 1,
W4_INT_SET_PARAM = 1,
W4_INT_ENC_INIT_SEQ = 1,
W4_INT_FINI_SEQ = 2,
W4_INT_DEC_PIC = 3,
W4_INT_ENC_PIC = 3,
W4_INT_SET_FRAMEBUF = 4,
W4_INT_FLUSH_DEC = 5,
W4_INT_ENC_SLICE_INT = 7,
W4_INT_GET_FW_VERSION = 8,
W4_INT_QUERY_DEC = 9,
W4_INT_SLEEP_VPU = 10,
W4_INT_WAKEUP_VPU = 11,
W4_INT_CHANGE_INT = 12,
W4_INT_CREATE_INSTANCE = 14,
W4_INT_BSBUF_EMPTY = 15,
/*!<< Bitstream buffer empty[dec]/full[enc] */
};
/* WAVE4 registers */
#define VPU_REG_BASE_ADDR 0xc8810000
#define VPU_REG_SIZE (0x4000 * MAX_NUM_VPU_CORE)
#define W4_REG_BASE 0x0000
#define W4_VPU_BUSY_STATUS (W4_REG_BASE + 0x0070)
#define W4_VPU_INT_REASON_CLEAR (W4_REG_BASE + 0x0034)
#define W4_VPU_VINT_CLEAR (W4_REG_BASE + 0x003C)
#define W4_VPU_VPU_INT_STS (W4_REG_BASE + 0x0044)
#define W4_VPU_INT_REASON (W4_REG_BASE + 0x004c)
#define W4_RET_SUCCESS (W4_REG_BASE + 0x0110)
#define W4_RET_FAIL_REASON (W4_REG_BASE + 0x0114)
/* WAVE4 INIT, WAKEUP */
#define W4_PO_CONF (W4_REG_BASE + 0x0000)
#define W4_VCPU_CUR_PC (W4_REG_BASE + 0x0004)
#define W4_VPU_VINT_ENABLE (W4_REG_BASE + 0x0048)
#define W4_VPU_RESET_REQ (W4_REG_BASE + 0x0050)
#define W4_VPU_RESET_STATUS (W4_REG_BASE + 0x0054)
#define W4_VPU_REMAP_CTRL (W4_REG_BASE + 0x0060)
#define W4_VPU_REMAP_VADDR (W4_REG_BASE + 0x0064)
#define W4_VPU_REMAP_PADDR (W4_REG_BASE + 0x0068)
#define W4_VPU_REMAP_CORE_START (W4_REG_BASE + 0x006C)
#define W4_VPU_BUSY_STATUS (W4_REG_BASE + 0x0070)
#define W4_HW_OPTION (W4_REG_BASE + 0x0124)
#define W4_CODE_SIZE (W4_REG_BASE + 0x011C)
/* Note: W4_INIT_CODE_BASE_ADDR should be aligned to 4KB */
#define W4_ADDR_CODE_BASE (W4_REG_BASE + 0x0118)
#define W4_CODE_PARAM (W4_REG_BASE + 0x0120)
#define W4_INIT_VPU_TIME_OUT_CNT (W4_REG_BASE + 0x0134)
/* WAVE4 Wave4BitIssueCommand */
#define W4_CORE_INDEX (W4_REG_BASE + 0x0104)
#define W4_INST_INDEX (W4_REG_BASE + 0x0108)
#define W4_COMMAND (W4_REG_BASE + 0x0100)
#define W4_VPU_HOST_INT_REQ (W4_REG_BASE + 0x0038)
#define W4_BS_RD_PTR (W4_REG_BASE + 0x0130)
#define W4_BS_WR_PTR (W4_REG_BASE + 0x0134)
#define W4_RET_ENC_PIC_BYTE (W4_REG_BASE + 0x01C8)
#define W4_REMAP_CODE_INDEX 0
#define ReadVpuRegister(addr) \
readl((void __iomem *)(s_vpu_register.virt_addr \
+ s_bit_firmware_info[core].reg_base_offset + addr))
#define WriteVpuRegister(addr, val) \
writel((u32)val, (void __iomem *)(s_vpu_register.virt_addr \
+ s_bit_firmware_info[core].reg_base_offset + addr))
#define WriteVpu(addr, val) writel((u32)val, (void __iomem *)addr)
#endif

View File

@@ -0,0 +1,17 @@
obj-m += stream_input.o
stream_input-objs += amports/amstream.o
stream_input-objs += amports/amstream_profile.o
stream_input-objs += amports/adec.o
stream_input-objs += parser/thread_rw.o
stream_input-objs += parser/streambuf.o
stream_input-objs += parser/esparser.o
stream_input-objs += parser/tsdemux.o
stream_input-objs += parser/psparser.o
stream_input-objs += parser/rmparser.o
obj-y += parser/hw_demux/
obj-y += tv_frontend/
# obj-y += box-frontend/avl6211/
# obj-y += box-frontend/atbm8881/
# obj-y += box-frontend/avl68xx/

View File

@@ -0,0 +1,2 @@
obj-y += amports.o
amports-objs += amstream.o amstream_profile.o adec.o

View File

@@ -0,0 +1,393 @@
/*
* 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 <linux/amlogic/media/codec_mm/configs.h>
#include "../parser/streambuf.h"
#include <linux/module.h>
#include <linux/of.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;
int offset;
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_dra",
"amadec_sipr",
"amadec_truehd",
"amadec_mpeg1",
"amadec_mpeg2",
"amadec_wmavoi",
"amadec_wmalossless",
"amadec_pcm_s24le",
"adec_max"
};
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 ssize_t addr_offset_show(struct class *class,
struct class_attribute *attr, char *buf)
{
return sprintf(buf, "%d\n", astream_dev->offset);
}
static struct class_attribute astream_class_attrs[] = {
__ATTR_RO(format),
__ATTR_RO(samplerate),
__ATTR_RO(channum),
__ATTR_RO(datawidth),
__ATTR_RO(pts),
__ATTR_RO(addr_offset),
__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;
}
int amstream_adec_show_fun(const char *trigger, int id, char *sbuf, int size)
{
int ret = -1;
void *buf, *getbuf = NULL;
if (size < PAGE_SIZE) {
void *getbuf = (void *)__get_free_page(GFP_KERNEL);
if (!getbuf)
return -ENOMEM;
buf = getbuf;
} else {
buf = sbuf;
}
switch (trigger[0]) {
case 'f':
ret = format_show(NULL, NULL, buf);
break;
case 's':
ret = samplerate_show(NULL, NULL, buf);
break;
case 'c':
ret = channum_show(NULL, NULL, buf);
break;
case 'd':
ret = datawidth_show(NULL, NULL, buf);
break;
case 'p':
ret = pts_show(NULL, NULL, buf);
break;
default:
ret = -1;
}
if (ret > 0 && getbuf != NULL) {
int ret = min_t(int, ret, size);
strncpy(sbuf, buf, ret);
}
if (getbuf != NULL)
free_page((unsigned long)getbuf);
return ret;
}
static struct mconfig adec_configs[] = {
MC_FUN("format", &amstream_adec_show_fun, NULL),
MC_FUN("samplerate", &amstream_adec_show_fun, NULL),
MC_FUN("channum", &amstream_adec_show_fun, NULL),
MC_FUN("datawidth", &amstream_adec_show_fun, NULL),
MC_FUN("pts", &amstream_adec_show_fun, NULL),
};
static struct mconfig_node adec_node;
s32 astream_dev_register(void)
{
s32 r;
struct device_node *node;
unsigned int cbus_base = 0xffd00000;
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) {
pr_info("astream device create fail.\n");
r = -ENOMEM;
goto err_3;
}
astream_dev->dev.class = &astream_class;
astream_dev->dev.release = astream_release;
astream_dev->offset = 0;
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 (MESON_CPU_MAJOR_ID_TXL < get_cpu_type()) {
node = of_find_node_by_path("/codec_io/io_cbus_base");
if (!node) {
pr_info("No io_cbus_base node found.");
goto err_1;
}
r = of_property_read_u32_index(node, "reg", 1, &cbus_base);
if (r) {
pr_info("No find node.\n");
goto err_1;
}
/*need to offset -0x100 in txlx.*/
astream_dev->offset = -0x100;
/*need to offset -0x180 in g12a.*/
if (MESON_CPU_MAJOR_ID_G12A <= get_cpu_type())
astream_dev->offset = -0x180;
astream_uio_info.mem[0].addr =
(cbus_base + CBUS_REG_OFFSET(AIU_AIFIFO_CTRL +
astream_dev->offset)) & (PAGE_MASK);
astream_uio_info.mem[3].addr =
(cbus_base + CBUS_REG_OFFSET(ASSIST_HW_REV +
0x100)) & (PAGE_MASK);
}
#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
INIT_REG_NODE_CONFIGS("media", &adec_node,
"adec", adec_configs, CONFIG_FOR_R);
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);
}
}

View File

@@ -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 */

View File

@@ -0,0 +1,52 @@
/*
* 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 <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);
/*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);
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -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);

View File

@@ -0,0 +1,939 @@
/*
* 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_PARSER_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_AIU_REG(AIU_MEM_AIFIFO_MAN_WP, wp/* & 0xffffff00*/);
}
return;
}
static irqreturn_t esparser_isr(int irq, void *dev_id)
{
u32 int_status = READ_PARSER_REG(PARSER_INT_STATUS);
WRITE_PARSER_REG(PARSER_INT_STATUS, int_status);
if (int_status & PARSER_INTSTAT_SC_FOUND) {
WRITE_PARSER_REG(PFIFO_RD_PTR, 0);
WRITE_PARSER_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_PARSER_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_AIU_REG(AIU_MEM_AIFIFO_MAN_WP) :
READ_PARSER_REG(PARSER_SUB_START_PTR);
} else {
wp =
#if 1/* MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON8 */
(type == BUF_TYPE_HEVC) ? READ_PARSER_REG(PARSER_VIDEO_WP) :
#endif
(type == BUF_TYPE_VIDEO) ? READ_PARSER_REG(PARSER_VIDEO_WP) :
(type == BUF_TYPE_AUDIO) ?
READ_AIU_REG(AIU_MEM_AIFIFO_MAN_WP) :
READ_PARSER_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_PARSER_REG(PFIFO_RD_PTR, 0);
WRITE_PARSER_REG(PFIFO_WR_PTR, 0);
WRITE_PARSER_REG_BITS(PARSER_CONTROL, len, ES_PACK_SIZE_BIT,
ES_PACK_SIZE_WID);
WRITE_PARSER_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_PARSER_REG(PARSER_FETCH_ADDR, buf_32);
} else {
WRITE_PARSER_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_PARSER_REG(PARSER_FETCH_CMD,
(7 << FETCH_ENDIAN) | len);
WRITE_PARSER_REG(PARSER_FETCH_ADDR, search_pattern_map);
WRITE_PARSER_REG(PARSER_FETCH_CMD,
(7 << FETCH_ENDIAN) | SEARCH_PATTERN_LEN);
} else {
WRITE_PARSER_REG(PARSER_FETCH_CMD,
(7 << FETCH_ENDIAN) | (len + 512));
}
ret = wait_event_interruptible_timeout(wq, search_done != 0,
HZ / 5);
if (ret == 0) {
WRITE_PARSER_REG(PARSER_FETCH_CMD, 0);
if (wp == buf_wp(type)) {
/*no data fetched */
return -EAGAIN;
} else {
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)
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_PARSER_REG(PARSER_SUB_START_PTR);
parser_sub_end_ptr = READ_PARSER_REG(PARSER_SUB_END_PTR);
parser_sub_rp = READ_PARSER_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_RESET_REG(RESET1_REGISTER, RESET_PARSER);
/* TS data path */
#ifndef CONFIG_AM_DVB
WRITE_DEMUX_REG(FEC_INPUT_CONTROL, 0);
#else
tsdemux_set_reset_flag();
#endif
CLEAR_DEMUX_REG_MASK(TS_HIU_CTL, 1 << USE_HI_BSF_INTERFACE);
CLEAR_DEMUX_REG_MASK(TS_HIU_CTL_2, 1 << USE_HI_BSF_INTERFACE);
CLEAR_DEMUX_REG_MASK(TS_HIU_CTL_3, 1 << USE_HI_BSF_INTERFACE);
CLEAR_DEMUX_REG_MASK(TS_FILE_CONFIG, (1 << TS_HIU_ENABLE));
WRITE_PARSER_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_PARSER_REG(PFIFO_RD_PTR, 0);
WRITE_PARSER_REG(PFIFO_WR_PTR, 0);
WRITE_PARSER_REG(PARSER_SEARCH_PATTERN, ES_START_CODE_PATTERN);
WRITE_PARSER_REG(PARSER_SEARCH_MASK, ES_START_CODE_MASK);
WRITE_PARSER_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_PARSER_REG(PARSER_CONTROL, PARSER_AUTOSEARCH);
}
/* hook stream buffer with PARSER */
if (has_hevc_vdec() && (pts_type == PTS_TYPE_HEVC)) {
WRITE_PARSER_REG(PARSER_VIDEO_START_PTR, vdec->input.start);
WRITE_PARSER_REG(PARSER_VIDEO_END_PTR, vdec->input.start
+ vdec->input.size - 8);
if (vdec_single(vdec)) {
CLEAR_PARSER_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_PARSER_REG_MASK(PARSER_ES_CONTROL,
ES_VID_MAN_RD_PTR);
WRITE_PARSER_REG(PARSER_VIDEO_WP, vdec->input.start);
WRITE_PARSER_REG(PARSER_VIDEO_RP, vdec->input.start);
}
video_data_parsed = 0;
} else if (pts_type == PTS_TYPE_VIDEO) {
WRITE_PARSER_REG(PARSER_VIDEO_START_PTR,
vdec->input.start);
WRITE_PARSER_REG(PARSER_VIDEO_END_PTR,
vdec->input.start + vdec->input.size - 8);
if (vdec_single(vdec)) {
CLEAR_PARSER_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_PARSER_REG_MASK(PARSER_ES_CONTROL,
ES_VID_MAN_RD_PTR);
WRITE_PARSER_REG(PARSER_VIDEO_WP,
vdec->input.start);
WRITE_PARSER_REG(PARSER_VIDEO_RP,
vdec->input.start);
}
video_data_parsed = 0;
} else if (pts_type == PTS_TYPE_AUDIO) {
/* set wp as buffer start */
SET_AIU_REG_MASK(AIU_MEM_AIFIFO_BUF_CNTL,
MEM_BUFCTRL_MANUAL);
WRITE_AIU_REG(AIU_MEM_AIFIFO_MAN_RP,
READ_AIU_REG(AIU_MEM_AIFIFO_START_PTR));
WRITE_AIU_REG_BITS(AIU_MEM_AIFIFO_CONTROL, 7, 3, 3);
SET_AIU_REG_MASK(AIU_MEM_AIFIFO_BUF_CNTL,
MEM_BUFCTRL_INIT);
CLEAR_AIU_REG_MASK(AIU_MEM_AIFIFO_BUF_CNTL,
MEM_BUFCTRL_INIT);
WRITE_AIU_REG(AIU_MEM_AIFIFO_MAN_WP,
READ_AIU_REG(AIU_MEM_AIFIFO_START_PTR));
audio_data_parsed = 0;
audio_buf_start =
READ_AIU_REG(AIU_MEM_AIFIFO_START_PTR);
audio_real_wp = audio_buf_start;
audio_buf_end = READ_AIU_REG(AIU_MEM_AIFIFO_END_PTR);
} else if (buf->type == BUF_TYPE_SUBTITLE) {
WRITE_PARSER_REG(PARSER_SUB_START_PTR,
parser_sub_start_ptr);
WRITE_PARSER_REG(PARSER_SUB_END_PTR,
parser_sub_end_ptr);
WRITE_PARSER_REG(PARSER_SUB_RP, parser_sub_rp);
SET_PARSER_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_PARSER_REG(PARSER_INT_STATUS, 0xffff);
WRITE_PARSER_REG(PARSER_INT_ENABLE,
PARSER_INTSTAT_SC_FOUND <<
PARSER_INT_HOST_EN_BIT);
}
mutex_unlock(&esparser_mutex);
if (!(vdec_get_debug_flags() & 1) &&
!codec_mm_video_tvp_enabled()) {
int block_size = (buf->type == BUF_TYPE_AUDIO) ?
PAGE_SIZE : PAGE_SIZE << 4;
int buf_num = (buf->type == BUF_TYPE_AUDIO) ?
20 : (2 * SZ_1M)/(PAGE_SIZE << 4);
if (!(buf->type == BUF_TYPE_SUBTITLE))
buf->write_thread = threadrw_alloc(buf_num,
block_size,
esparser_write_ex,
(buf->type == BUF_TYPE_AUDIO) ? 1 : 0);
/*manul mode for audio*/
}
return 0;
Err_2:
pts_stop(pts_type);
Err_1:
atomic_dec(&esparser_use_count);
buf->flag &= ~BUF_FLAG_PARSER;
mutex_unlock(&esparser_mutex);
return r;
}
void esparser_audio_reset_s(struct stream_buf_s *buf)
{
ulong flags;
DEFINE_SPINLOCK(lock);
spin_lock_irqsave(&lock, flags);
SET_AIU_REG_MASK(AIU_MEM_AIFIFO_BUF_CNTL, MEM_BUFCTRL_MANUAL);
WRITE_AIU_REG(AIU_MEM_AIFIFO_MAN_RP,
READ_AIU_REG(AIU_MEM_AIFIFO_START_PTR));
WRITE_AIU_REG_BITS(AIU_MEM_AIFIFO_CONTROL, 7, 3, 3);
SET_AIU_REG_MASK(AIU_MEM_AIFIFO_BUF_CNTL, MEM_BUFCTRL_INIT);
CLEAR_AIU_REG_MASK(AIU_MEM_AIFIFO_BUF_CNTL, MEM_BUFCTRL_INIT);
WRITE_AIU_REG(AIU_MEM_AIFIFO_MAN_WP,
READ_AIU_REG(AIU_MEM_AIFIFO_START_PTR));
buf->flag |= BUF_FLAG_PARSER;
audio_data_parsed = 0;
audio_real_wp = READ_AIU_REG(AIU_MEM_AIFIFO_START_PTR);
audio_buf_start = READ_AIU_REG(AIU_MEM_AIFIFO_START_PTR);
audio_buf_end = READ_AIU_REG(AIU_MEM_AIFIFO_END_PTR);
spin_unlock_irqrestore(&lock, flags);
return;
}
void esparser_audio_reset(struct stream_buf_s *buf)
{
ulong flags;
DEFINE_SPINLOCK(lock);
spin_lock_irqsave(&lock, flags);
WRITE_PARSER_REG(PARSER_AUDIO_WP,
READ_AIU_REG(AIU_MEM_AIFIFO_START_PTR));
WRITE_PARSER_REG(PARSER_AUDIO_RP,
READ_AIU_REG(AIU_MEM_AIFIFO_START_PTR));
WRITE_PARSER_REG(PARSER_AUDIO_START_PTR,
READ_AIU_REG(AIU_MEM_AIFIFO_START_PTR));
WRITE_PARSER_REG(PARSER_AUDIO_END_PTR,
READ_AIU_REG(AIU_MEM_AIFIFO_END_PTR));
CLEAR_PARSER_REG_MASK(PARSER_ES_CONTROL, ES_AUD_MAN_RD_PTR);
WRITE_AIU_REG(AIU_MEM_AIFIFO_BUF_CNTL, MEM_BUFCTRL_INIT);
CLEAR_AIU_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_PARSER_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) {
/*should not write partial data in drm mode*/
stbuf_wait_space(stbuf, count);
if (stbuf_space(stbuf) < count)
return -EAGAIN;
}
len = min_t(u32, len, count);
mutex_lock(&esparser_mutex);
if (stbuf->type != BUF_TYPE_AUDIO)
r = _esparser_write((const char __user *)realbuf, len,
stbuf->type, isphybuf);
else
r = _esparser_write_s((const char __user *)realbuf, len,
stbuf->type);
if (r < 0) {
pr_info("drm_write _esparser_write failed [%d]\n", r);
return r;
}
havewritebytes += r;
leftcount = totalcount - havewritebytes;
if (havewritebytes == totalcount) {
mutex_unlock(&esparser_mutex);
break; /* write ok; */
} else if ((len > 0) && (havewritebytes < totalcount)) {
DRM_PRNT
("d writebytes[%d] want[%d] total[%d] real[%d]\n",
havewritebytes, len, totalcount, realcount);
len = len - r; /* write again; */
realbuf = realbuf + r;
} else {
pr_info
("e writebytes[%d] want[%d] total[%d] real[%d]\n",
havewritebytes, len, totalcount, realcount);
}
mutex_unlock(&esparser_mutex);
}
return re_count;
}
/*
*flags:
*1:phy
*2:noblock
*/
ssize_t esparser_write_ex(struct file *file,
struct stream_buf_s *stbuf,
const char __user *buf, size_t count,
int flags)
{
s32 r;
u32 len = count;
if (buf == NULL || count == 0)
return -EINVAL;
/*subtitle have no level to check, */
if (stbuf->type != BUF_TYPE_SUBTITLE && stbuf_space(stbuf) < count) {
if ((flags & 2) || ((file != NULL) &&
(file->f_flags & O_NONBLOCK))) {
len = stbuf_space(stbuf);
if (len < 256) /* <1k.do eagain, */
return -EAGAIN;
} else {
len = min(stbuf_canusesize(stbuf) / 8, len);
if (stbuf_space(stbuf) < len) {
r = stbuf_wait_space(stbuf, len);
if (r < 0)
return r;
}
}
}
stbuf->last_write_jiffies64 = jiffies_64;
len = min_t(u32, len, count);
mutex_lock(&esparser_mutex);
if (stbuf->type == BUF_TYPE_AUDIO)
r = _esparser_write_s(buf, len, stbuf->type);
else
r = _esparser_write(buf, len, stbuf->type, flags & 1);
mutex_unlock(&esparser_mutex);
return r;
}
ssize_t esparser_write(struct file *file,
struct stream_buf_s *stbuf,
const char __user *buf, size_t count)
{
if (stbuf->write_thread) {
ssize_t ret;
ret = threadrw_write(file, stbuf, buf, count);
if (ret == -EAGAIN) {
u32 a, b;
int vdelay, adelay;
if ((stbuf->type != BUF_TYPE_VIDEO) &&
(stbuf->type != BUF_TYPE_HEVC))
return ret;
if (stbuf->buf_size > (SZ_1M * 30) ||
(threadrw_buffer_size(stbuf) > SZ_1M * 10) ||
!threadrw_support_more_buffers(stbuf))
return ret;
/*only chang buffer for video.*/
vdelay = calculation_stream_delayed_ms(
PTS_TYPE_VIDEO, &a, &b);
adelay = calculation_stream_delayed_ms(
PTS_TYPE_AUDIO, &a, &b);
if ((vdelay > 100 && vdelay < 2000) && /*vdelay valid.*/
((vdelay < 500) ||/*video delay is short!*/
(adelay > 0 && adelay < 1000))/*audio is low.*/
) {
/*on buffer fulled.
*if delay is less than 100ms we think errors,
*And we add more buffer on delay < 2s.
*/
int new_size = 2 * 1024 * 1024;
threadrw_alloc_more_buffer_size(
stbuf, new_size);
}
}
return ret;
}
return esparser_write_ex(file, stbuf, buf, count, 0);
}
void esparser_sub_reset(void)
{
ulong flags;
DEFINE_SPINLOCK(lock);
u32 parser_sub_start_ptr;
u32 parser_sub_end_ptr;
spin_lock_irqsave(&lock, flags);
parser_sub_start_ptr = READ_PARSER_REG(PARSER_SUB_START_PTR);
parser_sub_end_ptr = READ_PARSER_REG(PARSER_SUB_END_PTR);
WRITE_PARSER_REG(PARSER_SUB_START_PTR, parser_sub_start_ptr);
WRITE_PARSER_REG(PARSER_SUB_END_PTR, parser_sub_end_ptr);
WRITE_PARSER_REG(PARSER_SUB_RP, parser_sub_start_ptr);
WRITE_PARSER_REG(PARSER_SUB_WP, parser_sub_start_ptr);
SET_PARSER_REG_MASK(PARSER_ES_CONTROL,
(7 << ES_SUB_WR_ENDIAN_BIT) | ES_SUB_MAN_RD_PTR);
spin_unlock_irqrestore(&lock, flags);
}

Some files were not shown because too many files have changed in this diff Show More