xref: /linux/drivers/gpu/drm/i915/display/intel_cmtg.c (revision 1260ed77798502de9c98020040d2995008de10cc)
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