1 // SPDX-License-Identifier: MIT 2 /* 3 * Copyright (C) 2025 Intel Corporation 4 */ 5 6 #include <linux/string_choices.h> 7 #include <linux/types.h> 8 9 #include <drm/drm_device.h> 10 #include <drm/drm_print.h> 11 12 #include "i915_drv.h" 13 #include "i915_reg.h" 14 #include "intel_crtc.h" 15 #include "intel_cmtg.h" 16 #include "intel_cmtg_regs.h" 17 #include "intel_de.h" 18 #include "intel_display_device.h" 19 #include "intel_display_power.h" 20 21 /** 22 * DOC: Common Primary Timing Generator (CMTG) 23 * 24 * The CMTG is a timing generator that runs in parallel to transcoders timing 25 * generators (TG) to provide a synchronization mechanism where CMTG acts as 26 * primary and transcoders TGs act as secondary to the CMTG. The CMTG outputs 27 * its TG start and frame sync signals to the transcoders that are configured 28 * as secondary, which use those signals to synchronize their own timing with 29 * the CMTG's. 30 * 31 * The CMTG can be used only with eDP or MIPI command mode and supports the 32 * following use cases: 33 * 34 * - Dual eDP: The CMTG can be used to keep two eDP TGs in sync when on a 35 * dual eDP configuration (with or without PSR/PSR2 enabled). 36 * 37 * - Single eDP as secondary: It is also possible to use a single eDP 38 * configuration with the transcoder TG as secondary to the CMTG. That would 39 * allow a flow that would not require a modeset on the existing eDP when a 40 * new eDP is added for a dual eDP configuration with CMTG. 41 * 42 * - DC6v: In DC6v, the transcoder might be off but the CMTG keeps running to 43 * maintain frame timings. When exiting DC6v, the transcoder TG then is 44 * synced back the CMTG. 45 * 46 * Currently, the driver does not use the CMTG, but we need to make sure that 47 * we disable it in case we inherit a display configuration with it enabled. 48 */ 49 50 /* 51 * We describe here only the minimum data required to allow us to properly 52 * disable the CMTG if necessary. 53 */ 54 struct intel_cmtg_config { 55 bool cmtg_a_enable; 56 /* 57 * Xe2_LPD adds a second CMTG that can be used for dual eDP async mode. 58 */ 59 bool cmtg_b_enable; 60 bool trans_a_secondary; 61 bool trans_b_secondary; 62 }; 63 64 static bool intel_cmtg_has_cmtg_b(struct intel_display *display) 65 { 66 return DISPLAY_VER(display) >= 20; 67 } 68 69 static bool intel_cmtg_has_clock_sel(struct intel_display *display) 70 { 71 return DISPLAY_VER(display) >= 14; 72 } 73 74 static void intel_cmtg_dump_config(struct intel_display *display, 75 struct intel_cmtg_config *cmtg_config) 76 { 77 drm_dbg_kms(display->drm, 78 "CMTG readout: CMTG A: %s, CMTG B: %s, Transcoder A secondary: %s, Transcoder B secondary: %s\n", 79 str_enabled_disabled(cmtg_config->cmtg_a_enable), 80 intel_cmtg_has_cmtg_b(display) ? str_enabled_disabled(cmtg_config->cmtg_b_enable) : "n/a", 81 str_yes_no(cmtg_config->trans_a_secondary), 82 str_yes_no(cmtg_config->trans_b_secondary)); 83 } 84 85 static bool intel_cmtg_transcoder_is_secondary(struct intel_display *display, 86 enum transcoder trans) 87 { 88 enum intel_display_power_domain power_domain; 89 intel_wakeref_t wakeref; 90 u32 val = 0; 91 92 if (!HAS_TRANSCODER(display, trans)) 93 return false; 94 95 power_domain = POWER_DOMAIN_TRANSCODER(trans); 96 97 with_intel_display_power_if_enabled(display, power_domain, wakeref) 98 val = intel_de_read(display, TRANS_DDI_FUNC_CTL2(display, trans)); 99 100 return val & CMTG_SECONDARY_MODE; 101 } 102 103 static void intel_cmtg_get_config(struct intel_display *display, 104 struct intel_cmtg_config *cmtg_config) 105 { 106 u32 val; 107 108 val = intel_de_read(display, TRANS_CMTG_CTL_A); 109 cmtg_config->cmtg_a_enable = val & CMTG_ENABLE; 110 111 if (intel_cmtg_has_cmtg_b(display)) { 112 val = intel_de_read(display, TRANS_CMTG_CTL_B); 113 cmtg_config->cmtg_b_enable = val & CMTG_ENABLE; 114 } 115 116 cmtg_config->trans_a_secondary = intel_cmtg_transcoder_is_secondary(display, TRANSCODER_A); 117 cmtg_config->trans_b_secondary = intel_cmtg_transcoder_is_secondary(display, TRANSCODER_B); 118 } 119 120 static bool intel_cmtg_disable_requires_modeset(struct intel_display *display, 121 struct intel_cmtg_config *cmtg_config) 122 { 123 if (DISPLAY_VER(display) >= 20) 124 return false; 125 126 return cmtg_config->trans_a_secondary || cmtg_config->trans_b_secondary; 127 } 128 129 static void intel_cmtg_disable(struct intel_display *display, 130 struct intel_cmtg_config *cmtg_config) 131 { 132 u32 clk_sel_clr = 0; 133 u32 clk_sel_set = 0; 134 135 if (cmtg_config->trans_a_secondary) 136 intel_de_rmw(display, TRANS_DDI_FUNC_CTL2(display, TRANSCODER_A), 137 CMTG_SECONDARY_MODE, 0); 138 139 if (cmtg_config->trans_b_secondary) 140 intel_de_rmw(display, TRANS_DDI_FUNC_CTL2(display, TRANSCODER_B), 141 CMTG_SECONDARY_MODE, 0); 142 143 if (cmtg_config->cmtg_a_enable) { 144 drm_dbg_kms(display->drm, "Disabling CMTG A\n"); 145 intel_de_rmw(display, TRANS_CMTG_CTL_A, CMTG_ENABLE, 0); 146 clk_sel_clr |= CMTG_CLK_SEL_A_MASK; 147 clk_sel_set |= CMTG_CLK_SEL_A_DISABLED; 148 } 149 150 if (cmtg_config->cmtg_b_enable) { 151 drm_dbg_kms(display->drm, "Disabling CMTG B\n"); 152 intel_de_rmw(display, TRANS_CMTG_CTL_B, CMTG_ENABLE, 0); 153 clk_sel_clr |= CMTG_CLK_SEL_B_MASK; 154 clk_sel_set |= CMTG_CLK_SEL_B_DISABLED; 155 } 156 157 if (intel_cmtg_has_clock_sel(display) && clk_sel_clr) 158 intel_de_rmw(display, CMTG_CLK_SEL, clk_sel_clr, clk_sel_set); 159 } 160 161 /* 162 * Read out CMTG configuration and, on platforms that allow disabling it without 163 * a modeset, do it. 164 * 165 * This function must be called before any port PLL is disabled in the general 166 * sanitization process, because we need whatever port PLL that is providing the 167 * clock for CMTG to be on before accessing CMTG registers. 168 */ 169 void intel_cmtg_sanitize(struct intel_display *display) 170 { 171 struct intel_cmtg_config cmtg_config = {}; 172 173 if (!HAS_CMTG(display)) 174 return; 175 176 intel_cmtg_get_config(display, &cmtg_config); 177 intel_cmtg_dump_config(display, &cmtg_config); 178 179 /* 180 * FIXME: The driver is not prepared to handle cases where a modeset is 181 * required for disabling the CMTG: we need a proper way of tracking 182 * CMTG state and do the right syncronization with respect to triggering 183 * the modeset as part of the disable sequence. 184 */ 185 if (intel_cmtg_disable_requires_modeset(display, &cmtg_config)) 186 return; 187 188 intel_cmtg_disable(display, &cmtg_config); 189 } 190