599 lines · c
1/*2 * Copyright 2019 Advanced Micro Devices, Inc.3 *4 * Permission is hereby granted, free of charge, to any person obtaining a5 * copy of this software and associated documentation files (the "Software"),6 * to deal in the Software without restriction, including without limitation7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,8 * and/or sell copies of the Software, and to permit persons to whom the9 * Software is furnished to do so, subject to the following conditions:10 *11 * The above copyright notice and this permission notice shall be included in12 * all copies or substantial portions of the Software.13 *14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL17 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR18 * 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 OR20 * OTHER DEALINGS IN THE SOFTWARE.21 *22 * Authors: AMD23 *24 */25 26#include "hdcp.h"27 28static void push_error_status(struct mod_hdcp *hdcp,29 enum mod_hdcp_status status)30{31 struct mod_hdcp_trace *trace = &hdcp->connection.trace;32 33 if (trace->error_count < MAX_NUM_OF_ERROR_TRACE) {34 trace->errors[trace->error_count].status = status;35 trace->errors[trace->error_count].state_id = hdcp->state.id;36 trace->error_count++;37 HDCP_ERROR_TRACE(hdcp, status);38 }39 40 if (is_hdcp1(hdcp)) {41 hdcp->connection.hdcp1_retry_count++;42 if (hdcp->connection.hdcp1_retry_count == MAX_NUM_OF_ATTEMPTS)43 hdcp->connection.link.adjust.hdcp1.disable = 1;44 } else if (is_hdcp2(hdcp)) {45 hdcp->connection.hdcp2_retry_count++;46 if (hdcp->connection.hdcp2_retry_count == MAX_NUM_OF_ATTEMPTS)47 hdcp->connection.link.adjust.hdcp2.disable = 1;48 }49}50 51static uint8_t is_cp_desired_hdcp1(struct mod_hdcp *hdcp)52{53 int i, is_auth_needed = 0;54 55 /* if all displays on the link don't need authentication,56 * hdcp is not desired57 */58 for (i = 0; i < MAX_NUM_OF_DISPLAYS; i++) {59 if (hdcp->displays[i].state != MOD_HDCP_DISPLAY_INACTIVE &&60 hdcp->displays[i].adjust.disable != MOD_HDCP_DISPLAY_DISABLE_AUTHENTICATION) {61 is_auth_needed = 1;62 break;63 }64 }65 66 return is_auth_needed &&67 !hdcp->connection.link.adjust.hdcp1.disable &&68 !hdcp->connection.is_hdcp1_revoked;69}70 71static uint8_t is_cp_desired_hdcp2(struct mod_hdcp *hdcp)72{73 int i, is_auth_needed = 0;74 75 /* if all displays on the link don't need authentication,76 * hdcp is not desired77 */78 for (i = 0; i < MAX_NUM_OF_DISPLAYS; i++) {79 if (hdcp->displays[i].state != MOD_HDCP_DISPLAY_INACTIVE &&80 hdcp->displays[i].adjust.disable != MOD_HDCP_DISPLAY_DISABLE_AUTHENTICATION) {81 is_auth_needed = 1;82 break;83 }84 }85 86 return is_auth_needed &&87 !hdcp->connection.link.adjust.hdcp2.disable &&88 !hdcp->connection.is_hdcp2_revoked;89}90 91static enum mod_hdcp_status execution(struct mod_hdcp *hdcp,92 struct mod_hdcp_event_context *event_ctx,93 union mod_hdcp_transition_input *input)94{95 enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS;96 97 if (is_in_initialized_state(hdcp)) {98 if (event_ctx->event != MOD_HDCP_EVENT_CALLBACK) {99 event_ctx->unexpected_event = 1;100 goto out;101 }102 /* initialize transition input */103 memset(input, 0, sizeof(union mod_hdcp_transition_input));104 } else if (is_in_cp_not_desired_state(hdcp)) {105 if (event_ctx->event != MOD_HDCP_EVENT_CALLBACK) {106 event_ctx->unexpected_event = 1;107 goto out;108 }109 } else if (is_in_hdcp1_states(hdcp)) {110 status = mod_hdcp_hdcp1_execution(hdcp, event_ctx, &input->hdcp1);111 } else if (is_in_hdcp1_dp_states(hdcp)) {112 status = mod_hdcp_hdcp1_dp_execution(hdcp,113 event_ctx, &input->hdcp1);114 } else if (is_in_hdcp2_states(hdcp)) {115 status = mod_hdcp_hdcp2_execution(hdcp, event_ctx, &input->hdcp2);116 } else if (is_in_hdcp2_dp_states(hdcp)) {117 status = mod_hdcp_hdcp2_dp_execution(hdcp,118 event_ctx, &input->hdcp2);119 } else {120 event_ctx->unexpected_event = 1;121 goto out;122 }123out:124 return status;125}126 127static enum mod_hdcp_status transition(struct mod_hdcp *hdcp,128 struct mod_hdcp_event_context *event_ctx,129 union mod_hdcp_transition_input *input,130 struct mod_hdcp_output *output)131{132 enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS;133 134 if (event_ctx->unexpected_event)135 goto out;136 137 if (is_in_initialized_state(hdcp)) {138 if (is_dp_hdcp(hdcp))139 if (is_cp_desired_hdcp2(hdcp)) {140 callback_in_ms(0, output);141 set_state_id(hdcp, output, D2_A0_DETERMINE_RX_HDCP_CAPABLE);142 } else if (is_cp_desired_hdcp1(hdcp)) {143 callback_in_ms(0, output);144 set_state_id(hdcp, output, D1_A0_DETERMINE_RX_HDCP_CAPABLE);145 } else {146 callback_in_ms(0, output);147 set_state_id(hdcp, output, HDCP_CP_NOT_DESIRED);148 set_auth_complete(hdcp, output);149 }150 else if (is_hdmi_dvi_sl_hdcp(hdcp))151 if (is_cp_desired_hdcp2(hdcp)) {152 callback_in_ms(0, output);153 set_state_id(hdcp, output, H2_A0_KNOWN_HDCP2_CAPABLE_RX);154 } else if (is_cp_desired_hdcp1(hdcp)) {155 callback_in_ms(0, output);156 set_state_id(hdcp, output, H1_A0_WAIT_FOR_ACTIVE_RX);157 } else {158 callback_in_ms(0, output);159 set_state_id(hdcp, output, HDCP_CP_NOT_DESIRED);160 set_auth_complete(hdcp, output);161 }162 else {163 callback_in_ms(0, output);164 set_state_id(hdcp, output, HDCP_CP_NOT_DESIRED);165 set_auth_complete(hdcp, output);166 }167 } else if (is_in_cp_not_desired_state(hdcp)) {168 increment_stay_counter(hdcp);169 } else if (is_in_hdcp1_states(hdcp)) {170 status = mod_hdcp_hdcp1_transition(hdcp,171 event_ctx, &input->hdcp1, output);172 } else if (is_in_hdcp1_dp_states(hdcp)) {173 status = mod_hdcp_hdcp1_dp_transition(hdcp,174 event_ctx, &input->hdcp1, output);175 } else if (is_in_hdcp2_states(hdcp)) {176 status = mod_hdcp_hdcp2_transition(hdcp,177 event_ctx, &input->hdcp2, output);178 } else if (is_in_hdcp2_dp_states(hdcp)) {179 status = mod_hdcp_hdcp2_dp_transition(hdcp,180 event_ctx, &input->hdcp2, output);181 } else {182 status = MOD_HDCP_STATUS_INVALID_STATE;183 }184out:185 return status;186}187 188static enum mod_hdcp_status reset_authentication(struct mod_hdcp *hdcp,189 struct mod_hdcp_output *output)190{191 enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS;192 193 if (is_hdcp1(hdcp)) {194 if (hdcp->auth.trans_input.hdcp1.create_session != UNKNOWN) {195 /* TODO - update psp to unify create session failure196 * recovery between hdcp1 and 2.197 */198 mod_hdcp_hdcp1_destroy_session(hdcp);199 200 }201 202 HDCP_TOP_RESET_AUTH_TRACE(hdcp);203 memset(&hdcp->auth, 0, sizeof(struct mod_hdcp_authentication));204 memset(&hdcp->state, 0, sizeof(struct mod_hdcp_state));205 set_state_id(hdcp, output, HDCP_INITIALIZED);206 } else if (is_hdcp2(hdcp)) {207 if (hdcp->auth.trans_input.hdcp2.create_session == PASS) {208 status = mod_hdcp_hdcp2_destroy_session(hdcp);209 if (status != MOD_HDCP_STATUS_SUCCESS) {210 output->callback_needed = 0;211 output->watchdog_timer_needed = 0;212 goto out;213 }214 }215 216 HDCP_TOP_RESET_AUTH_TRACE(hdcp);217 memset(&hdcp->auth, 0, sizeof(struct mod_hdcp_authentication));218 memset(&hdcp->state, 0, sizeof(struct mod_hdcp_state));219 set_state_id(hdcp, output, HDCP_INITIALIZED);220 } else if (is_in_cp_not_desired_state(hdcp)) {221 HDCP_TOP_RESET_AUTH_TRACE(hdcp);222 memset(&hdcp->auth, 0, sizeof(struct mod_hdcp_authentication));223 memset(&hdcp->state, 0, sizeof(struct mod_hdcp_state));224 set_state_id(hdcp, output, HDCP_INITIALIZED);225 }226 227out:228 /* stop callback and watchdog requests from previous authentication*/229 output->watchdog_timer_stop = 1;230 output->callback_stop = 1;231 return status;232}233 234static enum mod_hdcp_status reset_connection(struct mod_hdcp *hdcp,235 struct mod_hdcp_output *output)236{237 enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS;238 239 memset(output, 0, sizeof(struct mod_hdcp_output));240 241 status = reset_authentication(hdcp, output);242 if (status != MOD_HDCP_STATUS_SUCCESS)243 goto out;244 245 if (current_state(hdcp) != HDCP_UNINITIALIZED) {246 HDCP_TOP_RESET_CONN_TRACE(hdcp);247 set_state_id(hdcp, output, HDCP_UNINITIALIZED);248 }249 memset(&hdcp->connection, 0, sizeof(hdcp->connection));250out:251 return status;252}253 254static enum mod_hdcp_status update_display_adjustments(struct mod_hdcp *hdcp,255 struct mod_hdcp_display *display,256 struct mod_hdcp_display_adjustment *adj)257{258 enum mod_hdcp_status status = MOD_HDCP_STATUS_NOT_IMPLEMENTED;259 260 if (is_in_authenticated_states(hdcp) &&261 is_dp_mst_hdcp(hdcp) &&262 display->adjust.disable == true &&263 adj->disable == false) {264 display->adjust.disable = false;265 if (is_hdcp1(hdcp))266 status = mod_hdcp_hdcp1_enable_dp_stream_encryption(hdcp);267 else if (is_hdcp2(hdcp))268 status = mod_hdcp_hdcp2_enable_dp_stream_encryption(hdcp);269 270 if (status != MOD_HDCP_STATUS_SUCCESS)271 display->adjust.disable = true;272 }273 274 if (status == MOD_HDCP_STATUS_SUCCESS &&275 memcmp(adj, &display->adjust,276 sizeof(struct mod_hdcp_display_adjustment)) != 0)277 status = MOD_HDCP_STATUS_NOT_IMPLEMENTED;278 279 return status;280}281/*282 * Implementation of functions in mod_hdcp.h283 */284size_t mod_hdcp_get_memory_size(void)285{286 return sizeof(struct mod_hdcp);287}288 289enum mod_hdcp_status mod_hdcp_setup(struct mod_hdcp *hdcp,290 struct mod_hdcp_config *config)291{292 struct mod_hdcp_output output;293 enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS;294 295 memset(&output, 0, sizeof(output));296 hdcp->config = *config;297 HDCP_TOP_INTERFACE_TRACE(hdcp);298 status = reset_connection(hdcp, &output);299 if (status != MOD_HDCP_STATUS_SUCCESS)300 push_error_status(hdcp, status);301 return status;302}303 304enum mod_hdcp_status mod_hdcp_teardown(struct mod_hdcp *hdcp)305{306 enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS;307 struct mod_hdcp_output output;308 309 HDCP_TOP_INTERFACE_TRACE(hdcp);310 memset(&output, 0, sizeof(output));311 status = reset_connection(hdcp, &output);312 if (status == MOD_HDCP_STATUS_SUCCESS)313 memset(hdcp, 0, sizeof(struct mod_hdcp));314 else315 push_error_status(hdcp, status);316 return status;317}318 319enum mod_hdcp_status mod_hdcp_add_display(struct mod_hdcp *hdcp,320 struct mod_hdcp_link *link, struct mod_hdcp_display *display,321 struct mod_hdcp_output *output)322{323 enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS;324 struct mod_hdcp_display *display_container = NULL;325 326 HDCP_TOP_INTERFACE_TRACE_WITH_INDEX(hdcp, display->index);327 memset(output, 0, sizeof(struct mod_hdcp_output));328 329 /* skip inactive display */330 if (display->state != MOD_HDCP_DISPLAY_ACTIVE) {331 status = MOD_HDCP_STATUS_SUCCESS;332 goto out;333 }334 335 /* check existing display container */336 if (get_active_display_at_index(hdcp, display->index)) {337 status = MOD_HDCP_STATUS_SUCCESS;338 goto out;339 }340 341 /* find an empty display container */342 display_container = get_empty_display_container(hdcp);343 if (!display_container) {344 status = MOD_HDCP_STATUS_DISPLAY_OUT_OF_BOUND;345 goto out;346 }347 348 /* reset existing authentication status */349 status = reset_authentication(hdcp, output);350 if (status != MOD_HDCP_STATUS_SUCCESS)351 goto out;352 353 /* reset retry counters */354 reset_retry_counts(hdcp);355 356 /* reset error trace */357 memset(&hdcp->connection.trace, 0, sizeof(hdcp->connection.trace));358 359 /* add display to connection */360 hdcp->connection.link = *link;361 *display_container = *display;362 status = mod_hdcp_add_display_to_topology(hdcp, display_container);363 364 if (status != MOD_HDCP_STATUS_SUCCESS)365 goto out;366 367 /* request authentication */368 if (current_state(hdcp) != HDCP_INITIALIZED)369 set_state_id(hdcp, output, HDCP_INITIALIZED);370 callback_in_ms(hdcp->connection.link.adjust.auth_delay * 1000, output);371out:372 if (status != MOD_HDCP_STATUS_SUCCESS)373 push_error_status(hdcp, status);374 375 return status;376}377 378enum mod_hdcp_status mod_hdcp_remove_display(struct mod_hdcp *hdcp,379 uint8_t index, struct mod_hdcp_output *output)380{381 enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS;382 struct mod_hdcp_display *display = NULL;383 384 HDCP_TOP_INTERFACE_TRACE_WITH_INDEX(hdcp, index);385 memset(output, 0, sizeof(struct mod_hdcp_output));386 387 /* find display in connection */388 display = get_active_display_at_index(hdcp, index);389 if (!display) {390 status = MOD_HDCP_STATUS_SUCCESS;391 goto out;392 }393 394 /* stop current authentication */395 status = reset_authentication(hdcp, output);396 if (status != MOD_HDCP_STATUS_SUCCESS)397 goto out;398 399 /* clear retry counters */400 reset_retry_counts(hdcp);401 402 /* reset error trace */403 memset(&hdcp->connection.trace, 0, sizeof(hdcp->connection.trace));404 405 /* remove display */406 status = mod_hdcp_remove_display_from_topology(hdcp, index);407 if (status != MOD_HDCP_STATUS_SUCCESS)408 goto out;409 memset(display, 0, sizeof(struct mod_hdcp_display));410 411 /* request authentication when connection is not reset */412 if (current_state(hdcp) != HDCP_UNINITIALIZED)413 callback_in_ms(hdcp->connection.link.adjust.auth_delay * 1000,414 output);415out:416 if (status != MOD_HDCP_STATUS_SUCCESS)417 push_error_status(hdcp, status);418 return status;419}420 421enum mod_hdcp_status mod_hdcp_update_display(struct mod_hdcp *hdcp,422 uint8_t index,423 struct mod_hdcp_link_adjustment *link_adjust,424 struct mod_hdcp_display_adjustment *display_adjust,425 struct mod_hdcp_output *output)426{427 enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS;428 struct mod_hdcp_display *display = NULL;429 430 HDCP_TOP_INTERFACE_TRACE_WITH_INDEX(hdcp, index);431 memset(output, 0, sizeof(struct mod_hdcp_output));432 433 /* find display in connection */434 display = get_active_display_at_index(hdcp, index);435 if (!display) {436 status = MOD_HDCP_STATUS_DISPLAY_NOT_FOUND;437 goto out;438 }439 440 /* skip if no changes */441 if (memcmp(link_adjust, &hdcp->connection.link.adjust,442 sizeof(struct mod_hdcp_link_adjustment)) == 0 &&443 memcmp(display_adjust, &display->adjust,444 sizeof(struct mod_hdcp_display_adjustment)) == 0) {445 status = MOD_HDCP_STATUS_SUCCESS;446 goto out;447 }448 449 if (memcmp(link_adjust, &hdcp->connection.link.adjust,450 sizeof(struct mod_hdcp_link_adjustment)) == 0 &&451 memcmp(display_adjust, &display->adjust,452 sizeof(struct mod_hdcp_display_adjustment)) != 0) {453 status = update_display_adjustments(hdcp, display, display_adjust);454 if (status != MOD_HDCP_STATUS_NOT_IMPLEMENTED)455 goto out;456 }457 458 /* stop current authentication */459 status = reset_authentication(hdcp, output);460 if (status != MOD_HDCP_STATUS_SUCCESS)461 goto out;462 463 /* clear retry counters */464 reset_retry_counts(hdcp);465 466 /* reset error trace */467 memset(&hdcp->connection.trace, 0, sizeof(hdcp->connection.trace));468 469 /* set new adjustment */470 hdcp->connection.link.adjust = *link_adjust;471 display->adjust = *display_adjust;472 473 /* request authentication when connection is not reset */474 if (current_state(hdcp) != HDCP_UNINITIALIZED)475 /* wait 100ms to debounce simultaneous updates for different indices */476 callback_in_ms(100, output);477 478out:479 if (status != MOD_HDCP_STATUS_SUCCESS)480 push_error_status(hdcp, status);481 return status;482}483 484enum mod_hdcp_status mod_hdcp_query_display(struct mod_hdcp *hdcp,485 uint8_t index, struct mod_hdcp_display_query *query)486{487 enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS;488 struct mod_hdcp_display *display = NULL;489 490 /* find display in connection */491 display = get_active_display_at_index(hdcp, index);492 if (!display) {493 status = MOD_HDCP_STATUS_DISPLAY_NOT_FOUND;494 goto out;495 }496 497 /* populate query */498 query->link = &hdcp->connection.link;499 query->display = display;500 query->trace = &hdcp->connection.trace;501 query->encryption_status = MOD_HDCP_ENCRYPTION_STATUS_HDCP_OFF;502 503 if (is_display_encryption_enabled(display)) {504 if (is_hdcp1(hdcp)) {505 query->encryption_status = MOD_HDCP_ENCRYPTION_STATUS_HDCP1_ON;506 } else if (is_hdcp2(hdcp)) {507 if (query->link->adjust.hdcp2.force_type == MOD_HDCP_FORCE_TYPE_0)508 query->encryption_status = MOD_HDCP_ENCRYPTION_STATUS_HDCP2_TYPE0_ON;509 else if (query->link->adjust.hdcp2.force_type == MOD_HDCP_FORCE_TYPE_1)510 query->encryption_status = MOD_HDCP_ENCRYPTION_STATUS_HDCP2_TYPE1_ON;511 else512 query->encryption_status = MOD_HDCP_ENCRYPTION_STATUS_HDCP2_ON;513 }514 } else {515 query->encryption_status = MOD_HDCP_ENCRYPTION_STATUS_HDCP_OFF;516 }517 518out:519 return status;520}521 522enum mod_hdcp_status mod_hdcp_reset_connection(struct mod_hdcp *hdcp,523 struct mod_hdcp_output *output)524{525 enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS;526 527 HDCP_TOP_INTERFACE_TRACE(hdcp);528 status = reset_connection(hdcp, output);529 if (status != MOD_HDCP_STATUS_SUCCESS)530 push_error_status(hdcp, status);531 532 return status;533}534 535enum mod_hdcp_status mod_hdcp_process_event(struct mod_hdcp *hdcp,536 enum mod_hdcp_event event, struct mod_hdcp_output *output)537{538 enum mod_hdcp_status exec_status, trans_status, reset_status, status;539 struct mod_hdcp_event_context event_ctx;540 541 HDCP_EVENT_TRACE(hdcp, event);542 memset(output, 0, sizeof(struct mod_hdcp_output));543 memset(&event_ctx, 0, sizeof(struct mod_hdcp_event_context));544 event_ctx.event = event;545 546 /* execute and transition */547 exec_status = execution(hdcp, &event_ctx, &hdcp->auth.trans_input);548 trans_status = transition(549 hdcp, &event_ctx, &hdcp->auth.trans_input, output);550 if (trans_status == MOD_HDCP_STATUS_SUCCESS) {551 status = MOD_HDCP_STATUS_SUCCESS;552 } else if (exec_status == MOD_HDCP_STATUS_SUCCESS) {553 status = MOD_HDCP_STATUS_INTERNAL_POLICY_FAILURE;554 push_error_status(hdcp, status);555 } else {556 status = exec_status;557 push_error_status(hdcp, status);558 }559 560 /* reset authentication if needed */561 if (trans_status == MOD_HDCP_STATUS_RESET_NEEDED) {562 mod_hdcp_log_ddc_trace(hdcp);563 reset_status = reset_authentication(hdcp, output);564 if (reset_status != MOD_HDCP_STATUS_SUCCESS)565 push_error_status(hdcp, reset_status);566 }567 568 /* Clear CP_IRQ status if needed */569 if (event_ctx.event == MOD_HDCP_EVENT_CPIRQ) {570 status = mod_hdcp_clear_cp_irq_status(hdcp);571 if (status != MOD_HDCP_STATUS_SUCCESS)572 push_error_status(hdcp, status);573 }574 575 return status;576}577 578enum mod_hdcp_operation_mode mod_hdcp_signal_type_to_operation_mode(579 enum signal_type signal)580{581 enum mod_hdcp_operation_mode mode = MOD_HDCP_MODE_OFF;582 583 switch (signal) {584 case SIGNAL_TYPE_DVI_SINGLE_LINK:585 case SIGNAL_TYPE_HDMI_TYPE_A:586 mode = MOD_HDCP_MODE_DEFAULT;587 break;588 case SIGNAL_TYPE_EDP:589 case SIGNAL_TYPE_DISPLAY_PORT:590 case SIGNAL_TYPE_DISPLAY_PORT_MST:591 mode = MOD_HDCP_MODE_DP;592 break;593 default:594 break;595 }596 597 return mode;598}599