root/drivers/gpu/drm/amd/display/modules/power/power_replay.c
// 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 = (uint16_t)link->replay_settings.frame_skip_number_table[PR_COASTING_TYPE_NOM];

        link->replay_settings.config.replay_timing_sync_supported = false;

        *is_full_screen_video = false;

        *is_ultra_sleep_mode = false;

        *is_video_playback = false;

        /* DSAT test scenario */
        if (active_replay_events & replay_event_test_harness_mode) {
                if (link->replay_settings.coasting_vtotal_table[PR_COASTING_TYPE_TEST_HARNESS])
                        *coasting_vtotal =
                                link->replay_settings.coasting_vtotal_table[PR_COASTING_TYPE_TEST_HARNESS];
                if (link->replay_settings.frame_skip_number_table[PR_COASTING_TYPE_TEST_HARNESS]) {
                        ASSERT(link->replay_settings.frame_skip_number_table[PR_COASTING_TYPE_TEST_HARNESS] <= 0xFFFF);
                        *frame_skip_number =
                                (uint16_t)link->replay_settings.frame_skip_number_table[PR_COASTING_TYPE_TEST_HARNESS];
                }

                /* During the ultra sleep mode testing, disable the timing sync in short vblank mode */
                if (active_replay_events & (replay_event_test_harness_enable_replay)) {
                        if ((active_replay_events & replay_event_test_harness_ultra_sleep) &&
                                  !link->replay_settings.config.replay_support_fast_resync_in_ultra_sleep_mode)
                                link->replay_settings.config.replay_timing_sync_supported = false;
                        return true;
                } else
                        return false;
        } else if (active_replay_events & (replay_event_test_harness_enable_replay)) {
                if (link->replay_settings.coasting_vtotal_table[PR_COASTING_TYPE_TEST_HARNESS])
                        *coasting_vtotal = link->replay_settings.coasting_vtotal_table[PR_COASTING_TYPE_TEST_HARNESS];
                if (link->replay_settings.frame_skip_number_table[PR_COASTING_TYPE_TEST_HARNESS]) {
                        uint32_t frame_skip_val =
                                link->replay_settings.frame_skip_number_table[PR_COASTING_TYPE_TEST_HARNESS];

                        ASSERT(frame_skip_val <= 0xFFFF);
                        *frame_skip_number = (uint16_t)frame_skip_val;
                }

                /* During the ultra sleep mode testing, disable the timing sync in short vblank mode */
                if ((active_replay_events & replay_event_test_harness_ultra_sleep) &&
                          !link->replay_settings.config.replay_support_fast_resync_in_ultra_sleep_mode) {
                        link->replay_settings.config.replay_timing_sync_supported = false;
                }
                return true;
        } else if (active_replay_events &
                        (replay_event_test_harness_disable_replay | replay_event_os_request_disable)) {
                // set last set coasting vtotal
                if (link->replay_settings.coasting_vtotal_table[PR_COASTING_TYPE_TEST_HARNESS])
                        *coasting_vtotal = link->replay_settings.coasting_vtotal_table[PR_COASTING_TYPE_TEST_HARNESS];
                if (link->replay_settings.frame_skip_number_table[PR_COASTING_TYPE_TEST_HARNESS]) {
                        uint32_t frame_skip_val =
                                link->replay_settings.frame_skip_number_table[PR_COASTING_TYPE_TEST_HARNESS];

                        ASSERT(frame_skip_val <= 0xFFFF);
                        *frame_skip_number = (uint16_t)frame_skip_val;
                }
                return false;
        }

        /* Inactive conditions */
        if (active_replay_events & (replay_event_edp_panel_off_disable_psr |
                        replay_event_hw_programming |
                        replay_event_vrr |
                        replay_event_immediate_flip |
                        replay_event_prepare_vtotal |
                        replay_event_vrr_transition |
                        replay_event_pause |
                        replay_event_disable_replay_while_DPMS |
                        replay_event_sleep_resume |
                        replay_event_disable_in_AC |
                        replay_event_disable_replay_while_detect_display |
                        replay_event_infopacket |
                        replay_event_crc_window_active))
                return false;

        // Full screen scenario
        if (active_replay_events & replay_event_full_screen) {
                if (!(replay_enable_option & pr_enable_option_full_screen))
                        return false;
        }

        /* Full screen video scenario */
        if (active_replay_events & replay_event_big_screen_video) {

                link->replay_settings.config.replay_timing_sync_supported = false;

                if (replay_enable_option & pr_enable_option_full_screen_video_coasting) {
                        unsigned int fsn_vid =
                                link->replay_settings.frame_skip_number_table[PR_COASTING_TYPE_FULL_SCREEN_VIDEO];

                        *coasting_vtotal =
                                link->replay_settings.coasting_vtotal_table[PR_COASTING_TYPE_FULL_SCREEN_VIDEO];
                        ASSERT(fsn_vid <= 0xFFFF);
                        *frame_skip_number = (uint16_t)fsn_vid;
                }

                *is_video_playback = true;

                if ((replay_enable_option & pr_enable_option_full_screen_video) &&
                        (replay_enable_option & pr_enable_option_full_screen_video_coasting)) {
                        *is_full_screen_video = true;
                        return true;
                } else
                        return false;
        }

        /* MPO video scenario
         * Some of the cases may contain a full screen UI layer in MPO video scenario which is
         * not the expected case to enable Replay.
         */
        if ((active_replay_events & replay_event_mpo_video_selective_update) &&
                !(active_replay_events & replay_event_full_screen)) {

                link->replay_settings.config.replay_timing_sync_supported = false;

                if (replay_enable_option & pr_enable_option_mpo_video_coasting) {
                        *coasting_vtotal = link->replay_settings.coasting_vtotal_table[PR_COASTING_TYPE_NOM];
                        {
                                uint32_t frame_skip_val =
                                        link->replay_settings.frame_skip_number_table[PR_COASTING_TYPE_NOM];

                                ASSERT(frame_skip_val <= 0xFFFF);
                                *frame_skip_number = (uint16_t)frame_skip_val;
                        }
                }

                *is_video_playback = true;

                if (replay_enable_option & pr_enable_option_mpo_video)
                        return true;
                else
                        return false;
        }

        /* Static screen scenario */
        if (!(active_replay_events & replay_event_vsync)) {

                if (replay_enable_option & pr_enable_option_static_screen_coasting) {
                        // Do not adjust eDP refresh rate if static screen + normal sleep mode
                        if ((!(link->replay_settings.config.replay_power_opt_supported &
                                replay_power_opt_z10_static_screen)) ||
                                (active_replay_events & replay_event_cursor_updating)) {
                                // normal sleep mode
                                *coasting_vtotal =
                                        link->replay_settings.coasting_vtotal_table[PR_COASTING_TYPE_NOM];
                                {
                                        uint32_t frame_skip_val =
                                                link->replay_settings.frame_skip_number_table[PR_COASTING_TYPE_NOM];

                                        ASSERT(frame_skip_val <= 0xFFFF);
                                        *frame_skip_number = (uint16_t)frame_skip_val;
                                }
                        } else {
                                // ultra sleep mode
                                *coasting_vtotal =
                                        link->replay_settings.coasting_vtotal_table[PR_COASTING_TYPE_STATIC];
                                {
                                        uint32_t frame_skip_val =
                                                link->replay_settings.frame_skip_number_table[PR_COASTING_TYPE_STATIC];

                                        ASSERT(frame_skip_val <= 0xFFFF);
                                        *frame_skip_number = (uint16_t)frame_skip_val;
                                }
                                *is_ultra_sleep_mode = true;
                        }
                }

                if (replay_enable_option & pr_enable_option_static_screen) {
                        if (!link->replay_settings.config.replay_support_fast_resync_in_ultra_sleep_mode)
                                link->replay_settings.config.replay_timing_sync_supported = false;
                        return true;
                } else
                        return false;
        }

        /* General UI scenario */
        if (active_replay_events & replay_event_general_ui) {
                if (replay_enable_option & pr_enable_option_general_ui)
                        return true;
                else
                        return false;
        }

        return false;
}

