1 | /* |
2 | * Copyright 2020 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 "reg_helper.h" |
27 | #include "dcn30_optc.h" |
28 | #include "dc.h" |
29 | #include "dcn_calc_math.h" |
30 | #include "dc_dmub_srv.h" |
31 | |
32 | #include "dml/dcn30/dcn30_fpu.h" |
33 | #include "dc_trace.h" |
34 | |
35 | #define REG(reg)\ |
36 | optc1->tg_regs->reg |
37 | |
38 | #define CTX \ |
39 | optc1->base.ctx |
40 | |
41 | #undef FN |
42 | #define FN(reg_name, field_name) \ |
43 | optc1->tg_shift->field_name, optc1->tg_mask->field_name |
44 | |
45 | void optc3_triplebuffer_lock(struct timing_generator *optc) |
46 | { |
47 | struct optc *optc1 = DCN10TG_FROM_TG(optc); |
48 | |
49 | REG_UPDATE(OTG_GLOBAL_CONTROL2, |
50 | OTG_MASTER_UPDATE_LOCK_SEL, optc->inst); |
51 | |
52 | REG_SET(OTG_VUPDATE_KEEPOUT, 0, |
53 | OTG_MASTER_UPDATE_LOCK_VUPDATE_KEEPOUT_EN, 1); |
54 | |
55 | REG_SET(OTG_MASTER_UPDATE_LOCK, 0, |
56 | OTG_MASTER_UPDATE_LOCK, 1); |
57 | |
58 | REG_WAIT(OTG_MASTER_UPDATE_LOCK, |
59 | UPDATE_LOCK_STATUS, 1, |
60 | 1, 10); |
61 | |
62 | TRACE_OPTC_LOCK_UNLOCK_STATE(optc1, optc->inst, true); |
63 | } |
64 | |
65 | void optc3_lock_doublebuffer_enable(struct timing_generator *optc) |
66 | { |
67 | struct optc *optc1 = DCN10TG_FROM_TG(optc); |
68 | uint32_t v_blank_start = 0; |
69 | uint32_t v_blank_end = 0; |
70 | uint32_t h_blank_start = 0; |
71 | uint32_t h_blank_end = 0; |
72 | |
73 | REG_GET_2(OTG_V_BLANK_START_END, |
74 | OTG_V_BLANK_START, &v_blank_start, |
75 | OTG_V_BLANK_END, &v_blank_end); |
76 | REG_GET_2(OTG_H_BLANK_START_END, |
77 | OTG_H_BLANK_START, &h_blank_start, |
78 | OTG_H_BLANK_END, &h_blank_end); |
79 | |
80 | REG_UPDATE_2(OTG_GLOBAL_CONTROL1, |
81 | MASTER_UPDATE_LOCK_DB_START_Y, v_blank_start - 1, |
82 | MASTER_UPDATE_LOCK_DB_END_Y, v_blank_start); |
83 | REG_UPDATE_2(OTG_GLOBAL_CONTROL4, |
84 | DIG_UPDATE_POSITION_X, h_blank_start - 180 - 1, |
85 | DIG_UPDATE_POSITION_Y, v_blank_start - 1); |
86 | // there is a DIG_UPDATE_VCOUNT_MODE and it is 0. |
87 | |
88 | REG_UPDATE_3(OTG_GLOBAL_CONTROL0, |
89 | MASTER_UPDATE_LOCK_DB_START_X, h_blank_start - 200 - 1, |
90 | MASTER_UPDATE_LOCK_DB_END_X, h_blank_start - 180, |
91 | MASTER_UPDATE_LOCK_DB_EN, 1); |
92 | REG_UPDATE(OTG_GLOBAL_CONTROL2, GLOBAL_UPDATE_LOCK_EN, 1); |
93 | |
94 | REG_SET_3(OTG_VUPDATE_KEEPOUT, 0, |
95 | MASTER_UPDATE_LOCK_VUPDATE_KEEPOUT_START_OFFSET, 0, |
96 | MASTER_UPDATE_LOCK_VUPDATE_KEEPOUT_END_OFFSET, 100, |
97 | OTG_MASTER_UPDATE_LOCK_VUPDATE_KEEPOUT_EN, 1); |
98 | |
99 | TRACE_OPTC_LOCK_UNLOCK_STATE(optc1, optc->inst, true); |
100 | } |
101 | |
102 | void optc3_lock_doublebuffer_disable(struct timing_generator *optc) |
103 | { |
104 | struct optc *optc1 = DCN10TG_FROM_TG(optc); |
105 | |
106 | REG_UPDATE_2(OTG_GLOBAL_CONTROL0, |
107 | MASTER_UPDATE_LOCK_DB_START_X, 0, |
108 | MASTER_UPDATE_LOCK_DB_END_X, 0); |
109 | REG_UPDATE_2(OTG_GLOBAL_CONTROL1, |
110 | MASTER_UPDATE_LOCK_DB_START_Y, 0, |
111 | MASTER_UPDATE_LOCK_DB_END_Y, 0); |
112 | |
113 | REG_UPDATE(OTG_GLOBAL_CONTROL2, GLOBAL_UPDATE_LOCK_EN, 0); |
114 | REG_UPDATE(OTG_GLOBAL_CONTROL0, MASTER_UPDATE_LOCK_DB_EN, 0); |
115 | |
116 | TRACE_OPTC_LOCK_UNLOCK_STATE(optc1, optc->inst, true); |
117 | } |
118 | |
119 | void optc3_lock(struct timing_generator *optc) |
120 | { |
121 | struct optc *optc1 = DCN10TG_FROM_TG(optc); |
122 | |
123 | REG_UPDATE(OTG_GLOBAL_CONTROL2, |
124 | OTG_MASTER_UPDATE_LOCK_SEL, optc->inst); |
125 | REG_SET(OTG_MASTER_UPDATE_LOCK, 0, |
126 | OTG_MASTER_UPDATE_LOCK, 1); |
127 | |
128 | REG_WAIT(OTG_MASTER_UPDATE_LOCK, |
129 | UPDATE_LOCK_STATUS, 1, |
130 | 1, 10); |
131 | |
132 | TRACE_OPTC_LOCK_UNLOCK_STATE(optc1, optc->inst, true); |
133 | } |
134 | |
135 | void optc3_set_out_mux(struct timing_generator *optc, enum otg_out_mux_dest dest) |
136 | { |
137 | struct optc *optc1 = DCN10TG_FROM_TG(optc); |
138 | |
139 | REG_UPDATE(OTG_CONTROL, OTG_OUT_MUX, dest); |
140 | } |
141 | |
142 | void optc3_program_blank_color(struct timing_generator *optc, |
143 | const struct tg_color *blank_color) |
144 | { |
145 | struct optc *optc1 = DCN10TG_FROM_TG(optc); |
146 | |
147 | REG_SET_3(OTG_BLANK_DATA_COLOR, 0, |
148 | OTG_BLANK_DATA_COLOR_BLUE_CB, blank_color->color_b_cb, |
149 | OTG_BLANK_DATA_COLOR_GREEN_Y, blank_color->color_g_y, |
150 | OTG_BLANK_DATA_COLOR_RED_CR, blank_color->color_r_cr); |
151 | |
152 | REG_SET_3(OTG_BLANK_DATA_COLOR_EXT, 0, |
153 | OTG_BLANK_DATA_COLOR_BLUE_CB_EXT, blank_color->color_b_cb >> 10, |
154 | OTG_BLANK_DATA_COLOR_GREEN_Y_EXT, blank_color->color_g_y >> 10, |
155 | OTG_BLANK_DATA_COLOR_RED_CR_EXT, blank_color->color_r_cr >> 10); |
156 | } |
157 | |
158 | void optc3_set_drr_trigger_window(struct timing_generator *optc, |
159 | uint32_t window_start, uint32_t window_end) |
160 | { |
161 | struct optc *optc1 = DCN10TG_FROM_TG(optc); |
162 | |
163 | REG_SET_2(OTG_DRR_TRIGGER_WINDOW, 0, |
164 | OTG_DRR_TRIGGER_WINDOW_START_X, window_start, |
165 | OTG_DRR_TRIGGER_WINDOW_END_X, window_end); |
166 | } |
167 | |
168 | void optc3_set_vtotal_change_limit(struct timing_generator *optc, |
169 | uint32_t limit) |
170 | { |
171 | struct optc *optc1 = DCN10TG_FROM_TG(optc); |
172 | |
173 | |
174 | REG_SET(OTG_DRR_V_TOTAL_CHANGE, 0, |
175 | OTG_DRR_V_TOTAL_CHANGE_LIMIT, limit); |
176 | } |
177 | |
178 | |
179 | /* Set DSC-related configuration. |
180 | * dsc_mode: 0 disables DSC, other values enable DSC in specified format |
181 | * sc_bytes_per_pixel: Bytes per pixel in u3.28 format |
182 | * dsc_slice_width: Slice width in pixels |
183 | */ |
184 | void optc3_set_dsc_config(struct timing_generator *optc, |
185 | enum optc_dsc_mode dsc_mode, |
186 | uint32_t dsc_bytes_per_pixel, |
187 | uint32_t dsc_slice_width) |
188 | { |
189 | struct optc *optc1 = DCN10TG_FROM_TG(optc); |
190 | |
191 | optc2_set_dsc_config(optc, dsc_mode, dsc_bytes_per_pixel, dsc_slice_width); |
192 | REG_UPDATE(OTG_V_SYNC_A_CNTL, OTG_V_SYNC_MODE, 0); |
193 | } |
194 | |
195 | void optc3_set_odm_bypass(struct timing_generator *optc, |
196 | const struct dc_crtc_timing *dc_crtc_timing) |
197 | { |
198 | struct optc *optc1 = DCN10TG_FROM_TG(optc); |
199 | enum h_timing_div_mode h_div = H_TIMING_NO_DIV; |
200 | |
201 | REG_SET_5(OPTC_DATA_SOURCE_SELECT, 0, |
202 | OPTC_NUM_OF_INPUT_SEGMENT, 0, |
203 | OPTC_SEG0_SRC_SEL, optc->inst, |
204 | OPTC_SEG1_SRC_SEL, 0xf, |
205 | OPTC_SEG2_SRC_SEL, 0xf, |
206 | OPTC_SEG3_SRC_SEL, 0xf |
207 | ); |
208 | |
209 | h_div = optc1_is_two_pixels_per_containter(timing: dc_crtc_timing); |
210 | REG_UPDATE(OTG_H_TIMING_CNTL, |
211 | OTG_H_TIMING_DIV_MODE, h_div); |
212 | |
213 | REG_SET(OPTC_MEMORY_CONFIG, 0, |
214 | OPTC_MEM_SEL, 0); |
215 | optc1->opp_count = 1; |
216 | } |
217 | |
218 | void optc3_set_odm_combine(struct timing_generator *optc, int *opp_id, int opp_cnt, |
219 | struct dc_crtc_timing *timing) |
220 | { |
221 | struct optc *optc1 = DCN10TG_FROM_TG(optc); |
222 | int mpcc_hactive = (timing->h_addressable + timing->h_border_left + timing->h_border_right) |
223 | / opp_cnt; |
224 | uint32_t memory_mask = 0; |
225 | |
226 | /* TODO: In pseudocode but does not affect maximus, delete comment if we dont need on asic |
227 | * REG_SET(OTG_GLOBAL_CONTROL2, 0, GLOBAL_UPDATE_LOCK_EN, 1); |
228 | * Program OTG register MASTER_UPDATE_LOCK_DB_X/Y to the position before DP frame start |
229 | * REG_SET_2(OTG_GLOBAL_CONTROL1, 0, |
230 | * MASTER_UPDATE_LOCK_DB_X, 160, |
231 | * MASTER_UPDATE_LOCK_DB_Y, 240); |
232 | */ |
233 | |
234 | ASSERT(opp_cnt == 2 || opp_cnt == 4); |
235 | |
236 | /* 2 pieces of memory required for up to 5120 displays, 4 for up to 8192, |
237 | * however, for ODM combine we can simplify by always using 4. |
238 | */ |
239 | if (opp_cnt == 2) { |
240 | /* To make sure there's no memory overlap, each instance "reserves" 2 |
241 | * memories and they are uniquely combined here. |
242 | */ |
243 | memory_mask = 0x3 << (opp_id[0] * 2) | 0x3 << (opp_id[1] * 2); |
244 | } else if (opp_cnt == 4) { |
245 | /* To make sure there's no memory overlap, each instance "reserves" 1 |
246 | * memory and they are uniquely combined here. |
247 | */ |
248 | memory_mask = 0x1 << (opp_id[0] * 2) | 0x1 << (opp_id[1] * 2) | 0x1 << (opp_id[2] * 2) | 0x1 << (opp_id[3] * 2); |
249 | } |
250 | |
251 | if (REG(OPTC_MEMORY_CONFIG)) |
252 | REG_SET(OPTC_MEMORY_CONFIG, 0, |
253 | OPTC_MEM_SEL, memory_mask); |
254 | |
255 | if (opp_cnt == 2) { |
256 | REG_SET_3(OPTC_DATA_SOURCE_SELECT, 0, |
257 | OPTC_NUM_OF_INPUT_SEGMENT, 1, |
258 | OPTC_SEG0_SRC_SEL, opp_id[0], |
259 | OPTC_SEG1_SRC_SEL, opp_id[1]); |
260 | } else if (opp_cnt == 4) { |
261 | REG_SET_5(OPTC_DATA_SOURCE_SELECT, 0, |
262 | OPTC_NUM_OF_INPUT_SEGMENT, 3, |
263 | OPTC_SEG0_SRC_SEL, opp_id[0], |
264 | OPTC_SEG1_SRC_SEL, opp_id[1], |
265 | OPTC_SEG2_SRC_SEL, opp_id[2], |
266 | OPTC_SEG3_SRC_SEL, opp_id[3]); |
267 | } |
268 | |
269 | REG_UPDATE(OPTC_WIDTH_CONTROL, |
270 | OPTC_SEGMENT_WIDTH, mpcc_hactive); |
271 | |
272 | REG_SET(OTG_H_TIMING_CNTL, 0, OTG_H_TIMING_DIV_MODE, opp_cnt - 1); |
273 | optc1->opp_count = opp_cnt; |
274 | } |
275 | |
276 | /** |
277 | * optc3_set_timing_double_buffer() - DRR double buffering control |
278 | * |
279 | * Sets double buffer point for V_TOTAL, H_TOTAL, VTOTAL_MIN, |
280 | * VTOTAL_MAX, VTOTAL_MIN_SEL and VTOTAL_MAX_SEL registers. |
281 | * |
282 | * @optc: timing_generator instance. |
283 | * @enable: Enable DRR double buffering control if true, disable otherwise. |
284 | * |
285 | * Options: any time, start of frame, dp start of frame (range timing) |
286 | */ |
287 | static void optc3_set_timing_double_buffer(struct timing_generator *optc, bool enable) |
288 | { |
289 | struct optc *optc1 = DCN10TG_FROM_TG(optc); |
290 | uint32_t mode = enable ? 2 : 0; |
291 | |
292 | REG_UPDATE(OTG_DOUBLE_BUFFER_CONTROL, |
293 | OTG_DRR_TIMING_DBUF_UPDATE_MODE, mode); |
294 | } |
295 | |
296 | void optc3_wait_drr_doublebuffer_pending_clear(struct timing_generator *optc) |
297 | { |
298 | struct optc *optc1 = DCN10TG_FROM_TG(optc); |
299 | |
300 | REG_WAIT(OTG_DOUBLE_BUFFER_CONTROL, OTG_DRR_TIMING_DBUF_UPDATE_PENDING, 0, 2, 100000); /* 1 vupdate at 5hz */ |
301 | |
302 | } |
303 | |
304 | void optc3_set_vtotal_min_max(struct timing_generator *optc, int vtotal_min, int vtotal_max) |
305 | { |
306 | struct dc *dc = optc->ctx->dc; |
307 | |
308 | if (dc->caps.dmub_caps.mclk_sw && !dc->debug.disable_fams) |
309 | dc_dmub_srv_drr_update_cmd(dc, tg_inst: optc->inst, vtotal_min, vtotal_max); |
310 | else |
311 | optc1_set_vtotal_min_max(optc, vtotal_min, vtotal_max); |
312 | } |
313 | |
314 | void optc3_tg_init(struct timing_generator *optc) |
315 | { |
316 | optc3_set_timing_double_buffer(optc, enable: true); |
317 | optc1_clear_optc_underflow(optc); |
318 | } |
319 | |
320 | static struct timing_generator_funcs dcn30_tg_funcs = { |
321 | .validate_timing = optc1_validate_timing, |
322 | .program_timing = optc1_program_timing, |
323 | .setup_vertical_interrupt0 = optc1_setup_vertical_interrupt0, |
324 | .setup_vertical_interrupt1 = optc1_setup_vertical_interrupt1, |
325 | .setup_vertical_interrupt2 = optc1_setup_vertical_interrupt2, |
326 | .program_global_sync = optc1_program_global_sync, |
327 | .enable_crtc = optc2_enable_crtc, |
328 | .disable_crtc = optc1_disable_crtc, |
329 | /* used by enable_timing_synchronization. Not need for FPGA */ |
330 | .is_counter_moving = optc1_is_counter_moving, |
331 | .get_position = optc1_get_position, |
332 | .get_frame_count = optc1_get_vblank_counter, |
333 | .get_scanoutpos = optc1_get_crtc_scanoutpos, |
334 | .get_otg_active_size = optc1_get_otg_active_size, |
335 | .set_early_control = optc1_set_early_control, |
336 | /* used by enable_timing_synchronization. Not need for FPGA */ |
337 | .wait_for_state = optc1_wait_for_state, |
338 | .set_blank_color = optc3_program_blank_color, |
339 | .did_triggered_reset_occur = optc1_did_triggered_reset_occur, |
340 | .triplebuffer_lock = optc3_triplebuffer_lock, |
341 | .triplebuffer_unlock = optc2_triplebuffer_unlock, |
342 | .enable_reset_trigger = optc1_enable_reset_trigger, |
343 | .enable_crtc_reset = optc1_enable_crtc_reset, |
344 | .disable_reset_trigger = optc1_disable_reset_trigger, |
345 | .lock = optc3_lock, |
346 | .unlock = optc1_unlock, |
347 | .lock_doublebuffer_enable = optc3_lock_doublebuffer_enable, |
348 | .lock_doublebuffer_disable = optc3_lock_doublebuffer_disable, |
349 | .enable_optc_clock = optc1_enable_optc_clock, |
350 | .set_drr = optc1_set_drr, |
351 | .get_last_used_drr_vtotal = optc2_get_last_used_drr_vtotal, |
352 | .set_vtotal_min_max = optc3_set_vtotal_min_max, |
353 | .set_static_screen_control = optc1_set_static_screen_control, |
354 | .program_stereo = optc1_program_stereo, |
355 | .is_stereo_left_eye = optc1_is_stereo_left_eye, |
356 | .tg_init = optc3_tg_init, |
357 | .is_tg_enabled = optc1_is_tg_enabled, |
358 | .is_optc_underflow_occurred = optc1_is_optc_underflow_occurred, |
359 | .clear_optc_underflow = optc1_clear_optc_underflow, |
360 | .setup_global_swap_lock = NULL, |
361 | .get_crc = optc1_get_crc, |
362 | .configure_crc = optc2_configure_crc, |
363 | .set_dsc_config = optc3_set_dsc_config, |
364 | .get_dsc_status = optc2_get_dsc_status, |
365 | .set_dwb_source = NULL, |
366 | .set_odm_bypass = optc3_set_odm_bypass, |
367 | .set_odm_combine = optc3_set_odm_combine, |
368 | .get_optc_source = optc2_get_optc_source, |
369 | .set_out_mux = optc3_set_out_mux, |
370 | .set_drr_trigger_window = optc3_set_drr_trigger_window, |
371 | .set_vtotal_change_limit = optc3_set_vtotal_change_limit, |
372 | .set_gsl = optc2_set_gsl, |
373 | .set_gsl_source_select = optc2_set_gsl_source_select, |
374 | .set_vtg_params = optc1_set_vtg_params, |
375 | .program_manual_trigger = optc2_program_manual_trigger, |
376 | .setup_manual_trigger = optc2_setup_manual_trigger, |
377 | .get_hw_timing = optc1_get_hw_timing, |
378 | .wait_drr_doublebuffer_pending_clear = optc3_wait_drr_doublebuffer_pending_clear, |
379 | }; |
380 | |
381 | void dcn30_timing_generator_init(struct optc *optc1) |
382 | { |
383 | optc1->base.funcs = &dcn30_tg_funcs; |
384 | |
385 | optc1->max_h_total = optc1->tg_mask->OTG_H_TOTAL + 1; |
386 | optc1->max_v_total = optc1->tg_mask->OTG_V_TOTAL + 1; |
387 | |
388 | optc1->min_h_blank = 32; |
389 | optc1->min_v_blank = 3; |
390 | optc1->min_v_blank_interlace = 5; |
391 | optc1->min_h_sync_width = 4; |
392 | optc1->min_v_sync_width = 1; |
393 | } |
394 | |