diff --git a/msm/sde/sde_crtc.c b/msm/sde/sde_crtc.c index 29f7f028d8b8..000294129d31 100644 --- a/msm/sde/sde_crtc.c +++ b/msm/sde/sde_crtc.c @@ -4708,6 +4708,8 @@ static void sde_crtc_install_properties(struct drm_crtc *crtc, struct drm_device *dev; struct sde_kms_info *info; struct sde_kms *sde_kms; + int i, j; + static const struct drm_prop_enum_list e_secure_level[] = { {SDE_DRM_SEC_NON_SEC, "sec_and_non_sec"}, {SDE_DRM_SEC_ONLY, "sec_only"}, @@ -4925,6 +4927,37 @@ static void sde_crtc_install_properties(struct drm_crtc *crtc, if (sde_kms->perf.max_core_clk_rate) sde_kms_info_add_keyint(info, "max_mdp_clk", sde_kms->perf.max_core_clk_rate); + + for (i = 0; i < catalog->limit_count; i++) { + sde_kms_info_add_keyint(info, + catalog->limit_cfg[i].name, + catalog->limit_cfg[i].lmt_case_cnt); + + for (j = 0; j < catalog->limit_cfg[i].lmt_case_cnt; j++) { + sde_kms_info_add_keyint(info, + catalog->limit_cfg[i].vector_cfg[j].usecase, + catalog->limit_cfg[i].vector_cfg[j].value); + } + + if (!strcmp(catalog->limit_cfg[i].name, + "sspp_linewidth_usecases")) + sde_kms_info_add_keyint(info, + "sspp_linewidth_values", + catalog->limit_cfg[i].lmt_vec_cnt); + else if (!strcmp(catalog->limit_cfg[i].name, + "sde_bwlimit_usecases")) + sde_kms_info_add_keyint(info, + "sde_bwlimit_values", + catalog->limit_cfg[i].lmt_vec_cnt); + + for (j = 0; j < catalog->limit_cfg[i].lmt_vec_cnt; j++) { + sde_kms_info_add_keyint(info, "limit_usecase", + catalog->limit_cfg[i].value_cfg[j].use_concur); + sde_kms_info_add_keyint(info, "limit_value", + catalog->limit_cfg[i].value_cfg[j].value); + } + } + sde_kms_info_add_keystr(info, "core_ib_ff", catalog->perf.core_ib_ff); sde_kms_info_add_keystr(info, "core_clk_ff", diff --git a/msm/sde/sde_hw_catalog.c b/msm/sde/sde_hw_catalog.c index bbdb8fd51b67..eccd9efbaa72 100644 --- a/msm/sde/sde_hw_catalog.c +++ b/msm/sde/sde_hw_catalog.c @@ -185,6 +185,7 @@ enum sde_prop { UBWC_BW_CALC_VERSION, PIPE_ORDER_VERSION, SEC_SID_MASK, + SDE_LIMITS, SDE_PROP_MAX, }; @@ -279,6 +280,14 @@ enum { INTF_PROP_MAX, }; +enum { + LIMIT_NAME, + LIMIT_USECASE, + LIMIT_ID, + LIMIT_VALUE, + LIMIT_PROP_MAX, +}; + enum { PP_OFF, PP_LEN, @@ -479,6 +488,7 @@ static struct sde_prop_type sde_prop[] = { {PIPE_ORDER_VERSION, "qcom,sde-pipe-order-version", false, PROP_TYPE_U32}, {SEC_SID_MASK, "qcom,sde-secure-sid-mask", false, PROP_TYPE_U32_ARRAY}, + {SDE_LIMITS, "qcom,sde-limits", false, PROP_TYPE_NODE}, }; static struct sde_prop_type sde_perf_prop[] = { @@ -765,6 +775,14 @@ static struct sde_prop_type qdss_prop[] = { {HW_LEN, "qcom,sde-qdss-size", false, PROP_TYPE_U32}, }; +static struct sde_prop_type limit_usecase_prop[] = { + {LIMIT_NAME, "qcom,sde-limit-name", false, PROP_TYPE_STRING}, + {LIMIT_USECASE, "qcom,sde-limit-cases", false, PROP_TYPE_STRING_ARRAY}, + {LIMIT_ID, "qcom,sde-limit-ids", false, PROP_TYPE_U32_ARRAY}, + {LIMIT_VALUE, "qcom,sde-limit-values", false, + PROP_TYPE_BIT_OFFSET_ARRAY}, +}; + /************************************************************* * static API list *************************************************************/ @@ -3146,6 +3164,149 @@ static int _sde_parse_prop_check(struct sde_mdss_cfg *cfg, return 0; } +static int sde_read_limit_node(struct device_node *snp, + struct sde_prop_value *lmt_val, struct sde_mdss_cfg *cfg) +{ + int j, i = 0, rc = 0; + const char *type = NULL; + struct device_node *node = NULL; + + for_each_child_of_node(snp, node) { + cfg->limit_cfg[i].vector_cfg = + kcalloc(cfg->limit_cfg[i].lmt_case_cnt, + sizeof(struct limit_vector_cfg), GFP_KERNEL); + if (!cfg->limit_cfg[i].vector_cfg) { + rc = -ENOMEM; + goto error; + } + + for (j = 0; j < cfg->limit_cfg[i].lmt_case_cnt; j++) { + of_property_read_string_index(node, + limit_usecase_prop[LIMIT_USECASE].prop_name, + j, &type); + cfg->limit_cfg[i].vector_cfg[j].usecase = type; + cfg->limit_cfg[i].vector_cfg[j].value = + PROP_VALUE_ACCESS(&lmt_val[i * LIMIT_PROP_MAX], + LIMIT_ID, j); + } + + cfg->limit_cfg[i].value_cfg = + kcalloc(cfg->limit_cfg[i].lmt_vec_cnt, + sizeof(struct limit_value_cfg), GFP_KERNEL); + + if (!cfg->limit_cfg[i].value_cfg) { + rc = -ENOMEM; + goto error; + } + + for (j = 0; j < cfg->limit_cfg[i].lmt_vec_cnt; j++) { + cfg->limit_cfg[i].value_cfg[j].use_concur = + PROP_BITVALUE_ACCESS( + &lmt_val[i * LIMIT_PROP_MAX], + LIMIT_VALUE, j, 0); + cfg->limit_cfg[i].value_cfg[j].value = + PROP_BITVALUE_ACCESS( + &lmt_val[i * LIMIT_PROP_MAX], + LIMIT_VALUE, j, 1); + + } + i++; + } + + return 0; +error: + for (j = 0; j < cfg->limit_count; j++) { + kfree(cfg->limit_cfg[j].vector_cfg); + kfree(cfg->limit_cfg[j].value_cfg); + } + + cfg->limit_count = 0; + return rc; +} + +static int sde_validate_limit_node(struct device_node *snp, + struct sde_prop_value *sde_limit_value, struct sde_mdss_cfg *cfg) +{ + int i = 0, rc = 0; + struct device_node *node = NULL; + int limit_value_count[LIMIT_PROP_MAX]; + bool limit_value_exists[LIMIT_SUBBLK_COUNT_MAX][LIMIT_PROP_MAX]; + const char *type = NULL; + + for_each_child_of_node(snp, node) { + rc = _validate_dt_entry(node, limit_usecase_prop, + ARRAY_SIZE(limit_usecase_prop), + limit_value_count, NULL); + if (rc) + goto end; + + rc = _read_dt_entry(node, limit_usecase_prop, + ARRAY_SIZE(limit_usecase_prop), limit_value_count, + &limit_value_exists[i][0], + &sde_limit_value[i * LIMIT_PROP_MAX]); + if (rc) + goto end; + + cfg->limit_cfg[i].lmt_case_cnt = + limit_value_count[LIMIT_ID]; + + cfg->limit_cfg[i].lmt_vec_cnt = + limit_value_count[LIMIT_VALUE]; + of_property_read_string(node, + limit_usecase_prop[LIMIT_NAME].prop_name, &type); + cfg->limit_cfg[i].name = type; + + if (!limit_value_count[LIMIT_ID] || + !limit_value_count[LIMIT_VALUE]) { + rc = -EINVAL; + goto end; + } + i++; + } + return 0; +end: + cfg->limit_count = 0; + return rc; +} + +static int sde_limit_parse_dt(struct device_node *np, struct sde_mdss_cfg *cfg) +{ + struct device_node *snp = NULL; + struct sde_prop_value *sde_limit_value = NULL; + int rc = 0; + + snp = of_get_child_by_name(np, sde_prop[SDE_LIMITS].prop_name); + if (!snp) + goto end; + + cfg->limit_count = of_get_child_count(snp); + if (cfg->limit_count < 0) { + rc = -EINVAL; + goto end; + } + + sde_limit_value = kzalloc(cfg->limit_count * LIMIT_PROP_MAX * + sizeof(struct sde_prop_value), GFP_KERNEL); + if (!sde_limit_value) { + rc = -ENOMEM; + goto end; + } + + rc = sde_validate_limit_node(snp, sde_limit_value, cfg); + if (rc) { + SDE_ERROR("validating limit node failed\n"); + goto end; + } + + rc = sde_read_limit_node(snp, sde_limit_value, cfg); + if (rc) + SDE_ERROR("reading limit node failed\n"); + +end: + kfree(sde_limit_value); + return rc; +} + static int sde_top_parse_dt(struct device_node *np, struct sde_mdss_cfg *cfg) { int rc, i, dma_rc, len, prop_count[SDE_PROP_MAX]; @@ -3256,6 +3417,10 @@ static int sde_top_parse_dt(struct device_node *np, struct sde_mdss_cfg *cfg) cfg->has_idle_pc = PROP_VALUE_ACCESS(prop_value, IDLE_PC, 0); cfg->pipe_order_type = PROP_VALUE_ACCESS(prop_value, PIPE_ORDER_VERSION, 0); + + rc = sde_limit_parse_dt(np, cfg); + if (rc) + SDE_DEBUG("parsing of sde limit failed\n"); end: kfree(prop_value); return rc; @@ -4176,6 +4341,11 @@ void sde_hw_catalog_deinit(struct sde_mdss_cfg *sde_cfg) kfree(sde_cfg->vbif[i].qos_tbl[j].priority_lvl); } + for (i = 0; i < sde_cfg->limit_count; i++) { + kfree(sde_cfg->limit_cfg[i].vector_cfg); + kfree(sde_cfg->limit_cfg[i].value_cfg); + } + for (i = 0; i < SDE_QOS_LUT_USAGE_MAX; i++) { kfree(sde_cfg->perf.sfe_lut_tbl[i].entries); kfree(sde_cfg->perf.qos_lut_tbl[i].entries); diff --git a/msm/sde/sde_hw_catalog.h b/msm/sde/sde_hw_catalog.h index 6900aa234311..b37fb5ae7647 100644 --- a/msm/sde/sde_hw_catalog.h +++ b/msm/sde/sde_hw_catalog.h @@ -80,6 +80,7 @@ #define MAX_XIN_COUNT 16 #define SSPP_SUBBLK_COUNT_MAX 2 +#define LIMIT_SUBBLK_COUNT_MAX 10 #define SDE_CTL_CFG_VERSION_1_0_0 0x100 #define MAX_INTF_PER_CTL_V1 2 @@ -1181,6 +1182,42 @@ struct sde_perf_cfg { u32 num_mnoc_ports; }; +/** + * struct limit_vector_cfg - information on the usecase for each limit + * @usecase: usecase for each limit + * @value: id corresponding to each usecase + */ +struct limit_vector_cfg { + const char *usecase; + u32 value; +}; + +/** + * struct limit_value_cfg - information on the value of usecase + * @use_concur: usecase for each limit + * @value: value corresponding to usecase for each limit + */ +struct limit_value_cfg { + u32 use_concur; + u32 value; +}; + +/** + * struct sde_limit_cfg - information om different mdp limits + * @name: name of the limit property + * @lmt_vec_cnt: number of vector values for each limit + * @lmt_case_cnt: number of usecases for each limit + * @vector_cfg: pointer to the vector entries containing info on usecase + * @value_cfg: pointer to the value of each vector entry + */ +struct sde_limit_cfg { + const char *name; + u32 lmt_vec_cnt; + u32 lmt_case_cnt; + struct limit_vector_cfg *vector_cfg; + struct limit_value_cfg *value_cfg; +}; + /** * struct sde_mdss_cfg - information of MDSS HW * This is the main catalog data structure representing @@ -1366,6 +1403,9 @@ struct sde_mdss_cfg { u32 qdss_count; struct sde_qdss_cfg qdss[MAX_BLOCKS]; + u32 limit_count; + struct sde_limit_cfg limit_cfg[LIMIT_SUBBLK_COUNT_MAX]; + /* Add additional block data structures here */ struct sde_perf_cfg perf;