bool mod_power_replay_set_coasting_vtotal(struct mod_power *mod_power,
        const struct dc_stream_state *stream,
        uint32_t coasting_vtotal,
        uint16_t frame_skip_number)
{
        struct core_power *core_power = NULL;
        struct dc_link *link = NULL;

        if (!stream)
                return false;

        link = dc_stream_get_link(stream);
        if (!link || !link->replay_settings.replay_feature_enabled)
                return false;

        if (mod_power == NULL)
                return false;

        core_power = MOD_POWER_TO_CORE(mod_power);

        if (core_power->num_entities == 0)
                return false;

        return link->dc->link_srv->edp_set_coasting_vtotal(link, coasting_vtotal, frame_skip_number);
}

void mod_power_replay_set_timing_sync_supported(struct mod_power *mod_power,
        const struct dc_stream_state *stream)
{
        struct core_power *core_power = NULL;
        struct dc_link *link = NULL;
        unsigned int stream_index = 0;
        union dmub_replay_cmd_set cmd_data = { 0 };

        if (!stream || mod_power == NULL)
                return;

        core_power = MOD_POWER_TO_CORE(mod_power);
        if (core_power->num_entities == 0)
                return;

        stream_index = map_index_from_stream(core_power, stream);
        if (stream_index > core_power->num_entities) //invalid index
                return;

        link = dc_stream_get_link(stream);
        if (!link || !link->replay_settings.replay_feature_enabled)
                return;

        cmd_data.sync_data.timing_sync_supported = link->replay_settings.config.replay_timing_sync_supported;

        link->dc->link_srv->edp_send_replay_cmd(link, Replay_Set_Timing_Sync_Supported,
                &cmd_data);
}

