xref: /linux/drivers/gpu/drm/amd/display/modules/power/power.c (revision 9611c0ce215a66770ccbe5c126bf57ba8c31bcad)
14cef2ac4SRay Wu /*
24cef2ac4SRay Wu  * Copyright 2016 Advanced Micro Devices, Inc.
34cef2ac4SRay Wu  *
44cef2ac4SRay Wu  * Permission is hereby granted, free of charge, to any person obtaining a
54cef2ac4SRay Wu  * copy of this software and associated documentation files (the "Software"),
64cef2ac4SRay Wu  * to deal in the Software without restriction, including without limitation
74cef2ac4SRay Wu  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
84cef2ac4SRay Wu  * and/or sell copies of the Software, and to permit persons to whom the
94cef2ac4SRay Wu  * Software is furnished to do so, subject to the following conditions:
104cef2ac4SRay Wu  *
114cef2ac4SRay Wu  * The above copyright notice and this permission notice shall be included in
124cef2ac4SRay Wu  * all copies or substantial portions of the Software.
134cef2ac4SRay Wu  *
144cef2ac4SRay Wu  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
154cef2ac4SRay Wu  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
164cef2ac4SRay Wu  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
174cef2ac4SRay Wu  * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
184cef2ac4SRay Wu  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
194cef2ac4SRay Wu  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
204cef2ac4SRay Wu  * OTHER DEALINGS IN THE SOFTWARE.
214cef2ac4SRay Wu  *
224cef2ac4SRay Wu  * Authors: AMD
234cef2ac4SRay Wu  *
244cef2ac4SRay Wu  */
254cef2ac4SRay Wu 
264cef2ac4SRay Wu #include "dm_services.h"
274cef2ac4SRay Wu #include "dc.h"
284cef2ac4SRay Wu #include "mod_power.h"
294cef2ac4SRay Wu #include "core_types.h"
304cef2ac4SRay Wu #include "dmcu.h"
314cef2ac4SRay Wu #include "abm.h"
324cef2ac4SRay Wu #include "power_helpers.h"
334cef2ac4SRay Wu #include "dce/dmub_psr.h"
344cef2ac4SRay Wu #include "dal_asic_id.h"
354cef2ac4SRay Wu #include "link_service.h"
364cef2ac4SRay Wu #include <linux/math.h>
374cef2ac4SRay Wu 
384cef2ac4SRay Wu #define DC_TRACE_LEVEL_MESSAGE(...) /* do nothing */
394cef2ac4SRay Wu #define DC_TRACE_LEVEL_MESSAGEP(...) /* do nothing */
404cef2ac4SRay Wu 
414cef2ac4SRay Wu #define MOD_POWER_MAX_CONCURRENT_STREAMS 32
424cef2ac4SRay Wu #define SMOOTH_BRIGHTNESS_ADJUSTMENT_TIME_IN_MS 500
434cef2ac4SRay Wu #define LOW_REFRESH_RATE_DURATION_US_UPPER_BOUND 25000
444cef2ac4SRay Wu 
454cef2ac4SRay Wu /* If system or panel does not report some sort of brightness percent to nits
464cef2ac4SRay Wu  * mapping, we will use following default values so backlight control using
474cef2ac4SRay Wu  * nits based interfaces will still work, but might not describe panel
484cef2ac4SRay Wu  * correctly. In this case percentage based backlight control should ideally
494cef2ac4SRay Wu  * be used.
504cef2ac4SRay Wu  * Min = 5 nits
514cef2ac4SRay Wu  * Max = 300 nits
524cef2ac4SRay Wu  */
534cef2ac4SRay Wu 
544cef2ac4SRay Wu static const unsigned int pwr_default_min_brightness_millinits = 1000;
554cef2ac4SRay Wu static const unsigned int pwr_default_sdr_brightness_millinits = 270000;
564cef2ac4SRay Wu 
574cef2ac4SRay Wu static const unsigned int default_ac_backlight_percent   = 100;
584cef2ac4SRay Wu static const unsigned int default_dc_backlight_percent   = 70;
594cef2ac4SRay Wu 
604cef2ac4SRay Wu #define MOD_POWER_TO_CORE(mod_power)\
61254a47ceSLohita Mudimela 		container_of(mod_power, struct core_power, mod_public)
624cef2ac4SRay Wu 
634cef2ac4SRay Wu /* Given a specific dc_stream* this function finds its equivalent
644cef2ac4SRay Wu  * on the core_freesync->map and returns the corresponding index
654cef2ac4SRay Wu  */
66b11107cbSLohita Mudimela unsigned int map_index_from_stream(struct core_power *core_power,
674cef2ac4SRay Wu 		const struct dc_stream_state *stream)
684cef2ac4SRay Wu {
694cef2ac4SRay Wu 	unsigned int index = 0;
704cef2ac4SRay Wu 
714cef2ac4SRay Wu 	for (index = 0; index < core_power->num_entities; index++) {
724cef2ac4SRay Wu 		if (core_power->map[index].stream == stream)
734cef2ac4SRay Wu 			return index;
744cef2ac4SRay Wu 	}
754cef2ac4SRay Wu 	/* Could not find stream requested, this is not trivial, fix when hit*/
764cef2ac4SRay Wu 	DC_TRACE_LEVEL_MESSAGE(DAL_TRACE_LEVEL_ERROR,
774cef2ac4SRay Wu 						WPP_BIT_FLAG_Firmware_PsrState,
784cef2ac4SRay Wu 						"map index from stream: ERROR: core_power=%p stream=%p",
794cef2ac4SRay Wu 						core_power,
804cef2ac4SRay Wu 						stream);
814cef2ac4SRay Wu 	ASSERT(false);
824cef2ac4SRay Wu 	/* We come here only when we can't map stream index.
834cef2ac4SRay Wu 	 * In good cases, this would happen when we attempt to change
844cef2ac4SRay Wu 	 * brightness before stream creation, in which case we create a
854cef2ac4SRay Wu 	 * dummy stream with index 0.
864cef2ac4SRay Wu 	 * With external monitor connected, the index passed from this return
874cef2ac4SRay Wu 	 * is 1. Passing anything greater than 0 from here would always point
884cef2ac4SRay Wu 	 * to bad memory.
894cef2ac4SRay Wu 	 */
904cef2ac4SRay Wu 	return 0;
914cef2ac4SRay Wu }
924cef2ac4SRay Wu 
934cef2ac4SRay Wu bool mod_power_hw_init(struct mod_power *mod_power)
944cef2ac4SRay Wu {
95254a47ceSLohita Mudimela 	/* Call backlight initialization */
96254a47ceSLohita Mudimela 	return mod_power_hw_init_backlight(mod_power);
974cef2ac4SRay Wu 
98254a47ceSLohita Mudimela 	/* Future: Add other HW init here */
994cef2ac4SRay Wu }
1004cef2ac4SRay Wu 
1014cef2ac4SRay Wu struct mod_power *mod_power_create(struct dc *dc,
1024cef2ac4SRay Wu 		struct mod_power_init_params *init_params,
1034cef2ac4SRay Wu 		unsigned int edp_num)
1044cef2ac4SRay Wu {
1054cef2ac4SRay Wu 	struct core_power *core_power = NULL;
1064cef2ac4SRay Wu 	int i = 0;
107d1c2c463SGaghik Khachatrian 	unsigned int abm_max_config = 0;
1084cef2ac4SRay Wu 	unsigned int inst = 0;
1094cef2ac4SRay Wu 	bool is_brightness_range_valid = false;
1104cef2ac4SRay Wu 
1114cef2ac4SRay Wu 	if (dc == NULL)
1124cef2ac4SRay Wu 		goto fail_dc_null;
1134cef2ac4SRay Wu 
1144cef2ac4SRay Wu 	core_power = kzalloc(sizeof(struct core_power), GFP_KERNEL);
1154cef2ac4SRay Wu 
1164cef2ac4SRay Wu 	if (core_power == NULL)
1174cef2ac4SRay Wu 		goto fail_alloc_context;
1184cef2ac4SRay Wu 
1194cef2ac4SRay Wu 	core_power->edp_num = edp_num;
1204cef2ac4SRay Wu 	core_power->map = kzalloc(sizeof(struct power_entity) * MOD_POWER_MAX_CONCURRENT_STREAMS,
1214cef2ac4SRay Wu 				  GFP_KERNEL);
1224cef2ac4SRay Wu 
1234cef2ac4SRay Wu 	if (core_power->map == NULL)
1244cef2ac4SRay Wu 		goto fail_alloc_map;
1254cef2ac4SRay Wu 
1264cef2ac4SRay Wu 	for (i = 0; i < MOD_POWER_MAX_CONCURRENT_STREAMS; i++) {
1274cef2ac4SRay Wu 		core_power->map[i].stream = NULL;
1284cef2ac4SRay Wu 	}
1294cef2ac4SRay Wu 
1304cef2ac4SRay Wu 	for (i = 0; i < MOD_POWER_MAX_CONCURRENT_STREAMS; i++) {
1314cef2ac4SRay Wu 		core_power->map[i].psr_context =
1324cef2ac4SRay Wu 				kzalloc(sizeof(struct mod_power_psr_context),
1334cef2ac4SRay Wu 					GFP_KERNEL);
1344cef2ac4SRay Wu 		if (core_power->map[i].psr_context == NULL)
1354cef2ac4SRay Wu 			goto fail_construct;
1364cef2ac4SRay Wu 	}
1374cef2ac4SRay Wu 
1384cef2ac4SRay Wu 	core_power->psr_smu_optimizations_support = init_params->allow_psr_smu_optimizations;
1394cef2ac4SRay Wu 	core_power->multi_disp_optimizations_support = init_params->allow_psr_multi_disp_optimizations;
1404cef2ac4SRay Wu 
1414cef2ac4SRay Wu 	for (inst = 0; inst < edp_num; inst++) {
1424cef2ac4SRay Wu 		core_power->bl_prop[inst].min_abm_backlight =
1434cef2ac4SRay Wu 				init_params[inst].min_abm_backlight;
1444cef2ac4SRay Wu 		core_power->bl_prop[inst].disable_fractional_pwm =
1454cef2ac4SRay Wu 				init_params[inst].disable_fractional_pwm;
1464cef2ac4SRay Wu 		core_power->bl_prop[inst].use_linear_backlight_curve =
1474cef2ac4SRay Wu 				init_params[inst].use_linear_backlight_curve;
1484cef2ac4SRay Wu 		core_power->bl_prop[inst].use_nits_based_brightness =
1494cef2ac4SRay Wu 				init_params[inst].use_nits_based_brightness;
1504cef2ac4SRay Wu 		core_power->bl_prop[inst].backlight_ramping_override =
1514cef2ac4SRay Wu 				init_params[inst].backlight_ramping_override;
1524cef2ac4SRay Wu 		core_power->bl_prop[inst].backlight_ramping_reduction =
1534cef2ac4SRay Wu 				init_params[inst].backlight_ramping_reduction;
1544cef2ac4SRay Wu 		core_power->bl_prop[inst].backlight_ramping_start =
1554cef2ac4SRay Wu 				init_params[inst].backlight_ramping_start;
1564cef2ac4SRay Wu 		core_power->bl_prop[inst].use_custom_backlight_caps =
1574cef2ac4SRay Wu 				init_params[inst].use_custom_backlight_caps;
1584cef2ac4SRay Wu 		core_power->bl_prop[inst].custom_backlight_caps_config_no =
1594cef2ac4SRay Wu 				init_params[inst].custom_backlight_caps_config_no;
1604cef2ac4SRay Wu 
1614cef2ac4SRay Wu 		// Do not allow less than 101 backlight levels
1624cef2ac4SRay Wu 		if (init_params[inst].num_backlight_levels < 101)
1634cef2ac4SRay Wu 			core_power->bl_prop[inst].num_backlight_levels = 101;
1644cef2ac4SRay Wu 		else
1654cef2ac4SRay Wu 			core_power->bl_prop[inst].num_backlight_levels =
1664cef2ac4SRay Wu 				init_params[inst].num_backlight_levels;
1674cef2ac4SRay Wu 
1684cef2ac4SRay Wu 		core_power->bl_prop[inst].backlight_lut = (unsigned int *)
1694cef2ac4SRay Wu 				(kzalloc(sizeof(unsigned int) *
1704cef2ac4SRay Wu 				core_power->bl_prop[inst].num_backlight_levels, GFP_KERNEL));
1714cef2ac4SRay Wu 		if (core_power->bl_prop[inst].backlight_lut == NULL)
1724cef2ac4SRay Wu 			goto fail_alloc_backlight_array;
1734cef2ac4SRay Wu 	}
1744cef2ac4SRay Wu 
1754cef2ac4SRay Wu 	core_power->varibright_prop.varibright_active = false;
1764cef2ac4SRay Wu 
1774cef2ac4SRay Wu 	core_power->varibright_prop.varibright_user_enable =
1784cef2ac4SRay Wu 			init_params->def_varibright_enable;
1794cef2ac4SRay Wu 
1804cef2ac4SRay Wu 	// Table of ABM levels here is 1-4, but level 0 also exists as 'off'
1814cef2ac4SRay Wu 	if (init_params->varibright_level <= abm_defines_max_level) {
1824cef2ac4SRay Wu 		core_power->varibright_prop.varibright_level =
1834cef2ac4SRay Wu 			init_params->varibright_level;
1844cef2ac4SRay Wu 
1854cef2ac4SRay Wu 	} else {
1864cef2ac4SRay Wu 		core_power->varibright_prop.varibright_level = 3;
1874cef2ac4SRay Wu 	}
1884cef2ac4SRay Wu 	if (init_params->def_varibright_level <= abm_defines_max_level) {
1894cef2ac4SRay Wu 		core_power->varibright_prop.def_varibright_level =
1904cef2ac4SRay Wu 			init_params->def_varibright_level;
1914cef2ac4SRay Wu 	} else {
1924cef2ac4SRay Wu 		core_power->varibright_prop.def_varibright_level = 3;
1934cef2ac4SRay Wu 	}
1944cef2ac4SRay Wu 
1954cef2ac4SRay Wu 	// ABM used to contain 4 different configs. There is only 3 since ABM 2.3.
1964cef2ac4SRay Wu 	if ((dc->res_pool->dmcu != NULL) && (dc->res_pool->dmcu->dmcu_version.abm_version < 0x23))
1974cef2ac4SRay Wu 		abm_max_config = 4;
1984cef2ac4SRay Wu 	else
1994cef2ac4SRay Wu 		abm_max_config = 3;
2004cef2ac4SRay Wu 
2014cef2ac4SRay Wu 	if (init_params->abm_config_setting < abm_max_config)
2024cef2ac4SRay Wu 		core_power->varibright_prop.varibright_config_setting =
2034cef2ac4SRay Wu 			init_params->abm_config_setting;
2044cef2ac4SRay Wu 	else
2054cef2ac4SRay Wu 		core_power->varibright_prop.varibright_config_setting = 0;
2064cef2ac4SRay Wu 
2074cef2ac4SRay Wu 	for (inst = 0; inst < edp_num; inst++) {
2084cef2ac4SRay Wu 		core_power->bl_prop[inst].backlight_lut[0] = init_params[inst].min_backlight_pwm;
2094cef2ac4SRay Wu 		core_power->bl_prop[inst].backlight_lut[
2104cef2ac4SRay Wu 			core_power->bl_prop[inst].num_backlight_levels-1] =
2114cef2ac4SRay Wu 				init_params[inst].max_backlight_pwm;
2124cef2ac4SRay Wu 		core_power->bl_prop[inst].min_backlight_pwm = init_params[inst].min_backlight_pwm;
2134cef2ac4SRay Wu 		core_power->bl_prop[inst].max_backlight_pwm = init_params[inst].max_backlight_pwm;
2144cef2ac4SRay Wu 		core_power->bl_prop[inst].ac_backlight_percent =
2154cef2ac4SRay Wu 				default_ac_backlight_percent;
2164cef2ac4SRay Wu 		core_power->bl_prop[inst].dc_backlight_percent =
2174cef2ac4SRay Wu 				default_dc_backlight_percent;
2184cef2ac4SRay Wu 		core_power->bl_prop[inst].backlight_caps_valid = false;
2194cef2ac4SRay Wu 
2204cef2ac4SRay Wu 		if (core_power->bl_prop[inst].use_nits_based_brightness) {
2214cef2ac4SRay Wu 			core_power->bl_prop[inst].min_brightness_millinits =
2224cef2ac4SRay Wu 					init_params[inst].panel_min_millinits;
2234cef2ac4SRay Wu 			core_power->bl_prop[inst].max_brightness_millinits =
2244cef2ac4SRay Wu 					init_params[inst].panel_max_millinits;
2254cef2ac4SRay Wu 		} else {
2264cef2ac4SRay Wu 
2274cef2ac4SRay Wu 			core_power->bl_prop[inst].min_brightness_millinits =
2284cef2ac4SRay Wu 					pwr_default_min_brightness_millinits;
2294cef2ac4SRay Wu 			core_power->bl_prop[inst].max_brightness_millinits =
2304cef2ac4SRay Wu 					pwr_default_sdr_brightness_millinits;
2314cef2ac4SRay Wu 		}
2324cef2ac4SRay Wu 
2334cef2ac4SRay Wu 		core_power->bl_prop[inst].backlight_range =
2344cef2ac4SRay Wu 				core_power->bl_prop[inst].max_backlight_pwm-
2354cef2ac4SRay Wu 				core_power->bl_prop[inst].min_backlight_pwm;
2364cef2ac4SRay Wu 
2374cef2ac4SRay Wu 		core_power->bl_prop[inst].nits_range =
2384cef2ac4SRay Wu 				core_power->bl_prop[inst].max_brightness_millinits -
2394cef2ac4SRay Wu 				core_power->bl_prop[inst].min_brightness_millinits;
2404cef2ac4SRay Wu 
2414cef2ac4SRay Wu 		core_power->bl_state[inst].smooth_brightness_enabled = true;
2424cef2ac4SRay Wu 	}
2434cef2ac4SRay Wu 
2444cef2ac4SRay Wu 	/* Check if at least 1 instance in core_power is populated before failing */
2454cef2ac4SRay Wu 	for (inst = 0; inst < edp_num; inst++) {
2464cef2ac4SRay Wu 		if (core_power->bl_prop[inst].nits_range != 0 && core_power->bl_prop[inst].backlight_range != 0) {
2474cef2ac4SRay Wu 			is_brightness_range_valid = true;
2484cef2ac4SRay Wu 			break;
2494cef2ac4SRay Wu 		}
2504cef2ac4SRay Wu 
2514cef2ac4SRay Wu 	}
2524cef2ac4SRay Wu 	if (!is_brightness_range_valid)
2534cef2ac4SRay Wu 		goto fail_bad_brightness_range;
2544cef2ac4SRay Wu 
2554cef2ac4SRay Wu 	core_power->num_entities = 0;
2564cef2ac4SRay Wu 
2574cef2ac4SRay Wu 	core_power->dc = dc;
2584cef2ac4SRay Wu 	for (inst = 0; inst < edp_num; inst++) {
2594cef2ac4SRay Wu 		initialize_backlight_caps(core_power, inst);
2604cef2ac4SRay Wu 		core_power->bl_state[inst].backlight_millipercent =
2614cef2ac4SRay Wu 			core_power->bl_prop[inst].dc_backlight_percent * 1000;
2624cef2ac4SRay Wu 		core_power->bl_state[inst].backlight_pwm = backlight_millipercent_to_pwm(core_power,
2634cef2ac4SRay Wu 		core_power->bl_state[inst].backlight_millipercent, inst);
2644cef2ac4SRay Wu 		core_power->bl_state[inst].backlight_millinit = backlight_millipercent_to_millinit(core_power,
2654cef2ac4SRay Wu 		core_power->bl_state[inst].backlight_millipercent, inst);
2664cef2ac4SRay Wu 	}
2674cef2ac4SRay Wu 
268254a47ceSLohita Mudimela 	return &core_power->mod_public;
2694cef2ac4SRay Wu 
2704cef2ac4SRay Wu fail_bad_brightness_range:
2714cef2ac4SRay Wu fail_alloc_backlight_array:
2724cef2ac4SRay Wu 	for (inst = 0; inst < edp_num; inst++)
2734cef2ac4SRay Wu 		if (core_power->bl_prop[inst].backlight_lut)
2744cef2ac4SRay Wu 			kfree(core_power->bl_prop[inst].backlight_lut);
2754cef2ac4SRay Wu fail_construct:
2764cef2ac4SRay Wu 	for (i = 0; i < MOD_POWER_MAX_CONCURRENT_STREAMS; i++) {
2774cef2ac4SRay Wu 		if (core_power->map[i].psr_context)
2784cef2ac4SRay Wu 			kfree(core_power->map[i].psr_context);
2794cef2ac4SRay Wu 	}
2804cef2ac4SRay Wu 	kfree(core_power->map);
2814cef2ac4SRay Wu 
2824cef2ac4SRay Wu fail_alloc_map:
2834cef2ac4SRay Wu 	kfree(core_power);
2844cef2ac4SRay Wu 
2854cef2ac4SRay Wu fail_alloc_context:
2864cef2ac4SRay Wu fail_dc_null:
2874cef2ac4SRay Wu 	return NULL;
2884cef2ac4SRay Wu }
2894cef2ac4SRay Wu 
2904cef2ac4SRay Wu void mod_power_destroy(struct mod_power *mod_power)
2914cef2ac4SRay Wu {
2924cef2ac4SRay Wu 	if (mod_power != NULL) {
293d1c2c463SGaghik Khachatrian 		unsigned int i;
2944cef2ac4SRay Wu 		struct core_power *core_power =
2954cef2ac4SRay Wu 				MOD_POWER_TO_CORE(mod_power);
2964cef2ac4SRay Wu 
2974cef2ac4SRay Wu 		for (i = 0; i < MOD_POWER_MAX_CONCURRENT_STREAMS; i++)
2984cef2ac4SRay Wu 			if (core_power->map[i].psr_context)
2994cef2ac4SRay Wu 				kfree(core_power->map[i].psr_context);
3004cef2ac4SRay Wu 
3014cef2ac4SRay Wu 		for (i = 0; i < core_power->num_entities; i++)
3024cef2ac4SRay Wu 			if (core_power->map[i].stream)
3034cef2ac4SRay Wu 				dc_stream_release(core_power->map[i].stream);
3044cef2ac4SRay Wu 
3054cef2ac4SRay Wu 		kfree(core_power->map);
3064cef2ac4SRay Wu 
3074cef2ac4SRay Wu 		for (i = 0; i < MAX_NUM_EDP; i++)
3084cef2ac4SRay Wu 			if (core_power->bl_prop[i].backlight_lut)
3094cef2ac4SRay Wu 				kfree(core_power->bl_prop[i].backlight_lut);
3104cef2ac4SRay Wu 
3114cef2ac4SRay Wu 		kfree(core_power);
3124cef2ac4SRay Wu 	}
3134cef2ac4SRay Wu }
3144cef2ac4SRay Wu 
3154cef2ac4SRay Wu bool mod_power_add_stream(struct mod_power *mod_power,
3164cef2ac4SRay Wu 		struct dc_stream_state *stream, struct psr_caps *caps)
3174cef2ac4SRay Wu {
3184cef2ac4SRay Wu 	struct core_power *core_power = NULL;
3194cef2ac4SRay Wu 
3204cef2ac4SRay Wu 	if (mod_power == NULL)
3214cef2ac4SRay Wu 		return false;
3224cef2ac4SRay Wu 
3234cef2ac4SRay Wu 	core_power = MOD_POWER_TO_CORE(mod_power);
3244cef2ac4SRay Wu 
3254cef2ac4SRay Wu 	if (core_power->num_entities < MOD_POWER_MAX_CONCURRENT_STREAMS) {
3264cef2ac4SRay Wu 		dc_stream_retain(stream);
3274cef2ac4SRay Wu 
3284cef2ac4SRay Wu 		core_power->map[core_power->num_entities].stream = stream;
3294cef2ac4SRay Wu 		core_power->map[core_power->num_entities].caps = caps;
3304cef2ac4SRay Wu 
3314cef2ac4SRay Wu 		// initialize cached PSR params to something "safe" (something that is
3324cef2ac4SRay Wu 		// consistent with disabled PSR state)
3334cef2ac4SRay Wu 		core_power->map[core_power->num_entities].psr_enabled = 0;
3344cef2ac4SRay Wu 		core_power->map[core_power->num_entities].psr_events = psr_event_vsync;
3354cef2ac4SRay Wu 		core_power->map[core_power->num_entities].psr_power_opt = 0;
3364cef2ac4SRay Wu 		core_power->num_entities++;
3374cef2ac4SRay Wu 		return true;
3384cef2ac4SRay Wu 	}
3394cef2ac4SRay Wu 
3404cef2ac4SRay Wu 	DC_TRACE_LEVEL_MESSAGE(DAL_TRACE_LEVEL_ERROR,
3414cef2ac4SRay Wu 						WPP_BIT_FLAG_Firmware_PsrState,
342d1c2c463SGaghik Khachatrian 						"mod_power: add_stream: ERROR: stream=%p num_entities=%u >= MOD_POWER_MAX_CONCURRENT_STREAMS",
3434cef2ac4SRay Wu 						stream,
3444cef2ac4SRay Wu 						core_power->num_entities);
3454cef2ac4SRay Wu 
3464cef2ac4SRay Wu 	return false;
3474cef2ac4SRay Wu }
3484cef2ac4SRay Wu 
3494cef2ac4SRay Wu bool mod_power_remove_stream(struct mod_power *mod_power,
3504cef2ac4SRay Wu 		const struct dc_stream_state *stream)
3514cef2ac4SRay Wu {
352d1c2c463SGaghik Khachatrian 	unsigned int i = 0;
3534cef2ac4SRay Wu 	struct core_power *core_power = NULL;
3544cef2ac4SRay Wu 	unsigned int index = 0;
3554cef2ac4SRay Wu 
3564cef2ac4SRay Wu 	if (mod_power == NULL)
3574cef2ac4SRay Wu 		return false;
3584cef2ac4SRay Wu 
3594cef2ac4SRay Wu 	core_power = MOD_POWER_TO_CORE(mod_power);
3604cef2ac4SRay Wu 	if (core_power->num_entities == 0) {
3614cef2ac4SRay Wu 		/* trying to remove a stream a second time or have not added yet */
3624cef2ac4SRay Wu 		BREAK_TO_DEBUGGER();
3634cef2ac4SRay Wu 		DC_TRACE_LEVEL_MESSAGE(DAL_TRACE_LEVEL_ERROR,
3644cef2ac4SRay Wu 							WPP_BIT_FLAG_Firmware_PsrState,
3654cef2ac4SRay Wu 							"mod_power: remove_stream: ERROR: num_entities=0 stream=%p",
3664cef2ac4SRay Wu 							stream);
3674cef2ac4SRay Wu 		return false;
3684cef2ac4SRay Wu 	}
3694cef2ac4SRay Wu 
3704cef2ac4SRay Wu 	index = map_index_from_stream(core_power, stream);
3714cef2ac4SRay Wu 
3724cef2ac4SRay Wu 	if (index >= core_power->num_entities) {
3734cef2ac4SRay Wu 		/* trying to remove a stream a second time or have not added yet */
3744cef2ac4SRay Wu 		BREAK_TO_DEBUGGER();
3754cef2ac4SRay Wu 		DC_TRACE_LEVEL_MESSAGE(DAL_TRACE_LEVEL_ERROR,
3764cef2ac4SRay Wu 							WPP_BIT_FLAG_Firmware_PsrState,
377d1c2c463SGaghik Khachatrian 							"mod_power: remove_stream: ERROR: index=%u >= num_entities=%u stream=%p",
3784cef2ac4SRay Wu 							index,
3794cef2ac4SRay Wu 							core_power->num_entities,
3804cef2ac4SRay Wu 							stream);
3814cef2ac4SRay Wu 		return false;
3824cef2ac4SRay Wu 	}
3834cef2ac4SRay Wu 
3844cef2ac4SRay Wu 	dc_stream_release(core_power->map[index].stream);
3854cef2ac4SRay Wu 	core_power->map[index].stream = NULL;
3864cef2ac4SRay Wu 	/* To remove this entity, shift everything after down */
3874cef2ac4SRay Wu 	for (i = index; i < core_power->num_entities - 1; i++) {
3884cef2ac4SRay Wu 		core_power->map[i].stream = core_power->map[i + 1].stream;
3894cef2ac4SRay Wu 		core_power->map[i].caps = core_power->map[i + 1].caps;
3904cef2ac4SRay Wu 
3914cef2ac4SRay Wu 		// copy over cached parameters in case they map to PSR capable display
3924cef2ac4SRay Wu 		core_power->map[i].psr_enabled = core_power->map[i + 1].psr_enabled;
3934cef2ac4SRay Wu 		core_power->map[i].psr_events = core_power->map[i + 1].psr_events;
3944cef2ac4SRay Wu 		core_power->map[i].psr_power_opt = core_power->map[i + 1].psr_power_opt;
3954cef2ac4SRay Wu 
3964cef2ac4SRay Wu 		memcpy(core_power->map[i].psr_context, core_power->map[i + 1].psr_context, sizeof(struct mod_power_psr_context));
3974cef2ac4SRay Wu 		memset(core_power->map[i + 1].psr_context, 0, sizeof(struct mod_power_psr_context));
3984cef2ac4SRay Wu 	}
3994cef2ac4SRay Wu 	core_power->num_entities--;
4004cef2ac4SRay Wu 
4014cef2ac4SRay Wu 	return true;
4024cef2ac4SRay Wu }
4034cef2ac4SRay Wu 
4044cef2ac4SRay Wu /*
4054cef2ac4SRay Wu  * Replace_stream should be used when there is a mode set for existing
4064cef2ac4SRay Wu  * display target with a valid stream. In this case might need to retain
4074cef2ac4SRay Wu  * cached PSR state (events, power opt, en/dis) if we are dealing with PSR
4084cef2ac4SRay Wu  * capable display. If mod_power_remove and mod_power_add are used instead,
4094cef2ac4SRay Wu  * then stream may be assigned to a different slot and may end up with
4104cef2ac4SRay Wu  * wrong cached PSR state. It is hard to tell which PSR events should
4114cef2ac4SRay Wu  * persist through mode set or what psr_events should be initialized to, so
4124cef2ac4SRay Wu  * it might be better just to retain them all.
4134cef2ac4SRay Wu  */
4144cef2ac4SRay Wu bool mod_power_replace_stream(struct mod_power *mod_power,
4154cef2ac4SRay Wu 		const struct dc_stream_state *current_stream,
4164cef2ac4SRay Wu 		struct dc_stream_state *new_stream,
4174cef2ac4SRay Wu 		struct psr_caps *new_caps)
4184cef2ac4SRay Wu {
4194cef2ac4SRay Wu 	struct core_power *core_power = NULL;
4204cef2ac4SRay Wu 	unsigned int index = 0;
4214cef2ac4SRay Wu 
4224cef2ac4SRay Wu 	if (mod_power == NULL)
4234cef2ac4SRay Wu 		return false;
4244cef2ac4SRay Wu 
4254cef2ac4SRay Wu 	core_power = MOD_POWER_TO_CORE(mod_power);
4264cef2ac4SRay Wu 	if (core_power->num_entities == 0) {
4274cef2ac4SRay Wu 		/* no streams exist in the table yet */
4284cef2ac4SRay Wu 		BREAK_TO_DEBUGGER();
4294cef2ac4SRay Wu 		DC_TRACE_LEVEL_MESSAGE(DAL_TRACE_LEVEL_ERROR,
4304cef2ac4SRay Wu 							WPP_BIT_FLAG_Firmware_PsrState,
4314cef2ac4SRay Wu 							"mod_power: replace_stream: ERROR: num_entities=0 stream=%p",
4324cef2ac4SRay Wu 							current_stream);
4334cef2ac4SRay Wu 		return false;
4344cef2ac4SRay Wu 	}
4354cef2ac4SRay Wu 
4364cef2ac4SRay Wu 	index = map_index_from_stream(core_power, current_stream);
4374cef2ac4SRay Wu 
4384cef2ac4SRay Wu 	if (index >= core_power->num_entities) {
4394cef2ac4SRay Wu 		/* trying to replace a non-existent stream */
4404cef2ac4SRay Wu 		BREAK_TO_DEBUGGER();
4414cef2ac4SRay Wu 		DC_TRACE_LEVEL_MESSAGE(DAL_TRACE_LEVEL_ERROR,
4424cef2ac4SRay Wu 							WPP_BIT_FLAG_Firmware_PsrState,
443d1c2c463SGaghik Khachatrian 							"mod_power: replace_stream: ERROR: index=%u >= num_entities=%u stream=%p",
4444cef2ac4SRay Wu 							index,
4454cef2ac4SRay Wu 							core_power->num_entities,
4464cef2ac4SRay Wu 							current_stream);
4474cef2ac4SRay Wu 		return false;
4484cef2ac4SRay Wu 	}
4494cef2ac4SRay Wu 
4504cef2ac4SRay Wu 	dc_stream_release(core_power->map[index].stream);
4514cef2ac4SRay Wu 	dc_stream_retain(new_stream);
4524cef2ac4SRay Wu 	core_power->map[index].stream = new_stream;
4534cef2ac4SRay Wu 	core_power->map[index].caps = new_caps;
4544cef2ac4SRay Wu 	memset(core_power->map[index].psr_context, 0, sizeof(struct mod_power_psr_context));
4554cef2ac4SRay Wu 
4564cef2ac4SRay Wu 	return true;
4574cef2ac4SRay Wu }
4584cef2ac4SRay Wu bool mod_power_notify_mode_change(struct mod_power *mod_power,
4594cef2ac4SRay Wu 		const struct dc_stream_state *stream,
4604cef2ac4SRay Wu 		bool is_hdr)
4614cef2ac4SRay Wu {
4624cef2ac4SRay Wu 	unsigned int stream_index = 0;
4634cef2ac4SRay Wu 	struct core_power *core_power = NULL;
4644cef2ac4SRay Wu 	struct dc_link *link = NULL;
4654cef2ac4SRay Wu 	struct dc *dc = NULL;
4664cef2ac4SRay Wu 	unsigned int panel_inst = 0;
467*7d447370SGabe Teeger 	uint8_t aux_inst = 0;
4684cef2ac4SRay Wu 
4694cef2ac4SRay Wu 	if ((mod_power == NULL) || (stream == NULL))
4704cef2ac4SRay Wu 		return false;
4714cef2ac4SRay Wu 
4724cef2ac4SRay Wu 	core_power = MOD_POWER_TO_CORE(mod_power);
4734cef2ac4SRay Wu 
4744cef2ac4SRay Wu 	if (core_power->num_entities == 0)
4754cef2ac4SRay Wu 		return false;
4764cef2ac4SRay Wu 
4774cef2ac4SRay Wu 	stream_index = map_index_from_stream(core_power, stream);
4784cef2ac4SRay Wu 
4794cef2ac4SRay Wu 	if (stream_index >= core_power->num_entities)
4804cef2ac4SRay Wu 		return false;
4814cef2ac4SRay Wu 
4824cef2ac4SRay Wu 	dc = core_power->dc;
4834cef2ac4SRay Wu 	link = dc_stream_get_link(stream);
484d7e41c6fSLohita Mudimela 
4854cef2ac4SRay Wu 	if (link != NULL && dc_get_edp_link_panel_inst(dc, link, &panel_inst)) {
486*7d447370SGabe Teeger 		if (link->ctx->dc->config.dp_connector_no_native_i2c && link->no_ddc_pin) {
487*7d447370SGabe Teeger 			aux_inst = (uint8_t)link->aux_hw_inst;
488*7d447370SGabe Teeger 		} else {
4894cef2ac4SRay Wu 			ASSERT(link->ddc->ddc_pin->hw_info.ddc_channel <= 0xFF);
490*7d447370SGabe Teeger 			aux_inst = (uint8_t)link->ddc->ddc_pin->hw_info.ddc_channel;
491*7d447370SGabe Teeger 		}
4924cef2ac4SRay Wu 
493254a47ceSLohita Mudimela 		mod_power_update_backlight_on_mode_change(core_power, link, panel_inst, aux_inst, is_hdr);
4944cef2ac4SRay Wu 
495b11107cbSLohita Mudimela 		/* Handle PSR notification */
496b11107cbSLohita Mudimela 		mod_power_psr_notify_mode_change(mod_power, stream, link, stream_index);
4974cef2ac4SRay Wu 
498d7e41c6fSLohita Mudimela 		/* Handle Replay notification */
499d7e41c6fSLohita Mudimela 		mod_power_replay_notify_mode_change(mod_power, dc, link, stream, stream_index);
5004cef2ac4SRay Wu 	}
5014cef2ac4SRay Wu 
5024cef2ac4SRay Wu 	return true;
5034cef2ac4SRay Wu }
504