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