1 | /* |
2 | * Copyright 2012-15 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 | |
27 | #include "reg_helper.h" |
28 | |
29 | #include "core_types.h" |
30 | #include "link_encoder.h" |
31 | #include "dcn31_dio_link_encoder.h" |
32 | #include "stream_encoder.h" |
33 | #include "dc_bios_types.h" |
34 | |
35 | #include "gpio_service_interface.h" |
36 | |
37 | #include "link_enc_cfg.h" |
38 | #include "dc_dmub_srv.h" |
39 | #include "dal_asic_id.h" |
40 | #include "link.h" |
41 | |
42 | #define CTX \ |
43 | enc10->base.ctx |
44 | #define DC_LOGGER \ |
45 | enc10->base.ctx->logger |
46 | |
47 | #define REG(reg)\ |
48 | (enc10->link_regs->reg) |
49 | |
50 | #undef FN |
51 | #define FN(reg_name, field_name) \ |
52 | enc10->link_shift->field_name, enc10->link_mask->field_name |
53 | |
54 | #define IND_REG(index) \ |
55 | (enc10->link_regs->index) |
56 | |
57 | #define AUX_REG(reg)\ |
58 | (enc10->aux_regs->reg) |
59 | |
60 | #define AUX_REG_READ(reg_name) \ |
61 | dm_read_reg(CTX, AUX_REG(reg_name)) |
62 | |
63 | #define AUX_REG_WRITE(reg_name, val) \ |
64 | dm_write_reg(CTX, AUX_REG(reg_name), val) |
65 | |
66 | #ifndef MIN |
67 | #define MIN(X, Y) ((X) < (Y) ? (X) : (Y)) |
68 | #endif |
69 | |
70 | static uint8_t phy_id_from_transmitter(enum transmitter t) |
71 | { |
72 | uint8_t phy_id; |
73 | |
74 | switch (t) { |
75 | case TRANSMITTER_UNIPHY_A: |
76 | phy_id = 0; |
77 | break; |
78 | case TRANSMITTER_UNIPHY_B: |
79 | phy_id = 1; |
80 | break; |
81 | case TRANSMITTER_UNIPHY_C: |
82 | phy_id = 2; |
83 | break; |
84 | case TRANSMITTER_UNIPHY_D: |
85 | phy_id = 3; |
86 | break; |
87 | case TRANSMITTER_UNIPHY_E: |
88 | phy_id = 4; |
89 | break; |
90 | case TRANSMITTER_UNIPHY_F: |
91 | phy_id = 5; |
92 | break; |
93 | case TRANSMITTER_UNIPHY_G: |
94 | phy_id = 6; |
95 | break; |
96 | default: |
97 | phy_id = 0; |
98 | break; |
99 | } |
100 | return phy_id; |
101 | } |
102 | |
103 | static bool has_query_dp_alt(struct link_encoder *enc) |
104 | { |
105 | struct dc_dmub_srv *dc_dmub_srv = enc->ctx->dmub_srv; |
106 | |
107 | if (enc->ctx->dce_version >= DCN_VERSION_3_15) |
108 | return true; |
109 | |
110 | /* Supports development firmware and firmware >= 4.0.11 */ |
111 | return dc_dmub_srv && |
112 | !(dc_dmub_srv->dmub->fw_version >= DMUB_FW_VERSION(4, 0, 0) && |
113 | dc_dmub_srv->dmub->fw_version <= DMUB_FW_VERSION(4, 0, 10)); |
114 | } |
115 | |
116 | static bool query_dp_alt_from_dmub(struct link_encoder *enc, |
117 | union dmub_rb_cmd *cmd) |
118 | { |
119 | struct dcn10_link_encoder *enc10 = TO_DCN10_LINK_ENC(enc); |
120 | |
121 | memset(cmd, 0, sizeof(*cmd)); |
122 | cmd->query_dp_alt.header.type = DMUB_CMD__VBIOS; |
123 | cmd->query_dp_alt.header.sub_type = |
124 | DMUB_CMD__VBIOS_TRANSMITTER_QUERY_DP_ALT; |
125 | cmd->query_dp_alt.header.payload_bytes = sizeof(cmd->query_dp_alt.data); |
126 | cmd->query_dp_alt.data.phy_id = phy_id_from_transmitter(t: enc10->base.transmitter); |
127 | |
128 | if (!dc_wake_and_execute_dmub_cmd(ctx: enc->ctx, cmd, wait_type: DM_DMUB_WAIT_TYPE_WAIT_WITH_REPLY)) |
129 | return false; |
130 | |
131 | return true; |
132 | } |
133 | |
134 | void dcn31_link_encoder_set_dio_phy_mux( |
135 | struct link_encoder *enc, |
136 | enum encoder_type_select sel, |
137 | uint32_t hpo_inst) |
138 | { |
139 | struct dcn10_link_encoder *enc10 = TO_DCN10_LINK_ENC(enc); |
140 | |
141 | switch (enc->transmitter) { |
142 | case TRANSMITTER_UNIPHY_A: |
143 | if (sel == ENCODER_TYPE_HDMI_FRL) |
144 | REG_UPDATE(DIO_LINKA_CNTL, |
145 | HPO_HDMI_ENC_SEL, hpo_inst); |
146 | else if (sel == ENCODER_TYPE_DP_128B132B) |
147 | REG_UPDATE(DIO_LINKA_CNTL, |
148 | HPO_DP_ENC_SEL, hpo_inst); |
149 | REG_UPDATE(DIO_LINKA_CNTL, |
150 | ENC_TYPE_SEL, sel); |
151 | break; |
152 | case TRANSMITTER_UNIPHY_B: |
153 | if (sel == ENCODER_TYPE_HDMI_FRL) |
154 | REG_UPDATE(DIO_LINKB_CNTL, |
155 | HPO_HDMI_ENC_SEL, hpo_inst); |
156 | else if (sel == ENCODER_TYPE_DP_128B132B) |
157 | REG_UPDATE(DIO_LINKB_CNTL, |
158 | HPO_DP_ENC_SEL, hpo_inst); |
159 | REG_UPDATE(DIO_LINKB_CNTL, |
160 | ENC_TYPE_SEL, sel); |
161 | break; |
162 | case TRANSMITTER_UNIPHY_C: |
163 | if (sel == ENCODER_TYPE_HDMI_FRL) |
164 | REG_UPDATE(DIO_LINKC_CNTL, |
165 | HPO_HDMI_ENC_SEL, hpo_inst); |
166 | else if (sel == ENCODER_TYPE_DP_128B132B) |
167 | REG_UPDATE(DIO_LINKC_CNTL, |
168 | HPO_DP_ENC_SEL, hpo_inst); |
169 | REG_UPDATE(DIO_LINKC_CNTL, |
170 | ENC_TYPE_SEL, sel); |
171 | break; |
172 | case TRANSMITTER_UNIPHY_D: |
173 | if (sel == ENCODER_TYPE_HDMI_FRL) |
174 | REG_UPDATE(DIO_LINKD_CNTL, |
175 | HPO_HDMI_ENC_SEL, hpo_inst); |
176 | else if (sel == ENCODER_TYPE_DP_128B132B) |
177 | REG_UPDATE(DIO_LINKD_CNTL, |
178 | HPO_DP_ENC_SEL, hpo_inst); |
179 | REG_UPDATE(DIO_LINKD_CNTL, |
180 | ENC_TYPE_SEL, sel); |
181 | break; |
182 | case TRANSMITTER_UNIPHY_E: |
183 | if (sel == ENCODER_TYPE_HDMI_FRL) |
184 | REG_UPDATE(DIO_LINKE_CNTL, |
185 | HPO_HDMI_ENC_SEL, hpo_inst); |
186 | else if (sel == ENCODER_TYPE_DP_128B132B) |
187 | REG_UPDATE(DIO_LINKE_CNTL, |
188 | HPO_DP_ENC_SEL, hpo_inst); |
189 | REG_UPDATE(DIO_LINKE_CNTL, |
190 | ENC_TYPE_SEL, sel); |
191 | break; |
192 | case TRANSMITTER_UNIPHY_F: |
193 | if (sel == ENCODER_TYPE_HDMI_FRL) |
194 | REG_UPDATE(DIO_LINKF_CNTL, |
195 | HPO_HDMI_ENC_SEL, hpo_inst); |
196 | else if (sel == ENCODER_TYPE_DP_128B132B) |
197 | REG_UPDATE(DIO_LINKF_CNTL, |
198 | HPO_DP_ENC_SEL, hpo_inst); |
199 | REG_UPDATE(DIO_LINKF_CNTL, |
200 | ENC_TYPE_SEL, sel); |
201 | break; |
202 | default: |
203 | /* Do nothing */ |
204 | break; |
205 | } |
206 | } |
207 | |
208 | static void enc31_hw_init(struct link_encoder *enc) |
209 | { |
210 | struct dcn10_link_encoder *enc10 = TO_DCN10_LINK_ENC(enc); |
211 | |
212 | /* |
213 | 00 - DP_AUX_DPHY_RX_DETECTION_THRESHOLD__1to2 : 1/2 |
214 | 01 - DP_AUX_DPHY_RX_DETECTION_THRESHOLD__3to4 : 3/4 |
215 | 02 - DP_AUX_DPHY_RX_DETECTION_THRESHOLD__7to8 : 7/8 |
216 | 03 - DP_AUX_DPHY_RX_DETECTION_THRESHOLD__15to16 : 15/16 |
217 | 04 - DP_AUX_DPHY_RX_DETECTION_THRESHOLD__31to32 : 31/32 |
218 | 05 - DP_AUX_DPHY_RX_DETECTION_THRESHOLD__63to64 : 63/64 |
219 | 06 - DP_AUX_DPHY_RX_DETECTION_THRESHOLD__127to128 : 127/128 |
220 | 07 - DP_AUX_DPHY_RX_DETECTION_THRESHOLD__255to256 : 255/256 |
221 | */ |
222 | |
223 | /* |
224 | AUX_REG_UPDATE_5(AUX_DPHY_RX_CONTROL0, |
225 | AUX_RX_START_WINDOW = 1 [6:4] |
226 | AUX_RX_RECEIVE_WINDOW = 1 default is 2 [10:8] |
227 | AUX_RX_HALF_SYM_DETECT_LEN = 1 [13:12] default is 1 |
228 | AUX_RX_TRANSITION_FILTER_EN = 1 [16] default is 1 |
229 | AUX_RX_ALLOW_BELOW_THRESHOLD_PHASE_DETECT [17] is 0 default is 0 |
230 | AUX_RX_ALLOW_BELOW_THRESHOLD_START [18] is 1 default is 1 |
231 | AUX_RX_ALLOW_BELOW_THRESHOLD_STOP [19] is 1 default is 1 |
232 | AUX_RX_PHASE_DETECT_LEN, [21,20] = 0x3 default is 3 |
233 | AUX_RX_DETECTION_THRESHOLD [30:28] = 1 |
234 | */ |
235 | // dmub will read AUX_DPHY_RX_CONTROL0/AUX_DPHY_TX_CONTROL from vbios table in dp_aux_init |
236 | |
237 | //AUX_DPHY_TX_REF_CONTROL'AUX_TX_REF_DIV HW default is 0x32; |
238 | // Set AUX_TX_REF_DIV Divider to generate 2 MHz reference from refclk |
239 | // 27MHz -> 0xd |
240 | // 100MHz -> 0x32 |
241 | // 48MHz -> 0x18 |
242 | |
243 | // Set TMDS_CTL0 to 1. This is a legacy setting. |
244 | REG_UPDATE(TMDS_CTL_BITS, TMDS_CTL0, 1); |
245 | |
246 | dcn10_aux_initialize(enc10); |
247 | } |
248 | |
249 | static const struct link_encoder_funcs dcn31_link_enc_funcs = { |
250 | .read_state = link_enc2_read_state, |
251 | .validate_output_with_stream = |
252 | dcn30_link_encoder_validate_output_with_stream, |
253 | .hw_init = enc31_hw_init, |
254 | .setup = dcn10_link_encoder_setup, |
255 | .enable_tmds_output = dcn10_link_encoder_enable_tmds_output, |
256 | .enable_dp_output = dcn31_link_encoder_enable_dp_output, |
257 | .enable_dp_mst_output = dcn31_link_encoder_enable_dp_mst_output, |
258 | .disable_output = dcn31_link_encoder_disable_output, |
259 | .dp_set_lane_settings = dcn10_link_encoder_dp_set_lane_settings, |
260 | .dp_set_phy_pattern = dcn10_link_encoder_dp_set_phy_pattern, |
261 | .update_mst_stream_allocation_table = |
262 | dcn10_link_encoder_update_mst_stream_allocation_table, |
263 | .psr_program_dp_dphy_fast_training = |
264 | dcn10_psr_program_dp_dphy_fast_training, |
265 | .psr_program_secondary_packet = dcn10_psr_program_secondary_packet, |
266 | .connect_dig_be_to_fe = dcn10_link_encoder_connect_dig_be_to_fe, |
267 | .enable_hpd = dcn10_link_encoder_enable_hpd, |
268 | .disable_hpd = dcn10_link_encoder_disable_hpd, |
269 | .is_dig_enabled = dcn10_is_dig_enabled, |
270 | .destroy = dcn10_link_encoder_destroy, |
271 | .fec_set_enable = enc2_fec_set_enable, |
272 | .fec_set_ready = enc2_fec_set_ready, |
273 | .fec_is_active = enc2_fec_is_active, |
274 | .get_dig_frontend = dcn10_get_dig_frontend, |
275 | .get_dig_mode = dcn10_get_dig_mode, |
276 | .is_in_alt_mode = dcn31_link_encoder_is_in_alt_mode, |
277 | .get_max_link_cap = dcn31_link_encoder_get_max_link_cap, |
278 | .set_dio_phy_mux = dcn31_link_encoder_set_dio_phy_mux, |
279 | }; |
280 | |
281 | void dcn31_link_encoder_construct( |
282 | struct dcn20_link_encoder *enc20, |
283 | const struct encoder_init_data *init_data, |
284 | const struct encoder_feature_support *enc_features, |
285 | const struct dcn10_link_enc_registers *link_regs, |
286 | const struct dcn10_link_enc_aux_registers *aux_regs, |
287 | const struct dcn10_link_enc_hpd_registers *hpd_regs, |
288 | const struct dcn10_link_enc_shift *link_shift, |
289 | const struct dcn10_link_enc_mask *link_mask) |
290 | { |
291 | struct bp_encoder_cap_info bp_cap_info = {0}; |
292 | const struct dc_vbios_funcs *bp_funcs = init_data->ctx->dc_bios->funcs; |
293 | enum bp_result result = BP_RESULT_OK; |
294 | struct dcn10_link_encoder *enc10 = &enc20->enc10; |
295 | |
296 | enc10->base.funcs = &dcn31_link_enc_funcs; |
297 | enc10->base.ctx = init_data->ctx; |
298 | enc10->base.id = init_data->encoder; |
299 | |
300 | enc10->base.hpd_source = init_data->hpd_source; |
301 | enc10->base.connector = init_data->connector; |
302 | |
303 | enc10->base.preferred_engine = ENGINE_ID_UNKNOWN; |
304 | |
305 | enc10->base.features = *enc_features; |
306 | |
307 | enc10->base.transmitter = init_data->transmitter; |
308 | |
309 | /* set the flag to indicate whether driver poll the I2C data pin |
310 | * while doing the DP sink detect |
311 | */ |
312 | |
313 | /* if (dal_adapter_service_is_feature_supported(as, |
314 | FEATURE_DP_SINK_DETECT_POLL_DATA_PIN)) |
315 | enc10->base.features.flags.bits. |
316 | DP_SINK_DETECT_POLL_DATA_PIN = true;*/ |
317 | |
318 | enc10->base.output_signals = |
319 | SIGNAL_TYPE_DVI_SINGLE_LINK | |
320 | SIGNAL_TYPE_DVI_DUAL_LINK | |
321 | SIGNAL_TYPE_LVDS | |
322 | SIGNAL_TYPE_DISPLAY_PORT | |
323 | SIGNAL_TYPE_DISPLAY_PORT_MST | |
324 | SIGNAL_TYPE_EDP | |
325 | SIGNAL_TYPE_HDMI_TYPE_A; |
326 | |
327 | /* For DCE 8.0 and 8.1, by design, UNIPHY is hardwired to DIG_BE. |
328 | * SW always assign DIG_FE 1:1 mapped to DIG_FE for non-MST UNIPHY. |
329 | * SW assign DIG_FE to non-MST UNIPHY first and MST last. So prefer |
330 | * DIG is per UNIPHY and used by SST DP, eDP, HDMI, DVI and LVDS. |
331 | * Prefer DIG assignment is decided by board design. |
332 | * For DCE 8.0, there are only max 6 UNIPHYs, we assume board design |
333 | * and VBIOS will filter out 7 UNIPHY for DCE 8.0. |
334 | * By this, adding DIGG should not hurt DCE 8.0. |
335 | * This will let DCE 8.1 share DCE 8.0 as much as possible |
336 | */ |
337 | |
338 | enc10->link_regs = link_regs; |
339 | enc10->aux_regs = aux_regs; |
340 | enc10->hpd_regs = hpd_regs; |
341 | enc10->link_shift = link_shift; |
342 | enc10->link_mask = link_mask; |
343 | |
344 | switch (enc10->base.transmitter) { |
345 | case TRANSMITTER_UNIPHY_A: |
346 | enc10->base.preferred_engine = ENGINE_ID_DIGA; |
347 | break; |
348 | case TRANSMITTER_UNIPHY_B: |
349 | enc10->base.preferred_engine = ENGINE_ID_DIGB; |
350 | break; |
351 | case TRANSMITTER_UNIPHY_C: |
352 | enc10->base.preferred_engine = ENGINE_ID_DIGC; |
353 | break; |
354 | case TRANSMITTER_UNIPHY_D: |
355 | enc10->base.preferred_engine = ENGINE_ID_DIGD; |
356 | break; |
357 | case TRANSMITTER_UNIPHY_E: |
358 | enc10->base.preferred_engine = ENGINE_ID_DIGE; |
359 | break; |
360 | case TRANSMITTER_UNIPHY_F: |
361 | enc10->base.preferred_engine = ENGINE_ID_DIGF; |
362 | break; |
363 | default: |
364 | ASSERT_CRITICAL(false); |
365 | enc10->base.preferred_engine = ENGINE_ID_UNKNOWN; |
366 | } |
367 | |
368 | /* default to one to mirror Windows behavior */ |
369 | enc10->base.features.flags.bits.HDMI_6GB_EN = 1; |
370 | |
371 | result = bp_funcs->get_encoder_cap_info(enc10->base.ctx->dc_bios, |
372 | enc10->base.id, &bp_cap_info); |
373 | |
374 | /* Override features with DCE-specific values */ |
375 | if (result == BP_RESULT_OK) { |
376 | enc10->base.features.flags.bits.IS_HBR2_CAPABLE = |
377 | bp_cap_info.DP_HBR2_EN; |
378 | enc10->base.features.flags.bits.IS_HBR3_CAPABLE = |
379 | bp_cap_info.DP_HBR3_EN; |
380 | enc10->base.features.flags.bits.HDMI_6GB_EN = bp_cap_info.HDMI_6GB_EN; |
381 | enc10->base.features.flags.bits.IS_DP2_CAPABLE = bp_cap_info.IS_DP2_CAPABLE; |
382 | enc10->base.features.flags.bits.IS_UHBR10_CAPABLE = bp_cap_info.DP_UHBR10_EN; |
383 | enc10->base.features.flags.bits.IS_UHBR13_5_CAPABLE = bp_cap_info.DP_UHBR13_5_EN; |
384 | enc10->base.features.flags.bits.IS_UHBR20_CAPABLE = bp_cap_info.DP_UHBR20_EN; |
385 | enc10->base.features.flags.bits.DP_IS_USB_C = |
386 | bp_cap_info.DP_IS_USB_C; |
387 | } else { |
388 | DC_LOG_WARNING("%s: Failed to get encoder_cap_info from VBIOS with error code %d!\n" , |
389 | __func__, |
390 | result); |
391 | } |
392 | if (enc10->base.ctx->dc->debug.hdmi20_disable) { |
393 | enc10->base.features.flags.bits.HDMI_6GB_EN = 0; |
394 | } |
395 | } |
396 | |
397 | void dcn31_link_encoder_construct_minimal( |
398 | struct dcn20_link_encoder *enc20, |
399 | struct dc_context *ctx, |
400 | const struct encoder_feature_support *enc_features, |
401 | const struct dcn10_link_enc_registers *link_regs, |
402 | enum engine_id eng_id) |
403 | { |
404 | struct dcn10_link_encoder *enc10 = &enc20->enc10; |
405 | |
406 | enc10->base.funcs = &dcn31_link_enc_funcs; |
407 | enc10->base.ctx = ctx; |
408 | enc10->base.id.type = OBJECT_TYPE_ENCODER; |
409 | enc10->base.hpd_source = HPD_SOURCEID_UNKNOWN; |
410 | enc10->base.connector.type = OBJECT_TYPE_CONNECTOR; |
411 | enc10->base.preferred_engine = eng_id; |
412 | enc10->base.features = *enc_features; |
413 | enc10->base.transmitter = TRANSMITTER_UNKNOWN; |
414 | enc10->link_regs = link_regs; |
415 | |
416 | enc10->base.output_signals = |
417 | SIGNAL_TYPE_DISPLAY_PORT | |
418 | SIGNAL_TYPE_DISPLAY_PORT_MST | |
419 | SIGNAL_TYPE_EDP; |
420 | } |
421 | |
422 | /* DPIA equivalent of link_transmitter_control. */ |
423 | static bool link_dpia_control(struct dc_context *dc_ctx, |
424 | struct dmub_cmd_dig_dpia_control_data *dpia_control) |
425 | { |
426 | union dmub_rb_cmd cmd; |
427 | |
428 | memset(&cmd, 0, sizeof(cmd)); |
429 | |
430 | cmd.dig1_dpia_control.header.type = DMUB_CMD__DPIA; |
431 | cmd.dig1_dpia_control.header.sub_type = |
432 | DMUB_CMD__DPIA_DIG1_DPIA_CONTROL; |
433 | cmd.dig1_dpia_control.header.payload_bytes = |
434 | sizeof(cmd.dig1_dpia_control) - |
435 | sizeof(cmd.dig1_dpia_control.header); |
436 | |
437 | cmd.dig1_dpia_control.dpia_control = *dpia_control; |
438 | |
439 | dc_wake_and_execute_dmub_cmd(ctx: dc_ctx, cmd: &cmd, wait_type: DM_DMUB_WAIT_TYPE_WAIT); |
440 | |
441 | return true; |
442 | } |
443 | |
444 | static void link_encoder_disable(struct dcn10_link_encoder *enc10) |
445 | { |
446 | /* reset training complete */ |
447 | REG_UPDATE(DP_LINK_CNTL, DP_LINK_TRAINING_COMPLETE, 0); |
448 | } |
449 | |
450 | void dcn31_link_encoder_enable_dp_output( |
451 | struct link_encoder *enc, |
452 | const struct dc_link_settings *link_settings, |
453 | enum clock_source_id clock_source) |
454 | { |
455 | struct dcn10_link_encoder *enc10 = TO_DCN10_LINK_ENC(enc); |
456 | |
457 | /* Enable transmitter and encoder. */ |
458 | if (!link_enc_cfg_is_transmitter_mappable(dc: enc->ctx->dc, link_enc: enc)) { |
459 | |
460 | DC_LOG_DEBUG("%s: enc_id(%d)\n" , __func__, enc->preferred_engine); |
461 | dcn20_link_encoder_enable_dp_output(enc, link_settings, clock_source); |
462 | |
463 | } else { |
464 | |
465 | struct dmub_cmd_dig_dpia_control_data dpia_control = { 0 }; |
466 | struct dc_link *link; |
467 | |
468 | link = link_enc_cfg_get_link_using_link_enc(dc: enc->ctx->dc, eng_id: enc->preferred_engine); |
469 | |
470 | enc1_configure_encoder(enc10, link_settings); |
471 | |
472 | dpia_control.action = (uint8_t)TRANSMITTER_CONTROL_ENABLE; |
473 | dpia_control.enc_id = enc->preferred_engine; |
474 | dpia_control.mode_laneset.digmode = 0; /* 0 for SST; 5 for MST */ |
475 | dpia_control.lanenum = (uint8_t)link_settings->lane_count; |
476 | dpia_control.symclk_10khz = link_settings->link_rate * |
477 | LINK_RATE_REF_FREQ_IN_KHZ / 10; |
478 | /* DIG_BE_CNTL.DIG_HPD_SELECT set to 5 (hpdsel - 1) to indicate HPD pin |
479 | * unused by DPIA. |
480 | */ |
481 | dpia_control.hpdsel = 6; |
482 | |
483 | if (link) { |
484 | dpia_control.dpia_id = link->ddc_hw_inst; |
485 | dpia_control.fec_rdy = link->dc->link_srv->dp_should_enable_fec(link); |
486 | } else { |
487 | DC_LOG_ERROR("%s: Failed to execute DPIA enable DMUB command.\n" , __func__); |
488 | BREAK_TO_DEBUGGER(); |
489 | return; |
490 | } |
491 | |
492 | DC_LOG_DEBUG("%s: DPIA(%d) - enc_id(%d)\n" , __func__, dpia_control.dpia_id, dpia_control.enc_id); |
493 | link_dpia_control(dc_ctx: enc->ctx, dpia_control: &dpia_control); |
494 | } |
495 | } |
496 | |
497 | void dcn31_link_encoder_enable_dp_mst_output( |
498 | struct link_encoder *enc, |
499 | const struct dc_link_settings *link_settings, |
500 | enum clock_source_id clock_source) |
501 | { |
502 | struct dcn10_link_encoder *enc10 = TO_DCN10_LINK_ENC(enc); |
503 | |
504 | /* Enable transmitter and encoder. */ |
505 | if (!link_enc_cfg_is_transmitter_mappable(dc: enc->ctx->dc, link_enc: enc)) { |
506 | |
507 | DC_LOG_DEBUG("%s: enc_id(%d)\n" , __func__, enc->preferred_engine); |
508 | dcn10_link_encoder_enable_dp_mst_output(enc, link_settings, clock_source); |
509 | |
510 | } else { |
511 | |
512 | struct dmub_cmd_dig_dpia_control_data dpia_control = { 0 }; |
513 | struct dc_link *link; |
514 | |
515 | link = link_enc_cfg_get_link_using_link_enc(dc: enc->ctx->dc, eng_id: enc->preferred_engine); |
516 | |
517 | enc1_configure_encoder(enc10, link_settings); |
518 | |
519 | dpia_control.action = (uint8_t)TRANSMITTER_CONTROL_ENABLE; |
520 | dpia_control.enc_id = enc->preferred_engine; |
521 | dpia_control.mode_laneset.digmode = 5; /* 0 for SST; 5 for MST */ |
522 | dpia_control.lanenum = (uint8_t)link_settings->lane_count; |
523 | dpia_control.symclk_10khz = link_settings->link_rate * |
524 | LINK_RATE_REF_FREQ_IN_KHZ / 10; |
525 | /* DIG_BE_CNTL.DIG_HPD_SELECT set to 5 (hpdsel - 1) to indicate HPD pin |
526 | * unused by DPIA. |
527 | */ |
528 | dpia_control.hpdsel = 6; |
529 | |
530 | if (link) { |
531 | dpia_control.dpia_id = link->ddc_hw_inst; |
532 | dpia_control.fec_rdy = link->dc->link_srv->dp_should_enable_fec(link); |
533 | } else { |
534 | DC_LOG_ERROR("%s: Failed to execute DPIA enable DMUB command.\n" , __func__); |
535 | BREAK_TO_DEBUGGER(); |
536 | return; |
537 | } |
538 | |
539 | DC_LOG_DEBUG("%s: DPIA(%d) - enc_id(%d)\n" , __func__, dpia_control.dpia_id, dpia_control.enc_id); |
540 | link_dpia_control(dc_ctx: enc->ctx, dpia_control: &dpia_control); |
541 | } |
542 | } |
543 | |
544 | void dcn31_link_encoder_disable_output( |
545 | struct link_encoder *enc, |
546 | enum signal_type signal) |
547 | { |
548 | struct dcn10_link_encoder *enc10 = TO_DCN10_LINK_ENC(enc); |
549 | |
550 | /* Disable transmitter and encoder. */ |
551 | if (!link_enc_cfg_is_transmitter_mappable(dc: enc->ctx->dc, link_enc: enc)) { |
552 | |
553 | DC_LOG_DEBUG("%s: enc_id(%d)\n" , __func__, enc->preferred_engine); |
554 | dcn10_link_encoder_disable_output(enc, signal); |
555 | |
556 | } else { |
557 | |
558 | struct dmub_cmd_dig_dpia_control_data dpia_control = { 0 }; |
559 | struct dc_link *link; |
560 | |
561 | if (enc->funcs->is_dig_enabled && !enc->funcs->is_dig_enabled(enc)) |
562 | return; |
563 | |
564 | link = link_enc_cfg_get_link_using_link_enc(dc: enc->ctx->dc, eng_id: enc->preferred_engine); |
565 | |
566 | dpia_control.action = (uint8_t)TRANSMITTER_CONTROL_DISABLE; |
567 | dpia_control.enc_id = enc->preferred_engine; |
568 | if (signal == SIGNAL_TYPE_DISPLAY_PORT) { |
569 | dpia_control.mode_laneset.digmode = 0; /* 0 for SST; 5 for MST */ |
570 | } else if (signal == SIGNAL_TYPE_DISPLAY_PORT_MST) { |
571 | dpia_control.mode_laneset.digmode = 5; /* 0 for SST; 5 for MST */ |
572 | } else { |
573 | DC_LOG_ERROR("%s: USB4 DPIA only supports DisplayPort.\n" , __func__); |
574 | BREAK_TO_DEBUGGER(); |
575 | } |
576 | |
577 | if (link) { |
578 | dpia_control.dpia_id = link->ddc_hw_inst; |
579 | } else { |
580 | DC_LOG_ERROR("%s: Failed to execute DPIA enable DMUB command.\n" , __func__); |
581 | BREAK_TO_DEBUGGER(); |
582 | return; |
583 | } |
584 | |
585 | DC_LOG_DEBUG("%s: DPIA(%d) - enc_id(%d)\n" , __func__, dpia_control.dpia_id, dpia_control.enc_id); |
586 | link_dpia_control(dc_ctx: enc->ctx, dpia_control: &dpia_control); |
587 | |
588 | link_encoder_disable(enc10); |
589 | } |
590 | } |
591 | |
592 | bool dcn31_link_encoder_is_in_alt_mode(struct link_encoder *enc) |
593 | { |
594 | struct dcn10_link_encoder *enc10 = TO_DCN10_LINK_ENC(enc); |
595 | union dmub_rb_cmd cmd; |
596 | uint32_t dp_alt_mode_disable; |
597 | |
598 | /* Only applicable to USB-C PHY. */ |
599 | if (!enc->features.flags.bits.DP_IS_USB_C) |
600 | return false; |
601 | |
602 | /* |
603 | * Use the new interface from DMCUB if available. |
604 | * Avoids hanging the RDCPSPIPE if DMCUB wasn't already running. |
605 | */ |
606 | if (has_query_dp_alt(enc)) { |
607 | if (!query_dp_alt_from_dmub(enc, cmd: &cmd)) |
608 | return false; |
609 | |
610 | return (cmd.query_dp_alt.data.is_dp_alt_disable == 0); |
611 | } |
612 | |
613 | /* Legacy path, avoid if possible. */ |
614 | if (enc->ctx->asic_id.hw_internal_rev != YELLOW_CARP_B0) { |
615 | REG_GET(RDPCSTX_PHY_CNTL6, RDPCS_PHY_DPALT_DISABLE, |
616 | &dp_alt_mode_disable); |
617 | } else { |
618 | /* |
619 | * B0 phys use a new set of registers to check whether alt mode is disabled. |
620 | * if value == 1 alt mode is disabled, otherwise it is enabled. |
621 | */ |
622 | if ((enc10->base.transmitter == TRANSMITTER_UNIPHY_A) || |
623 | (enc10->base.transmitter == TRANSMITTER_UNIPHY_B) || |
624 | (enc10->base.transmitter == TRANSMITTER_UNIPHY_E)) { |
625 | REG_GET(RDPCSTX_PHY_CNTL6, RDPCS_PHY_DPALT_DISABLE, |
626 | &dp_alt_mode_disable); |
627 | } else { |
628 | REG_GET(RDPCSPIPE_PHY_CNTL6, RDPCS_PHY_DPALT_DISABLE, |
629 | &dp_alt_mode_disable); |
630 | } |
631 | } |
632 | |
633 | return (dp_alt_mode_disable == 0); |
634 | } |
635 | |
636 | void dcn31_link_encoder_get_max_link_cap(struct link_encoder *enc, struct dc_link_settings *link_settings) |
637 | { |
638 | struct dcn10_link_encoder *enc10 = TO_DCN10_LINK_ENC(enc); |
639 | union dmub_rb_cmd cmd; |
640 | uint32_t is_in_usb_c_dp4_mode = 0; |
641 | |
642 | dcn10_link_encoder_get_max_link_cap(enc, link_settings); |
643 | |
644 | /* Take the link cap directly if not USB */ |
645 | if (!enc->features.flags.bits.DP_IS_USB_C) |
646 | return; |
647 | |
648 | /* |
649 | * Use the new interface from DMCUB if available. |
650 | * Avoids hanging the RDCPSPIPE if DMCUB wasn't already running. |
651 | */ |
652 | if (has_query_dp_alt(enc)) { |
653 | if (!query_dp_alt_from_dmub(enc, cmd: &cmd)) |
654 | return; |
655 | |
656 | if (cmd.query_dp_alt.data.is_usb && |
657 | cmd.query_dp_alt.data.is_dp4 == 0) |
658 | link_settings->lane_count = MIN(LANE_COUNT_TWO, link_settings->lane_count); |
659 | |
660 | return; |
661 | } |
662 | |
663 | /* Legacy path, avoid if possible. */ |
664 | if (enc->ctx->asic_id.hw_internal_rev != YELLOW_CARP_B0) { |
665 | REG_GET(RDPCSTX_PHY_CNTL6, RDPCS_PHY_DPALT_DP4, |
666 | &is_in_usb_c_dp4_mode); |
667 | } else { |
668 | if ((enc10->base.transmitter == TRANSMITTER_UNIPHY_A) || |
669 | (enc10->base.transmitter == TRANSMITTER_UNIPHY_B) || |
670 | (enc10->base.transmitter == TRANSMITTER_UNIPHY_E)) { |
671 | REG_GET(RDPCSTX_PHY_CNTL6, RDPCS_PHY_DPALT_DP4, |
672 | &is_in_usb_c_dp4_mode); |
673 | } else { |
674 | REG_GET(RDPCSPIPE_PHY_CNTL6, RDPCS_PHY_DPALT_DP4, |
675 | &is_in_usb_c_dp4_mode); |
676 | } |
677 | } |
678 | |
679 | if (!is_in_usb_c_dp4_mode) |
680 | link_settings->lane_count = MIN(LANE_COUNT_TWO, link_settings->lane_count); |
681 | } |
682 | |