| File: | dev/pci/drm/i915/display/intel_combo_phy.c |
| Warning: | line 425, column 33 The left operand of '+' is a garbage value due to array index out of bounds |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
| 1 | // SPDX-License-Identifier: MIT | |||
| 2 | /* | |||
| 3 | * Copyright © 2018 Intel Corporation | |||
| 4 | */ | |||
| 5 | ||||
| 6 | #include "intel_combo_phy.h" | |||
| 7 | #include "intel_combo_phy_regs.h" | |||
| 8 | #include "intel_de.h" | |||
| 9 | #include "intel_display_types.h" | |||
| 10 | ||||
| 11 | #define for_each_combo_phy(__dev_priv, __phy)for ((__phy) = PHY_A; (__phy) < I915_MAX_PHYS; (__phy)++) if (!(intel_phy_is_combo(__dev_priv, __phy))) {} else \ | |||
| 12 | for ((__phy) = PHY_A; (__phy) < I915_MAX_PHYS; (__phy)++) \ | |||
| 13 | for_each_if(intel_phy_is_combo(__dev_priv, __phy))if (!(intel_phy_is_combo(__dev_priv, __phy))) {} else | |||
| 14 | ||||
| 15 | #define for_each_combo_phy_reverse(__dev_priv, __phy)for ((__phy) = I915_MAX_PHYS; (__phy)-- > PHY_A;) if (!(intel_phy_is_combo (__dev_priv, __phy))) {} else \ | |||
| 16 | for ((__phy) = I915_MAX_PHYS; (__phy)-- > PHY_A;) \ | |||
| 17 | for_each_if(intel_phy_is_combo(__dev_priv, __phy))if (!(intel_phy_is_combo(__dev_priv, __phy))) {} else | |||
| 18 | ||||
| 19 | enum { | |||
| 20 | PROCMON_0_85V_DOT_0, | |||
| 21 | PROCMON_0_95V_DOT_0, | |||
| 22 | PROCMON_0_95V_DOT_1, | |||
| 23 | PROCMON_1_05V_DOT_0, | |||
| 24 | PROCMON_1_05V_DOT_1, | |||
| 25 | }; | |||
| 26 | ||||
| 27 | static const struct icl_procmon { | |||
| 28 | const char *name; | |||
| 29 | u32 dw1, dw9, dw10; | |||
| 30 | } icl_procmon_values[] = { | |||
| 31 | [PROCMON_0_85V_DOT_0] = { | |||
| 32 | .name = "0.85V dot0 (low-voltage)", | |||
| 33 | .dw1 = 0x00000000, .dw9 = 0x62AB67BB, .dw10 = 0x51914F96, | |||
| 34 | }, | |||
| 35 | [PROCMON_0_95V_DOT_0] = { | |||
| 36 | .name = "0.95V dot0", | |||
| 37 | .dw1 = 0x00000000, .dw9 = 0x86E172C7, .dw10 = 0x77CA5EAB, | |||
| 38 | }, | |||
| 39 | [PROCMON_0_95V_DOT_1] = { | |||
| 40 | .name = "0.95V dot1", | |||
| 41 | .dw1 = 0x00000000, .dw9 = 0x93F87FE1, .dw10 = 0x8AE871C5, | |||
| 42 | }, | |||
| 43 | [PROCMON_1_05V_DOT_0] = { | |||
| 44 | .name = "1.05V dot0", | |||
| 45 | .dw1 = 0x00000000, .dw9 = 0x98FA82DD, .dw10 = 0x89E46DC1, | |||
| 46 | }, | |||
| 47 | [PROCMON_1_05V_DOT_1] = { | |||
| 48 | .name = "1.05V dot1", | |||
| 49 | .dw1 = 0x00440000, .dw9 = 0x9A00AB25, .dw10 = 0x8AE38FF1, | |||
| 50 | }, | |||
| 51 | }; | |||
| 52 | ||||
| 53 | static const struct icl_procmon * | |||
| 54 | icl_get_procmon_ref_values(struct drm_i915_privateinteldrm_softc *dev_priv, enum phy phy) | |||
| 55 | { | |||
| 56 | const struct icl_procmon *procmon; | |||
| 57 | u32 val; | |||
| 58 | ||||
| 59 | val = intel_de_read(dev_priv, ICL_PORT_COMP_DW3(phy)((const i915_reg_t){ .reg = (((((const u32 []){ 0x162000, 0x6C000 , 0x160000, 0x161000, 0x16B000 })[phy]) + 0x100 + 4 * (3))) } )); | |||
| 60 | switch (val & (PROCESS_INFO_MASK(7 << 26) | VOLTAGE_INFO_MASK(3 << 24))) { | |||
| 61 | default: | |||
| 62 | MISSING_CASE(val)({ int __ret = !!(1); if (__ret) printf("Missing case (%s == %ld)\n" , "val", (long)(val)); __builtin_expect(!!(__ret), 0); }); | |||
| 63 | fallthroughdo {} while (0); | |||
| 64 | case VOLTAGE_INFO_0_85V(0 << 24) | PROCESS_INFO_DOT_0(0 << 26): | |||
| 65 | procmon = &icl_procmon_values[PROCMON_0_85V_DOT_0]; | |||
| 66 | break; | |||
| 67 | case VOLTAGE_INFO_0_95V(1 << 24) | PROCESS_INFO_DOT_0(0 << 26): | |||
| 68 | procmon = &icl_procmon_values[PROCMON_0_95V_DOT_0]; | |||
| 69 | break; | |||
| 70 | case VOLTAGE_INFO_0_95V(1 << 24) | PROCESS_INFO_DOT_1(1 << 26): | |||
| 71 | procmon = &icl_procmon_values[PROCMON_0_95V_DOT_1]; | |||
| 72 | break; | |||
| 73 | case VOLTAGE_INFO_1_05V(2 << 24) | PROCESS_INFO_DOT_0(0 << 26): | |||
| 74 | procmon = &icl_procmon_values[PROCMON_1_05V_DOT_0]; | |||
| 75 | break; | |||
| 76 | case VOLTAGE_INFO_1_05V(2 << 24) | PROCESS_INFO_DOT_1(1 << 26): | |||
| 77 | procmon = &icl_procmon_values[PROCMON_1_05V_DOT_1]; | |||
| 78 | break; | |||
| 79 | } | |||
| 80 | ||||
| 81 | return procmon; | |||
| 82 | } | |||
| 83 | ||||
| 84 | static void icl_set_procmon_ref_values(struct drm_i915_privateinteldrm_softc *dev_priv, | |||
| 85 | enum phy phy) | |||
| 86 | { | |||
| 87 | const struct icl_procmon *procmon; | |||
| 88 | u32 val; | |||
| 89 | ||||
| 90 | procmon = icl_get_procmon_ref_values(dev_priv, phy); | |||
| 91 | ||||
| 92 | val = intel_de_read(dev_priv, ICL_PORT_COMP_DW1(phy)((const i915_reg_t){ .reg = (((((const u32 []){ 0x162000, 0x6C000 , 0x160000, 0x161000, 0x16B000 })[phy]) + 0x100 + 4 * (1))) } )); | |||
| 93 | val &= ~((0xff << 16) | 0xff); | |||
| 94 | val |= procmon->dw1; | |||
| 95 | intel_de_write(dev_priv, ICL_PORT_COMP_DW1(phy)((const i915_reg_t){ .reg = (((((const u32 []){ 0x162000, 0x6C000 , 0x160000, 0x161000, 0x16B000 })[phy]) + 0x100 + 4 * (1))) } ), val); | |||
| 96 | ||||
| 97 | intel_de_write(dev_priv, ICL_PORT_COMP_DW9(phy)((const i915_reg_t){ .reg = (((((const u32 []){ 0x162000, 0x6C000 , 0x160000, 0x161000, 0x16B000 })[phy]) + 0x100 + 4 * (9))) } ), procmon->dw9); | |||
| 98 | intel_de_write(dev_priv, ICL_PORT_COMP_DW10(phy)((const i915_reg_t){ .reg = (((((const u32 []){ 0x162000, 0x6C000 , 0x160000, 0x161000, 0x16B000 })[phy]) + 0x100 + 4 * (10))) } ), procmon->dw10); | |||
| 99 | } | |||
| 100 | ||||
| 101 | static bool_Bool check_phy_reg(struct drm_i915_privateinteldrm_softc *dev_priv, | |||
| 102 | enum phy phy, i915_reg_t reg, u32 mask, | |||
| 103 | u32 expected_val) | |||
| 104 | { | |||
| 105 | u32 val = intel_de_read(dev_priv, reg); | |||
| 106 | ||||
| 107 | if ((val & mask) != expected_val) { | |||
| 108 | drm_dbg(&dev_priv->drm,__drm_dev_dbg(((void *)0), (&dev_priv->drm) ? (&dev_priv ->drm)->dev : ((void *)0), DRM_UT_DRIVER, "Combo PHY %c reg %08x state mismatch: " "current %08x mask %08x expected %08x\n", ((phy) + 'A'), reg .reg, val, mask, expected_val) | |||
| 109 | "Combo PHY %c reg %08x state mismatch: "__drm_dev_dbg(((void *)0), (&dev_priv->drm) ? (&dev_priv ->drm)->dev : ((void *)0), DRM_UT_DRIVER, "Combo PHY %c reg %08x state mismatch: " "current %08x mask %08x expected %08x\n", ((phy) + 'A'), reg .reg, val, mask, expected_val) | |||
| 110 | "current %08x mask %08x expected %08x\n",__drm_dev_dbg(((void *)0), (&dev_priv->drm) ? (&dev_priv ->drm)->dev : ((void *)0), DRM_UT_DRIVER, "Combo PHY %c reg %08x state mismatch: " "current %08x mask %08x expected %08x\n", ((phy) + 'A'), reg .reg, val, mask, expected_val) | |||
| 111 | phy_name(phy),__drm_dev_dbg(((void *)0), (&dev_priv->drm) ? (&dev_priv ->drm)->dev : ((void *)0), DRM_UT_DRIVER, "Combo PHY %c reg %08x state mismatch: " "current %08x mask %08x expected %08x\n", ((phy) + 'A'), reg .reg, val, mask, expected_val) | |||
| 112 | reg.reg, val, mask, expected_val)__drm_dev_dbg(((void *)0), (&dev_priv->drm) ? (&dev_priv ->drm)->dev : ((void *)0), DRM_UT_DRIVER, "Combo PHY %c reg %08x state mismatch: " "current %08x mask %08x expected %08x\n", ((phy) + 'A'), reg .reg, val, mask, expected_val); | |||
| 113 | return false0; | |||
| 114 | } | |||
| 115 | ||||
| 116 | return true1; | |||
| 117 | } | |||
| 118 | ||||
| 119 | static bool_Bool icl_verify_procmon_ref_values(struct drm_i915_privateinteldrm_softc *dev_priv, | |||
| 120 | enum phy phy) | |||
| 121 | { | |||
| 122 | const struct icl_procmon *procmon; | |||
| 123 | bool_Bool ret; | |||
| 124 | ||||
| 125 | procmon = icl_get_procmon_ref_values(dev_priv, phy); | |||
| 126 | ||||
| 127 | drm_dbg_kms(&dev_priv->drm,__drm_dev_dbg(((void *)0), (&dev_priv->drm) ? (&dev_priv ->drm)->dev : ((void *)0), DRM_UT_KMS, "Combo PHY %c Voltage/Process Info : %s\n" , ((phy) + 'A'), procmon->name) | |||
| 128 | "Combo PHY %c Voltage/Process Info : %s\n",__drm_dev_dbg(((void *)0), (&dev_priv->drm) ? (&dev_priv ->drm)->dev : ((void *)0), DRM_UT_KMS, "Combo PHY %c Voltage/Process Info : %s\n" , ((phy) + 'A'), procmon->name) | |||
| 129 | phy_name(phy), procmon->name)__drm_dev_dbg(((void *)0), (&dev_priv->drm) ? (&dev_priv ->drm)->dev : ((void *)0), DRM_UT_KMS, "Combo PHY %c Voltage/Process Info : %s\n" , ((phy) + 'A'), procmon->name); | |||
| 130 | ||||
| 131 | ret = check_phy_reg(dev_priv, phy, ICL_PORT_COMP_DW1(phy)((const i915_reg_t){ .reg = (((((const u32 []){ 0x162000, 0x6C000 , 0x160000, 0x161000, 0x16B000 })[phy]) + 0x100 + 4 * (1))) } ), | |||
| 132 | (0xff << 16) | 0xff, procmon->dw1); | |||
| 133 | ret &= check_phy_reg(dev_priv, phy, ICL_PORT_COMP_DW9(phy)((const i915_reg_t){ .reg = (((((const u32 []){ 0x162000, 0x6C000 , 0x160000, 0x161000, 0x16B000 })[phy]) + 0x100 + 4 * (9))) } ), | |||
| 134 | -1U, procmon->dw9); | |||
| 135 | ret &= check_phy_reg(dev_priv, phy, ICL_PORT_COMP_DW10(phy)((const i915_reg_t){ .reg = (((((const u32 []){ 0x162000, 0x6C000 , 0x160000, 0x161000, 0x16B000 })[phy]) + 0x100 + 4 * (10))) } ), | |||
| 136 | -1U, procmon->dw10); | |||
| 137 | ||||
| 138 | return ret; | |||
| 139 | } | |||
| 140 | ||||
| 141 | static bool_Bool has_phy_misc(struct drm_i915_privateinteldrm_softc *i915, enum phy phy) | |||
| 142 | { | |||
| 143 | /* | |||
| 144 | * Some platforms only expect PHY_MISC to be programmed for PHY-A and | |||
| 145 | * PHY-B and may not even have instances of the register for the | |||
| 146 | * other combo PHY's. | |||
| 147 | * | |||
| 148 | * ADL-S technically has three instances of PHY_MISC, but only requires | |||
| 149 | * that we program it for PHY A. | |||
| 150 | */ | |||
| 151 | ||||
| 152 | if (IS_ALDERLAKE_S(i915)IS_PLATFORM(i915, INTEL_ALDERLAKE_S)) | |||
| 153 | return phy == PHY_A; | |||
| 154 | else if (IS_JSL_EHL(i915)(IS_PLATFORM(i915, INTEL_JASPERLAKE) || IS_PLATFORM(i915, INTEL_ELKHARTLAKE )) || | |||
| 155 | IS_ROCKETLAKE(i915)IS_PLATFORM(i915, INTEL_ROCKETLAKE) || | |||
| 156 | IS_DG1(i915)IS_PLATFORM(i915, INTEL_DG1)) | |||
| 157 | return phy < PHY_C; | |||
| 158 | ||||
| 159 | return true1; | |||
| 160 | } | |||
| 161 | ||||
| 162 | static bool_Bool icl_combo_phy_enabled(struct drm_i915_privateinteldrm_softc *dev_priv, | |||
| 163 | enum phy phy) | |||
| 164 | { | |||
| 165 | /* The PHY C added by EHL has no PHY_MISC register */ | |||
| 166 | if (!has_phy_misc(dev_priv, phy)) | |||
| 167 | return intel_de_read(dev_priv, ICL_PORT_COMP_DW0(phy)((const i915_reg_t){ .reg = (((((const u32 []){ 0x162000, 0x6C000 , 0x160000, 0x161000, 0x16B000 })[phy]) + 0x100 + 4 * (0))) } )) & COMP_INIT(1 << 31); | |||
| 168 | else | |||
| 169 | return !(intel_de_read(dev_priv, ICL_PHY_MISC(phy)((const i915_reg_t){ .reg = (((0x64C00) + (phy) * ((0x64C04) - (0x64C00)))) })) & | |||
| 170 | ICL_PHY_MISC_DE_IO_COMP_PWR_DOWN(1 << 23)) && | |||
| 171 | (intel_de_read(dev_priv, ICL_PORT_COMP_DW0(phy)((const i915_reg_t){ .reg = (((((const u32 []){ 0x162000, 0x6C000 , 0x160000, 0x161000, 0x16B000 })[phy]) + 0x100 + 4 * (0))) } )) & COMP_INIT(1 << 31)); | |||
| 172 | } | |||
| 173 | ||||
| 174 | static bool_Bool ehl_vbt_ddi_d_present(struct drm_i915_privateinteldrm_softc *i915) | |||
| 175 | { | |||
| 176 | bool_Bool ddi_a_present = intel_bios_is_port_present(i915, PORT_A); | |||
| 177 | bool_Bool ddi_d_present = intel_bios_is_port_present(i915, PORT_D); | |||
| 178 | bool_Bool dsi_present = intel_bios_is_dsi_present(i915, NULL((void *)0)); | |||
| 179 | ||||
| 180 | /* | |||
| 181 | * VBT's 'dvo port' field for child devices references the DDI, not | |||
| 182 | * the PHY. So if combo PHY A is wired up to drive an external | |||
| 183 | * display, we should see a child device present on PORT_D and | |||
| 184 | * nothing on PORT_A and no DSI. | |||
| 185 | */ | |||
| 186 | if (ddi_d_present && !ddi_a_present && !dsi_present) | |||
| 187 | return true1; | |||
| 188 | ||||
| 189 | /* | |||
| 190 | * If we encounter a VBT that claims to have an external display on | |||
| 191 | * DDI-D _and_ an internal display on DDI-A/DSI leave an error message | |||
| 192 | * in the log and let the internal display win. | |||
| 193 | */ | |||
| 194 | if (ddi_d_present) | |||
| 195 | drm_err(&i915->drm,printf("drm:pid%d:%s *ERROR* " "[drm] " "*ERROR* " "VBT claims to have both internal and external displays on PHY A. Configuring for internal.\n" , ({struct cpu_info *__ci; asm volatile("movq %%gs:%P1,%0" : "=r" (__ci) :"n" (__builtin_offsetof(struct cpu_info, ci_self))); __ci;})->ci_curproc->p_p->ps_pid, __func__) | |||
| 196 | "VBT claims to have both internal and external displays on PHY A. Configuring for internal.\n")printf("drm:pid%d:%s *ERROR* " "[drm] " "*ERROR* " "VBT claims to have both internal and external displays on PHY A. Configuring for internal.\n" , ({struct cpu_info *__ci; asm volatile("movq %%gs:%P1,%0" : "=r" (__ci) :"n" (__builtin_offsetof(struct cpu_info, ci_self))); __ci;})->ci_curproc->p_p->ps_pid, __func__); | |||
| 197 | ||||
| 198 | return false0; | |||
| 199 | } | |||
| 200 | ||||
| 201 | static bool_Bool phy_is_master(struct drm_i915_privateinteldrm_softc *dev_priv, enum phy phy) | |||
| 202 | { | |||
| 203 | /* | |||
| 204 | * Certain PHYs are connected to compensation resistors and act | |||
| 205 | * as masters to other PHYs. | |||
| 206 | * | |||
| 207 | * ICL,TGL: | |||
| 208 | * A(master) -> B(slave), C(slave) | |||
| 209 | * RKL,DG1: | |||
| 210 | * A(master) -> B(slave) | |||
| 211 | * C(master) -> D(slave) | |||
| 212 | * ADL-S: | |||
| 213 | * A(master) -> B(slave), C(slave) | |||
| 214 | * D(master) -> E(slave) | |||
| 215 | * | |||
| 216 | * We must set the IREFGEN bit for any PHY acting as a master | |||
| 217 | * to another PHY. | |||
| 218 | */ | |||
| 219 | if (phy == PHY_A) | |||
| 220 | return true1; | |||
| 221 | else if (IS_ALDERLAKE_S(dev_priv)IS_PLATFORM(dev_priv, INTEL_ALDERLAKE_S)) | |||
| 222 | return phy == PHY_D; | |||
| 223 | else if (IS_DG1(dev_priv)IS_PLATFORM(dev_priv, INTEL_DG1) || IS_ROCKETLAKE(dev_priv)IS_PLATFORM(dev_priv, INTEL_ROCKETLAKE)) | |||
| 224 | return phy == PHY_C; | |||
| 225 | ||||
| 226 | return false0; | |||
| 227 | } | |||
| 228 | ||||
| 229 | static bool_Bool icl_combo_phy_verify_state(struct drm_i915_privateinteldrm_softc *dev_priv, | |||
| 230 | enum phy phy) | |||
| 231 | { | |||
| 232 | bool_Bool ret = true1; | |||
| 233 | u32 expected_val = 0; | |||
| 234 | ||||
| 235 | if (!icl_combo_phy_enabled(dev_priv, phy)) | |||
| 236 | return false0; | |||
| 237 | ||||
| 238 | if (DISPLAY_VER(dev_priv)((&(dev_priv)->__runtime)->display.ip.ver) >= 12) { | |||
| 239 | ret &= check_phy_reg(dev_priv, phy, ICL_PORT_TX_DW8_LN(0, phy)((const i915_reg_t){ .reg = (((((const u32 []){ 0x162000, 0x6C000 , 0x160000, 0x161000, 0x16B000 })[phy]) + (0x880 + (0) * 0x100 ) + 4 * (8))) }), | |||
| 240 | ICL_PORT_TX_DW8_ODCC_CLK_SEL((u32)((1UL << (31)) + 0)) | | |||
| 241 | ICL_PORT_TX_DW8_ODCC_CLK_DIV_SEL_MASK((u32)((((~0UL) >> (64 - (30) - 1)) & ((~0UL) << (29))) + 0)), | |||
| 242 | ICL_PORT_TX_DW8_ODCC_CLK_SEL((u32)((1UL << (31)) + 0)) | | |||
| 243 | ICL_PORT_TX_DW8_ODCC_CLK_DIV_SEL_DIV2((u32)((((typeof(((u32)((((~0UL) >> (64 - (30) - 1)) & ((~0UL) << (29))) + 0))))(0x1) << (__builtin_ffsll (((u32)((((~0UL) >> (64 - (30) - 1)) & ((~0UL) << (29))) + 0))) - 1)) & (((u32)((((~0UL) >> (64 - (30 ) - 1)) & ((~0UL) << (29))) + 0)))) + 0 + 0 + 0 + 0 ))); | |||
| 244 | ||||
| 245 | ret &= check_phy_reg(dev_priv, phy, ICL_PORT_PCS_DW1_LN(0, phy)((const i915_reg_t){ .reg = (((((const u32 []){ 0x162000, 0x6C000 , 0x160000, 0x161000, 0x16B000 })[phy]) + (0x800 + (0) * 0x100 ) + 4 * (1))) }), | |||
| 246 | DCC_MODE_SELECT_MASK(0x3 << 20), | |||
| 247 | DCC_MODE_SELECT_CONTINUOSLY(0x3 << 20)); | |||
| 248 | } | |||
| 249 | ||||
| 250 | ret &= icl_verify_procmon_ref_values(dev_priv, phy); | |||
| 251 | ||||
| 252 | if (phy_is_master(dev_priv, phy)) { | |||
| 253 | ret &= check_phy_reg(dev_priv, phy, ICL_PORT_COMP_DW8(phy)((const i915_reg_t){ .reg = (((((const u32 []){ 0x162000, 0x6C000 , 0x160000, 0x161000, 0x16B000 })[phy]) + 0x100 + 4 * (8))) } ), | |||
| 254 | IREFGEN(1 << 24), IREFGEN(1 << 24)); | |||
| 255 | ||||
| 256 | if (IS_JSL_EHL(dev_priv)(IS_PLATFORM(dev_priv, INTEL_JASPERLAKE) || IS_PLATFORM(dev_priv , INTEL_ELKHARTLAKE))) { | |||
| 257 | if (ehl_vbt_ddi_d_present(dev_priv)) | |||
| 258 | expected_val = ICL_PHY_MISC_MUX_DDID(1 << 28); | |||
| 259 | ||||
| 260 | ret &= check_phy_reg(dev_priv, phy, ICL_PHY_MISC(phy)((const i915_reg_t){ .reg = (((0x64C00) + (phy) * ((0x64C04) - (0x64C00)))) }), | |||
| 261 | ICL_PHY_MISC_MUX_DDID(1 << 28), | |||
| 262 | expected_val); | |||
| 263 | } | |||
| 264 | } | |||
| 265 | ||||
| 266 | ret &= check_phy_reg(dev_priv, phy, ICL_PORT_CL_DW5(phy)((const i915_reg_t){ .reg = (((((const u32 []){ 0x162000, 0x6C000 , 0x160000, 0x161000, 0x16B000 })[phy]) + 4 * (5))) }), | |||
| 267 | CL_POWER_DOWN_ENABLE(1 << 4), CL_POWER_DOWN_ENABLE(1 << 4)); | |||
| 268 | ||||
| 269 | return ret; | |||
| 270 | } | |||
| 271 | ||||
| 272 | void intel_combo_phy_power_up_lanes(struct drm_i915_privateinteldrm_softc *dev_priv, | |||
| 273 | enum phy phy, bool_Bool is_dsi, | |||
| 274 | int lane_count, bool_Bool lane_reversal) | |||
| 275 | { | |||
| 276 | u8 lane_mask; | |||
| 277 | u32 val; | |||
| 278 | ||||
| 279 | if (is_dsi) { | |||
| 280 | drm_WARN_ON(&dev_priv->drm, lane_reversal)({ int __ret = !!((lane_reversal)); if (__ret) printf("%s %s: " "%s", dev_driver_string(((&dev_priv->drm))->dev), "" , "drm_WARN_ON(" "lane_reversal" ")"); __builtin_expect(!!(__ret ), 0); }); | |||
| 281 | ||||
| 282 | switch (lane_count) { | |||
| 283 | case 1: | |||
| 284 | lane_mask = PWR_DOWN_LN_3_1_0(0xb << 4); | |||
| 285 | break; | |||
| 286 | case 2: | |||
| 287 | lane_mask = PWR_DOWN_LN_3_1(0xa << 4); | |||
| 288 | break; | |||
| 289 | case 3: | |||
| 290 | lane_mask = PWR_DOWN_LN_3(0x8 << 4); | |||
| 291 | break; | |||
| 292 | default: | |||
| 293 | MISSING_CASE(lane_count)({ int __ret = !!(1); if (__ret) printf("Missing case (%s == %ld)\n" , "lane_count", (long)(lane_count)); __builtin_expect(!!(__ret ), 0); }); | |||
| 294 | fallthroughdo {} while (0); | |||
| 295 | case 4: | |||
| 296 | lane_mask = PWR_UP_ALL_LANES(0x0 << 4); | |||
| 297 | break; | |||
| 298 | } | |||
| 299 | } else { | |||
| 300 | switch (lane_count) { | |||
| 301 | case 1: | |||
| 302 | lane_mask = lane_reversal ? PWR_DOWN_LN_2_1_0(0x7 << 4) : | |||
| 303 | PWR_DOWN_LN_3_2_1(0xe << 4); | |||
| 304 | break; | |||
| 305 | case 2: | |||
| 306 | lane_mask = lane_reversal ? PWR_DOWN_LN_1_0(0x3 << 4) : | |||
| 307 | PWR_DOWN_LN_3_2(0xc << 4); | |||
| 308 | break; | |||
| 309 | default: | |||
| 310 | MISSING_CASE(lane_count)({ int __ret = !!(1); if (__ret) printf("Missing case (%s == %ld)\n" , "lane_count", (long)(lane_count)); __builtin_expect(!!(__ret ), 0); }); | |||
| 311 | fallthroughdo {} while (0); | |||
| 312 | case 4: | |||
| 313 | lane_mask = PWR_UP_ALL_LANES(0x0 << 4); | |||
| 314 | break; | |||
| 315 | } | |||
| 316 | } | |||
| 317 | ||||
| 318 | val = intel_de_read(dev_priv, ICL_PORT_CL_DW10(phy)((const i915_reg_t){ .reg = (((((const u32 []){ 0x162000, 0x6C000 , 0x160000, 0x161000, 0x16B000 })[phy]) + 4 * (10))) })); | |||
| 319 | val &= ~PWR_DOWN_LN_MASK(0xf << 4); | |||
| 320 | val |= lane_mask; | |||
| 321 | intel_de_write(dev_priv, ICL_PORT_CL_DW10(phy)((const i915_reg_t){ .reg = (((((const u32 []){ 0x162000, 0x6C000 , 0x160000, 0x161000, 0x16B000 })[phy]) + 4 * (10))) }), val); | |||
| 322 | } | |||
| 323 | ||||
| 324 | static void icl_combo_phys_init(struct drm_i915_privateinteldrm_softc *dev_priv) | |||
| 325 | { | |||
| 326 | enum phy phy; | |||
| 327 | ||||
| 328 | for_each_combo_phy(dev_priv, phy)for ((phy) = PHY_A; (phy) < I915_MAX_PHYS; (phy)++) if (!( intel_phy_is_combo(dev_priv, phy))) {} else { | |||
| 329 | u32 val; | |||
| 330 | ||||
| 331 | if (icl_combo_phy_verify_state(dev_priv, phy)) { | |||
| 332 | drm_dbg(&dev_priv->drm,__drm_dev_dbg(((void *)0), (&dev_priv->drm) ? (&dev_priv ->drm)->dev : ((void *)0), DRM_UT_DRIVER, "Combo PHY %c already enabled, won't reprogram it.\n" , ((phy) + 'A')) | |||
| 333 | "Combo PHY %c already enabled, won't reprogram it.\n",__drm_dev_dbg(((void *)0), (&dev_priv->drm) ? (&dev_priv ->drm)->dev : ((void *)0), DRM_UT_DRIVER, "Combo PHY %c already enabled, won't reprogram it.\n" , ((phy) + 'A')) | |||
| 334 | phy_name(phy))__drm_dev_dbg(((void *)0), (&dev_priv->drm) ? (&dev_priv ->drm)->dev : ((void *)0), DRM_UT_DRIVER, "Combo PHY %c already enabled, won't reprogram it.\n" , ((phy) + 'A')); | |||
| 335 | continue; | |||
| 336 | } | |||
| 337 | ||||
| 338 | if (!has_phy_misc(dev_priv, phy)) | |||
| 339 | goto skip_phy_misc; | |||
| 340 | ||||
| 341 | /* | |||
| 342 | * EHL's combo PHY A can be hooked up to either an external | |||
| 343 | * display (via DDI-D) or an internal display (via DDI-A or | |||
| 344 | * the DSI DPHY). This is a motherboard design decision that | |||
| 345 | * can't be changed on the fly, so initialize the PHY's mux | |||
| 346 | * based on whether our VBT indicates the presence of any | |||
| 347 | * "internal" child devices. | |||
| 348 | */ | |||
| 349 | val = intel_de_read(dev_priv, ICL_PHY_MISC(phy)((const i915_reg_t){ .reg = (((0x64C00) + (phy) * ((0x64C04) - (0x64C00)))) })); | |||
| 350 | if (IS_JSL_EHL(dev_priv)(IS_PLATFORM(dev_priv, INTEL_JASPERLAKE) || IS_PLATFORM(dev_priv , INTEL_ELKHARTLAKE)) && phy == PHY_A) { | |||
| 351 | val &= ~ICL_PHY_MISC_MUX_DDID(1 << 28); | |||
| 352 | ||||
| 353 | if (ehl_vbt_ddi_d_present(dev_priv)) | |||
| 354 | val |= ICL_PHY_MISC_MUX_DDID(1 << 28); | |||
| 355 | } | |||
| 356 | ||||
| 357 | val &= ~ICL_PHY_MISC_DE_IO_COMP_PWR_DOWN(1 << 23); | |||
| 358 | intel_de_write(dev_priv, ICL_PHY_MISC(phy)((const i915_reg_t){ .reg = (((0x64C00) + (phy) * ((0x64C04) - (0x64C00)))) }), val); | |||
| 359 | ||||
| 360 | skip_phy_misc: | |||
| 361 | if (DISPLAY_VER(dev_priv)((&(dev_priv)->__runtime)->display.ip.ver) >= 12) { | |||
| 362 | val = intel_de_read(dev_priv, ICL_PORT_TX_DW8_LN(0, phy)((const i915_reg_t){ .reg = (((((const u32 []){ 0x162000, 0x6C000 , 0x160000, 0x161000, 0x16B000 })[phy]) + (0x880 + (0) * 0x100 ) + 4 * (8))) })); | |||
| 363 | val &= ~ICL_PORT_TX_DW8_ODCC_CLK_DIV_SEL_MASK((u32)((((~0UL) >> (64 - (30) - 1)) & ((~0UL) << (29))) + 0)); | |||
| 364 | val |= ICL_PORT_TX_DW8_ODCC_CLK_SEL((u32)((1UL << (31)) + 0)); | |||
| 365 | val |= ICL_PORT_TX_DW8_ODCC_CLK_DIV_SEL_DIV2((u32)((((typeof(((u32)((((~0UL) >> (64 - (30) - 1)) & ((~0UL) << (29))) + 0))))(0x1) << (__builtin_ffsll (((u32)((((~0UL) >> (64 - (30) - 1)) & ((~0UL) << (29))) + 0))) - 1)) & (((u32)((((~0UL) >> (64 - (30 ) - 1)) & ((~0UL) << (29))) + 0)))) + 0 + 0 + 0 + 0 )); | |||
| 366 | intel_de_write(dev_priv, ICL_PORT_TX_DW8_GRP(phy)((const i915_reg_t){ .reg = (((((const u32 []){ 0x162000, 0x6C000 , 0x160000, 0x161000, 0x16B000 })[phy]) + 0x680 + 4 * (8))) } ), val); | |||
| 367 | ||||
| 368 | val = intel_de_read(dev_priv, ICL_PORT_PCS_DW1_LN(0, phy)((const i915_reg_t){ .reg = (((((const u32 []){ 0x162000, 0x6C000 , 0x160000, 0x161000, 0x16B000 })[phy]) + (0x800 + (0) * 0x100 ) + 4 * (1))) })); | |||
| 369 | val &= ~DCC_MODE_SELECT_MASK(0x3 << 20); | |||
| 370 | val |= DCC_MODE_SELECT_CONTINUOSLY(0x3 << 20); | |||
| 371 | intel_de_write(dev_priv, ICL_PORT_PCS_DW1_GRP(phy)((const i915_reg_t){ .reg = (((((const u32 []){ 0x162000, 0x6C000 , 0x160000, 0x161000, 0x16B000 })[phy]) + 0x600 + 4 * (1))) } ), val); | |||
| 372 | } | |||
| 373 | ||||
| 374 | icl_set_procmon_ref_values(dev_priv, phy); | |||
| 375 | ||||
| 376 | if (phy_is_master(dev_priv, phy)) { | |||
| 377 | val = intel_de_read(dev_priv, ICL_PORT_COMP_DW8(phy)((const i915_reg_t){ .reg = (((((const u32 []){ 0x162000, 0x6C000 , 0x160000, 0x161000, 0x16B000 })[phy]) + 0x100 + 4 * (8))) } )); | |||
| 378 | val |= IREFGEN(1 << 24); | |||
| 379 | intel_de_write(dev_priv, ICL_PORT_COMP_DW8(phy)((const i915_reg_t){ .reg = (((((const u32 []){ 0x162000, 0x6C000 , 0x160000, 0x161000, 0x16B000 })[phy]) + 0x100 + 4 * (8))) } ), val); | |||
| 380 | } | |||
| 381 | ||||
| 382 | val = intel_de_read(dev_priv, ICL_PORT_COMP_DW0(phy)((const i915_reg_t){ .reg = (((((const u32 []){ 0x162000, 0x6C000 , 0x160000, 0x161000, 0x16B000 })[phy]) + 0x100 + 4 * (0))) } )); | |||
| 383 | val |= COMP_INIT(1 << 31); | |||
| 384 | intel_de_write(dev_priv, ICL_PORT_COMP_DW0(phy)((const i915_reg_t){ .reg = (((((const u32 []){ 0x162000, 0x6C000 , 0x160000, 0x161000, 0x16B000 })[phy]) + 0x100 + 4 * (0))) } ), val); | |||
| 385 | ||||
| 386 | val = intel_de_read(dev_priv, ICL_PORT_CL_DW5(phy)((const i915_reg_t){ .reg = (((((const u32 []){ 0x162000, 0x6C000 , 0x160000, 0x161000, 0x16B000 })[phy]) + 4 * (5))) })); | |||
| 387 | val |= CL_POWER_DOWN_ENABLE(1 << 4); | |||
| 388 | intel_de_write(dev_priv, ICL_PORT_CL_DW5(phy)((const i915_reg_t){ .reg = (((((const u32 []){ 0x162000, 0x6C000 , 0x160000, 0x161000, 0x16B000 })[phy]) + 4 * (5))) }), val); | |||
| 389 | } | |||
| 390 | } | |||
| 391 | ||||
| 392 | static void icl_combo_phys_uninit(struct drm_i915_privateinteldrm_softc *dev_priv) | |||
| 393 | { | |||
| 394 | enum phy phy; | |||
| 395 | ||||
| 396 | for_each_combo_phy_reverse(dev_priv, phy)for ((phy) = I915_MAX_PHYS; (phy)-- > PHY_A;) if (!(intel_phy_is_combo (dev_priv, phy))) {} else { | |||
| 397 | u32 val; | |||
| 398 | ||||
| 399 | if (phy
| |||
| 400 | !icl_combo_phy_verify_state(dev_priv, phy)) { | |||
| 401 | if (IS_TIGERLAKE(dev_priv)IS_PLATFORM(dev_priv, INTEL_TIGERLAKE) || IS_DG1(dev_priv)IS_PLATFORM(dev_priv, INTEL_DG1)) { | |||
| 402 | /* | |||
| 403 | * A known problem with old ifwi: | |||
| 404 | * https://gitlab.freedesktop.org/drm/intel/-/issues/2411 | |||
| 405 | * Suppress the warning for CI. Remove ASAP! | |||
| 406 | */ | |||
| 407 | drm_dbg_kms(&dev_priv->drm,__drm_dev_dbg(((void *)0), (&dev_priv->drm) ? (&dev_priv ->drm)->dev : ((void *)0), DRM_UT_KMS, "Combo PHY %c HW state changed unexpectedly\n" , ((phy) + 'A')) | |||
| 408 | "Combo PHY %c HW state changed unexpectedly\n",__drm_dev_dbg(((void *)0), (&dev_priv->drm) ? (&dev_priv ->drm)->dev : ((void *)0), DRM_UT_KMS, "Combo PHY %c HW state changed unexpectedly\n" , ((phy) + 'A')) | |||
| 409 | phy_name(phy))__drm_dev_dbg(((void *)0), (&dev_priv->drm) ? (&dev_priv ->drm)->dev : ((void *)0), DRM_UT_KMS, "Combo PHY %c HW state changed unexpectedly\n" , ((phy) + 'A')); | |||
| 410 | } else { | |||
| 411 | drm_warn(&dev_priv->drm,printf("drm:pid%d:%s *WARNING* " "[drm] " "Combo PHY %c HW state changed unexpectedly\n" , ({struct cpu_info *__ci; asm volatile("movq %%gs:%P1,%0" : "=r" (__ci) :"n" (__builtin_offsetof(struct cpu_info, ci_self))); __ci;})->ci_curproc->p_p->ps_pid, __func__ , ((phy) + 'A')) | |||
| 412 | "Combo PHY %c HW state changed unexpectedly\n",printf("drm:pid%d:%s *WARNING* " "[drm] " "Combo PHY %c HW state changed unexpectedly\n" , ({struct cpu_info *__ci; asm volatile("movq %%gs:%P1,%0" : "=r" (__ci) :"n" (__builtin_offsetof(struct cpu_info, ci_self))); __ci;})->ci_curproc->p_p->ps_pid, __func__ , ((phy) + 'A')) | |||
| 413 | phy_name(phy))printf("drm:pid%d:%s *WARNING* " "[drm] " "Combo PHY %c HW state changed unexpectedly\n" , ({struct cpu_info *__ci; asm volatile("movq %%gs:%P1,%0" : "=r" (__ci) :"n" (__builtin_offsetof(struct cpu_info, ci_self))); __ci;})->ci_curproc->p_p->ps_pid, __func__ , ((phy) + 'A')); | |||
| 414 | } | |||
| 415 | } | |||
| 416 | ||||
| 417 | if (!has_phy_misc(dev_priv, phy)) | |||
| 418 | goto skip_phy_misc; | |||
| 419 | ||||
| 420 | val = intel_de_read(dev_priv, ICL_PHY_MISC(phy)((const i915_reg_t){ .reg = (((0x64C00) + (phy) * ((0x64C04) - (0x64C00)))) })); | |||
| 421 | val |= ICL_PHY_MISC_DE_IO_COMP_PWR_DOWN(1 << 23); | |||
| 422 | intel_de_write(dev_priv, ICL_PHY_MISC(phy)((const i915_reg_t){ .reg = (((0x64C00) + (phy) * ((0x64C04) - (0x64C00)))) }), val); | |||
| 423 | ||||
| 424 | skip_phy_misc: | |||
| 425 | val = intel_de_read(dev_priv, ICL_PORT_COMP_DW0(phy)((const i915_reg_t){ .reg = (((((const u32 []){ 0x162000, 0x6C000 , 0x160000, 0x161000, 0x16B000 })[phy]) + 0x100 + 4 * (0))) } )); | |||
| ||||
| 426 | val &= ~COMP_INIT(1 << 31); | |||
| 427 | intel_de_write(dev_priv, ICL_PORT_COMP_DW0(phy)((const i915_reg_t){ .reg = (((((const u32 []){ 0x162000, 0x6C000 , 0x160000, 0x161000, 0x16B000 })[phy]) + 0x100 + 4 * (0))) } ), val); | |||
| 428 | } | |||
| 429 | } | |||
| 430 | ||||
| 431 | void intel_combo_phy_init(struct drm_i915_privateinteldrm_softc *i915) | |||
| 432 | { | |||
| 433 | icl_combo_phys_init(i915); | |||
| 434 | } | |||
| 435 | ||||
| 436 | void intel_combo_phy_uninit(struct drm_i915_privateinteldrm_softc *i915) | |||
| 437 | { | |||
| 438 | icl_combo_phys_uninit(i915); | |||
| ||||
| 439 | } |