From ccef10af4a2ef3066f4bdef01e84181b63a265c4 Mon Sep 17 00:00:00 2001 From: Li Huang Date: Thu, 17 Feb 2022 20:16:59 +0800 Subject: [PATCH] video: rockchip: rve: init ver 1.0.0 Signed-off-by: Li Huang Change-Id: I21d912272a7a0789c86fee033fa74cb01980f477 --- drivers/video/rockchip/Kconfig | 1 + drivers/video/rockchip/Makefile | 1 + drivers/video/rockchip/rve/Kconfig | 29 + drivers/video/rockchip/rve/Makefile | 9 + drivers/video/rockchip/rve/include/rve.h | 71 ++ .../video/rockchip/rve/include/rve_debugger.h | 132 +++ drivers/video/rockchip/rve/include/rve_drv.h | 290 ++++++ .../video/rockchip/rve/include/rve_fence.h | 32 + drivers/video/rockchip/rve/include/rve_job.h | 46 + drivers/video/rockchip/rve/include/rve_reg.h | 83 ++ drivers/video/rockchip/rve/rve_debugger.c | 553 +++++++++++ drivers/video/rockchip/rve/rve_drv.c | 740 ++++++++++++++ drivers/video/rockchip/rve/rve_fence.c | 136 +++ drivers/video/rockchip/rve/rve_job.c | 923 ++++++++++++++++++ drivers/video/rockchip/rve/rve_reg.c | 247 +++++ 15 files changed, 3293 insertions(+) create mode 100644 drivers/video/rockchip/rve/Kconfig create mode 100644 drivers/video/rockchip/rve/Makefile create mode 100644 drivers/video/rockchip/rve/include/rve.h create mode 100644 drivers/video/rockchip/rve/include/rve_debugger.h create mode 100644 drivers/video/rockchip/rve/include/rve_drv.h create mode 100644 drivers/video/rockchip/rve/include/rve_fence.h create mode 100644 drivers/video/rockchip/rve/include/rve_job.h create mode 100644 drivers/video/rockchip/rve/include/rve_reg.h create mode 100644 drivers/video/rockchip/rve/rve_debugger.c create mode 100644 drivers/video/rockchip/rve/rve_drv.c create mode 100644 drivers/video/rockchip/rve/rve_fence.c create mode 100644 drivers/video/rockchip/rve/rve_job.c create mode 100644 drivers/video/rockchip/rve/rve_reg.c diff --git a/drivers/video/rockchip/Kconfig b/drivers/video/rockchip/Kconfig index e8f18642e999..5779065b968a 100644 --- a/drivers/video/rockchip/Kconfig +++ b/drivers/video/rockchip/Kconfig @@ -2,5 +2,6 @@ source "drivers/video/rockchip/rga/Kconfig" source "drivers/video/rockchip/rga2/Kconfig" source "drivers/video/rockchip/rga3/Kconfig" +source "drivers/video/rockchip/rve/Kconfig" source "drivers/video/rockchip/iep/Kconfig" source "drivers/video/rockchip/mpp/Kconfig" diff --git a/drivers/video/rockchip/Makefile b/drivers/video/rockchip/Makefile index 6305e2326ef3..f5801d44fb33 100644 --- a/drivers/video/rockchip/Makefile +++ b/drivers/video/rockchip/Makefile @@ -2,5 +2,6 @@ obj-$(CONFIG_ROCKCHIP_RGA) += rga/ obj-$(CONFIG_ROCKCHIP_RGA2) += rga2/ obj-$(CONFIG_ROCKCHIP_MULTI_RGA) += rga3/ +obj-$(CONFIG_ROCKCHIP_RVE) += rve/ obj-$(CONFIG_IEP) += iep/ obj-$(CONFIG_ROCKCHIP_MPP_SERVICE) += mpp/ diff --git a/drivers/video/rockchip/rve/Kconfig b/drivers/video/rockchip/rve/Kconfig new file mode 100644 index 000000000000..d28b9d0599cf --- /dev/null +++ b/drivers/video/rockchip/rve/Kconfig @@ -0,0 +1,29 @@ +# SPDX-License-Identifier: GPL-2.0 +menuconfig ROCKCHIP_RVE + tristate "RVE" + depends on ARCH_ROCKCHIP + help + RVE module. + +if ROCKCHIP_RVE + +config ROCKCHIP_RVE_PROC_FS + bool "Enable RVE procfs" + select ROCKCHIP_RVE_DEBUGGER + depends on PROC_FS + help + Enable procfs to debug RVE driver. + +config ROCKCHIP_RVE_DEBUG_FS + bool "Enable RVE debugfs" + select ROCKCHIP_RVE_DEBUGGER + depends on DEBUG_FS + help + Enable debugfs to debug RVE driver. + +config ROCKCHIP_RVE_DEBUGGER + bool + help + Enabling the debugger of RVE, you can use procfs and debugfs for debugging. + +endif diff --git a/drivers/video/rockchip/rve/Makefile b/drivers/video/rockchip/rve/Makefile new file mode 100644 index 000000000000..e475b7717510 --- /dev/null +++ b/drivers/video/rockchip/rve/Makefile @@ -0,0 +1,9 @@ +# SPDX-License-Identifier: GPL-2.0 + +ccflags-y += -I$(srctree)/$(src)/include + +rve-y := rve_drv.o rve_job.o rve_reg.o +rve-$(CONFIG_ROCKCHIP_RVE_DEBUGGER) += rve_debugger.o +rve-$(CONFIG_SYNC_FILE) += rve_fence.o + +obj-$(CONFIG_ROCKCHIP_RVE) += rve.o diff --git a/drivers/video/rockchip/rve/include/rve.h b/drivers/video/rockchip/rve/include/rve.h new file mode 100644 index 000000000000..0667801264e4 --- /dev/null +++ b/drivers/video/rockchip/rve/include/rve.h @@ -0,0 +1,71 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) Rockchip Electronics Co., Ltd. + * + * Author: Huang Lee + */ +#ifndef _RVE_DRIVER_H_ +#define _RVE_DRIVER_H_ + +#include +#include + +/* Use 'r' as magic number */ +#define RVE_IOC_MAGIC 'r' +#define RVE_IOW(nr, type) _IOW(RVE_IOC_MAGIC, nr, type) +#define RVE_IOR(nr, type) _IOR(RVE_IOC_MAGIC, nr, type) +#define RVE_IOWR(nr, type) _IOWR(RVE_IOC_MAGIC, nr, type) + +#define RVE_IOC_GET_VER RVE_IOR(0x1, struct rve_version_t) +#define RVE_IOC_GET_HW_VER RVE_IOR(0x2, struct rve_hw_versions_t) +#define RVE_IOC_IMPORT_BUFFER RVE_IOWR(0x3, struct rve_buffer_pool) +#define RVE_IOC_RELEASE_BUFFER RVE_IOW(0x4, struct rve_buffer_pool) + +#define RVE_IOC_START_CONFIG RVE_IOR(0x5, uint32_t) +#define RVE_IOC_END_CONFIG RVE_IOWR(0x6, struct rve_user_ctx_t) +#define RVE_IOC_CMD_CONFIG RVE_IOWR(0x7, struct rve_user_ctx_t) +#define RVE_IOC_CANCEL_CONFIG RVE_IOWR(0x8, uint32_t) + +#define RVE_CMD_NUM_MAX 10 + +#define RVE_BUFFER_POOL_SIZE_MAX 40 + +enum rve_memory_type { + RVE_DMA_BUFFER = 0, + RVE_VIRTUAL_ADDRESS, + RVE_PHYSICAL_ADDRESS +}; + +#define RVE_SCHED_PRIORITY_DEFAULT 0 +#define RVE_SCHED_PRIORITY_MAX 6 + +#define RVE_VERSION_SIZE 16 +#define RVE_HW_SIZE 5 + +struct rve_version_t { + uint32_t major; + uint32_t minor; + uint32_t revision; + uint32_t prod_num; + uint8_t str[RVE_VERSION_SIZE]; +}; + +struct rve_hw_versions_t { + struct rve_version_t version[RVE_HW_SIZE]; + uint32_t size; +}; + +struct rve_user_ctx_t { + uint32_t header; + uint64_t regcmd_data; + int32_t in_fence_fd; + int32_t out_fence_fd; + int32_t cmd_num; + uint32_t id; + uint8_t priority; + uint32_t sync_mode; + + uint32_t reserve[32]; +}; + +#endif /*_RVE_DRIVER_H_*/ diff --git a/drivers/video/rockchip/rve/include/rve_debugger.h b/drivers/video/rockchip/rve/include/rve_debugger.h new file mode 100644 index 000000000000..8dbb46850532 --- /dev/null +++ b/drivers/video/rockchip/rve/include/rve_debugger.h @@ -0,0 +1,132 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) Rockchip Electronics Co., Ltd. + * + * Author: + * Cerf Yu + * Huang Lee + */ + +#ifndef _RVE_DEBUGGER_H_ +#define _RVE_DEBUGGER_H_ + +#ifdef CONFIG_ROCKCHIP_RVE_DEBUGGER + +extern int RVE_DEBUG_MONITOR; +extern int RVE_DEBUG_REG; +extern int RVE_DEBUG_MSG; +extern int RVE_DEBUG_TIME; +extern int RVE_DEBUG_CHECK_MODE; +extern int RVE_DEBUG_NONUSE; +extern int RVE_DEBUG_INT_FLAG; + +#define DEBUGGER_EN(name) (unlikely(RVE_DEBUG_##name ? true : false)) + +/* + * struct rve_debugger - RVE debugger information + * + * This structure represents a debugger to be created by the rve driver + * or core. + */ +struct rve_debugger { +#ifdef CONFIG_ROCKCHIP_RVE_DEBUG_FS + /* Directory of debugfs file */ + struct dentry *debugfs_dir; + struct list_head debugfs_entry_list; + struct mutex debugfs_lock; +#endif + +#ifdef CONFIG_ROCKCHIP_RVE_PROC_FS + /* Directory of procfs file */ + struct proc_dir_entry *procfs_dir; + struct list_head procfs_entry_list; + struct mutex procfs_lock; +#endif +}; + +/* + * struct rve_debugger_list - debugfs/procfs info list entry + * + * This structure represents a debugfs/procfs file to be created by the rve + * driver or core. + */ +struct rve_debugger_list { + /* File name */ + const char *name; + /* + * Show callback. &seq_file->private will be set to the &struct + * rve_debugger_node corresponding to the instance of this info + * on a given &struct rve_debugger. + */ + int (*show)(struct seq_file *seq, void *data); + /* + * Write callback. &seq_file->private will be set to the &struct + * rve_debugger_node corresponding to the instance of this info + * on a given &struct rve_debugger. + */ + ssize_t (*write)(struct file *file, const char __user *ubuf, + size_t len, loff_t *offp); + /* Procfs/Debugfs private data. */ + void *data; +}; + +/* + * struct rve_debugger_node - Nodes for debugfs/procfs + * + * This structure represents each instance of procfs/debugfs created from the + * template. + */ +struct rve_debugger_node { + struct rve_debugger *debugger; + + /* template for this node. */ + const struct rve_debugger_list *info_ent; + + /* Each Procfs/Debugfs file. */ +#ifdef CONFIG_ROCKCHIP_RVE_DEBUG_FS + struct dentry *dent; +#endif + +#ifdef CONFIG_ROCKCHIP_RVE_PROC_FS + struct proc_dir_entry *pent; +#endif + + struct list_head list; +}; + +#ifdef CONFIG_ROCKCHIP_RVE_DEBUG_FS +int rve_debugfs_init(void); +int rve_debugfs_remove(void); +#else +static inline int rve_debugfs_remove(void) +{ + return 0; +} +static inline int rve_debugfs_init(void) +{ + return 0; +} +#endif /* #ifdef CONFIG_ROCKCHIP_RVE_DEBUG_FS */ + +#ifdef CONFIG_ROCKCHIP_RVE_PROC_FS +int rve_procfs_remove(void); +int rve_procfs_init(void); +#else +static inline int rve_procfs_remove(void) +{ + return 0; +} +static inline int rve_procfs_init(void) +{ + return 0; +} +#endif /* #ifdef CONFIG_ROCKCHIP_RVE_PROC_FS */ + +#else + +#define DEBUGGER_EN(name) (unlikely(false)) + +#endif /* #ifdef CONFIG_ROCKCHIP_RVE_DEBUGGER */ + +#endif /* #ifndef _RVE_DEBUGGER_H_ */ + diff --git a/drivers/video/rockchip/rve/include/rve_drv.h b/drivers/video/rockchip/rve/include/rve_drv.h new file mode 100644 index 000000000000..a8d818f43bdd --- /dev/null +++ b/drivers/video/rockchip/rve/include/rve_drv.h @@ -0,0 +1,290 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) Rockchip Electronics Co., Ltd. + * + * Author: Huang Lee + */ + +#ifndef __LINUX_RVE_DRV_H_ +#define __LINUX_RVE_DRV_H_ + +#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 +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include "rve_debugger.h" +#include "rve.h" + +#define RVE_PD_AWAYS_ON 1 + +/* sample interval: 1000ms */ +#define RVE_LOAD_INTERVAL 1000000000 + +/* Driver information */ +#define DRIVER_DESC "RVE Device Driver" +#define DRIVER_NAME "rve" + +#define STR_HELPER(x) #x +#define STR(x) STR_HELPER(x) + +#define RVE_MAJOR_VERSION_MASK (0x0000FF00) +#define RVE_MINOR_VERSION_MASK (0x000000FF) +#define RVE_PROD_NUM_MASK (0xFFFF0000) + +#define DRIVER_MAJOR_VERSION 1 +#define DRIVER_MINOR_VERSION 0 +#define DRIVER_REVISION_VERSION 0 + +#define DRIVER_VERSION (STR(DRIVER_MAJOR_VERSION) "." STR(DRIVER_MINOR_VERSION) \ + "." STR(DRIVER_REVISION_VERSION)) + +/* time limit */ +#define RVE_ASYNC_TIMEOUT_DELAY 500 +#define RVE_SYNC_TIMEOUT_DELAY HZ +#define RVE_RESET_TIMEOUT 10000 + +#define RVE_BUFFER_POOL_MAX_SIZE 64 +#define RVE_MAX_SCHEDULER 1 + +#define RVE_MAX_BUS_CLK 10 + +extern struct rve_drvdata_t *rve_drvdata; + +enum { + RVE_SCHEDULER_CORE0 = 1, + RVE_NONE_CORE = 0, +}; + +enum { + RVE_CMD_SLAVE = 1, + RVE_CMD_MASTER = 2, +}; + +struct rve_fence_context { + unsigned int context; + unsigned int seqno; + spinlock_t spinlock; +}; + +struct rve_fence_waiter { + /* Base sync driver waiter structure */ + struct dma_fence_cb waiter; + + struct rve_job *job; +}; + +struct rve_scheduler_t; +struct rve_internal_ctx_t; + +struct rve_job { + struct list_head head; + struct rve_scheduler_t *scheduler; + + struct rve_cmd_reg_array_t *regcmd_data; + + struct rve_internal_ctx_t *ctx; + + /* for rve virtual_address */ + struct mm_struct *mm; + + struct dma_fence *out_fence; + struct dma_fence *in_fence; + spinlock_t fence_lock; + ktime_t timestamp; + ktime_t hw_running_time; + ktime_t hw_recoder_time; + unsigned int flags; + + int priority; + int core; + int ret; + pid_t pid; +}; + +struct rve_backend_ops { + int (*get_version)(struct rve_scheduler_t *scheduler); + int (*set_reg)(struct rve_job *job, struct rve_scheduler_t *scheduler); + int (*init_reg)(struct rve_job *job); + void (*soft_reset)(struct rve_scheduler_t *scheduler); +}; + +struct rve_timer { + u32 busy_time; + u32 busy_time_record; +}; + +struct rve_scheduler_t { + struct device *dev; + void __iomem *rve_base; + + struct clk *clks[RVE_MAX_BUS_CLK]; + int num_clks; + + int pd_refcount; + + struct rve_job *running_job; + struct list_head todo_list; + spinlock_t irq_lock; + wait_queue_head_t job_done_wq; + const struct rve_backend_ops *ops; + const struct rve_hw_data *data; + int job_count; + int irq; + struct rve_version_t version; + int core; + + struct rve_timer timer; + uint64_t total_int_cnt; +}; + +struct rve_cmd_reg_array_t { + uint32_t cmd_reg[58]; +}; + +struct rve_debug_info_t { + pid_t pid; + ktime_t timestamp; + ktime_t hw_time_total; + ktime_t last_job_use_time; + ktime_t last_job_hw_use_time; + ktime_t max_cost_time_per_sec; + + uint32_t rd_bandwidth; + uint32_t wr_bandwidth; + uint32_t cycle_cnt; +}; + +struct rve_internal_ctx_t { + struct rve_scheduler_t *scheduler; + + struct rve_cmd_reg_array_t *regcmd_data; + uint32_t cmd_num; + + uint32_t sync_mode; + int flags; + int id; + + uint32_t running_job_count; + uint32_t finished_job_count; + bool is_running; + int priority; + int32_t out_fence_fd; + int32_t in_fence_fd; + + struct dma_fence *out_fence; + + spinlock_t lock; + struct kref refcount; + + /* debug info */ + struct rve_debug_info_t debug_info; + + /* TODO: add some common work */ +}; + +struct rve_pending_ctx_manager { + struct mutex lock; + + /* + * @ctx_id_idr: + * + * Mapping of ctx id to object pointers. Used by the GEM + * subsystem. Protected by @lock. + */ + struct idr ctx_id_idr; + + int ctx_count; +}; + +struct rve_drvdata_t { + struct miscdevice miscdev; + + struct rve_fence_context *fence_ctx; + + /* used by rve2's mmu lock */ + struct mutex lock; + + struct rve_scheduler_t *scheduler[RVE_MAX_SCHEDULER]; + int num_of_scheduler; + + struct delayed_work power_off_work; + struct wake_lock wake_lock; + + struct rve_mm *mm; + + /* rve_job pending manager, import by RVE_IOC_START_CONFIG */ + struct rve_pending_ctx_manager *pend_ctx_manager; + +#ifdef CONFIG_ROCKCHIP_RVE_DEBUGGER + struct rve_debugger *debugger; +#endif +}; + +struct rve_irqs_data_t { + const char *name; + irqreturn_t (*irq_hdl)(int irq, void *ctx); + irqreturn_t (*irq_thread)(int irq, void *ctx); +}; + +struct rve_match_data_t { + const char * const *clks; + int num_clks; + const struct rve_irqs_data_t *irqs; + int num_irqs; +}; + +static inline int rve_read(int offset, struct rve_scheduler_t *scheduler) +{ + return readl(scheduler->rve_base + offset); +} + +static inline void rve_write(int value, int offset, struct rve_scheduler_t *scheduler) +{ + writel(value, scheduler->rve_base + offset); +} + +int rve_power_enable(struct rve_scheduler_t *scheduler); +int rve_power_disable(struct rve_scheduler_t *scheduler); + +#endif /* __LINUX_RVE_FENCE_H_ */ diff --git a/drivers/video/rockchip/rve/include/rve_fence.h b/drivers/video/rockchip/rve/include/rve_fence.h new file mode 100644 index 000000000000..e70b6ac0442b --- /dev/null +++ b/drivers/video/rockchip/rve/include/rve_fence.h @@ -0,0 +1,32 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) Rockchip Electronics Co., Ltd. + * + * Author: Huang Lee + */ + +#ifndef __LINUX_RVE_FENCE_H_ +#define __LINUX_RVE_FENCE_H_ + +#ifdef CONFIG_SYNC_FILE + +#include "rve_drv.h" + +struct rve_fence_context *rve_fence_context_alloc(void); + +void rve_fence_context_free(struct rve_fence_context *fence_ctx); + +int rve_out_fence_alloc(struct rve_job *job); + +int rve_out_fence_get_fd(struct rve_job *job); + +struct dma_fence *rve_get_input_fence(int in_fence_fd); + +int rve_wait_input_fence(struct dma_fence *in_fence); + +int rve_add_dma_fence_callback(struct rve_job *job, + struct dma_fence *in_fence, dma_fence_func_t func); + +#endif + +#endif /* __LINUX_RVE_FENCE_H_ */ diff --git a/drivers/video/rockchip/rve/include/rve_job.h b/drivers/video/rockchip/rve/include/rve_job.h new file mode 100644 index 000000000000..39f5f1b23c1b --- /dev/null +++ b/drivers/video/rockchip/rve/include/rve_job.h @@ -0,0 +1,46 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) Rockchip Electronics Co., Ltd. + * + * Author: Huang Lee + */ + +#ifndef __LINUX_RKRVE_JOB_H_ +#define __LINUX_RKRVE_JOB_H_ + +#include +#include + +#include "rve_drv.h" + +enum job_flags { + RVE_JOB_DONE = 1 << 0, + RVE_ASYNC = 1 << 1, + RVE_SYNC = 1 << 2, + RVE_JOB_USE_HANDLE = 1 << 3, + RVE_JOB_UNSUPPORT_RVE2 = 1 << 4, +}; + +struct rve_scheduler_t *rve_job_get_scheduler(struct rve_job *job); +struct rve_internal_ctx_t *rve_job_get_internal_ctx(struct rve_job *job); + +void rve_job_done(struct rve_scheduler_t *rve_scheduler, int ret); +int rve_job_commit(struct rve_internal_ctx_t *ctx); + +int rve_ctx_manager_init(struct rve_pending_ctx_manager **ctx_manager_session); +int rve_ctx_manager_remove(struct rve_pending_ctx_manager **ctx_manager_session); + +int rve_internal_ctx_alloc_to_get_idr_id(void); +void rve_internal_ctx_kref_release(struct kref *ref); + +int rve_job_config_by_user_ctx(struct rve_user_ctx_t *user_ctx); +int rve_job_commit_by_user_ctx(struct rve_user_ctx_t *user_ctx); +int rve_job_cancel_by_user_ctx(uint32_t ctx_id); + +struct rve_job * +rve_scheduler_get_pending_job_list(struct rve_scheduler_t *scheduler); + +struct rve_job * +rve_scheduler_get_running_job(struct rve_scheduler_t *scheduler); + +#endif /* __LINUX_RKRVE_JOB_H_ */ diff --git a/drivers/video/rockchip/rve/include/rve_reg.h b/drivers/video/rockchip/rve/include/rve_reg.h new file mode 100644 index 000000000000..f61987e75952 --- /dev/null +++ b/drivers/video/rockchip/rve/include/rve_reg.h @@ -0,0 +1,83 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __RVE_REG_H__ +#define __RVE_REG_H__ + +#include "rve_drv.h" + +/* sys reg */ +#define RVE_SWREG0_IVE_VERSION 0x000 +#define RVE_SWREG1_IVE_IRQ 0x004 +#define RVE_SWREG2_IRQ_CTRL 0x008 +#define RVE_SWREG3_IVE_IDLE_PRC_STA 0x00c +#define RVE_SWREG4_IVE_FORCE_IDLE_WBASE 0x010 +#define RVE_SWREG5_IVE_IDLE_CTRL 0x014 +#define RVE_SWREG6_IVE_WORK_STA 0x018 +#define RVE_SWREG7_IVE_SWAP 0x01c + +/* llp reg */ +#define RVE_SWLTB0_START_BASE 0x100 +#define RVE_SWLTB1_CTRL 0x104 +#define RVE_SWLTB2_CFG_DONE 0x108 +#define RVE_SWLTB3_ENABLE 0x10c +#define RVE_SWLTB4_PAUSE_CTRL 0x110 +#define RVE_SWLTB5_DECODED_NUM 0x114 +#define RVE_SWLTB6_SKIP_NUM 0x118 +#define RVE_SWLTB7_TOTAL_NUM 0x11c +#define RVE_SWLTB8_LAST_FRAME_BASE 0x120 +#define RVE_SWLTB9_LAST_IDX 0x124 + +/* op reg */ +#define RVE_SWCFG0_EN 0x200 +#define RVE_SWCFG4_OPERATOR 0x210 +#define RVE_SWCFG5_CTRL 0x214 +#define RVE_SWCFG6_TIMEOUT_THRESH 0x218 +#define RVE_SWCFG7_DDR_CTRL 0x21c +#define RVE_SWCFG9_PIC_INFO 0x224 +#define RVE_SWCFG10_HOR_STRIDE0 0x228 +#define RVE_SWCFG11_HOR_STRIDE1 0x22c +#define RVE_SWCFG12_SRC0_BASE 0x230 +#define RVE_SWCFG13_SRC1_BASE 0x234 +#define RVE_SWCFG14_SRC2_BASE 0x238 +#define RVE_SWCFG15_SRC3_BASE 0x23c +#define RVE_SWCFG16_DST0_BASE 0x240 +#define RVE_SWCFG17_DST1_BASE 0x244 +#define RVE_SWCFG18_DST2_BASE 0x248 +#define RVE_SWCFG20_OP_CTRL0 0x250 +#define RVE_SWCFG21_OP_CTRL1 0x254 +#define RVE_SWCFG22_OP_CTRL2 0x258 +#define RVE_SWCFG23_OP_CTRL3 0x25c +#define RVE_SWCFG24_OP_CTRL4 0x260 +#define RVE_SWCFG25_OP_CTRL5 0x264 +#define RVE_SWCFG26_OP_CTRL6 0x268 +#define RVE_SWCFG27_OP_CTRL7 0x26c +#define RVE_SWCFG28_OP_CTRL8 0x270 +#define RVE_SWCFG29_OP_CTRL9 0x274 + +/* monitor reg */ +#define RVE_SWCFG32_MONITOR_CTRL0 0x280 +#define RVE_SWCFG33_MONITOR_CTRL1 0x284 +#define RVE_SWCFG34_MONITOR_INFO0 0x288 +#define RVE_SWCFG35_MONITOR_INFO1 0x28c +#define RVE_SWCFG36_MONITOR_INFO2 0x290 +#define RVE_SWCFG37_MONITOR_INFO3 0x294 +#define RVE_SWCFG38_MONITOR_INFO4 0x298 +#define RVE_SWCFG39_MONITOR_INFO5 0x29c + +/* mmu reg */ + +/* common reg */ +#define RVE_SYS_REG 0x000 +#define RVE_LTB_REG 0x100 +#define RVE_CFG_REG 0x200 +#define RVE_MMU_REG 0x300 + +void rve_soft_reset(struct rve_scheduler_t *scheduler); +int rve_set_reg(struct rve_job *job, struct rve_scheduler_t *scheduler); +int rve_init_reg(struct rve_job *job); +int rve_get_version(struct rve_scheduler_t *scheduler); + +void rve_dump_read_back_reg(struct rve_scheduler_t *scheduler); +void rve_get_monitor_info(struct rve_internal_ctx_t *ctx, struct rve_scheduler_t *scheduler); + +#endif + diff --git a/drivers/video/rockchip/rve/rve_debugger.c b/drivers/video/rockchip/rve/rve_debugger.c new file mode 100644 index 000000000000..5c8d15bc7671 --- /dev/null +++ b/drivers/video/rockchip/rve/rve_debugger.c @@ -0,0 +1,553 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) Rockchip Electronics Co., Ltd. + * + * Author: + * Huang Lee + */ + +#define pr_fmt(fmt) "rve_debugger: " fmt + +#include +#include +#include +#include +#include +#include + +#include "rve.h" +#include "rve_debugger.h" +#include "rve_drv.h" + +#define RVE_DEBUGGER_ROOT_NAME "rve" + +#define STR_ENABLE(en) (en ? "EN" : "DIS") + +int RVE_DEBUG_REG; +int RVE_DEBUG_MSG; +int RVE_DEBUG_TIME; +int RVE_DEBUG_CHECK_MODE; +int RVE_DEBUG_NONUSE; +int RVE_DEBUG_INT_FLAG; +int RVE_DEBUG_MONITOR; + +static int rve_debug_show(struct seq_file *m, void *data) +{ + seq_printf(m, "REG [%s]\n" + "MSG [%s]\n" + "TIME [%s]\n" + "INT [%s]\n" + "CHECK [%s]\n" + "STOP [%s]\n" + "MONITOR [%s]", + STR_ENABLE(RVE_DEBUG_REG), + STR_ENABLE(RVE_DEBUG_MSG), + STR_ENABLE(RVE_DEBUG_TIME), + STR_ENABLE(RVE_DEBUG_INT_FLAG), + STR_ENABLE(RVE_DEBUG_CHECK_MODE), + STR_ENABLE(RVE_DEBUG_NONUSE), + STR_ENABLE(RVE_DEBUG_MONITOR)); + + seq_puts(m, "\nhelp:\n"); + seq_puts(m, + " 'echo reg > debug' to enable/disable register log printing.\n"); + seq_puts(m, + " 'echo msg > debug' to enable/disable message log printing.\n"); + seq_puts(m, + " 'echo time > debug' to enable/disable time log printing.\n"); + seq_puts(m, + " 'echo int > debug' to enable/disable interruppt log printing.\n"); + seq_puts(m, " 'echo check > debug' to enable/disable check mode.\n"); + seq_puts(m, + " 'echo stop > debug' to enable/disable stop using hardware\n"); + seq_puts(m, " 'echo mon > debug' to enable/disable monitor"); + + return 0; +} + +static ssize_t rve_debug_write(struct file *file, const char __user *ubuf, + size_t len, loff_t *offp) +{ + char buf[14]; + + if (len > sizeof(buf) - 1) + return -EINVAL; + if (copy_from_user(buf, ubuf, len)) + return -EFAULT; + buf[len - 1] = '\0'; + + if (strncmp(buf, "reg", 4) == 0) { + if (RVE_DEBUG_REG) { + RVE_DEBUG_REG = 0; + pr_err("close rve reg!\n"); + } else { + RVE_DEBUG_REG = 1; + pr_err("open rve reg!\n"); + } + } else if (strncmp(buf, "msg", 3) == 0) { + if (RVE_DEBUG_MSG) { + RVE_DEBUG_MSG = 0; + pr_err("close rve test MSG!\n"); + } else { + RVE_DEBUG_MSG = 1; + pr_err("open rve test MSG!\n"); + } + } else if (strncmp(buf, "time", 4) == 0) { + if (RVE_DEBUG_TIME) { + RVE_DEBUG_TIME = 0; + pr_err("close rve test time!\n"); + } else { + RVE_DEBUG_TIME = 1; + pr_err("open rve test time!\n"); + } + } else if (strncmp(buf, "check", 5) == 0) { + if (RVE_DEBUG_CHECK_MODE) { + RVE_DEBUG_CHECK_MODE = 0; + pr_err("close rve check flag!\n"); + } else { + RVE_DEBUG_CHECK_MODE = 1; + pr_err("open rve check flag!\n"); + } + } else if (strncmp(buf, "stop", 4) == 0) { + if (RVE_DEBUG_NONUSE) { + RVE_DEBUG_NONUSE = 0; + pr_err("using rve hardware!\n"); + } else { + RVE_DEBUG_NONUSE = 1; + pr_err("stop using rve hardware!\n"); + } + } else if (strncmp(buf, "int", 3) == 0) { + if (RVE_DEBUG_INT_FLAG) { + RVE_DEBUG_INT_FLAG = 0; + pr_err("close inturrupt MSG!\n"); + } else { + RVE_DEBUG_INT_FLAG = 1; + pr_err("open inturrupt MSG!\n"); + } + } else if (strncmp(buf, "mon", 3) == 0) { + if (RVE_DEBUG_MONITOR) { + RVE_DEBUG_MONITOR = 0; + pr_err("close monitor!\n"); + } else { + RVE_DEBUG_MONITOR = 1; + pr_err("open monitor!\n"); + } + } else if (strncmp(buf, "slt", 3) == 0) { + pr_err("Null"); + } + + return len; +} + +static int rve_version_show(struct seq_file *m, void *data) +{ + seq_printf(m, "%s: v%s\n", DRIVER_DESC, DRIVER_VERSION); + + return 0; +} + +static int rve_load_show(struct seq_file *m, void *data) +{ + struct rve_scheduler_t *scheduler = NULL; + unsigned long flags; + int i; + int load; + u32 busy_time_total; + + seq_printf(m, "num of scheduler = %d\n", rve_drvdata->num_of_scheduler); + seq_printf(m, "================= load ==================\n"); + + for (i = 0; i < rve_drvdata->num_of_scheduler; i++) { + scheduler = rve_drvdata->scheduler[i]; + + seq_printf(m, "scheduler[%d]: %s\n", + i, dev_driver_string(scheduler->dev)); + + spin_lock_irqsave(&scheduler->irq_lock, flags); + + busy_time_total = scheduler->timer.busy_time_record; + + spin_unlock_irqrestore(&scheduler->irq_lock, flags); + + load = (busy_time_total * 100000 / RVE_LOAD_INTERVAL); + seq_printf(m, "\t load = %d\n", load); + seq_printf(m, "-----------------------------------\n"); + } + return 0; +} + +static int rve_scheduler_show(struct seq_file *m, void *data) +{ + struct rve_scheduler_t *scheduler = NULL; + int i; + + seq_printf(m, "num of scheduler = %d\n", rve_drvdata->num_of_scheduler); + seq_printf(m, "===================================\n"); + + for (i = 0; i < rve_drvdata->num_of_scheduler; i++) { + scheduler = rve_drvdata->scheduler[i]; + + seq_printf(m, "scheduler[%d]: %s\n", i, dev_driver_string(scheduler->dev)); + seq_printf(m, "-----------------------------------\n"); + seq_printf(m, "pd_ref = %d\n", scheduler->pd_refcount); + seq_printf(m, "total_int_cnt = %llu\n", scheduler->total_int_cnt); + } + + return 0; +} + +static int rve_ctx_manager_show(struct seq_file *m, void *data) +{ + int id; + struct rve_pending_ctx_manager *ctx_manager; + struct rve_internal_ctx_t *ctx; + unsigned long flags; + int cmd_num = 0; + int finished_job_count = 0; + bool status = false; + pid_t pid; + + ktime_t last_job_hw_use_time; + ktime_t last_job_use_time; + ktime_t hw_time_total; + ktime_t max_cost_time_per_sec; + uint32_t rd_bandwidth, wr_bandwidth, cycle_cnt; + + ctx_manager = rve_drvdata->pend_ctx_manager; + + seq_puts(m, "rve internal ctx dump:\n"); + seq_printf(m, "ctx count = %d\n", ctx_manager->ctx_count); + + mutex_lock(&ctx_manager->lock); + + idr_for_each_entry(&ctx_manager->ctx_id_idr, ctx, id) { + seq_printf(m, "================= ctx id: %d =================\n", ctx->id); + + spin_lock_irqsave(&ctx->lock, flags); + + cmd_num = ctx->cmd_num; + finished_job_count = ctx->finished_job_count; + status = ctx->is_running; + pid = ctx->debug_info.pid; + last_job_hw_use_time = ctx->debug_info.last_job_hw_use_time; + last_job_use_time = ctx->debug_info.last_job_use_time; + hw_time_total = ctx->debug_info.hw_time_total; + max_cost_time_per_sec = ctx->debug_info.max_cost_time_per_sec; + rd_bandwidth = ctx->debug_info.rd_bandwidth; + wr_bandwidth = ctx->debug_info.wr_bandwidth; + cycle_cnt = ctx->debug_info.cycle_cnt; + + spin_unlock_irqrestore(&ctx->lock, flags); + + seq_printf(m, "----------------- RVE CTX INFO -----------------\n"); + seq_printf(m, "\t [pid: %d] status: %s\n", pid, status ? "active" : "pending"); + seq_printf(m, "\t set cmd num: %d\t finish job sum: %d\n", + cmd_num, finished_job_count); + seq_printf(m, "\t last_job_use_time: %llu us\t last_job_hw_use_time: %llu us", + ktime_to_us(last_job_use_time), ktime_to_us(last_job_hw_use_time)); + seq_printf(m, "\t hw_time_total: %llu us\t max_cost_time_per_sec: %llu us", + ktime_to_us(hw_time_total), ktime_to_us(max_cost_time_per_sec)); + seq_printf(m, "\t rd_bandwidth: %u bytes\t wr_bandwidth: %u bytes", + rd_bandwidth, wr_bandwidth); + seq_printf(m, "\t cycle_cnt: %u\t", cycle_cnt); + + seq_printf(m, "----------------- RVE INVOKE INFO -----------------\n"); + /* TODO: */ + } + + mutex_unlock(&ctx_manager->lock); + + return 0; +} + + +struct rve_debugger_list rve_debugger_root_list[] = { + {"debug", rve_debug_show, rve_debug_write, NULL}, + {"driver_version", rve_version_show, NULL, NULL}, + {"load", rve_load_show, NULL, NULL}, + {"scheduler_status", rve_scheduler_show, NULL, NULL}, + {"ctx_manager", rve_ctx_manager_show, NULL, NULL}, +}; + +#ifdef CONFIG_ROCKCHIP_RVE_DEBUG_FS + +static ssize_t rve_debugger_write(struct file *file, const char __user *ubuf, + size_t len, loff_t *offp) +{ + struct seq_file *priv = file->private_data; + struct rve_debugger_node *node = priv->private; + + if (node->info_ent->write) + return node->info_ent->write(file, ubuf, len, offp); + else + return len; +} + +static int rve_debugfs_open(struct inode *inode, struct file *file) +{ + struct rve_debugger_node *node = inode->i_private; + + return single_open(file, node->info_ent->show, node); +} + +static const struct file_operations rve_debugfs_fops = { + .owner = THIS_MODULE, + .open = rve_debugfs_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .write = rve_debugger_write, +}; + +static int rve_debugfs_remove_files(struct rve_debugger *debugger) +{ + struct rve_debugger_node *pos, *q; + struct list_head *entry_list; + + mutex_lock(&debugger->debugfs_lock); + + /* Delete debugfs entry list */ + entry_list = &debugger->debugfs_entry_list; + list_for_each_entry_safe(pos, q, entry_list, list) { + if (pos->dent == NULL) + continue; + list_del(&pos->list); + kfree(pos); + pos = NULL; + } + + /* Delete all debugfs node in this directory */ + debugfs_remove_recursive(debugger->debugfs_dir); + debugger->debugfs_dir = NULL; + + mutex_unlock(&debugger->debugfs_lock); + + return 0; +} + +static int rve_debugfs_create_files(const struct rve_debugger_list *files, + int count, struct dentry *root, + struct rve_debugger *debugger) +{ + int i; + struct dentry *ent; + struct rve_debugger_node *tmp; + + for (i = 0; i < count; i++) { + tmp = kmalloc(sizeof(struct rve_debugger_node), GFP_KERNEL); + if (tmp == NULL) { + pr_err("Cannot alloc node path /sys/kernel/debug/%pd/%s\n", + root, files[i].name); + goto MALLOC_FAIL; + } + + tmp->info_ent = &files[i]; + tmp->debugger = debugger; + + ent = debugfs_create_file(files[i].name, S_IFREG | S_IRUGO, + root, tmp, &rve_debugfs_fops); + if (!ent) { + pr_err("Cannot create /sys/kernel/debug/%pd/%s\n", root, + files[i].name); + goto CREATE_FAIL; + } + + tmp->dent = ent; + + mutex_lock(&debugger->debugfs_lock); + list_add_tail(&tmp->list, &debugger->debugfs_entry_list); + mutex_unlock(&debugger->debugfs_lock); + } + + return 0; + +CREATE_FAIL: + kfree(tmp); +MALLOC_FAIL: + rve_debugfs_remove_files(debugger); + + return -1; +} + +int rve_debugfs_remove(void) +{ + struct rve_debugger *debugger; + + debugger = rve_drvdata->debugger; + + rve_debugfs_remove_files(debugger); + + return 0; +} + +int rve_debugfs_init(void) +{ + int ret; + struct rve_debugger *debugger; + + debugger = rve_drvdata->debugger; + + debugger->debugfs_dir = + debugfs_create_dir(RVE_DEBUGGER_ROOT_NAME, NULL); + if (IS_ERR_OR_NULL(debugger->debugfs_dir)) { + pr_err("failed on mkdir /sys/kernel/debug/%s\n", + RVE_DEBUGGER_ROOT_NAME); + debugger->debugfs_dir = NULL; + return -EIO; + } + + ret = rve_debugfs_create_files(rve_debugger_root_list, ARRAY_SIZE(rve_debugger_root_list), + debugger->debugfs_dir, debugger); + if (ret) { + pr_err("Could not install rve_debugger_root_list debugfs\n"); + goto CREATE_FAIL; + } + + return 0; + +CREATE_FAIL: + rve_debugfs_remove(); + + return ret; +} +#endif /* #ifdef CONFIG_ROCKCHIP_RVE_DEBUG_FS */ + +#ifdef CONFIG_ROCKCHIP_RVE_PROC_FS +static int rve_procfs_open(struct inode *inode, struct file *file) +{ + struct rve_debugger_node *node = PDE_DATA(inode); + + return single_open(file, node->info_ent->show, node); +} + +static ssize_t rve_fops_write_u32(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + int rc; + struct seq_file *priv = file->private_data; + + rc = kstrtou32_from_user(buf, count, 0, priv->private); + if (rc) + return rc; + + return count; +} + +static const struct proc_ops rve_procfs_fops = { + .proc_open = rve_procfs_open, + .proc_read = seq_read, + .proc_lseek = seq_lseek, + .proc_release = single_release, + .proc_write = rve_fops_write_u32, +}; + +static int rve_procfs_remove_files(struct rve_debugger *debugger) +{ + struct rve_debugger_node *pos, *q; + struct list_head *entry_list; + + mutex_lock(&debugger->procfs_lock); + + /* Delete procfs entry list */ + entry_list = &debugger->procfs_entry_list; + list_for_each_entry_safe(pos, q, entry_list, list) { + if (pos->pent == NULL) + continue; + list_del(&pos->list); + kfree(pos); + pos = NULL; + } + + /* Delete all procfs node in this directory */ + proc_remove(debugger->procfs_dir); + debugger->procfs_dir = NULL; + + mutex_unlock(&debugger->procfs_lock); + + return 0; +} + +static int rve_procfs_create_files(const struct rve_debugger_list *files, + int count, struct proc_dir_entry *root, + struct rve_debugger *debugger) +{ + int i; + struct proc_dir_entry *ent; + struct rve_debugger_node *tmp; + + for (i = 0; i < count; i++) { + tmp = kmalloc(sizeof(struct rve_debugger_node), GFP_KERNEL); + if (tmp == NULL) { + pr_err("Cannot alloc node path for /proc/%s/%s\n", + RVE_DEBUGGER_ROOT_NAME, files[i].name); + goto MALLOC_FAIL; + } + + tmp->info_ent = &files[i]; + tmp->debugger = debugger; + + ent = proc_create_data(files[i].name, S_IFREG | S_IRUGO, + root, &rve_procfs_fops, tmp); + if (!ent) { + pr_err("Cannot create /proc/%s/%s\n", + RVE_DEBUGGER_ROOT_NAME, files[i].name); + goto CREATE_FAIL; + } + + tmp->pent = ent; + + mutex_lock(&debugger->procfs_lock); + list_add_tail(&tmp->list, &debugger->procfs_entry_list); + mutex_unlock(&debugger->procfs_lock); + } + + return 0; + +CREATE_FAIL: + kfree(tmp); +MALLOC_FAIL: + rve_procfs_remove_files(debugger); + return -1; +} + +int rve_procfs_remove(void) +{ + struct rve_debugger *debugger; + + debugger = rve_drvdata->debugger; + + rve_procfs_remove_files(debugger); + + return 0; +} + +int rve_procfs_init(void) +{ + int ret; + struct rve_debugger *debugger; + + debugger = rve_drvdata->debugger; + + debugger->procfs_dir = proc_mkdir(RVE_DEBUGGER_ROOT_NAME, NULL); + if (IS_ERR_OR_NULL(debugger->procfs_dir)) { + pr_err("failed on mkdir /proc/%s\n", RVE_DEBUGGER_ROOT_NAME); + debugger->procfs_dir = NULL; + return -EIO; + } + + ret = rve_procfs_create_files(rve_debugger_root_list, ARRAY_SIZE(rve_debugger_root_list), + debugger->procfs_dir, debugger); + if (ret) { + pr_err("Could not install rve_debugger_root_list procfs\n"); + goto CREATE_FAIL; + } + + return 0; + +CREATE_FAIL: + rve_procfs_remove(); + + return ret; +} +#endif /* #ifdef CONFIG_ROCKCHIP_RVE_PROC_FS */ + diff --git a/drivers/video/rockchip/rve/rve_drv.c b/drivers/video/rockchip/rve/rve_drv.c new file mode 100644 index 000000000000..eafb36cde48a --- /dev/null +++ b/drivers/video/rockchip/rve/rve_drv.c @@ -0,0 +1,740 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) Rockchip Electronics Co., Ltd. + * + * Author: Huang Lee + */ + +#define pr_fmt(fmt) "rve: " fmt + +#include "rve_job.h" +#include "rve_fence.h" +#include "rve_debugger.h" +#include "rve_reg.h" + +struct rve_drvdata_t *rve_drvdata; + +/* set hrtimer */ +static struct hrtimer timer; +static ktime_t kt; + +static const struct rve_backend_ops rve_ops = { + .get_version = rve_get_version, + .set_reg = rve_set_reg, + .init_reg = rve_init_reg, + .soft_reset = rve_soft_reset +}; + +static enum hrtimer_restart hrtimer_handler(struct hrtimer *timer) +{ + struct rve_drvdata_t *rve = rve_drvdata; + struct rve_scheduler_t *scheduler = NULL; + struct rve_job *job = NULL; + unsigned long flags; + int i; + + ktime_t now = ktime_get(); + + for (i = 0; i < rve->num_of_scheduler; i++) { + scheduler = rve->scheduler[i]; + + spin_lock_irqsave(&scheduler->irq_lock, flags); + + /* if timer action on job running */ + job = scheduler->running_job; + if (job) { + scheduler->timer.busy_time += ktime_us_delta(now, job->hw_recoder_time); + job->hw_recoder_time = now; + } + + scheduler->timer.busy_time_record = scheduler->timer.busy_time; + scheduler->timer.busy_time = 0; + + spin_unlock_irqrestore(&scheduler->irq_lock, flags); + + /* monitor */ + if (job && job->ctx) + rve_get_monitor_info(job->ctx, scheduler); + } + + hrtimer_forward_now(timer, kt); + return HRTIMER_RESTART; +} + +static void rve_init_timer(void) +{ + kt = ktime_set(0, RVE_LOAD_INTERVAL); + hrtimer_init(&timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + hrtimer_start(&timer, kt, HRTIMER_MODE_REL); + timer.function = hrtimer_handler; +} + +static void rve_cancel_timer(void) +{ + hrtimer_cancel(&timer); +} + +#ifndef RVE_PD_AWAYS_ON +int rve_power_enable(struct rve_scheduler_t *scheduler) +{ + int ret = -EINVAL; + int i; + + pm_runtime_get_sync(scheduler->dev); + pm_stay_awake(scheduler->dev); + + for (i = 0; i < scheduler->num_clks; i++) { + if (!IS_ERR(scheduler->clks[i])) { + ret = clk_prepare_enable(scheduler->clks[i]); + if (ret < 0) + goto err_enable_clk; + } + } + + return 0; + +err_enable_clk: + for (--i; i >= 0; --i) + if (!IS_ERR(scheduler->clks[i])) + clk_disable_unprepare(scheduler->clks[i]); + + pm_relax(scheduler->dev); + pm_runtime_put_sync_suspend(scheduler->dev); + + scheduler->pd_refcount++; + + return ret; +} + +int rve_power_disable(struct rve_scheduler_t *scheduler) +{ + int i; + + for (i = scheduler->num_clks - 1; i >= 0; i--) + if (!IS_ERR(scheduler->clks[i])) + clk_disable_unprepare(scheduler->clks[i]); + + pm_relax(scheduler->dev); + pm_runtime_put_sync_suspend(scheduler->dev); + + scheduler->pd_refcount--; + + return 0; +} + +#endif //RVE_PD_AWAYS_ON + +static long rve_ioctl_cmd_start(unsigned long arg) +{ + int rve_user_ctx_id; + int ret = 0; + + rve_user_ctx_id = rve_internal_ctx_alloc_to_get_idr_id(); + + if (copy_to_user((void *)arg, &rve_user_ctx_id, sizeof(int))) + ret = -EFAULT; + + return ret; +} + +static long rve_ioctl_cmd_config(unsigned long arg) +{ + struct rve_user_ctx_t user_ctx; + int ret = 0; + + if (unlikely(copy_from_user(&user_ctx, (struct rve_user_ctx_t *)arg, + sizeof(user_ctx)))) { + pr_err("rve_user_ctx copy_from_user failed!\n"); + return -EFAULT; + } + +/* TODO: + * if (rve_user_ctx.cmd_num > RVE_CMD_NUM_MAX) { + * pr_err("Cannot import more than %d buffers at a time!\n", + * RVE_CMD_NUM_MAX); + * return -EFBIG; + * } + */ + + if (user_ctx.id <= 0) { + pr_err("ctx id[%d] is invalid", user_ctx.id); + return -EINVAL; + } + + if (DEBUGGER_EN(MSG)) + pr_info("config cmd id = %d", user_ctx.id); + + /* find internal_ctx to set cmd by user ctx (internal ctx id) */ + ret = rve_job_config_by_user_ctx(&user_ctx); + if (ret < 0) { + pr_err("config ctx id[%d] failed!\n", user_ctx.id); + return -EFAULT; + } + + return ret; +} + +static long rve_ioctl_cmd_end(unsigned long arg) +{ + struct rve_user_ctx_t rve_user_ctx; + int ret = 0; + + if (unlikely(copy_from_user(&rve_user_ctx, (uint32_t *)arg, + sizeof(rve_user_ctx)))) { + pr_err("rve_user_ctx copy_from_user failed!\n"); + return -EFAULT; + } + + if (DEBUGGER_EN(MSG)) + pr_info("config end id = %d", rve_user_ctx.id); + + /* find internal_ctx to set cmd by user ctx (internal ctx id) */ + ret = rve_job_commit_by_user_ctx(&rve_user_ctx); + if (ret < 0) { + pr_err("commit ctx id[%d] failed!\n", rve_user_ctx.id); + return -EFAULT; + } + + if (copy_to_user((struct rve_user_ctx_t *)arg, + &rve_user_ctx, sizeof(struct rve_user_ctx_t))) { + pr_err("rve_user_ctx copy_to_user failed\n"); + return -EFAULT; + } + + return ret; +} + +static long rve_ioctl_cmd_cancel(unsigned long arg) +{ + uint32_t rve_user_ctx_id; + int ret = 0; + + if (unlikely(copy_from_user(&rve_user_ctx_id, (uint32_t *)arg, + sizeof(uint32_t)))) { + pr_err("rve_user_ctx copy_from_user failed!\n"); + return -EFAULT; + } + + if (DEBUGGER_EN(MSG)) + pr_info("config cancel id = %d", rve_user_ctx_id); + + /* find internal_ctx to set cmd by user ctx (internal ctx id) */ + ret = rve_job_cancel_by_user_ctx(rve_user_ctx_id); + if (ret < 0) { + pr_err("cancel ctx id[%d] failed!\n", rve_user_ctx_id); + return -EFAULT; + } + + return ret; +} + +static long rve_ioctl(struct file *file, uint32_t cmd, unsigned long arg) +{ + struct rve_drvdata_t *rve = rve_drvdata; + + int ret = 0; + int i = 0; + struct rve_version_t driver_version; + struct rve_hw_versions_t hw_versions; + + if (!rve) { + pr_err("rve_drvdata is null, rve is not init\n"); + return -ENODEV; + } + + //if (DEBUGGER_EN(NONUSE)) + // return 0; + + switch (cmd) { + case RVE_IOC_GET_HW_VER: + /* RVE hardware version */ + hw_versions.size = rve->num_of_scheduler > RVE_HW_SIZE ? + RVE_HW_SIZE : rve->num_of_scheduler; + + for (i = 0; i < hw_versions.size; i++) { + memcpy(&hw_versions.version[i], &rve->scheduler[i]->version, + sizeof(rve->scheduler[i]->version)); + } + + if (copy_to_user((void *)arg, &hw_versions, sizeof(hw_versions))) + ret = -EFAULT; + else + ret = true; + + break; + + case RVE_IOC_GET_VER: + /* Driver version */ + driver_version.major = DRIVER_MAJOR_VERSION; + driver_version.minor = DRIVER_MINOR_VERSION; + driver_version.revision = DRIVER_REVISION_VERSION; + driver_version.prod_num = 0; + strncpy((char *)driver_version.str, DRIVER_VERSION, sizeof(driver_version.str)); + + if (copy_to_user((void *)arg, &driver_version, sizeof(driver_version))) + ret = -EFAULT; + else + ret = true; + + break; + + case RVE_IOC_START_CONFIG: + ret = rve_ioctl_cmd_start(arg); + + break; + + case RVE_IOC_END_CONFIG: + ret = rve_ioctl_cmd_end(arg); + + break; + + case RVE_IOC_CMD_CONFIG: + ret = rve_ioctl_cmd_config(arg); + + break; + + case RVE_IOC_CANCEL_CONFIG: + ret = rve_ioctl_cmd_cancel(arg); + + break; + + default: + pr_err("unknown ioctl cmd!\n"); + ret = -EINVAL; + break; + } + + return ret; +} + +#ifdef CONFIG_ROCKCHIP_RVE_DEBUGGER +static int rve_debugger_init(struct rve_debugger **debugger_p) +{ + struct rve_debugger *debugger; + + *debugger_p = kzalloc(sizeof(struct rve_debugger), GFP_KERNEL); + if (*debugger_p == NULL) { + pr_err("can not alloc for rve debugger\n"); + return -ENOMEM; + } + + debugger = *debugger_p; + +#ifdef CONFIG_ROCKCHIP_RVE_DEBUG_FS + mutex_init(&debugger->debugfs_lock); + INIT_LIST_HEAD(&debugger->debugfs_entry_list); +#endif + +#ifdef CONFIG_ROCKCHIP_RVE_PROC_FS + mutex_init(&debugger->procfs_lock); + INIT_LIST_HEAD(&debugger->procfs_entry_list); +#endif + + rve_debugfs_init(); + rve_procfs_init(); + + return 0; +} + +static int rve_debugger_remove(struct rve_debugger **debugger_p) +{ + rve_debugfs_remove(); + rve_procfs_remove(); + + kfree(*debugger_p); + *debugger_p = NULL; + + return 0; +} +#endif + +static int rve_open(struct inode *inode, struct file *file) +{ + return nonseekable_open(inode, file); +} + +static int rve_release(struct inode *inode, struct file *file) +{ + pid_t pid; + int ctx_id; + struct rve_pending_ctx_manager *ctx_manager; + struct rve_internal_ctx_t *ctx; + + pid = current->pid; + + ctx_manager = rve_drvdata->pend_ctx_manager; + + mutex_lock(&ctx_manager->lock); + + idr_for_each_entry(&ctx_manager->ctx_id_idr, ctx, ctx_id) { + + mutex_unlock(&ctx_manager->lock); + + if (pid == ctx->debug_info.pid) { + pr_err("[pid:%d] destroy ctx[%d] when the user exits", pid, ctx->id); + kref_put(&ctx->refcount, rve_internal_ctx_kref_release); + } + + mutex_lock(&ctx_manager->lock); + } + + mutex_unlock(&ctx_manager->lock); + + return 0; +} + +static irqreturn_t rve_irq_handler(int irq, void *data) +{ + struct rve_scheduler_t *scheduler = data; + u32 error_flag; + + error_flag = rve_read(RVE_SWREG6_IVE_WORK_STA, scheduler); + + if (error_flag & 0x6) { + pr_err("irq thread work_status[%x]\n", error_flag); + + if (error_flag & 0x2) + pr_err("irq: bus error"); + else if (error_flag & 0x4) + pr_err("irq: timeout error"); + + scheduler->ops->soft_reset(scheduler); + } + + /* clear INT */ + rve_write(0x30000, RVE_SWREG1_IVE_IRQ, scheduler); + + return IRQ_WAKE_THREAD; +} + +static irqreturn_t rve_irq_thread(int irq, void *data) +{ + struct rve_scheduler_t *scheduler = data; + struct rve_job *job; + u32 error_flag; + + job = scheduler->running_job; + scheduler->total_int_cnt++; + + if (!job) { + pr_err("running job is invalid on irq thread\n"); + return IRQ_HANDLED; + } + + if (DEBUGGER_EN(INT_FLAG)) { + error_flag = rve_read(RVE_SWREG6_IVE_WORK_STA, scheduler); + + if (error_flag & 0x1) { + pr_err("irq thread work_status[%x]\n", error_flag); + + if (error_flag & 0x2) + pr_err("irq: bus error"); + else if (error_flag & 0x4) + pr_err("irq: timeout error"); + } + } + + rve_job_done(scheduler, 0); + + return IRQ_HANDLED; +} + +const struct file_operations rve_fops = { + .owner = THIS_MODULE, + .open = rve_open, + .release = rve_release, + .unlocked_ioctl = rve_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = rve_ioctl, +#endif +}; + +static struct miscdevice rve_dev = { + .name = "rve", + .fops = &rve_fops, +}; + +static const char *const rve_clks[] = { + "aclk_rve", + "hclk_rve", +}; + +static const struct rve_irqs_data_t rve_irqs[] = { + {"rve_irq", rve_irq_handler, rve_irq_thread} +}; + +static const struct rve_match_data_t rve_match_data = { + .clks = rve_clks, + .num_clks = ARRAY_SIZE(rve_clks), + .irqs = rve_irqs, + .num_irqs = ARRAY_SIZE(rve_irqs) +}; + +static const struct of_device_id rve_dt_ids[] = { + { + .compatible = "rockchip,rve", + .data = &rve_match_data, + }, + {}, +}; + +static void init_scheduler(struct rve_scheduler_t *scheduler, + const char *name) +{ + spin_lock_init(&scheduler->irq_lock); + INIT_LIST_HEAD(&scheduler->todo_list); + init_waitqueue_head(&scheduler->job_done_wq); + + if (!strcmp(name, "rve")) { + scheduler->ops = &rve_ops; + scheduler->core = RVE_SCHEDULER_CORE0; + } +} + +static int rve_drv_probe(struct platform_device *pdev) +{ + struct rve_drvdata_t *data = rve_drvdata; + struct resource *res; + int ret = 0; + const struct of_device_id *match = NULL; + struct device *dev = &pdev->dev; + const struct rve_match_data_t *match_data; + int i = 0, irq; + struct rve_scheduler_t *scheduler = NULL; + + if (!pdev->dev.of_node) + return -EINVAL; + + if (!strcmp(dev_driver_string(dev), "rve")) + match = of_match_device(rve_dt_ids, dev); + + if (!match) { + dev_err(dev, "%s missing DT entry!\n", dev_driver_string(dev)); + return -EINVAL; + } + + scheduler = + devm_kzalloc(&pdev->dev, sizeof(struct rve_scheduler_t), + GFP_KERNEL); + if (scheduler == NULL) { + pr_err("failed to allocate scheduler. dev name = %s\n", + dev_driver_string(dev)); + return -ENOMEM; + } + + init_scheduler(scheduler, dev_driver_string(dev)); + + scheduler->dev = &pdev->dev; + + /* map the registers */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + pr_err("get memory resource failed.\n"); + return -ENXIO; + } + + scheduler->rve_base = + devm_ioremap(&pdev->dev, res->start, resource_size(res)); + if (!scheduler->rve_base) { + pr_err("ioremap failed\n"); + ret = -ENOENT; + return ret; + } + + /* get the IRQ */ + match_data = match->data; + + /* there are irq names in dts */ + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(dev, "no irq %s in dts\n", match_data->irqs[0].name); + return irq; + } + + scheduler->irq = irq; + + pr_info("%s, irq = %d, match scheduler\n", + match_data->irqs[0].name, irq); + + ret = devm_request_threaded_irq(dev, irq, + match_data->irqs[0].irq_hdl, + match_data->irqs[0].irq_thread, IRQF_SHARED, + dev_driver_string(dev), scheduler); + if (ret < 0) { + pr_err("request irq name: %s failed: %d\n", + match_data->irqs[0].name, ret); + return ret; + } + +#ifndef RVE_PD_AWAYS_ON + for (i = 0; i < match_data->num_clks; i++) { + struct clk *clk = devm_clk_get(dev, match_data->clks[i]); + + if (IS_ERR(clk)) + pr_err("failed to get %s\n", match_data->clks[i]); + + scheduler->clks[i] = clk; + } + scheduler->num_clks = match_data->num_clks; +#endif + + platform_set_drvdata(pdev, scheduler); + + device_init_wakeup(dev, true); + + /* PM init */ +#ifndef RVE_PD_AWAYS_ON + pm_runtime_enable(&pdev->dev); + + ret = pm_runtime_get_sync(scheduler->dev); + if (ret < 0) { + pr_err("failed to get pm runtime, ret = %d\n", + ret); + goto failed; + } + + for (i = 0; i < scheduler->num_clks; i++) { + if (!IS_ERR(scheduler->clks[i])) { + ret = clk_prepare_enable(scheduler->clks[i]); + if (ret < 0) { + pr_err("failed to enable clk\n"); + goto failed; + } + } + } +#endif //RVE_PD_AWAYS_ON + + scheduler->ops->get_version(scheduler); + pr_info("Driver loaded successfully rve[%d] ver:%s\n", i, + scheduler->version.str); + + data->scheduler[data->num_of_scheduler] = scheduler; + + data->num_of_scheduler++; + +#ifndef RVE_PD_AWAYS_ON + for (i = scheduler->num_clks - 1; i >= 0; i--) + if (!IS_ERR(scheduler->clks[i])) + clk_disable_unprepare(scheduler->clks[i]); + + pm_runtime_put_sync(&pdev->dev); +#endif //RVE_PD_AWAYS_ON + + pr_info("probe successfully\n"); + + return 0; + +#ifndef RVE_PD_AWAYS_ON +failed: + device_init_wakeup(dev, false); + pm_runtime_disable(dev); + + return ret; +#endif //RVE_PD_AWAYS_ON +} + +static int rve_drv_remove(struct platform_device *pdev) +{ + device_init_wakeup(&pdev->dev, false); +#ifndef RVE_PD_AWAYS_ON + pm_runtime_disable(&pdev->dev); +#endif //RVE_PD_AWAYS_ON + + return 0; +} + +static struct platform_driver rve_driver = { + .probe = rve_drv_probe, + .remove = rve_drv_remove, + .driver = { + .name = "rve", + .of_match_table = of_match_ptr(rve_dt_ids), + }, +}; + +static int __init rve_init(void) +{ + int ret; + + rve_drvdata = kzalloc(sizeof(struct rve_drvdata_t), GFP_KERNEL); + if (rve_drvdata == NULL) { + pr_err("failed to allocate driver data.\n"); + return -ENOMEM; + } + + mutex_init(&rve_drvdata->lock); + + wake_lock_init(&rve_drvdata->wake_lock, WAKE_LOCK_SUSPEND, "rve"); + + ret = platform_driver_register(&rve_driver); + if (ret != 0) { + pr_err("Platform device rve3_core0_driver register failed (%d).\n", ret); + return ret; + } + + rve_init_timer(); + +#ifdef CONFIG_SYNC_FILE + rve_drvdata->fence_ctx = rve_fence_context_alloc(); + if (IS_ERR(rve_drvdata->fence_ctx)) { + pr_err("failed to allocate fence context for RVE\n"); + ret = PTR_ERR(rve_drvdata->fence_ctx); + return ret; + } +#endif + + ret = misc_register(&rve_dev); + if (ret) { + pr_err("cannot register miscdev (%d)\n", ret); + return ret; + } + + rve_ctx_manager_init(&rve_drvdata->pend_ctx_manager); + +#ifdef CONFIG_ROCKCHIP_RVE_DEBUGGER + rve_debugger_init(&rve_drvdata->debugger); +#endif + + pr_info("Module initialized. v%s\n", DRIVER_VERSION); + + return 0; +} + +static void __exit rve_exit(void) +{ +#ifdef CONFIG_ROCKCHIP_RVE_DEBUGGER + rve_debugger_remove(&rve_drvdata->debugger); +#endif + + rve_ctx_manager_remove(&rve_drvdata->pend_ctx_manager); + + wake_lock_destroy(&rve_drvdata->wake_lock); + +#ifdef CONFIG_SYNC_FILE + rve_fence_context_free(rve_drvdata->fence_ctx); +#endif + + rve_cancel_timer(); + + platform_driver_unregister(&rve_driver); + + misc_deregister(&(rve_drvdata->miscdev)); + + kfree(rve_drvdata); +} + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 0)) +#ifdef CONFIG_ROCKCHIP_THUNDER_BOOT +module_init(rve_init); +#else +late_initcall(rve_init); +#endif +#else +fs_initcall(rve_init); +#endif +module_exit(rve_exit); + +/* Module information */ +MODULE_AUTHOR("putin.li@rock-chips.com"); +MODULE_DESCRIPTION("Driver for rve device"); +MODULE_LICENSE("GPL"); diff --git a/drivers/video/rockchip/rve/rve_fence.c b/drivers/video/rockchip/rve/rve_fence.c new file mode 100644 index 000000000000..8d48f8033fa7 --- /dev/null +++ b/drivers/video/rockchip/rve/rve_fence.c @@ -0,0 +1,136 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) Rockchip Electronics Co., Ltd. + * + * Author: Huang Lee + */ + +#define pr_fmt(fmt) "rve_fence: " fmt + +#include +#include +#include + +#include "rve_fence.h" + +static const char *rve_fence_get_name(struct dma_fence *fence) +{ + return DRIVER_NAME; +} + +static const struct dma_fence_ops rve_fence_ops = { + .get_driver_name = rve_fence_get_name, + .get_timeline_name = rve_fence_get_name, +}; + +struct rve_fence_context *rve_fence_context_alloc(void) +{ + struct rve_fence_context *fence_ctx = NULL; + + fence_ctx = kzalloc(sizeof(*fence_ctx), GFP_KERNEL); + if (!fence_ctx) + return ERR_PTR(-ENOMEM); + + fence_ctx->context = dma_fence_context_alloc(1); + spin_lock_init(&fence_ctx->spinlock); + + return fence_ctx; +} + +void rve_fence_context_free(struct rve_fence_context *fence_ctx) +{ + kfree(fence_ctx); +} + +int rve_out_fence_alloc(struct rve_job *job) +{ + struct rve_fence_context *fence_ctx = rve_drvdata->fence_ctx; + struct dma_fence *fence = NULL; + + fence = kzalloc(sizeof(*fence), GFP_KERNEL); + if (!fence) + return -ENOMEM; + + dma_fence_init(fence, &rve_fence_ops, &job->fence_lock, + fence_ctx->context, ++fence_ctx->seqno); + + job->out_fence = fence; + + return 0; +} + +int rve_out_fence_get_fd(struct rve_job *job) +{ + struct sync_file *sync_file = NULL; + int fence_fd = -1; + + if (!job->out_fence) + return -EINVAL; + + fence_fd = get_unused_fd_flags(O_CLOEXEC); + if (fence_fd < 0) + return fence_fd; + + sync_file = sync_file_create(job->out_fence); + if (!sync_file) + return -ENOMEM; + + fd_install(fence_fd, sync_file->file); + + return fence_fd; +} + +struct dma_fence *rve_get_input_fence(int in_fence_fd) +{ + struct dma_fence *in_fence; + + in_fence = sync_file_get_fence(in_fence_fd); + + if (!in_fence) + pr_err("can not get in-fence from fd\n"); + + return in_fence; +} + +int rve_wait_input_fence(struct dma_fence *in_fence) +{ + int ret = 0; + + ret = dma_fence_wait(in_fence, true); + + dma_fence_put(in_fence); + + return ret; +} + +int rve_add_dma_fence_callback(struct rve_job *job, struct dma_fence *in_fence, + dma_fence_func_t func) +{ + struct rve_fence_waiter *waiter; + int ret; + + waiter = kmalloc(sizeof(*waiter), GFP_KERNEL); + if (!waiter) { + pr_err("%s: Failed to allocate waiter\n", __func__); + return -ENOMEM; + } + + waiter->job = job; + + ret = dma_fence_add_callback(in_fence, &waiter->waiter, func); + if (ret == -ENOENT) { + pr_err("'input fence' has been already signaled."); + goto err_free_waiter; + } else if (ret == -EINVAL) { + pr_err + ("%s: failed to add callback to dma_fence, err: %d\n", + __func__, ret); + goto err_free_waiter; + } + + return ret; + +err_free_waiter: + kfree(waiter); + return ret; +} diff --git a/drivers/video/rockchip/rve/rve_job.c b/drivers/video/rockchip/rve/rve_job.c new file mode 100644 index 000000000000..52135021716b --- /dev/null +++ b/drivers/video/rockchip/rve/rve_job.c @@ -0,0 +1,923 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) Rockchip Electronics Co., Ltd. + * + * Author: Huang Lee + */ + +#define pr_fmt(fmt) "rve_job: " fmt + +#include "rve_job.h" +#include "rve_fence.h" +#include "rve_reg.h" + +struct rve_job * +rve_scheduler_get_pending_job_list(struct rve_scheduler_t *scheduler) +{ + unsigned long flags; + struct rve_job *job; + + spin_lock_irqsave(&scheduler->irq_lock, flags); + + job = list_first_entry_or_null(&scheduler->todo_list, + struct rve_job, head); + + spin_unlock_irqrestore(&scheduler->irq_lock, flags); + + return job; +} + +struct rve_job * +rve_scheduler_get_running_job(struct rve_scheduler_t *scheduler) +{ + unsigned long flags; + struct rve_job *job; + + spin_lock_irqsave(&scheduler->irq_lock, flags); + + job = scheduler->running_job; + + spin_unlock_irqrestore(&scheduler->irq_lock, flags); + + return job; +} + +struct rve_scheduler_t *rve_job_get_scheduler(struct rve_job *job) +{ + return job->scheduler; +} + +struct rve_internal_ctx_t *rve_job_get_internal_ctx(struct rve_job *job) +{ + return job->ctx; +} + +static void rve_job_free(struct rve_job *job) +{ +#ifdef CONFIG_SYNC_FILE + if (job->out_fence) + dma_fence_put(job->out_fence); +#endif + + free_page((unsigned long)job); +} + +static int rve_job_cleanup(struct rve_job *job) +{ + ktime_t now = ktime_get(); + + if (DEBUGGER_EN(TIME)) { + pr_info("(pid:%d) job clean use time = %lld\n", job->pid, + ktime_us_delta(now, job->timestamp)); + } + rve_job_free(job); + + return 0; +} + +static struct rve_job *rve_job_alloc(struct rve_internal_ctx_t *ctx) +{ + struct rve_job *job = NULL; + + job = (struct rve_job *)get_zeroed_page(GFP_KERNEL | GFP_DMA32); + if (!job) + return NULL; + +#ifdef CONFIG_SYNC_FILE + spin_lock_init(&job->fence_lock); +#endif + INIT_LIST_HEAD(&job->head); + + job->timestamp = ktime_get(); + job->pid = current->pid; + job->regcmd_data = &ctx->regcmd_data[ctx->running_job_count]; + + job->scheduler = rve_drvdata->scheduler[0]; + job->core = rve_drvdata->scheduler[0]->core; + job->ctx = ctx; + ctx->scheduler = job->scheduler; + + if (ctx->priority > 0) { + if (ctx->priority > RVE_SCHED_PRIORITY_MAX) + job->priority = RVE_SCHED_PRIORITY_MAX; + else + job->priority = ctx->priority; + } + + return job; +} + +static struct rve_internal_ctx_t * +rve_internal_ctx_lookup(struct rve_pending_ctx_manager *ctx_manager, uint32_t id) +{ + struct rve_internal_ctx_t *ctx = NULL; + + mutex_lock(&ctx_manager->lock); + + ctx = idr_find(&ctx_manager->ctx_id_idr, id); + if (ctx == NULL) + pr_err("can not find internal ctx from id[%d]", id); + + mutex_unlock(&ctx_manager->lock); + + return ctx; +} + +/* + * Called at driver close to release the internal ctx's id references. + */ +static int rve_internal_ctx_free_remove_idr_cb(int id, void *ptr, void *data) +{ + struct rve_internal_ctx_t *ctx = ptr; + + idr_remove(&rve_drvdata->pend_ctx_manager->ctx_id_idr, ctx->id); + kfree(ctx); + + return 0; +} + +static int rve_internal_ctx_free_remove_idr(struct rve_internal_ctx_t *ctx) +{ + struct rve_pending_ctx_manager *ctx_manager; + + ctx_manager = rve_drvdata->pend_ctx_manager; + + mutex_lock(&ctx_manager->lock); + + ctx_manager->ctx_count--; + idr_remove(&ctx_manager->ctx_id_idr, ctx->id); + kfree(ctx); + + mutex_unlock(&ctx_manager->lock); + + return 0; +} + +static int rve_internal_ctx_signal(struct rve_job *job) +{ + struct rve_internal_ctx_t *ctx; + struct rve_scheduler_t *scheduler; + int finished_job_count; + unsigned long flags; + + scheduler = rve_job_get_scheduler(job); + if (scheduler == NULL) { + pr_err("failed to get scheduler, %s(%d)\n", __func__, __LINE__); + return -EFAULT; + } + + ctx = rve_job_get_internal_ctx(job); + if (IS_ERR_OR_NULL(ctx)) { + pr_err("can not find internal ctx"); + return -EINVAL; + } + + ctx->regcmd_data = job->regcmd_data; + + spin_lock_irqsave(&ctx->lock, flags); + + finished_job_count = ++ctx->finished_job_count; + + spin_unlock_irqrestore(&ctx->lock, flags); + + if (finished_job_count >= ctx->cmd_num) { +#ifdef CONFIG_SYNC_FILE + if (ctx->out_fence) + dma_fence_signal(ctx->out_fence); +#endif + + job->flags |= RVE_JOB_DONE; + + if (job->flags & RVE_ASYNC) + rve_job_cleanup(job); + + wake_up(&scheduler->job_done_wq); + + spin_lock_irqsave(&ctx->lock, flags); + + ctx->is_running = false; + ctx->out_fence = NULL; + + spin_unlock_irqrestore(&ctx->lock, flags); + } + + return 0; +} + +static void rve_job_dump_info(struct rve_job *job) +{ + pr_info("job: priority = %d, core = %d\n", + job->priority, job->core); +} + +static int rve_job_run(struct rve_job *job) +{ + struct rve_scheduler_t *scheduler; + int ret = 0; + + scheduler = rve_job_get_scheduler(job); + +#ifndef RVE_PD_AWAYS_ON + /* enable power */ + ret = rve_power_enable(scheduler); + if (ret < 0) { + pr_err("power enable failed"); + return ret; + } +#endif + + ret = scheduler->ops->init_reg(job); + if (ret < 0) { + pr_err("init reg failed"); + goto failed; + } + + ret = scheduler->ops->set_reg(job, scheduler); + if (ret < 0) { + pr_err("set reg failed"); + goto failed; + } + + /* for debug */ + if (DEBUGGER_EN(MSG)) + rve_job_dump_info(job); + + return ret; + +failed: +#ifndef RVE_PD_AWAYS_ON + rve_power_disable(scheduler); +#endif + + return ret; +} + +static void rve_job_next(struct rve_scheduler_t *scheduler) +{ + struct rve_job *job = NULL; + unsigned long flags; + +next_job: + spin_lock_irqsave(&scheduler->irq_lock, flags); + + if (scheduler->running_job || + list_empty(&scheduler->todo_list)) { + spin_unlock_irqrestore(&scheduler->irq_lock, flags); + return; + } + + job = list_first_entry(&scheduler->todo_list, struct rve_job, head); + + list_del_init(&job->head); + + scheduler->job_count--; + + scheduler->running_job = job; + + spin_unlock_irqrestore(&scheduler->irq_lock, flags); + + job->ret = rve_job_run(job); + + /* If some error before hw run */ + if (job->ret < 0) { + pr_err("some error on rve_job_run before hw start, %s(%d)\n", + __func__, __LINE__); + + spin_lock_irqsave(&scheduler->irq_lock, flags); + + scheduler->running_job = NULL; + + spin_unlock_irqrestore(&scheduler->irq_lock, flags); + + rve_internal_ctx_signal(job); + + goto next_job; + } +} + +static void rve_job_finish_and_next(struct rve_job *job, int ret) +{ + ktime_t now = ktime_get(); + struct rve_scheduler_t *scheduler; + + job->ret = ret; + + scheduler = rve_job_get_scheduler(job); + + if (DEBUGGER_EN(TIME)) { + pr_info("hw use time = %lld\n", ktime_us_delta(now, job->hw_running_time)); + pr_info("(pid:%d) job done use time = %lld\n", job->pid, + ktime_us_delta(now, job->timestamp)); + } + + rve_internal_ctx_signal(job); + + rve_job_next(scheduler); + +#ifndef RVE_PD_AWAYS_ON + rve_power_disable(scheduler); +#endif +} + +void rve_job_done(struct rve_scheduler_t *scheduler, int ret) +{ + struct rve_job *job; + unsigned long flags; + u32 error_flag; + uint32_t *cmd_reg; + int i; + + ktime_t now = ktime_get(); + + spin_lock_irqsave(&scheduler->irq_lock, flags); + + job = scheduler->running_job; + scheduler->running_job = NULL; + + scheduler->timer.busy_time += ktime_us_delta(now, job->hw_recoder_time); + + spin_unlock_irqrestore(&scheduler->irq_lock, flags); + + spin_lock_irqsave(&job->ctx->lock, flags); + + job->ctx->debug_info.max_cost_time_per_sec = + max(job->ctx->debug_info.last_job_hw_use_time, + job->ctx->debug_info.max_cost_time_per_sec); + job->ctx->debug_info.last_job_hw_use_time = job->hw_running_time - now; + job->ctx->debug_info.hw_time_total += job->ctx->debug_info.last_job_hw_use_time; + job->ctx->debug_info.last_job_use_time = job->timestamp - now; + + spin_unlock_irqrestore(&job->ctx->lock, flags); + + /* record CFG REG copy to user */ + cmd_reg = job->regcmd_data->cmd_reg; + for (i = 0; i < 40; i++) + cmd_reg[18 + i] = rve_read(RVE_CFG_REG + i * 4, scheduler); + + error_flag = rve_read(RVE_SWREG6_IVE_WORK_STA, scheduler); + + if (DEBUGGER_EN(MSG)) + pr_err("irq thread work_status[%.8x]\n", error_flag); + + rve_job_finish_and_next(job, ret); +} + +static void rve_job_timeout_clean(struct rve_scheduler_t *scheduler) +{ + unsigned long flags; + struct rve_job *job = NULL; + ktime_t now = ktime_get(); + + spin_lock_irqsave(&scheduler->irq_lock, flags); + + job = scheduler->running_job; + if (job && (job->flags & RVE_ASYNC) && + (ktime_to_ms(ktime_sub(now, job->hw_running_time)) >= RVE_ASYNC_TIMEOUT_DELAY)) { + scheduler->running_job = NULL; + + spin_unlock_irqrestore(&scheduler->irq_lock, flags); + + scheduler->ops->soft_reset(scheduler); + + rve_internal_ctx_signal(job); + +#ifndef RVE_PD_AWAYS_ON + rve_power_disable(scheduler); +#endif + } else { + spin_unlock_irqrestore(&scheduler->irq_lock, flags); + } +} + +static struct rve_scheduler_t *rve_job_schedule(struct rve_job *job) +{ + unsigned long flags; + struct rve_scheduler_t *scheduler = NULL; + struct rve_job *job_pos; + bool first_match = 0; + + scheduler = rve_job_get_scheduler(job); + if (scheduler == NULL) { + pr_err("failed to get scheduler, %s(%d)\n", __func__, __LINE__); + return NULL; + } + + /* Only async will timeout clean */ + rve_job_timeout_clean(scheduler); + + spin_lock_irqsave(&scheduler->irq_lock, flags); + + /* priority policy set by userspace */ + if (list_empty(&scheduler->todo_list) + || (job->priority == RVE_SCHED_PRIORITY_DEFAULT)) { + list_add_tail(&job->head, &scheduler->todo_list); + } else { + list_for_each_entry(job_pos, &scheduler->todo_list, head) { + if (job->priority > job_pos->priority && + (!first_match)) { + list_add(&job->head, &job_pos->head); + first_match = true; + } + + /* + * Increase the priority of subsequent tasks + * after inserting into the list + */ + if (first_match) + job_pos->priority++; + } + + if (!first_match) + list_add_tail(&job->head, &scheduler->todo_list); + } + + scheduler->job_count++; + + spin_unlock_irqrestore(&scheduler->irq_lock, flags); + + rve_job_next(scheduler); + + return scheduler; +} + +static void rve_running_job_abort(struct rve_job *job) +{ + unsigned long flags; + struct rve_scheduler_t *scheduler; + + scheduler = rve_job_get_scheduler(job); + + spin_lock_irqsave(&scheduler->irq_lock, flags); + + /* invalid job */ + if (job == scheduler->running_job) + scheduler->running_job = NULL; + + spin_unlock_irqrestore(&scheduler->irq_lock, flags); + + rve_job_cleanup(job); +} + +static void rve_invalid_job_abort(struct rve_job *job) +{ + rve_job_cleanup(job); +} + +static inline int rve_job_wait(struct rve_job *job) +{ + struct rve_scheduler_t *scheduler; + + int left_time; + ktime_t now; + int ret; + + scheduler = rve_job_get_scheduler(job); + + left_time = wait_event_interruptible_timeout(scheduler->job_done_wq, + job->flags & RVE_JOB_DONE, RVE_SYNC_TIMEOUT_DELAY); + + switch (left_time) { + case 0: + pr_err("%s timeout", __func__); + scheduler->ops->soft_reset(scheduler); + ret = -EBUSY; + break; + case -ERESTARTSYS: + ret = -ERESTARTSYS; + break; + default: + ret = 0; + break; + } + + now = ktime_get(); + + if (DEBUGGER_EN(TIME)) + pr_info("%s use time = %lld\n", __func__, + ktime_to_us(ktime_sub(now, job->hw_running_time))); + + return ret; +} + +#ifdef CONFIG_SYNC_FILE +static void rve_input_fence_signaled(struct dma_fence *fence, + struct dma_fence_cb *_waiter) +{ + struct rve_fence_waiter *waiter = (struct rve_fence_waiter *)_waiter; + struct rve_scheduler_t *scheduler = NULL; + + ktime_t now; + + now = ktime_get(); + + if (DEBUGGER_EN(TIME)) + pr_err("rve job wait in_fence signal use time = %lld\n", + ktime_to_us(ktime_sub(now, waiter->job->timestamp))); + + scheduler = rve_job_schedule(waiter->job); + + if (scheduler == NULL) + pr_err("failed to get scheduler, %s(%d)\n", __func__, __LINE__); + + kfree(waiter); +} +#endif + +int rve_internal_ctx_alloc_to_get_idr_id(void) +{ + struct rve_pending_ctx_manager *ctx_manager; + struct rve_internal_ctx_t *ctx; + + ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); + if (ctx == NULL) { + pr_err("can not kzalloc for rve_pending_ctx_manager\n"); + return -ENOMEM; + } + + ctx_manager = rve_drvdata->pend_ctx_manager; + if (ctx_manager == NULL) { + pr_err("rve_pending_ctx_manager is null!\n"); + kfree(ctx); + return -EFAULT; + } + + spin_lock_init(&ctx->lock); + + /* + * Get the user-visible handle using idr. Preload and perform + * allocation under our spinlock. + */ + + mutex_lock(&ctx_manager->lock); + + idr_preload(GFP_KERNEL); + ctx->id = idr_alloc(&ctx_manager->ctx_id_idr, ctx, 1, 0, GFP_KERNEL); + idr_preload_end(); + + ctx_manager->ctx_count++; + + kref_init(&ctx->refcount); + ctx->debug_info.pid = current->pid; + ctx->debug_info.timestamp = ktime_get(); + + mutex_unlock(&ctx_manager->lock); + + return ctx->id; +} + +int rve_job_config_by_user_ctx(struct rve_user_ctx_t *user_ctx) +{ + struct rve_pending_ctx_manager *ctx_manager; + struct rve_internal_ctx_t *ctx; + struct rve_cmd_reg_array_t *regcmd_data; + int ret = 0; + unsigned long flags; + + ctx_manager = rve_drvdata->pend_ctx_manager; + + ctx = rve_internal_ctx_lookup(ctx_manager, user_ctx->id); + if (IS_ERR_OR_NULL(ctx)) { + pr_err("can not find internal ctx from id[%d]", user_ctx->id); + return -EINVAL; + } + + spin_lock_irqsave(&ctx->lock, flags); + + if (ctx->is_running) { + pr_err("can not re-config when ctx is running"); + spin_unlock_irqrestore(&ctx->lock, flags); + return -EFAULT; + } + + spin_unlock_irqrestore(&ctx->lock, flags); + + regcmd_data = kmalloc(sizeof(struct rve_cmd_reg_array_t), GFP_KERNEL); + if (regcmd_data == NULL) { + pr_err("regcmd_data alloc error!\n"); + return -ENOMEM; + } + + /* TODO: user cmd_num */ + user_ctx->cmd_num = 1; + + if (unlikely(copy_from_user(regcmd_data, + u64_to_user_ptr(user_ctx->regcmd_data), + sizeof(struct rve_cmd_reg_array_t) * user_ctx->cmd_num))) { + pr_err("regcmd_data copy_from_user failed\n"); + ret = -EFAULT; + + goto err_free_regcmd_data; + } + + ctx->sync_mode = user_ctx->sync_mode; + ctx->cmd_num = user_ctx->cmd_num; + ctx->regcmd_data = regcmd_data; + ctx->priority = user_ctx->priority; + ctx->in_fence_fd = user_ctx->in_fence_fd; + + /* TODO: cmd addr */ + + return ret; + +err_free_regcmd_data: + kfree(regcmd_data); + return ret; +} + +int rve_job_commit_by_user_ctx(struct rve_user_ctx_t *user_ctx) +{ + struct rve_pending_ctx_manager *ctx_manager; + struct rve_internal_ctx_t *ctx; + int ret = 0; + unsigned long flags; + int i; + + ctx_manager = rve_drvdata->pend_ctx_manager; + + ctx = rve_internal_ctx_lookup(ctx_manager, user_ctx->id); + if (IS_ERR_OR_NULL(ctx)) { + pr_err("can not find internal ctx from id[%d]", user_ctx->id); + return -EINVAL; + } + + spin_lock_irqsave(&ctx->lock, flags); + + if (ctx->is_running) { + pr_err("can not re-config when ctx is running"); + spin_unlock_irqrestore(&ctx->lock, flags); + return -EFAULT; + } + + /* Reset */ + ctx->finished_job_count = 0; + ctx->running_job_count = 0; + ctx->is_running = true; + + spin_unlock_irqrestore(&ctx->lock, flags); + + for (i = 0; i < ctx->cmd_num; i++) { + ret = rve_job_commit(ctx); + if (ret < 0) { + pr_err("rve_job_commit failed, i = %d\n", i); + return -EFAULT; + } + + ctx->running_job_count++; + } + + user_ctx->out_fence_fd = ctx->out_fence_fd; + + if (unlikely(copy_to_user(u64_to_user_ptr(user_ctx->regcmd_data), + ctx->regcmd_data, + sizeof(struct rve_cmd_reg_array_t) * ctx->cmd_num))) { + pr_err("ctx->regcmd_data copy_to_user failed\n"); + return -EFAULT; + } + + return ret; +} + +void rve_internal_ctx_kref_release(struct kref *ref) +{ + struct rve_internal_ctx_t *ctx; + struct rve_scheduler_t *scheduler = NULL; + struct rve_job *job_pos, *job_q, *job; + int i; + bool need_reset = false; + unsigned long flags; + ktime_t now = ktime_get(); + + ctx = container_of(ref, struct rve_internal_ctx_t, refcount); + + spin_lock_irqsave(&ctx->lock, flags); + if (!ctx->is_running || ctx->finished_job_count >= ctx->cmd_num) { + spin_unlock_irqrestore(&ctx->lock, flags); + goto free_ctx; + } + spin_unlock_irqrestore(&ctx->lock, flags); + + for (i = 0; i < rve_drvdata->num_of_scheduler; i++) { + scheduler = rve_drvdata->scheduler[i]; + + spin_lock_irqsave(&scheduler->irq_lock, flags); + + list_for_each_entry_safe(job_pos, job_q, &scheduler->todo_list, head) { + if (ctx->id == job_pos->ctx->id) { + job = job_pos; + list_del_init(&job_pos->head); + + scheduler->job_count--; + } + } + + /* for load */ + if (scheduler->running_job) { + job = scheduler->running_job; + + if (job->ctx->id == ctx->id) { + scheduler->running_job = NULL; + scheduler->timer.busy_time += ktime_us_delta(now, job->hw_recoder_time); + need_reset = true; + } + } + + spin_unlock_irqrestore(&scheduler->irq_lock, flags); + + if (need_reset) { + pr_err("reset core[%d] by user cancel", scheduler->core); + scheduler->ops->soft_reset(scheduler); + + rve_job_finish_and_next(job, 0); + } + } + + kfree(ctx->regcmd_data); + +free_ctx: + rve_internal_ctx_free_remove_idr(ctx); +} + +int rve_job_cancel_by_user_ctx(uint32_t ctx_id) +{ + struct rve_pending_ctx_manager *ctx_manager; + struct rve_internal_ctx_t *ctx; + int ret = 0; + + ctx_manager = rve_drvdata->pend_ctx_manager; + + ctx = rve_internal_ctx_lookup(ctx_manager, ctx_id); + if (IS_ERR_OR_NULL(ctx)) { + pr_err("can not find internal ctx from id[%d]", ctx_id); + return -EINVAL; + } + + kref_put(&ctx->refcount, rve_internal_ctx_kref_release); + + return ret; +} + +int rve_job_commit(struct rve_internal_ctx_t *ctx) +{ + struct rve_job *job = NULL; + struct rve_scheduler_t *scheduler = NULL; +#ifdef CONFIG_SYNC_FILE + struct dma_fence *in_fence; +#endif + int ret = 0; + + /* TODO: remove */ + ctx->sync_mode = RVE_SYNC; + + job = rve_job_alloc(ctx); + if (!job) { + pr_err("failed to alloc rve job!\n"); + return -ENOMEM; + } + + if (ctx->sync_mode == RVE_ASYNC) { +#ifdef CONFIG_SYNC_FILE + job->flags |= RVE_ASYNC; + + if (ctx->out_fence) { + job->out_fence = ctx->out_fence; + } else { + ret = rve_out_fence_alloc(job); + if (ret) { + rve_job_free(job); + return ret; + } + + ctx->out_fence = job->out_fence; + } + + ctx->out_fence_fd = rve_out_fence_get_fd(job); + + if (DEBUGGER_EN(MSG)) + pr_info("in_fence_fd = %d", ctx->in_fence_fd); + + /* if input fence is valiable */ + if (ctx->in_fence_fd > 0) { + in_fence = rve_get_input_fence( + ctx->in_fence_fd); + if (!in_fence) { + pr_err("%s: failed to get input dma_fence\n", + __func__); + rve_job_free(job); + return ret; + } + + /* close input fence fd */ + ksys_close(ctx->in_fence_fd); + + ret = dma_fence_get_status(in_fence); + /* ret = 1: fence has been signaled */ + if (ret == 1) { + scheduler = rve_job_schedule(job); + + if (scheduler == NULL) { + pr_err("failed to get scheduler, %s(%d)\n", + __func__, __LINE__); + goto invalid_job; + } + /* if input fence is valid */ + } else if (ret == 0) { + ret = rve_add_dma_fence_callback(job, + in_fence, rve_input_fence_signaled); + if (ret < 0) { + pr_err("%s: failed to add fence callback\n", + __func__); + rve_job_free(job); + return ret; + } + } else { + pr_err("%s: fence status error\n", __func__); + rve_job_free(job); + return ret; + } + } else { + scheduler = rve_job_schedule(job); + + if (scheduler == NULL) { + pr_err("failed to get scheduler, %s(%d)\n", + __func__, __LINE__); + goto invalid_job; + } + } + + return ret; +#else + pr_err("can not support ASYNC mode, please enable CONFIG_SYNC_FILE"); + return -EFAULT; +#endif + + /* RVE_SYNC: wait until job finish */ + } else if (ctx->sync_mode == RVE_SYNC) { + scheduler = rve_job_schedule(job); + + if (scheduler == NULL) { + pr_err("failed to get scheduler, %s(%d)\n", __func__, + __LINE__); + goto invalid_job; + } + + ret = job->ret; + if (ret < 0) { + pr_err("some error on job, %s(%d)\n", __func__, + __LINE__); + goto running_job_abort; + } + + ret = rve_job_wait(job); + if (ret < 0) + goto running_job_abort; + + rve_job_cleanup(job); + } + return ret; + +invalid_job: + rve_invalid_job_abort(job); + return ret; + +/* only used by SYNC mode */ +running_job_abort: + rve_running_job_abort(job); + return ret; +} + +int rve_ctx_manager_init(struct rve_pending_ctx_manager **ctx_manager_session) +{ + struct rve_pending_ctx_manager *ctx_manager = NULL; + + *ctx_manager_session = kzalloc(sizeof(struct rve_pending_ctx_manager), GFP_KERNEL); + if (*ctx_manager_session == NULL) { + pr_err("can not kzalloc for rve_pending_ctx_manager\n"); + return -ENOMEM; + } + + ctx_manager = *ctx_manager_session; + + mutex_init(&ctx_manager->lock); + + idr_init_base(&ctx_manager->ctx_id_idr, 1); + + return 0; +} + +int rve_ctx_manager_remove(struct rve_pending_ctx_manager **ctx_manager_session) +{ + struct rve_pending_ctx_manager *ctx_manager = *ctx_manager_session; + + mutex_lock(&ctx_manager->lock); + + idr_for_each(&ctx_manager->ctx_id_idr, &rve_internal_ctx_free_remove_idr_cb, ctx_manager); + idr_destroy(&ctx_manager->ctx_id_idr); + + mutex_unlock(&ctx_manager->lock); + + kfree(*ctx_manager_session); + + *ctx_manager_session = NULL; + + return 0; +} diff --git a/drivers/video/rockchip/rve/rve_reg.c b/drivers/video/rockchip/rve/rve_reg.c new file mode 100644 index 000000000000..a5f2c1342e2b --- /dev/null +++ b/drivers/video/rockchip/rve/rve_reg.c @@ -0,0 +1,247 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) Rockchip Electronics Co., Ltd. + * + * Author: Huang Lee + */ + +#define pr_fmt(fmt) "rve_reg: " fmt + +#include "rve_reg.h" + +void rve_soft_reset(struct rve_scheduler_t *scheduler) +{ + u32 i; + u32 reg; + + rve_write(1, RVE_SWREG5_IVE_IDLE_CTRL, scheduler); + + if (DEBUGGER_EN(REG)) { + pr_err("dump reg info on soft reset"); + rve_dump_read_back_reg(scheduler); + } + + if (DEBUGGER_EN(MSG)) { + pr_err("soft reset idle_ctrl = %.8x, idle_prc_sta = %.8x", + rve_read(RVE_SWREG5_IVE_IDLE_CTRL, scheduler), + rve_read(RVE_SWREG3_IVE_IDLE_PRC_STA, scheduler)); + + pr_err("work status = %.8x", rve_read(RVE_SWREG6_IVE_WORK_STA, scheduler)); + } + + mdelay(20); + + for (i = 0; i < RVE_RESET_TIMEOUT; i++) { + reg = rve_read(RVE_SWREG3_IVE_IDLE_PRC_STA, scheduler); + if (reg & 0x2) { + pr_info("soft reset successfully"); + + /* reset sw_softrst_rdy_sta reg */ + rve_write(0x30000, RVE_SWREG3_IVE_IDLE_PRC_STA, scheduler); + + /* reset RVE_SWREG6_IVE_WORK_STA */ + rve_write(0xff0000, RVE_SWREG6_IVE_WORK_STA, scheduler); + + /* clean up int */ + rve_write(0x30000, RVE_SWREG1_IVE_IRQ, scheduler); + + break; + } + + udelay(1); + } + + if (i == RVE_RESET_TIMEOUT) + pr_err("soft reset timeout.\n"); + + if (DEBUGGER_EN(MSG)) { + pr_err("after soft reset idle_ctrl = %.8x, idle_prc_sta = %.8x", + rve_read(RVE_SWREG5_IVE_IDLE_CTRL, scheduler), + rve_read(RVE_SWREG3_IVE_IDLE_PRC_STA, scheduler)); + + pr_err("work status = %x", rve_read(RVE_SWREG6_IVE_WORK_STA, scheduler)); + } +} + +int rve_init_reg(struct rve_job *job) +{ + int ret = 0; + + if (DEBUGGER_EN(MSG)) + pr_err("TODO: debug info"); + + return ret; +} + +void rve_dump_read_back_reg(struct rve_scheduler_t *scheduler) +{ + int i; + unsigned long flags; + uint32_t sys_reg[8] = {0}; + uint32_t ltb_reg[12] = {0}; + uint32_t cfg_reg[40] = {0}; + uint32_t mmu_reg[12] = {0}; + + spin_lock_irqsave(&scheduler->irq_lock, flags); + + for (i = 0; i < 8; i++) + sys_reg[i] = rve_read(RVE_SYS_REG + i * 4, scheduler); + + for (i = 0; i < 12; i++) + ltb_reg[i] = rve_read(RVE_LTB_REG + i * 4, scheduler); + + for (i = 0; i < 40; i++) + cfg_reg[i] = rve_read(RVE_CFG_REG + i * 4, scheduler); + + for (i = 0; i < 12; i++) + mmu_reg[i] = rve_read(RVE_MMU_REG + i * 4, scheduler); + + spin_unlock_irqrestore(&scheduler->irq_lock, flags); + + pr_info("sys_reg:"); + for (i = 0; i < 2; i++) + pr_info("i = %x : %.8x %.8x %.8x %.8x\n", RVE_SYS_REG + i * 16, + sys_reg[0 + i * 4], sys_reg[1 + i * 4], + sys_reg[2 + i * 4], sys_reg[3 + i * 4]); + + pr_info("ltb_reg:"); + for (i = 0; i < 3; i++) + pr_info("i = %x : %.8x %.8x %.8x %.8x\n", RVE_LTB_REG + i * 16, + ltb_reg[0 + i * 4], ltb_reg[1 + i * 4], + ltb_reg[2 + i * 4], ltb_reg[3 + i * 4]); + + pr_info("cfg_reg:"); + for (i = 0; i < 10; i++) + pr_info("i = %x : %.8x %.8x %.8x %.8x\n", RVE_CFG_REG + i * 16, + cfg_reg[0 + i * 4], cfg_reg[1 + i * 4], + cfg_reg[2 + i * 4], cfg_reg[3 + i * 4]); + + pr_info("mmu_reg:"); + for (i = 0; i < 3; i++) + pr_info("i = %x : %.8x %.8x %.8x %.8x\n", RVE_MMU_REG + i * 16, + mmu_reg[0 + i * 4], mmu_reg[1 + i * 4], + mmu_reg[2 + i * 4], mmu_reg[3 + i * 4]); +} + +int rve_set_reg(struct rve_job *job, struct rve_scheduler_t *scheduler) +{ + ktime_t now = ktime_get(); + //uint32_t cmd_reg[58]; + uint32_t *cmd_reg; + int i; + + /* TODO: dump regcmd_data */ + cmd_reg = job->regcmd_data->cmd_reg; + + if (DEBUGGER_EN(REG)) { + pr_info("user readback:"); + for (i = 0; i < 14; i++) + pr_info("%.8x %.8x %.8x %.8x\n", + cmd_reg[0 + i * 4], cmd_reg[1 + i * 4], + cmd_reg[2 + i * 4], cmd_reg[3 + i * 4]); + pr_info("%.8x %.8x", cmd_reg[56], cmd_reg[57]); + } + + /* clean up irq status reg */ + rve_write(0x00000, RVE_SWREG6_IVE_WORK_STA, scheduler); + + /* TODO: llp mode */ + + if (DEBUGGER_EN(MSG)) { + pr_info("idle_ctrl = %x, idle_prc_sta = %x", + rve_read(RVE_SWREG5_IVE_IDLE_CTRL, scheduler), + rve_read(RVE_SWREG3_IVE_IDLE_PRC_STA, scheduler)); + + pr_info("work status = %x", rve_read(RVE_SWREG6_IVE_WORK_STA, scheduler)); + } + + if (DEBUGGER_EN(TIME)) + pr_info("set cmd use time = %lld\n", ktime_to_us(ktime_sub(now, job->timestamp))); + + job->hw_running_time = now; + job->hw_recoder_time = now; + + /* start hw, CMD buff */ + for (i = 0; i < 8; i++) + rve_write(cmd_reg[i], RVE_SYS_REG + i * 4, scheduler); + + for (i = 0; i < 10; i++) + rve_write(cmd_reg[8 + i], RVE_LTB_REG + i * 4, scheduler); + + /* 0x200(start)(40 - 1 = 39) need config after reg ready */ + for (i = 0; i < 39; i++) + rve_write(cmd_reg[19 + i], RVE_CFG_REG + (i + 1) * 4, scheduler); + + //TODO: + rve_write(0x30000, RVE_SWCFG5_CTRL, scheduler); + rve_write(0xf4240, RVE_SWCFG6_TIMEOUT_THRESH, scheduler); + rve_write(0x1f0001, RVE_SWCFG7_DDR_CTRL, scheduler); + + if (DEBUGGER_EN(MONITOR)) + rve_write(1, RVE_SWCFG32_MONITOR_CTRL0, scheduler); + + if (DEBUGGER_EN(REG)) { + pr_err("before config:"); + rve_dump_read_back_reg(scheduler); + } + + rve_write(cmd_reg[18], RVE_SWCFG0_EN, scheduler); + + if (DEBUGGER_EN(REG)) { + pr_err("after config:"); + rve_dump_read_back_reg(scheduler); + } + + return 0; +} + +int rve_get_version(struct rve_scheduler_t *scheduler) +{ + u32 major_version, minor_version, prod_num; + u32 reg_version; + + if (!scheduler) { + pr_err("scheduler is null\n"); + return -EINVAL; + } + + reg_version = rve_read(RVE_SWREG0_IVE_VERSION, scheduler); + + major_version = (reg_version & RVE_MAJOR_VERSION_MASK) >> 8; + minor_version = (reg_version & RVE_MINOR_VERSION_MASK); + prod_num = (reg_version & RVE_PROD_NUM_MASK) >> 16; + + snprintf(scheduler->version.str, sizeof(scheduler->version.str), "[%x]%x.%x", + prod_num, major_version, minor_version); + + scheduler->version.major = major_version; + scheduler->version.minor = minor_version; + scheduler->version.prod_num = prod_num; + + return 0; +} + +void rve_get_monitor_info(struct rve_internal_ctx_t *ctx, struct rve_scheduler_t *scheduler) +{ + unsigned long flags; + uint32_t rd_bandwidth, wr_bandwidth, cycle_cnt; + + /* monitor */ + if (DEBUGGER_EN(MONITOR)) { + rd_bandwidth = rve_read(RVE_SWCFG37_MONITOR_INFO3, scheduler); + wr_bandwidth = rve_read(RVE_SWCFG38_MONITOR_INFO4, scheduler); + cycle_cnt = rve_read(RVE_SWCFG39_MONITOR_INFO5, scheduler); + + /* reset per htimer occur */ + rve_write(2, RVE_SWCFG32_MONITOR_CTRL0, scheduler); + + spin_lock_irqsave(&ctx->lock, flags); + + ctx->debug_info.max_cost_time_per_sec = 0; + ctx->debug_info.rd_bandwidth = rd_bandwidth; + ctx->debug_info.wr_bandwidth = wr_bandwidth; + ctx->debug_info.cycle_cnt = cycle_cnt; + + spin_unlock_irqrestore(&ctx->lock, flags); + } +}