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