void mod_power_replay_disabled_adaptive_sync_sdp(struct mod_power *mod_power,
        const struct dc_stream_state *stream, bool force_disabled)
{
        struct core_power *core_power = NULL;
        struct dc_link *link = NULL;
        unsigned int stream_index = 0;
        union dmub_replay_cmd_set cmd_data = { 0 };

        if (!stream || mod_power == NULL)
                return;

        core_power = MOD_POWER_TO_CORE(mod_power);
        if (core_power->num_entities == 0)
                return;

        stream_index = map_index_from_stream(core_power, stream);
        if (stream_index > core_power->num_entities) //invalid index
                return;

        link = dc_stream_get_link(stream);
        if (!link || !link->replay_settings.replay_feature_enabled)
                return;

        cmd_data.disabled_adaptive_sync_sdp_data.force_disabled = force_disabled;

        link->dc->link_srv->edp_send_replay_cmd(link, Replay_Disabled_Adaptive_Sync_SDP,
                &cmd_data);
}

static void mod_power_replay_set_general_cmd(struct mod_power *mod_power,
        const struct dc_stream_state *stream,
        const enum dmub_cmd_replay_general_subtype general_cmd_type,
        const uint32_t param1, const uint32_t param2)
{
        struct core_power *core_power = NULL;
        struct dc_link *link = NULL;
        unsigned int stream_index = 0;
        union dmub_replay_cmd_set cmd_data = { 0 };

        if (!stream || mod_power == NULL)
                return;

        core_power = MOD_POWER_TO_CORE(mod_power);
        if (core_power->num_entities == 0)
                return;

        stream_index = map_index_from_stream(core_power, stream);
        if (stream_index > core_power->num_entities) //invalid index
                return;

        link = dc_stream_get_link(stream);
        if (!link || !link->replay_settings.replay_feature_enabled)
                return;

        cmd_data.set_general_cmd_data.subtype = general_cmd_type;
        cmd_data.set_general_cmd_data.param1 = param1;
        cmd_data.set_general_cmd_data.param2 = param2;
        link->dc->link_srv->edp_send_replay_cmd(link, Replay_Set_General_Cmd,
                &cmd_data);
}

