Files
android/drivers/misc/mediatek/vpu/vp6/vpu_utilization.c
T
threeeye bc1d038eac REVVL 6 Pro kernel bring-up: full working tree snapshot (2026-09-12)
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)
2026-09-12 13:36:46 +00:00

304 lines
7.0 KiB
C

/*
* Copyright (c) 2017 MediaTek Inc.
* Author: Mao Lin <Zih-Ling.Lin@mediatek.com>
*
* 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/devfreq.h>
#include <linux/kernel.h>
#include <linux/ktime.h>
#include <linux/platform_device.h>
#include <linux/pm_opp.h>
#include <linux/timekeeper_internal.h>
#include "vpu_utilization.h"
#include "vpu_cmn.h"
#define VPU_TIMER_PERIOD_NS 200000000 /* ns = 200 ms */
#define VPU_TIMER_PERIOD_MS 200
static int vpu_sum_usage(struct vpu_core *vpu_core, ktime_t now)
{
s64 diff;
diff = ktime_us_delta(now, vpu_core->working_start);
if (diff < 0)
goto reset;
if (vpu_core->util_state == VCT_EXECUTING)
vpu_core->acc_busy += (unsigned long) diff;
reset:
vpu_core->working_start = now;
return 0;
}
int vpu_utilization_compute_enter(struct vpu_core *vpu_core)
{
unsigned long flags;
struct vpu_util *vpu_util = vpu_core->vpu_util;
int ret = 0;
spin_lock_irqsave(&vpu_util->lock, flags);
ret = vpu_sum_usage(vpu_core, ktime_get());
if (vpu_core->util_state == VCT_IDLE)
vpu_core->util_state = VCT_EXECUTING;
spin_unlock_irqrestore(&vpu_util->lock, flags);
return ret;
}
int vpu_utilization_compute_leave(struct vpu_core *vpu_core)
{
unsigned long flags;
struct vpu_util *vpu_util = vpu_core->vpu_util;
int ret = 0;
spin_lock_irqsave(&vpu_util->lock, flags);
ret = vpu_sum_usage(vpu_core, ktime_get());
if (vpu_core->util_state == VCT_EXECUTING)
vpu_core->util_state = VCT_IDLE;
spin_unlock_irqrestore(&vpu_util->lock, flags);
return ret;
}
int vpu_dvfs_get_usage(const struct vpu_core *vpu_core,
unsigned long *total_time, unsigned long *busy_time)
{
unsigned long flags;
unsigned long busy = 0;
unsigned long total;
struct vpu_util *vpu_util = vpu_core->vpu_util;
int ret = 0;
spin_lock_irqsave(&vpu_util->lock, flags);
busy = max(busy, vpu_core->prev_busy);
total = vpu_util->prev_total;
spin_unlock_irqrestore(&vpu_util->lock, flags);
*busy_time = busy / USEC_PER_MSEC;
*total_time = total;
if (*busy_time > *total_time)
*busy_time = *total_time;
if (*total_time == 0) {
LOG_ERR("[vpu] %s total time error\n", __func__);
return -1;
}
return ret;
}
static int vpu_util_set_period(struct vpu_core *vpu_core,
unsigned int period_ms)
{
struct vpu_util *vpu_util = vpu_core->vpu_util;
vpu_util->period_time = ktime_set(0, period_ms * 1000000);
return 0;
}
static int vpu_util_set_active(struct vpu_core *vpu_core, bool active)
{
unsigned long flags;
struct vpu_util *vpu_util = vpu_core->vpu_util;
int ret = 1;
if ((vpu_util->active == true) && (active == false)) {
spin_lock_irqsave(&vpu_util->lock, flags);
vpu_util->active = false;
spin_unlock_irqrestore(&vpu_util->lock, flags);
while (ret > 0)
ret = hrtimer_cancel(&vpu_util->timer);
} else if ((vpu_util->active == false) && (active == true)) {
/* reset data */
vpu_util->period_start = ktime_get();
vpu_core->prev_busy = 0;
vpu_core->acc_busy = 0;
vpu_core->working_start = vpu_util->period_start;
vpu_core->util_state = VCT_IDLE;
vpu_util->prev_total = 0;
spin_lock_irqsave(&vpu_util->lock, flags);
vpu_util->active = true;
spin_unlock_irqrestore(&vpu_util->lock, flags);
hrtimer_start(&vpu_util->timer, vpu_util->period_time,
HRTIMER_MODE_REL);
}
return 0;
}
int vpu_set_util_test_parameter(struct vpu_device *vpu_device, uint8_t param,
int argc, int *args)
{
int ret = 0;
int core;
switch (param) {
case VPU_DEBUG_UTIL_PERIOD:
ret = (argc == 1) ? 0 : -EINVAL;
if (ret) {
LOG_ERR("invalid argument, expected:1, received:%d\n",
argc);
goto out;
}
for (core = 0 ; core < vpu_device->core_num; core++) {
struct vpu_core *vpu_core = vpu_device->vpu_core[core];
vpu_util_set_period(vpu_core, args[0]);
}
break;
case VPU_DEBUG_UTIL_ENABLE:
ret = (argc == 1) ? 0 : -EINVAL;
if (ret) {
LOG_ERR("invalid argument, expected:1, received:%d\n",
argc);
goto out;
}
for (core = 0 ; core < vpu_device->core_num; core++) {
struct vpu_core *vpu_core = vpu_device->vpu_core[core];
vpu_util_set_active(vpu_core, args[0]);
}
break;
default:
LOG_ERR("unsupport the vpu_util parameter:%d\n", param);
break;
}
out:
return ret;
}
static int vpu_timer_get_usage(struct vpu_util *vpu_util)
{
ktime_t now = ktime_get();
struct vpu_core *vpu_core = vpu_util->vpu_core;
unsigned long flags;
s64 diff;
int ret = 0;
spin_lock_irqsave(&vpu_util->lock, flags);
diff = ktime_ms_delta(now, vpu_util->period_start);
if (diff < 0) {
vpu_util->prev_total = 0;
goto reset_usage;
}
vpu_util->prev_total = (unsigned long) diff;
ret = vpu_sum_usage(vpu_core, now);
vpu_core->prev_busy = vpu_core->acc_busy;
reset_usage:
vpu_util->period_start = now;
vpu_core->acc_busy = 0;
vpu_core->working_start = vpu_util->period_start;
spin_unlock_irqrestore(&vpu_util->lock, flags);
#if defined(VPU_MET_READY)
MET_Events_BusyRate_Trace(vpu_core->vpu_device, vpu_core->core);
#endif
return ret;
}
static enum hrtimer_restart vpu_timer_callback(struct hrtimer *timer)
{
struct vpu_util *vpu_util;
int ret = 0;
vpu_util = container_of(timer, struct vpu_util, timer);
ret = vpu_timer_get_usage(vpu_util);
if (vpu_util->active) {
hrtimer_start(&vpu_util->timer, vpu_util->period_time,
HRTIMER_MODE_REL);
}
return HRTIMER_NORESTART;
}
int vpu_init_util(struct vpu_core *vpu_core)
{
struct vpu_device *vpu_device = vpu_core->vpu_device;
struct device *dev = vpu_device->dev;
struct vpu_util *vpu_util;
ktime_t now = ktime_get();
int ret = 0;
vpu_util = devm_kzalloc(dev, sizeof(*vpu_util), GFP_KERNEL);
if (!vpu_util)
return -ENOMEM;
vpu_util->dev = dev;
vpu_util->vpu_core = vpu_core;
vpu_util->period_time = ktime_set(0, VPU_TIMER_PERIOD_NS);
vpu_util->period_start = now;
vpu_core->prev_busy = 0;
vpu_core->acc_busy = 0;
vpu_core->working_start = vpu_util->period_start;
vpu_core->util_state = VCT_IDLE;
vpu_util->prev_total = 0;
spin_lock_init(&vpu_util->lock);
vpu_util->active = true;
hrtimer_init(&vpu_util->timer, CLOCK_MONOTONIC,
HRTIMER_MODE_REL);
vpu_util->timer.function = vpu_timer_callback;
hrtimer_start(&vpu_util->timer, vpu_util->period_time,
HRTIMER_MODE_REL);
vpu_core->vpu_util = vpu_util;
return ret;
}
int vpu_deinit_util(struct vpu_core *vpu_core)
{
struct vpu_device *vpu_device = vpu_core->vpu_device;
struct device *dev = vpu_device->dev;
struct vpu_util *vpu_util = vpu_core->vpu_util;
unsigned long flags;
int ret = 1;
spin_lock_irqsave(&vpu_util->lock, flags);
vpu_util->active = false;
spin_unlock_irqrestore(&vpu_util->lock, flags);
while (ret > 0)
ret = hrtimer_cancel(&vpu_util->timer);
devm_kfree(dev, vpu_core->vpu_util);
vpu_core->vpu_util = NULL;
return ret;
}