2673 lines
76 KiB
C
2673 lines
76 KiB
C
|
// SPDX-License-Identifier: GPL-2.0
|
||
|
/*
|
||
|
* Copyright (c) 2019 MediaTek Inc.
|
||
|
*/
|
||
|
|
||
|
#include <asm/cacheflush.h>
|
||
|
#include <linux/cdev.h>
|
||
|
#include <linux/delay.h>
|
||
|
#include <linux/dma-mapping.h>
|
||
|
#include <linux/file.h>
|
||
|
#include <linux/firmware.h>
|
||
|
#include <linux/fs.h>
|
||
|
#include <linux/init.h>
|
||
|
#include <linux/interrupt.h>
|
||
|
#include <linux/list.h>
|
||
|
#include <linux/kernel.h>
|
||
|
#include <linux/kthread.h>
|
||
|
#include <linux/module.h>
|
||
|
#include <linux/of_address.h>
|
||
|
#include <linux/of_irq.h>
|
||
|
#include <linux/of_platform.h>
|
||
|
#include <linux/sched.h>
|
||
|
#include <linux/sched/signal.h>
|
||
|
#include <linux/sched/task.h>
|
||
|
#include <linux/semaphore.h>
|
||
|
#include <linux/suspend.h>
|
||
|
#include <linux/uaccess.h>
|
||
|
#include <linux/compat.h>
|
||
|
#include <linux/freezer.h>
|
||
|
#include <linux/pm_runtime.h>
|
||
|
#include <linux/pm_wakeup.h>
|
||
|
#include <linux/soc/mediatek/mtk-cmdq.h>
|
||
|
#include <linux/mailbox/mtk-cmdq-mailbox.h>
|
||
|
#include <linux/mailbox_controller.h>
|
||
|
#include <linux/signal.h>
|
||
|
#include <trace/events/signal.h>
|
||
|
#include <linux/string.h>
|
||
|
|
||
|
#ifdef CONFIG_MTK_IOMMU_V2
|
||
|
#include <linux/iommu.h>
|
||
|
#endif
|
||
|
#include "mtk_vcodec_mem.h"
|
||
|
#include <uapi/linux/mtk_vcu_controls.h>
|
||
|
#include "mtk_vcu.h"
|
||
|
#include "smi_public.h"
|
||
|
#if defined(CONFIG_MTK_SVP_ON_MTEE_SUPPORT)
|
||
|
#include "tz_m4u.h"
|
||
|
#endif
|
||
|
|
||
|
/*
|
||
|
#undef pr_debug
|
||
|
#define pr_debug pr_info
|
||
|
|
||
|
#undef dev_dbg
|
||
|
#define dev_dbg dev_info
|
||
|
*/
|
||
|
|
||
|
/**
|
||
|
* VCU (Video Communication/Controller Unit) is a tiny processor
|
||
|
* controlling video hardware related to video codec, scaling and color
|
||
|
* format converting.
|
||
|
* VCU interfaces with other blocks by share memory and interrupt.
|
||
|
**/
|
||
|
#define VCU_PATH "/dev/vpud"
|
||
|
#define MDP_PATH "/dev/mdpd"
|
||
|
#define CAM_PATH "/dev/camd"
|
||
|
#define VCU_DEVNAME "vcu"
|
||
|
|
||
|
#ifdef CONFIG_MTK_ENG_BUILD
|
||
|
#define IPI_TIMEOUT_MS 16000U
|
||
|
#else
|
||
|
#define IPI_TIMEOUT_MS 6000U
|
||
|
#endif
|
||
|
|
||
|
#define VCU_FW_VER_LEN 16
|
||
|
#define VCODEC_INST_MAX 64
|
||
|
#define GCE_EVENT_MAX 64
|
||
|
#define GCE_THNUM_MAX 2
|
||
|
#define GCE_PENDING_CNT 10
|
||
|
/*mtk vcu support mpd max value*/
|
||
|
#define MTK_VCU_NR_MAX 3
|
||
|
|
||
|
/* vcu extended mapping length */
|
||
|
#define VCU_PMEM0_LEN(vcu_data) (vcu_data->extmem.p_len)
|
||
|
#define VCU_DMEM0_LEN(vcu_data) (vcu_data->extmem.d_len)
|
||
|
/* vcu extended user virtural address */
|
||
|
#define VCU_PMEM0_VMA(vcu_data) (vcu_data->extmem.p_vma)
|
||
|
#define VCU_DMEM0_VMA(vcu_data) (vcu_data->extmem.d_vma)
|
||
|
/* vcu extended kernel virtural address */
|
||
|
#define VCU_PMEM0_VIRT(vcu_data) (vcu_data->extmem.p_va)
|
||
|
#define VCU_DMEM0_VIRT(vcu_data) (vcu_data->extmem.d_va)
|
||
|
/* vcu extended phsyial address */
|
||
|
#define VCU_PMEM0_PHY(vcu_data) (vcu_data->extmem.p_pa)
|
||
|
#define VCU_DMEM0_PHY(vcu_data) (vcu_data->extmem.d_pa)
|
||
|
/* vcu extended iova address*/
|
||
|
#define VCU_PMEM0_IOVA(vcu_data) (vcu_data->extmem.p_iova)
|
||
|
#define VCU_DMEM0_IOVA(vcu_data) (vcu_data->extmem.d_iova)
|
||
|
#define VCU_SHMEM_SIZE 0x80000
|
||
|
|
||
|
#define MAP_SHMEM_ALLOC_BASE 0x80000000UL
|
||
|
#define MAP_SHMEM_ALLOC_RANGE VCU_SHMEM_SIZE
|
||
|
#define MAP_SHMEM_ALLOC_END (MAP_SHMEM_ALLOC_BASE + MAP_SHMEM_ALLOC_RANGE)
|
||
|
#define MAP_SHMEM_COMMIT_BASE 0x88000000UL
|
||
|
#define MAP_SHMEM_COMMIT_RANGE VCU_SHMEM_SIZE
|
||
|
#define MAP_SHMEM_COMMIT_END (MAP_SHMEM_COMMIT_BASE + MAP_SHMEM_COMMIT_RANGE)
|
||
|
|
||
|
#define MAP_SHMEM_MM_BASE 0x200000000UL
|
||
|
#define MAP_SHMEM_MM_CACHEABLE_BASE 0x400000000UL
|
||
|
#define MAP_SHMEM_PA_BASE 0x800000000UL
|
||
|
#define MAP_SHMEM_MM_RANGE 0x1FFFFFFFFUL
|
||
|
#define MAP_SHMEM_MM_END (MAP_SHMEM_MM_BASE + MAP_SHMEM_MM_RANGE)
|
||
|
#define MAP_SHMEM_MM_CACHEABLE_END (MAP_SHMEM_MM_CACHEABLE_BASE \
|
||
|
+ MAP_SHMEM_MM_RANGE)
|
||
|
|
||
|
struct mtk_vcu *vcu_ptr;
|
||
|
static char *vcodec_param_string = "";
|
||
|
|
||
|
inline unsigned int ipi_id_to_inst_id(int id)
|
||
|
{
|
||
|
/* Assume VENC uses instance 1 and others use 0. */
|
||
|
if (id < IPI_VENC_COMMON && id >= IPI_VCU_INIT)
|
||
|
return VCU_VDEC;
|
||
|
else
|
||
|
return VCU_VENC;
|
||
|
}
|
||
|
|
||
|
#define vcu_dbg_log(fmt, arg...) do { \
|
||
|
if (vcu_ptr->enable_vcu_dbg_log) \
|
||
|
pr_info(fmt, ##arg); \
|
||
|
} while (0)
|
||
|
|
||
|
#define MAP_PA_BASE_1GB 0x40000000 /* < 1GB registers */
|
||
|
#define VCU_MAP_HW_REG_NUM 4
|
||
|
/* VDEC VDEC_LAT VENC_CORE0 VENC_CORE1 */
|
||
|
|
||
|
/* Default vcu_mtkdev[0] handle vdec, vcu_mtkdev[1] handle mdp */
|
||
|
static struct mtk_vcu *vcu_mtkdev[MTK_VCU_NR_MAX];
|
||
|
|
||
|
static struct task_struct *vcud_task;
|
||
|
static struct files_struct *files;
|
||
|
|
||
|
/* for protecting vpud file struct */
|
||
|
struct mutex vpud_file_mutex;
|
||
|
|
||
|
static __attribute__((used)) unsigned int time_ms_s, time_ms_e;
|
||
|
#define time_check_start() { \
|
||
|
time_ms_s = jiffies_to_msecs(jiffies); \
|
||
|
}
|
||
|
#define time_check_end(timeout_ms, debug) do { \
|
||
|
time_ms_e = jiffies_to_msecs(jiffies); \
|
||
|
if ((time_ms_e - time_ms_s) > timeout_ms || \
|
||
|
debug) \
|
||
|
pr_info("[VCU][Info] %s L:%d take %u timeout %u ms", \
|
||
|
__func__, __LINE__, \
|
||
|
time_ms_e - time_ms_s, \
|
||
|
timeout_ms); \
|
||
|
} while (0)
|
||
|
|
||
|
/**
|
||
|
* struct vcu_mem - VCU memory information
|
||
|
*
|
||
|
* @p_vma: the user virtual memory address of
|
||
|
* VCU extended program memory
|
||
|
* @d_vma: the user virtual memory address of VCU extended data memory
|
||
|
* @p_va: the kernel virtual memory address of
|
||
|
* VCU extended program memory
|
||
|
* @d_va: the kernel virtual memory address of VCU extended data memory
|
||
|
* @p_pa: the physical memory address of VCU extended program memory
|
||
|
* @d_pa: the physical memory address of VCU extended data memory
|
||
|
* @p_iova: the iova memory address of VCU extended program memory
|
||
|
* @d_iova: the iova memory address of VCU extended data memory
|
||
|
*/
|
||
|
struct vcu_mem {
|
||
|
unsigned long p_vma;
|
||
|
unsigned long d_vma;
|
||
|
void *p_va;
|
||
|
void *d_va;
|
||
|
dma_addr_t p_pa;
|
||
|
dma_addr_t d_pa;
|
||
|
dma_addr_t p_iova;
|
||
|
dma_addr_t d_iova;
|
||
|
unsigned long p_len;
|
||
|
unsigned long d_len;
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* struct vcu_run - VCU initialization status
|
||
|
*
|
||
|
* @signaled: the signal of vcu initialization completed
|
||
|
* @fw_ver: VCU firmware version
|
||
|
* @dec_capability: decoder capability which is not used for now and
|
||
|
* the value is reserved for future use
|
||
|
* @enc_capability: encoder capability which is not used for now and
|
||
|
* the value is reserved for future use
|
||
|
* @wq: wait queue for VCU initialization status
|
||
|
*/
|
||
|
struct vcu_run {
|
||
|
u32 signaled;
|
||
|
char fw_ver[VCU_FW_VER_LEN];
|
||
|
unsigned int dec_capability;
|
||
|
unsigned int enc_capability;
|
||
|
wait_queue_head_t wq;
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* struct vcu_ipi_desc - VCU IPI descriptor
|
||
|
*
|
||
|
* @handler: IPI handler
|
||
|
* @name: the name of IPI handler
|
||
|
* @priv: the private data of IPI handler
|
||
|
*/
|
||
|
struct vcu_ipi_desc {
|
||
|
ipi_handler_t handler;
|
||
|
const char *name;
|
||
|
void *priv;
|
||
|
};
|
||
|
|
||
|
struct map_hw_reg {
|
||
|
unsigned long base;
|
||
|
unsigned long len;
|
||
|
};
|
||
|
|
||
|
struct gce_callback_data {
|
||
|
struct gce_cmdq_obj cmdq_buff;
|
||
|
struct mtk_vcu *vcu_ptr;
|
||
|
struct cmdq_pkt *pkt_ptr;
|
||
|
struct mtk_vcu_queue *vcu_queue;
|
||
|
};
|
||
|
|
||
|
struct gce_ctx_info {
|
||
|
void *v4l2_ctx;
|
||
|
u64 user_hdl;
|
||
|
atomic_t flush_done;
|
||
|
/* gce callbacked but user not waited cnt */
|
||
|
struct gce_callback_data buff[GCE_PENDING_CNT];
|
||
|
struct semaphore buff_sem[GCE_PENDING_CNT];
|
||
|
atomic_t flush_pending;
|
||
|
/* gce not callbacked cnt */
|
||
|
struct vcu_page_info used_pages[GCE_PENDING_CNT];
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* struct mtk_vcu - vcu driver data
|
||
|
* @extmem: VCU extended memory information
|
||
|
* @run: VCU initialization status
|
||
|
* @ipi_desc: VCU IPI descriptor
|
||
|
* @dev: VCU struct device
|
||
|
* @vcu_mutex: protect mtk_vcu (except recv_buf) and ensure only
|
||
|
* one client to use VCU service at a time. For example,
|
||
|
* suppose a client is using VCU to decode VP8.
|
||
|
* If the other client wants to encode VP8,
|
||
|
* it has to wait until VP8 decode completes.
|
||
|
* @vcu_gce_mutex protect mtk_vcu gce flush & callback power sequence
|
||
|
* @file: VCU daemon file pointer
|
||
|
* @is_open: The flag to indicate if VCUD device is open.
|
||
|
* @ack_wq: The wait queue for each codec and mdp. When sleeping
|
||
|
* processes wake up, they will check the condition
|
||
|
* "ipi_id_ack" to run the corresponding action or
|
||
|
* go back to sleep.
|
||
|
* @ipi_id_ack: The ACKs for registered IPI function sending
|
||
|
* interrupt to VCU
|
||
|
* @get_wq: When sleeping process waking up, it will check the
|
||
|
* condition "ipi_got" to run the corresponding action or
|
||
|
* go back to sleep.
|
||
|
* @ipi_got: The flags for IPI message polling from user.
|
||
|
* @ipi_done: The flags for IPI message polling from user again, which
|
||
|
* means the previous messages has been dispatched done in
|
||
|
* daemon.
|
||
|
* @user_obj: Temporary share_obj used for ipi_msg_get.
|
||
|
* @vcu_devno: The vcu_devno for vcu init vcu character device
|
||
|
* @vcu_cdev: The point of vcu character device.
|
||
|
* @vcu_class: The class_create for create vcu device
|
||
|
* @vcu_device: VCU struct device
|
||
|
* @vcuname: VCU struct device name in dtsi
|
||
|
* @path: The path to keep mdpd path or vcud path.
|
||
|
* @vpuid: VCU device id
|
||
|
*
|
||
|
*/
|
||
|
struct mtk_vcu {
|
||
|
struct vcu_mem extmem;
|
||
|
struct vcu_run run;
|
||
|
struct vcu_ipi_desc ipi_desc[IPI_MAX];
|
||
|
struct device *dev;
|
||
|
struct mutex vcu_mutex[VCU_CODEC_MAX];
|
||
|
struct mutex vcu_gce_mutex[VCU_CODEC_MAX];
|
||
|
struct mutex ctx_ipi_binding[VCU_CODEC_MAX];
|
||
|
/* for protecting vcu data structure */
|
||
|
struct mutex vcu_share;
|
||
|
struct file *file;
|
||
|
struct iommu_domain *io_domain;
|
||
|
bool iommu_padding;
|
||
|
/* temp for 33bits larb adding bits "1" iommu */
|
||
|
struct map_hw_reg map_base[VCU_MAP_HW_REG_NUM];
|
||
|
bool is_open;
|
||
|
wait_queue_head_t ack_wq[VCU_CODEC_MAX];
|
||
|
bool ipi_id_ack[IPI_MAX];
|
||
|
wait_queue_head_t get_wq[VCU_CODEC_MAX];
|
||
|
atomic_t ipi_got[VCU_CODEC_MAX];
|
||
|
atomic_t ipi_done[VCU_CODEC_MAX];
|
||
|
struct share_obj user_obj[VCU_CODEC_MAX];
|
||
|
dev_t vcu_devno;
|
||
|
struct cdev *vcu_cdev;
|
||
|
struct class *vcu_class;
|
||
|
struct device *vcu_device;
|
||
|
const char *vcuname;
|
||
|
const char *path;
|
||
|
int vcuid;
|
||
|
struct log_test_nofuse *vdec_log_info;
|
||
|
wait_queue_head_t vdec_log_get_wq;
|
||
|
atomic_t vdec_log_got;
|
||
|
struct cmdq_base *clt_base;
|
||
|
struct cmdq_client *clt_vdec[GCE_THNUM_MAX];
|
||
|
struct cmdq_client *clt_venc[GCE_THNUM_MAX];
|
||
|
struct cmdq_client *clt_venc_sec[GCE_THNUM_MAX];
|
||
|
int gce_th_num[VCU_CODEC_MAX];
|
||
|
int gce_codec_eid[GCE_EVENT_MAX];
|
||
|
struct gce_cmds *gce_cmds[VCU_CODEC_MAX];
|
||
|
struct mutex gce_cmds_mutex[VCU_CODEC_MAX];
|
||
|
void *curr_ctx[VCU_CODEC_MAX];
|
||
|
struct vb2_buffer *curr_src_vb[VCU_CODEC_MAX];
|
||
|
struct vb2_buffer *curr_dst_vb[VCU_CODEC_MAX];
|
||
|
wait_queue_head_t gce_wq[VCU_CODEC_MAX];
|
||
|
struct gce_ctx_info gce_info[VCODEC_INST_MAX];
|
||
|
atomic_t gce_job_cnt[VCU_CODEC_MAX][GCE_THNUM_MAX];
|
||
|
unsigned long flags[VCU_CODEC_MAX];
|
||
|
int open_cnt;
|
||
|
bool abort;
|
||
|
struct semaphore vpud_killed;
|
||
|
bool is_entering_suspend;
|
||
|
u32 gce_gpr[GCE_THNUM_MAX];
|
||
|
/* for gce poll timer, multi-thread sync */
|
||
|
|
||
|
/* for vpud sig check */
|
||
|
spinlock_t vpud_sig_lock;
|
||
|
int vpud_is_going_down;
|
||
|
|
||
|
/* for vcu dbg log*/
|
||
|
int enable_vcu_dbg_log;
|
||
|
};
|
||
|
|
||
|
static int mtk_vcu_write(const char *val, const struct kernel_param *kp);
|
||
|
|
||
|
static inline bool vcu_running(struct mtk_vcu *vcu)
|
||
|
{
|
||
|
return (bool)vcu->run.signaled;
|
||
|
}
|
||
|
|
||
|
int vcu_ipi_register(struct platform_device *pdev,
|
||
|
enum ipi_id id, ipi_handler_t handler,
|
||
|
const char *name, void *priv)
|
||
|
{
|
||
|
struct mtk_vcu *vcu = platform_get_drvdata(pdev);
|
||
|
struct vcu_ipi_desc *ipi_desc;
|
||
|
unsigned int i = 0;
|
||
|
|
||
|
if (vcu == NULL) {
|
||
|
dev_err(&pdev->dev, "vcu device in not ready\n");
|
||
|
return -EPROBE_DEFER;
|
||
|
}
|
||
|
|
||
|
if (id < IPI_VCU_INIT || id >= IPI_MAX) {
|
||
|
dev_info(&pdev->dev, "[VCU] failed to register ipi message (Invalid arg.)\n");
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
|
||
|
i = ipi_id_to_inst_id(id);
|
||
|
mutex_lock(&vcu->vcu_mutex[i]);
|
||
|
|
||
|
if (id >= IPI_VCU_INIT && id < IPI_MAX && handler != NULL) {
|
||
|
ipi_desc = vcu->ipi_desc;
|
||
|
ipi_desc[id].name = name;
|
||
|
ipi_desc[id].handler = handler;
|
||
|
ipi_desc[id].priv = priv;
|
||
|
mutex_unlock(&vcu->vcu_mutex[i]);
|
||
|
return 0;
|
||
|
}
|
||
|
mutex_unlock(&vcu->vcu_mutex[i]);
|
||
|
|
||
|
dev_err(&pdev->dev, "register vcu ipi id %d with invalid arguments\n",
|
||
|
id);
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
EXPORT_SYMBOL_GPL(vcu_ipi_register);
|
||
|
|
||
|
int vcu_ipi_send(struct platform_device *pdev,
|
||
|
enum ipi_id id, void *buf,
|
||
|
unsigned int len, void *priv)
|
||
|
{
|
||
|
unsigned int i = 0;
|
||
|
struct mtk_vcu *vcu = platform_get_drvdata(pdev);
|
||
|
struct vcu_ipi_desc *ipi_desc;
|
||
|
struct share_obj send_obj;
|
||
|
unsigned long timeout;
|
||
|
int ret;
|
||
|
|
||
|
if (id <= IPI_VCU_INIT || id >= IPI_MAX ||
|
||
|
len > sizeof(send_obj.share_buf) || buf == NULL) {
|
||
|
dev_err(&pdev->dev, "[VCU] failed to send ipi message (Invalid arg.)\n");
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
|
||
|
if (vcu_running(vcu) == false) {
|
||
|
dev_err(&pdev->dev, "[VCU] %s: VCU is not running\n", __func__);
|
||
|
return -EPERM;
|
||
|
}
|
||
|
|
||
|
i = ipi_id_to_inst_id(id);
|
||
|
|
||
|
mutex_lock(&vcu->vcu_mutex[i]);
|
||
|
if (vcu_ptr->abort) {
|
||
|
if (vcu_ptr->open_cnt > 0) {
|
||
|
dev_info(vcu->dev, "wait for vpud killed %d\n",
|
||
|
vcu_ptr->vpud_killed.count);
|
||
|
ret = down_interruptible(&vcu_ptr->vpud_killed);
|
||
|
}
|
||
|
dev_info(&pdev->dev, "[VCU] vpud killed\n");
|
||
|
mutex_unlock(&vcu->vcu_mutex[i]);
|
||
|
return -EIO;
|
||
|
}
|
||
|
vcu->ipi_id_ack[id] = false;
|
||
|
|
||
|
if (id >= IPI_VCU_INIT && id < IPI_MAX) {
|
||
|
ipi_desc = vcu->ipi_desc;
|
||
|
ipi_desc[id].priv = priv;
|
||
|
}
|
||
|
|
||
|
/* send the command to VCU */
|
||
|
memcpy((void *)vcu->user_obj[i].share_buf, buf, len);
|
||
|
vcu->user_obj[i].len = len;
|
||
|
vcu->user_obj[i].id = (int)id;
|
||
|
atomic_set(&vcu->ipi_got[i], 1);
|
||
|
atomic_set(&vcu->ipi_done[i], 0);
|
||
|
wake_up(&vcu->get_wq[i]);
|
||
|
|
||
|
/* wait for VCU's ACK */
|
||
|
timeout = msecs_to_jiffies(IPI_TIMEOUT_MS);
|
||
|
ret = wait_event_timeout(vcu->ack_wq[i], vcu->ipi_id_ack[id], timeout);
|
||
|
vcu->ipi_id_ack[id] = false;
|
||
|
|
||
|
if (vcu_ptr->abort || ret == 0) {
|
||
|
dev_info(&pdev->dev, "vcu ipi %d ack time out !%d", id, ret);
|
||
|
if (!vcu_ptr->abort) {
|
||
|
send_sig(SIGTERM, vcud_task, 0);
|
||
|
send_sig(SIGKILL, vcud_task, 0);
|
||
|
}
|
||
|
if (vcu_ptr->open_cnt > 0) {
|
||
|
dev_info(vcu->dev, "wait for vpud killed %d\n",
|
||
|
vcu_ptr->vpud_killed.count);
|
||
|
ret = down_interruptible(&vcu_ptr->vpud_killed);
|
||
|
}
|
||
|
dev_info(&pdev->dev, "[VCU] vpud killed\n");
|
||
|
ret = -EIO;
|
||
|
mutex_unlock(&vcu->vcu_mutex[i]);
|
||
|
goto end;
|
||
|
} else if (-ERESTARTSYS == ret) {
|
||
|
dev_err(&pdev->dev, "vcu ipi %d ack wait interrupted by a signal",
|
||
|
id);
|
||
|
ret = -ERESTARTSYS;
|
||
|
mutex_unlock(&vcu->vcu_mutex[i]);
|
||
|
goto end;
|
||
|
} else {
|
||
|
ret = 0;
|
||
|
mutex_unlock(&vcu->vcu_mutex[i]);
|
||
|
}
|
||
|
|
||
|
/* Waiting ipi_done, success means the daemon receiver thread
|
||
|
* dispatchs ipi msg done and returns to kernel for get next
|
||
|
* ipi msg.
|
||
|
* The dispatched ipi msg is being processed by app service.
|
||
|
* Usually, it takes dozens of microseconds in average.
|
||
|
*/
|
||
|
while (atomic_read(&vcu->ipi_done[i]) == 0)
|
||
|
cond_resched();
|
||
|
|
||
|
end:
|
||
|
return ret;
|
||
|
}
|
||
|
EXPORT_SYMBOL_GPL(vcu_ipi_send);
|
||
|
|
||
|
static int vcu_ipi_get(struct mtk_vcu *vcu, unsigned long arg)
|
||
|
{
|
||
|
unsigned int i = 0;
|
||
|
int ret;
|
||
|
unsigned char *user_data_addr = NULL;
|
||
|
struct share_obj share_buff_data;
|
||
|
|
||
|
user_data_addr = (unsigned char *)arg;
|
||
|
ret = (long)copy_from_user(&share_buff_data, user_data_addr,
|
||
|
(unsigned long)sizeof(struct share_obj));
|
||
|
if (ret != 0) {
|
||
|
pr_info("[VCU] %s(%d) Copy data from user failed!\n",
|
||
|
__func__, __LINE__);
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
|
||
|
i = ipi_id_to_inst_id(share_buff_data.id);
|
||
|
|
||
|
/* mutex protection here is unnecessary, since different app service
|
||
|
* threads of daemon are corresponding to different vcu_ipi_get thread.
|
||
|
* Different threads use differnet variables, e.g. ipi_done.
|
||
|
*/
|
||
|
atomic_set(&vcu->ipi_done[i], 1);
|
||
|
ret = wait_event_freezable(vcu->get_wq[i],
|
||
|
atomic_read(&vcu->ipi_got[i]));
|
||
|
|
||
|
if (ret != 0) {
|
||
|
pr_info("[VCU][%d][%d] wait event return %d @%s\n",
|
||
|
vcu->vcuid, i, ret, __func__);
|
||
|
return ret;
|
||
|
}
|
||
|
ret = copy_to_user(user_data_addr, &vcu->user_obj[i],
|
||
|
(unsigned long)sizeof(struct share_obj));
|
||
|
if (ret != 0) {
|
||
|
pr_info("[VCU] %s(%d) Copy data to user failed!\n",
|
||
|
__func__, __LINE__);
|
||
|
ret = -EINVAL;
|
||
|
}
|
||
|
atomic_set(&vcu->ipi_got[i], 0);
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
static int vcu_log_get(struct mtk_vcu *vcu, unsigned long arg)
|
||
|
{
|
||
|
int ret;
|
||
|
unsigned char *user_data_addr = NULL;
|
||
|
|
||
|
user_data_addr = (unsigned char *)arg;
|
||
|
|
||
|
ret = wait_event_freezable(vcu->vdec_log_get_wq,
|
||
|
atomic_read(&vcu->vdec_log_got));
|
||
|
if (ret != 0) {
|
||
|
pr_info("[VCU][%d] wait event return %d @%s\n",
|
||
|
vcu->vcuid, ret, __func__);
|
||
|
return ret;
|
||
|
}
|
||
|
ret = copy_to_user(user_data_addr, vcu->vdec_log_info,
|
||
|
(unsigned long)sizeof(struct log_test_nofuse));
|
||
|
if (ret != 0) {
|
||
|
pr_info("[VCU] %s(%d) Copy data to user failed!\n",
|
||
|
__func__, __LINE__);
|
||
|
ret = -EINVAL;
|
||
|
}
|
||
|
atomic_set(&vcu->vdec_log_got, 0);
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
|
||
|
static int vcu_gce_set_inst_id(void *ctx, u64 gce_handle)
|
||
|
{
|
||
|
int i;
|
||
|
char data;
|
||
|
|
||
|
mutex_lock(&vcu_ptr->vcu_share);
|
||
|
for (i = 0; i < VCODEC_INST_MAX; i++) {
|
||
|
if (vcu_ptr->gce_info[i].v4l2_ctx == NULL &&
|
||
|
!probe_kernel_address(ctx, data)) {
|
||
|
vcu_ptr->gce_info[i].v4l2_ctx = ctx;
|
||
|
vcu_ptr->gce_info[i].user_hdl = gce_handle;
|
||
|
mutex_unlock(&vcu_ptr->vcu_share);
|
||
|
vcu_dbg_log("[VCU] %s ctx %p hndl %llu create id %d\n",
|
||
|
__func__, ctx, gce_handle, i);
|
||
|
return i;
|
||
|
}
|
||
|
}
|
||
|
mutex_unlock(&vcu_ptr->vcu_share);
|
||
|
pr_info("[VCU] %s fail ctx %p hndl %llu\n",
|
||
|
__func__, ctx, gce_handle);
|
||
|
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
|
||
|
static int vcu_gce_get_inst_id(u64 gce_handle)
|
||
|
{
|
||
|
int i, temp;
|
||
|
|
||
|
mutex_lock(&vcu_ptr->vcu_share);
|
||
|
for (i = 0; i < VCODEC_INST_MAX; i++) {
|
||
|
if (vcu_ptr->gce_info[i].user_hdl == gce_handle) {
|
||
|
temp = atomic_read(&vcu_ptr->gce_info[i].flush_done);
|
||
|
mutex_unlock(&vcu_ptr->vcu_share);
|
||
|
vcu_dbg_log("[VCU] %s hndl %llu get id %d cnt %d\n",
|
||
|
__func__, gce_handle, i, temp);
|
||
|
return i;
|
||
|
}
|
||
|
}
|
||
|
mutex_unlock(&vcu_ptr->vcu_share);
|
||
|
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
static void vcu_gce_clear_inst_id(void *ctx)
|
||
|
{
|
||
|
int i, temp, temp2;
|
||
|
u64 gce_handle;
|
||
|
|
||
|
mutex_lock(&vcu_ptr->vcu_share);
|
||
|
for (i = 0; i < VCODEC_INST_MAX; i++) {
|
||
|
if (vcu_ptr->gce_info[i].v4l2_ctx == ctx) {
|
||
|
gce_handle = vcu_ptr->gce_info[i].user_hdl;
|
||
|
vcu_ptr->gce_info[i].v4l2_ctx = NULL;
|
||
|
vcu_ptr->gce_info[i].user_hdl = 0;
|
||
|
temp = atomic_read(&vcu_ptr->gce_info[i].flush_pending);
|
||
|
/* flush_pending > 0, ctx hw not unprepared */
|
||
|
temp2 = atomic_read(&vcu_ptr->gce_info[i].flush_done);
|
||
|
/* flush_done > 0, user event not waited */
|
||
|
atomic_set(&vcu_ptr->gce_info[i].flush_done, 0);
|
||
|
atomic_set(&vcu_ptr->gce_info[i].flush_pending, 0);
|
||
|
mutex_unlock(&vcu_ptr->vcu_share);
|
||
|
if (temp > 0)
|
||
|
vcu_aee_print(
|
||
|
"%s %p hndl %llu free id %d cnt %d %d\n",
|
||
|
__func__, ctx, gce_handle,
|
||
|
i, temp, temp2);
|
||
|
else if (temp2 > 0)
|
||
|
pr_info("%s %p hndl %llu free id %d cnt %d %d\n",
|
||
|
__func__, ctx, gce_handle,
|
||
|
i, temp, temp2);
|
||
|
else
|
||
|
vcu_dbg_log(
|
||
|
"%s %p hndl %llu free id %d cnt %d %d\n",
|
||
|
__func__, ctx, gce_handle,
|
||
|
i, temp, temp2);
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
mutex_unlock(&vcu_ptr->vcu_share);
|
||
|
|
||
|
pr_info("%s ctx %p not found!\n", __func__, ctx);
|
||
|
}
|
||
|
|
||
|
static void *vcu_check_gce_pa_base(struct mtk_vcu_queue *vcu_queue,
|
||
|
u64 addr, u64 length, bool null_err)
|
||
|
{
|
||
|
struct vcu_pa_pages *tmp;
|
||
|
struct list_head *p, *q;
|
||
|
|
||
|
list_for_each_safe(p, q, &vcu_queue->pa_pages.list) {
|
||
|
tmp = list_entry(p, struct vcu_pa_pages, list);
|
||
|
if (addr >= (u64)tmp->pa &&
|
||
|
addr + length <= (u64)tmp->pa + PAGE_SIZE)
|
||
|
return tmp;
|
||
|
}
|
||
|
if (null_err)
|
||
|
pr_info("%s addr %llx length %llx not found!\n", __func__, addr, length);
|
||
|
else
|
||
|
vcu_dbg_log("%s addr %llx length %llx not found!\n", __func__, addr, length);
|
||
|
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
static void vcu_gce_add_used_page(struct vcu_page_info *used_pages,
|
||
|
struct vcu_pa_pages *page)
|
||
|
{
|
||
|
struct vcu_page_info *page_info;
|
||
|
|
||
|
page_info = kmalloc(sizeof(struct vcu_page_info), GFP_KERNEL);
|
||
|
if (!page_info)
|
||
|
return;
|
||
|
|
||
|
atomic_inc(&page->ref_cnt);
|
||
|
page_info->page = page;
|
||
|
list_add_tail(&page_info->list, &used_pages->list);
|
||
|
}
|
||
|
|
||
|
static void vcu_gce_release_used_pages(struct vcu_page_info *used_pages)
|
||
|
{
|
||
|
struct vcu_page_info *page_info;
|
||
|
struct list_head *p, *q;
|
||
|
|
||
|
list_for_each_safe(p, q, &used_pages->list) {
|
||
|
page_info = list_entry(p, struct vcu_page_info, list);
|
||
|
list_del(p);
|
||
|
atomic_dec(&page_info->page->ref_cnt);
|
||
|
kfree(page_info);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static int vcu_check_reg_base(struct mtk_vcu *vcu, u64 addr, u64 length)
|
||
|
{
|
||
|
int i;
|
||
|
|
||
|
if (vcu->vcuid != 0 || addr >= MAP_PA_BASE_1GB)
|
||
|
return -EINVAL;
|
||
|
|
||
|
for (i = 0; i < (int)VCU_MAP_HW_REG_NUM; i++)
|
||
|
if (addr >= (u64)vcu->map_base[i].base &&
|
||
|
addr + length <= (u64)vcu->map_base[i].base + vcu->map_base[i].len)
|
||
|
return 0;
|
||
|
pr_info("%s addr %llx length %llx not found!\n", __func__, addr, length);
|
||
|
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
|
||
|
static void vcu_set_gce_cmd(struct cmdq_pkt *pkt,
|
||
|
struct mtk_vcu *vcu, unsigned int gce_index, unsigned int gce_order,
|
||
|
struct mtk_vcu_queue *q,
|
||
|
unsigned char cmd, u64 addr, u64 data, u32 mask, u32 gpr, u32 dma_offset, u32 dma_size)
|
||
|
{
|
||
|
void *src_page, *dst_page;
|
||
|
int reg_check;
|
||
|
|
||
|
switch (cmd) {
|
||
|
case CMD_READ:
|
||
|
if (vcu_check_reg_base(vcu, addr, 4) == 0)
|
||
|
cmdq_pkt_read_addr(pkt, addr, CMDQ_THR_SPR_IDX1);
|
||
|
else
|
||
|
pr_info("[VCU] CMD_READ wrong addr: 0x%llx\n", addr);
|
||
|
break;
|
||
|
case CMD_WRITE:
|
||
|
if (vcu_check_reg_base(vcu, addr, 4) == 0)
|
||
|
cmdq_pkt_write(pkt, vcu->clt_base, addr, data, mask);
|
||
|
else
|
||
|
pr_info("[VCU] CMD_WRITE wrong addr: 0x%llx 0x%llx 0x%x\n",
|
||
|
addr, data, mask);
|
||
|
break;
|
||
|
#if defined(CONFIG_MTK_SEC_VIDEO_PATH_SUPPORT)
|
||
|
case CMD_SEC_WRITE:
|
||
|
#if defined(CONFIG_MTK_CMDQ_MBOX_EXT)
|
||
|
if (vcu_check_reg_base(vcu, addr, 4) == 0) {
|
||
|
#if defined(CONFIG_MTK_SVP_ON_MTEE_SUPPORT)
|
||
|
cmdq_sec_pkt_write_reg(pkt,
|
||
|
addr,
|
||
|
data,
|
||
|
CMDQ_IWC_H_2_MVA,
|
||
|
dma_offset,
|
||
|
dma_size,
|
||
|
0,
|
||
|
SEC_ID_WFD);
|
||
|
#endif
|
||
|
} else {
|
||
|
pr_info("[VCU] CMD_SEC_WRITE wrong addr: 0x%x 0x%x 0x%x 0x%x\n",
|
||
|
addr, data, dma_offset, dma_size);
|
||
|
}
|
||
|
#endif
|
||
|
pr_debug("[VCU] %s addr: 0x%x, data: 0x%x, offset: 0x%x, size: 0x%x\n",
|
||
|
__func__, addr, data, dma_offset, dma_size);
|
||
|
break;
|
||
|
#endif
|
||
|
case CMD_POLL_REG:
|
||
|
if (vcu_check_reg_base(vcu, addr, 4) == 0)
|
||
|
cmdq_pkt_poll_addr(pkt, data, addr, mask, gpr);
|
||
|
else
|
||
|
pr_info("[VCU] CMD_POLL_REG wrong addr: 0x%llx 0x%llx 0x%x\n",
|
||
|
addr, data, mask);
|
||
|
break;
|
||
|
case CMD_WAIT_EVENT:
|
||
|
if (data < GCE_EVENT_MAX)
|
||
|
cmdq_pkt_wfe(pkt, vcu->gce_codec_eid[data]);
|
||
|
else
|
||
|
pr_info("[VCU] %s got wrong eid %llu\n",
|
||
|
__func__, data);
|
||
|
break;
|
||
|
case CMD_MEM_MV:
|
||
|
mutex_lock(&q->mmap_lock);
|
||
|
reg_check = vcu_check_reg_base(vcu, addr, 4);
|
||
|
src_page = vcu_check_gce_pa_base(q, addr, 4, reg_check != 0);
|
||
|
dst_page = vcu_check_gce_pa_base(q, data, 4, true);
|
||
|
if ((reg_check == 0 || src_page != NULL) && dst_page != NULL) {
|
||
|
if (src_page != NULL)
|
||
|
vcu_gce_add_used_page(
|
||
|
&vcu->gce_info[gce_index].used_pages[gce_order], src_page);
|
||
|
vcu_gce_add_used_page(
|
||
|
&vcu->gce_info[gce_index].used_pages[gce_order], dst_page);
|
||
|
cmdq_pkt_mem_move(pkt, vcu->clt_base, addr, data, CMDQ_THR_SPR_IDX1);
|
||
|
} else
|
||
|
pr_info("[VCU] CMD_MEM_MV wrong addr/data: 0x%llx 0x%llx\n",
|
||
|
addr, data);
|
||
|
mutex_unlock(&q->mmap_lock);
|
||
|
break;
|
||
|
case CMD_POLL_ADDR:
|
||
|
mutex_lock(&q->mmap_lock);
|
||
|
reg_check = vcu_check_reg_base(vcu, addr, 4);
|
||
|
src_page = vcu_check_gce_pa_base(q, addr, 4, reg_check != 0);
|
||
|
if (reg_check == 0 || src_page != NULL) {
|
||
|
if (src_page != NULL)
|
||
|
vcu_gce_add_used_page(
|
||
|
&vcu->gce_info[gce_index].used_pages[gce_order], src_page);
|
||
|
cmdq_pkt_poll_timeout(pkt, data, SUBSYS_NO_SUPPORT, addr, mask, ~0, gpr);
|
||
|
} else
|
||
|
pr_info("[VCU] CMD_POLL_REG wrong addr: 0x%llx 0x%llx 0x%x\n",
|
||
|
addr, data, mask);
|
||
|
mutex_unlock(&q->mmap_lock);
|
||
|
break;
|
||
|
default:
|
||
|
vcu_dbg_log("[VCU] unknown GCE cmd %d\n", cmd);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void vcu_gce_flush_callback(struct cmdq_cb_data data)
|
||
|
{
|
||
|
int i, j;
|
||
|
struct gce_callback_data *buff;
|
||
|
struct mtk_vcu *vcu;
|
||
|
unsigned int core_id;
|
||
|
unsigned int gce_order;
|
||
|
|
||
|
buff = (struct gce_callback_data *)data.data;
|
||
|
i = (buff->cmdq_buff.codec_type == VCU_VDEC) ? VCU_VDEC : VCU_VENC;
|
||
|
core_id = buff->cmdq_buff.core_id;
|
||
|
|
||
|
vcu = buff->vcu_ptr;
|
||
|
j = vcu_gce_get_inst_id(buff->cmdq_buff.gce_handle);
|
||
|
|
||
|
if (j < 0) {
|
||
|
pr_info("[VCU] flush_callback get_inst_id fail!!%d\n", j);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
atomic_inc(&vcu->gce_info[j].flush_done);
|
||
|
atomic_dec(&vcu->gce_info[j].flush_pending);
|
||
|
|
||
|
mutex_lock(&vcu->vcu_gce_mutex[i]);
|
||
|
if (i == VCU_VENC) {
|
||
|
venc_encode_pmqos_gce_end(vcu->gce_info[j].v4l2_ctx, core_id,
|
||
|
vcu->gce_job_cnt[i][core_id].counter);
|
||
|
}
|
||
|
if (atomic_dec_and_test(&vcu->gce_job_cnt[i][core_id]) &&
|
||
|
j >= 0 &&
|
||
|
vcu->gce_info[j].v4l2_ctx != NULL){
|
||
|
if (i == VCU_VENC) {
|
||
|
venc_encode_unprepare(vcu->gce_info[j].v4l2_ctx,
|
||
|
buff->cmdq_buff.core_id, &vcu->flags[i]);
|
||
|
#if defined(CONFIG_MTK_SEC_VIDEO_PATH_SUPPORT)
|
||
|
#if !(IS_ENABLED(CONFIG_MACH_MT6768) || IS_ENABLED(CONFIG_MACH_MT6779) ||\
|
||
|
IS_ENABLED(CONFIG_MACH_MT6785) || IS_ENABLED(CONFIG_MACH_MT8168))
|
||
|
if (buff->cmdq_buff.secure != 0)
|
||
|
cmdq_sec_mbox_switch_normal(vcu->clt_venc_sec[0],
|
||
|
(buff->cmdq_buff.secure == 3));
|
||
|
#endif
|
||
|
#endif
|
||
|
venc_unlock(vcu->gce_info[j].v4l2_ctx,
|
||
|
buff->cmdq_buff.core_id);
|
||
|
}
|
||
|
}
|
||
|
mutex_unlock(&vcu->vcu_gce_mutex[i]);
|
||
|
|
||
|
gce_order = buff->cmdq_buff.flush_order % GCE_PENDING_CNT;
|
||
|
vcu_gce_release_used_pages(&vcu->gce_info[j].used_pages[gce_order]);
|
||
|
|
||
|
wake_up(&vcu->gce_wq[i]);
|
||
|
|
||
|
vcu_dbg_log("[VCU][%d] %s: buff %p type %d order %d handle %llx\n",
|
||
|
core_id, __func__, buff, buff->cmdq_buff.codec_type,
|
||
|
buff->cmdq_buff.flush_order, buff->cmdq_buff.gce_handle);
|
||
|
|
||
|
cmdq_pkt_destroy(buff->pkt_ptr);
|
||
|
up(&vcu->gce_info[j].buff_sem[gce_order]);
|
||
|
}
|
||
|
|
||
|
static void vcu_gce_timeout_callback(struct cmdq_cb_data data)
|
||
|
{
|
||
|
struct gce_callback_data *buff;
|
||
|
struct mtk_vcu *vcu;
|
||
|
struct list_head *p, *q;
|
||
|
struct mtk_vcu_queue *vcu_queue;
|
||
|
struct vcu_pa_pages *tmp;
|
||
|
|
||
|
buff = (struct gce_callback_data *)data.data;
|
||
|
vcu = buff->vcu_ptr;
|
||
|
vcu_queue = buff->vcu_queue;
|
||
|
vcu_dbg_log("%s: buff %p vcu: %p, codec_typ: %d\n",
|
||
|
__func__, buff, vcu, buff->cmdq_buff.codec_type);
|
||
|
|
||
|
if (buff->cmdq_buff.codec_type == VCU_VENC)
|
||
|
mtk_vcodec_gce_timeout_dump(vcu->curr_ctx[VCU_VENC]);
|
||
|
else if (buff->cmdq_buff.codec_type == VCU_VDEC)
|
||
|
mtk_vcodec_gce_timeout_dump(vcu->curr_ctx[VCU_VDEC]);
|
||
|
|
||
|
mutex_lock(&vcu_queue->mmap_lock);
|
||
|
list_for_each_safe(p, q, &vcu_queue->pa_pages.list) {
|
||
|
tmp = list_entry(p, struct vcu_pa_pages, list);
|
||
|
pr_info("%s: vcu_pa_pages %lx kva %lx data %lx\n",
|
||
|
__func__, tmp->pa, tmp->kva,
|
||
|
*(unsigned long *)tmp->kva);
|
||
|
}
|
||
|
mutex_unlock(&vcu_queue->mmap_lock);
|
||
|
|
||
|
}
|
||
|
|
||
|
static int vcu_gce_cmd_flush(struct mtk_vcu *vcu,
|
||
|
struct mtk_vcu_queue *q, unsigned long arg)
|
||
|
{
|
||
|
int i, codec_type, gce_idx, ret;
|
||
|
unsigned char *user_data_addr = NULL;
|
||
|
struct gce_callback_data buff;
|
||
|
struct cmdq_pkt *pkt_ptr;
|
||
|
struct cmdq_client *cl;
|
||
|
struct gce_cmds *cmds;
|
||
|
unsigned int suspend_block_cnt = 0;
|
||
|
unsigned int core_id;
|
||
|
unsigned int gce_order;
|
||
|
|
||
|
vcu_dbg_log("[VCU] %s +\n", __func__);
|
||
|
|
||
|
time_check_start();
|
||
|
user_data_addr = (unsigned char *)arg;
|
||
|
ret = (long)copy_from_user(&buff.cmdq_buff, user_data_addr,
|
||
|
(unsigned long)sizeof(struct gce_cmdq_obj));
|
||
|
if (ret != 0L) {
|
||
|
pr_info("[VCU] %s(%d) gce_cmdq_obj copy_from_user failed!%d\n",
|
||
|
__func__, __LINE__, ret);
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
|
||
|
codec_type = (buff.cmdq_buff.codec_type == VCU_VDEC) ? VCU_VDEC : VCU_VENC;
|
||
|
gce_order = buff.cmdq_buff.flush_order % GCE_PENDING_CNT;
|
||
|
cmds = vcu->gce_cmds[codec_type];
|
||
|
|
||
|
mutex_lock(&vcu->gce_cmds_mutex[codec_type]);
|
||
|
if (buff.cmdq_buff.cmds_user_ptr > 0) {
|
||
|
user_data_addr = (unsigned char *)
|
||
|
(unsigned long)buff.cmdq_buff.cmds_user_ptr;
|
||
|
ret = (long)copy_from_user(cmds, user_data_addr,
|
||
|
(unsigned long)sizeof(struct gce_cmds));
|
||
|
if (ret != 0L) {
|
||
|
pr_info("[VCU] %s(%d) gce_cmds copy_from_user failed!%d\n",
|
||
|
__func__, __LINE__, ret);
|
||
|
mutex_unlock(&vcu->gce_cmds_mutex[codec_type]);
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
} else {
|
||
|
mutex_unlock(&vcu->gce_cmds_mutex[codec_type]);
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
|
||
|
buff.cmdq_buff.cmds_user_ptr = (u64)(unsigned long)cmds;
|
||
|
core_id = buff.cmdq_buff.core_id;
|
||
|
|
||
|
if (buff.cmdq_buff.codec_type >= VCU_CODEC_MAX ||
|
||
|
core_id >=
|
||
|
vcu->gce_th_num[buff.cmdq_buff.codec_type]) {
|
||
|
pr_info("[VCU] %s invalid core(th) id %d\n",
|
||
|
__func__, core_id);
|
||
|
mutex_unlock(&vcu->gce_cmds_mutex[codec_type]);
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
|
||
|
cl = (buff.cmdq_buff.codec_type == VCU_VDEC) ?
|
||
|
vcu->clt_vdec[core_id] :
|
||
|
vcu->clt_venc[core_id];
|
||
|
|
||
|
#if defined(CONFIG_MTK_SEC_VIDEO_PATH_SUPPORT)
|
||
|
if (buff.cmdq_buff.codec_type == VCU_VENC) {
|
||
|
if (buff.cmdq_buff.secure != 0) {
|
||
|
cl = vcu->clt_venc_sec[0];
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
if (cl == NULL) {
|
||
|
pr_info("[VCU] %s gce thread is null id %d type %d\n",
|
||
|
__func__, core_id,
|
||
|
buff.cmdq_buff.codec_type);
|
||
|
mutex_unlock(&vcu->gce_cmds_mutex[codec_type]);
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
|
||
|
buff.vcu_ptr = vcu;
|
||
|
buff.vcu_queue = q;
|
||
|
|
||
|
while (vcu_ptr->is_entering_suspend == 1) {
|
||
|
suspend_block_cnt++;
|
||
|
if (suspend_block_cnt > 500) {
|
||
|
pr_info("[VCU] gce_flush blocked by suspend\n");
|
||
|
suspend_block_cnt = 0;
|
||
|
}
|
||
|
usleep_range(10000, 20000);
|
||
|
}
|
||
|
|
||
|
gce_idx = vcu_gce_get_inst_id(buff.cmdq_buff.gce_handle);
|
||
|
|
||
|
if (gce_idx < 0)
|
||
|
gce_idx = vcu_gce_set_inst_id(vcu->curr_ctx[codec_type],
|
||
|
buff.cmdq_buff.gce_handle);
|
||
|
if (gce_idx < 0) {
|
||
|
mutex_unlock(&vcu->gce_cmds_mutex[codec_type]);
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
time_check_end(100, strlen(vcodec_param_string));
|
||
|
|
||
|
time_check_start();
|
||
|
mutex_lock(&vcu->vcu_gce_mutex[codec_type]);
|
||
|
|
||
|
if (buff.cmdq_buff.codec_type == VCU_VENC) {
|
||
|
int lock = -1;
|
||
|
|
||
|
while (lock != 0) {
|
||
|
lock = venc_lock(vcu->gce_info[gce_idx].v4l2_ctx, core_id,
|
||
|
(bool)buff.cmdq_buff.secure);
|
||
|
if (lock != 0) {
|
||
|
mutex_unlock(&vcu->vcu_gce_mutex[codec_type]);
|
||
|
usleep_range(1000, 2000);
|
||
|
mutex_lock(&vcu->vcu_gce_mutex[codec_type]);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (atomic_read(&vcu->gce_job_cnt[codec_type][core_id]) == 0 &&
|
||
|
vcu->gce_info[gce_idx].v4l2_ctx != NULL){
|
||
|
if (codec_type == VCU_VENC) {
|
||
|
venc_encode_prepare(vcu->gce_info[gce_idx].v4l2_ctx,
|
||
|
core_id, &vcu->flags[codec_type]);
|
||
|
}
|
||
|
}
|
||
|
vcu_dbg_log("vcu gce_info[%d].v4l2_ctx %p\n",
|
||
|
gce_idx, (gce_idx >= 0) ? vcu->gce_info[gce_idx].v4l2_ctx : NULL);
|
||
|
if (codec_type == VCU_VENC) {
|
||
|
venc_encode_pmqos_gce_begin(vcu->gce_info[gce_idx].v4l2_ctx, core_id,
|
||
|
vcu->gce_job_cnt[codec_type][core_id].counter);
|
||
|
}
|
||
|
atomic_inc(&vcu->gce_job_cnt[codec_type][core_id]);
|
||
|
mutex_unlock(&vcu->vcu_gce_mutex[codec_type]);
|
||
|
time_check_end(100, strlen(vcodec_param_string));
|
||
|
|
||
|
time_check_start();
|
||
|
pkt_ptr = cmdq_pkt_create(cl);
|
||
|
if (IS_ERR_OR_NULL(pkt_ptr)) {
|
||
|
pr_info("[VCU] cmdq_pkt_create fail\n");
|
||
|
pkt_ptr = NULL;
|
||
|
}
|
||
|
buff.pkt_ptr = pkt_ptr;
|
||
|
|
||
|
if (cmds->cmd_cnt >= VCODEC_CMDQ_CMD_MAX) {
|
||
|
pr_info("[VCU] cmd_cnt (%d) overflow!!\n", cmds->cmd_cnt);
|
||
|
cmds->cmd_cnt = VCODEC_CMDQ_CMD_MAX;
|
||
|
ret = -EINVAL;
|
||
|
}
|
||
|
|
||
|
#if defined(CONFIG_MTK_SEC_VIDEO_PATH_SUPPORT)
|
||
|
if (buff.cmdq_buff.codec_type == VCU_VENC) {
|
||
|
if (buff.cmdq_buff.secure != 0) {
|
||
|
const u64 dapc_engine =
|
||
|
(1LL << CMDQ_SEC_VENC_BSDMA) |
|
||
|
(1LL << CMDQ_SEC_VENC_CUR_LUMA) |
|
||
|
(1LL << CMDQ_SEC_VENC_CUR_CHROMA) |
|
||
|
(1LL << CMDQ_SEC_VENC_REF_LUMA) |
|
||
|
(1LL << CMDQ_SEC_VENC_REF_CHROMA) |
|
||
|
(1LL << CMDQ_SEC_VENC_REC) |
|
||
|
(1LL << CMDQ_SEC_VENC_SV_COMV) |
|
||
|
(1LL << CMDQ_SEC_VENC_RD_COMV);
|
||
|
|
||
|
const u64 port_sec_engine =
|
||
|
(1LL << CMDQ_SEC_VENC_BSDMA) |
|
||
|
(1LL << CMDQ_SEC_VENC_CUR_LUMA) |
|
||
|
(1LL << CMDQ_SEC_VENC_CUR_CHROMA) |
|
||
|
(1LL << CMDQ_SEC_VENC_REF_LUMA) |
|
||
|
(1LL << CMDQ_SEC_VENC_REF_CHROMA) |
|
||
|
(1LL << CMDQ_SEC_VENC_REC) |
|
||
|
(1LL << CMDQ_SEC_VENC_SV_COMV) |
|
||
|
(1LL << CMDQ_SEC_VENC_RD_COMV);
|
||
|
|
||
|
pr_debug("[VCU] dapc_engine: 0x%llx, port_sec_engine: 0x%llx\n",
|
||
|
dapc_engine, port_sec_engine);
|
||
|
#if defined(CONFIG_MTK_CMDQ_MBOX_EXT)
|
||
|
cmdq_sec_pkt_set_data(pkt_ptr, dapc_engine,
|
||
|
port_sec_engine, CMDQ_SEC_KERNEL_CONFIG_GENERAL,
|
||
|
CMDQ_METAEX_VENC);
|
||
|
|
||
|
if (buff.cmdq_buff.secure == 3) {
|
||
|
#if defined(CONFIG_MTK_SVP_ON_MTEE_SUPPORT)
|
||
|
// CMDQ MTEE hint
|
||
|
pr_debug("[VCU] Use MTEE\n");
|
||
|
cmdq_sec_pkt_set_mtee(pkt_ptr, (buff.cmdq_buff.secure == 3),
|
||
|
SEC_ID_WFD);
|
||
|
#endif
|
||
|
}
|
||
|
#endif
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
for (i = 0; i < cmds->cmd_cnt; i++) {
|
||
|
vcu_set_gce_cmd(pkt_ptr, vcu, gce_idx, gce_order, q, cmds->cmd[i],
|
||
|
cmds->addr[i], cmds->data[i],
|
||
|
cmds->mask[i], vcu->gce_gpr[core_id],
|
||
|
cmds->dma_offset[i], cmds->dma_size[i]);
|
||
|
}
|
||
|
|
||
|
ret = down_interruptible(&vcu_ptr->gce_info[gce_idx].buff_sem[gce_order]);
|
||
|
if (ret < 0) {
|
||
|
mutex_unlock(&vcu->gce_cmds_mutex[codec_type]);
|
||
|
return -ERESTARTSYS;
|
||
|
}
|
||
|
memcpy(&vcu_ptr->gce_info[gce_idx].buff[gce_order], &buff, sizeof(buff));
|
||
|
|
||
|
pkt_ptr->err_cb.cb =
|
||
|
(buff.cmdq_buff.secure == 0)?vcu_gce_timeout_callback:NULL;
|
||
|
pkt_ptr->err_cb.data = (void *)&vcu_ptr->gce_info[gce_idx].buff[gce_order];
|
||
|
|
||
|
pr_info("[VCU][%d] %s: buff %p type %d cnt %d order %d pkt %p hndl %llx %d %d\n",
|
||
|
core_id, __func__, &vcu_ptr->gce_info[gce_idx].buff[gce_order],
|
||
|
buff.cmdq_buff.codec_type,
|
||
|
cmds->cmd_cnt, buff.cmdq_buff.flush_order, pkt_ptr,
|
||
|
buff.cmdq_buff.gce_handle, ret, gce_idx);
|
||
|
mutex_unlock(&vcu->gce_cmds_mutex[codec_type]);
|
||
|
|
||
|
/* flush cmd async */
|
||
|
ret = cmdq_pkt_flush_threaded(pkt_ptr,
|
||
|
vcu_gce_flush_callback, (void *)&vcu_ptr->gce_info[gce_idx].buff[gce_order]);
|
||
|
|
||
|
if (ret < 0) {
|
||
|
pr_info("[VCU] cmdq flush fail pkt %p\n", pkt_ptr);
|
||
|
vcu_gce_release_used_pages(&vcu->gce_info[gce_idx].used_pages[gce_order]);
|
||
|
up(&vcu_ptr->gce_info[gce_idx].buff_sem[gce_order]);
|
||
|
} else
|
||
|
atomic_inc(&vcu_ptr->gce_info[gce_idx].flush_pending);
|
||
|
time_check_end(100, strlen(vcodec_param_string));
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
static int vcu_wait_gce_callback(struct mtk_vcu *vcu, unsigned long arg)
|
||
|
{
|
||
|
int ret, i, j;
|
||
|
unsigned char *user_data_addr = NULL;
|
||
|
struct gce_obj obj;
|
||
|
|
||
|
user_data_addr = (unsigned char *)arg;
|
||
|
ret = (long)copy_from_user(&obj, user_data_addr,
|
||
|
(unsigned long)sizeof(struct gce_obj));
|
||
|
if (ret != 0L) {
|
||
|
pr_info("[VCU] %s(%d) copy_from_user failed!%d\n",
|
||
|
__func__, __LINE__, ret);
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
|
||
|
i = (obj.codec_type == VCU_VDEC) ? VCU_VDEC : VCU_VENC;
|
||
|
vcu_dbg_log("[VCU] %s: type %d handle %llx\n",
|
||
|
__func__, obj.codec_type, obj.gce_handle);
|
||
|
|
||
|
/* use wait_event_interruptible not freezable due to
|
||
|
* slowmotion GCE case vcu_gce_cmd_flush will hold
|
||
|
* mutex in user process which cannot be freezed
|
||
|
*/
|
||
|
j = vcu_gce_get_inst_id(obj.gce_handle);
|
||
|
|
||
|
if (j < 0)
|
||
|
return -EINVAL;
|
||
|
|
||
|
ret = wait_event_interruptible(vcu->gce_wq[i],
|
||
|
atomic_read(&vcu->gce_info[j].flush_done) > 0);
|
||
|
if (ret != 0) {
|
||
|
pr_info("[VCU][%d][%d] wait event return %d @%s\n",
|
||
|
vcu->vcuid, i, ret, __func__);
|
||
|
return ret;
|
||
|
}
|
||
|
atomic_dec(&vcu->gce_info[j].flush_done);
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
int vcu_get_ctx_ipi_binding_lock(struct platform_device *pdev,
|
||
|
struct mutex **mutex, unsigned long type)
|
||
|
{
|
||
|
struct mtk_vcu *vcu = platform_get_drvdata(pdev);
|
||
|
|
||
|
*mutex = &vcu->ctx_ipi_binding[type];
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int vcu_set_codec_ctx(struct platform_device *pdev,
|
||
|
void *codec_ctx, struct vb2_buffer *src_vb,
|
||
|
struct vb2_buffer *dst_vb, unsigned long type)
|
||
|
{
|
||
|
struct mtk_vcu *vcu = platform_get_drvdata(pdev);
|
||
|
pr_debug("[VCU] type %lu vcu_set_codec_ctx %p src_vb %p dst_vb %p\n",
|
||
|
type, codec_ctx, src_vb, dst_vb);
|
||
|
|
||
|
vcu_dbg_log("[VCU] %s %p type %lu src_vb %p dst_vb %p\n",
|
||
|
__func__, codec_ctx, type, src_vb, dst_vb);
|
||
|
|
||
|
vcu->curr_ctx[type] = codec_ctx;
|
||
|
vcu->curr_src_vb[type] = src_vb;
|
||
|
vcu->curr_dst_vb[type] = dst_vb;
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int vcu_clear_codec_ctx(struct platform_device *pdev,
|
||
|
void *codec_ctx, unsigned long type)
|
||
|
{
|
||
|
struct mtk_vcu *vcu = platform_get_drvdata(pdev);
|
||
|
|
||
|
vcu_dbg_log("[VCU] %s %p type %lu\n", __func__, codec_ctx, type);
|
||
|
|
||
|
mutex_lock(&vcu->vcu_gce_mutex[type]);
|
||
|
vcu_gce_clear_inst_id(codec_ctx);
|
||
|
vcu->curr_ctx[type] = NULL;
|
||
|
vcu->curr_src_vb[type] = NULL;
|
||
|
vcu->curr_dst_vb[type] = NULL;
|
||
|
mutex_unlock(&vcu->vcu_gce_mutex[type]);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
unsigned int vcu_get_vdec_hw_capa(struct platform_device *pdev)
|
||
|
{
|
||
|
struct mtk_vcu *vcu = platform_get_drvdata(pdev);
|
||
|
|
||
|
return vcu->run.dec_capability;
|
||
|
}
|
||
|
EXPORT_SYMBOL_GPL(vcu_get_vdec_hw_capa);
|
||
|
|
||
|
unsigned int vcu_get_venc_hw_capa(struct platform_device *pdev)
|
||
|
{
|
||
|
struct mtk_vcu *vcu = platform_get_drvdata(pdev);
|
||
|
|
||
|
return vcu->run.enc_capability;
|
||
|
}
|
||
|
EXPORT_SYMBOL_GPL(vcu_get_venc_hw_capa);
|
||
|
|
||
|
void *vcu_mapping_dm_addr(struct platform_device *pdev,
|
||
|
uintptr_t dtcm_dmem_addr)
|
||
|
{
|
||
|
struct mtk_vcu *vcu;
|
||
|
uintptr_t d_vma, d_va_start;
|
||
|
uintptr_t d_off, d_va;
|
||
|
|
||
|
if (!IS_ERR_OR_NULL(pdev))
|
||
|
vcu = platform_get_drvdata(pdev);
|
||
|
else {
|
||
|
dev_info(&pdev->dev, "[VCU] %s: Invalid pdev %p\n",
|
||
|
__func__, pdev);
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
d_vma = (uintptr_t)(dtcm_dmem_addr);
|
||
|
d_va_start = (uintptr_t)VCU_DMEM0_VIRT(vcu);
|
||
|
d_off = d_vma - VCU_DMEM0_VMA(vcu);
|
||
|
|
||
|
if (dtcm_dmem_addr == 0UL || d_off > VCU_DMEM0_LEN(vcu)) {
|
||
|
dev_info(&pdev->dev, "[VCU] %s: Invalid vma 0x%lx len %lx\n",
|
||
|
__func__, dtcm_dmem_addr, VCU_DMEM0_LEN(vcu));
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
d_va = d_va_start + d_off;
|
||
|
dev_dbg(&pdev->dev, "[VCU] %s: 0x%lx -> 0x%lx\n",
|
||
|
__func__, d_vma, d_va);
|
||
|
|
||
|
return (void *)d_va;
|
||
|
}
|
||
|
EXPORT_SYMBOL_GPL(vcu_mapping_dm_addr);
|
||
|
|
||
|
struct platform_device *vcu_get_plat_device(struct platform_device *pdev)
|
||
|
{
|
||
|
struct device *dev = &pdev->dev;
|
||
|
struct device_node *vcu_node = NULL;
|
||
|
struct platform_device *vcu_pdev = NULL;
|
||
|
|
||
|
dev_dbg(&pdev->dev, "[VCU] %s\n", __func__);
|
||
|
|
||
|
vcu_node = of_parse_phandle(dev->of_node, "mediatek,vcu", 0);
|
||
|
if (vcu_node == NULL) {
|
||
|
dev_err(dev, "[VCU] can't get vcu node\n");
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
vcu_pdev = of_find_device_by_node(vcu_node);
|
||
|
if (WARN_ON(vcu_pdev == NULL) == true) {
|
||
|
dev_err(dev, "[VCU] vcu pdev failed\n");
|
||
|
of_node_put(vcu_node);
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
return vcu_pdev;
|
||
|
}
|
||
|
EXPORT_SYMBOL_GPL(vcu_get_plat_device);
|
||
|
|
||
|
int vcu_load_firmware(struct platform_device *pdev)
|
||
|
{
|
||
|
if (pdev == NULL) {
|
||
|
dev_err(&pdev->dev, "[VCU] VCU platform device is invalid\n");
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
EXPORT_SYMBOL_GPL(vcu_load_firmware);
|
||
|
|
||
|
int vcu_compare_version(struct platform_device *pdev,
|
||
|
const char *expected_version)
|
||
|
{
|
||
|
struct mtk_vcu *vcu = platform_get_drvdata(pdev);
|
||
|
int cur_major, cur_minor, cur_build, cur_rel, cur_ver_num;
|
||
|
int major, minor, build, rel, ver_num;
|
||
|
char *cur_version = vcu->run.fw_ver;
|
||
|
|
||
|
cur_ver_num = sscanf(cur_version, "%d.%d.%d-rc%d",
|
||
|
&cur_major, &cur_minor, &cur_build, &cur_rel);
|
||
|
if (cur_ver_num < 3)
|
||
|
return -1;
|
||
|
ver_num = sscanf(expected_version, "%d.%d.%d-rc%d",
|
||
|
&major, &minor, &build, &rel);
|
||
|
if (ver_num < 3)
|
||
|
return -1;
|
||
|
|
||
|
if (cur_major < major)
|
||
|
return -1;
|
||
|
if (cur_major > major)
|
||
|
return 1;
|
||
|
|
||
|
if (cur_minor < minor)
|
||
|
return -1;
|
||
|
if (cur_minor > minor)
|
||
|
return 1;
|
||
|
|
||
|
if (cur_build < build)
|
||
|
return -1;
|
||
|
if (cur_build > build)
|
||
|
return 1;
|
||
|
|
||
|
if (cur_ver_num < ver_num)
|
||
|
return -1;
|
||
|
if (cur_ver_num > ver_num)
|
||
|
return 1;
|
||
|
|
||
|
if (ver_num > 3) {
|
||
|
if (cur_rel < rel)
|
||
|
return -1;
|
||
|
if (cur_rel > rel)
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
EXPORT_SYMBOL_GPL(vcu_compare_version);
|
||
|
|
||
|
void vcu_get_file_lock(void)
|
||
|
{
|
||
|
mutex_lock(&vpud_file_mutex);
|
||
|
}
|
||
|
EXPORT_SYMBOL_GPL(vcu_get_file_lock);
|
||
|
|
||
|
void vcu_put_file_lock(void)
|
||
|
{
|
||
|
mutex_unlock(&vpud_file_mutex);
|
||
|
}
|
||
|
EXPORT_SYMBOL_GPL(vcu_put_file_lock);
|
||
|
|
||
|
void vcu_get_gce_lock(struct platform_device *pdev, unsigned long codec_type)
|
||
|
{
|
||
|
struct mtk_vcu *vcu = NULL;
|
||
|
|
||
|
if (pdev == NULL) {
|
||
|
pr_info("[VCU] %s platform device is null.\n", __func__);
|
||
|
return;
|
||
|
}
|
||
|
if (codec_type >= VCU_CODEC_MAX) {
|
||
|
pr_info("[VCU] %s invalid codec type %ld.\n", __func__, codec_type);
|
||
|
return;
|
||
|
}
|
||
|
vcu = platform_get_drvdata(pdev);
|
||
|
mutex_lock(&vcu->vcu_gce_mutex[codec_type]);
|
||
|
}
|
||
|
EXPORT_SYMBOL_GPL(vcu_get_gce_lock);
|
||
|
|
||
|
void vcu_put_gce_lock(struct platform_device *pdev, unsigned long codec_type)
|
||
|
{
|
||
|
struct mtk_vcu *vcu = NULL;
|
||
|
|
||
|
if (pdev == NULL) {
|
||
|
pr_info("[VCU] %s platform device is null.\n", __func__);
|
||
|
return;
|
||
|
}
|
||
|
if (codec_type >= VCU_CODEC_MAX) {
|
||
|
pr_info("[VCU] %s invalid codec type %ld.\n", __func__, codec_type);
|
||
|
return;
|
||
|
}
|
||
|
vcu = platform_get_drvdata(pdev);
|
||
|
mutex_unlock(&vcu->vcu_gce_mutex[codec_type]);
|
||
|
}
|
||
|
EXPORT_SYMBOL_GPL(vcu_put_gce_lock);
|
||
|
|
||
|
int vcu_get_sig_lock(unsigned long *flags)
|
||
|
{
|
||
|
return spin_trylock_irqsave(&vcu_ptr->vpud_sig_lock, *flags);
|
||
|
}
|
||
|
EXPORT_SYMBOL_GPL(vcu_get_sig_lock);
|
||
|
|
||
|
void vcu_put_sig_lock(unsigned long flags)
|
||
|
{
|
||
|
spin_unlock_irqrestore(&vcu_ptr->vpud_sig_lock, flags);
|
||
|
}
|
||
|
EXPORT_SYMBOL_GPL(vcu_put_sig_lock);
|
||
|
|
||
|
int vcu_check_vpud_alive(void)
|
||
|
{
|
||
|
return (vcu_ptr->vpud_is_going_down > 0) ? 0:1;
|
||
|
}
|
||
|
EXPORT_SYMBOL_GPL(vcu_check_vpud_alive);
|
||
|
|
||
|
void vcu_get_task(struct task_struct **task, struct files_struct **f,
|
||
|
int reset)
|
||
|
{
|
||
|
vcu_dbg_log("mtk_vcu_get_task %p\n", vcud_task);
|
||
|
|
||
|
if (reset == 1) {
|
||
|
vcud_task = NULL;
|
||
|
files = NULL;
|
||
|
}
|
||
|
|
||
|
if (task)
|
||
|
*task = vcud_task;
|
||
|
if (f)
|
||
|
*f = files;
|
||
|
}
|
||
|
EXPORT_SYMBOL_GPL(vcu_get_task);
|
||
|
|
||
|
static int vcu_ipi_handler(struct mtk_vcu *vcu, struct share_obj *rcv_obj)
|
||
|
{
|
||
|
struct vcu_ipi_desc *ipi_desc = vcu->ipi_desc;
|
||
|
int non_ack = 0;
|
||
|
int ret = -1;
|
||
|
unsigned int i = 0;
|
||
|
|
||
|
i = ipi_id_to_inst_id(rcv_obj->id);
|
||
|
|
||
|
if (rcv_obj->id < (int)IPI_MAX &&
|
||
|
ipi_desc[rcv_obj->id].handler != NULL) {
|
||
|
non_ack = ipi_desc[rcv_obj->id].handler(rcv_obj->share_buf,
|
||
|
rcv_obj->len,
|
||
|
ipi_desc[rcv_obj->id].priv);
|
||
|
if (rcv_obj->id > (int)IPI_VCU_INIT && non_ack == 0) {
|
||
|
vcu->ipi_id_ack[rcv_obj->id] = true;
|
||
|
wake_up(&vcu->ack_wq[i]);
|
||
|
}
|
||
|
ret = 0;
|
||
|
} else
|
||
|
dev_err(vcu->dev, "[VCU] No such ipi id = %d\n", rcv_obj->id);
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
static int vcu_ipi_init(struct mtk_vcu *vcu)
|
||
|
{
|
||
|
vcu->is_open = false;
|
||
|
mutex_init(&vcu->vcu_mutex[VCU_VDEC]);
|
||
|
mutex_init(&vcu->vcu_mutex[VCU_VENC]);
|
||
|
mutex_init(&vcu->vcu_gce_mutex[VCU_VDEC]);
|
||
|
mutex_init(&vcu->vcu_gce_mutex[VCU_VENC]);
|
||
|
mutex_init(&vcu->ctx_ipi_binding[VCU_VDEC]);
|
||
|
mutex_init(&vcu->ctx_ipi_binding[VCU_VENC]);
|
||
|
mutex_init(&vcu->vcu_share);
|
||
|
mutex_init(&vpud_file_mutex);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int vcu_init_ipi_handler(void *data, unsigned int len, void *priv)
|
||
|
{
|
||
|
struct mtk_vcu *vcu = (struct mtk_vcu *)priv;
|
||
|
struct vcu_run *run = (struct vcu_run *)data;
|
||
|
int wait_cnt = 0;
|
||
|
|
||
|
/* handle uninitialize message */
|
||
|
if (vcu->run.signaled == 1u && run->signaled == 0u) {
|
||
|
int i;
|
||
|
/* wake up the threads in daemon
|
||
|
* clear all pending ipi_msg
|
||
|
* release worker waiting timeout
|
||
|
*/
|
||
|
vcu->abort = true;
|
||
|
for (i = 0; i < IPI_MAX; i++)
|
||
|
vcu->ipi_id_ack[i] = true;
|
||
|
|
||
|
/* wait for GCE done & let IPI ack power off */
|
||
|
while (
|
||
|
atomic_read(&vcu_ptr->gce_job_cnt[VCU_VDEC][0]) > 0 ||
|
||
|
atomic_read(&vcu_ptr->gce_job_cnt[VCU_VDEC][1]) > 0 ||
|
||
|
atomic_read(&vcu_ptr->gce_job_cnt[VCU_VENC][0]) > 0 ||
|
||
|
atomic_read(&vcu_ptr->gce_job_cnt[VCU_VENC][1]) > 0) {
|
||
|
wait_cnt++;
|
||
|
if (wait_cnt > 5) {
|
||
|
pr_info("[VCU] Vpud killed gce status %d %d\n",
|
||
|
atomic_read(
|
||
|
&vcu_ptr->gce_job_cnt[VCU_VDEC][0]),
|
||
|
atomic_read(
|
||
|
&vcu_ptr->gce_job_cnt[VCU_VENC][0]));
|
||
|
break;
|
||
|
}
|
||
|
usleep_range(10000, 20000);
|
||
|
}
|
||
|
|
||
|
for (i = 0; i < VCU_CODEC_MAX; i++) {
|
||
|
atomic_set(&vcu->ipi_got[i], 1);
|
||
|
atomic_set(&vcu->ipi_done[i], 0);
|
||
|
memset(&vcu->user_obj[i], 0,
|
||
|
sizeof(struct share_obj));
|
||
|
wake_up(&vcu->get_wq[i]);
|
||
|
wake_up(&vcu->ack_wq[i]);
|
||
|
}
|
||
|
|
||
|
atomic_set(&vcu->vdec_log_got, 1);
|
||
|
wake_up(&vcu->vdec_log_get_wq);
|
||
|
vcu_get_file_lock();
|
||
|
vcu_get_task(NULL, NULL, 1);
|
||
|
vcu_put_file_lock();
|
||
|
|
||
|
dev_info(vcu->dev, "[VCU] vpud killing\n");
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
vcu->run.signaled = run->signaled;
|
||
|
strncpy(vcu->run.fw_ver, run->fw_ver, VCU_FW_VER_LEN);
|
||
|
vcu->run.dec_capability = run->dec_capability;
|
||
|
vcu->run.enc_capability = run->enc_capability;
|
||
|
|
||
|
dev_dbg(vcu->dev, "[VCU] fw ver: %s\n", vcu->run.fw_ver);
|
||
|
dev_dbg(vcu->dev, "[VCU] dec cap: %x\n", vcu->run.dec_capability);
|
||
|
dev_dbg(vcu->dev, "[VCU] enc cap: %x\n", vcu->run.enc_capability);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int mtk_vcu_open(struct inode *inode, struct file *file)
|
||
|
{
|
||
|
int vcuid = 0;
|
||
|
struct mtk_vcu_queue *vcu_queue;
|
||
|
|
||
|
if (strcmp(current->comm, "camd") == 0)
|
||
|
vcuid = 2;
|
||
|
else if (strcmp(current->comm, "mdpd") == 0)
|
||
|
vcuid = 1;
|
||
|
else if (strcmp(current->comm, "vpud") == 0) {
|
||
|
vcu_get_file_lock();
|
||
|
if (vcud_task &&
|
||
|
(current->tgid != vcud_task->tgid ||
|
||
|
current->group_leader != vcud_task->group_leader)) {
|
||
|
vcu_put_file_lock();
|
||
|
return -EACCES;
|
||
|
}
|
||
|
vcud_task = current->group_leader;
|
||
|
files = vcud_task->files;
|
||
|
vcu_put_file_lock();
|
||
|
vcuid = 0;
|
||
|
} else if (strcmp(current->comm, "vdec_srv") == 0 ||
|
||
|
strcmp(current->comm, "venc_srv") == 0) {
|
||
|
vcuid = 0;
|
||
|
} else {
|
||
|
pr_info("[VCU] thread name: %s\n", current->comm);
|
||
|
}
|
||
|
|
||
|
vcu_mtkdev[vcuid]->vcuid = vcuid;
|
||
|
if (IS_ERR_OR_NULL(vcu_mtkdev[vcuid]->clt_vdec[0]))
|
||
|
return -EINVAL;
|
||
|
|
||
|
vcu_queue = mtk_vcu_mem_init(vcu_mtkdev[vcuid]->dev,
|
||
|
vcu_mtkdev[vcuid]->clt_vdec[0]->chan->mbox->dev);
|
||
|
|
||
|
if (vcu_queue == NULL)
|
||
|
return -ENOMEM;
|
||
|
vcu_queue->vcu = vcu_mtkdev[vcuid];
|
||
|
file->private_data = vcu_queue;
|
||
|
vcu_ptr->vpud_killed.count = 0;
|
||
|
vcu_ptr->open_cnt++;
|
||
|
vcu_ptr->abort = false;
|
||
|
vcu_ptr->vpud_is_going_down = 0;
|
||
|
|
||
|
pr_info("[VCU] %s name: %s pid %d tgid %d open_cnt %d current %p group_leader %p\n",
|
||
|
__func__, current->comm, current->pid, current->tgid,
|
||
|
vcu_ptr->open_cnt, current, current->group_leader);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int mtk_vcu_release(struct inode *inode, struct file *file)
|
||
|
{
|
||
|
unsigned long flags;
|
||
|
|
||
|
if (file->private_data)
|
||
|
mtk_vcu_mem_release((struct mtk_vcu_queue *)file->private_data);
|
||
|
pr_info("[VCU] %s name: %s pid %d open_cnt %d\n", __func__,
|
||
|
current->comm, current->tgid, vcu_ptr->open_cnt);
|
||
|
vcu_ptr->open_cnt--;
|
||
|
if (vcu_ptr->open_cnt == 0) {
|
||
|
/* reset vpud due to abnormal situations. */
|
||
|
vcu_ptr->abort = true;
|
||
|
vcu_get_file_lock();
|
||
|
vcu_get_task(NULL, NULL, 1);
|
||
|
vcu_put_file_lock();
|
||
|
up(&vcu_ptr->vpud_killed); /* vdec worker */
|
||
|
up(&vcu_ptr->vpud_killed); /* venc worker */
|
||
|
|
||
|
/* reset vpud_is_going_down only on abnormal situations */
|
||
|
spin_lock_irqsave(&vcu_ptr->vpud_sig_lock, flags);
|
||
|
vcu_ptr->vpud_is_going_down = 0;
|
||
|
spin_unlock_irqrestore(&vcu_ptr->vpud_sig_lock, flags);
|
||
|
|
||
|
if (vcu_ptr->curr_ctx[VCU_VDEC])
|
||
|
vdec_check_release_lock(vcu_ptr->curr_ctx[VCU_VDEC]);
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static void vcu_free_d_ext_mem(struct mtk_vcu *vcu)
|
||
|
{
|
||
|
mutex_lock(&vcu->vcu_share);
|
||
|
mutex_lock(&vcu->vcu_mutex[VCU_VDEC]);
|
||
|
mutex_lock(&vcu->vcu_mutex[VCU_VENC]);
|
||
|
kfree(VCU_DMEM0_VIRT(vcu));
|
||
|
VCU_DMEM0_VIRT(vcu) = NULL;
|
||
|
mutex_unlock(&vcu->vcu_mutex[VCU_VENC]);
|
||
|
mutex_unlock(&vcu->vcu_mutex[VCU_VDEC]);
|
||
|
mutex_unlock(&vcu->vcu_share);
|
||
|
}
|
||
|
|
||
|
static int vcu_alloc_d_ext_mem(struct mtk_vcu *vcu, unsigned long len)
|
||
|
{
|
||
|
mutex_lock(&vcu->vcu_share);
|
||
|
mutex_lock(&vcu->vcu_mutex[VCU_VDEC]);
|
||
|
mutex_lock(&vcu->vcu_mutex[VCU_VENC]);
|
||
|
VCU_DMEM0_VIRT(vcu) = kmalloc(len, GFP_KERNEL);
|
||
|
VCU_DMEM0_PHY(vcu) = virt_to_phys(VCU_DMEM0_VIRT(vcu));
|
||
|
VCU_DMEM0_LEN(vcu) = len;
|
||
|
mutex_unlock(&vcu->vcu_mutex[VCU_VENC]);
|
||
|
mutex_unlock(&vcu->vcu_mutex[VCU_VDEC]);
|
||
|
mutex_unlock(&vcu->vcu_share);
|
||
|
|
||
|
if (!VCU_DMEM0_VIRT(vcu))
|
||
|
return -1;
|
||
|
|
||
|
dev_dbg(vcu->dev,
|
||
|
"[VCU] Data extend memory (len:%lu) phy=0x%llx virt=0x%p iova=0x%llx\n",
|
||
|
VCU_DMEM0_LEN(vcu),
|
||
|
(unsigned long long)VCU_DMEM0_PHY(vcu),
|
||
|
VCU_DMEM0_VIRT(vcu),
|
||
|
(unsigned long long)VCU_DMEM0_IOVA(vcu));
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static void mtk_vcu_page_vm_open(struct vm_area_struct *vma)
|
||
|
{
|
||
|
struct vcu_pa_pages *vcu_page = (struct vcu_pa_pages *)vma->vm_private_data;
|
||
|
|
||
|
atomic_inc(&vcu_page->ref_cnt);
|
||
|
|
||
|
vcu_dbg_log("[VCU] %s vma->start 0x%lx, end 0x%lx, pgoff 0x%lx, ref_cnt %d\n",
|
||
|
__func__, vma->vm_start, vma->vm_end, vma->vm_pgoff,
|
||
|
atomic_read(&vcu_page->ref_cnt));
|
||
|
}
|
||
|
|
||
|
static void mtk_vcu_page_vm_close(struct vm_area_struct *vma)
|
||
|
{
|
||
|
struct vcu_pa_pages *vcu_page = (struct vcu_pa_pages *)vma->vm_private_data;
|
||
|
|
||
|
if (atomic_read(&vcu_page->ref_cnt) > 0)
|
||
|
atomic_dec(&vcu_page->ref_cnt);
|
||
|
else
|
||
|
pr_info("[VCU][Error] %s ummap fail\n", __func__);
|
||
|
|
||
|
vcu_dbg_log("[VCU] %s vma->start 0x%lx, end 0x%lx, pgoff 0x%lx, ref_cnt %d\n",
|
||
|
__func__, vma->vm_start, vma->vm_end, vma->vm_pgoff,
|
||
|
atomic_read(&vcu_page->ref_cnt));
|
||
|
}
|
||
|
|
||
|
const struct vm_operations_struct mtk_vcu_page_vm_ops = {
|
||
|
.open = mtk_vcu_page_vm_open,
|
||
|
.close = mtk_vcu_page_vm_close,
|
||
|
};
|
||
|
|
||
|
static void mtk_vcu_buf_vm_close(struct vm_area_struct *vma)
|
||
|
{
|
||
|
void *mem_priv = (void *)vma->vm_private_data;
|
||
|
struct file *file = vma->vm_file;
|
||
|
struct mtk_vcu_queue *vcu_queue =
|
||
|
(struct mtk_vcu_queue *)file->private_data;
|
||
|
|
||
|
mtk_vcu_buffer_ref_dec(vcu_queue, mem_priv);
|
||
|
vcu_dbg_log("[VCU] %s vma->start 0x%lx, end 0x%lx, pgoff 0x%lx\n",
|
||
|
__func__, vma->vm_start, vma->vm_end, vma->vm_pgoff);
|
||
|
}
|
||
|
|
||
|
const struct vm_operations_struct mtk_vcu_buf_vm_ops = {
|
||
|
.close = mtk_vcu_buf_vm_close,
|
||
|
};
|
||
|
|
||
|
static int mtk_vcu_mmap(struct file *file, struct vm_area_struct *vma)
|
||
|
{
|
||
|
unsigned long length = vma->vm_end - vma->vm_start;
|
||
|
unsigned long pa_start = vma->vm_pgoff << PAGE_SHIFT;
|
||
|
unsigned long pa_start_base = pa_start;
|
||
|
unsigned long pa_end = pa_start + length;
|
||
|
#ifdef CONFIG_MTK_IOMMU_V2
|
||
|
unsigned long start = vma->vm_start;
|
||
|
unsigned long pos = 0;
|
||
|
#endif
|
||
|
struct mtk_vcu *vcu_dev;
|
||
|
struct mtk_vcu_queue *vcu_queue =
|
||
|
(struct mtk_vcu_queue *)file->private_data;
|
||
|
struct mem_obj mem_buff_data;
|
||
|
struct vb2_buffer *src_vb, *dst_vb;
|
||
|
void *ret = NULL;
|
||
|
|
||
|
vcu_dev = (struct mtk_vcu *)vcu_queue->vcu;
|
||
|
vcu_dbg_log("[VCU] %s vma->start 0x%lx, end 0x%lx, pgoff 0x%lx\n",
|
||
|
__func__, vma->vm_start, vma->vm_end, vma->vm_pgoff);
|
||
|
|
||
|
// First handle map pa case, because maybe pa will smaller than
|
||
|
// MAP_PA_BASE_1GB in 32bit project
|
||
|
if (vcu_queue->map_buf_pa >= MAP_SHMEM_PA_BASE) {
|
||
|
mutex_lock(&vcu_queue->mmap_lock);
|
||
|
vcu_queue->map_buf_pa = 0;
|
||
|
ret = vcu_check_gce_pa_base(vcu_queue, pa_start, length, true);
|
||
|
if (ret != NULL) {
|
||
|
vma->vm_ops = &mtk_vcu_page_vm_ops;
|
||
|
vma->vm_private_data = ret;
|
||
|
vma->vm_pgoff = pa_start >> PAGE_SHIFT;
|
||
|
vma->vm_page_prot =
|
||
|
pgprot_writecombine(vma->vm_page_prot);
|
||
|
mtk_vcu_page_vm_open(vma);
|
||
|
mutex_unlock(&vcu_queue->mmap_lock);
|
||
|
goto valid_map;
|
||
|
}
|
||
|
mutex_unlock(&vcu_queue->mmap_lock);
|
||
|
pr_info("[VCU] map pa fail with pa_start=0x%lx\n",
|
||
|
pa_start);
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
|
||
|
/*only vcud need this case*/
|
||
|
if (pa_start < MAP_PA_BASE_1GB) {
|
||
|
if (vcu_check_reg_base(vcu_dev, pa_start, length) == 0) {
|
||
|
vma->vm_pgoff = pa_start >> PAGE_SHIFT;
|
||
|
goto reg_valid_map;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (pa_start >= MAP_SHMEM_ALLOC_BASE && pa_end <= MAP_SHMEM_ALLOC_END) {
|
||
|
vma->vm_pgoff =
|
||
|
(unsigned long)(VCU_DMEM0_PHY(vcu_dev) >> PAGE_SHIFT);
|
||
|
goto valid_map;
|
||
|
}
|
||
|
|
||
|
if (pa_start >= MAP_SHMEM_COMMIT_BASE &&
|
||
|
pa_end <= MAP_SHMEM_COMMIT_END) {
|
||
|
VCU_DMEM0_VMA(vcu_dev) = vma->vm_start;
|
||
|
vma->vm_pgoff =
|
||
|
(unsigned long)(VCU_DMEM0_PHY(vcu_dev) >> PAGE_SHIFT);
|
||
|
goto valid_map;
|
||
|
}
|
||
|
|
||
|
if (pa_start_base >= MAP_SHMEM_MM_BASE &&
|
||
|
pa_start_base < MAP_SHMEM_PA_BASE) {
|
||
|
if (pa_start_base >= MAP_SHMEM_MM_CACHEABLE_BASE)
|
||
|
pa_start -= MAP_SHMEM_MM_CACHEABLE_BASE;
|
||
|
else
|
||
|
pa_start -= MAP_SHMEM_MM_BASE;
|
||
|
|
||
|
mem_buff_data.iova = (vcu_ptr->iommu_padding) ?
|
||
|
(pa_start | 0x100000000UL) : pa_start;
|
||
|
mem_buff_data.len = length;
|
||
|
src_vb = NULL;
|
||
|
dst_vb = NULL;
|
||
|
if (strcmp(current->comm, "vdec_srv") == 0) {
|
||
|
src_vb = vcu_dev->curr_src_vb[VCU_VDEC];
|
||
|
dst_vb = vcu_dev->curr_dst_vb[VCU_VDEC];
|
||
|
} else if (strcmp(current->comm, "venc_srv") == 0) {
|
||
|
src_vb = vcu_dev->curr_src_vb[VCU_VENC];
|
||
|
dst_vb = vcu_dev->curr_dst_vb[VCU_VENC];
|
||
|
}
|
||
|
|
||
|
ret = mtk_vcu_set_buffer(vcu_queue, &mem_buff_data,
|
||
|
src_vb, dst_vb);
|
||
|
if (!IS_ERR_OR_NULL(ret)) {
|
||
|
vma->vm_ops = &mtk_vcu_buf_vm_ops;
|
||
|
vma->vm_private_data = ret;
|
||
|
vma->vm_file = file;
|
||
|
}
|
||
|
#ifdef CONFIG_MTK_IOMMU_V2
|
||
|
while (length > 0) {
|
||
|
vma->vm_pgoff = iommu_iova_to_phys(vcu_dev->io_domain,
|
||
|
(vcu_ptr->iommu_padding) ?
|
||
|
((pa_start + pos) | 0x100000000UL) :
|
||
|
(pa_start + pos));
|
||
|
if (vma->vm_pgoff == 0) {
|
||
|
dev_info(vcu_dev->dev, "[VCU] iommu_iova_to_phys fail\n");
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
vma->vm_pgoff >>= PAGE_SHIFT;
|
||
|
if (pa_start_base < MAP_SHMEM_MM_CACHEABLE_BASE) {
|
||
|
vma->vm_page_prot =
|
||
|
pgprot_writecombine(vma->vm_page_prot);
|
||
|
}
|
||
|
if (remap_pfn_range(vma, start, vma->vm_pgoff,
|
||
|
PAGE_SIZE, vma->vm_page_prot) == true)
|
||
|
return -EAGAIN;
|
||
|
|
||
|
start += PAGE_SIZE;
|
||
|
pos += PAGE_SIZE;
|
||
|
if (length > PAGE_SIZE)
|
||
|
length -= PAGE_SIZE;
|
||
|
else
|
||
|
length = 0;
|
||
|
}
|
||
|
return 0;
|
||
|
#endif
|
||
|
}
|
||
|
dev_info(vcu_dev->dev, "[VCU] Invalid argument\n");
|
||
|
|
||
|
return -EINVAL;
|
||
|
|
||
|
reg_valid_map:
|
||
|
vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
|
||
|
|
||
|
valid_map:
|
||
|
dev_dbg(vcu_dev->dev, "[VCU] Mapping pgoff 0x%lx\n", vma->vm_pgoff);
|
||
|
|
||
|
if (remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff,
|
||
|
vma->vm_end - vma->vm_start,
|
||
|
vma->vm_page_prot) != 0)
|
||
|
return -EAGAIN;
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static long mtk_vcu_unlocked_ioctl(struct file *file, unsigned int cmd,
|
||
|
unsigned long arg)
|
||
|
{
|
||
|
long ret = -1;
|
||
|
void *mem_priv;
|
||
|
unsigned char *user_data_addr = NULL;
|
||
|
struct mtk_vcu *vcu_dev;
|
||
|
struct device *dev;
|
||
|
struct share_obj share_buff_data;
|
||
|
struct mem_obj mem_buff_data;
|
||
|
struct mtk_vcu_queue *vcu_queue =
|
||
|
(struct mtk_vcu_queue *)file->private_data;
|
||
|
|
||
|
vcu_dev = (struct mtk_vcu *)vcu_queue->vcu;
|
||
|
dev = vcu_dev->dev;
|
||
|
switch (cmd) {
|
||
|
case VCU_SET_OBJECT:
|
||
|
user_data_addr = (unsigned char *)arg;
|
||
|
ret = (long)copy_from_user(&share_buff_data, user_data_addr,
|
||
|
(unsigned long)sizeof(struct share_obj));
|
||
|
if (ret != 0L || share_buff_data.id >= (int)IPI_MAX ||
|
||
|
share_buff_data.id < (int)IPI_VCU_INIT) {
|
||
|
pr_info("[VCU] %s(%d) Copy data from user failed!\n",
|
||
|
__func__, __LINE__);
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
ret = vcu_ipi_handler(vcu_dev, &share_buff_data);
|
||
|
ret = (long)copy_to_user(user_data_addr, &share_buff_data,
|
||
|
(unsigned long)sizeof(struct share_obj));
|
||
|
if (ret != 0L) {
|
||
|
pr_info("[VCU] %s(%d) Copy data to user failed!\n",
|
||
|
__func__, __LINE__);
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
break;
|
||
|
case VCU_GET_OBJECT:
|
||
|
ret = vcu_ipi_get(vcu_dev, arg);
|
||
|
break;
|
||
|
case VCU_GET_LOG_OBJECT:
|
||
|
ret = vcu_log_get(vcu_dev, arg);
|
||
|
break;
|
||
|
case VCU_MVA_ALLOCATION:
|
||
|
case VCU_PA_ALLOCATION:
|
||
|
user_data_addr = (unsigned char *)arg;
|
||
|
ret = (long)copy_from_user(&mem_buff_data, user_data_addr,
|
||
|
(unsigned long)sizeof(struct mem_obj));
|
||
|
if (ret != 0L) {
|
||
|
pr_info("[VCU] %s(%d) Copy data from user failed!\n",
|
||
|
__func__, __LINE__);
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
|
||
|
mutex_lock(&vcu_queue->dev_lock);
|
||
|
if (cmd == VCU_MVA_ALLOCATION) {
|
||
|
mem_priv =
|
||
|
mtk_vcu_get_buffer(vcu_queue, &mem_buff_data);
|
||
|
} else {
|
||
|
mem_priv =
|
||
|
mtk_vcu_get_page(vcu_queue, &mem_buff_data);
|
||
|
}
|
||
|
mutex_unlock(&vcu_queue->dev_lock);
|
||
|
if (IS_ERR_OR_NULL(mem_priv) == true) {
|
||
|
mem_buff_data.va = (unsigned long)-1;
|
||
|
mem_buff_data.pa = (unsigned long)-1;
|
||
|
mem_buff_data.iova = (unsigned long)-1;
|
||
|
ret = (long)copy_to_user(user_data_addr,
|
||
|
&mem_buff_data,
|
||
|
(unsigned long)sizeof(struct mem_obj));
|
||
|
pr_info("[VCU] ALLOCATION %d failed!\n", cmd == VCU_MVA_ALLOCATION);
|
||
|
return PTR_ERR(mem_priv);
|
||
|
}
|
||
|
|
||
|
vcu_dbg_log("[VCU] ALLOCATION %d va %llx, pa %llx, iova %llx\n",
|
||
|
cmd == VCU_MVA_ALLOCATION, mem_buff_data.va,
|
||
|
mem_buff_data.pa, mem_buff_data.iova);
|
||
|
|
||
|
ret = (long)copy_to_user(user_data_addr, &mem_buff_data,
|
||
|
(unsigned long)sizeof(struct mem_obj));
|
||
|
if (ret != 0L) {
|
||
|
pr_info("[VCU] %s(%d) Copy data to user failed!\n",
|
||
|
__func__, __LINE__);
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
|
||
|
/* store map pa buffer type flag which will use in mmap*/
|
||
|
if (cmd == VCU_PA_ALLOCATION)
|
||
|
vcu_queue->map_buf_pa = mem_buff_data.pa + MAP_SHMEM_PA_BASE;
|
||
|
|
||
|
ret = 0;
|
||
|
break;
|
||
|
case VCU_MVA_FREE:
|
||
|
case VCU_PA_FREE:
|
||
|
user_data_addr = (unsigned char *)arg;
|
||
|
ret = (long)copy_from_user(&mem_buff_data, user_data_addr,
|
||
|
(unsigned long)sizeof(struct mem_obj));
|
||
|
if ((ret != 0L) ||
|
||
|
(mem_buff_data.iova == 0UL &&
|
||
|
mem_buff_data.va == 0UL)) {
|
||
|
pr_info("[VCU] %s(%d) Free buf failed!\n",
|
||
|
__func__, __LINE__);
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
|
||
|
mutex_lock(&vcu_queue->dev_lock);
|
||
|
if (cmd == VCU_MVA_FREE) {
|
||
|
if (vcu_ptr->iommu_padding)
|
||
|
mem_buff_data.iova |= 0x100000000UL;
|
||
|
ret = mtk_vcu_free_buffer(vcu_queue, &mem_buff_data);
|
||
|
} else {
|
||
|
ret = mtk_vcu_free_page(vcu_queue, &mem_buff_data);
|
||
|
}
|
||
|
mutex_unlock(&vcu_queue->dev_lock);
|
||
|
|
||
|
if (ret != 0L) {
|
||
|
pr_info("[VCU] VCU_FREE failed %d va %llx, pa %llx, iova %llx\n",
|
||
|
cmd == VCU_MVA_FREE, mem_buff_data.va,
|
||
|
mem_buff_data.pa, mem_buff_data.iova);
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
|
||
|
vcu_dbg_log("[VCU] FREE %d va %llx, pa %llx, iova %llx\n",
|
||
|
cmd == VCU_MVA_FREE, mem_buff_data.va,
|
||
|
mem_buff_data.pa, mem_buff_data.iova);
|
||
|
|
||
|
mem_buff_data.va = 0;
|
||
|
mem_buff_data.iova = 0;
|
||
|
mem_buff_data.pa = 0;
|
||
|
|
||
|
ret = (long)copy_to_user(user_data_addr, &mem_buff_data,
|
||
|
(unsigned long)sizeof(struct mem_obj));
|
||
|
if (ret != 0L) {
|
||
|
pr_info("[VCU] %s(%d) Copy data to user failed!\n",
|
||
|
__func__, __LINE__);
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
ret = 0;
|
||
|
break;
|
||
|
case VCU_CACHE_FLUSH_ALL:
|
||
|
dev_dbg(dev, "[VCU] Flush cache in kernel\n");
|
||
|
vcu_buffer_flush_all(dev, vcu_queue);
|
||
|
ret = 0;
|
||
|
break;
|
||
|
case VCU_CACHE_FLUSH_BUFF:
|
||
|
case VCU_CACHE_INVALIDATE_BUFF:
|
||
|
user_data_addr = (unsigned char *)arg;
|
||
|
ret = (long)copy_from_user(&mem_buff_data, user_data_addr,
|
||
|
(unsigned long)sizeof(struct mem_obj));
|
||
|
if (ret != 0L) {
|
||
|
pr_info("[VCU] %s(%d) Copy data from user failed!\n",
|
||
|
__func__, __LINE__);
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
if (vcu_ptr->iommu_padding)
|
||
|
mem_buff_data.iova |= 0x100000000UL;
|
||
|
ret = vcu_buffer_cache_sync(dev, vcu_queue,
|
||
|
(dma_addr_t)mem_buff_data.iova,
|
||
|
(size_t)mem_buff_data.len,
|
||
|
(cmd == VCU_CACHE_FLUSH_BUFF) ?
|
||
|
DMA_TO_DEVICE : DMA_FROM_DEVICE);
|
||
|
if (ret < 0)
|
||
|
return -EINVAL;
|
||
|
|
||
|
dev_dbg(dev, "[VCU] Cache flush buffer pa = %llx, size = %d\n",
|
||
|
mem_buff_data.iova, (unsigned int)mem_buff_data.len);
|
||
|
|
||
|
ret = (long)copy_to_user(user_data_addr, &mem_buff_data,
|
||
|
(unsigned long)sizeof(struct mem_obj));
|
||
|
if (ret != 0L) {
|
||
|
pr_info("[VCU] %s(%d) Copy data to user failed!\n",
|
||
|
__func__, __LINE__);
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
ret = 0;
|
||
|
break;
|
||
|
case VCU_GCE_SET_CMD_FLUSH:
|
||
|
ret = vcu_gce_cmd_flush(vcu_dev, vcu_queue, arg);
|
||
|
break;
|
||
|
case VCU_GCE_WAIT_CALLBACK:
|
||
|
ret = vcu_wait_gce_callback(vcu_dev, arg);
|
||
|
break;
|
||
|
default:
|
||
|
dev_err(dev, "[VCU] Unknown cmd\n");
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
#if IS_ENABLED(CONFIG_COMPAT)
|
||
|
static int compat_get_vpud_allocation_data(
|
||
|
struct compat_mem_obj __user *data32,
|
||
|
struct mem_obj __user *data)
|
||
|
{
|
||
|
compat_ulong_t l;
|
||
|
compat_u64 u;
|
||
|
unsigned int err = 0;
|
||
|
|
||
|
err = get_user(l, &data32->iova);
|
||
|
err |= put_user(l, &data->iova);
|
||
|
err |= get_user(l, &data32->len);
|
||
|
err |= put_user(l, &data->len);
|
||
|
err |= get_user(u, &data32->pa);
|
||
|
err |= put_user(u, &data->pa);
|
||
|
err |= get_user(u, &data32->va);
|
||
|
err |= put_user(u, &data->va);
|
||
|
|
||
|
return (int)err;
|
||
|
}
|
||
|
|
||
|
static int compat_put_vpud_allocation_data(
|
||
|
struct compat_mem_obj __user *data32,
|
||
|
struct mem_obj __user *data)
|
||
|
{
|
||
|
compat_ulong_t l;
|
||
|
compat_u64 u;
|
||
|
unsigned int err = 0;
|
||
|
|
||
|
err = get_user(l, &data->iova);
|
||
|
err |= put_user(l, &data32->iova);
|
||
|
err |= get_user(l, &data->len);
|
||
|
err |= put_user(l, &data32->len);
|
||
|
err |= get_user(u, &data->pa);
|
||
|
err |= put_user(u, &data32->pa);
|
||
|
err |= get_user(u, &data->va);
|
||
|
err |= put_user(u, &data32->va);
|
||
|
|
||
|
return (int)err;
|
||
|
}
|
||
|
|
||
|
static long mtk_vcu_unlocked_compat_ioctl(struct file *file, unsigned int cmd,
|
||
|
unsigned long arg)
|
||
|
{
|
||
|
int err = 0;
|
||
|
long ret = -1;
|
||
|
struct share_obj __user *share_data32;
|
||
|
struct compat_mem_obj __user *data32;
|
||
|
struct mem_obj __user *data;
|
||
|
|
||
|
switch (cmd) {
|
||
|
case COMPAT_VCU_SET_OBJECT:
|
||
|
case VCU_GET_OBJECT:
|
||
|
case VCU_GET_LOG_OBJECT:
|
||
|
case VCU_GCE_SET_CMD_FLUSH:
|
||
|
case VCU_GCE_WAIT_CALLBACK:
|
||
|
share_data32 = compat_ptr((uint32_t)arg);
|
||
|
ret = file->f_op->unlocked_ioctl(file,
|
||
|
cmd, (unsigned long)share_data32);
|
||
|
break;
|
||
|
case COMPAT_VCU_MVA_ALLOCATION:
|
||
|
case COMPAT_VCU_PA_ALLOCATION:
|
||
|
data32 = compat_ptr((uint32_t)arg);
|
||
|
data = compat_alloc_user_space(sizeof(struct mem_obj));
|
||
|
if (data == NULL)
|
||
|
return -EFAULT;
|
||
|
|
||
|
err = compat_get_vpud_allocation_data(data32, data);
|
||
|
if (err != 0)
|
||
|
return err;
|
||
|
if (cmd == COMPAT_VCU_MVA_ALLOCATION)
|
||
|
ret = file->f_op->unlocked_ioctl(file,
|
||
|
(uint32_t)VCU_MVA_ALLOCATION,
|
||
|
(unsigned long)data);
|
||
|
else
|
||
|
ret = file->f_op->unlocked_ioctl(file,
|
||
|
(uint32_t)VCU_PA_ALLOCATION,
|
||
|
(unsigned long)data);
|
||
|
err = compat_put_vpud_allocation_data(data32, data);
|
||
|
if (err != 0)
|
||
|
return err;
|
||
|
break;
|
||
|
case COMPAT_VCU_MVA_FREE:
|
||
|
case COMPAT_VCU_PA_FREE:
|
||
|
data32 = compat_ptr((uint32_t)arg);
|
||
|
data = compat_alloc_user_space(sizeof(struct mem_obj));
|
||
|
if (data == NULL)
|
||
|
return -EFAULT;
|
||
|
|
||
|
err = compat_get_vpud_allocation_data(data32, data);
|
||
|
if (err != 0)
|
||
|
return err;
|
||
|
if (cmd == COMPAT_VCU_MVA_FREE)
|
||
|
ret = file->f_op->unlocked_ioctl(file,
|
||
|
(uint32_t)VCU_MVA_FREE, (unsigned long)data);
|
||
|
else
|
||
|
ret = file->f_op->unlocked_ioctl(file,
|
||
|
(uint32_t)VCU_PA_FREE, (unsigned long)data);
|
||
|
err = compat_put_vpud_allocation_data(data32, data);
|
||
|
if (err != 0)
|
||
|
return err;
|
||
|
break;
|
||
|
case COMPAT_VCU_CACHE_FLUSH_ALL:
|
||
|
ret = file->f_op->unlocked_ioctl(file,
|
||
|
(uint32_t)VCU_CACHE_FLUSH_ALL, 0);
|
||
|
break;
|
||
|
case COMPAT_VCU_CACHE_FLUSH_BUFF:
|
||
|
case COMPAT_VCU_CACHE_INVALIDATE_BUFF:
|
||
|
data32 = compat_ptr((uint32_t)arg);
|
||
|
data = compat_alloc_user_space(sizeof(struct mem_obj));
|
||
|
if (data == NULL)
|
||
|
return -EFAULT;
|
||
|
|
||
|
err = compat_get_vpud_allocation_data(data32, data);
|
||
|
if (err != 0)
|
||
|
return err;
|
||
|
if (cmd == COMPAT_VCU_CACHE_FLUSH_BUFF) {
|
||
|
ret = file->f_op->unlocked_ioctl(file,
|
||
|
(uint32_t)VCU_CACHE_FLUSH_BUFF,
|
||
|
(unsigned long)data);
|
||
|
} else {
|
||
|
ret = file->f_op->unlocked_ioctl(file,
|
||
|
(uint32_t)VCU_CACHE_INVALIDATE_BUFF,
|
||
|
(unsigned long)data);
|
||
|
}
|
||
|
|
||
|
err = compat_put_vpud_allocation_data(data32, data);
|
||
|
if (err != 0)
|
||
|
return err;
|
||
|
break;
|
||
|
default:
|
||
|
pr_err("[VCU] Invalid cmd_number 0x%x.\n", cmd);
|
||
|
break;
|
||
|
}
|
||
|
return ret;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
int mtk_vcu_write(const char *val, const struct kernel_param *kp)
|
||
|
{
|
||
|
if (vcu_ptr != NULL &&
|
||
|
vcu_ptr->vdec_log_info != NULL &&
|
||
|
val != NULL) {
|
||
|
memcpy(vcu_ptr->vdec_log_info->log_info,
|
||
|
val, strnlen(val, LOG_INFO_SIZE - 1) + 1);
|
||
|
} else {
|
||
|
pr_info("[VCU] %s(%d) return\n", __func__, __LINE__);
|
||
|
return -EFAULT;
|
||
|
}
|
||
|
|
||
|
vcu_ptr->vdec_log_info->log_info[LOG_INFO_SIZE - 1] = '\0';
|
||
|
|
||
|
// check if need to enable VCU debug log
|
||
|
if (strstr(vcu_ptr->vdec_log_info->log_info, "vcu_log 1")) {
|
||
|
vcu_ptr->enable_vcu_dbg_log = 1;
|
||
|
pr_info("[VCU] enable vcu_log\n");
|
||
|
return 0;
|
||
|
} else if (strstr(vcu_ptr->vdec_log_info->log_info, "vcu_log 0")) {
|
||
|
vcu_ptr->enable_vcu_dbg_log = 0;
|
||
|
pr_info("[VCU] disable vcu_log\n");
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
pr_info("[log wakeup VPUD] log_info %p vcu_ptr %p val %p: %s %lu\n",
|
||
|
(char *)vcu_ptr->vdec_log_info->log_info,
|
||
|
vcu_ptr, val, val,
|
||
|
(unsigned long)strnlen(val, LOG_INFO_SIZE - 1) + 1);
|
||
|
|
||
|
atomic_set(&vcu_ptr->vdec_log_got, 1);
|
||
|
wake_up(&vcu_ptr->vdec_log_get_wq);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int vcu_set_log(const char *val)
|
||
|
{
|
||
|
return mtk_vcu_write(val, NULL);
|
||
|
}
|
||
|
EXPORT_SYMBOL_GPL(vcu_set_log);
|
||
|
|
||
|
static struct kernel_param_ops log_param_ops = {
|
||
|
.set = mtk_vcu_write,
|
||
|
};
|
||
|
|
||
|
module_param_cb(test_info, &log_param_ops, &vcodec_param_string, 0644);
|
||
|
|
||
|
static const struct file_operations vcu_fops = {
|
||
|
.owner = THIS_MODULE,
|
||
|
.unlocked_ioctl = mtk_vcu_unlocked_ioctl,
|
||
|
.open = mtk_vcu_open,
|
||
|
.release = mtk_vcu_release,
|
||
|
.mmap = mtk_vcu_mmap,
|
||
|
#if IS_ENABLED(CONFIG_COMPAT)
|
||
|
.compat_ioctl = mtk_vcu_unlocked_compat_ioctl,
|
||
|
#endif
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Suspend callbacks after user space processes are frozen
|
||
|
* Since user space processes are frozen, there is no need and cannot hold same
|
||
|
* mutex that protects lock owner while checking status.
|
||
|
* If video codec hardware is still active now, must not to enter suspend.
|
||
|
**/
|
||
|
static int mtk_vcu_suspend(struct device *pDev)
|
||
|
{
|
||
|
if (atomic_read(&vcu_ptr->ipi_done[VCU_VDEC]) == 0 ||
|
||
|
atomic_read(&vcu_ptr->ipi_done[VCU_VENC]) == 0 ||
|
||
|
atomic_read(&vcu_ptr->gce_job_cnt[VCU_VDEC][0]) > 0 ||
|
||
|
atomic_read(&vcu_ptr->gce_job_cnt[VCU_VDEC][1]) > 0 ||
|
||
|
atomic_read(&vcu_ptr->gce_job_cnt[VCU_VENC][0]) > 0 ||
|
||
|
atomic_read(&vcu_ptr->gce_job_cnt[VCU_VENC][1]) > 0) {
|
||
|
pr_info("[VCU] %s fail due to videocodec activity\n", __func__);
|
||
|
return -EBUSY;
|
||
|
}
|
||
|
pr_info("[VCU] %s done\n", __func__);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int mtk_vcu_resume(struct device *pDev)
|
||
|
{
|
||
|
pr_info("[VCU] %s done\n", __func__);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Suspend notifiers before user space processes are frozen.
|
||
|
* User space driver can still complete decoding/encoding of current frame.
|
||
|
* Change state to is_entering_suspend to stop send ipi_msg but allow current
|
||
|
* wait ipi_msg to be done.
|
||
|
* Since there is no critical section proection, it is possible for a new task
|
||
|
* to start after changing to is_entering_suspend state. This case will be
|
||
|
* handled by suspend callback mtk_vcu_suspend.
|
||
|
**/
|
||
|
static int mtk_vcu_suspend_notifier(struct notifier_block *nb,
|
||
|
unsigned long action, void *data)
|
||
|
{
|
||
|
int wait_cnt = 0;
|
||
|
|
||
|
pr_info("[VCU] %s ok action = %ld\n", __func__, action);
|
||
|
switch (action) {
|
||
|
case PM_SUSPEND_PREPARE:
|
||
|
vcu_ptr->is_entering_suspend = 1;
|
||
|
while (atomic_read(&vcu_ptr->ipi_done[VCU_VDEC]) == 0 ||
|
||
|
atomic_read(&vcu_ptr->ipi_done[VCU_VENC]) == 0 ||
|
||
|
atomic_read(&vcu_ptr->gce_job_cnt[VCU_VDEC][0]) > 0 ||
|
||
|
atomic_read(&vcu_ptr->gce_job_cnt[VCU_VDEC][1]) > 0 ||
|
||
|
atomic_read(&vcu_ptr->gce_job_cnt[VCU_VENC][0]) > 0 ||
|
||
|
atomic_read(&vcu_ptr->gce_job_cnt[VCU_VENC][1]) > 0) {
|
||
|
wait_cnt++;
|
||
|
if (wait_cnt > 5) {
|
||
|
pr_info("vcodec_pm_suspend waiting %d %d %d %d %d %d\n",
|
||
|
atomic_read(&vcu_ptr->ipi_done[VCU_VDEC]),
|
||
|
atomic_read(&vcu_ptr->ipi_done[VCU_VENC]),
|
||
|
atomic_read(
|
||
|
&vcu_ptr->gce_job_cnt[VCU_VDEC][0]),
|
||
|
atomic_read(
|
||
|
&vcu_ptr->gce_job_cnt[VCU_VDEC][1]),
|
||
|
atomic_read(
|
||
|
&vcu_ptr->gce_job_cnt[VCU_VENC][0]),
|
||
|
atomic_read(
|
||
|
&vcu_ptr->gce_job_cnt[VCU_VENC][1]));
|
||
|
/* Current task is still not finished, don't
|
||
|
* care, will check again in real suspend
|
||
|
*/
|
||
|
return NOTIFY_DONE;
|
||
|
}
|
||
|
usleep_range(10000, 20000);
|
||
|
}
|
||
|
return NOTIFY_OK;
|
||
|
case PM_POST_SUSPEND:
|
||
|
vcu_ptr->is_entering_suspend = 0;
|
||
|
return NOTIFY_OK;
|
||
|
default:
|
||
|
return NOTIFY_DONE;
|
||
|
}
|
||
|
return NOTIFY_DONE;
|
||
|
}
|
||
|
|
||
|
static int mtk_vcu_probe(struct platform_device *pdev)
|
||
|
{
|
||
|
struct mtk_vcu *vcu;
|
||
|
struct device *dev;
|
||
|
struct resource *res;
|
||
|
int i, j, ret = 0;
|
||
|
unsigned int vcuid = 0;
|
||
|
|
||
|
dev_dbg(&pdev->dev, "[VCU] initialization\n");
|
||
|
|
||
|
dev = &pdev->dev;
|
||
|
vcu = devm_kzalloc(dev, sizeof(*vcu), GFP_KERNEL);
|
||
|
if (vcu == NULL)
|
||
|
return -ENOMEM;
|
||
|
|
||
|
vcu_ptr = vcu;
|
||
|
ret = of_property_read_u32(dev->of_node, "mediatek,vcuid", &vcuid);
|
||
|
if (ret != 0) {
|
||
|
dev_err(dev, "[VCU] failed to find mediatek,vcuid\n");
|
||
|
return ret;
|
||
|
}
|
||
|
vcu_mtkdev[vcuid] = vcu;
|
||
|
|
||
|
#ifdef CONFIG_MTK_IOMMU_V2
|
||
|
vcu_mtkdev[vcuid]->io_domain = iommu_get_domain_for_dev(dev);
|
||
|
if (vcu_mtkdev[vcuid]->io_domain == NULL) {
|
||
|
dev_err(dev,
|
||
|
"[VCU] vcuid: %d get io_domain fail !!\n", vcuid);
|
||
|
return -EPROBE_DEFER;
|
||
|
}
|
||
|
dev_dbg(dev, "vcu io_domain: %p,vcuid:%d\n",
|
||
|
vcu_mtkdev[vcuid]->io_domain,
|
||
|
vcuid);
|
||
|
ret = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(64));
|
||
|
if (ret) {
|
||
|
ret = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32));
|
||
|
if (ret) {
|
||
|
dev_info(&pdev->dev, "64-bit DMA enable failed\n");
|
||
|
return ret;
|
||
|
}
|
||
|
vcu->iommu_padding = 0;
|
||
|
} else
|
||
|
vcu->iommu_padding = 1;
|
||
|
#endif
|
||
|
|
||
|
if (vcuid == 2)
|
||
|
vcu_mtkdev[vcuid]->path = CAM_PATH;
|
||
|
else if (vcuid == 1)
|
||
|
vcu_mtkdev[vcuid]->path = MDP_PATH;
|
||
|
else if (vcuid == 0) {
|
||
|
vcu_mtkdev[vcuid]->vdec_log_info = devm_kzalloc(dev,
|
||
|
sizeof(struct log_test_nofuse), GFP_KERNEL);
|
||
|
pr_info("[VCU] vdec_log_info %p %d vcuid %d vcu_ptr %p\n",
|
||
|
vcu_mtkdev[vcuid]->vdec_log_info,
|
||
|
(int)sizeof(struct log_test_nofuse),
|
||
|
(int)vcuid, vcu_ptr);
|
||
|
vcu_mtkdev[vcuid]->path = VCU_PATH;
|
||
|
} else
|
||
|
return -ENXIO;
|
||
|
|
||
|
ret = of_property_read_string(dev->of_node, "mediatek,vcuname",
|
||
|
&vcu_mtkdev[vcuid]->vcuname);
|
||
|
if (ret != 0) {
|
||
|
dev_err(dev, "[VCU] failed to find mediatek,vcuname\n");
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
vcu->dev = &pdev->dev;
|
||
|
platform_set_drvdata(pdev, vcu_mtkdev[vcuid]);
|
||
|
|
||
|
if (vcuid == 0) {
|
||
|
for (i = 0; i < (int)VCU_MAP_HW_REG_NUM; i++) {
|
||
|
res = platform_get_resource(pdev, IORESOURCE_MEM, i);
|
||
|
if (res == NULL) {
|
||
|
break;
|
||
|
}
|
||
|
vcu->map_base[i].base = res->start;
|
||
|
vcu->map_base[i].len = resource_size(res);
|
||
|
dev_dbg(dev, "[VCU] base[%d]: 0x%lx 0x%lx",
|
||
|
i, vcu->map_base[i].base,
|
||
|
vcu->map_base[i].len);
|
||
|
}
|
||
|
}
|
||
|
dev_dbg(dev, "[VCU] vcu ipi init\n");
|
||
|
ret = vcu_ipi_init(vcu);
|
||
|
if (ret != 0) {
|
||
|
dev_err(dev, "[VCU] Failed to init ipi\n");
|
||
|
goto err_ipi_init;
|
||
|
}
|
||
|
|
||
|
/* register vcu initialization IPI */
|
||
|
ret = vcu_ipi_register(pdev, IPI_VCU_INIT, vcu_init_ipi_handler,
|
||
|
"vcu_init", vcu);
|
||
|
if (ret != 0) {
|
||
|
dev_err(dev, "Failed to register IPI_VCU_INIT\n");
|
||
|
goto vcu_mutex_destroy;
|
||
|
}
|
||
|
|
||
|
init_waitqueue_head(&vcu->ack_wq[VCU_VDEC]);
|
||
|
init_waitqueue_head(&vcu->ack_wq[VCU_VENC]);
|
||
|
init_waitqueue_head(&vcu->get_wq[VCU_VDEC]);
|
||
|
init_waitqueue_head(&vcu->get_wq[VCU_VENC]);
|
||
|
init_waitqueue_head(&vcu->gce_wq[VCU_VDEC]);
|
||
|
init_waitqueue_head(&vcu->gce_wq[VCU_VENC]);
|
||
|
init_waitqueue_head(&vcu->vdec_log_get_wq);
|
||
|
atomic_set(&vcu->ipi_got[VCU_VDEC], 0);
|
||
|
atomic_set(&vcu->ipi_got[VCU_VENC], 0);
|
||
|
atomic_set(&vcu->ipi_done[VCU_VDEC], 1);
|
||
|
atomic_set(&vcu->ipi_done[VCU_VENC], 1);
|
||
|
atomic_set(&vcu->vdec_log_got, 0);
|
||
|
for (i = 0; i < (int)VCODEC_INST_MAX; i++) {
|
||
|
atomic_set(&vcu->gce_info[i].flush_done, 0);
|
||
|
atomic_set(&vcu->gce_info[i].flush_pending, 0);
|
||
|
vcu->gce_info[i].user_hdl = 0;
|
||
|
vcu->gce_info[i].v4l2_ctx = NULL;
|
||
|
for (j = 0; j < (int)GCE_PENDING_CNT; j++) {
|
||
|
INIT_LIST_HEAD(&vcu->gce_info[i].used_pages[j].list);
|
||
|
sema_init(&vcu->gce_info[i].buff_sem[j], 1);
|
||
|
}
|
||
|
}
|
||
|
atomic_set(&vcu->gce_job_cnt[VCU_VDEC][0], 0);
|
||
|
atomic_set(&vcu->gce_job_cnt[VCU_VDEC][1], 0);
|
||
|
atomic_set(&vcu->gce_job_cnt[VCU_VENC][0], 0);
|
||
|
atomic_set(&vcu->gce_job_cnt[VCU_VENC][1], 0);
|
||
|
/* init character device */
|
||
|
|
||
|
ret = alloc_chrdev_region(&vcu_mtkdev[vcuid]->vcu_devno, 0, 1,
|
||
|
vcu_mtkdev[vcuid]->vcuname);
|
||
|
if (ret < 0) {
|
||
|
dev_err(dev,
|
||
|
"[VCU] alloc_chrdev_region failed (%d)\n", ret);
|
||
|
goto err_alloc;
|
||
|
}
|
||
|
|
||
|
vcu_mtkdev[vcuid]->vcu_cdev = cdev_alloc();
|
||
|
vcu_mtkdev[vcuid]->vcu_cdev->owner = THIS_MODULE;
|
||
|
vcu_mtkdev[vcuid]->vcu_cdev->ops = &vcu_fops;
|
||
|
|
||
|
ret = cdev_add(vcu_mtkdev[vcuid]->vcu_cdev,
|
||
|
vcu_mtkdev[vcuid]->vcu_devno, 1);
|
||
|
if (ret < 0) {
|
||
|
dev_err(dev, "[VCU] class create fail (ret=%d)", ret);
|
||
|
goto err_add;
|
||
|
}
|
||
|
|
||
|
vcu_mtkdev[vcuid]->vcu_class = class_create(THIS_MODULE,
|
||
|
vcu_mtkdev[vcuid]->vcuname);
|
||
|
if (IS_ERR_OR_NULL(vcu_mtkdev[vcuid]->vcu_class) == true) {
|
||
|
ret = (int)PTR_ERR(vcu_mtkdev[vcuid]->vcu_class);
|
||
|
dev_err(dev, "[VCU] class create fail (ret=%d)", ret);
|
||
|
goto err_add;
|
||
|
}
|
||
|
|
||
|
vcu_mtkdev[vcuid]->vcu_device =
|
||
|
device_create(vcu_mtkdev[vcuid]->vcu_class,
|
||
|
NULL,
|
||
|
vcu_mtkdev[vcuid]->vcu_devno,
|
||
|
NULL, vcu_mtkdev[vcuid]->vcuname);
|
||
|
if (IS_ERR_OR_NULL(vcu_mtkdev[vcuid]->vcu_device) == true) {
|
||
|
ret = (int)PTR_ERR(vcu_mtkdev[vcuid]->vcu_device);
|
||
|
dev_err(dev, "[VCU] device_create fail (ret=%d)", ret);
|
||
|
goto err_device;
|
||
|
}
|
||
|
|
||
|
ret = of_property_read_u32(dev->of_node, "mediatek,dec_gce_th_num",
|
||
|
&vcu->gce_th_num[VCU_VDEC]);
|
||
|
if (ret != 0 || vcu->gce_th_num[VCU_VDEC] > GCE_THNUM_MAX)
|
||
|
vcu->gce_th_num[VCU_VDEC] = 1;
|
||
|
|
||
|
ret = of_property_read_u32(dev->of_node, "mediatek,enc_gce_th_num",
|
||
|
&vcu->gce_th_num[VCU_VENC]);
|
||
|
if (ret != 0 || vcu->gce_th_num[VCU_VENC] > GCE_THNUM_MAX)
|
||
|
vcu->gce_th_num[VCU_VENC] = 1;
|
||
|
|
||
|
for (i = 0; i < ARRAY_SIZE(vcu->gce_gpr); i++) {
|
||
|
ret = of_property_read_u32_index(dev->of_node, "gce-gpr",
|
||
|
i, &vcu->gce_gpr[i]);
|
||
|
if (ret < 0)
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
vcu->clt_base = cmdq_register_device(dev);
|
||
|
for (i = 0; i < vcu->gce_th_num[VCU_VDEC]; i++)
|
||
|
vcu->clt_vdec[i] = cmdq_mbox_create(dev, i);
|
||
|
for (i = 0; i < vcu->gce_th_num[VCU_VENC]; i++)
|
||
|
vcu->clt_venc[i] =
|
||
|
cmdq_mbox_create(dev, i + vcu->gce_th_num[VCU_VDEC]);
|
||
|
|
||
|
#if defined(CONFIG_MTK_SEC_VIDEO_PATH_SUPPORT)
|
||
|
vcu->clt_venc_sec[0] =
|
||
|
cmdq_mbox_create(dev,
|
||
|
vcu->gce_th_num[VCU_VDEC] + vcu->gce_th_num[VCU_VENC]);
|
||
|
#endif
|
||
|
|
||
|
if (IS_ERR_OR_NULL(vcu->clt_vdec[0]))
|
||
|
goto err_device;
|
||
|
|
||
|
dev_dbg(dev, "[VCU] GCE clt_base %p clt_vdec %d %p %p clt_venc %d %p %p %p dev %p",
|
||
|
vcu->clt_base, vcu->gce_th_num[VCU_VDEC],
|
||
|
vcu->clt_vdec[0], vcu->clt_vdec[1],
|
||
|
vcu->gce_th_num[VCU_VENC], vcu->clt_venc[0],
|
||
|
vcu->clt_venc[1], vcu->clt_venc_sec[0], dev);
|
||
|
|
||
|
for (i = 0; i < GCE_EVENT_MAX; i++)
|
||
|
vcu->gce_codec_eid[i] = -1;
|
||
|
vcu->gce_codec_eid[VDEC_EVENT_0] =
|
||
|
cmdq_dev_get_event(dev, "vdec_pic_start");
|
||
|
vcu->gce_codec_eid[VDEC_EVENT_1] =
|
||
|
cmdq_dev_get_event(dev, "vdec_decode_done");
|
||
|
vcu->gce_codec_eid[VDEC_EVENT_2] =
|
||
|
cmdq_dev_get_event(dev, "vdec_pause");
|
||
|
vcu->gce_codec_eid[VDEC_EVENT_3] =
|
||
|
cmdq_dev_get_event(dev, "vdec_dec_error");
|
||
|
vcu->gce_codec_eid[VDEC_EVENT_4] =
|
||
|
cmdq_dev_get_event(dev, "vdec_mc_busy_overflow_timeout");
|
||
|
vcu->gce_codec_eid[VDEC_EVENT_5] =
|
||
|
cmdq_dev_get_event(dev, "vdec_all_dram_req_done");
|
||
|
vcu->gce_codec_eid[VDEC_EVENT_6] =
|
||
|
cmdq_dev_get_event(dev, "vdec_ini_fetch_rdy");
|
||
|
vcu->gce_codec_eid[VDEC_EVENT_7] =
|
||
|
cmdq_dev_get_event(dev, "vdec_process_flag");
|
||
|
vcu->gce_codec_eid[VDEC_EVENT_8] =
|
||
|
cmdq_dev_get_event(dev, "vdec_search_start_code_done");
|
||
|
vcu->gce_codec_eid[VDEC_EVENT_9] =
|
||
|
cmdq_dev_get_event(dev, "vdec_ref_reorder_done");
|
||
|
vcu->gce_codec_eid[VDEC_EVENT_10] =
|
||
|
cmdq_dev_get_event(dev, "vdec_wp_tble_done");
|
||
|
vcu->gce_codec_eid[VDEC_EVENT_11] =
|
||
|
cmdq_dev_get_event(dev, "vdec_count_sram_clr_done");
|
||
|
vcu->gce_codec_eid[VDEC_EVENT_15] =
|
||
|
cmdq_dev_get_event(dev, "vdec_gce_cnt_op_threshold");
|
||
|
vcu->gce_codec_eid[VDEC_LAT_EVENT_0] =
|
||
|
cmdq_dev_get_event(dev, "vdec_lat_pic_start");
|
||
|
vcu->gce_codec_eid[VDEC_LAT_EVENT_1] =
|
||
|
cmdq_dev_get_event(dev, "vdec_lat_decode_done");
|
||
|
vcu->gce_codec_eid[VDEC_LAT_EVENT_2] =
|
||
|
cmdq_dev_get_event(dev, "vdec_lat_pause");
|
||
|
vcu->gce_codec_eid[VDEC_LAT_EVENT_3] =
|
||
|
cmdq_dev_get_event(dev, "vdec_lat_dec_error");
|
||
|
vcu->gce_codec_eid[VDEC_LAT_EVENT_4] =
|
||
|
cmdq_dev_get_event(dev, "vdec_lat_mc_busy_overflow_timeout");
|
||
|
vcu->gce_codec_eid[VDEC_LAT_EVENT_5] =
|
||
|
cmdq_dev_get_event(dev, "vdec_lat_all_dram_req_done");
|
||
|
vcu->gce_codec_eid[VDEC_LAT_EVENT_6] =
|
||
|
cmdq_dev_get_event(dev, "vdec_lat_ini_fetch_rdy");
|
||
|
vcu->gce_codec_eid[VDEC_LAT_EVENT_7] =
|
||
|
cmdq_dev_get_event(dev, "vdec_lat_process_flag");
|
||
|
vcu->gce_codec_eid[VDEC_LAT_EVENT_8] =
|
||
|
cmdq_dev_get_event(dev, "vdec_lat_search_start_code_done");
|
||
|
vcu->gce_codec_eid[VDEC_LAT_EVENT_9] =
|
||
|
cmdq_dev_get_event(dev, "vdec_lat_ref_reorder_done");
|
||
|
vcu->gce_codec_eid[VDEC_LAT_EVENT_10] =
|
||
|
cmdq_dev_get_event(dev, "vdec_lat_wp_tble_done");
|
||
|
vcu->gce_codec_eid[VDEC_LAT_EVENT_11] =
|
||
|
cmdq_dev_get_event(dev, "vdec_lat_count_sram_clr_done");
|
||
|
vcu->gce_codec_eid[VDEC_LAT_EVENT_15] =
|
||
|
cmdq_dev_get_event(dev, "vdec_lat_gce_cnt_op_threshold");
|
||
|
|
||
|
vcu->gce_codec_eid[VENC_EOF] =
|
||
|
cmdq_dev_get_event(dev, "venc_eof");
|
||
|
vcu->gce_codec_eid[VENC_CMDQ_PAUSE_DONE] =
|
||
|
cmdq_dev_get_event(dev, "venc_cmdq_pause_done");
|
||
|
vcu->gce_codec_eid[VENC_MB_DONE] =
|
||
|
cmdq_dev_get_event(dev, "venc_mb_done");
|
||
|
vcu->gce_codec_eid[VENC_128BYTE_CNT_DONE] =
|
||
|
cmdq_dev_get_event(dev, "venc_128B_cnt_done");
|
||
|
vcu->gce_codec_eid[VENC_EOF_C1] =
|
||
|
cmdq_dev_get_event(dev, "venc_eof_c1");
|
||
|
vcu->gce_codec_eid[VENC_WP_2ND_DONE] =
|
||
|
cmdq_dev_get_event(dev, "venc_wp_2nd_done");
|
||
|
vcu->gce_codec_eid[VENC_WP_3ND_DONE] =
|
||
|
cmdq_dev_get_event(dev, "venc_wp_3nd_done");
|
||
|
vcu->gce_codec_eid[VENC_SPS_DONE] =
|
||
|
cmdq_dev_get_event(dev, "venc_sps_done");
|
||
|
vcu->gce_codec_eid[VENC_PPS_DONE] =
|
||
|
cmdq_dev_get_event(dev, "venc_pps_done");
|
||
|
|
||
|
for (i = 0; i < (int)VCU_CODEC_MAX; i++) {
|
||
|
vcu->gce_cmds[i] = devm_kzalloc(dev,
|
||
|
sizeof(struct gce_cmds), GFP_KERNEL);
|
||
|
if (vcu->gce_cmds[i] == NULL)
|
||
|
goto err_device;
|
||
|
}
|
||
|
for (i = 0; i < (int)VCU_CODEC_MAX; i++)
|
||
|
mutex_init(&vcu->gce_cmds_mutex[i]);
|
||
|
sema_init(&vcu->vpud_killed, 1);
|
||
|
|
||
|
for (i = 0; i < (int)VCU_CODEC_MAX; i++) {
|
||
|
vcu->curr_ctx[i] = NULL;
|
||
|
vcu->curr_src_vb[i] = NULL;
|
||
|
vcu->curr_dst_vb[i] = NULL;
|
||
|
}
|
||
|
vcu->is_entering_suspend = 0;
|
||
|
pm_notifier(mtk_vcu_suspend_notifier, 0);
|
||
|
|
||
|
ret = vcu_alloc_d_ext_mem(vcu, VCU_SHMEM_SIZE);
|
||
|
if (ret != 0) {
|
||
|
dev_dbg(dev, "[VCU] allocate SHMEM failed\n");
|
||
|
goto err_after_gce;
|
||
|
}
|
||
|
|
||
|
//register_trace_signal_generate(probe_death_signal, NULL);
|
||
|
spin_lock_init(&vcu_ptr->vpud_sig_lock);
|
||
|
vcu_ptr->vpud_is_going_down = 0;
|
||
|
|
||
|
vcu_ptr->enable_vcu_dbg_log = 0;
|
||
|
|
||
|
dev_dbg(dev, "[VCU] initialization completed\n");
|
||
|
return 0;
|
||
|
|
||
|
err_after_gce:
|
||
|
for (i = 0; i < (int)VCU_CODEC_MAX; i++)
|
||
|
mutex_destroy(&vcu->gce_cmds_mutex[i]);
|
||
|
err_device:
|
||
|
class_destroy(vcu_mtkdev[vcuid]->vcu_class);
|
||
|
err_add:
|
||
|
cdev_del(vcu_mtkdev[vcuid]->vcu_cdev);
|
||
|
err_alloc:
|
||
|
unregister_chrdev_region(vcu_mtkdev[vcuid]->vcu_devno, 1);
|
||
|
vcu_mutex_destroy:
|
||
|
mutex_destroy(&vcu->vcu_mutex[VCU_VDEC]);
|
||
|
mutex_destroy(&vcu->vcu_mutex[VCU_VENC]);
|
||
|
mutex_destroy(&vcu->vcu_gce_mutex[VCU_VDEC]);
|
||
|
mutex_destroy(&vcu->vcu_gce_mutex[VCU_VENC]);
|
||
|
mutex_destroy(&vcu->ctx_ipi_binding[VCU_VDEC]);
|
||
|
mutex_destroy(&vcu->ctx_ipi_binding[VCU_VENC]);
|
||
|
mutex_destroy(&vcu->vcu_share);
|
||
|
mutex_destroy(&vpud_file_mutex);
|
||
|
err_ipi_init:
|
||
|
devm_kfree(dev, vcu);
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
static const struct of_device_id mtk_vcu_match[] = {
|
||
|
{.compatible = "mediatek,mt8173-vcu",},
|
||
|
{.compatible = "mediatek,mt2701-vpu",},
|
||
|
{.compatible = "mediatek,mt2712-vcu",},
|
||
|
{.compatible = "mediatek,mt6771-vcu",},
|
||
|
{.compatible = "mediatek-vcu",},
|
||
|
{},
|
||
|
};
|
||
|
|
||
|
MODULE_DEVICE_TABLE(of, mtk_vcu_match);
|
||
|
|
||
|
static int mtk_vcu_remove(struct platform_device *pdev)
|
||
|
{
|
||
|
struct mtk_vcu *vcu = platform_get_drvdata(pdev);
|
||
|
|
||
|
vcu_free_d_ext_mem(vcu);
|
||
|
if (vcu->is_open == true) {
|
||
|
filp_close(vcu->file, NULL);
|
||
|
vcu->is_open = false;
|
||
|
}
|
||
|
devm_kfree(&pdev->dev, vcu);
|
||
|
|
||
|
device_destroy(vcu->vcu_class, vcu->vcu_devno);
|
||
|
class_destroy(vcu->vcu_class);
|
||
|
cdev_del(vcu->vcu_cdev);
|
||
|
unregister_chrdev_region(vcu->vcu_devno, 1);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static const struct dev_pm_ops mtk_vcu_pm_ops = {
|
||
|
.suspend = mtk_vcu_suspend,
|
||
|
.resume = mtk_vcu_resume,
|
||
|
};
|
||
|
|
||
|
static struct platform_driver mtk_vcu_driver = {
|
||
|
.probe = mtk_vcu_probe,
|
||
|
.remove = mtk_vcu_remove,
|
||
|
.driver = {
|
||
|
.name = "mtk_vcu",
|
||
|
.owner = THIS_MODULE,
|
||
|
.pm = &mtk_vcu_pm_ops,
|
||
|
.of_match_table = mtk_vcu_match,
|
||
|
},
|
||
|
};
|
||
|
|
||
|
module_platform_driver(mtk_vcu_driver);
|
||
|
|
||
|
MODULE_LICENSE("GPL v2");
|
||
|
MODULE_DESCRIPTION("Mediatek Video Communication And Controller Unit driver");
|