void mod_power_replay_disabled_desync_error_detection(struct mod_power *mod_power,
        const struct dc_stream_state *stream,  bool force_disabled)
{
        mod_power_replay_set_general_cmd(mod_power, stream,
                        REPLAY_GENERAL_CMD_DISABLED_DESYNC_ERROR_DETECTION,
                        force_disabled, 0);
}

static void mod_power_replay_set_pseudo_vtotal(struct mod_power *mod_power,
        const struct dc_stream_state *stream, uint16_t vtotal)
{
        struct core_power *core_power = NULL;
        struct dc_link *link = NULL;
        unsigned int stream_index = 0;
        union dmub_replay_cmd_set cmd_data = { 0 };

        if (!stream || mod_power == NULL)
                return;

        core_power = MOD_POWER_TO_CORE(mod_power);
        if (core_power->num_entities == 0)
                return;

        stream_index = map_index_from_stream(core_power, stream);
        if (stream_index > core_power->num_entities) //invalid index
                return;

        link = dc_stream_get_link(stream);
        if (!link || !link->replay_settings.replay_feature_enabled)
                return;

        cmd_data.pseudo_vtotal_data.vtotal = vtotal;

        if (link->replay_settings.last_pseudo_vtotal != vtotal) {
                link->replay_settings.last_pseudo_vtotal = vtotal;
                link->dc->link_srv->edp_send_replay_cmd(link, Replay_Set_Pseudo_VTotal, &cmd_data);
        }
}

static void mod_power_update_error_status(struct mod_power *mod_power,
        const struct dc_stream_state *stream)
{
        struct dc_link *link = NULL;
        union replay_debug_flags *pDebug = NULL;

        if (mod_power == NULL || stream == NULL)
                return;

        link = dc_stream_get_link(stream);

        if (!link)
                return;

        pDebug = (union replay_debug_flags *)&link->replay_settings.config.debug_flags;

        if (pDebug->bitfields.enable_visual_confirm_debug == 0)
                return;

        mod_power_replay_set_general_cmd(mod_power, stream,
                REPLAY_GENERAL_CMD_UPDATE_ERROR_STATUS,
                link->replay_settings.config.replay_error_status.raw, 0);
}

void mod_power_set_low_rr_activate(struct mod_power *mod_power,
        const struct dc_stream_state *stream, bool low_rr_supported)
{
        struct dc_link *link = NULL;

        if (mod_power == NULL || stream == NULL)
                return;

        link = dc_stream_get_link(stream);

        if (!link)
                return;

        mod_power_replay_set_general_cmd(mod_power, stream,
                REPLAY_GENERAL_CMD_SET_LOW_RR_ACTIVATE,
                low_rr_supported, 0);
}

void mod_power_set_video_conferencing_activate(struct mod_power *mod_power,
        const struct dc_stream_state *stream, bool video_conferencing_activate)
{
        struct dc_link *link = NULL;

        if (mod_power == NULL || stream == NULL)
                return;

        link = dc_stream_get_link(stream);
        if (!link || !link->replay_settings.replay_feature_enabled)
                return;

        mod_power_replay_set_general_cmd(mod_power, stream,
                REPLAY_GENERAL_CMD_VIDEO_CONFERENCING,
                video_conferencing_activate, 0);
}

void mod_power_set_coasting_vtotal_without_frame_update(struct mod_power *mod_power,
        const struct dc_stream_state *stream, uint32_t coasting_vtotal)
{
        struct dc_link *link = NULL;

        if (mod_power == NULL || stream == NULL)
                return;

        link = dc_stream_get_link(stream);
        if (!link || !link->replay_settings.replay_feature_enabled)
                return;

        mod_power_replay_set_general_cmd(mod_power, stream,
                REPLAY_GENERAL_CMD_SET_COASTING_VTOTAL_WITHOUT_FRAME_UPDATE,
                coasting_vtotal, 0);
}

