// SPDX-License-Identifier: MIT
//
// Copyright 2026 Advanced Micro Devices, Inc.
#include "dm_services.h"
#include "dc.h"
#include "mod_power.h"
#include "core_types.h"
#include "dmcu.h"
#include "abm.h"
#include "power_helpers.h"
#include "dce/dmub_psr.h"
#include "dal_asic_id.h"
#include "link_service.h"
#include <linux/math.h>
#define DC_TRACE_LEVEL_MESSAGE(...) /* do nothing */
#define DC_TRACE_LEVEL_MESSAGEP(...) /* do nothing */
#include "power_helpers.h"
#include "dc/inc/hw/dmcu.h"
#include "dc/inc/hw/abm.h"
#include "dc.h"
#include "core_types.h"
#include "dmub_cmd.h"
#define MOD_POWER_TO_CORE(mod_power)\
container_of(mod_power, struct core_power, mod_public)
#define LOW_REFRESH_RATE_DURATION_US_UPPER_BOUND 25000
static bool mod_power_set_replay_active(struct dc_stream_state *stream,
bool replay_active,
bool wait,
bool force_static)
{
uint64_t state;
unsigned int retry_count;
const unsigned int max_retry = 1000;
struct dc_link *link = NULL;
if (!stream)
return false;
link = dc_stream_get_link(stream);
if (!link)
return false;
if (!dc_link_set_replay_allow_active(link, &replay_active, false, force_static, NULL))
return false;
if (wait == true) {
for (retry_count = 0; retry_count <= max_retry; retry_count++) {
dc_link_get_replay_state(link, &state);
if (replay_active) {
if (state != REPLAY_STATE_0 &&
(!force_static || state == REPLAY_STATE_3))
break;
} else {
if (state == REPLAY_STATE_0)
break;
}
udelay(500);
}
/* assert if max retry hit */
if (retry_count >= max_retry)
ASSERT(0);
} else {
/* To-do: Add trace log */
}
return true;
}
static unsigned int mod_power_replay_setup_power_opt(struct dc_link *link,
unsigned int active_replay_events, bool is_ultra_sleep_mode)
{
unsigned int power_opt = 0;
if (is_ultra_sleep_mode) {
/* Static Screen */
power_opt |= (replay_power_opt_smu_opt_static_screen | replay_power_opt_z10_static_screen);
} else if (active_replay_events & replay_event_test_harness_ultra_sleep) {
power_opt |= replay_power_opt_z10_static_screen;
}
/* replay_power_opt_flag is a configuration parameter into the module that determines
* which optimizations to enable during replay
*/
power_opt &= link->replay_settings.config.replay_power_opt_supported;
return power_opt;
}
static bool mod_power_replay_set_power_opt(struct mod_power *mod_power,
struct dc_stream_state *stream,
unsigned int active_replay_events,
bool is_ultra_sleep_mode)
{
(void)mod_power;
struct dc_link *link = NULL;
unsigned int power_opt = 0;
if (!stream)
return false;
link = dc_stream_get_link(stream);
if (!link || !link->replay_settings.replay_feature_enabled)
return false;
power_opt = mod_power_replay_setup_power_opt(link, active_replay_events, is_ultra_sleep_mode);
if (!dc_link_set_replay_allow_active(link, NULL, false, false, &power_opt))
return false;
return true;
}
bool mod_power_get_replay_event(struct mod_power *mod_power,
struct dc_stream_state *stream,
unsigned int *active_replay_events)
{
struct core_power *core_power = NULL;
unsigned int stream_index = 0;
if (mod_power == NULL)
return false;
core_power = MOD_POWER_TO_CORE(mod_power);
if (core_power->num_entities == 0)
return false;
stream_index = map_index_from_stream(core_power, stream);
*active_replay_events = core_power->map[stream_index].replay_events;
return true;
}
static bool mod_power_update_replay_active_status(unsigned int active_replay_events,
struct dc_link *link, uint32_t *coasting_vtotal, bool *is_full_screen_video,
bool *is_ultra_sleep_mode, uint16_t *frame_skip_number, bool *is_video_playback)
{
if (!link || !coasting_vtotal || !is_full_screen_video || !is_video_playback)
return false;
// Check coasting_vtotal_table has been updated.
if (!link->replay_settings.coasting_vtotal_table[PR_COASTING_TYPE_STATIC]
|| !link->replay_settings.coasting_vtotal_table[PR_COASTING_TYPE_NOM])
return false;
unsigned int replay_enable_option =
link->replay_settings.config.replay_enable_option;
/* TODO: To support test harness and DDS event */
*coasting_vtotal = link->replay_settings.coasting_vtotal_table[PR_COASTING_TYPE_NOM];
ASSERT(link->replay_settings.frame_skip_number_table[PR_COASTING_TYPE_NOM] <= 0xFFFF);
*frame_skip_number = (uint1