Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: MIT
0002 /*
0003  * Copyright © 2018 Intel Corporation
0004  */
0005 
0006 #include "intel_combo_phy.h"
0007 #include "intel_combo_phy_regs.h"
0008 #include "intel_de.h"
0009 #include "intel_display_types.h"
0010 
0011 #define for_each_combo_phy(__dev_priv, __phy) \
0012     for ((__phy) = PHY_A; (__phy) < I915_MAX_PHYS; (__phy)++)   \
0013         for_each_if(intel_phy_is_combo(__dev_priv, __phy))
0014 
0015 #define for_each_combo_phy_reverse(__dev_priv, __phy) \
0016     for ((__phy) = I915_MAX_PHYS; (__phy)-- > PHY_A;) \
0017         for_each_if(intel_phy_is_combo(__dev_priv, __phy))
0018 
0019 enum {
0020     PROCMON_0_85V_DOT_0,
0021     PROCMON_0_95V_DOT_0,
0022     PROCMON_0_95V_DOT_1,
0023     PROCMON_1_05V_DOT_0,
0024     PROCMON_1_05V_DOT_1,
0025 };
0026 
0027 static const struct icl_procmon {
0028     const char *name;
0029     u32 dw1, dw9, dw10;
0030 } icl_procmon_values[] = {
0031     [PROCMON_0_85V_DOT_0] = {
0032         .name = "0.85V dot0 (low-voltage)",
0033         .dw1 = 0x00000000, .dw9 = 0x62AB67BB, .dw10 = 0x51914F96,
0034     },
0035     [PROCMON_0_95V_DOT_0] = {
0036         .name = "0.95V dot0",
0037         .dw1 = 0x00000000, .dw9 = 0x86E172C7, .dw10 = 0x77CA5EAB,
0038     },
0039     [PROCMON_0_95V_DOT_1] = {
0040         .name = "0.95V dot1",
0041         .dw1 = 0x00000000, .dw9 = 0x93F87FE1, .dw10 = 0x8AE871C5,
0042     },
0043     [PROCMON_1_05V_DOT_0] = {
0044         .name = "1.05V dot0",
0045         .dw1 = 0x00000000, .dw9 = 0x98FA82DD, .dw10 = 0x89E46DC1,
0046     },
0047     [PROCMON_1_05V_DOT_1] = {
0048         .name = "1.05V dot1",
0049         .dw1 = 0x00440000, .dw9 = 0x9A00AB25, .dw10 = 0x8AE38FF1,
0050     },
0051 };
0052 
0053 static const struct icl_procmon *
0054 icl_get_procmon_ref_values(struct drm_i915_private *dev_priv, enum phy phy)
0055 {
0056     const struct icl_procmon *procmon;
0057     u32 val;
0058 
0059     val = intel_de_read(dev_priv, ICL_PORT_COMP_DW3(phy));
0060     switch (val & (PROCESS_INFO_MASK | VOLTAGE_INFO_MASK)) {
0061     default:
0062         MISSING_CASE(val);
0063         fallthrough;
0064     case VOLTAGE_INFO_0_85V | PROCESS_INFO_DOT_0:
0065         procmon = &icl_procmon_values[PROCMON_0_85V_DOT_0];
0066         break;
0067     case VOLTAGE_INFO_0_95V | PROCESS_INFO_DOT_0:
0068         procmon = &icl_procmon_values[PROCMON_0_95V_DOT_0];
0069         break;
0070     case VOLTAGE_INFO_0_95V | PROCESS_INFO_DOT_1:
0071         procmon = &icl_procmon_values[PROCMON_0_95V_DOT_1];
0072         break;
0073     case VOLTAGE_INFO_1_05V | PROCESS_INFO_DOT_0:
0074         procmon = &icl_procmon_values[PROCMON_1_05V_DOT_0];
0075         break;
0076     case VOLTAGE_INFO_1_05V | PROCESS_INFO_DOT_1:
0077         procmon = &icl_procmon_values[PROCMON_1_05V_DOT_1];
0078         break;
0079     }
0080 
0081     return procmon;
0082 }
0083 
0084 static void icl_set_procmon_ref_values(struct drm_i915_private *dev_priv,
0085                        enum phy phy)
0086 {
0087     const struct icl_procmon *procmon;
0088     u32 val;
0089 
0090     procmon = icl_get_procmon_ref_values(dev_priv, phy);
0091 
0092     val = intel_de_read(dev_priv, ICL_PORT_COMP_DW1(phy));
0093     val &= ~((0xff << 16) | 0xff);
0094     val |= procmon->dw1;
0095     intel_de_write(dev_priv, ICL_PORT_COMP_DW1(phy), val);
0096 
0097     intel_de_write(dev_priv, ICL_PORT_COMP_DW9(phy), procmon->dw9);
0098     intel_de_write(dev_priv, ICL_PORT_COMP_DW10(phy), procmon->dw10);
0099 }
0100 
0101 static bool check_phy_reg(struct drm_i915_private *dev_priv,
0102               enum phy phy, i915_reg_t reg, u32 mask,
0103               u32 expected_val)
0104 {
0105     u32 val = intel_de_read(dev_priv, reg);
0106 
0107     if ((val & mask) != expected_val) {
0108         drm_dbg(&dev_priv->drm,
0109             "Combo PHY %c reg %08x state mismatch: "
0110             "current %08x mask %08x expected %08x\n",
0111             phy_name(phy),
0112             reg.reg, val, mask, expected_val);
0113         return false;
0114     }
0115 
0116     return true;
0117 }
0118 
0119 static bool icl_verify_procmon_ref_values(struct drm_i915_private *dev_priv,
0120                       enum phy phy)
0121 {
0122     const struct icl_procmon *procmon;
0123     bool ret;
0124 
0125     procmon = icl_get_procmon_ref_values(dev_priv, phy);
0126 
0127     drm_dbg_kms(&dev_priv->drm,
0128             "Combo PHY %c Voltage/Process Info : %s\n",
0129             phy_name(phy), procmon->name);
0130 
0131     ret = check_phy_reg(dev_priv, phy, ICL_PORT_COMP_DW1(phy),
0132                 (0xff << 16) | 0xff, procmon->dw1);
0133     ret &= check_phy_reg(dev_priv, phy, ICL_PORT_COMP_DW9(phy),
0134                  -1U, procmon->dw9);
0135     ret &= check_phy_reg(dev_priv, phy, ICL_PORT_COMP_DW10(phy),
0136                  -1U, procmon->dw10);
0137 
0138     return ret;
0139 }
0140 
0141 static bool has_phy_misc(struct drm_i915_private *i915, enum phy phy)
0142 {
0143     /*
0144      * Some platforms only expect PHY_MISC to be programmed for PHY-A and
0145      * PHY-B and may not even have instances of the register for the
0146      * other combo PHY's.
0147      *
0148      * ADL-S technically has three instances of PHY_MISC, but only requires
0149      * that we program it for PHY A.
0150      */
0151 
0152     if (IS_ALDERLAKE_S(i915))
0153         return phy == PHY_A;
0154     else if (IS_JSL_EHL(i915) ||
0155          IS_ROCKETLAKE(i915) ||
0156          IS_DG1(i915))
0157         return phy < PHY_C;
0158 
0159     return true;
0160 }
0161 
0162 static bool icl_combo_phy_enabled(struct drm_i915_private *dev_priv,
0163                   enum phy phy)
0164 {
0165     /* The PHY C added by EHL has no PHY_MISC register */
0166     if (!has_phy_misc(dev_priv, phy))
0167         return intel_de_read(dev_priv, ICL_PORT_COMP_DW0(phy)) & COMP_INIT;
0168     else
0169         return !(intel_de_read(dev_priv, ICL_PHY_MISC(phy)) &
0170              ICL_PHY_MISC_DE_IO_COMP_PWR_DOWN) &&
0171             (intel_de_read(dev_priv, ICL_PORT_COMP_DW0(phy)) & COMP_INIT);
0172 }
0173 
0174 static bool ehl_vbt_ddi_d_present(struct drm_i915_private *i915)
0175 {
0176     bool ddi_a_present = intel_bios_is_port_present(i915, PORT_A);
0177     bool ddi_d_present = intel_bios_is_port_present(i915, PORT_D);
0178     bool dsi_present = intel_bios_is_dsi_present(i915, NULL);
0179 
0180     /*
0181      * VBT's 'dvo port' field for child devices references the DDI, not
0182      * the PHY.  So if combo PHY A is wired up to drive an external
0183      * display, we should see a child device present on PORT_D and
0184      * nothing on PORT_A and no DSI.
0185      */
0186     if (ddi_d_present && !ddi_a_present && !dsi_present)
0187         return true;
0188 
0189     /*
0190      * If we encounter a VBT that claims to have an external display on
0191      * DDI-D _and_ an internal display on DDI-A/DSI leave an error message
0192      * in the log and let the internal display win.
0193      */
0194     if (ddi_d_present)
0195         drm_err(&i915->drm,
0196             "VBT claims to have both internal and external displays on PHY A.  Configuring for internal.\n");
0197 
0198     return false;
0199 }
0200 
0201 static bool phy_is_master(struct drm_i915_private *dev_priv, enum phy phy)
0202 {
0203     /*
0204      * Certain PHYs are connected to compensation resistors and act
0205      * as masters to other PHYs.
0206      *
0207      * ICL,TGL:
0208      *   A(master) -> B(slave), C(slave)
0209      * RKL,DG1:
0210      *   A(master) -> B(slave)
0211      *   C(master) -> D(slave)
0212      * ADL-S:
0213      *   A(master) -> B(slave), C(slave)
0214      *   D(master) -> E(slave)
0215      *
0216      * We must set the IREFGEN bit for any PHY acting as a master
0217      * to another PHY.
0218      */
0219     if (phy == PHY_A)
0220         return true;
0221     else if (IS_ALDERLAKE_S(dev_priv))
0222         return phy == PHY_D;
0223     else if (IS_DG1(dev_priv) || IS_ROCKETLAKE(dev_priv))
0224         return phy == PHY_C;
0225 
0226     return false;
0227 }
0228 
0229 static bool icl_combo_phy_verify_state(struct drm_i915_private *dev_priv,
0230                        enum phy phy)
0231 {
0232     bool ret = true;
0233     u32 expected_val = 0;
0234 
0235     if (!icl_combo_phy_enabled(dev_priv, phy))
0236         return false;
0237 
0238     if (DISPLAY_VER(dev_priv) >= 12) {
0239         ret &= check_phy_reg(dev_priv, phy, ICL_PORT_TX_DW8_LN(0, phy),
0240                      ICL_PORT_TX_DW8_ODCC_CLK_SEL |
0241                      ICL_PORT_TX_DW8_ODCC_CLK_DIV_SEL_MASK,
0242                      ICL_PORT_TX_DW8_ODCC_CLK_SEL |
0243                      ICL_PORT_TX_DW8_ODCC_CLK_DIV_SEL_DIV2);
0244 
0245         ret &= check_phy_reg(dev_priv, phy, ICL_PORT_PCS_DW1_LN(0, phy),
0246                      DCC_MODE_SELECT_MASK,
0247                      DCC_MODE_SELECT_CONTINUOSLY);
0248     }
0249 
0250     ret &= icl_verify_procmon_ref_values(dev_priv, phy);
0251 
0252     if (phy_is_master(dev_priv, phy)) {
0253         ret &= check_phy_reg(dev_priv, phy, ICL_PORT_COMP_DW8(phy),
0254                      IREFGEN, IREFGEN);
0255 
0256         if (IS_JSL_EHL(dev_priv)) {
0257             if (ehl_vbt_ddi_d_present(dev_priv))
0258                 expected_val = ICL_PHY_MISC_MUX_DDID;
0259 
0260             ret &= check_phy_reg(dev_priv, phy, ICL_PHY_MISC(phy),
0261                          ICL_PHY_MISC_MUX_DDID,
0262                          expected_val);
0263         }
0264     }
0265 
0266     ret &= check_phy_reg(dev_priv, phy, ICL_PORT_CL_DW5(phy),
0267                  CL_POWER_DOWN_ENABLE, CL_POWER_DOWN_ENABLE);
0268 
0269     return ret;
0270 }
0271 
0272 void intel_combo_phy_power_up_lanes(struct drm_i915_private *dev_priv,
0273                     enum phy phy, bool is_dsi,
0274                     int lane_count, bool lane_reversal)
0275 {
0276     u8 lane_mask;
0277     u32 val;
0278 
0279     if (is_dsi) {
0280         drm_WARN_ON(&dev_priv->drm, lane_reversal);
0281 
0282         switch (lane_count) {
0283         case 1:
0284             lane_mask = PWR_DOWN_LN_3_1_0;
0285             break;
0286         case 2:
0287             lane_mask = PWR_DOWN_LN_3_1;
0288             break;
0289         case 3:
0290             lane_mask = PWR_DOWN_LN_3;
0291             break;
0292         default:
0293             MISSING_CASE(lane_count);
0294             fallthrough;
0295         case 4:
0296             lane_mask = PWR_UP_ALL_LANES;
0297             break;
0298         }
0299     } else {
0300         switch (lane_count) {
0301         case 1:
0302             lane_mask = lane_reversal ? PWR_DOWN_LN_2_1_0 :
0303                             PWR_DOWN_LN_3_2_1;
0304             break;
0305         case 2:
0306             lane_mask = lane_reversal ? PWR_DOWN_LN_1_0 :
0307                             PWR_DOWN_LN_3_2;
0308             break;
0309         default:
0310             MISSING_CASE(lane_count);
0311             fallthrough;
0312         case 4:
0313             lane_mask = PWR_UP_ALL_LANES;
0314             break;
0315         }
0316     }
0317 
0318     val = intel_de_read(dev_priv, ICL_PORT_CL_DW10(phy));
0319     val &= ~PWR_DOWN_LN_MASK;
0320     val |= lane_mask;
0321     intel_de_write(dev_priv, ICL_PORT_CL_DW10(phy), val);
0322 }
0323 
0324 static void icl_combo_phys_init(struct drm_i915_private *dev_priv)
0325 {
0326     enum phy phy;
0327 
0328     for_each_combo_phy(dev_priv, phy) {
0329         u32 val;
0330 
0331         if (icl_combo_phy_verify_state(dev_priv, phy)) {
0332             drm_dbg(&dev_priv->drm,
0333                 "Combo PHY %c already enabled, won't reprogram it.\n",
0334                 phy_name(phy));
0335             continue;
0336         }
0337 
0338         if (!has_phy_misc(dev_priv, phy))
0339             goto skip_phy_misc;
0340 
0341         /*
0342          * EHL's combo PHY A can be hooked up to either an external
0343          * display (via DDI-D) or an internal display (via DDI-A or
0344          * the DSI DPHY).  This is a motherboard design decision that
0345          * can't be changed on the fly, so initialize the PHY's mux
0346          * based on whether our VBT indicates the presence of any
0347          * "internal" child devices.
0348          */
0349         val = intel_de_read(dev_priv, ICL_PHY_MISC(phy));
0350         if (IS_JSL_EHL(dev_priv) && phy == PHY_A) {
0351             val &= ~ICL_PHY_MISC_MUX_DDID;
0352 
0353             if (ehl_vbt_ddi_d_present(dev_priv))
0354                 val |= ICL_PHY_MISC_MUX_DDID;
0355         }
0356 
0357         val &= ~ICL_PHY_MISC_DE_IO_COMP_PWR_DOWN;
0358         intel_de_write(dev_priv, ICL_PHY_MISC(phy), val);
0359 
0360 skip_phy_misc:
0361         if (DISPLAY_VER(dev_priv) >= 12) {
0362             val = intel_de_read(dev_priv, ICL_PORT_TX_DW8_LN(0, phy));
0363             val &= ~ICL_PORT_TX_DW8_ODCC_CLK_DIV_SEL_MASK;
0364             val |= ICL_PORT_TX_DW8_ODCC_CLK_SEL;
0365             val |= ICL_PORT_TX_DW8_ODCC_CLK_DIV_SEL_DIV2;
0366             intel_de_write(dev_priv, ICL_PORT_TX_DW8_GRP(phy), val);
0367 
0368             val = intel_de_read(dev_priv, ICL_PORT_PCS_DW1_LN(0, phy));
0369             val &= ~DCC_MODE_SELECT_MASK;
0370             val |= DCC_MODE_SELECT_CONTINUOSLY;
0371             intel_de_write(dev_priv, ICL_PORT_PCS_DW1_GRP(phy), val);
0372         }
0373 
0374         icl_set_procmon_ref_values(dev_priv, phy);
0375 
0376         if (phy_is_master(dev_priv, phy)) {
0377             val = intel_de_read(dev_priv, ICL_PORT_COMP_DW8(phy));
0378             val |= IREFGEN;
0379             intel_de_write(dev_priv, ICL_PORT_COMP_DW8(phy), val);
0380         }
0381 
0382         val = intel_de_read(dev_priv, ICL_PORT_COMP_DW0(phy));
0383         val |= COMP_INIT;
0384         intel_de_write(dev_priv, ICL_PORT_COMP_DW0(phy), val);
0385 
0386         val = intel_de_read(dev_priv, ICL_PORT_CL_DW5(phy));
0387         val |= CL_POWER_DOWN_ENABLE;
0388         intel_de_write(dev_priv, ICL_PORT_CL_DW5(phy), val);
0389     }
0390 }
0391 
0392 static void icl_combo_phys_uninit(struct drm_i915_private *dev_priv)
0393 {
0394     enum phy phy;
0395 
0396     for_each_combo_phy_reverse(dev_priv, phy) {
0397         u32 val;
0398 
0399         if (phy == PHY_A &&
0400             !icl_combo_phy_verify_state(dev_priv, phy)) {
0401             if (IS_TIGERLAKE(dev_priv) || IS_DG1(dev_priv)) {
0402                 /*
0403                  * A known problem with old ifwi:
0404                  * https://gitlab.freedesktop.org/drm/intel/-/issues/2411
0405                  * Suppress the warning for CI. Remove ASAP!
0406                  */
0407                 drm_dbg_kms(&dev_priv->drm,
0408                         "Combo PHY %c HW state changed unexpectedly\n",
0409                         phy_name(phy));
0410             } else {
0411                 drm_warn(&dev_priv->drm,
0412                      "Combo PHY %c HW state changed unexpectedly\n",
0413                      phy_name(phy));
0414             }
0415         }
0416 
0417         if (!has_phy_misc(dev_priv, phy))
0418             goto skip_phy_misc;
0419 
0420         val = intel_de_read(dev_priv, ICL_PHY_MISC(phy));
0421         val |= ICL_PHY_MISC_DE_IO_COMP_PWR_DOWN;
0422         intel_de_write(dev_priv, ICL_PHY_MISC(phy), val);
0423 
0424 skip_phy_misc:
0425         val = intel_de_read(dev_priv, ICL_PORT_COMP_DW0(phy));
0426         val &= ~COMP_INIT;
0427         intel_de_write(dev_priv, ICL_PORT_COMP_DW0(phy), val);
0428     }
0429 }
0430 
0431 void intel_combo_phy_init(struct drm_i915_private *i915)
0432 {
0433     icl_combo_phys_init(i915);
0434 }
0435 
0436 void intel_combo_phy_uninit(struct drm_i915_private *i915)
0437 {
0438     icl_combo_phys_uninit(i915);
0439 }