root/drivers/gpu/drm/i915/display/intel_combo_phy.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. cnl_get_procmon_ref_values
  2. cnl_set_procmon_ref_values
  3. check_phy_reg
  4. cnl_verify_procmon_ref_values
  5. cnl_combo_phy_enabled
  6. cnl_combo_phy_verify_state
  7. cnl_combo_phys_init
  8. cnl_combo_phys_uninit
  9. icl_combo_phy_enabled
  10. icl_combo_phy_verify_state
  11. intel_combo_phy_power_up_lanes
  12. ehl_combo_phy_a_mux
  13. icl_combo_phys_init
  14. icl_combo_phys_uninit
  15. intel_combo_phy_init
  16. intel_combo_phy_uninit

   1 // SPDX-License-Identifier: MIT
   2 /*
   3  * Copyright © 2018 Intel Corporation
   4  */
   5 
   6 #include "intel_combo_phy.h"
   7 #include "intel_display_types.h"
   8 
   9 #define for_each_combo_phy(__dev_priv, __phy) \
  10         for ((__phy) = PHY_A; (__phy) < I915_MAX_PHYS; (__phy)++)       \
  11                 for_each_if(intel_phy_is_combo(__dev_priv, __phy))
  12 
  13 #define for_each_combo_phy_reverse(__dev_priv, __phy) \
  14         for ((__phy) = I915_MAX_PHYS; (__phy)-- > PHY_A;) \
  15                 for_each_if(intel_phy_is_combo(__dev_priv, __phy))
  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 gen11 has a set for each combo PHY.
  42  * The CNL registers are equivalent to the gen11 PHY A registers, that's why we
  43  * call the ICL macros even though the function has CNL on its name.
  44  */
  45 static const struct cnl_procmon *
  46 cnl_get_procmon_ref_values(struct drm_i915_private *dev_priv, enum phy phy)
  47 {
  48         const struct cnl_procmon *procmon;
  49         u32 val;
  50 
  51         val = I915_READ(ICL_PORT_COMP_DW3(phy));
  52         switch (val & (PROCESS_INFO_MASK | VOLTAGE_INFO_MASK)) {
  53         default:
  54                 MISSING_CASE(val);
  55                 /* fall through */
  56         case VOLTAGE_INFO_0_85V | PROCESS_INFO_DOT_0:
  57                 procmon = &cnl_procmon_values[PROCMON_0_85V_DOT_0];
  58                 break;
  59         case VOLTAGE_INFO_0_95V | PROCESS_INFO_DOT_0:
  60                 procmon = &cnl_procmon_values[PROCMON_0_95V_DOT_0];
  61                 break;
  62         case VOLTAGE_INFO_0_95V | PROCESS_INFO_DOT_1:
  63                 procmon = &cnl_procmon_values[PROCMON_0_95V_DOT_1];
  64                 break;
  65         case VOLTAGE_INFO_1_05V | PROCESS_INFO_DOT_0:
  66                 procmon = &cnl_procmon_values[PROCMON_1_05V_DOT_0];
  67                 break;
  68         case VOLTAGE_INFO_1_05V | PROCESS_INFO_DOT_1:
  69                 procmon = &cnl_procmon_values[PROCMON_1_05V_DOT_1];
  70                 break;
  71         }
  72 
  73         return procmon;
  74 }
  75 
  76 static void cnl_set_procmon_ref_values(struct drm_i915_private *dev_priv,
  77                                        enum phy phy)
  78 {
  79         const struct cnl_procmon *procmon;
  80         u32 val;
  81 
  82         procmon = cnl_get_procmon_ref_values(dev_priv, phy);
  83 
  84         val = I915_READ(ICL_PORT_COMP_DW1(phy));
  85         val &= ~((0xff << 16) | 0xff);
  86         val |= procmon->dw1;
  87         I915_WRITE(ICL_PORT_COMP_DW1(phy), val);
  88 
  89         I915_WRITE(ICL_PORT_COMP_DW9(phy), procmon->dw9);
  90         I915_WRITE(ICL_PORT_COMP_DW10(phy), procmon->dw10);
  91 }
  92 
  93 static bool check_phy_reg(struct drm_i915_private *dev_priv,
  94                           enum phy phy, i915_reg_t reg, u32 mask,
  95                           u32 expected_val)
  96 {
  97         u32 val = I915_READ(reg);
  98 
  99         if ((val & mask) != expected_val) {
 100                 DRM_DEBUG_DRIVER("Combo PHY %c reg %08x state mismatch: "
 101                                  "current %08x mask %08x expected %08x\n",
 102                                  phy_name(phy),
 103                                  reg.reg, val, mask, expected_val);
 104                 return false;
 105         }
 106 
 107         return true;
 108 }
 109 
 110 static bool cnl_verify_procmon_ref_values(struct drm_i915_private *dev_priv,
 111                                           enum phy phy)
 112 {
 113         const struct cnl_procmon *procmon;
 114         bool ret;
 115 
 116         procmon = cnl_get_procmon_ref_values(dev_priv, phy);
 117 
 118         ret = check_phy_reg(dev_priv, phy, ICL_PORT_COMP_DW1(phy),
 119                             (0xff << 16) | 0xff, procmon->dw1);
 120         ret &= check_phy_reg(dev_priv, phy, ICL_PORT_COMP_DW9(phy),
 121                              -1U, procmon->dw9);
 122         ret &= check_phy_reg(dev_priv, phy, ICL_PORT_COMP_DW10(phy),
 123                              -1U, procmon->dw10);
 124 
 125         return ret;
 126 }
 127 
 128 static bool cnl_combo_phy_enabled(struct drm_i915_private *dev_priv)
 129 {
 130         return !(I915_READ(CHICKEN_MISC_2) & CNL_COMP_PWR_DOWN) &&
 131                 (I915_READ(CNL_PORT_COMP_DW0) & COMP_INIT);
 132 }
 133 
 134 static bool cnl_combo_phy_verify_state(struct drm_i915_private *dev_priv)
 135 {
 136         enum phy phy = PHY_A;
 137         bool ret;
 138 
 139         if (!cnl_combo_phy_enabled(dev_priv))
 140                 return false;
 141 
 142         ret = cnl_verify_procmon_ref_values(dev_priv, phy);
 143 
 144         ret &= check_phy_reg(dev_priv, phy, CNL_PORT_CL1CM_DW5,
 145                              CL_POWER_DOWN_ENABLE, CL_POWER_DOWN_ENABLE);
 146 
 147         return ret;
 148 }
 149 
 150 static void cnl_combo_phys_init(struct drm_i915_private *dev_priv)
 151 {
 152         u32 val;
 153 
 154         val = I915_READ(CHICKEN_MISC_2);
 155         val &= ~CNL_COMP_PWR_DOWN;
 156         I915_WRITE(CHICKEN_MISC_2, val);
 157 
 158         /* Dummy PORT_A to get the correct CNL register from the ICL macro */
 159         cnl_set_procmon_ref_values(dev_priv, PHY_A);
 160 
 161         val = I915_READ(CNL_PORT_COMP_DW0);
 162         val |= COMP_INIT;
 163         I915_WRITE(CNL_PORT_COMP_DW0, val);
 164 
 165         val = I915_READ(CNL_PORT_CL1CM_DW5);
 166         val |= CL_POWER_DOWN_ENABLE;
 167         I915_WRITE(CNL_PORT_CL1CM_DW5, val);
 168 }
 169 
 170 static void cnl_combo_phys_uninit(struct drm_i915_private *dev_priv)
 171 {
 172         u32 val;
 173 
 174         if (!cnl_combo_phy_verify_state(dev_priv))
 175                 DRM_WARN("Combo PHY HW state changed unexpectedly.\n");
 176 
 177         val = I915_READ(CHICKEN_MISC_2);
 178         val |= CNL_COMP_PWR_DOWN;
 179         I915_WRITE(CHICKEN_MISC_2, val);
 180 }
 181 
 182 static bool icl_combo_phy_enabled(struct drm_i915_private *dev_priv,
 183                                   enum phy phy)
 184 {
 185         /* The PHY C added by EHL has no PHY_MISC register */
 186         if (IS_ELKHARTLAKE(dev_priv) && phy == PHY_C)
 187                 return I915_READ(ICL_PORT_COMP_DW0(phy)) & COMP_INIT;
 188         else
 189                 return !(I915_READ(ICL_PHY_MISC(phy)) &
 190                          ICL_PHY_MISC_DE_IO_COMP_PWR_DOWN) &&
 191                         (I915_READ(ICL_PORT_COMP_DW0(phy)) & COMP_INIT);
 192 }
 193 
 194 static bool icl_combo_phy_verify_state(struct drm_i915_private *dev_priv,
 195                                        enum phy phy)
 196 {
 197         bool ret;
 198 
 199         if (!icl_combo_phy_enabled(dev_priv, phy))
 200                 return false;
 201 
 202         ret = cnl_verify_procmon_ref_values(dev_priv, phy);
 203 
 204         if (phy == PHY_A)
 205                 ret &= check_phy_reg(dev_priv, phy, ICL_PORT_COMP_DW8(phy),
 206                                      IREFGEN, IREFGEN);
 207 
 208         ret &= check_phy_reg(dev_priv, phy, ICL_PORT_CL_DW5(phy),
 209                              CL_POWER_DOWN_ENABLE, CL_POWER_DOWN_ENABLE);
 210 
 211         return ret;
 212 }
 213 
 214 void intel_combo_phy_power_up_lanes(struct drm_i915_private *dev_priv,
 215                                     enum phy phy, bool is_dsi,
 216                                     int lane_count, bool lane_reversal)
 217 {
 218         u8 lane_mask;
 219         u32 val;
 220 
 221         if (is_dsi) {
 222                 WARN_ON(lane_reversal);
 223 
 224                 switch (lane_count) {
 225                 case 1:
 226                         lane_mask = PWR_DOWN_LN_3_1_0;
 227                         break;
 228                 case 2:
 229                         lane_mask = PWR_DOWN_LN_3_1;
 230                         break;
 231                 case 3:
 232                         lane_mask = PWR_DOWN_LN_3;
 233                         break;
 234                 default:
 235                         MISSING_CASE(lane_count);
 236                         /* fall-through */
 237                 case 4:
 238                         lane_mask = PWR_UP_ALL_LANES;
 239                         break;
 240                 }
 241         } else {
 242                 switch (lane_count) {
 243                 case 1:
 244                         lane_mask = lane_reversal ? PWR_DOWN_LN_2_1_0 :
 245                                                     PWR_DOWN_LN_3_2_1;
 246                         break;
 247                 case 2:
 248                         lane_mask = lane_reversal ? PWR_DOWN_LN_1_0 :
 249                                                     PWR_DOWN_LN_3_2;
 250                         break;
 251                 default:
 252                         MISSING_CASE(lane_count);
 253                         /* fall-through */
 254                 case 4:
 255                         lane_mask = PWR_UP_ALL_LANES;
 256                         break;
 257                 }
 258         }
 259 
 260         val = I915_READ(ICL_PORT_CL_DW10(phy));
 261         val &= ~PWR_DOWN_LN_MASK;
 262         val |= lane_mask << PWR_DOWN_LN_SHIFT;
 263         I915_WRITE(ICL_PORT_CL_DW10(phy), val);
 264 }
 265 
 266 static u32 ehl_combo_phy_a_mux(struct drm_i915_private *i915, u32 val)
 267 {
 268         bool ddi_a_present = i915->vbt.ddi_port_info[PORT_A].child != NULL;
 269         bool ddi_d_present = i915->vbt.ddi_port_info[PORT_D].child != NULL;
 270         bool dsi_present = intel_bios_is_dsi_present(i915, NULL);
 271 
 272         /*
 273          * VBT's 'dvo port' field for child devices references the DDI, not
 274          * the PHY.  So if combo PHY A is wired up to drive an external
 275          * display, we should see a child device present on PORT_D and
 276          * nothing on PORT_A and no DSI.
 277          */
 278         if (ddi_d_present && !ddi_a_present && !dsi_present)
 279                 return val | ICL_PHY_MISC_MUX_DDID;
 280 
 281         /*
 282          * If we encounter a VBT that claims to have an external display on
 283          * DDI-D _and_ an internal display on DDI-A/DSI leave an error message
 284          * in the log and let the internal display win.
 285          */
 286         if (ddi_d_present)
 287                 DRM_ERROR("VBT claims to have both internal and external displays on PHY A.  Configuring for internal.\n");
 288 
 289         return val & ~ICL_PHY_MISC_MUX_DDID;
 290 }
 291 
 292 static void icl_combo_phys_init(struct drm_i915_private *dev_priv)
 293 {
 294         enum phy phy;
 295 
 296         for_each_combo_phy(dev_priv, phy) {
 297                 u32 val;
 298 
 299                 if (icl_combo_phy_verify_state(dev_priv, phy)) {
 300                         DRM_DEBUG_DRIVER("Combo PHY %c already enabled, won't reprogram it.\n",
 301                                          phy_name(phy));
 302                         continue;
 303                 }
 304 
 305                 /*
 306                  * Although EHL adds a combo PHY C, there's no PHY_MISC
 307                  * register for it and no need to program the
 308                  * DE_IO_COMP_PWR_DOWN setting on PHY C.
 309                  */
 310                 if (IS_ELKHARTLAKE(dev_priv) && phy == PHY_C)
 311                         goto skip_phy_misc;
 312 
 313                 /*
 314                  * EHL's combo PHY A can be hooked up to either an external
 315                  * display (via DDI-D) or an internal display (via DDI-A or
 316                  * the DSI DPHY).  This is a motherboard design decision that
 317                  * can't be changed on the fly, so initialize the PHY's mux
 318                  * based on whether our VBT indicates the presence of any
 319                  * "internal" child devices.
 320                  */
 321                 val = I915_READ(ICL_PHY_MISC(phy));
 322                 if (IS_ELKHARTLAKE(dev_priv) && phy == PHY_A)
 323                         val = ehl_combo_phy_a_mux(dev_priv, val);
 324                 val &= ~ICL_PHY_MISC_DE_IO_COMP_PWR_DOWN;
 325                 I915_WRITE(ICL_PHY_MISC(phy), val);
 326 
 327 skip_phy_misc:
 328                 cnl_set_procmon_ref_values(dev_priv, phy);
 329 
 330                 if (phy == PHY_A) {
 331                         val = I915_READ(ICL_PORT_COMP_DW8(phy));
 332                         val |= IREFGEN;
 333                         I915_WRITE(ICL_PORT_COMP_DW8(phy), val);
 334                 }
 335 
 336                 val = I915_READ(ICL_PORT_COMP_DW0(phy));
 337                 val |= COMP_INIT;
 338                 I915_WRITE(ICL_PORT_COMP_DW0(phy), val);
 339 
 340                 val = I915_READ(ICL_PORT_CL_DW5(phy));
 341                 val |= CL_POWER_DOWN_ENABLE;
 342                 I915_WRITE(ICL_PORT_CL_DW5(phy), val);
 343         }
 344 }
 345 
 346 static void icl_combo_phys_uninit(struct drm_i915_private *dev_priv)
 347 {
 348         enum phy phy;
 349 
 350         for_each_combo_phy_reverse(dev_priv, phy) {
 351                 u32 val;
 352 
 353                 if (phy == PHY_A &&
 354                     !icl_combo_phy_verify_state(dev_priv, phy))
 355                         DRM_WARN("Combo PHY %c HW state changed unexpectedly\n",
 356                                  phy_name(phy));
 357 
 358                 /*
 359                  * Although EHL adds a combo PHY C, there's no PHY_MISC
 360                  * register for it and no need to program the
 361                  * DE_IO_COMP_PWR_DOWN setting on PHY C.
 362                  */
 363                 if (IS_ELKHARTLAKE(dev_priv) && phy == PHY_C)
 364                         goto skip_phy_misc;
 365 
 366                 val = I915_READ(ICL_PHY_MISC(phy));
 367                 val |= ICL_PHY_MISC_DE_IO_COMP_PWR_DOWN;
 368                 I915_WRITE(ICL_PHY_MISC(phy), val);
 369 
 370 skip_phy_misc:
 371                 val = I915_READ(ICL_PORT_COMP_DW0(phy));
 372                 val &= ~COMP_INIT;
 373                 I915_WRITE(ICL_PORT_COMP_DW0(phy), val);
 374         }
 375 }
 376 
 377 void intel_combo_phy_init(struct drm_i915_private *i915)
 378 {
 379         if (INTEL_GEN(i915) >= 11)
 380                 icl_combo_phys_init(i915);
 381         else if (IS_CANNONLAKE(i915))
 382                 cnl_combo_phys_init(i915);
 383 }
 384 
 385 void intel_combo_phy_uninit(struct drm_i915_private *i915)
 386 {
 387         if (INTEL_GEN(i915) >= 11)
 388                 icl_combo_phys_uninit(i915);
 389         else if (IS_CANNONLAKE(i915))
 390                 cnl_combo_phys_uninit(i915);
 391 }

/* [<][>][^][v][top][bottom][index][help] */