1 | /* |
2 | * Copyright 2012-15 Advanced Micro Devices, Inc. |
3 | * |
4 | * Permission is hereby granted, free of charge, to any person obtaining a |
5 | * copy of this software and associated documentation files (the "Software"), |
6 | * to deal in the Software without restriction, including without limitation |
7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, |
8 | * and/or sell copies of the Software, and to permit persons to whom the |
9 | * Software is furnished to do so, subject to the following conditions: |
10 | * |
11 | * The above copyright notice and this permission notice shall be included in |
12 | * all copies or substantial portions of the Software. |
13 | * |
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
17 | * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR |
18 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, |
19 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR |
20 | * OTHER DEALINGS IN THE SOFTWARE. |
21 | * |
22 | * Authors: AMD |
23 | * |
24 | */ |
25 | |
26 | #include "dm_services.h" |
27 | |
28 | #include "dce/dce_12_0_offset.h" |
29 | #include "dce/dce_12_0_sh_mask.h" |
30 | #include "soc15_hw_ip.h" |
31 | #include "vega10_ip_offset.h" |
32 | |
33 | #include "dc_types.h" |
34 | #include "dc_bios_types.h" |
35 | |
36 | #include "include/grph_object_id.h" |
37 | #include "include/logger_interface.h" |
38 | #include "dce120_timing_generator.h" |
39 | |
40 | #include "timing_generator.h" |
41 | |
42 | #define CRTC_REG_UPDATE_N(reg_name, n, ...) \ |
43 | generic_reg_update_soc15(tg110->base.ctx, tg110->offsets.crtc, reg_name, n, __VA_ARGS__) |
44 | |
45 | #define CRTC_REG_SET_N(reg_name, n, ...) \ |
46 | generic_reg_set_soc15(tg110->base.ctx, tg110->offsets.crtc, reg_name, n, __VA_ARGS__) |
47 | |
48 | #define CRTC_REG_UPDATE(reg, field, val) \ |
49 | CRTC_REG_UPDATE_N(reg, 1, FD(reg##__##field), val) |
50 | |
51 | #define CRTC_REG_UPDATE_2(reg, field1, val1, field2, val2) \ |
52 | CRTC_REG_UPDATE_N(reg, 2, FD(reg##__##field1), val1, FD(reg##__##field2), val2) |
53 | |
54 | #define CRTC_REG_UPDATE_3(reg, field1, val1, field2, val2, field3, val3) \ |
55 | CRTC_REG_UPDATE_N(reg, 3, FD(reg##__##field1), val1, FD(reg##__##field2), val2, FD(reg##__##field3), val3) |
56 | |
57 | #define CRTC_REG_UPDATE_4(reg, field1, val1, field2, val2, field3, val3, field4, val4) \ |
58 | CRTC_REG_UPDATE_N(reg, 3, FD(reg##__##field1), val1, FD(reg##__##field2), val2, FD(reg##__##field3), val3, FD(reg##__##field4), val4) |
59 | |
60 | #define CRTC_REG_UPDATE_5(reg, field1, val1, field2, val2, field3, val3, field4, val4, field5, val5) \ |
61 | CRTC_REG_UPDATE_N(reg, 3, FD(reg##__##field1), val1, FD(reg##__##field2), val2, FD(reg##__##field3), val3, FD(reg##__##field4), val4, FD(reg##__##field5), val5) |
62 | |
63 | #define CRTC_REG_SET(reg, field, val) \ |
64 | CRTC_REG_SET_N(reg, 1, FD(reg##__##field), val) |
65 | |
66 | #define CRTC_REG_SET_2(reg, field1, val1, field2, val2) \ |
67 | CRTC_REG_SET_N(reg, 2, FD(reg##__##field1), val1, FD(reg##__##field2), val2) |
68 | |
69 | #define CRTC_REG_SET_3(reg, field1, val1, field2, val2, field3, val3) \ |
70 | CRTC_REG_SET_N(reg, 3, FD(reg##__##field1), val1, FD(reg##__##field2), val2, FD(reg##__##field3), val3) |
71 | |
72 | /* |
73 | ***************************************************************************** |
74 | * Function: is_in_vertical_blank |
75 | * |
76 | * @brief |
77 | * check the current status of CRTC to check if we are in Vertical Blank |
78 | * regioneased" state |
79 | * |
80 | * @return |
81 | * true if currently in blank region, false otherwise |
82 | * |
83 | ***************************************************************************** |
84 | */ |
85 | static bool dce120_timing_generator_is_in_vertical_blank( |
86 | struct timing_generator *tg) |
87 | { |
88 | uint32_t field = 0; |
89 | struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); |
90 | uint32_t value = dm_read_reg_soc15( |
91 | tg->ctx, |
92 | mmCRTC0_CRTC_STATUS, |
93 | tg110->offsets.crtc); |
94 | |
95 | field = get_reg_field_value(value, CRTC0_CRTC_STATUS, CRTC_V_BLANK); |
96 | return field == 1; |
97 | } |
98 | |
99 | |
100 | /* determine if given timing can be supported by TG */ |
101 | static bool dce120_timing_generator_validate_timing( |
102 | struct timing_generator *tg, |
103 | const struct dc_crtc_timing *timing, |
104 | enum signal_type signal) |
105 | { |
106 | uint32_t interlace_factor = timing->flags.INTERLACE ? 2 : 1; |
107 | uint32_t v_blank = |
108 | (timing->v_total - timing->v_addressable - |
109 | timing->v_border_top - timing->v_border_bottom) * |
110 | interlace_factor; |
111 | struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); |
112 | |
113 | if (!dce110_timing_generator_validate_timing( |
114 | tg, |
115 | timing, |
116 | signal)) |
117 | return false; |
118 | |
119 | |
120 | if (v_blank < tg110->min_v_blank || |
121 | timing->h_sync_width < tg110->min_h_sync_width || |
122 | timing->v_sync_width < tg110->min_v_sync_width) |
123 | return false; |
124 | |
125 | return true; |
126 | } |
127 | |
128 | static bool dce120_tg_validate_timing(struct timing_generator *tg, |
129 | const struct dc_crtc_timing *timing) |
130 | { |
131 | return dce120_timing_generator_validate_timing(tg, timing, signal: SIGNAL_TYPE_NONE); |
132 | } |
133 | |
134 | /******** HW programming ************/ |
135 | /* Disable/Enable Timing Generator */ |
136 | static bool dce120_timing_generator_enable_crtc(struct timing_generator *tg) |
137 | { |
138 | enum bp_result result; |
139 | struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); |
140 | |
141 | /* Set MASTER_UPDATE_MODE to 0 |
142 | * This is needed for DRR, and also suggested to be default value by Syed.*/ |
143 | |
144 | CRTC_REG_UPDATE(CRTC0_CRTC_MASTER_UPDATE_MODE, |
145 | MASTER_UPDATE_MODE, 0); |
146 | |
147 | CRTC_REG_UPDATE(CRTC0_CRTC_MASTER_UPDATE_LOCK, |
148 | UNDERFLOW_UPDATE_LOCK, 0); |
149 | |
150 | /* TODO API for AtomFirmware didn't change*/ |
151 | result = tg->bp->funcs->enable_crtc(tg->bp, tg110->controller_id, true); |
152 | |
153 | return result == BP_RESULT_OK; |
154 | } |
155 | |
156 | static void dce120_timing_generator_set_early_control( |
157 | struct timing_generator *tg, |
158 | uint32_t early_cntl) |
159 | { |
160 | struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); |
161 | |
162 | CRTC_REG_UPDATE(CRTC0_CRTC_CONTROL, |
163 | CRTC_HBLANK_EARLY_CONTROL, early_cntl); |
164 | } |
165 | |
166 | /**************** TG current status ******************/ |
167 | |
168 | /* return the current frame counter. Used by Linux kernel DRM */ |
169 | static uint32_t dce120_timing_generator_get_vblank_counter( |
170 | struct timing_generator *tg) |
171 | { |
172 | struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); |
173 | uint32_t value = dm_read_reg_soc15( |
174 | tg->ctx, |
175 | mmCRTC0_CRTC_STATUS_FRAME_COUNT, |
176 | tg110->offsets.crtc); |
177 | uint32_t field = get_reg_field_value( |
178 | value, CRTC0_CRTC_STATUS_FRAME_COUNT, CRTC_FRAME_COUNT); |
179 | |
180 | return field; |
181 | } |
182 | |
183 | /* Get current H and V position */ |
184 | static void dce120_timing_generator_get_crtc_position( |
185 | struct timing_generator *tg, |
186 | struct crtc_position *position) |
187 | { |
188 | struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); |
189 | uint32_t value = dm_read_reg_soc15( |
190 | tg->ctx, |
191 | mmCRTC0_CRTC_STATUS_POSITION, |
192 | tg110->offsets.crtc); |
193 | |
194 | position->horizontal_count = get_reg_field_value(value, |
195 | CRTC0_CRTC_STATUS_POSITION, CRTC_HORZ_COUNT); |
196 | |
197 | position->vertical_count = get_reg_field_value(value, |
198 | CRTC0_CRTC_STATUS_POSITION, CRTC_VERT_COUNT); |
199 | |
200 | value = dm_read_reg_soc15( |
201 | tg->ctx, |
202 | mmCRTC0_CRTC_NOM_VERT_POSITION, |
203 | tg110->offsets.crtc); |
204 | |
205 | position->nominal_vcount = get_reg_field_value(value, |
206 | CRTC0_CRTC_NOM_VERT_POSITION, CRTC_VERT_COUNT_NOM); |
207 | } |
208 | |
209 | /* wait until TG is in beginning of vertical blank region */ |
210 | static void dce120_timing_generator_wait_for_vblank(struct timing_generator *tg) |
211 | { |
212 | /* We want to catch beginning of VBlank here, so if the first try are |
213 | * in VBlank, we might be very close to Active, in this case wait for |
214 | * another frame |
215 | */ |
216 | while (dce120_timing_generator_is_in_vertical_blank(tg)) { |
217 | if (!tg->funcs->is_counter_moving(tg)) { |
218 | /* error - no point to wait if counter is not moving */ |
219 | break; |
220 | } |
221 | } |
222 | |
223 | while (!dce120_timing_generator_is_in_vertical_blank(tg)) { |
224 | if (!tg->funcs->is_counter_moving(tg)) { |
225 | /* error - no point to wait if counter is not moving */ |
226 | break; |
227 | } |
228 | } |
229 | } |
230 | |
231 | /* wait until TG is in beginning of active region */ |
232 | static void dce120_timing_generator_wait_for_vactive(struct timing_generator *tg) |
233 | { |
234 | while (dce120_timing_generator_is_in_vertical_blank(tg)) { |
235 | if (!tg->funcs->is_counter_moving(tg)) { |
236 | /* error - no point to wait if counter is not moving */ |
237 | break; |
238 | } |
239 | } |
240 | } |
241 | |
242 | /*********** Timing Generator Synchronization routines ****/ |
243 | |
244 | /* Setups Global Swap Lock group, TimingServer or TimingClient*/ |
245 | static void dce120_timing_generator_setup_global_swap_lock( |
246 | struct timing_generator *tg, |
247 | const struct dcp_gsl_params *gsl_params) |
248 | { |
249 | struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); |
250 | uint32_t value_crtc_vtotal = |
251 | dm_read_reg_soc15(tg->ctx, |
252 | mmCRTC0_CRTC_V_TOTAL, |
253 | tg110->offsets.crtc); |
254 | /* Checkpoint relative to end of frame */ |
255 | uint32_t check_point = |
256 | get_reg_field_value(value_crtc_vtotal, |
257 | CRTC0_CRTC_V_TOTAL, |
258 | CRTC_V_TOTAL); |
259 | |
260 | |
261 | dm_write_reg_soc15(tg->ctx, mmCRTC0_CRTC_GSL_WINDOW, tg110->offsets.crtc, 0); |
262 | |
263 | CRTC_REG_UPDATE_N(DCP0_DCP_GSL_CONTROL, 6, |
264 | /* This pipe will belong to GSL Group zero. */ |
265 | FD(DCP0_DCP_GSL_CONTROL__DCP_GSL0_EN), 1, |
266 | FD(DCP0_DCP_GSL_CONTROL__DCP_GSL_MASTER_EN), gsl_params->gsl_master == tg->inst, |
267 | FD(DCP0_DCP_GSL_CONTROL__DCP_GSL_HSYNC_FLIP_FORCE_DELAY), HFLIP_READY_DELAY, |
268 | /* Keep signal low (pending high) during 6 lines. |
269 | * Also defines minimum interval before re-checking signal. */ |
270 | FD(DCP0_DCP_GSL_CONTROL__DCP_GSL_HSYNC_FLIP_CHECK_DELAY), HFLIP_CHECK_DELAY, |
271 | /* DCP_GSL_PURPOSE_SURFACE_FLIP */ |
272 | FD(DCP0_DCP_GSL_CONTROL__DCP_GSL_SYNC_SOURCE), 0, |
273 | FD(DCP0_DCP_GSL_CONTROL__DCP_GSL_DELAY_SURFACE_UPDATE_PENDING), 1); |
274 | |
275 | CRTC_REG_SET_2( |
276 | CRTC0_CRTC_GSL_CONTROL, |
277 | CRTC_GSL_CHECK_LINE_NUM, check_point - FLIP_READY_BACK_LOOKUP, |
278 | CRTC_GSL_FORCE_DELAY, VFLIP_READY_DELAY); |
279 | } |
280 | |
281 | /* Clear all the register writes done by setup_global_swap_lock */ |
282 | static void dce120_timing_generator_tear_down_global_swap_lock( |
283 | struct timing_generator *tg) |
284 | { |
285 | struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); |
286 | |
287 | /* Settig HW default values from reg specs */ |
288 | CRTC_REG_SET_N(DCP0_DCP_GSL_CONTROL, 6, |
289 | FD(DCP0_DCP_GSL_CONTROL__DCP_GSL0_EN), 0, |
290 | FD(DCP0_DCP_GSL_CONTROL__DCP_GSL_MASTER_EN), 0, |
291 | FD(DCP0_DCP_GSL_CONTROL__DCP_GSL_HSYNC_FLIP_FORCE_DELAY), HFLIP_READY_DELAY, |
292 | FD(DCP0_DCP_GSL_CONTROL__DCP_GSL_HSYNC_FLIP_CHECK_DELAY), HFLIP_CHECK_DELAY, |
293 | /* DCP_GSL_PURPOSE_SURFACE_FLIP */ |
294 | FD(DCP0_DCP_GSL_CONTROL__DCP_GSL_SYNC_SOURCE), 0, |
295 | FD(DCP0_DCP_GSL_CONTROL__DCP_GSL_DELAY_SURFACE_UPDATE_PENDING), 0); |
296 | |
297 | CRTC_REG_SET_2(CRTC0_CRTC_GSL_CONTROL, |
298 | CRTC_GSL_CHECK_LINE_NUM, 0, |
299 | CRTC_GSL_FORCE_DELAY, 0x2); /*TODO Why this value here ?*/ |
300 | } |
301 | |
302 | /* Reset slave controllers on master VSync */ |
303 | static void dce120_timing_generator_enable_reset_trigger( |
304 | struct timing_generator *tg, |
305 | int source) |
306 | { |
307 | enum trigger_source_select trig_src_select = TRIGGER_SOURCE_SELECT_LOGIC_ZERO; |
308 | struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); |
309 | uint32_t rising_edge = 0; |
310 | uint32_t falling_edge = 0; |
311 | /* Setup trigger edge */ |
312 | uint32_t pol_value = dm_read_reg_soc15( |
313 | tg->ctx, |
314 | mmCRTC0_CRTC_V_SYNC_A_CNTL, |
315 | tg110->offsets.crtc); |
316 | |
317 | /* Register spec has reversed definition: |
318 | * 0 for positive, 1 for negative */ |
319 | if (get_reg_field_value(pol_value, |
320 | CRTC0_CRTC_V_SYNC_A_CNTL, |
321 | CRTC_V_SYNC_A_POL) == 0) { |
322 | rising_edge = 1; |
323 | } else { |
324 | falling_edge = 1; |
325 | } |
326 | |
327 | /* TODO What about other sources ?*/ |
328 | trig_src_select = TRIGGER_SOURCE_SELECT_GSL_GROUP0; |
329 | |
330 | CRTC_REG_UPDATE_N(CRTC0_CRTC_TRIGB_CNTL, 7, |
331 | FD(CRTC0_CRTC_TRIGB_CNTL__CRTC_TRIGB_SOURCE_SELECT), trig_src_select, |
332 | FD(CRTC0_CRTC_TRIGB_CNTL__CRTC_TRIGB_POLARITY_SELECT), TRIGGER_POLARITY_SELECT_LOGIC_ZERO, |
333 | FD(CRTC0_CRTC_TRIGB_CNTL__CRTC_TRIGB_RISING_EDGE_DETECT_CNTL), rising_edge, |
334 | FD(CRTC0_CRTC_TRIGB_CNTL__CRTC_TRIGB_FALLING_EDGE_DETECT_CNTL), falling_edge, |
335 | /* send every signal */ |
336 | FD(CRTC0_CRTC_TRIGB_CNTL__CRTC_TRIGB_FREQUENCY_SELECT), 0, |
337 | /* no delay */ |
338 | FD(CRTC0_CRTC_TRIGB_CNTL__CRTC_TRIGB_DELAY), 0, |
339 | /* clear trigger status */ |
340 | FD(CRTC0_CRTC_TRIGB_CNTL__CRTC_TRIGB_CLEAR), 1); |
341 | |
342 | CRTC_REG_UPDATE_3( |
343 | CRTC0_CRTC_FORCE_COUNT_NOW_CNTL, |
344 | CRTC_FORCE_COUNT_NOW_MODE, 2, |
345 | CRTC_FORCE_COUNT_NOW_TRIG_SEL, 1, |
346 | CRTC_FORCE_COUNT_NOW_CLEAR, 1); |
347 | } |
348 | |
349 | /* disabling trigger-reset */ |
350 | static void dce120_timing_generator_disable_reset_trigger( |
351 | struct timing_generator *tg) |
352 | { |
353 | struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); |
354 | |
355 | CRTC_REG_UPDATE_2( |
356 | CRTC0_CRTC_FORCE_COUNT_NOW_CNTL, |
357 | CRTC_FORCE_COUNT_NOW_MODE, 0, |
358 | CRTC_FORCE_COUNT_NOW_CLEAR, 1); |
359 | |
360 | CRTC_REG_UPDATE_3( |
361 | CRTC0_CRTC_TRIGB_CNTL, |
362 | CRTC_TRIGB_SOURCE_SELECT, TRIGGER_SOURCE_SELECT_LOGIC_ZERO, |
363 | CRTC_TRIGB_POLARITY_SELECT, TRIGGER_POLARITY_SELECT_LOGIC_ZERO, |
364 | /* clear trigger status */ |
365 | CRTC_TRIGB_CLEAR, 1); |
366 | |
367 | } |
368 | |
369 | /* Checks whether CRTC triggered reset occurred */ |
370 | static bool dce120_timing_generator_did_triggered_reset_occur( |
371 | struct timing_generator *tg) |
372 | { |
373 | struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); |
374 | uint32_t value = dm_read_reg_soc15( |
375 | tg->ctx, |
376 | mmCRTC0_CRTC_FORCE_COUNT_NOW_CNTL, |
377 | tg110->offsets.crtc); |
378 | |
379 | return get_reg_field_value(value, |
380 | CRTC0_CRTC_FORCE_COUNT_NOW_CNTL, |
381 | CRTC_FORCE_COUNT_NOW_OCCURRED) != 0; |
382 | } |
383 | |
384 | |
385 | /******** Stuff to move to other virtual HW objects *****************/ |
386 | /* Move to enable accelerated mode */ |
387 | static void dce120_timing_generator_disable_vga(struct timing_generator *tg) |
388 | { |
389 | uint32_t offset = 0; |
390 | uint32_t value = 0; |
391 | struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); |
392 | |
393 | switch (tg110->controller_id) { |
394 | case CONTROLLER_ID_D0: |
395 | offset = 0; |
396 | break; |
397 | case CONTROLLER_ID_D1: |
398 | offset = mmD2VGA_CONTROL - mmD1VGA_CONTROL; |
399 | break; |
400 | case CONTROLLER_ID_D2: |
401 | offset = mmD3VGA_CONTROL - mmD1VGA_CONTROL; |
402 | break; |
403 | case CONTROLLER_ID_D3: |
404 | offset = mmD4VGA_CONTROL - mmD1VGA_CONTROL; |
405 | break; |
406 | case CONTROLLER_ID_D4: |
407 | offset = mmD5VGA_CONTROL - mmD1VGA_CONTROL; |
408 | break; |
409 | case CONTROLLER_ID_D5: |
410 | offset = mmD6VGA_CONTROL - mmD1VGA_CONTROL; |
411 | break; |
412 | default: |
413 | break; |
414 | } |
415 | |
416 | value = dm_read_reg_soc15(tg->ctx, mmD1VGA_CONTROL, offset); |
417 | |
418 | set_reg_field_value(value, 0, D1VGA_CONTROL, D1VGA_MODE_ENABLE); |
419 | set_reg_field_value(value, 0, D1VGA_CONTROL, D1VGA_TIMING_SELECT); |
420 | set_reg_field_value( |
421 | value, 0, D1VGA_CONTROL, D1VGA_SYNC_POLARITY_SELECT); |
422 | set_reg_field_value(value, 0, D1VGA_CONTROL, D1VGA_OVERSCAN_COLOR_EN); |
423 | |
424 | dm_write_reg_soc15(tg->ctx, mmD1VGA_CONTROL, offset, value); |
425 | } |
426 | /* TODO: Should we move it to transform */ |
427 | /* Fully program CRTC timing in timing generator */ |
428 | static void dce120_timing_generator_program_blanking( |
429 | struct timing_generator *tg, |
430 | const struct dc_crtc_timing *timing) |
431 | { |
432 | uint32_t tmp1 = 0; |
433 | uint32_t tmp2 = 0; |
434 | uint32_t vsync_offset = timing->v_border_bottom + |
435 | timing->v_front_porch; |
436 | uint32_t v_sync_start = timing->v_addressable + vsync_offset; |
437 | |
438 | uint32_t hsync_offset = timing->h_border_right + |
439 | timing->h_front_porch; |
440 | uint32_t h_sync_start = timing->h_addressable + hsync_offset; |
441 | struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); |
442 | |
443 | CRTC_REG_UPDATE( |
444 | CRTC0_CRTC_H_TOTAL, |
445 | CRTC_H_TOTAL, |
446 | timing->h_total - 1); |
447 | |
448 | CRTC_REG_UPDATE( |
449 | CRTC0_CRTC_V_TOTAL, |
450 | CRTC_V_TOTAL, |
451 | timing->v_total - 1); |
452 | |
453 | /* In case of V_TOTAL_CONTROL is on, make sure V_TOTAL_MAX and |
454 | * V_TOTAL_MIN are equal to V_TOTAL. |
455 | */ |
456 | CRTC_REG_UPDATE( |
457 | CRTC0_CRTC_V_TOTAL_MAX, |
458 | CRTC_V_TOTAL_MAX, |
459 | timing->v_total - 1); |
460 | |
461 | CRTC_REG_UPDATE( |
462 | CRTC0_CRTC_V_TOTAL_MIN, |
463 | CRTC_V_TOTAL_MIN, |
464 | timing->v_total - 1); |
465 | |
466 | tmp1 = timing->h_total - |
467 | (h_sync_start + timing->h_border_left); |
468 | tmp2 = tmp1 + timing->h_addressable + |
469 | timing->h_border_left + timing->h_border_right; |
470 | |
471 | CRTC_REG_UPDATE_2( |
472 | CRTC0_CRTC_H_BLANK_START_END, |
473 | CRTC_H_BLANK_END, tmp1, |
474 | CRTC_H_BLANK_START, tmp2); |
475 | |
476 | tmp1 = timing->v_total - (v_sync_start + timing->v_border_top); |
477 | tmp2 = tmp1 + timing->v_addressable + timing->v_border_top + |
478 | timing->v_border_bottom; |
479 | |
480 | CRTC_REG_UPDATE_2( |
481 | CRTC0_CRTC_V_BLANK_START_END, |
482 | CRTC_V_BLANK_END, tmp1, |
483 | CRTC_V_BLANK_START, tmp2); |
484 | } |
485 | |
486 | /* TODO: Should we move it to opp? */ |
487 | /* Combine with below and move YUV/RGB color conversion to SW layer */ |
488 | static void dce120_timing_generator_program_blank_color( |
489 | struct timing_generator *tg, |
490 | const struct tg_color *black_color) |
491 | { |
492 | struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); |
493 | |
494 | CRTC_REG_UPDATE_3( |
495 | CRTC0_CRTC_BLACK_COLOR, |
496 | CRTC_BLACK_COLOR_B_CB, black_color->color_b_cb, |
497 | CRTC_BLACK_COLOR_G_Y, black_color->color_g_y, |
498 | CRTC_BLACK_COLOR_R_CR, black_color->color_r_cr); |
499 | } |
500 | /* Combine with above and move YUV/RGB color conversion to SW layer */ |
501 | static void dce120_timing_generator_set_overscan_color_black( |
502 | struct timing_generator *tg, |
503 | const struct tg_color *color) |
504 | { |
505 | struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); |
506 | uint32_t value = 0; |
507 | CRTC_REG_SET_3( |
508 | CRTC0_CRTC_OVERSCAN_COLOR, |
509 | CRTC_OVERSCAN_COLOR_BLUE, color->color_b_cb, |
510 | CRTC_OVERSCAN_COLOR_GREEN, color->color_g_y, |
511 | CRTC_OVERSCAN_COLOR_RED, color->color_r_cr); |
512 | |
513 | value = dm_read_reg_soc15( |
514 | tg->ctx, |
515 | mmCRTC0_CRTC_OVERSCAN_COLOR, |
516 | tg110->offsets.crtc); |
517 | |
518 | dm_write_reg_soc15( |
519 | tg->ctx, |
520 | mmCRTC0_CRTC_BLACK_COLOR, |
521 | tg110->offsets.crtc, |
522 | value); |
523 | |
524 | /* This is desirable to have a constant DAC output voltage during the |
525 | * blank time that is higher than the 0 volt reference level that the |
526 | * DAC outputs when the NBLANK signal |
527 | * is asserted low, such as for output to an analog TV. */ |
528 | dm_write_reg_soc15( |
529 | tg->ctx, |
530 | mmCRTC0_CRTC_BLANK_DATA_COLOR, |
531 | tg110->offsets.crtc, |
532 | value); |
533 | |
534 | /* TO DO we have to program EXT registers and we need to know LB DATA |
535 | * format because it is used when more 10 , i.e. 12 bits per color |
536 | * |
537 | * m_mmDxCRTC_OVERSCAN_COLOR_EXT |
538 | * m_mmDxCRTC_BLACK_COLOR_EXT |
539 | * m_mmDxCRTC_BLANK_DATA_COLOR_EXT |
540 | */ |
541 | } |
542 | |
543 | static void dce120_timing_generator_set_drr( |
544 | struct timing_generator *tg, |
545 | const struct drr_params *params) |
546 | { |
547 | |
548 | struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); |
549 | |
550 | if (params != NULL && |
551 | params->vertical_total_max > 0 && |
552 | params->vertical_total_min > 0) { |
553 | |
554 | CRTC_REG_UPDATE( |
555 | CRTC0_CRTC_V_TOTAL_MIN, |
556 | CRTC_V_TOTAL_MIN, params->vertical_total_min - 1); |
557 | CRTC_REG_UPDATE( |
558 | CRTC0_CRTC_V_TOTAL_MAX, |
559 | CRTC_V_TOTAL_MAX, params->vertical_total_max - 1); |
560 | CRTC_REG_SET_N(CRTC0_CRTC_V_TOTAL_CONTROL, 6, |
561 | FD(CRTC0_CRTC_V_TOTAL_CONTROL__CRTC_V_TOTAL_MIN_SEL), 1, |
562 | FD(CRTC0_CRTC_V_TOTAL_CONTROL__CRTC_V_TOTAL_MAX_SEL), 1, |
563 | FD(CRTC0_CRTC_V_TOTAL_CONTROL__CRTC_FORCE_LOCK_ON_EVENT), 0, |
564 | FD(CRTC0_CRTC_V_TOTAL_CONTROL__CRTC_FORCE_LOCK_TO_MASTER_VSYNC), 0, |
565 | FD(CRTC0_CRTC_V_TOTAL_CONTROL__CRTC_SET_V_TOTAL_MIN_MASK_EN), 0, |
566 | FD(CRTC0_CRTC_V_TOTAL_CONTROL__CRTC_SET_V_TOTAL_MIN_MASK), 0); |
567 | CRTC_REG_UPDATE( |
568 | CRTC0_CRTC_STATIC_SCREEN_CONTROL, |
569 | CRTC_STATIC_SCREEN_EVENT_MASK, |
570 | 0x180); |
571 | |
572 | } else { |
573 | CRTC_REG_SET_N(CRTC0_CRTC_V_TOTAL_CONTROL, 5, |
574 | FD(CRTC0_CRTC_V_TOTAL_CONTROL__CRTC_V_TOTAL_MIN_SEL), 0, |
575 | FD(CRTC0_CRTC_V_TOTAL_CONTROL__CRTC_V_TOTAL_MAX_SEL), 0, |
576 | FD(CRTC0_CRTC_V_TOTAL_CONTROL__CRTC_FORCE_LOCK_ON_EVENT), 0, |
577 | FD(CRTC0_CRTC_V_TOTAL_CONTROL__CRTC_FORCE_LOCK_TO_MASTER_VSYNC), 0, |
578 | FD(CRTC0_CRTC_V_TOTAL_CONTROL__CRTC_SET_V_TOTAL_MIN_MASK), 0); |
579 | CRTC_REG_UPDATE( |
580 | CRTC0_CRTC_V_TOTAL_MIN, |
581 | CRTC_V_TOTAL_MIN, 0); |
582 | CRTC_REG_UPDATE( |
583 | CRTC0_CRTC_V_TOTAL_MAX, |
584 | CRTC_V_TOTAL_MAX, 0); |
585 | CRTC_REG_UPDATE( |
586 | CRTC0_CRTC_STATIC_SCREEN_CONTROL, |
587 | CRTC_STATIC_SCREEN_EVENT_MASK, |
588 | 0); |
589 | } |
590 | } |
591 | |
592 | static void dce120_timing_generator_get_crtc_scanoutpos( |
593 | struct timing_generator *tg, |
594 | uint32_t *v_blank_start, |
595 | uint32_t *v_blank_end, |
596 | uint32_t *h_position, |
597 | uint32_t *v_position) |
598 | { |
599 | struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); |
600 | struct crtc_position position; |
601 | |
602 | uint32_t v_blank_start_end = dm_read_reg_soc15( |
603 | tg->ctx, |
604 | mmCRTC0_CRTC_V_BLANK_START_END, |
605 | tg110->offsets.crtc); |
606 | |
607 | *v_blank_start = get_reg_field_value(v_blank_start_end, |
608 | CRTC0_CRTC_V_BLANK_START_END, |
609 | CRTC_V_BLANK_START); |
610 | *v_blank_end = get_reg_field_value(v_blank_start_end, |
611 | CRTC0_CRTC_V_BLANK_START_END, |
612 | CRTC_V_BLANK_END); |
613 | |
614 | dce120_timing_generator_get_crtc_position( |
615 | tg, position: &position); |
616 | |
617 | *h_position = position.horizontal_count; |
618 | *v_position = position.vertical_count; |
619 | } |
620 | |
621 | static void dce120_timing_generator_enable_advanced_request( |
622 | struct timing_generator *tg, |
623 | bool enable, |
624 | const struct dc_crtc_timing *timing) |
625 | { |
626 | struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); |
627 | uint32_t v_sync_width_and_b_porch = |
628 | timing->v_total - timing->v_addressable - |
629 | timing->v_border_bottom - timing->v_front_porch; |
630 | uint32_t value = dm_read_reg_soc15( |
631 | tg->ctx, |
632 | mmCRTC0_CRTC_START_LINE_CONTROL, |
633 | tg110->offsets.crtc); |
634 | |
635 | set_reg_field_value( |
636 | value, |
637 | enable ? 0 : 1, |
638 | CRTC0_CRTC_START_LINE_CONTROL, |
639 | CRTC_LEGACY_REQUESTOR_EN); |
640 | |
641 | /* Program advanced line position acc.to the best case from fetching data perspective to hide MC latency |
642 | * and prefilling Line Buffer in V Blank (to 10 lines as LB can store max 10 lines) |
643 | */ |
644 | if (v_sync_width_and_b_porch > 10) |
645 | v_sync_width_and_b_porch = 10; |
646 | |
647 | set_reg_field_value( |
648 | value, |
649 | v_sync_width_and_b_porch, |
650 | CRTC0_CRTC_START_LINE_CONTROL, |
651 | CRTC_ADVANCED_START_LINE_POSITION); |
652 | |
653 | dm_write_reg_soc15(tg->ctx, |
654 | mmCRTC0_CRTC_START_LINE_CONTROL, |
655 | tg110->offsets.crtc, |
656 | value); |
657 | } |
658 | |
659 | static void dce120_tg_program_blank_color(struct timing_generator *tg, |
660 | const struct tg_color *black_color) |
661 | { |
662 | struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); |
663 | uint32_t value = 0; |
664 | |
665 | CRTC_REG_UPDATE_3( |
666 | CRTC0_CRTC_BLACK_COLOR, |
667 | CRTC_BLACK_COLOR_B_CB, black_color->color_b_cb, |
668 | CRTC_BLACK_COLOR_G_Y, black_color->color_g_y, |
669 | CRTC_BLACK_COLOR_R_CR, black_color->color_r_cr); |
670 | |
671 | value = dm_read_reg_soc15( |
672 | tg->ctx, |
673 | mmCRTC0_CRTC_BLACK_COLOR, |
674 | tg110->offsets.crtc); |
675 | dm_write_reg_soc15( |
676 | tg->ctx, |
677 | mmCRTC0_CRTC_BLANK_DATA_COLOR, |
678 | tg110->offsets.crtc, |
679 | value); |
680 | } |
681 | |
682 | static void dce120_tg_set_overscan_color(struct timing_generator *tg, |
683 | const struct tg_color *overscan_color) |
684 | { |
685 | struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); |
686 | |
687 | CRTC_REG_SET_3( |
688 | CRTC0_CRTC_OVERSCAN_COLOR, |
689 | CRTC_OVERSCAN_COLOR_BLUE, overscan_color->color_b_cb, |
690 | CRTC_OVERSCAN_COLOR_GREEN, overscan_color->color_g_y, |
691 | CRTC_OVERSCAN_COLOR_RED, overscan_color->color_r_cr); |
692 | } |
693 | |
694 | static void dce120_tg_program_timing(struct timing_generator *tg, |
695 | const struct dc_crtc_timing *timing, |
696 | int vready_offset, |
697 | int vstartup_start, |
698 | int vupdate_offset, |
699 | int vupdate_width, |
700 | const enum signal_type signal, |
701 | bool use_vbios) |
702 | { |
703 | if (use_vbios) |
704 | dce110_timing_generator_program_timing_generator(tg, dc_crtc_timing: timing); |
705 | else |
706 | dce120_timing_generator_program_blanking(tg, timing); |
707 | } |
708 | |
709 | static bool dce120_tg_is_blanked(struct timing_generator *tg) |
710 | { |
711 | struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); |
712 | uint32_t value = dm_read_reg_soc15( |
713 | tg->ctx, |
714 | mmCRTC0_CRTC_BLANK_CONTROL, |
715 | tg110->offsets.crtc); |
716 | |
717 | if (get_reg_field_value( |
718 | value, |
719 | CRTC0_CRTC_BLANK_CONTROL, |
720 | CRTC_BLANK_DATA_EN) == 1 && |
721 | get_reg_field_value( |
722 | value, |
723 | CRTC0_CRTC_BLANK_CONTROL, |
724 | CRTC_CURRENT_BLANK_STATE) == 1) |
725 | return true; |
726 | |
727 | return false; |
728 | } |
729 | |
730 | static void dce120_tg_set_blank(struct timing_generator *tg, |
731 | bool enable_blanking) |
732 | { |
733 | struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); |
734 | |
735 | CRTC_REG_SET( |
736 | CRTC0_CRTC_DOUBLE_BUFFER_CONTROL, |
737 | CRTC_BLANK_DATA_DOUBLE_BUFFER_EN, 1); |
738 | |
739 | if (enable_blanking) |
740 | CRTC_REG_SET(CRTC0_CRTC_BLANK_CONTROL, CRTC_BLANK_DATA_EN, 1); |
741 | else |
742 | dm_write_reg_soc15(tg->ctx, mmCRTC0_CRTC_BLANK_CONTROL, |
743 | tg110->offsets.crtc, 0); |
744 | } |
745 | |
746 | bool dce120_tg_validate_timing(struct timing_generator *tg, |
747 | const struct dc_crtc_timing *timing); |
748 | |
749 | static void dce120_tg_wait_for_state(struct timing_generator *tg, |
750 | enum crtc_state state) |
751 | { |
752 | switch (state) { |
753 | case CRTC_STATE_VBLANK: |
754 | dce120_timing_generator_wait_for_vblank(tg); |
755 | break; |
756 | |
757 | case CRTC_STATE_VACTIVE: |
758 | dce120_timing_generator_wait_for_vactive(tg); |
759 | break; |
760 | |
761 | default: |
762 | break; |
763 | } |
764 | } |
765 | |
766 | static void dce120_tg_set_colors(struct timing_generator *tg, |
767 | const struct tg_color *blank_color, |
768 | const struct tg_color *overscan_color) |
769 | { |
770 | if (blank_color != NULL) |
771 | dce120_tg_program_blank_color(tg, black_color: blank_color); |
772 | |
773 | if (overscan_color != NULL) |
774 | dce120_tg_set_overscan_color(tg, overscan_color); |
775 | } |
776 | |
777 | static void dce120_timing_generator_set_static_screen_control( |
778 | struct timing_generator *tg, |
779 | uint32_t event_triggers, |
780 | uint32_t num_frames) |
781 | { |
782 | struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); |
783 | |
784 | // By register spec, it only takes 8 bit value |
785 | if (num_frames > 0xFF) |
786 | num_frames = 0xFF; |
787 | |
788 | CRTC_REG_UPDATE_2(CRTC0_CRTC_STATIC_SCREEN_CONTROL, |
789 | CRTC_STATIC_SCREEN_EVENT_MASK, event_triggers, |
790 | CRTC_STATIC_SCREEN_FRAME_COUNT, num_frames); |
791 | } |
792 | |
793 | static void dce120_timing_generator_set_test_pattern( |
794 | struct timing_generator *tg, |
795 | /* TODO: replace 'controller_dp_test_pattern' by 'test_pattern_mode' |
796 | * because this is not DP-specific (which is probably somewhere in DP |
797 | * encoder) */ |
798 | enum controller_dp_test_pattern test_pattern, |
799 | enum dc_color_depth color_depth) |
800 | { |
801 | struct dc_context *ctx = tg->ctx; |
802 | uint32_t value; |
803 | struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); |
804 | enum test_pattern_color_format bit_depth; |
805 | enum test_pattern_dyn_range dyn_range; |
806 | enum test_pattern_mode mode; |
807 | /* color ramp generator mixes 16-bits color */ |
808 | uint32_t src_bpc = 16; |
809 | /* requested bpc */ |
810 | uint32_t dst_bpc; |
811 | uint32_t index; |
812 | /* RGB values of the color bars. |
813 | * Produce two RGB colors: RGB0 - white (all Fs) |
814 | * and RGB1 - black (all 0s) |
815 | * (three RGB components for two colors) |
816 | */ |
817 | uint16_t src_color[6] = {0xFFFF, 0xFFFF, 0xFFFF, 0x0000, |
818 | 0x0000, 0x0000}; |
819 | /* dest color (converted to the specified color format) */ |
820 | uint16_t dst_color[6]; |
821 | uint32_t inc_base; |
822 | |
823 | /* translate to bit depth */ |
824 | switch (color_depth) { |
825 | case COLOR_DEPTH_666: |
826 | bit_depth = TEST_PATTERN_COLOR_FORMAT_BPC_6; |
827 | break; |
828 | case COLOR_DEPTH_888: |
829 | bit_depth = TEST_PATTERN_COLOR_FORMAT_BPC_8; |
830 | break; |
831 | case COLOR_DEPTH_101010: |
832 | bit_depth = TEST_PATTERN_COLOR_FORMAT_BPC_10; |
833 | break; |
834 | case COLOR_DEPTH_121212: |
835 | bit_depth = TEST_PATTERN_COLOR_FORMAT_BPC_12; |
836 | break; |
837 | default: |
838 | bit_depth = TEST_PATTERN_COLOR_FORMAT_BPC_8; |
839 | break; |
840 | } |
841 | |
842 | switch (test_pattern) { |
843 | case CONTROLLER_DP_TEST_PATTERN_COLORSQUARES: |
844 | case CONTROLLER_DP_TEST_PATTERN_COLORSQUARES_CEA: |
845 | { |
846 | dyn_range = (test_pattern == |
847 | CONTROLLER_DP_TEST_PATTERN_COLORSQUARES_CEA ? |
848 | TEST_PATTERN_DYN_RANGE_CEA : |
849 | TEST_PATTERN_DYN_RANGE_VESA); |
850 | mode = TEST_PATTERN_MODE_COLORSQUARES_RGB; |
851 | |
852 | CRTC_REG_UPDATE_2(CRTC0_CRTC_TEST_PATTERN_PARAMETERS, |
853 | CRTC_TEST_PATTERN_VRES, 6, |
854 | CRTC_TEST_PATTERN_HRES, 6); |
855 | |
856 | CRTC_REG_UPDATE_4(CRTC0_CRTC_TEST_PATTERN_CONTROL, |
857 | CRTC_TEST_PATTERN_EN, 1, |
858 | CRTC_TEST_PATTERN_MODE, mode, |
859 | CRTC_TEST_PATTERN_DYNAMIC_RANGE, dyn_range, |
860 | CRTC_TEST_PATTERN_COLOR_FORMAT, bit_depth); |
861 | } |
862 | break; |
863 | |
864 | case CONTROLLER_DP_TEST_PATTERN_VERTICALBARS: |
865 | case CONTROLLER_DP_TEST_PATTERN_HORIZONTALBARS: |
866 | { |
867 | mode = (test_pattern == |
868 | CONTROLLER_DP_TEST_PATTERN_VERTICALBARS ? |
869 | TEST_PATTERN_MODE_VERTICALBARS : |
870 | TEST_PATTERN_MODE_HORIZONTALBARS); |
871 | |
872 | switch (bit_depth) { |
873 | case TEST_PATTERN_COLOR_FORMAT_BPC_6: |
874 | dst_bpc = 6; |
875 | break; |
876 | case TEST_PATTERN_COLOR_FORMAT_BPC_8: |
877 | dst_bpc = 8; |
878 | break; |
879 | case TEST_PATTERN_COLOR_FORMAT_BPC_10: |
880 | dst_bpc = 10; |
881 | break; |
882 | default: |
883 | dst_bpc = 8; |
884 | break; |
885 | } |
886 | |
887 | /* adjust color to the required colorFormat */ |
888 | for (index = 0; index < 6; index++) { |
889 | /* dst = 2^dstBpc * src / 2^srcBpc = src >> |
890 | * (srcBpc - dstBpc); |
891 | */ |
892 | dst_color[index] = |
893 | src_color[index] >> (src_bpc - dst_bpc); |
894 | /* CRTC_TEST_PATTERN_DATA has 16 bits, |
895 | * lowest 6 are hardwired to ZERO |
896 | * color bits should be left aligned aligned to MSB |
897 | * XXXXXXXXXX000000 for 10 bit, |
898 | * XXXXXXXX00000000 for 8 bit and XXXXXX0000000000 for 6 |
899 | */ |
900 | dst_color[index] <<= (16 - dst_bpc); |
901 | } |
902 | |
903 | dm_write_reg_soc15(ctx, mmCRTC0_CRTC_TEST_PATTERN_PARAMETERS, tg110->offsets.crtc, 0); |
904 | |
905 | /* We have to write the mask before data, similar to pipeline. |
906 | * For example, for 8 bpc, if we want RGB0 to be magenta, |
907 | * and RGB1 to be cyan, |
908 | * we need to make 7 writes: |
909 | * MASK DATA |
910 | * 000001 00000000 00000000 set mask to R0 |
911 | * 000010 11111111 00000000 R0 255, 0xFF00, set mask to G0 |
912 | * 000100 00000000 00000000 G0 0, 0x0000, set mask to B0 |
913 | * 001000 11111111 00000000 B0 255, 0xFF00, set mask to R1 |
914 | * 010000 00000000 00000000 R1 0, 0x0000, set mask to G1 |
915 | * 100000 11111111 00000000 G1 255, 0xFF00, set mask to B1 |
916 | * 100000 11111111 00000000 B1 255, 0xFF00 |
917 | * |
918 | * we will make a loop of 6 in which we prepare the mask, |
919 | * then write, then prepare the color for next write. |
920 | * first iteration will write mask only, |
921 | * but each next iteration color prepared in |
922 | * previous iteration will be written within new mask, |
923 | * the last component will written separately, |
924 | * mask is not changing between 6th and 7th write |
925 | * and color will be prepared by last iteration |
926 | */ |
927 | |
928 | /* write color, color values mask in CRTC_TEST_PATTERN_MASK |
929 | * is B1, G1, R1, B0, G0, R0 |
930 | */ |
931 | value = 0; |
932 | for (index = 0; index < 6; index++) { |
933 | /* prepare color mask, first write PATTERN_DATA |
934 | * will have all zeros |
935 | */ |
936 | set_reg_field_value( |
937 | value, |
938 | (1 << index), |
939 | CRTC0_CRTC_TEST_PATTERN_COLOR, |
940 | CRTC_TEST_PATTERN_MASK); |
941 | /* write color component */ |
942 | dm_write_reg_soc15(ctx, mmCRTC0_CRTC_TEST_PATTERN_COLOR, tg110->offsets.crtc, value); |
943 | /* prepare next color component, |
944 | * will be written in the next iteration |
945 | */ |
946 | set_reg_field_value( |
947 | value, |
948 | dst_color[index], |
949 | CRTC0_CRTC_TEST_PATTERN_COLOR, |
950 | CRTC_TEST_PATTERN_DATA); |
951 | } |
952 | /* write last color component, |
953 | * it's been already prepared in the loop |
954 | */ |
955 | dm_write_reg_soc15(ctx, mmCRTC0_CRTC_TEST_PATTERN_COLOR, tg110->offsets.crtc, value); |
956 | |
957 | /* enable test pattern */ |
958 | CRTC_REG_UPDATE_4(CRTC0_CRTC_TEST_PATTERN_CONTROL, |
959 | CRTC_TEST_PATTERN_EN, 1, |
960 | CRTC_TEST_PATTERN_MODE, mode, |
961 | CRTC_TEST_PATTERN_DYNAMIC_RANGE, 0, |
962 | CRTC_TEST_PATTERN_COLOR_FORMAT, bit_depth); |
963 | } |
964 | break; |
965 | |
966 | case CONTROLLER_DP_TEST_PATTERN_COLORRAMP: |
967 | { |
968 | mode = (bit_depth == |
969 | TEST_PATTERN_COLOR_FORMAT_BPC_10 ? |
970 | TEST_PATTERN_MODE_DUALRAMP_RGB : |
971 | TEST_PATTERN_MODE_SINGLERAMP_RGB); |
972 | |
973 | switch (bit_depth) { |
974 | case TEST_PATTERN_COLOR_FORMAT_BPC_6: |
975 | dst_bpc = 6; |
976 | break; |
977 | case TEST_PATTERN_COLOR_FORMAT_BPC_8: |
978 | dst_bpc = 8; |
979 | break; |
980 | case TEST_PATTERN_COLOR_FORMAT_BPC_10: |
981 | dst_bpc = 10; |
982 | break; |
983 | default: |
984 | dst_bpc = 8; |
985 | break; |
986 | } |
987 | |
988 | /* increment for the first ramp for one color gradation |
989 | * 1 gradation for 6-bit color is 2^10 |
990 | * gradations in 16-bit color |
991 | */ |
992 | inc_base = (src_bpc - dst_bpc); |
993 | |
994 | switch (bit_depth) { |
995 | case TEST_PATTERN_COLOR_FORMAT_BPC_6: |
996 | { |
997 | CRTC_REG_UPDATE_5(CRTC0_CRTC_TEST_PATTERN_PARAMETERS, |
998 | CRTC_TEST_PATTERN_INC0, inc_base, |
999 | CRTC_TEST_PATTERN_INC1, 0, |
1000 | CRTC_TEST_PATTERN_HRES, 6, |
1001 | CRTC_TEST_PATTERN_VRES, 6, |
1002 | CRTC_TEST_PATTERN_RAMP0_OFFSET, 0); |
1003 | } |
1004 | break; |
1005 | case TEST_PATTERN_COLOR_FORMAT_BPC_8: |
1006 | { |
1007 | CRTC_REG_UPDATE_5(CRTC0_CRTC_TEST_PATTERN_PARAMETERS, |
1008 | CRTC_TEST_PATTERN_INC0, inc_base, |
1009 | CRTC_TEST_PATTERN_INC1, 0, |
1010 | CRTC_TEST_PATTERN_HRES, 8, |
1011 | CRTC_TEST_PATTERN_VRES, 6, |
1012 | CRTC_TEST_PATTERN_RAMP0_OFFSET, 0); |
1013 | } |
1014 | break; |
1015 | case TEST_PATTERN_COLOR_FORMAT_BPC_10: |
1016 | { |
1017 | CRTC_REG_UPDATE_5(CRTC0_CRTC_TEST_PATTERN_PARAMETERS, |
1018 | CRTC_TEST_PATTERN_INC0, inc_base, |
1019 | CRTC_TEST_PATTERN_INC1, inc_base + 2, |
1020 | CRTC_TEST_PATTERN_HRES, 8, |
1021 | CRTC_TEST_PATTERN_VRES, 5, |
1022 | CRTC_TEST_PATTERN_RAMP0_OFFSET, 384 << 6); |
1023 | } |
1024 | break; |
1025 | default: |
1026 | break; |
1027 | } |
1028 | |
1029 | dm_write_reg_soc15(ctx, mmCRTC0_CRTC_TEST_PATTERN_COLOR, tg110->offsets.crtc, 0); |
1030 | |
1031 | /* enable test pattern */ |
1032 | dm_write_reg_soc15(ctx, mmCRTC0_CRTC_TEST_PATTERN_CONTROL, tg110->offsets.crtc, 0); |
1033 | |
1034 | CRTC_REG_UPDATE_4(CRTC0_CRTC_TEST_PATTERN_CONTROL, |
1035 | CRTC_TEST_PATTERN_EN, 1, |
1036 | CRTC_TEST_PATTERN_MODE, mode, |
1037 | CRTC_TEST_PATTERN_DYNAMIC_RANGE, 0, |
1038 | CRTC_TEST_PATTERN_COLOR_FORMAT, bit_depth); |
1039 | } |
1040 | break; |
1041 | case CONTROLLER_DP_TEST_PATTERN_VIDEOMODE: |
1042 | { |
1043 | value = 0; |
1044 | dm_write_reg_soc15(ctx, mmCRTC0_CRTC_TEST_PATTERN_CONTROL, tg110->offsets.crtc, value); |
1045 | dm_write_reg_soc15(ctx, mmCRTC0_CRTC_TEST_PATTERN_COLOR, tg110->offsets.crtc, value); |
1046 | dm_write_reg_soc15(ctx, mmCRTC0_CRTC_TEST_PATTERN_PARAMETERS, tg110->offsets.crtc, value); |
1047 | } |
1048 | break; |
1049 | default: |
1050 | break; |
1051 | } |
1052 | } |
1053 | |
1054 | static bool dce120_arm_vert_intr( |
1055 | struct timing_generator *tg, |
1056 | uint8_t width) |
1057 | { |
1058 | struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); |
1059 | uint32_t v_blank_start, v_blank_end, h_position, v_position; |
1060 | |
1061 | tg->funcs->get_scanoutpos( |
1062 | tg, |
1063 | &v_blank_start, |
1064 | &v_blank_end, |
1065 | &h_position, |
1066 | &v_position); |
1067 | |
1068 | if (v_blank_start == 0 || v_blank_end == 0) |
1069 | return false; |
1070 | |
1071 | CRTC_REG_SET_2( |
1072 | CRTC0_CRTC_VERTICAL_INTERRUPT0_POSITION, |
1073 | CRTC_VERTICAL_INTERRUPT0_LINE_START, v_blank_start, |
1074 | CRTC_VERTICAL_INTERRUPT0_LINE_END, v_blank_start + width); |
1075 | |
1076 | return true; |
1077 | } |
1078 | |
1079 | |
1080 | static bool dce120_is_tg_enabled(struct timing_generator *tg) |
1081 | { |
1082 | struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); |
1083 | uint32_t value, field; |
1084 | |
1085 | value = dm_read_reg_soc15(tg->ctx, mmCRTC0_CRTC_CONTROL, |
1086 | tg110->offsets.crtc); |
1087 | field = get_reg_field_value(value, CRTC0_CRTC_CONTROL, |
1088 | CRTC_CURRENT_MASTER_EN_STATE); |
1089 | |
1090 | return field == 1; |
1091 | } |
1092 | |
1093 | static bool dce120_configure_crc(struct timing_generator *tg, |
1094 | const struct crc_params *params) |
1095 | { |
1096 | struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); |
1097 | |
1098 | /* Cannot configure crc on a CRTC that is disabled */ |
1099 | if (!dce120_is_tg_enabled(tg)) |
1100 | return false; |
1101 | |
1102 | /* First, disable CRC before we configure it. */ |
1103 | dm_write_reg_soc15(tg->ctx, mmCRTC0_CRTC_CRC_CNTL, |
1104 | tg110->offsets.crtc, 0); |
1105 | |
1106 | if (!params->enable) |
1107 | return true; |
1108 | |
1109 | /* Program frame boundaries */ |
1110 | /* Window A x axis start and end. */ |
1111 | CRTC_REG_UPDATE_2(CRTC0_CRTC_CRC0_WINDOWA_X_CONTROL, |
1112 | CRTC_CRC0_WINDOWA_X_START, params->windowa_x_start, |
1113 | CRTC_CRC0_WINDOWA_X_END, params->windowa_x_end); |
1114 | |
1115 | /* Window A y axis start and end. */ |
1116 | CRTC_REG_UPDATE_2(CRTC0_CRTC_CRC0_WINDOWA_Y_CONTROL, |
1117 | CRTC_CRC0_WINDOWA_Y_START, params->windowa_y_start, |
1118 | CRTC_CRC0_WINDOWA_Y_END, params->windowa_y_end); |
1119 | |
1120 | /* Window B x axis start and end. */ |
1121 | CRTC_REG_UPDATE_2(CRTC0_CRTC_CRC0_WINDOWB_X_CONTROL, |
1122 | CRTC_CRC0_WINDOWB_X_START, params->windowb_x_start, |
1123 | CRTC_CRC0_WINDOWB_X_END, params->windowb_x_end); |
1124 | |
1125 | /* Window B y axis start and end. */ |
1126 | CRTC_REG_UPDATE_2(CRTC0_CRTC_CRC0_WINDOWB_Y_CONTROL, |
1127 | CRTC_CRC0_WINDOWB_Y_START, params->windowb_y_start, |
1128 | CRTC_CRC0_WINDOWB_Y_END, params->windowb_y_end); |
1129 | |
1130 | /* Set crc mode and selection, and enable. Only using CRC0*/ |
1131 | CRTC_REG_UPDATE_3(CRTC0_CRTC_CRC_CNTL, |
1132 | CRTC_CRC_EN, params->continuous_mode ? 1 : 0, |
1133 | CRTC_CRC0_SELECT, params->selection, |
1134 | CRTC_CRC_EN, 1); |
1135 | |
1136 | return true; |
1137 | } |
1138 | |
1139 | static bool dce120_get_crc(struct timing_generator *tg, uint32_t *r_cr, |
1140 | uint32_t *g_y, uint32_t *b_cb) |
1141 | { |
1142 | struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); |
1143 | uint32_t value, field; |
1144 | |
1145 | value = dm_read_reg_soc15(tg->ctx, mmCRTC0_CRTC_CRC_CNTL, |
1146 | tg110->offsets.crtc); |
1147 | field = get_reg_field_value(value, CRTC0_CRTC_CRC_CNTL, CRTC_CRC_EN); |
1148 | |
1149 | /* Early return if CRC is not enabled for this CRTC */ |
1150 | if (!field) |
1151 | return false; |
1152 | |
1153 | value = dm_read_reg_soc15(tg->ctx, mmCRTC0_CRTC_CRC0_DATA_RG, |
1154 | tg110->offsets.crtc); |
1155 | *r_cr = get_reg_field_value(value, CRTC0_CRTC_CRC0_DATA_RG, CRC0_R_CR); |
1156 | *g_y = get_reg_field_value(value, CRTC0_CRTC_CRC0_DATA_RG, CRC0_G_Y); |
1157 | |
1158 | value = dm_read_reg_soc15(tg->ctx, mmCRTC0_CRTC_CRC0_DATA_B, |
1159 | tg110->offsets.crtc); |
1160 | *b_cb = get_reg_field_value(value, CRTC0_CRTC_CRC0_DATA_B, CRC0_B_CB); |
1161 | |
1162 | return true; |
1163 | } |
1164 | |
1165 | static const struct timing_generator_funcs dce120_tg_funcs = { |
1166 | .validate_timing = dce120_tg_validate_timing, |
1167 | .program_timing = dce120_tg_program_timing, |
1168 | .enable_crtc = dce120_timing_generator_enable_crtc, |
1169 | .disable_crtc = dce110_timing_generator_disable_crtc, |
1170 | /* used by enable_timing_synchronization. Not need for FPGA */ |
1171 | .is_counter_moving = dce110_timing_generator_is_counter_moving, |
1172 | /* never be called */ |
1173 | .get_position = dce120_timing_generator_get_crtc_position, |
1174 | .get_frame_count = dce120_timing_generator_get_vblank_counter, |
1175 | .get_scanoutpos = dce120_timing_generator_get_crtc_scanoutpos, |
1176 | .set_early_control = dce120_timing_generator_set_early_control, |
1177 | /* used by enable_timing_synchronization. Not need for FPGA */ |
1178 | .wait_for_state = dce120_tg_wait_for_state, |
1179 | .set_blank = dce120_tg_set_blank, |
1180 | .is_blanked = dce120_tg_is_blanked, |
1181 | /* never be called */ |
1182 | .set_colors = dce120_tg_set_colors, |
1183 | .set_overscan_blank_color = dce120_timing_generator_set_overscan_color_black, |
1184 | .set_blank_color = dce120_timing_generator_program_blank_color, |
1185 | .disable_vga = dce120_timing_generator_disable_vga, |
1186 | .did_triggered_reset_occur = dce120_timing_generator_did_triggered_reset_occur, |
1187 | .setup_global_swap_lock = dce120_timing_generator_setup_global_swap_lock, |
1188 | .enable_reset_trigger = dce120_timing_generator_enable_reset_trigger, |
1189 | .disable_reset_trigger = dce120_timing_generator_disable_reset_trigger, |
1190 | .tear_down_global_swap_lock = dce120_timing_generator_tear_down_global_swap_lock, |
1191 | .enable_advanced_request = dce120_timing_generator_enable_advanced_request, |
1192 | .set_drr = dce120_timing_generator_set_drr, |
1193 | .get_last_used_drr_vtotal = NULL, |
1194 | .set_static_screen_control = dce120_timing_generator_set_static_screen_control, |
1195 | .set_test_pattern = dce120_timing_generator_set_test_pattern, |
1196 | .arm_vert_intr = dce120_arm_vert_intr, |
1197 | .is_tg_enabled = dce120_is_tg_enabled, |
1198 | .configure_crc = dce120_configure_crc, |
1199 | .get_crc = dce120_get_crc, |
1200 | }; |
1201 | |
1202 | |
1203 | void dce120_timing_generator_construct( |
1204 | struct dce110_timing_generator *tg110, |
1205 | struct dc_context *ctx, |
1206 | uint32_t instance, |
1207 | const struct dce110_timing_generator_offsets *offsets) |
1208 | { |
1209 | tg110->controller_id = CONTROLLER_ID_D0 + instance; |
1210 | tg110->base.inst = instance; |
1211 | |
1212 | tg110->offsets = *offsets; |
1213 | |
1214 | tg110->base.funcs = &dce120_tg_funcs; |
1215 | |
1216 | tg110->base.ctx = ctx; |
1217 | tg110->base.bp = ctx->dc_bios; |
1218 | |
1219 | tg110->max_h_total = CRTC0_CRTC_H_TOTAL__CRTC_H_TOTAL_MASK + 1; |
1220 | tg110->max_v_total = CRTC0_CRTC_V_TOTAL__CRTC_V_TOTAL_MASK + 1; |
1221 | |
1222 | /*//CRTC requires a minimum HBLANK = 32 pixels and o |
1223 | * Minimum HSYNC = 8 pixels*/ |
1224 | tg110->min_h_blank = 32; |
1225 | /*DCE12_CRTC_Block_ARch.doc*/ |
1226 | tg110->min_h_front_porch = 0; |
1227 | tg110->min_h_back_porch = 0; |
1228 | |
1229 | tg110->min_h_sync_width = 4; |
1230 | tg110->min_v_sync_width = 1; |
1231 | tg110->min_v_blank = 3; |
1232 | } |
1233 | |