Squashed backup commit (no shallow-clone history issues) of xiaomi-mt6833-dev/kernel_xiaomi_mt6833 (lineage-23.2 branch, upstream commit 9eece662e) plus all fixes applied to get everpal_defconfig building clean against a modern (GCC 15.2.0) toolchain -- see the previous commit message on the lineage-23.2 branch (also in this repo history is not available here since this is a squashed snapshot; full incremental history lives in the local DL380 checkout and the upstream GitHub remote). Upstream: https://github.com/xiaomi-mt6833-dev/kernel_xiaomi_mt6833 (lineage-23.2)
2291 lines
55 KiB
C
2291 lines
55 KiB
C
/*
|
|
* Copyright (C) 2016 MediaTek Inc.
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License version 2 as
|
|
* published by the Free Software Foundation.
|
|
*
|
|
* 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/cdev.h>
|
|
#include <linux/device.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/init.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/io.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/module.h>
|
|
#include <linux/of_address.h>
|
|
#include <linux/of_irq.h>
|
|
#include <linux/of_platform.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/proc_fs.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/types.h>
|
|
#include <linux/uaccess.h>
|
|
|
|
#ifdef CONFIG_COMPAT
|
|
/* 64 bit */
|
|
#include <linux/compat.h>
|
|
#include <linux/fs.h>
|
|
#endif
|
|
|
|
#include "vpu_cmn.h"
|
|
#include "vpu_dbg.h"
|
|
#include "vpu_load_image.h"
|
|
#include "vpubuf-core.h"
|
|
#include "vpu_utilization.h"
|
|
|
|
//#define VPU_LOAD_FW_SUPPORT
|
|
|
|
static int vpu_probe(struct platform_device *dev);
|
|
|
|
static int vpu_remove(struct platform_device *dev);
|
|
|
|
static int vpu_suspend(struct platform_device *dev, pm_message_t mesg);
|
|
|
|
static int vpu_resume(struct platform_device *dev);
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
/* VPU Driver: pm operations */
|
|
/*---------------------------------------------------------------------------*/
|
|
#ifdef CONFIG_PM
|
|
int vpu_pm_suspend(struct device *device)
|
|
{
|
|
struct platform_device *pdev = to_platform_device(device);
|
|
|
|
WARN_ON(pdev == NULL);
|
|
|
|
return vpu_suspend(pdev, PMSG_SUSPEND);
|
|
}
|
|
|
|
int vpu_pm_resume(struct device *device)
|
|
{
|
|
struct platform_device *pdev = to_platform_device(device);
|
|
|
|
WARN_ON(pdev == NULL);
|
|
|
|
return vpu_resume(pdev);
|
|
}
|
|
|
|
int vpu_pm_restore_noirq(struct device *device)
|
|
{
|
|
return 0;
|
|
}
|
|
#else
|
|
#define vpu_pm_suspend NULL
|
|
#define vpu_pm_resume NULL
|
|
#define vpu_pm_restore_noirq NULL
|
|
#endif
|
|
|
|
static const struct dev_pm_ops vpu_pm_ops = {
|
|
.suspend = vpu_pm_suspend,
|
|
.resume = vpu_pm_resume,
|
|
.freeze = vpu_pm_suspend,
|
|
.thaw = vpu_pm_resume,
|
|
.poweroff = vpu_pm_suspend,
|
|
.restore = vpu_pm_resume,
|
|
.restore_noirq = vpu_pm_restore_noirq,
|
|
};
|
|
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
/* VPU Core Driver: Prototype */
|
|
/*---------------------------------------------------------------------------*/
|
|
static int vpu_core_probe(struct platform_device *pdev)
|
|
{
|
|
struct device *dev = &pdev->dev;
|
|
int i, irq;
|
|
struct resource *mem;
|
|
struct vpu_core *vpu_core;
|
|
|
|
vpu_core = devm_kzalloc(dev, sizeof(*vpu_core), GFP_KERNEL);
|
|
if (!vpu_core)
|
|
return -ENOMEM;
|
|
|
|
LOG_INF("[vpu] %s %p\n", __func__, vpu_core);
|
|
|
|
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
|
if (mem == NULL) {
|
|
LOG_ERR("cannot get IORESOUCE_MEM\n");
|
|
return -ENOENT;
|
|
}
|
|
|
|
vpu_core->vpu_base = (unsigned long)devm_ioremap_resource(dev, mem);
|
|
if (IS_ERR((const void *)(uintptr_t)(vpu_core->vpu_base))) {
|
|
LOG_ERR("cannot get ioremap of vpu_base\n");
|
|
return -ENOENT;
|
|
}
|
|
|
|
/* interrupt resource */
|
|
irq = platform_get_irq(pdev, 0);
|
|
if (irq < 0) {
|
|
LOG_ERR("platform_get_irq fail\n");
|
|
return irq;
|
|
}
|
|
|
|
vpu_core->irq = irq;
|
|
vpu_core->irq_flags = irq_get_trigger_type(irq);
|
|
|
|
INIT_LIST_HEAD(&vpu_core->pool_list);
|
|
mutex_init(&vpu_core->servicepool_mutex);
|
|
vpu_core->servicepool_list_size = 0;
|
|
vpu_core->service_core_available = true;
|
|
mutex_init(&vpu_core->sdsp_control_mutex);
|
|
|
|
for (i = 0 ; i < VPU_REQ_MAX_NUM_PRIORITY ; i++)
|
|
vpu_core->priority_list[i] = 0;
|
|
|
|
vpu_core->dev = &pdev->dev;
|
|
platform_set_drvdata(pdev, vpu_core);
|
|
dev_set_drvdata(dev, vpu_core);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int vpu_core_remove(struct platform_device *pdev)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static const struct of_device_id vpu_core_of_ids[] = {
|
|
{.compatible = "mediatek,mt8168-vpu-core",},
|
|
{.compatible = "mediatek,mt6779-vpu-core",},
|
|
{}
|
|
};
|
|
|
|
#ifdef CONFIG_PM
|
|
static int vpu_core_runtime_suspend(struct device *dev)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static int vpu_core_runtime_resume(struct device *dev)
|
|
{
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
static const struct dev_pm_ops vpu_core_pm_ops = {
|
|
.suspend = NULL,
|
|
.resume = NULL,
|
|
#ifdef CONFIG_PM
|
|
.runtime_suspend = vpu_core_runtime_suspend,
|
|
.runtime_resume = vpu_core_runtime_resume,
|
|
.runtime_idle = NULL,
|
|
#endif
|
|
};
|
|
|
|
static struct platform_driver vpu_core_driver = {
|
|
.probe = vpu_core_probe,
|
|
.remove = vpu_core_remove,
|
|
.driver = {
|
|
.name = "mediatek,vpu-core",
|
|
.of_match_table = vpu_core_of_ids,
|
|
.pm = &vpu_core_pm_ops,
|
|
}
|
|
};
|
|
|
|
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
/* VPU Driver: Prototype */
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
static const struct of_device_id vpu_of_ids[] = {
|
|
{.compatible = "mediatek,mt8168-vpu",},
|
|
{.compatible = "mediatek,mt6779-vpu",},
|
|
{}
|
|
};
|
|
|
|
static struct platform_driver vpu_driver = {
|
|
.probe = vpu_probe,
|
|
.remove = vpu_remove,
|
|
.suspend = vpu_suspend,
|
|
.resume = vpu_resume,
|
|
.driver = {
|
|
.name = VPU_DEV_NAME,
|
|
.owner = THIS_MODULE,
|
|
.of_match_table = vpu_of_ids,
|
|
#ifdef CONFIG_PM
|
|
.pm = &vpu_pm_ops,
|
|
#endif
|
|
}
|
|
};
|
|
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
/* VPU Driver: file operations */
|
|
/*---------------------------------------------------------------------------*/
|
|
static int vpu_open(struct inode *inode, struct file *flip);
|
|
|
|
static int vpu_release(struct inode *inode, struct file *flip);
|
|
|
|
static int vpu_mmap(struct file *flip, struct vm_area_struct *vma);
|
|
|
|
#ifdef CONFIG_COMPAT
|
|
static long vpu_compat_ioctl(struct file *flip, unsigned int cmd,
|
|
unsigned long arg);
|
|
#endif
|
|
|
|
static long vpu_ioctl(struct file *flip, unsigned int cmd, unsigned long arg);
|
|
|
|
static const struct file_operations vpu_fops = {
|
|
.owner = THIS_MODULE,
|
|
.open = vpu_open,
|
|
.release = vpu_release,
|
|
.mmap = vpu_mmap,
|
|
#ifdef CONFIG_COMPAT
|
|
.compat_ioctl = vpu_compat_ioctl,
|
|
#endif
|
|
.unlocked_ioctl = vpu_ioctl
|
|
};
|
|
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
/* */
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
int vpu_create_user(struct vpu_device *vpu_device, struct vpu_user **user)
|
|
{
|
|
struct vpu_user *u;
|
|
int i = 0;
|
|
|
|
u = kzalloc(sizeof(vlist_type(struct vpu_user)), GFP_KERNEL);
|
|
if (!u)
|
|
return -ENOMEM;
|
|
|
|
mutex_init(&u->data_mutex);
|
|
mutex_lock(&vpu_device->debug_list_mutex);
|
|
vpu_device->vpu_num_users++;
|
|
mutex_unlock(&vpu_device->debug_list_mutex);
|
|
u->dev = vpu_device->dev;
|
|
u->id = NULL;
|
|
u->open_pid = current->pid;
|
|
u->open_tgid = current->tgid;
|
|
INIT_LIST_HEAD(&u->enque_list);
|
|
INIT_LIST_HEAD(&u->deque_list);
|
|
INIT_LIST_HEAD(&u->algo_list);
|
|
init_waitqueue_head(&u->deque_wait);
|
|
init_waitqueue_head(&u->delete_wait);
|
|
|
|
mutex_init(&u->dbgbuf_mutex);
|
|
INIT_LIST_HEAD(&u->dbgbuf_list);
|
|
|
|
for (i = 0 ; i < vpu_device->core_num ; i++)
|
|
u->running[i] = false;
|
|
|
|
u->deleting = false;
|
|
u->power_mode = VPU_POWER_MODE_DYNAMIC;
|
|
u->power_opp = VPU_POWER_OPP_UNREQUEST;
|
|
u->algo_num = 0;
|
|
|
|
mutex_lock(&vpu_device->user_mutex);
|
|
list_add_tail(vlist_link(u, struct vpu_user), &vpu_device->user_list);
|
|
mutex_unlock(&vpu_device->user_mutex);
|
|
|
|
*user = u;
|
|
return 0;
|
|
}
|
|
|
|
static int vpu_write_register(struct vpu_reg_values *regs)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
int vpu_push_request_to_queue(struct vpu_user *user, struct vpu_request *req)
|
|
{
|
|
struct vpu_device *vpu_device;
|
|
|
|
if (!user) {
|
|
LOG_ERR("empty user\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (user->deleting) {
|
|
LOG_WRN("push a request while deleting the user\n");
|
|
return -ENONET;
|
|
}
|
|
|
|
vpu_device = dev_get_drvdata(user->dev);
|
|
if (!vpu_device) {
|
|
LOG_ERR("Failed to get vpu_device\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
mutex_lock(&user->data_mutex);
|
|
list_add_tail(vlist_link(req, struct vpu_request), &user->enque_list);
|
|
mutex_unlock(&user->data_mutex);
|
|
|
|
wake_up(&vpu_device->req_wait);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int vpu_put_request_to_pool(struct vpu_user *user, struct vpu_request *req)
|
|
{
|
|
struct vpu_device *vpu_device = NULL;
|
|
struct vpu_core *vpu_core = NULL;
|
|
int i = 0, request_core_index = -1;
|
|
int j = 0, cnt = 0;
|
|
uint64_t handle_id;
|
|
int ion_fd;
|
|
|
|
if (!user) {
|
|
LOG_ERR("empty user\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (user->deleting) {
|
|
LOG_WRN("push a request while deleting the user\n");
|
|
return -ENONET;
|
|
}
|
|
|
|
vpu_device = dev_get_drvdata(user->dev);
|
|
if (!vpu_device) {
|
|
LOG_ERR("Failed to get vpu_device\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
for (i = 0 ; i < req->buffer_count; i++) {
|
|
for (j = 0 ; j < req->buffers[i].plane_count; j++) {
|
|
handle_id = 0;
|
|
LOG_DBG("[vpu] (%d) FD.0x%lx\n", cnt,
|
|
(unsigned long)(uintptr_t)(req->buf_ion_infos[cnt]));
|
|
ion_fd = req->buf_ion_infos[cnt];
|
|
|
|
handle_id = vbuf_import_handle(vpu_device, ion_fd);
|
|
if (IS_ERR((void *)(uintptr_t)handle_id)) {
|
|
LOG_WRN(
|
|
"[vpu_drv] import ion handle(0x%llx) failed!\n"
|
|
, handle_id);
|
|
} else {
|
|
if (vpu_device->vpu_log_level >
|
|
Log_STATE_MACHINE)
|
|
LOG_INF(
|
|
"[vpu_drv] (cnt_%d)ion_import (0x%llx)\n"
|
|
, cnt, handle_id);
|
|
/*
|
|
* import fd to handle for buffer ref count + 1
|
|
*/
|
|
req->buf_ion_infos[cnt] = handle_id;
|
|
}
|
|
cnt++;
|
|
}
|
|
}
|
|
|
|
/* CHRISTODO, specific vpu */
|
|
for (i = 0 ; i < vpu_device->core_num ; i++) {
|
|
if (req->requested_core == (0x1 << i)) {
|
|
request_core_index = i;
|
|
vpu_core = vpu_device->vpu_core[i];
|
|
if (!vpu_core->vpu_hw_support) {
|
|
LOG_ERR(
|
|
"[vpu_%d] not support. push to common queue\n"
|
|
, request_core_index);
|
|
request_core_index = -1;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
#if 0
|
|
LOG_DBG("[vpu] push request to euque CORE_IDNEX (0x%x/0x%x)...\n",
|
|
req->requested_core, request_core_index);
|
|
#endif
|
|
|
|
if (request_core_index >= vpu_device->core_num) {
|
|
LOG_ERR("wrong core index (0x%x/%d/%d)",
|
|
req->requested_core,
|
|
request_core_index,
|
|
vpu_device->core_num);
|
|
}
|
|
|
|
if (request_core_index > -1 &&
|
|
request_core_index < vpu_device->core_num) {
|
|
/* LOG_DBG("[vpu] push self pool, index(%d/0x%x)\n",
|
|
* request_core_index, req->requested_core);
|
|
*/
|
|
mutex_lock(&vpu_core->servicepool_mutex);
|
|
|
|
list_add_tail(vlist_link(req, struct vpu_request),
|
|
&vpu_core->pool_list);
|
|
|
|
vpu_core->servicepool_list_size += 1;
|
|
/*add priority list number*/
|
|
vpu_core->priority_list[req->priority] += 1;
|
|
mutex_unlock(&vpu_core->servicepool_mutex);
|
|
} else {
|
|
/* LOG_DBG("[vpu] push common pool, index(%d,0x%x)\n",
|
|
* request_core_index, req->requested_core);
|
|
*/
|
|
mutex_lock(&vpu_device->commonpool_mutex);
|
|
|
|
list_add_tail(vlist_link(req, struct vpu_request),
|
|
&vpu_device->cmnpool_list);
|
|
|
|
vpu_device->commonpool_list_size += 1;
|
|
/* LOG_DBG("[vpu] push common pool size (%d)\n",
|
|
* vpu_device->commonpool_list_size);
|
|
*/
|
|
mutex_unlock(&vpu_device->commonpool_mutex);
|
|
}
|
|
|
|
wake_up(&vpu_device->req_wait);
|
|
/*LOG_DBG("[vpu] vpu_push_request_to_queue ---\n");*/
|
|
|
|
return 0;
|
|
}
|
|
|
|
bool vpu_user_is_running(struct vpu_user *user)
|
|
{
|
|
bool running = false;
|
|
int i = 0;
|
|
struct vpu_device *vpu_device;
|
|
|
|
if (!user) {
|
|
LOG_ERR("empty user\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
vpu_device = dev_get_drvdata(user->dev);
|
|
if (!vpu_device) {
|
|
LOG_ERR("Failed to get vpu_device\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
mutex_lock(&user->data_mutex);
|
|
for (i = 0 ; i < vpu_device->core_num ; i++) {
|
|
if (user->running[i]) {
|
|
running = true;
|
|
break;
|
|
}
|
|
}
|
|
mutex_unlock(&user->data_mutex);
|
|
|
|
return running;
|
|
}
|
|
|
|
int vpu_flush_requests_from_queue(struct vpu_user *user)
|
|
{
|
|
#if 0
|
|
struct list_head *head, *temp;
|
|
struct vpu_request *req;
|
|
|
|
mutex_lock(&user->data_mutex);
|
|
|
|
if (!user->running && list_empty(&user->enque_list)) {
|
|
mutex_unlock(&user->data_mutex);
|
|
return 0;
|
|
}
|
|
|
|
user->flushing = true;
|
|
mutex_unlock(&user->data_mutex);
|
|
|
|
/* the running request will add to the deque before interrupt */
|
|
wait_event_interruptible(user->deque_wait, !user->running);
|
|
|
|
while (user->running)
|
|
ndelay(1000);
|
|
|
|
mutex_lock(&user->data_mutex);
|
|
/* push the remaining enque to the deque */
|
|
list_for_each_safe(head, temp, &user->enque_list) {
|
|
req = vlist_node_of(head, struct vpu_request);
|
|
req->status = VPU_REQ_STATUS_FLUSH;
|
|
list_del_init(head);
|
|
list_add_tail(head, &user->deque_list);
|
|
}
|
|
|
|
user->flushing = false;
|
|
LOG_DBG("flushed queue, user:%d\n", user->id);
|
|
|
|
mutex_unlock(&user->data_mutex);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
int vpu_pop_request_from_queue(struct vpu_user *user,
|
|
struct vpu_request **rreq)
|
|
{
|
|
int ret;
|
|
struct vpu_request *req;
|
|
|
|
/* wait until condition is true */
|
|
ret = wait_event_interruptible(
|
|
user->deque_wait,
|
|
!list_empty(&user->deque_list));
|
|
|
|
/* ret == -ERESTARTSYS, if signal interrupt */
|
|
if (ret < 0) {
|
|
LOG_ERR("interrupt by signal, %s, ret=%d\n",
|
|
"while pop a request", ret);
|
|
*rreq = NULL;
|
|
return -EINTR;
|
|
}
|
|
|
|
mutex_lock(&user->data_mutex);
|
|
/* This part should not be happened */
|
|
if (list_empty(&user->deque_list)) {
|
|
mutex_unlock(&user->data_mutex);
|
|
LOG_ERR("pop a request from empty queue! ret=%d\n", ret);
|
|
*rreq = NULL;
|
|
return -ENODATA;
|
|
};
|
|
|
|
/* get first node from deque list */
|
|
req = vlist_node_of(user->deque_list.next, struct vpu_request);
|
|
|
|
list_del_init(vlist_link(req, struct vpu_request));
|
|
mutex_unlock(&user->data_mutex);
|
|
|
|
*rreq = req;
|
|
return 0;
|
|
}
|
|
|
|
int vpu_get_request_from_queue(struct vpu_user *user,
|
|
uint64_t request_id, struct vpu_request **rreq)
|
|
{
|
|
struct vpu_device *vpu_device = dev_get_drvdata(user->dev);
|
|
int ret;
|
|
struct list_head *head = NULL;
|
|
struct vpu_request *req;
|
|
bool get = false;
|
|
int retry = 0;
|
|
|
|
do {
|
|
/* wait until condition is true */
|
|
ret = wait_event_interruptible(
|
|
user->deque_wait,
|
|
!list_empty(&user->deque_list));
|
|
|
|
/* ret == -ERESTARTSYS, if signal interrupt */
|
|
/* ERESTARTSYS may caused by Freezing user space processes */
|
|
|
|
if (ret < 0) {
|
|
LOG_ERR("interrupt by signal, %s, ret=%d\n",
|
|
"while pop a request", ret);
|
|
if (retry < 30) {
|
|
LOG_ERR("retry=%d and sleep 10ms\n", retry);
|
|
msleep(20);
|
|
retry += 1;
|
|
get = false;
|
|
continue;
|
|
} else {
|
|
LOG_ERR("retry %d times fail, return FAIL\n",
|
|
retry);
|
|
*rreq = NULL;
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
mutex_lock(&user->data_mutex);
|
|
/* This part should not be happened */
|
|
if (list_empty(&user->deque_list)) {
|
|
mutex_unlock(&user->data_mutex);
|
|
LOG_ERR("pop a request from empty queue! ret=%d\n",
|
|
ret);
|
|
*rreq = NULL;
|
|
return -ENODATA;
|
|
};
|
|
|
|
/* get corresponding node from deque list */
|
|
list_for_each(head, &user->deque_list)
|
|
{
|
|
req = vlist_node_of(head, struct vpu_request);
|
|
LOG_DBG("[vpu] req->request_id = 0x%lx, 0x%lx\n",
|
|
(unsigned long)req->request_id,
|
|
(unsigned long)request_id);
|
|
if ((unsigned long)req->request_id ==
|
|
(unsigned long)request_id) {
|
|
get = true;
|
|
LOG_DBG("[vpu] get = true\n");
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (vpu_device->vpu_log_level > VpuLogThre_PERFORMANCE)
|
|
LOG_INF("[vpu] %s (%d)\n", __func__, get);
|
|
if (get)
|
|
list_del_init(vlist_link(req, struct vpu_request));
|
|
|
|
mutex_unlock(&user->data_mutex);
|
|
} while (!get);
|
|
|
|
*rreq = req;
|
|
return 0;
|
|
}
|
|
|
|
int vpu_get_core_status(struct vpu_device *vpu_device,
|
|
struct vpu_status *status)
|
|
{
|
|
struct vpu_core *vpu_core;
|
|
int index = status->vpu_core_index; /* - 1;*/
|
|
|
|
if (index > -1 && index < vpu_device->core_num) {
|
|
vpu_core = vpu_device->vpu_core[index];
|
|
LOG_DBG("vpu_%d, support(%d/0x%x)\n",
|
|
index, vpu_core->vpu_hw_support,
|
|
vpu_device->efuse_data);
|
|
if (vpu_core->vpu_hw_support) {
|
|
mutex_lock(&vpu_core->servicepool_mutex);
|
|
|
|
status->vpu_core_available =
|
|
vpu_core->service_core_available;
|
|
|
|
status->pool_list_size =
|
|
vpu_core->servicepool_list_size;
|
|
mutex_unlock(&vpu_core->servicepool_mutex);
|
|
} else {
|
|
LOG_ERR("core_%d not support (0x%x).\n",
|
|
index, vpu_device->efuse_data);
|
|
return -EINVAL;
|
|
}
|
|
} else {
|
|
mutex_lock(&vpu_device->commonpool_mutex);
|
|
status->vpu_core_available = true;
|
|
status->pool_list_size = vpu_device->commonpool_list_size;
|
|
mutex_unlock(&vpu_device->commonpool_mutex);
|
|
}
|
|
|
|
LOG_DBG("[vpu]%s idx(%d), available(%d), size(%d)\n", __func__,
|
|
status->vpu_core_index,
|
|
status->vpu_core_available,
|
|
status->pool_list_size);
|
|
|
|
return 0;
|
|
}
|
|
|
|
bool vpu_is_available(struct vpu_device *vpu_device)
|
|
{
|
|
int i = 0;
|
|
int pool_wait_size = 0;
|
|
|
|
mutex_lock(&vpu_device->commonpool_mutex);
|
|
pool_wait_size = vpu_device->commonpool_list_size;
|
|
mutex_unlock(&vpu_device->commonpool_mutex);
|
|
|
|
|
|
if (pool_wait_size != 0) {
|
|
LOG_INF("common pool size = %d, no empty vpu \r\n",
|
|
pool_wait_size);
|
|
|
|
return false;
|
|
}
|
|
|
|
for (i = 0; i < vpu_device->core_num; i++) {
|
|
struct vpu_core *vpu_core = vpu_device->vpu_core[i];
|
|
|
|
mutex_lock(&vpu_core->servicepool_mutex);
|
|
|
|
if (vpu_core->service_core_available)
|
|
pool_wait_size = vpu_core->servicepool_list_size;
|
|
|
|
mutex_unlock(&vpu_core->servicepool_mutex);
|
|
|
|
LOG_INF("vpu_%d, pool size = %d\r\n", i, pool_wait_size);
|
|
if ((pool_wait_size == 0) &&
|
|
vpu_is_idle(vpu_device->vpu_core[i])) {
|
|
LOG_INF("vpu_%d, is available !!\r\n", i);
|
|
return true;
|
|
}
|
|
}
|
|
LOG_INF("GG, no vpu available !!\r\n");
|
|
|
|
return false;
|
|
}
|
|
|
|
int vpu_delete_user(struct vpu_user *user)
|
|
{
|
|
struct vpu_device *vpu_device;
|
|
struct list_head *head, *temp;
|
|
struct vpu_request *req;
|
|
struct vpu_algo *algo;
|
|
int ret = 0;
|
|
int retry = 0;
|
|
|
|
if (!user) {
|
|
LOG_ERR("delete empty user!\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
vpu_device = dev_get_drvdata(user->dev);
|
|
|
|
vpu_check_dbg_buf(user);
|
|
|
|
mutex_lock(&user->data_mutex);
|
|
user->deleting = true;
|
|
mutex_unlock(&user->data_mutex);
|
|
|
|
/*vpu_flush_requests_from_queue(user);*/
|
|
|
|
ret = wait_event_interruptible(
|
|
user->delete_wait,
|
|
!vpu_user_is_running(user));
|
|
|
|
/* ret == -ERESTARTSYS, if signal interrupt */
|
|
/* ERESTARTSYS may caused by Freezing user space processes */
|
|
if (ret < 0) {
|
|
LOG_WRN("[vpu]%s, ret=%d, wait delete user again\n",
|
|
"interrupt by signal", ret);
|
|
|
|
LOG_WRN("ERESTARTSYS = %d\n", ERESTARTSYS);
|
|
|
|
while (ret < 0 && retry < 30) {
|
|
msleep(20);
|
|
LOG_ERR("ret=%d retry=%d and sleep 10ms\n", ret, retry);
|
|
|
|
ret = wait_event_interruptible(user->delete_wait,
|
|
!vpu_user_is_running(user));
|
|
retry += 1;
|
|
}
|
|
}
|
|
|
|
|
|
/* clear the list of deque */
|
|
mutex_lock(&user->data_mutex);
|
|
list_for_each_safe(head, temp, &user->deque_list) {
|
|
req = vlist_node_of(head, struct vpu_request);
|
|
list_del(head);
|
|
vpu_free_request(req);
|
|
}
|
|
mutex_unlock(&user->data_mutex);
|
|
|
|
list_for_each_safe(head, temp, &user->algo_list) {
|
|
algo = vlist_node_of(head, struct vpu_algo);
|
|
list_del(head);
|
|
vpu_free_algo(algo);
|
|
}
|
|
|
|
/* confirm the lock has released */
|
|
if (user->locked)
|
|
vpu_hw_unlock(user);
|
|
|
|
mutex_lock(&vpu_device->user_mutex);
|
|
LOG_INF("deleted user[0x%lx]\n", (unsigned long)(user->id));
|
|
list_del(vlist_link(user, struct vpu_user));
|
|
mutex_unlock(&vpu_device->user_mutex);
|
|
|
|
kfree(user);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int vpu_dump_user_algo(struct seq_file *s, struct vpu_device *gvpu_device)
|
|
{
|
|
struct vpu_device *vpu_device = NULL;
|
|
struct vpu_user *user;
|
|
struct list_head *head_user;
|
|
struct list_head *head_algo;
|
|
struct vpu_algo *algo;
|
|
int header = 4;
|
|
unsigned long magic_num1 = 0;
|
|
unsigned long magic_num2 = 1;
|
|
const char *line_bar =
|
|
" +------+-----+--------------------------------+--------+-----------+----------+\n"
|
|
;
|
|
|
|
if (gvpu_device) {
|
|
vpu_device = gvpu_device;
|
|
} else {
|
|
if (s == NULL || s->private == NULL) {
|
|
LOG_ERR("[vpu seq_file] seq_file error");
|
|
return -EINVAL;
|
|
}
|
|
|
|
vpu_device = (struct vpu_device *)s->private;
|
|
}
|
|
|
|
vpu_print_seq(s, "%s", line_bar);
|
|
vpu_print_seq(s, " |%-6s|%-5s|%-32s|%-8s|%-11s|%-10s|\n",
|
|
"Header", "Id", "Name", "MagicNum", "MVA", "Length");
|
|
vpu_print_seq(s, "%s", line_bar);
|
|
|
|
|
|
mutex_lock(&vpu_device->user_mutex);
|
|
list_for_each(head_user, &vpu_device->user_list)
|
|
{
|
|
user = vlist_node_of(head_user, struct vpu_user);
|
|
list_for_each(head_algo, &user->algo_list)
|
|
{
|
|
algo = vlist_node_of(head_algo, struct vpu_algo);
|
|
vpu_print_seq(s,
|
|
" |%-6d|%-5d|%-32s|0x%-6lx|0x%-9lx|0x%-8x|\n",
|
|
header,
|
|
algo->id[0] ? algo->id[0]:algo->id[1],
|
|
algo->name,
|
|
(algo->id[0] ? magic_num1:magic_num2),
|
|
(unsigned long)algo->bin_ptr,
|
|
algo->bin_length);
|
|
}
|
|
}
|
|
mutex_unlock(&vpu_device->user_mutex);
|
|
vpu_print_seq(s, "%s", line_bar);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int vpu_dump_user(struct seq_file *s, struct vpu_device *gvpu_device)
|
|
{
|
|
struct vpu_device *vpu_device;
|
|
struct vpu_user *user;
|
|
struct list_head *head_user;
|
|
struct list_head *head_req;
|
|
uint32_t cnt_deq;
|
|
const char *line_bar =
|
|
" +------------------+------+------+-------+-------+\n";
|
|
|
|
if (gvpu_device) {
|
|
vpu_device = gvpu_device;
|
|
} else {
|
|
if (s == NULL || s->private == NULL) {
|
|
LOG_ERR("[vpu seq_file] seq_file error");
|
|
return -EINVAL;
|
|
}
|
|
|
|
vpu_device = (struct vpu_device *)s->private;
|
|
}
|
|
|
|
vpu_print_seq(s, "%s", line_bar);
|
|
vpu_print_seq(s, " |%-18s|%-6s|%-6s|%-7s|%-7s|\n",
|
|
"Id", "Pid", "Tid", "Deque", "Locked");
|
|
vpu_print_seq(s, "%s", line_bar);
|
|
|
|
mutex_lock(&vpu_device->user_mutex);
|
|
list_for_each(head_user, &vpu_device->user_list)
|
|
{
|
|
user = vlist_node_of(head_user, struct vpu_user);
|
|
cnt_deq = 0;
|
|
|
|
list_for_each(head_req, &user->deque_list)
|
|
{
|
|
cnt_deq++;
|
|
}
|
|
|
|
vpu_print_seq(s, " |0x%-16lx|%-6d|%-6d|%-7d|%7d|\n",
|
|
(unsigned long)(user->id),
|
|
user->open_pid,
|
|
user->open_tgid,
|
|
cnt_deq,
|
|
user->locked);
|
|
vpu_print_seq(s, "%s", line_bar);
|
|
}
|
|
mutex_unlock(&vpu_device->user_mutex);
|
|
vpu_print_seq(s, "\n");
|
|
|
|
return 0;
|
|
}
|
|
|
|
int vpu_alloc_debug_info(struct vpu_dev_debug_info **rdbginfo)
|
|
{
|
|
struct vpu_dev_debug_info *dbginfo;
|
|
|
|
dbginfo = kzalloc(sizeof(vlist_type(struct vpu_dev_debug_info)),
|
|
GFP_KERNEL);
|
|
if (dbginfo == NULL) {
|
|
LOG_ERR("%s, node=0x%p\n", __func__, dbginfo);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
*rdbginfo = dbginfo;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int vpu_free_debug_info(struct vpu_dev_debug_info *dbginfo)
|
|
{
|
|
if (dbginfo != NULL)
|
|
kfree(dbginfo);
|
|
return 0;
|
|
}
|
|
|
|
int vpu_dump_device_dbg(struct seq_file *s, struct vpu_device *gvpu_device)
|
|
{
|
|
struct vpu_device *vpu_device = NULL;
|
|
struct list_head *head = NULL;
|
|
struct vpu_dev_debug_info *dbg_info;
|
|
const char *line_bar =
|
|
" +-------+-------+-------+------------------------------+\n";
|
|
|
|
if (gvpu_device) {
|
|
vpu_device = gvpu_device;
|
|
} else {
|
|
if (s == NULL || s->private == NULL) {
|
|
LOG_ERR("[vpu seq_file] seq_file error");
|
|
return -EINVAL;
|
|
}
|
|
|
|
vpu_device = (struct vpu_device *)s->private;
|
|
}
|
|
|
|
vpu_print_seq(s, "========== vpu device debug info dump ==========\n");
|
|
vpu_print_seq(s, "%s", line_bar);
|
|
vpu_print_seq(s, " |%-7s|%-7s|%-7s|%-30s|\n",
|
|
"PID", "TGID", "OPENFD", "USER");
|
|
vpu_print_seq(s, "%s", line_bar);
|
|
|
|
mutex_lock(&vpu_device->debug_list_mutex);
|
|
list_for_each(head, &vpu_device->device_debug_list)
|
|
{
|
|
dbg_info = vlist_node_of(head, struct vpu_dev_debug_info);
|
|
vpu_print_seq(s, " |%-7d|%-7d|%-7d|%-30s|\n",
|
|
dbg_info->open_pid,
|
|
dbg_info->open_tgid,
|
|
dbg_info->dev_fd,
|
|
dbg_info->callername);
|
|
}
|
|
mutex_unlock(&vpu_device->debug_list_mutex);
|
|
vpu_print_seq(s, "%s", line_bar);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
/* IOCTL: implementation */
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
static int vpu_open(struct inode *inode, struct file *flip)
|
|
{
|
|
int ret = 0, i = 0;
|
|
bool not_support_vpu = true;
|
|
struct vpu_user *user;
|
|
struct vpu_device *vpu_device;
|
|
|
|
vpu_device =
|
|
container_of(inode->i_cdev, struct vpu_device, vpu_chardev);
|
|
|
|
for (i = 0 ; i < vpu_device->core_num ; i++) {
|
|
if (vpu_device->vpu_core[i]->vpu_hw_support) {
|
|
not_support_vpu = false;
|
|
break;
|
|
}
|
|
}
|
|
if (not_support_vpu) {
|
|
LOG_ERR("not support vpu...(%d/0x%x)\n",
|
|
not_support_vpu, vpu_device->efuse_data);
|
|
return -ENODEV;
|
|
}
|
|
|
|
LOG_INF("vpu_support core : 0x%x\n", vpu_device->efuse_data);
|
|
|
|
vpu_load_image(vpu_device);
|
|
|
|
vpu_create_user(vpu_device, &user);
|
|
if (IS_ERR_OR_NULL(user)) {
|
|
LOG_ERR("fail to create user\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
user->id = (unsigned long *)user;
|
|
LOG_INF("%s cnt(%d) user->id : 0x%lx, tids(%d/%d)\n", __func__,
|
|
vpu_device->vpu_num_users,
|
|
(unsigned long)(user->id),
|
|
user->open_pid, user->open_tgid);
|
|
flip->private_data = user;
|
|
|
|
return ret;
|
|
}
|
|
|
|
#ifdef CONFIG_COMPAT
|
|
static long vpu_compat_ioctl(struct file *flip, unsigned int cmd,
|
|
unsigned long arg)
|
|
{
|
|
return flip->f_op->unlocked_ioctl(flip, cmd,
|
|
(unsigned long)compat_ptr(arg));
|
|
}
|
|
#endif
|
|
|
|
static long vpu_ioctl(struct file *flip, unsigned int cmd, unsigned long arg)
|
|
{
|
|
int ret = 0;
|
|
int i = 0;
|
|
struct vpu_user *user = flip->private_data;
|
|
struct vpu_device *vpu_device = dev_get_drvdata(user->dev);
|
|
|
|
switch (cmd) {
|
|
case VPU_IOCTL_SET_POWER:
|
|
{
|
|
struct vpu_power power;
|
|
|
|
ret = copy_from_user(&power, (void *) arg,
|
|
sizeof(struct vpu_power));
|
|
if (ret) {
|
|
LOG_ERR("[SET_POWER] %s, ret=%d\n",
|
|
"copy 'struct power' failed", ret);
|
|
goto out;
|
|
}
|
|
|
|
ret = vpu_set_power(user, &power);
|
|
if (ret) {
|
|
LOG_ERR("[SET_POWER] set power failed, ret=%d\n", ret);
|
|
goto out;
|
|
}
|
|
break;
|
|
}
|
|
case VPU_IOCTL_EARA_LOCK_POWER:
|
|
{
|
|
struct vpu_lock_power vpu_lock_power;
|
|
|
|
ret = copy_from_user(&vpu_lock_power,
|
|
(void *) arg, sizeof(struct vpu_lock_power));
|
|
if (ret) {
|
|
LOG_ERR("[EARA_LOCK] %s, ret=%d\n",
|
|
"copy 'struct power' failed", ret);
|
|
goto out;
|
|
}
|
|
if ((vpu_lock_power.lock != true)
|
|
|| vpu_lock_power.priority != EARA_QOS) {
|
|
LOG_ERR("[EARA_LOCK] get arg fail\n");
|
|
goto out;
|
|
}
|
|
LOG_INF("[vpu] EARA_LOCK + core:%d, maxb:%d, minb:%d\n",
|
|
vpu_lock_power.core, vpu_lock_power.max_boost_value,
|
|
vpu_lock_power.min_boost_value);
|
|
ret = vpu_lock_set_power(vpu_device, &vpu_lock_power);
|
|
if (ret) {
|
|
LOG_ERR("[EARA_LOCK] lock power failed, ret=%d\n", ret);
|
|
goto out;
|
|
}
|
|
break;
|
|
}
|
|
case VPU_IOCTL_EARA_UNLOCK_POWER:
|
|
{
|
|
struct vpu_lock_power vpu_lock_power;
|
|
|
|
ret = copy_from_user(&vpu_lock_power,
|
|
(void *) arg, sizeof(struct vpu_lock_power));
|
|
if (ret) {
|
|
LOG_ERR("[EARA_UNLOCK] %s, ret=%d\n",
|
|
"copy 'struct power' failed", ret);
|
|
goto out;
|
|
}
|
|
if ((vpu_lock_power.lock != false)
|
|
|| vpu_lock_power.priority != EARA_QOS) {
|
|
LOG_ERR("[EARA_UNLOCK] get arg fail\n");
|
|
goto out;
|
|
}
|
|
LOG_INF("[vpu] EARA_UNLOCK + core:%d\n",
|
|
vpu_lock_power.core);
|
|
vpu_unlock_set_power(vpu_device, &vpu_lock_power);
|
|
if (ret) {
|
|
LOG_ERR("[EARA_UNLOCK]unlock failed, ret=%d\n", ret);
|
|
goto out;
|
|
}
|
|
|
|
break;
|
|
}
|
|
case VPU_IOCTL_POWER_HAL_LOCK_POWER:
|
|
{
|
|
struct vpu_lock_power vpu_lock_power;
|
|
|
|
ret = copy_from_user(&vpu_lock_power, (void *) arg,
|
|
sizeof(struct vpu_lock_power));
|
|
if (ret) {
|
|
LOG_ERR("[POWER_HAL_LOCK] %s, ret=%d\n",
|
|
"copy 'struct power' failed", ret);
|
|
goto out;
|
|
}
|
|
if ((vpu_lock_power.lock != true)
|
|
|| vpu_lock_power.priority != POWER_HAL) {
|
|
LOG_ERR("[POWER_HAL] get arg fail\n");
|
|
goto out;
|
|
}
|
|
LOG_INF("[vpu]POWER_HAL_LOCK+core:%d, maxb:%d, minb:%d\n",
|
|
vpu_lock_power.core, vpu_lock_power.max_boost_value,
|
|
vpu_lock_power.min_boost_value);
|
|
ret = vpu_lock_set_power(vpu_device, &vpu_lock_power);
|
|
if (ret) {
|
|
LOG_ERR("[POWER_HAL_LOCK]failed, ret=%d\n", ret);
|
|
goto out;
|
|
}
|
|
|
|
|
|
break;
|
|
}
|
|
case VPU_IOCTL_POWER_HAL_UNLOCK_POWER:
|
|
{
|
|
struct vpu_lock_power vpu_lock_power;
|
|
|
|
ret = copy_from_user(&vpu_lock_power, (void *) arg,
|
|
sizeof(struct vpu_lock_power));
|
|
if (ret) {
|
|
LOG_ERR("[POWER_HAL_UNLOCK] %s, ret=%d\n",
|
|
"copy 'struct power' failed", ret);
|
|
goto out;
|
|
}
|
|
if ((vpu_lock_power.lock != false)
|
|
|| vpu_lock_power.priority != POWER_HAL) {
|
|
LOG_ERR("[POWER_HAL_UNLOCK] get arg fail\n");
|
|
goto out;
|
|
}
|
|
LOG_INF("[vpu]POWER_HAL_UNLOCK+ core:%d\n",
|
|
vpu_lock_power.core);
|
|
ret = vpu_unlock_set_power(vpu_device, &vpu_lock_power);
|
|
if (ret) {
|
|
LOG_ERR("[POWER_HAL_UNLOCK]failed, ret=%d\n", ret);
|
|
goto out;
|
|
}
|
|
break;
|
|
}
|
|
case VPU_IOCTL_ENQUE_REQUEST:
|
|
{
|
|
struct vpu_request *req;
|
|
struct vpu_request *u_req;
|
|
int plane_count;
|
|
|
|
u_req = (struct vpu_request *) arg;
|
|
|
|
if (vpu_device->vpu_log_level > VpuLogThre_PERFORMANCE)
|
|
LOG_INF("[vpu] VPU_IOCTL_ENQUE_REQUEST +\n");
|
|
|
|
ret = vpu_alloc_request(&req);
|
|
if (ret) {
|
|
LOG_ERR("[ENQUE alloc request failed, ret=%d\n", ret);
|
|
goto out;
|
|
}
|
|
|
|
ret = get_user(req->requested_core, &u_req->requested_core);
|
|
|
|
|
|
if (req->requested_core == VPU_TRYLOCK_CORENUM &&
|
|
(ret == 0)) {
|
|
if (false == vpu_is_available(vpu_device)) {
|
|
LOG_INF("[vpu] vpu try lock fail!!\n");
|
|
vpu_free_request(req);
|
|
ret = -EFAULT;
|
|
goto out;
|
|
}
|
|
LOG_INF("[vpu] ret = %d req->requested_core = 0x%x +\n",
|
|
ret, req->requested_core);
|
|
} else if (req->requested_core != 0xFFFF &&
|
|
req->requested_core > VPU_MAX_NUM_CORES) {
|
|
LOG_ERR("req->requested_core=%d error\n",
|
|
req->requested_core);
|
|
vpu_free_request(req);
|
|
ret = -EFAULT;
|
|
goto out;
|
|
}
|
|
|
|
ret |= get_user(req->user_id, &u_req->user_id);
|
|
ret |= get_user(req->request_id, &u_req->request_id);
|
|
//ret |= get_user(req->requested_core, &u_req->requested_core);
|
|
ret |= copy_from_user(req->algo_id, u_req->algo_id,
|
|
VPU_MAX_NUM_CORES * sizeof(vpu_id_t));
|
|
ret |= get_user(req->frame_magic, &u_req->frame_magic);
|
|
ret |= get_user(req->status, &u_req->status);
|
|
ret |= get_user(req->buffer_count, &u_req->buffer_count);
|
|
ret |= get_user(req->sett_ptr, &u_req->sett_ptr);
|
|
ret |= get_user(req->sett_length, &u_req->sett_length);
|
|
ret |= get_user(req->priv, &u_req->priv);
|
|
ret |= get_user(req->power_param.bw,
|
|
&u_req->power_param.bw);
|
|
|
|
ret |= get_user(req->power_param.freq_step,
|
|
&u_req->power_param.freq_step);
|
|
|
|
ret |= get_user(req->power_param.opp_step,
|
|
&u_req->power_param.opp_step);
|
|
|
|
ret |= get_user(req->power_param.core,
|
|
&u_req->power_param.core);
|
|
|
|
ret |= get_user(req->power_param.boost_value,
|
|
&u_req->power_param.boost_value);
|
|
ret |= get_user(req->priority,
|
|
&u_req->priority);
|
|
|
|
if (req->priority >= VPU_REQ_MAX_NUM_PRIORITY) {
|
|
LOG_ERR("%s: ENQUE: invalid priority (%d)\n",
|
|
__func__, req->priority);
|
|
req->priority = 0;
|
|
}
|
|
|
|
/*opp_step counted by vpu driver*/
|
|
if (req->power_param.boost_value != 0xff) {
|
|
if (req->power_param.boost_value >= 0 &&
|
|
req->power_param.boost_value <= 100) {
|
|
req->power_param.opp_step =
|
|
vpu_boost_value_to_opp(vpu_device,
|
|
req->power_param.boost_value);
|
|
req->power_param.freq_step =
|
|
vpu_boost_value_to_opp(vpu_device,
|
|
req->power_param.boost_value);
|
|
} else {
|
|
req->power_param.opp_step = 0xFF;
|
|
req->power_param.freq_step = 0xFF;
|
|
}
|
|
}
|
|
req->user_id = (unsigned long *)user;
|
|
|
|
if (req->request_id == 0x0) {
|
|
LOG_ERR("wrong request_id (0x%lx)\n",
|
|
(unsigned long)req->request_id);
|
|
vpu_free_request(req);
|
|
ret = -EFAULT;
|
|
goto out;
|
|
}
|
|
|
|
if (ret)
|
|
LOG_ERR("[ENQUE] get params failed, ret=%d\n", ret);
|
|
else if (req->buffer_count > VPU_MAX_NUM_PORTS) {
|
|
LOG_ERR("[ENQUE] %s, count=%d\n",
|
|
"wrong buffer count", req->buffer_count);
|
|
vpu_free_request(req);
|
|
ret = -EINVAL;
|
|
goto out;
|
|
} else if (copy_from_user(req->buffers, u_req->buffers,
|
|
req->buffer_count * sizeof(struct vpu_buffer))) {
|
|
LOG_ERR("[ENQUE] %s, ret=%d\n",
|
|
"copy 'struct buffer' failed", ret);
|
|
vpu_free_request(req);
|
|
ret = -EINVAL;
|
|
goto out;
|
|
}
|
|
|
|
/* Check if user plane_count is valid */
|
|
for (i = 0 ; i < req->buffer_count; i++) {
|
|
plane_count = req->buffers[i].plane_count;
|
|
if ((plane_count > VPU_MAX_NUM_PLANE) ||
|
|
(plane_count == 0)) {
|
|
vpu_free_request(req);
|
|
ret = -EINVAL;
|
|
LOG_ERR("[ENQUE] Buf#%d plane_cnt:%d fail\n",
|
|
i, plane_count);
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
if (copy_from_user(req->buf_ion_infos, u_req->buf_ion_infos,
|
|
req->buffer_count * VPU_MAX_NUM_PLANE *
|
|
sizeof(uint64_t))) {
|
|
LOG_ERR("[ENQUE] %s, ret=%d\n",
|
|
"copy 'buf_share_fds' failed", ret);
|
|
} else if (vpu_put_request_to_pool(user, req)) {
|
|
LOG_ERR("[ENQUE] %s, ret=%d\n",
|
|
"push to user's queue failed", ret);
|
|
} else
|
|
break;
|
|
|
|
/* free the request, error happened here*/
|
|
vpu_free_request(req);
|
|
if (vpu_device->vpu_log_level > VpuLogThre_PERFORMANCE)
|
|
LOG_INF("[vpu] .VPU_IOCTL_ENQUE_REQUEST - ");
|
|
ret = -EFAULT;
|
|
break;
|
|
}
|
|
case VPU_IOCTL_DEQUE_REQUEST:
|
|
{
|
|
struct vpu_request *req;
|
|
uint64_t kernel_request_id;
|
|
struct vpu_request *u_req;
|
|
|
|
if (vpu_device->vpu_log_level > VpuLogThre_PERFORMANCE)
|
|
LOG_INF("[vpu] VPU_IOCTL_DEQUE_REQUEST + ");
|
|
|
|
u_req = (struct vpu_request *) arg;
|
|
#if 1
|
|
ret = get_user(kernel_request_id, &u_req->request_id);
|
|
if (ret) {
|
|
LOG_ERR("[REG] get 'req id' failed,%d\n", ret);
|
|
goto out;
|
|
}
|
|
|
|
LOG_DBG("[vpu] deque test: user_id_0x%lx, request_id_0x%lx",
|
|
(unsigned long)user,
|
|
(unsigned long)(kernel_request_id));
|
|
|
|
ret = vpu_get_request_from_queue(user, kernel_request_id, &req);
|
|
#else
|
|
LOG_DBG("[vpu] dequee test: user_id_0x%lx, request_id_0x%lx",
|
|
(unsigned long)user,
|
|
(unsigned long)(u_req->request_id));
|
|
|
|
ret = vpu_get_request_from_queue(user, u_req->request_id, &req);
|
|
#endif
|
|
if (ret) {
|
|
LOG_ERR("[DEQUE] pop request failed, ret=%d\n", ret);
|
|
goto out;
|
|
}
|
|
|
|
ret = put_user(req->status, &u_req->status);
|
|
ret |= put_user(req->occupied_core, &u_req->occupied_core);
|
|
ret |= put_user(req->frame_magic, &u_req->frame_magic);
|
|
ret |= put_user(req->busy_time, &u_req->busy_time);
|
|
ret |= put_user(req->bandwidth, &u_req->bandwidth);
|
|
if (ret)
|
|
LOG_ERR("[DEQUE] update status failed, ret=%d\n", ret);
|
|
|
|
ret = vpu_free_request(req);
|
|
if (ret) {
|
|
LOG_ERR("[DEQUE] free request, ret=%d\n", ret);
|
|
goto out;
|
|
}
|
|
if (vpu_device->vpu_log_level > VpuLogThre_PERFORMANCE)
|
|
LOG_INF("[vpu] VPU_IOCTL_DEQUE_REQUEST - ");
|
|
break;
|
|
}
|
|
case VPU_IOCTL_FLUSH_REQUEST:
|
|
{
|
|
ret = vpu_flush_requests_from_queue(user);
|
|
if (ret) {
|
|
LOG_ERR("[FLUSH] flush request failed, ret=%d\n", ret);
|
|
goto out;
|
|
}
|
|
break;
|
|
}
|
|
case VPU_IOCTL_GET_ALGO_INFO:
|
|
{
|
|
LOG_WRN("DO NOT SUPPORT VPU_IOCTL_GET_ALGO_INFO");
|
|
break;
|
|
}
|
|
case VPU_IOCTL_REG_WRITE:
|
|
{
|
|
struct vpu_reg_values regs;
|
|
|
|
ret = copy_from_user(®s, (void *) arg,
|
|
sizeof(struct vpu_reg_values));
|
|
if (ret) {
|
|
LOG_ERR("[REG] copy 'struct reg' failed,%d\n", ret);
|
|
goto out;
|
|
}
|
|
|
|
ret = vpu_write_register(®s);
|
|
if (ret) {
|
|
LOG_ERR("[REG] write reg failed,%d\n", ret);
|
|
goto out;
|
|
}
|
|
break;
|
|
}
|
|
case VPU_IOCTL_LOCK:
|
|
{
|
|
LOG_WRN("DO NOT SUPPORT VPU_IOCTL_LOCK\n");
|
|
break;
|
|
}
|
|
case VPU_IOCTL_UNLOCK:
|
|
{
|
|
LOG_WRN("DO NOT SUPPORT VPU_IOCTL_LOCK\n");
|
|
break;
|
|
}
|
|
case VPU_IOCTL_LOAD_ALG_TO_POOL:
|
|
{
|
|
char name[VPU_MAX_NAME_SIZE];
|
|
vpu_id_t algo_id[MTK_VPU_CORE];
|
|
int temp_algo_id;
|
|
struct vpu_algo *u_algo;
|
|
|
|
u_algo = (struct vpu_algo *) arg;
|
|
ret = copy_from_user(name, u_algo->name, VPU_MAX_NAME_SIZE);
|
|
if (ret) {
|
|
LOG_ERR("[GET_ALGO] copy 'name' failed, ret=%d\n", ret);
|
|
goto out;
|
|
}
|
|
name[VPU_MAX_NAME_SIZE - 1] = '\0';
|
|
|
|
|
|
for (i = 0 ; i < vpu_device->core_num; i++) {
|
|
temp_algo_id = vpu_get_algo_id_by_name(
|
|
vpu_device->vpu_core[i], name, user);
|
|
if (temp_algo_id < 0) {
|
|
LOG_ERR("[GET_ALGO] %s, name=%s, id:%d\n",
|
|
"can not find algo",
|
|
name, temp_algo_id);
|
|
ret = -ESPIPE;
|
|
goto out;
|
|
} else {
|
|
LOG_DBG("[GET_ALGO] core(%d) name=%s, id=%d\n",
|
|
i, name, temp_algo_id);
|
|
}
|
|
algo_id[i] = (vpu_id_t)temp_algo_id;
|
|
}
|
|
|
|
ret = copy_to_user(u_algo->id, algo_id,
|
|
vpu_device->core_num * sizeof(vpu_id_t));
|
|
if (ret) {
|
|
LOG_ERR("[GET_ALGO] update id failed, ret=%d\n", ret);
|
|
goto out;
|
|
}
|
|
|
|
break;
|
|
}
|
|
case VPU_IOCTL_CREATE_ALGO:
|
|
{
|
|
#ifdef VPU_LOAD_FW_SUPPORT
|
|
struct vpu_create_algo *u_create_algo;
|
|
struct vpu_create_algo create_algo = {0};
|
|
|
|
u_create_algo = (struct vpu_create_algo *) arg;
|
|
ret = get_user(create_algo.core, &u_create_algo->core);
|
|
|
|
if (ret) {
|
|
LOG_ERR("[CREATE_ALGO] 'core' failed, ret=%d\n",
|
|
ret);
|
|
goto out;
|
|
}
|
|
|
|
if (create_algo.core >= vpu_device->core_num) {
|
|
ret = -EINVAL;
|
|
goto out;
|
|
}
|
|
ret = copy_from_user(create_algo.name,
|
|
u_create_algo->name, (sizeof(char)*32));
|
|
if (ret) {
|
|
LOG_ERR("[CREATE_ALGO] 'name' failed, ret=%d\n",
|
|
ret);
|
|
goto out;
|
|
}
|
|
|
|
create_algo.name[(sizeof(char)*32) - 1] = '\0';
|
|
ret = get_user(create_algo.algo_length,
|
|
&u_create_algo->algo_length);
|
|
if (ret) {
|
|
LOG_ERR("[CREATE_ALGO] 'algo_length' failed, ret=%d\n",
|
|
ret);
|
|
goto out;
|
|
}
|
|
|
|
ret = get_user(create_algo.algo_ptr, &u_create_algo->algo_ptr);
|
|
if (ret) {
|
|
LOG_ERR("[CREATE_ALGO] 'algo_ptr' failed, ret=%d\n",
|
|
ret);
|
|
goto out;
|
|
}
|
|
|
|
ret = vpu_add_algo_to_user(user, &create_algo);
|
|
if (ret)
|
|
goto out;
|
|
#else
|
|
ret = -EINVAL;
|
|
LOG_WRN("[CREATE_ALGO] was not support!\n");
|
|
#endif
|
|
break;
|
|
}
|
|
case VPU_IOCTL_FREE_ALGO:
|
|
{
|
|
#ifdef VPU_LOAD_FW_SUPPORT
|
|
struct vpu_create_algo *u_create_algo;
|
|
struct vpu_create_algo create_algo = {0};
|
|
|
|
u_create_algo = (struct vpu_create_algo *) arg;
|
|
ret = get_user(create_algo.core, &u_create_algo->core);
|
|
if (ret) {
|
|
LOG_ERR("[FREE_ALGO]'core' failed, ret=%d\n",
|
|
ret);
|
|
goto out;
|
|
}
|
|
|
|
if (create_algo.core >= vpu_device->core_num) {
|
|
ret = -EINVAL;
|
|
goto out;
|
|
}
|
|
ret = copy_from_user(create_algo.name,
|
|
u_create_algo->name, (sizeof(char)*32));
|
|
if (ret) {
|
|
LOG_ERR("[FREE_ALGO] 'name' failed, ret=%d\n",
|
|
ret);
|
|
goto out;
|
|
}
|
|
create_algo.name[(sizeof(char)*32) - 1] = '\0';
|
|
|
|
vpu_free_algo_from_user(user, &create_algo);
|
|
#else
|
|
ret = -EINVAL;
|
|
LOG_WRN("[FREE_ALGO] was not support!\n");
|
|
#endif
|
|
break;
|
|
}
|
|
|
|
case VPU_IOCTL_GET_CORE_STATUS:
|
|
{
|
|
struct vpu_status *u_status = (struct vpu_status *) arg;
|
|
struct vpu_status status;
|
|
|
|
ret = copy_from_user(&status, (void *) arg,
|
|
sizeof(struct vpu_status));
|
|
if (ret) {
|
|
LOG_ERR("[%s]copy 'struct vpu_status' failed ret=%d\n",
|
|
"GET_CORE_STATUS", ret);
|
|
goto out;
|
|
}
|
|
|
|
ret = vpu_get_core_status(vpu_device, &status);
|
|
if (ret) {
|
|
LOG_ERR("[%s] vpu_get_core_status failed, ret=%d\n",
|
|
"GET_CORE_STATUS", ret);
|
|
goto out;
|
|
}
|
|
|
|
ret = put_user(status.vpu_core_available,
|
|
(bool *)&(u_status->vpu_core_available));
|
|
ret |= put_user(status.pool_list_size,
|
|
(int *)&(u_status->pool_list_size));
|
|
if (ret) {
|
|
LOG_ERR("[%s] put to user failed, ret=%d\n",
|
|
"GET_CORE_STATUS", ret);
|
|
goto out;
|
|
}
|
|
|
|
break;
|
|
}
|
|
case VPU_IOCTL_OPEN_DEV_NOTICE:
|
|
{
|
|
struct vpu_dev_debug_info *dev_debug_info;
|
|
struct vpu_dev_debug_info *u_dev_debug_info;
|
|
|
|
ret = vpu_alloc_debug_info(&dev_debug_info);
|
|
if (ret) {
|
|
LOG_ERR("[%s] alloc debug_info failed, ret=%d\n",
|
|
"OPEN_DEV_NOTICE", ret);
|
|
goto out;
|
|
}
|
|
|
|
u_dev_debug_info = (struct vpu_dev_debug_info *) arg;
|
|
ret = get_user(dev_debug_info->dev_fd,
|
|
&u_dev_debug_info->dev_fd);
|
|
if (ret) {
|
|
LOG_ERR("[%s] copy 'dev_fd' failed, ret=%d\n",
|
|
"VPU_IOCTL_OPEN_DEV_NOTICE", ret);
|
|
}
|
|
|
|
ret |= copy_from_user(dev_debug_info->callername,
|
|
u_dev_debug_info->callername, (sizeof(char)*32));
|
|
dev_debug_info->callername[(sizeof(char)*32) - 1] = '\0';
|
|
if (ret) {
|
|
LOG_ERR("[%s] copy 'callname' failed, ret=%d\n",
|
|
"VPU_IOCTL_OPEN_DEV_NOTICE", ret);
|
|
}
|
|
|
|
dev_debug_info->open_pid = user->open_pid;
|
|
dev_debug_info->open_tgid = user->open_tgid;
|
|
|
|
if (vpu_device->vpu_log_level > Log_ALGO_OPP_INFO) {
|
|
LOG_INF("[%s] user:%s/%d. pid(%d/%d)\n",
|
|
"VPU_IOCTL_OPEN_DEV_NOTICE",
|
|
dev_debug_info->callername,
|
|
dev_debug_info->dev_fd,
|
|
dev_debug_info->open_pid,
|
|
dev_debug_info->open_tgid);
|
|
}
|
|
|
|
if (ret) {
|
|
/* error handle, free memory */
|
|
vpu_free_debug_info(dev_debug_info);
|
|
} else {
|
|
mutex_lock(&vpu_device->debug_list_mutex);
|
|
list_add_tail(vlist_link(dev_debug_info,
|
|
struct vpu_dev_debug_info),
|
|
&vpu_device->device_debug_list);
|
|
mutex_unlock(&vpu_device->debug_list_mutex);
|
|
}
|
|
|
|
break;
|
|
}
|
|
case VPU_IOCTL_CLOSE_DEV_NOTICE:
|
|
{
|
|
int dev_fd;
|
|
struct list_head *head = NULL;
|
|
struct vpu_dev_debug_info *dbg_info;
|
|
bool get = false;
|
|
|
|
ret = copy_from_user(&dev_fd, (void *) arg, sizeof(int));
|
|
if (ret) {
|
|
LOG_ERR("[%s] copy 'dev_fd' failed, ret=%d\n",
|
|
"CLOSE_DEV_NOTICE", ret);
|
|
goto out;
|
|
}
|
|
|
|
mutex_lock(&vpu_device->debug_list_mutex);
|
|
list_for_each(head, &vpu_device->device_debug_list)
|
|
{
|
|
dbg_info = vlist_node_of(head,
|
|
struct vpu_dev_debug_info);
|
|
if (vpu_device->vpu_log_level > Log_ALGO_OPP_INFO) {
|
|
LOG_INF("[%s] req_user-> = %s/%d, %d/%d\n",
|
|
"VPU_IOCTL_CLOSE_DEV_NOTICE",
|
|
dbg_info->callername,
|
|
dbg_info->dev_fd, dbg_info->open_pid,
|
|
dbg_info->open_tgid);
|
|
}
|
|
|
|
if (dbg_info->dev_fd == dev_fd) {
|
|
LOG_DBG("[%s] get fd(%d) to close\n",
|
|
"VPU_IOCTL_CLOSE_DEV_NOTICE", dev_fd);
|
|
get = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (vpu_device->vpu_log_level > Log_ALGO_OPP_INFO) {
|
|
LOG_INF("[%s] user:%d. pid(%d/%d), get(%d)\n",
|
|
"VPU_IOCTL_CLOSE_DEV_NOTICE",
|
|
dev_fd, user->open_pid,
|
|
user->open_tgid, get);
|
|
}
|
|
|
|
if (get) {
|
|
list_del_init(vlist_link(dbg_info,
|
|
struct vpu_dev_debug_info));
|
|
vpu_free_debug_info(dbg_info);
|
|
mutex_unlock(&vpu_device->debug_list_mutex);
|
|
} else {
|
|
mutex_unlock(&vpu_device->debug_list_mutex);
|
|
LOG_ERR("[%s] want to close wrong fd(%d)\n",
|
|
"VPU_IOCTL_CLOSE_DEV_NOTICE", dev_fd);
|
|
ret = -ESPIPE;
|
|
goto out;
|
|
}
|
|
|
|
break;
|
|
}
|
|
case VPU_IOCTL_SDSP_SEC_LOCK:
|
|
{
|
|
struct vpu_core *vpu_core = NULL;
|
|
|
|
LOG_WRN("SDSP_SEC_LOCK mutex in\n");
|
|
|
|
if (vpu_device->in_sec_world)
|
|
break;
|
|
|
|
for (i = 0 ; i < vpu_device->core_num ; i++) {
|
|
vpu_core = vpu_device->vpu_core[i];
|
|
mutex_lock(&vpu_core->sdsp_control_mutex);
|
|
}
|
|
|
|
LOG_WRN("SDSP_SEC_LOCK mutex-m lock\n");
|
|
ret = vpu_sdsp_get_power(vpu_device);
|
|
LOG_WRN("SDSP_POWER_ON %s\n", ret == 0?"done":"fail");
|
|
if (!vpu_is_available(vpu_device))
|
|
LOG_WRN("vpu_queue is not empty!!\n");
|
|
|
|
/* Disable IRQ */
|
|
vpu_device->in_sec_world = true;
|
|
|
|
break;
|
|
}
|
|
case VPU_IOCTL_SDSP_SEC_UNLOCK:
|
|
{
|
|
struct vpu_core *vpu_core = NULL;
|
|
|
|
if (!vpu_device->in_sec_world)
|
|
break;
|
|
|
|
ret = vpu_sdsp_put_power(vpu_device);
|
|
LOG_WRN("DSP_SEC_UNLOCK %s\n", ret == 0?"done":"fail");
|
|
/* Enable IRQ */
|
|
vpu_device->in_sec_world = false;
|
|
|
|
for (i = 0 ; i < vpu_device->core_num ; i++) {
|
|
vpu_core = vpu_device->vpu_core[i];
|
|
mutex_unlock(&vpu_core->sdsp_control_mutex);
|
|
}
|
|
|
|
LOG_WRN("DSP_SEC_UNLOCK mutex-m unlock\n");
|
|
|
|
break;
|
|
}
|
|
case VPU_IOCTL_VBUF_INFO:
|
|
{
|
|
struct vbuf_info info_buf;
|
|
|
|
ret =
|
|
copy_from_user((void *)&info_buf, (void *)arg,
|
|
sizeof(struct vbuf_info));
|
|
if (ret) {
|
|
LOG_ERR("[VBUF_INFO] get params failed, ret=%d\n",
|
|
ret);
|
|
goto out;
|
|
}
|
|
|
|
ret = vbuf_std_info(vpu_device, &info_buf);
|
|
if (ret) {
|
|
LOG_ERR("[VBUF_INFO] info buf failed, ret=%d\n",
|
|
ret);
|
|
goto out;
|
|
}
|
|
|
|
ret =
|
|
copy_to_user((void *)arg, (void *)&info_buf,
|
|
sizeof(struct vbuf_info));
|
|
if (ret) {
|
|
LOG_ERR("[VBUF_INFO] update params failed, ret=%d\n",
|
|
ret);
|
|
goto out;
|
|
}
|
|
|
|
break;
|
|
}
|
|
case VPU_IOCTL_VBUF_ALLOC:
|
|
{
|
|
struct vbuf_alloc alloc_buf;
|
|
|
|
ret =
|
|
copy_from_user((void *)&alloc_buf, (void *)arg,
|
|
sizeof(struct vbuf_alloc));
|
|
if (ret) {
|
|
LOG_ERR("[VBUF_ALLOC] get params failed, ret=%d\n",
|
|
ret);
|
|
goto out;
|
|
}
|
|
|
|
ret = vbuf_std_alloc(vpu_device, &alloc_buf);
|
|
if (ret) {
|
|
LOG_ERR("[VBUF_ALLOC] alloc buf failed, ret=%d\n",
|
|
ret);
|
|
goto out;
|
|
}
|
|
|
|
vpu_add_dbg_buf(user, alloc_buf.handle);
|
|
|
|
ret =
|
|
copy_to_user((void *)arg, (void *)&alloc_buf,
|
|
sizeof(struct vbuf_alloc));
|
|
if (ret) {
|
|
LOG_ERR("[VBUF_ALLOC] update params failed, ret=%d\n",
|
|
ret);
|
|
goto out;
|
|
}
|
|
|
|
break;
|
|
}
|
|
case VPU_IOCTL_VBUF_FREE:
|
|
{
|
|
struct vbuf_free free_buf;
|
|
|
|
ret =
|
|
copy_from_user((void *)&free_buf, (void *)arg,
|
|
sizeof(struct vbuf_free));
|
|
if (ret) {
|
|
LOG_ERR("[VBUF_FREE] get params failed, ret=%d\n",
|
|
ret);
|
|
goto out;
|
|
}
|
|
|
|
ret = vbuf_std_free(vpu_device, &free_buf);
|
|
if (ret) {
|
|
LOG_ERR("[VBUF_FREE] free buf failed, ret=%d\n",
|
|
ret);
|
|
goto out;
|
|
}
|
|
|
|
vpu_delete_dbg_buf(user, free_buf.handle);
|
|
|
|
break;
|
|
}
|
|
case VPU_IOCTL_VBUF_SYNC:
|
|
{
|
|
struct vbuf_sync sync_buf;
|
|
|
|
ret =
|
|
copy_from_user((void *)&sync_buf, (void *)arg,
|
|
sizeof(struct vbuf_sync));
|
|
if (ret) {
|
|
LOG_ERR("[VBUF_SYNC] get params failed, ret=%d\n",
|
|
ret);
|
|
goto out;
|
|
}
|
|
|
|
ret = vbuf_std_sync(vpu_device, &sync_buf);
|
|
if (ret) {
|
|
LOG_ERR("[VBUF_SYNC] sync buf failed, ret=%d\n",
|
|
ret);
|
|
goto out;
|
|
}
|
|
|
|
break;
|
|
}
|
|
default:
|
|
LOG_WRN("ioctl: no such command!\n");
|
|
ret = -EINVAL;
|
|
break;
|
|
}
|
|
|
|
out:
|
|
if (ret) {
|
|
LOG_ERR("fail, cmd(%d), pid(%d), %s=%s, %s=%d, %s=%d\n",
|
|
cmd, user->open_pid,
|
|
"process", current->comm,
|
|
"pid", current->pid,
|
|
"tgid", current->tgid);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int vpu_release(struct inode *inode, struct file *flip)
|
|
{
|
|
struct vpu_device *vpu_device = NULL;
|
|
struct vpu_user *user = flip->private_data;
|
|
|
|
vpu_device = dev_get_drvdata(user->dev);
|
|
LOG_INF("%s cnt(%d) user->id : 0x%lx, tids(%d/%d)\n", __func__,
|
|
vpu_device->vpu_num_users,
|
|
(unsigned long)(user->id),
|
|
user->open_pid, user->open_tgid);
|
|
|
|
vpu_delete_user(user);
|
|
mutex_lock(&vpu_device->debug_list_mutex);
|
|
vpu_device->vpu_num_users--;
|
|
mutex_unlock(&vpu_device->debug_list_mutex);
|
|
|
|
if (vpu_device->vpu_num_users > 10)
|
|
vpu_dump_device_dbg(NULL, vpu_device);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int vpu_mmap(struct file *flip, struct vm_area_struct *vma)
|
|
{
|
|
unsigned long length = 0;
|
|
unsigned int pfn = 0x0;
|
|
|
|
length = (vma->vm_end - vma->vm_start);
|
|
vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
|
|
pfn = vma->vm_pgoff << PAGE_SHIFT;
|
|
|
|
LOG_INF("%s:%s=0x%lx,%s=0x%x,%s=0x%lx,%s=0x%lx,%s=0x%lx,%s=0x%lx\n",
|
|
__func__,
|
|
"vm_pgoff", vma->vm_pgoff,
|
|
"pfn", pfn,
|
|
"phy", vma->vm_pgoff << PAGE_SHIFT,
|
|
"vm_start", vma->vm_start,
|
|
"vm_end", vma->vm_end,
|
|
"length", length);
|
|
|
|
switch (pfn) {
|
|
|
|
default:
|
|
LOG_ERR("illegal hw addr for mmap!\n");
|
|
return -EAGAIN;
|
|
}
|
|
}
|
|
|
|
static inline void vpu_unreg_chardev(struct vpu_device *vpu_device)
|
|
{
|
|
/* Release char driver */
|
|
cdev_del(&vpu_device->vpu_chardev);
|
|
unregister_chrdev_region(vpu_device->vpu_devt, 1);
|
|
}
|
|
|
|
static inline int vpu_reg_chardev(struct vpu_device *vpu_device)
|
|
{
|
|
int ret = 0;
|
|
|
|
ret = alloc_chrdev_region(&vpu_device->vpu_devt, 0, 1, VPU_DEV_NAME);
|
|
if ((ret) < 0) {
|
|
LOG_ERR("alloc_chrdev_region failed, %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
/* Attatch file operation. */
|
|
cdev_init(&vpu_device->vpu_chardev, &vpu_fops);
|
|
|
|
vpu_device->vpu_chardev.owner = THIS_MODULE;
|
|
|
|
/* Add to system */
|
|
ret = cdev_add(&vpu_device->vpu_chardev, vpu_device->vpu_devt, 1);
|
|
if ((ret) < 0) {
|
|
LOG_ERR("Attatch file operation failed, %d\n", ret);
|
|
goto out;
|
|
}
|
|
|
|
out:
|
|
if (ret < 0)
|
|
vpu_unreg_chardev(vpu_device);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/******************************************************************************
|
|
* platform_driver
|
|
*****************************************************************************/
|
|
static int vpu_initialize(struct platform_device *pdev,
|
|
struct vpu_device *vpu_device)
|
|
{
|
|
#ifdef MTK_VPU_FPGA_PORTING
|
|
/* get register address */
|
|
#ifdef NEVER
|
|
if (strncmp(node->name, "ipu_conn",
|
|
strlen("ipu_conn"))) {
|
|
vpu_device->vpu_syscfg_base =
|
|
(unsigned long) of_iomap(node, 0);
|
|
} else if (strncmp(node->name, "ipu_adl",
|
|
strlen("ipu_adl"))) {
|
|
vpu_device->vpu_adlctrl_base =
|
|
(unsigned long) of_iomap(node, 0);
|
|
} else if (strncmp(node->name, "ipu_vcore",
|
|
strlen("ipu_vcore"))) {
|
|
vpu_device->vpu_vcorecfg_base =
|
|
(unsigned long) of_iomap(node, 0);
|
|
}
|
|
#endif /* NEVER */
|
|
struct device_node *tmp;
|
|
|
|
tmp = of_find_compatible_node(NULL, NULL, "mediatek,ipu_conn");
|
|
vpu_device->vpu_syscfg_base = (unsigned long) of_iomap(tmp, 0);
|
|
|
|
tmp = of_find_compatible_node(NULL, NULL, "mediatek,ipu_vcore");
|
|
vpu_device->vpu_vcorecfg_base = (unsigned long) of_iomap(tmp, 0);
|
|
|
|
LOG_DBG("probe 2, %s=0x%lx, %s=0x%lx, %s=0x%lx\n",
|
|
"vpu_syscfg_base", vpu_device->vpu_syscfg_base,
|
|
"vpu_adlctrl_base", vpu_device->vpu_adlctrl_base,
|
|
"vpu_vcorecfg_base", vpu_device->vpu_vcorecfg_base);
|
|
#endif
|
|
|
|
#ifdef MTK_VPU_EMULATOR
|
|
/* emulator will fill vpu_base and bin_base */
|
|
vpu_init_emulator(vpu_device);
|
|
#else
|
|
struct device_node *smi_node = NULL;
|
|
struct device_node *node;
|
|
struct resource *mem = NULL;
|
|
struct device *dev;
|
|
uint32_t phy_addr;
|
|
uint32_t phy_size;
|
|
int i, ret = 0;
|
|
|
|
node = pdev->dev.of_node;
|
|
/* get physical address of binary data loaded by LK */
|
|
if (of_property_read_u32(node, "bin-phy-addr", &phy_addr) ||
|
|
of_property_read_u32(node, "bin-size", &phy_size)) {
|
|
LOG_INF("fail to get phy address of vpu binary!\n");
|
|
vpu_device->vpu_load_image_state = VPU_LOAD_IMAGE_UNLOAD;
|
|
} else {
|
|
/* bin_base for cpu read/write */
|
|
vpu_device->bin_base =
|
|
(unsigned long)ioremap_wc(phy_addr, phy_size);
|
|
vpu_device->bin_pa = phy_addr;
|
|
vpu_device->bin_size = phy_size;
|
|
|
|
vpu_device->image_header =
|
|
(struct vpu_image_header *)
|
|
((uintptr_t)vpu_device->bin_base +
|
|
(VPU_OFFSET_IMAGE_HEADERS));
|
|
|
|
LOG_INF("probe, %s=0x%lx %s=0x%x, %s=0x%x\n",
|
|
"bin_base", (unsigned long)vpu_device->bin_base,
|
|
"phy_addr", phy_addr,
|
|
"phy_size", phy_size);
|
|
}
|
|
|
|
/* get smi common register */
|
|
#ifdef MTK_VPU_SMI_DEBUG_ON
|
|
smi_node = of_find_compatible_node(NULL, NULL,
|
|
"mediatek,smi_common");
|
|
vpu_device->smi_cmn_base = (unsigned long) of_iomap(smi_node, 0);
|
|
#endif
|
|
|
|
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
|
if (!mem) {
|
|
LOG_ERR("cannot get IORESOUCE_MEM\n");
|
|
return -ENOENT;
|
|
}
|
|
|
|
vpu_device->vpu_vcorecfg_base =
|
|
(unsigned long)devm_ioremap_resource(&pdev->dev, mem);
|
|
if (IS_ERR((void *)vpu_device->vpu_vcorecfg_base)) {
|
|
LOG_ERR("cannot get ioremap 1\n");
|
|
return -ENOENT;
|
|
}
|
|
|
|
LOG_INF(
|
|
"probe, smi_cmn_base: 0x%lx, vpu_vcorecfg_base:0x%lx\n",
|
|
vpu_device->smi_cmn_base,
|
|
vpu_device->vpu_vcorecfg_base);
|
|
#endif
|
|
|
|
vpu_init_algo(vpu_device);
|
|
LOG_DBG("[probe] init_algo done\n");
|
|
|
|
ret = vpu_init_device(vpu_device);
|
|
if (ret) {
|
|
LOG_ERR("vpu_init_device failed");
|
|
goto return_err;
|
|
}
|
|
|
|
for (i = 0; i < vpu_device->core_num; i++) {
|
|
struct vpu_core *vpu_core = vpu_device->vpu_core[i];
|
|
|
|
vpu_core->bin_base = vpu_device->bin_base;
|
|
ret = vpu_init_hw(vpu_core);
|
|
if (ret) {
|
|
LOG_ERR("vpu_init_hw failed");
|
|
goto deinit_core;
|
|
}
|
|
LOG_DBG("[probe] [%d] init_hw done\n", i);
|
|
|
|
ret = vpu_init_util(vpu_core);
|
|
if (ret) {
|
|
LOG_ERR("vpu_init_util failed");
|
|
goto deinit_core;
|
|
}
|
|
}
|
|
|
|
#ifdef MET_POLLING_MODE
|
|
vpu_init_profile(vpu_device);
|
|
LOG_DBG("[probe] [%d] vpu_init_profile done\n", i);
|
|
#endif
|
|
|
|
/* Only register char driver in the 1st time */
|
|
ret = vpu_init_debug(vpu_device);
|
|
if (ret) {
|
|
LOG_ERR("vpu_init_debug failed");
|
|
goto deinit_core;
|
|
}
|
|
|
|
/* Register char driver */
|
|
ret = vpu_reg_chardev(vpu_device);
|
|
if (ret) {
|
|
LOG_ERR("register char failed");
|
|
goto deinit_debug;
|
|
}
|
|
|
|
/* Create class register */
|
|
vpu_device->vpu_class = class_create(THIS_MODULE, "vpudrv");
|
|
if (IS_ERR(vpu_device->vpu_class)) {
|
|
ret = PTR_ERR(vpu_device->vpu_class);
|
|
LOG_ERR("Unable to create class, err = %d\n", ret);
|
|
goto unreg_chadev;
|
|
}
|
|
|
|
dev = device_create(vpu_device->vpu_class, NULL, vpu_device->vpu_devt,
|
|
NULL, VPU_DEV_NAME);
|
|
if (IS_ERR(dev)) {
|
|
ret = PTR_ERR(dev);
|
|
LOG_ERR("Failed to create device: /dev/%s, err = %d",
|
|
VPU_DEV_NAME, ret);
|
|
goto class_destroy;
|
|
}
|
|
|
|
return 0;
|
|
|
|
class_destroy:
|
|
class_destroy(vpu_device->vpu_class);
|
|
|
|
unreg_chadev:
|
|
vpu_unreg_chardev(vpu_device);
|
|
|
|
deinit_debug:
|
|
vpu_deinit_debug(vpu_device);
|
|
|
|
deinit_core:
|
|
for (i = 0; i < vpu_device->core_num; i++) {
|
|
struct vpu_core *vpu_core = vpu_device->vpu_core[i];
|
|
|
|
if (vpu_core->vpu_util != NULL)
|
|
vpu_deinit_util(vpu_core);
|
|
|
|
if (vpu_core->vpu_hw_init_done == 1)
|
|
vpu_uninit_hw(vpu_core);
|
|
}
|
|
|
|
vpu_uninit_device(vpu_device);
|
|
return_err:
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void vpu_deinitialize(struct vpu_device *vpu_device)
|
|
{
|
|
int i;
|
|
|
|
device_destroy(vpu_device->vpu_class, vpu_device->vpu_devt);
|
|
class_destroy(vpu_device->vpu_class);
|
|
vpu_device->vpu_class = NULL;
|
|
|
|
vpu_unreg_chardev(vpu_device);
|
|
|
|
vpu_deinit_debug(vpu_device);
|
|
|
|
#ifdef MET_POLLING_MODE
|
|
vpu_uninit_profile(vpu_device);
|
|
#endif
|
|
|
|
for (i = 0; i < vpu_device->core_num; i++) {
|
|
vpu_deinit_util(vpu_device->vpu_core[i]);
|
|
vpu_uninit_hw(vpu_device->vpu_core[i]);
|
|
}
|
|
|
|
vpu_uninit_device(vpu_device);
|
|
}
|
|
|
|
static int vpu_core_attach(struct platform_device *pdev,
|
|
struct vpu_device *vpu_device)
|
|
{
|
|
struct device *dev = &pdev->dev;
|
|
struct device_node *core_node;
|
|
struct platform_device *core_pdev;
|
|
int i = 0;
|
|
|
|
do {
|
|
struct vpu_core *vpu_core = NULL;
|
|
|
|
core_node = of_parse_phandle(dev->of_node,
|
|
"mediatek,vpucore", i++);
|
|
if (!core_node)
|
|
break;
|
|
|
|
core_pdev = of_find_device_by_node(core_node);
|
|
if (core_pdev)
|
|
vpu_core = platform_get_drvdata(core_pdev);
|
|
|
|
if (!vpu_core) {
|
|
LOG_ERR("Waiting for vpu core %s\n",
|
|
core_node->full_name);
|
|
of_node_put(core_node);
|
|
return -EPROBE_DEFER;
|
|
}
|
|
of_node_put(core_node);
|
|
|
|
if (i > MTK_VPU_CORE)
|
|
return -EINVAL;
|
|
|
|
vpu_core->core = i - 1;
|
|
sprintf(vpu_core->name, "vpu%d", vpu_core->core);
|
|
vpu_device->vpu_core[i - 1] = vpu_core;
|
|
vpu_core->vpu_device = vpu_device;
|
|
} while (core_node);
|
|
|
|
vpu_device->core_num = i - 1;
|
|
|
|
if (vpu_device->core_num == 0) {
|
|
LOG_ERR("no vpu core is available\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (vpu_device->core_num != MTK_VPU_CORE) {
|
|
LOG_ERR("core_num(%d) != MTK_VPU_CORE(%d)\n",
|
|
vpu_device->core_num, MTK_VPU_CORE);
|
|
return -EINVAL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int vpu_core_detach(struct platform_device *pdev,
|
|
struct vpu_device *vpu_device)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static int vpu_probe(struct platform_device *pdev)
|
|
{
|
|
struct vpu_device *vpu_device;
|
|
struct device *dev = &pdev->dev;
|
|
int ret = 0;
|
|
|
|
vpu_device = devm_kzalloc(dev, sizeof(struct vpu_device), GFP_KERNEL);
|
|
if (!vpu_device)
|
|
return -ENOMEM;
|
|
|
|
vpu_device->vpu_init_done = 0;
|
|
vpu_device->dev = dev;
|
|
ret = vpu_core_attach(pdev, vpu_device);
|
|
if (ret)
|
|
return ret;
|
|
|
|
INIT_LIST_HEAD(&vpu_device->user_list);
|
|
mutex_init(&vpu_device->user_mutex);
|
|
|
|
INIT_LIST_HEAD(&vpu_device->cmnpool_list);
|
|
mutex_init(&vpu_device->commonpool_mutex);
|
|
vpu_device->commonpool_list_size = 0;
|
|
init_waitqueue_head(&vpu_device->req_wait);
|
|
INIT_LIST_HEAD(&vpu_device->device_debug_list);
|
|
mutex_init(&vpu_device->debug_list_mutex);
|
|
idr_init(&vpu_device->addr_idr);
|
|
|
|
ret = vpu_initialize(pdev, vpu_device);
|
|
if (ret)
|
|
goto dev_out;
|
|
|
|
platform_set_drvdata(pdev, vpu_device);
|
|
|
|
vpu_device->vpu_init_done = 1;
|
|
LOG_INF("[probe] vpu probe total initialization done\n");
|
|
|
|
return 0;
|
|
|
|
dev_out:
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int vpu_remove(struct platform_device *pdev)
|
|
{
|
|
int ret;
|
|
struct vpu_device *vpu_device = platform_get_drvdata(pdev);
|
|
|
|
if (!vpu_device) {
|
|
LOG_ERR("Failed to get vpu_device\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
vpu_deinitialize(vpu_device);
|
|
|
|
idr_destroy(&vpu_device->addr_idr);
|
|
|
|
ret = vpu_core_detach(pdev, vpu_device);
|
|
if (ret)
|
|
return ret;
|
|
|
|
vpu_device->vpu_init_done = 0;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int vpu_suspend(struct platform_device *pdev, pm_message_t mesg)
|
|
{
|
|
int i = 0;
|
|
struct vpu_device *vpu_device = platform_get_drvdata(pdev);
|
|
|
|
if (!vpu_device) {
|
|
LOG_ERR("Failed to get vpu_device\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
for (i = 0 ; i < vpu_device->core_num ; i++)
|
|
vpu_quick_suspend(vpu_device->vpu_core[i]);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int vpu_resume(struct platform_device *pdev)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static int __init VPU_INIT(void)
|
|
{
|
|
LOG_DBG("platform_driver_register start\n");
|
|
if (platform_driver_register(&vpu_core_driver)) {
|
|
LOG_ERR("failed to register VPU core driver");
|
|
return -ENODEV;
|
|
}
|
|
|
|
if (platform_driver_register(&vpu_driver)) {
|
|
LOG_ERR("failed to register VPU driver");
|
|
return -ENODEV;
|
|
}
|
|
LOG_DBG("platform_driver_register finsish\n");
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void __exit VPU_EXIT(void)
|
|
{
|
|
platform_driver_unregister(&vpu_driver);
|
|
platform_driver_unregister(&vpu_core_driver);
|
|
}
|
|
|
|
|
|
module_init(VPU_INIT);
|
|
module_exit(VPU_EXIT);
|
|
MODULE_DESCRIPTION("MTK VPU Driver");
|
|
MODULE_AUTHOR("SW6");
|
|
MODULE_LICENSE("GPL");
|