.. | .. |
---|
46 | 46 | * This is a workaround for a bug that has existed since R5xx and has not been |
---|
47 | 47 | * fixed keep Front porch at minimum 2 for Interlaced mode or 1 for progressive. |
---|
48 | 48 | */ |
---|
49 | | -static void optc1_apply_front_porch_workaround( |
---|
50 | | - struct timing_generator *optc, |
---|
51 | | - struct dc_crtc_timing *timing) |
---|
| 49 | +static void apply_front_porch_workaround(struct dc_crtc_timing *timing) |
---|
52 | 50 | { |
---|
53 | 51 | if (timing->flags.INTERLACE == 1) { |
---|
54 | 52 | if (timing->v_front_porch < 2) |
---|
.. | .. |
---|
60 | 58 | } |
---|
61 | 59 | |
---|
62 | 60 | void optc1_program_global_sync( |
---|
63 | | - struct timing_generator *optc) |
---|
| 61 | + struct timing_generator *optc, |
---|
| 62 | + int vready_offset, |
---|
| 63 | + int vstartup_start, |
---|
| 64 | + int vupdate_offset, |
---|
| 65 | + int vupdate_width) |
---|
64 | 66 | { |
---|
65 | 67 | struct optc *optc1 = DCN10TG_FROM_TG(optc); |
---|
66 | 68 | |
---|
67 | | - if (optc->dlg_otg_param.vstartup_start == 0) { |
---|
| 69 | + optc1->vready_offset = vready_offset; |
---|
| 70 | + optc1->vstartup_start = vstartup_start; |
---|
| 71 | + optc1->vupdate_offset = vupdate_offset; |
---|
| 72 | + optc1->vupdate_width = vupdate_width; |
---|
| 73 | + |
---|
| 74 | + if (optc1->vstartup_start == 0) { |
---|
68 | 75 | BREAK_TO_DEBUGGER(); |
---|
69 | 76 | return; |
---|
70 | 77 | } |
---|
71 | 78 | |
---|
72 | 79 | REG_SET(OTG_VSTARTUP_PARAM, 0, |
---|
73 | | - VSTARTUP_START, optc->dlg_otg_param.vstartup_start); |
---|
| 80 | + VSTARTUP_START, optc1->vstartup_start); |
---|
74 | 81 | |
---|
75 | 82 | REG_SET_2(OTG_VUPDATE_PARAM, 0, |
---|
76 | | - VUPDATE_OFFSET, optc->dlg_otg_param.vupdate_offset, |
---|
77 | | - VUPDATE_WIDTH, optc->dlg_otg_param.vupdate_width); |
---|
| 83 | + VUPDATE_OFFSET, optc1->vupdate_offset, |
---|
| 84 | + VUPDATE_WIDTH, optc1->vupdate_width); |
---|
78 | 85 | |
---|
79 | 86 | REG_SET(OTG_VREADY_PARAM, 0, |
---|
80 | | - VREADY_OFFSET, optc->dlg_otg_param.vready_offset); |
---|
| 87 | + VREADY_OFFSET, optc1->vready_offset); |
---|
81 | 88 | } |
---|
82 | 89 | |
---|
83 | 90 | static void optc1_disable_stereo(struct timing_generator *optc) |
---|
.. | .. |
---|
87 | 94 | REG_SET(OTG_STEREO_CONTROL, 0, |
---|
88 | 95 | OTG_STEREO_EN, 0); |
---|
89 | 96 | |
---|
90 | | - REG_SET_3(OTG_3D_STRUCTURE_CONTROL, 0, |
---|
| 97 | + REG_SET_2(OTG_3D_STRUCTURE_CONTROL, 0, |
---|
91 | 98 | OTG_3D_STRUCTURE_EN, 0, |
---|
92 | | - OTG_3D_STRUCTURE_V_UPDATE_MODE, 0, |
---|
93 | 99 | OTG_3D_STRUCTURE_STEREO_SEL_OVR, 0); |
---|
94 | 100 | } |
---|
95 | 101 | |
---|
96 | | -static uint32_t get_start_vline(struct timing_generator *optc, const struct dc_crtc_timing *dc_crtc_timing) |
---|
97 | | -{ |
---|
98 | | - struct dc_crtc_timing patched_crtc_timing; |
---|
99 | | - int vesa_sync_start; |
---|
100 | | - int asic_blank_end; |
---|
101 | | - int interlace_factor; |
---|
102 | | - int vertical_line_start; |
---|
103 | | - |
---|
104 | | - patched_crtc_timing = *dc_crtc_timing; |
---|
105 | | - optc1_apply_front_porch_workaround(optc, &patched_crtc_timing); |
---|
106 | | - |
---|
107 | | - vesa_sync_start = patched_crtc_timing.h_addressable + |
---|
108 | | - patched_crtc_timing.h_border_right + |
---|
109 | | - patched_crtc_timing.h_front_porch; |
---|
110 | | - |
---|
111 | | - asic_blank_end = patched_crtc_timing.h_total - |
---|
112 | | - vesa_sync_start - |
---|
113 | | - patched_crtc_timing.h_border_left; |
---|
114 | | - |
---|
115 | | - interlace_factor = patched_crtc_timing.flags.INTERLACE ? 2 : 1; |
---|
116 | | - |
---|
117 | | - vesa_sync_start = patched_crtc_timing.v_addressable + |
---|
118 | | - patched_crtc_timing.v_border_bottom + |
---|
119 | | - patched_crtc_timing.v_front_porch; |
---|
120 | | - |
---|
121 | | - asic_blank_end = (patched_crtc_timing.v_total - |
---|
122 | | - vesa_sync_start - |
---|
123 | | - patched_crtc_timing.v_border_top) |
---|
124 | | - * interlace_factor; |
---|
125 | | - |
---|
126 | | - vertical_line_start = asic_blank_end - optc->dlg_otg_param.vstartup_start + 1; |
---|
127 | | - if (vertical_line_start < 0) { |
---|
128 | | - ASSERT(0); |
---|
129 | | - vertical_line_start = 0; |
---|
130 | | - } |
---|
131 | | - |
---|
132 | | - return vertical_line_start; |
---|
133 | | -} |
---|
134 | | - |
---|
135 | | -void optc1_program_vline_interrupt( |
---|
| 102 | +void optc1_setup_vertical_interrupt0( |
---|
136 | 103 | struct timing_generator *optc, |
---|
137 | | - const struct dc_crtc_timing *dc_crtc_timing, |
---|
138 | | - unsigned long long vsync_delta) |
---|
| 104 | + uint32_t start_line, |
---|
| 105 | + uint32_t end_line) |
---|
139 | 106 | { |
---|
140 | | - |
---|
141 | 107 | struct optc *optc1 = DCN10TG_FROM_TG(optc); |
---|
142 | | - |
---|
143 | | - unsigned long long req_delta_tens_of_usec = div64_u64((vsync_delta + 9999), 10000); |
---|
144 | | - unsigned long long pix_clk_hundreds_khz = div64_u64((dc_crtc_timing->pix_clk_khz + 99), 100); |
---|
145 | | - uint32_t req_delta_lines = (uint32_t) div64_u64( |
---|
146 | | - (req_delta_tens_of_usec * pix_clk_hundreds_khz + dc_crtc_timing->h_total - 1), |
---|
147 | | - dc_crtc_timing->h_total); |
---|
148 | | - |
---|
149 | | - uint32_t vsync_line = get_start_vline(optc, dc_crtc_timing); |
---|
150 | | - uint32_t start_line = 0; |
---|
151 | | - uint32_t endLine = 0; |
---|
152 | | - |
---|
153 | | - if (req_delta_lines != 0) |
---|
154 | | - req_delta_lines--; |
---|
155 | | - |
---|
156 | | - if (req_delta_lines > vsync_line) |
---|
157 | | - start_line = dc_crtc_timing->v_total - (req_delta_lines - vsync_line) - 1; |
---|
158 | | - else |
---|
159 | | - start_line = vsync_line - req_delta_lines; |
---|
160 | | - |
---|
161 | | - endLine = start_line + 2; |
---|
162 | | - |
---|
163 | | - if (endLine >= dc_crtc_timing->v_total) |
---|
164 | | - endLine = 2; |
---|
165 | 108 | |
---|
166 | 109 | REG_SET_2(OTG_VERTICAL_INTERRUPT0_POSITION, 0, |
---|
167 | 110 | OTG_VERTICAL_INTERRUPT0_LINE_START, start_line, |
---|
168 | | - OTG_VERTICAL_INTERRUPT0_LINE_END, endLine); |
---|
| 111 | + OTG_VERTICAL_INTERRUPT0_LINE_END, end_line); |
---|
| 112 | +} |
---|
| 113 | + |
---|
| 114 | +void optc1_setup_vertical_interrupt1( |
---|
| 115 | + struct timing_generator *optc, |
---|
| 116 | + uint32_t start_line) |
---|
| 117 | +{ |
---|
| 118 | + struct optc *optc1 = DCN10TG_FROM_TG(optc); |
---|
| 119 | + |
---|
| 120 | + REG_SET(OTG_VERTICAL_INTERRUPT1_POSITION, 0, |
---|
| 121 | + OTG_VERTICAL_INTERRUPT1_LINE_START, start_line); |
---|
| 122 | +} |
---|
| 123 | + |
---|
| 124 | +void optc1_setup_vertical_interrupt2( |
---|
| 125 | + struct timing_generator *optc, |
---|
| 126 | + uint32_t start_line) |
---|
| 127 | +{ |
---|
| 128 | + struct optc *optc1 = DCN10TG_FROM_TG(optc); |
---|
| 129 | + |
---|
| 130 | + REG_SET(OTG_VERTICAL_INTERRUPT2_POSITION, 0, |
---|
| 131 | + OTG_VERTICAL_INTERRUPT2_LINE_START, start_line); |
---|
169 | 132 | } |
---|
170 | 133 | |
---|
171 | 134 | /** |
---|
.. | .. |
---|
176 | 139 | void optc1_program_timing( |
---|
177 | 140 | struct timing_generator *optc, |
---|
178 | 141 | const struct dc_crtc_timing *dc_crtc_timing, |
---|
| 142 | + int vready_offset, |
---|
| 143 | + int vstartup_start, |
---|
| 144 | + int vupdate_offset, |
---|
| 145 | + int vupdate_width, |
---|
| 146 | + const enum signal_type signal, |
---|
179 | 147 | bool use_vbios) |
---|
180 | 148 | { |
---|
181 | 149 | struct dc_crtc_timing patched_crtc_timing; |
---|
182 | | - uint32_t vesa_sync_start; |
---|
183 | 150 | uint32_t asic_blank_end; |
---|
184 | 151 | uint32_t asic_blank_start; |
---|
185 | 152 | uint32_t v_total; |
---|
186 | 153 | uint32_t v_sync_end; |
---|
187 | | - uint32_t v_init, v_fp2; |
---|
188 | 154 | uint32_t h_sync_polarity, v_sync_polarity; |
---|
189 | | - uint32_t interlace_factor; |
---|
190 | 155 | uint32_t start_point = 0; |
---|
191 | 156 | uint32_t field_num = 0; |
---|
192 | | - uint32_t h_div_2; |
---|
193 | | - int32_t vertical_line_start; |
---|
| 157 | + enum h_timing_div_mode h_div = H_TIMING_NO_DIV; |
---|
194 | 158 | |
---|
195 | 159 | struct optc *optc1 = DCN10TG_FROM_TG(optc); |
---|
196 | 160 | |
---|
| 161 | + optc1->signal = signal; |
---|
| 162 | + optc1->vready_offset = vready_offset; |
---|
| 163 | + optc1->vstartup_start = vstartup_start; |
---|
| 164 | + optc1->vupdate_offset = vupdate_offset; |
---|
| 165 | + optc1->vupdate_width = vupdate_width; |
---|
197 | 166 | patched_crtc_timing = *dc_crtc_timing; |
---|
198 | | - optc1_apply_front_porch_workaround(optc, &patched_crtc_timing); |
---|
| 167 | + apply_front_porch_workaround(&patched_crtc_timing); |
---|
199 | 168 | |
---|
200 | 169 | /* Load horizontal timing */ |
---|
201 | 170 | |
---|
.. | .. |
---|
208 | 177 | OTG_H_SYNC_A_START, 0, |
---|
209 | 178 | OTG_H_SYNC_A_END, patched_crtc_timing.h_sync_width); |
---|
210 | 179 | |
---|
211 | | - /* asic_h_blank_end = HsyncWidth + HbackPorch = |
---|
212 | | - * vesa. usHorizontalTotal - vesa. usHorizontalSyncStart - |
---|
213 | | - * vesa.h_left_border |
---|
214 | | - */ |
---|
215 | | - vesa_sync_start = patched_crtc_timing.h_addressable + |
---|
216 | | - patched_crtc_timing.h_border_right + |
---|
| 180 | + /* blank_start = line end - front porch */ |
---|
| 181 | + asic_blank_start = patched_crtc_timing.h_total - |
---|
217 | 182 | patched_crtc_timing.h_front_porch; |
---|
218 | 183 | |
---|
219 | | - asic_blank_end = patched_crtc_timing.h_total - |
---|
220 | | - vesa_sync_start - |
---|
| 184 | + /* blank_end = blank_start - active */ |
---|
| 185 | + asic_blank_end = asic_blank_start - |
---|
| 186 | + patched_crtc_timing.h_border_right - |
---|
| 187 | + patched_crtc_timing.h_addressable - |
---|
221 | 188 | patched_crtc_timing.h_border_left; |
---|
222 | | - |
---|
223 | | - /* h_blank_start = v_blank_end + v_active */ |
---|
224 | | - asic_blank_start = asic_blank_end + |
---|
225 | | - patched_crtc_timing.h_border_left + |
---|
226 | | - patched_crtc_timing.h_addressable + |
---|
227 | | - patched_crtc_timing.h_border_right; |
---|
228 | 189 | |
---|
229 | 190 | REG_UPDATE_2(OTG_H_BLANK_START_END, |
---|
230 | 191 | OTG_H_BLANK_START, asic_blank_start, |
---|
.. | .. |
---|
237 | 198 | REG_UPDATE(OTG_H_SYNC_A_CNTL, |
---|
238 | 199 | OTG_H_SYNC_A_POL, h_sync_polarity); |
---|
239 | 200 | |
---|
240 | | - /* Load vertical timing */ |
---|
| 201 | + v_total = patched_crtc_timing.v_total - 1; |
---|
241 | 202 | |
---|
242 | | - /* CRTC_V_TOTAL = v_total - 1 */ |
---|
243 | | - if (patched_crtc_timing.flags.INTERLACE) { |
---|
244 | | - interlace_factor = 2; |
---|
245 | | - v_total = 2 * patched_crtc_timing.v_total; |
---|
246 | | - } else { |
---|
247 | | - interlace_factor = 1; |
---|
248 | | - v_total = patched_crtc_timing.v_total - 1; |
---|
249 | | - } |
---|
250 | 203 | REG_SET(OTG_V_TOTAL, 0, |
---|
251 | 204 | OTG_V_TOTAL, v_total); |
---|
252 | 205 | |
---|
.. | .. |
---|
259 | 212 | OTG_V_TOTAL_MIN, v_total); |
---|
260 | 213 | |
---|
261 | 214 | /* v_sync_start = 0, v_sync_end = v_sync_width */ |
---|
262 | | - v_sync_end = patched_crtc_timing.v_sync_width * interlace_factor; |
---|
| 215 | + v_sync_end = patched_crtc_timing.v_sync_width; |
---|
263 | 216 | |
---|
264 | 217 | REG_UPDATE_2(OTG_V_SYNC_A, |
---|
265 | 218 | OTG_V_SYNC_A_START, 0, |
---|
266 | 219 | OTG_V_SYNC_A_END, v_sync_end); |
---|
267 | 220 | |
---|
268 | | - vesa_sync_start = patched_crtc_timing.v_addressable + |
---|
269 | | - patched_crtc_timing.v_border_bottom + |
---|
| 221 | + /* blank_start = frame end - front porch */ |
---|
| 222 | + asic_blank_start = patched_crtc_timing.v_total - |
---|
270 | 223 | patched_crtc_timing.v_front_porch; |
---|
271 | 224 | |
---|
272 | | - asic_blank_end = (patched_crtc_timing.v_total - |
---|
273 | | - vesa_sync_start - |
---|
274 | | - patched_crtc_timing.v_border_top) |
---|
275 | | - * interlace_factor; |
---|
276 | | - |
---|
277 | | - /* v_blank_start = v_blank_end + v_active */ |
---|
278 | | - asic_blank_start = asic_blank_end + |
---|
279 | | - (patched_crtc_timing.v_border_top + |
---|
280 | | - patched_crtc_timing.v_addressable + |
---|
281 | | - patched_crtc_timing.v_border_bottom) |
---|
282 | | - * interlace_factor; |
---|
| 225 | + /* blank_end = blank_start - active */ |
---|
| 226 | + asic_blank_end = asic_blank_start - |
---|
| 227 | + patched_crtc_timing.v_border_bottom - |
---|
| 228 | + patched_crtc_timing.v_addressable - |
---|
| 229 | + patched_crtc_timing.v_border_top; |
---|
283 | 230 | |
---|
284 | 231 | REG_UPDATE_2(OTG_V_BLANK_START_END, |
---|
285 | 232 | OTG_V_BLANK_START, asic_blank_start, |
---|
286 | 233 | OTG_V_BLANK_END, asic_blank_end); |
---|
287 | | - |
---|
288 | | - /* Use OTG_VERTICAL_INTERRUPT2 replace VUPDATE interrupt, |
---|
289 | | - * program the reg for interrupt postition. |
---|
290 | | - */ |
---|
291 | | - vertical_line_start = asic_blank_end - optc->dlg_otg_param.vstartup_start + 1; |
---|
292 | | - if (vertical_line_start < 0) { |
---|
293 | | - ASSERT(0); |
---|
294 | | - vertical_line_start = 0; |
---|
295 | | - } |
---|
296 | | - REG_SET(OTG_VERTICAL_INTERRUPT2_POSITION, 0, |
---|
297 | | - OTG_VERTICAL_INTERRUPT2_LINE_START, vertical_line_start); |
---|
298 | 234 | |
---|
299 | 235 | /* v_sync polarity */ |
---|
300 | 236 | v_sync_polarity = patched_crtc_timing.flags.VSYNC_POSITIVE_POLARITY ? |
---|
301 | 237 | 0 : 1; |
---|
302 | 238 | |
---|
303 | 239 | REG_UPDATE(OTG_V_SYNC_A_CNTL, |
---|
304 | | - OTG_V_SYNC_A_POL, v_sync_polarity); |
---|
| 240 | + OTG_V_SYNC_A_POL, v_sync_polarity); |
---|
305 | 241 | |
---|
306 | | - v_init = asic_blank_start; |
---|
307 | | - if (optc->dlg_otg_param.signal == SIGNAL_TYPE_DISPLAY_PORT || |
---|
308 | | - optc->dlg_otg_param.signal == SIGNAL_TYPE_DISPLAY_PORT_MST || |
---|
309 | | - optc->dlg_otg_param.signal == SIGNAL_TYPE_EDP) { |
---|
| 242 | + if (optc1->signal == SIGNAL_TYPE_DISPLAY_PORT || |
---|
| 243 | + optc1->signal == SIGNAL_TYPE_DISPLAY_PORT_MST || |
---|
| 244 | + optc1->signal == SIGNAL_TYPE_EDP) { |
---|
310 | 245 | start_point = 1; |
---|
311 | 246 | if (patched_crtc_timing.flags.INTERLACE == 1) |
---|
312 | 247 | field_num = 1; |
---|
313 | 248 | } |
---|
314 | | - v_fp2 = 0; |
---|
315 | | - if (optc->dlg_otg_param.vstartup_start > asic_blank_end) |
---|
316 | | - v_fp2 = optc->dlg_otg_param.vstartup_start > asic_blank_end; |
---|
317 | 249 | |
---|
318 | 250 | /* Interlace */ |
---|
319 | | - if (patched_crtc_timing.flags.INTERLACE == 1) { |
---|
320 | | - REG_UPDATE(OTG_INTERLACE_CONTROL, |
---|
321 | | - OTG_INTERLACE_ENABLE, 1); |
---|
322 | | - v_init = v_init / 2; |
---|
323 | | - if ((optc->dlg_otg_param.vstartup_start/2)*2 > asic_blank_end) |
---|
324 | | - v_fp2 = v_fp2 / 2; |
---|
325 | | - } else |
---|
326 | | - REG_UPDATE(OTG_INTERLACE_CONTROL, |
---|
327 | | - OTG_INTERLACE_ENABLE, 0); |
---|
328 | | - |
---|
| 251 | + if (REG(OTG_INTERLACE_CONTROL)) { |
---|
| 252 | + if (patched_crtc_timing.flags.INTERLACE == 1) |
---|
| 253 | + REG_UPDATE(OTG_INTERLACE_CONTROL, |
---|
| 254 | + OTG_INTERLACE_ENABLE, 1); |
---|
| 255 | + else |
---|
| 256 | + REG_UPDATE(OTG_INTERLACE_CONTROL, |
---|
| 257 | + OTG_INTERLACE_ENABLE, 0); |
---|
| 258 | + } |
---|
329 | 259 | |
---|
330 | 260 | /* VTG enable set to 0 first VInit */ |
---|
331 | 261 | REG_UPDATE(CONTROL, |
---|
332 | 262 | VTG0_ENABLE, 0); |
---|
333 | | - |
---|
334 | | - REG_UPDATE_2(CONTROL, |
---|
335 | | - VTG0_FP2, v_fp2, |
---|
336 | | - VTG0_VCOUNT_INIT, v_init); |
---|
337 | 263 | |
---|
338 | 264 | /* original code is using VTG offset to address OTG reg, seems wrong */ |
---|
339 | 265 | REG_UPDATE_2(OTG_CONTROL, |
---|
340 | 266 | OTG_START_POINT_CNTL, start_point, |
---|
341 | 267 | OTG_FIELD_NUMBER_CNTL, field_num); |
---|
342 | 268 | |
---|
343 | | - optc1_program_global_sync(optc); |
---|
| 269 | + optc->funcs->program_global_sync(optc, |
---|
| 270 | + vready_offset, |
---|
| 271 | + vstartup_start, |
---|
| 272 | + vupdate_offset, |
---|
| 273 | + vupdate_width); |
---|
| 274 | + |
---|
| 275 | + optc->funcs->set_vtg_params(optc, dc_crtc_timing); |
---|
344 | 276 | |
---|
345 | 277 | /* TODO |
---|
346 | 278 | * patched_crtc_timing.flags.HORZ_COUNT_BY_TWO == 1 |
---|
.. | .. |
---|
352 | 284 | /* Enable stereo - only when we need to pack 3D frame. Other types |
---|
353 | 285 | * of stereo handled in explicit call |
---|
354 | 286 | */ |
---|
355 | | - h_div_2 = (dc_crtc_timing->pixel_encoding == PIXEL_ENCODING_YCBCR420) ? |
---|
356 | | - 1 : 0; |
---|
357 | 287 | |
---|
358 | | - REG_UPDATE(OTG_H_TIMING_CNTL, |
---|
359 | | - OTG_H_TIMING_DIV_BY2, h_div_2); |
---|
| 288 | + if (optc1_is_two_pixels_per_containter(&patched_crtc_timing) || optc1->opp_count == 2) |
---|
| 289 | + h_div = H_TIMING_DIV_BY2; |
---|
360 | 290 | |
---|
| 291 | + if (REG(OPTC_DATA_FORMAT_CONTROL)) { |
---|
| 292 | + uint32_t data_fmt = 0; |
---|
| 293 | + |
---|
| 294 | + if (patched_crtc_timing.pixel_encoding == PIXEL_ENCODING_YCBCR422) |
---|
| 295 | + data_fmt = 1; |
---|
| 296 | + else if (patched_crtc_timing.pixel_encoding == PIXEL_ENCODING_YCBCR420) |
---|
| 297 | + data_fmt = 2; |
---|
| 298 | + |
---|
| 299 | + REG_UPDATE(OPTC_DATA_FORMAT_CONTROL, OPTC_DATA_FORMAT, data_fmt); |
---|
| 300 | + } |
---|
| 301 | + |
---|
| 302 | +#if defined(CONFIG_DRM_AMD_DC_DCN3_0) |
---|
| 303 | + if (optc1->tg_mask->OTG_H_TIMING_DIV_MODE != 0) { |
---|
| 304 | + if (optc1->opp_count == 4) |
---|
| 305 | + h_div = H_TIMING_DIV_BY4; |
---|
| 306 | + |
---|
| 307 | + REG_UPDATE(OTG_H_TIMING_CNTL, |
---|
| 308 | + OTG_H_TIMING_DIV_MODE, h_div); |
---|
| 309 | + } else |
---|
| 310 | +#endif |
---|
| 311 | + { |
---|
| 312 | + REG_UPDATE(OTG_H_TIMING_CNTL, |
---|
| 313 | + OTG_H_TIMING_DIV_BY2, h_div); |
---|
| 314 | + } |
---|
| 315 | +} |
---|
| 316 | + |
---|
| 317 | +void optc1_set_vtg_params(struct timing_generator *optc, |
---|
| 318 | + const struct dc_crtc_timing *dc_crtc_timing) |
---|
| 319 | +{ |
---|
| 320 | + struct dc_crtc_timing patched_crtc_timing; |
---|
| 321 | + uint32_t asic_blank_end; |
---|
| 322 | + uint32_t v_init; |
---|
| 323 | + uint32_t v_fp2 = 0; |
---|
| 324 | + int32_t vertical_line_start; |
---|
| 325 | + |
---|
| 326 | + struct optc *optc1 = DCN10TG_FROM_TG(optc); |
---|
| 327 | + |
---|
| 328 | + patched_crtc_timing = *dc_crtc_timing; |
---|
| 329 | + apply_front_porch_workaround(&patched_crtc_timing); |
---|
| 330 | + |
---|
| 331 | + /* VCOUNT_INIT is the start of blank */ |
---|
| 332 | + v_init = patched_crtc_timing.v_total - patched_crtc_timing.v_front_porch; |
---|
| 333 | + |
---|
| 334 | + /* end of blank = v_init - active */ |
---|
| 335 | + asic_blank_end = v_init - |
---|
| 336 | + patched_crtc_timing.v_border_bottom - |
---|
| 337 | + patched_crtc_timing.v_addressable - |
---|
| 338 | + patched_crtc_timing.v_border_top; |
---|
| 339 | + |
---|
| 340 | + /* if VSTARTUP is before VSYNC, FP2 is the offset, otherwise 0 */ |
---|
| 341 | + vertical_line_start = asic_blank_end - optc1->vstartup_start + 1; |
---|
| 342 | + if (vertical_line_start < 0) |
---|
| 343 | + v_fp2 = -vertical_line_start; |
---|
| 344 | + |
---|
| 345 | + /* Interlace */ |
---|
| 346 | + if (REG(OTG_INTERLACE_CONTROL)) { |
---|
| 347 | + if (patched_crtc_timing.flags.INTERLACE == 1) { |
---|
| 348 | + v_init = v_init / 2; |
---|
| 349 | + if ((optc1->vstartup_start/2)*2 > asic_blank_end) |
---|
| 350 | + v_fp2 = v_fp2 / 2; |
---|
| 351 | + } |
---|
| 352 | + } |
---|
| 353 | + |
---|
| 354 | + REG_UPDATE_2(CONTROL, |
---|
| 355 | + VTG0_FP2, v_fp2, |
---|
| 356 | + VTG0_VCOUNT_INIT, v_init); |
---|
361 | 357 | } |
---|
362 | 358 | |
---|
363 | 359 | void optc1_set_blank_data_double_buffer(struct timing_generator *optc, bool enable) |
---|
.. | .. |
---|
371 | 367 | } |
---|
372 | 368 | |
---|
373 | 369 | /** |
---|
| 370 | + * optc1_set_timing_double_buffer() - DRR double buffering control |
---|
| 371 | + * |
---|
| 372 | + * Sets double buffer point for V_TOTAL, H_TOTAL, VTOTAL_MIN, |
---|
| 373 | + * VTOTAL_MAX, VTOTAL_MIN_SEL and VTOTAL_MAX_SEL registers. |
---|
| 374 | + * |
---|
| 375 | + * Options: any time, start of frame, dp start of frame (range timing) |
---|
| 376 | + */ |
---|
| 377 | +void optc1_set_timing_double_buffer(struct timing_generator *optc, bool enable) |
---|
| 378 | +{ |
---|
| 379 | + struct optc *optc1 = DCN10TG_FROM_TG(optc); |
---|
| 380 | + uint32_t mode = enable ? 2 : 0; |
---|
| 381 | + |
---|
| 382 | + REG_UPDATE(OTG_DOUBLE_BUFFER_CONTROL, |
---|
| 383 | + OTG_RANGE_TIMING_DBUF_UPDATE_MODE, mode); |
---|
| 384 | +} |
---|
| 385 | + |
---|
| 386 | +/** |
---|
374 | 387 | * unblank_crtc |
---|
375 | 388 | * Call ASIC Control Object to UnBlank CRTC. |
---|
376 | 389 | */ |
---|
377 | 390 | static void optc1_unblank_crtc(struct timing_generator *optc) |
---|
378 | 391 | { |
---|
379 | 392 | struct optc *optc1 = DCN10TG_FROM_TG(optc); |
---|
380 | | - uint32_t vertical_interrupt_enable = 0; |
---|
381 | | - |
---|
382 | | - REG_GET(OTG_VERTICAL_INTERRUPT2_CONTROL, |
---|
383 | | - OTG_VERTICAL_INTERRUPT2_INT_ENABLE, &vertical_interrupt_enable); |
---|
384 | | - |
---|
385 | | - /* temporary work around for vertical interrupt, once vertical interrupt enabled, |
---|
386 | | - * this check will be removed. |
---|
387 | | - */ |
---|
388 | | - if (vertical_interrupt_enable) |
---|
389 | | - optc1_set_blank_data_double_buffer(optc, true); |
---|
390 | 393 | |
---|
391 | 394 | REG_UPDATE_2(OTG_BLANK_CONTROL, |
---|
392 | 395 | OTG_BLANK_DATA_EN, 0, |
---|
393 | 396 | OTG_BLANK_DE_MODE, 0); |
---|
| 397 | + |
---|
| 398 | + /* W/A for automated testing |
---|
| 399 | + * Automated testing will fail underflow test as there |
---|
| 400 | + * sporadic underflows which occur during the optc blank |
---|
| 401 | + * sequence. As a w/a, clear underflow on unblank. |
---|
| 402 | + * This prevents the failure, but will not mask actual |
---|
| 403 | + * underflow that affect real use cases. |
---|
| 404 | + */ |
---|
| 405 | + optc1_clear_optc_underflow(optc); |
---|
394 | 406 | } |
---|
395 | 407 | |
---|
396 | 408 | /** |
---|
.. | .. |
---|
452 | 464 | OTG_CLOCK_ON, 1, |
---|
453 | 465 | 1, 1000); |
---|
454 | 466 | } else { |
---|
| 467 | + |
---|
| 468 | + //last chance to clear underflow, otherwise, it will always there due to clock is off. |
---|
| 469 | + if (optc->funcs->is_optc_underflow_occurred(optc) == true) |
---|
| 470 | + optc->funcs->clear_optc_underflow(optc); |
---|
| 471 | + |
---|
455 | 472 | REG_UPDATE_2(OTG_CLOCK_CONTROL, |
---|
456 | 473 | OTG_CLOCK_GATE_DIS, 0, |
---|
457 | 474 | OTG_CLOCK_EN, 0); |
---|
.. | .. |
---|
484 | 501 | REG_UPDATE(CONTROL, |
---|
485 | 502 | VTG0_ENABLE, 1); |
---|
486 | 503 | |
---|
| 504 | + REG_SEQ_START(); |
---|
| 505 | + |
---|
487 | 506 | /* Enable CRTC */ |
---|
488 | 507 | REG_UPDATE_2(OTG_CONTROL, |
---|
489 | 508 | OTG_DISABLE_POINT_CNTL, 3, |
---|
490 | 509 | OTG_MASTER_EN, 1); |
---|
| 510 | + |
---|
| 511 | + REG_SEQ_SUBMIT(); |
---|
| 512 | + REG_SEQ_WAIT_DONE(); |
---|
491 | 513 | |
---|
492 | 514 | return true; |
---|
493 | 515 | } |
---|
.. | .. |
---|
532 | 554 | struct timing_generator *optc, |
---|
533 | 555 | const struct dc_crtc_timing *timing) |
---|
534 | 556 | { |
---|
535 | | - uint32_t interlace_factor; |
---|
536 | 557 | uint32_t v_blank; |
---|
537 | 558 | uint32_t h_blank; |
---|
538 | 559 | uint32_t min_v_blank; |
---|
.. | .. |
---|
540 | 561 | |
---|
541 | 562 | ASSERT(timing != NULL); |
---|
542 | 563 | |
---|
543 | | - interlace_factor = timing->flags.INTERLACE ? 2 : 1; |
---|
544 | 564 | v_blank = (timing->v_total - timing->v_addressable - |
---|
545 | | - timing->v_border_top - timing->v_border_bottom) * |
---|
546 | | - interlace_factor; |
---|
| 565 | + timing->v_border_top - timing->v_border_bottom); |
---|
547 | 566 | |
---|
548 | 567 | h_blank = (timing->h_total - timing->h_addressable - |
---|
549 | 568 | timing->h_border_right - |
---|
.. | .. |
---|
615 | 634 | void optc1_lock(struct timing_generator *optc) |
---|
616 | 635 | { |
---|
617 | 636 | struct optc *optc1 = DCN10TG_FROM_TG(optc); |
---|
| 637 | + uint32_t regval = 0; |
---|
| 638 | + |
---|
| 639 | + regval = REG_READ(OTG_CONTROL); |
---|
| 640 | + |
---|
| 641 | + /* otg is not running, do not need to be locked */ |
---|
| 642 | + if ((regval & 0x1) == 0x0) |
---|
| 643 | + return; |
---|
618 | 644 | |
---|
619 | 645 | REG_SET(OTG_GLOBAL_CONTROL0, 0, |
---|
620 | 646 | OTG_MASTER_UPDATE_LOCK_SEL, optc->inst); |
---|
.. | .. |
---|
622 | 648 | OTG_MASTER_UPDATE_LOCK, 1); |
---|
623 | 649 | |
---|
624 | 650 | /* Should be fast, status does not update on maximus */ |
---|
625 | | - if (optc->ctx->dce_environment != DCE_ENV_FPGA_MAXIMUS) |
---|
| 651 | + if (optc->ctx->dce_environment != DCE_ENV_FPGA_MAXIMUS) { |
---|
| 652 | + |
---|
626 | 653 | REG_WAIT(OTG_MASTER_UPDATE_LOCK, |
---|
627 | 654 | UPDATE_LOCK_STATUS, 1, |
---|
628 | 655 | 1, 10); |
---|
| 656 | + } |
---|
629 | 657 | } |
---|
630 | 658 | |
---|
631 | 659 | void optc1_unlock(struct timing_generator *optc) |
---|
.. | .. |
---|
805 | 833 | |
---|
806 | 834 | void optc1_set_static_screen_control( |
---|
807 | 835 | struct timing_generator *optc, |
---|
808 | | - uint32_t value) |
---|
| 836 | + uint32_t event_triggers, |
---|
| 837 | + uint32_t num_frames) |
---|
809 | 838 | { |
---|
810 | 839 | struct optc *optc1 = DCN10TG_FROM_TG(optc); |
---|
| 840 | + |
---|
| 841 | + // By register spec, it only takes 8 bit value |
---|
| 842 | + if (num_frames > 0xFF) |
---|
| 843 | + num_frames = 0xFF; |
---|
811 | 844 | |
---|
812 | 845 | /* Bit 8 is no longer applicable in RV for PSR case, |
---|
813 | 846 | * set bit 8 to 0 if given |
---|
814 | 847 | */ |
---|
815 | | - if ((value & STATIC_SCREEN_EVENT_MASK_RANGETIMING_DOUBLE_BUFFER_UPDATE_EN) |
---|
| 848 | + if ((event_triggers & STATIC_SCREEN_EVENT_MASK_RANGETIMING_DOUBLE_BUFFER_UPDATE_EN) |
---|
816 | 849 | != 0) |
---|
817 | | - value = value & |
---|
| 850 | + event_triggers = event_triggers & |
---|
818 | 851 | ~STATIC_SCREEN_EVENT_MASK_RANGETIMING_DOUBLE_BUFFER_UPDATE_EN; |
---|
819 | 852 | |
---|
820 | 853 | REG_SET_2(OTG_STATIC_SCREEN_CONTROL, 0, |
---|
821 | | - OTG_STATIC_SCREEN_EVENT_MASK, value, |
---|
822 | | - OTG_STATIC_SCREEN_FRAME_COUNT, 2); |
---|
| 854 | + OTG_STATIC_SCREEN_EVENT_MASK, event_triggers, |
---|
| 855 | + OTG_STATIC_SCREEN_FRAME_COUNT, num_frames); |
---|
| 856 | +} |
---|
| 857 | + |
---|
| 858 | +void optc1_setup_manual_trigger(struct timing_generator *optc) |
---|
| 859 | +{ |
---|
| 860 | + struct optc *optc1 = DCN10TG_FROM_TG(optc); |
---|
| 861 | + |
---|
| 862 | + REG_SET(OTG_GLOBAL_CONTROL2, 0, |
---|
| 863 | + MANUAL_FLOW_CONTROL_SEL, optc->inst); |
---|
| 864 | + |
---|
| 865 | + REG_SET_8(OTG_TRIGA_CNTL, 0, |
---|
| 866 | + OTG_TRIGA_SOURCE_SELECT, 22, |
---|
| 867 | + OTG_TRIGA_SOURCE_PIPE_SELECT, optc->inst, |
---|
| 868 | + OTG_TRIGA_RISING_EDGE_DETECT_CNTL, 1, |
---|
| 869 | + OTG_TRIGA_FALLING_EDGE_DETECT_CNTL, 0, |
---|
| 870 | + OTG_TRIGA_POLARITY_SELECT, 0, |
---|
| 871 | + OTG_TRIGA_FREQUENCY_SELECT, 0, |
---|
| 872 | + OTG_TRIGA_DELAY, 0, |
---|
| 873 | + OTG_TRIGA_CLEAR, 1); |
---|
| 874 | +} |
---|
| 875 | + |
---|
| 876 | +void optc1_program_manual_trigger(struct timing_generator *optc) |
---|
| 877 | +{ |
---|
| 878 | + struct optc *optc1 = DCN10TG_FROM_TG(optc); |
---|
| 879 | + |
---|
| 880 | + REG_SET(OTG_MANUAL_FLOW_CONTROL, 0, |
---|
| 881 | + MANUAL_FLOW_CONTROL, 1); |
---|
| 882 | + |
---|
| 883 | + REG_SET(OTG_MANUAL_FLOW_CONTROL, 0, |
---|
| 884 | + MANUAL_FLOW_CONTROL, 0); |
---|
823 | 885 | } |
---|
824 | 886 | |
---|
825 | 887 | |
---|
.. | .. |
---|
842 | 904 | params->vertical_total_max > 0 && |
---|
843 | 905 | params->vertical_total_min > 0) { |
---|
844 | 906 | |
---|
| 907 | + if (params->vertical_total_mid != 0) { |
---|
| 908 | + |
---|
| 909 | + REG_SET(OTG_V_TOTAL_MID, 0, |
---|
| 910 | + OTG_V_TOTAL_MID, params->vertical_total_mid - 1); |
---|
| 911 | + |
---|
| 912 | + REG_UPDATE_2(OTG_V_TOTAL_CONTROL, |
---|
| 913 | + OTG_VTOTAL_MID_REPLACING_MAX_EN, 1, |
---|
| 914 | + OTG_VTOTAL_MID_FRAME_NUM, |
---|
| 915 | + (uint8_t)params->vertical_total_mid_frame_num); |
---|
| 916 | + |
---|
| 917 | + } |
---|
| 918 | + |
---|
845 | 919 | REG_SET(OTG_V_TOTAL_MAX, 0, |
---|
846 | 920 | OTG_V_TOTAL_MAX, params->vertical_total_max - 1); |
---|
847 | 921 | |
---|
.. | .. |
---|
854 | 928 | OTG_FORCE_LOCK_ON_EVENT, 0, |
---|
855 | 929 | OTG_SET_V_TOTAL_MIN_MASK_EN, 0, |
---|
856 | 930 | OTG_SET_V_TOTAL_MIN_MASK, 0); |
---|
| 931 | + |
---|
| 932 | + // Setup manual flow control for EOF via TRIG_A |
---|
| 933 | + optc->funcs->setup_manual_trigger(optc); |
---|
| 934 | + |
---|
857 | 935 | } else { |
---|
858 | 936 | REG_UPDATE_4(OTG_V_TOTAL_CONTROL, |
---|
859 | 937 | OTG_SET_V_TOTAL_MIN_MASK, 0, |
---|
.. | .. |
---|
1161 | 1239 | REG_UPDATE_3(OTG_STEREO_CONTROL, |
---|
1162 | 1240 | OTG_STEREO_EN, stereo_en, |
---|
1163 | 1241 | OTG_STEREO_SYNC_OUTPUT_LINE_NUM, 0, |
---|
1164 | | - OTG_STEREO_SYNC_OUTPUT_POLARITY, 0); |
---|
| 1242 | + OTG_STEREO_SYNC_OUTPUT_POLARITY, flags->RIGHT_EYE_POLARITY == 0 ? 0 : 1); |
---|
1165 | 1243 | |
---|
1166 | 1244 | if (flags->PROGRAM_POLARITY) |
---|
1167 | 1245 | REG_UPDATE(OTG_STEREO_CONTROL, |
---|
.. | .. |
---|
1173 | 1251 | OTG_DISABLE_STEREOSYNC_OUTPUT_FOR_DP, 1); |
---|
1174 | 1252 | |
---|
1175 | 1253 | if (flags->PROGRAM_STEREO) |
---|
1176 | | - REG_UPDATE_3(OTG_3D_STRUCTURE_CONTROL, |
---|
| 1254 | + REG_UPDATE_2(OTG_3D_STRUCTURE_CONTROL, |
---|
1177 | 1255 | OTG_3D_STRUCTURE_EN, flags->FRAME_PACKED, |
---|
1178 | | - OTG_3D_STRUCTURE_V_UPDATE_MODE, flags->FRAME_PACKED, |
---|
1179 | 1256 | OTG_3D_STRUCTURE_STEREO_SEL_OVR, flags->FRAME_PACKED); |
---|
1180 | 1257 | |
---|
1181 | 1258 | } |
---|
.. | .. |
---|
1206 | 1283 | |
---|
1207 | 1284 | return ret; |
---|
1208 | 1285 | } |
---|
| 1286 | + |
---|
| 1287 | +bool optc1_get_hw_timing(struct timing_generator *tg, |
---|
| 1288 | + struct dc_crtc_timing *hw_crtc_timing) |
---|
| 1289 | +{ |
---|
| 1290 | + struct dcn_otg_state s = {0}; |
---|
| 1291 | + |
---|
| 1292 | + if (tg == NULL || hw_crtc_timing == NULL) |
---|
| 1293 | + return false; |
---|
| 1294 | + |
---|
| 1295 | + optc1_read_otg_state(DCN10TG_FROM_TG(tg), &s); |
---|
| 1296 | + |
---|
| 1297 | + hw_crtc_timing->h_total = s.h_total + 1; |
---|
| 1298 | + hw_crtc_timing->h_addressable = s.h_total - ((s.h_total - s.h_blank_start) + s.h_blank_end); |
---|
| 1299 | + hw_crtc_timing->h_front_porch = s.h_total + 1 - s.h_blank_start; |
---|
| 1300 | + hw_crtc_timing->h_sync_width = s.h_sync_a_end - s.h_sync_a_start; |
---|
| 1301 | + |
---|
| 1302 | + hw_crtc_timing->v_total = s.v_total + 1; |
---|
| 1303 | + hw_crtc_timing->v_addressable = s.v_total - ((s.v_total - s.v_blank_start) + s.v_blank_end); |
---|
| 1304 | + hw_crtc_timing->v_front_porch = s.v_total + 1 - s.v_blank_start; |
---|
| 1305 | + hw_crtc_timing->v_sync_width = s.v_sync_a_end - s.v_sync_a_start; |
---|
| 1306 | + |
---|
| 1307 | + return true; |
---|
| 1308 | +} |
---|
| 1309 | + |
---|
1209 | 1310 | |
---|
1210 | 1311 | void optc1_read_otg_state(struct optc *optc1, |
---|
1211 | 1312 | struct dcn_otg_state *s) |
---|
.. | .. |
---|
1298 | 1399 | void optc1_tg_init(struct timing_generator *optc) |
---|
1299 | 1400 | { |
---|
1300 | 1401 | optc1_set_blank_data_double_buffer(optc, true); |
---|
| 1402 | + optc1_set_timing_double_buffer(optc, true); |
---|
1301 | 1403 | optc1_clear_optc_underflow(optc); |
---|
1302 | 1404 | } |
---|
1303 | 1405 | |
---|
.. | .. |
---|
1393 | 1495 | static const struct timing_generator_funcs dcn10_tg_funcs = { |
---|
1394 | 1496 | .validate_timing = optc1_validate_timing, |
---|
1395 | 1497 | .program_timing = optc1_program_timing, |
---|
1396 | | - .program_vline_interrupt = optc1_program_vline_interrupt, |
---|
| 1498 | + .setup_vertical_interrupt0 = optc1_setup_vertical_interrupt0, |
---|
| 1499 | + .setup_vertical_interrupt1 = optc1_setup_vertical_interrupt1, |
---|
| 1500 | + .setup_vertical_interrupt2 = optc1_setup_vertical_interrupt2, |
---|
1397 | 1501 | .program_global_sync = optc1_program_global_sync, |
---|
1398 | 1502 | .enable_crtc = optc1_enable_crtc, |
---|
1399 | 1503 | .disable_crtc = optc1_disable_crtc, |
---|
.. | .. |
---|
1428 | 1532 | .clear_optc_underflow = optc1_clear_optc_underflow, |
---|
1429 | 1533 | .get_crc = optc1_get_crc, |
---|
1430 | 1534 | .configure_crc = optc1_configure_crc, |
---|
| 1535 | + .set_vtg_params = optc1_set_vtg_params, |
---|
| 1536 | + .program_manual_trigger = optc1_program_manual_trigger, |
---|
| 1537 | + .setup_manual_trigger = optc1_setup_manual_trigger, |
---|
| 1538 | + .get_hw_timing = optc1_get_hw_timing, |
---|
1431 | 1539 | }; |
---|
1432 | 1540 | |
---|
1433 | 1541 | void dcn10_timing_generator_init(struct optc *optc1) |
---|
.. | .. |
---|
1443 | 1551 | optc1->min_h_sync_width = 8; |
---|
1444 | 1552 | optc1->min_v_sync_width = 1; |
---|
1445 | 1553 | } |
---|
| 1554 | + |
---|
| 1555 | +/* "Containter" vs. "pixel" is a concept within HW blocks, mostly those closer to the back-end. It works like this: |
---|
| 1556 | + * |
---|
| 1557 | + * - In most of the formats (RGB or YCbCr 4:4:4, 4:2:2 uncompressed and DSC 4:2:2 Simple) pixel rate is the same as |
---|
| 1558 | + * containter rate. |
---|
| 1559 | + * |
---|
| 1560 | + * - In 4:2:0 (DSC or uncompressed) there are two pixels per container, hence the target container rate has to be |
---|
| 1561 | + * halved to maintain the correct pixel rate. |
---|
| 1562 | + * |
---|
| 1563 | + * - Unlike 4:2:2 uncompressed, DSC 4:2:2 Native also has two pixels per container (this happens when DSC is applied |
---|
| 1564 | + * to it) and has to be treated the same as 4:2:0, i.e. target containter rate has to be halved in this case as well. |
---|
| 1565 | + * |
---|
| 1566 | + */ |
---|
| 1567 | +bool optc1_is_two_pixels_per_containter(const struct dc_crtc_timing *timing) |
---|
| 1568 | +{ |
---|
| 1569 | + bool two_pix = timing->pixel_encoding == PIXEL_ENCODING_YCBCR420; |
---|
| 1570 | + |
---|
| 1571 | + two_pix = two_pix || (timing->flags.DSC && timing->pixel_encoding == PIXEL_ENCODING_YCBCR422 |
---|
| 1572 | + && !timing->dsc_cfg.ycbcr422_simple); |
---|
| 1573 | + return two_pix; |
---|
| 1574 | +} |
---|
| 1575 | + |
---|