1 // SPDX-License-Identifier: MIT 2 /* 3 * Copyright © 2018 Intel Corporation 4 */ 5 6 #include "intel_combo_phy.h" 7 #include "intel_drv.h" 8 9 #define for_each_combo_port(__dev_priv, __port) \ 10 for ((__port) = PORT_A; (__port) < I915_MAX_PORTS; (__port)++) \ 11 for_each_if(intel_port_is_combophy(__dev_priv, __port)) 12 13 #define for_each_combo_port_reverse(__dev_priv, __port) \ 14 for ((__port) = I915_MAX_PORTS; (__port)-- > PORT_A;) \ 15 for_each_if(intel_port_is_combophy(__dev_priv, __port)) 16 17 enum { 18 PROCMON_0_85V_DOT_0, 19 PROCMON_0_95V_DOT_0, 20 PROCMON_0_95V_DOT_1, 21 PROCMON_1_05V_DOT_0, 22 PROCMON_1_05V_DOT_1, 23 }; 24 25 static const struct cnl_procmon { 26 u32 dw1, dw9, dw10; 27 } cnl_procmon_values[] = { 28 [PROCMON_0_85V_DOT_0] = 29 { .dw1 = 0x00000000, .dw9 = 0x62AB67BB, .dw10 = 0x51914F96, }, 30 [PROCMON_0_95V_DOT_0] = 31 { .dw1 = 0x00000000, .dw9 = 0x86E172C7, .dw10 = 0x77CA5EAB, }, 32 [PROCMON_0_95V_DOT_1] = 33 { .dw1 = 0x00000000, .dw9 = 0x93F87FE1, .dw10 = 0x8AE871C5, }, 34 [PROCMON_1_05V_DOT_0] = 35 { .dw1 = 0x00000000, .dw9 = 0x98FA82DD, .dw10 = 0x89E46DC1, }, 36 [PROCMON_1_05V_DOT_1] = 37 { .dw1 = 0x00440000, .dw9 = 0x9A00AB25, .dw10 = 0x8AE38FF1, }, 38 }; 39 40 /* 41 * CNL has just one set of registers, while ICL has two sets: one for port A and 42 * the other for port B. The CNL registers are equivalent to the ICL port A 43 * registers, that's why we call the ICL macros even though the function has CNL 44 * on its name. 45 */ 46 static const struct cnl_procmon * 47 cnl_get_procmon_ref_values(struct drm_i915_private *dev_priv, enum port port) 48 { 49 const struct cnl_procmon *procmon; 50 u32 val; 51 52 val = I915_READ(ICL_PORT_COMP_DW3(port)); 53 switch (val & (PROCESS_INFO_MASK | VOLTAGE_INFO_MASK)) { 54 default: 55 MISSING_CASE(val); 56 /* fall through */ 57 case VOLTAGE_INFO_0_85V | PROCESS_INFO_DOT_0: 58 procmon = &cnl_procmon_values[PROCMON_0_85V_DOT_0]; 59 break; 60 case VOLTAGE_INFO_0_95V | PROCESS_INFO_DOT_0: 61 procmon = &cnl_procmon_values[PROCMON_0_95V_DOT_0]; 62 break; 63 case VOLTAGE_INFO_0_95V | PROCESS_INFO_DOT_1: 64 procmon = &cnl_procmon_values[PROCMON_0_95V_DOT_1]; 65 break; 66 case VOLTAGE_INFO_1_05V | PROCESS_INFO_DOT_0: 67 procmon = &cnl_procmon_values[PROCMON_1_05V_DOT_0]; 68 break; 69 case VOLTAGE_INFO_1_05V | PROCESS_INFO_DOT_1: 70 procmon = &cnl_procmon_values[PROCMON_1_05V_DOT_1]; 71 break; 72 } 73 74 return procmon; 75 } 76 77 static void cnl_set_procmon_ref_values(struct drm_i915_private *dev_priv, 78 enum port port) 79 { 80 const struct cnl_procmon *procmon; 81 u32 val; 82 83 procmon = cnl_get_procmon_ref_values(dev_priv, port); 84 85 val = I915_READ(ICL_PORT_COMP_DW1(port)); 86 val &= ~((0xff << 16) | 0xff); 87 val |= procmon->dw1; 88 I915_WRITE(ICL_PORT_COMP_DW1(port), val); 89 90 I915_WRITE(ICL_PORT_COMP_DW9(port), procmon->dw9); 91 I915_WRITE(ICL_PORT_COMP_DW10(port), procmon->dw10); 92 } 93 94 static bool check_phy_reg(struct drm_i915_private *dev_priv, 95 enum port port, i915_reg_t reg, u32 mask, 96 u32 expected_val) 97 { 98 u32 val = I915_READ(reg); 99 100 if ((val & mask) != expected_val) { 101 DRM_DEBUG_DRIVER("Port %c combo PHY reg %08x state mismatch: " 102 "current %08x mask %08x expected %08x\n", 103 port_name(port), 104 reg.reg, val, mask, expected_val); 105 return false; 106 } 107 108 return true; 109 } 110 111 static bool cnl_verify_procmon_ref_values(struct drm_i915_private *dev_priv, 112 enum port port) 113 { 114 const struct cnl_procmon *procmon; 115 bool ret; 116 117 procmon = cnl_get_procmon_ref_values(dev_priv, port); 118 119 ret = check_phy_reg(dev_priv, port, ICL_PORT_COMP_DW1(port), 120 (0xff << 16) | 0xff, procmon->dw1); 121 ret &= check_phy_reg(dev_priv, port, ICL_PORT_COMP_DW9(port), 122 -1U, procmon->dw9); 123 ret &= check_phy_reg(dev_priv, port, ICL_PORT_COMP_DW10(port), 124 -1U, procmon->dw10); 125 126 return ret; 127 } 128 129 static bool cnl_combo_phy_enabled(struct drm_i915_private *dev_priv) 130 { 131 return !(I915_READ(CHICKEN_MISC_2) & CNL_COMP_PWR_DOWN) && 132 (I915_READ(CNL_PORT_COMP_DW0) & COMP_INIT); 133 } 134 135 static bool cnl_combo_phy_verify_state(struct drm_i915_private *dev_priv) 136 { 137 enum port port = PORT_A; 138 bool ret; 139 140 if (!cnl_combo_phy_enabled(dev_priv)) 141 return false; 142 143 ret = cnl_verify_procmon_ref_values(dev_priv, port); 144 145 ret &= check_phy_reg(dev_priv, port, CNL_PORT_CL1CM_DW5, 146 CL_POWER_DOWN_ENABLE, CL_POWER_DOWN_ENABLE); 147 148 return ret; 149 } 150 151 static void cnl_combo_phys_init(struct drm_i915_private *dev_priv) 152 { 153 u32 val; 154 155 val = I915_READ(CHICKEN_MISC_2); 156 val &= ~CNL_COMP_PWR_DOWN; 157 I915_WRITE(CHICKEN_MISC_2, val); 158 159 /* Dummy PORT_A to get the correct CNL register from the ICL macro */ 160 cnl_set_procmon_ref_values(dev_priv, PORT_A); 161 162 val = I915_READ(CNL_PORT_COMP_DW0); 163 val |= COMP_INIT; 164 I915_WRITE(CNL_PORT_COMP_DW0, val); 165 166 val = I915_READ(CNL_PORT_CL1CM_DW5); 167 val |= CL_POWER_DOWN_ENABLE; 168 I915_WRITE(CNL_PORT_CL1CM_DW5, val); 169 } 170 171 static void cnl_combo_phys_uninit(struct drm_i915_private *dev_priv) 172 { 173 u32 val; 174 175 if (!cnl_combo_phy_verify_state(dev_priv)) 176 DRM_WARN("Combo PHY HW state changed unexpectedly.\n"); 177 178 val = I915_READ(CHICKEN_MISC_2); 179 val |= CNL_COMP_PWR_DOWN; 180 I915_WRITE(CHICKEN_MISC_2, val); 181 } 182 183 static bool icl_combo_phy_enabled(struct drm_i915_private *dev_priv, 184 enum port port) 185 { 186 return !(I915_READ(ICL_PHY_MISC(port)) & 187 ICL_PHY_MISC_DE_IO_COMP_PWR_DOWN) && 188 (I915_READ(ICL_PORT_COMP_DW0(port)) & COMP_INIT); 189 } 190 191 static bool icl_combo_phy_verify_state(struct drm_i915_private *dev_priv, 192 enum port port) 193 { 194 bool ret; 195 196 if (!icl_combo_phy_enabled(dev_priv, port)) 197 return false; 198 199 ret = cnl_verify_procmon_ref_values(dev_priv, port); 200 201 if (port == PORT_A) 202 ret &= check_phy_reg(dev_priv, port, ICL_PORT_COMP_DW8(port), 203 IREFGEN, IREFGEN); 204 205 ret &= check_phy_reg(dev_priv, port, ICL_PORT_CL_DW5(port), 206 CL_POWER_DOWN_ENABLE, CL_POWER_DOWN_ENABLE); 207 208 return ret; 209 } 210 211 void intel_combo_phy_power_up_lanes(struct drm_i915_private *dev_priv, 212 enum port port, bool is_dsi, 213 int lane_count, bool lane_reversal) 214 { 215 u8 lane_mask; 216 u32 val; 217 218 if (is_dsi) { 219 WARN_ON(lane_reversal); 220 221 switch (lane_count) { 222 case 1: 223 lane_mask = PWR_DOWN_LN_3_1_0; 224 break; 225 case 2: 226 lane_mask = PWR_DOWN_LN_3_1; 227 break; 228 case 3: 229 lane_mask = PWR_DOWN_LN_3; 230 break; 231 default: 232 MISSING_CASE(lane_count); 233 /* fall-through */ 234 case 4: 235 lane_mask = PWR_UP_ALL_LANES; 236 break; 237 } 238 } else { 239 switch (lane_count) { 240 case 1: 241 lane_mask = lane_reversal ? PWR_DOWN_LN_2_1_0 : 242 PWR_DOWN_LN_3_2_1; 243 break; 244 case 2: 245 lane_mask = lane_reversal ? PWR_DOWN_LN_1_0 : 246 PWR_DOWN_LN_3_2; 247 break; 248 default: 249 MISSING_CASE(lane_count); 250 /* fall-through */ 251 case 4: 252 lane_mask = PWR_UP_ALL_LANES; 253 break; 254 } 255 } 256 257 val = I915_READ(ICL_PORT_CL_DW10(port)); 258 val &= ~PWR_DOWN_LN_MASK; 259 val |= lane_mask << PWR_DOWN_LN_SHIFT; 260 I915_WRITE(ICL_PORT_CL_DW10(port), val); 261 } 262 263 static void icl_combo_phys_init(struct drm_i915_private *dev_priv) 264 { 265 enum port port; 266 267 for_each_combo_port(dev_priv, port) { 268 u32 val; 269 270 if (icl_combo_phy_verify_state(dev_priv, port)) { 271 DRM_DEBUG_DRIVER("Port %c combo PHY already enabled, won't reprogram it.\n", 272 port_name(port)); 273 continue; 274 } 275 276 val = I915_READ(ICL_PHY_MISC(port)); 277 val &= ~ICL_PHY_MISC_DE_IO_COMP_PWR_DOWN; 278 I915_WRITE(ICL_PHY_MISC(port), val); 279 280 cnl_set_procmon_ref_values(dev_priv, port); 281 282 if (port == PORT_A) { 283 val = I915_READ(ICL_PORT_COMP_DW8(port)); 284 val |= IREFGEN; 285 I915_WRITE(ICL_PORT_COMP_DW8(port), val); 286 } 287 288 val = I915_READ(ICL_PORT_COMP_DW0(port)); 289 val |= COMP_INIT; 290 I915_WRITE(ICL_PORT_COMP_DW0(port), val); 291 292 val = I915_READ(ICL_PORT_CL_DW5(port)); 293 val |= CL_POWER_DOWN_ENABLE; 294 I915_WRITE(ICL_PORT_CL_DW5(port), val); 295 } 296 } 297 298 static void icl_combo_phys_uninit(struct drm_i915_private *dev_priv) 299 { 300 enum port port; 301 302 for_each_combo_port_reverse(dev_priv, port) { 303 u32 val; 304 305 if (port == PORT_A && 306 !icl_combo_phy_verify_state(dev_priv, port)) 307 DRM_WARN("Port %c combo PHY HW state changed unexpectedly\n", 308 port_name(port)); 309 310 val = I915_READ(ICL_PHY_MISC(port)); 311 val |= ICL_PHY_MISC_DE_IO_COMP_PWR_DOWN; 312 I915_WRITE(ICL_PHY_MISC(port), val); 313 314 val = I915_READ(ICL_PORT_COMP_DW0(port)); 315 val &= ~COMP_INIT; 316 I915_WRITE(ICL_PORT_COMP_DW0(port), val); 317 } 318 } 319 320 void intel_combo_phy_init(struct drm_i915_private *i915) 321 { 322 if (INTEL_GEN(i915) >= 11) 323 icl_combo_phys_init(i915); 324 else if (IS_CANNONLAKE(i915)) 325 cnl_combo_phys_init(i915); 326 } 327 328 void intel_combo_phy_uninit(struct drm_i915_private *i915) 329 { 330 if (INTEL_GEN(i915) >= 11) 331 icl_combo_phys_uninit(i915); 332 else if (IS_CANNONLAKE(i915)) 333 cnl_combo_phys_uninit(i915); 334 } 335