Merge "ARM: dts: msm: Add regs offsets for uidle feature for Kona"

This commit is contained in:
qctecmdr Service 2018-12-06 12:44:02 -08:00 committed by Gerrit - the friendly Code Review server
commit 433b82ebcd
23 changed files with 997 additions and 1 deletions

View file

@ -194,6 +194,9 @@ Optional properties:
vbif blocks. These offsets will be calculated from
register "vbif_phys" defined in reg property.
- qcom,sde-vbif-size: A u32 value indicates the vbif block address range.
- qcom,sde-uidle-off: A u32 value with the offset for the uidle
block, from the "mdp_phys".
- qcom,sde-uidle-size: A u32 value indicates the uidle block address range.
- qcom,sde-te-off: A u32 offset indicates the te block offset on pingpong.
This offset is 0x0 by default.
- qcom,sde-te2-off: A u32 offset indicates the te2 block offset on pingpong.
@ -683,6 +686,9 @@ Example:
qcom,sde-vbif-memtype-0 = <3 3 3 3 3 3 3 3>;
qcom,sde-vbif-memtype-1 = <3 3 3 3 3 3>;
qcom,sde-uidle-off = <0x80000>;
qcom,sde-uidle-size = <0x70>;
qcom,sde-dram-channels = <2>;
qcom,sde-num-nrt-paths = <1>;

View file

@ -152,6 +152,9 @@
/* offsets are based off dspp 0 and dspp 1 */
qcom,sde-dspp-ltm-off = <0x2a000 0x28100>;
qcom,sde-uidle-off = <0x80000>;
qcom,sde-uidle-size = <0x70>;
qcom,sde-vbif-off = <0>;
qcom,sde-vbif-size = <0x1040>;
qcom,sde-vbif-id = <0>;

View file

@ -46,6 +46,7 @@ msm_drm-$(CONFIG_DRM_MSM_SDE) += sde/sde_crtc.o \
sde/sde_hw_reg_dma_v1_color_proc.o \
sde/sde_hw_color_proc_v4.o \
sde/sde_hw_ad4.o \
sde/sde_hw_uidle.o \
sde_edid_parser.o \
sde_hdcp_1x.o \
sde_hdcp_2x.o

View file

