1 | /* |
2 | * Copyright 2020 Mauro Rossi <issor.oruam@gmail.com> |
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 DCE6 register header files */ |
29 | #include "dce/dce_6_0_d.h" |
30 | #include "dce/dce_6_0_sh_mask.h" |
31 | |
32 | #include "dc_types.h" |
33 | |
34 | #include "include/grph_object_id.h" |
35 | #include "include/logger_interface.h" |
36 | #include "../dce110/dce110_timing_generator.h" |
37 | #include "dce60_timing_generator.h" |
38 | |
39 | #include "timing_generator.h" |
40 | |
41 | enum black_color_format { |
42 | BLACK_COLOR_FORMAT_RGB_FULLRANGE = 0, /* used as index in array */ |
43 | BLACK_COLOR_FORMAT_RGB_LIMITED, |
44 | BLACK_COLOR_FORMAT_YUV_TV, |
45 | BLACK_COLOR_FORMAT_YUV_CV, |
46 | BLACK_COLOR_FORMAT_YUV_SUPER_AA, |
47 | |
48 | BLACK_COLOR_FORMAT_COUNT |
49 | }; |
50 | |
51 | static const struct dce110_timing_generator_offsets reg_offsets[] = { |
52 | { |
53 | .crtc = (mmCRTC0_DCFE_MEM_LIGHT_SLEEP_CNTL - mmCRTC0_DCFE_MEM_LIGHT_SLEEP_CNTL), |
54 | .dcp = (mmDCP0_GRPH_CONTROL - mmDCP0_GRPH_CONTROL), |
55 | }, |
56 | { |
57 | .crtc = (mmCRTC1_DCFE_MEM_LIGHT_SLEEP_CNTL - mmCRTC0_DCFE_MEM_LIGHT_SLEEP_CNTL), |
58 | .dcp = (mmDCP1_GRPH_CONTROL - mmDCP0_GRPH_CONTROL), |
59 | }, |
60 | { |
61 | .crtc = (mmCRTC2_DCFE_MEM_LIGHT_SLEEP_CNTL - mmCRTC0_DCFE_MEM_LIGHT_SLEEP_CNTL), |
62 | .dcp = (mmDCP2_GRPH_CONTROL - mmDCP0_GRPH_CONTROL), |
63 | }, |
64 | { |
65 | .crtc = (mmCRTC3_DCFE_MEM_LIGHT_SLEEP_CNTL - mmCRTC0_DCFE_MEM_LIGHT_SLEEP_CNTL), |
66 | .dcp = (mmDCP3_GRPH_CONTROL - mmDCP0_GRPH_CONTROL), |
67 | }, |
68 | { |
69 | .crtc = (mmCRTC4_DCFE_MEM_LIGHT_SLEEP_CNTL - mmCRTC0_DCFE_MEM_LIGHT_SLEEP_CNTL), |
70 | .dcp = (mmDCP4_GRPH_CONTROL - mmDCP0_GRPH_CONTROL), |
71 | }, |
72 | { |
73 | .crtc = (mmCRTC5_DCFE_MEM_LIGHT_SLEEP_CNTL - mmCRTC0_DCFE_MEM_LIGHT_SLEEP_CNTL), |
74 | .dcp = (mmDCP5_GRPH_CONTROL - mmDCP0_GRPH_CONTROL), |
75 | } |
76 | }; |
77 | |
78 | #define NUMBER_OF_FRAME_TO_WAIT_ON_TRIGGERED_RESET 10 |
79 | |
80 | #define MAX_H_TOTAL (CRTC_H_TOTAL__CRTC_H_TOTAL_MASK + 1) |
81 | #define MAX_V_TOTAL (CRTC_V_TOTAL__CRTC_V_TOTAL_MASKhw + 1) |
82 | |
83 | #define CRTC_REG(reg) (reg + tg110->offsets.crtc) |
84 | #define DCP_REG(reg) (reg + tg110->offsets.dcp) |
85 | #define DMIF_REG(reg) (reg + tg110->offsets.dmif) |
86 | |
87 | static void program_pix_dur(struct timing_generator *tg, uint32_t pix_clk_100hz) |
88 | { |
89 | uint64_t pix_dur; |
90 | uint32_t addr = mmDMIF_PG0_DPG_PIPE_ARBITRATION_CONTROL1 |
91 | + DCE110TG_FROM_TG(tg)->offsets.dmif; |
92 | uint32_t value = dm_read_reg(tg->ctx, addr); |
93 | |
94 | if (pix_clk_100hz == 0) |
95 | return; |
96 | |
97 | pix_dur = div_u64(dividend: 10000000000ull, divisor: pix_clk_100hz); |
98 | |
99 | set_reg_field_value( |
100 | value, |
101 | pix_dur, |
102 | DPG_PIPE_ARBITRATION_CONTROL1, |
103 | PIXEL_DURATION); |
104 | |
105 | dm_write_reg(tg->ctx, addr, value); |
106 | } |
107 | |
108 | static void program_timing(struct timing_generator *tg, |
109 | const struct dc_crtc_timing *timing, |
110 | int vready_offset, |
111 | int vstartup_start, |
112 | int vupdate_offset, |
113 | int vupdate_width, |
114 | const enum signal_type signal, |
115 | bool use_vbios) |
116 | { |
117 | if (!use_vbios) |
118 | program_pix_dur(tg, pix_clk_100hz: timing->pix_clk_100hz); |
119 | |
120 | dce110_tg_program_timing(tg, timing, vready_offset: 0, vstartup_start: 0, vupdate_offset: 0, vupdate_width: 0, signal: 0, use_vbios); |
121 | } |
122 | |
123 | static void dce60_timing_generator_enable_advanced_request( |
124 | struct timing_generator *tg, |
125 | bool enable, |
126 | const struct dc_crtc_timing *timing) |
127 | { |
128 | struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); |
129 | uint32_t addr = CRTC_REG(mmCRTC_START_LINE_CONTROL); |
130 | uint32_t value = dm_read_reg(tg->ctx, addr); |
131 | /* DCE6 has CRTC_PREFETCH_EN bit in CRTC_CONTROL register */ |
132 | uint32_t addr2 = CRTC_REG(mmCRTC_CONTROL); |
133 | uint32_t value2 = dm_read_reg(tg->ctx, addr2); |
134 | |
135 | /* DCE6 does not support CRTC_LEGACY_REQUESTOR_EN bit |
136 | so here is not possible to set bit based on enable argument */ |
137 | |
138 | if ((timing->v_sync_width + timing->v_front_porch) <= 3) { |
139 | set_reg_field_value( |
140 | value, |
141 | 3, |
142 | CRTC_START_LINE_CONTROL, |
143 | CRTC_ADVANCED_START_LINE_POSITION); |
144 | set_reg_field_value( |
145 | value2, |
146 | 0, |
147 | CRTC_CONTROL, |
148 | CRTC_PREFETCH_EN); |
149 | } else { |
150 | set_reg_field_value( |
151 | value, |
152 | 4, |
153 | CRTC_START_LINE_CONTROL, |
154 | CRTC_ADVANCED_START_LINE_POSITION); |
155 | set_reg_field_value( |
156 | value2, |
157 | 1, |
158 | CRTC_CONTROL, |
159 | CRTC_PREFETCH_EN); |
160 | } |
161 | |
162 | set_reg_field_value( |
163 | value, |
164 | 1, |
165 | CRTC_START_LINE_CONTROL, |
166 | CRTC_PROGRESSIVE_START_LINE_EARLY); |
167 | |
168 | set_reg_field_value( |
169 | value, |
170 | 1, |
171 | CRTC_START_LINE_CONTROL, |
172 | CRTC_INTERLACE_START_LINE_EARLY); |
173 | |
174 | dm_write_reg(tg->ctx, addr, value); |
175 | dm_write_reg(tg->ctx, addr2, value2); |
176 | } |
177 | |
178 | static bool dce60_is_tg_enabled(struct timing_generator *tg) |
179 | { |
180 | uint32_t addr = 0; |
181 | uint32_t value = 0; |
182 | uint32_t field = 0; |
183 | struct dce110_timing_generator *tg110 = DCE110TG_FROM_TG(tg); |
184 | |
185 | addr = CRTC_REG(mmCRTC_CONTROL); |
186 | value = dm_read_reg(tg->ctx, addr); |
187 | field = get_reg_field_value(value, CRTC_CONTROL, |
188 | CRTC_CURRENT_MASTER_EN_STATE); |
189 | return field == 1; |
190 | } |
191 | |
192 | static bool dce60_configure_crc(struct timing_generator *tg, |
193 | const struct crc_params *params) |
194 | { |
195 | /* Cannot configure crc on a CRTC that is disabled */ |
196 | if (!dce60_is_tg_enabled(tg)) |
197 | return false; |
198 | |
199 | /* DCE6 has no CRTC_CRC_CNTL register, nothing to do */ |
200 | |
201 | return true; |
202 | } |
203 | |
204 | static const struct timing_generator_funcs dce60_tg_funcs = { |
205 | .validate_timing = dce110_tg_validate_timing, |
206 | .program_timing = program_timing, |
207 | .enable_crtc = dce110_timing_generator_enable_crtc, |
208 | .disable_crtc = dce110_timing_generator_disable_crtc, |
209 | .is_counter_moving = dce110_timing_generator_is_counter_moving, |
210 | .get_position = dce110_timing_generator_get_position, |
211 | .get_frame_count = dce110_timing_generator_get_vblank_counter, |
212 | .get_scanoutpos = dce110_timing_generator_get_crtc_scanoutpos, |
213 | .set_early_control = dce110_timing_generator_set_early_control, |
214 | .wait_for_state = dce110_tg_wait_for_state, |
215 | .set_blank = dce110_tg_set_blank, |
216 | .is_blanked = dce110_tg_is_blanked, |
217 | .set_colors = dce110_tg_set_colors, |
218 | .set_overscan_blank_color = |
219 | dce110_timing_generator_set_overscan_color_black, |
220 | .set_blank_color = dce110_timing_generator_program_blank_color, |
221 | .disable_vga = dce110_timing_generator_disable_vga, |
222 | .did_triggered_reset_occur = |
223 | dce110_timing_generator_did_triggered_reset_occur, |
224 | .setup_global_swap_lock = |
225 | dce110_timing_generator_setup_global_swap_lock, |
226 | .enable_reset_trigger = dce110_timing_generator_enable_reset_trigger, |
227 | .disable_reset_trigger = dce110_timing_generator_disable_reset_trigger, |
228 | .tear_down_global_swap_lock = |
229 | dce110_timing_generator_tear_down_global_swap_lock, |
230 | .set_drr = dce110_timing_generator_set_drr, |
231 | .set_static_screen_control = |
232 | dce110_timing_generator_set_static_screen_control, |
233 | .set_test_pattern = dce110_timing_generator_set_test_pattern, |
234 | .arm_vert_intr = dce110_arm_vert_intr, |
235 | |
236 | /* DCE6.0 overrides */ |
237 | .enable_advanced_request = |
238 | dce60_timing_generator_enable_advanced_request, |
239 | .configure_crc = dce60_configure_crc, |
240 | .get_crc = dce110_get_crc, |
241 | }; |
242 | |
243 | void dce60_timing_generator_construct( |
244 | struct dce110_timing_generator *tg110, |
245 | struct dc_context *ctx, |
246 | uint32_t instance, |
247 | const struct dce110_timing_generator_offsets *offsets) |
248 | { |
249 | tg110->controller_id = CONTROLLER_ID_D0 + instance; |
250 | tg110->base.inst = instance; |
251 | tg110->offsets = *offsets; |
252 | tg110->derived_offsets = reg_offsets[instance]; |
253 | |
254 | tg110->base.funcs = &dce60_tg_funcs; |
255 | |
256 | tg110->base.ctx = ctx; |
257 | tg110->base.bp = ctx->dc_bios; |
258 | |
259 | tg110->max_h_total = CRTC_H_TOTAL__CRTC_H_TOTAL_MASK + 1; |
260 | tg110->max_v_total = CRTC_V_TOTAL__CRTC_V_TOTAL_MASK + 1; |
261 | |
262 | tg110->min_h_blank = 56; |
263 | tg110->min_h_front_porch = 4; |
264 | tg110->min_h_back_porch = 4; |
265 | } |
266 | |
267 | |