1 | /* |
2 | * Copyright 2016 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 | #include "dm_helpers.h" |
28 | #include "core_types.h" |
29 | #include "resource.h" |
30 | #include "dce/dce_hwseq.h" |
31 | #include "dce110/dce110_hwseq.h" |
32 | #include "dcn21_hwseq.h" |
33 | #include "vmid.h" |
34 | #include "reg_helper.h" |
35 | #include "hw/clk_mgr.h" |
36 | #include "dc_dmub_srv.h" |
37 | #include "abm.h" |
38 | #include "link.h" |
39 | |
40 | #define DC_LOGGER_INIT(logger) |
41 | |
42 | #define CTX \ |
43 | hws->ctx |
44 | #define REG(reg)\ |
45 | hws->regs->reg |
46 | |
47 | #undef FN |
48 | #define FN(reg_name, field_name) \ |
49 | hws->shifts->field_name, hws->masks->field_name |
50 | |
51 | /* Temporary read settings, future will get values from kmd directly */ |
52 | static void mmhub_update_page_table_config(struct dcn_hubbub_phys_addr_config *config, |
53 | struct dce_hwseq *hws) |
54 | { |
55 | uint32_t page_table_base_hi; |
56 | uint32_t page_table_base_lo; |
57 | |
58 | REG_GET(VM_CONTEXT0_PAGE_TABLE_BASE_ADDR_HI32, |
59 | PAGE_DIRECTORY_ENTRY_HI32, &page_table_base_hi); |
60 | REG_GET(VM_CONTEXT0_PAGE_TABLE_BASE_ADDR_LO32, |
61 | PAGE_DIRECTORY_ENTRY_LO32, &page_table_base_lo); |
62 | |
63 | config->gart_config.page_table_base_addr = ((uint64_t)page_table_base_hi << 32) | page_table_base_lo; |
64 | |
65 | } |
66 | |
67 | int dcn21_init_sys_ctx(struct dce_hwseq *hws, struct dc *dc, struct dc_phy_addr_space_config *pa_config) |
68 | { |
69 | struct dcn_hubbub_phys_addr_config config; |
70 | |
71 | config.system_aperture.fb_top = pa_config->system_aperture.fb_top; |
72 | config.system_aperture.fb_offset = pa_config->system_aperture.fb_offset; |
73 | config.system_aperture.fb_base = pa_config->system_aperture.fb_base; |
74 | config.system_aperture.agp_top = pa_config->system_aperture.agp_top; |
75 | config.system_aperture.agp_bot = pa_config->system_aperture.agp_bot; |
76 | config.system_aperture.agp_base = pa_config->system_aperture.agp_base; |
77 | config.gart_config.page_table_start_addr = pa_config->gart_config.page_table_start_addr; |
78 | config.gart_config.page_table_end_addr = pa_config->gart_config.page_table_end_addr; |
79 | config.gart_config.page_table_base_addr = pa_config->gart_config.page_table_base_addr; |
80 | |
81 | mmhub_update_page_table_config(config: &config, hws); |
82 | |
83 | return dc->res_pool->hubbub->funcs->init_dchub_sys_ctx(dc->res_pool->hubbub, &config); |
84 | } |
85 | |
86 | // work around for Renoir s0i3, if register is programmed, bypass golden init. |
87 | |
88 | bool dcn21_s0i3_golden_init_wa(struct dc *dc) |
89 | { |
90 | struct dce_hwseq *hws = dc->hwseq; |
91 | uint32_t value = 0; |
92 | |
93 | value = REG_READ(MICROSECOND_TIME_BASE_DIV); |
94 | |
95 | return value != 0x00120464; |
96 | } |
97 | |
98 | void dcn21_exit_optimized_pwr_state( |
99 | const struct dc *dc, |
100 | struct dc_state *context) |
101 | { |
102 | dc->clk_mgr->funcs->update_clocks( |
103 | dc->clk_mgr, |
104 | context, |
105 | false); |
106 | } |
107 | |
108 | void dcn21_optimize_pwr_state( |
109 | const struct dc *dc, |
110 | struct dc_state *context) |
111 | { |
112 | dc->clk_mgr->funcs->update_clocks( |
113 | dc->clk_mgr, |
114 | context, |
115 | true); |
116 | } |
117 | |
118 | /* If user hotplug a HDMI monitor while in monitor off, |
119 | * OS will do a mode set (with output timing) but keep output off. |
120 | * In this case DAL will ask vbios to power up the pll in the PHY. |
121 | * If user unplug the monitor (while we are on monitor off) or |
122 | * system attempt to enter modern standby (which we will disable PLL), |
123 | * PHY will hang on the next mode set attempt. |
124 | * if enable PLL follow by disable PLL (without executing lane enable/disable), |
125 | * RDPCS_PHY_DP_MPLLB_STATE remains 1, |
126 | * which indicate that PLL disable attempt actually didn't go through. |
127 | * As a workaround, insert PHY lane enable/disable before PLL disable. |
128 | */ |
129 | void dcn21_PLAT_58856_wa(struct dc_state *context, struct pipe_ctx *pipe_ctx) |
130 | { |
131 | if (!pipe_ctx->stream->dpms_off) |
132 | return; |
133 | |
134 | pipe_ctx->stream->dpms_off = false; |
135 | pipe_ctx->stream->ctx->dc->link_srv->set_dpms_on(context, pipe_ctx); |
136 | pipe_ctx->stream->ctx->dc->link_srv->set_dpms_off(pipe_ctx); |
137 | pipe_ctx->stream->dpms_off = true; |
138 | } |
139 | |
140 | static bool dmub_abm_set_pipe(struct abm *abm, uint32_t otg_inst, |
141 | uint32_t option, uint32_t panel_inst, uint32_t pwrseq_inst) |
142 | { |
143 | union dmub_rb_cmd cmd; |
144 | struct dc_context *dc = abm->ctx; |
145 | uint32_t ramping_boundary = 0xFFFF; |
146 | |
147 | memset(&cmd, 0, sizeof(cmd)); |
148 | cmd.abm_set_pipe.header.type = DMUB_CMD__ABM; |
149 | cmd.abm_set_pipe.header.sub_type = DMUB_CMD__ABM_SET_PIPE; |
150 | cmd.abm_set_pipe.abm_set_pipe_data.otg_inst = otg_inst; |
151 | cmd.abm_set_pipe.abm_set_pipe_data.pwrseq_inst = pwrseq_inst; |
152 | cmd.abm_set_pipe.abm_set_pipe_data.set_pipe_option = option; |
153 | cmd.abm_set_pipe.abm_set_pipe_data.panel_inst = panel_inst; |
154 | cmd.abm_set_pipe.abm_set_pipe_data.ramping_boundary = ramping_boundary; |
155 | cmd.abm_set_pipe.header.payload_bytes = sizeof(struct dmub_cmd_abm_set_pipe_data); |
156 | |
157 | dc_wake_and_execute_dmub_cmd(ctx: dc, cmd: &cmd, wait_type: DM_DMUB_WAIT_TYPE_WAIT); |
158 | |
159 | return true; |
160 | } |
161 | |
162 | static void dmub_abm_set_backlight(struct dc_context *dc, uint32_t backlight_pwm_u16_16, |
163 | uint32_t frame_ramp, uint32_t panel_inst) |
164 | { |
165 | union dmub_rb_cmd cmd; |
166 | |
167 | memset(&cmd, 0, sizeof(cmd)); |
168 | cmd.abm_set_backlight.header.type = DMUB_CMD__ABM; |
169 | cmd.abm_set_backlight.header.sub_type = DMUB_CMD__ABM_SET_BACKLIGHT; |
170 | cmd.abm_set_backlight.abm_set_backlight_data.frame_ramp = frame_ramp; |
171 | cmd.abm_set_backlight.abm_set_backlight_data.backlight_user_level = backlight_pwm_u16_16; |
172 | cmd.abm_set_backlight.abm_set_backlight_data.version = DMUB_CMD_ABM_CONTROL_VERSION_1; |
173 | cmd.abm_set_backlight.abm_set_backlight_data.panel_mask = (0x01 << panel_inst); |
174 | cmd.abm_set_backlight.header.payload_bytes = sizeof(struct dmub_cmd_abm_set_backlight_data); |
175 | |
176 | dc_wake_and_execute_dmub_cmd(ctx: dc, cmd: &cmd, wait_type: DM_DMUB_WAIT_TYPE_WAIT); |
177 | } |
178 | |
179 | void dcn21_set_abm_immediate_disable(struct pipe_ctx *pipe_ctx) |
180 | { |
181 | struct abm *abm = pipe_ctx->stream_res.abm; |
182 | uint32_t otg_inst = pipe_ctx->stream_res.tg->inst; |
183 | struct panel_cntl *panel_cntl = pipe_ctx->stream->link->panel_cntl; |
184 | struct dmcu *dmcu = pipe_ctx->stream->ctx->dc->res_pool->dmcu; |
185 | |
186 | if (dmcu) { |
187 | dce110_set_abm_immediate_disable(pipe_ctx); |
188 | return; |
189 | } |
190 | |
191 | if (abm && panel_cntl) { |
192 | if (abm->funcs && abm->funcs->set_pipe_ex) { |
193 | abm->funcs->set_pipe_ex(abm, otg_inst, SET_ABM_PIPE_IMMEDIATELY_DISABLE, |
194 | panel_cntl->inst, panel_cntl->pwrseq_inst); |
195 | } else { |
196 | dmub_abm_set_pipe(abm, |
197 | otg_inst, |
198 | SET_ABM_PIPE_IMMEDIATELY_DISABLE, |
199 | panel_inst: panel_cntl->inst, |
200 | pwrseq_inst: panel_cntl->pwrseq_inst); |
201 | } |
202 | panel_cntl->funcs->store_backlight_level(panel_cntl); |
203 | } |
204 | } |
205 | |
206 | void dcn21_set_pipe(struct pipe_ctx *pipe_ctx) |
207 | { |
208 | struct abm *abm = pipe_ctx->stream_res.abm; |
209 | struct timing_generator *tg = pipe_ctx->stream_res.tg; |
210 | struct panel_cntl *panel_cntl = pipe_ctx->stream->link->panel_cntl; |
211 | struct dmcu *dmcu = pipe_ctx->stream->ctx->dc->res_pool->dmcu; |
212 | uint32_t otg_inst; |
213 | |
214 | if (!abm || !tg || !panel_cntl) |
215 | return; |
216 | |
217 | otg_inst = tg->inst; |
218 | |
219 | if (dmcu) { |
220 | dce110_set_pipe(pipe_ctx); |
221 | return; |
222 | } |
223 | |
224 | if (abm->funcs && abm->funcs->set_pipe_ex) { |
225 | abm->funcs->set_pipe_ex(abm, |
226 | otg_inst, |
227 | SET_ABM_PIPE_NORMAL, |
228 | panel_cntl->inst, |
229 | panel_cntl->pwrseq_inst); |
230 | } else { |
231 | dmub_abm_set_pipe(abm, otg_inst, |
232 | SET_ABM_PIPE_NORMAL, |
233 | panel_inst: panel_cntl->inst, |
234 | pwrseq_inst: panel_cntl->pwrseq_inst); |
235 | } |
236 | } |
237 | |
238 | bool dcn21_set_backlight_level(struct pipe_ctx *pipe_ctx, |
239 | uint32_t backlight_pwm_u16_16, |
240 | uint32_t frame_ramp) |
241 | { |
242 | struct dc_context *dc = pipe_ctx->stream->ctx; |
243 | struct abm *abm = pipe_ctx->stream_res.abm; |
244 | struct timing_generator *tg = pipe_ctx->stream_res.tg; |
245 | struct panel_cntl *panel_cntl = pipe_ctx->stream->link->panel_cntl; |
246 | uint32_t otg_inst; |
247 | |
248 | if (!abm || !tg || !panel_cntl) |
249 | return false; |
250 | |
251 | otg_inst = tg->inst; |
252 | |
253 | if (dc->dc->res_pool->dmcu) { |
254 | dce110_set_backlight_level(pipe_ctx, backlight_pwm_u16_16, frame_ramp); |
255 | return true; |
256 | } |
257 | |
258 | if (abm->funcs && abm->funcs->set_pipe_ex) { |
259 | abm->funcs->set_pipe_ex(abm, |
260 | otg_inst, |
261 | SET_ABM_PIPE_NORMAL, |
262 | panel_cntl->inst, |
263 | panel_cntl->pwrseq_inst); |
264 | } else { |
265 | dmub_abm_set_pipe(abm, |
266 | otg_inst, |
267 | SET_ABM_PIPE_NORMAL, |
268 | panel_inst: panel_cntl->inst, |
269 | pwrseq_inst: panel_cntl->pwrseq_inst); |
270 | } |
271 | |
272 | if (abm->funcs && abm->funcs->set_backlight_level_pwm) |
273 | abm->funcs->set_backlight_level_pwm(abm, backlight_pwm_u16_16, |
274 | frame_ramp, 0, panel_cntl->inst); |
275 | else |
276 | dmub_abm_set_backlight(dc, backlight_pwm_u16_16, frame_ramp, panel_inst: panel_cntl->inst); |
277 | |
278 | return true; |
279 | } |
280 | |
281 | bool dcn21_is_abm_supported(struct dc *dc, |
282 | struct dc_state *context, struct dc_stream_state *stream) |
283 | { |
284 | int i; |
285 | |
286 | for (i = 0; i < dc->res_pool->pipe_count; i++) { |
287 | struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[i]; |
288 | |
289 | if (pipe_ctx->stream == stream && |
290 | (pipe_ctx->prev_odm_pipe == NULL && pipe_ctx->next_odm_pipe == NULL)) |
291 | return true; |
292 | } |
293 | return false; |
294 | } |
295 | |
296 | |