/* * Copyright 2016-2023 Advanced Micro Devices, Inc. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. * * Authors: AMD *
*/
if (refresh_in_uhz <= stream->timing.min_refresh_in_uhz) { /* When the target refresh rate is the minimum panel refresh rate, * round down the vtotal value to avoid stretching vblank over * panel's vtotal boundary.
*/
v_total = div64_u64(div64_u64(((unsignedlonglong)(
frame_duration_in_ns) * (stream->timing.pix_clk_100hz / 10)),
stream->timing.h_total), 1000000);
} elseif (refresh_in_uhz >= stream->timing.max_refresh_in_uhz) { /* When the target refresh rate is the maximum panel refresh rate * round up the vtotal value to prevent off-by-one error causing * v_total_min to be below the panel's lower bound
*/
v_total = div64_u64(div64_u64(((unsignedlonglong)(
frame_duration_in_ns) * (stream->timing.pix_clk_100hz / 10)),
stream->timing.h_total) + (1000000 - 1), 1000000);
} else {
v_total = div64_u64(div64_u64(((unsignedlonglong)(
frame_duration_in_ns) * (stream->timing.pix_clk_100hz / 10)),
stream->timing.h_total) + 500000, 1000000);
}
/* v_total cannot be less than nominal */ if (v_total < stream->timing.v_total) {
ASSERT(v_total < stream->timing.v_total);
v_total = stream->timing.v_total;
}
/* v_total cannot be less than nominal */ if (v_total < stream->timing.v_total) {
ASSERT(v_total < stream->timing.v_total);
v_total = stream->timing.v_total;
}
/* Calculate ratio between new and current frame duration with 3 digit */ unsignedint frame_duration_ratio = div64_u64(1000000,
(1000 + div64_u64(((unsignedlonglong)(
STATIC_SCREEN_RAMP_DELTA_REFRESH_RATE_PER_FRAME) *
current_duration_in_us),
1000000)));
/* Calculate delta between new and current frame duration in us */ unsignedint frame_duration_delta = div64_u64(((unsignedlonglong)(
current_duration_in_us) *
(1000 - frame_duration_ratio)), 1000);
/* Adjust frame duration delta based on ratio between current and * standard frame duration (frame duration at 60 Hz refresh rate).
*/ unsignedint ramp_rate_interpolated = div64_u64(((unsignedlonglong)(
frame_duration_delta) * current_duration_in_us), 16666);
/* Going to a higher refresh rate (lower frame duration) */ if (ramp_direction_is_up) { /* Reduce frame duration */
current_duration_in_us -= ramp_rate_interpolated;
/* Adjust for frame duration below min */ if (current_duration_in_us <= target_duration_in_us) {
in_out_vrr->fixed.ramping_active = false;
in_out_vrr->fixed.ramping_done = true;
current_duration_in_us =
calc_duration_in_us_from_refresh_in_uhz(
in_out_vrr->fixed.target_refresh_in_uhz);
} /* Going to a lower refresh rate (larger frame duration) */
} else { /* Increase frame duration */
current_duration_in_us += ramp_rate_interpolated;
/* Adjust for frame duration above max */ if (current_duration_in_us >= target_duration_in_us) {
in_out_vrr->fixed.ramping_active = false;
in_out_vrr->fixed.ramping_done = true;
current_duration_in_us =
calc_duration_in_us_from_refresh_in_uhz(
in_out_vrr->fixed.target_refresh_in_uhz);
}
}
/* Program BTR */ if ((last_render_time_in_us + in_out_vrr->btr.margin_in_us / 2) < max_render_time_in_us) { /* Exit Below the Range */ if (in_out_vrr->btr.btr_active) {
in_out_vrr->btr.frame_counter = 0;
in_out_vrr->btr.btr_active = false;
}
} elseif (last_render_time_in_us > (max_render_time_in_us + in_out_vrr->btr.margin_in_us / 2)) { /* Enter Below the Range */ if (!in_out_vrr->btr.btr_active)
in_out_vrr->btr.btr_active = true;
}
/* BTR set to "not active" so disengage */ if (!in_out_vrr->btr.btr_active) {
in_out_vrr->btr.inserted_duration_in_us = 0;
in_out_vrr->btr.frames_to_insert = 0;
in_out_vrr->btr.frame_counter = 0;
/* Restore FreeSync */
in_out_vrr->adjust.v_total_min =
mod_freesync_calc_v_total_from_refresh(stream,
in_out_vrr->max_refresh_in_uhz);
in_out_vrr->adjust.v_total_max =
mod_freesync_calc_v_total_from_refresh(stream,
in_out_vrr->min_refresh_in_uhz); /* BTR set to "active" so engage */
} else {
/* Calculate number of midPoint frames that could fit within * the render time interval - take ceil of this value
*/
mid_point_frames_ceil = (last_render_time_in_us +
in_out_vrr->btr.mid_point_in_us - 1) /
in_out_vrr->btr.mid_point_in_us;
/* Calculate number of midPoint frames that could fit within * the render time interval - take floor of this value
*/
mid_point_frames_floor = last_render_time_in_us /
in_out_vrr->btr.mid_point_in_us;
/* Choose number of frames to insert based on how close it * can get to the mid point of the variable range. * - Delta for CEIL: delta_from_mid_point_in_us_1 * - Delta for FLOOR: delta_from_mid_point_in_us_2
*/ if (mid_point_frames_ceil &&
(last_render_time_in_us / mid_point_frames_ceil) <
in_out_vrr->min_duration_in_us) { /* Check for out of range. * If using CEIL produces a value that is out of range, * then we are forced to use FLOOR.
*/
frames_to_insert = mid_point_frames_floor;
} elseif (mid_point_frames_floor < 2) { /* Check if FLOOR would result in non-LFC. In this case * choose to use CEIL
*/
frames_to_insert = mid_point_frames_ceil;
} elseif (delta_from_mid_point_in_us_1 < delta_from_mid_point_in_us_2) { /* If choosing CEIL results in a frame duration that is * closer to the mid point of the range. * Choose CEIL
*/
frames_to_insert = mid_point_frames_ceil;
} else { /* If choosing FLOOR results in a frame duration that is * closer to the mid point of the range. * Choose FLOOR
*/
frames_to_insert = mid_point_frames_floor;
}
/* Prefer current frame multiplier when BTR is enabled unless it drifts * too far from the midpoint
*/ if (delta_from_mid_point_in_us_1 < delta_from_mid_point_in_us_2) {
delta_from_mid_point_delta_in_us = delta_from_mid_point_in_us_2 -
delta_from_mid_point_in_us_1;
} else {
delta_from_mid_point_delta_in_us = delta_from_mid_point_in_us_1 -
delta_from_mid_point_in_us_2;
} if (in_out_vrr->btr.frames_to_insert != 0 &&
delta_from_mid_point_delta_in_us < BTR_DRIFT_MARGIN) { if (((last_render_time_in_us / in_out_vrr->btr.frames_to_insert) <
max_render_time_in_us) &&
((last_render_time_in_us / in_out_vrr->btr.frames_to_insert) >
in_out_vrr->min_duration_in_us))
frames_to_insert = in_out_vrr->btr.frames_to_insert;
}
/* Either we've calculated the number of frames to insert, * or we need to insert min duration frames
*/ if (frames_to_insert &&
(last_render_time_in_us / frames_to_insert) <
in_out_vrr->min_duration_in_us){
frames_to_insert -= (frames_to_insert > 1) ?
1 : 0;
}
if (frames_to_insert > 0)
inserted_frame_duration_in_us = last_render_time_in_us /
frames_to_insert;
if (inserted_frame_duration_in_us < in_out_vrr->min_duration_in_us)
inserted_frame_duration_in_us = in_out_vrr->min_duration_in_us;
/* Determine conditions for stopping workaround */ if (in_vrr->flip_interval.flip_interval_workaround_active &&
in_vrr->flip_interval.vsyncs_between_flip < VSYNCS_BETWEEN_FLIP_THRESHOLD &&
in_vrr->flip_interval.vsync_to_flip_in_us > FREESYNC_VSYNC_TO_FLIP_DELTA_IN_US) {
in_vrr->flip_interval.flip_interval_detect_counter = 0;
in_vrr->flip_interval.program_flip_interval_workaround = true;
in_vrr->flip_interval.flip_interval_workaround_active = false;
} else { /* Determine conditions for starting workaround */ if (in_vrr->flip_interval.vsyncs_between_flip >= VSYNCS_BETWEEN_FLIP_THRESHOLD &&
in_vrr->flip_interval.vsync_to_flip_in_us < FREESYNC_VSYNC_TO_FLIP_DELTA_IN_US) { /* Increase flip interval counter we have 2 vsyncs between flips and * vsync to flip interval is less than 500us
*/
in_vrr->flip_interval.flip_interval_detect_counter++; if (in_vrr->flip_interval.flip_interval_detect_counter > FREESYNC_CONSEC_FLIP_AFTER_VSYNC) { /* Start workaround if we detect 5 consecutive instances of the above case */
in_vrr->flip_interval.program_flip_interval_workaround = true;
in_vrr->flip_interval.flip_interval_workaround_active = true;
}
} else { /* Reset the flip interval counter if we condition is no longer met */
in_vrr->flip_interval.flip_interval_detect_counter = 0;
}
}
// For v1 & 2 infoframes program nominal if non-fs mode, otherwise full range /* PB7 = FreeSync Minimum refresh rate (Hz) */ if (vrr->state == VRR_STATE_ACTIVE_VARIABLE ||
vrr->state == VRR_STATE_ACTIVE_FIXED) {
infopacket->sb[7] = (unsignedchar)((vrr->min_refresh_in_uhz + 500000) / 1000000);
} else {
infopacket->sb[7] = (unsignedchar)((vrr->max_refresh_in_uhz + 500000) / 1000000);
}
/* PB8 = FreeSync Maximum refresh rate (Hz) * Note: We should never go above the field rate of the mode timing set.
*/
infopacket->sb[8] = (unsignedchar)((vrr->max_refresh_in_uhz + 500000) / 1000000);
}
void mod_freesync_build_vrr_infopacket(struct mod_freesync *mod_freesync, conststruct dc_stream_state *stream, conststruct mod_vrr_params *vrr, enum vrr_packet_type packet_type, enum color_transfer_func app_tf, struct dc_info_packet *infopacket, bool pack_sdp_v1_3)
{ /* SPD info packet for FreeSync * VTEM info packet for HdmiVRR * Check if Freesync is supported. Return if false. If true, * set the corresponding bit in the info packet
*/ if (!vrr->send_info_frame) return;
switch (packet_type) { case PACKET_TYPE_FS_V3:
build_vrr_infopacket_v3(stream->signal, vrr, app_tf, infopacket, stream->freesync_on_desktop); break; case PACKET_TYPE_FS_V2:
build_vrr_infopacket_v2(stream->signal, vrr, app_tf, infopacket, stream->freesync_on_desktop); break; case PACKET_TYPE_VRR: case PACKET_TYPE_FS_V1: default:
build_vrr_infopacket_v1(stream->signal, vrr, infopacket, stream->freesync_on_desktop);
}
/* Calculate nominal field rate for stream */
nominal_field_rate_in_uhz =
mod_freesync_calc_nominal_field_rate(stream);
if (stream->ctx->dc->caps.max_v_total != 0 && stream->timing.h_total != 0) {
min_hardware_refresh_in_uhz = div64_u64((stream->timing.pix_clk_100hz * 100000000ULL),
(stream->timing.h_total * (longlong)calc_max_hardware_v_total(stream)));
} /* Limit minimum refresh rate to what can be supported by hardware */
min_refresh_in_uhz = min_hardware_refresh_in_uhz > in_config->min_refresh_in_uhz ?
min_hardware_refresh_in_uhz : in_config->min_refresh_in_uhz;
max_refresh_in_uhz = in_config->max_refresh_in_uhz;
/* Full range may be larger than current video timing, so cap at nominal */ if (max_refresh_in_uhz > nominal_field_rate_in_uhz)
max_refresh_in_uhz = nominal_field_rate_in_uhz;
/* Full range may be larger than current video timing, so cap at nominal */ if (min_refresh_in_uhz > max_refresh_in_uhz)
min_refresh_in_uhz = max_refresh_in_uhz;
/* If a monitor reports exactly max refresh of 2x of min, enforce it on nominal */
rounded_nominal_in_uhz =
div_u64(nominal_field_rate_in_uhz + 50000, 100000) * 100000; if (in_config->max_refresh_in_uhz == (2 * in_config->min_refresh_in_uhz) &&
in_config->max_refresh_in_uhz == rounded_nominal_in_uhz)
min_refresh_in_uhz = div_u64(nominal_field_rate_in_uhz, 2);
if (!vrr_settings_require_update(core_freesync,
in_config, (unsignedint)min_refresh_in_uhz, (unsignedint)max_refresh_in_uhz,
in_out_vrr)) return;
/* Only execute if in fullscreen mode */ if (in_out_vrr->state == VRR_STATE_ACTIVE_VARIABLE &&
in_out_vrr->btr.btr_active) { /* TODO: pass in flag for Pre-DCE12 ASIC * in order for frame variable duration to take affect, * it needs to be done one VSYNC early, which is at * frameCounter == 1. * For DCE12 and newer updates to V_TOTAL_MIN/MAX * will take affect on current frame
*/ if (in_out_vrr->btr.frames_to_insert ==
in_out_vrr->btr.frame_counter) {
in_out_vrr->adjust.v_total_min =
calc_v_total_from_duration(stream,
in_out_vrr,
in_out_vrr->btr.inserted_duration_in_us);
in_out_vrr->adjust.v_total_max =
in_out_vrr->adjust.v_total_min;
}
if (in_out_vrr->btr.frame_counter > 0)
in_out_vrr->btr.frame_counter--;
/* If in fullscreen freesync mode or in video, do not program * static screen ramp values
*/ if (in_out_vrr->state == VRR_STATE_ACTIVE_VARIABLE)
in_out_vrr->fixed.ramping_active = false;
/* Gradual Static Screen Ramping Logic * Execute if ramp is active and user enabled freesync static screen
*/ if (in_out_vrr->state == VRR_STATE_ACTIVE_FIXED &&
in_out_vrr->fixed.ramping_active) {
update_v_total_for_static_ramp(
core_freesync, stream, in_out_vrr);
}
}
/* Calculate nominal field rate for stream, rounded up to nearest integer */
nominal_field_rate_in_uhz = stream->timing.pix_clk_100hz;
nominal_field_rate_in_uhz *= 100000000ULL;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.