void mod_power_set_replay_continuously_resync(struct mod_power *mod_power,
        const struct dc_stream_state *stream, bool enable)
{
        struct dc_link *link = NULL;

        if (mod_power == NULL || stream == NULL)
                return;

        link = dc_stream_get_link(stream);
        if (!link || !link->replay_settings.replay_feature_enabled)
                return;

        mod_power_replay_set_general_cmd(mod_power, stream,
                REPLAY_GENERAL_CMD_SET_CONTINUOUSLY_RESYNC,
                enable, 0);
}

void mod_power_set_live_capture_with_cvt_activate(struct mod_power *mod_power,
        const struct dc_stream_state *stream, bool live_capture_with_cvt_activate)
{
        struct dc_link *link = NULL;

        if (mod_power == NULL || stream == NULL)
                return;

        link = dc_stream_get_link(stream);
        if (!link || !link->replay_settings.replay_feature_enabled)
                return;

        // Check if LIVE_CAPTURE_WITH_CVT bit is enabled in replay optimization config
        if (!link->replay_settings.config.replay_optimization.bits.LIVE_CAPTURE_WITH_CVT)
                return;

        if (link->replay_settings.config.live_capture_with_cvt_activated != live_capture_with_cvt_activate) {
                link->replay_settings.config.live_capture_with_cvt_activated = live_capture_with_cvt_activate;
                mod_power_replay_set_general_cmd(mod_power, stream,
                        REPLAY_GENERAL_CMD_LIVE_CAPTURE_WITH_CVT,
                        live_capture_with_cvt_activate, 0);
        }
}

bool mod_power_set_replay_event(struct mod_power *mod_power,
        struct dc_stream_state *stream, bool set_event,
        enum replay_event event, bool wait_for_disable)
{
        struct core_power *core_power = NULL;
        struct dc_link *link = NULL;
        unsigned int stream_index = 0;
        unsigned int active_replay_events = 0;
        bool replay_active_request = false;
        bool force_static = false;
        uint32_t coasting_vtotal = 0;
        bool current_timing_sync_status = false;
        bool is_full_screen_video = false;
        bool is_ultra_sleep_mode = false;
        unsigned int sink_duration_us = 0;
        bool low_rr_active = false;
        uint16_t frame_skip_number = 0;
        bool is_video_playback = false;

