#define XELPDP_PORT_BUF_PORT_DATA_20BIT REG_FIELD_PREP(XELPDP_PORT_BUF_PORT_DATA_WIDTH_MASK, 1)
#define XELPDP_PORT_BUF_PORT_DATA_40BIT REG_FIELD_PREP(XELPDP_PORT_BUF_PORT_DATA_WIDTH_MASK, 2)
#define XELPDP_PORT_REVERSAL REG_BIT(16)
+#define XE3PLPDP_PHY_MODE_MASK REG_GENMASK(15, 12)
+#define XE3PLPDP_PHY_MODE_DP REG_FIELD_PREP(XE3PLPDP_PHY_MODE_MASK, 0x3)
#define XELPDP_PORT_BUF_IO_SELECT_TBT REG_BIT(11)
#define XELPDP_PORT_BUF_PHY_IDLE REG_BIT(7)
#define XELPDP_TC_PHY_OWNERSHIP REG_BIT(6)
_XELPDP_PORT_BUF_CTL2(port))
#define XELPDP_LANE_PIPE_RESET(lane) _PICK(lane, REG_BIT(31), REG_BIT(30))
#define XELPDP_LANE_PHY_CURRENT_STATUS(lane) _PICK(lane, REG_BIT(29), REG_BIT(28))
+#define XE3PLPDP_LANE_PHY_PULSE_STATUS(lane) _PICK(lane, REG_BIT(27), REG_BIT(26))
#define XELPDP_LANE_POWERDOWN_UPDATE(lane) _PICK(lane, REG_BIT(25), REG_BIT(24))
#define _XELPDP_LANE0_POWERDOWN_NEW_STATE_MASK REG_GENMASK(23, 20)
#define _XELPDP_LANE0_POWERDOWN_NEW_STATE(val) REG_FIELD_PREP(_XELPDP_LANE0_POWERDOWN_NEW_STATE_MASK, val)
#define XELPDP_POWER_STATE_ACTIVE(val) REG_FIELD_PREP(XELPDP_POWER_STATE_ACTIVE_MASK, val)
#define XELPDP_P0_STATE_ACTIVE 0x0
#define XELPDP_P2_STATE_READY 0x2
+#define XE3PLPD_P4_STATE_DISABLE 0x4
#define XELPDP_P2PG_STATE_DISABLE 0x9
#define XELPDP_P4PG_STATE_DISABLE 0xC
#define XELPDP_P2_STATE_RESET 0x2
--- /dev/null
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright © 2025 Intel Corporation
+ */
+
+#include <drm/drm_print.h>
+
+#include "i915_reg.h"
+#include "intel_cx0_phy.h"
+#include "intel_cx0_phy_regs.h"
+#include "intel_de.h"
+#include "intel_display.h"
+#include "intel_display_types.h"
+#include "intel_lt_phy.h"
+#include "intel_lt_phy_regs.h"
+#include "intel_tc.h"
+
+#define INTEL_LT_PHY_LANE0 BIT(0)
+#define INTEL_LT_PHY_LANE1 BIT(1)
+#define INTEL_LT_PHY_BOTH_LANES (INTEL_LT_PHY_LANE1 |\
+ INTEL_LT_PHY_LANE0)
+
+static u8 intel_lt_phy_get_owned_lane_mask(struct intel_encoder *encoder)
+{
+ struct intel_digital_port *dig_port = enc_to_dig_port(encoder);
+
+ if (!intel_tc_port_in_dp_alt_mode(dig_port))
+ return INTEL_LT_PHY_BOTH_LANES;
+
+ return intel_tc_port_max_lane_count(dig_port) > 2
+ ? INTEL_LT_PHY_BOTH_LANES : INTEL_LT_PHY_LANE0;
+}
+
+static void
+intel_lt_phy_setup_powerdown(struct intel_encoder *encoder, u8 lane_count)
+{
+ /*
+ * The new PORT_BUF_CTL6 stuff for dc5 entry and exit needs to be handled
+ * by dmc firmware not explicitly mentioned in Bspec. This leaves this
+ * function as a wrapper only but keeping it expecting future changes.
+ */
+ intel_cx0_setup_powerdown(encoder);
+}
+
+static void
+intel_lt_phy_lane_reset(struct intel_encoder *encoder,
+ u8 lane_count)
+{
+ struct intel_display *display = to_intel_display(encoder);
+ enum port port = encoder->port;
+ enum phy phy = intel_encoder_to_phy(encoder);
+ u8 owned_lane_mask = intel_lt_phy_get_owned_lane_mask(encoder);
+ u32 lane_pipe_reset = owned_lane_mask == INTEL_LT_PHY_BOTH_LANES
+ ? XELPDP_LANE_PIPE_RESET(0) | XELPDP_LANE_PIPE_RESET(1)
+ : XELPDP_LANE_PIPE_RESET(0);
+ u32 lane_phy_current_status = owned_lane_mask == INTEL_LT_PHY_BOTH_LANES
+ ? (XELPDP_LANE_PHY_CURRENT_STATUS(0) |
+ XELPDP_LANE_PHY_CURRENT_STATUS(1))
+ : XELPDP_LANE_PHY_CURRENT_STATUS(0);
+ u32 lane_phy_pulse_status = owned_lane_mask == INTEL_LT_PHY_BOTH_LANES
+ ? (XE3PLPDP_LANE_PHY_PULSE_STATUS(0) |
+ XE3PLPDP_LANE_PHY_PULSE_STATUS(1))
+ : XE3PLPDP_LANE_PHY_PULSE_STATUS(0);
+
+ intel_de_rmw(display, XE3PLPD_PORT_BUF_CTL5(port),
+ XE3PLPD_MACCLK_RATE_MASK, XE3PLPD_MACCLK_RATE_DEF);
+
+ intel_de_rmw(display, XELPDP_PORT_BUF_CTL1(display, port),
+ XE3PLPDP_PHY_MODE_MASK, XE3PLPDP_PHY_MODE_DP);
+
+ intel_lt_phy_setup_powerdown(encoder, lane_count);
+
+ intel_de_rmw(display, XE3PLPD_PORT_BUF_CTL5(port),
+ XE3PLPD_MACCLK_RESET_0, 0);
+
+ intel_de_rmw(display, XELPDP_PORT_CLOCK_CTL(display, port),
+ XELPDP_LANE_PCLK_PLL_REQUEST(0),
+ XELPDP_LANE_PCLK_PLL_REQUEST(0));
+
+ if (intel_de_wait_custom(display, XELPDP_PORT_CLOCK_CTL(display, port),
+ XELPDP_LANE_PCLK_PLL_ACK(0),
+ XELPDP_LANE_PCLK_PLL_ACK(0),
+ XE3PLPD_MACCLK_TURNON_LATENCY_US,
+ XE3PLPD_MACCLK_TURNON_LATENCY_MS, NULL))
+ drm_warn(display->drm, "PHY %c PLL MacCLK assertion Ack not done after %dus.\n",
+ phy_name(phy), XE3PLPD_MACCLK_TURNON_LATENCY_MS * 1000);
+
+ intel_de_rmw(display, XELPDP_PORT_CLOCK_CTL(display, port),
+ XELPDP_FORWARD_CLOCK_UNGATE,
+ XELPDP_FORWARD_CLOCK_UNGATE);
+
+ intel_de_rmw(display, XELPDP_PORT_BUF_CTL2(display, port),
+ lane_pipe_reset | lane_phy_pulse_status, 0);
+
+ if (intel_de_wait_custom(display, XELPDP_PORT_BUF_CTL2(display, port),
+ lane_phy_current_status, 0,
+ XE3PLPD_RESET_END_LATENCY_US, 2, NULL))
+ drm_warn(display->drm,
+ "PHY %c failed to bring out of Lane reset after %dus.\n",
+ phy_name(phy), XE3PLPD_RESET_END_LATENCY_US);
+
+ if (intel_de_wait_custom(display, XELPDP_PORT_BUF_CTL2(display, port),
+ lane_phy_pulse_status, lane_phy_pulse_status,
+ XE3PLPD_RATE_CALIB_DONE_LATENCY_US, 0, NULL))
+ drm_warn(display->drm, "PHY %c PLL rate not changed after %dus.\n",
+ phy_name(phy), XE3PLPD_RATE_CALIB_DONE_LATENCY_US);
+
+ intel_de_rmw(display, XELPDP_PORT_BUF_CTL2(display, port), lane_phy_pulse_status, 0);
+}
+
+void intel_lt_phy_pll_enable(struct intel_encoder *encoder,
+ const struct intel_crtc_state *crtc_state)
+{
+ /* 1. Enable MacCLK at default 162 MHz frequency. */
+ intel_lt_phy_lane_reset(encoder, crtc_state->lane_count);
+
+ /* 2. Program PORT_CLOCK_CTL register to configure clock muxes, gating, and SSC. */
+ /* 3. Change owned PHY lanes power to Ready state. */
+ /*
+ * 4. Read the PHY message bus VDR register PHY_VDR_0_Config check enabled PLL type,
+ * encoded rate and encoded mode.
+ */
+ /*
+ * 5. Program the PHY internal PLL registers over PHY message bus for the desired
+ * frequency and protocol type
+ */
+ /* 6. Use the P2P transaction flow */
+ /*
+ * 6.1. Set the PHY VDR register 0xCC4[Rate Control VDR Update] = 1 over PHY message
+ * bus for Owned PHY Lanes.
+ */
+ /*
+ * 6.2. Poll for P2P Transaction Ready = "1" and read the MAC message bus VDR register
+ * at offset 0xC00 for Owned PHY Lanes.
+ */
+ /* 6.3. Clear P2P transaction Ready bit. */
+ /* 7. Program PORT_CLOCK_CTL[PCLK PLL Request LN0] = 0. */
+ /* 8. Poll for PORT_CLOCK_CTL[PCLK PLL Ack LN0]= 0. */
+ /*
+ * 9. Follow the Display Voltage Frequency Switching - Sequence Before Frequency Change.
+ * We handle this step in bxt_set_cdclk()
+ */
+ /* 10. Program DDI_CLK_VALFREQ to match intended DDI clock frequency. */
+ /* 11. Program PORT_CLOCK_CTL[PCLK PLL Request LN0] = 1. */
+ /* 12. Poll for PORT_CLOCK_CTL[PCLK PLL Ack LN0]= 1. */
+ /* 13. Ungate the forward clock by setting PORT_CLOCK_CTL[Forward Clock Ungate] = 1. */
+ /* 14. SW clears PORT_BUF_CTL2 [PHY Pulse Status]. */
+ /*
+ * 15. Clear the PHY VDR register 0xCC4[Rate Control VDR Update] over PHY message bus for
+ * Owned PHY Lanes.
+ */
+ /* 16. Poll for PORT_BUF_CTL2 register PHY Pulse Status = 1 for Owned PHY Lanes. */
+ /* 17. SW clears PORT_BUF_CTL2 [PHY Pulse Status]. */
+ /*
+ * 18. Follow the Display Voltage Frequency Switching - Sequence After Frequency Change.
+ * We handle this step in bxt_set_cdclk()
+ */
+ /* 19. Move the PHY powerdown state to Active and program to enable/disable transmitters */
+}