@@ -448,6 +448,152 @@ static const struct intel_c10mpllb_state * const mtl_c10_edp_tables[] = {
NULL,
};
+/*
+ * HDMI link rates with 38.4 MHz reference clock.
+ */
+
+static const struct intel_c10mpllb_state mtl_c10_hdmi_25_175 = {
+ .clock = 25175,
+ .pll[0] = 0x4,
+ .pll[1] = 0,
+ .pll[2] = 0xB2,
+ .pll[3] = 0,
+ .pll[4] = 0,
+ .pll[5] = 0,
+ .pll[6] = 0,
+ .pll[7] = 0,
+ .pll[8] = 0x20,
+ .pll[9] = 0x1,
+ .pll[10] = 0,
+ .pll[11] = 0,
+ .pll[12] = 0,
+ .pll[13] = 0,
+ .pll[14] = 0,
+ .pll[15] = 0xD,
+ .pll[16] = 0x6,
+ .pll[17] = 0x8F,
+ .pll[18] = 0x84,
+ .pll[19] = 0x23,
+};
+
+static const struct intel_c10mpllb_state mtl_c10_hdmi_27_0 = {
+ .clock = 27000,
+ .pll[0] = 0x34,
+ .pll[1] = 0,
+ .pll[2] = 0xC0,
+ .pll[3] = 0,
+ .pll[4] = 0,
+ .pll[5] = 0,
+ .pll[6] = 0,
+ .pll[7] = 0,
+ .pll[8] = 0x20,
+ .pll[9] = 0x1,
+ .pll[10] = 0,
+ .pll[11] = 0,
+ .pll[12] = 0x80,
+ .pll[13] = 0,
+ .pll[14] = 0,
+ .pll[15] = 0xD,
+ .pll[16] = 0x6,
+ .pll[17] = 0xCF,
+ .pll[18] = 0x84,
+ .pll[19] = 0x23,
+};
+
+static const struct intel_c10mpllb_state mtl_c10_hdmi_74_25 = {
+ .clock = 74250,
+ .pll[0] = 0xF4,
+ .pll[1] = 0,
+ .pll[2] = 0x7A,
+ .pll[3] = 0,
+ .pll[4] = 0,
+ .pll[5] = 0,
+ .pll[6] = 0,
+ .pll[7] = 0,
+ .pll[8] = 0x20,
+ .pll[9] = 0x1,
+ .pll[10] = 0,
+ .pll[11] = 0,
+ .pll[12] = 0x58,
+ .pll[13] = 0,
+ .pll[14] = 0,
+ .pll[15] = 0xB,
+ .pll[16] = 0x6,
+ .pll[17] = 0xF,
+ .pll[18] = 0x85,
+ .pll[19] = 0x23,
+};
+
+static const struct intel_c10mpllb_state mtl_c10_hdmi_148_5 = {
+ .clock = 148500,
+ .pll[0] = 0xF4,
+ .pll[1] = 0,
+ .pll[2] = 0x7A,
+ .pll[3] = 0,
+ .pll[4] = 0,
+ .pll[5] = 0,
+ .pll[6] = 0,
+ .pll[7] = 0,
+ .pll[8] = 0x20,
+ .pll[9] = 0x1,
+ .pll[10] = 0,
+ .pll[11] = 0,
+ .pll[12] = 0x58,
+ .pll[13] = 0,
+ .pll[14] = 0,
+ .pll[15] = 0xA,
+ .pll[16] = 0x6,
+ .pll[17] = 0xF,
+ .pll[18] = 0x85,
+ .pll[19] = 0x23,
+};
+
+static const struct intel_c10mpllb_state mtl_c10_hdmi_594 = {
+ .clock = 594000,
+ .pll[0] = 0xF4,
+ .pll[1] = 0,
+ .pll[2] = 0x7A,
+ .pll[3] = 0,
+ .pll[4] = 0,
+ .pll[5] = 0,
+ .pll[6] = 0,
+ .pll[7] = 0,
+ .pll[8] = 0x20,
+ .pll[9] = 0x1,
+ .pll[10] = 0,
+ .pll[11] = 0,
+ .pll[12] = 0x58,
+ .pll[13] = 0,
+ .pll[14] = 0,
+ .pll[15] = 0x8,
+ .pll[16] = 0x6,
+ .pll[17] = 0xF,
+ .pll[18] = 0x85,
+ .pll[19] = 0x23,
+};
+
+static const struct intel_c10mpllb_state * const mtl_c10_hdmi_tables[] = {
+ &mtl_c10_hdmi_25_175,
+ &mtl_c10_hdmi_27_0,
+ &mtl_c10_hdmi_74_25,
+ &mtl_c10_hdmi_148_5,
+ &mtl_c10_hdmi_594,
+ NULL,
+};
+
+int intel_c10_phy_check_hdmi_link_rate(int clock)
+{
+ const struct intel_c10mpllb_state * const *tables = mtl_c10_hdmi_tables;
+ int i;
+
+ for (i = 0; tables[i]; i++) {
+ if (clock == tables[i]->clock)
+ return MODE_OK;
+ }
+
+ return MODE_CLOCK_RANGE;
+}
+
static const struct intel_c10mpllb_state * const *
intel_c10_mpllb_tables_get(struct intel_crtc_state *crtc_state,
struct intel_encoder *encoder)
@@ -457,9 +603,10 @@ intel_c10_mpllb_tables_get(struct intel_crtc_state *crtc_state,
return mtl_c10_edp_tables;
else
return mtl_c10_dp_tables;
+ } else if (intel_crtc_has_type(crtc_state, INTEL_OUTPUT_HDMI)) {
+ return mtl_c10_hdmi_tables;
}
- /* TODO: Add HDMI Support */
MISSING_CASE(encoder->type);
return NULL;
}
@@ -467,9 +614,20 @@ intel_c10_mpllb_tables_get(struct intel_crtc_state *crtc_state,
static int intel_c10mpllb_calc_state(struct intel_crtc_state *crtc_state,
struct intel_encoder *encoder)
{
+ struct drm_i915_private *i915 = to_i915(encoder->base.dev);
const struct intel_c10mpllb_state * const *tables;
+ enum phy phy = intel_port_to_phy(i915, encoder->port);
int i;
+ if (intel_crtc_has_type(crtc_state, INTEL_OUTPUT_HDMI)) {
+ if (intel_c10_phy_check_hdmi_link_rate(crtc_state->port_clock)
+ != MODE_OK) {
+ drm_dbg_kms(&i915->drm, "Can't support HDMI link rate %d on phy %c.\n",
+ crtc_state->port_clock, phy_name(phy));
+ return -EINVAL;
+ }
+ }
+
tables = intel_c10_mpllb_tables_get(crtc_state, encoder);
if (!tables)
return -EINVAL;
@@ -521,7 +679,8 @@ void intel_c10mpllb_readout_hw_state(struct intel_encoder *encoder,
cmn = intel_cx0_read(i915, encoder->port, lane, PHY_C10_VDR_CMN(0));
tx0 = intel_cx0_read(i915, encoder->port, lane, PHY_C10_VDR_TX(0));
- if (tx0 != C10_TX0_VAL || cmn != C10_CMN0_DP_VAL)
+ if (tx0 != C10_TX0_VAL || cmn != (intel_encoder_is_dp(encoder) ?
+ C10_CMN0_DP_VAL : C10_CMN0_HDMI_VAL))
drm_warn(&i915->drm, "Unexpected tx: %x or cmn: %x for phy: %c.\n",
tx0, cmn, phy_name(phy));
}
@@ -537,11 +696,10 @@ static void intel_c10_pll_program(struct drm_i915_private *i915,
INTEL_CX0_LANE0;
enum intel_cx0_lanes follower_lane = lane_reversal ? INTEL_CX0_LANE0 :
INTEL_CX0_LANE1;
-
int i;
struct intel_dp *intel_dp;
bool use_ssc = false;
- u8 cmn0 = 0;
+ u8 cmn0;
if (intel_crtc_has_dp_encoder(crtc_state)) {
intel_dp = enc_to_intel_dp(encoder);
@@ -552,6 +710,8 @@ static void intel_c10_pll_program(struct drm_i915_private *i915,
use_ssc = false;
cmn0 = C10_CMN0_DP_VAL;
+ } else {
+ cmn0 = C10_CMN0_HDMI_VAL;
}
intel_cx0_write(i915, encoder->port, INTEL_CX0_BOTH_LANES, PHY_C10_VDR_CONTROL(1),
@@ -96,6 +96,7 @@ enum intel_cx0_lanes {
#define C10_PLL15_TXCLKDIV_MASK REG_GENMASK8(2, 0)
#define PHY_C10_VDR_CMN(idx) (0xC20 + (idx))
#define C10_CMN0_DP_VAL 0x21
+#define C10_CMN0_HDMI_VAL 0x1
#define C10_CMN3_TXVBOOST_MASK REG_GENMASK8(7, 5)
#define C10_CMN3_TXVBOOST(val) REG_FIELD_PREP8(C10_CMN3_TXVBOOST_MASK, val)
#define PHY_C10_VDR_TX(idx) (0xC30 + (idx))
@@ -141,5 +142,6 @@ int intel_c10mpllb_calc_port_clock(struct intel_encoder *encoder,
const struct intel_c10mpllb_state *pll_state);
void intel_c10mpllb_state_verify(struct intel_atomic_state *state,
struct intel_crtc_state *new_crtc_state);
+int intel_c10_phy_check_hdmi_link_rate(int clock);
#endif /* __INTEL_CX0_PHY_H__ */
@@ -44,6 +44,7 @@
#include "i915_drv.h"
#include "intel_atomic.h"
#include "intel_connector.h"
+#include "intel_cx0_phy.h"
#include "intel_ddi.h"
#include "intel_de.h"
#include "intel_display_types.h"
@@ -1875,7 +1876,9 @@ hdmi_port_clock_valid(struct intel_hdmi *hdmi,
* FIXME: We will hopefully get an algorithmic way of programming
* the MPLLB for HDMI in the future.
*/
- if (IS_DG2(dev_priv))
+ if (IS_METEORLAKE(dev_priv))
+ return intel_c10_phy_check_hdmi_link_rate(clock);
+ else if (IS_DG2(dev_priv))
return intel_snps_phy_check_hdmi_link_rate(clock);
return MODE_OK;