        if (!stream)
                return false;

        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);

        if (set_event)
                core_power->map[stream_index].replay_events |= event;
        else
                core_power->map[stream_index].replay_events &= ~event;

        link = dc_stream_get_link(stream);
        if (!link || !link->replay_settings.replay_feature_enabled)
                return false;

        if ((core_power->map[stream_index].replay_events & replay_event_disable_replay_while_switching_mux) != 0)
                return false;

        if ((core_power->map[stream_index].replay_events & replay_event_os_override_hold) != 0)
                return false;

        active_replay_events = core_power->map[stream_index].replay_events;

        current_timing_sync_status =
                link->replay_settings.config.replay_timing_sync_supported;

        replay_active_request = mod_power_update_replay_active_status(active_replay_events,
                link, &coasting_vtotal, &is_full_screen_video, &is_ultra_sleep_mode, &frame_skip_number, &is_video_playback);

        if (is_full_screen_video)
                mod_power_replay_set_pseudo_vtotal(mod_power, stream,
                        link->replay_settings.low_rr_full_screen_video_pseudo_vtotal);
        else
                mod_power_replay_set_pseudo_vtotal(mod_power, stream, 0);

        //If timing_sync_status change, then re-enabled set timing_sync_supported value and re-enabled replay
        if (current_timing_sync_status != link->replay_settings.config.replay_timing_sync_supported)
                mod_power_replay_set_timing_sync_supported(mod_power, stream);

        if (link->replay_settings.config.low_rr_supported) {
                sink_duration_us =
                        (unsigned int)(div_u64(((unsigned long long)(coasting_vtotal)
                                * 10000) * stream->timing.h_total,
                                        stream->timing.pix_clk_100hz));
                low_rr_active = sink_duration_us < LOW_REFRESH_RATE_DURATION_US_UPPER_BOUND ? false : true;
                if (low_rr_active != link->replay_settings.config.low_rr_activated) {
                        mod_power_set_low_rr_activate(mod_power, stream, low_rr_active);
                        link->replay_settings.config.low_rr_activated = low_rr_active;
                }
        }

        // The function return fail when
        // 1. DMUB function is not support (for backward compatible).
        // 2. active_replay_events or coasting_vtotal is not updated in the same time
        if (!mod_power_replay_set_power_opt_and_coasting_vtotal(mod_power,
                stream, active_replay_events, coasting_vtotal, is_ultra_sleep_mode, frame_skip_number)) {
                if (!mod_power_replay_set_power_opt(mod_power, stream, active_replay_events, is_ultra_sleep_mode))
                        return false;

                if (!mod_power_replay_set_coasting_vtotal(mod_power, stream, coasting_vtotal, frame_skip_number))
                        return false;
        }

        mod_power_set_live_capture_with_cvt_activate(mod_power, stream, is_video_playback);

        mod_power_update_error_status(mod_power, stream);

        // If Replay is going to be enable (No matter is disable -> enable or enable -> enable), we don't need to wait.
        // If Replay is going to be disable
        //     if disable -> disable
        //         -> Replay DMUB state should be state 0.
        //            So no matter wait_for_disable is true or not, it should makes no difference.
        //     if enable -> disable -> We should wait if wait_for_disable is true.
        if (replay_active_request)
                wait_for_disable = false;

        if (!mod_power_set_replay_active(stream, replay_active_request, wait_for_disable, force_static))
                return false;

        return true;
}

bool mod_power_get_replay_active_status(const struct dc_stream_state *stream,
        bool *replay_active)
{
        const struct dc_link *link = NULL;

        if (!stream)
                return false;

        link = dc_stream_get_link(stream);
        *replay_active = link->replay_settings.replay_allow_active;

        return true;
}

void mod_power_replay_residency(const struct dc_stream_state *stream,
        unsigned int *residency, const bool is_start, const bool is_alpm)
{
        const struct dc_link *link = NULL;
        enum pr_residency_mode mode;

        if (!stream)
                return;

        link = dc_stream_get_link(stream);

        if (is_alpm)
                mode = PR_RESIDENCY_MODE_ALPM;
        else
                mode = PR_RESIDENCY_MODE_PHY;

        if (link && link->dc && link->dc->link_srv)
                link->dc->link_srv->edp_replay_residency(link, residency, is_start, mode);
}

bool mod_power_replay_set_power_opt_and_coasting_vtotal(struct mod_power *mod_power,
        const struct dc_stream_state *stream, unsigned int active_replay_events, uint32_t coasting_vtotal,
        bool is_ultra_sleep_mode, uint16_t frame_skip_number)
{
        struct core_power *core_power = NULL;
        struct dc_link *link = NULL;
        unsigned int power_opt = 0;

        if (!stream)
                return false;

        if (mod_power == NULL)
                return false;

        core_power = MOD_POWER_TO_CORE(mod_power);

        if (core_power->num_entities == 0)
                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);

        return link->dc->link_srv->edp_set_replay_power_opt_and_coasting_vtotal(link, &power_opt, coasting_vtotal, frame_skip_number);
}

