drm/msm/sde: add dynamic clock and bandwidth commit support

Current crtc supports static clock and bandwidth defined in
device tree.  Add support for dynamic clock and bandwidth
control using new atomic crtc properties. User mode
can adjust core clock rate, instantaneous and arbitrated
bandwidth per crtc commit.

Change-Id: I753a141bbc023d89f1532943f48252875883d684
Signed-off-by: Alan Kwong <akwong@codeaurora.org>
This commit is contained in:
Alan Kwong 2016-11-06 21:17:12 -05:00
parent 0d9edca812
commit 306d4ef12f
14 changed files with 747 additions and 0 deletions

View file

@ -69,6 +69,20 @@ Required properties:
Optional properties:
- qcom,dsi-phy-regulator-ldo-mode: Boolean value indicating if the LDO mode PHY
regulator is wanted.
- qcom,mdss-mdp-transfer-time-us: Specifies the dsi transfer time for command mode
panels in microseconds. Driver uses this number to adjust
the clock rate according to the expected transfer time.
Increasing this value would slow down the mdp processing
and can result in slower performance.
Decreasing this value can speed up the mdp processing,
but this can also impact power consumption.
As a rule this time should not be higher than the time
that would be expected with the processing at the
dsi link rate since anyways this would be the maximum
transfer time that could be achieved.
If ping pong split is enabled, this time should not be higher
than two times the dsi link rate time.
If the property is not specified, then the default value is 14000 us.
Example:
mdss_dsi0: qcom,mdss_dsi@fd922800 {
@ -105,6 +119,8 @@ Example:
qcom,master-dsi;
qcom,sync-dual-dsi;
qcom,mdss-mdp-transfer-time-us = <12000>;
pinctrl-names = "default", "sleep";
pinctrl-0 = <&mdss_dsi_active>;
pinctrl-1 = <&mdss_dsi_suspend>;

View file

@ -230,6 +230,18 @@ Optional properties:
for particular chipset.
These paths must be defined after rt-paths in
"qcom,msm-bus,vectors-KBps" vector request.
- qcom,sde-max-bw-low-kbps: This value indicates the max bandwidth in Kbps
that can be supported without underflow.
This is a low bandwidth threshold which should
be applied in most scenarios to be safe from
underflows when unable to satisfy bandwidth
requirements.
- qcom,sde-max-bw-high-kbps: This value indicates the max bandwidth in Kbps
that can be supported without underflow.
This is a high bandwidth threshold which can be
applied in scenarios where panel interface can
be more tolerant to memory latency such as
command mode panels.
Bus Scaling Subnodes:
- qcom,sde-reg-bus: Property to provide Bus scaling for register access for
@ -380,6 +392,9 @@ Example:
qcom,sde-dram-channels = <2>;
qcom,sde-num-nrt-paths = <1>;
qcom,sde-max-bw-high-kbps = <9000000>;
qcom,sde-max-bw-low-kbps = <9000000>;
qcom,sde-sspp-vig-blocks {
qcom,sde-vig-csc-off = <0x320>;
qcom,sde-vig-qseed-off = <0x200>;

View file

@ -80,3 +80,17 @@ Optional properties:
-- qcom,supply-post-on-sleep: time to sleep (ms) after turning on
-- qcom,supply-pre-off-sleep: time to sleep (ms) before turning off
-- qcom,supply-post-off-sleep: time to sleep (ms) after turning off
- qcom,mdss-mdp-transfer-time-us: Specifies the dsi transfer time for command mode
panels in microseconds. Driver uses this number to adjust
the clock rate according to the expected transfer time.
Increasing this value would slow down the mdp processing
and can result in slower performance.
Decreasing this value can speed up the mdp processing,
but this can also impact power consumption.
As a rule this time should not be higher than the time
that would be expected with the processing at the
dsi link rate since anyways this would be the maximum
transfer time that could be achieved.
If ping pong split enabled, this time should not be higher
than two times the dsi link rate time.
If the property is not specified, then the default value is 14000 us.

View file

@ -318,12 +318,15 @@ struct dsi_video_engine_cfg {
* @wr_mem_continue: DCS command for write_memory_continue.
* @insert_dcs_command: Insert DCS command as first byte of payload
* of the pixel data.
* @mdp_transfer_time_us Specifies the mdp transfer time for command mode
* panels in microseconds
*/
struct dsi_cmd_engine_cfg {
u32 max_cmd_packets_interleave;
u32 wr_mem_start;
u32 wr_mem_continue;
bool insert_dcs_command;
u32 mdp_transfer_time_us;
};
/**

View file

@ -313,6 +313,8 @@ int dsi_conn_post_init(struct drm_connector *connector,
break;
case DSI_OP_CMD_MODE:
sde_kms_info_add_keystr(info, "panel mode", "command");
sde_kms_info_add_keyint(info, "mdp_transfer_time_us",
panel->cmd_config.mdp_transfer_time_us);
break;
default:
pr_debug("invalid panel type:%d\n", panel->mode.panel_mode);

View file

@ -22,6 +22,8 @@
#define DSI_PANEL_DEFAULT_LABEL "Default dsi panel"
#define DEFAULT_MDP_TRANSFER_TIME 14000
static int dsi_panel_vreg_get(struct dsi_panel *panel)
{
int rc = 0;
@ -954,6 +956,14 @@ static int dsi_panel_parse_cmd_host_config(struct dsi_cmd_engine_cfg *cfg,
goto error;
}
if (of_property_read_u32(of_node, "qcom,mdss-mdp-transfer-time-us",
&val)) {
pr_debug("[%s] Fallback to default transfer-time-us\n", name);
cfg->mdp_transfer_time_us = DEFAULT_MDP_TRANSFER_TIME;
} else {
cfg->mdp_transfer_time_us = val;
}
error:
return rc;
}

View file

@ -125,6 +125,9 @@ enum msm_mdp_crtc_property {
CRTC_PROP_INPUT_FENCE_TIMEOUT = CRTC_PROP_BLOBCOUNT,
CRTC_PROP_OUTPUT_FENCE,
CRTC_PROP_OUTPUT_FENCE_OFFSET,
CRTC_PROP_CORE_CLK,
CRTC_PROP_CORE_AB,
CRTC_PROP_CORE_IB,
/* total # of properties */
CRTC_PROP_COUNT

View file

@ -33,8 +33,469 @@
#include "sde_core_perf.h"
#include "sde_trace.h"
static struct sde_kms *_sde_crtc_get_kms(struct drm_crtc *crtc)
{
struct msm_drm_private *priv;
if (!crtc->dev || !crtc->dev->dev_private) {
SDE_ERROR("invalid device\n");
return NULL;
}
priv = crtc->dev->dev_private;
if (!priv || !priv->kms) {
SDE_ERROR("invalid kms\n");
return NULL;
}
return to_sde_kms(priv->kms);
}
static bool _sde_core_perf_crtc_is_power_on(struct drm_crtc *crtc)
{
return sde_crtc_is_enabled(crtc);
}
static bool _sde_core_video_mode_intf_connected(struct drm_crtc *crtc)
{
struct drm_crtc *tmp_crtc;
if (!crtc)
return 0;
drm_for_each_crtc(tmp_crtc, crtc->dev) {
if ((sde_crtc_get_intf_mode(tmp_crtc) == INTF_MODE_VIDEO) &&
_sde_core_perf_crtc_is_power_on(tmp_crtc)) {
SDE_DEBUG("video interface connected crtc:%d\n",
tmp_crtc->base.id);
return true;
}
}
return false;
}
int sde_core_perf_crtc_check(struct drm_crtc *crtc,
struct drm_crtc_state *state)
{
u32 bw, threshold;
u64 bw_sum_of_intfs = 0;
bool is_video_mode;
struct sde_crtc_state *sde_cstate;
struct drm_crtc *tmp_crtc;
struct sde_kms *kms;
if (!crtc || !state) {
SDE_ERROR("invalid crtc\n");
return -EINVAL;
}
kms = _sde_crtc_get_kms(crtc);
if (!kms || !kms->catalog) {
SDE_ERROR("invalid parameters\n");
return 0;
}
/* we only need bandwidth check on real-time clients (interfaces) */
if (sde_crtc_is_wb(crtc))
return 0;
sde_cstate = to_sde_crtc_state(state);
bw_sum_of_intfs = sde_crtc_get_property(sde_cstate, CRTC_PROP_CORE_AB);
drm_for_each_crtc(tmp_crtc, crtc->dev) {
if (_sde_core_perf_crtc_is_power_on(tmp_crtc) &&
sde_crtc_is_rt(tmp_crtc) && tmp_crtc != crtc) {
struct sde_crtc_state *tmp_cstate =
to_sde_crtc_state(tmp_crtc->state);
bw_sum_of_intfs += tmp_cstate->cur_perf.bw_ctl;
}
}
/* convert bandwidth to kb */
bw = DIV_ROUND_UP_ULL(bw_sum_of_intfs, 1000);
SDE_DEBUG("calculated bandwidth=%uk\n", bw);
is_video_mode = sde_crtc_get_intf_mode(crtc) == INTF_MODE_VIDEO;
threshold = (is_video_mode ||
_sde_core_video_mode_intf_connected(crtc)) ?
kms->catalog->perf.max_bw_low : kms->catalog->perf.max_bw_high;
SDE_DEBUG("final threshold bw limit = %d\n", threshold);
if (!threshold) {
sde_cstate->cur_perf.bw_ctl = 0;
SDE_ERROR("no bandwidth limits specified\n");
return -E2BIG;
} else if (bw > threshold) {
sde_cstate->cur_perf.bw_ctl = 0;
SDE_DEBUG("exceeds bandwidth: %ukb > %ukb\n", bw, threshold);
return -E2BIG;
}
return 0;
}
static void _sde_core_perf_calc_crtc(struct sde_kms *kms,
struct drm_crtc *crtc,
struct sde_core_perf_params *perf)
{
struct sde_crtc_state *sde_cstate;
sde_cstate = to_sde_crtc_state(crtc->state);
memset(perf, 0, sizeof(struct sde_core_perf_params));
perf->bw_ctl = sde_crtc_get_property(sde_cstate, CRTC_PROP_CORE_AB);
perf->max_per_pipe_ib =
sde_crtc_get_property(sde_cstate, CRTC_PROP_CORE_IB);
perf->core_clk_rate =
sde_crtc_get_property(sde_cstate, CRTC_PROP_CORE_CLK);
SDE_DEBUG("crtc=%d clk_rate=%u ib=%llu ab=%llu\n",
crtc->base.id, perf->core_clk_rate,
perf->max_per_pipe_ib, perf->bw_ctl);
}
static u64 _sde_core_perf_crtc_calc_client_vote(struct sde_kms *kms,
struct drm_crtc *crtc, struct sde_core_perf_params *perf,
bool nrt_client, u32 core_clk)
{
u64 bw_sum_of_intfs = 0;
struct drm_crtc *tmp_crtc;
drm_for_each_crtc(tmp_crtc, crtc->dev) {
if (_sde_core_perf_crtc_is_power_on(crtc) &&
/* RealTime clients */
((!nrt_client) ||
/* Non-RealTime clients */
(nrt_client && sde_crtc_is_nrt(tmp_crtc)))) {
struct sde_crtc_state *sde_cstate =
to_sde_crtc_state(tmp_crtc->state);
perf->max_per_pipe_ib = max(perf->max_per_pipe_ib,
sde_cstate->cur_perf.max_per_pipe_ib);
bw_sum_of_intfs += sde_cstate->cur_perf.bw_ctl;
SDE_DEBUG("crtc=%d bw=%llu\n",
tmp_crtc->base.id,
sde_cstate->cur_perf.bw_ctl);
}
}
return bw_sum_of_intfs;
}
static void _sde_core_perf_crtc_update_client_vote(struct sde_kms *kms,
struct sde_core_perf_params *params, bool nrt_client, u64 bw_vote)
{
struct msm_drm_private *priv = kms->dev->dev_private;
u64 bus_ab_quota, bus_ib_quota;
bus_ab_quota = max(bw_vote, kms->perf.perf_tune.min_bus_vote);
bus_ib_quota = params->max_per_pipe_ib;
SDE_ATRACE_INT("bus_quota", bus_ib_quota);
sde_power_data_bus_set_quota(&priv->phandle, kms->core_client,
nrt_client ? SDE_POWER_HANDLE_DATA_BUS_CLIENT_NRT :
SDE_POWER_HANDLE_DATA_BUS_CLIENT_RT,
bus_ab_quota, bus_ib_quota);
SDE_DEBUG("client:%s ab=%llu ib=%llu\n", nrt_client ? "nrt" : "rt",
bus_ab_quota, bus_ib_quota);
}
static void _sde_core_perf_crtc_update_bus(struct sde_kms *kms,
struct drm_crtc *crtc, u32 core_clk)
{
u64 bw_sum_of_rt_intfs = 0, bw_sum_of_nrt_intfs = 0;
struct sde_core_perf_params params = {0};
SDE_ATRACE_BEGIN(__func__);
/*
* non-real time client
*/
if (sde_crtc_is_nrt(crtc)) {
bw_sum_of_nrt_intfs = _sde_core_perf_crtc_calc_client_vote(
kms, crtc, &params, true, core_clk);
_sde_core_perf_crtc_update_client_vote(kms, &params, true,
bw_sum_of_nrt_intfs);
}
/*
* real time client
*/
if (!sde_crtc_is_nrt(crtc) ||
sde_crtc_is_wb(crtc)) {
bw_sum_of_rt_intfs = _sde_core_perf_crtc_calc_client_vote(kms,
crtc, &params, false, core_clk);
_sde_core_perf_crtc_update_client_vote(kms, &params, false,
bw_sum_of_rt_intfs);
}
SDE_ATRACE_END(__func__);
}
/**
* @sde_core_perf_crtc_release_bw() - request zero bandwidth
* @crtc - pointer to a crtc
*
* Function checks a state variable for the crtc, if all pending commit
* requests are done, meaning no more bandwidth is needed, release
* bandwidth request.
*/
void sde_core_perf_crtc_release_bw(struct drm_crtc *crtc)
{
struct drm_crtc *tmp_crtc;
struct sde_crtc_state *sde_cstate;
struct sde_kms *kms;
if (!crtc) {
SDE_ERROR("invalid crtc\n");
return;
}
kms = _sde_crtc_get_kms(crtc);
if (!kms || !kms->catalog) {
SDE_ERROR("invalid kms\n");
return;
}
sde_cstate = to_sde_crtc_state(crtc->state);
/* only do this for command panel or writeback */
if ((sde_crtc_get_intf_mode(crtc) != INTF_MODE_CMD) &&
(sde_crtc_get_intf_mode(crtc) != INTF_MODE_WB_LINE))
return;
/*
* If video interface present, cmd panel bandwidth cannot be
* released.
*/
if (sde_crtc_get_intf_mode(crtc) == INTF_MODE_CMD)
drm_for_each_crtc(tmp_crtc, crtc->dev) {
if (_sde_core_perf_crtc_is_power_on(tmp_crtc) &&
sde_crtc_get_intf_mode(tmp_crtc) ==
INTF_MODE_VIDEO)
return;
}
/* Release the bandwidth */
if (kms->perf.enable_bw_release) {
trace_sde_cmd_release_bw(crtc->base.id);
sde_cstate->cur_perf.bw_ctl = 0;
sde_cstate->new_perf.bw_ctl = 0;
SDE_DEBUG("Release BW crtc=%d\n", crtc->base.id);
_sde_core_perf_crtc_update_bus(kms, crtc, 0);
}
}
static int _sde_core_select_clk_lvl(struct sde_kms *kms,
u32 clk_rate)
{
return clk_round_rate(kms->perf.core_clk, clk_rate);
}
static u32 _sde_core_perf_get_core_clk_rate(struct sde_kms *kms)
{
u32 clk_rate = 0;
struct drm_crtc *crtc;
struct sde_crtc_state *sde_cstate;
int ncrtc = 0;
drm_for_each_crtc(crtc, kms->dev) {
if (_sde_core_perf_crtc_is_power_on(crtc)) {
sde_cstate = to_sde_crtc_state(crtc->state);
clk_rate = max(sde_cstate->cur_perf.core_clk_rate,
clk_rate);
clk_rate = clk_round_rate(kms->perf.core_clk, clk_rate);
}
ncrtc++;
}
clk_rate = _sde_core_select_clk_lvl(kms, clk_rate);
SDE_DEBUG("clk:%u ncrtc:%d\n", clk_rate, ncrtc);
return clk_rate;
}
void sde_core_perf_crtc_update(struct drm_crtc *crtc,
int params_changed, bool stop_req)
{
struct sde_core_perf_params *new, *old;
int update_bus = 0, update_clk = 0;
u32 clk_rate = 0;
struct sde_crtc *sde_crtc;
struct sde_crtc_state *sde_cstate;
int ret;
struct msm_drm_private *priv;
struct sde_kms *kms;
if (!crtc) {
SDE_ERROR("invalid crtc\n");
return;
}
kms = _sde_crtc_get_kms(crtc);
if (!kms || !kms->catalog) {
SDE_ERROR("invalid kms\n");
return;
}
priv = kms->dev->dev_private;
sde_crtc = to_sde_crtc(crtc);
sde_cstate = to_sde_crtc_state(crtc->state);
SDE_DEBUG("crtc:%d stop_req:%d core_clk:%u\n",
crtc->base.id, stop_req, kms->perf.core_clk_rate);
SDE_ATRACE_BEGIN(__func__);
old = &sde_cstate->cur_perf;
new = &sde_cstate->new_perf;
if (_sde_core_perf_crtc_is_power_on(crtc) && !stop_req) {
if (params_changed)
_sde_core_perf_calc_crtc(kms, crtc, new);
/*
* cases for bus bandwidth update.
* 1. new bandwidth vote or writeback output vote
* are higher than current vote for update request.
* 2. new bandwidth vote or writeback output vote are
* lower than current vote at end of commit or stop.
*/
if ((params_changed && ((new->bw_ctl > old->bw_ctl))) ||
(!params_changed && ((new->bw_ctl < old->bw_ctl)))) {
SDE_DEBUG("crtc=%d p=%d new_bw=%llu,old_bw=%llu\n",
crtc->base.id, params_changed, new->bw_ctl,
old->bw_ctl);
old->bw_ctl = new->bw_ctl;
old->max_per_pipe_ib = new->max_per_pipe_ib;
update_bus = 1;
}
if ((params_changed &&
(new->core_clk_rate > old->core_clk_rate)) ||
(!params_changed &&
(new->core_clk_rate < old->core_clk_rate))) {
old->core_clk_rate = new->core_clk_rate;
update_clk = 1;
}
} else {
SDE_DEBUG("crtc=%d disable\n", crtc->base.id);
memset(old, 0, sizeof(*old));
memset(new, 0, sizeof(*new));
update_bus = 1;
update_clk = 1;
}
/*
* Calculate mdp clock before bandwidth calculation. If traffic shaper
* is enabled and clock increased, the bandwidth calculation can
* use the new clock for the rotator bw calculation.
*/
if (update_clk)
clk_rate = _sde_core_perf_get_core_clk_rate(kms);
if (update_bus)
_sde_core_perf_crtc_update_bus(kms, crtc, clk_rate);
/*
* Update the clock after bandwidth vote to ensure
* bandwidth is available before clock rate is increased.
*/
if (update_clk) {
SDE_ATRACE_INT(kms->perf.clk_name, clk_rate);
SDE_EVT32(kms->dev, stop_req, clk_rate);
ret = sde_power_clk_set_rate(&priv->phandle,
kms->perf.clk_name, clk_rate);
if (ret) {
SDE_ERROR("failed to set %s clock rate %u\n",
kms->perf.clk_name, clk_rate);
goto end;
}
kms->perf.core_clk_rate = clk_rate;
SDE_DEBUG("update clk rate = %d HZ\n", clk_rate);
}
end:
SDE_ATRACE_END(__func__);
}
#ifdef CONFIG_DEBUG_FS
static ssize_t _sde_core_perf_mode_write(struct file *file,
const char __user *user_buf, size_t count, loff_t *ppos)
{
struct sde_core_perf *perf = file->private_data;
struct sde_perf_cfg *cfg = &perf->catalog->perf;
int perf_mode = 0;
char buf[10];
if (!perf)
return -ENODEV;
if (count >= sizeof(buf))
return -EFAULT;
if (copy_from_user(buf, user_buf, count))
return -EFAULT;
buf[count] = 0; /* end of string */
if (kstrtoint(buf, 0, &perf_mode))
return -EFAULT;
if (perf_mode) {
/* run the driver with max clk and BW vote */
perf->perf_tune.min_core_clk = perf->max_core_clk_rate;
perf->perf_tune.min_bus_vote =
(u64) cfg->max_bw_high * 1000;
} else {
/* reset the perf tune params to 0 */
perf->perf_tune.min_core_clk = 0;
perf->perf_tune.min_bus_vote = 0;
}
return count;
}
static ssize_t _sde_core_perf_mode_read(struct file *file,
char __user *buff, size_t count, loff_t *ppos)
{
struct sde_core_perf *perf = file->private_data;
int len = 0;
char buf[40] = {'\0'};
if (!perf)
return -ENODEV;
if (*ppos)
return 0; /* the end */
len = snprintf(buf, sizeof(buf), "min_mdp_clk %lu min_bus_vote %llu\n",
perf->perf_tune.min_core_clk,
perf->perf_tune.min_bus_vote);
if (len < 0 || len >= sizeof(buf))
return 0;
if ((count < sizeof(buf)) || copy_to_user(buff, buf, len))
return -EFAULT;
*ppos += len; /* increase offset */
return len;
}
static const struct file_operations sde_core_perf_mode_fops = {
.open = simple_open,
.read = _sde_core_perf_mode_read,
.write = _sde_core_perf_mode_write,
};
static void sde_debugfs_core_perf_destroy(struct sde_core_perf *perf)
{
debugfs_remove_recursive(perf->debugfs_root);
@ -44,6 +505,7 @@ static void sde_debugfs_core_perf_destroy(struct sde_core_perf *perf)
static int sde_debugfs_core_perf_init(struct sde_core_perf *perf,
struct dentry *parent)
{
struct sde_mdss_cfg *catalog = perf->catalog;
struct msm_drm_private *priv;
struct sde_kms *sde_kms;
@ -65,6 +527,14 @@ static int sde_debugfs_core_perf_init(struct sde_core_perf *perf,
&perf->max_core_clk_rate);
debugfs_create_u32("core_clk_rate", 0644, perf->debugfs_root,
&perf->core_clk_rate);
debugfs_create_u32("enable_bw_release", 0644, perf->debugfs_root,
(u32 *)&perf->enable_bw_release);
debugfs_create_u32("threshold_low", 0644, perf->debugfs_root,
(u32 *)&catalog->perf.max_bw_low);
debugfs_create_u32("threshold_high", 0644, perf->debugfs_root,
(u32 *)&catalog->perf.max_bw_high);
debugfs_create_file("perf_mode", 0644, perf->debugfs_root,
(u32 *)perf, &sde_core_perf_mode_fops);
return 0;
}

View file

@ -16,10 +16,33 @@
#include <linux/types.h>
#include <linux/dcache.h>
#include <linux/mutex.h>
#include <drm/drm_crtc.h>
#include "sde_hw_catalog.h"
#include "sde_power_handle.h"
/**
* struct sde_core_perf_params - definition of performance parameters
* @max_per_pipe_ib: maximum instantaneous bandwidth request
* @bw_ctl: arbitrated bandwidth request
* @core_clk_rate: core clock rate request
*/
struct sde_core_perf_params {
u64 max_per_pipe_ib;
u64 bw_ctl;
u32 core_clk_rate;
};
/**
* struct sde_core_perf_tune - definition of performance tuning control
* @min_core_clk: minimum core clock
* @min_bus_vote: minimum bus vote
*/
struct sde_core_perf_tune {
unsigned long min_core_clk;
u64 min_bus_vote;
};
/**
* struct sde_core_perf - definition of core performance context
* @dev: Pointer to drm device
@ -32,6 +55,8 @@
* @core_clk: Pointer to core clock structure
* @core_clk_rate: current core clock rate
* @max_core_clk_rate: maximum allowable core clock rate
* @perf_tune: debug control for performance tuning
* @enable_bw_release: debug control for bandwidth release
*/
struct sde_core_perf {
struct drm_device *dev;
@ -44,8 +69,34 @@ struct sde_core_perf {
struct clk *core_clk;
u32 core_clk_rate;
u64 max_core_clk_rate;
struct sde_core_perf_tune perf_tune;
u32 enable_bw_release;
};
/**
* sde_core_perf_crtc_check - validate performance of the given crtc state
* @crtc: Pointer to crtc
* @state: Pointer to new crtc state
* return: zero if success, or error code otherwise
*/
int sde_core_perf_crtc_check(struct drm_crtc *crtc,
struct drm_crtc_state *state);
/**
* sde_core_perf_crtc_update - update performance of the given crtc
* @crtc: Pointer to crtc
* @params_changed: true if crtc parameters are modified
* @stop_req: true if this is a stop request
*/
void sde_core_perf_crtc_update(struct drm_crtc *crtc,
int params_changed, bool stop_req);
/**
* sde_core_perf_crtc_release_bw - release bandwidth of the given crtc
* @crtc: Pointer to crtc
*/
void sde_core_perf_crtc_release_bw(struct drm_crtc *crtc);
/**
* sde_core_perf_destroy - destroy the given core performance context
* @perf: Pointer to core performance context

View file

@ -29,6 +29,7 @@
#include "sde_encoder.h"
#include "sde_connector.h"
#include "sde_power_handle.h"
#include "sde_core_perf.h"
/* default input fence timeout, in ms */
#define SDE_CRTC_INPUT_FENCE_TIMEOUT 2000
@ -445,6 +446,7 @@ static void sde_crtc_frame_event_work(struct kthread_work *work)
SDE_EVT32(DRMID(crtc), fevent->event, 1);
sde_power_data_bus_bandwidth_ctrl(&priv->phandle,
sde_kms->core_client, false);
sde_core_perf_crtc_release_bw(crtc);
} else {
SDE_EVT32(DRMID(crtc), fevent->event, 2);
}
@ -765,6 +767,9 @@ static void sde_crtc_atomic_flush(struct drm_crtc *crtc,
/* wait for acquire fences before anything else is done */
_sde_crtc_wait_for_fences(crtc);
/* update performance setting before crtc kickoff */
sde_core_perf_crtc_update(crtc, 1, false);
/*
* Final plane updates: Give each plane a chance to complete all
* required writes/flushing before crtc's "flush
@ -970,9 +975,12 @@ static void sde_crtc_disable(struct drm_crtc *crtc)
SDE_EVT32(DRMID(crtc));
sde_power_data_bus_bandwidth_ctrl(&priv->phandle,
sde_kms->core_client, false);
sde_core_perf_crtc_release_bw(crtc);
atomic_set(&sde_crtc->frame_pending, 0);
}
sde_core_perf_crtc_update(crtc, 0, true);
drm_for_each_encoder(encoder, crtc->dev) {
if (encoder->crtc != crtc)
continue;
@ -1160,6 +1168,13 @@ static int sde_crtc_atomic_check(struct drm_crtc *crtc,
SDE_DEBUG("%s: zpos %d", sde_crtc->name, z_pos);
}
rc = sde_core_perf_crtc_check(crtc, state);
if (rc) {
SDE_ERROR("crtc%d failed performance check %d\n",
crtc->base.id, rc);
goto end;
}
end:
return rc;
}
@ -1220,6 +1235,7 @@ static void sde_crtc_install_properties(struct drm_crtc *crtc,
struct sde_crtc *sde_crtc;
struct drm_device *dev;
struct sde_kms_info *info;
struct sde_kms *sde_kms;
SDE_DEBUG("\n");
@ -1230,6 +1246,7 @@ static void sde_crtc_install_properties(struct drm_crtc *crtc,
sde_crtc = to_sde_crtc(crtc);
dev = crtc->dev;
sde_kms = _sde_crtc_get_kms(crtc);
info = kzalloc(sizeof(struct sde_kms_info), GFP_KERNEL);
if (!info) {
@ -1249,6 +1266,19 @@ static void sde_crtc_install_properties(struct drm_crtc *crtc,
"output_fence_offset", 0x0, 0, 1, 0,
CRTC_PROP_OUTPUT_FENCE_OFFSET);
msm_property_install_range(&sde_crtc->property_info,
"core_clk", 0x0, 0, U64_MAX,
sde_kms->perf.max_core_clk_rate,
CRTC_PROP_CORE_CLK);
msm_property_install_range(&sde_crtc->property_info,
"core_ab", 0x0, 0, U64_MAX,
SDE_POWER_HANDLE_DATA_BUS_AB_QUOTA,
CRTC_PROP_CORE_AB);
msm_property_install_range(&sde_crtc->property_info,
"core_ib", 0x0, 0, U64_MAX,
SDE_POWER_HANDLE_DATA_BUS_IB_QUOTA,
CRTC_PROP_CORE_IB);
msm_property_install_blob(&sde_crtc->property_info, "capabilities",
DRM_MODE_PROP_IMMUTABLE, CRTC_PROP_INFO);
sde_kms_info_reset(info);
@ -1545,6 +1575,10 @@ static int sde_crtc_debugfs_state_show(struct seq_file *s, void *v)
seq_printf(s, "num_connectors: %d\n", cstate->num_connectors);
seq_printf(s, "is_rt: %d\n", cstate->is_rt);
seq_printf(s, "intf_mode: %d\n", cstate->intf_mode);
seq_printf(s, "bw_ctl: %llu\n", cstate->cur_perf.bw_ctl);
seq_printf(s, "core_clk_rate: %u\n", cstate->cur_perf.core_clk_rate);
seq_printf(s, "max_per_pipe_ib: %llu\n",
cstate->cur_perf.max_per_pipe_ib);
return 0;
}
@ -1568,6 +1602,10 @@ static void _sde_crtc_init_debugfs(struct sde_crtc *sde_crtc,
debugfs_create_file("status", S_IRUGO,
sde_crtc->debugfs_root,
sde_crtc, &debugfs_status_fops);
debugfs_create_file("state", S_IRUGO | S_IWUSR,
sde_crtc->debugfs_root,
&sde_crtc->base,
&sde_crtc_debugfs_state_fops);
}
}
}

View file

@ -17,6 +17,7 @@
#include "msm_prop.h"
#include "sde_fence.h"
#include "sde_kms.h"
#include "sde_core_perf.h"
#define SDE_CRTC_NAME_SIZE 12
@ -146,6 +147,9 @@ struct sde_crtc_state {
uint64_t property_values[CRTC_PROP_COUNT];
uint64_t input_fence_timeout_ns;
struct drm_property_blob *property_blobs[CRTC_PROP_COUNT];
struct sde_core_perf_params cur_perf;
struct sde_core_perf_params new_perf;
};
#define to_sde_crtc_state(x) \
@ -234,4 +238,46 @@ void sde_crtc_cancel_pending_flip(struct drm_crtc *crtc, struct drm_file *file);
*/
bool sde_crtc_is_rt(struct drm_crtc *crtc);
/**
* sde_crtc_get_intf_mode - get interface mode of the given crtc
* @crtc: Pointert to crtc
*/
static inline enum sde_intf_mode sde_crtc_get_intf_mode(struct drm_crtc *crtc)
{
struct sde_crtc_state *cstate =
crtc ? to_sde_crtc_state(crtc->state) : NULL;
return cstate ? cstate->intf_mode : INTF_MODE_NONE;
}
/**
* sde_core_perf_crtc_is_wb - check if writeback is primary output of this crtc
* @crtc: Pointer to crtc
*/
static inline bool sde_crtc_is_wb(struct drm_crtc *crtc)
{
struct sde_crtc_state *cstate =
crtc ? to_sde_crtc_state(crtc->state) : NULL;
return cstate ? (cstate->intf_mode == INTF_MODE_WB_LINE) : false;
}
/**
* sde_crtc_is_nrt - check if primary output of this crtc is non-realtime client
* @crtc: Pointer to crtc
*/
static inline bool sde_crtc_is_nrt(struct drm_crtc *crtc)
{
return sde_crtc_is_wb(crtc);
}
/**
* sde_crtc_is_enabled - check if sde crtc is enabled or not
* @crtc: Pointer to crtc
*/
static inline bool sde_crtc_is_enabled(struct drm_crtc *crtc)
{
return crtc ? crtc->enabled : false;
}
#endif /* _SDE_CRTC_H_ */

View file

@ -107,6 +107,12 @@ enum sde_prop {
SDE_PROP_MAX,
};
enum {
PERF_MAX_BW_LOW,
PERF_MAX_BW_HIGH,
PERF_PROP_MAX,
};
enum {
SSPP_OFF,
SSPP_SIZE,
@ -267,6 +273,11 @@ static struct sde_prop_type sde_prop[] = {
{SRC_SPLIT, "qcom,sde-has-src-split", false, PROP_TYPE_BOOL},
};
static struct sde_prop_type sde_perf_prop[] = {
{PERF_MAX_BW_LOW, "qcom,sde-max-bw-low-kbps", false, PROP_TYPE_U32},
{PERF_MAX_BW_HIGH, "qcom,sde-max-bw-high-kbps", false, PROP_TYPE_U32},
};
static struct sde_prop_type sspp_prop[] = {
{SSPP_OFF, "qcom,sde-sspp-off", true, PROP_TYPE_U32_ARRAY},
{SSPP_SIZE, "qcom,sde-sspp-src-size", false, PROP_TYPE_U32},
@ -1817,6 +1828,46 @@ end:
return rc;
}
static int sde_perf_parse_dt(struct device_node *np, struct sde_mdss_cfg *cfg)
{
int rc, len, prop_count[PERF_PROP_MAX];
struct sde_prop_value *prop_value = NULL;
bool prop_exists[PERF_PROP_MAX];
if (!cfg) {
SDE_ERROR("invalid argument\n");
rc = -EINVAL;
goto end;
}
prop_value = kzalloc(SDE_PROP_MAX *
sizeof(struct sde_prop_value), GFP_KERNEL);
if (!prop_value) {
rc = -ENOMEM;
goto end;
}
rc = _validate_dt_entry(np, sde_perf_prop, ARRAY_SIZE(sde_perf_prop),
prop_count, &len);
if (rc)
goto freeprop;
rc = _read_dt_entry(np, sde_perf_prop, ARRAY_SIZE(sde_perf_prop),
prop_count, prop_exists, prop_value);
if (rc)
goto freeprop;
cfg->perf.max_bw_low =
PROP_VALUE_ACCESS(prop_value, PERF_MAX_BW_LOW, 0);
cfg->perf.max_bw_high =
PROP_VALUE_ACCESS(prop_value, PERF_MAX_BW_HIGH, 0);
freeprop:
kfree(prop_value);
end:
return rc;
}
static void sde_hardware_caps(struct sde_mdss_cfg *sde_cfg, uint32_t hw_rev)
{
switch (hw_rev) {
@ -1918,6 +1969,10 @@ struct sde_mdss_cfg *sde_hw_catalog_init(struct drm_device *dev, u32 hw_rev)
if (rc)
goto end;
rc = sde_perf_parse_dt(np, sde_cfg);
if (rc)
goto end;
sde_hardware_caps(sde_cfg, hw_rev);
return sde_cfg;

View file

@ -597,6 +597,16 @@ struct sde_vbif_cfg {
struct sde_vbif_dynamic_ot_tbl dynamic_ot_wr_tbl;
};
/**
* struct sde_perf_cfg - performance control settings
* @max_bw_low low threshold of maximum bandwidth (kbps)
* @max_bw_high high threshold of maximum bandwidth (kbps)
*/
struct sde_perf_cfg {
u32 max_bw_low;
u32 max_bw_high;
};
/**
* struct sde_mdss_cfg - information of MDSS HW
* This is the main catalog data structure representing
@ -658,6 +668,8 @@ struct sde_mdss_cfg {
u32 vbif_count;
struct sde_vbif_cfg vbif[MAX_BLOCKS];
/* Add additional block data structures here */
struct sde_perf_cfg perf;
};
struct sde_mdss_hw_cfg_handler {

View file

@ -113,6 +113,18 @@ TRACE_EVENT(sde_perf_update_bus,
)
TRACE_EVENT(sde_cmd_release_bw,
TP_PROTO(u32 crtc_id),
TP_ARGS(crtc_id),
TP_STRUCT__entry(
__field(u32, crtc_id)
),
TP_fast_assign(
__entry->crtc_id = crtc_id;
),
TP_printk("crtc:%d", __entry->crtc_id)
);
TRACE_EVENT(sde_mark_write,
TP_PROTO(int pid, const char *name, bool trace_begin),
TP_ARGS(pid, name, trace_begin),