1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
|
// SPDX-License-Identifier: MIT
/*
* Copyright © 2026 Intel Corporation
*
*/
#include <drm/drm_print.h>
#include "intel_de.h"
#include "intel_dip.h"
#include "intel_dip_regs.h"
#include "intel_display_types.h"
#include "intel_hdmi.h"
static int intel_dip_get_as_sdp_transmission_line(const struct intel_crtc_state *crtc_state)
{
struct intel_display *display = to_intel_display(crtc_state);
if (!HAS_EMP_AS_SDP_TL(display))
return 0;
/*
* EMP_AS_SDP_TL defines the T1 position as the default AS SDP
* Transmission Line, which corresponds to the start of the
* VSYNC pulse.
*
* Use the T1 position for now.
*/
return crtc_state->vrr.vsync_start;
}
u16 intel_dip_read_emp_as_sdp_tl(const struct intel_crtc_state *crtc_state)
{
struct intel_display *display = to_intel_display(crtc_state);
enum transcoder cpu_transcoder = crtc_state->cpu_transcoder;
u32 val;
if (!HAS_EMP_AS_SDP_TL(display))
return 0;
val = intel_de_read(display, EMP_AS_SDP_TL(display, cpu_transcoder));
return REG_FIELD_GET(EMP_AS_SDP_DB_TL_MASK, val);
}
void intel_dip_write_emp_as_sdp_tl(const struct intel_crtc_state *crtc_state)
{
struct intel_display *display = to_intel_display(crtc_state);
enum transcoder cpu_transcoder = crtc_state->cpu_transcoder;
if (!HAS_EMP_AS_SDP_TL(display))
return;
intel_de_write(display,
EMP_AS_SDP_TL(display, cpu_transcoder),
EMP_AS_SDP_DB_TL(crtc_state->dip.emp_as_sdp_tl));
}
static int intel_dip_sdp_stagger_to_tl(struct intel_crtc_state *crtc_state,
int stagger)
{
return crtc_state->dip.cmn_sdp_tl + stagger;
}
static
void intel_dip_cmn_sdp_tl_compute_config_late(struct intel_crtc_state *crtc_state)
{
struct intel_display *display = to_intel_display(crtc_state);
bool as_sdp;
if (!HAS_COMMON_SDP_TL(display))
return;
as_sdp = crtc_state->infoframes.enable &
intel_hdmi_infoframe_enable(DP_SDP_ADAPTIVE_SYNC);
/*
* When AS SDP is enabled :
* - The common SDP Transmission Line matches the EMP SDP Transmission Line.
*
* When AS SDP is disabled:
* - Bspec mentions the positions as lines of delayed vblank.
* - Guardband = 1st line of delayed vblank
* - Common SDP Transmission line is set to 2nd line of delayed vblank.
*/
if (as_sdp)
crtc_state->dip.cmn_sdp_tl = crtc_state->dip.emp_as_sdp_tl;
else
crtc_state->dip.cmn_sdp_tl = crtc_state->vrr.guardband - 1;
if (drm_WARN_ON(display->drm,
crtc_state->dip.cmn_sdp_tl >=
crtc_state->vrr.guardband + crtc_state->set_context_latency))
return;
/*
* Currently we are programming the default stagger values, but these
* can be optimized if required, based on number of SDPs enabled.
*
* Default values of the Transmission lines for SDPs other than AS SDP:
* VSC : CMN SDP Transmission line
* GMP : CMN SDP Transmission line
* PPS : CMN SDP Transmission line + 1
* VSC_EXT: CMN SDP Transmission line + 2
*/
crtc_state->dip.vsc_sdp_tl = crtc_state->dip.cmn_sdp_tl;
crtc_state->dip.gmp_sdp_tl =
intel_dip_sdp_stagger_to_tl(crtc_state, GMP_STAGGER_DEFAULT);
crtc_state->dip.pps_sdp_tl =
intel_dip_sdp_stagger_to_tl(crtc_state, PPS_STAGGER_DEFAULT);
crtc_state->dip.vsc_ext_sdp_tl =
intel_dip_sdp_stagger_to_tl(crtc_state, VSC_EXT_STAGGER_DEFAULT);
}
void intel_dip_sdp_tl_compute_config_late(struct intel_crtc_state *crtc_state)
{
crtc_state->dip.emp_as_sdp_tl = intel_dip_get_as_sdp_transmission_line(crtc_state);
intel_dip_cmn_sdp_tl_compute_config_late(crtc_state);
}
static
void intel_dip_cmn_sdp_transmission_line_get_config(struct intel_crtc_state *crtc_state)
{
struct intel_display *display = to_intel_display(crtc_state);
enum transcoder cpu_transcoder = crtc_state->cpu_transcoder;
u16 vsc_ext_stagger, pps_stagger, gmp_stagger;
u32 val;
if (!HAS_COMMON_SDP_TL(display))
return;
val = intel_de_read(display, CMN_SDP_TL(display, cpu_transcoder));
if (!(val & TRANSMISSION_LINE_ENABLE))
return;
crtc_state->dip.cmn_sdp_tl = REG_FIELD_GET(BASE_TRANSMISSION_LINE_MASK, val);
/* SDP VSC uses same transmission line as CMN base transmission line */
crtc_state->dip.vsc_sdp_tl = crtc_state->dip.cmn_sdp_tl;
val = intel_de_read(display, CMN_SDP_TL_STGR_CTL(display, cpu_transcoder));
vsc_ext_stagger = REG_FIELD_GET(VSC_EXT_STAGGER_MASK, val);
pps_stagger = REG_FIELD_GET(PPS_STAGGER_MASK, val);
gmp_stagger = REG_FIELD_GET(GMP_STAGGER_MASK, val);
crtc_state->dip.vsc_ext_sdp_tl =
intel_dip_sdp_stagger_to_tl(crtc_state, vsc_ext_stagger);
crtc_state->dip.pps_sdp_tl =
intel_dip_sdp_stagger_to_tl(crtc_state, pps_stagger);
crtc_state->dip.gmp_sdp_tl =
intel_dip_sdp_stagger_to_tl(crtc_state, gmp_stagger);
}
void intel_dip_sdp_transmission_line_get_config(struct intel_crtc_state *crtc_state)
{
crtc_state->dip.emp_as_sdp_tl = intel_dip_read_emp_as_sdp_tl(crtc_state);
intel_dip_cmn_sdp_transmission_line_get_config(crtc_state);
}
static int intel_dip_sdp_tl_to_stagger(const struct intel_crtc_state *crtc_state,
u16 sdp_transmission_line)
{
return sdp_transmission_line - crtc_state->dip.cmn_sdp_tl;
}
void intel_dip_cmn_sdp_transmission_line_enable(const struct intel_crtc_state *crtc_state)
{
struct intel_display *display = to_intel_display(crtc_state);
enum transcoder cpu_transcoder = crtc_state->cpu_transcoder;
int gmp_stagger;
int pps_stagger;
int vsc_ext_stagger;
if (!HAS_COMMON_SDP_TL(display))
return;
gmp_stagger = intel_dip_sdp_tl_to_stagger(crtc_state,
crtc_state->dip.gmp_sdp_tl);
pps_stagger = intel_dip_sdp_tl_to_stagger(crtc_state,
crtc_state->dip.pps_sdp_tl);
vsc_ext_stagger = intel_dip_sdp_tl_to_stagger(crtc_state,
crtc_state->dip.vsc_ext_sdp_tl);
if (drm_WARN_ON(display->drm, gmp_stagger < 0))
return;
if (drm_WARN_ON(display->drm, pps_stagger < 0))
return;
if (drm_WARN_ON(display->drm, vsc_ext_stagger < 0))
return;
intel_de_write(display, CMN_SDP_TL_STGR_CTL(display, cpu_transcoder),
GMP_STAGGER(gmp_stagger) |
PPS_STAGGER(pps_stagger) |
VSC_EXT_STAGGER(vsc_ext_stagger));
intel_de_write(display, CMN_SDP_TL(display, cpu_transcoder),
TRANSMISSION_LINE_ENABLE |
BASE_TRANSMISSION_LINE(crtc_state->dip.cmn_sdp_tl));
}
void intel_dip_cmn_sdp_transmission_line_disable(const struct intel_crtc_state *old_crtc_state)
{
struct intel_display *display = to_intel_display(old_crtc_state);
enum transcoder cpu_transcoder = old_crtc_state->cpu_transcoder;
if (!HAS_COMMON_SDP_TL(display))
return;
intel_de_write(display, CMN_SDP_TL(display, cpu_transcoder), 0);
}
|