// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2020 MediaTek Inc. */ #include #include #include #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, };