Re: [PATCH 1/4] drm/amd/display: Add 2.1 FreeSync support for AMD VSDB EDID Block
Harry Wentland <[email protected]> Fri, 31 Jul 2026 15:37:58 -0400
| Newsgroups | org.freedesktop.lists.amd-gfx |
|---|---|
| Message-ID | <[email protected]> |
On 2026-07-30 13:17, Fangzhi Zuo wrote: > From: Fangzhi Zuo <[email protected]> > > why: > HDMI FRL sinks were not parsed for the AMD VSDB and no VTEM info packet > was emitted for them, so 2.1 FreeSync over HDMI FRL did not work. It is > backward-compatible with 2.0 FreeSync. > > how: > - Accept SIGNAL_TYPE_HDMI_FRL alongside SIGNAL_TYPE_HDMI_TYPE_A when > parsing the AMD VSDB in amdgpu_dm_update_freesync_caps(). > - Build and send the VTEM info packet via mod_build_infopacket_vtem() > when the stream signal is HDMI FRL during the freesync state update. > - Set the VTEM Data_Set_Length to 0 when no VTEM feature is enabled. > build_infopacket_header_vtem() hardcodes Data_Set_Length = 4, so a VTEM > with Data_Set_Length = 4 would be transmitted even when no VTEM feature > is enabled (VRR_EN = 0 and no FVA), e.g. when the sink advertises > VRRMIN = 0 and vrr_capable is false. This fails HDMI GCTS HF1-58 step > 6.2. > The VTEM must keep being transmitted every MTW while VRR is enabled > (HF1-58 steps 8.1 and 8.3), so it cannot simply be suppressed per > frame. Instead, follow the MLDS option in HDMI 2.1 10.10.2.4: keep > transmitting the VTEM but set Data_Set_Length = 0 when no feature is > enabled. When VRR becomes active the full Data_Set_Length = 4 payload > with VRR_EN = 1 is sent as before. > > Signed-off-by: Fangzhi Zuo <[email protected]> > --- > .../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 3 + > .../display/amdgpu_dm/amdgpu_dm_connector.c | 4 +- > .../amd/display/modules/inc/mod_info_packet.h | 4 + > .../display/modules/info_packet/info_packet.c | 109 ++++++++++++++++++ > 4 files changed, 119 insertions(+), 1 deletion(-) > > diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c > index 5a9afc0607b2..ccf882a22a57 100644 > --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c > +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c > @@ -3881,6 +3881,9 @@ static void update_freesync_state_on_stream( > &vrr_infopacket, > pack_sdp_v1_3); > > + if (new_stream->sink->sink_signal == SIGNAL_TYPE_HDMI_FRL) > + mod_build_infopacket_vtem(new_stream, &vrr_params, 0, &vrr_infopacket); > + > new_crtc_state->freesync_vrr_info_changed |= > (memcmp(&new_crtc_state->vrr_infopacket, > &vrr_infopacket, > diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_connector.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_connector.c > index 5e3dfeaed76b..2deb5abae264 100644 > --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_connector.c > +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_connector.c > @@ -3631,7 +3631,9 @@ void amdgpu_dm_update_freesync_caps(struct drm_connector *connector, > amdgpu_dm_connector->as_type = ADAPTIVE_SYNC_TYPE_EDP; > } > > - } else if (drm_edid && sink->sink_signal == SIGNAL_TYPE_HDMI_TYPE_A) { > + } else if (drm_edid && > + (sink->sink_signal == SIGNAL_TYPE_HDMI_TYPE_A || > + sink->sink_signal == SIGNAL_TYPE_HDMI_FRL)) { > i = parse_hdmi_amd_vsdb(amdgpu_dm_connector, edid, &vsdb_info); > if (i >= 0) { > amdgpu_dm_connector->vsdb_info = vsdb_info; > diff --git a/drivers/gpu/drm/amd/display/modules/inc/mod_info_packet.h b/drivers/gpu/drm/amd/display/modules/inc/mod_info_packet.h > index eee8206bc531..5181d889fe7f 100644 > --- a/drivers/gpu/drm/amd/display/modules/inc/mod_info_packet.h > +++ b/drivers/gpu/drm/amd/display/modules/inc/mod_info_packet.h > @@ -67,6 +67,10 @@ struct AS_Df_params { > struct frame_duration_op decrease; > }; > > +void mod_build_infopacket_vtem(const struct dc_stream_state *stream, > + const struct mod_vrr_params *vrr, int fva_factor, > + struct dc_info_packet *infopacket); > + > void mod_build_adaptive_sync_infopacket(const struct dc_stream_state *stream, > enum adaptive_sync_type asType, const struct AS_Df_params *param, > struct dc_info_packet *info_packet); > diff --git a/drivers/gpu/drm/amd/display/modules/info_packet/info_packet.c b/drivers/gpu/drm/amd/display/modules/info_packet/info_packet.c > index f5ac4bf32a78..e956c707ac50 100644 > --- a/drivers/gpu/drm/amd/display/modules/info_packet/info_packet.c > +++ b/drivers/gpu/drm/amd/display/modules/info_packet/info_packet.c > @@ -291,6 +291,21 @@ void set_vsc_packet_colorimetry_data( > info_packet->sb[18] = 0; > } > > +static void setFieldWithMask(unsigned char *dest, unsigned int mask, unsigned int value) Don't use camel-case. We should rename it to set_field_with_mask(...). > +{ > + unsigned int shift = 0; > + > + if (!mask || !dest) > + return; > + > + while (!((mask >> shift) & 1)) > + shift++; > + > + *dest = *dest & ~mask; > + value = value & (mask >> shift); > + *dest = *dest | (value << shift); > +} > + > void mod_build_vsc_infopacket(const struct dc_stream_state *stream, > struct dc_info_packet *info_packet, > enum dc_color_space cs, > @@ -644,6 +659,100 @@ void mod_build_hf_vsif_infopacket(const struct dc_stream_state *stream, > info_packet->valid = true; > } > > +static void build_vtem_infopacket_data(const struct dc_stream_state *stream, > + const struct mod_vrr_params *vrr, int fva_factor, > + struct dc_info_packet *infopacket) > +{ > + unsigned int fieldRateInHz; Same here: field_rate_in_hz > + > + /* FVA Factor setting */ > + setFieldWithMask(&infopacket->sb[VTEM_MD0], MASK_VTEM_MD0__FVA_FACTOR_M1, > + (fva_factor > 0)?(fva_factor-1):0); Formatting. There should be whitespace around the `?`, `-`, and `:`. > + /* VRR Parameters */ > + if (vrr->state == VRR_STATE_ACTIVE_VARIABLE || > + vrr->state == VRR_STATE_ACTIVE_FIXED) { Formatting. The vrr->state on the second line should align with the vrr->state on the prior line. > + setFieldWithMask(&infopacket->sb[VTEM_MD0], MASK_VTEM_MD0__VRR_EN, 1); > + } else { > + setFieldWithMask(&infopacket->sb[VTEM_MD0], MASK_VTEM_MD0__VRR_EN, 0); > + } > + > + if (vrr->state == VRR_STATE_ACTIVE_FIXED) > + setFieldWithMask(&infopacket->sb[VTEM_MD0], MASK_VTEM_MD0__M_CONST, vrr->m_const); > + > + if (!stream->timing.vic) { > + setFieldWithMask(&infopacket->sb[VTEM_MD1], MASK_VTEM_MD1__BASE_VFRONT, > + stream->timing.v_front_porch); > + > + > + /* TODO: In dal2, we check mode flags for a reduced blanking timing. > + * Need a way to relay that information to this function. > + * if("ReducedBlanking") > + * { > + * setFieldWithMask(&infopacket->sb[VRR_VTEM_MD2], MASK__VRR_VTEM_MD2__RB, 1; > + * } > + */ > + > + fieldRateInHz = stream->timing.pix_clk_100hz * 100; > + fieldRateInHz /= stream->timing.h_total; > + fieldRateInHz = (fieldRateInHz + stream->timing.v_total / 2) > + / stream->timing.v_total; > + > + setFieldWithMask(&infopacket->sb[VTEM_MD2], MASK_VTEM_MD2__BASE_REFRESH_RATE_98, > + fieldRateInHz >> 8); > + setFieldWithMask(&infopacket->sb[VTEM_MD3], MASK_VTEM_MD3__BASE_REFRESH_RATE_07, > + fieldRateInHz); > + > + } > + > + /* > + * When no VTEM feature is enabled (neither VRR nor FVA), signal a > + * zero-length data set (MLDS) by clearing Data_Set_Length. HDMI 2.1 > + * 10.10.2.4 requires the Source to either stop transmitting the VTEM > + * or set Data_Set_Length = 0 when no feature is enabled; keeping the > + * VTEM with Data_Set_Length = 0 preserves the every-MTW cadence while > + * staying compliant (e.g. HDMI GCTS HF1-58 step 6.2). > + */ > + if (vrr->state != VRR_STATE_ACTIVE_VARIABLE && > + vrr->state != VRR_STATE_ACTIVE_FIXED && fva_factor == 0) > + setFieldWithMask(&infopacket->sb[VTEM_PB6], > + MASK_VTEM_PB6__DATA_SET_LENGTH_LSB, 0); > + > + infopacket->valid = true; > +} > + > +static void build_infopacket_header_vtem(enum signal_type signal, > + struct dc_info_packet *infopacket) > +{ > + // HEADER > + > + // HB0, HB1, HB2 indicates PacketType VTEMPacket Formatting: convert these C++-style comments to C-style. > + infopacket->hb0 = 0x7F; > + infopacket->hb1 = 0xC0; > + infopacket->hb2 = 0x00; //sequence_index > + > + setFieldWithMask(&infopacket->sb[VTEM_PB0], MASK_VTEM_PB0__VFR, 1); > + setFieldWithMask(&infopacket->sb[VTEM_PB2], MASK_VTEM_PB2__ORGANIZATION_ID, 1); > + setFieldWithMask(&infopacket->sb[VTEM_PB3], MASK_VTEM_PB3__DATA_SET_TAG_MSB, 0); > + setFieldWithMask(&infopacket->sb[VTEM_PB4], MASK_VTEM_PB4__DATA_SET_TAG_LSB, 1); > + setFieldWithMask(&infopacket->sb[VTEM_PB5], MASK_VTEM_PB5__DATA_SET_LENGTH_MSB, 0); > + setFieldWithMask(&infopacket->sb[VTEM_PB6], MASK_VTEM_PB6__DATA_SET_LENGTH_LSB, 4); > +} > + > +void mod_build_infopacket_vtem(const struct dc_stream_state *stream, > + const struct mod_vrr_params *vrr, int fva_factor, > + struct dc_info_packet *infopacket) > +{ > + //VTEM info packet for HdmiVrr > + C++-style comment > + memset(infopacket, 0, sizeof(struct dc_info_packet)); > + > + //VTEM Packet is structured differently C++-style comment Happy to have all these style comments fixed separately in an amd-dal commit or a new asdn commit. Either way, this is Reviewed-by: Harry Wentland <[email protected]> Harry > + build_infopacket_header_vtem(stream->signal, infopacket); > + build_vtem_infopacket_data(stream, vrr, fva_factor, infopacket); > + > + infopacket->valid = true; > +} > + > void mod_build_adaptive_sync_infopacket(const struct dc_stream_state *stream, > enum adaptive_sync_type asType, > const struct AS_Df_params *param,