// SPDX-License-Identifier: MIT /* * Copyright © 2026 Intel Corporation * */ #include #include "intel_de.h" #include "intel_dip.h" #include "intel_dip_regs.h" #include "intel_display_types.h" #include "intel_hdmi.h" static int intel_dip_get_as_sdp_transmission_line(const struct intel_crtc_state *crtc_state) { struct intel_display *display = to_intel_display(crtc_state); if (!HAS_EMP_AS_SDP_TL(display)) return 0; /* * EMP_AS_SDP_TL defines the T1 position as the default AS SDP * Transmission Line, which corresponds to the start of the * VSYNC pulse. * * Use the T1 position for now. */ return crtc_state->vrr.vsync_start; } u16 intel_dip_read_emp_as_sdp_tl(const struct intel_crtc_state *crtc_state) { struct intel_display *display = to_intel_display(crtc_state); enum transcoder cpu_transcoder = crtc_state->cpu_transcoder; u32 val; if (!HAS_EMP_AS_SDP_TL(display)) return 0; val = intel_de_read(display, EMP_AS_SDP_TL(display, cpu_transcoder)); return REG_FIELD_GET(EMP_AS_SDP_DB_TL_MASK, val); } void intel_dip_write_emp_as_sdp_tl(const struct intel_crtc_state *crtc_state) { struct intel_display *display = to_intel_display(crtc_state); enum transcoder cpu_transcoder = crtc_state->cpu_transcoder; if (!HAS_EMP_AS_SDP_TL(display)) return; intel_de_write(display, EMP_AS_SDP_TL(display, cpu_transcoder), EMP_AS_SDP_DB_TL(crtc_state->dip.emp_as_sdp_tl)); } static int intel_dip_sdp_stagger_to_tl(struct intel_crtc_state *crtc_state, int stagger) { return crtc_state->dip.cmn_sdp_tl + stagger; } static void intel_dip_cmn_sdp_tl_compute_config_late(struct intel_crtc_state *crtc_state) { struct intel_display *display = to_intel_display(crtc_state); bool as_sdp; if (!HAS_COMMON_SDP_TL(display)) return; as_sdp = crtc_state->infoframes.enable & intel_hdmi_infoframe_enable(DP_SDP_ADAPTIVE_SYNC); /* * When AS SDP is enabled : * - The common SDP Transmission Line matches the EMP SDP Transmission Line. * * When AS SDP is disabled: * - Bspec mentions the positions as lines of delayed vblank. * - Guardband = 1st line of delayed vblank * - Common SDP Transmission line is set to 2nd line of delayed vblank. */ if (as_sdp) crtc_state->dip.cmn_sdp_tl = crtc_state->dip.emp_as_sdp_tl; else crtc_state->dip.cmn_sdp_tl = crtc_state->vrr.guardband - 1; if (drm_WARN_ON(display->drm, crtc_state->dip.cmn_sdp_tl >= crtc_state->vrr.guardband + crtc_state->set_context_latency)) return; /* * Currently we are programming the default stagger values, but these * can be optimized if required, based on number of SDPs enabled. * * Default values of the Transmission lines for SDPs other than AS SDP: * VSC : CMN SDP Transmission line * GMP : CMN SDP Transmission line * PPS : CMN SDP Transmission line + 1 * VSC_EXT: CMN SDP Transmission line + 2 */ crtc_state->dip.vsc_sdp_tl = crtc_state->dip.cmn_sdp_tl; crtc_state->dip.gmp_sdp_tl = intel_dip_sdp_stagger_to_tl(crtc_state, GMP_STAGGER_DEFAULT); crtc_state->dip.pps_sdp_tl = intel_dip_sdp_stagger_to_tl(crtc_state, PPS_STAGGER_DEFAULT); crtc_state->dip.vsc_ext_sdp_tl = intel_dip_sdp_stagger_to_tl(crtc_state, VSC_EXT_STAGGER_DEFAULT); } void intel_dip_sdp_tl_compute_config_late(struct intel_crtc_state *crtc_state) { crtc_state->dip.emp_as_sdp_tl = intel_dip_get_as_sdp_transmission_line(crtc_state); intel_dip_cmn_sdp_tl_compute_config_late(crtc_state); } static void intel_dip_cmn_sdp_transmission_line_get_config(struct intel_crtc_state *crtc_state) { struct intel_display *display = to_intel_display(crtc_state); enum transcoder cpu_transcoder = crtc_state->cpu_transcoder; u16 vsc_ext_stagger, pps_stagger, gmp_stagger; u32 val; if (!HAS_COMMON_SDP_TL(display)) return; val = intel_de_read(display, CMN_SDP_TL(display, cpu_transcoder)); if (!(val & TRANSMISSION_LINE_ENABLE)) return; crtc_state->dip.cmn_sdp_tl = REG_FIELD_GET(BASE_TRANSMISSION_LINE_MASK, val); /* SDP VSC uses same transmission line as CMN base transmission line */ crtc_state->dip.vsc_sdp_tl = crtc_state->dip.cmn_sdp_tl; val = intel_de_read(display, CMN_SDP_TL_STGR_CTL(display, cpu_transcoder)); vsc_ext_stagger = REG_FIELD_GET(VSC_EXT_STAGGER_MASK, val); pps_stagger = REG_FIELD_GET(PPS_STAGGER_MASK, val); gmp_stagger = REG_FIELD_GET(GMP_STAGGER_MASK, val); crtc_state->dip.vsc_ext_sdp_tl = intel_dip_sdp_stagger_to_tl(crtc_state, vsc_ext_stagger); crtc_state->dip.pps_sdp_tl = intel_dip_sdp_stagger_to_tl(crtc_state, pps_stagger); crtc_state->dip.gmp_sdp_tl = intel_dip_sdp_stagger_to_tl(crtc_state, gmp_stagger); } void intel_dip_sdp_transmission_line_get_config(struct intel_crtc_state *crtc_state) { crtc_state->dip.emp_as_sdp_tl = intel_dip_read_emp_as_sdp_tl(crtc_state); intel_dip_cmn_sdp_transmission_line_get_config(crtc_state); } static int intel_dip_sdp_tl_to_stagger(const struct intel_crtc_state *crtc_state, u16 sdp_transmission_line) { return sdp_transmission_line - crtc_state->dip.cmn_sdp_tl; } void intel_dip_cmn_sdp_transmission_line_enable(const struct intel_crtc_state *crtc_state) { struct intel_display *display = to_intel_display(crtc_state); enum transcoder cpu_transcoder = crtc_state->cpu_transcoder; int gmp_stagger; int pps_stagger; int vsc_ext_stagger; if (!HAS_COMMON_SDP_TL(display)) return; gmp_stagger = intel_dip_sdp_tl_to_stagger(crtc_state, crtc_state->dip.gmp_sdp_tl); pps_stagger = intel_dip_sdp_tl_to_stagger(crtc_state, crtc_state->dip.pps_sdp_tl); vsc_ext_stagger = intel_dip_sdp_tl_to_stagger(crtc_state, crtc_state->dip.vsc_ext_sdp_tl); if (drm_WARN_ON(display->drm, gmp_stagger < 0)) return; if (drm_WARN_ON(display->drm, pps_stagger < 0)) return; if (drm_WARN_ON(display->drm, vsc_ext_stagger < 0)) return; intel_de_write(display, CMN_SDP_TL_STGR_CTL(display, cpu_transcoder), GMP_STAGGER(gmp_stagger) | PPS_STAGGER(pps_stagger) | VSC_EXT_STAGGER(vsc_ext_stagger)); intel_de_write(display, CMN_SDP_TL(display, cpu_transcoder), TRANSMISSION_LINE_ENABLE | BASE_TRANSMISSION_LINE(crtc_state->dip.cmn_sdp_tl)); } void intel_dip_cmn_sdp_transmission_line_disable(const struct intel_crtc_state *old_crtc_state) { struct intel_display *display = to_intel_display(old_crtc_state); enum transcoder cpu_transcoder = old_crtc_state->cpu_transcoder; if (!HAS_COMMON_SDP_TL(display)) return; intel_de_write(display, CMN_SDP_TL(display, cpu_transcoder), 0); }