disp: msm: sde: add profiling counters support for RSC
Add support for enabling and reading profiling counters via debugfs. This change also introduces RSC rev 4 (first rev supporting profiling counters), enabling all relevant rev 3 features as well. Change-Id: I0326215b069a37c91072965379b0b4843916ee0a Signed-off-by: Steve Cohen <cohens@codeaurora.org>
This commit is contained in:
parent
7e6415ab67
commit
80aa9f9c32
@ -1,6 +1,6 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Copyright (c) 2016-2019, The Linux Foundation. All rights reserved.
|
||||
* Copyright (c) 2016-2020, The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef _SDE_RSC_H_
|
||||
@ -12,6 +12,7 @@
|
||||
#define SDE_RSC_INDEX 0
|
||||
|
||||
#define MAX_RSC_CLIENT_NAME_LEN 128
|
||||
#define NUM_RSC_PROFILING_COUNTERS 3
|
||||
|
||||
/* DRM Object IDs are numbered excluding 0, use 0 to indicate invalid CRTC */
|
||||
#define SDE_RSC_INVALID_CRTC_ID 0
|
||||
|
177
msm/sde_rsc.c
177
msm/sde_rsc.c
@ -682,9 +682,9 @@ static int sde_rsc_switch_to_vid(struct sde_rsc_priv *rsc,
|
||||
rc = rsc->hw_ops.state_update(rsc, SDE_RSC_VID_STATE);
|
||||
if (!rc) {
|
||||
rpmh_mode_solver_set(rsc->rpmh_dev,
|
||||
rsc->version == SDE_RSC_REV_3 ? true : false);
|
||||
rsc->version >= SDE_RSC_REV_3);
|
||||
sde_rsc_set_data_bus_mode(&rsc->phandle,
|
||||
rsc->version == SDE_RSC_REV_3 ?
|
||||
rsc->version >= SDE_RSC_REV_3 ?
|
||||
QCOM_ICC_TAG_WAKE : QCOM_ICC_TAG_AMC);
|
||||
}
|
||||
}
|
||||
@ -1176,6 +1176,49 @@ static int _sde_debugfs_status_open(struct inode *inode, struct file *file)
|
||||
return single_open(file, _sde_debugfs_status_show, inode->i_private);
|
||||
}
|
||||
|
||||
static int _sde_debugfs_counters_show(struct seq_file *s, void *data)
|
||||
{
|
||||
struct sde_rsc_priv *rsc = s->private;
|
||||
u32 counters[NUM_RSC_PROFILING_COUNTERS];
|
||||
int i, ret;
|
||||
|
||||
if (!rsc)
|
||||
return -EINVAL;
|
||||
|
||||
if (!rsc->hw_ops.get_counters) {
|
||||
seq_puts(s, "counters are not supported on this target\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
memset(counters, 0, sizeof(counters));
|
||||
mutex_lock(&rsc->client_lock);
|
||||
if (rsc->current_state == SDE_RSC_IDLE_STATE) {
|
||||
pr_debug("counters are not supported during idle state\n");
|
||||
seq_puts(s, "no access to counters during idle pc\n");
|
||||
goto end;
|
||||
}
|
||||
|
||||
ret = rsc->hw_ops.get_counters(rsc, counters);
|
||||
if (ret) {
|
||||
pr_err("sde rsc: get_counters failed ret:%d\n", ret);
|
||||
seq_puts(s, "failed to retrieve counts!\n");
|
||||
goto end;
|
||||
}
|
||||
|
||||
seq_puts(s, "rsc profiling counters:\n");
|
||||
for (i = 0; i < NUM_RSC_PROFILING_COUNTERS; ++i)
|
||||
seq_printf(s, "\tmode[%d] = 0x%08x:\n", i, counters[i]);
|
||||
|
||||
end:
|
||||
mutex_unlock(&rsc->client_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _sde_debugfs_counters_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, _sde_debugfs_counters_show, inode->i_private);
|
||||
}
|
||||
|
||||
static int _sde_debugfs_generic_noseek_open(struct inode *inode,
|
||||
struct file *file)
|
||||
{
|
||||
@ -1184,6 +1227,108 @@ static int _sde_debugfs_generic_noseek_open(struct inode *inode,
|
||||
return nonseekable_open(inode, file);
|
||||
}
|
||||
|
||||
static ssize_t _sde_debugfs_profiling_read(struct file *file, char __user *buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct sde_rsc_priv *rsc = file->private_data;
|
||||
size_t max_size = min_t(size_t, count, MAX_BUFFER_SIZE);
|
||||
char buffer[MAX_BUFFER_SIZE];
|
||||
int blen = 0;
|
||||
|
||||
if (*ppos || !rsc)
|
||||
return 0;
|
||||
|
||||
if (!rsc->hw_ops.setup_counters) {
|
||||
blen += scnprintf(&buffer[blen], max_size - blen,
|
||||
"counters are not supported on this target\n");
|
||||
goto end;
|
||||
}
|
||||
|
||||
mutex_lock(&rsc->client_lock);
|
||||
if (rsc->current_state == SDE_RSC_IDLE_STATE) {
|
||||
pr_debug("counters are not supported during idle state\n");
|
||||
blen += scnprintf(&buffer[blen], max_size - blen,
|
||||
"no access to counters during idle pc\n");
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
blen += scnprintf(&buffer[blen], max_size - blen,
|
||||
"%s\n", rsc->profiling_en ? "Y" : "N");
|
||||
|
||||
unlock:
|
||||
mutex_unlock(&rsc->client_lock);
|
||||
end:
|
||||
if (copy_to_user(buf, buffer, blen))
|
||||
return -EFAULT;
|
||||
|
||||
*ppos += blen;
|
||||
return blen;
|
||||
}
|
||||
|
||||
static ssize_t _sde_debugfs_profiling_write(struct file *file,
|
||||
const char __user *p, size_t count, loff_t *ppos)
|
||||
{
|
||||
struct sde_rsc_priv *rsc = file->private_data;
|
||||
bool input_valid, input_value;
|
||||
char *input;
|
||||
int rc;
|
||||
|
||||
if (!rsc || !rsc->hw_ops.setup_counters || !count ||
|
||||
count > MAX_COUNT_SIZE_SUPPORTED)
|
||||
return 0;
|
||||
|
||||
input = kmalloc(count + 1, GFP_KERNEL);
|
||||
if (!input)
|
||||
return -ENOMEM;
|
||||
|
||||
if (copy_from_user(input, p, count)) {
|
||||
kfree(input);
|
||||
return -EFAULT;
|
||||
}
|
||||
input[count] = '\0';
|
||||
|
||||
switch (input[0]) {
|
||||
case 'y':
|
||||
case 'Y':
|
||||
case '1':
|
||||
input_valid = true;
|
||||
input_value = true;
|
||||
break;
|
||||
case 'n':
|
||||
case 'N':
|
||||
case '0':
|
||||
input_valid = true;
|
||||
input_value = false;
|
||||
break;
|
||||
default:
|
||||
input_valid = false;
|
||||
count = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
mutex_lock(&rsc->client_lock);
|
||||
if (rsc->current_state == SDE_RSC_IDLE_STATE) {
|
||||
pr_debug("debug node is not supported during idle state\n");
|
||||
count = 0;
|
||||
goto end;
|
||||
}
|
||||
|
||||
pr_debug("input %s, profiling_en: %d\n",
|
||||
input_valid ? "valid" : "invalid", rsc->profiling_en);
|
||||
|
||||
if (input_valid) {
|
||||
rsc->profiling_en = input_value;
|
||||
rc = rsc->hw_ops.setup_counters(rsc, rsc->profiling_en);
|
||||
if (rc)
|
||||
pr_err("setup_counters failed, rc:%d\n", rc);
|
||||
}
|
||||
|
||||
end:
|
||||
mutex_unlock(&rsc->client_lock);
|
||||
kfree(input);
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t _sde_debugfs_mode_ctrl_read(struct file *file, char __user *buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
@ -1380,6 +1525,19 @@ static const struct file_operations vsync_status_fops = {
|
||||
.write = _sde_debugfs_vsync_mode_write,
|
||||
};
|
||||
|
||||
static const struct file_operations profiling_enable_fops = {
|
||||
.open = _sde_debugfs_generic_noseek_open,
|
||||
.read = _sde_debugfs_profiling_read,
|
||||
.write = _sde_debugfs_profiling_write,
|
||||
};
|
||||
|
||||
static const struct file_operations profiling_counts_fops = {
|
||||
.open = _sde_debugfs_counters_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
};
|
||||
|
||||
static void _sde_rsc_init_debugfs(struct sde_rsc_priv *rsc, char *name)
|
||||
{
|
||||
rsc->debugfs_root = debugfs_create_dir(name, NULL);
|
||||
@ -1393,6 +1551,14 @@ static void _sde_rsc_init_debugfs(struct sde_rsc_priv *rsc, char *name)
|
||||
&mode_control_fops);
|
||||
debugfs_create_file("vsync_mode", 0600, rsc->debugfs_root, rsc,
|
||||
&vsync_status_fops);
|
||||
if (rsc->profiling_supp) {
|
||||
debugfs_create_file("profiling_en", 0600, rsc->debugfs_root,
|
||||
rsc, &profiling_enable_fops);
|
||||
debugfs_create_file("profiling_counts", 0400,
|
||||
rsc->debugfs_root, rsc,
|
||||
&profiling_counts_fops);
|
||||
}
|
||||
|
||||
debugfs_create_x32("debug_mode", 0600, rsc->debugfs_root,
|
||||
&rsc->debug_mode);
|
||||
}
|
||||
@ -1515,12 +1681,12 @@ static int sde_rsc_probe(struct platform_device *pdev)
|
||||
of_property_read_u32(pdev->dev.of_node, "qcom,sde-rsc-version",
|
||||
&rsc->version);
|
||||
|
||||
if (rsc->version == SDE_RSC_REV_2)
|
||||
if (rsc->version >= SDE_RSC_REV_2)
|
||||
rsc->single_tcs_execution_time = SINGLE_TCS_EXECUTION_TIME_V2;
|
||||
else
|
||||
rsc->single_tcs_execution_time = SINGLE_TCS_EXECUTION_TIME_V1;
|
||||
|
||||
if (rsc->version == SDE_RSC_REV_3) {
|
||||
if (rsc->version >= SDE_RSC_REV_3) {
|
||||
rsc->time_slot_0_ns = rsc->single_tcs_execution_time
|
||||
+ RSC_MODE_INSTRUCTION_TIME;
|
||||
rsc->backoff_time_ns = RSC_MODE_INSTRUCTION_TIME;
|
||||
@ -1534,6 +1700,9 @@ static int sde_rsc_probe(struct platform_device *pdev)
|
||||
+ RSC_MODE_THRESHOLD_OVERHEAD;
|
||||
}
|
||||
|
||||
if (rsc->version >= SDE_RSC_REV_4)
|
||||
rsc->profiling_supp = true;
|
||||
|
||||
ret = sde_power_resource_init(pdev, &rsc->phandle);
|
||||
if (ret) {
|
||||
pr_err("sde rsc:power resource init failed ret:%d\n", ret);
|
||||
|
@ -1,6 +1,6 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Copyright (c) 2016-2019, The Linux Foundation. All rights reserved.
|
||||
* Copyright (c) 2016-2020, The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef _SDE_RSC_HW_H_
|
||||
@ -65,6 +65,10 @@
|
||||
|
||||
#define SDE_RSCC_TCS_DRV0_CONTROL 0x1c14
|
||||
|
||||
#define SDE_RSCC_LPM_PROFILING_COUNTER0_EN_DRV0 0x4d00
|
||||
#define SDE_RSCC_LPM_PROFILING_COUNTER0_CLR_DRV0 0x4d04
|
||||
#define SDE_RSCC_LPM_PROFILING_COUNTER0_STATUS_DRV0 0x4d08
|
||||
|
||||
#define SDE_RSCC_WRAPPER_CTRL 0x000
|
||||
#define SDE_RSCC_WRAPPER_OVERRIDE_CTRL 0x004
|
||||
#define SDE_RSCC_WRAPPER_STATIC_WAKEUP_0 0x008
|
||||
|
@ -1,6 +1,6 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2016-2019, The Linux Foundation. All rights reserved.
|
||||
* Copyright (c) 2016-2020, The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) "[sde_rsc_hw:%s:%d]: " fmt, __func__, __LINE__
|
||||
@ -561,6 +561,50 @@ int rsc_hw_bwi_status_v3(struct sde_rsc_priv *rsc, bool bw_indication)
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int rsc_hw_profiling_counter_ctrl(struct sde_rsc_priv *rsc, bool enable)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!rsc) {
|
||||
pr_debug("invalid input param\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
for (i = 0; i < NUM_RSC_PROFILING_COUNTERS; ++i) {
|
||||
dss_reg_w(&rsc->drv_io,
|
||||
SDE_RSCC_LPM_PROFILING_COUNTER0_EN_DRV0 +
|
||||
(0x20 * i), enable ? 1 : 0, rsc->debug_mode);
|
||||
dss_reg_w(&rsc->drv_io,
|
||||
SDE_RSCC_LPM_PROFILING_COUNTER0_CLR_DRV0 +
|
||||
(0x20 * i), 1, rsc->debug_mode);
|
||||
}
|
||||
|
||||
wmb(); /* make sure counters are cleared now */
|
||||
pr_debug("rsc profiling counters %s and cleared\n",
|
||||
enable ? "enabled" : "disabled");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rsc_hw_get_profiling_counter_status(struct sde_rsc_priv *rsc,
|
||||
u32 *counters)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!rsc || !counters) {
|
||||
pr_debug("invalid input param, %d %d\n",
|
||||
rsc ? 0 : 1, counters ? 0 : 1);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
for (i = 0; i < NUM_RSC_PROFILING_COUNTERS; ++i)
|
||||
counters[i] = dss_reg_r(&rsc->drv_io,
|
||||
SDE_RSCC_LPM_PROFILING_COUNTER0_STATUS_DRV0 +
|
||||
(0x20 * i), rsc->debug_mode);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rsc_hw_timer_update_v3(struct sde_rsc_priv *rsc)
|
||||
{
|
||||
if (!rsc) {
|
||||
@ -620,6 +664,10 @@ int sde_rsc_hw_register_v3(struct sde_rsc_priv *rsc)
|
||||
rsc->hw_ops.debug_show = sde_rsc_debug_show;
|
||||
rsc->hw_ops.mode_ctrl = rsc_hw_mode_ctrl;
|
||||
rsc->hw_ops.debug_dump = rsc_hw_debug_dump;
|
||||
if (rsc->profiling_supp) {
|
||||
rsc->hw_ops.setup_counters = rsc_hw_profiling_counter_ctrl;
|
||||
rsc->hw_ops.get_counters = rsc_hw_get_profiling_counter_status;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Copyright (c) 2016-2019, The Linux Foundation. All rights reserved.
|
||||
* Copyright (c) 2016-2020, The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef _SDE_RSC_PRIV_H_
|
||||
@ -27,6 +27,7 @@
|
||||
#define SDE_RSC_REV_1 0x1
|
||||
#define SDE_RSC_REV_2 0x2
|
||||
#define SDE_RSC_REV_3 0x3
|
||||
#define SDE_RSC_REV_4 0x4
|
||||
|
||||
#define SDE_RSC_HW_MAJOR_MINOR_STEP(major, minor, step) \
|
||||
(((major & 0xff) << 16) |\
|
||||
@ -78,8 +79,10 @@ enum rsc_vsync_req {
|
||||
* @debug_dump: dump debug bus registers or enable debug bus
|
||||
* @state_update: Enable/override the solver based on rsc state
|
||||
* status (command/video)
|
||||
* @mode_show: shows current mode status, mode0/1/2
|
||||
* @debug_show: Show current debug status.
|
||||
* @mode_ctrl: shows current mode status, mode0/1/2
|
||||
* @setup_counters: Enable/disable RSC profiling counters
|
||||
* @get_counters: Get current status of profiling counters
|
||||
*/
|
||||
|
||||
struct sde_rsc_hw_ops {
|
||||
@ -96,6 +99,8 @@ struct sde_rsc_hw_ops {
|
||||
int (*debug_show)(struct seq_file *s, struct sde_rsc_priv *rsc);
|
||||
int (*mode_ctrl)(struct sde_rsc_priv *rsc, enum rsc_mode_req request,
|
||||
char *buffer, int buffer_size, u32 mode);
|
||||
int (*setup_counters)(struct sde_rsc_priv *rsc, bool enable);
|
||||
int (*get_counters)(struct sde_rsc_priv *rsc, u32 *counters);
|
||||
};
|
||||
|
||||
/**
|
||||
@ -186,6 +191,8 @@ struct sde_rsc_bw_config {
|
||||
* bw_config: check sde_rsc_bw_config structure description.
|
||||
* dev: rsc device node
|
||||
* resource_refcount: Track rsc resource refcount
|
||||
* profiling_supp: Indicates if HW has support for profiling counters
|
||||
* profiling_en: Flag for rsc lpm profiling counters, true=enabled
|
||||
*/
|
||||
struct sde_rsc_priv {
|
||||
u32 version;
|
||||
@ -227,6 +234,8 @@ struct sde_rsc_priv {
|
||||
struct sde_rsc_bw_config bw_config;
|
||||
struct device *dev;
|
||||
atomic_t resource_refcount;
|
||||
bool profiling_supp;
|
||||
bool profiling_en;
|
||||
};
|
||||
|
||||
/**
|
||||
|
Loading…
Reference in New Issue
Block a user