android_kernel_samsung_sm86.../qcom/opensource/graphics-kernel/kgsl_bus.c
David Wronek 880d405719 Add 'qcom/opensource/graphics-kernel/' from commit 'b4fdc4c04295ac59109ae19d64747522740c3f14'
git-subtree-dir: qcom/opensource/graphics-kernel
git-subtree-mainline: 992813d9c1
git-subtree-split: b4fdc4c042
Change-Id:
repo: https://git.codelinaro.org/clo/la/platform/vendor/qcom/opensource/graphics-kernel
tag: GRAPHICS.LA.14.0.r1-07700-lanai.0
2024-10-06 16:44:56 +02:00

214 lines
5.3 KiB
C

// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2019-2021, The Linux Foundation. All rights reserved.
* Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#include <dt-bindings/interconnect/qcom,icc.h>
#include <linux/interconnect.h>
#include <linux/of.h>
#include <soc/qcom/of_common.h>
#include "kgsl_bus.h"
#include "kgsl_device.h"
#include "kgsl_trace.h"
static u32 _ab_buslevel_update(struct kgsl_pwrctrl *pwr,
u32 ib)
{
if (!ib)
return 0;
/*
* In the absence of any other settings, make ab 25% of ib
* where the ib vote is in kbps
*/
if ((!pwr->bus_percent_ab) && (!pwr->bus_ab_mbytes))
return 25 * ib / 100000;
if (pwr->bus_width)
return pwr->bus_ab_mbytes;
return (pwr->bus_percent_ab * pwr->bus_max) / 100;
}
int kgsl_bus_update(struct kgsl_device *device,
enum kgsl_bus_vote vote_state)
{
struct kgsl_pwrctrl *pwr = &device->pwrctrl;
int buslevel;
u32 ab;
/* the bus should be ON to update the active frequency */
if ((vote_state != KGSL_BUS_VOTE_OFF) &&
!(test_bit(KGSL_PWRFLAGS_AXI_ON, &pwr->power_flags)))
return 0;
/*
* If the bus should remain on calculate our request and submit it,
* otherwise request bus level 0, off.
*/
switch (vote_state) {
case KGSL_BUS_VOTE_OFF:
/* If the bus is being turned off, reset to default level */
pwr->cur_dcvs_buslevel = 0;
pwr->bus_mod = 0;
pwr->bus_percent_ab = 0;
pwr->bus_ab_mbytes = 0;
ab = 0;
break;
case KGSL_BUS_VOTE_ON:
{
/* FIXME: this might be wrong? */
int cur = pwr->pwrlevels[pwr->active_pwrlevel].bus_freq;
buslevel = min_t(int, pwr->pwrlevels[0].bus_max,
cur + pwr->bus_mod);
buslevel = max_t(int, buslevel, 1);
pwr->cur_dcvs_buslevel = buslevel;
ab = _ab_buslevel_update(pwr, pwr->ddr_table[buslevel]);
break;
}
case KGSL_BUS_VOTE_MINIMUM:
/* Request bus level 1, minimum non-zero value */
pwr->cur_dcvs_buslevel = 1;
pwr->bus_mod = 0;
pwr->bus_percent_ab = 0;
pwr->bus_ab_mbytes = 0;
ab = _ab_buslevel_update(pwr,
pwr->ddr_table[pwr->cur_dcvs_buslevel]);
break;
case KGSL_BUS_VOTE_RT_HINT_ON:
pwr->rt_bus_hint_active = true;
/* Only update IB during bus hint */
ab = pwr->cur_ab;
break;
case KGSL_BUS_VOTE_RT_HINT_OFF:
pwr->rt_bus_hint_active = false;
/* Only update IB during bus hint */
ab = pwr->cur_ab;
break;
}
buslevel = pwr->rt_bus_hint_active ?
max(pwr->cur_dcvs_buslevel, pwr->rt_bus_hint) :
pwr->cur_dcvs_buslevel;
return device->ftbl->gpu_bus_set(device, buslevel, ab);
}
void kgsl_icc_set_tag(struct kgsl_pwrctrl *pwr, int buslevel)
{
if (buslevel == pwr->pwrlevels[0].bus_max)
icc_set_tag(pwr->icc_path, QCOM_ICC_TAG_ALWAYS | QCOM_ICC_TAG_PERF_MODE);
else
icc_set_tag(pwr->icc_path, QCOM_ICC_TAG_ALWAYS);
}
static void validate_pwrlevels(struct kgsl_device *device, u32 *ibs,
int count)
{
struct kgsl_pwrctrl *pwr = &device->pwrctrl;
int i;
for (i = 0; i < pwr->num_pwrlevels - 1; i++) {
struct kgsl_pwrlevel *pwrlevel = &pwr->pwrlevels[i];
if (pwrlevel->bus_freq >= count) {
dev_err(device->dev, "Bus setting for GPU freq %d is out of bounds\n",
pwrlevel->gpu_freq);
pwrlevel->bus_freq = count - 1;
}
if (pwrlevel->bus_max >= count) {
dev_err(device->dev, "Bus max for GPU freq %d is out of bounds\n",
pwrlevel->gpu_freq);
pwrlevel->bus_max = count - 1;
}
if (pwrlevel->bus_min >= count) {
dev_err(device->dev, "Bus min for GPU freq %d is out of bounds\n",
pwrlevel->gpu_freq);
pwrlevel->bus_min = count - 1;
}
if (pwrlevel->bus_min > pwrlevel->bus_max) {
dev_err(device->dev, "Bus min is bigger than bus max for GPU freq %d\n",
pwrlevel->gpu_freq);
pwrlevel->bus_min = pwrlevel->bus_max;
}
}
}
u32 *kgsl_bus_get_table(struct platform_device *pdev,
const char *name, int *count)
{
u32 *levels;
int i, num = of_property_count_elems_of_size(pdev->dev.of_node,
name, sizeof(u32));
/* If the bus wasn't specified, then build a static table */
if (num <= 0)
return ERR_PTR(-EINVAL);
levels = kcalloc(num, sizeof(*levels), GFP_KERNEL);
if (!levels)
return ERR_PTR(-ENOMEM);
for (i = 0; i < num; i++)
of_property_read_u32_index(pdev->dev.of_node,
name, i, &levels[i]);
*count = num;
return levels;
}
int kgsl_bus_init(struct kgsl_device *device, struct platform_device *pdev)
{
struct kgsl_pwrctrl *pwr = &device->pwrctrl;
int count;
int ddr = of_fdt_get_ddrtype();
if (ddr >= 0) {
char str[32];
snprintf(str, sizeof(str), "qcom,bus-table-ddr%d", ddr);
pwr->ddr_table = kgsl_bus_get_table(pdev, str, &count);
if (!IS_ERR(pwr->ddr_table))
goto done;
}
/* Look if a generic table is present */
pwr->ddr_table = kgsl_bus_get_table(pdev, "qcom,bus-table-ddr", &count);
if (IS_ERR(pwr->ddr_table)) {
int ret = PTR_ERR(pwr->ddr_table);
pwr->ddr_table = NULL;
return ret;
}
done:
pwr->ddr_table_count = count;
validate_pwrlevels(device, pwr->ddr_table, pwr->ddr_table_count);
pwr->icc_path = of_icc_get(&pdev->dev, "gpu_icc_path");
if (IS_ERR(pwr->icc_path) && !gmu_core_scales_bandwidth(device)) {
WARN(1, "The CPU has no way to set the GPU bus levels\n");
kfree(pwr->ddr_table);
pwr->ddr_table = NULL;
return PTR_ERR(pwr->icc_path);
}
return 0;
}
void kgsl_bus_close(struct kgsl_device *device)
{
kfree(device->pwrctrl.ddr_table);
device->pwrctrl.ddr_table = NULL;
icc_put(device->pwrctrl.icc_path);
device->pwrctrl.icc_path = NULL;
}