Files
omarchy-pkgs/pkgbuilds/linux-omarchy-bore/0450-amd-hdmi-vrr-allm.patch
T

457 lines
17 KiB
Diff

diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
@@ -104,6 +104,7 @@
#include "ivsrcid/dcn/irqsrcs_dcn_1_0.h"
#include "modules/inc/mod_freesync.h"
+#include "modules/inc/mod_info_packet.h"
#include "modules/inc/mod_power.h"
#include "modules/power/power_helpers.h"
@@ -10088,6 +10089,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,
@@ -10099,6 +10103,36 @@ static void update_freesync_state_on_stream(
new_stream->vrr_infopacket = vrr_infopacket;
new_stream->allow_freesync = mod_freesync_get_freesync_enabled(&vrr_params);
+ /*
+ * HDMI ALLM: when Gaming-VRR is active (VRR_EN=1) and the sink
+ * advertises ALLM in the SCDS, the Source shall transmit the HF-VSIF
+ * with ALLM_Mode=1 (HDMI 2.1 Section 7.6.6).
+ */
+ if (new_stream->signal == SIGNAL_TYPE_HDMI_TYPE_A ||
+ new_stream->signal == SIGNAL_TYPE_HDMI_FRL) {
+ struct dc_info_packet hf_vsif = {0};
+ bool sink_allm = aconn && aconn->base.display_info.hdmi.allm;
+ bool allm = sink_allm &&
+ (vrr_params.state == VRR_STATE_ACTIVE_VARIABLE ||
+ vrr_params.state == VRR_STATE_ACTIVE_FIXED);
+ bool allm_changed;
+
+ if (allm)
+ mod_build_hf_vsif_infopacket(new_stream, &hf_vsif, allm, allm);
+
+ allm_changed = memcmp(&new_stream->hfvsif_infopacket, &hf_vsif,
+ sizeof(hf_vsif)) != 0;
+ new_crtc_state->freesync_vrr_info_changed |= allm_changed;
+ new_stream->hfvsif_infopacket = hf_vsif;
+
+ if (allm_changed)
+ drm_dbg_driver(adev_to_drm(adev),
+ "ALLM: flip on crtc=%u: sink_allm=%d vrr_state=%d -> ALLM_Mode=%d\n",
+ new_crtc_state->base.crtc->base.id,
+ sink_allm,
+ vrr_params.state, allm);
+ }
+
if (new_crtc_state->freesync_vrr_info_changed)
drm_dbg_kms(adev_to_drm(adev), "VRR packet update: crtc=%u enabled=%d state=%d",
new_crtc_state->base.crtc->base.id,
@@ -10665,9 +10699,12 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_commit *state,
}
if (acrtc_state->stream) {
- if (acrtc_state->freesync_vrr_info_changed)
+ if (acrtc_state->freesync_vrr_info_changed) {
bundle->stream_update.vrr_infopacket =
&acrtc_state->stream->vrr_infopacket;
+ bundle->stream_update.hfvsif_infopacket =
+ &acrtc_state->stream->hfvsif_infopacket;
+ }
}
}
@@ -12069,6 +12106,14 @@ static void get_freesync_config_for_crtc(
}
out:
new_crtc_state->freesync_config = config;
+
+ drm_dbg_driver(new_con_state->base.connector->dev,
+ "VRR: cfg vrr_enabled=%d vrr_supported=%d fs_capable=%d vrefresh=%d min=%d max=%d state=%d\n",
+ new_crtc_state->base.vrr_enabled,
+ new_crtc_state->vrr_supported,
+ new_con_state->freesync_capable, vrefresh,
+ aconnector->min_vfreq, aconnector->max_vfreq,
+ config.state);
}
static void reset_freesync_config_for_crtc(
@@ -14031,6 +14076,15 @@ void amdgpu_dm_update_freesync_caps(struct drm_connector *connector,
if (!adev->dm.freesync_module || !dc_supports_vrr(sink->ctx->dce_version))
goto update;
+ drm_dbg_driver(adev_to_drm(adev),
+ "VRR: enter signal=%d hdmi_vrr=%d mrange[%d-%d] hdmi.vrr_cap[sup=%d min=%d max=%d]\n",
+ sink->sink_signal, connector->display_info.hdmi.vrr_cap.supported,
+ connector->display_info.monitor_range.min_vfreq,
+ connector->display_info.monitor_range.max_vfreq,
+ connector->display_info.hdmi.vrr_cap.supported,
+ connector->display_info.hdmi.vrr_cap.vrr_min,
+ connector->display_info.hdmi.vrr_cap.vrr_max);
+
edid = drm_edid_raw(drm_edid); // FIXME: Get rid of drm_edid_raw()
/* Some eDP panels only have the refresh rate range info in DisplayID */
@@ -14056,7 +14110,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;
@@ -14072,6 +14128,59 @@ void amdgpu_dm_update_freesync_caps(struct drm_connector *connector,
connector->display_info.monitor_range.max_vfreq = vsdb_info.max_refresh_rate_hz;
}
}
+
+ drm_dbg_driver(adev_to_drm(adev),
+ "VRR: amd_vsdb i=%d fs_sup=%d min=%d max=%d fs_capable=%d\n",
+ i, vsdb_info.freesync_supported,
+ vsdb_info.min_refresh_rate_hz,
+ vsdb_info.max_refresh_rate_hz, freesync_capable);
+
+ /*
+ * If AMD VSDB didn't provide a valid FreeSync range, fall back to
+ * the HDMI 2.1 VRR capability parsed from the HF-VSDB.
+ */
+ if (!freesync_capable && connector->display_info.hdmi.vrr_cap.supported) {
+ struct drm_hdmi_vrr_cap *vrr_cap =
+ &connector->display_info.hdmi.vrr_cap;
+
+ drm_dbg_driver(adev_to_drm(adev),
+ "VRR: HF-VSDB fallback: hdmi_vrr=1 vrr_cap[sup=%d min=%d max=%d] mrange_max=%d\n",
+ vrr_cap->supported, vrr_cap->vrr_min, vrr_cap->vrr_max,
+ connector->display_info.monitor_range.max_vfreq);
+
+ if (vrr_cap->supported && vrr_cap->vrr_min > 0) {
+ amdgpu_dm_connector->min_vfreq = vrr_cap->vrr_min;
+ amdgpu_dm_connector->max_vfreq = vrr_cap->vrr_max ?
+ vrr_cap->vrr_max :
+ connector->display_info.monitor_range.max_vfreq;
+
+ /*
+ * VRRMAX = 0 in the HF-VSDB means "up to the Base
+ * Refresh Rate". If the EDID also did not provide a
+ * monitor range max, fall back to the Base Refresh
+ * Rate (the highest refresh rate of the preferred
+ * timing) so a valid VRR range is still reported to
+ * userspace.
+ */
+ if (!amdgpu_dm_connector->max_vfreq) {
+ struct drm_display_mode *brr_mode =
+ get_highest_refresh_rate_mode(amdgpu_dm_connector, true);
+
+ if (brr_mode)
+ amdgpu_dm_connector->max_vfreq =
+ drm_mode_vrefresh(brr_mode);
+ }
+
+ if (amdgpu_dm_connector->max_vfreq -
+ amdgpu_dm_connector->min_vfreq > 10)
+ freesync_capable = true;
+
+ connector->display_info.monitor_range.min_vfreq =
+ amdgpu_dm_connector->min_vfreq;
+ connector->display_info.monitor_range.max_vfreq =
+ amdgpu_dm_connector->max_vfreq;
+ }
+ }
}
if (amdgpu_dm_connector->dc_link)
@@ -14113,6 +14222,11 @@ void amdgpu_dm_update_freesync_caps(struct drm_connector *connector,
if (dm_con_state)
dm_con_state->freesync_capable = freesync_capable;
+ drm_dbg_driver(adev_to_drm(adev),
+ "VRR: caps result: freesync_capable=%d min_vfreq=%d max_vfreq=%d\n",
+ freesync_capable, amdgpu_dm_connector->min_vfreq,
+ amdgpu_dm_connector->max_vfreq);
+
if (connector->state && amdgpu_dm_connector->dc_link && !freesync_capable &&
amdgpu_dm_connector->dc_link->replay_settings.config.replay_supported) {
amdgpu_dm_connector->dc_link->replay_settings.config.replay_supported = false;
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
--- 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
--- 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 set_field_with_mask(unsigned char *dest, unsigned int mask, unsigned int value)
+{
+ 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 field_rate_in_hz;
+
+ /* FVA Factor setting */
+ set_field_with_mask(&infopacket->sb[VTEM_MD0], MASK_VTEM_MD0__FVA_FACTOR_M1,
+ (fva_factor > 0) ? (fva_factor - 1) : 0);
+ /* VRR Parameters */
+ if (vrr->state == VRR_STATE_ACTIVE_VARIABLE ||
+ vrr->state == VRR_STATE_ACTIVE_FIXED) {
+ set_field_with_mask(&infopacket->sb[VTEM_MD0], MASK_VTEM_MD0__VRR_EN, 1);
+ } else {
+ set_field_with_mask(&infopacket->sb[VTEM_MD0], MASK_VTEM_MD0__VRR_EN, 0);
+ }
+
+ if (vrr->state == VRR_STATE_ACTIVE_FIXED)
+ set_field_with_mask(&infopacket->sb[VTEM_MD0], MASK_VTEM_MD0__M_CONST, vrr->m_const);
+
+ if (!stream->timing.vic) {
+ set_field_with_mask(&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")
+ * {
+ * set_field_with_mask(&infopacket->sb[VRR_VTEM_MD2], MASK__VRR_VTEM_MD2__RB, 1;
+ * }
+ */
+
+ field_rate_in_hz = stream->timing.pix_clk_100hz * 100;
+ field_rate_in_hz /= stream->timing.h_total;
+ field_rate_in_hz = (field_rate_in_hz + stream->timing.v_total / 2)
+ / stream->timing.v_total;
+
+ set_field_with_mask(&infopacket->sb[VTEM_MD2], MASK_VTEM_MD2__BASE_REFRESH_RATE_98,
+ field_rate_in_hz >> 8);
+ set_field_with_mask(&infopacket->sb[VTEM_MD3], MASK_VTEM_MD3__BASE_REFRESH_RATE_07,
+ field_rate_in_hz);
+
+ }
+
+ /*
+ * 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)
+ set_field_with_mask(&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 */
+ infopacket->hb0 = 0x7F;
+ infopacket->hb1 = 0xC0;
+ infopacket->hb2 = 0x00; /* sequence_index */
+
+ set_field_with_mask(&infopacket->sb[VTEM_PB0], MASK_VTEM_PB0__VFR, 1);
+ set_field_with_mask(&infopacket->sb[VTEM_PB2], MASK_VTEM_PB2__ORGANIZATION_ID, 1);
+ set_field_with_mask(&infopacket->sb[VTEM_PB3], MASK_VTEM_PB3__DATA_SET_TAG_MSB, 0);
+ set_field_with_mask(&infopacket->sb[VTEM_PB4], MASK_VTEM_PB4__DATA_SET_TAG_LSB, 1);
+ set_field_with_mask(&infopacket->sb[VTEM_PB5], MASK_VTEM_PB5__DATA_SET_LENGTH_MSB, 0);
+ set_field_with_mask(&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 */
+
+ memset(infopacket, 0, sizeof(struct dc_info_packet));
+
+ /* VTEM Packet is structured differently */
+ 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,
diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c
--- a/drivers/gpu/drm/drm_edid.c
+++ b/drivers/gpu/drm/drm_edid.c
@@ -6182,6 +6182,33 @@ static void drm_parse_ycbcr420_deep_color_info(struct drm_connector *connector,
hdmi->y420_dc_modes = dc_mask;
}
+static void drm_parse_hdmi_gaming_info(struct drm_hdmi_info *hdmi, const u8 *db)
+{
+ struct drm_hdmi_vrr_cap *vrr = &hdmi->vrr_cap;
+
+ if (cea_db_payload_len(db) < 8)
+ return;
+
+ hdmi->fapa_start_location = db[8] & DRM_EDID_FAPA_START_LOCATION;
+ hdmi->allm = db[8] & DRM_EDID_ALLM;
+ vrr->fva = db[8] & DRM_EDID_FVA;
+ vrr->cnmvrr = db[8] & DRM_EDID_CNMVRR;
+ vrr->cinema_vrr = db[8] & DRM_EDID_CINEMA_VRR;
+ vrr->mdelta = db[8] & DRM_EDID_MDELTA;
+
+ if (cea_db_payload_len(db) < 9)
+ return;
+
+ vrr->vrr_min = db[9] & DRM_EDID_VRR_MIN_MASK;
+ vrr->supported = (vrr->vrr_min > 0 && vrr->vrr_min <= 48);
+
+ if (cea_db_payload_len(db) < 10)
+ return;
+
+ vrr->vrr_max = (db[9] & DRM_EDID_VRR_MAX_UPPER_MASK) << 2 | db[10];
+ vrr->supported &= (vrr->vrr_max == 0 || vrr->vrr_max >= 100);
+}
+
static void drm_parse_dsc_info(struct drm_hdmi_dsc_cap *hdmi_dsc,
const u8 *hf_scds)
{
@@ -6308,6 +6335,8 @@ static void drm_parse_hdmi_forum_scds(struct drm_connector *connector,
drm_parse_ycbcr420_deep_color_info(connector, hf_scds);
+ drm_parse_hdmi_gaming_info(&connector->display_info.hdmi, hf_scds);
+
if (cea_db_payload_len(hf_scds) >= 11 && hf_scds[11]) {
drm_parse_dsc_info(hdmi_dsc, hf_scds);
dsc_support = true;
@@ -6317,6 +6346,19 @@ static void drm_parse_hdmi_forum_scds(struct drm_connector *connector,
"[CONNECTOR:%d:%s] HF-VSDB: max TMDS clock: %d KHz, HDMI 2.1 support: %s, DSC 1.2 support: %s\n",
connector->base.id, connector->name,
max_tmds_clock, str_yes_no(max_frl_rate), str_yes_no(dsc_support));
+ drm_dbg_kms(connector->dev,
+ "[CONNECTOR:%d:%s] FAPA in blanking: %s, ALLM support: %s, Fast Vactive support: %s\n",
+ connector->base.id, connector->name, str_yes_no(hdmi->fapa_start_location),
+ str_yes_no(hdmi->allm), str_yes_no(hdmi->vrr_cap.fva));
+ drm_dbg_kms(connector->dev,
+ "[CONNECTOR:%d:%s] Negative M VRR support: %s, CinemaVRR support: %s, Mdelta: %d\n",
+ connector->base.id, connector->name, str_yes_no(hdmi->vrr_cap.cnmvrr),
+ str_yes_no(hdmi->vrr_cap.cinema_vrr), hdmi->vrr_cap.mdelta);
+ drm_dbg_kms(connector->dev,
+ "[CONNECTOR:%d:%s] VRRmin: %u, VRRmax: %u, VRR supported: %s\n",
+ connector->base.id, connector->name, hdmi->vrr_cap.vrr_min,
+ hdmi->vrr_cap.vrr_max, str_yes_no(hdmi->vrr_cap.supported));
+
}
static void drm_parse_hdmi_deep_color_info(struct drm_connector *connector,
diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h
--- a/include/drm/drm_connector.h
+++ b/include/drm/drm_connector.h
@@ -254,6 +254,44 @@ struct drm_scdc {
struct drm_scrambling scrambling;
};
+/**
+ * struct drm_hdmi_vrr_cap - Information about VRR capabilities of a HDMI sink
+ *
+ * Describes the VRR support provided by HDMI 2.1 sink. The information is
+ * fetched fom additional HFVSDB blocks defined for HDMI 2.1.
+ */
+struct drm_hdmi_vrr_cap {
+ /** @fva: flag for Fast VActive (Quick Frame Transport) support */
+ bool fva;
+
+ /** @mcnmvrr: flag for Negative M VRR support */
+ bool cnmvrr;
+
+ /** @mcinema_vrr: flag for Cinema VRR support */
+ bool cinema_vrr;
+
+ /** @mdelta: flag for limited frame-to-frame compensation support */
+ bool mdelta;
+
+ /**
+ * @vrr_min : minimum supported variable refresh rate in Hz.
+ * Valid values only inide 1 - 48 range
+ */
+ u16 vrr_min;
+
+ /**
+ * @vrr_max : maximum supported variable refresh rate in Hz (optional).
+ * Valid values are either 0 (max based on video mode) or >= 100
+ */
+ u16 vrr_max;
+
+ /**
+ * @supported: flag for vrr support based on checking for VRRmin and
+ * VRRmax values having correct values.
+ */
+ bool supported;
+};
+
/**
* struct drm_hdmi_dsc_cap - DSC capabilities of HDMI sink
*
@@ -330,6 +368,15 @@ struct drm_hdmi_info {
/** @max_lanes: supported by sink */
u8 max_lanes;
+ /** @fapa_start_location: flag for the FAPA in blanking support */
+ bool fapa_start_location;
+
+ /** @allm: flag for Auto Low Latency Mode support by sink */
+ bool allm;
+
+ /** @vrr_cap: VRR capabilities of the sink */
+ struct drm_hdmi_vrr_cap vrr_cap;
+
/** @dsc_cap: DSC capabilities of the sink */
struct drm_hdmi_dsc_cap dsc_cap;
};