diff --git a/msm/sde/sde_core_perf.c b/msm/sde/sde_core_perf.c index 07351819e8..c15ed85889 100644 --- a/msm/sde/sde_core_perf.c +++ b/msm/sde/sde_core_perf.c @@ -829,6 +829,31 @@ void sde_core_perf_crtc_release_bw(struct drm_crtc *crtc) } } +void sde_core_perf_crtc_reserve_res(struct drm_crtc *crtc, u64 reserve_rate) +{ + struct sde_crtc *sde_crtc; + struct sde_kms *kms; + struct msm_drm_private *priv; + + if (!crtc) { + SDE_ERROR("invalid crtc\n"); + return; + } + + /* use current perf, which are the values voted */ + sde_crtc = to_sde_crtc(crtc); + kms = _sde_crtc_get_kms(crtc); + priv = kms->dev->dev_private; + + kms->perf.core_clk_reserve_rate = max(kms->perf.core_clk_reserve_rate, reserve_rate); + kms->perf.core_clk_reserve_rate = min(kms->perf.core_clk_reserve_rate, + kms->perf.max_core_clk_rate); + sde_power_clk_reserve_rate(&priv->phandle, kms->perf.clk_name, + kms->perf.core_clk_reserve_rate); + + SDE_DEBUG("reserve clk:%llu\n", kms->perf.core_clk_reserve_rate); +} + static u64 _sde_core_perf_get_core_clk_rate(struct sde_kms *kms) { u64 clk_rate = kms->perf.perf_tune.min_core_clk; diff --git a/msm/sde/sde_core_perf.h b/msm/sde/sde_core_perf.h index 8e9ac9b795..884e0334ad 100644 --- a/msm/sde/sde_core_perf.h +++ b/msm/sde/sde_core_perf.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* - * Copyright (c) 2016-2020, The Linux Foundation. All rights reserved. + * Copyright (c) 2016-2021, The Linux Foundation. All rights reserved. */ #ifndef _SDE_CORE_PERF_H_ @@ -78,6 +78,7 @@ struct sde_core_perf_tune { * @uidle_enabled: indicates if uidle is already enabled * @idle_sys_cache_enabled: override system cache enable state * for idle usecase + * @core_clk_reserve_rate: reserve core clk rate for built-in display */ struct sde_core_perf { struct drm_device *dev; @@ -99,6 +100,7 @@ struct sde_core_perf { bool llcc_active[SDE_SYS_CACHE_MAX]; bool uidle_enabled; bool idle_sys_cache_enabled; + u64 core_clk_reserve_rate; }; /** @@ -131,6 +133,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_reserve_res - reserve core clock resource for built-in displays. + * @crtc: Pointer to crtc + * @reserve_rate: core clock rate for built-in display + */ +void sde_core_perf_crtc_reserve_res(struct drm_crtc *crtc, u64 reserve_rate); + /** * sde_core_perf_crtc_update_uidle - attempts to enable uidle of the given crtc * @crtc: Pointer to crtc diff --git a/msm/sde/sde_crtc.c b/msm/sde/sde_crtc.c index 4e977940b9..6373507f5b 100644 --- a/msm/sde/sde_crtc.c +++ b/msm/sde/sde_crtc.c @@ -4160,6 +4160,43 @@ static int _sde_crtc_vblank_enable( return 0; } +static void _sde_crtc_reserve_resource(struct drm_crtc *crtc, struct drm_connector *conn) +{ + u32 min_transfer_time = 0, lm_count = 1; + u64 mode_clock_hz = 0, updated_fps = 0, topology_id; + struct drm_encoder *encoder; + + if (!crtc || !conn) + return; + + encoder = conn->state->best_encoder; + if (!sde_encoder_is_built_in_display(encoder)) + return; + + if (sde_encoder_check_curr_mode(encoder, MSM_DISPLAY_CMD_MODE)) + sde_encoder_get_transfer_time(encoder, &min_transfer_time); + + if (min_transfer_time) + updated_fps = DIV_ROUND_UP(1000000, min_transfer_time); + else + updated_fps = drm_mode_vrefresh(&crtc->mode); + + topology_id = sde_connector_get_topology_name(conn); + if (TOPOLOGY_DUALPIPE_MODE(topology_id)) + lm_count = 2; + else if (TOPOLOGY_QUADPIPE_MODE(topology_id)) + lm_count = 4; + + /* mode clock = [(h * v * fps * 1.05) / (num_lm)] */ + mode_clock_hz = mult_frac(crtc->mode.htotal * crtc->mode.vtotal * updated_fps, 105, 100); + mode_clock_hz = div_u64(mode_clock_hz, lm_count); + SDE_DEBUG("[%s] h=%d v=%d fps=%d lm=%d mode_clk=%u\n", + crtc->mode.name, crtc->mode.htotal, crtc->mode.vtotal, + updated_fps, lm_count, mode_clock_hz); + + sde_core_perf_crtc_reserve_res(crtc, mode_clock_hz); +} + /** * sde_crtc_duplicate_state - state duplicate hook * @crtc: Pointer to drm crtc structure @@ -4666,8 +4703,10 @@ static void sde_crtc_enable(struct drm_crtc *crtc, sde_crtc_handle_power_event, crtc, sde_crtc->name); /* Enable ESD thread */ - for (i = 0; i < cstate->num_connectors; i++) + for (i = 0; i < cstate->num_connectors; i++) { sde_connector_schedule_status_work(cstate->connectors[i], true); + _sde_crtc_reserve_resource(crtc, cstate->connectors[i]); + } } /* no input validation - caller API has all the checks */ diff --git a/msm/sde_power_handle.c b/msm/sde_power_handle.c index 8e3b2753c1..a88bab1b0a 100644 --- a/msm/sde_power_handle.c +++ b/msm/sde_power_handle.c @@ -800,6 +800,32 @@ void sde_power_resource_deinit(struct platform_device *pdev, sde_rsc_client_destroy(phandle->rsc_client); } +static void sde_power_mmrm_reserve(struct sde_power_handle *phandle) +{ + int i; + struct dss_module_power *mp = &phandle->mp; + u64 rate = phandle->mmrm_reserve.clk_rate; + + if (!phandle->mmrm_enable) + return; + + for (i = 0; i < mp->num_clk; i++) { + if (!strcmp(mp->clk_config[i].clk_name, phandle->mmrm_reserve.clk_name)) { + if (mp->clk_config[i].max_rate) + rate = min(rate, (u64)mp->clk_config[i].max_rate); + + mp->clk_config[i].rate = rate; + mp->clk_config[i].mmrm.flags = + MMRM_CLIENT_DATA_FLAG_RESERVE_ONLY; + + SDE_ATRACE_BEGIN("sde_clk_set_rate"); + msm_dss_single_clk_set_rate(&mp->clk_config[i]); + SDE_ATRACE_END("sde_clk_set_rate"); + break; + } + } +} + int sde_power_scale_reg_bus(struct sde_power_handle *phandle, u32 usecase_ndx, bool skip_lock) { @@ -897,6 +923,7 @@ int sde_power_resource_enable(struct sde_power_handle *phandle, bool enable) SDE_EVT32_VERBOSE(enable, SDE_EVTLOG_FUNC_CASE2); sde_power_rsc_update(phandle, false); + sde_power_mmrm_reserve(phandle); msm_dss_enable_clk(mp->clk_config, mp->num_clk, enable); sde_power_scale_reg_bus(phandle, VOTE_INDEX_DISABLE, true); @@ -936,6 +963,25 @@ vreg_err: return rc; } +int sde_power_clk_reserve_rate(struct sde_power_handle *phandle, char *clock_name, u64 rate) +{ + if (!phandle) { + pr_err("invalid input power handle\n"); + return -EINVAL; + } else if (!phandle->mmrm_enable) { + pr_debug("mmrm disabled, return early\n"); + return 0; + } + + mutex_lock(&phandle->phandle_lock); + phandle->mmrm_reserve.clk_rate = rate; + strlcpy(phandle->mmrm_reserve.clk_name, clock_name, + sizeof(phandle->mmrm_reserve.clk_name)); + mutex_unlock(&phandle->phandle_lock); + + return 0; +} + int sde_power_clk_set_rate(struct sde_power_handle *phandle, char *clock_name, u64 rate, u32 flags) { diff --git a/msm/sde_power_handle.h b/msm/sde_power_handle.h index a2bc6a8cd4..260d00a875 100644 --- a/msm/sde_power_handle.h +++ b/msm/sde_power_handle.h @@ -147,6 +147,16 @@ struct sde_power_event { bool active; }; +/* + * struct sde_power_mmrm_reserve - mmrm resource reservation for clk and bw + * @clk_name: name of resource reservation clock + * @clk_rate: resource reservation clock rate + */ +struct sde_power_mmrm_reserve { + char clk_name[32]; + u64 clk_rate; +}; + /** * struct sde_power_handle: power handle main struct * @mp: module power for clock and regulator @@ -159,6 +169,7 @@ struct sde_power_event { * @rsc_client_init: boolean to control rsc client create * @mmrm_enable: boolean to indicate if mmrm is enabled * @ib_quota: ib quota of the given bus + * @mmrm_reserve: mmrm resource reservation */ struct sde_power_handle { struct dss_module_power mp; @@ -173,6 +184,8 @@ struct sde_power_handle { bool rsc_client_init; bool mmrm_enable; u64 ib_quota[SDE_POWER_HANDLE_DBUS_ID_MAX]; + + struct sde_power_mmrm_reserve mmrm_reserve; }; /** @@ -226,6 +239,16 @@ int sde_power_scale_reg_bus(struct sde_power_handle *phandle, int sde_power_data_bus_state_update(struct sde_power_handle *phandle, bool enable); +/** + * sde_power_clk_reserve_rate() - reserve the clock rate with mmrm + * @pdata: power handle containing the resources + * @clock_name: clock name which needs rate update. + * @rate: Requested rate. + * + * Return: error code. + */ +int sde_power_clk_reserve_rate(struct sde_power_handle *pdata, char *clock_name, u64 rate); + /** * sde_power_clk_set_rate() - set the clock rate * @pdata: power handle containing the resources