void mod_power_replay_notify_mode_change(struct mod_power *mod_power,
        struct dc *dc,
        struct dc_link *link,
        const struct dc_stream_state *stream,
        unsigned int stream_index)
{
        struct core_power *core_power = NULL;
        int active_replay_events = 0;

        if (!mod_power || !dc || !link || !stream)
                return;

        core_power = MOD_POWER_TO_CORE(mod_power);
        active_replay_events = core_power->map[stream_index].replay_events;

        link->replay_settings.replay_smu_opt_enable =
                (link->replay_settings.config.replay_smu_opt_supported &&
                mod_power_only_edp(dc->current_state, stream));

        if (active_replay_events & replay_event_os_request_force_ffu) {
                link->replay_settings.config.os_request_force_ffu = true;
        }

        if (dc_is_embedded_signal(stream->signal))
                dc->link_srv->dp_setup_replay(link, stream);
}

void init_replay_config(struct dc_link *link, struct replay_config *pr_config)
{
        link->replay_settings.config = *pr_config;
}

void set_replay_frame_skip_number(struct dc_link *link,
        enum replay_coasting_vtotal_type type,
        uint32_t coasting_vtotal_refresh_rate_uhz,
        uint32_t flicker_free_refresh_rate_uhz,
        bool is_defer)
{
        uint32_t *frame_skip_number_array = NULL;
        uint32_t frame_skip_number = 0;

        if (link == NULL)
                return;

        if (link->replay_settings.config.frame_skip_supported == false)
                return;

        if (flicker_free_refresh_rate_uhz == 0 || coasting_vtotal_refresh_rate_uhz == 0)
                return;

        if (is_defer)
                frame_skip_number_array = link->replay_settings.defer_frame_skip_number_table;
        else
                frame_skip_number_array = link->replay_settings.frame_skip_number_table;

        if (frame_skip_number_array == NULL)
                return;

        frame_skip_number = (coasting_vtotal_refresh_rate_uhz + 500000) / flicker_free_refresh_rate_uhz;

        if (frame_skip_number >= 1)
                frame_skip_number_array[type] = frame_skip_number - 1;
        else
                frame_skip_number_array[type] = 0;
}

void set_replay_defer_update_coasting_vtotal(struct dc_link *link,
        enum replay_coasting_vtotal_type type,
        uint32_t vtotal)
{
        link->replay_settings.defer_update_coasting_vtotal_table[type] = vtotal;
}

void update_replay_coasting_vtotal_from_defer(struct dc_link *link,
        enum replay_coasting_vtotal_type type)
{
        link->replay_settings.coasting_vtotal_table[type] =
                link->replay_settings.defer_update_coasting_vtotal_table[type];
        link->replay_settings.frame_skip_number_table[type] =
                link->replay_settings.defer_frame_skip_number_table[type];
}

void set_replay_coasting_vtotal(struct dc_link *link,
        enum replay_coasting_vtotal_type type,
        uint32_t vtotal)
{
        link->replay_settings.coasting_vtotal_table[type] = vtotal;
}

void set_replay_low_rr_full_screen_video_src_vtotal(struct dc_link *link, uint16_t vtotal)
{
        link->replay_settings.low_rr_full_screen_video_pseudo_vtotal = vtotal;
}

void calculate_replay_link_off_frame_count(struct dc_link *link,
        uint16_t vtotal, uint16_t htotal)
{
        uint32_t max_link_off_frame_count = 0;
        uint16_t max_deviation_line = 0,  pixel_deviation_per_line = 0;

        if (!link || link->replay_settings.config.replay_version != DC_FREESYNC_REPLAY)
                return;

        max_deviation_line = link->dpcd_caps.pr_info.max_deviation_line;
        pixel_deviation_per_line = link->dpcd_caps.pr_info.pixel_deviation_per_line;

        if (htotal != 0 && vtotal != 0 && pixel_deviation_per_line != 0)
                max_link_off_frame_count = htotal * max_deviation_line / (pixel_deviation_per_line * vtotal);
        else
                ASSERT(0);

        link->replay_settings.link_off_frame_count = max_link_off_frame_count;
}

void reset_replay_dsync_error_count(struct dc_link *link)
{
        link->replay_settings.replay_desync_error_fail_count = 0;
}