@ -20,6 +20,7 @@
#include "sde_kms.h"
#include "sde_trace.h"
#include "sde_crtc.h"
#include "sde_encoder.h"
#include "sde_core_perf.h"
#define SDE_PERF_MODE_STRING_SIZE 128
@ -400,6 +401,173 @@ static void _sde_core_perf_crtc_update_llcc(struct sde_kms *kms,
total_llcc_active ? true : false);
}
static void _sde_core_uidle_setup_wd(struct sde_kms *kms,
bool enable)
{
struct sde_uidle_wd_cfg wd;
struct sde_hw_uidle *uidle;
uidle = kms->hw_uidle;
wd.enable = enable;
wd.clear = false;
wd.granularity = SDE_UIDLE_WD_GRANULARITY;
wd.heart_beat = SDE_UIDLE_WD_HEART_BEAT;
wd.load_value = SDE_UIDLE_WD_LOAD_VAL;
if (uidle->ops.setup_wd_timer)
uidle->ops.setup_wd_timer(uidle, &wd);
}
static void _sde_core_uidle_setup_cfg(struct sde_kms *kms,
bool enable)
{
struct sde_uidle_ctl_cfg cfg;
struct sde_hw_uidle *uidle;
uidle = kms->hw_uidle;
cfg.uidle_enable = enable;
cfg.fal10_danger =
kms->catalog->uidle_cfg.fal10_danger;
cfg.fal10_exit_cnt =
kms->catalog->uidle_cfg.fal10_exit_cnt;
cfg.fal10_exit_danger =
kms->catalog->uidle_cfg.fal10_exit_danger;
SDE_DEBUG("fal10_danger:%d fal10_exit_cnt:%d fal10_exit_danger:%d\n",
cfg.fal10_danger, cfg.fal10_exit_cnt, cfg.fal10_exit_danger);
SDE_EVT32(enable, cfg.fal10_danger, cfg.fal10_exit_cnt,
cfg.fal10_exit_danger);
if (uidle->ops.set_uidle_ctl)
uidle->ops.set_uidle_ctl(uidle, &cfg);
}
static void _sde_core_uidle_setup_ctl(struct drm_crtc *crtc,
bool enable)
{
struct drm_encoder *drm_enc;
/* Disable uidle in the CTL */
drm_for_each_encoder(drm_enc, crtc->dev) {
if (drm_enc->crtc != crtc)
continue;
sde_encoder_uidle_enable(drm_enc, enable);
}
}
static int _sde_core_perf_enable_uidle(struct sde_kms *kms,
struct drm_crtc *crtc, bool enable)
{
int rc = 0;
if (!kms->dev || !kms->dev->dev || !kms->hw_uidle) {
SDE_ERROR("wrong params won't enable uidlen");
rc = -EINVAL;
goto exit;
}
/* if no status change, just return */
if ((enable && kms->perf.uidle_enabled) ||
(!enable && !kms->perf.uidle_enabled)) {
SDE_DEBUG("no status change enable:%d uidle:%d\n",
enable, kms->perf.uidle_enabled);
goto exit;
}
SDE_EVT32(enable);
_sde_core_uidle_setup_wd(kms, enable);
_sde_core_uidle_setup_cfg(kms, enable);
_sde_core_uidle_setup_ctl(crtc, enable);
kms->perf.uidle_enabled = enable;
exit:
return rc;
}
static inline bool _sde_core_perf_is_wb(struct drm_crtc *crtc)
{
enum sde_intf_mode if_mode = INTF_MODE_NONE;
if_mode = sde_crtc_get_intf_mode(crtc);
if (if_mode == INTF_MODE_WB_BLOCK ||
if_mode == INTF_MODE_WB_LINE)
return true;
return false;
}
static bool _sde_core_perf_is_cwb(struct drm_crtc *crtc)
{
struct drm_encoder *encoder;
/* if any other encoder is connected to same crtc in clone mode */
drm_for_each_encoder(encoder, crtc->dev) {
if (encoder->crtc == crtc &&
sde_encoder_in_clone_mode(encoder)) {
return true;
}
}
return false;
}
void sde_core_perf_crtc_update_uidle(struct drm_crtc *crtc,
bool enable)
{
struct drm_crtc *tmp_crtc;
struct sde_kms *kms;
bool disable_uidle = false;
u32 fps;
if (!crtc) {
SDE_ERROR("invalid crtc\n");
return;
}
kms = _sde_crtc_get_kms(crtc);
if (!kms || !kms->catalog) {
SDE_ERROR("invalid kms\n");
return;
}
mutex_lock(&sde_core_perf_lock);
if (!kms->perf.catalog->uidle_cfg.uidle_rev ||
!kms->perf.catalog->uidle_cfg.debugfs_ctrl) {
SDE_DEBUG("uidle is not enabled %d %d\n",
kms->perf.catalog->uidle_cfg.uidle_rev,
kms->perf.catalog->uidle_cfg.debugfs_ctrl);
goto exit;
}
drm_for_each_crtc(tmp_crtc, crtc->dev) {
if (_sde_core_perf_crtc_is_power_on(tmp_crtc)) {
fps = sde_crtc_get_fps_mode(tmp_crtc);
SDE_DEBUG("crtc=%d fps:%d wb:%d cwb:%d dis:%d en:%d\n",
tmp_crtc->base.id, fps,
_sde_core_perf_is_wb(tmp_crtc),
_sde_core_perf_is_cwb(tmp_crtc),
disable_uidle, enable);
if (_sde_core_perf_is_wb(tmp_crtc) ||
_sde_core_perf_is_cwb(tmp_crtc) || (!fps ||
fps > kms->perf.catalog->uidle_cfg.max_fps)) {
disable_uidle = true;
break;
}
}
}
_sde_core_perf_enable_uidle(kms, crtc,
(enable && !disable_uidle) ? true : false);
exit:
mutex_unlock(&sde_core_perf_lock);
}
static void _sde_core_perf_crtc_update_bus(struct sde_kms *kms,
struct drm_crtc *crtc, u32 bus_id)
{

View file

@ -62,6 +62,7 @@ struct sde_core_perf_tune {
* @sde_rsc_available: is display rsc available
* @bw_vote_mode_updated: bandwidth vote mode update
* @llcc_active: status of the llcc, true if active.
* @uidle_enabled: indicates if uidle is already enabled
*/
struct sde_core_perf {
struct drm_device *dev;
@ -82,6 +83,7 @@ struct sde_core_perf {
bool sde_rsc_available;
bool bw_vote_mode_updated;
bool llcc_active;
bool uidle_enabled;
};
/**
@ -108,6 +110,13 @@ void sde_core_perf_crtc_update(struct drm_crtc *crtc,
*/
void sde_core_perf_crtc_release_bw(struct drm_crtc *crtc);
/**
* sde_core_perf_crtc_update_uidle - attempts to enable uidle of the given crtc
* @crtc: Pointer to crtc
* @enable: enable/disable uidle
*/
void sde_core_perf_crtc_update_uidle(struct drm_crtc *crtc, bool enable);
/**
* sde_core_perf_destroy - destroy the given core performance context
* @perf: Pointer to core performance context

View file

@ -2056,6 +2056,22 @@ enum sde_intf_mode sde_crtc_get_intf_mode(struct drm_crtc *crtc)
return INTF_MODE_NONE;
}
u32 sde_crtc_get_fps_mode(struct drm_crtc *crtc)
{
struct drm_encoder *encoder;
if (!crtc || !crtc->dev) {
SDE_ERROR("invalid crtc\n");
return INTF_MODE_NONE;
}
drm_for_each_encoder(encoder, crtc->dev)
if (encoder->crtc == crtc)
return sde_encoder_get_fps(encoder);
return 0;
}
static void sde_crtc_vblank_cb(void *data)
{
struct drm_crtc *crtc = (struct drm_crtc *)data;
@ -3751,6 +3767,9 @@ static void sde_crtc_disable(struct drm_crtc *crtc)
}
sde_crtc->enabled = false;
/* Try to disable uidle */
sde_core_perf_crtc_update_uidle(crtc, false);
if (atomic_read(&sde_crtc->frame_pending)) {
SDE_ERROR("crtc%d frame_pending%d\n", crtc->base.id,
atomic_read(&sde_crtc->frame_pending));
@ -3861,7 +3880,14 @@ static void sde_crtc_enable(struct drm_crtc *crtc,
SDE_EVT32(DRMID(crtc), sde_crtc->enabled, sde_crtc->suspend,
sde_crtc->vblank_requested);
/* return early if crtc is already enabled */
/*
* Try to enable uidle (if possible), we do this before the call
* to return early during seamless dms mode, so any fps
* change is also consider to enable/disable UIDLE
*/
sde_core_perf_crtc_update_uidle(crtc, true);
/* return early if crtc is already enabled, do this after UIDLE check */
if (sde_crtc->enabled) {
if (msm_is_mode_seamless_dms(&crtc->state->adjusted_mode))
SDE_DEBUG("%s extra crtc enable expected during DMS\n",

View file

@ -511,6 +511,12 @@ int sde_crtc_register_custom_event(struct sde_kms *kms,
*/
enum sde_intf_mode sde_crtc_get_intf_mode(struct drm_crtc *crtc);
/**
* sde_crtc_get_fps_mode - get frame rate of the given crtc
* @crtc: Pointert to crtc
*/
u32 sde_crtc_get_fps_mode(struct drm_crtc *crtc);
/**
* sde_crtc_get_client_type - check the crtc type- rt, nrt, rsc, etc.
* @crtc: Pointer to crtc

View file

@ -279,6 +279,22 @@ struct sde_encoder_virt {
#define to_sde_encoder_virt(x) container_of(x, struct sde_encoder_virt, base)
void sde_encoder_uidle_enable(struct drm_encoder *drm_enc, bool enable)
{
struct sde_encoder_virt *sde_enc;
int i;
sde_enc = to_sde_encoder_virt(drm_enc);
for (i = 0; i < sde_enc->num_phys_encs; i++) {
struct sde_encoder_phys *phys = sde_enc->phys_encs[i];
if (phys && phys->hw_ctl && phys->hw_ctl->ops.uidle_enable) {
SDE_EVT32(DRMID(drm_enc), enable);
phys->hw_ctl->ops.uidle_enable(phys->hw_ctl, enable);
}
}
}
static void _sde_encoder_pm_qos_add_request(struct drm_encoder *drm_enc)
{
struct msm_drm_private *priv;
@ -5186,6 +5202,26 @@ int sde_encoder_wait_for_event(struct drm_encoder *drm_enc,
return ret;
}
u32 sde_encoder_get_fps(struct drm_encoder *drm_enc)
{
struct msm_mode_info mode_info;
int rc;
if (!drm_enc) {
SDE_ERROR("invalid encoder\n");
return 0;
}
rc = _sde_encoder_get_mode_info(drm_enc, &mode_info);
if (rc) {
SDE_ERROR_ENC(to_sde_encoder_virt(drm_enc),
"failed to get mode info\n");
return 0;
}
return mode_info.frame_rate;
}
enum sde_intf_mode sde_encoder_get_intf_mode(struct drm_encoder *encoder)
{
struct sde_encoder_virt *sde_enc = NULL;

View file

@ -165,6 +165,12 @@ int sde_encoder_wait_for_event(struct drm_encoder *drm_encoder,
*/
int sde_encoder_idle_request(struct drm_encoder *drm_enc);
/*
* sde_encoder_get_fps - get interface frame rate of the given encoder
* @encoder: Pointer to drm encoder object
*/
u32 sde_encoder_get_fps(struct drm_encoder *encoder);
/*
* sde_encoder_get_intf_mode - get interface mode of the given encoder
* @encoder: Pointer to drm encoder object
@ -293,4 +299,11 @@ void sde_encoder_control_idle_pc(struct drm_encoder *enc, bool enable);
*/
int sde_encoder_in_cont_splash(struct drm_encoder *enc);
/**
* sde_encoder_uidle_enable - control enable/disable of uidle
* @drm_enc: Pointer to drm encoder structure
* @enable: enable/disable flag
*/
void sde_encoder_uidle_enable(struct drm_encoder *drm_enc, bool enable);
#endif /* __SDE_ENCODER_H__ */

View file

@ -14,6 +14,7 @@
#include "sde_hw_catalog.h"
#include "sde_hw_catalog_format.h"
#include "sde_kms.h"
#include "sde_hw_uidle.h"
/*************************************************************
* MACRO DEFINITION
@ -131,6 +132,17 @@
#define DEFAULT_CPU_MASK 0
#define DEFAULT_CPU_DMA_LATENCY PM_QOS_DEFAULT_VALUE
/* Uidle values */
#define SDE_UIDLE_FAL10_EXIT_CNT 128
#define SDE_UIDLE_FAL10_EXIT_DANGER 4
#define SDE_UIDLE_FAL10_DANGER 6
#define SDE_UIDLE_FAL10_TARGET_IDLE 50
#define SDE_UIDLE_FAL1_TARGET_IDLE 10
#define SDE_UIDLE_FAL10_THRESHOLD 12
#define SDE_UIDLE_MAX_DWNSCALE 1500
#define SDE_UIDLE_MAX_FPS 60
/*************************************************************
* DTSI PROPERTY INDEX
*************************************************************/
@ -374,6 +386,12 @@ enum {
VBIF_PROP_MAX,
};
enum {
UIDLE_OFF,
UIDLE_LEN,
UIDLE_PROP_MAX,
};
enum {
REG_DMA_OFF,
REG_DMA_VERSION,
@ -687,6 +705,11 @@ static struct sde_prop_type vbif_prop[] = {
{VBIF_MEMTYPE_1, "qcom,sde-vbif-memtype-1", false, PROP_TYPE_U32_ARRAY},
};
static struct sde_prop_type uidle_prop[] = {
{UIDLE_OFF, "qcom,sde-uidle-off", false, PROP_TYPE_U32},
{UIDLE_LEN, "qcom,sde-uidle-size", false, PROP_TYPE_U32},
};
static struct sde_prop_type reg_dma_prop[REG_DMA_PROP_MAX] = {
[REG_DMA_OFF] = {REG_DMA_OFF, "qcom,sde-reg-dma-off", false,
PROP_TYPE_U32},
@ -1434,6 +1457,9 @@ static int sde_sspp_parse_dt(struct device_node *np,
goto end;
}
if (sde_cfg->uidle_cfg.uidle_rev)
set_bit(SDE_PERF_SSPP_UIDLE, &sspp->perf_features);
snprintf(sblk->src_blk.name, SDE_HW_BLK_NAME_LEN, "sspp_src_%u",
sspp->id - SSPP_VIG0);
@ -1545,6 +1571,8 @@ static int sde_ctl_parse_dt(struct device_node *np,
set_bit(SDE_CTL_PINGPONG_SPLIT, &ctl->features);
if (IS_SDE_CTL_REV_100(sde_cfg->ctl_rev))
set_bit(SDE_CTL_ACTIVE_CFG, &ctl->features);
if (IS_SDE_UIDLE_REV_100(sde_cfg->uidle_cfg.uidle_rev))
set_bit(SDE_CTL_UIDLE, &ctl->features);
}
end:
kfree(prop_value);
@ -2513,6 +2541,71 @@ end:
return rc;
}
static int sde_uidle_parse_dt(struct device_node *np,
struct sde_mdss_cfg *sde_cfg)
{
int rc = 0, prop_count[UIDLE_PROP_MAX];
bool prop_exists[UIDLE_PROP_MAX];
struct sde_prop_value *prop_value = NULL;
u32 off_count;
if (!sde_cfg) {
SDE_ERROR("invalid argument\n");
rc = -EINVAL;
goto end;
}
if (!sde_cfg->uidle_cfg.uidle_rev)
goto end;
prop_value = kcalloc(UIDLE_PROP_MAX,
sizeof(struct sde_prop_value), GFP_KERNEL);
if (!prop_value) {
rc = -ENOMEM;
goto end;
}
rc = _validate_dt_entry(np, uidle_prop, ARRAY_SIZE(uidle_prop),
prop_count, &off_count);
if (rc)
goto end;
rc = _read_dt_entry(np, uidle_prop, ARRAY_SIZE(uidle_prop), prop_count,
prop_exists, prop_value);
if (rc)
goto end;
if (!prop_exists[UIDLE_LEN] || !prop_exists[UIDLE_OFF]) {
SDE_DEBUG("offset/len missing, will disable uidle:%d,%d\n",
prop_exists[UIDLE_LEN], prop_exists[UIDLE_OFF]);
rc = -EINVAL;
goto end;
}
sde_cfg->uidle_cfg.id = UIDLE;
sde_cfg->uidle_cfg.base =
PROP_VALUE_ACCESS(prop_value, UIDLE_OFF, 0);
sde_cfg->uidle_cfg.len =
PROP_VALUE_ACCESS(prop_value, UIDLE_LEN, 0);
/* validate */
if (!sde_cfg->uidle_cfg.base || !sde_cfg->uidle_cfg.len) {
SDE_ERROR("invalid reg/len [%d, %d], will disable uidle\n",
sde_cfg->uidle_cfg.base, sde_cfg->uidle_cfg.len);
rc = -EINVAL;
}
end:
if (rc && sde_cfg->uidle_cfg.uidle_rev) {
SDE_DEBUG("wrong dt entries, will disable uidle\n");
sde_cfg->uidle_cfg.uidle_rev = 0;
}
kfree(prop_value);
/* optional feature, so always return success */
return 0;
}
static int sde_vbif_parse_dt(struct device_node *np,
struct sde_mdss_cfg *sde_cfg)
{
@ -3489,6 +3582,28 @@ end:
return rc;
}
static void _sde_hw_setup_uidle(struct sde_uidle_cfg *uidle_cfg)
{
if (!uidle_cfg->uidle_rev)
return;
if (IS_SDE_UIDLE_REV_100(uidle_cfg->uidle_rev)) {
uidle_cfg->fal10_exit_cnt = SDE_UIDLE_FAL10_EXIT_CNT;
uidle_cfg->fal10_exit_danger = SDE_UIDLE_FAL10_EXIT_DANGER;
uidle_cfg->fal10_danger = SDE_UIDLE_FAL10_DANGER;
uidle_cfg->fal10_target_idle_time = SDE_UIDLE_FAL10_TARGET_IDLE;
uidle_cfg->fal1_target_idle_time = SDE_UIDLE_FAL1_TARGET_IDLE;
uidle_cfg->fal10_threshold = SDE_UIDLE_FAL10_THRESHOLD;
uidle_cfg->max_dwnscale = SDE_UIDLE_MAX_DWNSCALE;
uidle_cfg->max_fps = SDE_UIDLE_MAX_FPS;
uidle_cfg->debugfs_ctrl = true;
} else {
pr_err("invalid uidle rev:0x%x, disabling uidle\n",
uidle_cfg->uidle_rev);
uidle_cfg->uidle_rev = 0;
}
}
static int _sde_hardware_pre_caps(struct sde_mdss_cfg *sde_cfg, uint32_t hw_rev)
{
int i, rc = 0;
@ -3605,6 +3720,7 @@ static int _sde_hardware_pre_caps(struct sde_mdss_cfg *sde_cfg, uint32_t hw_rev)
MAX_DOWNSCALE_RATIO_INLINE_ROT_RT_DEFAULT;
sde_cfg->true_inline_dwnscale_nrt =
MAX_DOWNSCALE_RATIO_INLINE_ROT_NRT_DEFAULT;
sde_cfg->uidle_cfg.uidle_rev = SDE_UIDLE_VERSION_1_0_0;
} else {
SDE_ERROR("unsupported chipset id:%X\n", hw_rev);
sde_cfg->perf.min_prefill_lines = 0xffff;
@ -3614,6 +3730,8 @@ static int _sde_hardware_pre_caps(struct sde_mdss_cfg *sde_cfg, uint32_t hw_rev)
if (!rc)
rc = sde_hardware_format_caps(sde_cfg, hw_rev);
_sde_hw_setup_uidle(&sde_cfg->uidle_cfg);
return rc;
}
@ -3752,6 +3870,14 @@ struct sde_mdss_cfg *sde_hw_catalog_init(struct drm_device *dev, u32 hw_rev)
if (rc)
goto end;
/* uidle must be done before sspp and ctl,
* so if something goes wrong, we won't
* enable it in ctl and sspp.
*/
rc = sde_uidle_parse_dt(np, sde_cfg);
if (rc)
goto end;
rc = sde_ctl_parse_dt(np, sde_cfg);
if (rc)
goto end;

View file

@ -90,6 +90,13 @@
#define IS_SDE_INLINE_ROT_REV_100(rev) \
((rev) == SDE_INLINE_ROT_VERSION_1_0_0)
/*
* UIDLE supported versions
*/
#define SDE_UIDLE_VERSION_1_0_0 0x100
#define IS_SDE_UIDLE_REV_100(rev) \
((rev) == SDE_UIDLE_VERSION_1_0_0)
#define SDE_HW_UBWC_VER(rev) \
SDE_HW_VER((((rev) >> 8) & 0xF), (((rev) >> 4) & 0xF), ((rev) & 0xF))
@ -231,6 +238,7 @@ enum {
* @SDE_PERF_SSPP_CDP Supports client driven prefetch
* @SDE_PERF_SSPP_QOS_FL_NOCALC Avoid fill level calc for QoS/danger/safe
* @SDE_PERF_SSPP_SYS_CACHE, SSPP supports system cache
* @SDE_PERF_SSPP_UIDLE, sspp supports uidle
* @SDE_PERF_SSPP_MAX Maximum value
*/
enum {
@ -241,6 +249,7 @@ enum {
SDE_PERF_SSPP_CDP,
SDE_PERF_SSPP_QOS_FL_NOCALC,
SDE_PERF_SSPP_SYS_CACHE,
SDE_PERF_SSPP_UIDLE,
SDE_PERF_SSPP_MAX
};
@ -349,6 +358,7 @@ enum {
* @SDE_CTL_PRIMARY_PREF CTL preferred for primary display
* @SDE_CTL_ACTIVE_CFG CTL configuration is specified using active
* blocks
* @SDE_CTL_UIDLE CTL supports uidle
* @SDE_CTL_MAX
*/
enum {
@ -356,6 +366,7 @@ enum {
SDE_CTL_PINGPONG_SPLIT,
SDE_CTL_PRIMARY_PREF,
SDE_CTL_ACTIVE_CFG,
SDE_CTL_UIDLE,
SDE_CTL_MAX
};
@ -716,6 +727,40 @@ struct sde_mdp_cfg {
struct sde_clk_ctrl_reg clk_ctrls[SDE_CLK_CTRL_MAX];
};
/* struct sde_uidle_cfg : MDP TOP-BLK instance info
* @id: index identifying this block
* @base: register base offset to mdss
* @features: bit mask identifying sub-blocks/features
* @fal10_exit_cnt: fal10 exit counter
* @fal10_exit_danger: fal10 exit danger level
* @fal10_danger: fal10 danger level
* @fal10_target_idle_time: fal10 targeted time in uS
* @fal1_target_idle_time: fal1 targeted time in uS
* @fal10_threshold: fal10 threshold value
* @max_downscale: maximum downscaling ratio x1000.
* This ratio is multiplied x1000 to allow
* 3 decimal precision digits.
* @max_fps: maximum fps to allow micro idle
* @uidle_rev: uidle revision supported by the target,
* zero if no support
* @debugfs_ctrl: uidle is enabled/disabled through debugfs
*/
struct sde_uidle_cfg {
SDE_HW_BLK_INFO;
/* global settings */
u32 fal10_exit_cnt;
u32 fal10_exit_danger;
u32 fal10_danger;
/* per-pipe settings */
u32 fal10_target_idle_time;
u32 fal1_target_idle_time;
u32 fal10_threshold;
u32 max_dwnscale;
u32 max_fps;
u32 uidle_rev;
bool debugfs_ctrl;
};
/* struct sde_mdp_cfg : MDP TOP-BLK instance info
* @id: index identifying this block
* @base: register base offset to mdss
@ -1110,6 +1155,7 @@ struct sde_perf_cfg {
* @has_3d_merge_reset Supports 3D merge reset
* @has_decimation Supports decimation
* @sc_cfg: system cache configuration
* @uidle_cfg Settings for uidle feature
* @sui_misr_supported indicate if secure-ui-misr is supported
* @sui_block_xin_mask mask of all the xin-clients to be blocked during
* secure-ui when secure-ui-misr feature is supported
@ -1181,6 +1227,9 @@ struct sde_mdss_cfg {
u32 mdp_count;
struct sde_mdp_cfg mdp[MAX_BLOCKS];
/* uidle is a singleton */
struct sde_uidle_cfg uidle_cfg;
u32 ctl_count;
struct sde_ctl_cfg ctl[MAX_BLOCKS];

View file

@ -45,6 +45,7 @@
#define CTL_PERIPH_FLUSH 0x128
#define CTL_INTF_MASTER 0x134
#define CTL_UIDLE_ACTIVE 0x138
#define CTL_MIXER_BORDER_OUT BIT(24)
#define CTL_FLUSH_MASK_ROT BIT(27)
@ -303,6 +304,19 @@ static inline u32 sde_hw_ctl_get_flush_register(struct sde_hw_ctl *ctx)
return SDE_REG_READ(c, CTL_FLUSH);
}
static inline void sde_hw_ctl_uidle_enable(struct sde_hw_ctl *ctx, bool enable)
{
u32 val;
if (!ctx)
return;
val = SDE_REG_READ(&ctx->hw, CTL_UIDLE_ACTIVE);
val = (val & ~BIT(0)) | (enable ? BIT(0) : 0);
SDE_REG_WRITE(&ctx->hw, CTL_UIDLE_ACTIVE, val);
}
static inline int sde_hw_ctl_update_bitmask_sspp(struct sde_hw_ctl *ctx,
enum sde_sspp sspp,
bool enable)
@ -1242,6 +1256,9 @@ static void _setup_ctl_ops(struct sde_hw_ctl_ops *ops,
ops->update_bitmask_dspp_pavlut = sde_hw_ctl_update_bitmask_dspp_pavlut;
ops->reg_dma_flush = sde_hw_reg_dma_flush;
ops->get_start_state = sde_hw_ctl_get_start_state;
if (cap & BIT(SDE_CTL_UIDLE))
ops->uidle_enable = sde_hw_ctl_uidle_enable;
};
static struct sde_hw_blk_ops sde_hw_ops = {

View file

@ -176,6 +176,13 @@ struct sde_hw_ctl_ops {
*/
int (*trigger_rot_start)(struct sde_hw_ctl *ctx);
/**
* enable/disable UIDLE feature
* @ctx : ctl path ctx pointer
* @enable: true to enable the feature
*/
void (*uidle_enable)(struct sde_hw_ctl *ctx, bool enable);
/**
* Clear the value of the cached pending_flush_mask
* No effect on hardware

View file

@ -107,6 +107,11 @@ enum sde_hw_blk_type {
SDE_HW_BLK_MAX,
};
enum sde_uidle {
UIDLE = 0x1,
UIDLE_MAX,
};
enum sde_mdp {
MDP_TOP = 0x1,
MDP_MAX,
@ -505,6 +510,7 @@ struct sde_mdss_color {
#define SDE_DBG_MASK_ROT (1 << 12)
#define SDE_DBG_MASK_DS (1 << 13)
#define SDE_DBG_MASK_REGDMA (1 << 14)
#define SDE_DBG_MASK_UIDLE (1 << 15)
/**
* struct sde_hw_cp_cfg: hardware dspp/lm feature payload.

View file

@ -42,6 +42,9 @@
#define SSPP_EXCL_REC_SIZE_REC1 0x184
#define SSPP_EXCL_REC_XY_REC1 0x188
#define SSPP_UIDLE_CTRL_VALUE 0x1f0
#define SSPP_UIDLE_CTRL_VALUE_REC1 0x1f4
/* SSPP_DGM */
#define SSPP_DGM_OP_MODE 0x804
#define SSPP_DGM_OP_MODE_REC1 0x1804
@ -953,6 +956,31 @@ static void sde_hw_sspp_setup_sys_cache(struct sde_hw_pipe *ctx,
SDE_REG_WRITE(&ctx->hw, SSPP_SYS_CACHE_MODE + idx, val);
}
static void sde_hw_sspp_setup_uidle(struct sde_hw_pipe *ctx,
struct sde_hw_pipe_uidle_cfg *cfg,
enum sde_sspp_multirect_index index)
{
u32 idx, val;
u32 offset;
if (_sspp_subblk_offset(ctx, SDE_SSPP_SRC, &idx))
return;
if (index == SDE_SSPP_RECT_1)
offset = SSPP_UIDLE_CTRL_VALUE_REC1;
else
offset = SSPP_UIDLE_CTRL_VALUE;
val = SDE_REG_READ(&ctx->hw, offset + idx);
val = (val & ~BIT(31)) | (cfg->enable ? BIT(31) : 0x0);
val = (val & ~0xFF00000) | (cfg->fal_allowed_threshold << 20);
val = (val & ~0xF0000) | (cfg->fal10_exit_threshold << 16);
val = (val & ~0xF00) | (cfg->fal10_threshold << 8);
val = (val & ~0xF) | (cfg->fal1_threshold << 0);
SDE_REG_WRITE(&ctx->hw, offset + idx, val);
}
static void _setup_layer_ops_colorproc(struct sde_hw_pipe *c,
unsigned long features)
{
@ -1152,6 +1180,9 @@ static void _setup_layer_ops(struct sde_hw_pipe *c,
if (test_bit(SDE_PERF_SSPP_CDP, &perf_features))
c->ops.setup_cdp = sde_hw_sspp_setup_cdp;
if (test_bit(SDE_PERF_SSPP_UIDLE, &perf_features))
c->ops.setup_uidle = sde_hw_sspp_setup_uidle;
_setup_layer_ops_colorproc(c, features);
if (test_bit(SDE_SSPP_DGM_INVERSE_PMA, &features))

View file

@ -259,6 +259,22 @@ struct sde_hw_pipe_sc_cfg {
u32 flags;
};
/**
* struct sde_hw_pipe_uidle_cfg - uidle configuration
* @enable: disables uidle
* @fal_allowed_threshold: minimum fl to allow uidle
* @fal10_exit_threshold: number of lines to indicate fal_10_exit is okay
* @fal10_threshold: number of lines where fal_10_is okay
* @fal1_threshold: number of lines where fal_1 is okay
*/
struct sde_hw_pipe_uidle_cfg {
u32 enable;
u32 fal_allowed_threshold;
u32 fal10_exit_threshold;
u32 fal10_threshold;
u32 fal1_threshold;
};
/**
* struct sde_hw_pipe_ts_cfg - traffic shaper configuration
* @size: size to prefill in bytes, or zero to disable
@ -514,6 +530,16 @@ struct sde_hw_sspp_ops {
void (*setup_sys_cache)(struct sde_hw_pipe *ctx,
struct sde_hw_pipe_sc_cfg *cfg);
/**
* setup_uidle - set uidle configuration
* @ctx: Pointer to pipe context
* @cfg: Pointer to uidle configuration
* @index: rectangle index in multirect
*/
void (*setup_uidle)(struct sde_hw_pipe *ctx,
struct sde_hw_pipe_uidle_cfg *cfg,
enum sde_sspp_multirect_index index);
/**
* setup_ts_prefill - setup prefill traffic shaper
* @ctx: Pointer to pipe context

View file

@ -0,0 +1,214 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2018, The Linux Foundation. All rights reserved.
*
*/
#include "sde_hwio.h"
#include "sde_hw_catalog.h"
#include "sde_hw_top.h"
#include "sde_dbg.h"
#include "sde_kms.h"
#define UIDLE_CTL 0x0
#define UIDLE_STATUS 0x4
#define UIDLE_FAL10_VETO_OVERRIDE 0x8
#define UIDLE_WD_TIMER_CTL 0x10
#define UIDLE_WD_TIMER_CTL2 0x14
#define UIDLE_WD_TIMER_LOAD_VALUE 0x18
#define UIDLE_DANGER_STATUS_0 0x20
#define UIDLE_DANGER_STATUS_1 0x24
#define UIDLE_SAFE_STATUS_0 0x30
#define UIDLE_SAFE_STATUS_1 0x34
#define UIDLE_IDLE_STATUS_0 0x38
#define UIDLE_IDLE_STATUS_1 0x3c
#define UIDLE_FAL_STATUS_0 0x40
#define UIDLE_FAL_STATUS_1 0x44
#define UIDLE_GATE_CNTR_CTL 0x50
#define UIDLE_FAL1_GATE_CNTR 0x54
#define UIDLE_FAL10_GATE_CNTR 0x58
#define UIDLE_FAL_WAIT_GATE_CNTR 0x5c
#define UIDLE_FAL1_NUM_TRANSITIONS_CNTR 0x60
#define UIDLE_FAL10_NUM_TRANSITIONS_CNTR 0x64
#define UIDLE_MIN_GATE_CNTR 0x68
#define UIDLE_MAX_GATE_CNTR 0x6c
static const struct sde_uidle_cfg *_top_offset(enum sde_uidle uidle,
struct sde_mdss_cfg *m, void __iomem *addr,
unsigned long len, struct sde_hw_blk_reg_map *b)
{
/* Make sure length of regs offsets is within the mapped memory */
if ((uidle == m->uidle_cfg.id) &&
(m->uidle_cfg.base + m->uidle_cfg.len) < len) {
b->base_off = addr;
b->blk_off = m->uidle_cfg.base;
b->length = m->uidle_cfg.len;
b->hwversion = m->hwversion;
b->log_mask = SDE_DBG_MASK_UIDLE;
SDE_DEBUG("base:0x%p blk_off:0x%x length:%d hwversion:0x%x\n",
b->base_off, b->blk_off, b->length, b->hwversion);
return &m->uidle_cfg;
}
SDE_ERROR("wrong uidle mapping params, will disable UIDLE!\n");
SDE_ERROR("base_off:0x%x id:%d base:0x%x len:%d mmio_len:%d\n",
addr, m->uidle_cfg.id, m->uidle_cfg.base,
m->uidle_cfg.len, len);
m->uidle_cfg.uidle_rev = 0;
return ERR_PTR(-EINVAL);
}
void sde_hw_uidle_get_status(struct sde_hw_uidle *uidle,
struct sde_uidle_status *status)
{
struct sde_hw_blk_reg_map *c = &uidle->hw;
status->uidle_danger_status_0 =
SDE_REG_READ(c, UIDLE_DANGER_STATUS_0);
status->uidle_danger_status_1 =
SDE_REG_READ(c, UIDLE_DANGER_STATUS_1);
status->uidle_safe_status_0 =
SDE_REG_READ(c, UIDLE_SAFE_STATUS_0);
status->uidle_safe_status_1 =
SDE_REG_READ(c, UIDLE_SAFE_STATUS_1);
status->uidle_idle_status_0 =
SDE_REG_READ(c, UIDLE_IDLE_STATUS_0);
status->uidle_idle_status_1 =
SDE_REG_READ(c, UIDLE_IDLE_STATUS_1);
status->uidle_fal_status_0 =
SDE_REG_READ(c, UIDLE_FAL_STATUS_0);
status->uidle_fal_status_1 =
SDE_REG_READ(c, UIDLE_FAL_STATUS_1);
}
void sde_hw_uidle_get_cntr(struct sde_hw_uidle *uidle,
struct sde_uidle_cntr *cntr)
{
struct sde_hw_blk_reg_map *c = &uidle->hw;
u32 reg_val;
cntr->fal1_gate_cntr =
SDE_REG_READ(c, UIDLE_FAL1_GATE_CNTR);
cntr->fal10_gate_cntr =
SDE_REG_READ(c, UIDLE_FAL10_GATE_CNTR);
cntr->fal_wait_gate_cntr =
SDE_REG_READ(c, UIDLE_FAL_WAIT_GATE_CNTR);
cntr->fal1_num_transitions_cntr =
SDE_REG_READ(c, UIDLE_FAL1_NUM_TRANSITIONS_CNTR);
cntr->fal10_num_transitions_cntr =
SDE_REG_READ(c, UIDLE_FAL10_NUM_TRANSITIONS_CNTR);
cntr->min_gate_cntr =
SDE_REG_READ(c, UIDLE_MIN_GATE_CNTR);
cntr->max_gate_cntr =
SDE_REG_READ(c, UIDLE_MAX_GATE_CNTR);
/* clear counters after read */
reg_val = SDE_REG_READ(c, UIDLE_GATE_CNTR_CTL);
reg_val = reg_val | BIT(31);
SDE_REG_WRITE(c, UIDLE_GATE_CNTR_CTL, reg_val);
reg_val = (reg_val & ~BIT(31));
SDE_REG_WRITE(c, UIDLE_GATE_CNTR_CTL, reg_val);
}
void sde_hw_uidle_setup_cntr(struct sde_hw_uidle *uidle, bool enable)
{
struct sde_hw_blk_reg_map *c = &uidle->hw;
u32 reg_val;
reg_val = SDE_REG_READ(c, UIDLE_GATE_CNTR_CTL);
reg_val = (reg_val & ~BIT(8)) | (enable ? BIT(8) : 0);
SDE_REG_WRITE(c, UIDLE_GATE_CNTR_CTL, reg_val);
}
void sde_hw_uidle_setup_wd_timer(struct sde_hw_uidle *uidle,
struct sde_uidle_wd_cfg *cfg)
{
struct sde_hw_blk_reg_map *c = &uidle->hw;
u32 val_ctl, val_ctl2, val_ld;
val_ctl = SDE_REG_READ(c, UIDLE_WD_TIMER_CTL);
val_ctl2 = SDE_REG_READ(c, UIDLE_WD_TIMER_CTL2);
val_ld = SDE_REG_READ(c, UIDLE_WD_TIMER_LOAD_VALUE);
val_ctl = (val_ctl & ~BIT(0)) | (cfg->clear ? BIT(0) : 0);
val_ctl2 = (val_ctl2 & ~BIT(0)) | (cfg->enable ? BIT(0) : 0);
val_ctl2 = (val_ctl2 & ~GENMASK(4, 1)) |
((cfg->granularity & 0xF) << 1);
val_ctl2 = (val_ctl2 & ~BIT(8)) | (cfg->heart_beat ? BIT(8) : 0);
val_ld = cfg->load_value;
SDE_REG_WRITE(c, UIDLE_WD_TIMER_CTL, val_ctl);
SDE_REG_WRITE(c, UIDLE_WD_TIMER_CTL2, val_ctl2);
SDE_REG_WRITE(c, UIDLE_WD_TIMER_LOAD_VALUE, val_ld);
}
void sde_hw_uidle_setup_ctl(struct sde_hw_uidle *uidle,
struct sde_uidle_ctl_cfg *cfg)
{
struct sde_hw_blk_reg_map *c = &uidle->hw;
u32 reg_val;
reg_val = SDE_REG_READ(c, UIDLE_CTL);
reg_val = (reg_val & ~BIT(31)) | (cfg->uidle_enable ? BIT(31) : 0);
reg_val = (reg_val & ~FAL10_DANGER_MSK) |
((cfg->fal10_danger << FAL10_DANGER_SHFT) &
FAL10_DANGER_MSK);
reg_val = (reg_val & ~FAL10_EXIT_DANGER_MSK) |
((cfg->fal10_exit_danger << FAL10_EXIT_DANGER_SHFT) &
FAL10_EXIT_DANGER_MSK);
reg_val = (reg_val & ~FAL10_EXIT_CNT_MSK) |
((cfg->fal10_exit_cnt << FAL10_EXIT_CNT_SHFT) &
FAL10_EXIT_CNT_MSK);
SDE_REG_WRITE(c, UIDLE_CTL, reg_val);
}
static inline void _setup_uidle_ops(struct sde_hw_uidle_ops *ops,
unsigned long cap)
{
ops->set_uidle_ctl = sde_hw_uidle_setup_ctl;
ops->setup_wd_timer = sde_hw_uidle_setup_wd_timer;
ops->uidle_setup_cntr = sde_hw_uidle_setup_cntr;
ops->uidle_get_cntr = sde_hw_uidle_get_cntr;
ops->uidle_get_status = sde_hw_uidle_get_status;
}
struct sde_hw_uidle *sde_hw_uidle_init(enum sde_uidle idx,
void __iomem *addr, unsigned long len,
struct sde_mdss_cfg *m)
{
struct sde_hw_uidle *c;
const struct sde_uidle_cfg *cfg;
c = kzalloc(sizeof(*c), GFP_KERNEL);
if (!c)
return ERR_PTR(-ENOMEM);
cfg = _top_offset(idx, m, addr, len, &c->hw);
if (IS_ERR_OR_NULL(cfg)) {
kfree(c);
return ERR_PTR(-EINVAL);
}
/*
* Assign ops
*/
c->idx = idx;
c->cap = cfg;
_setup_uidle_ops(&c->ops, c->cap->features);
sde_dbg_reg_register_dump_range(SDE_DBG_NAME, "uidle", c->hw.blk_off,
c->hw.blk_off + c->hw.length, 0);
return c;
}

View file

@ -0,0 +1,130 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2018, The Linux Foundation. All rights reserved.
*
*/
#ifndef _SDE_HW_UIDLE_H
#define _SDE_HW_UIDLE_H
#include "sde_hw_catalog.h"
#include "sde_hw_mdss.h"
#include "sde_hw_util.h"
struct sde_hw_uidle;
#define FAL10_DANGER_SHFT 0
#define FAL10_EXIT_DANGER_SHFT 4
#define FAL10_EXIT_CNT_SHFT 16
#define FAL10_DANGER_MSK GENMASK(2, FAL10_DANGER_SHFT)
#define FAL10_EXIT_DANGER_MSK GENMASK(6, FAL10_EXIT_DANGER_SHFT)
#define FAL10_EXIT_CNT_MSK GENMASK(23, FAL10_EXIT_CNT_SHFT)
#define SDE_UIDLE_WD_GRANULARITY 1
#define SDE_UIDLE_WD_HEART_BEAT 0
#define SDE_UIDLE_WD_LOAD_VAL 3
struct sde_uidle_ctl_cfg {
u32 fal10_exit_cnt;
u32 fal10_exit_danger;
u32 fal10_danger;
bool uidle_enable;
};
struct sde_uidle_wd_cfg {
u32 granularity;
u32 heart_beat;
u32 load_value;
bool clear;
bool enable;
};
struct sde_uidle_cntr {
u32 fal1_gate_cntr;
u32 fal10_gate_cntr;
u32 fal_wait_gate_cntr;
u32 fal1_num_transitions_cntr;
u32 fal10_num_transitions_cntr;
u32 min_gate_cntr;
u32 max_gate_cntr;
};
struct sde_uidle_status {
u32 uidle_danger_status_0;
u32 uidle_danger_status_1;
u32 uidle_safe_status_0;
u32 uidle_safe_status_1;
u32 uidle_idle_status_0;
u32 uidle_idle_status_1;
u32 uidle_fal_status_0;
u32 uidle_fal_status_1;
};
struct sde_hw_uidle_ops {
/**
* set_uidle_ctl - set uidle global config
* @uidle: uidle context driver
* @cfg: uidle global config
*/
void (*set_uidle_ctl)(struct sde_hw_uidle *uidle,
struct sde_uidle_ctl_cfg *cfg);
/**
* setup_wd_timer - set uidle watchdog timer
* @uidle: uidle context driver
* @cfg: uidle wd timer config
*/
void (*setup_wd_timer)(struct sde_hw_uidle *uidle,
struct sde_uidle_wd_cfg *cfg);
/**
* uidle_setup_cntr - set uidle perf counters
* @uidle: uidle context driver
* @enable: true to enable the counters
*/
void (*uidle_setup_cntr)(struct sde_hw_uidle *uidle,
bool enable);
/**
* uidle_get_cntr - get uidle perf counters
* @uidle: uidle context driver
* @cntr: pointer to return the counters
*/
void (*uidle_get_cntr)(struct sde_hw_uidle *uidle,
struct sde_uidle_cntr *cntr);
/**
* uidle_get_status - get uidle status
* @uidle: uidle context driver
* @status: pointer to return the status of uidle
*/
void (*uidle_get_status)(struct sde_hw_uidle *uidle,
struct sde_uidle_status *status);
};
struct sde_hw_uidle {
/* base */
struct sde_hw_blk_reg_map hw;
/* uidle */
const struct sde_uidle_cfg *cap;
/* ops */
struct sde_hw_uidle_ops ops;
/*
* uidle is common across all displays, lock to serialize access.
* must be taken by client before using any ops
*/
struct mutex uidle_lock;
enum sde_uidle idx;
};
struct sde_hw_uidle *sde_hw_uidle_init(enum sde_uidle idx,
void __iomem *addr, unsigned long len,
struct sde_mdss_cfg *m);
#endif /*_SDE_HW_UIDLE_H */

View file

@ -536,3 +536,42 @@ uint32_t sde_copy_formats(
return i;
}
/**
* sde_get_linetime - returns the line time for a given mode
* @mode: pointer to drm mode to calculate the line time
* Return: line time of display mode in nS
*/
uint32_t sde_get_linetime(struct drm_display_mode *mode)
{
u64 pclk_rate;
u32 pclk_period;
u32 line_time;
pclk_rate = mode->clock; /* pixel clock in kHz */
if (pclk_rate == 0) {
SDE_ERROR("pclk is 0, cannot calculate line time\n");
return 0;
}
pclk_period = DIV_ROUND_UP_ULL(1000000000ull, pclk_rate);
if (pclk_period == 0) {
SDE_ERROR("pclk period is 0\n");
return 0;
}
/*
* Line time calculation based on Pixel clock and HTOTAL.
* Final unit is in ns.
*/
line_time = (pclk_period * mode->htotal) / 1000;
if (line_time == 0) {
SDE_ERROR("line time calculation is 0\n");
return 0;
}
pr_debug("clk_rate=%lldkHz, clk_period=%d, linetime=%dns, htotal=%d\n",
pclk_rate, pclk_period, line_time, mode->htotal);
return line_time;
}

View file

@ -204,6 +204,8 @@ uint32_t sde_copy_formats(
const struct sde_format_extended *src_list,
uint32_t src_list_size);
uint32_t sde_get_linetime(struct drm_display_mode *mode);
static inline bool is_qseed3_rev_qseed3lite(struct sde_mdss_cfg *sde_cfg)
{
return ((sde_cfg->qseed_type == SDE_SSPP_SCALER_QSEED3LITE) ?

View file

@ -3360,6 +3360,22 @@ static int sde_kms_hw_init(struct msm_kms *kms)
}
}
if (sde_kms->catalog->uidle_cfg.uidle_rev) {
sde_kms->hw_uidle = sde_hw_uidle_init(UIDLE, sde_kms->mmio,
sde_kms->mmio_len, sde_kms->catalog);
if (IS_ERR_OR_NULL(sde_kms->hw_uidle)) {
rc = PTR_ERR(sde_kms->hw_uidle);
if (!sde_kms->hw_uidle)
rc = -EINVAL;
/* uidle is optional, so do not make it a fatal error */
SDE_ERROR("failed to init uidle rc:%d\n", rc);
sde_kms->hw_uidle = NULL;
rc = 0;
}
} else {
sde_kms->hw_uidle = NULL;
}
rc = sde_core_perf_init(&sde_kms->perf, dev, sde_kms->catalog,
&priv->phandle, priv->pclient, "core_clk");
if (rc) {

View file

@ -35,6 +35,7 @@
#include "sde_hw_interrupts.h"
#include "sde_hw_wb.h"
#include "sde_hw_top.h"
#include "sde_hw_uidle.h"
#include "sde_rm.h"
#include "sde_power_handle.h"
#include "sde_irq.h"
@ -259,6 +260,7 @@ struct sde_kms {
struct sde_splash_data splash_data;
struct sde_hw_vbif *hw_vbif[VBIF_MAX];
struct sde_hw_mdp *hw_mdp;
struct sde_hw_uidle *hw_uidle;
int dsi_display_count;
void **dsi_displays;
int wb_display_count;

View file

@ -2697,6 +2697,65 @@ static void _sde_plane_sspp_setup_sys_cache(struct sde_plane *psde,
}
}
static inline bool _sde_plane_allow_uidle(struct sde_plane *psde,
struct sde_rect *src, struct sde_rect *dst)
{
u32 max_downscale = psde->catalog->uidle_cfg.max_dwnscale;
u32 downscale = (src->h * 1000)/dst->h;
return (downscale > max_downscale) ? false : true;
}
static void _sde_plane_setup_uidle(struct drm_crtc *crtc,
struct sde_plane *psde, struct sde_plane_state *pstate,
struct sde_rect *src, struct sde_rect *dst)
{
struct sde_hw_pipe_uidle_cfg cfg;
u32 line_time = sde_get_linetime(&crtc->mode); /* nS */
u32 fal1_target_idle_time_ns =
psde->catalog->uidle_cfg.fal1_target_idle_time * 1000; /* nS */
u32 fal10_target_idle_time_ns =
psde->catalog->uidle_cfg.fal10_target_idle_time * 1000; /* nS */
u32 fal10_threshold =
psde->catalog->uidle_cfg.fal10_threshold; /* uS */
if (line_time && fal10_threshold && fal10_target_idle_time_ns &&
fal1_target_idle_time_ns) {
cfg.enable = _sde_plane_allow_uidle(psde, src, dst);
cfg.fal10_threshold = fal10_threshold;
cfg.fal10_exit_threshold = fal10_threshold + 2;
cfg.fal1_threshold = 1 +
(fal1_target_idle_time_ns*1000/line_time*2)/1000;
cfg.fal_allowed_threshold = fal10_threshold +
(fal10_target_idle_time_ns*1000/line_time*2)/1000;
} else {
SDE_ERROR("invalid settings, will disable UIDLE %d %d %d %d\n",
line_time, fal10_threshold, fal10_target_idle_time_ns,
fal1_target_idle_time_ns);
cfg.enable = false;
cfg.fal10_threshold = 0;
cfg.fal1_threshold = 0;
cfg.fal_allowed_threshold = 0;
}
SDE_DEBUG_PLANE(psde,
"tholds: fal10=%d fal10_exit=%d fal1=%d fal_allowed=%d\n",
cfg.fal10_threshold, cfg.fal10_exit_threshold,
cfg.fal1_threshold, cfg.fal_allowed_threshold);
SDE_DEBUG_PLANE(psde,
"times: line:%d fal1_idle:%d fal10_idle:%d dwnscale:%d\n",
line_time, fal1_target_idle_time_ns,
fal10_target_idle_time_ns,
psde->catalog->uidle_cfg.max_dwnscale);
SDE_EVT32(cfg.enable, cfg.fal10_threshold, cfg.fal10_exit_threshold,
cfg.fal1_threshold, cfg.fal_allowed_threshold,
psde->catalog->uidle_cfg.max_dwnscale);
psde->pipe_hw->ops.setup_uidle(
psde->pipe_hw, &cfg,
pstate->multirect_index);
}
static int sde_plane_sspp_atomic_update(struct drm_plane *plane,
struct drm_plane_state *old_state)
{
@ -2896,6 +2955,10 @@ static int sde_plane_sspp_atomic_update(struct drm_plane *plane,
dst.x -= crtc_roi->x;
dst.y -= crtc_roi->y;
/* check for UIDLE */
if (psde->pipe_hw->ops.setup_uidle)
_sde_plane_setup_uidle(crtc, psde, pstate, &src, &dst);
psde->pipe_cfg.src_rect = src;
psde->pipe_cfg.dst_rect = dst;