mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-07 03:15:31 +09:00
video: rockchip: mpp: new video codec driver
This video codec driver used in kernel 4.19 version, which is a new design framework. In this version, codecs integrate similar hardware, so are divided into vdpux vepux rkvdec rkvenc vepu22, and so on. Change-Id: Ic29b28cef8fce394ac9f950472204c172842a2df Signed-off-by: Ding Wei <leo.ding@rock-chips.com>
This commit is contained in:
41
drivers/video/rockchip/mpp/Kconfig
Normal file
41
drivers/video/rockchip/mpp/Kconfig
Normal file
@@ -0,0 +1,41 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0+ OR MIT)
|
||||
|
||||
menuconfig ROCKCHIP_MPP_SERVICE
|
||||
tristate "mpp service framework"
|
||||
depends on ARCH_ROCKCHIP
|
||||
default n
|
||||
help
|
||||
rockchip mpp service framework.
|
||||
|
||||
config ROCKCHIP_MPP_RKVDEC
|
||||
tristate "RKV decoder device driver"
|
||||
depends on ROCKCHIP_MPP_SERVICE
|
||||
default n
|
||||
help
|
||||
rockchip mpp rkv combo decoder and hevc decoder.
|
||||
|
||||
config ROCKCHIP_MPP_VDPU1
|
||||
tristate "VPU decoder v1 device driver"
|
||||
depends on ROCKCHIP_MPP_SERVICE
|
||||
default n
|
||||
help
|
||||
rockchip mpp vpu decoder v1.
|
||||
|
||||
config ROCKCHIP_MPP_VEPU1
|
||||
tristate "VPU encoder v1 device driver"
|
||||
depends on ROCKCHIP_MPP_SERVICE
|
||||
default n
|
||||
help
|
||||
rockchip mpp vpu encoder v1.
|
||||
|
||||
config ROCKCHIP_MPP_VDPU2
|
||||
tristate "VPU decoder v2 device driver"
|
||||
depends on ROCKCHIP_MPP_SERVICE
|
||||
default n
|
||||
|
||||
config ROCKCHIP_MPP_VEPU2
|
||||
tristate "VPU encoder v2 device driver"
|
||||
depends on ROCKCHIP_MPP_SERVICE
|
||||
default n
|
||||
help
|
||||
rockchip mpp vpu encoder v2
|
||||
11
drivers/video/rockchip/mpp/Makefile
Normal file
11
drivers/video/rockchip/mpp/Makefile
Normal file
@@ -0,0 +1,11 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0+ OR MIT)
|
||||
|
||||
rk_vcodec-objs := mpp_service.o mpp_common.o mpp_iommu.o
|
||||
|
||||
rk_vcodec-$(CONFIG_ROCKCHIP_MPP_RKVDEC) += mpp_rkvdec.o
|
||||
rk_vcodec-$(CONFIG_ROCKCHIP_MPP_VDPU1) += mpp_vdpu1.o
|
||||
rk_vcodec-$(CONFIG_ROCKCHIP_MPP_VEPU1) += mpp_vepu1.o
|
||||
rk_vcodec-$(CONFIG_ROCKCHIP_MPP_VDPU2) += mpp_vdpu2.o
|
||||
rk_vcodec-$(CONFIG_ROCKCHIP_MPP_VEPU2) += mpp_vepu2.o
|
||||
|
||||
obj-$(CONFIG_ROCKCHIP_MPP_SERVICE) += rk_vcodec.o
|
||||
1139
drivers/video/rockchip/mpp/mpp_common.c
Normal file
1139
drivers/video/rockchip/mpp/mpp_common.c
Normal file
File diff suppressed because it is too large
Load Diff
376
drivers/video/rockchip/mpp/mpp_common.h
Normal file
376
drivers/video/rockchip/mpp/mpp_common.h
Normal file
@@ -0,0 +1,376 @@
|
||||
/* SPDX-License-Identifier: (GPL-2.0+ OR MIT) */
|
||||
/*
|
||||
* Copyright (c) 2019 Fuzhou Rockchip Electronics Co., Ltd
|
||||
*
|
||||
* author:
|
||||
* Alpha Lin, alpha.lin@rock-chips.com
|
||||
* Randy Li, randy.li@rock-chips.com
|
||||
* Ding Wei, leo.ding@rock-chips.com
|
||||
*
|
||||
*/
|
||||
#ifndef __ROCKCHIP_MPP_COMMON_H__
|
||||
#define __ROCKCHIP_MPP_COMMON_H__
|
||||
|
||||
#include <linux/cdev.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/dma-buf.h>
|
||||
#include <linux/kfifo.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/time.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/reset.h>
|
||||
#include <linux/irqreturn.h>
|
||||
#include <linux/poll.h>
|
||||
|
||||
#define MHZ (1000 * 1000)
|
||||
|
||||
#define EXTRA_INFO_MAGIC (0x4C4A46)
|
||||
#define JPEG_IOC_EXTRA_SIZE (48)
|
||||
|
||||
/* Use 'l' as magic number */
|
||||
#define MPP_IOC_MAGIC 'l'
|
||||
|
||||
#define MPP_IOC_SET_CLIENT_TYPE _IOW(MPP_IOC_MAGIC, 1, __u32)
|
||||
#define MPP_IOC_GET_HW_FUSE_STATUS _IOW(MPP_IOC_MAGIC, 2, unsigned long)
|
||||
|
||||
#define MPP_IOC_SET_REG _IOW(MPP_IOC_MAGIC, 3, unsigned long)
|
||||
#define MPP_IOC_GET_REG _IOW(MPP_IOC_MAGIC, 4, unsigned long)
|
||||
|
||||
#define MPP_IOC_PROBE_IOMMU_STATUS _IOR(MPP_IOC_MAGIC, 5, __u32)
|
||||
#define MPP_IOC_SET_DRIVER_DATA _IOW(MPP_IOC_MAGIC, 64, u32)
|
||||
|
||||
#define MPP_IOC_CUSTOM_BASE (0x1000)
|
||||
|
||||
enum MPP_DEVICE_TYPE {
|
||||
MPP_DEVICE_ENC = 0x0,
|
||||
MPP_DEVICE_DEC = 0x1,
|
||||
MPP_DEVICE_PP = 0x2,
|
||||
MPP_DEVICE_DEC_PP = 0x3,
|
||||
MPP_DEVICE_DEC_HEVC = 0x4,
|
||||
MPP_DEVICE_DEC_RKV = 0x5,
|
||||
MPP_DEVICE_ENC_RKV = 0x6,
|
||||
MPP_DEVICE_DEC_AVSPLUS = 0x7,
|
||||
MPP_DEVICE_ENC_VEPU22 = 0x8,
|
||||
MPP_DEVICE_BUTT,
|
||||
};
|
||||
|
||||
enum MPP_DRIVER_TYPE {
|
||||
MPP_DRIVER_NULL = 0,
|
||||
MPP_DRIVER_VDPU1,
|
||||
MPP_DRIVER_VEPU1,
|
||||
MPP_DRIVER_VDPU2,
|
||||
MPP_DRIVER_VEPU2,
|
||||
MPP_DRIVER_VEPU22,
|
||||
MPP_DRIVER_RKVDEC,
|
||||
MPP_DRIVER_RKVENC,
|
||||
MPP_DRIVER_BUTT,
|
||||
};
|
||||
|
||||
struct mpp_request {
|
||||
__u32 *req;
|
||||
__u32 size;
|
||||
};
|
||||
|
||||
struct mpp_grf_info {
|
||||
u32 mode_ctrl;
|
||||
u32 mode_val;
|
||||
struct regmap *grf;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct for hardware info
|
||||
*/
|
||||
struct mpp_hw_info {
|
||||
/* register number */
|
||||
u32 reg_num;
|
||||
/* start index of register */
|
||||
u32 regidx_start;
|
||||
/* end index of register */
|
||||
u32 regidx_end;
|
||||
/* register of enable hardware */
|
||||
int regidx_en;
|
||||
};
|
||||
|
||||
struct mpp_trans_info {
|
||||
const int count;
|
||||
const char * const table;
|
||||
};
|
||||
|
||||
struct extra_info_elem {
|
||||
u32 index;
|
||||
u32 offset;
|
||||
};
|
||||
|
||||
struct extra_info_for_iommu {
|
||||
u32 magic;
|
||||
u32 cnt;
|
||||
struct extra_info_elem elem[20];
|
||||
};
|
||||
|
||||
struct mpp_dev_var {
|
||||
enum MPP_DEVICE_TYPE device_type;
|
||||
|
||||
/* info for each hardware */
|
||||
struct mpp_hw_info *hw_info;
|
||||
struct mpp_trans_info *trans_info;
|
||||
struct mpp_hw_ops *hw_ops;
|
||||
struct mpp_dev_ops *dev_ops;
|
||||
};
|
||||
|
||||
struct mpp_mem_region {
|
||||
struct list_head srv_lnk;
|
||||
struct list_head reg_lnk;
|
||||
struct list_head session_lnk;
|
||||
/* address for iommu */
|
||||
dma_addr_t iova;
|
||||
unsigned long len;
|
||||
u32 reg_idx;
|
||||
void *hdl;
|
||||
};
|
||||
|
||||
struct mpp_dma_session;
|
||||
|
||||
struct mpp_taskqueue;
|
||||
|
||||
struct mpp_dev {
|
||||
struct device *dev;
|
||||
const struct mpp_dev_var *var;
|
||||
struct mpp_hw_ops *hw_ops;
|
||||
struct mpp_dev_ops *dev_ops;
|
||||
|
||||
int irq;
|
||||
u32 irq_status;
|
||||
|
||||
void __iomem *reg_base;
|
||||
struct mpp_grf_info *grf_info;
|
||||
struct mpp_iommu_info *iommu_info;
|
||||
|
||||
struct rw_semaphore rw_sem;
|
||||
/* lock for reset */
|
||||
struct mutex reset_lock;
|
||||
atomic_t reset_request;
|
||||
atomic_t total_running;
|
||||
/* task for work queue */
|
||||
struct workqueue_struct *workq;
|
||||
struct work_struct work;
|
||||
/* set session max buffers */
|
||||
u32 session_max_buffers;
|
||||
/* point to MPP Service */
|
||||
struct mpp_taskqueue *queue;
|
||||
struct mpp_service *srv;
|
||||
};
|
||||
|
||||
struct mpp_task;
|
||||
|
||||
struct mpp_session {
|
||||
enum MPP_DEVICE_TYPE device_type;
|
||||
/* the session related device private data */
|
||||
struct mpp_service *srv;
|
||||
struct mpp_dev *mpp;
|
||||
struct mpp_dma_session *dma;
|
||||
|
||||
/* session tasks list lock */
|
||||
struct mutex lock;
|
||||
struct list_head pending;
|
||||
|
||||
DECLARE_KFIFO_PTR(done_fifo, struct mpp_task *);
|
||||
|
||||
wait_queue_head_t wait;
|
||||
pid_t pid;
|
||||
atomic_t task_running;
|
||||
};
|
||||
|
||||
/* The context for the a task */
|
||||
struct mpp_task {
|
||||
/* context belong to */
|
||||
struct mpp_session *session;
|
||||
|
||||
/* link to session pending */
|
||||
struct list_head session_link;
|
||||
/* link to service node pending */
|
||||
struct list_head service_link;
|
||||
/* The DMA buffer used in this task */
|
||||
struct list_head mem_region_list;
|
||||
|
||||
/* record context running start time */
|
||||
struct timeval start;
|
||||
};
|
||||
|
||||
struct mpp_taskqueue {
|
||||
/* taskqueue structure global lock */
|
||||
struct mutex lock;
|
||||
|
||||
struct list_head pending;
|
||||
atomic_t running;
|
||||
struct mpp_task *cur_task;
|
||||
struct mpp_service *srv;
|
||||
};
|
||||
|
||||
struct mpp_service {
|
||||
struct class *cls;
|
||||
struct device *dev;
|
||||
dev_t dev_id;
|
||||
struct cdev mpp_cdev;
|
||||
struct device *child_dev;
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
struct dentry *debugfs;
|
||||
#endif
|
||||
atomic_t shutdown_request;
|
||||
/* follows for device probe */
|
||||
struct mpp_grf_info grf_infos[MPP_DRIVER_BUTT];
|
||||
struct platform_driver *sub_drivers[MPP_DRIVER_BUTT];
|
||||
/* follows for attach service */
|
||||
struct mpp_dev *sub_devices[MPP_DEVICE_BUTT];
|
||||
struct mpp_taskqueue *task_queues[MPP_DEVICE_BUTT];
|
||||
};
|
||||
|
||||
/*
|
||||
* struct mpp_hw_ops - context specific operations for device
|
||||
* @init Do something when hardware probe.
|
||||
* @exit Do something when hardware remove.
|
||||
* @power_on Get pm and enable clks.
|
||||
* @power_off Put pm and disable clks.
|
||||
* @get_freq Get special freq for setting.
|
||||
* @set_freq Set freq to hardware.
|
||||
* @reduce_freq Reduce freq when hardware is not running.
|
||||
* @reset When error, reset hardware.
|
||||
*/
|
||||
struct mpp_hw_ops {
|
||||
int (*init)(struct mpp_dev *mpp);
|
||||
int (*exit)(struct mpp_dev *mpp);
|
||||
int (*power_on)(struct mpp_dev *mpp);
|
||||
int (*power_off)(struct mpp_dev *mpp);
|
||||
int (*get_freq)(struct mpp_dev *mpp,
|
||||
struct mpp_task *mpp_task);
|
||||
int (*set_freq)(struct mpp_dev *mpp,
|
||||
struct mpp_task *mpp_task);
|
||||
int (*reduce_freq)(struct mpp_dev *mpp);
|
||||
int (*reset)(struct mpp_dev *mpp);
|
||||
};
|
||||
|
||||
/*
|
||||
* struct mpp_dev_ops - context specific operations for task
|
||||
* @alloc_task Alloc and set task.
|
||||
* @prepare Check HW status for determining run next task or not.
|
||||
* @run Start a single {en,de}coding run. Set registers to hardware.
|
||||
* @irq Deal with hardware interrupt top-half.
|
||||
* @isr Deal with hardware interrupt bottom-half.
|
||||
* @finish Read back processing results and additional data from hardware.
|
||||
* @result Read status to userspace.
|
||||
* @free_task Release the resource allocate which alloc.
|
||||
* @ioctl Special cammand from userspace.
|
||||
* @open Open a instance for hardware when set client.
|
||||
* @release Specific instance release operation for hardware.
|
||||
* @free Specific instance free operation for hardware.
|
||||
*/
|
||||
struct mpp_dev_ops {
|
||||
void *(*alloc_task)(struct mpp_session *session,
|
||||
void __user *src, u32 size);
|
||||
int (*prepare)(struct mpp_dev *mpp, struct mpp_task *task);
|
||||
int (*run)(struct mpp_dev *mpp, struct mpp_task *task);
|
||||
int (*irq)(struct mpp_dev *mpp);
|
||||
int (*isr)(struct mpp_dev *mpp);
|
||||
int (*finish)(struct mpp_dev *mpp, struct mpp_task *task);
|
||||
int (*result)(struct mpp_dev *mpp, struct mpp_task *task,
|
||||
u32 __user *dst, u32 size);
|
||||
int (*free_task)(struct mpp_session *session,
|
||||
struct mpp_task *task);
|
||||
long (*ioctl)(struct mpp_session *isession,
|
||||
unsigned int cmd, unsigned long arg);
|
||||
struct mpp_session *(*init_session)(struct mpp_dev *mpp);
|
||||
int (*release_session)(struct mpp_session *session);
|
||||
};
|
||||
|
||||
/* It can handle the default ioctl */
|
||||
long mpp_dev_ioctl(struct file *filp, unsigned int cmd,
|
||||
unsigned long arg);
|
||||
#ifdef CONFIG_COMPAT
|
||||
long mpp_dev_compat_ioctl(struct file *filp, unsigned int cmd,
|
||||
unsigned long arg);
|
||||
#endif
|
||||
int mpp_dev_open(struct inode *inode, struct file *filp);
|
||||
int mpp_dev_release(struct inode *inode, struct file *filp);
|
||||
unsigned int mpp_dev_poll(struct file *filp, poll_table *wait);
|
||||
|
||||
struct mpp_mem_region *
|
||||
mpp_task_attach_fd(struct mpp_task *task, int fd);
|
||||
int mpp_translate_reg_address(struct mpp_dev *data,
|
||||
struct mpp_task *task,
|
||||
int fmt, u32 *reg);
|
||||
int mpp_translate_extra_info(struct mpp_task *task,
|
||||
struct extra_info_for_iommu *ext_inf,
|
||||
u32 *reg);
|
||||
|
||||
int mpp_task_init(struct mpp_session *session,
|
||||
struct mpp_task *task);
|
||||
int mpp_task_finish(struct mpp_session *session,
|
||||
struct mpp_task *task);
|
||||
int mpp_task_finalize(struct mpp_session *session,
|
||||
struct mpp_task *task);
|
||||
|
||||
int mpp_dev_probe(struct mpp_dev *mpp,
|
||||
struct platform_device *pdev);
|
||||
int mpp_dev_remove(struct mpp_dev *mpp);
|
||||
|
||||
irqreturn_t mpp_dev_irq(int irq, void *param);
|
||||
irqreturn_t mpp_dev_isr_sched(int irq, void *param);
|
||||
|
||||
int mpp_safe_reset(struct reset_control *rst);
|
||||
int mpp_safe_unreset(struct reset_control *rst);
|
||||
|
||||
int mpp_set_grf(struct mpp_grf_info *grf_info);
|
||||
|
||||
int mpp_time_record(struct mpp_task *task);
|
||||
int mpp_time_diff(struct mpp_task *task);
|
||||
|
||||
int mpp_dump_reg(u32 *regs, u32 start_idx, u32 end_idx);
|
||||
|
||||
static inline int mpp_write(struct mpp_dev *mpp, u32 reg, u32 val)
|
||||
{
|
||||
int idx = reg / sizeof(u32);
|
||||
|
||||
mpp_debug(DEBUG_SET_REG, "write reg[%d]: %08x\n", idx, val);
|
||||
writel(val, mpp->reg_base + reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int mpp_write_relaxed(struct mpp_dev *mpp, u32 reg, u32 val)
|
||||
{
|
||||
int idx = reg / sizeof(u32);
|
||||
|
||||
mpp_debug(DEBUG_SET_REG, "write reg[%d]: %08x\n", idx, val);
|
||||
writel_relaxed(val, mpp->reg_base + reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline u32 mpp_read(struct mpp_dev *mpp, u32 reg)
|
||||
{
|
||||
int idx = reg / sizeof(u32);
|
||||
u32 val = readl(mpp->reg_base + reg);
|
||||
|
||||
mpp_debug(DEBUG_GET_REG, "read reg[%d] 0x%x: %08x\n", idx, reg, val);
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
static inline u32 mpp_read_relaxed(struct mpp_dev *mpp, u32 reg)
|
||||
{
|
||||
int idx = reg / sizeof(u32);
|
||||
u32 val = readl_relaxed(mpp->reg_base + reg);
|
||||
|
||||
mpp_debug(DEBUG_GET_REG, "read reg[%d] 0x%x: %08x\n", idx, reg, val);
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
extern struct platform_driver rockchip_rkvdec_driver;
|
||||
extern struct platform_driver rockchip_rkvenc_driver;
|
||||
extern struct platform_driver rockchip_vdpu1_driver;
|
||||
extern struct platform_driver rockchip_vepu1_driver;
|
||||
extern struct platform_driver rockchip_vdpu2_driver;
|
||||
extern struct platform_driver rockchip_vepu2_driver;
|
||||
extern struct platform_driver rockchip_vepu22_driver;
|
||||
|
||||
#endif
|
||||
87
drivers/video/rockchip/mpp/mpp_debug.h
Normal file
87
drivers/video/rockchip/mpp/mpp_debug.h
Normal file
@@ -0,0 +1,87 @@
|
||||
/* SPDX-License-Identifier: (GPL-2.0+ OR MIT) */
|
||||
/*
|
||||
* Copyright (c) 2019 Fuzhou Rockchip Electronics Co., Ltd
|
||||
*
|
||||
* author:
|
||||
* Alpha Lin, alpha.lin@rock-chips.com
|
||||
* Randy Li, randy.li@rock-chips.com
|
||||
* Ding Wei, leo.ding@rock-chips.com
|
||||
*
|
||||
*/
|
||||
#ifndef __ROCKCHIP_MPP_DEBUG_H__
|
||||
#define __ROCKCHIP_MPP_DEBUG_H__
|
||||
|
||||
#include <linux/types.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_DUMP_ERR_REG 0x00000400
|
||||
#define DEBUG_LINK_TABLE 0x00000800
|
||||
|
||||
#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 DEBUG_RESET 0x00020000
|
||||
|
||||
#define PRINT_FUNCTION 0x80000000
|
||||
#define PRINT_LINE 0x40000000
|
||||
|
||||
extern unsigned int mpp_dev_debug;
|
||||
|
||||
#define mpp_debug_unlikely(type) \
|
||||
(unlikely(mpp_dev_debug & type))
|
||||
|
||||
#define mpp_debug_func(type, fmt, args...) \
|
||||
do { \
|
||||
if (unlikely(mpp_dev_debug & type)) { \
|
||||
pr_info("%s:%d: " fmt, \
|
||||
__func__, __LINE__, ##args); \
|
||||
} \
|
||||
} while (0)
|
||||
#define mpp_debug(type, fmt, args...) \
|
||||
do { \
|
||||
if (unlikely(mpp_dev_debug & type)) { \
|
||||
pr_info(fmt, ##args); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define mpp_debug_enter() \
|
||||
do { \
|
||||
if (unlikely(mpp_dev_debug & DEBUG_FUNCTION)) { \
|
||||
pr_info("%s:%d: enter\n", \
|
||||
__func__, __LINE__); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define mpp_debug_leave() \
|
||||
do { \
|
||||
if (unlikely(mpp_dev_debug & DEBUG_FUNCTION)) { \
|
||||
pr_info("%s:%d: leave\n", \
|
||||
__func__, __LINE__); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define mpp_err(fmt, args...) \
|
||||
pr_err("%s:%d: " fmt, __func__, __LINE__, ##args)
|
||||
|
||||
#endif
|
||||
391
drivers/video/rockchip/mpp/mpp_iommu.c
Normal file
391
drivers/video/rockchip/mpp/mpp_iommu.c
Normal file
@@ -0,0 +1,391 @@
|
||||
// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
|
||||
/*
|
||||
* Copyright (c) 2019 Fuzhou Rockchip Electronics Co., Ltd
|
||||
*
|
||||
* author:
|
||||
* Alpha Lin, alpha.lin@rock-chips.com
|
||||
* Randy Li, randy.li@rock-chips.com
|
||||
* Ding Wei, leo.ding@rock-chips.com
|
||||
*
|
||||
*/
|
||||
#include <linux/dma-buf.h>
|
||||
#include <linux/dma-iommu.h>
|
||||
#include <linux/iommu.h>
|
||||
#include <linux/kref.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include "mpp_iommu.h"
|
||||
|
||||
static struct mpp_dma_buffer *
|
||||
mpp_dma_find_buffer_fd(struct mpp_dma_session *session, int fd)
|
||||
{
|
||||
struct dma_buf *dmabuf;
|
||||
struct mpp_dma_buffer *out = NULL;
|
||||
struct mpp_dma_buffer *buffer = NULL, *n;
|
||||
|
||||
dmabuf = dma_buf_get(fd);
|
||||
if (IS_ERR(dmabuf))
|
||||
return NULL;
|
||||
|
||||
list_for_each_entry_safe(buffer, n,
|
||||
&session->buffer_list, list) {
|
||||
/*
|
||||
* As long as the last reference is hold by the buffer pool,
|
||||
* the same fd won't be assigned to the other application.
|
||||
*/
|
||||
if (buffer->fd == fd &&
|
||||
buffer->dmabuf == dmabuf) {
|
||||
out = buffer;
|
||||
break;
|
||||
}
|
||||
}
|
||||
dma_buf_put(dmabuf);
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
/* Release the buffer from the current list */
|
||||
static void mpp_dma_release_buffer(struct kref *ref)
|
||||
{
|
||||
struct mpp_dma_buffer *buffer =
|
||||
container_of(ref, struct mpp_dma_buffer, ref);
|
||||
|
||||
mutex_lock(&buffer->session->list_mutex);
|
||||
buffer->session->buffer_count--;
|
||||
list_del_init(&buffer->list);
|
||||
mutex_unlock(&buffer->session->list_mutex);
|
||||
|
||||
dma_buf_unmap_attachment(buffer->attach, buffer->sgt, buffer->dir);
|
||||
dma_buf_detach(buffer->dmabuf, buffer->attach);
|
||||
dma_buf_put(buffer->dmabuf);
|
||||
kfree(buffer);
|
||||
}
|
||||
|
||||
/* Remove the oldest buffer when count more than the setting */
|
||||
static int
|
||||
mpp_dma_remove_extra_buffer(struct mpp_dma_session *session)
|
||||
{
|
||||
struct mpp_dma_buffer *n;
|
||||
struct mpp_dma_buffer *oldest = NULL, *buffer = NULL;
|
||||
ktime_t oldest_time = ktime_set(0, 0);
|
||||
|
||||
if (session->buffer_count > session->max_buffers) {
|
||||
list_for_each_entry_safe(buffer, n,
|
||||
&session->buffer_list,
|
||||
list) {
|
||||
if (ktime_to_ns(oldest_time) == 0 ||
|
||||
ktime_after(oldest_time, buffer->last_used)) {
|
||||
oldest_time = buffer->last_used;
|
||||
oldest = buffer;
|
||||
}
|
||||
}
|
||||
kref_put(&oldest->ref, mpp_dma_release_buffer);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mpp_dma_release(struct mpp_dma_session *session,
|
||||
struct mpp_dma_buffer *buffer)
|
||||
{
|
||||
if (IS_ERR_OR_NULL(buffer))
|
||||
return -EINVAL;
|
||||
|
||||
kref_put(&buffer->ref, mpp_dma_release_buffer);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mpp_dma_release_fd(struct mpp_dma_session *session, int fd)
|
||||
{
|
||||
struct device *dev = session->dev;
|
||||
struct mpp_dma_buffer *buffer = NULL;
|
||||
|
||||
buffer = mpp_dma_find_buffer_fd(session, fd);
|
||||
if (IS_ERR_OR_NULL(buffer)) {
|
||||
dev_err(dev, "can not find %d buffer in list\n", fd);
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
kref_put(&buffer->ref, mpp_dma_release_buffer);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct mpp_dma_buffer *
|
||||
mpp_dma_alloc(struct mpp_dma_session *session, size_t size)
|
||||
{
|
||||
size_t align_size;
|
||||
dma_addr_t iova;
|
||||
struct mpp_dma_buffer *buffer;
|
||||
|
||||
buffer = kzalloc(sizeof(*buffer), GFP_KERNEL);
|
||||
if (!buffer)
|
||||
return NULL;
|
||||
|
||||
align_size = PAGE_ALIGN(size);
|
||||
buffer->vaddr = dma_alloc_coherent(session->dev,
|
||||
align_size,
|
||||
&iova,
|
||||
GFP_KERNEL);
|
||||
if (!buffer->vaddr)
|
||||
goto fail_dma_alloc;
|
||||
|
||||
buffer->size = PAGE_ALIGN(size);
|
||||
buffer->iova = iova;
|
||||
|
||||
return buffer;
|
||||
fail_dma_alloc:
|
||||
kfree(buffer);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int mpp_dma_free(struct mpp_dma_session *session,
|
||||
struct mpp_dma_buffer *buffer)
|
||||
{
|
||||
dma_free_coherent(session->dev, buffer->size,
|
||||
buffer->vaddr, buffer->iova);
|
||||
buffer->vaddr = NULL;
|
||||
buffer->iova = 0;
|
||||
buffer->size = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct mpp_dma_buffer *
|
||||
mpp_dma_import_fd(struct mpp_dma_session *session, int fd)
|
||||
{
|
||||
int ret = 0;
|
||||
struct sg_table *sgt;
|
||||
struct dma_buf *dmabuf;
|
||||
struct mpp_dma_buffer *buffer;
|
||||
struct dma_buf_attachment *attach;
|
||||
|
||||
if (!session)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
/* remove the oldest before add buffer */
|
||||
mpp_dma_remove_extra_buffer(session);
|
||||
|
||||
dmabuf = dma_buf_get(fd);
|
||||
if (IS_ERR(dmabuf))
|
||||
return NULL;
|
||||
|
||||
/* Check whether in session */
|
||||
buffer = mpp_dma_find_buffer_fd(session, fd);
|
||||
if (!IS_ERR_OR_NULL(buffer)) {
|
||||
if (buffer->dmabuf == dmabuf) {
|
||||
if (kref_get_unless_zero(&buffer->ref)) {
|
||||
buffer->last_used = ktime_get();
|
||||
dma_buf_put(dmabuf);
|
||||
return buffer;
|
||||
}
|
||||
}
|
||||
dev_dbg(session->dev, "missing the fd %d\n", fd);
|
||||
kref_put(&buffer->ref, mpp_dma_release_buffer);
|
||||
}
|
||||
|
||||
/* A new DMA buffer */
|
||||
buffer = kzalloc(sizeof(*buffer), GFP_KERNEL);
|
||||
if (!buffer) {
|
||||
ret = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
buffer->dmabuf = dmabuf;
|
||||
buffer->fd = fd;
|
||||
buffer->dir = DMA_BIDIRECTIONAL;
|
||||
buffer->last_used = ktime_get();
|
||||
|
||||
attach = dma_buf_attach(buffer->dmabuf, session->dev);
|
||||
if (IS_ERR(attach)) {
|
||||
ret = PTR_ERR(attach);
|
||||
goto fail_attach;
|
||||
}
|
||||
|
||||
sgt = dma_buf_map_attachment(attach, buffer->dir);
|
||||
if (IS_ERR(sgt)) {
|
||||
ret = PTR_ERR(sgt);
|
||||
goto fail_map;
|
||||
}
|
||||
|
||||
buffer->iova = sg_dma_address(sgt->sgl);
|
||||
buffer->size = sg_dma_len(sgt->sgl);
|
||||
|
||||
buffer->attach = attach;
|
||||
buffer->sgt = sgt;
|
||||
buffer->session = session;
|
||||
|
||||
kref_init(&buffer->ref);
|
||||
/* Increase the reference for used outside the buffer pool */
|
||||
kref_get(&buffer->ref);
|
||||
INIT_LIST_HEAD(&buffer->list);
|
||||
|
||||
mutex_lock(&session->list_mutex);
|
||||
session->buffer_count++;
|
||||
list_add_tail(&buffer->list, &session->buffer_list);
|
||||
mutex_unlock(&session->list_mutex);
|
||||
|
||||
return buffer;
|
||||
|
||||
fail_map:
|
||||
dma_buf_detach(buffer->dmabuf, attach);
|
||||
fail_attach:
|
||||
kfree(buffer);
|
||||
fail:
|
||||
dma_buf_put(dmabuf);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
int mpp_dma_unmap_kernel(struct mpp_dma_session *session,
|
||||
struct mpp_dma_buffer *buffer)
|
||||
{
|
||||
void *vaddr = buffer->vaddr;
|
||||
struct dma_buf *dmabuf = buffer->dmabuf;
|
||||
|
||||
if (IS_ERR_OR_NULL(vaddr) ||
|
||||
IS_ERR_OR_NULL(dmabuf))
|
||||
return -EINVAL;
|
||||
|
||||
dma_buf_vunmap(dmabuf, vaddr);
|
||||
buffer->vaddr = NULL;
|
||||
|
||||
dma_buf_end_cpu_access(dmabuf, DMA_FROM_DEVICE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mpp_dma_map_kernel(struct mpp_dma_session *session,
|
||||
struct mpp_dma_buffer *buffer)
|
||||
{
|
||||
int ret;
|
||||
void *vaddr;
|
||||
struct dma_buf *dmabuf = buffer->dmabuf;
|
||||
|
||||
if (IS_ERR_OR_NULL(dmabuf))
|
||||
return -EINVAL;
|
||||
|
||||
ret = dma_buf_begin_cpu_access(dmabuf, DMA_FROM_DEVICE);
|
||||
if (ret) {
|
||||
dev_dbg(session->dev, "can't access the dma buffer\n");
|
||||
goto failed_access;
|
||||
}
|
||||
|
||||
vaddr = dma_buf_vmap(dmabuf);
|
||||
if (!vaddr) {
|
||||
dev_dbg(session->dev, "can't vmap the dma buffer\n");
|
||||
ret = -EIO;
|
||||
goto failed_vmap;
|
||||
}
|
||||
|
||||
buffer->vaddr = vaddr;
|
||||
|
||||
return 0;
|
||||
|
||||
failed_vmap:
|
||||
dma_buf_end_cpu_access(dmabuf, DMA_FROM_DEVICE);
|
||||
failed_access:
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int mpp_dma_session_destroy(struct mpp_dma_session *session)
|
||||
{
|
||||
struct mpp_dma_buffer *n, *buffer = NULL;
|
||||
|
||||
if (!session)
|
||||
return -EINVAL;
|
||||
|
||||
list_for_each_entry_safe(buffer, n,
|
||||
&session->buffer_list,
|
||||
list) {
|
||||
kref_put(&buffer->ref, mpp_dma_release_buffer);
|
||||
}
|
||||
|
||||
kfree(session);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct mpp_dma_session *
|
||||
mpp_dma_session_create(struct device *dev)
|
||||
{
|
||||
struct mpp_dma_session *session = NULL;
|
||||
|
||||
session = kzalloc(sizeof(*session), GFP_KERNEL);
|
||||
if (!session)
|
||||
return session;
|
||||
|
||||
INIT_LIST_HEAD(&session->buffer_list);
|
||||
mutex_init(&session->list_mutex);
|
||||
|
||||
session->dev = dev;
|
||||
|
||||
return session;
|
||||
}
|
||||
|
||||
int mpp_iommu_detach(struct mpp_iommu_info *info)
|
||||
{
|
||||
struct iommu_domain *domain = info->domain;
|
||||
struct iommu_group *group = info->group;
|
||||
|
||||
iommu_detach_group(domain, group);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mpp_iommu_attach(struct mpp_iommu_info *info)
|
||||
{
|
||||
struct iommu_domain *domain = info->domain;
|
||||
struct iommu_group *group = info->group;
|
||||
int ret;
|
||||
|
||||
ret = iommu_attach_group(domain, group);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct mpp_iommu_info *
|
||||
mpp_iommu_probe(struct device *dev)
|
||||
{
|
||||
struct mpp_iommu_info *info = NULL;
|
||||
int ret = 0;
|
||||
|
||||
info = kzalloc(sizeof(*info), GFP_KERNEL);
|
||||
if (!info) {
|
||||
ret = -ENOMEM;
|
||||
goto err;
|
||||
}
|
||||
|
||||
info->group = iommu_group_get(dev);
|
||||
if (!info->group) {
|
||||
ret = -EINVAL;
|
||||
goto err_free_info;
|
||||
}
|
||||
|
||||
info->domain = iommu_get_domain_for_dev(dev);
|
||||
if (!info->domain) {
|
||||
ret = -EINVAL;
|
||||
goto err_put_group;
|
||||
}
|
||||
|
||||
return info;
|
||||
|
||||
err_put_group:
|
||||
iommu_group_put(info->group);
|
||||
err_free_info:
|
||||
kfree(info);
|
||||
err:
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
int mpp_iommu_remove(struct mpp_iommu_info *info)
|
||||
{
|
||||
iommu_group_put(info->group);
|
||||
kfree(info);
|
||||
|
||||
return 0;
|
||||
}
|
||||
81
drivers/video/rockchip/mpp/mpp_iommu.h
Normal file
81
drivers/video/rockchip/mpp/mpp_iommu.h
Normal file
@@ -0,0 +1,81 @@
|
||||
/* SPDX-License-Identifier: (GPL-2.0+ OR MIT) */
|
||||
/*
|
||||
* Copyright (c) 2019 Fuzhou Rockchip Electronics Co., Ltd
|
||||
*
|
||||
* author:
|
||||
* Alpha Lin, alpha.lin@rock-chips.com
|
||||
* Randy Li, randy.li@rock-chips.com
|
||||
* Ding Wei, leo.ding@rock-chips.com
|
||||
*
|
||||
*/
|
||||
#ifndef __ROCKCHIP_MPP_IOMMU_H__
|
||||
#define __ROCKCHIP_MPP_IOMMU_H__
|
||||
|
||||
#include <linux/iommu.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
|
||||
struct mpp_dma_buffer {
|
||||
struct list_head list;
|
||||
struct mpp_dma_session *session;
|
||||
|
||||
/* DMABUF information */
|
||||
struct dma_buf *dmabuf;
|
||||
struct dma_buf_attachment *attach;
|
||||
struct sg_table *sgt;
|
||||
struct sg_table *copy_sgt;
|
||||
enum dma_data_direction dir;
|
||||
|
||||
int fd;
|
||||
dma_addr_t iova;
|
||||
unsigned long size;
|
||||
void *vaddr;
|
||||
|
||||
struct kref ref;
|
||||
ktime_t last_used;
|
||||
};
|
||||
|
||||
struct mpp_dma_session {
|
||||
struct list_head buffer_list;
|
||||
/* the mutex for the above buffer list */
|
||||
struct mutex list_mutex;
|
||||
/* the max buffer num for the buffer list */
|
||||
u32 max_buffers;
|
||||
/* the count for the buffer list */
|
||||
int buffer_count;
|
||||
|
||||
struct device *dev;
|
||||
};
|
||||
|
||||
struct mpp_iommu_info {
|
||||
struct iommu_domain *domain;
|
||||
struct iommu_group *group;
|
||||
};
|
||||
|
||||
struct mpp_dma_session *
|
||||
mpp_dma_session_create(struct device *dev);
|
||||
int mpp_dma_session_destroy(struct mpp_dma_session *session);
|
||||
|
||||
struct mpp_dma_buffer *
|
||||
mpp_dma_alloc(struct mpp_dma_session *session, size_t size);
|
||||
int mpp_dma_free(struct mpp_dma_session *session,
|
||||
struct mpp_dma_buffer *buffer);
|
||||
|
||||
struct mpp_dma_buffer *
|
||||
mpp_dma_import_fd(struct mpp_dma_session *session, int fd);
|
||||
int mpp_dma_release(struct mpp_dma_session *session,
|
||||
struct mpp_dma_buffer *buffer);
|
||||
int mpp_dma_release_fd(struct mpp_dma_session *session, int fd);
|
||||
|
||||
int mpp_dma_unmap_kernel(struct mpp_dma_session *session,
|
||||
struct mpp_dma_buffer *buffer);
|
||||
int mpp_dma_map_kernel(struct mpp_dma_session *session,
|
||||
struct mpp_dma_buffer *buffer);
|
||||
|
||||
struct mpp_iommu_info *
|
||||
mpp_iommu_probe(struct device *dev);
|
||||
int mpp_iommu_remove(struct mpp_iommu_info *info);
|
||||
|
||||
int mpp_iommu_attach(struct mpp_iommu_info *info);
|
||||
int mpp_iommu_detach(struct mpp_iommu_info *info);
|
||||
|
||||
#endif
|
||||
1600
drivers/video/rockchip/mpp/mpp_rkvdec.c
Normal file
1600
drivers/video/rockchip/mpp/mpp_rkvdec.c
Normal file
File diff suppressed because it is too large
Load Diff
303
drivers/video/rockchip/mpp/mpp_service.c
Normal file
303
drivers/video/rockchip/mpp/mpp_service.c
Normal file
@@ -0,0 +1,303 @@
|
||||
// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
|
||||
/*
|
||||
* Copyright (c) 2019 Fuzhou Rockchip Electronics Co., Ltd
|
||||
*
|
||||
* author:
|
||||
* Alpha Lin, alpha.lin@rock-chips.com
|
||||
* Randy Li, randy.li@rock-chips.com
|
||||
* Ding Wei, leo.ding@rock-chips.com
|
||||
*
|
||||
*/
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/completion.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
|
||||
#include "mpp_debug.h"
|
||||
#include "mpp_common.h"
|
||||
#include "mpp_iommu.h"
|
||||
|
||||
#define MPP_CLASS_NAME "mpp_class"
|
||||
#define MPP_SERVICE_NAME "mpp_service"
|
||||
|
||||
#define MPP_REGISTER_DRIVER(X, x) {\
|
||||
if (IS_ENABLED(CONFIG_ROCKCHIP_MPP_##X))\
|
||||
mpp_add_driver(MPP_DRIVER_##X, &rockchip_##x##_driver);\
|
||||
}
|
||||
|
||||
unsigned int mpp_dev_debug;
|
||||
module_param(mpp_dev_debug, uint, 0644);
|
||||
MODULE_PARM_DESC(mpp_dev_debug, "bit switch for mpp debug information");
|
||||
|
||||
static struct mpp_grf_info *mpp_grf_infos;
|
||||
static struct platform_driver **mpp_sub_drivers;
|
||||
|
||||
static int mpp_init_grf(struct device_node *np,
|
||||
const char *name,
|
||||
struct mpp_grf_info *grf_info)
|
||||
{
|
||||
grf_info->grf = syscon_regmap_lookup_by_phandle(np, name);
|
||||
if (IS_ERR_OR_NULL(grf_info->grf)) {
|
||||
grf_info->grf = NULL;
|
||||
} else {
|
||||
of_property_read_u32_index(np, name, 1,
|
||||
&grf_info->mode_ctrl);
|
||||
of_property_read_u32_index(np, name, 2,
|
||||
&grf_info->mode_val);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mpp_init_drvdata(struct mpp_taskqueue *queue,
|
||||
struct mpp_service *srv)
|
||||
{
|
||||
mutex_init(&queue->lock);
|
||||
atomic_set(&queue->running, 0);
|
||||
INIT_LIST_HEAD(&queue->pending);
|
||||
|
||||
queue->srv = srv;
|
||||
queue->cur_task = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct file_operations mpp_dev_fops = {
|
||||
.unlocked_ioctl = mpp_dev_ioctl,
|
||||
.open = mpp_dev_open,
|
||||
.release = mpp_dev_release,
|
||||
.poll = mpp_dev_poll,
|
||||
#ifdef CONFIG_COMPAT
|
||||
.compat_ioctl = mpp_dev_compat_ioctl,
|
||||
#endif
|
||||
};
|
||||
|
||||
static int mpp_register_service(struct mpp_service *srv,
|
||||
const char *service_name)
|
||||
{
|
||||
int ret;
|
||||
struct device *dev = srv->dev;
|
||||
|
||||
/* create a device */
|
||||
ret = alloc_chrdev_region(&srv->dev_id, 0, 1, service_name);
|
||||
if (ret) {
|
||||
dev_err(dev, "alloc dev_t failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
cdev_init(&srv->mpp_cdev, &mpp_dev_fops);
|
||||
srv->mpp_cdev.owner = THIS_MODULE;
|
||||
srv->mpp_cdev.ops = &mpp_dev_fops;
|
||||
|
||||
ret = cdev_add(&srv->mpp_cdev, srv->dev_id, 1);
|
||||
if (ret) {
|
||||
unregister_chrdev_region(srv->dev_id, 1);
|
||||
dev_err(dev, "add device failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
srv->child_dev = device_create(srv->cls, dev, srv->dev_id,
|
||||
NULL, "%s", service_name);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mpp_remove_service(struct mpp_service *srv)
|
||||
{
|
||||
device_destroy(srv->cls, srv->dev_id);
|
||||
cdev_del(&srv->mpp_cdev);
|
||||
unregister_chrdev_region(srv->dev_id, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mpp_debugfs_remove(struct mpp_service *srv)
|
||||
{
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
debugfs_remove_recursive(srv->debugfs);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mpp_debugfs_init(struct mpp_service *srv)
|
||||
{
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
srv->debugfs = debugfs_create_dir(MPP_SERVICE_NAME, NULL);
|
||||
if (IS_ERR_OR_NULL(srv->debugfs)) {
|
||||
mpp_err("failed on open debugfs\n");
|
||||
srv->debugfs = NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mpp_service_probe(struct platform_device *pdev)
|
||||
{
|
||||
int ret;
|
||||
u32 taskqueue_cnt;
|
||||
struct mpp_service *srv = NULL;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *np = dev->of_node;
|
||||
|
||||
dev_info(dev, "probe start\n");
|
||||
srv = devm_kzalloc(dev, sizeof(*srv), GFP_KERNEL);
|
||||
if (!srv)
|
||||
return -ENOMEM;
|
||||
|
||||
srv->dev = dev;
|
||||
atomic_set(&srv->shutdown_request, 0);
|
||||
platform_set_drvdata(pdev, srv);
|
||||
|
||||
srv->cls = class_create(THIS_MODULE, MPP_CLASS_NAME);
|
||||
if (PTR_ERR_OR_ZERO(srv->cls))
|
||||
return PTR_ERR(srv->cls);
|
||||
|
||||
of_property_read_u32(np, "rockchip,taskqueue-count",
|
||||
&taskqueue_cnt);
|
||||
if (taskqueue_cnt > MPP_DEVICE_BUTT) {
|
||||
dev_err(dev, "rockchip,taskqueue-count %d must less than %d\n",
|
||||
taskqueue_cnt, MPP_DEVICE_BUTT);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (taskqueue_cnt) {
|
||||
u32 i = 0;
|
||||
struct mpp_taskqueue *queue;
|
||||
|
||||
for (i = 0; i < taskqueue_cnt; i++) {
|
||||
queue = devm_kzalloc(dev, sizeof(*queue), GFP_KERNEL);
|
||||
if (!queue)
|
||||
continue;
|
||||
|
||||
mpp_init_drvdata(queue, srv);
|
||||
srv->task_queues[i] = queue;
|
||||
}
|
||||
}
|
||||
|
||||
mpp_init_grf(np, "rkvdec,grf", &srv->grf_infos[MPP_DRIVER_RKVDEC]);
|
||||
mpp_init_grf(np, "rkvenc,grf", &srv->grf_infos[MPP_DRIVER_RKVENC]);
|
||||
mpp_init_grf(np, "vdpu1,grf", &srv->grf_infos[MPP_DRIVER_VDPU1]);
|
||||
mpp_init_grf(np, "vepu1,grf", &srv->grf_infos[MPP_DRIVER_VEPU1]);
|
||||
mpp_init_grf(np, "vdpu2,grf", &srv->grf_infos[MPP_DRIVER_VDPU2]);
|
||||
mpp_init_grf(np, "vepu2,grf", &srv->grf_infos[MPP_DRIVER_VEPU2]);
|
||||
mpp_init_grf(np, "vepu22,grf", &srv->grf_infos[MPP_DRIVER_VEPU22]);
|
||||
mpp_grf_infos = srv->grf_infos;
|
||||
|
||||
ret = mpp_register_service(srv, MPP_SERVICE_NAME);
|
||||
if (ret) {
|
||||
dev_err(dev, "register %s device\n", MPP_SERVICE_NAME);
|
||||
goto fail_register;
|
||||
}
|
||||
mpp_sub_drivers = srv->sub_drivers;
|
||||
|
||||
mpp_debugfs_init(srv);
|
||||
|
||||
dev_info(dev, "probe success\n");
|
||||
|
||||
return 0;
|
||||
|
||||
fail_register:
|
||||
class_destroy(srv->cls);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mpp_service_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct mpp_service *srv = platform_get_drvdata(pdev);
|
||||
|
||||
dev_info(dev, "remove device\n");
|
||||
|
||||
mpp_remove_service(srv);
|
||||
class_destroy(srv->cls);
|
||||
mpp_debugfs_remove(srv);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id mpp_dt_ids[] = {
|
||||
{
|
||||
.compatible = "rockchip,mpp-service",
|
||||
},
|
||||
{ },
|
||||
};
|
||||
|
||||
static struct platform_driver mpp_service_driver = {
|
||||
.probe = mpp_service_probe,
|
||||
.remove = mpp_service_remove,
|
||||
.driver = {
|
||||
.name = "mpp_service",
|
||||
.of_match_table = of_match_ptr(mpp_dt_ids),
|
||||
},
|
||||
};
|
||||
|
||||
static int mpp_add_driver(enum MPP_DRIVER_TYPE type,
|
||||
struct platform_driver *driver)
|
||||
{
|
||||
int ret;
|
||||
|
||||
mpp_set_grf(&mpp_grf_infos[type]);
|
||||
|
||||
ret = platform_driver_register(driver);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
mpp_sub_drivers[type] = driver;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mpp_remove_driver(int i, struct platform_driver *driver)
|
||||
{
|
||||
if (driver) {
|
||||
mpp_set_grf(&mpp_grf_infos[i]);
|
||||
platform_driver_unregister(driver);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init mpp_service_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = platform_driver_register(&mpp_service_driver);
|
||||
if (ret) {
|
||||
pr_err("Mpp service device register failed (%d).\n", ret);
|
||||
return ret;
|
||||
}
|
||||
MPP_REGISTER_DRIVER(RKVDEC, rkvdec);
|
||||
MPP_REGISTER_DRIVER(RKVENC, rkvenc);
|
||||
MPP_REGISTER_DRIVER(VDPU1, vdpu1);
|
||||
MPP_REGISTER_DRIVER(VEPU1, vepu1);
|
||||
MPP_REGISTER_DRIVER(VDPU2, vdpu2);
|
||||
MPP_REGISTER_DRIVER(VEPU2, vepu2);
|
||||
MPP_REGISTER_DRIVER(VEPU22, vepu22);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit mpp_service_exit(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < MPP_DRIVER_BUTT; i++)
|
||||
mpp_remove_driver(i, mpp_sub_drivers[i]);
|
||||
|
||||
platform_driver_unregister(&mpp_service_driver);
|
||||
}
|
||||
|
||||
module_init(mpp_service_init);
|
||||
module_exit(mpp_service_exit);
|
||||
|
||||
MODULE_LICENSE("Dual MIT/GPL");
|
||||
MODULE_VERSION("1.0.build.201911131848");
|
||||
MODULE_AUTHOR("Ding Wei leo.ding@rock-chips.com");
|
||||
MODULE_DESCRIPTION("Rockchip mpp service driver");
|
||||
769
drivers/video/rockchip/mpp/mpp_vdpu1.c
Normal file
769
drivers/video/rockchip/mpp/mpp_vdpu1.c
Normal file
@@ -0,0 +1,769 @@
|
||||
// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
|
||||
/*
|
||||
* Copyright (c) 2019 Fuzhou Rockchip Electronics Co., Ltd
|
||||
*
|
||||
* author:
|
||||
* Alpha Lin, alpha.lin@rock-chips.com
|
||||
* Randy Li, randy.li@rock-chips.com
|
||||
* Ding Wei, leo.ding@rock-chips.com
|
||||
*
|
||||
*/
|
||||
#include <asm/cacheflush.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <soc/rockchip/pm_domains.h>
|
||||
|
||||
#include "mpp_debug.h"
|
||||
#include "mpp_common.h"
|
||||
#include "mpp_iommu.h"
|
||||
|
||||
#define VDPU1_DRIVER_NAME "mpp_vdpu1"
|
||||
|
||||
#define VDPU1_SESSION_MAX_BUFFERS 40
|
||||
/* The maximum registers number of all the version */
|
||||
#define VDPU1_REG_NUM 60
|
||||
#define VDPU1_REG_START_INDEX 0
|
||||
#define VDPU1_REG_END_INDEX 59
|
||||
|
||||
#define VDPU1_REG_PP_NUM 101
|
||||
#define VDPU1_REG_PP_START_INDEX 0
|
||||
#define VDPU1_REG_PP_END_INDEX 100
|
||||
|
||||
#define VDPU1_REG_DEC_INT_EN 0x004
|
||||
#define VDPU1_REG_DEC_INT_EN_INDEX (1)
|
||||
/* B slice detected, used in 8190 decoder and later */
|
||||
#define VDPU1_INT_PIC_INF BIT(24)
|
||||
#define VDPU1_INT_TIMEOUT BIT(18)
|
||||
#define VDPU1_INT_SLICE BIT(17)
|
||||
#define VDPU1_INT_STRM_ERROR BIT(16)
|
||||
#define VDPU1_INT_ASO_ERROR BIT(15)
|
||||
#define VDPU1_INT_BUF_EMPTY BIT(14)
|
||||
#define VDPU1_INT_BUS_ERROR BIT(13)
|
||||
#define VDPU1_DEC_INT BIT(12)
|
||||
#define VDPU1_DEC_INT_RAW BIT(8)
|
||||
#define VDPU1_DEC_IRQ_DIS BIT(4)
|
||||
#define VDPU1_DEC_START BIT(0)
|
||||
|
||||
/* NOTE: Don't enable it or decoding AVC would meet problem at rk3288 */
|
||||
#define VDPU1_REG_DEC_EN 0x008
|
||||
#define VDPU1_CLOCK_GATE_EN BIT(10)
|
||||
|
||||
#define VDPU1_REG_SYS_CTRL 0x00c
|
||||
#define VDPU1_REG_SYS_CTRL_INDEX (3)
|
||||
#define VDPU1_GET_FORMAT(x) (((x) >> 28) & 0xf)
|
||||
#define VDPU1_FMT_H264D 0
|
||||
#define VDPU1_FMT_MPEG4D 1
|
||||
#define VDPU1_FMT_H263D 2
|
||||
#define VDPU1_FMT_JPEGD 3
|
||||
#define VDPU1_FMT_VC1D 4
|
||||
#define VDPU1_FMT_MPEG2D 5
|
||||
#define VDPU1_FMT_MPEG1D 6
|
||||
#define VDPU1_FMT_VP6D 7
|
||||
#define VDPU1_FMT_RESERVED 8
|
||||
#define VDPU1_FMT_VP7D 9
|
||||
#define VDPU1_FMT_VP8D 10
|
||||
#define VDPU1_FMT_AVSD 11
|
||||
|
||||
#define VDPU1_REG_STREAM_RLC_BASE 0x030
|
||||
#define VDPU1_REG_STREAM_RLC_BASE_INDEX (12)
|
||||
|
||||
#define VDPU1_REG_DIR_MV_BASE 0x0a4
|
||||
#define VDPU1_REG_DIR_MV_BASE_INDEX (41)
|
||||
|
||||
#define VDPU1_REG_CLR_CACHE_BASE 0x810
|
||||
|
||||
#define to_vdpu_task(task) \
|
||||
container_of(task, struct vdpu_task, mpp_task)
|
||||
#define to_vdpu_dev(dev) \
|
||||
container_of(dev, struct vdpu_dev, mpp)
|
||||
|
||||
struct vdpu_task {
|
||||
struct mpp_task mpp_task;
|
||||
struct mpp_hw_info *hw_info;
|
||||
/* enable of post process */
|
||||
bool pp_enable;
|
||||
|
||||
unsigned long aclk_freq;
|
||||
u32 reg[VDPU1_REG_PP_NUM];
|
||||
u32 idx;
|
||||
struct extra_info_for_iommu ext_inf;
|
||||
u32 strm_addr;
|
||||
u32 irq_status;
|
||||
};
|
||||
|
||||
struct vdpu_dev {
|
||||
struct mpp_dev mpp;
|
||||
|
||||
struct clk *aclk;
|
||||
struct clk *hclk;
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
struct dentry *debugfs;
|
||||
#endif
|
||||
u32 aclk_debug;
|
||||
u32 session_max_buffers_debug;
|
||||
|
||||
struct reset_control *rst_a;
|
||||
struct reset_control *rst_h;
|
||||
struct vdpu_task *current_task;
|
||||
};
|
||||
|
||||
static struct mpp_hw_info vdpu_v1_hw_info = {
|
||||
.reg_num = VDPU1_REG_NUM,
|
||||
.regidx_start = VDPU1_REG_START_INDEX,
|
||||
.regidx_end = VDPU1_REG_END_INDEX,
|
||||
.regidx_en = VDPU1_REG_DEC_INT_EN_INDEX,
|
||||
};
|
||||
|
||||
static struct mpp_hw_info vdpu_pp_v1_hw_info = {
|
||||
.reg_num = VDPU1_REG_PP_NUM,
|
||||
.regidx_start = VDPU1_REG_PP_START_INDEX,
|
||||
.regidx_end = VDPU1_REG_PP_END_INDEX,
|
||||
.regidx_en = VDPU1_REG_DEC_INT_EN_INDEX,
|
||||
};
|
||||
|
||||
/*
|
||||
* file handle translate information
|
||||
*/
|
||||
static const char trans_tbl_avsd[] = {
|
||||
12, 13, 14, 15, 16, 17, 40, 41, 45
|
||||
};
|
||||
|
||||
static const char trans_tbl_default[] = {
|
||||
12, 13, 14, 15, 16, 17, 40, 41
|
||||
};
|
||||
|
||||
static const char trans_tbl_jpegd[] = {
|
||||
12, 13, 14, 40, 66, 67
|
||||
};
|
||||
|
||||
static const char trans_tbl_h264d[] = {
|
||||
12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27,
|
||||
28, 29, 40
|
||||
};
|
||||
|
||||
static const char trans_tbl_vc1d[] = {
|
||||
12, 13, 14, 15, 16, 17, 27, 41
|
||||
};
|
||||
|
||||
static const char trans_tbl_vp6d[] = {
|
||||
12, 13, 14, 18, 27, 40
|
||||
};
|
||||
|
||||
static const char trans_tbl_vp8d[] = {
|
||||
10, 12, 13, 14, 18, 19, 22, 23, 24, 25, 26, 27, 28, 29, 40
|
||||
};
|
||||
|
||||
static struct mpp_trans_info vdpu_v1_trans[] = {
|
||||
[VDPU1_FMT_H264D] = {
|
||||
.count = sizeof(trans_tbl_h264d),
|
||||
.table = trans_tbl_h264d,
|
||||
},
|
||||
[VDPU1_FMT_H263D] = {
|
||||
.count = sizeof(trans_tbl_default),
|
||||
.table = trans_tbl_default,
|
||||
},
|
||||
[VDPU1_FMT_MPEG4D] = {
|
||||
.count = sizeof(trans_tbl_default),
|
||||
.table = trans_tbl_default,
|
||||
},
|
||||
[VDPU1_FMT_JPEGD] = {
|
||||
.count = sizeof(trans_tbl_jpegd),
|
||||
.table = trans_tbl_jpegd,
|
||||
},
|
||||
[VDPU1_FMT_VC1D] = {
|
||||
.count = sizeof(trans_tbl_vc1d),
|
||||
.table = trans_tbl_vc1d,
|
||||
},
|
||||
[VDPU1_FMT_MPEG2D] = {
|
||||
.count = sizeof(trans_tbl_default),
|
||||
.table = trans_tbl_default,
|
||||
},
|
||||
[VDPU1_FMT_MPEG1D] = {
|
||||
.count = sizeof(trans_tbl_default),
|
||||
.table = trans_tbl_default,
|
||||
},
|
||||
[VDPU1_FMT_VP6D] = {
|
||||
.count = sizeof(trans_tbl_vp6d),
|
||||
.table = trans_tbl_vp6d,
|
||||
},
|
||||
[VDPU1_FMT_RESERVED] = {
|
||||
.count = 0,
|
||||
.table = NULL,
|
||||
},
|
||||
[VDPU1_FMT_VP7D] = {
|
||||
.count = sizeof(trans_tbl_default),
|
||||
.table = trans_tbl_default,
|
||||
},
|
||||
[VDPU1_FMT_VP8D] = {
|
||||
.count = sizeof(trans_tbl_vp8d),
|
||||
.table = trans_tbl_vp8d,
|
||||
},
|
||||
[VDPU1_FMT_AVSD] = {
|
||||
.count = sizeof(trans_tbl_avsd),
|
||||
.table = trans_tbl_avsd,
|
||||
},
|
||||
};
|
||||
|
||||
static void *vdpu_alloc_task(struct mpp_session *session,
|
||||
void __user *src, u32 size)
|
||||
{
|
||||
u32 reg_len;
|
||||
u32 extinf_len;
|
||||
u32 fmt = 0;
|
||||
int err = -EFAULT;
|
||||
struct vdpu_task *task = NULL;
|
||||
u32 dwsize = size / sizeof(u32);
|
||||
struct mpp_dev *mpp = session->mpp;
|
||||
|
||||
mpp_debug_enter();
|
||||
|
||||
task = kzalloc(sizeof(*task), GFP_KERNEL);
|
||||
if (!task)
|
||||
return NULL;
|
||||
|
||||
mpp_task_init(session, &task->mpp_task);
|
||||
|
||||
if (session->device_type == MPP_DEVICE_DEC_PP) {
|
||||
task->pp_enable = true;
|
||||
task->hw_info = &vdpu_pp_v1_hw_info;
|
||||
} else {
|
||||
task->hw_info = mpp->var->hw_info;
|
||||
}
|
||||
reg_len = min(task->hw_info->reg_num, dwsize);
|
||||
extinf_len = dwsize > reg_len ? (dwsize - reg_len) * 4 : 0;
|
||||
|
||||
if (copy_from_user(task->reg, src, reg_len * 4)) {
|
||||
mpp_err("error: copy_from_user failed in reg_init\n");
|
||||
err = -EFAULT;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
fmt = VDPU1_GET_FORMAT(task->reg[VDPU1_REG_SYS_CTRL_INDEX]);
|
||||
if (extinf_len > 0) {
|
||||
if (likely(fmt == VDPU1_FMT_JPEGD)) {
|
||||
err = copy_from_user(&task->ext_inf,
|
||||
(u8 *)src + size
|
||||
- JPEG_IOC_EXTRA_SIZE,
|
||||
JPEG_IOC_EXTRA_SIZE);
|
||||
} else {
|
||||
u32 ext_cpy = min_t(size_t, extinf_len,
|
||||
sizeof(task->ext_inf));
|
||||
err = copy_from_user(&task->ext_inf,
|
||||
(u32 *)src + reg_len,
|
||||
ext_cpy);
|
||||
}
|
||||
|
||||
if (err) {
|
||||
mpp_err("copy_from_user failed when extra info\n");
|
||||
err = -EFAULT;
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
err = mpp_translate_reg_address(session->mpp,
|
||||
&task->mpp_task,
|
||||
fmt,
|
||||
task->reg);
|
||||
if (err) {
|
||||
mpp_err("error: translate reg address failed.\n");
|
||||
mpp_dump_reg(task->reg,
|
||||
task->hw_info->regidx_start,
|
||||
task->hw_info->regidx_end);
|
||||
goto fail;
|
||||
}
|
||||
/*
|
||||
* 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
|
||||
*/
|
||||
if (likely(fmt == VDPU1_FMT_H264D)) {
|
||||
struct mpp_mem_region *mem_region = NULL;
|
||||
dma_addr_t iova = 0;
|
||||
u32 offset = task->reg[VDPU1_REG_DIR_MV_BASE_INDEX];
|
||||
int fd = task->reg[VDPU1_REG_DIR_MV_BASE_INDEX] & 0x3ff;
|
||||
|
||||
offset = offset >> 10 << 4;
|
||||
mem_region = mpp_task_attach_fd(&task->mpp_task, fd);
|
||||
if (IS_ERR(mem_region)) {
|
||||
err = PTR_ERR(mem_region);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
iova = mem_region->iova;
|
||||
mpp_debug(DEBUG_IOMMU, "DMV[%3d]: %3d => %pad + offset %10d\n",
|
||||
VDPU1_REG_DIR_MV_BASE_INDEX, fd, &iova, offset);
|
||||
task->reg[VDPU1_REG_DIR_MV_BASE_INDEX] = iova + offset;
|
||||
}
|
||||
|
||||
task->strm_addr = task->reg[VDPU1_REG_STREAM_RLC_BASE_INDEX];
|
||||
|
||||
mpp_debug(DEBUG_SET_REG, "extra info cnt %u, magic %08x",
|
||||
task->ext_inf.cnt, task->ext_inf.magic);
|
||||
mpp_translate_extra_info(&task->mpp_task, &task->ext_inf, task->reg);
|
||||
|
||||
mpp_debug_leave();
|
||||
|
||||
return &task->mpp_task;
|
||||
|
||||
fail:
|
||||
mpp_task_finalize(session, &task->mpp_task);
|
||||
kfree(task);
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
static int vdpu_prepare(struct mpp_dev *mpp,
|
||||
struct mpp_task *task)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int vdpu_run(struct mpp_dev *mpp,
|
||||
struct mpp_task *mpp_task)
|
||||
{
|
||||
u32 i;
|
||||
u32 regidx_start;
|
||||
u32 regidx_end;
|
||||
u32 regidx_en;
|
||||
struct vdpu_task *task = NULL;
|
||||
struct vdpu_dev *dec = NULL;
|
||||
|
||||
mpp_debug_enter();
|
||||
|
||||
task = to_vdpu_task(mpp_task);
|
||||
dec = to_vdpu_dev(mpp);
|
||||
|
||||
/* FIXME: spin lock here */
|
||||
dec->current_task = task;
|
||||
/* clear cache */
|
||||
mpp_write_relaxed(mpp, VDPU1_REG_CLR_CACHE_BASE, 0);
|
||||
/* set registers for hardware */
|
||||
regidx_start = task->hw_info->regidx_start;
|
||||
regidx_end = task->hw_info->regidx_end;
|
||||
regidx_en = task->hw_info->regidx_en;
|
||||
for (i = regidx_start; i <= regidx_end; i++) {
|
||||
if (i == regidx_en)
|
||||
continue;
|
||||
mpp_write_relaxed(mpp, i * sizeof(u32), task->reg[i]);
|
||||
}
|
||||
/* Flush the registers */
|
||||
wmb();
|
||||
mpp_write(mpp, VDPU1_REG_DEC_INT_EN,
|
||||
task->reg[regidx_en] | VDPU1_DEC_START);
|
||||
|
||||
mpp_debug_leave();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vdpu_finish(struct mpp_dev *mpp,
|
||||
struct mpp_task *mpp_task)
|
||||
{
|
||||
u32 i;
|
||||
u32 regidx_start;
|
||||
u32 regidx_end;
|
||||
u32 dec_get;
|
||||
s32 dec_length;
|
||||
struct vdpu_task *task = to_vdpu_task(mpp_task);
|
||||
|
||||
mpp_debug_enter();
|
||||
|
||||
/* read register after running */
|
||||
regidx_start = task->hw_info->regidx_start;
|
||||
regidx_end = task->hw_info->regidx_end;
|
||||
for (i = regidx_start; i <= regidx_end; i++)
|
||||
task->reg[i] = mpp_read_relaxed(mpp, i * sizeof(u32));
|
||||
task->reg[VDPU1_REG_DEC_INT_EN_INDEX] = task->irq_status;
|
||||
/* revert hack for decoded length */
|
||||
dec_get = task->reg[VDPU1_REG_STREAM_RLC_BASE_INDEX];
|
||||
dec_length = dec_get - task->strm_addr;
|
||||
task->reg[VDPU1_REG_STREAM_RLC_BASE_INDEX] = dec_length << 10;
|
||||
mpp_debug(DEBUG_REGISTER,
|
||||
"dec_get %08x dec_length %d\n",
|
||||
dec_get, dec_length);
|
||||
|
||||
mpp_debug_leave();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vdpu_result(struct mpp_dev *mpp,
|
||||
struct mpp_task *mpp_task,
|
||||
u32 __user *dst, u32 size)
|
||||
{
|
||||
struct vdpu_task *task = to_vdpu_task(mpp_task);
|
||||
|
||||
/* FIXME may overflow the kernel */
|
||||
if (copy_to_user(dst, task->reg, size)) {
|
||||
mpp_err("copy_to_user failed\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vdpu_free_task(struct mpp_session *session,
|
||||
struct mpp_task *mpp_task)
|
||||
{
|
||||
struct vdpu_task *task = to_vdpu_task(mpp_task);
|
||||
|
||||
mpp_task_finalize(session, mpp_task);
|
||||
kfree(task);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vdpu_debugfs_remove(struct mpp_dev *mpp)
|
||||
{
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
struct vdpu_dev *dec = to_vdpu_dev(mpp);
|
||||
|
||||
debugfs_remove_recursive(dec->debugfs);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vdpu_debugfs_init(struct mpp_dev *mpp)
|
||||
{
|
||||
struct vdpu_dev *dec = to_vdpu_dev(mpp);
|
||||
struct device_node *np = mpp->dev->of_node;
|
||||
|
||||
dec->aclk_debug = 0;
|
||||
dec->session_max_buffers_debug = 0;
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
dec->debugfs = debugfs_create_dir(np->name, mpp->srv->debugfs);
|
||||
if (IS_ERR_OR_NULL(dec->debugfs)) {
|
||||
mpp_err("failed on open debugfs\n");
|
||||
dec->debugfs = NULL;
|
||||
return -EIO;
|
||||
}
|
||||
debugfs_create_u32("aclk", 0644,
|
||||
dec->debugfs, &dec->aclk_debug);
|
||||
debugfs_create_u32("session_buffers", 0644,
|
||||
dec->debugfs, &dec->session_max_buffers_debug);
|
||||
#endif
|
||||
if (dec->session_max_buffers_debug)
|
||||
mpp->session_max_buffers = dec->session_max_buffers_debug;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vdpu_init(struct mpp_dev *mpp)
|
||||
{
|
||||
struct vdpu_dev *dec = to_vdpu_dev(mpp);
|
||||
|
||||
mpp->grf_info = &mpp->srv->grf_infos[MPP_DRIVER_VDPU1];
|
||||
|
||||
dec->aclk = devm_clk_get(mpp->dev, "aclk_vcodec");
|
||||
if (IS_ERR_OR_NULL(dec->aclk)) {
|
||||
mpp_err("failed on clk_get aclk_vcodec\n");
|
||||
dec->aclk = NULL;
|
||||
}
|
||||
dec->hclk = devm_clk_get(mpp->dev, "hclk_vcodec");
|
||||
if (IS_ERR_OR_NULL(dec->hclk)) {
|
||||
mpp_err("failed on clk_get hclk_vcodec\n");
|
||||
dec->hclk = NULL;
|
||||
}
|
||||
|
||||
dec->rst_a = devm_reset_control_get_shared(mpp->dev, "video_a");
|
||||
if (IS_ERR_OR_NULL(dec->rst_a)) {
|
||||
mpp_err("No aclk reset resource define\n");
|
||||
dec->rst_a = NULL;
|
||||
}
|
||||
dec->rst_h = devm_reset_control_get_shared(mpp->dev, "video_h");
|
||||
if (IS_ERR_OR_NULL(dec->rst_h)) {
|
||||
mpp_err("No hclk reset resource define\n");
|
||||
dec->rst_h = NULL;
|
||||
}
|
||||
mpp_safe_unreset(dec->rst_a);
|
||||
mpp_safe_unreset(dec->rst_h);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vdpu_power_on(struct mpp_dev *mpp)
|
||||
{
|
||||
struct vdpu_dev *dec = to_vdpu_dev(mpp);
|
||||
|
||||
if (dec->aclk)
|
||||
clk_prepare_enable(dec->aclk);
|
||||
if (dec->hclk)
|
||||
clk_prepare_enable(dec->hclk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vdpu_power_off(struct mpp_dev *mpp)
|
||||
{
|
||||
struct vdpu_dev *dec = to_vdpu_dev(mpp);
|
||||
|
||||
if (dec->aclk)
|
||||
clk_disable_unprepare(dec->aclk);
|
||||
if (dec->hclk)
|
||||
clk_disable_unprepare(dec->hclk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vdpu_get_freq(struct mpp_dev *mpp,
|
||||
struct mpp_task *mpp_task)
|
||||
{
|
||||
struct vdpu_task *task = to_vdpu_task(mpp_task);
|
||||
|
||||
task->aclk_freq = 300;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vdpu_set_freq(struct mpp_dev *mpp,
|
||||
struct mpp_task *mpp_task)
|
||||
{
|
||||
struct vdpu_dev *dec = to_vdpu_dev(mpp);
|
||||
struct vdpu_task *task = to_vdpu_task(mpp_task);
|
||||
|
||||
/* check whether use debug freq */
|
||||
task->aclk_freq = dec->aclk_debug ?
|
||||
dec->aclk_debug : task->aclk_freq;
|
||||
|
||||
clk_set_rate(dec->aclk, task->aclk_freq * MHZ);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vdpu_reduce_freq(struct mpp_dev *mpp)
|
||||
{
|
||||
struct vdpu_dev *dec = to_vdpu_dev(mpp);
|
||||
|
||||
if (dec->aclk)
|
||||
clk_set_rate(dec->aclk, 50 * MHZ);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vdpu_irq(struct mpp_dev *mpp)
|
||||
{
|
||||
mpp->irq_status = mpp_read(mpp, VDPU1_REG_DEC_INT_EN);
|
||||
if (!(mpp->irq_status & VDPU1_DEC_INT_RAW))
|
||||
return IRQ_NONE;
|
||||
|
||||
mpp_write(mpp, VDPU1_REG_DEC_INT_EN, 0);
|
||||
/* set clock gating to save power */
|
||||
mpp_write(mpp, VDPU1_REG_DEC_EN, VDPU1_CLOCK_GATE_EN);
|
||||
|
||||
return IRQ_WAKE_THREAD;
|
||||
}
|
||||
|
||||
static int vdpu_isr(struct mpp_dev *mpp)
|
||||
{
|
||||
u32 err_mask;
|
||||
struct vdpu_task *task = NULL;
|
||||
struct mpp_task *mpp_task = NULL;
|
||||
struct vdpu_dev *dec = to_vdpu_dev(mpp);
|
||||
|
||||
/* FIXME use a spin lock here */
|
||||
task = dec->current_task;
|
||||
if (!task) {
|
||||
dev_err(dec->mpp.dev, "no current task\n");
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
mpp_task = &task->mpp_task;
|
||||
mpp_time_diff(mpp_task);
|
||||
dec->current_task = NULL;
|
||||
task->irq_status = mpp->irq_status;
|
||||
mpp_debug(DEBUG_IRQ_STATUS, "irq_status: %08x\n",
|
||||
task->irq_status);
|
||||
|
||||
err_mask = VDPU1_INT_TIMEOUT
|
||||
| VDPU1_INT_STRM_ERROR
|
||||
| VDPU1_INT_ASO_ERROR
|
||||
| VDPU1_INT_BUF_EMPTY
|
||||
| VDPU1_INT_BUS_ERROR;
|
||||
|
||||
if (err_mask & task->irq_status)
|
||||
atomic_inc(&mpp->reset_request);
|
||||
|
||||
mpp_task_finish(mpp_task->session, mpp_task);
|
||||
|
||||
mpp_debug_leave();
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int vdpu_reset(struct mpp_dev *mpp)
|
||||
{
|
||||
struct vdpu_dev *dec = to_vdpu_dev(mpp);
|
||||
|
||||
if (dec->rst_a && dec->rst_h) {
|
||||
mpp_debug(DEBUG_RESET, "reset in\n");
|
||||
|
||||
/* Don't skip this or iommu won't work after reset */
|
||||
rockchip_pmu_idle_request(mpp->dev, true);
|
||||
mpp_safe_reset(dec->rst_a);
|
||||
mpp_safe_reset(dec->rst_h);
|
||||
udelay(5);
|
||||
mpp_safe_unreset(dec->rst_a);
|
||||
mpp_safe_unreset(dec->rst_h);
|
||||
rockchip_pmu_idle_request(mpp->dev, false);
|
||||
|
||||
mpp_debug(DEBUG_RESET, "reset out\n");
|
||||
}
|
||||
mpp_write(mpp, VDPU1_REG_DEC_INT_EN, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct mpp_hw_ops vdpu_v1_hw_ops = {
|
||||
.init = vdpu_init,
|
||||
.power_on = vdpu_power_on,
|
||||
.power_off = vdpu_power_off,
|
||||
.get_freq = vdpu_get_freq,
|
||||
.set_freq = vdpu_set_freq,
|
||||
.reduce_freq = vdpu_reduce_freq,
|
||||
.reset = vdpu_reset,
|
||||
};
|
||||
|
||||
static struct mpp_dev_ops vdpu_v1_dev_ops = {
|
||||
.alloc_task = vdpu_alloc_task,
|
||||
.prepare = vdpu_prepare,
|
||||
.run = vdpu_run,
|
||||
.irq = vdpu_irq,
|
||||
.isr = vdpu_isr,
|
||||
.finish = vdpu_finish,
|
||||
.result = vdpu_result,
|
||||
.free_task = vdpu_free_task,
|
||||
};
|
||||
|
||||
static const struct mpp_dev_var vdpu_v1_data = {
|
||||
.device_type = MPP_DEVICE_DEC,
|
||||
.hw_info = &vdpu_v1_hw_info,
|
||||
.trans_info = vdpu_v1_trans,
|
||||
.hw_ops = &vdpu_v1_hw_ops,
|
||||
.dev_ops = &vdpu_v1_dev_ops,
|
||||
};
|
||||
|
||||
static const struct mpp_dev_var avsd_plus_data = {
|
||||
.device_type = MPP_DEVICE_DEC_AVSPLUS,
|
||||
.hw_info = &vdpu_v1_hw_info,
|
||||
.trans_info = vdpu_v1_trans,
|
||||
.hw_ops = &vdpu_v1_hw_ops,
|
||||
.dev_ops = &vdpu_v1_dev_ops,
|
||||
};
|
||||
|
||||
static const struct of_device_id mpp_vdpu1_dt_match[] = {
|
||||
{
|
||||
.compatible = "rockchip,vpu-decoder-v1",
|
||||
.data = &vdpu_v1_data,
|
||||
},
|
||||
{
|
||||
.compatible = "rockchip,avs-plus-decoder",
|
||||
.data = &avsd_plus_data,
|
||||
},
|
||||
{},
|
||||
};
|
||||
|
||||
static int vdpu_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct vdpu_dev *dec = NULL;
|
||||
struct mpp_dev *mpp = NULL;
|
||||
const struct of_device_id *match = NULL;
|
||||
int ret = 0;
|
||||
|
||||
dev_info(dev, "probe device\n");
|
||||
dec = devm_kzalloc(dev, sizeof(struct vdpu_dev), GFP_KERNEL);
|
||||
if (!dec)
|
||||
return -ENOMEM;
|
||||
platform_set_drvdata(pdev, dec);
|
||||
|
||||
mpp = &dec->mpp;
|
||||
if (pdev->dev.of_node) {
|
||||
match = of_match_node(mpp_vdpu1_dt_match, pdev->dev.of_node);
|
||||
if (match)
|
||||
mpp->var = (struct mpp_dev_var *)match->data;
|
||||
}
|
||||
|
||||
ret = mpp_dev_probe(mpp, pdev);
|
||||
if (ret) {
|
||||
dev_err(dev, "probe sub driver failed\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = devm_request_threaded_irq(dev, mpp->irq,
|
||||
mpp_dev_irq,
|
||||
mpp_dev_isr_sched,
|
||||
IRQF_SHARED,
|
||||
dev_name(dev), mpp);
|
||||
if (ret) {
|
||||
dev_err(dev, "register interrupter runtime failed\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (mpp->var->device_type == MPP_DEVICE_DEC) {
|
||||
mpp->srv->sub_devices[MPP_DEVICE_PP] = mpp;
|
||||
mpp->srv->sub_devices[MPP_DEVICE_DEC_PP] = mpp;
|
||||
}
|
||||
mpp->session_max_buffers = VDPU1_SESSION_MAX_BUFFERS;
|
||||
vdpu_debugfs_init(mpp);
|
||||
dev_info(dev, "probing finish\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vdpu_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct vdpu_dev *dec = platform_get_drvdata(pdev);
|
||||
|
||||
dev_info(dev, "remove device\n");
|
||||
mpp_dev_remove(&dec->mpp);
|
||||
vdpu_debugfs_remove(&dec->mpp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void vdpu_shutdown(struct platform_device *pdev)
|
||||
{
|
||||
int ret;
|
||||
int val;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct vdpu_dev *dec = platform_get_drvdata(pdev);
|
||||
struct mpp_dev *mpp = &dec->mpp;
|
||||
|
||||
dev_info(dev, "shutdown device\n");
|
||||
|
||||
atomic_inc(&mpp->srv->shutdown_request);
|
||||
ret = readx_poll_timeout(atomic_read,
|
||||
&mpp->total_running,
|
||||
val, val == 0, 20000, 200000);
|
||||
if (ret == -ETIMEDOUT)
|
||||
dev_err(dev, "wait total running time out\n");
|
||||
}
|
||||
|
||||
struct platform_driver rockchip_vdpu1_driver = {
|
||||
.probe = vdpu_probe,
|
||||
.remove = vdpu_remove,
|
||||
.shutdown = vdpu_shutdown,
|
||||
.driver = {
|
||||
.name = VDPU1_DRIVER_NAME,
|
||||
.of_match_table = of_match_ptr(mpp_vdpu1_dt_match),
|
||||
},
|
||||
};
|
||||
EXPORT_SYMBOL(rockchip_vdpu1_driver);
|
||||
720
drivers/video/rockchip/mpp/mpp_vdpu2.c
Normal file
720
drivers/video/rockchip/mpp/mpp_vdpu2.c
Normal file
@@ -0,0 +1,720 @@
|
||||
// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
|
||||
/*
|
||||
* Copyright (c) 2019 Fuzhou Rockchip Electronics Co., Ltd
|
||||
*
|
||||
* author:
|
||||
* Alpha Lin, alpha.lin@rock-chips.com
|
||||
* Randy Li, randy.li@rock-chips.com
|
||||
* Ding Wei, leo.ding@rock-chips.com
|
||||
*
|
||||
*/
|
||||
#include <asm/cacheflush.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <soc/rockchip/pm_domains.h>
|
||||
|
||||
#include "mpp_debug.h"
|
||||
#include "mpp_common.h"
|
||||
#include "mpp_iommu.h"
|
||||
|
||||
#define VDPU2_DRIVER_NAME "mpp_vdpu2"
|
||||
|
||||
#define VDPU2_SESSION_MAX_BUFFERS 40
|
||||
/* The maximum registers number of all the version */
|
||||
#define VDPU2_REG_NUM 159
|
||||
#define VDPU2_REG_START_INDEX 50
|
||||
#define VDPU2_REG_END_INDEX 158
|
||||
|
||||
#define VDPU2_REG_SYS_CTRL 0x0d4
|
||||
#define VDPU2_REG_SYS_CTRL_INDEX (53)
|
||||
#define VDPU2_GET_FORMAT(x) ((x) & 0xf)
|
||||
#define VDPU2_FMT_H264D 0
|
||||
#define VDPU2_FMT_MPEG4D 1
|
||||
#define VDPU2_FMT_H263D 2
|
||||
#define VDPU2_FMT_JPEGD 3
|
||||
#define VDPU2_FMT_VC1D 4
|
||||
#define VDPU2_FMT_MPEG2D 5
|
||||
#define VDPU2_FMT_MPEG1D 6
|
||||
#define VDPU2_FMT_VP6D 7
|
||||
#define VDPU2_FMT_RESERVED 8
|
||||
#define VDPU2_FMT_VP7D 9
|
||||
#define VDPU2_FMT_VP8D 10
|
||||
#define VDPU2_FMT_AVSD 11
|
||||
|
||||
#define VDPU2_REG_DEC_INT 0x0dc
|
||||
#define VDPU2_REG_DEC_INT_INDEX (55)
|
||||
#define VDPU2_INT_TIMEOUT BIT(13)
|
||||
#define VDPU2_INT_STRM_ERROR BIT(12)
|
||||
#define VDPU2_INT_SLICE BIT(9)
|
||||
#define VDPU2_INT_ASO_ERROR BIT(8)
|
||||
#define VDPU2_INT_BUF_EMPTY BIT(6)
|
||||
#define VDPU2_INT_BUS_ERROR BIT(5)
|
||||
#define VDPU2_DEC_INT BIT(4)
|
||||
#define VDPU2_DEC_IRQ_DIS BIT(1)
|
||||
#define VDPU2_DEC_INT_RAW BIT(0)
|
||||
|
||||
#define VDPU2_REG_DEC_EN 0x0e4
|
||||
#define VDPU2_REG_DEC_EN_INDEX (57)
|
||||
#define VDPU2_DEC_CLOCK_GATE_EN BIT(4)
|
||||
#define VDPU2_DEC_START BIT(0)
|
||||
|
||||
#define VDPU2_REG_DIR_MV_BASE 0x0f8
|
||||
#define VDPU2_REG_DIR_MV_BASE_INDEX (62)
|
||||
|
||||
#define VDPU2_REG_STREAM_RLC_BASE 0x100
|
||||
#define VDPU2_REG_STREAM_RLC_BASE_INDEX (64)
|
||||
|
||||
#define VDPU2_REG_CLR_CACHE_BASE 0x810
|
||||
|
||||
#define to_vdpu_task(task) \
|
||||
container_of(task, struct vdpu_task, mpp_task)
|
||||
#define to_vdpu_dev(dev) \
|
||||
container_of(dev, struct vdpu_dev, mpp)
|
||||
|
||||
struct vdpu_task {
|
||||
struct mpp_task mpp_task;
|
||||
struct mpp_hw_info *hw_info;
|
||||
/* enable of post process */
|
||||
bool pp_enable;
|
||||
|
||||
unsigned long aclk_freq;
|
||||
u32 reg[VDPU2_REG_NUM];
|
||||
u32 idx;
|
||||
struct extra_info_for_iommu ext_inf;
|
||||
u32 strm_addr;
|
||||
u32 irq_status;
|
||||
};
|
||||
|
||||
struct vdpu_dev {
|
||||
struct mpp_dev mpp;
|
||||
|
||||
struct clk *aclk;
|
||||
struct clk *hclk;
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
struct dentry *debugfs;
|
||||
#endif
|
||||
u32 aclk_debug;
|
||||
u32 session_max_buffers_debug;
|
||||
|
||||
struct reset_control *rst_a;
|
||||
struct reset_control *rst_h;
|
||||
struct vdpu_task *current_task;
|
||||
};
|
||||
|
||||
static struct mpp_hw_info vdpu_v2_hw_info = {
|
||||
.reg_num = VDPU2_REG_NUM,
|
||||
.regidx_start = VDPU2_REG_START_INDEX,
|
||||
.regidx_end = VDPU2_REG_END_INDEX,
|
||||
.regidx_en = VDPU2_REG_DEC_EN_INDEX,
|
||||
};
|
||||
|
||||
/*
|
||||
* file handle translate information
|
||||
*/
|
||||
static const char trans_tbl_default[] = {
|
||||
61, 62, 63, 64, 131, 134, 135, 148
|
||||
};
|
||||
|
||||
static const char trans_tbl_jpegd[] = {
|
||||
21, 22, 61, 63, 64, 131
|
||||
};
|
||||
|
||||
static const char trans_tbl_h264d[] = {
|
||||
61, 63, 64, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97,
|
||||
98, 99
|
||||
};
|
||||
|
||||
static const char trans_tbl_vc1d[] = {
|
||||
62, 63, 64, 131, 134, 135, 145, 148
|
||||
};
|
||||
|
||||
static const char trans_tbl_vp6d[] = {
|
||||
61, 63, 64, 131, 136, 145
|
||||
};
|
||||
|
||||
static const char trans_tbl_vp8d[] = {
|
||||
61, 63, 64, 131, 136, 137, 140, 141, 142, 143, 144, 145, 146, 147, 149
|
||||
};
|
||||
|
||||
static struct mpp_trans_info vdpu_v2_trans[] = {
|
||||
[VDPU2_FMT_H264D] = {
|
||||
.count = sizeof(trans_tbl_h264d),
|
||||
.table = trans_tbl_h264d,
|
||||
},
|
||||
[VDPU2_FMT_H263D] = {
|
||||
.count = sizeof(trans_tbl_default),
|
||||
.table = trans_tbl_default,
|
||||
},
|
||||
[VDPU2_FMT_MPEG4D] = {
|
||||
.count = sizeof(trans_tbl_default),
|
||||
.table = trans_tbl_default,
|
||||
},
|
||||
[VDPU2_FMT_JPEGD] = {
|
||||
.count = sizeof(trans_tbl_jpegd),
|
||||
.table = trans_tbl_jpegd,
|
||||
},
|
||||
[VDPU2_FMT_VC1D] = {
|
||||
.count = sizeof(trans_tbl_vc1d),
|
||||
.table = trans_tbl_vc1d,
|
||||
},
|
||||
[VDPU2_FMT_MPEG2D] = {
|
||||
.count = sizeof(trans_tbl_default),
|
||||
.table = trans_tbl_default,
|
||||
},
|
||||
[VDPU2_FMT_MPEG1D] = {
|
||||
.count = sizeof(trans_tbl_default),
|
||||
.table = trans_tbl_default,
|
||||
},
|
||||
[VDPU2_FMT_VP6D] = {
|
||||
.count = sizeof(trans_tbl_vp6d),
|
||||
.table = trans_tbl_vp6d,
|
||||
},
|
||||
[VDPU2_FMT_RESERVED] = {
|
||||
.count = 0,
|
||||
.table = NULL,
|
||||
},
|
||||
[VDPU2_FMT_VP7D] = {
|
||||
.count = sizeof(trans_tbl_default),
|
||||
.table = trans_tbl_default,
|
||||
},
|
||||
[VDPU2_FMT_VP8D] = {
|
||||
.count = sizeof(trans_tbl_vp8d),
|
||||
.table = trans_tbl_vp8d,
|
||||
},
|
||||
[VDPU2_FMT_AVSD] = {
|
||||
.count = sizeof(trans_tbl_default),
|
||||
.table = trans_tbl_default,
|
||||
},
|
||||
};
|
||||
|
||||
static void *vdpu_alloc_task(struct mpp_session *session,
|
||||
void __user *src, u32 size)
|
||||
{
|
||||
u32 reg_len;
|
||||
u32 extinf_len;
|
||||
u32 fmt = 0;
|
||||
int err = -EFAULT;
|
||||
struct vdpu_task *task = NULL;
|
||||
u32 dwsize = size / sizeof(u32);
|
||||
struct mpp_dev *mpp = session->mpp;
|
||||
|
||||
mpp_debug_enter();
|
||||
|
||||
task = kzalloc(sizeof(*task), GFP_KERNEL);
|
||||
if (!task)
|
||||
return NULL;
|
||||
|
||||
mpp_task_init(session, &task->mpp_task);
|
||||
|
||||
task->hw_info = mpp->var->hw_info;
|
||||
reg_len = min(task->hw_info->reg_num, dwsize);
|
||||
extinf_len = dwsize > reg_len ? (dwsize - reg_len) * 4 : 0;
|
||||
|
||||
if (copy_from_user(task->reg, src, reg_len * 4)) {
|
||||
mpp_err("error: copy_from_user failed in reg_init\n");
|
||||
err = -EFAULT;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
fmt = VDPU2_GET_FORMAT(task->reg[VDPU2_REG_SYS_CTRL_INDEX]);
|
||||
if (extinf_len > 0) {
|
||||
if (likely(fmt == VDPU2_FMT_JPEGD)) {
|
||||
err = copy_from_user(&task->ext_inf,
|
||||
(u8 *)src + size
|
||||
- JPEG_IOC_EXTRA_SIZE,
|
||||
JPEG_IOC_EXTRA_SIZE);
|
||||
} else {
|
||||
u32 ext_cpy = min_t(size_t, extinf_len,
|
||||
sizeof(task->ext_inf));
|
||||
err = copy_from_user(&task->ext_inf,
|
||||
(u32 *)src + reg_len, ext_cpy);
|
||||
}
|
||||
|
||||
if (err) {
|
||||
mpp_err("copy_from_user failed when extra info\n");
|
||||
err = -EFAULT;
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
err = mpp_translate_reg_address(session->mpp,
|
||||
&task->mpp_task,
|
||||
fmt, task->reg);
|
||||
if (err) {
|
||||
mpp_err("error: translate reg address failed.\n");
|
||||
mpp_dump_reg(task->reg,
|
||||
task->hw_info->regidx_start,
|
||||
task->hw_info->regidx_end);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (likely(fmt == VDPU2_FMT_H264D)) {
|
||||
struct mpp_mem_region *mem_region = NULL;
|
||||
dma_addr_t iova = 0;
|
||||
u32 offset = task->reg[VDPU2_REG_DIR_MV_BASE_INDEX];
|
||||
int fd = task->reg[VDPU2_REG_DIR_MV_BASE_INDEX] & 0x3ff;
|
||||
|
||||
offset = offset >> 10 << 4;
|
||||
mem_region = mpp_task_attach_fd(&task->mpp_task, fd);
|
||||
if (IS_ERR(mem_region)) {
|
||||
err = PTR_ERR(mem_region);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
iova = mem_region->iova;
|
||||
mpp_debug(DEBUG_IOMMU, "DMV[%3d]: %3d => %pad + offset %10d\n",
|
||||
VDPU2_REG_DIR_MV_BASE_INDEX, fd, &iova, offset);
|
||||
task->reg[VDPU2_REG_DIR_MV_BASE_INDEX] = iova + offset;
|
||||
}
|
||||
|
||||
task->strm_addr = task->reg[VDPU2_REG_STREAM_RLC_BASE_INDEX];
|
||||
|
||||
mpp_debug(DEBUG_SET_REG, "extra info cnt %u, magic %08x",
|
||||
task->ext_inf.cnt, task->ext_inf.magic);
|
||||
mpp_translate_extra_info(&task->mpp_task, &task->ext_inf, task->reg);
|
||||
|
||||
mpp_debug_leave();
|
||||
|
||||
return &task->mpp_task;
|
||||
|
||||
fail:
|
||||
mpp_task_finalize(session, &task->mpp_task);
|
||||
kfree(task);
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
static int vdpu_prepare(struct mpp_dev *mpp,
|
||||
struct mpp_task *task)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int vdpu_run(struct mpp_dev *mpp,
|
||||
struct mpp_task *mpp_task)
|
||||
{
|
||||
u32 i;
|
||||
u32 regidx_start;
|
||||
u32 regidx_end;
|
||||
u32 regidx_en;
|
||||
struct vdpu_task *task = NULL;
|
||||
struct vdpu_dev *dec = NULL;
|
||||
|
||||
mpp_debug_enter();
|
||||
|
||||
task = to_vdpu_task(mpp_task);
|
||||
dec = to_vdpu_dev(mpp);
|
||||
|
||||
/* FIXME: spin lock here */
|
||||
dec->current_task = task;
|
||||
|
||||
/* clear cache */
|
||||
mpp_write_relaxed(mpp, VDPU2_REG_CLR_CACHE_BASE, 0);
|
||||
/* set registers for hardware */
|
||||
regidx_start = task->hw_info->regidx_start;
|
||||
regidx_end = task->hw_info->regidx_end;
|
||||
regidx_en = task->hw_info->regidx_en;
|
||||
for (i = regidx_start; i <= regidx_end; i++) {
|
||||
if (i == regidx_en)
|
||||
continue;
|
||||
mpp_write_relaxed(mpp, i * sizeof(u32), task->reg[i]);
|
||||
}
|
||||
/* Flush the registers */
|
||||
wmb();
|
||||
mpp_write(mpp, VDPU2_REG_DEC_EN,
|
||||
task->reg[regidx_en] | VDPU2_DEC_START);
|
||||
|
||||
mpp_debug_leave();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vdpu_finish(struct mpp_dev *mpp,
|
||||
struct mpp_task *mpp_task)
|
||||
{
|
||||
u32 i;
|
||||
u32 regidx_start;
|
||||
u32 regidx_end;
|
||||
u32 dec_get;
|
||||
s32 dec_length;
|
||||
struct vdpu_task *task = to_vdpu_task(mpp_task);
|
||||
|
||||
mpp_debug_enter();
|
||||
|
||||
/* read register after running */
|
||||
regidx_start = task->hw_info->regidx_start;
|
||||
regidx_end = task->hw_info->regidx_end;
|
||||
for (i = regidx_start; i <= regidx_end; i++)
|
||||
task->reg[i] = mpp_read_relaxed(mpp, i * sizeof(u32));
|
||||
task->reg[VDPU2_REG_DEC_INT_INDEX] = task->irq_status;
|
||||
/* revert hack for decoded length */
|
||||
dec_get = task->reg[VDPU2_REG_STREAM_RLC_BASE_INDEX];
|
||||
dec_length = dec_get - task->strm_addr;
|
||||
task->reg[VDPU2_REG_STREAM_RLC_BASE_INDEX] = dec_length << 10;
|
||||
mpp_debug(DEBUG_REGISTER,
|
||||
"dec_get %08x dec_length %d\n",
|
||||
dec_get, dec_length);
|
||||
|
||||
mpp_debug_leave();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vdpu_result(struct mpp_dev *mpp,
|
||||
struct mpp_task *mpp_task,
|
||||
u32 __user *dst, u32 size)
|
||||
{
|
||||
struct vdpu_task *task = to_vdpu_task(mpp_task);
|
||||
|
||||
/* FIXME may overflow the kernel */
|
||||
if (copy_to_user(dst, task->reg, size)) {
|
||||
mpp_err("copy_to_user failed\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vdpu_free_task(struct mpp_session *session,
|
||||
struct mpp_task *mpp_task)
|
||||
{
|
||||
struct vdpu_task *task = to_vdpu_task(mpp_task);
|
||||
|
||||
mpp_task_finalize(session, mpp_task);
|
||||
kfree(task);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vdpu_debugfs_remove(struct mpp_dev *mpp)
|
||||
{
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
struct vdpu_dev *dec = to_vdpu_dev(mpp);
|
||||
|
||||
debugfs_remove_recursive(dec->debugfs);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vdpu_debugfs_init(struct mpp_dev *mpp)
|
||||
{
|
||||
struct vdpu_dev *dec = to_vdpu_dev(mpp);
|
||||
struct device_node *np = mpp->dev->of_node;
|
||||
|
||||
dec->aclk_debug = 0;
|
||||
dec->session_max_buffers_debug = 0;
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
dec->debugfs = debugfs_create_dir(np->name, mpp->srv->debugfs);
|
||||
if (IS_ERR_OR_NULL(dec->debugfs)) {
|
||||
mpp_err("failed on open debugfs\n");
|
||||
dec->debugfs = NULL;
|
||||
return -EIO;
|
||||
}
|
||||
debugfs_create_u32("aclk", 0644,
|
||||
dec->debugfs, &dec->aclk_debug);
|
||||
debugfs_create_u32("session_buffers", 0644,
|
||||
dec->debugfs, &dec->session_max_buffers_debug);
|
||||
#endif
|
||||
if (dec->session_max_buffers_debug)
|
||||
mpp->session_max_buffers = dec->session_max_buffers_debug;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vdpu_init(struct mpp_dev *mpp)
|
||||
{
|
||||
struct vdpu_dev *dec = to_vdpu_dev(mpp);
|
||||
|
||||
mpp->grf_info = &mpp->srv->grf_infos[MPP_DRIVER_VDPU2];
|
||||
|
||||
dec->aclk = devm_clk_get(mpp->dev, "aclk_vcodec");
|
||||
if (IS_ERR(dec->aclk)) {
|
||||
mpp_err("failed on clk_get aclk_vcodec\n");
|
||||
dec->aclk = NULL;
|
||||
}
|
||||
dec->hclk = devm_clk_get(mpp->dev, "hclk_vcodec");
|
||||
if (IS_ERR(dec->hclk)) {
|
||||
mpp_err("failed on clk_get hclk_vcodec\n");
|
||||
dec->hclk = NULL;
|
||||
}
|
||||
|
||||
dec->rst_a = devm_reset_control_get_shared(mpp->dev, "video_a");
|
||||
if (IS_ERR_OR_NULL(dec->rst_a)) {
|
||||
mpp_err("No aclk reset resource define\n");
|
||||
dec->rst_a = NULL;
|
||||
}
|
||||
dec->rst_h = devm_reset_control_get_shared(mpp->dev, "video_h");
|
||||
if (IS_ERR_OR_NULL(dec->rst_h)) {
|
||||
mpp_err("No hclk reset resource define\n");
|
||||
dec->rst_h = NULL;
|
||||
}
|
||||
mpp_safe_unreset(dec->rst_a);
|
||||
mpp_safe_unreset(dec->rst_h);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vdpu_power_on(struct mpp_dev *mpp)
|
||||
{
|
||||
struct vdpu_dev *dec = to_vdpu_dev(mpp);
|
||||
|
||||
if (dec->aclk)
|
||||
clk_prepare_enable(dec->aclk);
|
||||
if (dec->hclk)
|
||||
clk_prepare_enable(dec->hclk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vdpu_power_off(struct mpp_dev *mpp)
|
||||
{
|
||||
struct vdpu_dev *dec = to_vdpu_dev(mpp);
|
||||
|
||||
if (dec->aclk)
|
||||
clk_disable_unprepare(dec->aclk);
|
||||
if (dec->hclk)
|
||||
clk_disable_unprepare(dec->hclk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vdpu_get_freq(struct mpp_dev *mpp,
|
||||
struct mpp_task *mpp_task)
|
||||
{
|
||||
struct vdpu_task *task = to_vdpu_task(mpp_task);
|
||||
|
||||
task->aclk_freq = 300;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vdpu_set_freq(struct mpp_dev *mpp,
|
||||
struct mpp_task *mpp_task)
|
||||
{
|
||||
struct vdpu_dev *dec = to_vdpu_dev(mpp);
|
||||
struct vdpu_task *task = to_vdpu_task(mpp_task);
|
||||
|
||||
/* check whether use debug freq */
|
||||
task->aclk_freq = dec->aclk_debug ?
|
||||
dec->aclk_debug : task->aclk_freq;
|
||||
|
||||
clk_set_rate(dec->aclk, task->aclk_freq * MHZ);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vdpu_reduce_freq(struct mpp_dev *mpp)
|
||||
{
|
||||
struct vdpu_dev *dec = to_vdpu_dev(mpp);
|
||||
|
||||
if (dec->aclk)
|
||||
clk_set_rate(dec->aclk, 50 * MHZ);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vdpu_irq(struct mpp_dev *mpp)
|
||||
{
|
||||
mpp->irq_status = mpp_read(mpp, VDPU2_REG_DEC_INT);
|
||||
if (!(mpp->irq_status & VDPU2_DEC_INT_RAW))
|
||||
return IRQ_NONE;
|
||||
|
||||
mpp_write(mpp, VDPU2_REG_DEC_INT, 0);
|
||||
/* set clock gating to save power */
|
||||
mpp_write(mpp, VDPU2_REG_DEC_EN, VDPU2_DEC_CLOCK_GATE_EN);
|
||||
|
||||
return IRQ_WAKE_THREAD;
|
||||
}
|
||||
|
||||
static int vdpu_isr(struct mpp_dev *mpp)
|
||||
{
|
||||
u32 err_mask;
|
||||
struct vdpu_task *task = NULL;
|
||||
struct mpp_task *mpp_task = NULL;
|
||||
struct vdpu_dev *dec = to_vdpu_dev(mpp);
|
||||
|
||||
/* FIXME use a spin lock here */
|
||||
task = dec->current_task;
|
||||
if (!task) {
|
||||
dev_err(dec->mpp.dev, "no current task\n");
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
mpp_task = &task->mpp_task;
|
||||
mpp_time_diff(mpp_task);
|
||||
dec->current_task = NULL;
|
||||
task->irq_status = mpp->irq_status;
|
||||
mpp_debug(DEBUG_IRQ_STATUS, "irq_status: %08x\n",
|
||||
task->irq_status);
|
||||
|
||||
err_mask = VDPU2_INT_TIMEOUT
|
||||
| VDPU2_INT_STRM_ERROR
|
||||
| VDPU2_INT_ASO_ERROR
|
||||
| VDPU2_INT_BUF_EMPTY
|
||||
| VDPU2_INT_BUS_ERROR;
|
||||
|
||||
if (err_mask & task->irq_status)
|
||||
atomic_inc(&mpp->reset_request);
|
||||
|
||||
mpp_task_finish(mpp_task->session, mpp_task);
|
||||
|
||||
mpp_debug_leave();
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int vdpu_reset(struct mpp_dev *mpp)
|
||||
{
|
||||
struct vdpu_dev *dec = to_vdpu_dev(mpp);
|
||||
|
||||
mpp_write(mpp, VDPU2_REG_DEC_EN, 0);
|
||||
mpp_write(mpp, VDPU2_REG_DEC_INT, 0);
|
||||
if (dec->rst_a && dec->rst_h) {
|
||||
/* Don't skip this or iommu won't work after reset */
|
||||
rockchip_pmu_idle_request(mpp->dev, true);
|
||||
mpp_safe_reset(dec->rst_a);
|
||||
mpp_safe_reset(dec->rst_h);
|
||||
udelay(5);
|
||||
mpp_safe_unreset(dec->rst_a);
|
||||
mpp_safe_unreset(dec->rst_h);
|
||||
rockchip_pmu_idle_request(mpp->dev, false);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct mpp_hw_ops vdpu_v2_hw_ops = {
|
||||
.init = vdpu_init,
|
||||
.power_on = vdpu_power_on,
|
||||
.power_off = vdpu_power_off,
|
||||
.get_freq = vdpu_get_freq,
|
||||
.set_freq = vdpu_set_freq,
|
||||
.reduce_freq = vdpu_reduce_freq,
|
||||
.reset = vdpu_reset,
|
||||
};
|
||||
|
||||
static struct mpp_dev_ops vdpu_v2_dev_ops = {
|
||||
.alloc_task = vdpu_alloc_task,
|
||||
.prepare = vdpu_prepare,
|
||||
.run = vdpu_run,
|
||||
.irq = vdpu_irq,
|
||||
.isr = vdpu_isr,
|
||||
.finish = vdpu_finish,
|
||||
.result = vdpu_result,
|
||||
.free_task = vdpu_free_task,
|
||||
};
|
||||
|
||||
static const struct mpp_dev_var vdpu_v2_data = {
|
||||
.device_type = MPP_DEVICE_DEC,
|
||||
.hw_info = &vdpu_v2_hw_info,
|
||||
.trans_info = vdpu_v2_trans,
|
||||
.hw_ops = &vdpu_v2_hw_ops,
|
||||
.dev_ops = &vdpu_v2_dev_ops,
|
||||
};
|
||||
|
||||
static const struct of_device_id mpp_vdpu2_dt_match[] = {
|
||||
{
|
||||
.compatible = "rockchip,vpu-decoder-v2",
|
||||
.data = &vdpu_v2_data,
|
||||
},
|
||||
{},
|
||||
};
|
||||
|
||||
static int vdpu_probe(struct platform_device *pdev)
|
||||
{
|
||||
int ret = 0;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct vdpu_dev *dec = NULL;
|
||||
struct mpp_dev *mpp = NULL;
|
||||
const struct of_device_id *match = NULL;
|
||||
|
||||
dev_info(dev, "probe device\n");
|
||||
dec = devm_kzalloc(dev, sizeof(struct vdpu_dev), GFP_KERNEL);
|
||||
if (!dec)
|
||||
return -ENOMEM;
|
||||
platform_set_drvdata(pdev, dec);
|
||||
|
||||
mpp = &dec->mpp;
|
||||
|
||||
if (pdev->dev.of_node) {
|
||||
match = of_match_node(mpp_vdpu2_dt_match,
|
||||
pdev->dev.of_node);
|
||||
if (match)
|
||||
mpp->var = (struct mpp_dev_var *)match->data;
|
||||
}
|
||||
|
||||
ret = mpp_dev_probe(mpp, pdev);
|
||||
if (ret) {
|
||||
dev_err(dev, "probe sub driver failed\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = devm_request_threaded_irq(dev, mpp->irq,
|
||||
mpp_dev_irq,
|
||||
mpp_dev_isr_sched,
|
||||
IRQF_SHARED,
|
||||
dev_name(dev), mpp);
|
||||
if (ret) {
|
||||
dev_err(dev, "register interrupter runtime failed\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (mpp->var->device_type == MPP_DEVICE_DEC) {
|
||||
mpp->srv->sub_devices[MPP_DEVICE_PP] = mpp;
|
||||
mpp->srv->sub_devices[MPP_DEVICE_DEC_PP] = mpp;
|
||||
}
|
||||
mpp->session_max_buffers = VDPU2_SESSION_MAX_BUFFERS;
|
||||
vdpu_debugfs_init(mpp);
|
||||
dev_info(dev, "probing finish\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vdpu_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct vdpu_dev *dec = platform_get_drvdata(pdev);
|
||||
|
||||
dev_info(dev, "remove device\n");
|
||||
mpp_dev_remove(&dec->mpp);
|
||||
vdpu_debugfs_remove(&dec->mpp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void vdpu_shutdown(struct platform_device *pdev)
|
||||
{
|
||||
int ret;
|
||||
int val;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct vdpu_dev *dec = platform_get_drvdata(pdev);
|
||||
struct mpp_dev *mpp = &dec->mpp;
|
||||
|
||||
dev_info(dev, "shutdown device\n");
|
||||
|
||||
atomic_inc(&mpp->srv->shutdown_request);
|
||||
ret = readx_poll_timeout(atomic_read,
|
||||
&mpp->total_running,
|
||||
val, val == 0, 20000, 200000);
|
||||
if (ret == -ETIMEDOUT)
|
||||
dev_err(dev, "wait total running time out\n");
|
||||
}
|
||||
|
||||
struct platform_driver rockchip_vdpu2_driver = {
|
||||
.probe = vdpu_probe,
|
||||
.remove = vdpu_remove,
|
||||
.shutdown = vdpu_shutdown,
|
||||
.driver = {
|
||||
.name = VDPU2_DRIVER_NAME,
|
||||
.of_match_table = of_match_ptr(mpp_vdpu2_dt_match),
|
||||
},
|
||||
};
|
||||
EXPORT_SYMBOL(rockchip_vdpu2_driver);
|
||||
617
drivers/video/rockchip/mpp/mpp_vepu1.c
Normal file
617
drivers/video/rockchip/mpp/mpp_vepu1.c
Normal file
@@ -0,0 +1,617 @@
|
||||
// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
|
||||
/*
|
||||
* Copyright (c) 2019 Fuzhou Rockchip Electronics Co., Ltd
|
||||
*
|
||||
* author:
|
||||
* Alpha Lin, alpha.lin@rock-chips.com
|
||||
* Randy Li, randy.li@rock-chips.com
|
||||
* Ding Wei, leo.ding@rock-chips.com
|
||||
*
|
||||
*/
|
||||
#include <asm/cacheflush.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <soc/rockchip/pm_domains.h>
|
||||
|
||||
#include "mpp_debug.h"
|
||||
#include "mpp_common.h"
|
||||
#include "mpp_iommu.h"
|
||||
|
||||
#define VEPU1_DRIVER_NAME "mpp_vepu1"
|
||||
|
||||
#define VEPU1_SESSION_MAX_BUFFERS 20
|
||||
/* The maximum registers number of all the version */
|
||||
#define VEPU1_REG_NUM 164
|
||||
#define VEPU1_REG_START_INDEX 0
|
||||
#define VEPU1_REG_END_INDEX 163
|
||||
|
||||
#define VEPU1_REG_INT 0x004
|
||||
#define VEPU1_REG_INT_INDEX (1)
|
||||
#define VEPU1_INT_SLICE BIT(8)
|
||||
#define VEPU1_INT_TIMEOUT BIT(6)
|
||||
#define VEPU1_INT_BUF_FULL BIT(5)
|
||||
#define VEPU1_INT_RESET BIT(4)
|
||||
#define VEPU1_INT_BUS_ERROR BIT(3)
|
||||
#define VEPU1_INT_RDY BIT(2)
|
||||
#define VEPU1_IRQ_DIS BIT(1)
|
||||
#define VEPU1_INT_RAW BIT(0)
|
||||
|
||||
#define VEPU1_REG_ENC_EN 0x038
|
||||
#define VEPU1_REG_ENC_EN_INDEX (14)
|
||||
#define VEPU1_INT_TIMEOUT_EN BIT(31)
|
||||
#define VEPU1_INT_SLICE_EN BIT(28)
|
||||
#define VEPU1_ENC_START BIT(0)
|
||||
|
||||
#define VEPU1_GET_FORMAT(x) (((x) >> 1) & 0x3)
|
||||
#define VEPU1_FORMAT_MASK (0x06)
|
||||
|
||||
#define VEPU1_FMT_RESERVED (0)
|
||||
#define VEPU1_FMT_VP8E (1)
|
||||
#define VEPU1_FMT_JPEGE (2)
|
||||
#define VEPU1_FMT_H264E (3)
|
||||
|
||||
#define VEPU1_REG_CLR_CACHE_BASE 0xc10
|
||||
|
||||
#define to_vepu_task(task) \
|
||||
container_of(task, struct vepu_task, mpp_task)
|
||||
#define to_vepu_dev(dev) \
|
||||
container_of(dev, struct vepu_dev, mpp)
|
||||
|
||||
struct vepu_task {
|
||||
struct mpp_task mpp_task;
|
||||
struct mpp_hw_info *hw_info;
|
||||
|
||||
unsigned long aclk_freq;
|
||||
u32 reg[VEPU1_REG_NUM];
|
||||
u32 idx;
|
||||
struct extra_info_for_iommu ext_inf;
|
||||
u32 irq_status;
|
||||
};
|
||||
|
||||
struct vepu_dev {
|
||||
struct mpp_dev mpp;
|
||||
|
||||
struct clk *aclk;
|
||||
struct clk *hclk;
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
struct dentry *debugfs;
|
||||
#endif
|
||||
u32 aclk_debug;
|
||||
u32 session_max_buffers_debug;
|
||||
|
||||
struct reset_control *rst_a;
|
||||
struct reset_control *rst_h;
|
||||
|
||||
struct vepu_task *current_task;
|
||||
};
|
||||
|
||||
static struct mpp_hw_info vepu_v1_hw_info = {
|
||||
.reg_num = VEPU1_REG_NUM,
|
||||
.regidx_start = VEPU1_REG_START_INDEX,
|
||||
.regidx_end = VEPU1_REG_END_INDEX,
|
||||
.regidx_en = VEPU1_REG_ENC_EN_INDEX,
|
||||
};
|
||||
|
||||
/*
|
||||
* file handle translate information
|
||||
*/
|
||||
static const char trans_tbl_default[] = {
|
||||
5, 6, 7, 8, 9, 10, 11, 12, 13, 51
|
||||
};
|
||||
|
||||
static const char trans_tbl_vp8e[] = {
|
||||
5, 6, 7, 8, 9, 10, 11, 12, 13, 16, 17, 26, 51, 52, 58, 59
|
||||
};
|
||||
|
||||
static struct mpp_trans_info trans_rk_vepu1[] = {
|
||||
[VEPU1_FMT_RESERVED] = {
|
||||
.count = 0,
|
||||
.table = NULL,
|
||||
},
|
||||
[VEPU1_FMT_VP8E] = {
|
||||
.count = sizeof(trans_tbl_vp8e),
|
||||
.table = trans_tbl_vp8e,
|
||||
},
|
||||
[VEPU1_FMT_JPEGE] = {
|
||||
.count = sizeof(trans_tbl_default),
|
||||
.table = trans_tbl_default,
|
||||
},
|
||||
[VEPU1_FMT_H264E] = {
|
||||
.count = sizeof(trans_tbl_default),
|
||||
.table = trans_tbl_default,
|
||||
},
|
||||
};
|
||||
|
||||
static void *vepu_alloc_task(struct mpp_session *session,
|
||||
void __user *src, u32 size)
|
||||
{
|
||||
u32 reg_len;
|
||||
u32 extinf_len;
|
||||
u32 fmt = 0;
|
||||
int err = -EFAULT;
|
||||
struct vepu_task *task = NULL;
|
||||
u32 dwsize = size / sizeof(u32);
|
||||
struct mpp_dev *mpp = session->mpp;
|
||||
|
||||
mpp_debug_enter();
|
||||
|
||||
task = kzalloc(sizeof(*task), GFP_KERNEL);
|
||||
if (!task)
|
||||
return NULL;
|
||||
|
||||
mpp_task_init(session, &task->mpp_task);
|
||||
|
||||
task->hw_info = mpp->var->hw_info;
|
||||
reg_len = min(task->hw_info->reg_num, dwsize);
|
||||
extinf_len = dwsize > reg_len ? (dwsize - reg_len) * 4 : 0;
|
||||
|
||||
if (copy_from_user(task->reg, src, reg_len * 4)) {
|
||||
mpp_err("error: copy_from_user failed in reg_init\n");
|
||||
err = -EFAULT;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
fmt = VEPU1_GET_FORMAT(task->reg[VEPU1_REG_ENC_EN_INDEX]);
|
||||
if (extinf_len > 0) {
|
||||
if (likely(fmt == VEPU1_FMT_JPEGE)) {
|
||||
err = copy_from_user(&task->ext_inf,
|
||||
(u8 *)src + size
|
||||
- JPEG_IOC_EXTRA_SIZE,
|
||||
JPEG_IOC_EXTRA_SIZE);
|
||||
} else {
|
||||
u32 ext_cpy = min_t(size_t, extinf_len,
|
||||
sizeof(task->ext_inf));
|
||||
err = copy_from_user(&task->ext_inf,
|
||||
(u32 *)src + reg_len, ext_cpy);
|
||||
}
|
||||
|
||||
if (err) {
|
||||
mpp_err("copy_from_user failed when extra info\n");
|
||||
err = -EFAULT;
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
err = mpp_translate_reg_address(session->mpp,
|
||||
&task->mpp_task,
|
||||
fmt, task->reg);
|
||||
if (err) {
|
||||
mpp_err("error: translate reg address failed.\n");
|
||||
mpp_dump_reg(task->reg,
|
||||
task->hw_info->regidx_start,
|
||||
task->hw_info->regidx_end);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
mpp_debug(DEBUG_SET_REG, "extra info cnt %u, magic %08x",
|
||||
task->ext_inf.cnt, task->ext_inf.magic);
|
||||
mpp_translate_extra_info(&task->mpp_task,
|
||||
&task->ext_inf,
|
||||
task->reg);
|
||||
|
||||
mpp_debug_leave();
|
||||
|
||||
return &task->mpp_task;
|
||||
|
||||
fail:
|
||||
mpp_task_finalize(session, &task->mpp_task);
|
||||
kfree(task);
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
static int vepu_prepare(struct mpp_dev *mpp,
|
||||
struct mpp_task *task)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int vepu_run(struct mpp_dev *mpp,
|
||||
struct mpp_task *mpp_task)
|
||||
{
|
||||
u32 i;
|
||||
u32 regidx_start;
|
||||
u32 regidx_end;
|
||||
u32 regidx_en;
|
||||
struct vepu_task *task = NULL;
|
||||
struct vepu_dev *enc = NULL;
|
||||
|
||||
mpp_debug_enter();
|
||||
|
||||
task = to_vepu_task(mpp_task);
|
||||
enc = to_vepu_dev(mpp);
|
||||
|
||||
/* FIXME: spin lock here */
|
||||
enc->current_task = task;
|
||||
/* clear cache */
|
||||
mpp_write_relaxed(mpp, VEPU1_REG_CLR_CACHE_BASE, 0);
|
||||
/* set registers for hardware */
|
||||
regidx_start = task->hw_info->regidx_start;
|
||||
regidx_end = task->hw_info->regidx_end;
|
||||
regidx_en = task->hw_info->regidx_en;
|
||||
/* First, flush correct encoder format */
|
||||
mpp_write_relaxed(mpp, VEPU1_REG_ENC_EN,
|
||||
task->reg[regidx_en] & VEPU1_FORMAT_MASK);
|
||||
/* Second, flush others register */
|
||||
for (i = regidx_start; i <= regidx_end; i++) {
|
||||
if (i == regidx_en)
|
||||
continue;
|
||||
mpp_write_relaxed(mpp, i * sizeof(u32), task->reg[i]);
|
||||
}
|
||||
/* Last, flush start registers */
|
||||
wmb();
|
||||
mpp_write(mpp, VEPU1_REG_ENC_EN,
|
||||
task->reg[regidx_en] | VEPU1_ENC_START);
|
||||
|
||||
mpp_debug_leave();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vepu_irq(struct mpp_dev *mpp)
|
||||
{
|
||||
mpp->irq_status = mpp_read(mpp, VEPU1_REG_INT);
|
||||
if (!(mpp->irq_status & VEPU1_INT_RAW))
|
||||
return IRQ_NONE;
|
||||
|
||||
mpp_write(mpp, VEPU1_REG_INT, 0);
|
||||
|
||||
return IRQ_WAKE_THREAD;
|
||||
}
|
||||
|
||||
static int vepu_isr(struct mpp_dev *mpp)
|
||||
{
|
||||
u32 err_mask;
|
||||
struct vepu_task *task = NULL;
|
||||
struct mpp_task *mpp_task = NULL;
|
||||
struct vepu_dev *enc = to_vepu_dev(mpp);
|
||||
|
||||
/* FIXME use a spin lock here */
|
||||
task = enc->current_task;
|
||||
if (!task) {
|
||||
dev_err(enc->mpp.dev, "no current task\n");
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
mpp_task = &task->mpp_task;
|
||||
mpp_time_diff(mpp_task);
|
||||
enc->current_task = NULL;
|
||||
task->irq_status = mpp->irq_status;
|
||||
mpp_debug(DEBUG_IRQ_STATUS, "irq_status: %08x\n",
|
||||
task->irq_status);
|
||||
|
||||
err_mask = VEPU1_INT_TIMEOUT
|
||||
| VEPU1_INT_BUF_FULL
|
||||
| VEPU1_INT_BUS_ERROR;
|
||||
|
||||
if (err_mask & task->irq_status)
|
||||
atomic_inc(&mpp->reset_request);
|
||||
|
||||
mpp_task_finish(mpp_task->session, mpp_task);
|
||||
|
||||
mpp_debug_leave();
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int vepu_finish(struct mpp_dev *mpp,
|
||||
struct mpp_task *mpp_task)
|
||||
{
|
||||
u32 i;
|
||||
u32 regidx_start;
|
||||
u32 regidx_end;
|
||||
struct vepu_task *task = to_vepu_task(mpp_task);
|
||||
|
||||
mpp_debug_enter();
|
||||
/* read register after running */
|
||||
regidx_start = task->hw_info->regidx_start;
|
||||
regidx_end = task->hw_info->regidx_end;
|
||||
for (i = regidx_start; i <= regidx_end; i++)
|
||||
task->reg[i] = mpp_read_relaxed(mpp, i * sizeof(u32));
|
||||
task->reg[VEPU1_REG_INT_INDEX] = task->irq_status;
|
||||
|
||||
mpp_debug_leave();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vepu_result(struct mpp_dev *mpp,
|
||||
struct mpp_task *mpp_task,
|
||||
u32 __user *dst, u32 size)
|
||||
{
|
||||
struct vepu_task *task = to_vepu_task(mpp_task);
|
||||
|
||||
/* FIXME may overflow the kernel */
|
||||
if (copy_to_user(dst, task->reg, size)) {
|
||||
mpp_err("copy_to_user failed\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vepu_free_task(struct mpp_session *session,
|
||||
struct mpp_task *mpp_task)
|
||||
{
|
||||
struct vepu_task *task = to_vepu_task(mpp_task);
|
||||
|
||||
mpp_task_finalize(session, mpp_task);
|
||||
kfree(task);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vepu_debugfs_remove(struct mpp_dev *mpp)
|
||||
{
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
struct vepu_dev *enc = to_vepu_dev(mpp);
|
||||
|
||||
debugfs_remove_recursive(enc->debugfs);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vepu_debugfs_init(struct mpp_dev *mpp)
|
||||
{
|
||||
struct vepu_dev *enc = to_vepu_dev(mpp);
|
||||
struct device_node *np = mpp->dev->of_node;
|
||||
|
||||
enc->aclk_debug = 0;
|
||||
enc->session_max_buffers_debug = 0;
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
enc->debugfs = debugfs_create_dir(np->name, mpp->srv->debugfs);
|
||||
if (IS_ERR_OR_NULL(enc->debugfs)) {
|
||||
mpp_err("failed on open debugfs\n");
|
||||
enc->debugfs = NULL;
|
||||
return -EIO;
|
||||
}
|
||||
debugfs_create_u32("aclk", 0644,
|
||||
enc->debugfs, &enc->aclk_debug);
|
||||
debugfs_create_u32("session_buffers", 0644,
|
||||
enc->debugfs, &enc->session_max_buffers_debug);
|
||||
#endif
|
||||
if (enc->session_max_buffers_debug)
|
||||
mpp->session_max_buffers = enc->session_max_buffers_debug;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vepu_init(struct mpp_dev *mpp)
|
||||
{
|
||||
struct vepu_dev *enc = to_vepu_dev(mpp);
|
||||
|
||||
mpp->grf_info = &mpp->srv->grf_infos[MPP_DRIVER_VEPU1];
|
||||
|
||||
enc->aclk = devm_clk_get(mpp->dev, "aclk_vcodec");
|
||||
if (IS_ERR(enc->aclk)) {
|
||||
mpp_err("failed on clk_get aclk_vcodec\n");
|
||||
enc->aclk = NULL;
|
||||
}
|
||||
enc->hclk = devm_clk_get(mpp->dev, "hclk_vcodec");
|
||||
if (IS_ERR(enc->hclk)) {
|
||||
mpp_err("failed on clk_get hclk_vcodec\n");
|
||||
enc->hclk = NULL;
|
||||
}
|
||||
|
||||
enc->rst_a = devm_reset_control_get_shared(mpp->dev, "video_a");
|
||||
if (IS_ERR_OR_NULL(enc->rst_a)) {
|
||||
mpp_err("No aclk reset resource define\n");
|
||||
enc->rst_a = NULL;
|
||||
}
|
||||
enc->rst_h = devm_reset_control_get_shared(mpp->dev, "video_h");
|
||||
if (IS_ERR_OR_NULL(enc->rst_h)) {
|
||||
mpp_err("No hclk reset resource define\n");
|
||||
enc->rst_h = NULL;
|
||||
}
|
||||
mpp_safe_unreset(enc->rst_a);
|
||||
mpp_safe_unreset(enc->rst_h);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vepu_power_on(struct mpp_dev *mpp)
|
||||
{
|
||||
struct vepu_dev *enc = to_vepu_dev(mpp);
|
||||
|
||||
if (enc->aclk)
|
||||
clk_prepare_enable(enc->aclk);
|
||||
if (enc->hclk)
|
||||
clk_prepare_enable(enc->hclk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vepu_power_off(struct mpp_dev *mpp)
|
||||
{
|
||||
struct vepu_dev *enc = to_vepu_dev(mpp);
|
||||
|
||||
if (enc->aclk)
|
||||
clk_disable_unprepare(enc->aclk);
|
||||
if (enc->hclk)
|
||||
clk_disable_unprepare(enc->hclk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vepu_get_freq(struct mpp_dev *mpp,
|
||||
struct mpp_task *mpp_task)
|
||||
{
|
||||
struct vepu_task *task = to_vepu_task(mpp_task);
|
||||
|
||||
task->aclk_freq = 300;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vepu_set_freq(struct mpp_dev *mpp,
|
||||
struct mpp_task *mpp_task)
|
||||
{
|
||||
struct vepu_dev *enc = to_vepu_dev(mpp);
|
||||
struct vepu_task *task = to_vepu_task(mpp_task);
|
||||
|
||||
/* check whether use debug freq */
|
||||
task->aclk_freq = enc->aclk_debug ?
|
||||
enc->aclk_debug : task->aclk_freq;
|
||||
|
||||
clk_set_rate(enc->aclk, task->aclk_freq * MHZ);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vepu_reduce_freq(struct mpp_dev *mpp)
|
||||
{
|
||||
struct vepu_dev *enc = to_vepu_dev(mpp);
|
||||
|
||||
if (enc->aclk)
|
||||
clk_set_rate(enc->aclk, 50 * MHZ);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vepu_reset(struct mpp_dev *mpp)
|
||||
{
|
||||
struct vepu_dev *enc = to_vepu_dev(mpp);
|
||||
|
||||
if (enc->rst_a && enc->rst_h) {
|
||||
/* Don't skip this or iommu won't work after reset */
|
||||
rockchip_pmu_idle_request(mpp->dev, true);
|
||||
mpp_safe_reset(enc->rst_a);
|
||||
mpp_safe_reset(enc->rst_h);
|
||||
udelay(5);
|
||||
mpp_safe_unreset(enc->rst_a);
|
||||
mpp_safe_unreset(enc->rst_h);
|
||||
rockchip_pmu_idle_request(mpp->dev, false);
|
||||
}
|
||||
mpp_write(mpp, VEPU1_REG_ENC_EN, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct mpp_hw_ops vepu_v1_hw_ops = {
|
||||
.init = vepu_init,
|
||||
.power_on = vepu_power_on,
|
||||
.power_off = vepu_power_off,
|
||||
.get_freq = vepu_get_freq,
|
||||
.set_freq = vepu_set_freq,
|
||||
.reduce_freq = vepu_reduce_freq,
|
||||
.reset = vepu_reset,
|
||||
};
|
||||
|
||||
static struct mpp_dev_ops vepu_v1_dev_ops = {
|
||||
.alloc_task = vepu_alloc_task,
|
||||
.prepare = vepu_prepare,
|
||||
.run = vepu_run,
|
||||
.irq = vepu_irq,
|
||||
.isr = vepu_isr,
|
||||
.finish = vepu_finish,
|
||||
.result = vepu_result,
|
||||
.free_task = vepu_free_task,
|
||||
};
|
||||
|
||||
static const struct mpp_dev_var vepu_v1_data = {
|
||||
.device_type = MPP_DEVICE_ENC,
|
||||
.hw_info = &vepu_v1_hw_info,
|
||||
.trans_info = trans_rk_vepu1,
|
||||
.hw_ops = &vepu_v1_hw_ops,
|
||||
.dev_ops = &vepu_v1_dev_ops,
|
||||
};
|
||||
|
||||
static const struct of_device_id mpp_vepu1_dt_match[] = {
|
||||
{
|
||||
.compatible = "rockchip,vpu-encoder-v1",
|
||||
.data = &vepu_v1_data,
|
||||
},
|
||||
{},
|
||||
};
|
||||
|
||||
static int vepu_probe(struct platform_device *pdev)
|
||||
{
|
||||
int ret = 0;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct vepu_dev *enc = NULL;
|
||||
struct mpp_dev *mpp = NULL;
|
||||
const struct of_device_id *match = NULL;
|
||||
|
||||
dev_info(dev, "probe device\n");
|
||||
enc = devm_kzalloc(dev, sizeof(struct vepu_dev), GFP_KERNEL);
|
||||
if (!enc)
|
||||
return -ENOMEM;
|
||||
|
||||
mpp = &enc->mpp;
|
||||
platform_set_drvdata(pdev, enc);
|
||||
|
||||
if (pdev->dev.of_node) {
|
||||
match = of_match_node(mpp_vepu1_dt_match, pdev->dev.of_node);
|
||||
if (match)
|
||||
mpp->var = (struct mpp_dev_var *)match->data;
|
||||
}
|
||||
|
||||
ret = mpp_dev_probe(mpp, pdev);
|
||||
if (ret) {
|
||||
dev_err(dev, "probe sub driver failed\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = devm_request_threaded_irq(dev, mpp->irq,
|
||||
mpp_dev_irq,
|
||||
mpp_dev_isr_sched,
|
||||
IRQF_SHARED,
|
||||
dev_name(dev), mpp);
|
||||
if (ret) {
|
||||
dev_err(dev, "register interrupter runtime failed\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
mpp->session_max_buffers = VEPU1_SESSION_MAX_BUFFERS;
|
||||
vepu_debugfs_init(mpp);
|
||||
dev_info(dev, "probing finish\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vepu_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct vepu_dev *enc = platform_get_drvdata(pdev);
|
||||
|
||||
dev_info(dev, "remove device\n");
|
||||
mpp_dev_remove(&enc->mpp);
|
||||
vepu_debugfs_remove(&enc->mpp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void vepu_shutdown(struct platform_device *pdev)
|
||||
{
|
||||
int ret;
|
||||
int val;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct vepu_dev *enc = platform_get_drvdata(pdev);
|
||||
struct mpp_dev *mpp = &enc->mpp;
|
||||
|
||||
dev_info(dev, "shutdown device\n");
|
||||
|
||||
atomic_inc(&mpp->srv->shutdown_request);
|
||||
ret = readx_poll_timeout(atomic_read,
|
||||
&mpp->total_running,
|
||||
val, val == 0, 20000, 200000);
|
||||
if (ret == -ETIMEDOUT)
|
||||
dev_err(dev, "wait total running time out\n");
|
||||
}
|
||||
|
||||
struct platform_driver rockchip_vepu1_driver = {
|
||||
.probe = vepu_probe,
|
||||
.remove = vepu_remove,
|
||||
.shutdown = vepu_shutdown,
|
||||
.driver = {
|
||||
.name = VEPU1_DRIVER_NAME,
|
||||
.of_match_table = of_match_ptr(mpp_vepu1_dt_match),
|
||||
},
|
||||
};
|
||||
EXPORT_SYMBOL(rockchip_vepu1_driver);
|
||||
624
drivers/video/rockchip/mpp/mpp_vepu2.c
Normal file
624
drivers/video/rockchip/mpp/mpp_vepu2.c
Normal file
@@ -0,0 +1,624 @@
|
||||
// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
|
||||
/*
|
||||
* Copyright (c) 2019 Fuzhou Rockchip Electronics Co., Ltd
|
||||
*
|
||||
* author:
|
||||
* Alpha Lin, alpha.lin@rock-chips.com
|
||||
* Randy Li, randy.li@rock-chips.com
|
||||
* Ding Wei, leo.ding@rock-chips.com
|
||||
*
|
||||
*/
|
||||
#include <asm/cacheflush.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <soc/rockchip/pm_domains.h>
|
||||
|
||||
#include "mpp_debug.h"
|
||||
#include "mpp_common.h"
|
||||
#include "mpp_iommu.h"
|
||||
|
||||
#define VEPU2_DRIVER_NAME "mpp_vepu2"
|
||||
|
||||
#define VEPU2_SESSION_MAX_BUFFERS 20
|
||||
/* The maximum registers number of all the version */
|
||||
#define VEPU2_REG_NUM 184
|
||||
#define VEPU2_REG_START_INDEX 0
|
||||
#define VEPU2_REG_END_INDEX 183
|
||||
|
||||
#define VEPU2_REG_ENC_EN 0x19c
|
||||
#define VEPU2_REG_ENC_EN_INDEX (103)
|
||||
#define VEPU2_ENC_START BIT(0)
|
||||
|
||||
#define VEPU2_GET_FORMAT(x) (((x) >> 4) & 0x3)
|
||||
#define VEPU2_FORMAT_MASK (0x30)
|
||||
|
||||
#define VEPU2_FMT_RESERVED (0)
|
||||
#define VEPU2_FMT_VP8E (1)
|
||||
#define VEPU2_FMT_JPEGE (2)
|
||||
#define VEPU2_FMT_H264E (3)
|
||||
|
||||
#define VEPU2_REG_MB_CTRL 0x1a0
|
||||
#define VEPU2_REG_MB_CTRL_INDEX (104)
|
||||
|
||||
#define VEPU2_REG_INT 0x1b4
|
||||
#define VEPU2_REG_INT_INDEX (109)
|
||||
#define VEPU2_MV_SAD_WR_EN BIT(24)
|
||||
#define VEPU2_ROCON_WRITE_DIS BIT(20)
|
||||
#define VEPU2_INT_SLICE_EN BIT(16)
|
||||
#define VEPU2_CLOCK_GATE_EN BIT(12)
|
||||
#define VEPU2_INT_TIMEOUT_EN BIT(10)
|
||||
#define VEPU2_INT_CLEAR BIT(9)
|
||||
#define VEPU2_IRQ_DIS BIT(8)
|
||||
#define VEPU2_INT_TIMEOUT BIT(6)
|
||||
#define VEPU2_INT_BUF_FULL BIT(5)
|
||||
#define VEPU2_INT_BUS_ERROR BIT(4)
|
||||
#define VEPU2_INT_SLICE BIT(2)
|
||||
#define VEPU2_INT_RDY BIT(1)
|
||||
#define VEPU2_INT_RAW BIT(0)
|
||||
|
||||
#define RKVPUE2_REG_DMV_4P_1P(i) (0x1e0 + ((i) << 4))
|
||||
#define RKVPUE2_REG_DMV_4P_1P_INDEX(i) (120 + (i))
|
||||
|
||||
#define VEPU2_REG_CLR_CACHE_BASE 0xc10
|
||||
|
||||
#define to_vepu_task(task) \
|
||||
container_of(task, struct vepu_task, mpp_task)
|
||||
#define to_vepu_dev(dev) \
|
||||
container_of(dev, struct vepu_dev, mpp)
|
||||
|
||||
struct vepu_task {
|
||||
struct mpp_task mpp_task;
|
||||
struct mpp_hw_info *hw_info;
|
||||
unsigned long aclk_freq;
|
||||
u32 reg[VEPU2_REG_NUM];
|
||||
u32 idx;
|
||||
struct extra_info_for_iommu ext_inf;
|
||||
u32 irq_status;
|
||||
};
|
||||
|
||||
struct vepu_dev {
|
||||
struct mpp_dev mpp;
|
||||
|
||||
struct clk *aclk;
|
||||
struct clk *hclk;
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
struct dentry *debugfs;
|
||||
#endif
|
||||
u32 aclk_debug;
|
||||
u32 session_max_buffers_debug;
|
||||
|
||||
struct reset_control *rst_a;
|
||||
struct reset_control *rst_h;
|
||||
struct vepu_task *current_task;
|
||||
};
|
||||
|
||||
static struct mpp_hw_info vepu_v2_hw_info = {
|
||||
.reg_num = VEPU2_REG_NUM,
|
||||
.regidx_start = VEPU2_REG_START_INDEX,
|
||||
.regidx_end = VEPU2_REG_END_INDEX,
|
||||
.regidx_en = VEPU2_REG_ENC_EN_INDEX,
|
||||
};
|
||||
|
||||
/*
|
||||
* file handle translate information
|
||||
*/
|
||||
static const char trans_tbl_default[] = {
|
||||
48, 49, 50, 56, 57, 63, 64, 77, 78, 81
|
||||
};
|
||||
|
||||
static const char trans_tbl_vp8e[] = {
|
||||
27, 44, 45, 48, 49, 50, 56, 57, 63, 64,
|
||||
76, 77, 78, 80, 81, 106, 108,
|
||||
};
|
||||
|
||||
static struct mpp_trans_info trans_rk_vepu2[] = {
|
||||
[VEPU2_FMT_RESERVED] = {
|
||||
.count = 0,
|
||||
.table = NULL,
|
||||
},
|
||||
[VEPU2_FMT_VP8E] = {
|
||||
.count = sizeof(trans_tbl_vp8e),
|
||||
.table = trans_tbl_vp8e,
|
||||
},
|
||||
[VEPU2_FMT_JPEGE] = {
|
||||
.count = sizeof(trans_tbl_default),
|
||||
.table = trans_tbl_default,
|
||||
},
|
||||
[VEPU2_FMT_H264E] = {
|
||||
.count = sizeof(trans_tbl_default),
|
||||
.table = trans_tbl_default,
|
||||
},
|
||||
};
|
||||
|
||||
static void *vepu_alloc_task(struct mpp_session *session,
|
||||
void __user *src, u32 size)
|
||||
{
|
||||
u32 reg_len;
|
||||
u32 extinf_len;
|
||||
u32 fmt = 0;
|
||||
int err = -EFAULT;
|
||||
struct vepu_task *task = NULL;
|
||||
u32 dwsize = size / sizeof(u32);
|
||||
struct mpp_dev *mpp = session->mpp;
|
||||
|
||||
mpp_debug_enter();
|
||||
|
||||
task = kzalloc(sizeof(*task), GFP_KERNEL);
|
||||
if (!task)
|
||||
return NULL;
|
||||
|
||||
mpp_task_init(session, &task->mpp_task);
|
||||
task->hw_info = mpp->var->hw_info;
|
||||
reg_len = min(task->hw_info->reg_num, dwsize);
|
||||
extinf_len = dwsize > reg_len ? (dwsize - reg_len) * 4 : 0;
|
||||
|
||||
if (copy_from_user(task->reg, src, reg_len * 4)) {
|
||||
mpp_err("error: copy_from_user failed in reg_init\n");
|
||||
err = -EFAULT;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
fmt = VEPU2_GET_FORMAT(task->reg[VEPU2_REG_ENC_EN_INDEX]);
|
||||
if (extinf_len > 0) {
|
||||
if (likely(fmt == VEPU2_FMT_JPEGE)) {
|
||||
err = copy_from_user(&task->ext_inf,
|
||||
(u8 *)src + size
|
||||
- JPEG_IOC_EXTRA_SIZE,
|
||||
JPEG_IOC_EXTRA_SIZE);
|
||||
} else {
|
||||
u32 ext_cpy = min_t(size_t, extinf_len,
|
||||
sizeof(task->ext_inf));
|
||||
err = copy_from_user(&task->ext_inf,
|
||||
(u32 *)src + reg_len,
|
||||
ext_cpy);
|
||||
}
|
||||
|
||||
if (err) {
|
||||
mpp_err("copy_from_user failed when extra info\n");
|
||||
err = -EFAULT;
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
err = mpp_translate_reg_address(session->mpp,
|
||||
&task->mpp_task,
|
||||
fmt, task->reg);
|
||||
if (err) {
|
||||
mpp_err("error: translate reg address failed.\n");
|
||||
mpp_dump_reg(task->reg,
|
||||
task->hw_info->regidx_start,
|
||||
task->hw_info->regidx_end);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
mpp_debug(DEBUG_SET_REG, "extra info cnt %u, magic %08x",
|
||||
task->ext_inf.cnt, task->ext_inf.magic);
|
||||
mpp_translate_extra_info(&task->mpp_task,
|
||||
&task->ext_inf, task->reg);
|
||||
|
||||
mpp_debug_leave();
|
||||
|
||||
return &task->mpp_task;
|
||||
|
||||
fail:
|
||||
mpp_task_finalize(session, &task->mpp_task);
|
||||
kfree(task);
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
static int vepu_prepare(struct mpp_dev *mpp,
|
||||
struct mpp_task *task)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int vepu_run(struct mpp_dev *mpp,
|
||||
struct mpp_task *mpp_task)
|
||||
{
|
||||
u32 i;
|
||||
u32 regidx_start;
|
||||
u32 regidx_end;
|
||||
u32 regidx_en;
|
||||
struct vepu_task *task = NULL;
|
||||
struct vepu_dev *enc = NULL;
|
||||
|
||||
mpp_debug_enter();
|
||||
|
||||
task = to_vepu_task(mpp_task);
|
||||
enc = to_vepu_dev(mpp);
|
||||
|
||||
/* FIXME: spin lock here */
|
||||
enc->current_task = task;
|
||||
/* clear cache */
|
||||
mpp_write_relaxed(mpp, VEPU2_REG_CLR_CACHE_BASE, 0);
|
||||
|
||||
regidx_start = task->hw_info->regidx_start;
|
||||
regidx_end = task->hw_info->regidx_end;
|
||||
regidx_en = task->hw_info->regidx_en;
|
||||
/* First, flush correct encoder format */
|
||||
mpp_write_relaxed(mpp, VEPU2_REG_ENC_EN,
|
||||
task->reg[regidx_en] & VEPU2_FORMAT_MASK);
|
||||
/* Second, flush others register */
|
||||
for (i = regidx_start; i <= regidx_end; i++) {
|
||||
if (i == regidx_en)
|
||||
continue;
|
||||
mpp_write_relaxed(mpp, i * sizeof(u32), task->reg[i]);
|
||||
}
|
||||
/* Last, flush the registers */
|
||||
wmb();
|
||||
mpp_write(mpp, VEPU2_REG_ENC_EN,
|
||||
task->reg[regidx_en] | VEPU2_ENC_START);
|
||||
|
||||
mpp_debug_leave();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vepu_irq(struct mpp_dev *mpp)
|
||||
{
|
||||
mpp->irq_status = mpp_read(mpp, VEPU2_REG_INT);
|
||||
if (!(mpp->irq_status & VEPU2_INT_RAW))
|
||||
return IRQ_NONE;
|
||||
|
||||
mpp_write(mpp, VEPU2_REG_INT, 0);
|
||||
|
||||
return IRQ_WAKE_THREAD;
|
||||
}
|
||||
|
||||
static int vepu_isr(struct mpp_dev *mpp)
|
||||
{
|
||||
u32 err_mask;
|
||||
struct vepu_task *task = NULL;
|
||||
struct mpp_task *mpp_task = NULL;
|
||||
struct vepu_dev *enc = to_vepu_dev(mpp);
|
||||
|
||||
/* FIXME use a spin lock here */
|
||||
task = enc->current_task;
|
||||
if (!task) {
|
||||
dev_err(enc->mpp.dev, "no current task\n");
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
mpp_task = &task->mpp_task;
|
||||
mpp_time_diff(mpp_task);
|
||||
enc->current_task = NULL;
|
||||
task->irq_status = mpp->irq_status;
|
||||
mpp_debug(DEBUG_IRQ_STATUS, "irq_status: %08x\n",
|
||||
task->irq_status);
|
||||
|
||||
err_mask = VEPU2_INT_TIMEOUT
|
||||
| VEPU2_INT_BUF_FULL
|
||||
| VEPU2_INT_BUS_ERROR;
|
||||
|
||||
if (err_mask & task->irq_status)
|
||||
atomic_inc(&mpp->reset_request);
|
||||
|
||||
mpp_task_finish(mpp_task->session, mpp_task);
|
||||
|
||||
mpp_debug_leave();
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int vepu_finish(struct mpp_dev *mpp,
|
||||
struct mpp_task *mpp_task)
|
||||
{
|
||||
u32 i;
|
||||
u32 regidx_start;
|
||||
u32 regidx_end;
|
||||
struct vepu_task *task = to_vepu_task(mpp_task);
|
||||
|
||||
mpp_debug_enter();
|
||||
/* read register after running */
|
||||
regidx_start = task->hw_info->regidx_start;
|
||||
regidx_end = task->hw_info->regidx_end;
|
||||
for (i = regidx_start; i <= regidx_end; i++)
|
||||
task->reg[i] = mpp_read_relaxed(mpp, i * sizeof(u32));
|
||||
task->reg[VEPU2_REG_INT_INDEX] = task->irq_status;
|
||||
|
||||
mpp_debug_leave();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vepu_result(struct mpp_dev *mpp,
|
||||
struct mpp_task *mpp_task,
|
||||
u32 __user *dst, u32 size)
|
||||
{
|
||||
struct vepu_task *task = to_vepu_task(mpp_task);
|
||||
|
||||
/* FIXME may overflow the kernel */
|
||||
if (copy_to_user(dst, task->reg, size)) {
|
||||
mpp_err("copy_to_user failed\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vepu_free_task(struct mpp_session *session,
|
||||
struct mpp_task *mpp_task)
|
||||
{
|
||||
struct vepu_task *task = to_vepu_task(mpp_task);
|
||||
|
||||
mpp_task_finalize(session, mpp_task);
|
||||
kfree(task);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vepu_debugfs_remove(struct mpp_dev *mpp)
|
||||
{
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
struct vepu_dev *enc = to_vepu_dev(mpp);
|
||||
|
||||
debugfs_remove_recursive(enc->debugfs);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vepu_debugfs_init(struct mpp_dev *mpp)
|
||||
{
|
||||
struct vepu_dev *enc = to_vepu_dev(mpp);
|
||||
struct device_node *np = mpp->dev->of_node;
|
||||
|
||||
enc->aclk_debug = 0;
|
||||
enc->session_max_buffers_debug = 0;
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
enc->debugfs = debugfs_create_dir(np->name, mpp->srv->debugfs);
|
||||
if (IS_ERR_OR_NULL(enc->debugfs)) {
|
||||
mpp_err("failed on open debugfs\n");
|
||||
enc->debugfs = NULL;
|
||||
return -EIO;
|
||||
}
|
||||
debugfs_create_u32("aclk", 0644,
|
||||
enc->debugfs, &enc->aclk_debug);
|
||||
debugfs_create_u32("session_buffers", 0644,
|
||||
enc->debugfs, &enc->session_max_buffers_debug);
|
||||
#endif
|
||||
if (enc->session_max_buffers_debug)
|
||||
mpp->session_max_buffers = enc->session_max_buffers_debug;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vepu_init(struct mpp_dev *mpp)
|
||||
{
|
||||
struct vepu_dev *enc = to_vepu_dev(mpp);
|
||||
|
||||
mpp->grf_info = &mpp->srv->grf_infos[MPP_DRIVER_VEPU2];
|
||||
|
||||
enc->aclk = devm_clk_get(mpp->dev, "aclk_vcodec");
|
||||
if (IS_ERR(enc->aclk)) {
|
||||
mpp_err("failed on clk_get aclk_vcodec\n");
|
||||
enc->aclk = NULL;
|
||||
}
|
||||
enc->hclk = devm_clk_get(mpp->dev, "hclk_vcodec");
|
||||
if (IS_ERR(enc->hclk)) {
|
||||
mpp_err("failed on clk_get hclk_vcodec\n");
|
||||
enc->hclk = NULL;
|
||||
}
|
||||
|
||||
enc->rst_a = devm_reset_control_get_shared(mpp->dev, "video_a");
|
||||
if (IS_ERR_OR_NULL(enc->rst_a)) {
|
||||
mpp_err("No aclk reset resource define\n");
|
||||
enc->rst_a = NULL;
|
||||
}
|
||||
enc->rst_h = devm_reset_control_get_shared(mpp->dev, "video_h");
|
||||
if (IS_ERR_OR_NULL(enc->rst_h)) {
|
||||
mpp_err("No hclk reset resource define\n");
|
||||
enc->rst_h = NULL;
|
||||
}
|
||||
mpp_safe_unreset(enc->rst_a);
|
||||
mpp_safe_unreset(enc->rst_h);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vepu_power_on(struct mpp_dev *mpp)
|
||||
{
|
||||
struct vepu_dev *enc = to_vepu_dev(mpp);
|
||||
|
||||
if (enc->aclk)
|
||||
clk_prepare_enable(enc->aclk);
|
||||
if (enc->hclk)
|
||||
clk_prepare_enable(enc->hclk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vepu_power_off(struct mpp_dev *mpp)
|
||||
{
|
||||
struct vepu_dev *enc = to_vepu_dev(mpp);
|
||||
|
||||
if (enc->aclk)
|
||||
clk_disable_unprepare(enc->aclk);
|
||||
if (enc->hclk)
|
||||
clk_disable_unprepare(enc->hclk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vepu_get_freq(struct mpp_dev *mpp,
|
||||
struct mpp_task *mpp_task)
|
||||
{
|
||||
struct vepu_task *task = to_vepu_task(mpp_task);
|
||||
|
||||
task->aclk_freq = 300;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vepu_set_freq(struct mpp_dev *mpp,
|
||||
struct mpp_task *mpp_task)
|
||||
{
|
||||
struct vepu_dev *enc = to_vepu_dev(mpp);
|
||||
struct vepu_task *task = to_vepu_task(mpp_task);
|
||||
|
||||
/* check whether use debug freq */
|
||||
task->aclk_freq = enc->aclk_debug ?
|
||||
enc->aclk_debug : task->aclk_freq;
|
||||
|
||||
clk_set_rate(enc->aclk, task->aclk_freq * MHZ);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vepu_reduce_freq(struct mpp_dev *mpp)
|
||||
{
|
||||
struct vepu_dev *enc = to_vepu_dev(mpp);
|
||||
|
||||
if (enc->aclk)
|
||||
clk_set_rate(enc->aclk, 50 * MHZ);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vepu_reset(struct mpp_dev *mpp)
|
||||
{
|
||||
struct vepu_dev *enc = to_vepu_dev(mpp);
|
||||
|
||||
if (enc->rst_a && enc->rst_h) {
|
||||
/* Don't skip this or iommu won't work after reset */
|
||||
rockchip_pmu_idle_request(mpp->dev, true);
|
||||
mpp_safe_reset(enc->rst_a);
|
||||
mpp_safe_reset(enc->rst_h);
|
||||
udelay(5);
|
||||
mpp_safe_unreset(enc->rst_a);
|
||||
mpp_safe_unreset(enc->rst_h);
|
||||
rockchip_pmu_idle_request(mpp->dev, false);
|
||||
}
|
||||
mpp_write(mpp, VEPU2_REG_INT, VEPU2_INT_CLEAR);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct mpp_hw_ops vepu_v2_hw_ops = {
|
||||
.init = vepu_init,
|
||||
.power_on = vepu_power_on,
|
||||
.power_off = vepu_power_off,
|
||||
.get_freq = vepu_get_freq,
|
||||
.set_freq = vepu_set_freq,
|
||||
.reduce_freq = vepu_reduce_freq,
|
||||
.reset = vepu_reset,
|
||||
};
|
||||
|
||||
static struct mpp_dev_ops vepu_v2_dev_ops = {
|
||||
.alloc_task = vepu_alloc_task,
|
||||
.prepare = vepu_prepare,
|
||||
.run = vepu_run,
|
||||
.irq = vepu_irq,
|
||||
.isr = vepu_isr,
|
||||
.finish = vepu_finish,
|
||||
.result = vepu_result,
|
||||
.free_task = vepu_free_task,
|
||||
};
|
||||
|
||||
static const struct mpp_dev_var vepu_v2_data = {
|
||||
.device_type = MPP_DEVICE_ENC,
|
||||
.hw_info = &vepu_v2_hw_info,
|
||||
.trans_info = trans_rk_vepu2,
|
||||
.hw_ops = &vepu_v2_hw_ops,
|
||||
.dev_ops = &vepu_v2_dev_ops,
|
||||
};
|
||||
|
||||
static const struct of_device_id mpp_vepu2_dt_match[] = {
|
||||
{
|
||||
.compatible = "rockchip,vpu-encoder-v2",
|
||||
.data = &vepu_v2_data,
|
||||
},
|
||||
{},
|
||||
};
|
||||
|
||||
static int vepu_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct vepu_dev *enc = NULL;
|
||||
struct mpp_dev *mpp = NULL;
|
||||
const struct of_device_id *match = NULL;
|
||||
int ret = 0;
|
||||
|
||||
dev_info(dev, "probe device\n");
|
||||
enc = devm_kzalloc(dev, sizeof(struct vepu_dev), GFP_KERNEL);
|
||||
if (!enc)
|
||||
return -ENOMEM;
|
||||
|
||||
mpp = &enc->mpp;
|
||||
platform_set_drvdata(pdev, enc);
|
||||
|
||||
if (pdev->dev.of_node) {
|
||||
match = of_match_node(mpp_vepu2_dt_match, pdev->dev.of_node);
|
||||
if (match)
|
||||
mpp->var = (struct mpp_dev_var *)match->data;
|
||||
}
|
||||
|
||||
ret = mpp_dev_probe(mpp, pdev);
|
||||
if (ret) {
|
||||
dev_err(dev, "probe sub driver failed\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = devm_request_threaded_irq(dev, mpp->irq,
|
||||
mpp_dev_irq,
|
||||
mpp_dev_isr_sched,
|
||||
IRQF_SHARED,
|
||||
dev_name(dev), mpp);
|
||||
if (ret) {
|
||||
dev_err(dev, "register interrupter runtime failed\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
mpp->session_max_buffers = VEPU2_SESSION_MAX_BUFFERS;
|
||||
vepu_debugfs_init(mpp);
|
||||
dev_info(dev, "probing finish\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vepu_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct vepu_dev *enc = platform_get_drvdata(pdev);
|
||||
|
||||
dev_info(dev, "remove device\n");
|
||||
mpp_dev_remove(&enc->mpp);
|
||||
vepu_debugfs_remove(&enc->mpp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void vepu_shutdown(struct platform_device *pdev)
|
||||
{
|
||||
int ret;
|
||||
int val;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct vepu_dev *enc = platform_get_drvdata(pdev);
|
||||
struct mpp_dev *mpp = &enc->mpp;
|
||||
|
||||
dev_info(dev, "shutdown device\n");
|
||||
|
||||
atomic_inc(&mpp->srv->shutdown_request);
|
||||
ret = readx_poll_timeout(atomic_read,
|
||||
&mpp->total_running,
|
||||
val, val == 0, 20000, 200000);
|
||||
if (ret == -ETIMEDOUT)
|
||||
dev_err(dev, "wait total running time out\n");
|
||||
}
|
||||
|
||||
struct platform_driver rockchip_vepu2_driver = {
|
||||
.probe = vepu_probe,
|
||||
.remove = vepu_remove,
|
||||
.shutdown = vepu_shutdown,
|
||||
.driver = {
|
||||
.name = VEPU2_DRIVER_NAME,
|
||||
.of_match_table = of_match_ptr(mpp_vepu2_dt_match),
|
||||
},
|
||||
};
|
||||
EXPORT_SYMBOL(rockchip_vepu2_driver);
|
||||
@@ -1,18 +0,0 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
menu "ROCKCHIP_MPP"
|
||||
depends on ARCH_ROCKCHIP
|
||||
|
||||
config ROCKCHIP_MPP_SERVICE
|
||||
tristate "ROCKCHIP MPP SERVICE driver"
|
||||
default n
|
||||
help
|
||||
rockchip mpp service.
|
||||
|
||||
config ROCKCHIP_MPP_DEVICE
|
||||
tristate "ROCKCHIP MPP DEVICE driver"
|
||||
depends on ROCKCHIP_MPP_SERVICE
|
||||
default n
|
||||
help
|
||||
rockchip mpp module.
|
||||
|
||||
endmenu
|
||||
@@ -1,5 +0,0 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
obj-$(CONFIG_ROCKCHIP_MPP_SERVICE) += mpp_service.o
|
||||
obj-$(CONFIG_ROCKCHIP_MPP_DEVICE) += mpp_dev_rkvenc.o mpp_dev_vepu.o \
|
||||
mpp_dev_h265e.o mpp_dev_common.o vpu_iommu_drm.o vpu_iommu_ion.o \
|
||||
vpu_iommu_ops.o
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,298 +0,0 @@
|
||||
/**
|
||||
* Copyright (C) 2016 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 __ROCKCHIP_MPP_DEV_COMMON_H
|
||||
#define __ROCKCHIP_MPP_DEV_COMMON_H
|
||||
|
||||
#include <linux/cdev.h>
|
||||
#include <linux/dma-buf.h>
|
||||
#include <linux/rockchip_ion.h>
|
||||
#include <linux/rockchip-iovmm.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/wakelock.h>
|
||||
|
||||
#include <video/rk_vpu_service.h>
|
||||
|
||||
extern int mpp_dev_debug;
|
||||
|
||||
#define MPP_IOC_CUSTOM_BASE 0x1000
|
||||
|
||||
/*
|
||||
* 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_DUMP_ERR_REG 0x00000400
|
||||
|
||||
#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 DEBUG_RESET 0x00020000
|
||||
|
||||
#define PRINT_FUNCTION 0x80000000
|
||||
#define PRINT_LINE 0x40000000
|
||||
|
||||
#define DEBUG
|
||||
#ifdef DEBUG
|
||||
#define mpp_debug_func(type, fmt, args...) \
|
||||
do { \
|
||||
if (unlikely(mpp_dev_debug & type)) { \
|
||||
pr_info("%s:%d: " fmt, \
|
||||
__func__, __LINE__, ##args); \
|
||||
} \
|
||||
} while (0)
|
||||
#define mpp_debug(type, fmt, args...) \
|
||||
do { \
|
||||
if (unlikely(mpp_dev_debug & type)) { \
|
||||
pr_info(fmt, ##args); \
|
||||
} \
|
||||
} while (0)
|
||||
#else
|
||||
#define mpp_debug_func(level, fmt, args...)
|
||||
#define mpp_debug(level, fmt, args...)
|
||||
#endif
|
||||
|
||||
#define mpp_debug_enter() mpp_debug_func(DEBUG_FUNCTION, "enter\n")
|
||||
#define mpp_debug_leave() mpp_debug_func(DEBUG_FUNCTION, "leave\n")
|
||||
|
||||
#define mpp_err(fmt, args...) \
|
||||
pr_err("%s:%d: " fmt, __func__, __LINE__, ##args)
|
||||
|
||||
struct mpp_trans_info {
|
||||
const int count;
|
||||
const char * const table;
|
||||
};
|
||||
|
||||
enum RKVENC_MODE {
|
||||
RKVENC_MODE_NONE,
|
||||
RKVENC_MODE_ONEFRAME,
|
||||
RKVENC_MODE_LINKTABLE_FIX,
|
||||
RKVENC_MODE_LINKTABLE_UPDATE,
|
||||
RKVENC_MODE_NUM
|
||||
};
|
||||
|
||||
struct rockchip_mpp_dev;
|
||||
struct mpp_service;
|
||||
struct mpp_ctx;
|
||||
|
||||
struct mpp_mem_region {
|
||||
struct list_head srv_lnk;
|
||||
struct list_head reg_lnk;
|
||||
struct list_head session_lnk;
|
||||
/* virtual address for iommu */
|
||||
unsigned long iova;
|
||||
unsigned long len;
|
||||
u32 reg_idx;
|
||||
int hdl;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct for process register set
|
||||
*
|
||||
* @author ChenHengming (2011-5-4)
|
||||
*/
|
||||
struct mpp_ctx {
|
||||
/* context belong to */
|
||||
struct rockchip_mpp_dev *mpp;
|
||||
struct mpp_session *session;
|
||||
|
||||
/* link to service session */
|
||||
struct list_head session_link;
|
||||
/* link to service list */
|
||||
struct list_head status_link;
|
||||
|
||||
struct list_head mem_region_list;
|
||||
|
||||
/* record context running start time */
|
||||
struct timeval start;
|
||||
};
|
||||
|
||||
enum vpu_ctx_state {
|
||||
MMU_ACTIVATED = BIT(0),
|
||||
};
|
||||
|
||||
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];
|
||||
};
|
||||
|
||||
struct rockchip_mpp_dev_variant {
|
||||
u32 data_len;
|
||||
u32 reg_len;
|
||||
struct mpp_trans_info *trans_info;
|
||||
char *mmu_dev_dts_name;
|
||||
|
||||
int (*hw_probe)(struct rockchip_mpp_dev *mpp);
|
||||
void (*hw_remove)(struct rockchip_mpp_dev *mpp);
|
||||
void (*power_on)(struct rockchip_mpp_dev *mpp);
|
||||
void (*power_off)(struct rockchip_mpp_dev *mpp);
|
||||
int (*reset)(struct rockchip_mpp_dev *mpp);
|
||||
};
|
||||
|
||||
struct rockchip_mpp_dev {
|
||||
struct mpp_dev_ops *ops;
|
||||
|
||||
struct cdev cdev;
|
||||
dev_t dev_t;
|
||||
struct device *child_dev;
|
||||
|
||||
int irq;
|
||||
struct mpp_service *srv;
|
||||
|
||||
void __iomem *reg_base;
|
||||
struct list_head lnk_service;
|
||||
|
||||
struct device *dev;
|
||||
|
||||
unsigned long state;
|
||||
struct vpu_iommu_info *iommu_info;
|
||||
|
||||
const struct rockchip_mpp_dev_variant *variant;
|
||||
|
||||
struct device *mmu_dev;
|
||||
u32 iommu_enable;
|
||||
|
||||
struct wake_lock wake_lock;
|
||||
struct delayed_work power_off_work;
|
||||
/* record previous power-on time */
|
||||
ktime_t last;
|
||||
atomic_t power_on_cnt;
|
||||
atomic_t power_off_cnt;
|
||||
atomic_t total_running;
|
||||
atomic_t enabled;
|
||||
atomic_t reset_request;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct mpp_dev_ops - context specific operations for mpp_device
|
||||
*
|
||||
* @init Prepare for registers file for specific hardware.
|
||||
* @prepare Check HW status for determining run next task or not.
|
||||
* @run Start a single {en,de}coding run. Set registers to hardware.
|
||||
* @done Read back processing results and additional data from hardware.
|
||||
* @result Read status to userspace.
|
||||
* @deinit Release the resource allocate during init.
|
||||
* @ioctl ioctl for special HW besides the common ioctl.
|
||||
* @irq interrupt service for specific hardware.
|
||||
* @open a specific instance open operation for hardware.
|
||||
* @release a specific instance release operation for hardware.
|
||||
*/
|
||||
struct mpp_dev_ops {
|
||||
/* size: in bytes, data sent from userspace, length in bytes */
|
||||
struct mpp_ctx *(*init)(struct rockchip_mpp_dev *mpp,
|
||||
struct mpp_session *session,
|
||||
void __user *src, u32 size);
|
||||
int (*prepare)(struct rockchip_mpp_dev *mpp);
|
||||
int (*run)(struct rockchip_mpp_dev *mpp);
|
||||
int (*done)(struct rockchip_mpp_dev *mpp);
|
||||
int (*irq)(struct rockchip_mpp_dev *mpp);
|
||||
int (*result)(struct rockchip_mpp_dev *mpp, struct mpp_ctx *ctx,
|
||||
u32 __user *dst);
|
||||
void (*deinit)(struct rockchip_mpp_dev *mpp);
|
||||
long (*ioctl)(struct mpp_session *isession,
|
||||
unsigned int cmd, unsigned long arg);
|
||||
struct mpp_session *(*open)(struct rockchip_mpp_dev *mpp);
|
||||
void (*release)(struct mpp_session *session);
|
||||
void (*free)(struct mpp_session *session);
|
||||
};
|
||||
|
||||
void mpp_dump_reg(void __iomem *regs, int count);
|
||||
void mpp_dump_reg_mem(u32 *regs, int count);
|
||||
int mpp_reg_address_translate(struct rockchip_mpp_dev *data,
|
||||
u32 *reg,
|
||||
struct mpp_ctx *ctx,
|
||||
int idx);
|
||||
void mpp_translate_extra_info(struct mpp_ctx *ctx,
|
||||
struct extra_info_for_iommu *ext_inf,
|
||||
u32 *reg);
|
||||
|
||||
int mpp_dev_common_ctx_init(struct rockchip_mpp_dev *mpp, struct mpp_ctx *cfg);
|
||||
void mpp_dev_common_ctx_deinit(struct rockchip_mpp_dev *mpp,
|
||||
struct mpp_ctx *ctx);
|
||||
void mpp_dev_power_on(struct rockchip_mpp_dev *mpp);
|
||||
void mpp_dev_power_off(struct rockchip_mpp_dev *mpp);
|
||||
bool mpp_dev_is_power_on(struct rockchip_mpp_dev *mpp);
|
||||
|
||||
static inline void mpp_write_relaxed(struct rockchip_mpp_dev *mpp,
|
||||
u32 val, u32 reg)
|
||||
{
|
||||
mpp_debug(DEBUG_SET_REG, "MARK: set reg[%03d]: %08x\n", reg / 4, val);
|
||||
writel_relaxed(val, mpp->reg_base + reg);
|
||||
}
|
||||
|
||||
static inline void mpp_write(struct rockchip_mpp_dev *mpp,
|
||||
u32 val, u32 reg)
|
||||
{
|
||||
mpp_debug(DEBUG_SET_REG, "MARK: set reg[%03d]: %08x\n", reg / 4, val);
|
||||
writel(val, mpp->reg_base + reg);
|
||||
}
|
||||
|
||||
static inline u32 mpp_read(struct rockchip_mpp_dev *mpp, u32 reg)
|
||||
{
|
||||
u32 val = readl(mpp->reg_base + reg);
|
||||
|
||||
mpp_debug(DEBUG_GET_REG, "MARK: get reg[%03d] 0x%x: %08x\n", reg / 4,
|
||||
reg, val);
|
||||
return val;
|
||||
}
|
||||
|
||||
static inline void mpp_time_record(struct mpp_ctx *ctx)
|
||||
{
|
||||
if (unlikely(mpp_dev_debug & DEBUG_TIMING) && ctx)
|
||||
do_gettimeofday(&ctx->start);
|
||||
}
|
||||
|
||||
static inline void mpp_time_diff(struct mpp_ctx *ctx)
|
||||
{
|
||||
struct timeval end;
|
||||
|
||||
do_gettimeofday(&end);
|
||||
mpp_debug(DEBUG_TIMING, "consume: %ld us\n",
|
||||
(end.tv_sec - ctx->start.tv_sec) * 1000000 +
|
||||
(end.tv_usec - ctx->start.tv_usec));
|
||||
}
|
||||
|
||||
extern const struct rockchip_mpp_dev_variant rkvenc_variant;
|
||||
extern const struct rockchip_mpp_dev_variant vepu_variant;
|
||||
extern const struct rockchip_mpp_dev_variant h265e_variant;
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,155 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Fuzhou Rockchip Electronics Co., Ltd
|
||||
* author: hehua,hh@rock-chips.com
|
||||
* lixinhuang, buluess.li@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 __ROCKCHIP_MPP_DEV_H265E_H
|
||||
#define __ROCKCHIP_MPP_DEV_H265E_H
|
||||
|
||||
#include "mpp_dev_h265e_define.h"
|
||||
#include "mpp_service.h"
|
||||
#include <linux/ioctl.h>
|
||||
#include <linux/wakelock.h>
|
||||
|
||||
#define MPP_DEV_H265E_SET_COLOR_PALETTE \
|
||||
_IOW(VPU_IOC_MAGIC, MPP_IOC_CUSTOM_BASE + 1, u32)
|
||||
|
||||
#define MPP_DEV_H265E_SET_PARAMETER \
|
||||
_IOW(VPU_IOC_MAGIC, \
|
||||
MPP_IOC_CUSTOM_BASE + 6, struct mpp_h265e_cfg)
|
||||
|
||||
#define MPP_DEV_H265E_GET_HEAD_PARAMETER \
|
||||
_IOW(VPU_IOC_MAGIC, \
|
||||
MPP_IOC_CUSTOM_BASE + 7, struct hal_h265e_header)
|
||||
|
||||
#define H265E_INSTANCE_NUM 4
|
||||
|
||||
enum H265E_MODE {
|
||||
H265E_MODE_NONE,
|
||||
H265E_MODE_ONEFRAME,
|
||||
H265E_MODE_LINKTABLE_FIX,
|
||||
H265E_MODE_LINKTABLE_UPDATE,
|
||||
H265E_MODE_NUM
|
||||
};
|
||||
|
||||
struct regmap;
|
||||
|
||||
struct h265e_result {
|
||||
u32 bs_size;
|
||||
u32 enc_pic_cnt;
|
||||
u32 pic_type;
|
||||
u32 num_of_slice;
|
||||
u32 pick_skipped;
|
||||
u32 num_intra;
|
||||
u32 num_merge;
|
||||
u32 num_skip_block;
|
||||
u32 avg_ctu_qp;
|
||||
int recon_frame_index;
|
||||
u32 gop_idx;
|
||||
u32 poc;
|
||||
u32 src_idx;
|
||||
u32 fail_reason;
|
||||
};
|
||||
|
||||
struct mpp_h265e_buffer {
|
||||
unsigned long dma_addr;
|
||||
u32 size;
|
||||
int hdl;
|
||||
};
|
||||
|
||||
struct mpp_h265e_frame_buffer {
|
||||
struct mpp_h265e_buffer buffer;
|
||||
u32 y;
|
||||
u32 cb;
|
||||
u32 cr;
|
||||
};
|
||||
|
||||
struct h265e_ctx {
|
||||
struct mpp_ctx ictx;
|
||||
enum H265E_MODE mode;
|
||||
struct mpp_h265e_buffer bs;
|
||||
char __iomem *bs_data;/*for debug read data*/
|
||||
struct mpp_h265e_buffer src;
|
||||
struct mpp_h265e_buffer roi;
|
||||
struct mpp_h265e_buffer ctu;
|
||||
|
||||
struct mpp_h265e_encode_info cfg;
|
||||
|
||||
/* store status read from hw, oneframe mode used only */
|
||||
struct h265e_result result;
|
||||
};
|
||||
|
||||
enum H265E_INSTANCE_STATUS {
|
||||
H265E_INSTANCE_STATUS_ERROR,
|
||||
H265E_INSTANCE_STATUS_OPENED,
|
||||
H265E_INSTANCE_STATUS_SET_PARAMETER,
|
||||
H265E_INSTANCE_STATUS_ENCODE,
|
||||
H265E_INSTANCE_STATUS_CLOSE
|
||||
};
|
||||
|
||||
struct rockchip_h265e_instance {
|
||||
int index;
|
||||
atomic_t is_used;
|
||||
struct mpp_h265e_buffer work;
|
||||
struct mpp_h265e_buffer temp;
|
||||
struct mpp_h265e_buffer mv;
|
||||
struct mpp_h265e_buffer fbc_luma;
|
||||
struct mpp_h265e_buffer fbc_chroma;
|
||||
struct mpp_h265e_buffer sub_sample;
|
||||
/*
|
||||
* for recon frames
|
||||
*/
|
||||
struct mpp_h265e_frame_buffer frame_buffer[16];
|
||||
|
||||
int min_frame_buffer_count;
|
||||
int min_src_frame_count;
|
||||
int src_idx;
|
||||
int status;
|
||||
struct mpp_h265e_cfg cfg;
|
||||
struct mpp_session *session;
|
||||
};
|
||||
|
||||
struct rockchip_h265e_dev {
|
||||
struct rockchip_mpp_dev dev;
|
||||
struct rockchip_h265e_instance instance[H265E_INSTANCE_NUM];
|
||||
struct mpp_h265e_buffer temp;
|
||||
|
||||
u32 lkt_index;
|
||||
u32 irq_status;
|
||||
atomic_t is_init;
|
||||
atomic_t load_firmware;
|
||||
|
||||
struct delayed_work work_list;
|
||||
struct mutex lock;
|
||||
|
||||
char __iomem *firmware_cpu_addr;
|
||||
struct mpp_h265e_buffer firmware;
|
||||
|
||||
struct clk *aclk;
|
||||
struct clk *aclk_axi2sram;
|
||||
struct clk *pclk;
|
||||
struct clk *core;
|
||||
struct clk *dsp;
|
||||
void __iomem *grf_base;
|
||||
u32 mode_bit;
|
||||
u32 mode_ctrl;
|
||||
struct regmap *grf;
|
||||
};
|
||||
|
||||
struct h265e_session {
|
||||
struct mpp_session isession;
|
||||
int instance_index;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,759 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Fuzhou Rockchip Electronics Co., Ltd
|
||||
* author: hehua,hh@rock-chips.com
|
||||
* lixinhuang, buluess.li@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 __MPP_DEV_H265E_DEFINE_H__
|
||||
#define __MPP_DEV_H265E_DEFINE_H__
|
||||
|
||||
#include <linux/bitops.h>
|
||||
|
||||
#define H265E_MVCOL_BUF_SIZE(w, h) \
|
||||
((((w) + 63) / 64) * (((h) + 63) / 64) * 128)
|
||||
#define H265E_FBC_LUMA_TABLE_SIZE(w, h) \
|
||||
((((h) + 15) / 16) * (((w) + 255) / 256) * 128)
|
||||
#define H265E_FBC_CHROMA_TABLE_SIZE(w, h) \
|
||||
((((h) + 15) / 16) * (((w) / 2 + 255) / 256) * 128)
|
||||
#define H265E_SUBSAMPLED_ONE_SIZE(w, h) \
|
||||
(((((w) / 4) + 15) & ~15) * ((((h) / 4) + 7) & ~7))
|
||||
|
||||
#define H265E_PIC_TYPE_I 0
|
||||
#define H265E_PIC_TYPE_P 1
|
||||
|
||||
enum H265E_VPU_COMMAND {
|
||||
H265E_CMD_INIT_VPU = 0x0001,
|
||||
H265E_CMD_SET_PARAM = 0x0002,
|
||||
H265E_CMD_FINI_SEQ = 0x0004,
|
||||
H265E_CMD_ENC_PIC = 0x0008,
|
||||
H265E_CMD_SET_FRAMEBUF = 0x0010,
|
||||
H265E_CMD_FLUSH_DECODER = 0x0020,
|
||||
H265E_CMD_GET_FW_VERSION = 0x0100,
|
||||
H265E_CMD_QUERY_DECODER = 0x0200,
|
||||
H265E_CMD_SLEEP_VPU = 0x0400,
|
||||
H265E_CMD_WAKEUP_VPU = 0x0800,
|
||||
H265E_CMD_CREATE_INSTANCE = 0x4000,
|
||||
H265E_CMD_RESET_VPU = 0x10000,
|
||||
H265E_CMD_MAX_VPU_COMD = 0x10000,
|
||||
};
|
||||
|
||||
enum H265E_PIC_CODE_OPTION {
|
||||
CODEOPT_ENC_HEADER_IMPLICIT = BIT(0),
|
||||
CODEOPT_ENC_VCL = BIT(1),
|
||||
CODEOPT_ENC_VPS = BIT(2),
|
||||
CODEOPT_ENC_SPS = BIT(3),
|
||||
CODEOPT_ENC_PPS = BIT(4),
|
||||
CODEOPT_ENC_AUD = BIT(5),
|
||||
CODEOPT_ENC_EOS = BIT(6),
|
||||
CODEOPT_ENC_EOB = BIT(7),
|
||||
CODEOPT_ENC_RESERVED = BIT(8),
|
||||
CODEOPT_ENC_VUI = BIT(9),
|
||||
};
|
||||
|
||||
enum H265E_TILED_MAP_TYPE {
|
||||
LINEAR_FRAME_MAP = 0,
|
||||
TILED_FRAME_V_MAP = 1,
|
||||
TILED_FRAME_H_MAP = 2,
|
||||
TILED_FIELD_V_MAP = 3,
|
||||
TILED_MIXED_V_MAP = 4,
|
||||
TILED_FRAME_MB_RASTER_MAP = 5,
|
||||
TILED_FIELD_MB_RASTER_MAP = 6,
|
||||
TILED_FRAME_NO_BANK_MAP = 7,
|
||||
TILED_FIELD_NO_BANK_MAP = 8,
|
||||
LINEAR_FIELD_MAP = 9,
|
||||
CODA_TILED_MAP_TYPE_MAX = 10,
|
||||
COMPRESSED_FRAME_MAP = 10,
|
||||
TILED_SUB_CTU_MAP = 11,
|
||||
ARM_COMPRESSED_FRAME_MAP = 12,
|
||||
};
|
||||
|
||||
#define H265E_MAX_NUM_TEMPORAL_LAYER 7
|
||||
#define H265E_MAX_GOP_NUM 8
|
||||
#define H265E_MIN_PIC_WIDTH 256
|
||||
#define H265E_MIN_PIC_HEIGHT 128
|
||||
#define H265E_MAX_PIC_WIDTH 1920
|
||||
#define H265E_MAX_PIC_HEIGHT 1080
|
||||
#define MAX_ROI_NUMBER 64
|
||||
|
||||
enum H265E_GOP_PRESET_IDX {
|
||||
PRESET_IDX_CUSTOM_GOP = 0,
|
||||
PRESET_IDX_ALL_I = 1,
|
||||
PRESET_IDX_IPP = 2,
|
||||
PRESET_IDX_IPPPP = 6,
|
||||
};
|
||||
|
||||
enum H265E_SET_PARAM_OPTION {
|
||||
H265E_OPT_COMMON = 0,
|
||||
H265E_OPT_CUSTOM_GOP = 1,
|
||||
H265E_OPT_CUSTOM_HEADER = 2,
|
||||
H265E_OPT_VUI = 3,
|
||||
H265E_OPT_ALL_PARAM = 0xffffffff
|
||||
};
|
||||
|
||||
enum H265E_PARAM_CHANEGED {
|
||||
H265E_PARAM_CHANEGED_COMMON = 1,
|
||||
H265E_PARAM_CHANEGED_CUSTOM_GOP = 2,
|
||||
H265E_PARAM_CHANEGED_VUI = 4,
|
||||
H265E_PARAM_CHANEGED_REGISTER_BUFFER = 8,
|
||||
};
|
||||
|
||||
enum H265E_COMON_CFG_MASK {
|
||||
/* COMMON parameters*/
|
||||
H265E_CFG_SEQ_SRC_SIZE_CHANGE = BIT(0),
|
||||
H265E_CFG_SEQ_PARAM_CHANGE = BIT(1),
|
||||
H265E_CFG_GOP_PARAM_CHANGE = BIT(2),
|
||||
H265E_CFG_INTRA_PARAM_CHANGE = BIT(3),
|
||||
H265E_CFG_CONF_WIN_TOP_BOT_CHANGE = BIT(4),
|
||||
H265E_CFG_CONF_WIN_LEFT_RIGHT_CHANGE = BIT(5),
|
||||
H265E_CFG_FRAME_RATE_CHANGE = BIT(6),
|
||||
H265E_CFG_INDEPENDENT_SLICE_CHANGE = BIT(7),
|
||||
H265E_CFG_DEPENDENT_SLICE_CHANGE = BIT(8),
|
||||
H265E_CFG_INTRA_REFRESH_CHANGE = BIT(9),
|
||||
H265E_CFG_PARAM_CHANGE = BIT(10),
|
||||
H265E_CFG_CHANGE_RESERVED = BIT(11),
|
||||
H265E_CFG_RC_PARAM_CHANGE = BIT(12),
|
||||
H265E_CFG_RC_MIN_MAX_QP_CHANGE = BIT(13),
|
||||
H265E_CFG_RC_TARGET_RATE_LAYER_0_3_CHANGE = BIT(14),
|
||||
H265E_CFG_RC_TARGET_RATE_LAYER_4_7_CHANGE = BIT(15),
|
||||
|
||||
H265E_CFG_SET_NUM_UNITS_IN_TICK = BIT(18),
|
||||
H265E_CFG_SET_TIME_SCALE = BIT(19),
|
||||
H265E_CFG_SET_NUM_TICKS_POC_DIFF_ONE = BIT(20),
|
||||
H265E_CFG_RC_TRANS_RATE_CHANGE = BIT(21),
|
||||
H265E_CFG_RC_TARGET_RATE_CHANGE = BIT(22),
|
||||
H265E_CFG_ROT_PARAM_CHANGE = BIT(23),
|
||||
H265E_CFG_NR_PARAM_CHANGE = BIT(24),
|
||||
H265E_CFG_NR_WEIGHT_CHANGE = BIT(25),
|
||||
|
||||
H265E_CFG_SET_VCORE_LIMIT = BIT(27),
|
||||
H265E_CFG_CHANGE_SET_PARAM_ALL = (0xFFFFFFFF),
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief This is a data structure for setting
|
||||
* CTU level options (ROI, CTU mode, CTU QP) in HEVC encoder.
|
||||
*/
|
||||
struct h265e_ctu {
|
||||
u32 roi_enable;
|
||||
u32 roi_delta_qp;
|
||||
u32 map_endian;
|
||||
|
||||
/*
|
||||
* Stride of CTU-level ROI/mode/QP map
|
||||
* Set this with (Width + CTB_SIZE - 1) / CTB_SIZE
|
||||
*/
|
||||
u32 map_stride;
|
||||
/*
|
||||
* It enables CTU QP map that allows
|
||||
* CTUs to be encoded with the given QPs.
|
||||
* NOTE: rcEnable should be turned off for this,
|
||||
* encoding with the given CTU QPs.
|
||||
*/
|
||||
u32 ctu_qp_enable;
|
||||
};
|
||||
|
||||
struct h265e_sei {
|
||||
u8 prefix_sei_nal_enable;
|
||||
|
||||
/*
|
||||
* A flag whether to encode PREFIX_SEI_DATA
|
||||
* with a picture of this command or with a source
|
||||
* picture of the buffer at the moment
|
||||
* 0 : encode PREFIX_SEI_DATA when a source picture is encoded.
|
||||
* 1 : encode PREFIX_SEI_DATA at this command.
|
||||
*/
|
||||
u8 prefix_sei_data_order;
|
||||
|
||||
/*
|
||||
* enables to encode the suffix SEI NAL which is given by host.
|
||||
*/
|
||||
u8 suffix_sei_nal_enable;
|
||||
|
||||
/*
|
||||
* A flag whether to encode SUFFIX_SEI_DATA
|
||||
* with a picture of this command or with a source
|
||||
* picture of the buffer at the moment
|
||||
* 0 : encode SUFFIX_SEI_DATA when a source picture is encoded.
|
||||
* 1 : encode SUFFIX_SEI_DATA at this command.
|
||||
*/
|
||||
u8 suffix_sei_data_enc_order;
|
||||
|
||||
/*
|
||||
* The total byte size of the prefix SEI
|
||||
*/
|
||||
u32 prefix_sei_data_size;
|
||||
|
||||
/*
|
||||
* The start address of the total prefix SEI NALs to be encoded
|
||||
*/
|
||||
u32 prefix_sei_nal_addr;
|
||||
|
||||
/*
|
||||
* The total byte size of the suffix SEI
|
||||
*/
|
||||
u32 suffix_sei_data_size;
|
||||
|
||||
/*
|
||||
* The start address of the total suffix SEI NALs to be encoded
|
||||
*/
|
||||
u32 suffix_sei_nal_addr;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief This is a data structure for setting
|
||||
* VUI parameters in HEVC encoder.
|
||||
*/
|
||||
struct h265e_vui {
|
||||
/*
|
||||
* VUI parameter flag
|
||||
*/
|
||||
u32 flags;
|
||||
/**< aspect_ratio_idc */
|
||||
u32 aspect_ratio_idc;
|
||||
/**< sar_width, sar_height
|
||||
* (only valid when aspect_ratio_idc is equal to 255)
|
||||
*/
|
||||
u32 sar_size;
|
||||
/**< overscan_appropriate_flag */
|
||||
u32 over_scan_appropriate;
|
||||
/**< VUI parameter flag */
|
||||
u32 signal;
|
||||
/**< chroma_sample_loc_type_top_field,
|
||||
*chroma_sample_loc_type_bottom_field
|
||||
*/
|
||||
u32 chroma_sample_loc;
|
||||
/**< def_disp_win_left_offset, def_disp_win_right_offset */
|
||||
u32 disp_win_left_right;
|
||||
/**< def_disp_win_top_offset, def_disp_win_bottom_offset */
|
||||
u32 disp_win_top_bottom;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief This is a data structure for
|
||||
*custom GOP parameters of the given picture.
|
||||
*/
|
||||
struct h265e_custom_gop_pic {
|
||||
/**< A picture type of #th picture in the custom GOP */
|
||||
u32 type;
|
||||
/**< A POC offset of #th picture in the custom GOP */
|
||||
u32 offset;
|
||||
/**< A quantization parameter of #th picture in the custom GOP */
|
||||
u32 qp;
|
||||
/**< POC offset of reference L0 of #th picture in the custom GOP */
|
||||
u32 ref_poc_l0;
|
||||
/**< POC offset of reference L1 of #th picture in the custom GOP */
|
||||
u32 ref_poc_l1;
|
||||
/**< A temporal ID of #th picture in the custom GOP */
|
||||
u32 temporal_id;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief This is a data structure for custom GOP parameters.
|
||||
*/
|
||||
struct h265e_custom_gop {
|
||||
/**< Size of the custom GOP (0~8) */
|
||||
u32 custom_gop_size;
|
||||
/**< It derives a lamda weight internally
|
||||
* instead of using lamda weight specified.
|
||||
*/
|
||||
u32 use_derive_lambda_weight;
|
||||
/**< picture parameters of #th picture in the custom gop */
|
||||
struct h265e_custom_gop_pic pic[H265E_MAX_GOP_NUM];
|
||||
/**< a lamda weight of #th picture in the custom gop */
|
||||
u32 gop_pic_lambda[H265E_MAX_GOP_NUM];
|
||||
};
|
||||
|
||||
struct enc_code_opt {
|
||||
/**< whether host encode a header implicitly or not.
|
||||
* if this value is 1, below encode options will be ignored
|
||||
*/
|
||||
int implicit_header_encode;
|
||||
int encode_vcl;/**< a flag to encode vcl nal unit explicitly*/
|
||||
int encode_vps;/**< a flag to encode vps nal unit explicitly*/
|
||||
int encode_sps;/**< a flag to encode sps nal unit explicitly*/
|
||||
int encode_pps;/**< a flag to encode pps nal unit explicitly*/
|
||||
int encode_aud;/**< a flag to encode aud nal unit explicitly*/
|
||||
int encode_eos;/**< a flag to encode eos nal unit explicitly*/
|
||||
int encode_eob;/**< a flag to encode eob nal unit explicitly*/
|
||||
int encode_vui;/**< a flag to encode vui nal unit explicitly*/
|
||||
};
|
||||
|
||||
enum H265E_SRC_FORMAT {
|
||||
H265E_SRC_YUV_420 = 0,
|
||||
H265E_SRC_YUV_420_YU12 = 0, /* 3Plane 1.Y, 2.U, 3.V*/
|
||||
H265E_SRC_YUV_420_YV12, /* 3Plane 1.Y, 2.V, 3.U*/
|
||||
H265E_SRC_YUV_420_NV12, /* 2 Plane 1.Y 2. UV*/
|
||||
H265E_SRC_YUV_420_NV21, /* 2 Plane 1.Y 2. VU*/
|
||||
H265E_SRC_YUV_420_MAX,
|
||||
};
|
||||
|
||||
struct hal_h265e_header {
|
||||
u32 buf;
|
||||
u32 size;
|
||||
};
|
||||
|
||||
struct mpp_h265e_cfg {
|
||||
/*
|
||||
* A profile indicator
|
||||
* 1 : main
|
||||
* 2 : main10
|
||||
*/
|
||||
u8 profile;
|
||||
|
||||
/*
|
||||
* only support to level 4.1
|
||||
*/
|
||||
u8 level; /**< A level indicator (level * 10) */
|
||||
|
||||
/*
|
||||
* A tier indicator
|
||||
* 0 : main
|
||||
* 1 : high
|
||||
*/
|
||||
u8 tier;
|
||||
|
||||
/*
|
||||
* A chroma format indecator, only support YUV420
|
||||
*/
|
||||
u8 chroma_idc;
|
||||
|
||||
/*
|
||||
* the source's width and height
|
||||
*/
|
||||
u16 width;
|
||||
u16 height;
|
||||
u16 width_stride;
|
||||
u16 height_stride;
|
||||
|
||||
/*
|
||||
* bitdepth,only support 8 bits(only support 8 bits)
|
||||
*/
|
||||
u8 bit_depth;
|
||||
|
||||
/*
|
||||
* source yuv's format. The value is defined
|
||||
* in H265E_FrameBufferFormat(only support YUV420)
|
||||
* the value could be YU12,YV12,NV12,NV21
|
||||
*/
|
||||
u8 src_format;
|
||||
|
||||
u8 src_endian;
|
||||
u8 bs_endian;
|
||||
u8 fb_endian;
|
||||
u8 frame_rate;
|
||||
u8 frame_skip;
|
||||
u32 bit_rate;
|
||||
|
||||
u32 map_type;
|
||||
u32 line_buf_int_en;
|
||||
u32 slice_int_enable;
|
||||
u32 ring_buffer_enable;
|
||||
|
||||
struct enc_code_opt code_option;
|
||||
/*
|
||||
* A chroma format indecator, only support YUV420
|
||||
*/
|
||||
int lossless_enable;/**< It enables lossless coding */
|
||||
/**< It enables constrained intra prediction */
|
||||
int const_intra_pred_flag;
|
||||
/**< The value of chroma(cb) qp offset (only for WAVE420L) */
|
||||
int chroma_cb_qp_offset;
|
||||
/**< The value of chroma(cr) qp offset (only for WAVE420L) */
|
||||
int chroma_cr_qp_offset;
|
||||
/**
|
||||
* A GOP structure option
|
||||
* 0: Custom GOP
|
||||
* 1 : I-I-I-I,..I (all intra, gop_size=1)
|
||||
* 2 : I-P-P-P,... P (consecutive P, gop_size=1)
|
||||
* 6 : I-P-P-P-P (consecutive P, gop_size=4)
|
||||
*/
|
||||
u32 gop_idx;
|
||||
|
||||
/**
|
||||
* An intra picture refresh mode
|
||||
* 0 : Non-IRAP
|
||||
* 1 : CRA
|
||||
* 2 : IDR
|
||||
*/
|
||||
u32 decoding_refresh_type;
|
||||
|
||||
/*
|
||||
* A quantization parameter of intra picture
|
||||
*/
|
||||
u32 intra_qp;
|
||||
|
||||
/*
|
||||
* A period of intra picture in GOP size
|
||||
*/
|
||||
u32 intra_period;
|
||||
|
||||
/** A conformance window size of TOP,BUTTOM,LEFT,RIGHT */
|
||||
u16 conf_win_top;
|
||||
u16 conf_win_bot;
|
||||
u16 conf_win_left;
|
||||
u16 conf_win_right;
|
||||
|
||||
/*
|
||||
* A slice mode for independent slice
|
||||
* 0 : no multi-slice
|
||||
* 1 : Slice in CTU number
|
||||
* 2 : Slice in number of byte
|
||||
*/
|
||||
u32 independ_slice_mode;
|
||||
|
||||
/*
|
||||
* The number of CTU or bytes for a slice
|
||||
* when independ_slice_mode is set with 1 or 2.
|
||||
*/
|
||||
u32 independ_slice_mode_arg;
|
||||
|
||||
/**
|
||||
*A slice mode for dependent slice
|
||||
* 0 : no multi-slice
|
||||
* 1 : Slice in CTU number
|
||||
* 2 : Slice in number of byte
|
||||
*/
|
||||
u32 depend_slice_mode;
|
||||
|
||||
/*
|
||||
* The number of CTU or bytes for a slice
|
||||
* when depend_slice_mode is set with 1 or 2.
|
||||
*/
|
||||
u32 depend_slice_mode_arg;
|
||||
|
||||
/*
|
||||
* An intra refresh mode
|
||||
* 0 : No intra refresh
|
||||
* 1 : Row
|
||||
* 2 : Column
|
||||
* 3 : Step size in CTU
|
||||
*/
|
||||
u32 intra_refresh_mode;
|
||||
|
||||
/*
|
||||
* The number of CTU (only valid when intraRefreshMode is 3.)
|
||||
*/
|
||||
u32 intra_refresh_arg;
|
||||
|
||||
/*
|
||||
* It uses one of the recommended encoder parameter presets.
|
||||
* 0 : Custom
|
||||
* 1 : Recommend enc params
|
||||
* (slow encoding speed, highest picture quality)
|
||||
* 2 : Boost mode (normal encoding speed, normal picture quality)
|
||||
* 3 : Fast mode (high encoding speed, low picture quality)
|
||||
*/
|
||||
u8 use_recommend_param;
|
||||
u8 scaling_list_enable; /**< It enables a scaling list */
|
||||
|
||||
/*
|
||||
* It specifies CU size.
|
||||
* 3'b001: 8x8
|
||||
* 3'b010: 16x16
|
||||
* 3'b100 : 32x32
|
||||
*/
|
||||
u8 cu_size_mode;
|
||||
u8 tmvp_enable;
|
||||
u8 wpp_enable; /**< It enables wave-front parallel processing. */
|
||||
u8 max_num_merge; /**< Maximum number of merge candidates (0~2) */
|
||||
u8 dynamic_merge_8x8_enable;
|
||||
u8 dynamic_merge_16x16_enable;
|
||||
u8 dynamic_merge_32x32_enable;
|
||||
u8 disable_deblk; /**< It disables in-loop deblocking filtering. */
|
||||
/**< it enables filtering across slice
|
||||
* boundaries for in-loop deblocking.
|
||||
*/
|
||||
u8 lf_cross_slice_boundary_enable;
|
||||
/**< BetaOffsetDiv2 for deblocking filter */
|
||||
u8 beta_offset_div2;
|
||||
/**< TcOffsetDiv3 for deblocking filter */
|
||||
u8 tc_offset_div2;
|
||||
/**< It enables transform skip for an intra CU. */
|
||||
u8 skip_intra_trans;
|
||||
/**< It enables SAO (sample adaptive offset). */
|
||||
u8 sao_enable;
|
||||
/**< It enables to make intra CUs in an inter slice. */
|
||||
u8 intra_in_inter_slice_enable;
|
||||
/**< It enables intra NxN PUs. */
|
||||
u8 intra_nxn_enable;
|
||||
|
||||
/*
|
||||
* specifies intra QP offset relative
|
||||
* to inter QP (Only available when rc_enable is enabled)
|
||||
*/
|
||||
s8 intra_qp_offset;
|
||||
|
||||
/*
|
||||
* It specifies encoder initial delay,
|
||||
* Only available when RateControl is enabled
|
||||
* (encoder initial delay = initial_delay * init_buf_levelx8 / 8)
|
||||
*/
|
||||
int init_buf_levelx8;
|
||||
|
||||
/*
|
||||
* specifies picture bits allocation mode.
|
||||
* Only available when RateControl is enabled
|
||||
* and GOP size is larger than 1
|
||||
* 0: More referenced pictures have
|
||||
* better quality than less referenced pictures
|
||||
* 1: All pictures in a GOP have similar image quality
|
||||
* 2: Each picture bits in a GOP is allocated according to FixedRatioN
|
||||
*/
|
||||
u8 bit_alloc_mode;
|
||||
|
||||
/*
|
||||
* A fixed bit ratio (1 ~ 255) for each picture of GOP's bitallocation
|
||||
* N = 0 ~ (MAX_GOP_SIZE - 1)
|
||||
* MAX_GOP_SIZE = 8
|
||||
* For instance when MAX_GOP_SIZE is 3, FixedBitRaio0
|
||||
* to FixedBitRaio2 can be set as 2, 1, and 1 respectively for
|
||||
* the fixed bit ratio 2:1:1. This is only valid when BitAllocMode is 2.
|
||||
*/
|
||||
u8 fixed_bit_ratio[H265E_MAX_GOP_NUM];
|
||||
|
||||
/*
|
||||
* enable rate control
|
||||
*/
|
||||
u32 rc_enable;
|
||||
|
||||
/*
|
||||
* enable CU level rate control
|
||||
*/
|
||||
u8 cu_level_rc_enable;
|
||||
|
||||
/*
|
||||
* enable CU QP adjustment for subjective quality enhancement
|
||||
*/
|
||||
u8 hvs_qp_enable;
|
||||
|
||||
/*
|
||||
* enable QP scaling factor for CU QP adjustment when hvs_qp_enable = 1
|
||||
*/
|
||||
u8 hvs_qp_scale_enable;
|
||||
|
||||
/*
|
||||
* A QP scaling factor for CU QP adjustment when hvs_qp_enable = 1
|
||||
*/
|
||||
s8 hvs_qp_scale;
|
||||
|
||||
/*
|
||||
* A minimum QP for rate control
|
||||
*/
|
||||
u8 min_qp;
|
||||
|
||||
/*
|
||||
* A maximum QP for rate control
|
||||
*/
|
||||
u8 max_qp;
|
||||
|
||||
/*
|
||||
* A maximum delta QP for rate control
|
||||
*/
|
||||
u8 max_delta_qp;
|
||||
|
||||
/*
|
||||
* A peak transmission bitrate in bps
|
||||
*/
|
||||
u32 trans_rate;
|
||||
/*< It specifies the number of time units of
|
||||
* a clock operating at the frequency time_scale Hz
|
||||
*/
|
||||
u32 num_units_in_tick;
|
||||
/**< It specifies the number of time units that pass in one second */
|
||||
u32 time_scale;
|
||||
/**< It specifies the number of clock ticks corresponding to a
|
||||
* difference of picture order count values equal to 1
|
||||
*/
|
||||
u32 num_ticks_poc_diff_one;
|
||||
|
||||
/*< The value of initial QP by host.
|
||||
* This value is meaningless if INITIAL_RC_QP == 63
|
||||
*/
|
||||
int initial_rc_qp;
|
||||
|
||||
/*
|
||||
* enables noise reduction algorithm to Y/Cb/Cr component.
|
||||
*/
|
||||
u8 nr_y_enable;
|
||||
u8 nr_cb_enable;
|
||||
u8 nr_cr_enable;
|
||||
|
||||
/*
|
||||
* enables noise estimation for reduction. When this is disabled,
|
||||
* noise estimation is carried out ouside VPU.
|
||||
*/
|
||||
u8 nr_noise_est_enable;
|
||||
/*
|
||||
* It specifies Y/Cb/Cr noise standard deviation
|
||||
* if no use of noise estimation (nr_noise_est_enable=0)
|
||||
*/
|
||||
u8 nr_noise_sigma_y;
|
||||
u8 nr_noise_sigma_cb;
|
||||
u8 nr_noise_sigma_cr;
|
||||
/* ENC_NR_WEIGHT*/
|
||||
/*< A weight to Y noise level for intra picture (0 ~ 31).
|
||||
* nr_intra_weight_y/4 is multiplied to the noise
|
||||
* level that has been estimated.
|
||||
* This weight is put for intra frame to be filtered more strongly or
|
||||
* more weakly than just with the estimated noise level.
|
||||
*/
|
||||
u8 nr_intra_weight_y;
|
||||
/**< A weight to Cb noise level for intra picture (0 ~ 31). */
|
||||
u8 nr_intra_weight_cb;
|
||||
/**< A weight to Cr noise level for intra picture (0 ~ 31). */
|
||||
u8 nr_intra_weight_cr;
|
||||
/*< A weight to Y noise level for inter picture (0 ~ 31).
|
||||
* nr_inter_weight_y/4 is multiplied to the noise
|
||||
* level that has been estimated.
|
||||
* This weight is put for inter frame to be filtered more strongly or
|
||||
* more weakly than just with the estimated noise level.
|
||||
*/
|
||||
u8 nr_inter_weight_y;
|
||||
/**< A weight to Cb noise level for inter picture (0 ~ 31). */
|
||||
u8 nr_inter_weight_cb;
|
||||
/**< A weight to Cr noise level for inter picture (0 ~ 31). */
|
||||
u8 nr_inter_weight_cr;
|
||||
/*
|
||||
* a minimum QP for intra picture (0 ~ 51).
|
||||
* It is only available when rc_enable is 1.
|
||||
*/
|
||||
u8 intra_min_qp;
|
||||
|
||||
/*
|
||||
* a maximum QP for intra picture (0 ~ 51).
|
||||
* It is only available when rc_enable is 1.
|
||||
*/
|
||||
u8 intra_max_qp;
|
||||
|
||||
u32 initial_delay;
|
||||
|
||||
u8 hrd_rbsp_in_vps;
|
||||
u8 hrd_rbsp_in_vui;
|
||||
u32 vui_rbsp;
|
||||
|
||||
u32 hrd_rbsp_data_size; /**< The size of the HRD rbsp data */
|
||||
u32 hrd_rbsp_data_addr; /**< The address of the HRD rbsp data */
|
||||
|
||||
u32 vui_rbsp_data_size; /**< The size of the VUI rbsp data */
|
||||
u32 vui_rbsp_data_addr; /**< The address of the VUI rbsp data */
|
||||
|
||||
u8 use_long_term;
|
||||
u8 use_cur_as_longterm_pic;
|
||||
u8 use_longterm_ref;
|
||||
|
||||
struct h265e_custom_gop gop;
|
||||
struct h265e_ctu ctu;
|
||||
struct h265e_vui vui;
|
||||
struct h265e_sei sei;
|
||||
|
||||
/*
|
||||
* define which type of parameters are changed,
|
||||
* only support common parameter chanegd now,
|
||||
* see H265eCommonCfgMask
|
||||
*/
|
||||
u32 cfg_option;
|
||||
|
||||
/*
|
||||
* define which parameters are changed,see H265E_SET_PARAM_OPTION
|
||||
*/
|
||||
u32 cfg_mask;
|
||||
};
|
||||
|
||||
struct mpp_h265e_encode_info {
|
||||
/*
|
||||
* the address of source(yuv) data for encoding
|
||||
*/
|
||||
u32 src_fd;
|
||||
|
||||
/*
|
||||
* the size of source(yuv) data for encoding
|
||||
*/
|
||||
u32 src_size;
|
||||
|
||||
/*
|
||||
* the address of bitstream buffer
|
||||
*/
|
||||
u32 bs_fd;
|
||||
|
||||
/*
|
||||
* the size of bitstream buffer
|
||||
*/
|
||||
u32 bs_size;
|
||||
u32 roi_fd;
|
||||
u32 ctu_qp_fd;
|
||||
u32 stream_end;
|
||||
|
||||
/*
|
||||
* skip current frame
|
||||
*/
|
||||
u32 skip_pic;
|
||||
|
||||
/*
|
||||
* A flag to use a force picture quantization parameter
|
||||
*/
|
||||
u32 force_qp_enable;
|
||||
|
||||
/*
|
||||
*Force picture quantization parameter for I picture
|
||||
*/
|
||||
u32 force_qp_i;
|
||||
|
||||
/*
|
||||
* Force picture quantization parameter for P picture
|
||||
*/
|
||||
u32 force_qp_p;
|
||||
|
||||
/*
|
||||
* A flag to use a force picture type
|
||||
*/
|
||||
u32 force_frame_type_enable;
|
||||
|
||||
/*
|
||||
* A force picture type (I, P, B, IDR, CRA)
|
||||
*/
|
||||
u32 force_frame_type;
|
||||
};
|
||||
|
||||
enum INTERRUPT_BIT {
|
||||
INT_BIT_INIT = 0,
|
||||
INT_BIT_SEQ_INIT = 1,
|
||||
INT_BIT_SEQ_END = 2,
|
||||
INT_BIT_PIC_RUN = 3,
|
||||
INT_BIT_FRAMEBUF_SET = 4,
|
||||
INT_BIT_ENC_HEADER = 5,
|
||||
INT_BIT_DEC_PARA_SET = 7,
|
||||
INT_BIT_DEC_BUF_FLUSH = 8,
|
||||
INT_BIT_USERDATA = 9,
|
||||
INT_BIT_DEC_FIELD = 10,
|
||||
INT_BIT_DEC_MB_ROWS = 13,
|
||||
INT_BIT_BIT_BUF_EMPTY = 14,
|
||||
INT_BIT_BIT_BUF_FULL = 15
|
||||
};
|
||||
|
||||
enum H265E_INTERRUPT_BIT {
|
||||
INT_H265E_INIT = 0,
|
||||
INT_H265E_DEC_PIC_HDR = 1,
|
||||
INT_H265E_FINI_SEQ = 2,
|
||||
INT_H265E_ENC_PIC = 3,
|
||||
INT_H265E_SET_FRAMEBUF = 4,
|
||||
INT_H265E_FLUSH_DECODER = 5,
|
||||
INT_H265E_GET_FW_VERSION = 8,
|
||||
INT_H265E_QUERY_DECODER = 9,
|
||||
INT_H265E_SLEEP_VPU = 10,
|
||||
INT_H265E_WAKEUP_VPU = 11,
|
||||
INT_H265E_CHANGE_INST = 12,
|
||||
INT_H265E_CREATE_INSTANCE = 14,
|
||||
INT_H265E_BIT_BUF_EMPTY = 15,
|
||||
INT_H265E_BIT_BUF_FULL = 15, /* Encoder */
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,235 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Fuzhou Rockchip Electronics Co., Ltd
|
||||
* author: hehua,hh@rock-chips.com
|
||||
* lixinhuang, buluess.li@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 _MPP_DEV_H265E_REGISTER_H_
|
||||
#define _MPP_DEV_H265E_REGISTER_H_
|
||||
|
||||
#define H265E_PO_CONF 0x0000
|
||||
#define H265E_VCPU_CUR_PC 0x0004
|
||||
#define H265E_VPU_PDBG_CTRL 0x0010
|
||||
#define H265E_VPU_PDBG_IDX_REG 0x0014
|
||||
#define H265E_VPU_PDBG_WDATA_REG 0x0018
|
||||
#define H265E_VPU_PDBG_RDATA_REG 0x001C
|
||||
#define H265E_VPU_FIO_CTRL_ADDR 0x0020
|
||||
#define H265E_VPU_FIO_DATA 0x0024
|
||||
#define H265E_VPU_VINT_REASON_USR 0x0030
|
||||
#define H265E_VPU_VINT_REASON_CLR 0x0034
|
||||
#define H265E_VPU_HOST_INT_REQ 0x0038
|
||||
#define H265E_VPU_VINT_CLEAR 0x003C
|
||||
#define H265E_VPU_HINT_CLEAR 0x0040
|
||||
#define H265E_VPU_VPU_INT_STS 0x0044
|
||||
#define H265E_VPU_VINT_ENABLE 0x0048
|
||||
|
||||
#define H265E_CMD_REG_END 0x0200
|
||||
|
||||
#define H265E_VPU_VINT_REASON 0x004c
|
||||
#define H265E_VPU_RESET_REQ 0x0050
|
||||
#define H265E_VPU_RESET_STATUS 0x0070
|
||||
#define H265E_VPU_REMAP_CTRL 0x0060
|
||||
#define H265E_VPU_REMAP_VADDR 0x0064
|
||||
#define H265E_VPU_REMAP_PADDR 0x0068
|
||||
#define H265E_VPU_REMAP_CORE_START 0x006C
|
||||
#define H265E_VPU_BUSY_STATUS 0x0070
|
||||
#define H265E_COMMAND 0x0100
|
||||
#define H265E_CORE_INDEX 0x0104
|
||||
#define H265E_INST_INDEX 0x0108
|
||||
#define H265E_ENC_SET_PARAM_OPTION 0x010C
|
||||
#define H265E_RET_FW_VERSION 0x0118
|
||||
#define H265E_ADDR_CODE_BASE 0x0118
|
||||
#define H265E_CODE_SIZE 0x011C
|
||||
#define H265E_CODE_PARAM 0x0120
|
||||
#define H265E_HW_OPTION 0x0124
|
||||
|
||||
#define H265E_RET_SUCCESS 0x0110
|
||||
#define H265E_VPU_HOST_INT_REQ 0x0038
|
||||
#define H265E_SFB_OPTION 0x010C
|
||||
#define H265E_RET_FAIL_REASON 0x0114
|
||||
#define H265E_BS_START_ADDR 0x0120
|
||||
#define H265E_COMMON_PIC_INFO 0x0120
|
||||
#define H265E_BS_SIZE 0x0124
|
||||
#define H265E_PIC_SIZE 0x0124
|
||||
#define H265E_BS_PARAM 0x0128
|
||||
#define H265E_SET_FB_NUM 0x0128
|
||||
#define H265E_BS_OPTION 0x012C
|
||||
#define H265E_BS_RD_PTR 0x0130
|
||||
#define H265E_BS_WR_PTR 0x0134
|
||||
#define H265E_ADDR_WORK_BASE 0x0138
|
||||
#define H265E_WORK_SIZE 0x013c
|
||||
#define H265E_WORK_PARAM 0x0140
|
||||
#define H265E_ADDR_TEMP_BASE 0x0144
|
||||
#define H265E_TEMP_SIZE 0x0148
|
||||
#define H265E_TEMP_PARAM 0x014C
|
||||
#define H265E_FBC_STRIDE 0x0154
|
||||
#define H265E_ENC_SET_PARAM_ENABLE 0x015C
|
||||
#define H265E_ENC_SEQ_SRC_SIZE 0x0160
|
||||
#define H265E_ADDR_LUMA_BASE0 0x0160
|
||||
#define H265E_ADDR_CB_BASE0 0x0164
|
||||
#define H265E_ADDR_CR_BASE0 0x0168
|
||||
#define H265E_ADDR_FBC_Y_OFFSET0 0x0168
|
||||
#define H265E_ADDR_FBC_C_OFFSET0 0x016C
|
||||
|
||||
#define H265E_ENC_SEQ_PARAM 0x016C
|
||||
#define H265E_ENC_SEQ_GOP_PARAM 0x0170
|
||||
#define H265E_ENC_SRC_PIC_IDX 0x0170
|
||||
#define H265E_ENC_SEQ_INTRA_PARAM 0x0174
|
||||
#define H265E_ENC_SEQ_CONF_WIN_TOP_BOT 0x0178
|
||||
#define H265E_ENC_SEQ_CONF_WIN_LEFT_RIGHT 0x017C
|
||||
#define H265E_ENC_SEQ_FRAME_RATE 0x0180
|
||||
#define H265E_ENC_SEQ_INDEPENDENT_SLICE 0x0184
|
||||
#define H265E_ENC_SEQ_DEPENDENT_SLICE 0x0188
|
||||
|
||||
#define H265E_ENC_SEQ_INTRA_REFRESH 0x018C
|
||||
#define H265E_ENC_PARAM 0x0190
|
||||
#define H265E_ENC_RC_INTRA_MIN_MAX_QP 0x0194
|
||||
#define H265E_ENC_RC_PARAM 0x0198
|
||||
#define H265E_ENC_RC_MIN_MAX_QP 0x019C
|
||||
#define H265E_ENC_RC_BIT_RATIO_LAYER_0_3 0x01A0
|
||||
#define H265E_ENC_RC_BIT_RATIO_LAYER_4_7 0x01A4
|
||||
#define H265E_ENC_NR_PARAM 0x01A8
|
||||
#define H265E_ENC_NR_WEIGHT 0x01AC
|
||||
#define H265E_ENC_NUM_UNITS_IN_TICK 0x01B0
|
||||
#define H265E_ENC_TIME_SCALE 0x01B4
|
||||
#define H265E_ENC_NUM_TICKS_POC_DIFF_ONE 0x01B8
|
||||
#define H265E_ENC_RC_TRANS_RATE 0x01BC
|
||||
#define H265E_ENC_RC_TARGET_RATE 0x01C0
|
||||
#define H265E_ENC_ROT_PARAM 0x01C4
|
||||
#define H265E_ENC_ROT_RESERVED 0x01C8
|
||||
#define H265E_RET_ENC_MIN_FB_NUM 0x01CC
|
||||
#define H265E_RET_ENC_NAL_INFO_TO_BE_ENCODED 0x01D0
|
||||
#define H265E_RET_ENC_MIN_SRC_BUF_NUM 0x01D8
|
||||
|
||||
#define H265E_ADDR_MV_COL0 0x01E0
|
||||
#define H265E_ADDR_MV_COL1 0x01E4
|
||||
#define H265E_ADDR_MV_COL2 0x01E8
|
||||
#define H265E_ADDR_MV_COL3 0x01EC
|
||||
#define H265E_ADDR_MV_COL4 0x01F0
|
||||
#define H265E_ADDR_MV_COL5 0x01F4
|
||||
#define H265E_ADDR_MV_COL6 0x01F8
|
||||
#define H265E_ADDR_MV_COL7 0x01FC
|
||||
|
||||
#define H265E_ADDR_SEC_AXI_BASE 0x150
|
||||
#define H265E_SEC_AXI_SIZE 0x154
|
||||
#define H265E_USE_SEC_AXI 0x158
|
||||
|
||||
/************************************************************************/
|
||||
/*H265 ENCODER - SET_PARAM + CUSTOM_GOP */
|
||||
/************************************************************************/
|
||||
#define H265E_ENC_SET_CUSTOM_GOP_ENABLE 0x015C
|
||||
#define H265E_ENC_CUSTOM_GOP_PARAM 0x0160
|
||||
#define H265E_ENC_CUSTOM_GOP_PIC_PARAM_0 0x0164
|
||||
#define H265E_ENC_CUSTOM_GOP_PIC_PARAM_1 0x0168
|
||||
#define H265E_ENC_CUSTOM_GOP_PIC_PARAM_2 0x016C
|
||||
#define H265E_ENC_CUSTOM_GOP_PIC_PARAM_3 0x0170
|
||||
#define H265E_ENC_CUSTOM_GOP_PIC_PARAM_4 0x0174
|
||||
#define H265E_ENC_CUSTOM_GOP_PIC_PARAM_5 0x0178
|
||||
#define H265E_ENC_CUSTOM_GOP_PIC_PARAM_6 0x017C
|
||||
#define H265E_ENC_CUSTOM_GOP_PIC_PARAM_7 0x0180
|
||||
#define H265E_ENC_CUSTOM_GOP_RESERVED 0x0184
|
||||
#define H265E_ENC_CUSTOM_GOP_PIC_LAMBDA_0 0x0188
|
||||
#define H265E_ENC_CUSTOM_GOP_PIC_LAMBDA_1 0x018C
|
||||
#define H265E_ENC_CUSTOM_GOP_PIC_LAMBDA_2 0x0190
|
||||
#define H265E_ENC_CUSTOM_GOP_PIC_LAMBDA_3 0x0194
|
||||
#define H265E_ENC_CUSTOM_GOP_PIC_LAMBDA_4 0x0198
|
||||
#define H265E_ENC_CUSTOM_GOP_PIC_LAMBDA_5 0x019C
|
||||
#define H265E_ENC_CUSTOM_GOP_PIC_LAMBDA_6 0x01A0
|
||||
#define H265E_ENC_CUSTOM_GOP_PIC_LAMBDA_7 0x01A4
|
||||
|
||||
/************************************************************************/
|
||||
/* H265 ENCODER - SET_PARAM + VUI */
|
||||
/************************************************************************/
|
||||
#define H265E_ENC_VUI_PARAM_FLAGS 0x015C
|
||||
#define H265E_ENC_VUI_ASPECT_RATIO_IDC 0x0160
|
||||
#define H265E_ENC_VUI_SAR_SIZE 0x0164
|
||||
#define H265E_ENC_VUI_OVERSCAN_APPROPRIATE 0x0168
|
||||
#define H265E_ENC_VUI_VIDEO_SIGNAL 0x016C
|
||||
#define H265E_ENC_VUI_CHROMA_SAMPLE_LOC 0x0170
|
||||
#define H265E_ENC_VUI_DISP_WIN_LEFT_RIGHT 0x0174
|
||||
#define H265E_ENC_VUI_DISP_WIN_TOP_BOT 0x0178
|
||||
|
||||
#define H265E_ENC_VUI_HRD_RBSP_PARAM_FLAG 0x017C
|
||||
#define H265E_ENC_VUI_RBSP_ADDR 0x0180
|
||||
#define H265E_ENC_VUI_RBSP_SIZE 0x0184
|
||||
#define H265E_ENC_HRD_RBSP_ADDR 0x0188
|
||||
#define H265E_ENC_HRD_RBSP_SIZE 0x018C
|
||||
|
||||
/************************************************************************/
|
||||
/* H265 ENCODER - SET_FRAMEBUF */
|
||||
/************************************************************************/
|
||||
#define H265E_FBC_STRIDE_Y 0x150
|
||||
#define H265E_FBC_STRIDE_C 0x154
|
||||
/* 1/4 sub-sampled buffer (for S2 ME)
|
||||
* SUB_SAMPLED_ONE_FB_SIZE = ALIGN16(width/4) * ALIGN8(height/4)
|
||||
* total size for sub-sampled buffer = SUB_SAMPLED_ONE_FB_SIZE * SET_FB_NUM
|
||||
*/
|
||||
#define H265E_ADDR_SUB_SAMPLED_FB_BASE 0x0158
|
||||
#define H265E_SUB_SAMPLED_ONE_FB_SIZE 0x015C
|
||||
|
||||
/************************************************************************/
|
||||
/* ENCODER - ENC_PIC */
|
||||
/************************************************************************/
|
||||
#define H265E_CMD_ENC_ADDR_REPORT_BASE 0x015C
|
||||
#define H265E_CMD_ENC_REPORT_SIZE 0x0160
|
||||
#define H265E_CMD_ENC_REPORT_PARAM 0x0164
|
||||
#define H265E_CMD_ENC_CODE_OPTION 0x0168
|
||||
#define H265E_CMD_ENC_PIC_PARAM 0x016C
|
||||
#define H265E_CMD_ENC_SRC_PIC_IDX 0x0170
|
||||
#define H265E_CMD_ENC_SRC_ADDR_Y 0x0174
|
||||
#define H265E_CMD_ENC_SRC_ADDR_U 0x0178
|
||||
#define H265E_CMD_ENC_SRC_ADDR_V 0x017C
|
||||
#define H265E_CMD_ENC_SRC_STRIDE 0x0180
|
||||
#define H265E_CMD_ENC_SRC_FORMAT 0x0184
|
||||
#define H265E_CMD_ENC_PREFIX_SEI_NAL_ADDR 0x0188
|
||||
#define H265E_CMD_ENC_PREFIX_SEI_INFO 0x018C
|
||||
#define H265E_CMD_ENC_SUFFIX_SEI_NAL_ADDR 0x0190
|
||||
#define H265E_CMD_ENC_SUFFIX_SEI_INFO 0x0194
|
||||
#define H265E_CMD_ENC_LONGTERM_PIC 0x0198
|
||||
#define H265E_CMD_ENC_SUB_FRAME_SYNC_CONFIG 0x019C
|
||||
#define H265E_CMD_ENC_CTU_OPT_PARAM 0x01A0
|
||||
#define H265E_CMD_ENC_ROI_ADDR_CTU_MAP 0x01A4
|
||||
#define H265E_CMD_ENC_CTU_QP_MAP_ADDR 0x01AC
|
||||
#define H265E_CMD_ENC_SRC_TIMESTAMP_LOW 0x01B0
|
||||
#define H265E_CMD_ENC_SRC_TIMESTAMP_HIGH 0x01B4
|
||||
|
||||
#define H265E_CMD_ENC_FC_PARAM 0x01E8
|
||||
#define H265E_CMD_ENC_FC_TABLE_ADDR_Y 0x01EC
|
||||
#define H265E_CMD_ENC_FC_TABLE_ADDR_C 0x01F0
|
||||
|
||||
#define H265E_RET_ENC_PIC_IDX 0x01A8
|
||||
#define H265E_RET_ENC_PIC_SLICE_NUM 0x01AC
|
||||
#define H265E_RET_ENC_PIC_SKIP 0x01B0
|
||||
#define H265E_RET_ENC_PIC_NUM_INTRA 0x01B4
|
||||
#define H265E_RET_ENC_PIC_NUM_MERGE 0x01B8
|
||||
#define H265E_RET_ENC_PIC_FLAG 0x01BC
|
||||
#define H265E_RET_ENC_PIC_NUM_SKIP 0x01C0
|
||||
#define H265E_RET_ENC_PIC_AVG_CU_QP 0x01C4
|
||||
#define H265E_RET_ENC_PIC_BYTE 0x01C8
|
||||
#define H265E_RET_ENC_GOP_PIC_IDX 0x01CC
|
||||
#define H265E_RET_ENC_PIC_POC 0x01D0
|
||||
#define H265E_RET_ENC_USED_SRC_IDX 0x01D8
|
||||
#define H265E_RET_ENC_PIC_NUM 0x01DC
|
||||
#define H265E_RET_ENC_PIC_TYPE 0x01E0
|
||||
#define H265E_RET_ENC_VCL_NUT 0x01E4
|
||||
|
||||
#define H265E_PERF_AXI_CTRL 0x0240
|
||||
#define H265E_PERF_LATENCY_CTRL0 0x0264
|
||||
#define H265E_PERF_LATENCY_CTRL1 0x0268
|
||||
#define H265E_PERF_RD_MAX_LATENCY_NUM0 0x026C
|
||||
#define H265E_PERF_RD_LATENCY_SAMP_NUM 0x0270
|
||||
#define H265E_PERF_RD_LATENCY_ACC_SUM 0x0274
|
||||
#define H265E_PERF_RD_AXI_TOTAL_BYTE 0x0278
|
||||
#define H265E_PERF_WR_AXI_TOTAL_BYTE 0x027C
|
||||
#define H265E_PERF_WORKING_CNT 0x0280
|
||||
#endif
|
||||
@@ -1,813 +0,0 @@
|
||||
/**
|
||||
* Copyright (C) 2016 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/reset.h>
|
||||
|
||||
#include "vpu_iommu_ops.h"
|
||||
#include "mpp_service.h"
|
||||
#include "mpp_dev_common.h"
|
||||
#include "mpp_dev_rkvenc.h"
|
||||
|
||||
#define MPP_ALIGN_SIZE 0x1000
|
||||
|
||||
#define LINK_TABLE_START 12
|
||||
#define LINK_TABLE_LEN 128
|
||||
|
||||
#define RKVENC_ENC_START 0x004
|
||||
#define RKVENC_LKT_NUM(x) (((x) & 0xff) << 0)
|
||||
#define RKVENC_CMD(x) (((x) & 0x3) << 8)
|
||||
#define RKVENC_CLK_GATE_EN BIT(16)
|
||||
#define RKVENC_SAFE_CLR 0x008
|
||||
#define RKVENC_LKT_ADDR 0x00c
|
||||
#define RKVENC_INT_EN 0x010
|
||||
#define RKVENC_INT_EN_SAFE_CLEAR BIT(2)
|
||||
#define RKVENC_INT_EN_TIMEOUT BIT(8)
|
||||
#define RKVENC_INT_MSK 0x014
|
||||
#define RKVENC_INT_MSK_OVERFLOW BIT(4)
|
||||
#define RKVENC_INT_MSK_W_FIFO_FULL BIT(5)
|
||||
#define RKVENC_INT_MSK_W_CHN_ERROR BIT(6)
|
||||
#define RKVENC_INT_MSK_R_CHN_ERROR BIT(7)
|
||||
#define RKVENC_INT_MSK_TIMEOUT BIT(8)
|
||||
#define RKVENC_INT_CLR 0x018
|
||||
#define RKVENC_INT_STATUS 0x01c
|
||||
#define RKVENC_ONE_FRAME_FINISH BIT(0)
|
||||
#define RKVENC_LINK_TABLE_FINISH BIT(1)
|
||||
#define RKVENC_SAFE_CLEAR_FINISH BIT(2)
|
||||
#define RKVENC_ONE_SLICE_FINISH BIT(3)
|
||||
#define RKVENC_BIT_STREAM_OVERFLOW BIT(4)
|
||||
#define RKVENC_AXI_WRITE_FIFO_FULL BIT(5)
|
||||
#define RKVENC_AXI_WRITE_CHANNEL_ERROR BIT(6)
|
||||
#define RKVENC_AXI_READ_CHANNEL_ERROR BIT(7)
|
||||
#define RKVENC_TIMEOUT_ERROR BIT(8)
|
||||
#define RKVENC_INT_ERROR_BITS ((RKVENC_BIT_STREAM_OVERFLOW) | \
|
||||
(RKVENC_AXI_WRITE_FIFO_FULL) | \
|
||||
(RKVENC_AXI_WRITE_CHANNEL_ERROR) |\
|
||||
(RKVENC_AXI_READ_CHANNEL_ERROR) | \
|
||||
(RKVENC_TIMEOUT_ERROR))
|
||||
#define RKVENC_ENC_PIC 0x034
|
||||
#define RKVENC_ENC_PIC_NODE_INT_EN BIT(31)
|
||||
#define RKVENC_ENC_WDG 0x038
|
||||
#define RKVENC_PPLN_ENC_LMT(x) (((x) & 0xff) << 0)
|
||||
#define RKVENC_OSD_CFG 0x1c0
|
||||
#define RKVENC_OSD_PLT_TYPE BIT(17)
|
||||
#define RKVENC_OSD_CLK_SEL_BIT BIT(16)
|
||||
#define RKVENC_STATUS(i) (0x210 + (4 * (i)))
|
||||
#define RKVENC_BSL_STATUS 0x210
|
||||
#define RKVENC_BITSTREAM_LENGTH(x) ((x) & 0x7FFFFFF)
|
||||
#define RKVENC_ENC_STATUS 0x220
|
||||
#define RKVENC_ENC_STATUS_ENC(x) (((x) >> 0) & 0x3)
|
||||
#define RKVENC_LKT_STATUS 0x224
|
||||
#define RKVENC_LKT_STATUS_FNUM_ENC(x) (((x) >> 0) & 0xff)
|
||||
#define RKVENC_LKT_STATUS_FNUM_CFG(x) (((x) >> 8) & 0xff)
|
||||
#define RKVENC_LKT_STATUS_FNUM_INT(x) (((x) >> 16) & 0xff)
|
||||
#define RKVENC_OSD_PLT(i) (0x400 + (4 * (i)))
|
||||
|
||||
#define to_rkvenc_ctx(ctx) \
|
||||
container_of(ctx, struct rkvenc_ctx, ictx)
|
||||
#define to_rkvenc_session(session) \
|
||||
container_of(session, struct rkvenc_session, isession)
|
||||
#define to_rkvenc_dev(dev) \
|
||||
container_of(dev, struct rockchip_rkvenc_dev, idev)
|
||||
|
||||
/*
|
||||
* file handle translate information
|
||||
*/
|
||||
static const char trans_tbl_rkvenc[] = {
|
||||
70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86,
|
||||
124, 125, 126, 127, 128, 129, 130, 131
|
||||
};
|
||||
|
||||
static struct mpp_trans_info trans_rkvenc[1] = {
|
||||
[0] = {
|
||||
.count = sizeof(trans_tbl_rkvenc),
|
||||
.table = trans_tbl_rkvenc,
|
||||
},
|
||||
};
|
||||
|
||||
static struct mpp_dev_rkvenc_reg mpp_rkvenc_dummy_reg = {
|
||||
.enc_rsl = 0x00070007, /* 64x64 */
|
||||
.enc_pic = 0x00001714, /* h264, qp 30 */
|
||||
.enc_wdg = 0x00000002,
|
||||
.dtrns_map = 0x00007000,
|
||||
.dtrns_cfg = 0x0000007f,
|
||||
.src_fmt = 0x00000018, /* nv12 */
|
||||
.src_strd = 0x003f003f,
|
||||
.sli_spl = 0x00000004,
|
||||
.me_rnge = 0x00002f7b,
|
||||
.me_cnst = 0x000e0505,
|
||||
.me_ram = 0x000e79ab,
|
||||
.rc_qp = 0x07340000,
|
||||
.rdo_cfg = 0x00000002,
|
||||
.synt_nal = 0x00000017,
|
||||
.synt_sps = 0x0000019c,
|
||||
.synt_pps = 0x01000d03,
|
||||
.synt_sli0 = 0x00000002,
|
||||
};
|
||||
|
||||
static int rockchip_mpp_rkvenc_reset(struct rockchip_mpp_dev *mpp);
|
||||
|
||||
/*
|
||||
* In order to workaround hw bug which make the first frame run failure with
|
||||
* timeout interrupt occur, we make a dummy 64x64 encoding on power on here to
|
||||
* cover the hw bug.
|
||||
*/
|
||||
static void rockchip_mpp_war_init(struct rockchip_mpp_dev *mpp)
|
||||
{
|
||||
struct rockchip_rkvenc_dev *enc = to_rkvenc_dev(mpp);
|
||||
size_t img_width = 64;
|
||||
size_t img_height = 64;
|
||||
size_t img_y_size = img_width * img_height;
|
||||
size_t img_uv_size = img_y_size / 2;
|
||||
size_t img_u_size = img_uv_size / 2;
|
||||
size_t img_size = img_y_size + img_uv_size;
|
||||
|
||||
enc->war_reg = &mpp_rkvenc_dummy_reg;
|
||||
|
||||
/* 4k align required */
|
||||
enc->war_reg->adr_rfpw = enc->war_dma_addr;
|
||||
enc->war_reg->adr_srcy = enc->war_reg->adr_rfpw + img_size;
|
||||
enc->war_reg->adr_srcu = enc->war_reg->adr_srcy + img_y_size;
|
||||
enc->war_reg->adr_srcv = enc->war_reg->adr_srcu + img_u_size;
|
||||
enc->war_reg->adr_bsbb = enc->war_reg->adr_srcv + img_u_size;
|
||||
enc->war_reg->adr_bsbt = enc->war_reg->adr_bsbb + img_size;
|
||||
enc->war_reg->adr_bsbr = enc->war_reg->adr_bsbb;
|
||||
enc->war_reg->adr_bsbw = enc->war_reg->adr_bsbb;
|
||||
|
||||
/* 1k align required */
|
||||
enc->war_reg->adr_dspw = enc->war_dma_addr + 0x4000;
|
||||
enc->war_reg->adr_dspr = enc->war_reg->adr_dspw + 0x400;
|
||||
|
||||
enc->dummy_ctx = kzalloc(sizeof(*enc->dummy_ctx), GFP_KERNEL);
|
||||
if (!enc->dummy_ctx)
|
||||
return;
|
||||
|
||||
enc->dummy_ctx->ictx.mpp = mpp;
|
||||
enc->dummy_ctx->ictx.session = NULL;
|
||||
enc->dummy_ctx->mode = RKVENC_MODE_ONEFRAME;
|
||||
enc->dummy_ctx->cfg.mode = RKVENC_MODE_ONEFRAME;
|
||||
atomic_set(&enc->dummy_ctx_in_used, 0);
|
||||
memcpy(enc->dummy_ctx->cfg.elem[0].reg, enc->war_reg,
|
||||
sizeof(*enc->war_reg));
|
||||
enc->dummy_ctx->cfg.elem[0].reg_num = sizeof(*enc->war_reg) / 4;
|
||||
}
|
||||
|
||||
static void rockchip_mpp_rkvenc_cfg_palette(struct rockchip_mpp_dev *mpp,
|
||||
struct mpp_session *isession)
|
||||
{
|
||||
struct rkvenc_session *session;
|
||||
int i;
|
||||
u32 reg;
|
||||
|
||||
mpp_debug_enter();
|
||||
|
||||
if (!isession) {
|
||||
mpp_debug(DEBUG_TASK_INFO, "fake ctx, do not cfg palette\n");
|
||||
return;
|
||||
}
|
||||
session = to_rkvenc_session(isession);
|
||||
|
||||
if (!session->palette_valid)
|
||||
return;
|
||||
|
||||
reg = mpp_read(mpp, RKVENC_OSD_CFG);
|
||||
mpp_write(mpp, reg & (~RKVENC_OSD_CLK_SEL_BIT), RKVENC_OSD_CFG);
|
||||
|
||||
for (i = 0; i < RKVENC_OSD_PLT_LEN; i++)
|
||||
mpp_write(mpp, session->palette.plalette[i].elem,
|
||||
RKVENC_OSD_PLT(i));
|
||||
|
||||
mpp_write(mpp, reg | RKVENC_OSD_CLK_SEL_BIT, RKVENC_OSD_CFG);
|
||||
|
||||
mpp_debug_leave();
|
||||
}
|
||||
|
||||
static struct mpp_ctx *rockchip_mpp_rkvenc_init(struct rockchip_mpp_dev *mpp,
|
||||
struct mpp_session *session,
|
||||
void __user *src, u32 size)
|
||||
{
|
||||
struct rockchip_rkvenc_dev *enc = to_rkvenc_dev(mpp);
|
||||
struct rkvenc_ctx *ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
|
||||
int i;
|
||||
|
||||
mpp_debug_enter();
|
||||
|
||||
if (!ctx)
|
||||
return NULL;
|
||||
|
||||
/* HW defeat workaround start */
|
||||
if (!mpp_dev_is_power_on(mpp) && enc->dummy_ctx &&
|
||||
atomic_inc_return(&enc->dummy_ctx_in_used) == 1) {
|
||||
mpp_debug(DEBUG_RESET, "add a dummy ctx\n");
|
||||
mpp_srv_pending_locked(mpp->srv, &enc->dummy_ctx->ictx);
|
||||
}
|
||||
|
||||
mpp_dev_common_ctx_init(mpp, &ctx->ictx);
|
||||
|
||||
ctx->ictx.session = session;
|
||||
ctx->mode = RKVENC_MODE_LINKTABLE_FIX;
|
||||
|
||||
size = size > sizeof(ctx->cfg) ? sizeof(ctx->cfg) : size;
|
||||
|
||||
if (copy_from_user(&ctx->cfg, src, size)) {
|
||||
mpp_err("error: copy_from_user failed in reg_init\n");
|
||||
kfree(ctx);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ctx->mode = ctx->cfg.mode;
|
||||
if (ctx->mode >= RKVENC_MODE_NUM || ctx->mode == RKVENC_MODE_NONE) {
|
||||
mpp_err("Invalid rkvenc running mode %d\n", (int)ctx->mode);
|
||||
kfree(ctx);
|
||||
return NULL;
|
||||
} else if (ctx->mode == RKVENC_MODE_ONEFRAME && ctx->cfg.tbl_num > 1) {
|
||||
mpp_err("Configuration miss match, ignore redundant cfg\n");
|
||||
ctx->cfg.tbl_num = 1;
|
||||
}
|
||||
|
||||
mpp_debug(DEBUG_SET_REG, "tbl num %u, mode %u\n",
|
||||
ctx->cfg.tbl_num, ctx->cfg.mode);
|
||||
|
||||
for (i = 0; i < ctx->cfg.tbl_num; i++) {
|
||||
if (mpp_reg_address_translate(mpp, ctx->cfg.elem[i].reg,
|
||||
&ctx->ictx, 0) < 0) {
|
||||
mpp_err("error: translate reg address failed.\n");
|
||||
|
||||
if (unlikely(mpp_dev_debug & DEBUG_DUMP_ERR_REG))
|
||||
mpp_dump_reg_mem(ctx->cfg.elem[i].reg,
|
||||
ctx->cfg.elem[i].reg_num);
|
||||
|
||||
mpp_dev_common_ctx_deinit(mpp, &ctx->ictx);
|
||||
kfree(ctx);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
mpp_debug(DEBUG_SET_REG, "extra info cnt %u, magic %08x",
|
||||
ctx->cfg.elem[i].ext_inf.cnt,
|
||||
ctx->cfg.elem[i].ext_inf.magic);
|
||||
|
||||
mpp_translate_extra_info(&ctx->ictx, &ctx->cfg.elem[i].ext_inf,
|
||||
ctx->cfg.elem[i].reg);
|
||||
}
|
||||
|
||||
mpp_debug_leave();
|
||||
|
||||
return &ctx->ictx;
|
||||
}
|
||||
|
||||
static int rockchip_mpp_rkvenc_reset_init(struct rockchip_mpp_dev *mpp)
|
||||
{
|
||||
struct rockchip_rkvenc_dev *enc = to_rkvenc_dev(mpp);
|
||||
|
||||
mpp_debug(DEBUG_RESET, "reset init in:\n");
|
||||
enc->rst_a = devm_reset_control_get(mpp->dev, "video_a");
|
||||
enc->rst_h = devm_reset_control_get(mpp->dev, "video_h");
|
||||
enc->rst_v = devm_reset_control_get(mpp->dev, "video_c");
|
||||
|
||||
if (IS_ERR_OR_NULL(enc->rst_a)) {
|
||||
mpp_err("No aclk reset resource define\n");
|
||||
enc->rst_a = NULL;
|
||||
}
|
||||
|
||||
if (IS_ERR_OR_NULL(enc->rst_h)) {
|
||||
mpp_err("No hclk reset resource define\n");
|
||||
enc->rst_h = NULL;
|
||||
}
|
||||
|
||||
if (IS_ERR_OR_NULL(enc->rst_v)) {
|
||||
mpp_err("No core reset resource define\n");
|
||||
enc->rst_v = NULL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rockchip_mpp_rkvenc_reset(struct rockchip_mpp_dev *mpp)
|
||||
{
|
||||
struct rockchip_rkvenc_dev *enc = to_rkvenc_dev(mpp);
|
||||
int cnt = 100;
|
||||
|
||||
if (enc->rst_a && enc->rst_h && enc->rst_v) {
|
||||
mpp_debug(DEBUG_RESET, "reset in\n");
|
||||
mpp_write(mpp, 0, RKVENC_INT_EN);
|
||||
mpp_write(mpp, 1, RKVENC_SAFE_CLR);
|
||||
while (cnt-- > 0) {
|
||||
int status;
|
||||
|
||||
usleep_range(100, 200);
|
||||
status = mpp_read(mpp, RKVENC_ENC_STATUS);
|
||||
if (status & 4) {
|
||||
mpp_debug(DEBUG_RESET, "st_enc %08x\n", status);
|
||||
break;
|
||||
}
|
||||
}
|
||||
reset_control_assert(enc->rst_v);
|
||||
reset_control_assert(enc->rst_a);
|
||||
reset_control_assert(enc->rst_h);
|
||||
|
||||
udelay(1);
|
||||
|
||||
reset_control_deassert(enc->rst_v);
|
||||
reset_control_deassert(enc->rst_a);
|
||||
reset_control_deassert(enc->rst_h);
|
||||
mpp_debug(DEBUG_RESET, "reset out\n");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rockchip_mpp_rkvenc_prepare(struct rockchip_mpp_dev *mpp)
|
||||
{
|
||||
struct rkvenc_ctx *ctx_curr;
|
||||
struct rkvenc_ctx *ctx_ready;
|
||||
struct rockchip_rkvenc_dev *enc = to_rkvenc_dev(mpp);
|
||||
u32 lkt_status;
|
||||
u32 fnum_int;
|
||||
u32 fnum_cfg;
|
||||
u32 fnum_enc;
|
||||
u8 *cpu_addr;
|
||||
int i;
|
||||
|
||||
u32 reg = 0;
|
||||
|
||||
mpp_debug_enter();
|
||||
|
||||
if (!mpp_srv_is_running(mpp->srv))
|
||||
return 0;
|
||||
|
||||
/* if service running, determine link table mode */
|
||||
ctx_curr = to_rkvenc_ctx(mpp_srv_get_current_ctx(mpp->srv));
|
||||
ctx_ready = to_rkvenc_ctx(mpp_srv_get_pending_ctx(mpp->srv));
|
||||
|
||||
if (ctx_curr->mode != RKVENC_MODE_LINKTABLE_UPDATE ||
|
||||
ctx_ready->mode != ctx_curr->mode) {
|
||||
mpp_debug(DEBUG_TASK_INFO,
|
||||
"link table condition not fulfill\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
lkt_status = mpp_read(mpp, RKVENC_LKT_STATUS);
|
||||
fnum_int = RKVENC_LKT_STATUS_FNUM_INT(lkt_status);
|
||||
fnum_cfg = RKVENC_LKT_STATUS_FNUM_CFG(lkt_status);
|
||||
fnum_enc = RKVENC_LKT_STATUS_FNUM_ENC(lkt_status);
|
||||
cpu_addr = (u8 *)enc->lkt_cpu_addr + fnum_cfg * LINK_TABLE_LEN * 4;
|
||||
|
||||
mpp_dev_power_on(mpp);
|
||||
|
||||
mpp_debug(DEBUG_GET_REG, "frame number int %u, cfg %u, enc %u\n",
|
||||
fnum_int, fnum_cfg, fnum_enc);
|
||||
|
||||
for (i = 0; i < ctx_ready->cfg.tbl_num; i++) {
|
||||
u32 *src = ctx_ready->cfg.elem[i].reg;
|
||||
|
||||
memcpy(cpu_addr + i * LINK_TABLE_LEN * 4,
|
||||
&src[LINK_TABLE_START], LINK_TABLE_LEN * 4);
|
||||
}
|
||||
|
||||
reg = RKVENC_CLK_GATE_EN |
|
||||
RKVENC_CMD(ctx_curr->mode) |
|
||||
RKVENC_LKT_NUM(ctx_ready->cfg.tbl_num);
|
||||
mpp_write_relaxed(mpp, reg, RKVENC_ENC_START);
|
||||
|
||||
/* remove from pending queue */
|
||||
mpp_dev_common_ctx_deinit(mpp, &ctx_ready->ictx);
|
||||
|
||||
mpp_debug_leave();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rockchip_mpp_rkvenc_run(struct rockchip_mpp_dev *mpp)
|
||||
{
|
||||
struct rkvenc_ctx *ctx =
|
||||
to_rkvenc_ctx(mpp_srv_get_current_ctx(mpp->srv));
|
||||
struct rockchip_rkvenc_dev *enc = to_rkvenc_dev(mpp);
|
||||
u32 reg;
|
||||
int i;
|
||||
|
||||
mpp_debug_enter();
|
||||
|
||||
switch (ctx->mode) {
|
||||
case RKVENC_MODE_ONEFRAME:
|
||||
{
|
||||
u32 *src = ctx->cfg.elem[0].reg;
|
||||
|
||||
for (i = 2; i < (LINK_TABLE_START + LINK_TABLE_LEN); i++)
|
||||
mpp_write_relaxed(mpp, src[i], i * 4);
|
||||
|
||||
rockchip_mpp_rkvenc_cfg_palette(mpp, ctx->ictx.session);
|
||||
|
||||
mpp_write_relaxed(mpp, 0x1ff, RKVENC_INT_EN);
|
||||
reg = RKVENC_CLK_GATE_EN
|
||||
| RKVENC_CMD(1);
|
||||
mpp_write(mpp, reg, RKVENC_ENC_START);
|
||||
|
||||
break;
|
||||
}
|
||||
case RKVENC_MODE_LINKTABLE_FIX:
|
||||
case RKVENC_MODE_LINKTABLE_UPDATE:
|
||||
{
|
||||
for (i = 0; i < ctx->cfg.tbl_num; i++) {
|
||||
u32 *src = ctx->cfg.elem[i].reg;
|
||||
|
||||
memcpy(enc->lkt_cpu_addr +
|
||||
i * LINK_TABLE_LEN * 4,
|
||||
&src[LINK_TABLE_START],
|
||||
LINK_TABLE_LEN * 4);
|
||||
}
|
||||
|
||||
rockchip_mpp_rkvenc_cfg_palette(mpp, ctx->ictx.session);
|
||||
|
||||
mpp_write_relaxed(mpp,
|
||||
enc->lkt_dma_addr,
|
||||
RKVENC_LKT_ADDR);
|
||||
mpp_write_relaxed(mpp, 0xffffffff, RKVENC_INT_EN);
|
||||
|
||||
reg = RKVENC_LKT_NUM(ctx->cfg.tbl_num) |
|
||||
RKVENC_CMD(RKVENC_MODE_LINKTABLE_FIX) |
|
||||
RKVENC_CLK_GATE_EN;
|
||||
|
||||
mpp_write_relaxed(mpp, reg, RKVENC_ENC_START);
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
mpp_debug_leave();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rockchip_mpp_rkvenc_done(struct rockchip_mpp_dev *mpp)
|
||||
{
|
||||
struct mpp_ctx *ictx = mpp_srv_get_current_ctx(mpp->srv);
|
||||
struct rkvenc_ctx *ctx;
|
||||
struct rockchip_rkvenc_dev *enc = to_rkvenc_dev(mpp);
|
||||
struct rkvenc_result *result;
|
||||
int i;
|
||||
|
||||
mpp_debug_enter();
|
||||
|
||||
if (IS_ERR_OR_NULL(ictx)) {
|
||||
mpp_err("Invaidate context to save result\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
ctx = to_rkvenc_ctx(ictx);
|
||||
|
||||
if (enc->irq_status & RKVENC_INT_ERROR_BITS)
|
||||
/*
|
||||
* according to war running, if the dummy encoding
|
||||
* running with timeout, we enable a safe clear process,
|
||||
* we reset the ip, and complete the war procedure.
|
||||
*/
|
||||
atomic_inc(&mpp->reset_request);
|
||||
|
||||
if (ctx == enc->dummy_ctx) {
|
||||
mpp_debug(DEBUG_RESET, "war done\n");
|
||||
|
||||
/* for war do not trigger service done process */
|
||||
list_del_init(&ictx->status_link);
|
||||
atomic_set(&enc->dummy_ctx_in_used, 0);
|
||||
|
||||
/* dummy ctx, do not trigger service to wake up done process */
|
||||
return -1;
|
||||
}
|
||||
|
||||
result = &ctx->result;
|
||||
switch (ctx->mode) {
|
||||
case RKVENC_MODE_ONEFRAME:
|
||||
result->tbl_num = 1;
|
||||
result->elem[0].status = enc->irq_status;
|
||||
for (i = 0; i < sizeof(result->elem[0].result) / 4; i++)
|
||||
result->elem[0].result[i] =
|
||||
mpp_read(mpp,
|
||||
RKVENC_STATUS(i));
|
||||
break;
|
||||
case RKVENC_MODE_LINKTABLE_FIX:
|
||||
case RKVENC_MODE_LINKTABLE_UPDATE:
|
||||
{
|
||||
u32 lkt_status = mpp_read(mpp, RKVENC_LKT_STATUS);
|
||||
u32 fnum_int = RKVENC_LKT_STATUS_FNUM_INT(lkt_status);
|
||||
u32 fnum_cfg = RKVENC_LKT_STATUS_FNUM_CFG(lkt_status);
|
||||
u32 fnum_enc = RKVENC_LKT_STATUS_FNUM_ENC(lkt_status);
|
||||
|
||||
u32 *lkt_cpu_addr = (u32 *)enc->lkt_cpu_addr;
|
||||
|
||||
if (unlikely(mpp_dev_debug & DEBUG_DUMP_ERR_REG))
|
||||
mpp_dump_reg_mem(lkt_cpu_addr, LINK_TABLE_LEN);
|
||||
|
||||
result->tbl_num = fnum_int;
|
||||
for (i = 0; i < fnum_int; i++) {
|
||||
result->elem[i].status = enc->irq_status;
|
||||
memcpy(result->elem[i].result,
|
||||
&lkt_cpu_addr[i * LINK_TABLE_LEN + 120],
|
||||
sizeof(result->elem[i].result));
|
||||
mpp_debug(DEBUG_GET_REG, "stream length %u\n",
|
||||
result->elem[i].result[0]);
|
||||
}
|
||||
mpp_debug(DEBUG_GET_REG, "frame number %u, %u, %u\n",
|
||||
fnum_int, fnum_cfg, fnum_enc);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
mpp_debug_leave();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rockchip_mpp_rkvenc_irq(struct rockchip_mpp_dev *mpp)
|
||||
{
|
||||
struct rockchip_rkvenc_dev *enc = to_rkvenc_dev(mpp);
|
||||
|
||||
enc->irq_status = mpp_read(mpp, RKVENC_INT_STATUS);
|
||||
|
||||
mpp_debug_enter();
|
||||
|
||||
if (enc->irq_status == 0)
|
||||
return -1;
|
||||
|
||||
mpp_debug(DEBUG_IRQ_STATUS, "irq_status: %08x\n", enc->irq_status);
|
||||
mpp_write(mpp, 0xffffffff, RKVENC_INT_CLR);
|
||||
if (enc->irq_status & RKVENC_INT_ERROR_BITS) {
|
||||
mpp_err("error irq %08x\n", enc->irq_status);
|
||||
/* time out error */
|
||||
mpp_write(mpp, RKVENC_INT_ERROR_BITS, RKVENC_INT_MSK);
|
||||
}
|
||||
|
||||
mpp_debug_leave();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rockchip_mpp_rkvenc_result(struct rockchip_mpp_dev *mpp,
|
||||
struct mpp_ctx *ictx, u32 __user *dst)
|
||||
{
|
||||
struct rkvenc_ctx *ctx = to_rkvenc_ctx(ictx);
|
||||
struct rkvenc_result *result = &ctx->result;
|
||||
unsigned long tbl_size = sizeof(result->tbl_num) +
|
||||
sizeof(result->elem[0]) * result->tbl_num;
|
||||
|
||||
switch (ctx->mode) {
|
||||
case RKVENC_MODE_ONEFRAME:
|
||||
case RKVENC_MODE_LINKTABLE_FIX:
|
||||
case RKVENC_MODE_LINKTABLE_UPDATE:
|
||||
{
|
||||
if (copy_to_user(dst, &ctx->result, tbl_size)) {
|
||||
mpp_err("copy result to user failed\n");
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
mpp_err("invalid context mode %d\n", (int)ctx->mode);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long rockchip_mpp_rkvenc_ioctl(struct mpp_session *isession,
|
||||
unsigned int cmd,
|
||||
unsigned long arg)
|
||||
{
|
||||
struct rkvenc_session *session = to_rkvenc_session(isession);
|
||||
|
||||
mpp_debug_enter();
|
||||
|
||||
switch (cmd) {
|
||||
case MPP_DEV_RKVENC_SET_COLOR_PALETTE:
|
||||
if (copy_from_user(&session->palette, (void __user *)arg,
|
||||
sizeof(session->palette))) {
|
||||
mpp_err("copy palette from user failed\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
session->palette_valid = true;
|
||||
|
||||
break;
|
||||
default:
|
||||
mpp_err("%s, unknown ioctl cmd %x\n",
|
||||
dev_name(isession->mpp->dev), cmd);
|
||||
break;
|
||||
}
|
||||
|
||||
mpp_debug_leave();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct mpp_session *mpp_dev_rkvenc_open(struct rockchip_mpp_dev *mpp)
|
||||
{
|
||||
struct rkvenc_session *session = kzalloc(sizeof(*session), GFP_KERNEL);
|
||||
|
||||
mpp_debug_enter();
|
||||
|
||||
if (!session)
|
||||
return NULL;
|
||||
|
||||
session->palette_valid = false;
|
||||
|
||||
mpp_debug_leave();
|
||||
|
||||
return &session->isession;
|
||||
}
|
||||
|
||||
static void mpp_dev_rkvenc_free(struct mpp_session *isession)
|
||||
{
|
||||
struct rkvenc_session *session = to_rkvenc_session(isession);
|
||||
|
||||
kfree(session);
|
||||
}
|
||||
|
||||
struct mpp_dev_ops rkvenc_ops = {
|
||||
.init = rockchip_mpp_rkvenc_init,
|
||||
.prepare = rockchip_mpp_rkvenc_prepare,
|
||||
.run = rockchip_mpp_rkvenc_run,
|
||||
.done = rockchip_mpp_rkvenc_done,
|
||||
.irq = rockchip_mpp_rkvenc_irq,
|
||||
.result = rockchip_mpp_rkvenc_result,
|
||||
.ioctl = rockchip_mpp_rkvenc_ioctl,
|
||||
.open = mpp_dev_rkvenc_open,
|
||||
.free = mpp_dev_rkvenc_free,
|
||||
};
|
||||
|
||||
static void rockchip_mpp_rkvenc_power_on(struct rockchip_mpp_dev *mpp)
|
||||
{
|
||||
struct rockchip_rkvenc_dev *enc = to_rkvenc_dev(mpp);
|
||||
|
||||
if (enc->aclk)
|
||||
clk_prepare_enable(enc->aclk);
|
||||
if (enc->hclk)
|
||||
clk_prepare_enable(enc->hclk);
|
||||
if (enc->core)
|
||||
clk_prepare_enable(enc->core);
|
||||
|
||||
/*
|
||||
* Because hw cannot reset status fully in all its modules, we make a
|
||||
* reset here to make sure the hw status fully reset.
|
||||
*/
|
||||
rockchip_mpp_rkvenc_reset(mpp);
|
||||
}
|
||||
|
||||
static void rockchip_mpp_rkvenc_power_off(struct rockchip_mpp_dev *mpp)
|
||||
{
|
||||
struct rockchip_rkvenc_dev *enc = to_rkvenc_dev(mpp);
|
||||
|
||||
if (enc->core)
|
||||
clk_disable_unprepare(enc->core);
|
||||
if (enc->hclk)
|
||||
clk_disable_unprepare(enc->hclk);
|
||||
if (enc->aclk)
|
||||
clk_disable_unprepare(enc->aclk);
|
||||
}
|
||||
|
||||
static int rockchip_mpp_rkvenc_probe(struct rockchip_mpp_dev *mpp)
|
||||
{
|
||||
struct rockchip_rkvenc_dev *enc = to_rkvenc_dev(mpp);
|
||||
struct mpp_session *session = list_first_entry(&mpp->srv->session,
|
||||
struct mpp_session,
|
||||
list_session);
|
||||
int ret;
|
||||
size_t tmp;
|
||||
|
||||
enc->idev.ops = &rkvenc_ops;
|
||||
|
||||
enc->lkt_hdl = vpu_iommu_alloc(mpp->iommu_info, session,
|
||||
LINK_TABLE_LEN * 4 * 256,
|
||||
MPP_ALIGN_SIZE);
|
||||
|
||||
if (enc->lkt_hdl < 0) {
|
||||
dev_err(mpp->dev, "allocate link table buffer failure\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = vpu_iommu_map_iommu(mpp->iommu_info, session,
|
||||
enc->lkt_hdl, &enc->lkt_dma_addr, &tmp);
|
||||
|
||||
if (ret < 0) {
|
||||
dev_err(mpp->dev, "get link table dma_addr failed\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
enc->lkt_cpu_addr = vpu_iommu_map_kernel(mpp->iommu_info,
|
||||
session, enc->lkt_hdl);
|
||||
|
||||
/*
|
||||
* buffer for workaround context running, include input picture, output
|
||||
* stream, reconstruction picture. we set the output stream buffer to 1
|
||||
* time picture size, so the total buffer size is 3 times picture size,
|
||||
* 64 * 64 * 3 / 2 * 3 = 4.5 * 4k.
|
||||
*/
|
||||
enc->war_hdl = vpu_iommu_alloc(mpp->iommu_info, session,
|
||||
MPP_ALIGN_SIZE * 5,
|
||||
MPP_ALIGN_SIZE);
|
||||
if (enc->war_hdl < 0) {
|
||||
dev_err(mpp->dev, "allocate workaround buffer failure\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ret = vpu_iommu_map_iommu(mpp->iommu_info, session,
|
||||
enc->war_hdl, &enc->war_dma_addr, &tmp);
|
||||
|
||||
if (ret < 0) {
|
||||
dev_err(mpp->dev, "get war dma_addr failed\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
rockchip_mpp_war_init(mpp);
|
||||
|
||||
enc->aclk = devm_clk_get(mpp->dev, "aclk_vcodec");
|
||||
if (IS_ERR_OR_NULL(enc->aclk)) {
|
||||
dev_err(mpp->dev, "failed on clk_get aclk\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
enc->hclk = devm_clk_get(mpp->dev, "hclk_vcodec");
|
||||
if (IS_ERR_OR_NULL(enc->hclk)) {
|
||||
dev_err(mpp->dev, "failed on clk_get hclk\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
enc->core = devm_clk_get(mpp->dev, "clk_core");
|
||||
if (IS_ERR_OR_NULL(enc->core)) {
|
||||
dev_err(mpp->dev, "failed on clk_get core\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
rockchip_mpp_rkvenc_reset_init(mpp);
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
kfree(enc->dummy_ctx);
|
||||
|
||||
if (enc->war_hdl >= 0) {
|
||||
vpu_iommu_unmap_iommu(mpp->iommu_info,
|
||||
session, enc->war_hdl);
|
||||
vpu_iommu_free(mpp->iommu_info, session, enc->war_hdl);
|
||||
}
|
||||
if (enc->lkt_cpu_addr)
|
||||
vpu_iommu_unmap_kernel(mpp->iommu_info, session, enc->lkt_hdl);
|
||||
if (enc->lkt_hdl >= 0) {
|
||||
vpu_iommu_unmap_iommu(mpp->iommu_info,
|
||||
session, enc->lkt_hdl);
|
||||
vpu_iommu_free(mpp->iommu_info, session, enc->lkt_hdl);
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void rockchip_mpp_rkvenc_remove(struct rockchip_mpp_dev *mpp)
|
||||
{
|
||||
struct rockchip_rkvenc_dev *enc = to_rkvenc_dev(mpp);
|
||||
struct mpp_session *session = list_first_entry(&mpp->srv->session,
|
||||
struct mpp_session,
|
||||
list_session);
|
||||
|
||||
vpu_iommu_unmap_kernel(mpp->iommu_info, session, enc->lkt_hdl);
|
||||
vpu_iommu_unmap_iommu(mpp->iommu_info,
|
||||
session, enc->lkt_hdl);
|
||||
vpu_iommu_free(mpp->iommu_info, session, enc->lkt_hdl);
|
||||
|
||||
vpu_iommu_unmap_iommu(mpp->iommu_info,
|
||||
session, enc->war_hdl);
|
||||
vpu_iommu_free(mpp->iommu_info, session, enc->war_hdl);
|
||||
|
||||
kfree(enc->dummy_ctx);
|
||||
}
|
||||
|
||||
const struct rockchip_mpp_dev_variant rkvenc_variant = {
|
||||
.data_len = sizeof(struct rockchip_rkvenc_dev),
|
||||
.reg_len = 140,
|
||||
.trans_info = trans_rkvenc,
|
||||
.hw_probe = rockchip_mpp_rkvenc_probe,
|
||||
.hw_remove = rockchip_mpp_rkvenc_remove,
|
||||
.power_on = rockchip_mpp_rkvenc_power_on,
|
||||
.power_off = rockchip_mpp_rkvenc_power_off,
|
||||
.reset = rockchip_mpp_rkvenc_reset,
|
||||
};
|
||||
EXPORT_SYMBOL(rkvenc_variant);
|
||||
|
||||
@@ -1,177 +0,0 @@
|
||||
/**
|
||||
* Copyright (C) 2016 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 __ROCKCHIP_MPP_DEV_RKVENC_H
|
||||
#define __ROCKCHIP_MPP_DEV_RKVENC_H
|
||||
|
||||
union rkvenc_osd_palette_elem {
|
||||
struct {
|
||||
u8 y;
|
||||
u8 u;
|
||||
u8 v;
|
||||
u8 alpha;
|
||||
};
|
||||
u32 elem;
|
||||
};
|
||||
|
||||
#define RKVENC_OSD_PLT_LEN 256
|
||||
struct rkvenc_osd_palette {
|
||||
union rkvenc_osd_palette_elem plalette[RKVENC_OSD_PLT_LEN];
|
||||
};
|
||||
|
||||
#define MPP_DEV_RKVENC_SET_COLOR_PALETTE \
|
||||
_IOW(VPU_IOC_MAGIC, MPP_IOC_CUSTOM_BASE + 1, \
|
||||
struct rkvenc_osd_palette)
|
||||
|
||||
struct rkvenc_config_elem {
|
||||
u32 reg_num;
|
||||
u32 reg[140];
|
||||
struct extra_info_for_iommu ext_inf;
|
||||
};
|
||||
|
||||
struct rkvenc_config {
|
||||
u32 mode;
|
||||
u32 tbl_num;
|
||||
struct rkvenc_config_elem elem[10];
|
||||
};
|
||||
|
||||
struct rkvenc_result_elem {
|
||||
u32 status;
|
||||
u32 result[11];
|
||||
};
|
||||
|
||||
struct rkvenc_result {
|
||||
u32 tbl_num;
|
||||
struct rkvenc_result_elem elem[10];
|
||||
};
|
||||
|
||||
struct rkvenc_ctx {
|
||||
struct mpp_ctx ictx;
|
||||
enum RKVENC_MODE mode;
|
||||
|
||||
struct rkvenc_config cfg;
|
||||
|
||||
/* store status read from hw, oneframe mode used only */
|
||||
struct rkvenc_result result;
|
||||
};
|
||||
|
||||
struct rkvenc_session {
|
||||
struct mpp_session isession;
|
||||
|
||||
struct rkvenc_osd_palette palette;
|
||||
bool palette_valid;
|
||||
};
|
||||
|
||||
struct mpp_dev_rkvenc_reg {
|
||||
u32 unused_00;
|
||||
u32 enc_strt;
|
||||
u32 enc_clr;
|
||||
u32 lkt_addr;
|
||||
u32 int_en;
|
||||
u32 int_msk;
|
||||
u32 int_clr;
|
||||
u32 unused_20[4];
|
||||
u32 int_stus;
|
||||
/* 12 */
|
||||
u32 enc_rsl;
|
||||
u32 enc_pic;
|
||||
u32 enc_wdg;
|
||||
u32 dtrns_map;
|
||||
u32 dtrns_cfg;
|
||||
u32 src_fmt;
|
||||
u32 src_udfy;
|
||||
u32 src_udfu;
|
||||
u32 src_udfv;
|
||||
u32 src_udfo;
|
||||
u32 src_proc;
|
||||
u32 src_tthrd;
|
||||
u32 src_stbl[5];
|
||||
u32 h3d_tbl[40];
|
||||
u32 src_strd;
|
||||
u32 adr_srcy;
|
||||
u32 adr_srcu;
|
||||
u32 adr_srcv;
|
||||
u32 adr_fltw;
|
||||
u32 adr_fltr;
|
||||
u32 adr_ctuc;
|
||||
u32 adr_rfpw;
|
||||
u32 adr_rfpr;
|
||||
u32 adr_cmvw;
|
||||
u32 adr_cmvr;
|
||||
u32 adr_dspw;
|
||||
u32 adr_dspr;
|
||||
u32 adr_meiw;
|
||||
u32 adr_bsbt;
|
||||
u32 adr_bsbb;
|
||||
u32 adr_bsbr;
|
||||
u32 adr_bsbw;
|
||||
u32 sli_spl;
|
||||
u32 sli_spl_byte;
|
||||
u32 me_rnge;
|
||||
u32 me_cnst;
|
||||
u32 me_ram;
|
||||
u32 rc_cfg;
|
||||
u32 rc_erp[5];
|
||||
u32 rc_adj[2];
|
||||
u32 rc_qp;
|
||||
u32 rc_tgt;
|
||||
u32 rdo_cfg;
|
||||
u32 synt_nal;
|
||||
u32 synt_sps;
|
||||
u32 synt_pps;
|
||||
u32 synt_sli0;
|
||||
u32 synt_sli1;
|
||||
u32 synt_sli2_rodr;
|
||||
u32 synt_ref_mark0;
|
||||
u32 synt_ref_mark1;
|
||||
u32 osd_cfg;
|
||||
u32 osd_inv;
|
||||
u32 unused_1c8[2];
|
||||
u32 osd_pos[8];
|
||||
u32 osd_addr[8];
|
||||
u32 unused_210[9];
|
||||
};
|
||||
|
||||
struct rockchip_rkvenc_dev {
|
||||
struct rockchip_mpp_dev idev;
|
||||
unsigned long lkt_dma_addr;
|
||||
int lkt_hdl;
|
||||
void *lkt_cpu_addr;
|
||||
u32 irq_status;
|
||||
unsigned long war_dma_addr;
|
||||
int war_hdl;
|
||||
struct mpp_dev_rkvenc_reg *war_reg;
|
||||
struct rkvenc_ctx *dummy_ctx;
|
||||
atomic_t dummy_ctx_in_used;
|
||||
|
||||
struct clk *aclk;
|
||||
struct clk *hclk;
|
||||
struct clk *core;
|
||||
|
||||
struct reset_control *rst_a;
|
||||
struct reset_control *rst_h;
|
||||
struct reset_control *rst_v;
|
||||
};
|
||||
|
||||
struct link_table_elem {
|
||||
unsigned long lkt_dma_addr;
|
||||
int lkt_hdl;
|
||||
void *lkt_cpu_addr;
|
||||
u32 lkt_index;
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,383 +0,0 @@
|
||||
/**
|
||||
* Copyright (C) 2016 Fuzhou Rockchip Electronics Co., Ltd
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/reset.h>
|
||||
#include <linux/rockchip/grf.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/uaccess.h>
|
||||
|
||||
#include "vpu_iommu_ops.h"
|
||||
#include "mpp_dev_common.h"
|
||||
#include "mpp_dev_vepu.h"
|
||||
#include "mpp_service.h"
|
||||
|
||||
#define to_vepu_ctx(ctx) \
|
||||
container_of(ctx, struct vepu_ctx, ictx)
|
||||
#define to_vepu_dev(dev) \
|
||||
container_of(dev, struct rockchip_vepu_dev, idev)
|
||||
|
||||
#define VEPU_REG_INTERRUPT 0x1b4
|
||||
#define VEPU_REG_ENC_START 0x19c
|
||||
#define VEPU_ENC_GET_FORMAT(x) (((x) >> 4) & 0x3)
|
||||
#define VEPU_ENC_FMT_VP8E 1
|
||||
#define VEPU_ENC_ENABLE BIT(0)
|
||||
|
||||
#define MHZ (1000 * 1000)
|
||||
|
||||
#define DEFAULT_ACLK_RATE_ON (200 * MHZ)
|
||||
#define DEFAULT_CCLK_RATE_ON (300 * MHZ)
|
||||
#define DEFAULT_ACLK_RATE_OFF (50 * MHZ)
|
||||
#define DEFAULT_CCLK_RATE_OFF (50 * MHZ)
|
||||
|
||||
/*
|
||||
* file handle translate information
|
||||
*/
|
||||
static const char trans_tbl_default[] = {
|
||||
77, 78, 56, 57, 63, 64, 48, 49, 50, 81
|
||||
};
|
||||
|
||||
static const char trans_tbl_vp8e[] = {
|
||||
77, 78, 56, 57, 63, 64, 48, 49, 50, 76, 106, 108, 81, 80, 44, 45, 27
|
||||
};
|
||||
|
||||
static struct mpp_trans_info trans_vepu[2] = {
|
||||
[0] = {
|
||||
.count = sizeof(trans_tbl_default),
|
||||
.table = trans_tbl_default,
|
||||
},
|
||||
[1] = {
|
||||
.count = sizeof(trans_tbl_vp8e),
|
||||
.table = trans_tbl_vp8e,
|
||||
},
|
||||
};
|
||||
|
||||
static struct mpp_ctx *rockchip_mpp_vepu_init(struct rockchip_mpp_dev *mpp,
|
||||
struct mpp_session *session,
|
||||
void __user *src, u32 size)
|
||||
{
|
||||
struct vepu_ctx *ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
|
||||
u32 reg_len;
|
||||
u32 extinf_len;
|
||||
u32 fmt = 0;
|
||||
int trans_idx = 0;
|
||||
u32 dwsize = size / sizeof(u32);
|
||||
|
||||
mpp_debug_enter();
|
||||
|
||||
if (!ctx)
|
||||
return NULL;
|
||||
|
||||
mpp_dev_common_ctx_init(mpp, &ctx->ictx);
|
||||
|
||||
ctx->ictx.session = session;
|
||||
|
||||
reg_len = dwsize > ROCKCHIP_VEPU_REG_LEN ?
|
||||
ROCKCHIP_VEPU_REG_LEN : dwsize;
|
||||
extinf_len = dwsize > reg_len ? (dwsize - reg_len) * 4 : 0;
|
||||
|
||||
if (copy_from_user(ctx->reg, src, reg_len * 4)) {
|
||||
mpp_err("error: copy_from_user failed in reg_init\n");
|
||||
kfree(ctx);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (extinf_len > 0) {
|
||||
u32 ext_cpy = min_t(size_t, extinf_len, sizeof(ctx->ext_inf));
|
||||
|
||||
if (copy_from_user(&ctx->ext_inf, (u8 *)src +
|
||||
reg_len * sizeof(u32),
|
||||
ext_cpy)) {
|
||||
mpp_err("copy_from_user failed when extra info\n");
|
||||
kfree(ctx);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
fmt = VEPU_ENC_GET_FORMAT(ctx->reg[VEPU_REG_ENC_START / 4]);
|
||||
if (fmt == VEPU_ENC_FMT_VP8E)
|
||||
trans_idx = 1;
|
||||
|
||||
if (mpp_reg_address_translate(mpp, ctx->reg, &ctx->ictx,
|
||||
trans_idx) < 0) {
|
||||
mpp_err("error: translate reg address failed.\n");
|
||||
|
||||
if (unlikely(mpp_dev_debug & DEBUG_DUMP_ERR_REG))
|
||||
mpp_dump_reg_mem(ctx->reg, ROCKCHIP_VEPU_REG_LEN);
|
||||
|
||||
mpp_dev_common_ctx_deinit(mpp, &ctx->ictx);
|
||||
kfree(ctx);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
mpp_debug(DEBUG_SET_REG, "extra info cnt %u, magic %08x",
|
||||
ctx->ext_inf.cnt, ctx->ext_inf.magic);
|
||||
|
||||
mpp_translate_extra_info(&ctx->ictx, &ctx->ext_inf, ctx->reg);
|
||||
|
||||
mpp_debug_leave();
|
||||
|
||||
return &ctx->ictx;
|
||||
}
|
||||
|
||||
static int rockchip_mpp_vepu_reset_init(struct rockchip_mpp_dev *mpp)
|
||||
{
|
||||
struct rockchip_vepu_dev *enc = to_vepu_dev(mpp);
|
||||
|
||||
mpp_debug(DEBUG_RESET, "reset init in:\n");
|
||||
enc->rst_a = devm_reset_control_get(mpp->dev, "video_a");
|
||||
enc->rst_h = devm_reset_control_get(mpp->dev, "video_h");
|
||||
|
||||
if (IS_ERR_OR_NULL(enc->rst_a)) {
|
||||
mpp_err("No aclk reset resource define\n");
|
||||
enc->rst_a = NULL;
|
||||
}
|
||||
|
||||
if (IS_ERR_OR_NULL(enc->rst_h)) {
|
||||
mpp_err("No hclk reset resource define\n");
|
||||
enc->rst_h = NULL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rockchip_mpp_vepu_reset(struct rockchip_mpp_dev *mpp)
|
||||
{
|
||||
struct rockchip_vepu_dev *enc = to_vepu_dev(mpp);
|
||||
|
||||
if (enc->rst_a && enc->rst_h) {
|
||||
mpp_debug(DEBUG_RESET, "reset in\n");
|
||||
reset_control_assert(enc->rst_a);
|
||||
reset_control_assert(enc->rst_h);
|
||||
|
||||
udelay(1);
|
||||
|
||||
reset_control_deassert(enc->rst_a);
|
||||
reset_control_deassert(enc->rst_h);
|
||||
mpp_debug(DEBUG_RESET, "reset out\n");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rockchip_mpp_vepu_run(struct rockchip_mpp_dev *mpp)
|
||||
{
|
||||
struct vepu_ctx *ctx =
|
||||
to_vepu_ctx(mpp_srv_get_current_ctx(mpp->srv));
|
||||
struct rockchip_vepu_dev *enc = to_vepu_dev(mpp);
|
||||
int i;
|
||||
|
||||
mpp_debug_enter();
|
||||
|
||||
/*
|
||||
* before encoding running, we have to switch grf ctrl bit to ensure
|
||||
* ip inner-sram controlled by vepu
|
||||
*/
|
||||
#ifdef CONFIG_MFD_SYSCON
|
||||
if (enc->grf) {
|
||||
u32 raw;
|
||||
u32 bits = BIT(enc->mode_bit);
|
||||
|
||||
regmap_read(enc->grf, enc->mode_ctrl, &raw);
|
||||
regmap_write(enc->grf, enc->mode_ctrl,
|
||||
(raw & (~bits)) | (bits << 16));
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* NOTE: encoder need to setup mode first
|
||||
*/
|
||||
mpp_write(mpp,
|
||||
ctx->reg[VEPU_REG_ENC_START / 4] & (~VEPU_ENC_ENABLE),
|
||||
VEPU_REG_ENC_START);
|
||||
|
||||
for (i = 0; i < ROCKCHIP_VEPU_REG_LEN; i++) {
|
||||
if (i * 4 != VEPU_REG_ENC_START)
|
||||
mpp_write_relaxed(mpp, ctx->reg[i], i * 4);
|
||||
}
|
||||
|
||||
mpp_write(mpp, ctx->reg[VEPU_REG_ENC_START / 4], VEPU_REG_ENC_START);
|
||||
|
||||
mpp_debug_leave();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rockchip_mpp_vepu_done(struct rockchip_mpp_dev *mpp)
|
||||
{
|
||||
struct mpp_ctx *ictx = mpp_srv_get_current_ctx(mpp->srv);
|
||||
struct vepu_ctx *ctx;
|
||||
struct rockchip_vepu_dev *enc = to_vepu_dev(mpp);
|
||||
int i;
|
||||
|
||||
mpp_debug_enter();
|
||||
|
||||
if (IS_ERR_OR_NULL(ictx)) {
|
||||
mpp_err("Invaidate context to save result\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
ctx = to_vepu_ctx(ictx);
|
||||
|
||||
for (i = 0; i < ROCKCHIP_VEPU_REG_LEN; i++)
|
||||
ctx->reg[i] = mpp_read(mpp, i * 4);
|
||||
|
||||
ctx->reg[VEPU_REG_INTERRUPT / 4] = enc->irq_status;
|
||||
|
||||
mpp_debug_leave();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rockchip_mpp_vepu_irq(struct rockchip_mpp_dev *mpp)
|
||||
{
|
||||
struct rockchip_vepu_dev *enc = to_vepu_dev(mpp);
|
||||
|
||||
enc->irq_status = mpp_read(mpp, VEPU_REG_INTERRUPT);
|
||||
|
||||
mpp_debug_enter();
|
||||
|
||||
if (enc->irq_status == 0)
|
||||
return -1;
|
||||
|
||||
mpp_debug(DEBUG_IRQ_STATUS, "irq_status: %08x\n", enc->irq_status);
|
||||
mpp_write(mpp, 0, VEPU_REG_INTERRUPT);
|
||||
|
||||
mpp_debug_leave();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rockchip_mpp_vepu_result(struct rockchip_mpp_dev *mpp,
|
||||
struct mpp_ctx *ictx, u32 __user *dst)
|
||||
{
|
||||
struct vepu_ctx *ctx = to_vepu_ctx(ictx);
|
||||
|
||||
if (copy_to_user(dst, ctx->reg, ROCKCHIP_VEPU_REG_LEN * 4)) {
|
||||
mpp_err("copy_to_user failed\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct mpp_dev_ops vepu_ops = {
|
||||
.init = rockchip_mpp_vepu_init,
|
||||
.run = rockchip_mpp_vepu_run,
|
||||
.done = rockchip_mpp_vepu_done,
|
||||
.irq = rockchip_mpp_vepu_irq,
|
||||
.result = rockchip_mpp_vepu_result,
|
||||
};
|
||||
|
||||
static void rockchip_mpp_vepu_power_on(struct rockchip_mpp_dev *mpp)
|
||||
{
|
||||
struct rockchip_vepu_dev *enc = to_vepu_dev(mpp);
|
||||
|
||||
if (enc->aclk) {
|
||||
clk_set_rate(enc->aclk, DEFAULT_ACLK_RATE_ON);
|
||||
clk_prepare_enable(enc->aclk);
|
||||
}
|
||||
if (enc->hclk)
|
||||
clk_prepare_enable(enc->hclk);
|
||||
if (enc->cclk) {
|
||||
clk_set_rate(enc->cclk, DEFAULT_CCLK_RATE_ON);
|
||||
clk_prepare_enable(enc->cclk);
|
||||
}
|
||||
}
|
||||
|
||||
static void rockchip_mpp_vepu_power_off(struct rockchip_mpp_dev *mpp)
|
||||
{
|
||||
struct rockchip_vepu_dev *enc = to_vepu_dev(mpp);
|
||||
|
||||
if (enc->aclk) {
|
||||
clk_set_rate(enc->aclk, DEFAULT_ACLK_RATE_OFF);
|
||||
clk_disable_unprepare(enc->aclk);
|
||||
}
|
||||
if (enc->hclk)
|
||||
clk_disable_unprepare(enc->hclk);
|
||||
if (enc->cclk) {
|
||||
clk_set_rate(enc->cclk, DEFAULT_CCLK_RATE_OFF);
|
||||
clk_disable_unprepare(enc->cclk);
|
||||
}
|
||||
}
|
||||
|
||||
static int rockchip_mpp_vepu_probe(struct rockchip_mpp_dev *mpp)
|
||||
{
|
||||
struct rockchip_vepu_dev *enc = to_vepu_dev(mpp);
|
||||
struct device_node *np = mpp->dev->of_node;
|
||||
|
||||
enc->idev.ops = &vepu_ops;
|
||||
|
||||
enc->aclk = devm_clk_get(mpp->dev, "aclk_vcodec");
|
||||
if (IS_ERR_OR_NULL(enc->aclk)) {
|
||||
dev_err(mpp->dev, "failed on clk_get aclk\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
enc->hclk = devm_clk_get(mpp->dev, "hclk_vcodec");
|
||||
if (IS_ERR_OR_NULL(enc->hclk)) {
|
||||
dev_err(mpp->dev, "failed on clk_get hclk\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
enc->cclk = devm_clk_get(mpp->dev, "clk_core");
|
||||
if (IS_ERR_OR_NULL(enc->cclk)) {
|
||||
dev_err(mpp->dev, "failed on clk_get cclk\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (of_property_read_bool(np, "mode_ctrl")) {
|
||||
of_property_read_u32(np, "mode_bit", &enc->mode_bit);
|
||||
of_property_read_u32(np, "mode_ctrl", &enc->mode_ctrl);
|
||||
|
||||
#ifdef COFNIG_MFD_SYSCON
|
||||
enc->grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
|
||||
if (IS_ERR_OR_NULL(enc->grf)) {
|
||||
enc->grf = NULL;
|
||||
mpp_err("can't find vpu grf property\n");
|
||||
goto fail;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
rockchip_mpp_vepu_reset_init(mpp);
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void rockchip_mpp_vepu_remove(struct rockchip_mpp_dev *mpp)
|
||||
{
|
||||
}
|
||||
|
||||
const struct rockchip_mpp_dev_variant vepu_variant = {
|
||||
.data_len = sizeof(struct rockchip_vepu_dev),
|
||||
.reg_len = ROCKCHIP_VEPU_REG_LEN,
|
||||
.trans_info = trans_vepu,
|
||||
.mmu_dev_dts_name = NULL,
|
||||
.hw_probe = rockchip_mpp_vepu_probe,
|
||||
.hw_remove = rockchip_mpp_vepu_remove,
|
||||
.power_on = rockchip_mpp_vepu_power_on,
|
||||
.power_off = rockchip_mpp_vepu_power_off,
|
||||
.reset = rockchip_mpp_vepu_reset,
|
||||
};
|
||||
EXPORT_SYMBOL(vepu_variant);
|
||||
@@ -1,47 +0,0 @@
|
||||
/**
|
||||
* Copyright (C) 2016 Fuzhou Rockchip Electronics Co., Ltd
|
||||
* 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 __ROCKCHIP_MPP_DEV_VEPU_H
|
||||
#define __ROCKCHIP_MPP_DEV_VEPU_H
|
||||
|
||||
#define ROCKCHIP_VEPU_REG_LEN 184
|
||||
|
||||
struct regmap;
|
||||
|
||||
struct rockchip_vepu_dev {
|
||||
struct rockchip_mpp_dev idev;
|
||||
|
||||
u32 irq_status;
|
||||
|
||||
struct clk *aclk;
|
||||
struct clk *hclk;
|
||||
struct clk *cclk;
|
||||
|
||||
struct reset_control *rst_a;
|
||||
struct reset_control *rst_h;
|
||||
|
||||
u32 mode_bit;
|
||||
u32 mode_ctrl;
|
||||
struct regmap *grf;
|
||||
};
|
||||
|
||||
struct vepu_ctx {
|
||||
struct mpp_ctx ictx;
|
||||
|
||||
u32 reg[ROCKCHIP_VEPU_REG_LEN];
|
||||
struct extra_info_for_iommu ext_inf;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,228 +0,0 @@
|
||||
/**
|
||||
* Copyright (C) 2016 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 <linux/delay.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_platform.h>
|
||||
|
||||
#include "mpp_dev_common.h"
|
||||
#include "mpp_service.h"
|
||||
|
||||
void mpp_srv_lock(struct mpp_service *pservice)
|
||||
{
|
||||
mutex_lock(&pservice->lock);
|
||||
}
|
||||
EXPORT_SYMBOL(mpp_srv_lock);
|
||||
|
||||
void mpp_srv_unlock(struct mpp_service *pservice)
|
||||
{
|
||||
mutex_unlock(&pservice->lock);
|
||||
}
|
||||
EXPORT_SYMBOL(mpp_srv_unlock);
|
||||
|
||||
/* service queue schedule */
|
||||
void mpp_srv_pending_locked(struct mpp_service *pservice,
|
||||
struct mpp_ctx *ctx)
|
||||
{
|
||||
mpp_srv_lock(pservice);
|
||||
|
||||
list_add_tail(&ctx->status_link, &pservice->pending);
|
||||
|
||||
mpp_srv_unlock(pservice);
|
||||
}
|
||||
EXPORT_SYMBOL(mpp_srv_pending_locked);
|
||||
|
||||
void mpp_srv_run(struct mpp_service *pservice)
|
||||
{
|
||||
struct mpp_ctx *ctx = mpp_srv_get_pending_ctx(pservice);
|
||||
|
||||
list_del_init(&ctx->status_link);
|
||||
list_add_tail(&ctx->status_link, &pservice->running);
|
||||
}
|
||||
EXPORT_SYMBOL(mpp_srv_run);
|
||||
|
||||
void mpp_srv_done(struct mpp_service *pservice)
|
||||
{
|
||||
struct mpp_ctx *ctx = list_entry(pservice->running.next,
|
||||
struct mpp_ctx, status_link);
|
||||
|
||||
list_del_init(&ctx->session_link);
|
||||
list_add_tail(&ctx->session_link, &ctx->session->done);
|
||||
|
||||
list_del_init(&ctx->status_link);
|
||||
list_add_tail(&ctx->status_link, &pservice->done);
|
||||
|
||||
wake_up(&ctx->session->wait);
|
||||
}
|
||||
EXPORT_SYMBOL(mpp_srv_done);
|
||||
|
||||
struct mpp_ctx *mpp_srv_get_pending_ctx(struct mpp_service *pservice)
|
||||
{
|
||||
return list_entry(pservice->pending.next, struct mpp_ctx, status_link);
|
||||
}
|
||||
EXPORT_SYMBOL(mpp_srv_get_pending_ctx);
|
||||
|
||||
struct mpp_ctx *mpp_srv_get_current_ctx(struct mpp_service *pservice)
|
||||
{
|
||||
return list_entry(pservice->running.next, struct mpp_ctx, status_link);
|
||||
}
|
||||
EXPORT_SYMBOL(mpp_srv_get_current_ctx);
|
||||
|
||||
struct mpp_ctx *mpp_srv_get_last_running_ctx(struct mpp_service *pservice)
|
||||
{
|
||||
return list_entry(pservice->running.prev, struct mpp_ctx, status_link);
|
||||
}
|
||||
EXPORT_SYMBOL(mpp_srv_get_last_running_ctx);
|
||||
|
||||
struct mpp_session *mpp_srv_get_current_session(struct mpp_service *pservice)
|
||||
{
|
||||
struct mpp_ctx *ctx = list_entry(pservice->running.next,
|
||||
struct mpp_ctx, status_link);
|
||||
return ctx ? ctx->session : NULL;
|
||||
}
|
||||
EXPORT_SYMBOL(mpp_srv_get_current_session);
|
||||
|
||||
struct mpp_ctx *mpp_srv_get_done_ctx(struct mpp_session *session)
|
||||
{
|
||||
return list_entry(session->done.next, struct mpp_ctx, session_link);
|
||||
}
|
||||
EXPORT_SYMBOL(mpp_srv_get_done_ctx);
|
||||
|
||||
bool mpp_srv_pending_is_empty(struct mpp_service *pservice)
|
||||
{
|
||||
return !!list_empty(&pservice->pending);
|
||||
}
|
||||
EXPORT_SYMBOL(mpp_srv_pending_is_empty);
|
||||
|
||||
void mpp_srv_attach(struct mpp_service *pservice, struct list_head *elem)
|
||||
{
|
||||
INIT_LIST_HEAD(elem);
|
||||
list_add_tail(elem, &pservice->subdev_list);
|
||||
pservice->dev_cnt++;
|
||||
}
|
||||
EXPORT_SYMBOL(mpp_srv_attach);
|
||||
|
||||
void mpp_srv_detach(struct mpp_service *pservice, struct list_head *elem)
|
||||
{
|
||||
list_del_init(elem);
|
||||
pservice->dev_cnt--;
|
||||
}
|
||||
EXPORT_SYMBOL(mpp_srv_detach);
|
||||
|
||||
bool mpp_srv_is_running(struct mpp_service *pservice)
|
||||
{
|
||||
return !list_empty(&pservice->running);
|
||||
}
|
||||
EXPORT_SYMBOL(mpp_srv_is_running);
|
||||
|
||||
static void mpp_init_drvdata(struct mpp_service *pservice)
|
||||
{
|
||||
INIT_LIST_HEAD(&pservice->pending);
|
||||
mutex_init(&pservice->lock);
|
||||
|
||||
INIT_LIST_HEAD(&pservice->done);
|
||||
INIT_LIST_HEAD(&pservice->session);
|
||||
INIT_LIST_HEAD(&pservice->subdev_list);
|
||||
INIT_LIST_HEAD(&pservice->running);
|
||||
}
|
||||
|
||||
#if defined(CONFIG_OF)
|
||||
static const struct of_device_id mpp_service_dt_ids[] = {
|
||||
{ .compatible = "rockchip,mpp_service", },
|
||||
{ },
|
||||
};
|
||||
#endif
|
||||
|
||||
static int mpp_probe(struct platform_device *pdev)
|
||||
{
|
||||
int ret = 0;
|
||||
struct resource *res = NULL;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
struct mpp_service *pservice =
|
||||
devm_kzalloc(dev, sizeof(*pservice),
|
||||
GFP_KERNEL);
|
||||
|
||||
dev_info(dev, "%s enter\n", __func__);
|
||||
|
||||
pservice->dev = dev;
|
||||
|
||||
mpp_init_drvdata(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)) {
|
||||
dev_err(dev, "ioremap registers base failed\n");
|
||||
ret = PTR_ERR(pservice->reg_base);
|
||||
pservice->reg_base = 0;
|
||||
}
|
||||
} else {
|
||||
pservice->reg_base = 0;
|
||||
}
|
||||
|
||||
pservice->cls = class_create(THIS_MODULE, dev_name(dev));
|
||||
|
||||
if (IS_ERR(pservice->cls)) {
|
||||
ret = PTR_ERR(pservice->cls);
|
||||
dev_err(dev, "class_create err:%d\n", ret);
|
||||
return -1;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, pservice);
|
||||
dev_info(dev, "init success\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mpp_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct mpp_service *pservice = platform_get_drvdata(pdev);
|
||||
|
||||
class_destroy(pservice->cls);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver mpp_driver = {
|
||||
.probe = mpp_probe,
|
||||
.remove = mpp_remove,
|
||||
.driver = {
|
||||
.name = "mpp",
|
||||
.owner = THIS_MODULE,
|
||||
#if defined(CONFIG_OF)
|
||||
.of_match_table = of_match_ptr(mpp_service_dt_ids),
|
||||
#endif
|
||||
},
|
||||
};
|
||||
|
||||
static int __init mpp_service_init(void)
|
||||
{
|
||||
int ret = platform_driver_register(&mpp_driver);
|
||||
|
||||
if (ret) {
|
||||
mpp_err("Platform device register failed (%d).\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
subsys_initcall(mpp_service_init);
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
@@ -1,75 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2016 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 __ROCKCHIP_MPP_SERVICE_H
|
||||
#define __ROCKCHIP_MPP_SERVICE_H
|
||||
|
||||
#include <linux/ioctl.h>
|
||||
|
||||
#include "mpp_dev_common.h"
|
||||
|
||||
struct mpp_session {
|
||||
/* a linked list of data so we can access them for debugging */
|
||||
struct list_head list_session;
|
||||
/* the session related device private data */
|
||||
struct rockchip_mpp_dev *mpp;
|
||||
struct list_head done;
|
||||
wait_queue_head_t wait;
|
||||
pid_t pid;
|
||||
atomic_t task_running;
|
||||
};
|
||||
|
||||
enum mpp_srv_state {
|
||||
HW_RUNNING = BIT(1)
|
||||
};
|
||||
|
||||
struct mpp_service {
|
||||
/* service structure global lock */
|
||||
struct mutex lock;
|
||||
struct list_head pending;
|
||||
struct list_head done;
|
||||
struct list_head running;
|
||||
/* link to list_session in struct mpp_session */
|
||||
struct list_head session;
|
||||
|
||||
struct device *dev;
|
||||
|
||||
void __iomem *reg_base;
|
||||
|
||||
struct class *cls;
|
||||
|
||||
u32 dev_cnt;
|
||||
struct list_head subdev_list;
|
||||
};
|
||||
|
||||
void mpp_srv_lock(struct mpp_service *pservice);
|
||||
void mpp_srv_unlock(struct mpp_service *pservice);
|
||||
void mpp_srv_pending_locked(struct mpp_service *pservice, struct mpp_ctx *ctx);
|
||||
void mpp_srv_run(struct mpp_service *pservice);
|
||||
void mpp_srv_done(struct mpp_service *pservice);
|
||||
void mpp_srv_attach(struct mpp_service *pservice, struct list_head *elem);
|
||||
void mpp_srv_detach(struct mpp_service *pservice, struct list_head *elem);
|
||||
struct mpp_ctx *mpp_srv_get_pending_ctx(struct mpp_service *pservice);
|
||||
struct mpp_ctx *mpp_srv_get_current_ctx(struct mpp_service *pservice);
|
||||
struct mpp_ctx *mpp_srv_get_last_running_ctx(struct mpp_service *pservice);
|
||||
struct mpp_session *mpp_srv_get_current_session(struct mpp_service *pservice);
|
||||
bool mpp_srv_pending_is_empty(struct mpp_service *pservice);
|
||||
struct mpp_ctx *mpp_srv_get_done_ctx(struct mpp_session *session);
|
||||
bool mpp_srv_is_power_on(struct mpp_service *pservice);
|
||||
bool mpp_srv_is_running(struct mpp_service *pservice);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,936 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Fuzhou Rockchip Electronics Co., Ltd
|
||||
* author: Jung Zhao jung.zhao@rock-chips.com
|
||||
* Randy Li, randy.li@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.
|
||||
*
|
||||
*/
|
||||
#include <linux/dma-iommu.h>
|
||||
|
||||
#include <linux/dma-buf.h>
|
||||
#include <drm/drmP.h>
|
||||
#include <drm/drm_atomic.h>
|
||||
#include <drm/drm_crtc_helper.h>
|
||||
#include <drm/drm_fb_helper.h>
|
||||
#include <drm/drm_sync_helper.h>
|
||||
#include <drm/rockchip_drm.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/rockchip-iovmm.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/memblock.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_graph.h>
|
||||
#include <linux/component.h>
|
||||
#include <linux/fence.h>
|
||||
#include <linux/console.h>
|
||||
#include <linux/kref.h>
|
||||
#include <linux/fdtable.h>
|
||||
#include <linux/ktime.h>
|
||||
#include <linux/iova.h>
|
||||
|
||||
#include "vpu_iommu_ops.h"
|
||||
|
||||
#define VPU_DRM_BUFFER_ALLOC 0x00000001
|
||||
|
||||
struct vpu_drm_buffer {
|
||||
struct list_head list;
|
||||
struct dma_buf *dma_buf;
|
||||
union {
|
||||
unsigned long iova;
|
||||
unsigned long phys;
|
||||
};
|
||||
void *cpu_addr;
|
||||
unsigned long size;
|
||||
int index;
|
||||
struct dma_buf_attachment *attach;
|
||||
struct sg_table *sgt;
|
||||
struct sg_table *copy_sgt;
|
||||
struct page **pages;
|
||||
struct kref ref;
|
||||
struct vpu_iommu_session_info *session_info;
|
||||
ktime_t last_used;
|
||||
int flags;
|
||||
};
|
||||
|
||||
struct vpu_iommu_drm_info {
|
||||
struct iommu_domain *domain;
|
||||
bool attached;
|
||||
};
|
||||
|
||||
static struct vpu_drm_buffer *
|
||||
vpu_drm_get_buffer_no_lock(struct vpu_iommu_session_info *session_info,
|
||||
int idx)
|
||||
{
|
||||
struct vpu_drm_buffer *drm_buffer = NULL, *n;
|
||||
|
||||
list_for_each_entry_safe(drm_buffer, n, &session_info->buffer_list,
|
||||
list) {
|
||||
if (drm_buffer->index == idx) {
|
||||
drm_buffer->last_used = ktime_get();
|
||||
return drm_buffer;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct vpu_drm_buffer*
|
||||
vpu_drm_get_buffer_fd_no_lock(struct vpu_iommu_session_info *session_info,
|
||||
int fd)
|
||||
{
|
||||
struct vpu_drm_buffer *drm_buffer = NULL, *n;
|
||||
struct dma_buf *dma_buf = NULL;
|
||||
|
||||
dma_buf = dma_buf_get(fd);
|
||||
|
||||
list_for_each_entry_safe(drm_buffer, n, &session_info->buffer_list,
|
||||
list) {
|
||||
if (drm_buffer->dma_buf == dma_buf) {
|
||||
drm_buffer->last_used = ktime_get();
|
||||
dma_buf_put(dma_buf);
|
||||
return drm_buffer;
|
||||
}
|
||||
}
|
||||
|
||||
dma_buf_put(dma_buf);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void vpu_drm_detach(struct vpu_iommu_info *iommu_info)
|
||||
{
|
||||
struct vpu_iommu_drm_info *drm_info = iommu_info->private;
|
||||
struct device *dev = iommu_info->dev;
|
||||
struct iommu_domain *domain = drm_info->domain;
|
||||
|
||||
mutex_lock(&iommu_info->iommu_mutex);
|
||||
|
||||
if (!drm_info->attached) {
|
||||
mutex_unlock(&iommu_info->iommu_mutex);
|
||||
return;
|
||||
}
|
||||
|
||||
iommu_detach_device(domain, dev);
|
||||
drm_info->attached = false;
|
||||
|
||||
mutex_unlock(&iommu_info->iommu_mutex);
|
||||
}
|
||||
|
||||
static int vpu_drm_attach_unlock(struct vpu_iommu_info *iommu_info)
|
||||
{
|
||||
struct vpu_iommu_drm_info *drm_info = iommu_info->private;
|
||||
struct device *dev = iommu_info->dev;
|
||||
struct iommu_domain *domain = drm_info->domain;
|
||||
int ret = 0;
|
||||
|
||||
ret = dma_set_coherent_mask(dev, DMA_BIT_MASK(32));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
dma_set_max_seg_size(dev, DMA_BIT_MASK(32));
|
||||
ret = iommu_attach_device(domain, dev);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to attach iommu device\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int vpu_drm_attach(struct vpu_iommu_info *iommu_info)
|
||||
{
|
||||
struct vpu_iommu_drm_info *drm_info = iommu_info->private;
|
||||
int ret;
|
||||
|
||||
mutex_lock(&iommu_info->iommu_mutex);
|
||||
|
||||
if (drm_info->attached) {
|
||||
mutex_unlock(&iommu_info->iommu_mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = vpu_drm_attach_unlock(iommu_info);
|
||||
if (ret) {
|
||||
mutex_unlock(&iommu_info->iommu_mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
drm_info->attached = true;
|
||||
|
||||
mutex_unlock(&iommu_info->iommu_mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void *vpu_drm_sgt_map_kernel(struct vpu_drm_buffer *drm_buffer)
|
||||
{
|
||||
struct vpu_iommu_session_info *session_info =
|
||||
drm_buffer->session_info;
|
||||
struct device *dev = session_info->dev;
|
||||
struct scatterlist *sgl, *sg;
|
||||
int nr_pages = PAGE_ALIGN(drm_buffer->size) >> PAGE_SHIFT;
|
||||
int i = 0, j = 0, k = 0;
|
||||
struct page *page;
|
||||
|
||||
drm_buffer->pages = kmalloc_array(nr_pages, sizeof(*drm_buffer->pages),
|
||||
GFP_KERNEL);
|
||||
if (!(drm_buffer->pages)) {
|
||||
dev_err(dev, "drm map can not alloc pages\n");
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
sgl = drm_buffer->copy_sgt->sgl;
|
||||
|
||||
for_each_sg(sgl, sg, drm_buffer->copy_sgt->nents, i) {
|
||||
page = sg_page(sg);
|
||||
for (j = 0; j < sg->length / PAGE_SIZE; j++)
|
||||
drm_buffer->pages[k++] = page++;
|
||||
}
|
||||
|
||||
return vmap(drm_buffer->pages, nr_pages, VM_MAP,
|
||||
pgprot_noncached(PAGE_KERNEL));
|
||||
}
|
||||
|
||||
static void vpu_drm_sgt_unmap_kernel(struct vpu_drm_buffer *drm_buffer)
|
||||
{
|
||||
vunmap(drm_buffer->cpu_addr);
|
||||
kfree(drm_buffer->pages);
|
||||
}
|
||||
|
||||
static int vpu_finalise_sg(struct scatterlist *sg,
|
||||
int nents,
|
||||
dma_addr_t dma_addr)
|
||||
{
|
||||
struct scatterlist *s, *cur = sg;
|
||||
unsigned long seg_mask = DMA_BIT_MASK(32);
|
||||
unsigned int cur_len = 0, max_len = DMA_BIT_MASK(32);
|
||||
int i, count = 0;
|
||||
|
||||
for_each_sg(sg, s, nents, i) {
|
||||
/* Restore this segment's original unaligned fields first */
|
||||
unsigned int s_iova_off = sg_dma_address(s);
|
||||
unsigned int s_length = sg_dma_len(s);
|
||||
unsigned int s_iova_len = s->length;
|
||||
|
||||
s->offset += s_iova_off;
|
||||
s->length = s_length;
|
||||
sg_dma_address(s) = DMA_ERROR_CODE;
|
||||
sg_dma_len(s) = 0;
|
||||
|
||||
/*
|
||||
* Now fill in the real DMA data. If...
|
||||
* - there is a valid output segment to append to
|
||||
* - and this segment starts on an IOVA page boundary
|
||||
* - but doesn't fall at a segment boundary
|
||||
* - and wouldn't make the resulting output segment too long
|
||||
*/
|
||||
if (cur_len && !s_iova_off && (dma_addr & seg_mask) &&
|
||||
(cur_len + s_length <= max_len)) {
|
||||
/* ...then concatenate it with the previous one */
|
||||
cur_len += s_length;
|
||||
} else {
|
||||
/* Otherwise start the next output segment */
|
||||
if (i > 0)
|
||||
cur = sg_next(cur);
|
||||
cur_len = s_length;
|
||||
count++;
|
||||
|
||||
sg_dma_address(cur) = dma_addr + s_iova_off;
|
||||
}
|
||||
|
||||
sg_dma_len(cur) = cur_len;
|
||||
dma_addr += s_iova_len;
|
||||
|
||||
if (s_length + s_iova_off < s_iova_len)
|
||||
cur_len = 0;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
static void vpu_invalidate_sg(struct scatterlist *sg, int nents)
|
||||
{
|
||||
struct scatterlist *s;
|
||||
int i;
|
||||
|
||||
for_each_sg(sg, s, nents, i) {
|
||||
if (sg_dma_address(s) != DMA_ERROR_CODE)
|
||||
s->offset += sg_dma_address(s);
|
||||
if (sg_dma_len(s))
|
||||
s->length = sg_dma_len(s);
|
||||
sg_dma_address(s) = DMA_ERROR_CODE;
|
||||
sg_dma_len(s) = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static dma_addr_t vpu_dma_map_sg(struct iommu_domain *domain,
|
||||
struct scatterlist *sg,
|
||||
int nents, int prot)
|
||||
{
|
||||
struct iova_domain *iovad = domain->iova_cookie;
|
||||
struct iova *iova;
|
||||
struct scatterlist *s, *prev = NULL;
|
||||
dma_addr_t dma_addr;
|
||||
size_t iova_len = 0;
|
||||
unsigned long mask = DMA_BIT_MASK(32);
|
||||
unsigned long shift = iova_shift(iovad);
|
||||
int i;
|
||||
|
||||
/*
|
||||
* Work out how much IOVA space we need, and align the segments to
|
||||
* IOVA granules for the IOMMU driver to handle. With some clever
|
||||
* trickery we can modify the list in-place, but reversibly, by
|
||||
* stashing the unaligned parts in the as-yet-unused DMA fields.
|
||||
*/
|
||||
for_each_sg(sg, s, nents, i) {
|
||||
size_t s_iova_off = iova_offset(iovad, s->offset);
|
||||
size_t s_length = s->length;
|
||||
size_t pad_len = (mask - iova_len + 1) & mask;
|
||||
|
||||
sg_dma_address(s) = s_iova_off;
|
||||
sg_dma_len(s) = s_length;
|
||||
s->offset -= s_iova_off;
|
||||
s_length = iova_align(iovad, s_length + s_iova_off);
|
||||
s->length = s_length;
|
||||
|
||||
/*
|
||||
* Due to the alignment of our single IOVA allocation, we can
|
||||
* depend on these assumptions about the segment boundary mask:
|
||||
* - If mask size >= IOVA size, then the IOVA range cannot
|
||||
* possibly fall across a boundary, so we don't care.
|
||||
* - If mask size < IOVA size, then the IOVA range must start
|
||||
* exactly on a boundary, therefore we can lay things out
|
||||
* based purely on segment lengths without needing to know
|
||||
* the actual addresses beforehand.
|
||||
* - The mask must be a power of 2, so pad_len == 0 if
|
||||
* iova_len == 0, thus we cannot dereference prev the first
|
||||
* time through here (i.e. before it has a meaningful value).
|
||||
*/
|
||||
if (pad_len && pad_len < s_length - 1) {
|
||||
prev->length += pad_len;
|
||||
iova_len += pad_len;
|
||||
}
|
||||
|
||||
iova_len += s_length;
|
||||
prev = s;
|
||||
}
|
||||
|
||||
iova = alloc_iova(iovad, iova_align(iovad, iova_len) >> shift,
|
||||
mask >> shift, true);
|
||||
if (!iova)
|
||||
goto out_restore_sg;
|
||||
|
||||
/*
|
||||
* We'll leave any physical concatenation to the IOMMU driver's
|
||||
* implementation - it knows better than we do.
|
||||
*/
|
||||
dma_addr = iova_dma_addr(iovad, iova);
|
||||
if (iommu_map_sg(domain, dma_addr, sg, nents, prot) < iova_len)
|
||||
goto out_free_iova;
|
||||
|
||||
return vpu_finalise_sg(sg, nents, dma_addr);
|
||||
|
||||
out_free_iova:
|
||||
__free_iova(iovad, iova);
|
||||
out_restore_sg:
|
||||
vpu_invalidate_sg(sg, nents);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void vpu_dma_unmap_sg(struct iommu_domain *domain,
|
||||
dma_addr_t dma_addr)
|
||||
{
|
||||
struct iova_domain *iovad = domain->iova_cookie;
|
||||
unsigned long shift = iova_shift(iovad);
|
||||
unsigned long pfn = dma_addr >> shift;
|
||||
struct iova *iova = find_iova(iovad, pfn);
|
||||
size_t size;
|
||||
|
||||
if (WARN_ON(!iova))
|
||||
return;
|
||||
|
||||
size = iova_size(iova) << shift;
|
||||
size -= iommu_unmap(domain, pfn << shift, size);
|
||||
/* ...and if we can't, then something is horribly, horribly wrong */
|
||||
WARN_ON(size > 0);
|
||||
__free_iova(iovad, iova);
|
||||
}
|
||||
|
||||
static void vpu_drm_clear_map(struct kref *ref)
|
||||
{
|
||||
struct vpu_drm_buffer *drm_buffer =
|
||||
container_of(ref,
|
||||
struct vpu_drm_buffer,
|
||||
ref);
|
||||
struct vpu_iommu_session_info *session_info =
|
||||
drm_buffer->session_info;
|
||||
struct vpu_iommu_info *iommu_info = session_info->iommu_info;
|
||||
struct vpu_iommu_drm_info *drm_info = iommu_info->private;
|
||||
struct sg_table *table;
|
||||
struct scatterlist *sg;
|
||||
struct page *page;
|
||||
int i;
|
||||
|
||||
mutex_lock(&iommu_info->iommu_mutex);
|
||||
drm_info = session_info->iommu_info->private;
|
||||
|
||||
if (drm_buffer->cpu_addr) {
|
||||
vpu_drm_sgt_unmap_kernel(drm_buffer);
|
||||
drm_buffer->cpu_addr = NULL;
|
||||
}
|
||||
|
||||
vpu_dma_unmap_sg(drm_info->domain, drm_buffer->iova);
|
||||
if (drm_buffer->flags & VPU_DRM_BUFFER_ALLOC) {
|
||||
table = drm_buffer->copy_sgt;
|
||||
for_each_sg(table->sgl, sg, table->nents, i) {
|
||||
page = sg_page(sg);
|
||||
__free_pages(page, compound_order(page));
|
||||
}
|
||||
}
|
||||
sg_free_table(drm_buffer->copy_sgt);
|
||||
kfree(drm_buffer->copy_sgt);
|
||||
|
||||
if (drm_buffer->attach) {
|
||||
dma_buf_unmap_attachment(drm_buffer->attach, drm_buffer->sgt,
|
||||
DMA_BIDIRECTIONAL);
|
||||
dma_buf_detach(drm_buffer->dma_buf, drm_buffer->attach);
|
||||
dma_buf_put(drm_buffer->dma_buf);
|
||||
drm_buffer->attach = NULL;
|
||||
}
|
||||
|
||||
mutex_unlock(&iommu_info->iommu_mutex);
|
||||
}
|
||||
|
||||
static void vcdoec_drm_dump_info(struct vpu_iommu_session_info *session_info)
|
||||
{
|
||||
struct vpu_drm_buffer *drm_buffer = NULL, *n;
|
||||
|
||||
vpu_iommu_debug(session_info->debug_level, DEBUG_IOMMU_OPS_DUMP,
|
||||
"still there are below buffers stored in list\n");
|
||||
list_for_each_entry_safe(drm_buffer, n, &session_info->buffer_list,
|
||||
list) {
|
||||
vpu_iommu_debug(session_info->debug_level, DEBUG_IOMMU_OPS_DUMP,
|
||||
"index %d drm_buffer dma_buf %p cpu_addr %p\n",
|
||||
drm_buffer->index,
|
||||
drm_buffer->dma_buf, drm_buffer->cpu_addr);
|
||||
}
|
||||
}
|
||||
|
||||
static int vpu_drm_free(struct vpu_iommu_session_info *session_info,
|
||||
int idx)
|
||||
{
|
||||
struct device *dev = session_info->dev;
|
||||
/* please double-check all maps have been release */
|
||||
struct vpu_drm_buffer *drm_buffer;
|
||||
|
||||
mutex_lock(&session_info->list_mutex);
|
||||
drm_buffer = vpu_drm_get_buffer_no_lock(session_info, idx);
|
||||
|
||||
if (!drm_buffer) {
|
||||
dev_err(dev, "can not find %d buffer in list\n", idx);
|
||||
mutex_unlock(&session_info->list_mutex);
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (atomic_read(&drm_buffer->ref.refcount) == 0) {
|
||||
if (drm_buffer->dma_buf)
|
||||
dma_buf_put(drm_buffer->dma_buf);
|
||||
list_del_init(&drm_buffer->list);
|
||||
kfree(drm_buffer);
|
||||
session_info->buffer_nums--;
|
||||
vpu_iommu_debug(session_info->debug_level, DEBUG_IOMMU_NORMAL,
|
||||
"buffer nums %d\n", session_info->buffer_nums);
|
||||
}
|
||||
mutex_unlock(&session_info->list_mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
vpu_drm_unmap_iommu(struct vpu_iommu_session_info *session_info,
|
||||
int idx)
|
||||
{
|
||||
struct device *dev = session_info->dev;
|
||||
struct vpu_drm_buffer *drm_buffer;
|
||||
|
||||
/* Force to flush iommu table */
|
||||
if (of_machine_is_compatible("rockchip,rk3288"))
|
||||
rockchip_iovmm_invalidate_tlb(session_info->mmu_dev);
|
||||
|
||||
mutex_lock(&session_info->list_mutex);
|
||||
drm_buffer = vpu_drm_get_buffer_no_lock(session_info, idx);
|
||||
mutex_unlock(&session_info->list_mutex);
|
||||
|
||||
if (!drm_buffer) {
|
||||
dev_err(dev, "can not find %d buffer in list\n", idx);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
kref_put(&drm_buffer->ref, vpu_drm_clear_map);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vpu_drm_map_iommu(struct vpu_iommu_session_info *session_info,
|
||||
int idx,
|
||||
unsigned long *iova,
|
||||
unsigned long *size)
|
||||
{
|
||||
struct device *dev = session_info->dev;
|
||||
struct vpu_drm_buffer *drm_buffer;
|
||||
|
||||
/* Force to flush iommu table */
|
||||
if (of_machine_is_compatible("rockchip,rk3288"))
|
||||
rockchip_iovmm_invalidate_tlb(session_info->mmu_dev);
|
||||
|
||||
mutex_lock(&session_info->list_mutex);
|
||||
drm_buffer = vpu_drm_get_buffer_no_lock(session_info, idx);
|
||||
mutex_unlock(&session_info->list_mutex);
|
||||
|
||||
if (!drm_buffer) {
|
||||
dev_err(dev, "can not find %d buffer in list\n", idx);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
kref_get(&drm_buffer->ref);
|
||||
if (iova)
|
||||
*iova = drm_buffer->iova;
|
||||
if (size)
|
||||
*size = drm_buffer->size;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
vpu_drm_unmap_kernel(struct vpu_iommu_session_info *session_info, int idx)
|
||||
{
|
||||
struct device *dev = session_info->dev;
|
||||
struct vpu_drm_buffer *drm_buffer;
|
||||
|
||||
mutex_lock(&session_info->list_mutex);
|
||||
drm_buffer = vpu_drm_get_buffer_no_lock(session_info, idx);
|
||||
mutex_unlock(&session_info->list_mutex);
|
||||
|
||||
if (!drm_buffer) {
|
||||
dev_err(dev, "can not find %d buffer in list\n", idx);
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (drm_buffer->cpu_addr) {
|
||||
vpu_drm_sgt_unmap_kernel(drm_buffer);
|
||||
drm_buffer->cpu_addr = NULL;
|
||||
}
|
||||
|
||||
kref_put(&drm_buffer->ref, vpu_drm_clear_map);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
vpu_drm_free_fd(struct vpu_iommu_session_info *session_info, int fd)
|
||||
{
|
||||
struct device *dev = session_info->dev;
|
||||
/* please double-check all maps have been release */
|
||||
struct vpu_drm_buffer *drm_buffer = NULL;
|
||||
|
||||
mutex_lock(&session_info->list_mutex);
|
||||
drm_buffer = vpu_drm_get_buffer_fd_no_lock(session_info, fd);
|
||||
|
||||
if (!drm_buffer) {
|
||||
dev_err(dev, "can not find %d buffer in list\n", fd);
|
||||
mutex_unlock(&session_info->list_mutex);
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
mutex_unlock(&session_info->list_mutex);
|
||||
|
||||
vpu_drm_unmap_iommu(session_info, drm_buffer->index);
|
||||
|
||||
mutex_lock(&session_info->list_mutex);
|
||||
if (atomic_read(&drm_buffer->ref.refcount) == 0) {
|
||||
if (drm_buffer->dma_buf)
|
||||
dma_buf_put(drm_buffer->dma_buf);
|
||||
list_del_init(&drm_buffer->list);
|
||||
kfree(drm_buffer);
|
||||
session_info->buffer_nums--;
|
||||
vpu_iommu_debug(session_info->debug_level, DEBUG_IOMMU_NORMAL,
|
||||
"buffer nums %d\n", session_info->buffer_nums);
|
||||
}
|
||||
mutex_unlock(&session_info->list_mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
vpu_drm_clear_session(struct vpu_iommu_session_info *session_info)
|
||||
{
|
||||
struct vpu_drm_buffer *drm_buffer = NULL, *n;
|
||||
|
||||
list_for_each_entry_safe(drm_buffer, n, &session_info->buffer_list,
|
||||
list) {
|
||||
kref_put(&drm_buffer->ref, vpu_drm_clear_map);
|
||||
vpu_drm_free(session_info, drm_buffer->index);
|
||||
}
|
||||
}
|
||||
|
||||
static void*
|
||||
vpu_drm_map_kernel(struct vpu_iommu_session_info *session_info, int idx)
|
||||
{
|
||||
struct device *dev = session_info->dev;
|
||||
struct vpu_drm_buffer *drm_buffer;
|
||||
|
||||
mutex_lock(&session_info->list_mutex);
|
||||
drm_buffer = vpu_drm_get_buffer_no_lock(session_info, idx);
|
||||
mutex_unlock(&session_info->list_mutex);
|
||||
|
||||
if (!drm_buffer) {
|
||||
dev_err(dev, "can not find %d buffer in list\n", idx);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!drm_buffer->cpu_addr)
|
||||
drm_buffer->cpu_addr =
|
||||
vpu_drm_sgt_map_kernel(drm_buffer);
|
||||
|
||||
kref_get(&drm_buffer->ref);
|
||||
|
||||
return drm_buffer->cpu_addr;
|
||||
}
|
||||
|
||||
static void vpu_drm_remove_extra_buffer_no_lock(struct vpu_iommu_session_info *session_info)
|
||||
{
|
||||
struct vpu_drm_buffer *oldest_buffer = NULL, *loop_buffer = NULL;
|
||||
struct vpu_drm_buffer *n;
|
||||
ktime_t oldest_time = ktime_set(0, 0);
|
||||
|
||||
if (session_info->buffer_nums > BUFFER_LIST_MAX_NUMS) {
|
||||
list_for_each_entry_safe(loop_buffer, n,
|
||||
&session_info->buffer_list, list) {
|
||||
if (loop_buffer->flags & VPU_DRM_BUFFER_ALLOC)
|
||||
continue;
|
||||
|
||||
if (ktime_to_ns(oldest_time) == 0 ||
|
||||
ktime_after(oldest_time,
|
||||
loop_buffer->last_used)) {
|
||||
oldest_time = loop_buffer->last_used;
|
||||
oldest_buffer = loop_buffer;
|
||||
}
|
||||
}
|
||||
kref_put(&oldest_buffer->ref, vpu_drm_clear_map);
|
||||
dma_buf_put(oldest_buffer->dma_buf);
|
||||
list_del_init(&oldest_buffer->list);
|
||||
kfree(oldest_buffer);
|
||||
session_info->buffer_nums--;
|
||||
}
|
||||
}
|
||||
|
||||
static int vpu_drm_import(struct vpu_iommu_session_info *session_info,
|
||||
int fd)
|
||||
{
|
||||
struct vpu_drm_buffer *drm_buffer = NULL, *n;
|
||||
struct vpu_iommu_info *iommu_info = session_info->iommu_info;
|
||||
struct vpu_iommu_drm_info *drm_info = iommu_info->private;
|
||||
struct device *dev = session_info->dev;
|
||||
struct dma_buf_attachment *attach;
|
||||
struct sg_table *sgt;
|
||||
struct dma_buf *dma_buf;
|
||||
struct scatterlist *sg, *s;
|
||||
int i;
|
||||
int ret = 0;
|
||||
|
||||
dma_buf = dma_buf_get(fd);
|
||||
if (IS_ERR(dma_buf)) {
|
||||
ret = PTR_ERR(dma_buf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
list_for_each_entry_safe(drm_buffer, n,
|
||||
&session_info->buffer_list, list) {
|
||||
if (drm_buffer->dma_buf == dma_buf) {
|
||||
dma_buf_put(dma_buf);
|
||||
drm_buffer->last_used = ktime_get();
|
||||
return drm_buffer->index;
|
||||
}
|
||||
}
|
||||
|
||||
drm_buffer = kzalloc(sizeof(*drm_buffer), GFP_KERNEL);
|
||||
if (!drm_buffer) {
|
||||
ret = -ENOMEM;
|
||||
return ret;
|
||||
}
|
||||
|
||||
drm_buffer->dma_buf = dma_buf;
|
||||
drm_buffer->session_info = session_info;
|
||||
drm_buffer->last_used = ktime_get();
|
||||
|
||||
kref_init(&drm_buffer->ref);
|
||||
|
||||
mutex_lock(&iommu_info->iommu_mutex);
|
||||
drm_info = session_info->iommu_info->private;
|
||||
|
||||
attach = dma_buf_attach(drm_buffer->dma_buf, dev);
|
||||
if (IS_ERR(attach)) {
|
||||
ret = PTR_ERR(attach);
|
||||
goto fail_out;
|
||||
}
|
||||
|
||||
get_dma_buf(drm_buffer->dma_buf);
|
||||
|
||||
sgt = dma_buf_map_attachment(attach, DMA_BIDIRECTIONAL);
|
||||
if (IS_ERR(sgt)) {
|
||||
ret = PTR_ERR(sgt);
|
||||
goto fail_detach;
|
||||
}
|
||||
|
||||
/*
|
||||
* Since we call dma_buf_map_attachment outside attach/detach, this
|
||||
* will cause incorrectly map. we have to re-build map table native
|
||||
* and for avoiding destroy their origin map table, we need use a
|
||||
* copy one sg_table.
|
||||
*/
|
||||
drm_buffer->copy_sgt = kmalloc(sizeof(*drm_buffer->copy_sgt),
|
||||
GFP_KERNEL);
|
||||
if (!drm_buffer->copy_sgt) {
|
||||
ret = -ENOMEM;
|
||||
goto fail_detach;
|
||||
}
|
||||
|
||||
ret = sg_alloc_table(drm_buffer->copy_sgt, sgt->nents, GFP_KERNEL);
|
||||
s = drm_buffer->copy_sgt->sgl;
|
||||
for_each_sg(sgt->sgl, sg, sgt->nents, i) {
|
||||
sg_set_page(s, sg_page(sg),
|
||||
PAGE_SIZE << compound_order(sg_page(sg)), 0);
|
||||
sg_dma_address(s) = page_to_phys(sg_page(sg));
|
||||
s->offset = sg->offset;
|
||||
s->length = sg->length;
|
||||
s = sg_next(s);
|
||||
}
|
||||
|
||||
vpu_dma_map_sg(drm_info->domain, drm_buffer->copy_sgt->sgl,
|
||||
drm_buffer->copy_sgt->nents,
|
||||
IOMMU_READ | IOMMU_WRITE);
|
||||
drm_buffer->iova = sg_dma_address(drm_buffer->copy_sgt->sgl);
|
||||
drm_buffer->size = drm_buffer->dma_buf->size;
|
||||
|
||||
drm_buffer->attach = attach;
|
||||
drm_buffer->sgt = sgt;
|
||||
|
||||
mutex_unlock(&iommu_info->iommu_mutex);
|
||||
|
||||
INIT_LIST_HEAD(&drm_buffer->list);
|
||||
mutex_lock(&session_info->list_mutex);
|
||||
session_info->buffer_nums++;
|
||||
vpu_iommu_debug(session_info->debug_level, DEBUG_IOMMU_NORMAL,
|
||||
"buffer nums %d\n", session_info->buffer_nums);
|
||||
vpu_drm_remove_extra_buffer_no_lock(session_info);
|
||||
drm_buffer->index = session_info->max_idx;
|
||||
list_add_tail(&drm_buffer->list, &session_info->buffer_list);
|
||||
session_info->max_idx++;
|
||||
if ((session_info->max_idx & 0xfffffff) == 0)
|
||||
session_info->max_idx = 0;
|
||||
mutex_unlock(&session_info->list_mutex);
|
||||
|
||||
return drm_buffer->index;
|
||||
|
||||
fail_detach:
|
||||
dev_err(dev, "dmabuf map attach failed\n");
|
||||
dma_buf_detach(drm_buffer->dma_buf, attach);
|
||||
dma_buf_put(drm_buffer->dma_buf);
|
||||
fail_out:
|
||||
kfree(drm_buffer);
|
||||
mutex_unlock(&iommu_info->iommu_mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int vpu_drm_alloc(struct vpu_iommu_session_info *session_info,
|
||||
unsigned long size,
|
||||
unsigned long align)
|
||||
{
|
||||
struct sg_table *table;
|
||||
struct scatterlist *sg;
|
||||
struct list_head pages;
|
||||
struct page *page, *tmp_page;
|
||||
long size_remaining = PAGE_ALIGN(size);
|
||||
struct vpu_drm_buffer *drm_buffer;
|
||||
struct vpu_iommu_info *iommu_info = session_info->iommu_info;
|
||||
struct vpu_iommu_drm_info *drm_info = iommu_info->private;
|
||||
int i;
|
||||
|
||||
if (align > PAGE_SIZE)
|
||||
return -EINVAL;
|
||||
|
||||
if (size / PAGE_SIZE > totalram_pages / 2)
|
||||
return -ENOMEM;
|
||||
|
||||
drm_buffer = kzalloc(sizeof(*drm_buffer), GFP_KERNEL);
|
||||
if (!drm_buffer)
|
||||
return -ENOMEM;
|
||||
|
||||
drm_buffer->session_info = session_info;
|
||||
drm_buffer->last_used = ktime_set(0, 0);
|
||||
|
||||
kref_init(&drm_buffer->ref);
|
||||
|
||||
INIT_LIST_HEAD(&pages);
|
||||
|
||||
i = 0;
|
||||
while (size_remaining > 0) {
|
||||
gfp_t gfp_flags = (GFP_HIGHUSER | __GFP_ZERO | __GFP_NOWARN |
|
||||
__GFP_NORETRY) & ~__GFP_DIRECT_RECLAIM;
|
||||
page = alloc_pages(gfp_flags | __GFP_COMP, 8);
|
||||
if (!page)
|
||||
goto free_pages;
|
||||
|
||||
size_remaining -= PAGE_SIZE << compound_order(page);
|
||||
list_add_tail(&page->lru, &pages);
|
||||
i++;
|
||||
}
|
||||
|
||||
table = kmalloc(sizeof(*table), GFP_KERNEL);
|
||||
if (!table)
|
||||
goto free_pages;
|
||||
|
||||
if (sg_alloc_table(table, i, GFP_KERNEL))
|
||||
goto free_table;
|
||||
|
||||
sg = table->sgl;
|
||||
list_for_each_entry_safe(page, tmp_page, &pages, lru) {
|
||||
sg_set_page(sg, page, PAGE_SIZE << compound_order(page), 0);
|
||||
sg = sg_next(sg);
|
||||
list_del(&page->lru);
|
||||
}
|
||||
|
||||
mutex_lock(&iommu_info->iommu_mutex);
|
||||
drm_info = session_info->iommu_info->private;
|
||||
drm_buffer->copy_sgt = table;
|
||||
|
||||
vpu_dma_map_sg(drm_info->domain, drm_buffer->copy_sgt->sgl,
|
||||
drm_buffer->copy_sgt->nents,
|
||||
IOMMU_READ | IOMMU_WRITE);
|
||||
drm_buffer->iova = sg_dma_address(drm_buffer->copy_sgt->sgl);
|
||||
drm_buffer->size = size;
|
||||
drm_buffer->flags = VPU_DRM_BUFFER_ALLOC;
|
||||
|
||||
mutex_unlock(&iommu_info->iommu_mutex);
|
||||
|
||||
INIT_LIST_HEAD(&drm_buffer->list);
|
||||
mutex_lock(&session_info->list_mutex);
|
||||
session_info->buffer_nums++;
|
||||
vpu_iommu_debug(session_info->debug_level, DEBUG_IOMMU_NORMAL,
|
||||
"buffer nums %d\n", session_info->buffer_nums);
|
||||
vpu_drm_remove_extra_buffer_no_lock(session_info);
|
||||
drm_buffer->index = session_info->max_idx;
|
||||
list_add_tail(&drm_buffer->list, &session_info->buffer_list);
|
||||
session_info->max_idx++;
|
||||
if ((session_info->max_idx & 0xfffffff) == 0)
|
||||
session_info->max_idx = 0;
|
||||
mutex_unlock(&session_info->list_mutex);
|
||||
|
||||
return drm_buffer->index;
|
||||
|
||||
free_table:
|
||||
kfree(table);
|
||||
free_pages:
|
||||
list_for_each_entry_safe(page, tmp_page, &pages, lru)
|
||||
__free_pages(page, 8);
|
||||
|
||||
kfree(drm_buffer);
|
||||
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
static int vpu_drm_create(struct vpu_iommu_info *iommu_info)
|
||||
{
|
||||
struct vpu_iommu_drm_info *drm_info;
|
||||
struct iommu_group *group;
|
||||
int ret;
|
||||
|
||||
iommu_info->private = kzalloc(sizeof(*drm_info),
|
||||
GFP_KERNEL);
|
||||
drm_info = iommu_info->private;
|
||||
if (!drm_info)
|
||||
return -ENOMEM;
|
||||
|
||||
drm_info->domain = iommu_domain_alloc(&platform_bus_type);
|
||||
drm_info->attached = false;
|
||||
if (!drm_info->domain)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = iommu_get_dma_cookie(drm_info->domain);
|
||||
if (ret)
|
||||
goto err_free_domain;
|
||||
|
||||
group = iommu_group_get(iommu_info->dev);
|
||||
if (!group) {
|
||||
group = iommu_group_alloc();
|
||||
if (IS_ERR(group)) {
|
||||
dev_err(iommu_info->dev,
|
||||
"Failed to allocate IOMMU group\n");
|
||||
goto err_put_cookie;
|
||||
}
|
||||
ret = iommu_group_add_device(group, iommu_info->dev);
|
||||
if (ret) {
|
||||
dev_err(iommu_info->dev,
|
||||
"failed to add device to IOMMU group\n");
|
||||
goto err_put_cookie;
|
||||
}
|
||||
}
|
||||
iommu_dma_init_domain(drm_info->domain, 0x10000000, SZ_2G);
|
||||
iommu_group_put(group);
|
||||
|
||||
return 0;
|
||||
|
||||
err_put_cookie:
|
||||
iommu_put_dma_cookie(drm_info->domain);
|
||||
err_free_domain:
|
||||
iommu_domain_free(drm_info->domain);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int vpu_drm_destroy(struct vpu_iommu_info *iommu_info)
|
||||
{
|
||||
struct vpu_iommu_drm_info *drm_info = iommu_info->private;
|
||||
|
||||
iommu_put_dma_cookie(drm_info->domain);
|
||||
iommu_domain_free(drm_info->domain);
|
||||
|
||||
kfree(drm_info);
|
||||
iommu_info->private = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct vpu_iommu_ops drm_ops = {
|
||||
.create = vpu_drm_create,
|
||||
.alloc = vpu_drm_alloc,
|
||||
.import = vpu_drm_import,
|
||||
.free = vpu_drm_free,
|
||||
.free_fd = vpu_drm_free_fd,
|
||||
.map_kernel = vpu_drm_map_kernel,
|
||||
.unmap_kernel = vpu_drm_unmap_kernel,
|
||||
.map_iommu = vpu_drm_map_iommu,
|
||||
.unmap_iommu = vpu_drm_unmap_iommu,
|
||||
.destroy = vpu_drm_destroy,
|
||||
.dump = vcdoec_drm_dump_info,
|
||||
.attach = vpu_drm_attach,
|
||||
.detach = vpu_drm_detach,
|
||||
.clear = vpu_drm_clear_session,
|
||||
};
|
||||
|
||||
void vpu_iommu_drm_set_ops(struct vpu_iommu_info *iommu_info)
|
||||
{
|
||||
if (!iommu_info)
|
||||
return;
|
||||
iommu_info->ops = &drm_ops;
|
||||
}
|
||||
@@ -1,410 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Fuzhou Rockchip Electronics Co., Ltd
|
||||
* author: Jung Zhao jung.zhao@rock-chips.com
|
||||
* Randy Li, randy.li@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.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "vpu_iommu_ops.h"
|
||||
|
||||
#if defined(CONFIG_ION_ROCKCHIP)
|
||||
#include <linux/rockchip_ion.h>
|
||||
#include <linux/rockchip-iovmm.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/memblock.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_graph.h>
|
||||
#include <linux/component.h>
|
||||
#include <linux/fence.h>
|
||||
#include <linux/console.h>
|
||||
#include <linux/kref.h>
|
||||
#include <linux/fdtable.h>
|
||||
|
||||
struct vpu_ion_buffer {
|
||||
struct list_head list;
|
||||
struct ion_handle *handle;
|
||||
int index;
|
||||
};
|
||||
|
||||
struct vpu_iommu_ion_info {
|
||||
struct ion_client *ion_client;
|
||||
bool attached;
|
||||
};
|
||||
|
||||
static struct vpu_ion_buffer*
|
||||
vpu_ion_get_buffer_no_lock(struct vpu_iommu_session_info *session_info,
|
||||
int idx)
|
||||
{
|
||||
struct vpu_ion_buffer *ion_buffer = NULL, *n;
|
||||
|
||||
list_for_each_entry_safe(ion_buffer, n,
|
||||
&session_info->buffer_list, list) {
|
||||
if (ion_buffer->index == idx)
|
||||
return ion_buffer;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
vpu_ion_clear_session(struct vpu_iommu_session_info *session_info)
|
||||
{
|
||||
/* do nothing */
|
||||
}
|
||||
|
||||
static int vpu_ion_attach(struct vpu_iommu_info *iommu_info)
|
||||
{
|
||||
struct vpu_iommu_ion_info *ion_info = iommu_info->private;
|
||||
int ret;
|
||||
|
||||
mutex_lock(&iommu_info->iommu_mutex);
|
||||
|
||||
if (ion_info->attached) {
|
||||
mutex_unlock(&iommu_info->iommu_mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
rockchip_iovmm_activate(iommu_info->dev);
|
||||
|
||||
ion_info->attached = true;
|
||||
|
||||
mutex_unlock(&iommu_info->iommu_mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void vpu_ion_detach(struct vpu_iommu_info *iommu_info)
|
||||
{
|
||||
struct vpu_iommu_ion_info *ion_info = iommu_info->private;
|
||||
|
||||
mutex_lock(&iommu_info->iommu_mutex);
|
||||
|
||||
if (!ion_info->attached) {
|
||||
mutex_unlock(&iommu_info->iommu_mutex);
|
||||
return;
|
||||
}
|
||||
|
||||
rockchip_iovmm_deactivate(iommu_info->dev);
|
||||
ion_info->attached = false;
|
||||
|
||||
mutex_unlock(&iommu_info->iommu_mutex);
|
||||
}
|
||||
|
||||
static int vpu_ion_destroy(struct vpu_iommu_info *iommu_info)
|
||||
{
|
||||
struct vpu_iommu_ion_info *ion_info = iommu_info->private;
|
||||
|
||||
vpu_ion_detach(iommu_info);
|
||||
kfree(ion_info);
|
||||
iommu_info->private = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
vpu_ion_free(struct vpu_iommu_session_info *session_info, int idx)
|
||||
{
|
||||
struct vpu_ion_buffer *ion_buffer;
|
||||
|
||||
mutex_lock(&session_info->list_mutex);
|
||||
ion_buffer = vpu_ion_get_buffer_no_lock(session_info, idx);
|
||||
|
||||
if (!ion_buffer) {
|
||||
mutex_unlock(&session_info->list_mutex);
|
||||
pr_err("%s can not find %d buffer in list\n", __func__, idx);
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
list_del_init(&ion_buffer->list);
|
||||
mutex_unlock(&session_info->list_mutex);
|
||||
kfree(ion_buffer);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
vpu_ion_unmap_iommu(struct vpu_iommu_session_info *session_info, int idx)
|
||||
{
|
||||
struct vpu_ion_buffer *ion_buffer;
|
||||
struct vpu_iommu_info *iommu_info = session_info->iommu_info;
|
||||
struct vpu_iommu_ion_info *ion_info = iommu_info->private;
|
||||
|
||||
mutex_lock(&session_info->list_mutex);
|
||||
ion_buffer = vpu_ion_get_buffer_no_lock(session_info, idx);
|
||||
mutex_unlock(&session_info->list_mutex);
|
||||
|
||||
if (!ion_buffer) {
|
||||
pr_err("%s can not find %d buffer in list\n", __func__, idx);
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ion_free(ion_info->ion_client, ion_buffer->handle);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
vpu_ion_map_iommu(struct vpu_iommu_session_info *session_info, int idx,
|
||||
unsigned long *iova, unsigned long *size)
|
||||
{
|
||||
struct vpu_ion_buffer *ion_buffer;
|
||||
struct device *dev = session_info->dev;
|
||||
struct vpu_iommu_info *iommu_info = session_info->iommu_info;
|
||||
struct vpu_iommu_ion_info *ion_info = iommu_info->private;
|
||||
int ret = 0;
|
||||
|
||||
/* Force to flush iommu table */
|
||||
rockchip_iovmm_invalidate_tlb(session_info->dev);
|
||||
|
||||
mutex_lock(&session_info->list_mutex);
|
||||
ion_buffer = vpu_ion_get_buffer_no_lock(session_info, idx);
|
||||
mutex_unlock(&session_info->list_mutex);
|
||||
|
||||
if (!ion_buffer) {
|
||||
pr_err("%s can not find %d buffer in list\n", __func__, idx);
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (session_info->mmu_dev)
|
||||
ret = ion_map_iommu(dev, ion_info->ion_client,
|
||||
ion_buffer->handle, iova, size);
|
||||
else
|
||||
ret = ion_phys(ion_info->ion_client, ion_buffer->handle,
|
||||
iova, (size_t *)size);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
vpu_ion_unmap_kernel(struct vpu_iommu_session_info *session_info,
|
||||
int idx)
|
||||
{
|
||||
struct vpu_ion_buffer *ion_buffer;
|
||||
|
||||
mutex_lock(&session_info->list_mutex);
|
||||
ion_buffer = vpu_ion_get_buffer_no_lock(session_info, idx);
|
||||
mutex_unlock(&session_info->list_mutex);
|
||||
|
||||
if (!ion_buffer) {
|
||||
pr_err("%s can not find %d buffer in list\n", __func__, idx);
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void*
|
||||
vpu_ion_map_kernel(struct vpu_iommu_session_info *session_info, int idx)
|
||||
{
|
||||
struct vpu_ion_buffer *ion_buffer;
|
||||
struct vpu_iommu_info *iommu_info = session_info->iommu_info;
|
||||
struct vpu_iommu_ion_info *ion_info = iommu_info->private;
|
||||
|
||||
rockchip_iovmm_invalidate_tlb(session_info->dev);
|
||||
|
||||
mutex_lock(&session_info->list_mutex);
|
||||
ion_buffer = vpu_ion_get_buffer_no_lock(session_info, idx);
|
||||
mutex_unlock(&session_info->list_mutex);
|
||||
|
||||
if (!ion_buffer) {
|
||||
pr_err("%s can not find %d buffer in list\n", __func__, idx);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return ion_map_kernel(ion_info->ion_client, ion_buffer->handle);
|
||||
}
|
||||
|
||||
static int vpu_ion_alloc(struct vpu_iommu_session_info *session_info,
|
||||
unsigned long size,
|
||||
unsigned long align)
|
||||
{
|
||||
struct vpu_ion_buffer *ion_buffer = NULL;
|
||||
struct vpu_iommu_info *iommu_info = session_info->iommu_info;
|
||||
struct vpu_iommu_ion_info *ion_info = iommu_info->private;
|
||||
unsigned int heap_id_mask;
|
||||
|
||||
if (iommu_info->mmu_dev)
|
||||
heap_id_mask = ION_HEAP_TYPE_SYSTEM;
|
||||
else
|
||||
heap_id_mask = ION_HEAP_TYPE_DMA;
|
||||
|
||||
ion_buffer = kzalloc(sizeof(*ion_buffer), GFP_KERNEL);
|
||||
if (!ion_buffer)
|
||||
return -ENOMEM;
|
||||
|
||||
ion_buffer->handle = ion_alloc(ion_info->ion_client, size,
|
||||
align, heap_id_mask, 0);
|
||||
|
||||
INIT_LIST_HEAD(&ion_buffer->list);
|
||||
mutex_lock(&session_info->list_mutex);
|
||||
ion_buffer->index = session_info->max_idx;
|
||||
list_add_tail(&ion_buffer->list, &session_info->buffer_list);
|
||||
session_info->max_idx++;
|
||||
if ((session_info->max_idx & 0xfffffff) == 0)
|
||||
session_info->max_idx = 0;
|
||||
mutex_unlock(&session_info->list_mutex);
|
||||
|
||||
return ion_buffer->index;
|
||||
}
|
||||
|
||||
static int
|
||||
vpu_ion_import(struct vpu_iommu_session_info *session_info, int fd)
|
||||
{
|
||||
struct vpu_ion_buffer *ion_buffer = NULL;
|
||||
struct vpu_iommu_info *iommu_info = session_info->iommu_info;
|
||||
struct vpu_iommu_ion_info *ion_info = iommu_info->private;
|
||||
|
||||
ion_buffer = kzalloc(sizeof(*ion_buffer), GFP_KERNEL);
|
||||
if (!ion_buffer)
|
||||
return -ENOMEM;
|
||||
|
||||
ion_buffer->handle = ion_import_dma_buf(ion_info->ion_client, fd);
|
||||
|
||||
INIT_LIST_HEAD(&ion_buffer->list);
|
||||
mutex_lock(&session_info->list_mutex);
|
||||
ion_buffer->index = session_info->max_idx;
|
||||
list_add_tail(&ion_buffer->list, &session_info->buffer_list);
|
||||
session_info->max_idx++;
|
||||
if ((session_info->max_idx & 0xfffffff) == 0)
|
||||
session_info->max_idx = 0;
|
||||
mutex_unlock(&session_info->list_mutex);
|
||||
|
||||
return ion_buffer->index;
|
||||
}
|
||||
|
||||
static int vpu_ion_create(struct vpu_iommu_info *iommu_info)
|
||||
{
|
||||
struct vpu_iommu_ion_info *ion_info;
|
||||
|
||||
iommu_info->private = kmalloc(sizeof(*ion_info), GFP_KERNEL);
|
||||
|
||||
ion_info = iommu_info->private;
|
||||
if (!ion_info)
|
||||
return -ENOMEM;
|
||||
|
||||
ion_info->ion_client = rockchip_ion_client_create("vpu");
|
||||
ion_info->attached = false;
|
||||
|
||||
vpu_ion_attach(iommu_info);
|
||||
|
||||
return IS_ERR(ion_info->ion_client) ? -1 : 0;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static void
|
||||
vpu_ion_clear_session(struct vpu_iommu_session_info *session_info)
|
||||
{
|
||||
/* do nothing */
|
||||
}
|
||||
|
||||
static int vpu_ion_attach(struct vpu_iommu_info *iommu_info)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void vpu_ion_detach(struct vpu_iommu_info *iommu_info)
|
||||
{
|
||||
}
|
||||
|
||||
static int vpu_ion_destroy(struct vpu_iommu_info *iommu_info)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
vpu_ion_free(struct vpu_iommu_session_info *session_info, int idx)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
vpu_ion_unmap_iommu(struct vpu_iommu_session_info *session_info, int idx)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
vpu_ion_map_iommu(struct vpu_iommu_session_info *session_info, int idx,
|
||||
unsigned long *iova, unsigned long *size)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
vpu_ion_unmap_kernel(struct vpu_iommu_session_info *session_info,
|
||||
int idx)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void*
|
||||
vpu_ion_map_kernel(struct vpu_iommu_session_info *session_info, int idx)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int vpu_ion_alloc(struct vpu_iommu_session_info *session_info,
|
||||
unsigned long size,
|
||||
unsigned long align)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
vpu_ion_import(struct vpu_iommu_session_info *session_info, int fd)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vpu_ion_create(struct vpu_iommu_info *iommu_info)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
static struct vpu_iommu_ops ion_ops = {
|
||||
.create = vpu_ion_create,
|
||||
.destroy = vpu_ion_destroy,
|
||||
.alloc = vpu_ion_alloc,
|
||||
.import = vpu_ion_import,
|
||||
.free = vpu_ion_free,
|
||||
.free_fd = NULL,
|
||||
.map_kernel = vpu_ion_map_kernel,
|
||||
.unmap_kernel = vpu_ion_unmap_kernel,
|
||||
.map_iommu = vpu_ion_map_iommu,
|
||||
.unmap_iommu = vpu_ion_unmap_iommu,
|
||||
.dump = NULL,
|
||||
.attach = vpu_ion_attach,
|
||||
.detach = vpu_ion_detach,
|
||||
.clear = vpu_ion_clear_session,
|
||||
};
|
||||
|
||||
/*
|
||||
* we do not manage the ref number ourselves,
|
||||
* since ion will help us to do that. what we
|
||||
* need to do is just map/unmap and import/free
|
||||
* every time
|
||||
*/
|
||||
void vpu_iommu_ion_set_ops(struct vpu_iommu_info *iommu_info)
|
||||
{
|
||||
if (!iommu_info)
|
||||
return;
|
||||
iommu_info->ops = &ion_ops;
|
||||
}
|
||||
@@ -1,291 +0,0 @@
|
||||
/**
|
||||
* Copyright (C) 2016 Fuzhou Rockchip Electronics Co., Ltd
|
||||
* author: Jung Zhao jung.zhao@rock-chips.com
|
||||
* Randy Li, randy.li@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.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include "vpu_iommu_ops.h"
|
||||
|
||||
static
|
||||
struct vpu_iommu_session_info *vpu_iommu_get_session_info(struct vpu_iommu_info *iommu_info,
|
||||
struct mpp_session *session)
|
||||
{
|
||||
struct vpu_iommu_session_info *session_info = NULL, *n;
|
||||
|
||||
list_for_each_entry_safe(session_info, n, &iommu_info->session_list,
|
||||
head) {
|
||||
if (session_info->session == session)
|
||||
return session_info;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int vpu_iommu_create(struct vpu_iommu_info *iommu_info)
|
||||
{
|
||||
if (!iommu_info || !iommu_info->ops->create)
|
||||
return -EINVAL;
|
||||
|
||||
return iommu_info->ops->create(iommu_info);
|
||||
}
|
||||
|
||||
int vpu_iommu_alloc(struct vpu_iommu_info *iommu_info,
|
||||
struct mpp_session *session,
|
||||
unsigned long size,
|
||||
unsigned long align)
|
||||
{
|
||||
struct vpu_iommu_session_info *session_info = NULL;
|
||||
|
||||
if (!iommu_info || !iommu_info->ops->alloc || !session)
|
||||
return -EINVAL;
|
||||
|
||||
session_info = vpu_iommu_get_session_info(iommu_info, session);
|
||||
if (!session_info) {
|
||||
session_info = kzalloc(sizeof(*session_info), GFP_KERNEL);
|
||||
if (!session_info)
|
||||
return -ENOMEM;
|
||||
|
||||
INIT_LIST_HEAD(&session_info->head);
|
||||
INIT_LIST_HEAD(&session_info->buffer_list);
|
||||
mutex_init(&session_info->list_mutex);
|
||||
session_info->max_idx = 0;
|
||||
session_info->session = session;
|
||||
session_info->mmu_dev = iommu_info->mmu_dev;
|
||||
session_info->dev = iommu_info->dev;
|
||||
session_info->iommu_info = iommu_info;
|
||||
session_info->buffer_nums = 0;
|
||||
mutex_lock(&iommu_info->list_mutex);
|
||||
list_add_tail(&session_info->head, &iommu_info->session_list);
|
||||
mutex_unlock(&iommu_info->list_mutex);
|
||||
}
|
||||
|
||||
session_info->debug_level = iommu_info->debug_level;
|
||||
|
||||
return iommu_info->ops->alloc(session_info, size, align);
|
||||
}
|
||||
|
||||
int vpu_iommu_import(struct vpu_iommu_info *iommu_info,
|
||||
struct mpp_session *session, int fd)
|
||||
{
|
||||
struct vpu_iommu_session_info *session_info = NULL;
|
||||
|
||||
if (!iommu_info || !iommu_info->ops->import || !session)
|
||||
return -EINVAL;
|
||||
|
||||
session_info = vpu_iommu_get_session_info(iommu_info, session);
|
||||
if (!session_info) {
|
||||
session_info = kzalloc(sizeof(*session_info), GFP_KERNEL);
|
||||
if (!session_info)
|
||||
return -ENOMEM;
|
||||
|
||||
INIT_LIST_HEAD(&session_info->head);
|
||||
INIT_LIST_HEAD(&session_info->buffer_list);
|
||||
mutex_init(&session_info->list_mutex);
|
||||
session_info->max_idx = 0;
|
||||
session_info->session = session;
|
||||
session_info->mmu_dev = iommu_info->mmu_dev;
|
||||
session_info->dev = iommu_info->dev;
|
||||
session_info->iommu_info = iommu_info;
|
||||
session_info->buffer_nums = 0;
|
||||
mutex_lock(&iommu_info->list_mutex);
|
||||
list_add_tail(&session_info->head, &iommu_info->session_list);
|
||||
mutex_unlock(&iommu_info->list_mutex);
|
||||
}
|
||||
|
||||
session_info->debug_level = iommu_info->debug_level;
|
||||
|
||||
return iommu_info->ops->import(session_info, fd);
|
||||
}
|
||||
|
||||
int vpu_iommu_free(struct vpu_iommu_info *iommu_info,
|
||||
struct mpp_session *session, int idx)
|
||||
{
|
||||
struct vpu_iommu_session_info *session_info = NULL;
|
||||
|
||||
session_info = vpu_iommu_get_session_info(iommu_info, session);
|
||||
|
||||
if (!iommu_info || !iommu_info->ops->free || !session_info)
|
||||
return -EINVAL;
|
||||
|
||||
return iommu_info->ops->free(session_info, idx);
|
||||
}
|
||||
|
||||
int vpu_iommu_free_fd(struct vpu_iommu_info *iommu_info,
|
||||
struct mpp_session *session, int fd)
|
||||
{
|
||||
struct vpu_iommu_session_info *session_info = NULL;
|
||||
|
||||
session_info = vpu_iommu_get_session_info(iommu_info, session);
|
||||
|
||||
if (!iommu_info || !iommu_info->ops->free_fd || !session_info)
|
||||
return -EINVAL;
|
||||
|
||||
return iommu_info->ops->free_fd(session_info, fd);
|
||||
}
|
||||
|
||||
void *vpu_iommu_map_kernel(struct vpu_iommu_info *iommu_info,
|
||||
struct mpp_session *session, int idx)
|
||||
{
|
||||
struct vpu_iommu_session_info *session_info = NULL;
|
||||
|
||||
session_info = vpu_iommu_get_session_info(iommu_info, session);
|
||||
|
||||
if (!iommu_info || !iommu_info->ops->map_kernel || !session_info)
|
||||
return NULL;
|
||||
|
||||
return iommu_info->ops->map_kernel(session_info, idx);
|
||||
}
|
||||
|
||||
int vpu_iommu_unmap_kernel(struct vpu_iommu_info *iommu_info,
|
||||
struct mpp_session *session, int idx)
|
||||
{
|
||||
struct vpu_iommu_session_info *session_info = NULL;
|
||||
|
||||
session_info = vpu_iommu_get_session_info(iommu_info, session);
|
||||
|
||||
if (!iommu_info || !iommu_info->ops->unmap_kernel || !session_info)
|
||||
return -EINVAL;
|
||||
|
||||
return iommu_info->ops->unmap_kernel(session_info, idx);
|
||||
}
|
||||
|
||||
int vpu_iommu_map_iommu(struct vpu_iommu_info *iommu_info,
|
||||
struct mpp_session *session,
|
||||
int idx, unsigned long *iova,
|
||||
unsigned long *size)
|
||||
{
|
||||
struct vpu_iommu_session_info *session_info = NULL;
|
||||
|
||||
session_info = vpu_iommu_get_session_info(iommu_info, session);
|
||||
|
||||
if (!iommu_info || !iommu_info->ops->map_iommu || !session_info)
|
||||
return -EINVAL;
|
||||
|
||||
return iommu_info->ops->map_iommu(session_info, idx, iova, size);
|
||||
}
|
||||
|
||||
int vpu_iommu_unmap_iommu(struct vpu_iommu_info *iommu_info,
|
||||
struct mpp_session *session, int idx)
|
||||
{
|
||||
struct vpu_iommu_session_info *session_info = NULL;
|
||||
|
||||
session_info = vpu_iommu_get_session_info(iommu_info, session);
|
||||
|
||||
if (!iommu_info || !iommu_info->ops->unmap_iommu || !session_info)
|
||||
return -EINVAL;
|
||||
|
||||
return iommu_info->ops->unmap_iommu(session_info, idx);
|
||||
}
|
||||
|
||||
int vpu_iommu_destroy(struct vpu_iommu_info *iommu_info)
|
||||
{
|
||||
if (!iommu_info || !iommu_info->ops->destroy)
|
||||
return -EINVAL;
|
||||
|
||||
return iommu_info->ops->destroy(iommu_info);
|
||||
}
|
||||
|
||||
void vpu_iommu_dump(struct vpu_iommu_info *iommu_info,
|
||||
struct mpp_session *session)
|
||||
{
|
||||
struct vpu_iommu_session_info *session_info = NULL;
|
||||
|
||||
session_info = vpu_iommu_get_session_info(iommu_info, session);
|
||||
|
||||
if (!iommu_info || !iommu_info->ops->dump || !session_info)
|
||||
return;
|
||||
|
||||
iommu_info->ops->dump(session_info);
|
||||
}
|
||||
|
||||
void vpu_iommu_clear(struct vpu_iommu_info *iommu_info,
|
||||
struct mpp_session *session)
|
||||
{
|
||||
struct vpu_iommu_session_info *session_info = NULL;
|
||||
|
||||
session_info = vpu_iommu_get_session_info(iommu_info, session);
|
||||
|
||||
if (!iommu_info || !iommu_info->ops->clear || !session_info)
|
||||
return;
|
||||
|
||||
iommu_info->ops->clear(session_info);
|
||||
|
||||
mutex_lock(&iommu_info->list_mutex);
|
||||
list_del_init(&session_info->head);
|
||||
kfree(session_info);
|
||||
mutex_unlock(&iommu_info->list_mutex);
|
||||
}
|
||||
|
||||
int vpu_iommu_attach(struct vpu_iommu_info *iommu_info)
|
||||
{
|
||||
if (!iommu_info || !iommu_info->ops->attach)
|
||||
return 0;
|
||||
|
||||
return iommu_info->ops->attach(iommu_info);
|
||||
}
|
||||
|
||||
void vpu_iommu_detach(struct vpu_iommu_info *iommu_info)
|
||||
{
|
||||
if (!iommu_info || !iommu_info->ops->detach)
|
||||
return;
|
||||
|
||||
return iommu_info->ops->detach(iommu_info);
|
||||
}
|
||||
|
||||
struct vpu_iommu_info*
|
||||
vpu_iommu_info_create(struct device *dev,
|
||||
struct device *mmu_dev,
|
||||
int alloc_type)
|
||||
{
|
||||
struct vpu_iommu_info *iommu_info = NULL;
|
||||
|
||||
iommu_info = kzalloc(sizeof(*iommu_info), GFP_KERNEL);
|
||||
if (!iommu_info)
|
||||
return NULL;
|
||||
|
||||
iommu_info->dev = dev;
|
||||
INIT_LIST_HEAD(&iommu_info->session_list);
|
||||
mutex_init(&iommu_info->list_mutex);
|
||||
mutex_init(&iommu_info->iommu_mutex);
|
||||
switch (alloc_type) {
|
||||
#ifdef CONFIG_DRM
|
||||
case ALLOCATOR_USE_DRM:
|
||||
vpu_iommu_drm_set_ops(iommu_info);
|
||||
break;
|
||||
#endif
|
||||
#ifdef CONFIG_ION
|
||||
case ALLOCATOR_USE_ION:
|
||||
vpu_iommu_ion_set_ops(iommu_info);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
iommu_info->ops = NULL;
|
||||
break;
|
||||
}
|
||||
|
||||
iommu_info->mmu_dev = mmu_dev;
|
||||
|
||||
vpu_iommu_create(iommu_info);
|
||||
|
||||
return iommu_info;
|
||||
}
|
||||
|
||||
int vpu_iommu_info_destroy(struct vpu_iommu_info *iommu_info)
|
||||
{
|
||||
vpu_iommu_destroy(iommu_info);
|
||||
kfree(iommu_info);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,139 +0,0 @@
|
||||
/**
|
||||
* Copyright (C) 2016 Fuzhou Rockchip Electronics Co., Ltd
|
||||
* author: Jung Zhao jung.zhao@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 __VPU_IOMMU_OPS_H__
|
||||
#define __VPU_IOMMU_OPS_H__
|
||||
|
||||
#include <linux/platform_device.h>
|
||||
#include "mpp_service.h"
|
||||
|
||||
#define BUFFER_LIST_MAX_NUMS 30
|
||||
|
||||
#define ALLOCATOR_USE_ION 0x00000000
|
||||
#define ALLOCATOR_USE_DRM 0x00000001
|
||||
|
||||
#define DEBUG_IOMMU_OPS_DUMP 0x00020000
|
||||
#define DEBUG_IOMMU_NORMAL 0x00040000
|
||||
|
||||
#define vpu_iommu_debug_func(debug_level, type, fmt, args...) \
|
||||
do { \
|
||||
if (unlikely((debug_level) & type)) { \
|
||||
pr_info("%s:%d: " fmt, \
|
||||
__func__, __LINE__, ##args); \
|
||||
} \
|
||||
} while (0)
|
||||
#define vpu_iommu_debug(debug_level, type, fmt, args...) \
|
||||
do { \
|
||||
if (unlikely((debug_level) & type)) { \
|
||||
pr_info(fmt, ##args); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
struct vpu_iommu_info;
|
||||
struct vpu_iommu_session_info;
|
||||
|
||||
struct vpu_iommu_ops {
|
||||
int (*create)(struct vpu_iommu_info *iommu_info);
|
||||
int (*alloc)(struct vpu_iommu_session_info *session_info,
|
||||
unsigned long size,
|
||||
unsigned long align);
|
||||
int (*import)(struct vpu_iommu_session_info *session_info, int fd);
|
||||
int (*free)(struct vpu_iommu_session_info *session_info, int idx);
|
||||
int (*free_fd)(struct vpu_iommu_session_info *session_info, int fd);
|
||||
void* (*map_kernel)(struct vpu_iommu_session_info *session_info,
|
||||
int idx);
|
||||
int (*unmap_kernel)(struct vpu_iommu_session_info *session_info,
|
||||
int idx);
|
||||
int (*map_iommu)(struct vpu_iommu_session_info *session_info,
|
||||
int idx,
|
||||
unsigned long *iova, unsigned long *size);
|
||||
int (*unmap_iommu)(struct vpu_iommu_session_info *session_info,
|
||||
int idx);
|
||||
int (*destroy)(struct vpu_iommu_info *iommu_info);
|
||||
void (*dump)(struct vpu_iommu_session_info *session_info);
|
||||
int (*attach)(struct vpu_iommu_info *iommu_info);
|
||||
void (*detach)(struct vpu_iommu_info *iommu_info);
|
||||
void (*clear)(struct vpu_iommu_session_info *session_info);
|
||||
};
|
||||
|
||||
struct vpu_iommu_session_info {
|
||||
struct list_head head;
|
||||
struct mpp_session *session;
|
||||
int buffer_nums;
|
||||
struct list_head buffer_list;
|
||||
struct mutex list_mutex;
|
||||
int max_idx;
|
||||
struct device *dev;
|
||||
struct device *mmu_dev;
|
||||
struct vpu_iommu_info *iommu_info;
|
||||
int debug_level;
|
||||
};
|
||||
|
||||
struct vpu_iommu_info {
|
||||
struct list_head session_list;
|
||||
struct mutex list_mutex;
|
||||
struct mutex iommu_mutex;
|
||||
struct device *dev;
|
||||
struct device *mmu_dev;
|
||||
struct vpu_iommu_ops *ops;
|
||||
int debug_level;
|
||||
void *private;
|
||||
};
|
||||
|
||||
#ifdef CONFIG_DRM
|
||||
void vpu_iommu_drm_set_ops(struct vpu_iommu_info *iommu_info);
|
||||
#endif
|
||||
#ifdef CONFIG_ION
|
||||
void vpu_iommu_ion_set_ops(struct vpu_iommu_info *iommu_info);
|
||||
#endif
|
||||
|
||||
struct vpu_iommu_info *vpu_iommu_info_create(struct device *dev,
|
||||
struct device *mmu_dev,
|
||||
int alloc_type);
|
||||
int vpu_iommu_info_destroy(struct vpu_iommu_info *iommu_info);
|
||||
|
||||
int vpu_iommu_create(struct vpu_iommu_info *iommu_info);
|
||||
int vpu_iommu_alloc(struct vpu_iommu_info *iommu_info,
|
||||
struct mpp_session *session,
|
||||
unsigned long size,
|
||||
unsigned long align);
|
||||
int vpu_iommu_import(struct vpu_iommu_info *iommu_info,
|
||||
struct mpp_session *session, int fd);
|
||||
int vpu_iommu_free(struct vpu_iommu_info *iommu_info,
|
||||
struct mpp_session *session, int idx);
|
||||
int vpu_iommu_free_fd(struct vpu_iommu_info *iommu_info,
|
||||
struct mpp_session *session, int fd);
|
||||
void *vpu_iommu_map_kernel(struct vpu_iommu_info *iommu_info,
|
||||
struct mpp_session *session, int idx);
|
||||
int vpu_iommu_unmap_kernel(struct vpu_iommu_info *iommu_info,
|
||||
struct mpp_session *session, int idx);
|
||||
int vpu_iommu_map_iommu(struct vpu_iommu_info *iommu_info,
|
||||
struct mpp_session *session,
|
||||
int idx,
|
||||
unsigned long *iova,
|
||||
unsigned long *size);
|
||||
int vpu_iommu_unmap_iommu(struct vpu_iommu_info *iommu_info,
|
||||
struct mpp_session *session,
|
||||
int idx);
|
||||
int vpu_iommu_destroy(struct vpu_iommu_info *iommu_info);
|
||||
void vpu_iommu_dump(struct vpu_iommu_info *iommu_info,
|
||||
struct mpp_session *session);
|
||||
void vpu_iommu_clear(struct vpu_iommu_info *iommu_info,
|
||||
struct mpp_session *session);
|
||||
|
||||
int vpu_iommu_attach(struct vpu_iommu_info *iommu_info);
|
||||
void vpu_iommu_detach(struct vpu_iommu_info *iommu_info);
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user