kernel-brax3-ubuntu-touch/drivers/misc/mediatek/apusys/vpu/4.5/vpu_cmd.c
erascape f319b992b1 kernel-5.15: Initial import brax3 UT kernel
* halium configs enabled

Signed-off-by: erascape <erascape@proton.me>
2025-09-23 15:17:10 +00:00

446 lines
9.7 KiB
C

// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2020 MediaTek Inc.
*/
#include <linux/wait.h>
#include <linux/mutex.h>
#include <linux/sched/clock.h>
#include "vpu_power.h"
#include "vpu_cfg.h"
#include "vpu_cmd.h"
#include "vpu_reg.h"
#include "vpu_algo.h"
static unsigned int vpu_prio(int p)
{
if (p < 0)
return 0;
if (p >= VPU_MAX_PRIORITY)
return (VPU_MAX_PRIORITY - 1);
return (unsigned int)p;
}
/**
* vpu_cmd_init - Initialize command control
* @vd: the pointer of vpu_device.
*/
int vpu_cmd_init(struct vpu_device *vd)
{
int i;
int ret = 0;
struct vpu_cmd_ctl *c;
dma_addr_t iova;
struct vpu_mem_ops *mops = vd_mops(vd);
struct vpu_config *cfg = vd_cfg(vd);
vd->cmd_timeout = cfg->cmd_timeout;
atomic_set(&vd->cmd_prio, 0);
atomic_set(&vd->cmd_active, 0);
vd->cmd_prio_max = (cfg->xos == VPU_XOS) ? VPU_MAX_PRIORITY : 1;
for (i = 0; i < vd->cmd_prio_max; i++) {
c = &vd->cmd[i];
mutex_init(&c->lock);
init_waitqueue_head(&c->wait);
c->done = false;
c->boost = VPU_PWR_NO_BOOST;
c->vi.bin = VPU_MEM_ALLOC;
c->vi.size = VPU_CMD_SIZE;
c->exe_cnt = 0;
iova = mops->alloc(vd->dev, &c->vi);
if (!iova) {
vd->cmd_prio_max = i;
vpu_cmd_exit(vd);
ret = -ENOMEM;
break;
}
}
return ret;
}
/**
* vpu_cmd_init_rv - Initialize command control for rv solution
* @vd: the pointer of vpu_device.
*/
int vpu_cmd_init_rv(struct vpu_device *vd)
{
int ret = 0;
dma_addr_t iova = 0;
struct vpu_mem_ops *mops = vd_mops(vd);
vd->iova_cmd.bin = VPU_MEM_ALLOC;
vd->iova_cmd.size = VPU_CMD_SIZE * VPU_MAX_PRIORITY;
iova = mops->alloc(vd->dev, &vd->iova_cmd);
if (!iova) {
vpu_cmd_exit_rv(vd);
ret = -ENOMEM;
}
return ret;
}
/**
* vpu_cmd_exit - free command control
* @vd: the pointer of vpu_device.
*/
void vpu_cmd_exit(struct vpu_device *vd)
{
int i;
struct vpu_mem_ops *mops = vd_mops(vd);
for (i = 0; i < vd->cmd_prio_max; i++)
mops->free(vd->dev, &vd->cmd[i].vi);
}
/**
* vpu_cmd_exit_rv - free command control for rv solution
* @vd: the pointer of vpu_device.
*/
void vpu_cmd_exit_rv(struct vpu_device *vd)
{
struct vpu_mem_ops *mops = vd_mops(vd);
mops->free(vd->dev, &vd->iova_cmd);
}
/**
* vpu_cmd_clear - clear command control when power down
* @vd: vpu_device
*/
void vpu_cmd_clear(struct vpu_device *vd)
{
int i;
struct vpu_cmd_ctl *c;
struct vpu_algo_list *al;
for (i = 0; i < vd->cmd_prio_max; i++) {
c = &vd->cmd[i];
c->cmd = 0;
c->done = false;
c->alg_ret = 0;
c->result = 0;
c->start_t = 0;
c->end_t = 0;
c->boost = VPU_PWR_NO_BOOST;
if (c->alg && c->alg->al) {
al = c->alg->al;
if (al->ops->unload)
al->ops->unload(al, i);
}
atomic_set(&vd->cmd_active, 0);
}
}
/**
* vpu_cmd_lock - Lock command execution for given priority
* @vd: the pointer of vpu_device.
* @prio: priority
*/
void vpu_cmd_lock(struct vpu_device *vd, int prio)
{
unsigned int p = vpu_prio(prio);
struct vpu_cmd_ctl *c = &vd->cmd[p];
mutex_lock_nested(&c->lock, VPU_MUTEX_CMD + p);
c->start_t = sched_clock();
c->end_t = 0;
}
/**
* vpu_cmd_unlock - Unlock command execution for given priority
* @vd: the pointer of vpu_device.
* @prio: priority
*/
void vpu_cmd_unlock(struct vpu_device *vd, int prio)
{
struct vpu_cmd_ctl *c = &vd->cmd[vpu_prio(prio)];
c->end_t = sched_clock();
mutex_unlock(&c->lock);
}
/**
* vpu_cmd_lock_all - Lock command execution for all priorities,
* used for suspend, power control.
* @vd: the pointer of vpu_device.
*/
void vpu_cmd_lock_all(struct vpu_device *vd)
{
int i;
for (i = 0; i < vd->cmd_prio_max; i++)
mutex_lock_nested(&vd->cmd[i].lock, VPU_MUTEX_CMD + i);
mutex_lock_nested(&vd->lock, VPU_MUTEX_DEV);
}
/**
* vpu_cmd_lock_all - Unlock command execution for all priorities
* @vd: the pointer of vpu_device.
*/
void vpu_cmd_unlock_all(struct vpu_device *vd)
{
int i;
mutex_unlock(&vd->lock);
for (i = (vd->cmd_prio_max - 1); i >= 0; i--)
mutex_unlock(&vd->cmd[i].lock);
}
/**
* vpu_cmd_run - initialize command of given priority before run
* @vd: the pointer of vpu_device.
* @prio: priority
* @cmd: command at INFO01
*/
void vpu_cmd_run(struct vpu_device *vd, int prio, uint32_t cmd)
{
struct vpu_cmd_ctl *c = &vd->cmd[vpu_prio(prio)];
c->cmd = cmd;
c->done = false;
c->alg_ret = 0;
c->result = 0;
c->exe_cnt++;
atomic_inc(&vd->cmd_active);
}
/**
* vpu_cmd_done - finalize command of given priority when done
* @vd: the pointer of vpu_device.
* @prio: priority
* @result: execution result from INFO00
* @alg_ret: algorithm return value from INFO02
*/
void vpu_cmd_done(struct vpu_device *vd, int prio,
uint32_t result, uint32_t alg_ret)
{
struct vpu_cmd_ctl *c = &vd->cmd[vpu_prio(prio)];
c->done = true;
c->alg_ret = alg_ret;
c->result = result;
atomic_dec(&vd->cmd_active);
}
/**
* vpu_cmd_alg_set - Set algorithm to the command control of given priority
* @vd: the pointer of vpu_device.
* @prio: priority
*/
void vpu_cmd_alg_set(struct vpu_device *vd, int prio, struct __vpu_algo *alg)
{
vd->cmd[vpu_prio(prio)].alg = alg;
}
/**
* vpu_cmd_alg - Return the algorithm from the command control
* of given priority
* @vd: the pointer of vpu_device.
* @prio: priority
*/
struct __vpu_algo *vpu_cmd_alg(struct vpu_device *vd, int prio)
{
return vd->cmd[vpu_prio(prio)].alg;
}
/**
* vpu_cmd_waitq - Return the wait queue to the command control
* of given priority
* @vd: the pointer of vpu_device.
* @prio: priority
*/
wait_queue_head_t *vpu_cmd_waitq(struct vpu_device *vd, int prio)
{
return &vd->cmd[vpu_prio(prio)].wait;
}
/**
* vpu_cmd_wake_all - Unconditionally wakeup all wait queues
*
* @vd: the pointer of vpu_device.
* Note: this function should be used by vpu_aee_exp() only
*/
void vpu_cmd_wake_all(struct vpu_device *vd)
{
int i;
if (xos_type(vd) != VPU_XOS)
return;
for (i = 0; i < vd->cmd_prio_max; i++) {
vd->cmd[i].done = true;
vd->cmd[i].result = VPU_STATE_ABORT;
wake_up_interruptible(vpu_cmd_waitq(vd, i));
}
}
/**
* vpu_cmd_alg - Return the algorithm's name from the command control
* of given priority
* @vd: the pointer of vpu_device.
* @prio: priority
*/
const char *vpu_cmd_alg_name(struct vpu_device *vd, int prio)
{
struct __vpu_algo *alg;
alg = vpu_cmd_alg(vd, prio);
return (alg) ? alg->a.name : "";
}
/**
* vpu_cmd_result - Get translated VPU execution results of bootcode (INFO00)
* from the command control of given priority
* @vd: the pointer of vpu_device.
* @prio: priority
*/
int vpu_cmd_result(struct vpu_device *vd, int prio)
{
switch (vd->cmd[vpu_prio(prio)].result) {
case VPU_STATE_READY:
case VPU_STATE_IDLE:
return 0;
case VPU_STATE_NOT_READY:
case VPU_STATE_ERROR:
return -EIO;
case VPU_STATE_BUSY:
return -EBUSY;
case VPU_STATE_TERMINATED:
case VPU_STATE_ABORT:
return -EBADFD;
default:
return -EINVAL;
}
return 0;
}
/**
* vpu_cmd_alg_ret - Get Algorithm's return value (INFO02)
* from the command control of given priority
* @vd: the pointer of vpu_device.
* @prio: priority
*/
uint32_t vpu_cmd_alg_ret(struct vpu_device *vd, int prio)
{
return vd->cmd[vpu_prio(prio)].alg_ret;
}
/**
* vpu_cmd_va - Get the virtual address of the D2D command buffer
* of given priority
* @vd: the pointer of vpu_device.
* @prio: priority
*/
static void *vpu_cmd_buf_va(struct vpu_device *vd, int prio)
{
return (void *)vd->cmd[vpu_prio(prio)].vi.m.va;
}
/**
* vpu_cmd_iova - Get the iova of the D2D command buffer
* of given priority
* @vd: the pointer of vpu_device.
* @prio: priority
*/
uint32_t vpu_cmd_buf_iova(struct vpu_device *vd, int prio)
{
return (uint32_t)vd->cmd[vpu_prio(prio)].vi.m.pa;
}
/**
* vpu_cmd_sync - Sync D2D command buffer of given priority
* for execution
* @vd: the pointer of vpu_device.
* @prio: priority
*/
static void vpu_cmd_buf_sync(struct vpu_device *vd, int prio)
{
vd_mops(vd)->sync_for_device(vd->dev, &vd->cmd[vpu_prio(prio)].vi);
}
/**
* vpu_cmd_buf_set - Set D2D command buffer of given priority
* @vd: the pointer of vpu_device.
* @prio: priority
* @buf: D2D command buffer
* @size: D2D command buffer size
*/
int vpu_cmd_buf_set(struct vpu_device *vd, int prio, void *buf, size_t size)
{
if (size > VPU_CMD_SIZE)
return -EINVAL;
memcpy(vpu_cmd_buf_va(vd, prio), buf, size);
vpu_cmd_buf_sync(vd, prio);
return 0;
}
/**
* vpu_cmd_boost_elevate - get the maximum boot value across all priorites
* @vd: vpu_device
*/
static int vpu_cmd_boost_elevate(struct vpu_device *vd)
{
int i;
int boost = -1;
if (xos_type(vd) != VPU_XOS)
return vd->cmd[0].boost;
for (i = 0; i < vd->cmd_prio_max; i++) {
if (vd->cmd[i].boost == VPU_PWR_NO_BOOST)
continue;
boost = max_t(int, vd->cmd[i].boost, boost);
}
return (boost >= 0) ? boost : VPU_PWR_NO_BOOST;
}
/**
* vpu_cmd_boost_set - set boost value to the command of given priority
* @vd: vpu_device
* @prio: priority
* @boost: boost value from vpu_request
*
* Returns the elevated boost value across all priorities
*/
int vpu_cmd_boost_set(struct vpu_device *vd, int prio, int boost)
{
vd->cmd[vpu_prio(prio)].boost = boost;
return vpu_cmd_boost_elevate(vd);
}
/**
* vpu_cmd_boost_set - unset boost value to the command of given priority
* @vd: vpu_device
* @prio: priority
*
* Returns the elevated boost value across all priorities
*/
int vpu_cmd_boost_put(struct vpu_device *vd, int prio)
{
return vpu_cmd_boost_set(vd, prio, VPU_PWR_NO_BOOST);
}
/**
* vpu_cmd_boost_set - get the boost value of given priority
* @vd: vpu_device
* @prio: priority
*/
int vpu_cmd_boost(struct vpu_device *vd, int prio)
{
return vd->cmd[vpu_prio(prio)].boost;
}
struct vpu_cmd_ops vpu_cmd_ops_ap = {
.init = vpu_cmd_init,
};
struct vpu_cmd_ops vpu_cmd_ops_rv = {
.init = vpu_cmd_init_rv,
};