From 0e6af837fe53008aa3719966cd2157d141863586 Mon Sep 17 00:00:00 2001 From: "sayon.chen" Date: Tue, 8 Mar 2016 18:02:32 +0800 Subject: [PATCH] video: rockchip: vcodec: add vpu codec drivers move vpu codec code to drivers/video/rockchip Change-Id: Idf4100181200cf28a18990da7088bee495f10fcb Signed-off-by: sayon.chen --- arch/arm/mach-rockchip/vcodec_service.c | 3153 ----------------- arch/arm/mach-rockchip/vcodec_service.h | 271 -- drivers/video/rockchip/Kconfig | 1 + drivers/video/rockchip/Makefile | 2 +- drivers/video/rockchip/vcodec/Kconfig | 9 + drivers/video/rockchip/vcodec/Makefile | 1 + .../video/rockchip/vcodec/vcodec_hw_info.h | 227 ++ drivers/video/rockchip/vcodec/vcodec_hw_rkv.h | 226 ++ drivers/video/rockchip/vcodec/vcodec_hw_vpu.h | 318 ++ .../video/rockchip/vcodec/vcodec_hw_vpu2.h | 275 ++ .../video/rockchip/vcodec/vcodec_service.c | 2938 +++++++++++++++ .../video/rockchip/vcodec/vcodec_service.h | 115 + 12 files changed, 4111 insertions(+), 3425 deletions(-) delete mode 100755 arch/arm/mach-rockchip/vcodec_service.c delete mode 100644 arch/arm/mach-rockchip/vcodec_service.h create mode 100644 drivers/video/rockchip/vcodec/Kconfig create mode 100644 drivers/video/rockchip/vcodec/Makefile create mode 100644 drivers/video/rockchip/vcodec/vcodec_hw_info.h create mode 100644 drivers/video/rockchip/vcodec/vcodec_hw_rkv.h create mode 100644 drivers/video/rockchip/vcodec/vcodec_hw_vpu.h create mode 100644 drivers/video/rockchip/vcodec/vcodec_hw_vpu2.h create mode 100644 drivers/video/rockchip/vcodec/vcodec_service.c create mode 100644 drivers/video/rockchip/vcodec/vcodec_service.h diff --git a/arch/arm/mach-rockchip/vcodec_service.c b/arch/arm/mach-rockchip/vcodec_service.c deleted file mode 100755 index c28f44dd02c9..000000000000 --- a/arch/arm/mach-rockchip/vcodec_service.c +++ /dev/null @@ -1,3153 +0,0 @@ -/** - * Copyright (C) 2014 ROCKCHIP, Inc. - * author: chenhengming chm@rock-chips.com - * Alpha Lin, alpha.lin@rock-chips.com - * - * This software is licensed under the terms of the GNU General Public - * License version 2, as published by the Free Software Foundation, and - * may be copied, distributed, and modified under those terms. - * - * 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 pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#if defined(CONFIG_ION_ROCKCHIP) -#include -#endif - -#if defined(CONFIG_ROCKCHIP_IOMMU) & defined(CONFIG_ION_ROCKCHIP) -#define CONFIG_VCODEC_MMU -#endif - -#ifdef CONFIG_VCODEC_MMU -#include -#include -#endif - -#ifdef CONFIG_DEBUG_FS -#include -#endif - -#if defined(CONFIG_ARCH_RK319X) -#include -#endif - -#include "vcodec_service.h" - -/* - * debug flag usage: - * +------+-------------------+ - * | 8bit | 24bit | - * +------+-------------------+ - * 0~23 bit is for different information type - * 24~31 bit is for information print format - */ - -#define DEBUG_POWER 0x00000001 -#define DEBUG_CLOCK 0x00000002 -#define DEBUG_IRQ_STATUS 0x00000004 -#define DEBUG_IOMMU 0x00000008 -#define DEBUG_IOCTL 0x00000010 -#define DEBUG_FUNCTION 0x00000020 -#define DEBUG_REGISTER 0x00000040 -#define DEBUG_EXTRA_INFO 0x00000080 -#define DEBUG_TIMING 0x00000100 - -#define PRINT_FUNCTION 0x80000000 -#define PRINT_LINE 0x40000000 - -static int debug; -module_param(debug, int, S_IRUGO | S_IWUSR); -MODULE_PARM_DESC(debug, - "Debug level - higher value produces more verbose messages"); - -#define HEVC_TEST_ENABLE 0 -#define VCODEC_CLOCK_ENABLE 1 - -typedef enum { - VPU_DEC_ID_9190 = 0x6731, - VPU_ID_8270 = 0x8270, - VPU_ID_4831 = 0x4831, - HEVC_ID = 0x6867, -} VPU_HW_ID; - -enum VPU_HW_SPEC { - VPU_TYPE_VPU, - VPU_TYPE_HEVC, - VPU_TYPE_COMBO_NOENC, - VPU_TYPE_COMBO -}; - -typedef enum { - VPU_DEC_TYPE_9190 = 0, - VPU_ENC_TYPE_8270 = 0x100, - VPU_ENC_TYPE_4831 , -} VPU_HW_TYPE_E; - -typedef enum VPU_FREQ { - VPU_FREQ_200M, - VPU_FREQ_266M, - VPU_FREQ_300M, - VPU_FREQ_400M, - VPU_FREQ_500M, - VPU_FREQ_600M, - VPU_FREQ_DEFAULT, - VPU_FREQ_BUT, -} VPU_FREQ; - -typedef struct { - VPU_HW_ID hw_id; - unsigned long hw_addr; - unsigned long enc_offset; - unsigned long enc_reg_num; - unsigned long enc_io_size; - unsigned long dec_offset; - unsigned long dec_reg_num; - unsigned long dec_io_size; -} VPU_HW_INFO_E; - -struct extra_info_elem { - u32 index; - u32 offset; -}; - -#define EXTRA_INFO_MAGIC 0x4C4A46 - -struct extra_info_for_iommu { - u32 magic; - u32 cnt; - struct extra_info_elem elem[20]; -}; - -#define MHZ (1000*1000) - -#define REG_NUM_9190_DEC (60) -#define REG_NUM_9190_PP (41) -#define REG_NUM_9190_DEC_PP (REG_NUM_9190_DEC+REG_NUM_9190_PP) - -#define REG_NUM_DEC_PP (REG_NUM_9190_DEC+REG_NUM_9190_PP) - -#define REG_NUM_ENC_8270 (96) -#define REG_SIZE_ENC_8270 (0x200) -#define REG_NUM_ENC_4831 (164) -#define REG_SIZE_ENC_4831 (0x400) - -#define REG_NUM_HEVC_DEC (68) - -#define SIZE_REG(reg) ((reg)*4) - -static VPU_HW_INFO_E vpu_hw_set[] = { - [0] = { - .hw_id = VPU_ID_8270, - .hw_addr = 0, - .enc_offset = 0x0, - .enc_reg_num = REG_NUM_ENC_8270, - .enc_io_size = REG_NUM_ENC_8270 * 4, - .dec_offset = REG_SIZE_ENC_8270, - .dec_reg_num = REG_NUM_9190_DEC_PP, - .dec_io_size = REG_NUM_9190_DEC_PP * 4, - }, - [1] = { - .hw_id = VPU_ID_4831, - .hw_addr = 0, - .enc_offset = 0x0, - .enc_reg_num = REG_NUM_ENC_4831, - .enc_io_size = REG_NUM_ENC_4831 * 4, - .dec_offset = REG_SIZE_ENC_4831, - .dec_reg_num = REG_NUM_9190_DEC_PP, - .dec_io_size = REG_NUM_9190_DEC_PP * 4, - }, - [2] = { - .hw_id = HEVC_ID, - .hw_addr = 0, - .dec_offset = 0x0, - .dec_reg_num = REG_NUM_HEVC_DEC, - .dec_io_size = REG_NUM_HEVC_DEC * 4, - }, - [3] = { - .hw_id = VPU_DEC_ID_9190, - .hw_addr = 0, - .enc_offset = 0x0, - .enc_reg_num = 0, - .enc_io_size = 0, - .dec_offset = 0, - .dec_reg_num = REG_NUM_9190_DEC_PP, - .dec_io_size = REG_NUM_9190_DEC_PP * 4, - }, -}; - -#ifndef BIT -#define BIT(x) (1<<(x)) -#endif - -// interrupt and error status register -#define DEC_INTERRUPT_REGISTER 1 -#define DEC_INTERRUPT_BIT BIT(8) -#define DEC_READY_BIT BIT(12) -#define DEC_BUS_ERROR_BIT BIT(13) -#define DEC_BUFFER_EMPTY_BIT BIT(14) -#define DEC_ASO_ERROR_BIT BIT(15) -#define DEC_STREAM_ERROR_BIT BIT(16) -#define DEC_SLICE_DONE_BIT BIT(17) -#define DEC_TIMEOUT_BIT BIT(18) -#define DEC_ERR_MASK DEC_BUS_ERROR_BIT \ - |DEC_BUFFER_EMPTY_BIT \ - |DEC_STREAM_ERROR_BIT \ - |DEC_TIMEOUT_BIT - -#define PP_INTERRUPT_REGISTER 60 -#define PP_INTERRUPT_BIT BIT(8) -#define PP_READY_BIT BIT(12) -#define PP_BUS_ERROR_BIT BIT(13) -#define PP_ERR_MASK PP_BUS_ERROR_BIT - -#define ENC_INTERRUPT_REGISTER 1 -#define ENC_INTERRUPT_BIT BIT(0) -#define ENC_READY_BIT BIT(2) -#define ENC_BUS_ERROR_BIT BIT(3) -#define ENC_BUFFER_FULL_BIT BIT(5) -#define ENC_TIMEOUT_BIT BIT(6) -#define ENC_ERR_MASK ENC_BUS_ERROR_BIT \ - |ENC_BUFFER_FULL_BIT \ - |ENC_TIMEOUT_BIT - -#define HEVC_INTERRUPT_REGISTER 1 -#define HEVC_DEC_INT_RAW_BIT BIT(9) -#define HEVC_DEC_BUS_ERROR_BIT BIT(13) -#define HEVC_DEC_STR_ERROR_BIT BIT(14) -#define HEVC_DEC_TIMEOUT_BIT BIT(15) -#define HEVC_DEC_BUFFER_EMPTY_BIT BIT(16) -#define HEVC_DEC_COLMV_ERROR_BIT BIT(17) -#define HEVC_DEC_ERR_MASK HEVC_DEC_BUS_ERROR_BIT \ - |HEVC_DEC_STR_ERROR_BIT \ - |HEVC_DEC_TIMEOUT_BIT \ - |HEVC_DEC_BUFFER_EMPTY_BIT \ - |HEVC_DEC_COLMV_ERROR_BIT - - -// gating configuration set -#define VPU_REG_EN_ENC 14 -#define VPU_REG_ENC_GATE 2 -#define VPU_REG_ENC_GATE_BIT (1<<4) - -#define VPU_REG_EN_DEC 1 -#define VPU_REG_DEC_GATE 2 -#define VPU_REG_DEC_GATE_BIT (1<<10) -#define VPU_REG_EN_PP 0 -#define VPU_REG_PP_GATE 1 -#define VPU_REG_PP_GATE_BIT (1<<8) -#define VPU_REG_EN_DEC_PP 1 -#define VPU_REG_DEC_PP_GATE 61 -#define VPU_REG_DEC_PP_GATE_BIT (1<<8) - -#define DEBUG -#ifdef DEBUG -#define vpu_debug_func(type, fmt, args...) \ - do { \ - if (unlikely(debug & type)) { \ - pr_info("%s:%d: " fmt, \ - __func__, __LINE__, ##args); \ - } \ - } while (0) -#define vpu_debug(type, fmt, args...) \ - do { \ - if (unlikely(debug & type)) { \ - pr_info(fmt, ##args); \ - } \ - } while (0) -#else -#define vpu_debug_func(level, fmt, args...) -#define vpu_debug(level, fmt, args...) -#endif - -#define vpu_debug_enter() vpu_debug_func(DEBUG_FUNCTION, "enter\n") -#define vpu_debug_leave() vpu_debug_func(DEBUG_FUNCTION, "leave\n") - -#define vpu_err(fmt, args...) \ - pr_err("%s:%d: " fmt, __func__, __LINE__, ##args) - -#if defined(CONFIG_VCODEC_MMU) -static u8 addr_tbl_vpu_h264dec[] = { - 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, - 25, 26, 27, 28, 29, 40, 41 -}; - -static u8 addr_tbl_vpu_vp8dec[] = { - 10, 12, 13, 14, 18, 19, 22, 23, 24, 25, 26, 27, 28, 29, 40 -}; - -static u8 addr_tbl_vpu_vp6dec[] = { - 12, 13, 14, 18, 27, 40 -}; - -static u8 addr_tbl_vpu_vc1dec[] = { - 12, 13, 14, 15, 16, 17, 27, 41 -}; - -static u8 addr_tbl_vpu_jpegdec[] = { - 12, 40, 66, 67 -}; - -static u8 addr_tbl_vpu_defaultdec[] = { - 12, 13, 14, 15, 16, 17, 40, 41 -}; - -static u8 addr_tbl_vpu_enc[] = { - 5, 6, 7, 8, 9, 10, 11, 12, 13, 51 -}; - -static u8 addr_tbl_hevc_dec[] = { - 4, 6, 7, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, - 21, 22, 23, 24, 42, 43 -}; -#endif - -enum VPU_DEC_FMT { - VPU_DEC_FMT_H264, - VPU_DEC_FMT_MPEG4, - VPU_DEC_FMT_H263, - VPU_DEC_FMT_JPEG, - VPU_DEC_FMT_VC1, - VPU_DEC_FMT_MPEG2, - VPU_DEC_FMT_MPEG1, - VPU_DEC_FMT_VP6, - VPU_DEC_FMT_RV, - VPU_DEC_FMT_VP7, - VPU_DEC_FMT_VP8, - VPU_DEC_FMT_AVS, - VPU_DEC_FMT_SVC, - VPU_DEC_FMT_VC2, - VPU_DEC_FMT_MVC, - VPU_DEC_FMT_THEORA, - VPU_DEC_FMT_RES -}; - -/** - * struct for process session which connect to vpu - * - * @author ChenHengming (2011-5-3) - */ -typedef struct vpu_session { - enum VPU_CLIENT_TYPE type; - /* a linked list of data so we can access them for debugging */ - struct list_head list_session; - /* a linked list of register data waiting for process */ - struct list_head waiting; - /* a linked list of register data in processing */ - struct list_head running; - /* a linked list of register data processed */ - struct list_head done; - wait_queue_head_t wait; - pid_t pid; - atomic_t task_running; -} vpu_session; - -/** - * struct for process register set - * - * @author ChenHengming (2011-5-4) - */ -typedef struct vpu_reg { - enum VPU_CLIENT_TYPE type; - VPU_FREQ freq; - vpu_session *session; - struct vpu_subdev_data *data; - struct list_head session_link; /* link to vpu service session */ - struct list_head status_link; /* link to register set list */ - unsigned long size; -#if defined(CONFIG_VCODEC_MMU) - struct list_head mem_region_list; - u32 dec_base; -#endif - u32 *reg; -} vpu_reg; - -typedef struct vpu_device { - atomic_t irq_count_codec; - atomic_t irq_count_pp; - unsigned long iobaseaddr; - unsigned int iosize; - volatile u32 *hwregs; -} vpu_device; - -enum vcodec_device_id { - VCODEC_DEVICE_ID_VPU, - VCODEC_DEVICE_ID_HEVC, - VCODEC_DEVICE_ID_COMBO -}; - -enum VCODEC_RUNNING_MODE { - VCODEC_RUNNING_MODE_NONE = -1, - VCODEC_RUNNING_MODE_VPU, - VCODEC_RUNNING_MODE_HEVC, -}; - -struct vcodec_mem_region { - struct list_head srv_lnk; - struct list_head reg_lnk; - struct list_head session_lnk; - unsigned long iova; /* virtual address for iommu */ - unsigned long len; - u32 reg_idx; - struct ion_handle *hdl; -}; - -enum vpu_ctx_state { - MMU_ACTIVATED = BIT(0) -}; - -struct vpu_subdev_data { - struct cdev cdev; - dev_t dev_t; - struct class *cls; - struct device *child_dev; - - int irq_enc; - int irq_dec; - struct vpu_service_info *pservice; - - u32 *regs; - enum VCODEC_RUNNING_MODE mode; - struct list_head lnk_service; - - struct device *dev; - - vpu_device enc_dev; - vpu_device dec_dev; - VPU_HW_INFO_E *hw_info; - - u32 reg_size; - unsigned long state; - -#ifdef CONFIG_DEBUG_FS - struct dentry *debugfs_dir; - struct dentry *debugfs_file_regs; -#endif - -#if defined(CONFIG_VCODEC_MMU) - struct device *mmu_dev; -#endif -}; - -typedef struct vpu_service_info { - struct wake_lock wake_lock; - struct delayed_work power_off_work; - struct mutex lock; - struct list_head waiting; /* link to link_reg in struct vpu_reg */ - struct list_head running; /* link to link_reg in struct vpu_reg */ - struct list_head done; /* link to link_reg in struct vpu_reg */ - struct list_head session; /* link to list_session in struct vpu_session */ - atomic_t total_running; - atomic_t enabled; - atomic_t power_on_cnt; - atomic_t power_off_cnt; - vpu_reg *reg_codec; - vpu_reg *reg_pproc; - vpu_reg *reg_resev; - struct vpu_dec_config dec_config; - struct vpu_enc_config enc_config; - - bool auto_freq; - bool bug_dec_addr; - atomic_t freq_status; - - struct clk *aclk_vcodec; - struct clk *hclk_vcodec; - struct clk *clk_core; - struct clk *clk_cabac; - struct clk *pd_video; - -#ifdef CONFIG_RESET_CONTROLLER - struct reset_control *rst_a; - struct reset_control *rst_h; - struct reset_control *rst_v; -#endif - struct device *dev; - - u32 irq_status; - atomic_t reset_request; -#if defined(CONFIG_VCODEC_MMU) - struct ion_client *ion_client; - struct list_head mem_region_list; -#endif - - enum vcodec_device_id dev_id; - - enum VCODEC_RUNNING_MODE curr_mode; - u32 prev_mode; - - struct delayed_work simulate_work; - - u32 mode_bit; - u32 mode_ctrl; - u32 *reg_base; - u32 ioaddr; - struct regmap *grf; - u32 *grf_base; - - char *name; - - u32 subcnt; - struct list_head subdev_list; -} vpu_service_info; - -struct vcodec_combo { - struct vpu_service_info *vpu_srv; - struct vpu_service_info *hevc_srv; - struct list_head waiting; - struct list_head running; - struct mutex run_lock; - vpu_reg *reg_codec; - enum vcodec_device_id current_hw_mode; -}; - -struct vpu_request { - u32 *req; - u32 size; -}; - -#ifdef CONFIG_COMPAT -struct compat_vpu_request { - compat_uptr_t req; - u32 size; -}; -#endif - -/* debugfs root directory for all device (vpu, hevc).*/ -static struct dentry *parent; - -#ifdef CONFIG_DEBUG_FS -static int vcodec_debugfs_init(void); -static void vcodec_debugfs_exit(void); -static struct dentry* vcodec_debugfs_create_device_dir(char *dirname, struct dentry *parent); -static int debug_vcodec_open(struct inode *inode, struct file *file); - -static const struct file_operations debug_vcodec_fops = { - .open = debug_vcodec_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; -#endif - -#define VDPU_SOFT_RESET_REG 101 -#define VDPU_CLEAN_CACHE_REG 516 -#define VEPU_CLEAN_CACHE_REG 772 -#define HEVC_CLEAN_CACHE_REG 260 - -#define VPU_REG_ENABLE(base, reg) do { \ - base[reg] = 1; \ - } while (0) - -#define VDPU_SOFT_RESET(base) VPU_REG_ENABLE(base, VDPU_SOFT_RESET_REG) -#define VDPU_CLEAN_CACHE(base) VPU_REG_ENABLE(base, VDPU_CLEAN_CACHE_REG) -#define VEPU_CLEAN_CACHE(base) VPU_REG_ENABLE(base, VEPU_CLEAN_CACHE_REG) -#define HEVC_CLEAN_CACHE(base) VPU_REG_ENABLE(base, HEVC_CLEAN_CACHE_REG) - -#define VPU_POWER_OFF_DELAY 4*HZ /* 4s */ -#define VPU_TIMEOUT_DELAY 2*HZ /* 2s */ - -typedef struct { - char *name; - struct timeval start; - struct timeval end; - u32 error_mask; -} task_info; - -typedef enum { - TASK_VPU_ENC, - TASK_VPU_DEC, - TASK_VPU_PP, - TASK_RKDEC_HEVC, - TASK_TYPE_BUTT, -} TASK_TYPE; - -task_info tasks[TASK_TYPE_BUTT] = { - { - .name = "enc", - .error_mask = ENC_ERR_MASK - }, - { - .name = "dec", - .error_mask = DEC_ERR_MASK - }, - { - .name = "pp", - .error_mask = PP_ERR_MASK - }, - { - .name = "hevc", - .error_mask = HEVC_DEC_ERR_MASK - }, -}; - -static void time_record(task_info *task, int is_end) -{ - if (unlikely(debug & DEBUG_TIMING)) { - do_gettimeofday((is_end)?(&task->end):(&task->start)); - } -} - -static void time_diff(task_info *task) -{ - vpu_debug(DEBUG_TIMING, "%s task: %ld ms\n", task->name, - (task->end.tv_sec - task->start.tv_sec) * 1000 + - (task->end.tv_usec - task->start.tv_usec) / 1000); -} - -static void vcodec_enter_mode(struct vpu_subdev_data *data) -{ - int bits; - u32 raw = 0; - struct vpu_service_info *pservice = data->pservice; - struct vpu_subdev_data *subdata, *n; - if (pservice->subcnt < 2) { -#if defined(CONFIG_VCODEC_MMU) - if (data->mmu_dev && !test_bit(MMU_ACTIVATED, &data->state)) { - set_bit(MMU_ACTIVATED, &data->state); - if (atomic_read(&pservice->enabled)) - rockchip_iovmm_activate(data->dev); - else - BUG_ON(!atomic_read(&pservice->enabled)); - } -#endif - return; - } - - if (pservice->curr_mode == data->mode) - return; - - vpu_debug(DEBUG_IOMMU, "vcodec enter mode %d\n", data->mode); -#if defined(CONFIG_VCODEC_MMU) - list_for_each_entry_safe(subdata, n, &pservice->subdev_list, lnk_service) { - if (data != subdata && subdata->mmu_dev && - test_bit(MMU_ACTIVATED, &subdata->state)) { - clear_bit(MMU_ACTIVATED, &subdata->state); - rockchip_iovmm_deactivate(subdata->dev); - } - } -#endif - bits = 1 << pservice->mode_bit; -#ifdef CONFIG_MFD_SYSCON - if (pservice->grf) { - regmap_read(pservice->grf, pservice->mode_ctrl, &raw); - - if (data->mode == VCODEC_RUNNING_MODE_HEVC) - regmap_write(pservice->grf, pservice->mode_ctrl, - raw | bits | (bits << 16)); - else - regmap_write(pservice->grf, pservice->mode_ctrl, - (raw & (~bits)) | (bits << 16)); - } else if (pservice->grf_base) { - raw = readl_relaxed(pservice->grf_base + pservice->mode_ctrl / 4); - if (data->mode == VCODEC_RUNNING_MODE_HEVC) - writel_relaxed(raw | bits | (bits << 16), - pservice->grf_base + pservice->mode_ctrl / 4); - else - writel_relaxed((raw & (~bits)) | (bits << 16), - pservice->grf_base + pservice->mode_ctrl / 4); - } else { - vpu_err("no grf resource define, switch decoder failed\n"); - return; - } -#else - if (pervice->grf_base) { - raw = readl_relaxed(pservice->grf_base + pservice->mode_ctrl / 4); - if (data->mode == VCODEC_RUNNING_MODE_HEVC) - writel_relaxed(raw | bits | (bits << 16), - pservice->grf_base + pservice->mode_ctrl / 4); - else - writel_relaxed((raw & (~bits)) | (bits << 16), - pservice->grf_base + pservice->mode_ctrl / 4); - } else { - vpu_err("no grf resource define, switch decoder failed\n"); - return; - } -#endif -#if defined(CONFIG_VCODEC_MMU) - if (data->mmu_dev && !test_bit(MMU_ACTIVATED, &data->state)) { - set_bit(MMU_ACTIVATED, &data->state); - if (atomic_read(&pservice->enabled)) - rockchip_iovmm_activate(data->dev); - else - BUG_ON(!atomic_read(&pservice->enabled)); - } -#endif - pservice->prev_mode = pservice->curr_mode; - pservice->curr_mode = data->mode; -} - -static void vcodec_exit_mode(struct vpu_subdev_data *data) -{ - if (data->mmu_dev && test_bit(MMU_ACTIVATED, &data->state)) { - clear_bit(MMU_ACTIVATED, &data->state); - rockchip_iovmm_deactivate(data->dev); - data->pservice->curr_mode = VCODEC_RUNNING_MODE_NONE; - } -} - -static int vpu_get_clk(struct vpu_service_info *pservice) -{ -#if VCODEC_CLOCK_ENABLE - switch (pservice->dev_id) { - case VCODEC_DEVICE_ID_HEVC: - pservice->pd_video = devm_clk_get(pservice->dev, "pd_hevc"); - if (IS_ERR(pservice->pd_video)) { - dev_err(pservice->dev, "failed on clk_get pd_hevc\n"); - return -1; - } - case VCODEC_DEVICE_ID_COMBO: - pservice->clk_cabac = devm_clk_get(pservice->dev, "clk_cabac"); - if (IS_ERR(pservice->clk_cabac)) { - dev_err(pservice->dev, "failed on clk_get clk_cabac\n"); - pservice->clk_cabac = NULL; - } - pservice->clk_core = devm_clk_get(pservice->dev, "clk_core"); - if (IS_ERR(pservice->clk_core)) { - dev_err(pservice->dev, "failed on clk_get clk_core\n"); - return -1; - } - case VCODEC_DEVICE_ID_VPU: - pservice->aclk_vcodec = devm_clk_get(pservice->dev, "aclk_vcodec"); - if (IS_ERR(pservice->aclk_vcodec)) { - dev_err(pservice->dev, "failed on clk_get aclk_vcodec\n"); - return -1; - } - - pservice->hclk_vcodec = devm_clk_get(pservice->dev, "hclk_vcodec"); - if (IS_ERR(pservice->hclk_vcodec)) { - dev_err(pservice->dev, "failed on clk_get hclk_vcodec\n"); - return -1; - } - if (pservice->pd_video == NULL) { - pservice->pd_video = devm_clk_get(pservice->dev, "pd_video"); - if (IS_ERR(pservice->pd_video)) { - pservice->pd_video = NULL; - dev_info(pservice->dev, "do not have pd_video\n"); - } - } - break; - default: - ; - } - - return 0; -#else - return 0; -#endif -} - -static void vpu_put_clk(struct vpu_service_info *pservice) -{ -#if VCODEC_CLOCK_ENABLE - if (pservice->pd_video) - devm_clk_put(pservice->dev, pservice->pd_video); - if (pservice->aclk_vcodec) - devm_clk_put(pservice->dev, pservice->aclk_vcodec); - if (pservice->hclk_vcodec) - devm_clk_put(pservice->dev, pservice->hclk_vcodec); - if (pservice->clk_core) - devm_clk_put(pservice->dev, pservice->clk_core); - if (pservice->clk_cabac) - devm_clk_put(pservice->dev, pservice->clk_cabac); -#endif -} - -static void vpu_reset(struct vpu_subdev_data *data) -{ - struct vpu_service_info *pservice = data->pservice; - enum pmu_idle_req type = IDLE_REQ_VIDEO; - - if (pservice->dev_id == VCODEC_DEVICE_ID_HEVC) - type = IDLE_REQ_HEVC; - - pr_info("%s: resetting...", dev_name(pservice->dev)); - -#if defined(CONFIG_ARCH_RK29) - clk_disable(aclk_ddr_vepu); - cru_set_soft_reset(SOFT_RST_CPU_VODEC_A2A_AHB, true); - cru_set_soft_reset(SOFT_RST_DDR_VCODEC_PORT, true); - cru_set_soft_reset(SOFT_RST_VCODEC_AHB_BUS, true); - cru_set_soft_reset(SOFT_RST_VCODEC_AXI_BUS, true); - mdelay(10); - cru_set_soft_reset(SOFT_RST_VCODEC_AXI_BUS, false); - cru_set_soft_reset(SOFT_RST_VCODEC_AHB_BUS, false); - cru_set_soft_reset(SOFT_RST_DDR_VCODEC_PORT, false); - cru_set_soft_reset(SOFT_RST_CPU_VODEC_A2A_AHB, false); - clk_enable(aclk_ddr_vepu); -#elif defined(CONFIG_ARCH_RK30) - pmu_set_idle_request(IDLE_REQ_VIDEO, true); - cru_set_soft_reset(SOFT_RST_CPU_VCODEC, true); - cru_set_soft_reset(SOFT_RST_VCODEC_NIU_AXI, true); - cru_set_soft_reset(SOFT_RST_VCODEC_AHB, true); - cru_set_soft_reset(SOFT_RST_VCODEC_AXI, true); - mdelay(1); - cru_set_soft_reset(SOFT_RST_VCODEC_AXI, false); - cru_set_soft_reset(SOFT_RST_VCODEC_AHB, false); - cru_set_soft_reset(SOFT_RST_VCODEC_NIU_AXI, false); - cru_set_soft_reset(SOFT_RST_CPU_VCODEC, false); - pmu_set_idle_request(IDLE_REQ_VIDEO, false); -#else -#endif - WARN_ON(pservice->reg_codec != NULL); - WARN_ON(pservice->reg_pproc != NULL); - WARN_ON(pservice->reg_resev != NULL); - pservice->reg_codec = NULL; - pservice->reg_pproc = NULL; - pservice->reg_resev = NULL; - - pr_info("for 3288/3368..."); -#ifdef CONFIG_RESET_CONTROLLER - if (pservice->rst_a && pservice->rst_h) { - if (rockchip_pmu_ops.set_idle_request) - rockchip_pmu_ops.set_idle_request(type, true); - pr_info("reset in\n"); - if (pservice->rst_v) - reset_control_assert(pservice->rst_v); - reset_control_assert(pservice->rst_a); - reset_control_assert(pservice->rst_h); - usleep_range(10, 20); - reset_control_deassert(pservice->rst_h); - reset_control_deassert(pservice->rst_a); - if (pservice->rst_v) - reset_control_deassert(pservice->rst_v); - if (rockchip_pmu_ops.set_idle_request) - rockchip_pmu_ops.set_idle_request(type, false); - } -#endif - -#if defined(CONFIG_VCODEC_MMU) - if (data->mmu_dev && test_bit(MMU_ACTIVATED, &data->state)) { - clear_bit(MMU_ACTIVATED, &data->state); - if (atomic_read(&pservice->enabled)) - rockchip_iovmm_deactivate(data->dev); - else - BUG_ON(!atomic_read(&pservice->enabled)); - } -#endif - atomic_set(&pservice->reset_request, 0); - pr_info("done\n"); -} - -static void reg_deinit(struct vpu_subdev_data *data, vpu_reg *reg); -static void vpu_service_session_clear(struct vpu_subdev_data *data, vpu_session *session) -{ - vpu_reg *reg, *n; - list_for_each_entry_safe(reg, n, &session->waiting, session_link) { - reg_deinit(data, reg); - } - list_for_each_entry_safe(reg, n, &session->running, session_link) { - reg_deinit(data, reg); - } - list_for_each_entry_safe(reg, n, &session->done, session_link) { - reg_deinit(data, reg); - } -} - -static void vpu_service_dump(struct vpu_service_info *pservice) -{ -} - -static void vpu_service_power_off(struct vpu_service_info *pservice) -{ - int total_running; - struct vpu_subdev_data *data = NULL, *n; - int ret = atomic_add_unless(&pservice->enabled, -1, 0); - if (!ret) - return; - - total_running = atomic_read(&pservice->total_running); - if (total_running) { - pr_alert("alert: power off when %d task running!!\n", total_running); - mdelay(50); - pr_alert("alert: delay 50 ms for running task\n"); - vpu_service_dump(pservice); - } - - pr_info("%s: power off...", dev_name(pservice->dev)); - udelay(10); -#if defined(CONFIG_VCODEC_MMU) - list_for_each_entry_safe(data, n, &pservice->subdev_list, lnk_service) { - if (data->mmu_dev && test_bit(MMU_ACTIVATED, &data->state)) { - clear_bit(MMU_ACTIVATED, &data->state); - rockchip_iovmm_deactivate(data->dev); - } - } - pservice->curr_mode = VCODEC_RUNNING_MODE_NONE; -#endif - -#if VCODEC_CLOCK_ENABLE - if (pservice->pd_video) - clk_disable_unprepare(pservice->pd_video); - if (pservice->hclk_vcodec) - clk_disable_unprepare(pservice->hclk_vcodec); - if (pservice->aclk_vcodec) - clk_disable_unprepare(pservice->aclk_vcodec); - if (pservice->clk_core) - clk_disable_unprepare(pservice->clk_core); - if (pservice->clk_cabac) - clk_disable_unprepare(pservice->clk_cabac); -#endif - - atomic_add(1, &pservice->power_off_cnt); - wake_unlock(&pservice->wake_lock); - pr_info("done\n"); -} - -static inline void vpu_queue_power_off_work(struct vpu_service_info *pservice) -{ - queue_delayed_work(system_nrt_wq, &pservice->power_off_work, VPU_POWER_OFF_DELAY); -} - -static void vpu_power_off_work(struct work_struct *work_s) -{ - struct delayed_work *dlwork = container_of(work_s, struct delayed_work, work); - struct vpu_service_info *pservice = container_of(dlwork, struct vpu_service_info, power_off_work); - - if (mutex_trylock(&pservice->lock)) { - vpu_service_power_off(pservice); - mutex_unlock(&pservice->lock); - } else { - /* Come back later if the device is busy... */ - vpu_queue_power_off_work(pservice); - } -} - -static void vpu_service_power_on(struct vpu_service_info *pservice) -{ - int ret; - static ktime_t last; - ktime_t now = ktime_get(); - if (ktime_to_ns(ktime_sub(now, last)) > NSEC_PER_SEC) { - cancel_delayed_work_sync(&pservice->power_off_work); - vpu_queue_power_off_work(pservice); - last = now; - } - ret = atomic_add_unless(&pservice->enabled, 1, 1); - if (!ret) - return ; - - pr_info("%s: power on\n", dev_name(pservice->dev)); - -#define BIT_VCODEC_CLK_SEL (1<<10) - if (cpu_is_rk312x()) - writel_relaxed(readl_relaxed(RK_GRF_VIRT + RK312X_GRF_SOC_CON1) | - BIT_VCODEC_CLK_SEL | (BIT_VCODEC_CLK_SEL << 16), - RK_GRF_VIRT + RK312X_GRF_SOC_CON1); - -#if VCODEC_CLOCK_ENABLE - if (pservice->aclk_vcodec) - clk_prepare_enable(pservice->aclk_vcodec); - if (pservice->hclk_vcodec) - clk_prepare_enable(pservice->hclk_vcodec); - if (pservice->clk_core) - clk_prepare_enable(pservice->clk_core); - if (pservice->clk_cabac) - clk_prepare_enable(pservice->clk_cabac); - if (pservice->pd_video) - clk_prepare_enable(pservice->pd_video); -#endif - - udelay(10); - atomic_add(1, &pservice->power_on_cnt); - wake_lock(&pservice->wake_lock); -} - -static inline bool reg_check_rmvb_wmv(vpu_reg *reg) -{ - u32 type = (reg->reg[3] & 0xF0000000) >> 28; - return ((type == 8) || (type == 4)); -} - -static inline bool reg_check_interlace(vpu_reg *reg) -{ - u32 type = (reg->reg[3] & (1 << 23)); - return (type > 0); -} - -static inline enum VPU_DEC_FMT reg_check_fmt(vpu_reg *reg) -{ - enum VPU_DEC_FMT type = (enum VPU_DEC_FMT)((reg->reg[3] & 0xF0000000) >> 28); - return type; -} - -static inline int reg_probe_width(vpu_reg *reg) -{ - int width_in_mb = reg->reg[4] >> 23; - return width_in_mb * 16; -} - -static inline int reg_probe_hevc_y_stride(vpu_reg *reg) -{ - int y_virstride = reg->reg[8]; - return y_virstride; -} - -#if defined(CONFIG_VCODEC_MMU) -static int vcodec_fd_to_iova(struct vpu_subdev_data *data, vpu_reg *reg,int fd) -{ - struct vpu_service_info *pservice = data->pservice; - struct ion_handle *hdl; - int ret = 0; - struct vcodec_mem_region *mem_region; - - hdl = ion_import_dma_buf(pservice->ion_client, fd); - if (IS_ERR(hdl)) { - vpu_err("import dma-buf from fd %d failed\n", fd); - return PTR_ERR(hdl); - } - mem_region = kzalloc(sizeof(struct vcodec_mem_region), GFP_KERNEL); - - if (mem_region == NULL) { - vpu_err("allocate memory for iommu memory region failed\n"); - ion_free(pservice->ion_client, hdl); - return -1; - } - - mem_region->hdl = hdl; - ret = ion_map_iommu(data->dev, pservice->ion_client, - mem_region->hdl, &mem_region->iova, &mem_region->len); - - if (ret < 0) { - vpu_err("ion map iommu failed\n"); - kfree(mem_region); - ion_free(pservice->ion_client, hdl); - return ret; - } - INIT_LIST_HEAD(&mem_region->reg_lnk); - list_add_tail(&mem_region->reg_lnk, ®->mem_region_list); - return mem_region->iova; -} - -static int vcodec_bufid_to_iova(struct vpu_subdev_data *data, u8 *tbl, - int size, vpu_reg *reg, - struct extra_info_for_iommu *ext_inf) -{ - struct vpu_service_info *pservice = data->pservice; - int i; - int usr_fd = 0; - int offset = 0; - - if (tbl == NULL || size <= 0) { - dev_err(pservice->dev, "input arguments invalidate\n"); - return -1; - } - - for (i = 0; i < size; i++) { - usr_fd = reg->reg[tbl[i]] & 0x3FF; - - if (tbl[i] == 41 && data->hw_info->hw_id != HEVC_ID && - (reg->type == VPU_DEC || reg->type == VPU_DEC_PP)) - /* special for vpu dec num 41 regitster */ - offset = reg->reg[tbl[i]] >> 10 << 4; - else - offset = reg->reg[tbl[i]] >> 10; - - if (usr_fd != 0) { - struct ion_handle *hdl; - int ret = 0; - struct vcodec_mem_region *mem_region; - - hdl = ion_import_dma_buf(pservice->ion_client, usr_fd); - if (IS_ERR(hdl)) { - dev_err(pservice->dev, "import dma-buf from fd %d failed, reg[%d]\n", usr_fd, tbl[i]); - return PTR_ERR(hdl); - } - - if (tbl[i] == 42 && data->hw_info->hw_id == HEVC_ID){ - int i = 0; - char *pps; - pps = (char *)ion_map_kernel(pservice->ion_client,hdl); - for (i=0; i<64; i++) { - u32 scaling_offset; - u32 tmp; - int scaling_fd= 0; - scaling_offset = (u32)pps[i*80+74]; - scaling_offset += (u32)pps[i*80+75] << 8; - scaling_offset += (u32)pps[i*80+76] << 16; - scaling_offset += (u32)pps[i*80+77] << 24; - scaling_fd = scaling_offset&0x3ff; - scaling_offset = scaling_offset >> 10; - if(scaling_fd > 0) { - tmp = vcodec_fd_to_iova(data, reg, scaling_fd); - tmp += scaling_offset; - pps[i*80+74] = tmp & 0xff; - pps[i*80+75] = (tmp >> 8) & 0xff; - pps[i*80+76] = (tmp >> 16) & 0xff; - pps[i*80+77] = (tmp >> 24) & 0xff; - } - } - } - - mem_region = kzalloc(sizeof(struct vcodec_mem_region), GFP_KERNEL); - - if (mem_region == NULL) { - dev_err(pservice->dev, "allocate memory for iommu memory region failed\n"); - ion_free(pservice->ion_client, hdl); - return -1; - } - - mem_region->hdl = hdl; - mem_region->reg_idx = tbl[i]; - ret = ion_map_iommu(data->dev, - pservice->ion_client, - mem_region->hdl, - &mem_region->iova, - &mem_region->len); - - if (ret < 0) { - dev_err(pservice->dev, "ion map iommu failed\n"); - kfree(mem_region); - ion_free(pservice->ion_client, hdl); - return ret; - } - - /* special for vpu dec num 12: record decoded length - hacking for decoded length - NOTE: not a perfect fix, the fd is not recorded */ - if (tbl[i] == 12 && data->hw_info->hw_id != HEVC_ID && - (reg->type == VPU_DEC || reg->type == VPU_DEC_PP)) { - reg->dec_base = mem_region->iova + offset; - vpu_debug(DEBUG_REGISTER, "dec_set %08x\n", reg->dec_base); - } - - reg->reg[tbl[i]] = mem_region->iova + offset; - INIT_LIST_HEAD(&mem_region->reg_lnk); - list_add_tail(&mem_region->reg_lnk, ®->mem_region_list); - } - } - - if (ext_inf != NULL && ext_inf->magic == EXTRA_INFO_MAGIC) { - for (i=0; icnt; i++) { - vpu_debug(DEBUG_IOMMU, "reg[%d] + offset %d\n", - ext_inf->elem[i].index, - ext_inf->elem[i].offset); - reg->reg[ext_inf->elem[i].index] += - ext_inf->elem[i].offset; - } - } - - return 0; -} - -static int vcodec_reg_address_translate(struct vpu_subdev_data *data, - vpu_reg *reg, - struct extra_info_for_iommu *ext_inf) -{ - VPU_HW_ID hw_id; - u8 *tbl; - int size = 0; - - hw_id = data->hw_info->hw_id; - - if (hw_id == HEVC_ID) { - tbl = addr_tbl_hevc_dec; - size = sizeof(addr_tbl_hevc_dec); - } else { - if (reg->type == VPU_DEC || reg->type == VPU_DEC_PP) { - switch (reg_check_fmt(reg)) { - case VPU_DEC_FMT_H264: - { - tbl = addr_tbl_vpu_h264dec; - size = sizeof(addr_tbl_vpu_h264dec); - break; - } - case VPU_DEC_FMT_VP8: - case VPU_DEC_FMT_VP7: - { - tbl = addr_tbl_vpu_vp8dec; - size = sizeof(addr_tbl_vpu_vp8dec); - break; - } - - case VPU_DEC_FMT_VP6: - { - tbl = addr_tbl_vpu_vp6dec; - size = sizeof(addr_tbl_vpu_vp6dec); - break; - } - case VPU_DEC_FMT_VC1: - { - tbl = addr_tbl_vpu_vc1dec; - size = sizeof(addr_tbl_vpu_vc1dec); - break; - } - - case VPU_DEC_FMT_JPEG: - { - tbl = addr_tbl_vpu_jpegdec; - size = sizeof(addr_tbl_vpu_jpegdec); - break; - } - default: - tbl = addr_tbl_vpu_defaultdec; - size = sizeof(addr_tbl_vpu_defaultdec); - break; - } - } else if (reg->type == VPU_ENC) { - tbl = addr_tbl_vpu_enc; - size = sizeof(addr_tbl_vpu_enc); - } - } - - if (size != 0) { - return vcodec_bufid_to_iova(data, tbl, size, reg, ext_inf); - } else { - return -1; - } -} -#endif - -static vpu_reg *reg_init(struct vpu_subdev_data *data, - vpu_session *session, void __user *src, u32 size) -{ - struct vpu_service_info *pservice = data->pservice; - int extra_size = 0; - struct extra_info_for_iommu extra_info; - vpu_reg *reg = kmalloc(sizeof(vpu_reg) + data->reg_size, GFP_KERNEL); - - vpu_debug_enter(); - - if (NULL == reg) { - vpu_err("error: kmalloc fail in reg_init\n"); - return NULL; - } - - if (size > data->reg_size) { - /*printk("warning: vpu reg size %u is larger than hw reg size %u\n", - size, data->reg_size);*/ - extra_size = size - data->reg_size; - size = data->reg_size; - } - reg->session = session; - reg->data = data; - reg->type = session->type; - reg->size = size; - reg->freq = VPU_FREQ_DEFAULT; - reg->reg = (u32 *)®[1]; - INIT_LIST_HEAD(®->session_link); - INIT_LIST_HEAD(®->status_link); - -#if defined(CONFIG_VCODEC_MMU) - if (data->mmu_dev) - INIT_LIST_HEAD(®->mem_region_list); -#endif - - if (copy_from_user(®->reg[0], (void __user *)src, size)) { - vpu_err("error: copy_from_user failed in reg_init\n"); - kfree(reg); - return NULL; - } - - if (copy_from_user(&extra_info, (u8 *)src + size, extra_size)) { - vpu_err("error: copy_from_user failed in reg_init\n"); - kfree(reg); - return NULL; - } - -#if defined(CONFIG_VCODEC_MMU) - if (data->mmu_dev && - 0 > vcodec_reg_address_translate(data, reg, &extra_info)) { - vpu_err("error: translate reg address failed\n"); - kfree(reg); - return NULL; - } -#endif - - mutex_lock(&pservice->lock); - list_add_tail(®->status_link, &pservice->waiting); - list_add_tail(®->session_link, &session->waiting); - mutex_unlock(&pservice->lock); - - if (pservice->auto_freq) { - if (!soc_is_rk2928g()) { - if (reg->type == VPU_DEC || reg->type == VPU_DEC_PP) { - if (reg_check_rmvb_wmv(reg)) { - reg->freq = VPU_FREQ_200M; - } else if (reg_check_fmt(reg) == VPU_DEC_FMT_H264) { - if (reg_probe_width(reg) > 3200) { - /*raise frequency for 4k avc.*/ - reg->freq = VPU_FREQ_600M; - } - } else { - if (reg_check_interlace(reg)) { - reg->freq = VPU_FREQ_400M; - } - } - } - if (data->hw_info->hw_id == HEVC_ID) { - if (reg_probe_hevc_y_stride(reg) > 60000) - reg->freq = VPU_FREQ_400M; - } - if (reg->type == VPU_PP) { - reg->freq = VPU_FREQ_400M; - } - } - } - vpu_debug_leave(); - return reg; -} - -static void reg_deinit(struct vpu_subdev_data *data, vpu_reg *reg) -{ - struct vpu_service_info *pservice = data->pservice; -#if defined(CONFIG_VCODEC_MMU) - struct vcodec_mem_region *mem_region = NULL, *n; -#endif - - list_del_init(®->session_link); - list_del_init(®->status_link); - if (reg == pservice->reg_codec) - pservice->reg_codec = NULL; - if (reg == pservice->reg_pproc) - pservice->reg_pproc = NULL; - -#if defined(CONFIG_VCODEC_MMU) - /* release memory region attach to this registers table. */ - if (data->mmu_dev) { - list_for_each_entry_safe(mem_region, n, - ®->mem_region_list, reg_lnk) { - /* do not unmap iommu manually, - unmap will proccess when memory release */ - /*vcodec_enter_mode(data); - ion_unmap_iommu(data->dev, - pservice->ion_client, - mem_region->hdl); - vcodec_exit_mode();*/ - ion_free(pservice->ion_client, mem_region->hdl); - list_del_init(&mem_region->reg_lnk); - kfree(mem_region); - } - } -#endif - - kfree(reg); -} - -static void reg_from_wait_to_run(struct vpu_service_info *pservice, vpu_reg *reg) -{ - vpu_debug_enter(); - list_del_init(®->status_link); - list_add_tail(®->status_link, &pservice->running); - - list_del_init(®->session_link); - list_add_tail(®->session_link, ®->session->running); - vpu_debug_leave(); -} - -static void reg_copy_from_hw(vpu_reg *reg, volatile u32 *src, u32 count) -{ - int i; - u32 *dst = (u32 *)®->reg[0]; - vpu_debug_enter(); - for (i = 0; i < count; i++) - *dst++ = *src++; - vpu_debug_leave(); -} - -static void reg_from_run_to_done(struct vpu_subdev_data *data, - vpu_reg *reg) -{ - struct vpu_service_info *pservice = data->pservice; - int irq_reg = -1; - - vpu_debug_enter(); - - list_del_init(®->status_link); - list_add_tail(®->status_link, &pservice->done); - - list_del_init(®->session_link); - list_add_tail(®->session_link, ®->session->done); - - /*vcodec_enter_mode(data);*/ - switch (reg->type) { - case VPU_ENC : { - pservice->reg_codec = NULL; - reg_copy_from_hw(reg, data->enc_dev.hwregs, data->hw_info->enc_reg_num); - irq_reg = ENC_INTERRUPT_REGISTER; - break; - } - case VPU_DEC : { - int reg_len = REG_NUM_9190_DEC; - pservice->reg_codec = NULL; - reg_copy_from_hw(reg, data->dec_dev.hwregs, reg_len); -#if defined(CONFIG_VCODEC_MMU) - /* revert hack for decoded length */ - if (data->hw_info->hw_id != HEVC_ID) { - u32 dec_get = reg->reg[12]; - s32 dec_length = dec_get - reg->dec_base; - vpu_debug(DEBUG_REGISTER, "dec_get %08x dec_length %d\n", dec_get, dec_length); - reg->reg[12] = dec_length << 10; - } -#endif - irq_reg = DEC_INTERRUPT_REGISTER; - break; - } - case VPU_PP : { - pservice->reg_pproc = NULL; - reg_copy_from_hw(reg, data->dec_dev.hwregs + PP_INTERRUPT_REGISTER, REG_NUM_9190_PP); - data->dec_dev.hwregs[PP_INTERRUPT_REGISTER] = 0; - break; - } - case VPU_DEC_PP : { - pservice->reg_codec = NULL; - pservice->reg_pproc = NULL; - reg_copy_from_hw(reg, data->dec_dev.hwregs, REG_NUM_9190_DEC_PP); - data->dec_dev.hwregs[PP_INTERRUPT_REGISTER] = 0; -#if defined(CONFIG_VCODEC_MMU) - /* revert hack for decoded length */ - if (data->hw_info->hw_id != HEVC_ID) { - u32 dec_get = reg->reg[12]; - s32 dec_length = dec_get - reg->dec_base; - vpu_debug(DEBUG_REGISTER, "dec_get %08x dec_length %d\n", dec_get, dec_length); - reg->reg[12] = dec_length << 10; - } -#endif - break; - } - default : { - vpu_err("error: copy reg from hw with unknown type %d\n", reg->type); - break; - } - } - vcodec_exit_mode(data); - - if (irq_reg != -1) - reg->reg[irq_reg] = pservice->irq_status; - - atomic_sub(1, ®->session->task_running); - atomic_sub(1, &pservice->total_running); - wake_up(®->session->wait); - - vpu_debug_leave(); -} - -static void vpu_service_set_freq(struct vpu_service_info *pservice, vpu_reg *reg) -{ - VPU_FREQ curr = atomic_read(&pservice->freq_status); - if (curr == reg->freq) - return; - atomic_set(&pservice->freq_status, reg->freq); - switch (reg->freq) { - case VPU_FREQ_200M : { - clk_set_rate(pservice->aclk_vcodec, 200*MHZ); - } break; - case VPU_FREQ_266M : { - clk_set_rate(pservice->aclk_vcodec, 266*MHZ); - } break; - case VPU_FREQ_300M : { - clk_set_rate(pservice->aclk_vcodec, 300*MHZ); - } break; - case VPU_FREQ_400M : { - clk_set_rate(pservice->aclk_vcodec, 400*MHZ); - } break; - case VPU_FREQ_500M : { - clk_set_rate(pservice->aclk_vcodec, 500*MHZ); - } break; - case VPU_FREQ_600M : { - clk_set_rate(pservice->aclk_vcodec, 600*MHZ); - } break; - default : { - if (soc_is_rk2928g()) - clk_set_rate(pservice->aclk_vcodec, 400*MHZ); - else - clk_set_rate(pservice->aclk_vcodec, 300*MHZ); - } break; - } -} - -static void reg_copy_to_hw(struct vpu_subdev_data *data, vpu_reg *reg) -{ - struct vpu_service_info *pservice = data->pservice; - int i; - u32 *src = (u32 *)®->reg[0]; - vpu_debug_enter(); - - atomic_add(1, &pservice->total_running); - atomic_add(1, ®->session->task_running); - if (pservice->auto_freq) - vpu_service_set_freq(pservice, reg); - - vcodec_enter_mode(data); - - switch (reg->type) { - case VPU_ENC : { - int enc_count = data->hw_info->enc_reg_num; - u32 *dst = (u32 *)data->enc_dev.hwregs; - - pservice->reg_codec = reg; - - dst[VPU_REG_EN_ENC] = src[VPU_REG_EN_ENC] & 0x6; - - for (i = 0; i < VPU_REG_EN_ENC; i++) - dst[i] = src[i]; - - for (i = VPU_REG_EN_ENC + 1; i < enc_count; i++) - dst[i] = src[i]; - - VEPU_CLEAN_CACHE(dst); - - dsb(sy); - - dst[VPU_REG_ENC_GATE] = src[VPU_REG_ENC_GATE] | VPU_REG_ENC_GATE_BIT; - dst[VPU_REG_EN_ENC] = src[VPU_REG_EN_ENC]; - - time_record(&tasks[TASK_VPU_ENC], 0); - } break; - case VPU_DEC : { - u32 *dst = (u32 *)data->dec_dev.hwregs; - - pservice->reg_codec = reg; - - if (data->hw_info->hw_id != HEVC_ID) { - for (i = REG_NUM_9190_DEC - 1; i > VPU_REG_DEC_GATE; i--) - dst[i] = src[i]; - VDPU_CLEAN_CACHE(dst); - } else { - for (i = REG_NUM_HEVC_DEC - 1; i > VPU_REG_EN_DEC; i--) - dst[i] = src[i]; - HEVC_CLEAN_CACHE(dst); - } - - dsb(sy); - - if (data->hw_info->hw_id != HEVC_ID) { - dst[VPU_REG_DEC_GATE] = src[VPU_REG_DEC_GATE] | VPU_REG_DEC_GATE_BIT; - dst[VPU_REG_EN_DEC] = src[VPU_REG_EN_DEC]; - } else { - dst[VPU_REG_EN_DEC] = src[VPU_REG_EN_DEC]; - } - dsb(sy); - dmb(sy); - - time_record(&tasks[TASK_VPU_DEC], 0); - } break; - case VPU_PP : { - u32 *dst = (u32 *)data->dec_dev.hwregs + PP_INTERRUPT_REGISTER; - pservice->reg_pproc = reg; - - dst[VPU_REG_PP_GATE] = src[VPU_REG_PP_GATE] | VPU_REG_PP_GATE_BIT; - - for (i = VPU_REG_PP_GATE + 1; i < REG_NUM_9190_PP; i++) - dst[i] = src[i]; - - dsb(sy); - - dst[VPU_REG_EN_PP] = src[VPU_REG_EN_PP]; - - time_record(&tasks[TASK_VPU_PP], 0); - } break; - case VPU_DEC_PP : { - u32 *dst = (u32 *)data->dec_dev.hwregs; - pservice->reg_codec = reg; - pservice->reg_pproc = reg; - - VDPU_SOFT_RESET(dst); - VDPU_CLEAN_CACHE(dst); - - for (i = VPU_REG_EN_DEC_PP + 1; i < REG_NUM_9190_DEC_PP; i++) - dst[i] = src[i]; - - dst[VPU_REG_EN_DEC_PP] = src[VPU_REG_EN_DEC_PP] | 0x2; - dsb(sy); - - dst[VPU_REG_DEC_PP_GATE] = src[VPU_REG_DEC_PP_GATE] | VPU_REG_PP_GATE_BIT; - dst[VPU_REG_DEC_GATE] = src[VPU_REG_DEC_GATE] | VPU_REG_DEC_GATE_BIT; - dst[VPU_REG_EN_DEC] = src[VPU_REG_EN_DEC]; - - time_record(&tasks[TASK_VPU_DEC], 0); - } break; - default : { - vpu_err("error: unsupport session type %d", reg->type); - atomic_sub(1, &pservice->total_running); - atomic_sub(1, ®->session->task_running); - } break; - } - - /*vcodec_exit_mode(data);*/ - vpu_debug_leave(); -} - -static void try_set_reg(struct vpu_subdev_data *data) -{ - struct vpu_service_info *pservice = data->pservice; - vpu_debug_enter(); - if (!list_empty(&pservice->waiting)) { - int can_set = 0; - bool change_able = (NULL == pservice->reg_codec) && (NULL == pservice->reg_pproc); - int reset_request = atomic_read(&pservice->reset_request); - vpu_reg *reg = list_entry(pservice->waiting.next, vpu_reg, status_link); - - vpu_service_power_on(pservice); - - // first check can_set flag - if (change_able || !reset_request) { - switch (reg->type) { - case VPU_ENC : { - if (change_able) - can_set = 1; - } break; - case VPU_DEC : { - if (NULL == pservice->reg_codec) - can_set = 1; - if (pservice->auto_freq && (NULL != pservice->reg_pproc)) - can_set = 0; - } break; - case VPU_PP : { - if (NULL == pservice->reg_codec) { - if (NULL == pservice->reg_pproc) - can_set = 1; - } else { - if ((VPU_DEC == pservice->reg_codec->type) && (NULL == pservice->reg_pproc)) - can_set = 1; - /* can not charge frequency when vpu is working */ - if (pservice->auto_freq) - can_set = 0; - } - } break; - case VPU_DEC_PP : { - if (change_able) - can_set = 1; - } break; - default : { - printk("undefined reg type %d\n", reg->type); - } break; - } - } - - // then check reset request - if (reset_request && !change_able) - reset_request = 0; - - // do reset before setting registers - if (reset_request) - vpu_reset(data); - - if (can_set) { - reg_from_wait_to_run(pservice, reg); - reg_copy_to_hw(reg->data, reg); - } - } - vpu_debug_leave(); -} - -static int return_reg(struct vpu_subdev_data *data, - vpu_reg *reg, u32 __user *dst) -{ - int ret = 0; - vpu_debug_enter(); - switch (reg->type) { - case VPU_ENC : { - if (copy_to_user(dst, ®->reg[0], data->hw_info->enc_io_size)) - ret = -EFAULT; - break; - } - case VPU_DEC : { - int reg_len = data->hw_info->hw_id == HEVC_ID ? REG_NUM_HEVC_DEC : REG_NUM_9190_DEC; - if (copy_to_user(dst, ®->reg[0], SIZE_REG(reg_len))) - ret = -EFAULT; - break; - } - case VPU_PP : { - if (copy_to_user(dst, ®->reg[0], SIZE_REG(REG_NUM_9190_PP))) - ret = -EFAULT; - break; - } - case VPU_DEC_PP : { - if (copy_to_user(dst, ®->reg[0], SIZE_REG(REG_NUM_9190_DEC_PP))) - ret = -EFAULT; - break; - } - default : { - ret = -EFAULT; - vpu_err("error: copy reg to user with unknown type %d\n", reg->type); - break; - } - } - reg_deinit(data, reg); - vpu_debug_leave(); - return ret; -} - -static long vpu_service_ioctl(struct file *filp, unsigned int cmd, - unsigned long arg) -{ - struct vpu_subdev_data *data = - container_of(filp->f_dentry->d_inode->i_cdev, - struct vpu_subdev_data, cdev); - struct vpu_service_info *pservice = data->pservice; - vpu_session *session = (vpu_session *)filp->private_data; - vpu_debug_enter(); - if (NULL == session) - return -EINVAL; - - switch (cmd) { - case VPU_IOC_SET_CLIENT_TYPE : { - session->type = (enum VPU_CLIENT_TYPE)arg; - vpu_debug(DEBUG_IOCTL, "VPU_IOC_SET_CLIENT_TYPE %d\n", session->type); - break; - } - case VPU_IOC_GET_HW_FUSE_STATUS : { - struct vpu_request req; - vpu_debug(DEBUG_IOCTL, "VPU_IOC_GET_HW_FUSE_STATUS type %d\n", session->type); - if (copy_from_user(&req, (void __user *)arg, sizeof(struct vpu_request))) { - vpu_err("error: VPU_IOC_GET_HW_FUSE_STATUS copy_from_user failed\n"); - return -EFAULT; - } else { - if (VPU_ENC != session->type) { - if (copy_to_user((void __user *)req.req, - &pservice->dec_config, - sizeof(struct vpu_dec_config))) { - vpu_err("error: VPU_IOC_GET_HW_FUSE_STATUS copy_to_user failed type %d\n", - session->type); - return -EFAULT; - } - } else { - if (copy_to_user((void __user *)req.req, - &pservice->enc_config, - sizeof(struct vpu_enc_config ))) { - vpu_err("error: VPU_IOC_GET_HW_FUSE_STATUS copy_to_user failed type %d\n", - session->type); - return -EFAULT; - } - } - } - - break; - } - case VPU_IOC_SET_REG : { - struct vpu_request req; - vpu_reg *reg; - vpu_debug(DEBUG_IOCTL, "VPU_IOC_SET_REG type %d\n", session->type); - if (copy_from_user(&req, (void __user *)arg, - sizeof(struct vpu_request))) { - vpu_err("error: VPU_IOC_SET_REG copy_from_user failed\n"); - return -EFAULT; - } - reg = reg_init(data, session, - (void __user *)req.req, req.size); - if (NULL == reg) { - return -EFAULT; - } else { - mutex_lock(&pservice->lock); - try_set_reg(data); - mutex_unlock(&pservice->lock); - } - - break; - } - case VPU_IOC_GET_REG : { - struct vpu_request req; - vpu_reg *reg; - vpu_debug(DEBUG_IOCTL, "VPU_IOC_GET_REG type %d\n", session->type); - if (copy_from_user(&req, (void __user *)arg, - sizeof(struct vpu_request))) { - vpu_err("error: VPU_IOC_GET_REG copy_from_user failed\n"); - return -EFAULT; - } else { - int ret = wait_event_timeout(session->wait, !list_empty(&session->done), VPU_TIMEOUT_DELAY); - if (!list_empty(&session->done)) { - if (ret < 0) { - vpu_err("warning: pid %d wait task sucess but wait_evernt ret %d\n", session->pid, ret); - } - ret = 0; - } else { - if (unlikely(ret < 0)) { - vpu_err("error: pid %d wait task ret %d\n", session->pid, ret); - } else if (0 == ret) { - vpu_err("error: pid %d wait %d task done timeout\n", session->pid, atomic_read(&session->task_running)); - ret = -ETIMEDOUT; - } - } - if (ret < 0) { - int task_running = atomic_read(&session->task_running); - mutex_lock(&pservice->lock); - vpu_service_dump(pservice); - if (task_running) { - atomic_set(&session->task_running, 0); - atomic_sub(task_running, &pservice->total_running); - printk("%d task is running but not return, reset hardware...", task_running); - vpu_reset(data); - printk("done\n"); - } - vpu_service_session_clear(data, session); - mutex_unlock(&pservice->lock); - return ret; - } - } - mutex_lock(&pservice->lock); - reg = list_entry(session->done.next, vpu_reg, session_link); - return_reg(data, reg, (u32 __user *)req.req); - mutex_unlock(&pservice->lock); - break; - } - case VPU_IOC_PROBE_IOMMU_STATUS: { - int iommu_enable = 0; - - vpu_debug(DEBUG_IOCTL, "VPU_IOC_PROBE_IOMMU_STATUS\n"); - -#if defined(CONFIG_VCODEC_MMU) - iommu_enable = data->mmu_dev ? 1 : 0; -#endif - - if (copy_to_user((void __user *)arg, &iommu_enable, sizeof(int))) { - vpu_err("error: VPU_IOC_PROBE_IOMMU_STATUS copy_to_user failed\n"); - return -EFAULT; - } - break; - } - default : { - vpu_err("error: unknow vpu service ioctl cmd %x\n", cmd); - break; - } - } - vpu_debug_leave(); - return 0; -} - -#ifdef CONFIG_COMPAT -static long compat_vpu_service_ioctl(struct file *filp, unsigned int cmd, - unsigned long arg) -{ - struct vpu_subdev_data *data = - container_of(filp->f_dentry->d_inode->i_cdev, - struct vpu_subdev_data, cdev); - struct vpu_service_info *pservice = data->pservice; - vpu_session *session = (vpu_session *)filp->private_data; - vpu_debug_enter(); - vpu_debug(3, "cmd %x, COMPAT_VPU_IOC_SET_CLIENT_TYPE %x\n", cmd, - (u32)COMPAT_VPU_IOC_SET_CLIENT_TYPE); - if (NULL == session) - return -EINVAL; - - switch (cmd) { - case COMPAT_VPU_IOC_SET_CLIENT_TYPE : { - session->type = (enum VPU_CLIENT_TYPE)arg; - vpu_debug(DEBUG_IOCTL, "COMPAT_VPU_IOC_SET_CLIENT_TYPE type %d\n", session->type); - break; - } - case COMPAT_VPU_IOC_GET_HW_FUSE_STATUS : { - struct compat_vpu_request req; - vpu_debug(DEBUG_IOCTL, "COMPAT_VPU_IOC_GET_HW_FUSE_STATUS type %d\n", session->type); - if (copy_from_user(&req, compat_ptr((compat_uptr_t)arg), - sizeof(struct compat_vpu_request))) { - vpu_err("error: VPU_IOC_GET_HW_FUSE_STATUS" - " copy_from_user failed\n"); - return -EFAULT; - } else { - if (VPU_ENC != session->type) { - if (copy_to_user(compat_ptr((compat_uptr_t)req.req), - &pservice->dec_config, - sizeof(struct vpu_dec_config))) { - vpu_err("error: VPU_IOC_GET_HW_FUSE_STATUS " - "copy_to_user failed type %d\n", - session->type); - return -EFAULT; - } - } else { - if (copy_to_user(compat_ptr((compat_uptr_t)req.req), - &pservice->enc_config, - sizeof(struct vpu_enc_config ))) { - vpu_err("error: VPU_IOC_GET_HW_FUSE_STATUS" - " copy_to_user failed type %d\n", - session->type); - return -EFAULT; - } - } - } - - break; - } - case COMPAT_VPU_IOC_SET_REG : { - struct compat_vpu_request req; - vpu_reg *reg; - vpu_debug(DEBUG_IOCTL, "COMPAT_VPU_IOC_SET_REG type %d\n", session->type); - if (copy_from_user(&req, compat_ptr((compat_uptr_t)arg), - sizeof(struct compat_vpu_request))) { - vpu_err("VPU_IOC_SET_REG copy_from_user failed\n"); - return -EFAULT; - } - reg = reg_init(data, session, - compat_ptr((compat_uptr_t)req.req), req.size); - if (NULL == reg) { - return -EFAULT; - } else { - mutex_lock(&pservice->lock); - try_set_reg(data); - mutex_unlock(&pservice->lock); - } - - break; - } - case COMPAT_VPU_IOC_GET_REG : { - struct compat_vpu_request req; - vpu_reg *reg; - vpu_debug(DEBUG_IOCTL, "COMPAT_VPU_IOC_GET_REG type %d\n", session->type); - if (copy_from_user(&req, compat_ptr((compat_uptr_t)arg), - sizeof(struct compat_vpu_request))) { - vpu_err("VPU_IOC_GET_REG copy_from_user failed\n"); - return -EFAULT; - } else { - int ret = wait_event_timeout(session->wait, !list_empty(&session->done), VPU_TIMEOUT_DELAY); - if (!list_empty(&session->done)) { - if (ret < 0) { - vpu_err("warning: pid %d wait task sucess but wait_evernt ret %d\n", session->pid, ret); - } - ret = 0; - } else { - if (unlikely(ret < 0)) { - vpu_err("error: pid %d wait task ret %d\n", session->pid, ret); - } else if (0 == ret) { - vpu_err("error: pid %d wait %d task done timeout\n", session->pid, atomic_read(&session->task_running)); - ret = -ETIMEDOUT; - } - } - if (ret < 0) { - int task_running = atomic_read(&session->task_running); - mutex_lock(&pservice->lock); - vpu_service_dump(pservice); - if (task_running) { - atomic_set(&session->task_running, 0); - atomic_sub(task_running, &pservice->total_running); - printk("%d task is running but not return, reset hardware...", task_running); - vpu_reset(data); - printk("done\n"); - } - vpu_service_session_clear(data, session); - mutex_unlock(&pservice->lock); - return ret; - } - } - mutex_lock(&pservice->lock); - reg = list_entry(session->done.next, vpu_reg, session_link); - return_reg(data, reg, compat_ptr((compat_uptr_t)req.req)); - mutex_unlock(&pservice->lock); - break; - } - case COMPAT_VPU_IOC_PROBE_IOMMU_STATUS : { - int iommu_enable = 0; - - vpu_debug(DEBUG_IOCTL, "COMPAT_VPU_IOC_PROBE_IOMMU_STATUS\n"); -#if defined(CONFIG_VCODEC_MMU) - iommu_enable = data->mmu_dev ? 1 : 0; -#endif - - if (copy_to_user(compat_ptr((compat_uptr_t)arg), &iommu_enable, sizeof(int))) { - vpu_err("error: VPU_IOC_PROBE_IOMMU_STATUS copy_to_user failed\n"); - return -EFAULT; - } - break; - } - default : { - vpu_err("error: unknow vpu service ioctl cmd %x\n", cmd); - break; - } - } - vpu_debug_leave(); - return 0; -} -#endif - -static int vpu_service_check_hw(struct vpu_subdev_data *data, u32 hw_addr) -{ - int ret = -EINVAL, i = 0; - volatile u32 *tmp = (volatile u32 *)ioremap_nocache(hw_addr, 0x4); - u32 enc_id = *tmp; - - enc_id = (enc_id >> 16) & 0xFFFF; - pr_info("checking hw id %x\n", enc_id); - data->hw_info = NULL; - for (i = 0; i < ARRAY_SIZE(vpu_hw_set); i++) { - if (enc_id == vpu_hw_set[i].hw_id) { - data->hw_info = &vpu_hw_set[i]; - ret = 0; - break; - } - } - iounmap((void *)tmp); - return ret; -} - -static int vpu_service_open(struct inode *inode, struct file *filp) -{ - struct vpu_subdev_data *data = container_of(inode->i_cdev, struct vpu_subdev_data, cdev); - struct vpu_service_info *pservice = data->pservice; - vpu_session *session = (vpu_session *)kmalloc(sizeof(vpu_session), GFP_KERNEL); - - vpu_debug_enter(); - - if (NULL == session) { - vpu_err("error: unable to allocate memory for vpu_session."); - return -ENOMEM; - } - - session->type = VPU_TYPE_BUTT; - session->pid = current->pid; - INIT_LIST_HEAD(&session->waiting); - INIT_LIST_HEAD(&session->running); - INIT_LIST_HEAD(&session->done); - INIT_LIST_HEAD(&session->list_session); - init_waitqueue_head(&session->wait); - atomic_set(&session->task_running, 0); - mutex_lock(&pservice->lock); - list_add_tail(&session->list_session, &pservice->session); - filp->private_data = (void *)session; - mutex_unlock(&pservice->lock); - - pr_debug("dev opened\n"); - vpu_debug_leave(); - return nonseekable_open(inode, filp); -} - -static int vpu_service_release(struct inode *inode, struct file *filp) -{ - struct vpu_subdev_data *data = container_of(inode->i_cdev, struct vpu_subdev_data, cdev); - struct vpu_service_info *pservice = data->pservice; - int task_running; - vpu_session *session = (vpu_session *)filp->private_data; - vpu_debug_enter(); - if (NULL == session) - return -EINVAL; - - task_running = atomic_read(&session->task_running); - if (task_running) { - vpu_err("error: vpu_service session %d still has %d task running when closing\n", session->pid, task_running); - msleep(50); - } - wake_up(&session->wait); - - mutex_lock(&pservice->lock); - /* remove this filp from the asynchronusly notified filp's */ - list_del_init(&session->list_session); - vpu_service_session_clear(data, session); - kfree(session); - filp->private_data = NULL; - mutex_unlock(&pservice->lock); - - pr_debug("dev closed\n"); - vpu_debug_leave(); - return 0; -} - -static const struct file_operations vpu_service_fops = { - .unlocked_ioctl = vpu_service_ioctl, - .open = vpu_service_open, - .release = vpu_service_release, -#ifdef CONFIG_COMPAT - .compat_ioctl = compat_vpu_service_ioctl, -#endif -}; - -static irqreturn_t vdpu_irq(int irq, void *dev_id); -static irqreturn_t vdpu_isr(int irq, void *dev_id); -static irqreturn_t vepu_irq(int irq, void *dev_id); -static irqreturn_t vepu_isr(int irq, void *dev_id); -static void get_hw_info(struct vpu_subdev_data *data); - -#ifdef CONFIG_VCODEC_MMU -static struct device *rockchip_get_sysmmu_dev(const char *compt) -{ - struct device_node *dn = NULL; - struct platform_device *pd = NULL; - struct device *ret = NULL ; - - dn = of_find_compatible_node(NULL,NULL,compt); - if(!dn) { - printk("can't find device node %s \r\n",compt); - return NULL; - } - - pd = of_find_device_by_node(dn); - if(!pd) { - printk("can't find platform device in device node %s\n",compt); - return NULL; - } - ret = &pd->dev; - - return ret; - -} -#ifdef CONFIG_IOMMU_API -static inline void platform_set_sysmmu(struct device *iommu, - struct device *dev) -{ - dev->archdata.iommu = iommu; -} -#else -static inline void platform_set_sysmmu(struct device *iommu, - struct device *dev) -{ -} -#endif - -int vcodec_sysmmu_fault_hdl(struct device *dev, - enum rk_iommu_inttype itype, - unsigned long pgtable_base, - unsigned long fault_addr, unsigned int status) -{ - struct platform_device *pdev; - struct vpu_subdev_data *data; - struct vpu_service_info *pservice; - - vpu_debug_enter(); - - pdev = container_of(dev, struct platform_device, dev); - - data = platform_get_drvdata(pdev); - pservice = data->pservice; - - if (pservice->reg_codec) { - struct vcodec_mem_region *mem, *n; - int i = 0; - vpu_debug(DEBUG_IOMMU, "vcodec, fault addr 0x%08x\n", (u32)fault_addr); - list_for_each_entry_safe(mem, n, - &pservice->reg_codec->mem_region_list, - reg_lnk) { - vpu_debug(DEBUG_IOMMU, "vcodec, reg[%02u] mem region [%02d] 0x%08x %ld\n", - mem->reg_idx, i, (u32)mem->iova, mem->len); - i++; - } - - pr_alert("vcodec, page fault occur, reset hw\n"); - pservice->reg_codec->reg[101] = 1; - vpu_reset(data); - } - - return 0; -} -#endif - -#if HEVC_TEST_ENABLE -static int hevc_test_case0(vpu_service_info *pservice); -#endif -#if defined(CONFIG_ION_ROCKCHIP) -extern struct ion_client *rockchip_ion_client_create(const char * name); -#endif - -static int vcodec_subdev_probe(struct platform_device *pdev, - struct vpu_service_info *pservice) -{ - int ret = 0; - struct resource *res = NULL; - u32 ioaddr = 0; - struct device *dev = &pdev->dev; - char *name = (char*)dev_name(dev); - struct device_node *np = pdev->dev.of_node; - struct vpu_subdev_data *data = - devm_kzalloc(dev, sizeof(struct vpu_subdev_data), GFP_KERNEL); -#if defined(CONFIG_VCODEC_MMU) - u32 iommu_en = 0; - char mmu_dev_dts_name[40]; - of_property_read_u32(np, "iommu_enabled", &iommu_en); -#endif - pr_info("probe device %s\n", dev_name(dev)); - - data->pservice = pservice; - data->dev = dev; - - of_property_read_string(np, "name", (const char**)&name); - of_property_read_u32(np, "dev_mode", (u32*)&data->mode); - /*dev_set_name(dev, name);*/ - - if (pservice->reg_base == 0) { - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - data->regs = devm_ioremap_resource(dev, res); - if (IS_ERR(data->regs)) { - ret = PTR_ERR(data->regs); - goto err; - } - ioaddr = res->start; - } else { - data->regs = pservice->reg_base; - ioaddr = pservice->ioaddr; - } - - clear_bit(MMU_ACTIVATED, &data->state); - vcodec_enter_mode(data); - ret = vpu_service_check_hw(data, ioaddr); - if (ret < 0) { - vpu_err("error: hw info check faild\n"); - goto err; - } - - data->dec_dev.iosize = data->hw_info->dec_io_size; - data->dec_dev.hwregs = (volatile u32 *)((u8 *)data->regs + data->hw_info->dec_offset); - data->reg_size = data->dec_dev.iosize; - - if (data->mode == VCODEC_RUNNING_MODE_VPU) { - data->enc_dev.iosize = data->hw_info->enc_io_size; - data->reg_size = data->reg_size > data->enc_dev.iosize ? data->reg_size : data->enc_dev.iosize; - data->enc_dev.hwregs = (volatile u32 *)((u8 *)data->regs + data->hw_info->enc_offset); - } - - data->irq_enc = platform_get_irq_byname(pdev, "irq_enc"); - if (data->irq_enc > 0) { - ret = devm_request_threaded_irq(dev, - data->irq_enc, vepu_irq, vepu_isr, - IRQF_SHARED, dev_name(dev), - (void *)data); - if (ret) { - dev_err(dev, - "error: can't request vepu irq %d\n", - data->irq_enc); - goto err; - } - } - data->irq_dec = platform_get_irq_byname(pdev, "irq_dec"); - if (data->irq_dec > 0) { - ret = devm_request_threaded_irq(dev, - data->irq_dec, vdpu_irq, vdpu_isr, - IRQF_SHARED, dev_name(dev), - (void *)data); - if (ret) { - dev_err(dev, - "error: can't request vdpu irq %d\n", - data->irq_dec); - goto err; - } - } - atomic_set(&data->dec_dev.irq_count_codec, 0); - atomic_set(&data->dec_dev.irq_count_pp, 0); - atomic_set(&data->enc_dev.irq_count_codec, 0); - atomic_set(&data->enc_dev.irq_count_pp, 0); -#if defined(CONFIG_VCODEC_MMU) - if (iommu_en) { - if (data->mode == VCODEC_RUNNING_MODE_HEVC) - sprintf(mmu_dev_dts_name, - HEVC_IOMMU_COMPATIBLE_NAME); - else - sprintf(mmu_dev_dts_name, - VPU_IOMMU_COMPATIBLE_NAME); - - data->mmu_dev = - rockchip_get_sysmmu_dev(mmu_dev_dts_name); - - if (data->mmu_dev) - platform_set_sysmmu(data->mmu_dev, dev); - - rockchip_iovmm_set_fault_handler(dev, vcodec_sysmmu_fault_hdl); - } -#endif - get_hw_info(data); - pservice->auto_freq = true; - - vcodec_exit_mode(data); - /* create device node */ - ret = alloc_chrdev_region(&data->dev_t, 0, 1, name); - if (ret) { - dev_err(dev, "alloc dev_t failed\n"); - goto err; - } - - cdev_init(&data->cdev, &vpu_service_fops); - - data->cdev.owner = THIS_MODULE; - data->cdev.ops = &vpu_service_fops; - - ret = cdev_add(&data->cdev, data->dev_t, 1); - - if (ret) { - dev_err(dev, "add dev_t failed\n"); - goto err; - } - - data->cls = class_create(THIS_MODULE, name); - - if (IS_ERR(data->cls)) { - ret = PTR_ERR(data->cls); - dev_err(dev, "class_create err:%d\n", ret); - goto err; - } - - data->child_dev = device_create(data->cls, dev, - data->dev_t, NULL, name); - - platform_set_drvdata(pdev, data); - - INIT_LIST_HEAD(&data->lnk_service); - list_add_tail(&data->lnk_service, &pservice->subdev_list); - -#ifdef CONFIG_DEBUG_FS - data->debugfs_dir = - vcodec_debugfs_create_device_dir((char*)name, parent); - if (data->debugfs_dir == NULL) - vpu_err("create debugfs dir %s failed\n", name); - - data->debugfs_file_regs = - debugfs_create_file("regs", 0664, - data->debugfs_dir, data, - &debug_vcodec_fops); -#endif - return 0; -err: - if (data->irq_enc > 0) - free_irq(data->irq_enc, (void *)data); - if (data->irq_dec > 0) - free_irq(data->irq_dec, (void *)data); - - if (data->child_dev) { - device_destroy(data->cls, data->dev_t); - cdev_del(&data->cdev); - unregister_chrdev_region(data->dev_t, 1); - } - - if (data->cls) - class_destroy(data->cls); - return -1; -} - -static void vcodec_subdev_remove(struct vpu_subdev_data *data) -{ - device_destroy(data->cls, data->dev_t); - class_destroy(data->cls); - cdev_del(&data->cdev); - unregister_chrdev_region(data->dev_t, 1); - - free_irq(data->irq_enc, (void *)&data); - free_irq(data->irq_dec, (void *)&data); - -#ifdef CONFIG_DEBUG_FS - debugfs_remove_recursive(data->debugfs_dir); -#endif -} - -static void vcodec_read_property(struct device_node *np, - struct vpu_service_info *pservice) -{ - pservice->mode_bit = 0; - pservice->mode_ctrl = 0; - pservice->subcnt = 0; - pservice->grf_base = NULL; - - of_property_read_u32(np, "subcnt", &pservice->subcnt); - - if (pservice->subcnt > 1) { - of_property_read_u32(np, "mode_bit", &pservice->mode_bit); - of_property_read_u32(np, "mode_ctrl", &pservice->mode_ctrl); - } -#ifdef CONFIG_MFD_SYSCON - pservice->grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf"); - if (IS_ERR_OR_NULL(pservice->grf)) { - pservice->grf = NULL; -#ifdef CONFIG_ARM - pservice->grf_base = RK_GRF_VIRT; -#else - vpu_err("can't find vpu grf property\n"); - return; -#endif - } -#else -#ifdef CONFIG_ARM - pservice->grf_base = RK_GRF_VIRT; -#else - vpu_err("can't find vpu grf property\n"); - return; -#endif -#endif - -#ifdef CONFIG_RESET_CONTROLLER - pservice->rst_a = devm_reset_control_get(pservice->dev, "video_a"); - pservice->rst_h = devm_reset_control_get(pservice->dev, "video_h"); - pservice->rst_v = devm_reset_control_get(pservice->dev, "video"); - - if (IS_ERR_OR_NULL(pservice->rst_a)) { - pr_warn("No reset resource define\n"); - pservice->rst_a = NULL; - } - - if (IS_ERR_OR_NULL(pservice->rst_h)) { - pr_warn("No reset resource define\n"); - pservice->rst_h = NULL; - } - - if (IS_ERR_OR_NULL(pservice->rst_v)) { - pr_warn("No reset resource define\n"); - pservice->rst_v = NULL; - } -#endif - - of_property_read_string(np, "name", (const char**)&pservice->name); -} - -static void vcodec_init_drvdata(struct vpu_service_info *pservice) -{ - pservice->dev_id = VCODEC_DEVICE_ID_VPU; - pservice->curr_mode = -1; - - wake_lock_init(&pservice->wake_lock, WAKE_LOCK_SUSPEND, "vpu"); - INIT_LIST_HEAD(&pservice->waiting); - INIT_LIST_HEAD(&pservice->running); - mutex_init(&pservice->lock); - - INIT_LIST_HEAD(&pservice->done); - INIT_LIST_HEAD(&pservice->session); - INIT_LIST_HEAD(&pservice->subdev_list); - - pservice->reg_pproc = NULL; - atomic_set(&pservice->total_running, 0); - atomic_set(&pservice->enabled, 0); - atomic_set(&pservice->power_on_cnt, 0); - atomic_set(&pservice->power_off_cnt, 0); - atomic_set(&pservice->reset_request, 0); - - INIT_DELAYED_WORK(&pservice->power_off_work, vpu_power_off_work); - - pservice->ion_client = rockchip_ion_client_create("vpu"); - if (IS_ERR(pservice->ion_client)) { - vpu_err("failed to create ion client for vcodec ret %ld\n", - PTR_ERR(pservice->ion_client)); - } else { - vpu_debug(DEBUG_IOMMU, "vcodec ion client create success!\n"); - } -} - -static int vcodec_probe(struct platform_device *pdev) -{ - int i; - int ret = 0; - struct resource *res = NULL; - struct device *dev = &pdev->dev; - struct device_node *np = pdev->dev.of_node; - struct vpu_service_info *pservice = - devm_kzalloc(dev, sizeof(struct vpu_service_info), GFP_KERNEL); - - pr_info("probe device %s\n", dev_name(dev)); - - pservice->dev = dev; - - vcodec_read_property(np, pservice); - vcodec_init_drvdata(pservice); - - if (strncmp(pservice->name, "hevc_service", 12) == 0) - pservice->dev_id = VCODEC_DEVICE_ID_HEVC; - else if (strncmp(pservice->name, "vpu_service", 11) == 0) - pservice->dev_id = VCODEC_DEVICE_ID_VPU; - else - pservice->dev_id = VCODEC_DEVICE_ID_COMBO; - - if (0 > vpu_get_clk(pservice)) - goto err; - - vpu_service_power_on(pservice); - - if (of_property_read_bool(np, "reg")) { - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - - pservice->reg_base = devm_ioremap_resource(pservice->dev, res); - if (IS_ERR(pservice->reg_base)) { - vpu_err("ioremap registers base failed\n"); - ret = PTR_ERR(pservice->reg_base); - goto err; - } - pservice->ioaddr = res->start; - } else { - pservice->reg_base = 0; - } - - if (of_property_read_bool(np, "subcnt")) { - for (i = 0; isubcnt; i++) { - struct device_node *sub_np; - struct platform_device *sub_pdev; - sub_np = of_parse_phandle(np, "rockchip,sub", i); - sub_pdev = of_find_device_by_node(sub_np); - - vcodec_subdev_probe(sub_pdev, pservice); - } - } else { - vcodec_subdev_probe(pdev, pservice); - } - platform_set_drvdata(pdev, pservice); - - vpu_service_power_off(pservice); - - pr_info("init success\n"); - - return 0; - -err: - pr_info("init failed\n"); - vpu_service_power_off(pservice); - vpu_put_clk(pservice); - wake_lock_destroy(&pservice->wake_lock); - - if (res) - devm_release_mem_region(&pdev->dev, res->start, resource_size(res)); - - return ret; -} - -static int vcodec_remove(struct platform_device *pdev) -{ - struct vpu_service_info *pservice = platform_get_drvdata(pdev); - struct resource *res; - struct vpu_subdev_data *data, *n; - - list_for_each_entry_safe(data, n, &pservice->subdev_list, lnk_service) { - vcodec_subdev_remove(data); - } - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - devm_release_mem_region(&pdev->dev, res->start, resource_size(res)); - vpu_put_clk(pservice); - wake_lock_destroy(&pservice->wake_lock); - - return 0; -} - -#if defined(CONFIG_OF) -static const struct of_device_id vcodec_service_dt_ids[] = { - {.compatible = "vpu_service",}, - {.compatible = "rockchip,hevc_service",}, - {.compatible = "rockchip,vpu_combo",}, - {}, -}; -#endif - -static struct platform_driver vcodec_driver = { - .probe = vcodec_probe, - .remove = vcodec_remove, - .driver = { - .name = "vcodec", - .owner = THIS_MODULE, -#if defined(CONFIG_OF) - .of_match_table = of_match_ptr(vcodec_service_dt_ids), -#endif - }, -}; - -static void get_hw_info(struct vpu_subdev_data *data) -{ - struct vpu_service_info *pservice = data->pservice; - struct vpu_dec_config *dec = &pservice->dec_config; - struct vpu_enc_config *enc = &pservice->enc_config; - if (data->mode == VCODEC_RUNNING_MODE_VPU) { - u32 configReg = data->dec_dev.hwregs[VPU_DEC_HWCFG0]; - u32 asicID = data->dec_dev.hwregs[0]; - - dec->h264_support = (configReg >> DWL_H264_E) & 0x3U; - dec->jpegSupport = (configReg >> DWL_JPEG_E) & 0x01U; - if (dec->jpegSupport && ((configReg >> DWL_PJPEG_E) & 0x01U)) - dec->jpegSupport = JPEG_PROGRESSIVE; - dec->mpeg4Support = (configReg >> DWL_MPEG4_E) & 0x3U; - dec->vc1Support = (configReg >> DWL_VC1_E) & 0x3U; - dec->mpeg2Support = (configReg >> DWL_MPEG2_E) & 0x01U; - dec->sorensonSparkSupport = (configReg >> DWL_SORENSONSPARK_E) & 0x01U; - dec->refBufSupport = (configReg >> DWL_REF_BUFF_E) & 0x01U; - dec->vp6Support = (configReg >> DWL_VP6_E) & 0x01U; - - dec->maxDecPicWidth = 4096; - - /* 2nd Config register */ - configReg = data->dec_dev.hwregs[VPU_DEC_HWCFG1]; - if (dec->refBufSupport) { - if ((configReg >> DWL_REF_BUFF_ILACE_E) & 0x01U) - dec->refBufSupport |= 2; - if ((configReg >> DWL_REF_BUFF_DOUBLE_E) & 0x01U) - dec->refBufSupport |= 4; - } - dec->customMpeg4Support = (configReg >> DWL_MPEG4_CUSTOM_E) & 0x01U; - dec->vp7Support = (configReg >> DWL_VP7_E) & 0x01U; - dec->vp8Support = (configReg >> DWL_VP8_E) & 0x01U; - dec->avsSupport = (configReg >> DWL_AVS_E) & 0x01U; - - /* JPEG xtensions */ - if (((asicID >> 16) >= 0x8190U) || ((asicID >> 16) == 0x6731U)) - dec->jpegESupport = (configReg >> DWL_JPEG_EXT_E) & 0x01U; - else - dec->jpegESupport = JPEG_EXT_NOT_SUPPORTED; - - if (((asicID >> 16) >= 0x9170U) || ((asicID >> 16) == 0x6731U) ) - dec->rvSupport = (configReg >> DWL_RV_E) & 0x03U; - else - dec->rvSupport = RV_NOT_SUPPORTED; - dec->mvcSupport = (configReg >> DWL_MVC_E) & 0x03U; - - if (dec->refBufSupport && (asicID >> 16) == 0x6731U ) - dec->refBufSupport |= 8; /* enable HW support for offset */ - - if (!cpu_is_rk3036()) { - configReg = data->enc_dev.hwregs[63]; - enc->maxEncodedWidth = configReg & ((1 << 11) - 1); - enc->h264Enabled = (configReg >> 27) & 1; - enc->mpeg4Enabled = (configReg >> 26) & 1; - enc->jpegEnabled = (configReg >> 25) & 1; - enc->vsEnabled = (configReg >> 24) & 1; - enc->rgbEnabled = (configReg >> 28) & 1; - enc->reg_size = data->reg_size; - enc->reserv[0] = enc->reserv[1] = 0; - } - pservice->auto_freq = true; - vpu_debug(DEBUG_EXTRA_INFO, "vpu_service set to auto frequency mode\n"); - atomic_set(&pservice->freq_status, VPU_FREQ_BUT); - - pservice->bug_dec_addr = cpu_is_rk30xx(); - } else { - if (cpu_is_rk3036() || cpu_is_rk312x()) - dec->maxDecPicWidth = 1920; - else - dec->maxDecPicWidth = 4096; - /* disable frequency switch in hevc.*/ - pservice->auto_freq = false; - } -} - -static bool check_irq_err(task_info *task, u32 irq_status) -{ - return (task->error_mask & irq_status) ? true : false; -} - -static irqreturn_t vdpu_irq(int irq, void *dev_id) -{ - struct vpu_subdev_data *data = (struct vpu_subdev_data*)dev_id; - struct vpu_service_info *pservice = data->pservice; - vpu_device *dev = &data->dec_dev; - u32 raw_status; - u32 dec_status; - - /*vcodec_enter_mode(data);*/ - - dec_status = raw_status = readl(dev->hwregs + DEC_INTERRUPT_REGISTER); - - if (dec_status & DEC_INTERRUPT_BIT) { - time_record(&tasks[TASK_VPU_DEC], 1); - vpu_debug(DEBUG_IRQ_STATUS, "vdpu_irq dec status %08x\n", dec_status); - if ((dec_status & 0x40001) == 0x40001) { - do { - dec_status = - readl(dev->hwregs + - DEC_INTERRUPT_REGISTER); - } while ((dec_status & 0x40001) == 0x40001); - } - - if (check_irq_err((data->hw_info->hw_id == HEVC_ID)? - (&tasks[TASK_RKDEC_HEVC]) : (&tasks[TASK_VPU_DEC]), - dec_status)) { - atomic_add(1, &pservice->reset_request); - } - - writel(0, dev->hwregs + DEC_INTERRUPT_REGISTER); - atomic_add(1, &dev->irq_count_codec); - time_diff(&tasks[TASK_VPU_DEC]); - } - - if (data->hw_info->hw_id != HEVC_ID) { - u32 pp_status = readl(dev->hwregs + PP_INTERRUPT_REGISTER); - if (pp_status & PP_INTERRUPT_BIT) { - time_record(&tasks[TASK_VPU_PP], 1); - vpu_debug(DEBUG_IRQ_STATUS, "vdpu_irq pp status %08x\n", pp_status); - - if (check_irq_err(&tasks[TASK_VPU_PP], dec_status)) - atomic_add(1, &pservice->reset_request); - - /* clear pp IRQ */ - writel(pp_status & (~DEC_INTERRUPT_BIT), dev->hwregs + PP_INTERRUPT_REGISTER); - atomic_add(1, &dev->irq_count_pp); - time_diff(&tasks[TASK_VPU_PP]); - } - } - - pservice->irq_status = raw_status; - - /*vcodec_exit_mode(pservice);*/ - - if (atomic_read(&dev->irq_count_pp) || - atomic_read(&dev->irq_count_codec)) - return IRQ_WAKE_THREAD; - else - return IRQ_NONE; -} - -static irqreturn_t vdpu_isr(int irq, void *dev_id) -{ - struct vpu_subdev_data *data = (struct vpu_subdev_data*)dev_id; - struct vpu_service_info *pservice = data->pservice; - vpu_device *dev = &data->dec_dev; - - mutex_lock(&pservice->lock); - if (atomic_read(&dev->irq_count_codec)) { - atomic_sub(1, &dev->irq_count_codec); - if (NULL == pservice->reg_codec) { - vpu_err("error: dec isr with no task waiting\n"); - } else { - reg_from_run_to_done(data, pservice->reg_codec); - /* avoid vpu timeout and can't recover problem */ - VDPU_SOFT_RESET(data->regs); - } - } - - if (atomic_read(&dev->irq_count_pp)) { - atomic_sub(1, &dev->irq_count_pp); - if (NULL == pservice->reg_pproc) { - vpu_err("error: pp isr with no task waiting\n"); - } else { - reg_from_run_to_done(data, pservice->reg_pproc); - } - } - try_set_reg(data); - mutex_unlock(&pservice->lock); - return IRQ_HANDLED; -} - -static irqreturn_t vepu_irq(int irq, void *dev_id) -{ - struct vpu_subdev_data *data = (struct vpu_subdev_data*)dev_id; - struct vpu_service_info *pservice = data->pservice; - vpu_device *dev = &data->enc_dev; - u32 irq_status; - - /*vcodec_enter_mode(data);*/ - irq_status= readl(dev->hwregs + ENC_INTERRUPT_REGISTER); - - vpu_debug(DEBUG_IRQ_STATUS, "vepu_irq irq status %x\n", irq_status); - - if (likely(irq_status & ENC_INTERRUPT_BIT)) { - time_record(&tasks[TASK_VPU_ENC], 1); - - if (check_irq_err(&tasks[TASK_VPU_ENC], irq_status)) - atomic_add(1, &pservice->reset_request); - - /* clear enc IRQ */ - writel(irq_status & (~ENC_INTERRUPT_BIT), dev->hwregs + ENC_INTERRUPT_REGISTER); - atomic_add(1, &dev->irq_count_codec); - time_diff(&tasks[TASK_VPU_ENC]); - } - - pservice->irq_status = irq_status; - - /*vcodec_exit_mode(pservice);*/ - - if (atomic_read(&dev->irq_count_codec)) - return IRQ_WAKE_THREAD; - else - return IRQ_NONE; -} - -static irqreturn_t vepu_isr(int irq, void *dev_id) -{ - struct vpu_subdev_data *data = (struct vpu_subdev_data*)dev_id; - struct vpu_service_info *pservice = data->pservice; - vpu_device *dev = &data->enc_dev; - - mutex_lock(&pservice->lock); - if (atomic_read(&dev->irq_count_codec)) { - atomic_sub(1, &dev->irq_count_codec); - if (NULL == pservice->reg_codec) { - vpu_err("error: enc isr with no task waiting\n"); - } else { - reg_from_run_to_done(data, pservice->reg_codec); - } - } - try_set_reg(data); - mutex_unlock(&pservice->lock); - return IRQ_HANDLED; -} - -static int __init vcodec_service_init(void) -{ - int ret; - - if ((ret = platform_driver_register(&vcodec_driver)) != 0) { - vpu_err("Platform device register failed (%d).\n", ret); - return ret; - } - -#ifdef CONFIG_DEBUG_FS - vcodec_debugfs_init(); -#endif - - return ret; -} - -static void __exit vcodec_service_exit(void) -{ -#ifdef CONFIG_DEBUG_FS - vcodec_debugfs_exit(); -#endif - - platform_driver_unregister(&vcodec_driver); -} - -module_init(vcodec_service_init); -module_exit(vcodec_service_exit); - -#ifdef CONFIG_DEBUG_FS -#include - -static int vcodec_debugfs_init() -{ - parent = debugfs_create_dir("vcodec", NULL); - if (!parent) - return -1; - - return 0; -} - -static void vcodec_debugfs_exit() -{ - debugfs_remove(parent); -} - -static struct dentry* vcodec_debugfs_create_device_dir(char *dirname, struct dentry *parent) -{ - return debugfs_create_dir(dirname, parent); -} - -static int debug_vcodec_show(struct seq_file *s, void *unused) -{ - struct vpu_subdev_data *data = s->private; - struct vpu_service_info *pservice = data->pservice; - unsigned int i, n; - vpu_reg *reg, *reg_tmp; - vpu_session *session, *session_tmp; - - mutex_lock(&pservice->lock); - vpu_service_power_on(pservice); - if (data->hw_info->hw_id != HEVC_ID) { - seq_printf(s, "\nENC Registers:\n"); - n = data->enc_dev.iosize >> 2; - for (i = 0; i < n; i++) - seq_printf(s, "\tswreg%d = %08X\n", i, readl(data->enc_dev.hwregs + i)); - } - seq_printf(s, "\nDEC Registers:\n"); - n = data->dec_dev.iosize >> 2; - for (i = 0; i < n; i++) - seq_printf(s, "\tswreg%d = %08X\n", i, readl(data->dec_dev.hwregs + i)); - - seq_printf(s, "\nvpu service status:\n"); - list_for_each_entry_safe(session, session_tmp, &pservice->session, list_session) { - seq_printf(s, "session pid %d type %d:\n", session->pid, session->type); - /*seq_printf(s, "waiting reg set %d\n");*/ - list_for_each_entry_safe(reg, reg_tmp, &session->waiting, session_link) { - seq_printf(s, "waiting register set\n"); - } - list_for_each_entry_safe(reg, reg_tmp, &session->running, session_link) { - seq_printf(s, "running register set\n"); - } - list_for_each_entry_safe(reg, reg_tmp, &session->done, session_link) { - seq_printf(s, "done register set\n"); - } - } - - seq_printf(s, "\npower counter: on %d off %d\n", - atomic_read(&pservice->power_on_cnt), - atomic_read(&pservice->power_off_cnt)); - mutex_unlock(&pservice->lock); - vpu_service_power_off(pservice); - - return 0; -} - -static int debug_vcodec_open(struct inode *inode, struct file *file) -{ - return single_open(file, debug_vcodec_show, inode->i_private); -} - -#endif - -#if HEVC_TEST_ENABLE & defined(CONFIG_ION_ROCKCHIP) -#include "hevc_test_inc/pps_00.h" -#include "hevc_test_inc/register_00.h" -#include "hevc_test_inc/rps_00.h" -#include "hevc_test_inc/scaling_list_00.h" -#include "hevc_test_inc/stream_00.h" - -#include "hevc_test_inc/pps_01.h" -#include "hevc_test_inc/register_01.h" -#include "hevc_test_inc/rps_01.h" -#include "hevc_test_inc/scaling_list_01.h" -#include "hevc_test_inc/stream_01.h" - -#include "hevc_test_inc/cabac.h" - -extern struct ion_client *rockchip_ion_client_create(const char * name); - -static struct ion_client *ion_client = NULL; -u8* get_align_ptr(u8* tbl, int len, u32 *phy) -{ - int size = (len+15) & (~15); - struct ion_handle *handle; - u8 *ptr; - - if (ion_client == NULL) - ion_client = rockchip_ion_client_create("vcodec"); - - handle = ion_alloc(ion_client, (size_t)len, 16, ION_HEAP(ION_CMA_HEAP_ID), 0); - - ptr = ion_map_kernel(ion_client, handle); - - ion_phys(ion_client, handle, phy, &size); - - memcpy(ptr, tbl, len); - - return ptr; -} - -u8* get_align_ptr_no_copy(int len, u32 *phy) -{ - int size = (len+15) & (~15); - struct ion_handle *handle; - u8 *ptr; - - if (ion_client == NULL) - ion_client = rockchip_ion_client_create("vcodec"); - - handle = ion_alloc(ion_client, (size_t)len, 16, ION_HEAP(ION_CMA_HEAP_ID), 0); - - ptr = ion_map_kernel(ion_client, handle); - - ion_phys(ion_client, handle, phy, &size); - - return ptr; -} - -#define TEST_CNT 2 -static int hevc_test_case0(vpu_service_info *pservice) -{ - vpu_session session; - vpu_reg *reg; - unsigned long size = 272; - int testidx = 0; - int ret = 0; - u8 *pps_tbl[TEST_CNT]; - u8 *register_tbl[TEST_CNT]; - u8 *rps_tbl[TEST_CNT]; - u8 *scaling_list_tbl[TEST_CNT]; - u8 *stream_tbl[TEST_CNT]; - - int stream_size[2]; - int pps_size[2]; - int rps_size[2]; - int scl_size[2]; - int cabac_size[2]; - - u32 phy_pps; - u32 phy_rps; - u32 phy_scl; - u32 phy_str; - u32 phy_yuv; - u32 phy_ref; - u32 phy_cabac; - - volatile u8 *stream_buf; - volatile u8 *pps_buf; - volatile u8 *rps_buf; - volatile u8 *scl_buf; - volatile u8 *yuv_buf; - volatile u8 *cabac_buf; - volatile u8 *ref_buf; - - u8 *pps; - u8 *yuv[2]; - int i; - - pps_tbl[0] = pps_00; - pps_tbl[1] = pps_01; - - register_tbl[0] = register_00; - register_tbl[1] = register_01; - - rps_tbl[0] = rps_00; - rps_tbl[1] = rps_01; - - scaling_list_tbl[0] = scaling_list_00; - scaling_list_tbl[1] = scaling_list_01; - - stream_tbl[0] = stream_00; - stream_tbl[1] = stream_01; - - stream_size[0] = sizeof(stream_00); - stream_size[1] = sizeof(stream_01); - - pps_size[0] = sizeof(pps_00); - pps_size[1] = sizeof(pps_01); - - rps_size[0] = sizeof(rps_00); - rps_size[1] = sizeof(rps_01); - - scl_size[0] = sizeof(scaling_list_00); - scl_size[1] = sizeof(scaling_list_01); - - cabac_size[0] = sizeof(Cabac_table); - cabac_size[1] = sizeof(Cabac_table); - - /* create session */ - session.pid = current->pid; - session.type = VPU_DEC; - INIT_LIST_HEAD(&session.waiting); - INIT_LIST_HEAD(&session.running); - INIT_LIST_HEAD(&session.done); - INIT_LIST_HEAD(&session.list_session); - init_waitqueue_head(&session.wait); - atomic_set(&session.task_running, 0); - list_add_tail(&session.list_session, &pservice->session); - - yuv[0] = get_align_ptr_no_copy(256*256*2, &phy_yuv); - yuv[1] = get_align_ptr_no_copy(256*256*2, &phy_ref); - - while (testidx < TEST_CNT) { - /* create registers */ - reg = kmalloc(sizeof(vpu_reg)+pservice->reg_size, GFP_KERNEL); - if (NULL == reg) { - vpu_err("error: kmalloc fail in reg_init\n"); - return -1; - } - - if (size > pservice->reg_size) { - printk("warning: vpu reg size %lu is larger than hw reg size %lu\n", size, pservice->reg_size); - size = pservice->reg_size; - } - reg->session = &session; - reg->type = session.type; - reg->size = size; - reg->freq = VPU_FREQ_DEFAULT; - reg->reg = (unsigned long *)®[1]; - INIT_LIST_HEAD(®->session_link); - INIT_LIST_HEAD(®->status_link); - - /* TODO: stuff registers */ - memcpy(®->reg[0], register_tbl[testidx], /*sizeof(register_00)*/ 176); - - stream_buf = get_align_ptr(stream_tbl[testidx], stream_size[testidx], &phy_str); - pps_buf = get_align_ptr(pps_tbl[0], pps_size[0], &phy_pps); - rps_buf = get_align_ptr(rps_tbl[testidx], rps_size[testidx], &phy_rps); - scl_buf = get_align_ptr(scaling_list_tbl[testidx], scl_size[testidx], &phy_scl); - cabac_buf = get_align_ptr(Cabac_table, cabac_size[testidx], &phy_cabac); - - pps = pps_buf; - - /* TODO: replace reigster address */ - for (i=0; i<64; i++) { - u32 scaling_offset; - u32 tmp; - - scaling_offset = (u32)pps[i*80+74]; - scaling_offset += (u32)pps[i*80+75] << 8; - scaling_offset += (u32)pps[i*80+76] << 16; - scaling_offset += (u32)pps[i*80+77] << 24; - - tmp = phy_scl + scaling_offset; - - pps[i*80+74] = tmp & 0xff; - pps[i*80+75] = (tmp >> 8) & 0xff; - pps[i*80+76] = (tmp >> 16) & 0xff; - pps[i*80+77] = (tmp >> 24) & 0xff; - } - - printk("%s %d, phy stream %08x, phy pps %08x, phy rps %08x\n", - __func__, __LINE__, phy_str, phy_pps, phy_rps); - - reg->reg[1] = 0x21; - reg->reg[4] = phy_str; - reg->reg[5] = ((stream_size[testidx]+15)&(~15))+64; - reg->reg[6] = phy_cabac; - reg->reg[7] = testidx?phy_ref:phy_yuv; - reg->reg[42] = phy_pps; - reg->reg[43] = phy_rps; - for (i = 10; i <= 24; i++) - reg->reg[i] = phy_yuv; - - mutex_lock(pservice->lock); - list_add_tail(®->status_link, &pservice->waiting); - list_add_tail(®->session_link, &session.waiting); - mutex_unlock(pservice->lock); - - /* stuff hardware */ - try_set_reg(data); - - /* wait for result */ - ret = wait_event_timeout(session.wait, !list_empty(&session.done), VPU_TIMEOUT_DELAY); - if (!list_empty(&session.done)) { - if (ret < 0) - vpu_err("warning: pid %d wait task sucess but wait_evernt ret %d\n", session.pid, ret); - ret = 0; - } else { - if (unlikely(ret < 0)) { - vpu_err("error: pid %d wait task ret %d\n", session.pid, ret); - } else if (0 == ret) { - vpu_err("error: pid %d wait %d task done timeout\n", session.pid, atomic_read(&session.task_running)); - ret = -ETIMEDOUT; - } - } - if (ret < 0) { - int task_running = atomic_read(&session.task_running); - int n; - mutex_lock(pservice->lock); - vpu_service_dump(pservice); - if (task_running) { - atomic_set(&session.task_running, 0); - atomic_sub(task_running, &pservice->total_running); - printk("%d task is running but not return, reset hardware...", task_running); - vpu_reset(data); - printk("done\n"); - } - vpu_service_session_clear(pservice, &session); - mutex_unlock(pservice->lock); - - printk("\nDEC Registers:\n"); - n = data->dec_dev.iosize >> 2; - for (i=0; idec_dev.hwregs + i)); - - vpu_err("test index %d failed\n", testidx); - break; - } else { - vpu_debug(DEBUG_EXTRA_INFO, "test index %d success\n", testidx); - - vpu_reg *reg = list_entry(session.done.next, vpu_reg, session_link); - - for (i=0; i<68; i++) { - if (i % 4 == 0) - printk("%02d: ", i); - printk("%08x ", reg->reg[i]); - if ((i+1) % 4 == 0) - printk("\n"); - } - - testidx++; - } - - reg_deinit(data, reg); - } - - return 0; -} - -#endif - diff --git a/arch/arm/mach-rockchip/vcodec_service.h b/arch/arm/mach-rockchip/vcodec_service.h deleted file mode 100644 index 70fa21f3056d..000000000000 --- a/arch/arm/mach-rockchip/vcodec_service.h +++ /dev/null @@ -1,271 +0,0 @@ - -/* arch/arm/mach-rk29/include/mach/vpu_service.h - * - * Copyright (C) 2010 ROCKCHIP, Inc. - * author: chenhengming chm@rock-chips.com - * - * This software is licensed under the terms of the GNU General Public - * License version 2, as published by the Free Software Foundation, and - * may be copied, distributed, and modified under those terms. - * - * 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 __ARCH_ARM_MACH_RK29_VCODEC_SERVICE_H -#define __ARCH_ARM_MACH_RK29_VCODEC_SERVICE_H - -#include /* needed for the _IOW etc stuff used later */ - -/* - * Ioctl definitions - */ - -/* Use 'k' as magic number */ -#define VPU_IOC_MAGIC 'l' - -#define VPU_IOC_SET_CLIENT_TYPE _IOW(VPU_IOC_MAGIC, 1, unsigned long) -#define VPU_IOC_GET_HW_FUSE_STATUS _IOW(VPU_IOC_MAGIC, 2, unsigned long) - -#define VPU_IOC_SET_REG _IOW(VPU_IOC_MAGIC, 3, unsigned long) -#define VPU_IOC_GET_REG _IOW(VPU_IOC_MAGIC, 4, unsigned long) - -#define VPU_IOC_PROBE_IOMMU_STATUS _IOR(VPU_IOC_MAGIC, 5, unsigned long) - -#ifdef CONFIG_COMPAT -#define COMPAT_VPU_IOC_SET_CLIENT_TYPE _IOW(VPU_IOC_MAGIC, 1, u32) -#define COMPAT_VPU_IOC_GET_HW_FUSE_STATUS _IOW(VPU_IOC_MAGIC, 2, u32) - -#define COMPAT_VPU_IOC_SET_REG _IOW(VPU_IOC_MAGIC, 3, u32) -#define COMPAT_VPU_IOC_GET_REG _IOW(VPU_IOC_MAGIC, 4, u32) - -#define COMPAT_VPU_IOC_PROBE_IOMMU_STATUS _IOR(VPU_IOC_MAGIC, 5, u32) -#endif - -enum VPU_CLIENT_TYPE { - VPU_ENC = 0x0, - VPU_DEC = 0x1, - VPU_PP = 0x2, - VPU_DEC_PP = 0x3, - VPU_TYPE_BUTT , -}; - -/* Hardware decoder configuration description */ - -struct vpu_dec_config { - u32 maxDecPicWidth; /* Maximum video decoding width supported */ - u32 maxPpOutPicWidth; /* Maximum output width of Post-Processor */ - u32 h264_support; /* HW supports h.264 */ - u32 jpegSupport; /* HW supports JPEG */ - u32 mpeg4Support; /* HW supports MPEG-4 */ - u32 customMpeg4Support; /* HW supports custom MPEG-4 features */ - u32 vc1Support; /* HW supports VC-1 Simple */ - u32 mpeg2Support; /* HW supports MPEG-2 */ - u32 ppSupport; /* HW supports post-processor */ - u32 ppConfig; /* HW post-processor functions bitmask */ - u32 sorensonSparkSupport; /* HW supports Sorenson Spark */ - u32 refBufSupport; /* HW supports reference picture buffering */ - u32 vp6Support; /* HW supports VP6 */ - u32 vp7Support; /* HW supports VP7 */ - u32 vp8Support; /* HW supports VP8 */ - u32 avsSupport; /* HW supports AVS */ - u32 jpegESupport; /* HW supports JPEG extensions */ - u32 rvSupport; /* HW supports REAL */ - u32 mvcSupport; /* HW supports H264 MVC extension */ -}; - -/* Hardware encoder configuration description */ - -struct vpu_enc_config { - u32 maxEncodedWidth; /* Maximum supported width for video encoding (not JPEG) */ - u32 h264Enabled; /* HW supports H.264 */ - u32 jpegEnabled; /* HW supports JPEG */ - u32 mpeg4Enabled; /* HW supports MPEG-4 */ - u32 vsEnabled; /* HW supports video stabilization */ - u32 rgbEnabled; /* HW supports RGB input */ - u32 reg_size; - u32 reserv[2]; /* reverved */ -}; - -union vpu_config { - struct vpu_dec_config vpu_dec_conf; - struct vpu_enc_config vpu_enc_conf; -}; - -struct VPUHwCfgReq_t { - u32 *cfg; - u32 size; -}; - -#define DWL_MPEG2_E 31 /* 1 bit */ -#define DWL_VC1_E 29 /* 2 bits */ -#define DWL_JPEG_E 28 /* 1 bit */ -#define DWL_MPEG4_E 26 /* 2 bits */ -#define DWL_H264_E 24 /* 2 bits */ -#define DWL_VP6_E 23 /* 1 bit */ -#define DWL_PJPEG_E 22 /* 1 bit */ -#define DWL_REF_BUFF_E 20 /* 1 bit */ - -#define DWL_JPEG_EXT_E 31 /* 1 bit */ -#define DWL_REF_BUFF_ILACE_E 30 /* 1 bit */ -#define DWL_MPEG4_CUSTOM_E 29 /* 1 bit */ -#define DWL_REF_BUFF_DOUBLE_E 28 /* 1 bit */ -#define DWL_RV_E 26 /* 2 bits */ -#define DWL_VP7_E 24 /* 1 bit */ -#define DWL_VP8_E 23 /* 1 bit */ -#define DWL_AVS_E 22 /* 1 bit */ -#define DWL_MVC_E 20 /* 2 bits */ - -#define DWL_CFG_E 24 /* 4 bits */ -#define DWL_PP_E 16 /* 1 bit */ - -#define DWL_SORENSONSPARK_E 11 /* 1 bit */ - -#define DWL_H264_FUSE_E 31 /* 1 bit */ -#define DWL_MPEG4_FUSE_E 30 /* 1 bit */ -#define DWL_MPEG2_FUSE_E 29 /* 1 bit */ -#define DWL_SORENSONSPARK_FUSE_E 28 /* 1 bit */ -#define DWL_JPEG_FUSE_E 27 /* 1 bit */ -#define DWL_VP6_FUSE_E 26 /* 1 bit */ -#define DWL_VC1_FUSE_E 25 /* 1 bit */ -#define DWL_PJPEG_FUSE_E 24 /* 1 bit */ -#define DWL_CUSTOM_MPEG4_FUSE_E 23 /* 1 bit */ -#define DWL_RV_FUSE_E 22 /* 1 bit */ -#define DWL_VP7_FUSE_E 21 /* 1 bit */ -#define DWL_VP8_FUSE_E 20 /* 1 bit */ -#define DWL_AVS_FUSE_E 19 /* 1 bit */ -#define DWL_MVC_FUSE_E 18 /* 1 bit */ - -#define DWL_DEC_MAX_1920_FUSE_E 15 /* 1 bit */ -#define DWL_DEC_MAX_1280_FUSE_E 14 /* 1 bit */ -#define DWL_DEC_MAX_720_FUSE_E 13 /* 1 bit */ -#define DWL_DEC_MAX_352_FUSE_E 12 /* 1 bit */ -#define DWL_REF_BUFF_FUSE_E 7 /* 1 bit */ - - -#define DWL_PP_FUSE_E 31 /* 1 bit */ -#define DWL_PP_DEINTERLACE_FUSE_E 30 /* 1 bit */ -#define DWL_PP_ALPHA_BLEND_FUSE_E 29 /* 1 bit */ -#define DWL_PP_MAX_1920_FUSE_E 15 /* 1 bit */ -#define DWL_PP_MAX_1280_FUSE_E 14 /* 1 bit */ -#define DWL_PP_MAX_720_FUSE_E 13 /* 1 bit */ -#define DWL_PP_MAX_352_FUSE_E 12 /* 1 bit */ - - -#define MPEG4_NOT_SUPPORTED (u32)(0x00) -#define MPEG4_SIMPLE_PROFILE (u32)(0x01) -#define MPEG4_ADVANCED_SIMPLE_PROFILE (u32)(0x02) -#define MPEG4_CUSTOM_NOT_SUPPORTED (u32)(0x00) -#define MPEG4_CUSTOM_FEATURE_1 (u32)(0x01) -#define H264_NOT_SUPPORTED (u32)(0x00) -#define H264_BASELINE_PROFILE (u32)(0x01) -#define H264_MAIN_PROFILE (u32)(0x02) -#define H264_HIGH_PROFILE (u32)(0x03) -#define VC1_NOT_SUPPORTED (u32)(0x00) -#define VC1_SIMPLE_PROFILE (u32)(0x01) -#define VC1_MAIN_PROFILE (u32)(0x02) -#define VC1_ADVANCED_PROFILE (u32)(0x03) -#define MPEG2_NOT_SUPPORTED (u32)(0x00) -#define MPEG2_MAIN_PROFILE (u32)(0x01) -#define JPEG_NOT_SUPPORTED (u32)(0x00) -#define JPEG_BASELINE (u32)(0x01) -#define JPEG_PROGRESSIVE (u32)(0x02) -#define PP_NOT_SUPPORTED (u32)(0x00) -#define PP_SUPPORTED (u32)(0x01) -#define PP_DITHERING (u32)(0x10000000) -#define PP_SCALING (u32)(0x0C000000) -#define PP_DEINTERLACING (u32)(0x02000000) -#define PP_ALPHA_BLENDING (u32)(0x01000000) -#define SORENSON_SPARK_NOT_SUPPORTED (u32)(0x00) -#define SORENSON_SPARK_SUPPORTED (u32)(0x01) -#define VP6_NOT_SUPPORTED (u32)(0x00) -#define VP6_SUPPORTED (u32)(0x01) -#define VP7_NOT_SUPPORTED (u32)(0x00) -#define VP7_SUPPORTED (u32)(0x01) -#define VP8_NOT_SUPPORTED (u32)(0x00) -#define VP8_SUPPORTED (u32)(0x01) -#define REF_BUF_NOT_SUPPORTED (u32)(0x00) -#define REF_BUF_SUPPORTED (u32)(0x01) -#define REF_BUF_INTERLACED (u32)(0x02) -#define REF_BUF_DOUBLE (u32)(0x04) -#define AVS_NOT_SUPPORTED (u32)(0x00) -#define AVS_SUPPORTED (u32)(0x01) -#define JPEG_EXT_NOT_SUPPORTED (u32)(0x00) -#define JPEG_EXT_SUPPORTED (u32)(0x01) -#define RV_NOT_SUPPORTED (u32)(0x00) -#define RV_SUPPORTED (u32)(0x01) -#define MVC_NOT_SUPPORTED (u32)(0x00) -#define MVC_SUPPORTED (u32)(0x01) - -#define H264_NOT_SUPPORTED_FUSE (u32)(0x00) -#define H264_FUSE_ENABLED (u32)(0x01) -#define MPEG4_NOT_SUPPORTED_FUSE (u32)(0x00) -#define MPEG4_FUSE_ENABLED (u32)(0x01) -#define MPEG2_NOT_SUPPORTED_FUSE (u32)(0x00) -#define MPEG2_FUSE_ENABLED (u32)(0x01) -#define SORENSON_SPARK_NOT_SUPPORTED_FUSE (u32)(0x00) -#define SORENSON_SPARK_ENABLED (u32)(0x01) -#define JPEG_NOT_SUPPORTED_FUSE (u32)(0x00) -#define JPEG_FUSE_ENABLED (u32)(0x01) -#define VP6_NOT_SUPPORTED_FUSE (u32)(0x00) -#define VP6_FUSE_ENABLED (u32)(0x01) -#define VP7_NOT_SUPPORTED_FUSE (u32)(0x00) -#define VP7_FUSE_ENABLED (u32)(0x01) -#define VP8_NOT_SUPPORTED_FUSE (u32)(0x00) -#define VP8_FUSE_ENABLED (u32)(0x01) -#define VC1_NOT_SUPPORTED_FUSE (u32)(0x00) -#define VC1_FUSE_ENABLED (u32)(0x01) -#define JPEG_PROGRESSIVE_NOT_SUPPORTED_FUSE (u32)(0x00) -#define JPEG_PROGRESSIVE_FUSE_ENABLED (u32)(0x01) -#define REF_BUF_NOT_SUPPORTED_FUSE (u32)(0x00) -#define REF_BUF_FUSE_ENABLED (u32)(0x01) -#define AVS_NOT_SUPPORTED_FUSE (u32)(0x00) -#define AVS_FUSE_ENABLED (u32)(0x01) -#define RV_NOT_SUPPORTED_FUSE (u32)(0x00) -#define RV_FUSE_ENABLED (u32)(0x01) -#define MVC_NOT_SUPPORTED_FUSE (u32)(0x00) -#define MVC_FUSE_ENABLED (u32)(0x01) - -#define PP_NOT_SUPPORTED_FUSE (u32)(0x00) -#define PP_FUSE_ENABLED (u32)(0x01) -#define PP_FUSE_DEINTERLACING_ENABLED (u32)(0x40000000) -#define PP_FUSE_ALPHA_BLENDING_ENABLED (u32)(0x20000000) -#define MAX_PP_OUT_WIDHT_1920_FUSE_ENABLED (u32)(0x00008000) -#define MAX_PP_OUT_WIDHT_1280_FUSE_ENABLED (u32)(0x00004000) -#define MAX_PP_OUT_WIDHT_720_FUSE_ENABLED (u32)(0x00002000) -#define MAX_PP_OUT_WIDHT_352_FUSE_ENABLED (u32)(0x00001000) - -#define VPU_DEC_HWCFG0 50 -#define VPU_DEC_HWCFG1 51 -#define VPU_DEC_HW_FUSE_CFG 57 -#define VPU_PP_HW_SYNTH_CFG 40 -#define VPU_PP_HW_FUSE_CFG 41 - -struct VPUHwFuseStatus_t { - u32 h264SupportFuse; /* HW supports h.264 */ - u32 mpeg4SupportFuse; /* HW supports MPEG-4 */ - u32 mpeg2SupportFuse; /* HW supports MPEG-2 */ - u32 sorensonSparkSupportFuse; /* HW supports Sorenson Spark */ - u32 jpegSupportFuse; /* HW supports JPEG */ - u32 vp6SupportFuse; /* HW supports VP6 */ - u32 vp7SupportFuse; /* HW supports VP6 */ - u32 vp8SupportFuse; /* HW supports VP6 */ - u32 vc1SupportFuse; /* HW supports VC-1 Simple */ - u32 jpegProgSupportFuse; /* HW supports Progressive JPEG */ - u32 ppSupportFuse; /* HW supports post-processor */ - u32 ppConfigFuse; /* HW post-processor functions bitmask */ - u32 maxDecPicWidthFuse; /* Maximum video decoding width supported */ - u32 maxPpOutPicWidthFuse; /* Maximum output width of Post-Processor */ - u32 refBufSupportFuse; /* HW supports reference picture buffering */ - u32 avsSupportFuse; /* one of the AVS values defined above */ - u32 rvSupportFuse; /* one of the REAL values defined above */ - u32 mvcSupportFuse; - u32 customMpeg4SupportFuse; /* Fuse for custom MPEG-4 */ -}; - - -#endif - diff --git a/drivers/video/rockchip/Kconfig b/drivers/video/rockchip/Kconfig index 6034139cacf2..670a5cd12135 100755 --- a/drivers/video/rockchip/Kconfig +++ b/drivers/video/rockchip/Kconfig @@ -65,6 +65,7 @@ source "drivers/video/rockchip/transmitter/Kconfig" source "drivers/video/rockchip/hdmi/Kconfig" source "drivers/video/rockchip/tve/Kconfig" source "drivers/video/rockchip/rga/Kconfig" +source "drivers/video/rockchip/vcodec/Kconfig" source "drivers/video/rockchip/rga2/Kconfig" source "drivers/video/rockchip/iep/Kconfig" diff --git a/drivers/video/rockchip/Makefile b/drivers/video/rockchip/Makefile index 4d899cbd9a69..b828e66823f6 100755 --- a/drivers/video/rockchip/Makefile +++ b/drivers/video/rockchip/Makefile @@ -6,5 +6,5 @@ obj-$(CONFIG_FB_ROCKCHIP) += display-sys.o lcdc/ obj-$(CONFIG_RK_HDMI) += hdmi/ obj-$(CONFIG_IEP) += iep/ obj-$(CONFIG_RK_TVENCODER) += tve/ - +obj-$(CONFIG_RK_VCODEC) += vcodec/ diff --git a/drivers/video/rockchip/vcodec/Kconfig b/drivers/video/rockchip/vcodec/Kconfig new file mode 100644 index 000000000000..149b2dffcfdf --- /dev/null +++ b/drivers/video/rockchip/vcodec/Kconfig @@ -0,0 +1,9 @@ +menu "VCODEC" + depends on ARCH_ROCKCHIP + +config RK_VCODEC + tristate "VCODEC (VPU HEVC) service driver in kernel" + depends on ARCH_ROCKCHIP + default n + +endmenu diff --git a/drivers/video/rockchip/vcodec/Makefile b/drivers/video/rockchip/vcodec/Makefile new file mode 100644 index 000000000000..cf0cdcbaddf7 --- /dev/null +++ b/drivers/video/rockchip/vcodec/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_RK_VCODEC) += vcodec_service.o diff --git a/drivers/video/rockchip/vcodec/vcodec_hw_info.h b/drivers/video/rockchip/vcodec/vcodec_hw_info.h new file mode 100644 index 000000000000..e8de8b1370c2 --- /dev/null +++ b/drivers/video/rockchip/vcodec/vcodec_hw_info.h @@ -0,0 +1,227 @@ +/** + * Copyright (C) 2015 Fuzhou Rockchip Electronics Co., Ltd + * author: Herman Chen herman.chen@rock-chips.com + * Alpha Lin, alpha.lin@rock-chips.com + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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 __ARCH_ARM_MACH_ROCKCHIP_VCODEC_HW_INFO_H +#define __ARCH_ARM_MACH_ROCKCHIP_VCODEC_HW_INFO_H + +/* + * Hardware id is for hardware detection + * Driver will read the hardware ID register first, then try to find a mactch + * hardware from the enum ID below. + */ +enum VPU_HW_ID { + VPU_DEC_ID_9190 = 0x6731, + VPU_ID_8270 = 0x8270, + VPU_ID_4831 = 0x4831, + HEVC_ID = 0x6867, + RKV_DEC_ID = 0x6876, + VPU2_ID = 0x0000, +}; + +/* + * Different hardware has different feature. So we catalogue these features + * into three class: + * + * 1. register io feature determined by hardware type + * including register offset, register file size, etc + * + * 2. runtime register config feature determined by task type + * including irq / enable / length register, bit mask, etc + * + * 3. file handle translate feature determined by vcodec format type + * register translation map table + * + * These three type features composite a complete codec information structure + */ +enum HW_TYPE { + HW_VPU, + HW_VPU2, + HW_RKV, + HW_TYPE_BUTT, +}; + +enum TASK_TYPE { + TASK_ENC, + TASK_DEC, + TASK_PP, + TASK_DEC_PP, + TASK_TYPE_BUTT, +}; + +enum FORMAT_TYPE { + FMT_DEC_BASE = 0, + FMT_JPEGD = FMT_DEC_BASE, + + FMT_H263D, + FMT_H264D, + FMT_H265D, + + FMT_MPEG1D, + FMT_MPEG2D, + FMT_MPEG4D, + + FMT_VP6D, + FMT_VP7D, + FMT_VP8D, + FMT_VP9D, + + FMT_VC1D, + FMT_AVSD, + + FMT_DEC_BUTT, + + FMT_PP_BASE = FMT_DEC_BUTT, + FMT_PP = FMT_PP_BASE, + FMT_PP_BUTT, + + FMT_ENC_BASE = FMT_PP_BUTT, + FMT_JPEGE = FMT_ENC_BASE, + + FMT_H264E, + + FMT_VP8E, + + FMT_ENC_BUTT, + FMT_TYPE_BUTT = FMT_ENC_BUTT, +}; + +/** + * struct for hardware task operation + */ +struct vpu_hw_info { + enum VPU_HW_ID hw_id; + u32 enc_offset; + u32 enc_reg_num; + u32 enc_io_size; + + u32 dec_offset; + u32 dec_reg_num; + u32 dec_io_size; + + /* + * register range for enc/dec/pp/dec_pp + * base/end of dec/pp/dec_pp specify the register range to config + */ + u32 base_dec; + u32 base_pp; + u32 base_dec_pp; + u32 end_dec; + u32 end_pp; + u32 end_dec_pp; +}; + +struct vpu_task_info { + char *name; + struct timeval start; + struct timeval end; + + /* + * task enable register + * use for enable hardware task process + * -1 for invalid + */ + int reg_en; + + /* register of task auto gating, alway valid */ + int reg_gating; + + /* register of task irq, alway valid */ + int reg_irq; + + /* + * stream length register + * only valid for decoder task + * -1 for invalid (encoder) + */ + int reg_len; + + /* + * direct mv register + * special offset scale, offset multiply by 16 + * + * valid on vpu & vpu2 + * -1 for invalid + */ + int reg_dir_mv; + + /* + * pps register + * special register for scaling list address process + * + * valid on rkv + * -1 for invalid + */ + int reg_pps; + + /* + * decoder pipeline mode register + * + * valid on vpu & vpu2 + * -1 for invalid + */ + int reg_pipe; + + /* task enable bit mask for enable register */ + u32 enable_mask; + + /* task auto gating mask for enable register */ + u32 gating_mask; + + /* task pipeline mode mask for pipe register */ + u32 pipe_mask; + + /* task inturrpt bit mask for irq register */ + u32 irq_mask; + + /* task ready bit mask for irq register */ + u32 ready_mask; + + /* task error bit mask for irq register */ + u32 error_mask; + + enum FORMAT_TYPE (*get_fmt)(u32 *regs); +}; + +struct vpu_trans_info { + const int count; + const char * const table; +}; + +struct vcodec_info { + enum VPU_HW_ID hw_id; + struct vpu_hw_info *hw_info; + struct vpu_task_info *task_info; + const struct vpu_trans_info *trans_info; +}; + +#define DEF_FMT_TRANS_TBL(fmt, args...) \ + static const char trans_tbl_##fmt[] = { \ + args \ + } + +#define SETUP_FMT_TBL(id, fmt) \ + [id] = { \ + .count = sizeof(trans_tbl_##fmt), \ + .table = trans_tbl_##fmt, \ + } + +#define EMPTY_FMT_TBL(id) \ + [id] = { \ + .count = 0, \ + .table = NULL, \ + } + +#endif diff --git a/drivers/video/rockchip/vcodec/vcodec_hw_rkv.h b/drivers/video/rockchip/vcodec/vcodec_hw_rkv.h new file mode 100644 index 000000000000..27e1adcd4bbc --- /dev/null +++ b/drivers/video/rockchip/vcodec/vcodec_hw_rkv.h @@ -0,0 +1,226 @@ +/** + * Copyright (C) 2015 Fuzhou Rockchip Electronics Co., Ltd + * author: chenhengming chm@rock-chips.com + * Alpha Lin, alpha.lin@rock-chips.com + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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 __ARCH_ARM_MACH_ROCKCHIP_VCODEC_HW_RKV_H +#define __ARCH_ARM_MACH_ROCKCHIP_VCODEC_HW_RKV_H + +#include "vcodec_hw_info.h" + +/* hardware information */ +#define REG_NUM_HEVC_DEC (68) +#define REG_NUM_RKV_DEC (78) + +/* enable and gating register */ +#define RKV_REG_EN_DEC 1 +#define RKV_REG_DEC_GATING_BIT BIT(1) + +/* interrupt and error status register */ +#define HEVC_INTERRUPT_REGISTER 1 +#define HEVC_INTERRUPT_BIT BIT(8) +#define HEVC_DEC_INT_RAW_BIT BIT(9) +#define HEVC_READY_BIT BIT(12) +#define HEVC_DEC_BUS_ERROR_BIT BIT(13) +#define HEVC_DEC_STR_ERROR_BIT BIT(14) +#define HEVC_DEC_TIMEOUT_BIT BIT(15) +#define HEVC_DEC_BUFFER_EMPTY_BIT BIT(16) +#define HEVC_DEC_COLMV_ERROR_BIT BIT(17) +#define HEVC_DEC_ERR_MASK (HEVC_DEC_BUS_ERROR_BIT \ + |HEVC_DEC_STR_ERROR_BIT \ + |HEVC_DEC_TIMEOUT_BIT \ + |HEVC_DEC_BUFFER_EMPTY_BIT \ + |HEVC_DEC_COLMV_ERROR_BIT) + +#define RKV_DEC_INTERRUPT_REGISTER 1 +#define RKV_DEC_INTERRUPT_BIT BIT(8) +#define RKV_DEC_INT_RAW_BIT BIT(9) +#define RKV_DEC_READY_BIT BIT(12) +#define RKV_DEC_BUS_ERROR_BIT BIT(13) +#define RKV_DEC_STR_ERROR_BIT BIT(14) +#define RKV_DEC_TIMEOUT_BIT BIT(15) +#define RKV_DEC_BUFFER_EMPTY_BIT BIT(16) +#define RKV_DEC_COLMV_ERROR_BIT BIT(17) +#define RKV_DEC_ERR_MASK (RKV_DEC_BUS_ERROR_BIT \ + |RKV_DEC_STR_ERROR_BIT \ + |RKV_DEC_TIMEOUT_BIT \ + |RKV_DEC_BUFFER_EMPTY_BIT \ + |RKV_DEC_COLMV_ERROR_BIT) + +static const enum FORMAT_TYPE rkv_dec_fmt_tbl[] = { + [0] = FMT_H265D, + [1] = FMT_H264D, + [2] = FMT_VP9D, + [3] = FMT_TYPE_BUTT, +}; + +static enum FORMAT_TYPE rkv_dec_get_fmt(u32 *regs) +{ + u32 fmt_id = (regs[2] >> 20) & 0x3; + enum FORMAT_TYPE type = rkv_dec_fmt_tbl[fmt_id]; + return type; +} + +static struct vpu_task_info task_rkv[TASK_TYPE_BUTT] = { + { + .name = "invalid", + .reg_en = 0, + .reg_irq = 0, + .reg_len = 0, + .reg_dir_mv = 0, + .reg_pps = 0, + .reg_pipe = 0, + .enable_mask = 0, + .gating_mask = 0, + .pipe_mask = 0, + .irq_mask = 0, + .ready_mask = 0, + .error_mask = 0, + .get_fmt = NULL, + }, + { + .name = "rkvdec", + .reg_en = RKV_REG_EN_DEC, + .reg_irq = RKV_DEC_INTERRUPT_REGISTER, + .reg_len = 4, + .reg_dir_mv = 52, + .reg_pps = 42, + .reg_pipe = 0, + .enable_mask = 0, + .gating_mask = RKV_REG_DEC_GATING_BIT, + .irq_mask = HEVC_INTERRUPT_BIT, + .pipe_mask = 0, + .ready_mask = HEVC_READY_BIT, + .error_mask = HEVC_DEC_ERR_MASK, + .get_fmt = rkv_dec_get_fmt, + }, + { + .name = "invalid", + .reg_en = 0, + .reg_irq = 0, + .reg_len = 0, + .reg_dir_mv = 0, + .reg_pps = 0, + .reg_pipe = 0, + .enable_mask = 0, + .gating_mask = 0, + .pipe_mask = 0, + .irq_mask = 0, + .ready_mask = 0, + .error_mask = 0, + .get_fmt = NULL, + }, + { + .name = "invalid", + .reg_en = 0, + .reg_irq = 0, + .reg_len = 0, + .reg_dir_mv = 0, + .reg_pps = 0, + .reg_pipe = 0, + .enable_mask = 0, + .gating_mask = 0, + .pipe_mask = 0, + .irq_mask = 0, + .ready_mask = 0, + .error_mask = 0, + .get_fmt = NULL, + },}; + +static struct vpu_hw_info hw_rkhevc = { + .hw_id = HEVC_ID, + + .enc_offset = 0, + .enc_reg_num = 0, + .enc_io_size = 0, + + .dec_offset = 0, + .dec_reg_num = REG_NUM_HEVC_DEC, + .dec_io_size = REG_NUM_HEVC_DEC * 4, + + /* NOTE: can not write to register 0 */ + .base_dec = 1, + .base_pp = 0, + .base_dec_pp = 0, + .end_dec = REG_NUM_HEVC_DEC, + .end_pp = 0, + .end_dec_pp = 0, +}; + +static struct vpu_hw_info hw_rkvdec = { + .hw_id = RKV_DEC_ID, + + .enc_offset = 0, + .enc_reg_num = 0, + .enc_io_size = 0, + + .dec_offset = 0x0, + .dec_reg_num = REG_NUM_RKV_DEC, + .dec_io_size = REG_NUM_RKV_DEC * 4, + + /* NOTE: can not write to register 0 */ + .base_dec = 1, + .base_pp = 0, + .base_dec_pp = 0, + .end_dec = REG_NUM_RKV_DEC, + .end_pp = 0, + .end_dec_pp = 0, +}; + +/* + * file handle translate information + */ +DEF_FMT_TRANS_TBL(rkv_h264d, + 4, 6, 7, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, + 23, 24, 41, 42, 43, 48, 75 +); + +DEF_FMT_TRANS_TBL(rkv_h265d, + 4, 6, 7, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, + 23, 24, 42, 43 +); + +DEF_FMT_TRANS_TBL(rkv_vp9d, + 4, 6, 7, 11, 12, 13, 14, 15, + 16, 52 +); + +const struct vpu_trans_info trans_rkv[FMT_TYPE_BUTT] = { + EMPTY_FMT_TBL(FMT_JPEGD), + EMPTY_FMT_TBL(FMT_H263D), + SETUP_FMT_TBL(FMT_H264D , rkv_h264d), + SETUP_FMT_TBL(FMT_H265D , rkv_h265d), + + EMPTY_FMT_TBL(FMT_MPEG1D), + EMPTY_FMT_TBL(FMT_MPEG2D), + EMPTY_FMT_TBL(FMT_MPEG4D), + + EMPTY_FMT_TBL(FMT_VP6D), + EMPTY_FMT_TBL(FMT_VP7D), + EMPTY_FMT_TBL(FMT_VP8D), + SETUP_FMT_TBL(FMT_VP9D , rkv_vp9d), + + EMPTY_FMT_TBL(FMT_PP), + + EMPTY_FMT_TBL(FMT_VC1D), + EMPTY_FMT_TBL(FMT_AVSD), + + EMPTY_FMT_TBL(FMT_JPEGE), + EMPTY_FMT_TBL(FMT_H264E), + EMPTY_FMT_TBL(FMT_VP8E), +}; + +#endif diff --git a/drivers/video/rockchip/vcodec/vcodec_hw_vpu.h b/drivers/video/rockchip/vcodec/vcodec_hw_vpu.h new file mode 100644 index 000000000000..3b8201abd21c --- /dev/null +++ b/drivers/video/rockchip/vcodec/vcodec_hw_vpu.h @@ -0,0 +1,318 @@ +/** + * Copyright (C) 2015 Fuzhou Rockchip Electronics Co., Ltd + * author: chenhengming chm@rock-chips.com + * Alpha Lin, alpha.lin@rock-chips.com + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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 __ARCH_ARM_MACH_ROCKCHIP_VCODEC_HW_VPU_H +#define __ARCH_ARM_MACH_ROCKCHIP_VCODEC_HW_VPU_H + +#include "vcodec_hw_info.h" + +/* hardware information */ +#define REG_NUM_9190_DEC (60) +#define REG_NUM_9190_PP (41) +#define REG_NUM_9190_DEC_PP (REG_NUM_9190_DEC + REG_NUM_9190_PP) + +#define REG_NUM_DEC_PP (REG_NUM_9190_DEC + REG_NUM_9190_PP) + +#define REG_NUM_ENC_8270 (96) +#define REG_SIZE_ENC_8270 (0x200) +#define REG_NUM_ENC_4831 (164) +#define REG_SIZE_ENC_4831 (0x400) + + +/* enable and gating register */ +#define VPU_REG_EN_ENC 14 +#define VPU_REG_ENC_GATE 2 +#define VPU_REG_ENC_GATE_BIT BIT(4) + +#define VPU_REG_EN_DEC 1 +#define VPU_REG_DEC_GATE 2 +#define VPU_REG_DEC_GATE_BIT BIT(10) +#define VPU_REG_EN_PP 0 +#define VPU_REG_PP_GATE 1 +#define VPU_REG_PP_GATE_BIT BIT(8) +#define VPU_REG_EN_DEC_PP 1 +#define VPU_REG_DEC_PP_GATE 61 +#define VPU_REG_DEC_PP_GATE_BIT BIT(8) + +/* interrupt and error status register */ +#define VPU_DEC_INTERRUPT_REGISTER 1 +#define VPU_DEC_INTERRUPT_BIT BIT(8) +#define VPU_DEC_READY_BIT BIT(12) +#define VPU_DEC_BUS_ERROR_BIT BIT(13) +#define VPU_DEC_BUFFER_EMPTY_BIT BIT(14) +#define VPU_DEC_ASO_ERROR_BIT BIT(15) +#define VPU_DEC_STREAM_ERROR_BIT BIT(16) +#define VPU_DEC_SLICE_DONE_BIT BIT(17) +#define VPU_DEC_TIMEOUT_BIT BIT(18) +#define VPU_DEC_ERR_MASK (VPU_DEC_BUS_ERROR_BIT \ + |VPU_DEC_BUFFER_EMPTY_BIT \ + |VPU_DEC_STREAM_ERROR_BIT \ + |VPU_DEC_TIMEOUT_BIT) + +#define VPU_PP_INTERRUPT_REGISTER 60 +#define VPU_PP_PIPELINE_MODE_BIT BIT(1) +#define VPU_PP_INTERRUPT_BIT BIT(8) +#define VPU_PP_READY_BIT BIT(12) +#define VPU_PP_BUS_ERROR_BIT BIT(13) +#define VPU_PP_ERR_MASK VPU_PP_BUS_ERROR_BIT + +#define VPU_ENC_INTERRUPT_REGISTER 1 +#define VPU_ENC_INTERRUPT_BIT BIT(0) +#define VPU_ENC_READY_BIT BIT(2) +#define VPU_ENC_BUS_ERROR_BIT BIT(3) +#define VPU_ENC_BUFFER_FULL_BIT BIT(5) +#define VPU_ENC_TIMEOUT_BIT BIT(6) +#define VPU_ENC_ERR_MASK (VPU_ENC_BUS_ERROR_BIT \ + |VPU_ENC_BUFFER_FULL_BIT \ + |VPU_ENC_TIMEOUT_BIT) + +static const enum FORMAT_TYPE vpu_dec_fmt_tbl[] = { + [0] = FMT_H264D, + [1] = FMT_MPEG4D, + [2] = FMT_H263D, + [3] = FMT_JPEGD, + [4] = FMT_VC1D, + [5] = FMT_MPEG2D, + [6] = FMT_MPEG1D, + [7] = FMT_VP6D, + [8] = FMT_TYPE_BUTT, + [9] = FMT_VP7D, + [10] = FMT_VP8D, + [11] = FMT_AVSD, + [12] = FMT_TYPE_BUTT, + [13] = FMT_TYPE_BUTT, + [14] = FMT_TYPE_BUTT, + [15] = FMT_TYPE_BUTT, +}; + +static enum FORMAT_TYPE vpu_dec_get_fmt(u32 *regs) +{ + u32 fmt_id = (regs[3] >> 28) & 0xf; + enum FORMAT_TYPE type = vpu_dec_fmt_tbl[fmt_id]; + return type; +} + +static enum FORMAT_TYPE vpu_pp_get_fmt(u32 *regs) +{ + return FMT_PP; +} + +static const enum FORMAT_TYPE vpu_enc_fmt_tbl[] = { + [0] = FMT_TYPE_BUTT, + [1] = FMT_VP8E, + [2] = FMT_JPEGE, + [3] = FMT_H264E, +}; + +static enum FORMAT_TYPE vpu_enc_get_fmt(u32 *regs) +{ + u32 fmt_id = (regs[VPU_REG_EN_ENC] >> 1) & 0x3; + enum FORMAT_TYPE type = vpu_enc_fmt_tbl[fmt_id]; + return type; +} + +static struct vpu_task_info task_vpu[TASK_TYPE_BUTT] = { + { + .name = "vpu_enc", + .reg_en = VPU_REG_EN_ENC, + .reg_irq = VPU_ENC_INTERRUPT_REGISTER, + .reg_len = -1, + .reg_dir_mv = -1, + .reg_pps = -1, + .reg_pipe = -1, + .enable_mask = 0x6, + .gating_mask = 0, + .pipe_mask = 0, + .irq_mask = VPU_ENC_INTERRUPT_BIT, + .ready_mask = VPU_ENC_READY_BIT, + .error_mask = VPU_ENC_ERR_MASK, + .get_fmt = vpu_enc_get_fmt, + }, + { + .name = "vpu_dec", + .reg_en = VPU_REG_EN_DEC, + .reg_irq = VPU_DEC_INTERRUPT_REGISTER, + .reg_len = 12, + .reg_dir_mv = 41, + .reg_pps = -1, + .reg_pipe = VPU_PP_INTERRUPT_REGISTER, + .enable_mask = 0, + .gating_mask = 0, + .pipe_mask = VPU_PP_PIPELINE_MODE_BIT, + .irq_mask = VPU_DEC_INTERRUPT_BIT, + .ready_mask = VPU_DEC_READY_BIT, + .error_mask = VPU_DEC_ERR_MASK, + .get_fmt = vpu_dec_get_fmt, + }, + { + .name = "vpu_pp", + .reg_en = VPU_REG_EN_PP, + .reg_irq = VPU_PP_INTERRUPT_REGISTER, + .reg_len = -1, + .reg_dir_mv = -1, + .reg_pps = -1, + .reg_pipe = VPU_PP_INTERRUPT_REGISTER, + .enable_mask = 0, + .gating_mask = 0, + .pipe_mask = VPU_PP_PIPELINE_MODE_BIT, + .irq_mask = VPU_PP_INTERRUPT_BIT, + .ready_mask = VPU_PP_READY_BIT, + .error_mask = VPU_PP_ERR_MASK, + .get_fmt = vpu_pp_get_fmt, + }, + { + .name = "vpu_dec_pp", + .reg_en = VPU_REG_EN_DEC, + .reg_irq = VPU_DEC_INTERRUPT_REGISTER, + .reg_len = 12, + .reg_dir_mv = 41, + .reg_pps = -1, + .reg_pipe = VPU_PP_INTERRUPT_REGISTER, + .enable_mask = 0, + .gating_mask = 0, + .pipe_mask = VPU_PP_PIPELINE_MODE_BIT, + .irq_mask = VPU_DEC_INTERRUPT_BIT, + .ready_mask = VPU_DEC_READY_BIT, + .error_mask = VPU_DEC_ERR_MASK, + .get_fmt = vpu_dec_get_fmt, + }, +}; + +static struct vpu_hw_info hw_vpu_8270 = { + .hw_id = VPU_ID_8270, + + .enc_offset = 0x0, + .enc_reg_num = REG_NUM_ENC_8270, + .enc_io_size = REG_NUM_ENC_8270 * 4, + + .dec_offset = REG_SIZE_ENC_8270, + .dec_reg_num = REG_NUM_9190_DEC_PP, + .dec_io_size = REG_NUM_9190_DEC_PP * 4, + + .base_dec = 0, + .base_pp = VPU_PP_INTERRUPT_REGISTER, + .base_dec_pp = 0, + .end_dec = REG_NUM_9190_DEC, + .end_pp = REG_NUM_9190_DEC_PP, + .end_dec_pp = REG_NUM_9190_DEC_PP, +}; + +static struct vpu_hw_info hw_vpu_4831 = { + .hw_id = VPU_ID_4831, + + .enc_offset = 0x0, + .enc_reg_num = REG_NUM_ENC_4831, + .enc_io_size = REG_NUM_ENC_4831 * 4, + + .dec_offset = REG_SIZE_ENC_4831, + .dec_reg_num = REG_NUM_9190_DEC_PP, + .dec_io_size = REG_NUM_9190_DEC_PP * 4, + + .base_dec = 0, + .base_pp = VPU_PP_INTERRUPT_REGISTER, + .base_dec_pp = 0, + .end_dec = REG_NUM_9190_DEC, + .end_pp = REG_NUM_9190_DEC_PP, + .end_dec_pp = REG_NUM_9190_DEC_PP, +}; + +static struct vpu_hw_info hw_vpu_9190 = { + .hw_id = VPU_DEC_ID_9190, + + .enc_offset = 0x0, + .enc_reg_num = 0, + .enc_io_size = 0, + + .dec_offset = 0, + .dec_reg_num = REG_NUM_9190_DEC_PP, + .dec_io_size = REG_NUM_9190_DEC_PP * 4, + + .base_dec = 0, + .base_pp = VPU_PP_INTERRUPT_REGISTER, + .base_dec_pp = 0, + .end_dec = REG_NUM_9190_DEC, + .end_pp = REG_NUM_9190_DEC_PP, + .end_dec_pp = REG_NUM_9190_DEC_PP, +}; + +/* + * file handle translate information + */ +DEF_FMT_TRANS_TBL(vpu_jpegd, + 12, 40, 66, 67 +); + +DEF_FMT_TRANS_TBL(vpu_h264d, + 12, 13, 14, 15, 16, 17, 18, 19, + 20, 21, 22, 23, 24, 25, 26, 27, + 28, 29, 40, 41 +); + +DEF_FMT_TRANS_TBL(vpu_vp6d, + 12, 13, 14, 18, 27, 40 +); + +DEF_FMT_TRANS_TBL(vpu_vp8d, + 10, 12, 13, 14, 18, 19, 22, 23, + 24, 25, 26, 27, 28, 29, 40 +); + +DEF_FMT_TRANS_TBL(vpu_vc1d, + 12, 13, 14, 15, 16, 17, 27, 41 +); + +DEF_FMT_TRANS_TBL(vpu_defaultd, + 12, 13, 14, 15, 16, 17, 40, 41 +); + +DEF_FMT_TRANS_TBL(vpu_default_pp, + 63, 64, 65, 66, 67, 73, 74 +); + +DEF_FMT_TRANS_TBL(vpu_vp8e, + 5, 6, 7, 8, 9, 10, 11, 12, 13, 16, 17, 26, 51, 52, 58, 59 +); + +DEF_FMT_TRANS_TBL(vpu_defaulte, + 5, 6, 7, 8, 9, 10, 11, 12, 13, 51 +); + +const struct vpu_trans_info trans_vpu[FMT_TYPE_BUTT] = { + SETUP_FMT_TBL(FMT_JPEGD , vpu_jpegd), + SETUP_FMT_TBL(FMT_H263D , vpu_defaultd), + SETUP_FMT_TBL(FMT_H264D , vpu_h264d), + EMPTY_FMT_TBL(FMT_H265D), + + SETUP_FMT_TBL(FMT_MPEG1D, vpu_defaultd), + SETUP_FMT_TBL(FMT_MPEG2D, vpu_defaultd), + SETUP_FMT_TBL(FMT_MPEG4D, vpu_defaultd), + + SETUP_FMT_TBL(FMT_VP6D , vpu_vp6d), + SETUP_FMT_TBL(FMT_VP7D , vpu_defaultd), + SETUP_FMT_TBL(FMT_VP8D , vpu_vp8d), + EMPTY_FMT_TBL(FMT_VP9D), + + SETUP_FMT_TBL(FMT_VC1D , vpu_vc1d), + SETUP_FMT_TBL(FMT_AVSD , vpu_defaultd), + + SETUP_FMT_TBL(FMT_PP , vpu_default_pp), + + SETUP_FMT_TBL(FMT_JPEGE , vpu_defaulte), + SETUP_FMT_TBL(FMT_H264E , vpu_defaulte), + SETUP_FMT_TBL(FMT_VP8E , vpu_vp8e), +}; + +#endif diff --git a/drivers/video/rockchip/vcodec/vcodec_hw_vpu2.h b/drivers/video/rockchip/vcodec/vcodec_hw_vpu2.h new file mode 100644 index 000000000000..5da74b55431f --- /dev/null +++ b/drivers/video/rockchip/vcodec/vcodec_hw_vpu2.h @@ -0,0 +1,275 @@ +/** + * Copyright (C) 2015 Fuzhou Rockchip Electronics Co., Ltd + * author: chenhengming chm@rock-chips.com + * Alpha Lin, alpha.lin@rock-chips.com + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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 __ARCH_ARM_MACH_ROCKCHIP_VCODEC_HW_VPU2_H +#define __ARCH_ARM_MACH_ROCKCHIP_VCODEC_HW_VPU2_H + +#include "vcodec_hw_info.h" + +/* hardware information */ +#define REG_NUM_VPU2_DEC (159) +#define REG_NUM_VPU2_DEC_START (50) +#define REG_NUM_VPU2_DEC_END (159) +#define REG_NUM_VPU2_PP (41) +#define REG_NUM_VPU2_DEC_PP (159) +#define REG_NUM_VPU2_ENC (184) +#define REG_NUM_VPU2_DEC_OFFSET (0x400) + +/* enable and gating register */ +#define VPU2_REG_EN_ENC 103 +#define VPU2_REG_ENC_GATE 109 +#define VPU2_REG_ENC_GATE_BIT BIT(4) + +#define VPU2_REG_EN_DEC 57 +#define VPU2_REG_DEC_GATE 57 +#define VPU2_REG_DEC_GATE_BIT BIT(4) +#define VPU2_REG_EN_PP 41 +#define VPU2_REG_PP_GATE 1 +#define VPU2_REG_PP_GATE_BIT BIT(8) +#define VPU2_REG_EN_DEC_PP 57 +#define VPU2_REG_DEC_PP_GATE 57 +#define VPU2_REG_DEC_PP_GATE_BIT BIT(4) + +/* interrupt and error status register */ +#define VPU2_DEC_INTERRUPT_REGISTER 55 +#define VPU2_DEC_INTERRUPT_BIT BIT(0) +#define VPU2_DEC_READY_BIT BIT(4) +#define VPU2_DEC_BUS_ERROR_BIT BIT(5) +#define VPU2_DEC_BUFFER_EMPTY_BIT BIT(6) +#define VPU2_DEC_ASO_ERROR_BIT BIT(8) +#define VPU2_DEC_SLICE_DONE_BIT BIT(9) +#define VPU2_DEC_STREAM_ERROR_BIT BIT(12) +#define VPU2_DEC_TIMEOUT_BIT BIT(13) +#define VPU2_DEC_ERR_MASK (VPU2_DEC_BUS_ERROR_BIT \ + |VPU2_DEC_BUFFER_EMPTY_BIT \ + |VPU2_DEC_STREAM_ERROR_BIT \ + |VPU2_DEC_TIMEOUT_BIT) + +#define VPU2_PP_INTERRUPT_REGISTER 40 +#define VPU2_PP_INTERRUPT_BIT BIT(0) +#define VPU2_PP_READY_BIT BIT(2) +#define VPU2_PP_BUS_ERROR_BIT BIT(3) +#define VPU2_PP_ERR_MASK VPU2_PP_BUS_ERROR_BIT +#define VPU2_PP_PIPELINE_REGISTER 41 +#define VPU2_PP_PIPELINE_MODE_BIT BIT(4) + +#define VPU2_ENC_INTERRUPT_REGISTER 109 +#define VPU2_ENC_INTERRUPT_BIT BIT(0) +#define VPU2_ENC_READY_BIT BIT(1) +#define VPU2_ENC_BUS_ERROR_BIT BIT(4) +#define VPU2_ENC_BUFFER_FULL_BIT BIT(5) +#define VPU2_ENC_TIMEOUT_BIT BIT(6) +#define VPU2_ENC_ERR_MASK (VPU2_ENC_BUS_ERROR_BIT \ + |VPU2_ENC_BUFFER_FULL_BIT \ + |VPU2_ENC_TIMEOUT_BIT) + +static const enum FORMAT_TYPE vpu2_dec_fmt_tbl[] = { + [0] = FMT_H264D, + [1] = FMT_MPEG4D, + [2] = FMT_H263D, + [3] = FMT_JPEGD, + [4] = FMT_VC1D, + [5] = FMT_MPEG2D, + [6] = FMT_MPEG1D, + [7] = FMT_VP6D, + [8] = FMT_TYPE_BUTT, + [9] = FMT_VP7D, + [10] = FMT_VP8D, + [11] = FMT_AVSD, + [12] = FMT_TYPE_BUTT, + [13] = FMT_TYPE_BUTT, + [14] = FMT_TYPE_BUTT, + [15] = FMT_TYPE_BUTT, +}; + +static enum FORMAT_TYPE vpu2_dec_get_fmt(u32 *regs) +{ + u32 fmt_id = regs[53] & 0xf; + enum FORMAT_TYPE type = vpu2_dec_fmt_tbl[fmt_id]; + return type; +} + +static enum FORMAT_TYPE vpu2_pp_get_fmt(u32 *regs) +{ + return FMT_PP; +} + +static const enum FORMAT_TYPE vpu2_enc_fmt_tbl[] = { + [0] = FMT_TYPE_BUTT, + [1] = FMT_VP8E, + [2] = FMT_JPEGE, + [3] = FMT_H264E, +}; + +static enum FORMAT_TYPE vpu2_enc_get_fmt(u32 *regs) +{ + u32 fmt_id = (regs[VPU2_REG_EN_ENC] >> 4) & 0x3; + enum FORMAT_TYPE type = vpu2_enc_fmt_tbl[fmt_id]; + return type; +} + +static struct vpu_task_info task_vpu2[TASK_TYPE_BUTT] = { + { + .name = "vpu2_enc", + .reg_en = VPU2_REG_EN_ENC, + .reg_gating = VPU2_REG_ENC_GATE, + .reg_irq = VPU2_ENC_INTERRUPT_REGISTER, + .reg_len = -1, + .reg_dir_mv = -1, + .reg_pps = -1, + .reg_pipe = -1, + .enable_mask = 0x30, + .gating_mask = VPU2_REG_ENC_GATE_BIT, + .pipe_mask = 0, + .irq_mask = VPU2_ENC_INTERRUPT_BIT, + .ready_mask = VPU2_ENC_READY_BIT, + .error_mask = VPU2_ENC_ERR_MASK, + .get_fmt = vpu2_enc_get_fmt, + }, + { + .name = "vpu2_dec", + .reg_en = VPU2_REG_EN_DEC, + .reg_irq = VPU2_DEC_INTERRUPT_REGISTER, + .reg_len = 64, + .reg_dir_mv = 62, + .reg_pps = -1, + .reg_pipe = VPU2_PP_PIPELINE_REGISTER, + .enable_mask = 0, + .gating_mask = VPU2_REG_DEC_GATE_BIT, + .pipe_mask = VPU2_PP_PIPELINE_MODE_BIT, + .irq_mask = VPU2_DEC_INTERRUPT_BIT, + .ready_mask = VPU2_DEC_READY_BIT, + .error_mask = VPU2_DEC_ERR_MASK, + .get_fmt = vpu2_dec_get_fmt, + }, + { + .name = "vpu2_pp", + .reg_en = VPU2_REG_EN_PP, + .reg_irq = VPU2_PP_INTERRUPT_REGISTER, + .reg_len = -1, + .reg_dir_mv = -1, + .reg_pps = -1, + .reg_pipe = VPU2_PP_PIPELINE_REGISTER, + .enable_mask = 0, + .gating_mask = VPU2_REG_PP_GATE_BIT, + .pipe_mask = VPU2_PP_PIPELINE_MODE_BIT, + .irq_mask = VPU2_PP_INTERRUPT_BIT, + .ready_mask = VPU2_PP_READY_BIT, + .error_mask = VPU2_PP_ERR_MASK, + .get_fmt = vpu2_pp_get_fmt, + }, + { + .name = "vpu2_dec_pp", + .reg_en = VPU2_REG_EN_DEC_PP, + .reg_irq = VPU2_DEC_INTERRUPT_REGISTER, + .reg_len = 64, + .reg_dir_mv = 62, + .reg_pps = -1, + .reg_pipe = VPU2_PP_PIPELINE_REGISTER, + .enable_mask = 0, + .gating_mask = VPU2_REG_DEC_GATE_BIT, + .pipe_mask = VPU2_PP_PIPELINE_MODE_BIT, + .irq_mask = VPU2_DEC_INTERRUPT_BIT, + .ready_mask = VPU2_DEC_READY_BIT, + .error_mask = VPU2_DEC_ERR_MASK, + .get_fmt = vpu2_dec_get_fmt, + }, +}; + +static struct vpu_hw_info hw_vpu2 = { + .hw_id = VPU2_ID, + + .enc_offset = 0x0, + .enc_reg_num = REG_NUM_VPU2_ENC, + .enc_io_size = REG_NUM_VPU2_ENC * 4, + + .dec_offset = REG_NUM_VPU2_DEC_OFFSET, + .dec_reg_num = REG_NUM_VPU2_DEC_PP, + .dec_io_size = REG_NUM_VPU2_DEC_PP * 4, + + .base_dec = REG_NUM_VPU2_DEC_START, + .base_pp = 0, + .base_dec_pp = 0, + .end_dec = REG_NUM_VPU2_DEC_END, + .end_pp = REG_NUM_VPU2_PP, + .end_dec_pp = REG_NUM_VPU2_DEC_END, +}; + +/* + * file handle translate information + */ +DEF_FMT_TRANS_TBL(vpu2_jpegd, + 131, 64, 63, 61, 21, 22 +); + +DEF_FMT_TRANS_TBL(vpu2_h264d, + 64, 63, 84, 85, 86, 87, 88, 89, + 90, 91, 92, 93, 94, 95, 96, 97, + 98, 99, 61, 62 +); + +DEF_FMT_TRANS_TBL(vpu2_vp6d, + 64, 63, 131, 136, 145, 61 +); + +DEF_FMT_TRANS_TBL(vpu2_vp8d, + 149, 64, 63, 131, 136, 137, 140, 141, + 142, 143, 144, 145, 146, 147, 61 +); + +DEF_FMT_TRANS_TBL(vpu2_vc1d, + 64, 63, 131, 148, 134, 135, 145, 62 +); + +DEF_FMT_TRANS_TBL(vpu2_default_dec, + 64, 63, 131, 148, 134, 135, 61, 62 +); + +DEF_FMT_TRANS_TBL(vpu2_default_pp, + 12, 13, 18, 19, 20, 21, 22 +); + +DEF_FMT_TRANS_TBL(vpu2_default_enc, + 77, 78, 56, 57, 63, 64, 48, 49, + 50, 81 +); + +const struct vpu_trans_info trans_vpu2[FMT_TYPE_BUTT] = { + SETUP_FMT_TBL(FMT_JPEGD , vpu2_jpegd), + SETUP_FMT_TBL(FMT_H263D , vpu2_default_dec), + SETUP_FMT_TBL(FMT_H264D , vpu2_h264d), + EMPTY_FMT_TBL(FMT_H265D), + + SETUP_FMT_TBL(FMT_MPEG1D, vpu2_default_dec), + SETUP_FMT_TBL(FMT_MPEG2D, vpu2_default_dec), + SETUP_FMT_TBL(FMT_MPEG4D, vpu2_default_dec), + + SETUP_FMT_TBL(FMT_VP6D , vpu2_vp6d), + SETUP_FMT_TBL(FMT_VP7D , vpu2_default_dec), + SETUP_FMT_TBL(FMT_VP8D , vpu2_vp8d), + EMPTY_FMT_TBL(FMT_VP9D), + + SETUP_FMT_TBL(FMT_PP , vpu2_default_pp), + + SETUP_FMT_TBL(FMT_VC1D , vpu2_vc1d), + SETUP_FMT_TBL(FMT_AVSD , vpu2_default_dec), + + SETUP_FMT_TBL(FMT_JPEGE , vpu2_default_enc), + SETUP_FMT_TBL(FMT_H264E , vpu2_default_enc), + SETUP_FMT_TBL(FMT_VP8E , vpu2_default_enc), +}; + +#endif diff --git a/drivers/video/rockchip/vcodec/vcodec_service.c b/drivers/video/rockchip/vcodec/vcodec_service.c new file mode 100644 index 000000000000..d97b0f5545ef --- /dev/null +++ b/drivers/video/rockchip/vcodec/vcodec_service.c @@ -0,0 +1,2938 @@ +/** + * Copyright (C) 2015 Fuzhou Rockchip Electronics Co., Ltd + * author: chenhengming chm@rock-chips.com + * Alpha Lin, alpha.lin@rock-chips.com + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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 pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#if defined(CONFIG_ION_ROCKCHIP) +#include +#endif + +#if defined(CONFIG_RK_IOMMU) & defined(CONFIG_ION_ROCKCHIP) +#define CONFIG_VCODEC_MMU +#include +#include +#endif + +#include "vcodec_hw_info.h" +#include "vcodec_hw_vpu.h" +#include "vcodec_hw_rkv.h" +#include "vcodec_hw_vpu2.h" + +#include "vcodec_service.h" + +/* + * debug flag usage: + * +------+-------------------+ + * | 8bit | 24bit | + * +------+-------------------+ + * 0~23 bit is for different information type + * 24~31 bit is for information print format + */ + +#define DEBUG_POWER 0x00000001 +#define DEBUG_CLOCK 0x00000002 +#define DEBUG_IRQ_STATUS 0x00000004 +#define DEBUG_IOMMU 0x00000008 +#define DEBUG_IOCTL 0x00000010 +#define DEBUG_FUNCTION 0x00000020 +#define DEBUG_REGISTER 0x00000040 +#define DEBUG_EXTRA_INFO 0x00000080 +#define DEBUG_TIMING 0x00000100 +#define DEBUG_TASK_INFO 0x00000200 + +#define DEBUG_SET_REG 0x00001000 +#define DEBUG_GET_REG 0x00002000 +#define DEBUG_PPS_FILL 0x00004000 +#define DEBUG_IRQ_CHECK 0x00008000 +#define DEBUG_CACHE_32B 0x00010000 + +#define PRINT_FUNCTION 0x80000000 +#define PRINT_LINE 0x40000000 + +static int debug; +module_param(debug, int, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(debug, "bit switch for vcodec_service debug information"); + +#define VCODEC_CLOCK_ENABLE 1 + +/* + * hardware information organization + * + * In order to support multiple hardware with different version the hardware + * information is organized as follow: + * + * 1. First, index hardware by register size / position. + * These information is fix for each hardware and do not relate to runtime + * work flow. It only related to resource allocation. + * Descriptor: struct vpu_hw_info + * + * 2. Then, index hardware by runtime configuration + * These information is related to runtime setting behave including enable + * register, irq register and other key control flag + * Descriptor: struct vpu_task_info + * + * 3. Final, on iommu case the fd translation is required + * Descriptor: struct vpu_trans_info + */ + +enum VPU_FREQ { + VPU_FREQ_200M, + VPU_FREQ_266M, + VPU_FREQ_300M, + VPU_FREQ_400M, + VPU_FREQ_500M, + VPU_FREQ_600M, + VPU_FREQ_DEFAULT, + VPU_FREQ_BUT, +}; + +struct extra_info_elem { + u32 index; + u32 offset; +}; + +#define EXTRA_INFO_MAGIC 0x4C4A46 + +struct extra_info_for_iommu { + u32 magic; + u32 cnt; + struct extra_info_elem elem[20]; +}; + +#define MHZ (1000*1000) +#define SIZE_REG(reg) ((reg)*4) + +static struct vcodec_info vcodec_info_set[] = { + [0] = { + .hw_id = VPU_ID_8270, + .hw_info = &hw_vpu_8270, + .task_info = task_vpu, + .trans_info = trans_vpu, + }, + [1] = { + .hw_id = VPU_ID_4831, + .hw_info = &hw_vpu_4831, + .task_info = task_vpu, + .trans_info = trans_vpu, + }, + [2] = { + .hw_id = VPU_DEC_ID_9190, + .hw_info = &hw_vpu_9190, + .task_info = task_vpu, + .trans_info = trans_vpu, + }, + [3] = { + .hw_id = HEVC_ID, + .hw_info = &hw_rkhevc, + .task_info = task_rkv, + .trans_info = trans_rkv, + }, + [4] = { + .hw_id = RKV_DEC_ID, + .hw_info = &hw_rkvdec, + .task_info = task_rkv, + .trans_info = trans_rkv, + }, + [5] = { + .hw_id = VPU2_ID, + .hw_info = &hw_vpu2, + .task_info = task_vpu2, + .trans_info = trans_vpu2, + }, +}; + +#define DEBUG +#ifdef DEBUG +#define vpu_debug_func(type, fmt, args...) \ + do { \ + if (unlikely(debug & type)) { \ + pr_info("%s:%d: " fmt, \ + __func__, __LINE__, ##args); \ + } \ + } while (0) +#define vpu_debug(type, fmt, args...) \ + do { \ + if (unlikely(debug & type)) { \ + pr_info(fmt, ##args); \ + } \ + } while (0) +#else +#define vpu_debug_func(level, fmt, args...) +#define vpu_debug(level, fmt, args...) +#endif + +#define vpu_debug_enter() vpu_debug_func(DEBUG_FUNCTION, "enter\n") +#define vpu_debug_leave() vpu_debug_func(DEBUG_FUNCTION, "leave\n") + +#define vpu_err(fmt, args...) \ + pr_err("%s:%d: " fmt, __func__, __LINE__, ##args) + +enum VPU_DEC_FMT { + VPU_DEC_FMT_H264, + VPU_DEC_FMT_MPEG4, + VPU_DEC_FMT_H263, + VPU_DEC_FMT_JPEG, + VPU_DEC_FMT_VC1, + VPU_DEC_FMT_MPEG2, + VPU_DEC_FMT_MPEG1, + VPU_DEC_FMT_VP6, + VPU_DEC_FMT_RESERV0, + VPU_DEC_FMT_VP7, + VPU_DEC_FMT_VP8, + VPU_DEC_FMT_AVS, + VPU_DEC_FMT_RES +}; + +/** + * struct for process session which connect to vpu + * + * @author ChenHengming (2011-5-3) + */ +struct vpu_session { + enum VPU_CLIENT_TYPE type; + /* a linked list of data so we can access them for debugging */ + struct list_head list_session; + /* a linked list of register data waiting for process */ + struct list_head waiting; + /* a linked list of register data in processing */ + struct list_head running; + /* a linked list of register data processed */ + struct list_head done; + wait_queue_head_t wait; + pid_t pid; + atomic_t task_running; +}; + +/** + * struct for process register set + * + * @author ChenHengming (2011-5-4) + */ +struct vpu_reg { + enum VPU_CLIENT_TYPE type; + enum VPU_FREQ freq; + struct vpu_session *session; + struct vpu_subdev_data *data; + struct vpu_task_info *task; + const struct vpu_trans_info *trans; + + /* link to vpu service session */ + struct list_head session_link; + /* link to register set list */ + struct list_head status_link; + + unsigned long size; + struct list_head mem_region_list; + u32 dec_base; + u32 *reg; +}; + +struct vpu_device { + atomic_t irq_count_codec; + atomic_t irq_count_pp; + unsigned int iosize; + u32 *regs; +}; + +enum vcodec_device_id { + VCODEC_DEVICE_ID_VPU, + VCODEC_DEVICE_ID_HEVC, + VCODEC_DEVICE_ID_COMBO, + VCODEC_DEVICE_ID_RKVDEC, + VCODEC_DEVICE_ID_BUTT +}; + +enum VCODEC_RUNNING_MODE { + VCODEC_RUNNING_MODE_NONE = -1, + VCODEC_RUNNING_MODE_VPU, + VCODEC_RUNNING_MODE_HEVC, + VCODEC_RUNNING_MODE_RKVDEC +}; + +struct vcodec_mem_region { + struct list_head srv_lnk; + struct list_head reg_lnk; + struct list_head session_lnk; + unsigned long iova; /* virtual address for iommu */ + unsigned long len; + u32 reg_idx; + struct ion_handle *hdl; +}; + +enum vpu_ctx_state { + MMU_ACTIVATED = BIT(0) +}; + +struct vpu_subdev_data { + struct cdev cdev; + dev_t dev_t; + struct class *cls; + struct device *child_dev; + + int irq_enc; + int irq_dec; + struct vpu_service_info *pservice; + + u32 *regs; + enum VCODEC_RUNNING_MODE mode; + struct list_head lnk_service; + + struct device *dev; + + struct vpu_device enc_dev; + struct vpu_device dec_dev; + + enum VPU_HW_ID hw_id; + struct vpu_hw_info *hw_info; + struct vpu_task_info *task_info; + const struct vpu_trans_info *trans_info; + + u32 reg_size; + unsigned long state; + +#ifdef CONFIG_DEBUG_FS + struct dentry *debugfs_dir; + struct dentry *debugfs_file_regs; +#endif + + struct device *mmu_dev; +}; + +struct vpu_service_info { + struct wake_lock wake_lock; + struct delayed_work power_off_work; + /* vpu service structure global lock */ + struct mutex lock; + /* link to link_reg in struct vpu_reg */ + struct list_head waiting; + /* link to link_reg in struct vpu_reg */ + struct list_head running; + /* link to link_reg in struct vpu_reg */ + struct list_head done; + /* link to list_session in struct vpu_session */ + struct list_head session; + atomic_t total_running; + atomic_t enabled; + atomic_t power_on_cnt; + atomic_t power_off_cnt; + struct vpu_reg *reg_codec; + struct vpu_reg *reg_pproc; + struct vpu_reg *reg_resev; + struct vpu_dec_config dec_config; + struct vpu_enc_config enc_config; + + bool auto_freq; + bool bug_dec_addr; + atomic_t freq_status; + + struct clk *aclk_vcodec; + struct clk *hclk_vcodec; + struct clk *clk_core; + struct clk *clk_cabac; + struct clk *pd_video; + +#ifdef CONFIG_RESET_CONTROLLER + struct reset_control *rst_a; + struct reset_control *rst_h; + struct reset_control *rst_v; +#endif + struct device *dev; + + u32 irq_status; + atomic_t reset_request; + struct ion_client *ion_client; + struct list_head mem_region_list; + + enum vcodec_device_id dev_id; + + enum VCODEC_RUNNING_MODE curr_mode; + u32 prev_mode; + + struct delayed_work simulate_work; + + u32 mode_bit; + u32 mode_ctrl; + u32 *reg_base; + u32 ioaddr; + struct regmap *grf; + u32 *grf_base; + + char *name; + + u32 subcnt; + struct list_head subdev_list; +}; + +struct vpu_request { + u32 *req; + u32 size; +}; + +#ifdef CONFIG_COMPAT +struct compat_vpu_request { + compat_uptr_t req; + u32 size; +}; +#endif + +/* debugfs root directory for all device (vpu, hevc).*/ +static struct dentry *parent; + +#ifdef CONFIG_DEBUG_FS +static int vcodec_debugfs_init(void); +static void vcodec_debugfs_exit(void); +static struct dentry *vcodec_debugfs_create_device_dir( + char *dirname, struct dentry *parent); +static int debug_vcodec_open(struct inode *inode, struct file *file); + +static const struct file_operations debug_vcodec_fops = { + .open = debug_vcodec_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; +#endif + +#define VDPU_SOFT_RESET_REG 101 +#define VDPU_CLEAN_CACHE_REG 516 +#define VEPU_CLEAN_CACHE_REG 772 +#define HEVC_CLEAN_CACHE_REG 260 + +#define VPU_REG_ENABLE(base, reg) writel_relaxed(1, base + reg) + +#define VDPU_SOFT_RESET(base) VPU_REG_ENABLE(base, VDPU_SOFT_RESET_REG) +#define VDPU_CLEAN_CACHE(base) VPU_REG_ENABLE(base, VDPU_CLEAN_CACHE_REG) +#define VEPU_CLEAN_CACHE(base) VPU_REG_ENABLE(base, VEPU_CLEAN_CACHE_REG) +#define HEVC_CLEAN_CACHE(base) VPU_REG_ENABLE(base, HEVC_CLEAN_CACHE_REG) + +#define VPU_POWER_OFF_DELAY (4 * HZ) /* 4s */ +#define VPU_TIMEOUT_DELAY (2 * HZ) /* 2s */ + +static void time_record(struct vpu_task_info *task, int is_end) +{ + if (unlikely(debug & DEBUG_TIMING) && task) + do_gettimeofday((is_end) ? (&task->end) : (&task->start)); +} + +static void time_diff(struct vpu_task_info *task) +{ + vpu_debug(DEBUG_TIMING, "%s task: %ld ms\n", task->name, + (task->end.tv_sec - task->start.tv_sec) * 1000 + + (task->end.tv_usec - task->start.tv_usec) / 1000); +} + +static void vcodec_enter_mode(struct vpu_subdev_data *data) +{ + int bits; + u32 raw = 0; + struct vpu_service_info *pservice = data->pservice; +#if defined(CONFIG_VCODEC_MMU) + struct vpu_subdev_data *subdata, *n; +#endif + if (pservice->subcnt < 2) { +#if defined(CONFIG_VCODEC_MMU) + if (data->mmu_dev && !test_bit(MMU_ACTIVATED, &data->state)) { + set_bit(MMU_ACTIVATED, &data->state); + if (atomic_read(&pservice->enabled)) + rockchip_iovmm_activate(data->dev); + else + BUG_ON(!atomic_read(&pservice->enabled)); + } +#endif + return; + } + + if (pservice->curr_mode == data->mode) + return; + + vpu_debug(DEBUG_IOMMU, "vcodec enter mode %d\n", data->mode); +#if defined(CONFIG_VCODEC_MMU) + list_for_each_entry_safe(subdata, n, + &pservice->subdev_list, lnk_service) { + if (data != subdata && subdata->mmu_dev && + test_bit(MMU_ACTIVATED, &subdata->state)) { + clear_bit(MMU_ACTIVATED, &subdata->state); + rockchip_iovmm_deactivate(subdata->dev); + } + } +#endif + bits = 1 << pservice->mode_bit; +#ifdef CONFIG_MFD_SYSCON + if (pservice->grf) { + regmap_read(pservice->grf, pservice->mode_ctrl, &raw); + + if (data->mode == VCODEC_RUNNING_MODE_HEVC) + regmap_write(pservice->grf, pservice->mode_ctrl, + raw | bits | (bits << 16)); + else + regmap_write(pservice->grf, pservice->mode_ctrl, + (raw & (~bits)) | (bits << 16)); + } else if (pservice->grf_base) { + u32 *grf_base = pservice->grf_base; + + raw = readl_relaxed(grf_base + pservice->mode_ctrl / 4); + if (data->mode == VCODEC_RUNNING_MODE_HEVC) + writel_relaxed(raw | bits | (bits << 16), + grf_base + pservice->mode_ctrl / 4); + else + writel_relaxed((raw & (~bits)) | (bits << 16), + grf_base + pservice->mode_ctrl / 4); + } else { + vpu_err("no grf resource define, switch decoder failed\n"); + return; + } +#else + if (pservice->grf_base) { + u32 *grf_base = pservice->grf_base; + + raw = readl_relaxed(grf_base + pservice->mode_ctrl / 4); + if (data->mode == VCODEC_RUNNING_MODE_HEVC) + writel_relaxed(raw | bits | (bits << 16), + grf_base + pservice->mode_ctrl / 4); + else + writel_relaxed((raw & (~bits)) | (bits << 16), + grf_base + pservice->mode_ctrl / 4); + } else { + vpu_err("no grf resource define, switch decoder failed\n"); + return; + } +#endif +#if defined(CONFIG_VCODEC_MMU) + if (data->mmu_dev && !test_bit(MMU_ACTIVATED, &data->state)) { + set_bit(MMU_ACTIVATED, &data->state); + if (atomic_read(&pservice->enabled)) + rockchip_iovmm_activate(data->dev); + else + BUG_ON(!atomic_read(&pservice->enabled)); + } +#endif + pservice->prev_mode = pservice->curr_mode; + pservice->curr_mode = data->mode; +} + +static void vcodec_exit_mode(struct vpu_subdev_data *data) +{ + if (data->mmu_dev && test_bit(MMU_ACTIVATED, &data->state)) { + clear_bit(MMU_ACTIVATED, &data->state); +#if defined(CONFIG_VCODEC_MMU) + rockchip_iovmm_deactivate(data->dev); +#endif + data->pservice->curr_mode = VCODEC_RUNNING_MODE_NONE; + } +} + +static int vpu_get_clk(struct vpu_service_info *pservice) +{ +#if VCODEC_CLOCK_ENABLE + struct device *dev = pservice->dev; + + switch (pservice->dev_id) { + case VCODEC_DEVICE_ID_HEVC: + pservice->pd_video = devm_clk_get(dev, "pd_hevc"); + if (IS_ERR(pservice->pd_video)) { + dev_err(dev, "failed on clk_get pd_hevc\n"); + return -1; + } + case VCODEC_DEVICE_ID_COMBO: + case VCODEC_DEVICE_ID_RKVDEC: + pservice->clk_cabac = devm_clk_get(dev, "clk_cabac"); + if (IS_ERR(pservice->clk_cabac)) { + dev_err(dev, "failed on clk_get clk_cabac\n"); + pservice->clk_cabac = NULL; + } + pservice->clk_core = devm_clk_get(dev, "clk_core"); + if (IS_ERR(pservice->clk_core)) { + dev_err(dev, "failed on clk_get clk_core\n"); + return -1; + } + case VCODEC_DEVICE_ID_VPU: + pservice->aclk_vcodec = devm_clk_get(dev, "aclk_vcodec"); + if (IS_ERR(pservice->aclk_vcodec)) { + dev_err(dev, "failed on clk_get aclk_vcodec\n"); + return -1; + } + + pservice->hclk_vcodec = devm_clk_get(dev, "hclk_vcodec"); + if (IS_ERR(pservice->hclk_vcodec)) { + dev_err(dev, "failed on clk_get hclk_vcodec\n"); + return -1; + } + if (pservice->pd_video == NULL) { + pservice->pd_video = devm_clk_get(dev, "pd_video"); + if (IS_ERR(pservice->pd_video)) { + pservice->pd_video = NULL; + dev_info(dev, "do not have pd_video\n"); + } + } + break; + default: + break; + } + + return 0; +#else + return 0; +#endif +} + +static void vpu_put_clk(struct vpu_service_info *pservice) +{ +#if VCODEC_CLOCK_ENABLE + if (pservice->pd_video) + devm_clk_put(pservice->dev, pservice->pd_video); + if (pservice->aclk_vcodec) + devm_clk_put(pservice->dev, pservice->aclk_vcodec); + if (pservice->hclk_vcodec) + devm_clk_put(pservice->dev, pservice->hclk_vcodec); + if (pservice->clk_core) + devm_clk_put(pservice->dev, pservice->clk_core); + if (pservice->clk_cabac) + devm_clk_put(pservice->dev, pservice->clk_cabac); +#endif +} + +static void vpu_reset(struct vpu_subdev_data *data) +{ + struct vpu_service_info *pservice = data->pservice; + enum pmu_idle_req type = IDLE_REQ_VIDEO; + + if (pservice->dev_id == VCODEC_DEVICE_ID_HEVC) + type = IDLE_REQ_HEVC; + + pr_info("%s: resetting...", dev_name(pservice->dev)); + +#if defined(CONFIG_ARCH_RK29) + clk_disable(aclk_ddr_vepu); + cru_set_soft_reset(SOFT_RST_CPU_VODEC_A2A_AHB, true); + cru_set_soft_reset(SOFT_RST_DDR_VCODEC_PORT, true); + cru_set_soft_reset(SOFT_RST_VCODEC_AHB_BUS, true); + cru_set_soft_reset(SOFT_RST_VCODEC_AXI_BUS, true); + mdelay(10); + cru_set_soft_reset(SOFT_RST_VCODEC_AXI_BUS, false); + cru_set_soft_reset(SOFT_RST_VCODEC_AHB_BUS, false); + cru_set_soft_reset(SOFT_RST_DDR_VCODEC_PORT, false); + cru_set_soft_reset(SOFT_RST_CPU_VODEC_A2A_AHB, false); + clk_enable(aclk_ddr_vepu); +#elif defined(CONFIG_ARCH_RK30) + pmu_set_idle_request(IDLE_REQ_VIDEO, true); + cru_set_soft_reset(SOFT_RST_CPU_VCODEC, true); + cru_set_soft_reset(SOFT_RST_VCODEC_NIU_AXI, true); + cru_set_soft_reset(SOFT_RST_VCODEC_AHB, true); + cru_set_soft_reset(SOFT_RST_VCODEC_AXI, true); + mdelay(1); + cru_set_soft_reset(SOFT_RST_VCODEC_AXI, false); + cru_set_soft_reset(SOFT_RST_VCODEC_AHB, false); + cru_set_soft_reset(SOFT_RST_VCODEC_NIU_AXI, false); + cru_set_soft_reset(SOFT_RST_CPU_VCODEC, false); + pmu_set_idle_request(IDLE_REQ_VIDEO, false); +#else +#endif + WARN_ON(pservice->reg_codec != NULL); + WARN_ON(pservice->reg_pproc != NULL); + WARN_ON(pservice->reg_resev != NULL); + pservice->reg_codec = NULL; + pservice->reg_pproc = NULL; + pservice->reg_resev = NULL; + + pr_info("for 3288/3368..."); +#if 0 //def CONFIG_RESET_CONTROLLER + if (pservice->rst_a && pservice->rst_h) { + if (rockchip_pmu_ops.set_idle_request) + rockchip_pmu_ops.set_idle_request(type, true); + pr_info("reset in\n"); + if (pservice->rst_v) + reset_control_assert(pservice->rst_v); + reset_control_assert(pservice->rst_a); + reset_control_assert(pservice->rst_h); + udelay(5); + reset_control_deassert(pservice->rst_h); + reset_control_deassert(pservice->rst_a); + if (pservice->rst_v) + reset_control_deassert(pservice->rst_v); + if (rockchip_pmu_ops.set_idle_request) + rockchip_pmu_ops.set_idle_request(type, false); + } +#endif + +#if defined(CONFIG_VCODEC_MMU) + if (data->mmu_dev && test_bit(MMU_ACTIVATED, &data->state)) { + clear_bit(MMU_ACTIVATED, &data->state); + if (atomic_read(&pservice->enabled)) + rockchip_iovmm_deactivate(data->dev); + else + BUG_ON(!atomic_read(&pservice->enabled)); + } +#endif + atomic_set(&pservice->reset_request, 0); + pr_info("done\n"); +} + +static void reg_deinit(struct vpu_subdev_data *data, struct vpu_reg *reg); +static void vpu_service_session_clear(struct vpu_subdev_data *data, + struct vpu_session *session) +{ + struct vpu_reg *reg, *n; + + list_for_each_entry_safe(reg, n, &session->waiting, session_link) { + reg_deinit(data, reg); + } + list_for_each_entry_safe(reg, n, &session->running, session_link) { + reg_deinit(data, reg); + } + list_for_each_entry_safe(reg, n, &session->done, session_link) { + reg_deinit(data, reg); + } +} + +static void vpu_service_dump(struct vpu_service_info *pservice) +{ +} + + +static void vpu_service_power_off(struct vpu_service_info *pservice) +{ + int total_running; + struct vpu_subdev_data *data = NULL, *n; + int ret = atomic_add_unless(&pservice->enabled, -1, 0); + + if (!ret) + return; + + total_running = atomic_read(&pservice->total_running); + if (total_running) { + pr_alert("alert: power off when %d task running!!\n", + total_running); + mdelay(50); + pr_alert("alert: delay 50 ms for running task\n"); + vpu_service_dump(pservice); + } + + pr_info("%s: power off...", dev_name(pservice->dev)); + + udelay(5); + +#if defined(CONFIG_VCODEC_MMU) + list_for_each_entry_safe(data, n, &pservice->subdev_list, lnk_service) { + if (data->mmu_dev && test_bit(MMU_ACTIVATED, &data->state)) { + clear_bit(MMU_ACTIVATED, &data->state); + rockchip_iovmm_deactivate(data->dev); + } + } + pservice->curr_mode = VCODEC_RUNNING_MODE_NONE; +#endif + +#if VCODEC_CLOCK_ENABLE + if (pservice->pd_video) + clk_disable_unprepare(pservice->pd_video); + if (pservice->hclk_vcodec) + clk_disable_unprepare(pservice->hclk_vcodec); + if (pservice->aclk_vcodec) + clk_disable_unprepare(pservice->aclk_vcodec); + if (pservice->clk_core) + clk_disable_unprepare(pservice->clk_core); + if (pservice->clk_cabac) + clk_disable_unprepare(pservice->clk_cabac); +#endif + + atomic_add(1, &pservice->power_off_cnt); + wake_unlock(&pservice->wake_lock); + pr_info("done\n"); +} + +static inline void vpu_queue_power_off_work(struct vpu_service_info *pservice) +{ + queue_delayed_work(system_wq, &pservice->power_off_work, + VPU_POWER_OFF_DELAY); +} + +static void vpu_power_off_work(struct work_struct *work_s) +{ + struct delayed_work *dlwork = container_of(work_s, + struct delayed_work, work); + struct vpu_service_info *pservice = container_of(dlwork, + struct vpu_service_info, power_off_work); + + if (mutex_trylock(&pservice->lock)) { + vpu_service_power_off(pservice); + mutex_unlock(&pservice->lock); + } else { + /* Come back later if the device is busy... */ + vpu_queue_power_off_work(pservice); + } +} + +static void vpu_service_power_on(struct vpu_service_info *pservice) +{ + int ret; + static ktime_t last; + ktime_t now = ktime_get(); + + if (ktime_to_ns(ktime_sub(now, last)) > NSEC_PER_SEC) { + cancel_delayed_work_sync(&pservice->power_off_work); + vpu_queue_power_off_work(pservice); + last = now; + } + ret = atomic_add_unless(&pservice->enabled, 1, 1); + if (!ret) + return; + + pr_info("%s: power on\n", dev_name(pservice->dev)); + +#define BIT_VCODEC_CLK_SEL (1<<10) + if (cpu_is_rk312x()) + writel_relaxed(readl_relaxed(RK_GRF_VIRT + RK312X_GRF_SOC_CON1) + | BIT_VCODEC_CLK_SEL | (BIT_VCODEC_CLK_SEL << 16), + RK_GRF_VIRT + RK312X_GRF_SOC_CON1); + +#if VCODEC_CLOCK_ENABLE + if (pservice->aclk_vcodec) + clk_prepare_enable(pservice->aclk_vcodec); + if (pservice->hclk_vcodec) + clk_prepare_enable(pservice->hclk_vcodec); + if (pservice->clk_core) + clk_prepare_enable(pservice->clk_core); + if (pservice->clk_cabac) + clk_prepare_enable(pservice->clk_cabac); + if (pservice->pd_video) + clk_prepare_enable(pservice->pd_video); +#endif + + udelay(5); + atomic_add(1, &pservice->power_on_cnt); + wake_lock(&pservice->wake_lock); +} + +static inline bool reg_check_interlace(struct vpu_reg *reg) +{ + u32 type = (reg->reg[3] & (1 << 23)); + + return (type > 0); +} + +static inline enum VPU_DEC_FMT reg_check_fmt(struct vpu_reg *reg) +{ + enum VPU_DEC_FMT type = (enum VPU_DEC_FMT)((reg->reg[3] >> 28) & 0xf); + + return type; +} + +static inline int reg_probe_width(struct vpu_reg *reg) +{ + int width_in_mb = reg->reg[4] >> 23; + + return width_in_mb * 16; +} + +static inline int reg_probe_hevc_y_stride(struct vpu_reg *reg) +{ + int y_virstride = reg->reg[8]; + + return y_virstride; +} + +static int vcodec_fd_to_iova(struct vpu_subdev_data *data, + struct vpu_reg *reg, int fd) +{ + struct vpu_service_info *pservice = data->pservice; + struct ion_handle *hdl; + int ret = 0; + struct vcodec_mem_region *mem_region; + + hdl = ion_import_dma_buf(pservice->ion_client, fd); + if (IS_ERR(hdl)) { + vpu_err("import dma-buf from fd %d failed\n", fd); + return PTR_ERR(hdl); + } + mem_region = kzalloc(sizeof(*mem_region), GFP_KERNEL); + + if (mem_region == NULL) { + vpu_err("allocate memory for iommu memory region failed\n"); + ion_free(pservice->ion_client, hdl); + return -1; + } + + mem_region->hdl = hdl; + if (data->mmu_dev) + ret = ion_map_iommu(data->dev, pservice->ion_client, + mem_region->hdl, &mem_region->iova, + &mem_region->len); + else + ret = ion_phys(pservice->ion_client, + mem_region->hdl, + (ion_phys_addr_t *)&mem_region->iova, + (size_t *)&mem_region->len); + + if (ret < 0) { + vpu_err("fd %d ion map iommu failed\n", fd); + kfree(mem_region); + ion_free(pservice->ion_client, hdl); + return ret; + } + INIT_LIST_HEAD(&mem_region->reg_lnk); + list_add_tail(&mem_region->reg_lnk, ®->mem_region_list); + return mem_region->iova; +} + +/* + * NOTE: rkvdec/rkhevc put scaling list address in pps buffer hardware will read + * it by pps id in video stream data. + * + * So we need to translate the address in iommu case. The address data is also + * 10bit fd + 22bit offset mode. + * Because userspace decoder do not give the pps id in the register file sets + * kernel driver need to translate each scaling list address in pps buffer which + * means 256 pps for H.264, 64 pps for H.265. + * + * In order to optimize the performance kernel driver ask userspace decoder to + * set all scaling list address in pps buffer to the same one which will be used + * on current decoding task. Then kernel driver can only translate the first + * address then copy it all pps buffer. + */ +static void fill_scaling_list_addr_in_pps( + struct vpu_subdev_data *data, + struct vpu_reg *reg, + char *pps, + int pps_info_count, + int pps_info_size, + int scaling_list_addr_offset) +{ + int base = scaling_list_addr_offset; + int scaling_fd = 0; + u32 scaling_offset; + + scaling_offset = (u32)pps[base + 0]; + scaling_offset += (u32)pps[base + 1] << 8; + scaling_offset += (u32)pps[base + 2] << 16; + scaling_offset += (u32)pps[base + 3] << 24; + + scaling_fd = scaling_offset & 0x3ff; + scaling_offset = scaling_offset >> 10; + + if (scaling_fd > 0) { + int i = 0; + u32 tmp = vcodec_fd_to_iova(data, reg, scaling_fd); + tmp += scaling_offset; + + for (i = 0; i < pps_info_count; i++, base += pps_info_size) { + pps[base + 0] = (tmp >> 0) & 0xff; + pps[base + 1] = (tmp >> 8) & 0xff; + pps[base + 2] = (tmp >> 16) & 0xff; + pps[base + 3] = (tmp >> 24) & 0xff; + } + } +} + +static int vcodec_bufid_to_iova(struct vpu_subdev_data *data, const u8 *tbl, + int size, struct vpu_reg *reg, + struct extra_info_for_iommu *ext_inf) +{ + struct vpu_service_info *pservice = data->pservice; + struct vpu_task_info *task = reg->task; + enum FORMAT_TYPE type; + struct ion_handle *hdl; + int ret = 0; + struct vcodec_mem_region *mem_region; + int i; + int offset = 0; + + if (tbl == NULL || size <= 0) { + dev_err(pservice->dev, "input arguments invalidate\n"); + return -1; + } + + if (task->get_fmt) + type = task->get_fmt(reg->reg); + else { + pr_err("invalid task with NULL get_fmt\n"); + return -1; + } + + for (i = 0; i < size; i++) { + int usr_fd = reg->reg[tbl[i]] & 0x3FF; + + /* if userspace do not set the fd at this register, skip */ + if (usr_fd == 0) + continue; + + /* + * special offset scale case + * + * This translation is for fd + offset translation. + * One register has 32bits. We need to transfer both buffer file + * handle and the start address offset so we packet file handle + * and offset together using below format. + * + * 0~9 bit for buffer file handle range 0 ~ 1023 + * 10~31 bit for offset range 0 ~ 4M + * + * But on 4K case the offset can be larger the 4M + * So on H.264 4K vpu/vpu2 decoder we scale the offset by 16 + * But MPEG4 will use the same register for colmv and it do not + * need scale. + * + * RKVdec do not have this issue. + */ + if ((type == FMT_H264D || type == FMT_VP9D) && + task->reg_dir_mv > 0 && task->reg_dir_mv == tbl[i]) + offset = reg->reg[tbl[i]] >> 10 << 4; + else + offset = reg->reg[tbl[i]] >> 10; + + vpu_debug(DEBUG_IOMMU, "pos %3d fd %3d offset %10d\n", + tbl[i], usr_fd, offset); + + hdl = ion_import_dma_buf(pservice->ion_client, usr_fd); + if (IS_ERR(hdl)) { + dev_err(pservice->dev, + "import dma-buf from fd %d failed, reg[%d]\n", + usr_fd, tbl[i]); + return PTR_ERR(hdl); + } + + if (task->reg_pps > 0 && task->reg_pps == tbl[i]) { + int pps_info_offset; + int pps_info_count; + int pps_info_size; + int scaling_list_addr_offset; + + switch (type) { + case FMT_H264D: { + pps_info_offset = offset; + pps_info_count = 256; + pps_info_size = 32; + scaling_list_addr_offset = 23; + } break; + case FMT_H265D: { + pps_info_offset = 0; + pps_info_count = 64; + pps_info_size = 80; + scaling_list_addr_offset = 74; + } break; + default: { + pps_info_offset = 0; + pps_info_count = 0; + pps_info_size = 0; + scaling_list_addr_offset = 0; + } break; + } + + vpu_debug(DEBUG_PPS_FILL, + "scaling list filling parameter:\n"); + vpu_debug(DEBUG_PPS_FILL, + "pps_info_offset %d\n", pps_info_offset); + vpu_debug(DEBUG_PPS_FILL, + "pps_info_count %d\n", pps_info_count); + vpu_debug(DEBUG_PPS_FILL, + "pps_info_size %d\n", pps_info_size); + vpu_debug(DEBUG_PPS_FILL, + "scaling_list_addr_offset %d\n", + scaling_list_addr_offset); + + if (pps_info_count) { + char *pps = (char *)ion_map_kernel( + pservice->ion_client, hdl); + vpu_debug(DEBUG_PPS_FILL, + "scaling list setting pps %p\n", pps); + pps += pps_info_offset; + + fill_scaling_list_addr_in_pps( + data, reg, pps, + pps_info_count, + pps_info_size, + scaling_list_addr_offset); + } + } + + mem_region = kzalloc(sizeof(*mem_region), GFP_KERNEL); + + if (!mem_region) { + ion_free(pservice->ion_client, hdl); + return -1; + } + + mem_region->hdl = hdl; + mem_region->reg_idx = tbl[i]; + + if (data->mmu_dev) + ret = ion_map_iommu(data->dev, + pservice->ion_client, + mem_region->hdl, + &mem_region->iova, + &mem_region->len); + else + ret = ion_phys(pservice->ion_client, + mem_region->hdl, + (ion_phys_addr_t *)&mem_region->iova, + (size_t *)&mem_region->len); + + if (ret < 0) { + dev_err(pservice->dev, "reg %d fd %d ion map iommu failed\n", + tbl[i], usr_fd); + kfree(mem_region); + ion_free(pservice->ion_client, hdl); + return ret; + } + + /* + * special for vpu dec num 12: record decoded length + * hacking for decoded length + * NOTE: not a perfect fix, the fd is not recorded + */ + if (task->reg_len > 0 && task->reg_len == tbl[i]) { + reg->dec_base = mem_region->iova + offset; + vpu_debug(DEBUG_REGISTER, "dec_set %08x\n", + reg->dec_base); + } + + reg->reg[tbl[i]] = mem_region->iova + offset; + INIT_LIST_HEAD(&mem_region->reg_lnk); + list_add_tail(&mem_region->reg_lnk, ®->mem_region_list); + } + + if (ext_inf != NULL && ext_inf->magic == EXTRA_INFO_MAGIC) { + for (i = 0; i < ext_inf->cnt; i++) { + vpu_debug(DEBUG_IOMMU, "reg[%d] + offset %d\n", + ext_inf->elem[i].index, + ext_inf->elem[i].offset); + reg->reg[ext_inf->elem[i].index] += + ext_inf->elem[i].offset; + } + } + + return 0; +} + +static int vcodec_reg_address_translate(struct vpu_subdev_data *data, + struct vpu_reg *reg, + struct extra_info_for_iommu *ext_inf) +{ + enum FORMAT_TYPE type = reg->task->get_fmt(reg->reg); + + if (type < FMT_TYPE_BUTT) { + const struct vpu_trans_info *info = ®->trans[type]; + const u8 *tbl = info->table; + int size = info->count; + + return vcodec_bufid_to_iova(data, tbl, size, reg, ext_inf); + } + pr_err("found invalid format type!\n"); + return -1; +} + +static void get_reg_freq(struct vpu_subdev_data *data, struct vpu_reg *reg) +{ + + if (!soc_is_rk2928g()) { + if (reg->type == VPU_DEC || reg->type == VPU_DEC_PP) { + if (reg_check_fmt(reg) == VPU_DEC_FMT_H264) { + if (reg_probe_width(reg) > 3200) { + /*raise frequency for 4k avc.*/ + reg->freq = VPU_FREQ_600M; + } + } else { + if (reg_check_interlace(reg)) + reg->freq = VPU_FREQ_400M; + } + } + if (data->hw_id == HEVC_ID) { + if (reg_probe_hevc_y_stride(reg) > 60000) + reg->freq = VPU_FREQ_400M; + } + if (reg->type == VPU_PP) + reg->freq = VPU_FREQ_400M; + } +} + +static struct vpu_reg *reg_init(struct vpu_subdev_data *data, + struct vpu_session *session, + void __user *src, u32 size) +{ + struct vpu_service_info *pservice = data->pservice; + int extra_size = 0; + struct extra_info_for_iommu extra_info; + struct vpu_reg *reg = kzalloc(sizeof(*reg) + data->reg_size, + GFP_KERNEL); + + vpu_debug_enter(); + + if (NULL == reg) { + vpu_err("error: kmalloc fail in reg_init\n"); + return NULL; + } + + if (size > data->reg_size) { + pr_err("vpu reg size %u is larger than hw reg size %u\n", + size, data->reg_size); + extra_size = size - data->reg_size; + size = data->reg_size; + } + reg->session = session; + reg->data = data; + reg->type = session->type; + reg->size = size; + reg->freq = VPU_FREQ_DEFAULT; + reg->task = &data->task_info[session->type]; + reg->trans = data->trans_info; + reg->reg = (u32 *)®[1]; + INIT_LIST_HEAD(®->session_link); + INIT_LIST_HEAD(®->status_link); + + INIT_LIST_HEAD(®->mem_region_list); + + if (copy_from_user(®->reg[0], (void __user *)src, size)) { + vpu_err("error: copy_from_user failed in reg_init\n"); + kfree(reg); + return NULL; + } + + if (copy_from_user(&extra_info, (u8 *)src + size, extra_size)) { + vpu_err("error: copy_from_user failed in reg_init\n"); + kfree(reg); + return NULL; + } + + if (0 > vcodec_reg_address_translate(data, reg, &extra_info)) { + int i = 0; + + vpu_err("error: translate reg address failed, dumping regs\n"); + for (i = 0; i < size >> 2; i++) + pr_err("reg[%02d]: %08x\n", i, *((u32 *)src + i)); + + kfree(reg); + return NULL; + } + + mutex_lock(&pservice->lock); + list_add_tail(®->status_link, &pservice->waiting); + list_add_tail(®->session_link, &session->waiting); + mutex_unlock(&pservice->lock); + + if (pservice->auto_freq) + get_reg_freq(data, reg); + + vpu_debug_leave(); + return reg; +} + +static void reg_deinit(struct vpu_subdev_data *data, struct vpu_reg *reg) +{ + struct vpu_service_info *pservice = data->pservice; + struct vcodec_mem_region *mem_region = NULL, *n; + + list_del_init(®->session_link); + list_del_init(®->status_link); + if (reg == pservice->reg_codec) + pservice->reg_codec = NULL; + if (reg == pservice->reg_pproc) + pservice->reg_pproc = NULL; + + /* release memory region attach to this registers table. */ + list_for_each_entry_safe(mem_region, n, + ®->mem_region_list, reg_lnk) { + ion_free(pservice->ion_client, mem_region->hdl); + list_del_init(&mem_region->reg_lnk); + kfree(mem_region); + } + + kfree(reg); +} + +static void reg_from_wait_to_run(struct vpu_service_info *pservice, + struct vpu_reg *reg) +{ + vpu_debug_enter(); + list_del_init(®->status_link); + list_add_tail(®->status_link, &pservice->running); + + list_del_init(®->session_link); + list_add_tail(®->session_link, ®->session->running); + vpu_debug_leave(); +} + +static void reg_copy_from_hw(struct vpu_reg *reg, u32 *src, u32 count) +{ + int i; + u32 *dst = reg->reg; + + vpu_debug_enter(); + for (i = 0; i < count; i++, src++) + *dst++ = readl_relaxed(src); + + dst = (u32 *)®->reg[0]; + for (i = 0; i < count; i++) + vpu_debug(DEBUG_GET_REG, "get reg[%02d] %08x\n", i, dst[i]); + + vpu_debug_leave(); +} + +static void reg_from_run_to_done(struct vpu_subdev_data *data, + struct vpu_reg *reg) +{ + struct vpu_service_info *pservice = data->pservice; + struct vpu_hw_info *hw_info = data->hw_info; + struct vpu_task_info *task = reg->task; + + vpu_debug_enter(); + + list_del_init(®->status_link); + list_add_tail(®->status_link, &pservice->done); + + list_del_init(®->session_link); + list_add_tail(®->session_link, ®->session->done); + + switch (reg->type) { + case VPU_ENC: { + pservice->reg_codec = NULL; + reg_copy_from_hw(reg, data->enc_dev.regs, hw_info->enc_reg_num); + reg->reg[task->reg_irq] = pservice->irq_status; + } break; + case VPU_DEC: { + pservice->reg_codec = NULL; + reg_copy_from_hw(reg, data->dec_dev.regs, hw_info->dec_reg_num); + + /* revert hack for decoded length */ + if (task->reg_len > 0) { + int reg_len = task->reg_len; + u32 dec_get = reg->reg[reg_len]; + s32 dec_length = dec_get - reg->dec_base; + + vpu_debug(DEBUG_REGISTER, + "dec_get %08x dec_length %d\n", + dec_get, dec_length); + reg->reg[reg_len] = dec_length << 10; + } + + reg->reg[task->reg_irq] = pservice->irq_status; + } break; + case VPU_PP: { + pservice->reg_pproc = NULL; + reg_copy_from_hw(reg, data->dec_dev.regs, hw_info->dec_reg_num); + writel_relaxed(0, data->dec_dev.regs + task->reg_irq); + } break; + case VPU_DEC_PP: { + u32 pipe_mode; + u32 *regs = data->dec_dev.regs; + + pservice->reg_codec = NULL; + pservice->reg_pproc = NULL; + + reg_copy_from_hw(reg, data->dec_dev.regs, hw_info->dec_reg_num); + + /* NOTE: remove pp pipeline mode flag first */ + pipe_mode = readl_relaxed(regs + task->reg_pipe); + pipe_mode &= ~task->pipe_mask; + writel_relaxed(pipe_mode, regs + task->reg_pipe); + + /* revert hack for decoded length */ + if (task->reg_len > 0) { + int reg_len = task->reg_len; + u32 dec_get = reg->reg[reg_len]; + s32 dec_length = dec_get - reg->dec_base; + + vpu_debug(DEBUG_REGISTER, + "dec_get %08x dec_length %d\n", + dec_get, dec_length); + reg->reg[reg_len] = dec_length << 10; + } + + reg->reg[task->reg_irq] = pservice->irq_status; + } break; + default: { + vpu_err("error: copy reg from hw with unknown type %d\n", + reg->type); + } break; + } + vcodec_exit_mode(data); + + atomic_sub(1, ®->session->task_running); + atomic_sub(1, &pservice->total_running); + wake_up(®->session->wait); + + vpu_debug_leave(); +} + +static void vpu_service_set_freq(struct vpu_service_info *pservice, + struct vpu_reg *reg) +{ + enum VPU_FREQ curr = atomic_read(&pservice->freq_status); + + if (curr == reg->freq) + return; + + atomic_set(&pservice->freq_status, reg->freq); + switch (reg->freq) { + case VPU_FREQ_200M: { + clk_set_rate(pservice->aclk_vcodec, 200*MHZ); + } break; + case VPU_FREQ_266M: { + clk_set_rate(pservice->aclk_vcodec, 266*MHZ); + } break; + case VPU_FREQ_300M: { + clk_set_rate(pservice->aclk_vcodec, 300*MHZ); + } break; + case VPU_FREQ_400M: { + clk_set_rate(pservice->aclk_vcodec, 400*MHZ); + } break; + case VPU_FREQ_500M: { + clk_set_rate(pservice->aclk_vcodec, 500*MHZ); + } break; + case VPU_FREQ_600M: { + clk_set_rate(pservice->aclk_vcodec, 600*MHZ); + } break; + default: { + unsigned long rate = 300*MHZ; + + if (soc_is_rk2928g()) + rate = 400*MHZ; + + clk_set_rate(pservice->aclk_vcodec, rate); + } break; + } +} + +static void reg_copy_to_hw(struct vpu_subdev_data *data, struct vpu_reg *reg) +{ + struct vpu_service_info *pservice = data->pservice; + struct vpu_task_info *task = reg->task; + struct vpu_hw_info *hw_info = data->hw_info; + int i; + u32 *src = (u32 *)®->reg[0]; + u32 enable_mask = task->enable_mask; + u32 gating_mask = task->gating_mask; + u32 reg_en = task->reg_en; + + vpu_debug_enter(); + + atomic_add(1, &pservice->total_running); + atomic_add(1, ®->session->task_running); + + if (pservice->auto_freq) + vpu_service_set_freq(pservice, reg); + + vcodec_enter_mode(data); + + switch (reg->type) { + case VPU_ENC: { + u32 *dst = data->enc_dev.regs; + u32 base = 0; + u32 end = hw_info->enc_reg_num; + /* u32 reg_gating = task->reg_gating; */ + + pservice->reg_codec = reg; + + vpu_debug(DEBUG_TASK_INFO, "reg: base %3d end %d en %2d mask: en %x gate %x\n", + base, end, reg_en, enable_mask, gating_mask); + + VEPU_CLEAN_CACHE(dst); + + if (debug & DEBUG_SET_REG) + for (i = base; i < end; i++) + vpu_debug(DEBUG_SET_REG, "set reg[%02d] %08x\n", + i, src[i]); + + /* + * NOTE: encoder need to setup mode first + */ + writel_relaxed(src[reg_en] & enable_mask, dst + reg_en); + + /* NOTE: encoder gating is not on enable register */ + /* src[reg_gating] |= gating_mask; */ + + for (i = base; i < end; i++) { + if (i != reg_en) + writel_relaxed(src[i], dst + i); + } + + writel(src[reg_en], dst + reg_en); + dsb(sy); + + time_record(reg->task, 0); + } break; + case VPU_DEC: { + u32 *dst = data->dec_dev.regs; + u32 len = hw_info->dec_reg_num; + u32 base = hw_info->base_dec; + u32 end = hw_info->end_dec; + + pservice->reg_codec = reg; + + vpu_debug(DEBUG_TASK_INFO, "reg: base %3d end %d en %2d mask: en %x gate %x\n", + base, end, reg_en, enable_mask, gating_mask); + + VDPU_CLEAN_CACHE(dst); + + /* on rkvdec set cache size to 64byte */ + if (pservice->dev_id == VCODEC_DEVICE_ID_RKVDEC) { + u32 *cache_base = dst + 0x100; + u32 val = (debug & DEBUG_CACHE_32B) ? (0x3) : (0x13); + writel_relaxed(val, cache_base + 0x07); + writel_relaxed(val, cache_base + 0x17); + } + + if (debug & DEBUG_SET_REG) + for (i = 0; i < len; i++) + vpu_debug(DEBUG_SET_REG, "set reg[%02d] %08x\n", + i, src[i]); + + /* + * NOTE: The end register is invalid. Do NOT write to it + * Also the base register must be written + */ + for (i = base; i < end; i++) { + if (i != reg_en) + writel_relaxed(src[i], dst + i); + } + + writel(src[reg_en] | gating_mask, dst + reg_en); + dsb(sy); + + time_record(reg->task, 0); + } break; + case VPU_PP: { + u32 *dst = data->dec_dev.regs; + u32 base = hw_info->base_pp; + u32 end = hw_info->end_pp; + + pservice->reg_pproc = reg; + + vpu_debug(DEBUG_TASK_INFO, "reg: base %3d end %d en %2d mask: en %x gate %x\n", + base, end, reg_en, enable_mask, gating_mask); + + if (debug & DEBUG_SET_REG) + for (i = base; i < end; i++) + vpu_debug(DEBUG_SET_REG, "set reg[%02d] %08x\n", + i, src[i]); + + for (i = base; i < end; i++) { + if (i != reg_en) + writel_relaxed(src[i], dst + i); + } + + writel(src[reg_en] | gating_mask, dst + reg_en); + dsb(sy); + + time_record(reg->task, 0); + } break; + case VPU_DEC_PP: { + u32 *dst = data->dec_dev.regs; + u32 base = hw_info->base_dec_pp; + u32 end = hw_info->end_dec_pp; + + pservice->reg_codec = reg; + pservice->reg_pproc = reg; + + vpu_debug(DEBUG_TASK_INFO, "reg: base %3d end %d en %2d mask: en %x gate %x\n", + base, end, reg_en, enable_mask, gating_mask); + + /* VDPU_SOFT_RESET(dst); */ + VDPU_CLEAN_CACHE(dst); + + if (debug & DEBUG_SET_REG) + for (i = base; i < end; i++) + vpu_debug(DEBUG_SET_REG, "set reg[%02d] %08x\n", + i, src[i]); + + for (i = base; i < end; i++) { + if (i != reg_en) + writel_relaxed(src[i], dst + i); + } + + /* NOTE: dec output must be disabled */ + + writel(src[reg_en] | gating_mask, dst + reg_en); + dsb(sy); + + time_record(reg->task, 0); + } break; + default: { + vpu_err("error: unsupport session type %d", reg->type); + atomic_sub(1, &pservice->total_running); + atomic_sub(1, ®->session->task_running); + } break; + } + + vpu_debug_leave(); +} + +static void try_set_reg(struct vpu_subdev_data *data) +{ + struct vpu_service_info *pservice = data->pservice; + + vpu_debug_enter(); + if (!list_empty(&pservice->waiting)) { + struct vpu_reg *reg_codec = pservice->reg_codec; + struct vpu_reg *reg_pproc = pservice->reg_pproc; + int can_set = 0; + bool change_able = (reg_codec == NULL) && (reg_pproc == NULL); + int reset_request = atomic_read(&pservice->reset_request); + struct vpu_reg *reg = list_entry(pservice->waiting.next, + struct vpu_reg, status_link); + + vpu_service_power_on(pservice); + + if (change_able || !reset_request) { + switch (reg->type) { + case VPU_ENC: { + if (change_able) + can_set = 1; + } break; + case VPU_DEC: { + if (reg_codec == NULL) + can_set = 1; + if (pservice->auto_freq && (reg_pproc != NULL)) + can_set = 0; + } break; + case VPU_PP: { + if (reg_codec == NULL) { + if (reg_pproc == NULL) + can_set = 1; + } else { + if ((reg_codec->type == VPU_DEC) && + (reg_pproc == NULL)) + can_set = 1; + + /* + * NOTE: + * can not charge frequency + * when vpu is working + */ + if (pservice->auto_freq) + can_set = 0; + } + } break; + case VPU_DEC_PP: { + if (change_able) + can_set = 1; + } break; + default: { + pr_err("undefined reg type %d\n", reg->type); + } break; + } + } + + /* then check reset request */ + if (reset_request && !change_able) + reset_request = 0; + + /* do reset before setting registers */ + if (reset_request) + vpu_reset(data); + + if (can_set) { + reg_from_wait_to_run(pservice, reg); + reg_copy_to_hw(reg->data, reg); + } + } + vpu_debug_leave(); +} + +static int return_reg(struct vpu_subdev_data *data, + struct vpu_reg *reg, u32 __user *dst) +{ + struct vpu_hw_info *hw_info = data->hw_info; + size_t size = reg->size; + u32 base; + + vpu_debug_enter(); + switch (reg->type) { + case VPU_ENC: { + base = 0; + } break; + case VPU_DEC: { + base = hw_info->base_dec_pp; + } break; + case VPU_PP: { + base = hw_info->base_pp; + } break; + case VPU_DEC_PP: { + base = hw_info->base_dec_pp; + } break; + default: { + vpu_err("error: copy reg to user with unknown type %d\n", + reg->type); + return -EFAULT; + } break; + } + + if (copy_to_user(dst, ®->reg[base], size)) { + vpu_err("error: return_reg copy_to_user failed\n"); + return -EFAULT; + } + + reg_deinit(data, reg); + vpu_debug_leave(); + return 0; +} + +static long vpu_service_ioctl(struct file *filp, unsigned int cmd, + unsigned long arg) +{ + struct vpu_subdev_data *data = + container_of(filp->f_path.dentry->d_inode->i_cdev, + struct vpu_subdev_data, cdev); + struct vpu_service_info *pservice = data->pservice; + struct vpu_session *session = (struct vpu_session *)filp->private_data; + + vpu_debug_enter(); + if (NULL == session) + return -EINVAL; + + switch (cmd) { + case VPU_IOC_SET_CLIENT_TYPE: { + session->type = (enum VPU_CLIENT_TYPE)arg; + vpu_debug(DEBUG_IOCTL, "pid %d set client type %d\n", + session->pid, session->type); + } break; + case VPU_IOC_GET_HW_FUSE_STATUS: { + struct vpu_request req; + + vpu_debug(DEBUG_IOCTL, "pid %d get hw status %d\n", + session->pid, session->type); + if (copy_from_user(&req, (void __user *)arg, sizeof(req))) { + vpu_err("error: get hw status copy_from_user failed\n"); + return -EFAULT; + } else { + void *config = (session->type != VPU_ENC) ? + ((void *)&pservice->dec_config) : + ((void *)&pservice->enc_config); + size_t size = (session->type != VPU_ENC) ? + (sizeof(struct vpu_dec_config)) : + (sizeof(struct vpu_enc_config)); + if (copy_to_user((void __user *)req.req, + config, size)) { + vpu_err("error: get hw status copy_to_user failed type %d\n", + session->type); + return -EFAULT; + } + } + } break; + case VPU_IOC_SET_REG: { + struct vpu_request req; + struct vpu_reg *reg; + + vpu_debug(DEBUG_IOCTL, "pid %d set reg type %d\n", + session->pid, session->type); + if (copy_from_user(&req, (void __user *)arg, + sizeof(struct vpu_request))) { + vpu_err("error: set reg copy_from_user failed\n"); + return -EFAULT; + } + reg = reg_init(data, session, (void __user *)req.req, req.size); + if (NULL == reg) { + return -EFAULT; + } else { + mutex_lock(&pservice->lock); + try_set_reg(data); + mutex_unlock(&pservice->lock); + } + } break; + case VPU_IOC_GET_REG: { + struct vpu_request req; + struct vpu_reg *reg; + int ret; + + vpu_debug(DEBUG_IOCTL, "pid %d get reg type %d\n", + session->pid, session->type); + if (copy_from_user(&req, (void __user *)arg, + sizeof(struct vpu_request))) { + vpu_err("error: get reg copy_from_user failed\n"); + return -EFAULT; + } + + ret = wait_event_timeout(session->wait, + !list_empty(&session->done), + VPU_TIMEOUT_DELAY); + + if (!list_empty(&session->done)) { + if (ret < 0) + vpu_err("warning: pid %d wait task error ret %d\n", + session->pid, ret); + ret = 0; + } else { + if (unlikely(ret < 0)) { + vpu_err("error: pid %d wait task ret %d\n", + session->pid, ret); + } else if (ret == 0) { + vpu_err("error: pid %d wait %d task done timeout\n", + session->pid, + atomic_read(&session->task_running)); + ret = -ETIMEDOUT; + } + } + + if (ret < 0) { + int task_running = atomic_read(&session->task_running); + + mutex_lock(&pservice->lock); + vpu_service_dump(pservice); + if (task_running) { + atomic_set(&session->task_running, 0); + atomic_sub(task_running, + &pservice->total_running); + pr_err("%d task is running but not return, reset hardware...", + task_running); + vpu_reset(data); + pr_err("done\n"); + } + vpu_service_session_clear(data, session); + mutex_unlock(&pservice->lock); + return ret; + } + + mutex_lock(&pservice->lock); + reg = list_entry(session->done.next, + struct vpu_reg, session_link); + return_reg(data, reg, (u32 __user *)req.req); + mutex_unlock(&pservice->lock); + } break; + case VPU_IOC_PROBE_IOMMU_STATUS: { + int iommu_enable = 1; + + vpu_debug(DEBUG_IOCTL, "VPU_IOC_PROBE_IOMMU_STATUS\n"); + + if (copy_to_user((void __user *)arg, + &iommu_enable, sizeof(int))) { + vpu_err("error: iommu status copy_to_user failed\n"); + return -EFAULT; + } + } break; + default: { + vpu_err("error: unknow vpu service ioctl cmd %x\n", cmd); + } break; + } + vpu_debug_leave(); + return 0; +} + +#ifdef CONFIG_COMPAT +static long compat_vpu_service_ioctl(struct file *filp, unsigned int cmd, + unsigned long arg) +{ + struct vpu_subdev_data *data = + container_of(filp->f_path.dentry->d_inode->i_cdev, + struct vpu_subdev_data, cdev); + struct vpu_service_info *pservice = data->pservice; + struct vpu_session *session = (struct vpu_session *)filp->private_data; + + vpu_debug_enter(); + vpu_debug(3, "cmd %x, COMPAT_VPU_IOC_SET_CLIENT_TYPE %x\n", cmd, + (u32)COMPAT_VPU_IOC_SET_CLIENT_TYPE); + if (NULL == session) + return -EINVAL; + + switch (cmd) { + case COMPAT_VPU_IOC_SET_CLIENT_TYPE: { + session->type = (enum VPU_CLIENT_TYPE)arg; + vpu_debug(DEBUG_IOCTL, "compat set client type %d\n", + session->type); + } break; + case COMPAT_VPU_IOC_GET_HW_FUSE_STATUS: { + struct compat_vpu_request req; + + vpu_debug(DEBUG_IOCTL, "compat get hw status %d\n", + session->type); + if (copy_from_user(&req, compat_ptr((compat_uptr_t)arg), + sizeof(struct compat_vpu_request))) { + vpu_err("error: compat get hw status copy_from_user failed\n"); + return -EFAULT; + } else { + void *config = (session->type != VPU_ENC) ? + ((void *)&pservice->dec_config) : + ((void *)&pservice->enc_config); + size_t size = (session->type != VPU_ENC) ? + (sizeof(struct vpu_dec_config)) : + (sizeof(struct vpu_enc_config)); + + if (copy_to_user(compat_ptr((compat_uptr_t)req.req), + config, size)) { + vpu_err("error: compat get hw status copy_to_user failed type %d\n", + session->type); + return -EFAULT; + } + } + } break; + case COMPAT_VPU_IOC_SET_REG: { + struct compat_vpu_request req; + struct vpu_reg *reg; + + vpu_debug(DEBUG_IOCTL, "compat set reg type %d\n", + session->type); + if (copy_from_user(&req, compat_ptr((compat_uptr_t)arg), + sizeof(struct compat_vpu_request))) { + vpu_err("compat set_reg copy_from_user failed\n"); + return -EFAULT; + } + reg = reg_init(data, session, + compat_ptr((compat_uptr_t)req.req), req.size); + if (NULL == reg) { + return -EFAULT; + } else { + mutex_lock(&pservice->lock); + try_set_reg(data); + mutex_unlock(&pservice->lock); + } + } break; + case COMPAT_VPU_IOC_GET_REG: { + struct compat_vpu_request req; + struct vpu_reg *reg; + int ret; + + vpu_debug(DEBUG_IOCTL, "compat get reg type %d\n", + session->type); + if (copy_from_user(&req, compat_ptr((compat_uptr_t)arg), + sizeof(struct compat_vpu_request))) { + vpu_err("compat get reg copy_from_user failed\n"); + return -EFAULT; + } + + ret = wait_event_timeout(session->wait, + !list_empty(&session->done), + VPU_TIMEOUT_DELAY); + + if (!list_empty(&session->done)) { + if (ret < 0) + vpu_err("warning: pid %d wait task error ret %d\n", + session->pid, ret); + ret = 0; + } else { + if (unlikely(ret < 0)) { + vpu_err("error: pid %d wait task ret %d\n", + session->pid, ret); + } else if (ret == 0) { + vpu_err("error: pid %d wait %d task done timeout\n", + session->pid, + atomic_read(&session->task_running)); + ret = -ETIMEDOUT; + } + } + + if (ret < 0) { + int task_running = atomic_read(&session->task_running); + + mutex_lock(&pservice->lock); + vpu_service_dump(pservice); + if (task_running) { + atomic_set(&session->task_running, 0); + atomic_sub(task_running, + &pservice->total_running); + pr_err("%d task is running but not return, reset hardware...", + task_running); + vpu_reset(data); + pr_err("done\n"); + } + vpu_service_session_clear(data, session); + mutex_unlock(&pservice->lock); + return ret; + } + + mutex_lock(&pservice->lock); + reg = list_entry(session->done.next, + struct vpu_reg, session_link); + return_reg(data, reg, compat_ptr((compat_uptr_t)req.req)); + mutex_unlock(&pservice->lock); + } break; + case COMPAT_VPU_IOC_PROBE_IOMMU_STATUS: { + int iommu_enable = 1; + + vpu_debug(DEBUG_IOCTL, "COMPAT_VPU_IOC_PROBE_IOMMU_STATUS\n"); + + if (copy_to_user(compat_ptr((compat_uptr_t)arg), + &iommu_enable, sizeof(int))) { + vpu_err("error: VPU_IOC_PROBE_IOMMU_STATUS copy_to_user failed\n"); + return -EFAULT; + } + } break; + default: { + vpu_err("error: unknow vpu service ioctl cmd %x\n", cmd); + } break; + } + vpu_debug_leave(); + return 0; +} +#endif + +static int vpu_service_check_hw(struct vpu_subdev_data *data) +{ + int ret = -EINVAL, i = 0; + u32 hw_id = readl_relaxed(data->regs); + + hw_id = (hw_id >> 16) & 0xFFFF; + pr_info("checking hw id %x\n", hw_id); + data->hw_info = NULL; + for (i = 0; i < ARRAY_SIZE(vcodec_info_set); i++) { + struct vcodec_info *info = &vcodec_info_set[i]; + + if (hw_id == info->hw_id) { + data->hw_id = info->hw_id; + data->hw_info = info->hw_info; + data->task_info = info->task_info; + data->trans_info = info->trans_info; + ret = 0; + break; + } + } + return ret; +} + +static int vpu_service_open(struct inode *inode, struct file *filp) +{ + struct vpu_subdev_data *data = container_of( + inode->i_cdev, struct vpu_subdev_data, cdev); + struct vpu_service_info *pservice = data->pservice; + struct vpu_session *session = kmalloc(sizeof(*session), GFP_KERNEL); + + vpu_debug_enter(); + + if (NULL == session) { + vpu_err("error: unable to allocate memory for vpu_session."); + return -ENOMEM; + } + + session->type = VPU_TYPE_BUTT; + session->pid = current->pid; + INIT_LIST_HEAD(&session->waiting); + INIT_LIST_HEAD(&session->running); + INIT_LIST_HEAD(&session->done); + INIT_LIST_HEAD(&session->list_session); + init_waitqueue_head(&session->wait); + atomic_set(&session->task_running, 0); + mutex_lock(&pservice->lock); + list_add_tail(&session->list_session, &pservice->session); + filp->private_data = (void *)session; + mutex_unlock(&pservice->lock); + + pr_debug("dev opened\n"); + vpu_debug_leave(); + return nonseekable_open(inode, filp); +} + +static int vpu_service_release(struct inode *inode, struct file *filp) +{ + struct vpu_subdev_data *data = container_of( + inode->i_cdev, struct vpu_subdev_data, cdev); + struct vpu_service_info *pservice = data->pservice; + int task_running; + struct vpu_session *session = (struct vpu_session *)filp->private_data; + + vpu_debug_enter(); + if (NULL == session) + return -EINVAL; + + task_running = atomic_read(&session->task_running); + if (task_running) { + pr_err("error: session %d still has %d task running when closing\n", + session->pid, task_running); + msleep(50); + } + wake_up(&session->wait); + + mutex_lock(&pservice->lock); + /* remove this filp from the asynchronusly notified filp's */ + list_del_init(&session->list_session); + vpu_service_session_clear(data, session); + kfree(session); + filp->private_data = NULL; + mutex_unlock(&pservice->lock); + + pr_debug("dev closed\n"); + vpu_debug_leave(); + return 0; +} + +static const struct file_operations vpu_service_fops = { + .unlocked_ioctl = vpu_service_ioctl, + .open = vpu_service_open, + .release = vpu_service_release, +#ifdef CONFIG_COMPAT + .compat_ioctl = compat_vpu_service_ioctl, +#endif +}; + +static irqreturn_t vdpu_irq(int irq, void *dev_id); +static irqreturn_t vdpu_isr(int irq, void *dev_id); +static irqreturn_t vepu_irq(int irq, void *dev_id); +static irqreturn_t vepu_isr(int irq, void *dev_id); +static void get_hw_info(struct vpu_subdev_data *data); + +static struct device *rockchip_get_sysmmu_dev(const char *compt) +{ + struct device_node *dn = NULL; + struct platform_device *pd = NULL; + struct device *ret = NULL; + + dn = of_find_compatible_node(NULL, NULL, compt); + if (!dn) { + pr_err("can't find device node %s \r\n", compt); + return NULL; + } + + pd = of_find_device_by_node(dn); + if (!pd) { + pr_err("can't find platform device in device node %s\n", compt); + return NULL; + } + ret = &pd->dev; + + return ret; +} + +#ifdef CONFIG_IOMMU_API +static inline void platform_set_sysmmu(struct device *iommu, + struct device *dev) +{ + dev->archdata.iommu = iommu; +} +#else +static inline void platform_set_sysmmu(struct device *iommu, + struct device *dev) +{ +} +#endif + +int vcodec_sysmmu_fault_hdl(struct device *dev, + enum rk_iommu_inttype itype, + unsigned long pgtable_base, + unsigned long fault_addr, unsigned int status) +{ + struct platform_device *pdev; + struct vpu_service_info *pservice; + struct vpu_subdev_data *data; + + vpu_debug_enter(); + + if (dev == NULL) { + pr_err("invalid NULL dev\n"); + return 0; + } + + pdev = container_of(dev, struct platform_device, dev); + if (pdev == NULL) { + pr_err("invalid NULL platform_device\n"); + return 0; + } + + data = platform_get_drvdata(pdev); + if (data == NULL) { + pr_err("invalid NULL vpu_subdev_data\n"); + return 0; + } + + pservice = data->pservice; + if (pservice == NULL) { + pr_err("invalid NULL vpu_service_info\n"); + return 0; + } + + if (pservice->reg_codec) { + struct vpu_reg *reg = pservice->reg_codec; + struct vcodec_mem_region *mem, *n; + int i = 0; + + pr_err("vcodec, fault addr 0x%08lx\n", fault_addr); + if (!list_empty(®->mem_region_list)) { + list_for_each_entry_safe(mem, n, ®->mem_region_list, + reg_lnk) { + pr_err("vcodec, reg[%02u] mem region [%02d] 0x%lx %lx\n", + mem->reg_idx, i, mem->iova, mem->len); + i++; + } + } else { + pr_err("no memory region mapped\n"); + } + + if (reg->data) { + struct vpu_subdev_data *data = reg->data; + u32 *base = (u32 *)data->dec_dev.regs; + u32 len = data->hw_info->dec_reg_num; + + pr_err("current errror register set:\n"); + + for (i = 0; i < len; i++) + pr_err("reg[%02d] %08x\n", + i, readl_relaxed(base + i)); + } + + pr_alert("vcodec, page fault occur, reset hw\n"); + + /* reg->reg[101] = 1; */ + vpu_reset(data); + } + + return 0; +} + +static int vcodec_subdev_probe(struct platform_device *pdev, + struct vpu_service_info *pservice) +{ + int ret = 0; + struct resource *res = NULL; + u32 ioaddr = 0; + u8 *regs = NULL; + struct vpu_hw_info *hw_info = NULL; + struct device *dev = &pdev->dev; + char *name = (char *)dev_name(dev); + struct device_node *np = pdev->dev.of_node; + struct vpu_subdev_data *data = + devm_kzalloc(dev, sizeof(struct vpu_subdev_data), GFP_KERNEL); + u32 iommu_en = 0; + char mmu_dev_dts_name[40]; + + of_property_read_u32(np, "iommu_enabled", &iommu_en); + + pr_info("probe device %s\n", dev_name(dev)); + + data->pservice = pservice; + data->dev = dev; + + of_property_read_string(np, "name", (const char **)&name); + of_property_read_u32(np, "dev_mode", (u32 *)&data->mode); + + if (pservice->reg_base == 0) { + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + data->regs = devm_ioremap_resource(dev, res); + if (IS_ERR(data->regs)) { + ret = PTR_ERR(data->regs); + goto err; + } + ioaddr = res->start; + } else { + data->regs = pservice->reg_base; + ioaddr = pservice->ioaddr; + } + + clear_bit(MMU_ACTIVATED, &data->state); + vcodec_enter_mode(data); + ret = vpu_service_check_hw(data); + if (ret < 0) { + vpu_err("error: hw info check faild\n"); + goto err; + } + + hw_info = data->hw_info; + regs = (u8 *)data->regs; + + if (hw_info->dec_reg_num) { + data->dec_dev.iosize = hw_info->dec_io_size; + data->dec_dev.regs = (u32 *)(regs + hw_info->dec_offset); + } + + if (hw_info->enc_reg_num) { + data->enc_dev.iosize = hw_info->enc_io_size; + data->enc_dev.regs = (u32 *)(regs + hw_info->enc_offset); + } + + data->reg_size = max(hw_info->dec_io_size, hw_info->enc_io_size); + + data->irq_enc = platform_get_irq_byname(pdev, "irq_enc"); + if (data->irq_enc > 0) { + ret = devm_request_threaded_irq(dev, data->irq_enc, + vepu_irq, vepu_isr, + IRQF_SHARED, dev_name(dev), + (void *)data); + if (ret) { + dev_err(dev, "error: can't request vepu irq %d\n", + data->irq_enc); + goto err; + } + } + data->irq_dec = platform_get_irq_byname(pdev, "irq_dec"); + if (data->irq_dec > 0) { + ret = devm_request_threaded_irq(dev, data->irq_dec, + vdpu_irq, vdpu_isr, + IRQF_SHARED, dev_name(dev), + (void *)data); + if (ret) { + dev_err(dev, "error: can't request vdpu irq %d\n", + data->irq_dec); + goto err; + } + } + atomic_set(&data->dec_dev.irq_count_codec, 0); + atomic_set(&data->dec_dev.irq_count_pp, 0); + atomic_set(&data->enc_dev.irq_count_codec, 0); + atomic_set(&data->enc_dev.irq_count_pp, 0); + + if (iommu_en) { + if (data->mode == VCODEC_RUNNING_MODE_HEVC) + sprintf(mmu_dev_dts_name, + HEVC_IOMMU_COMPATIBLE_NAME); + else if (data->mode == VCODEC_RUNNING_MODE_VPU) + sprintf(mmu_dev_dts_name, + VPU_IOMMU_COMPATIBLE_NAME); + else if (data->mode == VCODEC_RUNNING_MODE_RKVDEC) + sprintf(mmu_dev_dts_name, VDEC_IOMMU_COMPATIBLE_NAME); + else + sprintf(mmu_dev_dts_name, + HEVC_IOMMU_COMPATIBLE_NAME); + + data->mmu_dev = + rockchip_get_sysmmu_dev(mmu_dev_dts_name); + + if (data->mmu_dev) + platform_set_sysmmu(data->mmu_dev, dev); + + rockchip_iovmm_set_fault_handler(dev, vcodec_sysmmu_fault_hdl); + } + + get_hw_info(data); + pservice->auto_freq = true; + + vcodec_exit_mode(data); + /* create device node */ + ret = alloc_chrdev_region(&data->dev_t, 0, 1, name); + if (ret) { + dev_err(dev, "alloc dev_t failed\n"); + goto err; + } + + cdev_init(&data->cdev, &vpu_service_fops); + + data->cdev.owner = THIS_MODULE; + data->cdev.ops = &vpu_service_fops; + + ret = cdev_add(&data->cdev, data->dev_t, 1); + + if (ret) { + dev_err(dev, "add dev_t failed\n"); + goto err; + } + + data->cls = class_create(THIS_MODULE, name); + + if (IS_ERR(data->cls)) { + ret = PTR_ERR(data->cls); + dev_err(dev, "class_create err:%d\n", ret); + goto err; + } + + data->child_dev = device_create(data->cls, dev, + data->dev_t, NULL, name); + + platform_set_drvdata(pdev, data); + + INIT_LIST_HEAD(&data->lnk_service); + list_add_tail(&data->lnk_service, &pservice->subdev_list); + +#ifdef CONFIG_DEBUG_FS + data->debugfs_dir = vcodec_debugfs_create_device_dir(name, parent); + if (!IS_ERR_OR_NULL(data->debugfs_dir)) + data->debugfs_file_regs = + debugfs_create_file("regs", 0664, data->debugfs_dir, + data, &debug_vcodec_fops); + else + vpu_err("create debugfs dir %s failed\n", name); +#endif + return 0; +err: + if (data->child_dev) { + device_destroy(data->cls, data->dev_t); + cdev_del(&data->cdev); + unregister_chrdev_region(data->dev_t, 1); + } + + if (data->cls) + class_destroy(data->cls); + return -1; +} + +static void vcodec_subdev_remove(struct vpu_subdev_data *data) +{ + struct vpu_service_info *pservice = data->pservice; + + mutex_lock(&pservice->lock); + cancel_delayed_work_sync(&pservice->power_off_work); + vpu_service_power_off(pservice); + mutex_unlock(&pservice->lock); + + device_destroy(data->cls, data->dev_t); + class_destroy(data->cls); + cdev_del(&data->cdev); + unregister_chrdev_region(data->dev_t, 1); + +#ifdef CONFIG_DEBUG_FS + if (!IS_ERR_OR_NULL(data->debugfs_dir)) + debugfs_remove_recursive(data->debugfs_dir); +#endif +} + +static void vcodec_read_property(struct device_node *np, + struct vpu_service_info *pservice) +{ + pservice->mode_bit = 0; + pservice->mode_ctrl = 0; + pservice->subcnt = 0; + pservice->grf_base = NULL; + + of_property_read_u32(np, "subcnt", &pservice->subcnt); + + if (pservice->subcnt > 1) { + of_property_read_u32(np, "mode_bit", &pservice->mode_bit); + of_property_read_u32(np, "mode_ctrl", &pservice->mode_ctrl); + } +#ifdef CONFIG_MFD_SYSCON + pservice->grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf"); + if (IS_ERR_OR_NULL(pservice->grf)) { + pservice->grf = NULL; +#ifdef CONFIG_ARM + pservice->grf_base = RK_GRF_VIRT; +#else + vpu_err("can't find vpu grf property\n"); + return; +#endif + } +#else +#ifdef CONFIG_ARM + pservice->grf_base = RK_GRF_VIRT; +#else + vpu_err("can't find vpu grf property\n"); + return; +#endif +#endif + +#ifdef CONFIG_RESET_CONTROLLER + pservice->rst_a = devm_reset_control_get(pservice->dev, "video_a"); + pservice->rst_h = devm_reset_control_get(pservice->dev, "video_h"); + pservice->rst_v = devm_reset_control_get(pservice->dev, "video"); + + if (IS_ERR_OR_NULL(pservice->rst_a)) { + pr_warn("No aclk reset resource define\n"); + pservice->rst_a = NULL; + } + + if (IS_ERR_OR_NULL(pservice->rst_h)) { + pr_warn("No hclk reset resource define\n"); + pservice->rst_h = NULL; + } + + if (IS_ERR_OR_NULL(pservice->rst_v)) { + pr_warn("No core reset resource define\n"); + pservice->rst_v = NULL; + } +#endif + + of_property_read_string(np, "name", (const char **)&pservice->name); +} + +static void vcodec_init_drvdata(struct vpu_service_info *pservice) +{ + pservice->dev_id = VCODEC_DEVICE_ID_VPU; + pservice->curr_mode = -1; + + wake_lock_init(&pservice->wake_lock, WAKE_LOCK_SUSPEND, "vpu"); + INIT_LIST_HEAD(&pservice->waiting); + INIT_LIST_HEAD(&pservice->running); + mutex_init(&pservice->lock); + + INIT_LIST_HEAD(&pservice->done); + INIT_LIST_HEAD(&pservice->session); + INIT_LIST_HEAD(&pservice->subdev_list); + + pservice->reg_pproc = NULL; + atomic_set(&pservice->total_running, 0); + atomic_set(&pservice->enabled, 0); + atomic_set(&pservice->power_on_cnt, 0); + atomic_set(&pservice->power_off_cnt, 0); + atomic_set(&pservice->reset_request, 0); + + INIT_DELAYED_WORK(&pservice->power_off_work, vpu_power_off_work); + + pservice->ion_client = rockchip_ion_client_create("vpu"); + if (IS_ERR(pservice->ion_client)) { + vpu_err("failed to create ion client for vcodec ret %ld\n", + PTR_ERR(pservice->ion_client)); + } else { + vpu_debug(DEBUG_IOMMU, "vcodec ion client create success!\n"); + } +} + +static int vcodec_probe(struct platform_device *pdev) +{ + int i; + int ret = 0; + struct resource *res = NULL; + struct device *dev = &pdev->dev; + struct device_node *np = pdev->dev.of_node; + struct vpu_service_info *pservice = + devm_kzalloc(dev, sizeof(struct vpu_service_info), GFP_KERNEL); + + pservice->dev = dev; + + vcodec_read_property(np, pservice); + vcodec_init_drvdata(pservice); + + if (strncmp(pservice->name, "hevc_service", 12) == 0) + pservice->dev_id = VCODEC_DEVICE_ID_HEVC; + else if (strncmp(pservice->name, "vpu_service", 11) == 0) + pservice->dev_id = VCODEC_DEVICE_ID_VPU; + else if (strncmp(pservice->name, "rkvdec", 6) == 0) + pservice->dev_id = VCODEC_DEVICE_ID_RKVDEC; + else + pservice->dev_id = VCODEC_DEVICE_ID_COMBO; + + if (0 > vpu_get_clk(pservice)) + goto err; + + vpu_service_power_on(pservice); + + if (of_property_read_bool(np, "reg")) { + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + + pservice->reg_base = devm_ioremap_resource(pservice->dev, res); + if (IS_ERR(pservice->reg_base)) { + vpu_err("ioremap registers base failed\n"); + ret = PTR_ERR(pservice->reg_base); + goto err; + } + pservice->ioaddr = res->start; + } else { + pservice->reg_base = 0; + } + + if (of_property_read_bool(np, "subcnt")) { + for (i = 0; i < pservice->subcnt; i++) { + struct device_node *sub_np; + struct platform_device *sub_pdev; + + sub_np = of_parse_phandle(np, "rockchip,sub", i); + sub_pdev = of_find_device_by_node(sub_np); + + vcodec_subdev_probe(sub_pdev, pservice); + } + } else { + vcodec_subdev_probe(pdev, pservice); + } + + vpu_service_power_off(pservice); + + pr_info("init success\n"); + + return 0; + +err: + pr_info("init failed\n"); + vpu_service_power_off(pservice); + vpu_put_clk(pservice); + wake_lock_destroy(&pservice->wake_lock); + + return ret; +} + +static int vcodec_remove(struct platform_device *pdev) +{ + struct vpu_subdev_data *data = platform_get_drvdata(pdev); + + vcodec_subdev_remove(data); + return 0; +} + +#if defined(CONFIG_OF) +static const struct of_device_id vcodec_service_dt_ids[] = { + {.compatible = "rockchip,vpu_service",}, + {.compatible = "rockchip,hevc_service",}, + {.compatible = "rockchip,vpu_combo",}, + {.compatible = "rockchip,rkvdec",}, + {}, +}; +#endif + +static struct platform_driver vcodec_driver = { + .probe = vcodec_probe, + .remove = vcodec_remove, + .driver = { + .name = "vcodec", + .owner = THIS_MODULE, +#if defined(CONFIG_OF) + .of_match_table = of_match_ptr(vcodec_service_dt_ids), +#endif + }, +}; + +static void get_hw_info(struct vpu_subdev_data *data) +{ + struct vpu_service_info *pservice = data->pservice; + struct vpu_dec_config *dec = &pservice->dec_config; + struct vpu_enc_config *enc = &pservice->enc_config; + + if (cpu_is_rk2928() || cpu_is_rk3036() || + cpu_is_rk30xx() || cpu_is_rk312x() || + cpu_is_rk3188()) + dec->max_dec_pic_width = 1920; + else + dec->max_dec_pic_width = 4096; + + if (data->mode == VCODEC_RUNNING_MODE_VPU) { + dec->h264_support = 3; + dec->jpeg_support = 1; + dec->mpeg4_support = 2; + dec->vc1_support = 3; + dec->mpeg2_support = 1; + dec->pp_support = 1; + dec->sorenson_support = 1; + dec->ref_buf_support = 3; + dec->vp6_support = 1; + dec->vp7_support = 1; + dec->vp8_support = 1; + dec->avs_support = 1; + dec->jpeg_ext_support = 0; + dec->custom_mpeg4_support = 1; + dec->reserve = 0; + dec->mvc_support = 1; + + if (!cpu_is_rk3036()) { + u32 config_reg = readl_relaxed(data->enc_dev.regs + 63); + + enc->max_encoded_width = config_reg & ((1 << 11) - 1); + enc->h264_enabled = 1; + enc->mpeg4_enabled = (config_reg >> 26) & 1; + enc->jpeg_enabled = 1; + enc->vs_enabled = (config_reg >> 24) & 1; + enc->rgb_enabled = (config_reg >> 28) & 1; + enc->reg_size = data->reg_size; + enc->reserv[0] = 0; + enc->reserv[1] = 0; + } + + pservice->auto_freq = true; + vpu_debug(DEBUG_EXTRA_INFO, "vpu_service set to auto frequency mode\n"); + atomic_set(&pservice->freq_status, VPU_FREQ_BUT); + + pservice->bug_dec_addr = cpu_is_rk30xx(); + } else if (data->mode == VCODEC_RUNNING_MODE_RKVDEC) { + pservice->auto_freq = true; + atomic_set(&pservice->freq_status, VPU_FREQ_BUT); + } else { + /* disable frequency switch in hevc.*/ + pservice->auto_freq = false; + } +} + +static bool check_irq_err(struct vpu_task_info *task, u32 irq_status) +{ + vpu_debug(DEBUG_IRQ_CHECK, "task %s status %08x mask %08x\n", + task->name, irq_status, task->error_mask); + + return (task->error_mask & irq_status) ? true : false; +} + +static irqreturn_t vdpu_irq(int irq, void *dev_id) +{ + struct vpu_subdev_data *data = (struct vpu_subdev_data *)dev_id; + struct vpu_service_info *pservice = data->pservice; + struct vpu_task_info *task = NULL; + struct vpu_device *dev = &data->dec_dev; + u32 hw_id = data->hw_info->hw_id; + u32 raw_status; + u32 dec_status; + + task = &data->task_info[TASK_DEC]; + + raw_status = readl_relaxed(dev->regs + task->reg_irq); + dec_status = raw_status; + + vpu_debug(DEBUG_TASK_INFO, "vdpu_irq reg %d status %x mask: irq %x ready %x error %0x\n", + task->reg_irq, dec_status, + task->irq_mask, task->ready_mask, task->error_mask); + + if (dec_status & task->irq_mask) { + time_record(task, 1); + vpu_debug(DEBUG_IRQ_STATUS, "vdpu_irq dec status %08x\n", + dec_status); + if ((dec_status & 0x40001) == 0x40001) { + do { + dec_status = + readl_relaxed(dev->regs + + task->reg_irq); + } while ((dec_status & 0x40001) == 0x40001); + } + + if (check_irq_err(task, dec_status)) + atomic_add(1, &pservice->reset_request); + + writel_relaxed(0, dev->regs + task->reg_irq); + + /* + * NOTE: rkvdec need to reset after each task to avoid timeout + * error on H.264 switch to H.265 + */ + if (data->mode == VCODEC_RUNNING_MODE_RKVDEC) + writel(0x100000, dev->regs + task->reg_irq); + + /* set clock gating to save power */ + writel(task->gating_mask, dev->regs + task->reg_irq); + + atomic_add(1, &dev->irq_count_codec); + time_diff(task); + } + + task = &data->task_info[TASK_PP]; + if (hw_id != HEVC_ID && hw_id != RKV_DEC_ID) { + u32 pp_status = readl_relaxed(dev->regs + task->irq_mask); + + if (pp_status & task->irq_mask) { + time_record(task, 1); + vpu_debug(DEBUG_IRQ_STATUS, "vdpu_irq pp status %08x\n", + pp_status); + + if (check_irq_err(task, dec_status)) + atomic_add(1, &pservice->reset_request); + + /* clear pp IRQ */ + writel_relaxed(pp_status & (~task->reg_irq), + dev->regs + task->irq_mask); + atomic_add(1, &dev->irq_count_pp); + time_diff(task); + } + } + + pservice->irq_status = raw_status; + + if (atomic_read(&dev->irq_count_pp) || + atomic_read(&dev->irq_count_codec)) + return IRQ_WAKE_THREAD; + else + return IRQ_NONE; +} + +static irqreturn_t vdpu_isr(int irq, void *dev_id) +{ + struct vpu_subdev_data *data = (struct vpu_subdev_data *)dev_id; + struct vpu_service_info *pservice = data->pservice; + struct vpu_device *dev = &data->dec_dev; + + mutex_lock(&pservice->lock); + if (atomic_read(&dev->irq_count_codec)) { + atomic_sub(1, &dev->irq_count_codec); + if (pservice->reg_codec == NULL) { + vpu_err("error: dec isr with no task waiting\n"); + } else { + reg_from_run_to_done(data, pservice->reg_codec); + /* avoid vpu timeout and can't recover problem */ + VDPU_SOFT_RESET(data->regs); + } + } + + if (atomic_read(&dev->irq_count_pp)) { + atomic_sub(1, &dev->irq_count_pp); + if (pservice->reg_pproc == NULL) + vpu_err("error: pp isr with no task waiting\n"); + else + reg_from_run_to_done(data, pservice->reg_pproc); + } + try_set_reg(data); + mutex_unlock(&pservice->lock); + return IRQ_HANDLED; +} + +static irqreturn_t vepu_irq(int irq, void *dev_id) +{ + struct vpu_subdev_data *data = (struct vpu_subdev_data *)dev_id; + struct vpu_service_info *pservice = data->pservice; + struct vpu_task_info *task = &data->task_info[TASK_ENC]; + struct vpu_device *dev = &data->enc_dev; + u32 irq_status; + + irq_status = readl_relaxed(dev->regs + task->reg_irq); + + vpu_debug(DEBUG_TASK_INFO, "vepu_irq reg %d status %x mask: irq %x ready %x error %0x\n", + task->reg_irq, irq_status, + task->irq_mask, task->ready_mask, task->error_mask); + + vpu_debug(DEBUG_IRQ_STATUS, "vepu_irq enc status %08x\n", irq_status); + + if (likely(irq_status & task->irq_mask)) { + time_record(task, 1); + + if (check_irq_err(task, irq_status)) + atomic_add(1, &pservice->reset_request); + + /* clear enc IRQ */ + writel_relaxed(irq_status & (~task->irq_mask), + dev->regs + task->reg_irq); + + atomic_add(1, &dev->irq_count_codec); + time_diff(task); + } + + pservice->irq_status = irq_status; + + if (atomic_read(&dev->irq_count_codec)) + return IRQ_WAKE_THREAD; + else + return IRQ_NONE; +} + +static irqreturn_t vepu_isr(int irq, void *dev_id) +{ + struct vpu_subdev_data *data = (struct vpu_subdev_data *)dev_id; + struct vpu_service_info *pservice = data->pservice; + struct vpu_device *dev = &data->enc_dev; + + mutex_lock(&pservice->lock); + if (atomic_read(&dev->irq_count_codec)) { + atomic_sub(1, &dev->irq_count_codec); + if (NULL == pservice->reg_codec) + vpu_err("error: enc isr with no task waiting\n"); + else + reg_from_run_to_done(data, pservice->reg_codec); + } + try_set_reg(data); + mutex_unlock(&pservice->lock); + return IRQ_HANDLED; +} + +static int __init vcodec_service_init(void) +{ + int ret = platform_driver_register(&vcodec_driver); + + if (ret) { + vpu_err("Platform device register failed (%d).\n", ret); + return ret; + } + +#ifdef CONFIG_DEBUG_FS + vcodec_debugfs_init(); +#endif + + return ret; +} + +static void __exit vcodec_service_exit(void) +{ +#ifdef CONFIG_DEBUG_FS + vcodec_debugfs_exit(); +#endif + + platform_driver_unregister(&vcodec_driver); +} + +module_init(vcodec_service_init); +module_exit(vcodec_service_exit); +MODULE_LICENSE("Proprietary"); + +#ifdef CONFIG_DEBUG_FS +#include + +static int vcodec_debugfs_init(void) +{ + parent = debugfs_create_dir("vcodec", NULL); + if (!parent) + return -1; + + return 0; +} + +static void vcodec_debugfs_exit(void) +{ + debugfs_remove(parent); +} + +static struct dentry *vcodec_debugfs_create_device_dir( + char *dirname, struct dentry *parent) +{ + return debugfs_create_dir(dirname, parent); +} + +static int debug_vcodec_show(struct seq_file *s, void *unused) +{ + struct vpu_subdev_data *data = s->private; + struct vpu_service_info *pservice = data->pservice; + unsigned int i, n; + struct vpu_reg *reg, *reg_tmp; + struct vpu_session *session, *session_tmp; + + mutex_lock(&pservice->lock); + vpu_service_power_on(pservice); + if (data->hw_info->hw_id != HEVC_ID) { + seq_puts(s, "\nENC Registers:\n"); + n = data->enc_dev.iosize >> 2; + + for (i = 0; i < n; i++) + seq_printf(s, "\tswreg%d = %08X\n", i, + readl_relaxed(data->enc_dev.regs + i)); + } + + seq_puts(s, "\nDEC Registers:\n"); + + n = data->dec_dev.iosize >> 2; + for (i = 0; i < n; i++) + seq_printf(s, "\tswreg%d = %08X\n", i, + readl_relaxed(data->dec_dev.regs + i)); + + seq_puts(s, "\nvpu service status:\n"); + + list_for_each_entry_safe(session, session_tmp, + &pservice->session, list_session) { + seq_printf(s, "session pid %d type %d:\n", + session->pid, session->type); + + list_for_each_entry_safe(reg, reg_tmp, + &session->waiting, session_link) { + seq_printf(s, "waiting register set %p\n", reg); + } + list_for_each_entry_safe(reg, reg_tmp, + &session->running, session_link) { + seq_printf(s, "running register set %p\n", reg); + } + list_for_each_entry_safe(reg, reg_tmp, + &session->done, session_link) { + seq_printf(s, "done register set %p\n", reg); + } + } + + seq_printf(s, "\npower counter: on %d off %d\n", + atomic_read(&pservice->power_on_cnt), + atomic_read(&pservice->power_off_cnt)); + + mutex_unlock(&pservice->lock); + vpu_service_power_off(pservice); + + return 0; +} + +static int debug_vcodec_open(struct inode *inode, struct file *file) +{ + return single_open(file, debug_vcodec_show, inode->i_private); +} + +#endif + diff --git a/drivers/video/rockchip/vcodec/vcodec_service.h b/drivers/video/rockchip/vcodec/vcodec_service.h new file mode 100644 index 000000000000..0b9298cfcec9 --- /dev/null +++ b/drivers/video/rockchip/vcodec/vcodec_service.h @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2015 Fuzhou Rockchip Electronics Co., Ltd + * author: chenhengming chm@rock-chips.com + * Alpha Lin, alpha.lin@rock-chips.com + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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 __ARCH_ARM_MACH_RK29_VCODEC_SERVICE_H +#define __ARCH_ARM_MACH_RK29_VCODEC_SERVICE_H + +#include /* needed for the _IOW etc stuff used later */ + +/* + * Ioctl definitions + */ + +/* Use 'k' as magic number */ +#define VPU_IOC_MAGIC 'l' + +#define VPU_IOC_SET_CLIENT_TYPE _IOW(VPU_IOC_MAGIC, 1, unsigned long) +#define VPU_IOC_GET_HW_FUSE_STATUS _IOW(VPU_IOC_MAGIC, 2, unsigned long) + +#define VPU_IOC_SET_REG _IOW(VPU_IOC_MAGIC, 3, unsigned long) +#define VPU_IOC_GET_REG _IOW(VPU_IOC_MAGIC, 4, unsigned long) + +#define VPU_IOC_PROBE_IOMMU_STATUS _IOR(VPU_IOC_MAGIC, 5, unsigned long) + +#ifdef CONFIG_COMPAT +#define COMPAT_VPU_IOC_SET_CLIENT_TYPE _IOW(VPU_IOC_MAGIC, 1, u32) +#define COMPAT_VPU_IOC_GET_HW_FUSE_STATUS _IOW(VPU_IOC_MAGIC, 2, u32) + +#define COMPAT_VPU_IOC_SET_REG _IOW(VPU_IOC_MAGIC, 3, u32) +#define COMPAT_VPU_IOC_GET_REG _IOW(VPU_IOC_MAGIC, 4, u32) + +#define COMPAT_VPU_IOC_PROBE_IOMMU_STATUS _IOR(VPU_IOC_MAGIC, 5, u32) +#endif + +enum VPU_CLIENT_TYPE { + VPU_ENC = 0x0, + VPU_DEC = 0x1, + VPU_PP = 0x2, + VPU_DEC_PP = 0x3, + VPU_TYPE_BUTT, +}; + +/* Hardware decoder configuration description */ +struct vpu_dec_config { + /* Maximum video decoding width supported */ + u32 max_dec_pic_width; + /* Maximum output width of Post-Processor */ + u32 max_pp_out_pic_width; + /* HW supports h.264 */ + u32 h264_support; + /* HW supports JPEG */ + u32 jpeg_support; + /* HW supports MPEG-4 */ + u32 mpeg4_support; + /* HW supports custom MPEG-4 features */ + u32 custom_mpeg4_support; + /* HW supports VC-1 Simple */ + u32 vc1_support; + /* HW supports MPEG-2 */ + u32 mpeg2_support; + /* HW supports post-processor */ + u32 pp_support; + /* HW post-processor functions bitmask */ + u32 pp_config; + /* HW supports Sorenson Spark */ + u32 sorenson_support; + /* HW supports reference picture buffering */ + u32 ref_buf_support; + /* HW supports VP6 */ + u32 vp6_support; + /* HW supports VP7 */ + u32 vp7_support; + /* HW supports VP8 */ + u32 vp8_support; + /* HW supports AVS */ + u32 avs_support; + /* HW supports JPEG extensions */ + u32 jpeg_ext_support; + u32 reserve; + /* HW supports H264 MVC extension */ + u32 mvc_support; +}; + +/* Hardware encoder configuration description */ +struct vpu_enc_config { + /* Maximum supported width for video encoding (not JPEG) */ + u32 max_encoded_width; + /* HW supports H.264 */ + u32 h264_enabled; + /* HW supports JPEG */ + u32 jpeg_enabled; + /* HW supports MPEG-4 */ + u32 mpeg4_enabled; + /* HW supports video stabilization */ + u32 vs_enabled; + /* HW supports RGB input */ + u32 rgb_enabled; + u32 reg_size; + u32 reserv[2]; +}; + +#endif + -- 2.34.1