1 | /* |
2 | * Copyright 2022 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 | /* FILE POLICY AND INTENDED USAGE: |
27 | * This file implements retrieval and configuration of eDP panel features such |
28 | * as PSR and ABM and it also manages specs defined eDP panel power sequences. |
29 | */ |
30 | |
31 | #include "link_edp_panel_control.h" |
32 | #include "link_dpcd.h" |
33 | #include "link_dp_capability.h" |
34 | #include "dm_helpers.h" |
35 | #include "dal_asic_id.h" |
36 | #include "link_dp_phy.h" |
37 | #include "dce/dmub_psr.h" |
38 | #include "dc/dc_dmub_srv.h" |
39 | #include "dce/dmub_replay.h" |
40 | #include "abm.h" |
41 | #define DC_LOGGER \ |
42 | link->ctx->logger |
43 | #define DC_LOGGER_INIT(logger) |
44 | |
45 | #define DP_SINK_PR_ENABLE_AND_CONFIGURATION 0x37B |
46 | |
47 | /* Travis */ |
48 | static const uint8_t DP_VGA_LVDS_CONVERTER_ID_2[] = "sivarT" ; |
49 | /* Nutmeg */ |
50 | static const uint8_t DP_VGA_LVDS_CONVERTER_ID_3[] = "dnomlA" ; |
51 | |
52 | void dp_set_panel_mode(struct dc_link *link, enum dp_panel_mode panel_mode) |
53 | { |
54 | union dpcd_edp_config edp_config_set; |
55 | bool panel_mode_edp = false; |
56 | enum dc_status result; |
57 | |
58 | memset(&edp_config_set, '\0', sizeof(union dpcd_edp_config)); |
59 | |
60 | switch (panel_mode) { |
61 | case DP_PANEL_MODE_EDP: |
62 | case DP_PANEL_MODE_SPECIAL: |
63 | panel_mode_edp = true; |
64 | break; |
65 | |
66 | default: |
67 | break; |
68 | } |
69 | |
70 | /*set edp panel mode in receiver*/ |
71 | result = core_link_read_dpcd( |
72 | link, |
73 | DP_EDP_CONFIGURATION_SET, |
74 | data: &edp_config_set.raw, |
75 | size: sizeof(edp_config_set.raw)); |
76 | |
77 | if (result == DC_OK && |
78 | edp_config_set.bits.PANEL_MODE_EDP |
79 | != panel_mode_edp) { |
80 | |
81 | edp_config_set.bits.PANEL_MODE_EDP = |
82 | panel_mode_edp; |
83 | result = core_link_write_dpcd( |
84 | link, |
85 | DP_EDP_CONFIGURATION_SET, |
86 | data: &edp_config_set.raw, |
87 | size: sizeof(edp_config_set.raw)); |
88 | |
89 | ASSERT(result == DC_OK); |
90 | } |
91 | |
92 | link->panel_mode = panel_mode; |
93 | DC_LOG_DETECTION_DP_CAPS("Link: %d eDP panel mode supported: %d " |
94 | "eDP panel mode enabled: %d \n" , |
95 | link->link_index, |
96 | link->dpcd_caps.panel_mode_edp, |
97 | panel_mode_edp); |
98 | } |
99 | |
100 | enum dp_panel_mode dp_get_panel_mode(struct dc_link *link) |
101 | { |
102 | /* We need to explicitly check that connector |
103 | * is not DP. Some Travis_VGA get reported |
104 | * by video bios as DP. |
105 | */ |
106 | if (link->connector_signal != SIGNAL_TYPE_DISPLAY_PORT) { |
107 | |
108 | switch (link->dpcd_caps.branch_dev_id) { |
109 | case DP_BRANCH_DEVICE_ID_0022B9: |
110 | /* alternate scrambler reset is required for Travis |
111 | * for the case when external chip does not |
112 | * provide sink device id, alternate scrambler |
113 | * scheme will be overriden later by querying |
114 | * Encoder features |
115 | */ |
116 | if (strncmp( |
117 | link->dpcd_caps.branch_dev_name, |
118 | DP_VGA_LVDS_CONVERTER_ID_2, |
119 | sizeof( |
120 | link->dpcd_caps. |
121 | branch_dev_name)) == 0) { |
122 | return DP_PANEL_MODE_SPECIAL; |
123 | } |
124 | break; |
125 | case DP_BRANCH_DEVICE_ID_00001A: |
126 | /* alternate scrambler reset is required for Travis |
127 | * for the case when external chip does not provide |
128 | * sink device id, alternate scrambler scheme will |
129 | * be overriden later by querying Encoder feature |
130 | */ |
131 | if (strncmp(link->dpcd_caps.branch_dev_name, |
132 | DP_VGA_LVDS_CONVERTER_ID_3, |
133 | sizeof( |
134 | link->dpcd_caps. |
135 | branch_dev_name)) == 0) { |
136 | return DP_PANEL_MODE_SPECIAL; |
137 | } |
138 | break; |
139 | default: |
140 | break; |
141 | } |
142 | } |
143 | |
144 | if (link->dpcd_caps.panel_mode_edp && |
145 | (link->connector_signal == SIGNAL_TYPE_EDP || |
146 | (link->connector_signal == SIGNAL_TYPE_DISPLAY_PORT && |
147 | link->is_internal_display))) { |
148 | return DP_PANEL_MODE_EDP; |
149 | } |
150 | |
151 | return DP_PANEL_MODE_DEFAULT; |
152 | } |
153 | |
154 | bool edp_set_backlight_level_nits(struct dc_link *link, |
155 | bool isHDR, |
156 | uint32_t backlight_millinits, |
157 | uint32_t transition_time_in_ms) |
158 | { |
159 | struct dpcd_source_backlight_set dpcd_backlight_set; |
160 | uint8_t backlight_control = isHDR ? 1 : 0; |
161 | |
162 | if (!link || (link->connector_signal != SIGNAL_TYPE_EDP && |
163 | link->connector_signal != SIGNAL_TYPE_DISPLAY_PORT)) |
164 | return false; |
165 | |
166 | // OLEDs have no PWM, they can only use AUX |
167 | if (link->dpcd_sink_ext_caps.bits.oled == 1) |
168 | backlight_control = 1; |
169 | |
170 | *(uint32_t *)&dpcd_backlight_set.backlight_level_millinits = backlight_millinits; |
171 | *(uint16_t *)&dpcd_backlight_set.backlight_transition_time_ms = (uint16_t)transition_time_in_ms; |
172 | |
173 | |
174 | if (!link->dpcd_caps.panel_luminance_control) { |
175 | if (core_link_write_dpcd(link, DP_SOURCE_BACKLIGHT_LEVEL, |
176 | data: (uint8_t *)(&dpcd_backlight_set), |
177 | size: sizeof(dpcd_backlight_set)) != DC_OK) |
178 | return false; |
179 | |
180 | if (core_link_write_dpcd(link, DP_SOURCE_BACKLIGHT_CONTROL, |
181 | data: &backlight_control, size: 1) != DC_OK) |
182 | return false; |
183 | } else { |
184 | uint8_t backlight_enable = 0; |
185 | struct target_luminance_value *target_luminance = NULL; |
186 | |
187 | //if target luminance value is greater than 24 bits, clip the value to 24 bits |
188 | if (backlight_millinits > 0xFFFFFF) |
189 | backlight_millinits = 0xFFFFFF; |
190 | |
191 | target_luminance = (struct target_luminance_value *)&backlight_millinits; |
192 | |
193 | core_link_read_dpcd(link, DP_EDP_BACKLIGHT_MODE_SET_REGISTER, |
194 | data: &backlight_enable, size: sizeof(uint8_t)); |
195 | |
196 | backlight_enable |= DP_EDP_PANEL_LUMINANCE_CONTROL_ENABLE; |
197 | |
198 | if (core_link_write_dpcd(link, DP_EDP_BACKLIGHT_MODE_SET_REGISTER, |
199 | data: &backlight_enable, |
200 | size: sizeof(backlight_enable)) != DC_OK) |
201 | return false; |
202 | |
203 | if (core_link_write_dpcd(link, DP_EDP_PANEL_TARGET_LUMINANCE_VALUE, |
204 | data: (uint8_t *)(target_luminance), |
205 | size: sizeof(struct target_luminance_value)) != DC_OK) |
206 | return false; |
207 | } |
208 | |
209 | return true; |
210 | } |
211 | |
212 | bool edp_get_backlight_level_nits(struct dc_link *link, |
213 | uint32_t *backlight_millinits_avg, |
214 | uint32_t *backlight_millinits_peak) |
215 | { |
216 | union dpcd_source_backlight_get dpcd_backlight_get; |
217 | |
218 | memset(&dpcd_backlight_get, 0, sizeof(union dpcd_source_backlight_get)); |
219 | |
220 | if (!link || (link->connector_signal != SIGNAL_TYPE_EDP && |
221 | link->connector_signal != SIGNAL_TYPE_DISPLAY_PORT)) |
222 | return false; |
223 | |
224 | if (!core_link_read_dpcd(link, DP_SOURCE_BACKLIGHT_CURRENT_PEAK, |
225 | data: dpcd_backlight_get.raw, |
226 | size: sizeof(union dpcd_source_backlight_get))) |
227 | return false; |
228 | |
229 | *backlight_millinits_avg = |
230 | dpcd_backlight_get.bytes.backlight_millinits_avg; |
231 | *backlight_millinits_peak = |
232 | dpcd_backlight_get.bytes.backlight_millinits_peak; |
233 | |
234 | /* On non-supported panels dpcd_read usually succeeds with 0 returned */ |
235 | if (*backlight_millinits_avg == 0 || |
236 | *backlight_millinits_avg > *backlight_millinits_peak) |
237 | return false; |
238 | |
239 | return true; |
240 | } |
241 | |
242 | bool edp_backlight_enable_aux(struct dc_link *link, bool enable) |
243 | { |
244 | uint8_t backlight_enable = enable ? 1 : 0; |
245 | |
246 | if (!link || (link->connector_signal != SIGNAL_TYPE_EDP && |
247 | link->connector_signal != SIGNAL_TYPE_DISPLAY_PORT)) |
248 | return false; |
249 | |
250 | if (core_link_write_dpcd(link, DP_SOURCE_BACKLIGHT_ENABLE, |
251 | data: &backlight_enable, size: 1) != DC_OK) |
252 | return false; |
253 | |
254 | return true; |
255 | } |
256 | |
257 | // we read default from 0x320 because we expect BIOS wrote it there |
258 | // regular get_backlight_nit reads from panel set at 0x326 |
259 | static bool read_default_bl_aux(struct dc_link *link, uint32_t *backlight_millinits) |
260 | { |
261 | if (!link || (link->connector_signal != SIGNAL_TYPE_EDP && |
262 | link->connector_signal != SIGNAL_TYPE_DISPLAY_PORT)) |
263 | return false; |
264 | |
265 | if (!link->dpcd_caps.panel_luminance_control) { |
266 | if (!core_link_read_dpcd(link, DP_SOURCE_BACKLIGHT_LEVEL, |
267 | data: (uint8_t *)backlight_millinits, |
268 | size: sizeof(uint32_t))) |
269 | return false; |
270 | } else { |
271 | //setting to 0 as a precaution, since target_luminance_value is 3 bytes |
272 | memset(backlight_millinits, 0, sizeof(uint32_t)); |
273 | |
274 | if (!core_link_read_dpcd(link, DP_EDP_PANEL_TARGET_LUMINANCE_VALUE, |
275 | data: (uint8_t *)backlight_millinits, |
276 | size: sizeof(struct target_luminance_value))) |
277 | return false; |
278 | } |
279 | |
280 | return true; |
281 | } |
282 | |
283 | bool set_default_brightness_aux(struct dc_link *link) |
284 | { |
285 | uint32_t default_backlight; |
286 | |
287 | if (link && link->dpcd_sink_ext_caps.bits.oled == 1) { |
288 | if (!read_default_bl_aux(link, backlight_millinits: &default_backlight)) |
289 | default_backlight = 150000; |
290 | // if > 5000, it might be wrong readback. 0 nits is a valid default value for OLED panel. |
291 | if (default_backlight < 1000 || default_backlight > 5000000) |
292 | default_backlight = 150000; |
293 | |
294 | return edp_set_backlight_level_nits(link, isHDR: true, |
295 | backlight_millinits: default_backlight, transition_time_in_ms: 0); |
296 | } |
297 | return false; |
298 | } |
299 | |
300 | bool edp_is_ilr_optimization_enabled(struct dc_link *link) |
301 | { |
302 | if (link->dpcd_caps.edp_supported_link_rates_count == 0 || !link->panel_config.ilr.optimize_edp_link_rate) |
303 | return false; |
304 | return true; |
305 | } |
306 | |
307 | enum dc_link_rate get_max_link_rate_from_ilr_table(struct dc_link *link) |
308 | { |
309 | enum dc_link_rate link_rate = link->reported_link_cap.link_rate; |
310 | |
311 | for (int i = 0; i < link->dpcd_caps.edp_supported_link_rates_count; i++) { |
312 | if (link_rate < link->dpcd_caps.edp_supported_link_rates[i]) |
313 | link_rate = link->dpcd_caps.edp_supported_link_rates[i]; |
314 | } |
315 | |
316 | return link_rate; |
317 | } |
318 | |
319 | bool edp_is_ilr_optimization_required(struct dc_link *link, |
320 | struct dc_crtc_timing *crtc_timing) |
321 | { |
322 | struct dc_link_settings link_setting; |
323 | uint8_t link_bw_set; |
324 | uint8_t link_rate_set; |
325 | uint32_t req_bw; |
326 | union lane_count_set lane_count_set = {0}; |
327 | |
328 | ASSERT(link || crtc_timing); // invalid input |
329 | |
330 | if (!edp_is_ilr_optimization_enabled(link)) |
331 | return false; |
332 | |
333 | |
334 | // Read DPCD 00100h to find if standard link rates are set |
335 | core_link_read_dpcd(link, DP_LINK_BW_SET, |
336 | data: &link_bw_set, size: sizeof(link_bw_set)); |
337 | |
338 | if (link_bw_set) { |
339 | DC_LOG_EVENT_LINK_TRAINING("eDP ILR: Optimization required, VBIOS used link_bw_set\n" ); |
340 | return true; |
341 | } |
342 | |
343 | // Read DPCD 00115h to find the edp link rate set used |
344 | core_link_read_dpcd(link, DP_LINK_RATE_SET, |
345 | data: &link_rate_set, size: sizeof(link_rate_set)); |
346 | |
347 | // Read DPCD 00101h to find out the number of lanes currently set |
348 | core_link_read_dpcd(link, DP_LANE_COUNT_SET, |
349 | data: &lane_count_set.raw, size: sizeof(lane_count_set)); |
350 | |
351 | req_bw = dc_bandwidth_in_kbps_from_timing(timing: crtc_timing, link_encoding: dc_link_get_highest_encoding_format(link)); |
352 | |
353 | if (!crtc_timing->flags.DSC) |
354 | edp_decide_link_settings(link, link_setting: &link_setting, req_bw); |
355 | else |
356 | decide_edp_link_settings_with_dsc(link, link_setting: &link_setting, req_bw, max_link_rate: LINK_RATE_UNKNOWN); |
357 | |
358 | if (link->dpcd_caps.edp_supported_link_rates[link_rate_set] != link_setting.link_rate || |
359 | lane_count_set.bits.LANE_COUNT_SET != link_setting.lane_count) { |
360 | DC_LOG_EVENT_LINK_TRAINING("eDP ILR: Optimization required, VBIOS link_rate_set not optimal\n" ); |
361 | return true; |
362 | } |
363 | |
364 | DC_LOG_EVENT_LINK_TRAINING("eDP ILR: No optimization required, VBIOS set optimal link_rate_set\n" ); |
365 | return false; |
366 | } |
367 | |
368 | void edp_panel_backlight_power_on(struct dc_link *link, bool wait_for_hpd) |
369 | { |
370 | if (link->connector_signal != SIGNAL_TYPE_EDP) |
371 | return; |
372 | |
373 | link->dc->hwss.edp_power_control(link, true); |
374 | if (wait_for_hpd) |
375 | link->dc->hwss.edp_wait_for_hpd_ready(link, true); |
376 | if (link->dc->hwss.edp_backlight_control) |
377 | link->dc->hwss.edp_backlight_control(link, true); |
378 | } |
379 | |
380 | void edp_set_panel_power(struct dc_link *link, bool powerOn) |
381 | { |
382 | if (powerOn) { |
383 | // 1. panel VDD on |
384 | if (!link->dc->config.edp_no_power_sequencing) |
385 | link->dc->hwss.edp_power_control(link, true); |
386 | link->dc->hwss.edp_wait_for_hpd_ready(link, true); |
387 | |
388 | // 2. panel BL on |
389 | if (link->dc->hwss.edp_backlight_control) |
390 | link->dc->hwss.edp_backlight_control(link, true); |
391 | |
392 | // 3. Rx power on |
393 | dpcd_write_rx_power_ctrl(link, on: true); |
394 | } else { |
395 | // 3. Rx power off |
396 | dpcd_write_rx_power_ctrl(link, on: false); |
397 | |
398 | // 2. panel BL off |
399 | if (link->dc->hwss.edp_backlight_control) |
400 | link->dc->hwss.edp_backlight_control(link, false); |
401 | |
402 | // 1. panel VDD off |
403 | if (!link->dc->config.edp_no_power_sequencing) |
404 | link->dc->hwss.edp_power_control(link, false); |
405 | } |
406 | } |
407 | |
408 | bool edp_wait_for_t12(struct dc_link *link) |
409 | { |
410 | if (link->connector_signal == SIGNAL_TYPE_EDP && link->dc->hwss.edp_wait_for_T12) { |
411 | link->dc->hwss.edp_wait_for_T12(link); |
412 | |
413 | return true; |
414 | } |
415 | |
416 | return false; |
417 | } |
418 | |
419 | void edp_add_delay_for_T9(struct dc_link *link) |
420 | { |
421 | if (link && link->panel_config.pps.extra_delay_backlight_off > 0) |
422 | fsleep(usecs: link->panel_config.pps.extra_delay_backlight_off * 1000); |
423 | } |
424 | |
425 | bool edp_receiver_ready_T9(struct dc_link *link) |
426 | { |
427 | unsigned int tries = 0; |
428 | unsigned char sinkstatus = 0; |
429 | unsigned char edpRev = 0; |
430 | enum dc_status result = DC_OK; |
431 | |
432 | result = core_link_read_dpcd(link, DP_EDP_DPCD_REV, data: &edpRev, size: sizeof(edpRev)); |
433 | |
434 | /* start from eDP version 1.2, SINK_STAUS indicate the sink is ready.*/ |
435 | if (result == DC_OK && edpRev >= DP_EDP_12) { |
436 | do { |
437 | sinkstatus = 1; |
438 | result = core_link_read_dpcd(link, DP_SINK_STATUS, data: &sinkstatus, size: sizeof(sinkstatus)); |
439 | if (sinkstatus == 0) |
440 | break; |
441 | if (result != DC_OK) |
442 | break; |
443 | udelay(100); //MAx T9 |
444 | } while (++tries < 50); |
445 | } |
446 | |
447 | return result; |
448 | } |
449 | |
450 | bool edp_receiver_ready_T7(struct dc_link *link) |
451 | { |
452 | unsigned char sinkstatus = 0; |
453 | unsigned char edpRev = 0; |
454 | enum dc_status result = DC_OK; |
455 | |
456 | /* use absolute time stamp to constrain max T7*/ |
457 | unsigned long long enter_timestamp = 0; |
458 | unsigned long long finish_timestamp = 0; |
459 | unsigned long long time_taken_in_ns = 0; |
460 | |
461 | result = core_link_read_dpcd(link, DP_EDP_DPCD_REV, data: &edpRev, size: sizeof(edpRev)); |
462 | |
463 | if (result == DC_OK && edpRev >= DP_EDP_12) { |
464 | /* start from eDP version 1.2, SINK_STAUS indicate the sink is ready.*/ |
465 | enter_timestamp = dm_get_timestamp(ctx: link->ctx); |
466 | do { |
467 | sinkstatus = 0; |
468 | result = core_link_read_dpcd(link, DP_SINK_STATUS, data: &sinkstatus, size: sizeof(sinkstatus)); |
469 | if (sinkstatus == 1) |
470 | break; |
471 | if (result != DC_OK) |
472 | break; |
473 | udelay(25); |
474 | finish_timestamp = dm_get_timestamp(ctx: link->ctx); |
475 | time_taken_in_ns = dm_get_elapse_time_in_ns(ctx: link->ctx, current_time_stamp: finish_timestamp, last_time_stamp: enter_timestamp); |
476 | } while (time_taken_in_ns < 50 * 1000000); //MAx T7 is 50ms |
477 | } |
478 | |
479 | if (link && link->panel_config.pps.extra_t7_ms > 0) |
480 | fsleep(usecs: link->panel_config.pps.extra_t7_ms * 1000); |
481 | |
482 | return result; |
483 | } |
484 | |
485 | bool edp_power_alpm_dpcd_enable(struct dc_link *link, bool enable) |
486 | { |
487 | bool ret = false; |
488 | union dpcd_alpm_configuration alpm_config; |
489 | |
490 | if (link->psr_settings.psr_version == DC_PSR_VERSION_SU_1) { |
491 | memset(&alpm_config, 0, sizeof(alpm_config)); |
492 | |
493 | alpm_config.bits.ENABLE = (enable ? true : false); |
494 | ret = dm_helpers_dp_write_dpcd(ctx: link->ctx, link, |
495 | DP_RECEIVER_ALPM_CONFIG, data: &alpm_config.raw, |
496 | size: sizeof(alpm_config.raw)); |
497 | } |
498 | return ret; |
499 | } |
500 | |
501 | static struct pipe_ctx *get_pipe_from_link(const struct dc_link *link) |
502 | { |
503 | int i; |
504 | struct dc *dc = link->ctx->dc; |
505 | struct pipe_ctx *pipe_ctx = NULL; |
506 | |
507 | for (i = 0; i < MAX_PIPES; i++) { |
508 | if (dc->current_state->res_ctx.pipe_ctx[i].stream) { |
509 | if (dc->current_state->res_ctx.pipe_ctx[i].stream->link == link) { |
510 | pipe_ctx = &dc->current_state->res_ctx.pipe_ctx[i]; |
511 | break; |
512 | } |
513 | } |
514 | } |
515 | |
516 | return pipe_ctx; |
517 | } |
518 | |
519 | bool edp_set_backlight_level(const struct dc_link *link, |
520 | uint32_t backlight_pwm_u16_16, |
521 | uint32_t frame_ramp) |
522 | { |
523 | struct dc *dc = link->ctx->dc; |
524 | |
525 | DC_LOGGER_INIT(link->ctx->logger); |
526 | DC_LOG_BACKLIGHT("New Backlight level: %d (0x%X)\n" , |
527 | backlight_pwm_u16_16, backlight_pwm_u16_16); |
528 | |
529 | if (dc_is_embedded_signal(signal: link->connector_signal)) { |
530 | struct pipe_ctx *pipe_ctx = get_pipe_from_link(link); |
531 | |
532 | if (link->panel_cntl) |
533 | link->panel_cntl->stored_backlight_registers.USER_LEVEL = backlight_pwm_u16_16; |
534 | |
535 | if (pipe_ctx) { |
536 | /* Disable brightness ramping when the display is blanked |
537 | * as it can hang the DMCU |
538 | */ |
539 | if (pipe_ctx->plane_state == NULL) |
540 | frame_ramp = 0; |
541 | } else { |
542 | return false; |
543 | } |
544 | |
545 | dc->hwss.set_backlight_level( |
546 | pipe_ctx, |
547 | backlight_pwm_u16_16, |
548 | frame_ramp); |
549 | } |
550 | return true; |
551 | } |
552 | |
553 | bool edp_set_psr_allow_active(struct dc_link *link, const bool *allow_active, |
554 | bool wait, bool force_static, const unsigned int *power_opts) |
555 | { |
556 | struct dc *dc = link->ctx->dc; |
557 | struct dmcu *dmcu = dc->res_pool->dmcu; |
558 | struct dmub_psr *psr = dc->res_pool->psr; |
559 | unsigned int panel_inst; |
560 | |
561 | if (psr == NULL && force_static) |
562 | return false; |
563 | |
564 | if (!dc_get_edp_link_panel_inst(dc, link, inst_out: &panel_inst)) |
565 | return false; |
566 | |
567 | if ((allow_active != NULL) && (*allow_active == true) && (link->type == dc_connection_none)) { |
568 | // Don't enter PSR if panel is not connected |
569 | return false; |
570 | } |
571 | |
572 | /* Set power optimization flag */ |
573 | if (power_opts && link->psr_settings.psr_power_opt != *power_opts) { |
574 | link->psr_settings.psr_power_opt = *power_opts; |
575 | |
576 | if (psr != NULL && link->psr_settings.psr_feature_enabled && psr->funcs->psr_set_power_opt) |
577 | psr->funcs->psr_set_power_opt(psr, link->psr_settings.psr_power_opt, panel_inst); |
578 | } |
579 | |
580 | if (psr != NULL && link->psr_settings.psr_feature_enabled && |
581 | force_static && psr->funcs->psr_force_static) |
582 | psr->funcs->psr_force_static(psr, panel_inst); |
583 | |
584 | /* Enable or Disable PSR */ |
585 | if (allow_active && link->psr_settings.psr_allow_active != *allow_active) { |
586 | link->psr_settings.psr_allow_active = *allow_active; |
587 | |
588 | if (!link->psr_settings.psr_allow_active) |
589 | dc_z10_restore(dc); |
590 | |
591 | if (psr != NULL && link->psr_settings.psr_feature_enabled) { |
592 | psr->funcs->psr_enable(psr, link->psr_settings.psr_allow_active, wait, panel_inst); |
593 | } else if ((dmcu != NULL && dmcu->funcs->is_dmcu_initialized(dmcu)) && |
594 | link->psr_settings.psr_feature_enabled) |
595 | dmcu->funcs->set_psr_enable(dmcu, link->psr_settings.psr_allow_active, wait); |
596 | else |
597 | return false; |
598 | } |
599 | return true; |
600 | } |
601 | |
602 | bool edp_get_psr_state(const struct dc_link *link, enum dc_psr_state *state) |
603 | { |
604 | struct dc *dc = link->ctx->dc; |
605 | struct dmcu *dmcu = dc->res_pool->dmcu; |
606 | struct dmub_psr *psr = dc->res_pool->psr; |
607 | unsigned int panel_inst; |
608 | |
609 | if (!dc_get_edp_link_panel_inst(dc, link, inst_out: &panel_inst)) |
610 | return false; |
611 | |
612 | if (psr != NULL && link->psr_settings.psr_feature_enabled) |
613 | psr->funcs->psr_get_state(psr, state, panel_inst); |
614 | else if (dmcu != NULL && link->psr_settings.psr_feature_enabled) |
615 | dmcu->funcs->get_psr_state(dmcu, state); |
616 | |
617 | return true; |
618 | } |
619 | |
620 | static inline enum physical_phy_id |
621 | transmitter_to_phy_id(struct dc_link *link) |
622 | { |
623 | struct dc_context *dc_ctx = link->ctx; |
624 | enum transmitter transmitter_value = link->link_enc->transmitter; |
625 | |
626 | switch (transmitter_value) { |
627 | case TRANSMITTER_UNIPHY_A: |
628 | return PHYLD_0; |
629 | case TRANSMITTER_UNIPHY_B: |
630 | return PHYLD_1; |
631 | case TRANSMITTER_UNIPHY_C: |
632 | return PHYLD_2; |
633 | case TRANSMITTER_UNIPHY_D: |
634 | return PHYLD_3; |
635 | case TRANSMITTER_UNIPHY_E: |
636 | return PHYLD_4; |
637 | case TRANSMITTER_UNIPHY_F: |
638 | return PHYLD_5; |
639 | case TRANSMITTER_NUTMEG_CRT: |
640 | return PHYLD_6; |
641 | case TRANSMITTER_TRAVIS_CRT: |
642 | return PHYLD_7; |
643 | case TRANSMITTER_TRAVIS_LCD: |
644 | return PHYLD_8; |
645 | case TRANSMITTER_UNIPHY_G: |
646 | return PHYLD_9; |
647 | case TRANSMITTER_COUNT: |
648 | return PHYLD_COUNT; |
649 | case TRANSMITTER_UNKNOWN: |
650 | return PHYLD_UNKNOWN; |
651 | default: |
652 | DC_ERROR("Unknown transmitter value %d\n" , transmitter_value); |
653 | return PHYLD_UNKNOWN; |
654 | } |
655 | } |
656 | |
657 | bool edp_setup_psr(struct dc_link *link, |
658 | const struct dc_stream_state *stream, struct psr_config *psr_config, |
659 | struct psr_context *psr_context) |
660 | { |
661 | struct dc *dc; |
662 | struct dmcu *dmcu; |
663 | struct dmub_psr *psr; |
664 | int i; |
665 | unsigned int panel_inst; |
666 | /* updateSinkPsrDpcdConfig*/ |
667 | union dpcd_psr_configuration psr_configuration; |
668 | union dpcd_sink_active_vtotal_control_mode vtotal_control = {0}; |
669 | |
670 | psr_context->controllerId = CONTROLLER_ID_UNDEFINED; |
671 | |
672 | if (!link) |
673 | return false; |
674 | |
675 | dc = link->ctx->dc; |
676 | dmcu = dc->res_pool->dmcu; |
677 | psr = dc->res_pool->psr; |
678 | |
679 | if (!dmcu && !psr) |
680 | return false; |
681 | |
682 | if (!dc_get_edp_link_panel_inst(dc, link, inst_out: &panel_inst)) |
683 | return false; |
684 | |
685 | |
686 | memset(&psr_configuration, 0, sizeof(psr_configuration)); |
687 | |
688 | psr_configuration.bits.ENABLE = 1; |
689 | psr_configuration.bits.CRC_VERIFICATION = 1; |
690 | psr_configuration.bits.FRAME_CAPTURE_INDICATION = |
691 | psr_config->psr_frame_capture_indication_req; |
692 | |
693 | /* Check for PSR v2*/ |
694 | if (link->psr_settings.psr_version == DC_PSR_VERSION_SU_1) { |
695 | /* For PSR v2 selective update. |
696 | * Indicates whether sink should start capturing |
697 | * immediately following active scan line, |
698 | * or starting with the 2nd active scan line. |
699 | */ |
700 | psr_configuration.bits.LINE_CAPTURE_INDICATION = 0; |
701 | /*For PSR v2, determines whether Sink should generate |
702 | * IRQ_HPD when CRC mismatch is detected. |
703 | */ |
704 | psr_configuration.bits.IRQ_HPD_WITH_CRC_ERROR = 1; |
705 | /* For PSR v2, set the bit when the Source device will |
706 | * be enabling PSR2 operation. |
707 | */ |
708 | psr_configuration.bits.ENABLE_PSR2 = 1; |
709 | /* For PSR v2, the Sink device must be able to receive |
710 | * SU region updates early in the frame time. |
711 | */ |
712 | psr_configuration.bits.EARLY_TRANSPORT_ENABLE = 1; |
713 | } |
714 | |
715 | dm_helpers_dp_write_dpcd( |
716 | ctx: link->ctx, |
717 | link, |
718 | address: 368, |
719 | data: &psr_configuration.raw, |
720 | size: sizeof(psr_configuration.raw)); |
721 | |
722 | if (link->psr_settings.psr_version == DC_PSR_VERSION_SU_1) { |
723 | edp_power_alpm_dpcd_enable(link, enable: true); |
724 | psr_context->su_granularity_required = |
725 | psr_config->su_granularity_required; |
726 | psr_context->su_y_granularity = |
727 | psr_config->su_y_granularity; |
728 | psr_context->line_time_in_us = psr_config->line_time_in_us; |
729 | |
730 | /* linux must be able to expose AMD Source DPCD definition |
731 | * in order to support FreeSync PSR |
732 | */ |
733 | if (link->psr_settings.psr_vtotal_control_support) { |
734 | psr_context->rate_control_caps = psr_config->rate_control_caps; |
735 | vtotal_control.bits.ENABLE = true; |
736 | core_link_write_dpcd(link, DP_SINK_PSR_ACTIVE_VTOTAL_CONTROL_MODE, |
737 | data: &vtotal_control.raw, size: sizeof(vtotal_control.raw)); |
738 | } |
739 | } |
740 | |
741 | psr_context->channel = link->ddc->ddc_pin->hw_info.ddc_channel; |
742 | psr_context->transmitterId = link->link_enc->transmitter; |
743 | psr_context->engineId = link->link_enc->preferred_engine; |
744 | |
745 | for (i = 0; i < MAX_PIPES; i++) { |
746 | if (dc->current_state->res_ctx.pipe_ctx[i].stream |
747 | == stream) { |
748 | /* dmcu -1 for all controller id values, |
749 | * therefore +1 here |
750 | */ |
751 | psr_context->controllerId = |
752 | dc->current_state->res_ctx. |
753 | pipe_ctx[i].stream_res.tg->inst + 1; |
754 | break; |
755 | } |
756 | } |
757 | |
758 | /* Hardcoded for now. Can be Pcie or Uniphy (or Unknown)*/ |
759 | psr_context->phyType = PHY_TYPE_UNIPHY; |
760 | /*PhyId is associated with the transmitter id*/ |
761 | psr_context->smuPhyId = transmitter_to_phy_id(link); |
762 | |
763 | psr_context->crtcTimingVerticalTotal = stream->timing.v_total; |
764 | psr_context->vsync_rate_hz = div64_u64(dividend: div64_u64(dividend: (stream-> |
765 | timing.pix_clk_100hz * 100), |
766 | divisor: stream->timing.v_total), |
767 | divisor: stream->timing.h_total); |
768 | |
769 | psr_context->psrSupportedDisplayConfig = true; |
770 | psr_context->psrExitLinkTrainingRequired = |
771 | psr_config->psr_exit_link_training_required; |
772 | psr_context->sdpTransmitLineNumDeadline = |
773 | psr_config->psr_sdp_transmit_line_num_deadline; |
774 | psr_context->psrFrameCaptureIndicationReq = |
775 | psr_config->psr_frame_capture_indication_req; |
776 | |
777 | psr_context->skipPsrWaitForPllLock = 0; /* only = 1 in KV */ |
778 | |
779 | psr_context->numberOfControllers = |
780 | link->dc->res_pool->timing_generator_count; |
781 | |
782 | psr_context->rfb_update_auto_en = true; |
783 | |
784 | /* 2 frames before enter PSR. */ |
785 | psr_context->timehyst_frames = 2; |
786 | /* half a frame |
787 | * (units in 100 lines, i.e. a value of 1 represents 100 lines) |
788 | */ |
789 | psr_context->hyst_lines = stream->timing.v_total / 2 / 100; |
790 | psr_context->aux_repeats = 10; |
791 | |
792 | psr_context->psr_level.u32all = 0; |
793 | |
794 | /*skip power down the single pipe since it blocks the cstate*/ |
795 | if (link->ctx->asic_id.chip_family >= FAMILY_RV) { |
796 | switch (link->ctx->asic_id.chip_family) { |
797 | case FAMILY_YELLOW_CARP: |
798 | case AMDGPU_FAMILY_GC_10_3_6: |
799 | case AMDGPU_FAMILY_GC_11_0_1: |
800 | if (dc->debug.disable_z10 || dc->debug.psr_skip_crtc_disable) |
801 | psr_context->psr_level.bits.SKIP_CRTC_DISABLE = true; |
802 | break; |
803 | default: |
804 | psr_context->psr_level.bits.SKIP_CRTC_DISABLE = true; |
805 | break; |
806 | } |
807 | } |
808 | |
809 | /* SMU will perform additional powerdown sequence. |
810 | * For unsupported ASICs, set psr_level flag to skip PSR |
811 | * static screen notification to SMU. |
812 | * (Always set for DAL2, did not check ASIC) |
813 | */ |
814 | psr_context->allow_smu_optimizations = psr_config->allow_smu_optimizations; |
815 | psr_context->allow_multi_disp_optimizations = psr_config->allow_multi_disp_optimizations; |
816 | |
817 | /* Complete PSR entry before aborting to prevent intermittent |
818 | * freezes on certain eDPs |
819 | */ |
820 | psr_context->psr_level.bits.DISABLE_PSR_ENTRY_ABORT = 1; |
821 | |
822 | /* Disable ALPM first for compatible non-ALPM panel now */ |
823 | psr_context->psr_level.bits.DISABLE_ALPM = 0; |
824 | psr_context->psr_level.bits.ALPM_DEFAULT_PD_MODE = 1; |
825 | |
826 | /* Controls additional delay after remote frame capture before |
827 | * continuing power down, default = 0 |
828 | */ |
829 | psr_context->frame_delay = 0; |
830 | |
831 | psr_context->dsc_slice_height = psr_config->dsc_slice_height; |
832 | |
833 | if (psr) { |
834 | link->psr_settings.psr_feature_enabled = psr->funcs->psr_copy_settings(psr, |
835 | link, psr_context, panel_inst); |
836 | link->psr_settings.psr_power_opt = 0; |
837 | link->psr_settings.psr_allow_active = 0; |
838 | } else { |
839 | link->psr_settings.psr_feature_enabled = dmcu->funcs->setup_psr(dmcu, link, psr_context); |
840 | } |
841 | |
842 | /* psr_enabled == 0 indicates setup_psr did not succeed, but this |
843 | * should not happen since firmware should be running at this point |
844 | */ |
845 | if (link->psr_settings.psr_feature_enabled == 0) |
846 | ASSERT(0); |
847 | |
848 | return true; |
849 | |
850 | } |
851 | |
852 | void edp_get_psr_residency(const struct dc_link *link, uint32_t *residency) |
853 | { |
854 | struct dc *dc = link->ctx->dc; |
855 | struct dmub_psr *psr = dc->res_pool->psr; |
856 | unsigned int panel_inst; |
857 | |
858 | if (!dc_get_edp_link_panel_inst(dc, link, inst_out: &panel_inst)) |
859 | return; |
860 | |
861 | // PSR residency measurements only supported on DMCUB |
862 | if (psr != NULL && link->psr_settings.psr_feature_enabled) |
863 | psr->funcs->psr_get_residency(psr, residency, panel_inst); |
864 | else |
865 | *residency = 0; |
866 | } |
867 | bool edp_set_sink_vtotal_in_psr_active(const struct dc_link *link, uint16_t psr_vtotal_idle, uint16_t psr_vtotal_su) |
868 | { |
869 | struct dc *dc = link->ctx->dc; |
870 | struct dmub_psr *psr = dc->res_pool->psr; |
871 | |
872 | if (psr == NULL || !link->psr_settings.psr_feature_enabled || !link->psr_settings.psr_vtotal_control_support) |
873 | return false; |
874 | |
875 | psr->funcs->psr_set_sink_vtotal_in_psr_active(psr, psr_vtotal_idle, psr_vtotal_su); |
876 | |
877 | return true; |
878 | } |
879 | |
880 | bool edp_set_replay_allow_active(struct dc_link *link, const bool *allow_active, |
881 | bool wait, bool force_static, const unsigned int *power_opts) |
882 | { |
883 | struct dc *dc = link->ctx->dc; |
884 | struct dmub_replay *replay = dc->res_pool->replay; |
885 | unsigned int panel_inst; |
886 | |
887 | if (replay == NULL && force_static) |
888 | return false; |
889 | |
890 | if (!dc_get_edp_link_panel_inst(dc, link, inst_out: &panel_inst)) |
891 | return false; |
892 | |
893 | /* Set power optimization flag */ |
894 | if (power_opts && link->replay_settings.replay_power_opt_active != *power_opts) { |
895 | if (replay != NULL && link->replay_settings.replay_feature_enabled && |
896 | replay->funcs->replay_set_power_opt) { |
897 | replay->funcs->replay_set_power_opt(replay, *power_opts, panel_inst); |
898 | link->replay_settings.replay_power_opt_active = *power_opts; |
899 | } |
900 | } |
901 | |
902 | /* Activate or deactivate Replay */ |
903 | if (allow_active && link->replay_settings.replay_allow_active != *allow_active) { |
904 | // TODO: Handle mux change case if force_static is set |
905 | // If force_static is set, just change the replay_allow_active state directly |
906 | if (replay != NULL && link->replay_settings.replay_feature_enabled) |
907 | replay->funcs->replay_enable(replay, *allow_active, wait, panel_inst); |
908 | link->replay_settings.replay_allow_active = *allow_active; |
909 | } |
910 | |
911 | return true; |
912 | } |
913 | |
914 | bool edp_get_replay_state(const struct dc_link *link, uint64_t *state) |
915 | { |
916 | struct dc *dc = link->ctx->dc; |
917 | struct dmub_replay *replay = dc->res_pool->replay; |
918 | unsigned int panel_inst; |
919 | enum replay_state pr_state = REPLAY_STATE_0; |
920 | |
921 | if (!dc_get_edp_link_panel_inst(dc, link, inst_out: &panel_inst)) |
922 | return false; |
923 | |
924 | if (replay != NULL && link->replay_settings.replay_feature_enabled) |
925 | replay->funcs->replay_get_state(replay, &pr_state, panel_inst); |
926 | *state = pr_state; |
927 | |
928 | return true; |
929 | } |
930 | |
931 | bool edp_setup_replay(struct dc_link *link, const struct dc_stream_state *stream) |
932 | { |
933 | /* To-do: Setup Replay */ |
934 | struct dc *dc; |
935 | struct dmub_replay *replay; |
936 | int i; |
937 | unsigned int panel_inst; |
938 | struct replay_context replay_context = { 0 }; |
939 | unsigned int lineTimeInNs = 0; |
940 | |
941 | |
942 | union replay_enable_and_configuration replay_config; |
943 | |
944 | union dpcd_alpm_configuration alpm_config; |
945 | |
946 | replay_context.controllerId = CONTROLLER_ID_UNDEFINED; |
947 | |
948 | if (!link) |
949 | return false; |
950 | |
951 | dc = link->ctx->dc; |
952 | |
953 | replay = dc->res_pool->replay; |
954 | |
955 | if (!replay) |
956 | return false; |
957 | |
958 | if (!dc_get_edp_link_panel_inst(dc, link, inst_out: &panel_inst)) |
959 | return false; |
960 | |
961 | replay_context.aux_inst = link->ddc->ddc_pin->hw_info.ddc_channel; |
962 | replay_context.digbe_inst = link->link_enc->transmitter; |
963 | replay_context.digfe_inst = link->link_enc->preferred_engine; |
964 | |
965 | for (i = 0; i < MAX_PIPES; i++) { |
966 | if (dc->current_state->res_ctx.pipe_ctx[i].stream |
967 | == stream) { |
968 | /* dmcu -1 for all controller id values, |
969 | * therefore +1 here |
970 | */ |
971 | replay_context.controllerId = |
972 | dc->current_state->res_ctx.pipe_ctx[i].stream_res.tg->inst + 1; |
973 | break; |
974 | } |
975 | } |
976 | |
977 | lineTimeInNs = |
978 | ((stream->timing.h_total * 1000000) / |
979 | (stream->timing.pix_clk_100hz / 10)) + 1; |
980 | |
981 | replay_context.line_time_in_ns = lineTimeInNs; |
982 | |
983 | link->replay_settings.replay_feature_enabled = |
984 | replay->funcs->replay_copy_settings(replay, link, &replay_context, panel_inst); |
985 | if (link->replay_settings.replay_feature_enabled) { |
986 | |
987 | replay_config.bits.FREESYNC_PANEL_REPLAY_MODE = 1; |
988 | replay_config.bits.TIMING_DESYNC_ERROR_VERIFICATION = |
989 | link->replay_settings.config.replay_timing_sync_supported; |
990 | replay_config.bits.STATE_TRANSITION_ERROR_DETECTION = 1; |
991 | dm_helpers_dp_write_dpcd(ctx: link->ctx, link, |
992 | DP_SINK_PR_ENABLE_AND_CONFIGURATION, |
993 | data: (uint8_t *)&(replay_config.raw), size: sizeof(uint8_t)); |
994 | |
995 | memset(&alpm_config, 0, sizeof(alpm_config)); |
996 | alpm_config.bits.ENABLE = 1; |
997 | dm_helpers_dp_write_dpcd( |
998 | ctx: link->ctx, |
999 | link, |
1000 | DP_RECEIVER_ALPM_CONFIG, |
1001 | data: &alpm_config.raw, |
1002 | size: sizeof(alpm_config.raw)); |
1003 | } |
1004 | return true; |
1005 | } |
1006 | |
1007 | /* |
1008 | * This is general Interface for Replay to set an 32 bit variable to dmub |
1009 | * replay_FW_Message_type: Indicates which instruction or variable pass to DMUB |
1010 | * cmd_data: Value of the config. |
1011 | */ |
1012 | bool edp_send_replay_cmd(struct dc_link *link, |
1013 | enum replay_FW_Message_type msg, |
1014 | union dmub_replay_cmd_set *cmd_data) |
1015 | { |
1016 | struct dc *dc = link->ctx->dc; |
1017 | struct dmub_replay *replay = dc->res_pool->replay; |
1018 | unsigned int panel_inst; |
1019 | |
1020 | if (!replay) |
1021 | return false; |
1022 | |
1023 | DC_LOGGER_INIT(link->ctx->logger); |
1024 | |
1025 | if (dc_get_edp_link_panel_inst(dc, link, inst_out: &panel_inst)) |
1026 | cmd_data->panel_inst = panel_inst; |
1027 | else { |
1028 | DC_LOG_DC("%s(): get edp panel inst fail " , __func__); |
1029 | return false; |
1030 | } |
1031 | |
1032 | replay->funcs->replay_send_cmd(replay, msg, cmd_data); |
1033 | |
1034 | return true; |
1035 | } |
1036 | |
1037 | bool edp_set_coasting_vtotal(struct dc_link *link, uint32_t coasting_vtotal) |
1038 | { |
1039 | struct dc *dc = link->ctx->dc; |
1040 | struct dmub_replay *replay = dc->res_pool->replay; |
1041 | unsigned int panel_inst; |
1042 | |
1043 | if (!replay) |
1044 | return false; |
1045 | |
1046 | if (!dc_get_edp_link_panel_inst(dc, link, inst_out: &panel_inst)) |
1047 | return false; |
1048 | |
1049 | if (coasting_vtotal && link->replay_settings.coasting_vtotal != coasting_vtotal) { |
1050 | replay->funcs->replay_set_coasting_vtotal(replay, coasting_vtotal, panel_inst); |
1051 | link->replay_settings.coasting_vtotal = coasting_vtotal; |
1052 | } |
1053 | |
1054 | return true; |
1055 | } |
1056 | |
1057 | bool edp_replay_residency(const struct dc_link *link, |
1058 | unsigned int *residency, const bool is_start, const bool is_alpm) |
1059 | { |
1060 | struct dc *dc = link->ctx->dc; |
1061 | struct dmub_replay *replay = dc->res_pool->replay; |
1062 | unsigned int panel_inst; |
1063 | |
1064 | if (!dc_get_edp_link_panel_inst(dc, link, inst_out: &panel_inst)) |
1065 | return false; |
1066 | |
1067 | if (replay != NULL && link->replay_settings.replay_feature_enabled) |
1068 | replay->funcs->replay_residency(replay, panel_inst, residency, is_start, is_alpm); |
1069 | else |
1070 | *residency = 0; |
1071 | |
1072 | return true; |
1073 | } |
1074 | |
1075 | bool edp_set_replay_power_opt_and_coasting_vtotal(struct dc_link *link, |
1076 | const unsigned int *power_opts, uint32_t coasting_vtotal) |
1077 | { |
1078 | struct dc *dc = link->ctx->dc; |
1079 | struct dmub_replay *replay = dc->res_pool->replay; |
1080 | unsigned int panel_inst; |
1081 | |
1082 | if (!dc_get_edp_link_panel_inst(dc, link, inst_out: &panel_inst)) |
1083 | return false; |
1084 | |
1085 | /* Only both power and coasting vtotal changed, this func could return true */ |
1086 | if (power_opts && link->replay_settings.replay_power_opt_active != *power_opts && |
1087 | coasting_vtotal && link->replay_settings.coasting_vtotal != coasting_vtotal) { |
1088 | if (link->replay_settings.replay_feature_enabled && |
1089 | replay->funcs->replay_set_power_opt_and_coasting_vtotal) { |
1090 | replay->funcs->replay_set_power_opt_and_coasting_vtotal(replay, |
1091 | *power_opts, panel_inst, coasting_vtotal); |
1092 | link->replay_settings.replay_power_opt_active = *power_opts; |
1093 | link->replay_settings.coasting_vtotal = coasting_vtotal; |
1094 | } else |
1095 | return false; |
1096 | } else |
1097 | return false; |
1098 | |
1099 | return true; |
1100 | } |
1101 | |
1102 | static struct abm *get_abm_from_stream_res(const struct dc_link *link) |
1103 | { |
1104 | int i; |
1105 | struct dc *dc = link->ctx->dc; |
1106 | struct abm *abm = NULL; |
1107 | |
1108 | for (i = 0; i < MAX_PIPES; i++) { |
1109 | struct pipe_ctx pipe_ctx = dc->current_state->res_ctx.pipe_ctx[i]; |
1110 | struct dc_stream_state *stream = pipe_ctx.stream; |
1111 | |
1112 | if (stream && stream->link == link) { |
1113 | abm = pipe_ctx.stream_res.abm; |
1114 | break; |
1115 | } |
1116 | } |
1117 | return abm; |
1118 | } |
1119 | |
1120 | int edp_get_backlight_level(const struct dc_link *link) |
1121 | { |
1122 | struct abm *abm = get_abm_from_stream_res(link); |
1123 | struct panel_cntl *panel_cntl = link->panel_cntl; |
1124 | struct dc *dc = link->ctx->dc; |
1125 | struct dmcu *dmcu = dc->res_pool->dmcu; |
1126 | bool fw_set_brightness = true; |
1127 | |
1128 | if (dmcu) |
1129 | fw_set_brightness = dmcu->funcs->is_dmcu_initialized(dmcu); |
1130 | |
1131 | if (!fw_set_brightness && panel_cntl->funcs->get_current_backlight) |
1132 | return panel_cntl->funcs->get_current_backlight(panel_cntl); |
1133 | else if (abm != NULL && abm->funcs->get_current_backlight != NULL) |
1134 | return (int) abm->funcs->get_current_backlight(abm); |
1135 | else |
1136 | return DC_ERROR_UNEXPECTED; |
1137 | } |
1138 | |
1139 | int edp_get_target_backlight_pwm(const struct dc_link *link) |
1140 | { |
1141 | struct abm *abm = get_abm_from_stream_res(link); |
1142 | |
1143 | if (abm == NULL || abm->funcs->get_target_backlight == NULL) |
1144 | return DC_ERROR_UNEXPECTED; |
1145 | |
1146 | return (int) abm->funcs->get_target_backlight(abm); |
1147 | } |
1148 | |