1 /* 2 * Copyright 2019 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 "hdcp.h" 27 28 static 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 } else if (is_hdcp2(hdcp)) { 43 hdcp->connection.hdcp2_retry_count++; 44 } 45 } 46 47 static uint8_t is_cp_desired_hdcp1(struct mod_hdcp *hdcp) 48 { 49 int i, is_auth_needed = 0; 50 51 /* if all displays on the link don't need authentication, 52 * hdcp is not desired 53 */ 54 for (i = 0; i < MAX_NUM_OF_DISPLAYS; i++) { 55 if (hdcp->displays[i].state != MOD_HDCP_DISPLAY_INACTIVE && 56 !hdcp->displays[i].adjust.disable) { 57 is_auth_needed = 1; 58 break; 59 } 60 } 61 62 return (hdcp->connection.hdcp1_retry_count < MAX_NUM_OF_ATTEMPTS) && 63 is_auth_needed && 64 !hdcp->connection.link.adjust.hdcp1.disable && 65 !hdcp->connection.is_hdcp1_revoked; 66 } 67 68 static uint8_t is_cp_desired_hdcp2(struct mod_hdcp *hdcp) 69 { 70 int i, is_auth_needed = 0; 71 72 /* if all displays on the link don't need authentication, 73 * hdcp is not desired 74 */ 75 for (i = 0; i < MAX_NUM_OF_DISPLAYS; i++) { 76 if (hdcp->displays[i].state != MOD_HDCP_DISPLAY_INACTIVE && 77 !hdcp->displays[i].adjust.disable) { 78 is_auth_needed = 1; 79 break; 80 } 81 } 82 83 return (hdcp->connection.hdcp2_retry_count < MAX_NUM_OF_ATTEMPTS) && 84 is_auth_needed && 85 !hdcp->connection.link.adjust.hdcp2.disable && 86 !hdcp->connection.is_hdcp2_revoked; 87 } 88 89 static enum mod_hdcp_status execution(struct mod_hdcp *hdcp, 90 struct mod_hdcp_event_context *event_ctx, 91 union mod_hdcp_transition_input *input) 92 { 93 enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS; 94 95 if (is_in_initialized_state(hdcp)) { 96 if (event_ctx->event != MOD_HDCP_EVENT_CALLBACK) { 97 event_ctx->unexpected_event = 1; 98 goto out; 99 } 100 /* initialize transition input */ 101 memset(input, 0, sizeof(union mod_hdcp_transition_input)); 102 } else if (is_in_cp_not_desired_state(hdcp)) { 103 if (event_ctx->event != MOD_HDCP_EVENT_CALLBACK) { 104 event_ctx->unexpected_event = 1; 105 goto out; 106 } 107 } else if (is_in_hdcp1_states(hdcp)) { 108 status = mod_hdcp_hdcp1_execution(hdcp, event_ctx, &input->hdcp1); 109 } else if (is_in_hdcp1_dp_states(hdcp)) { 110 status = mod_hdcp_hdcp1_dp_execution(hdcp, 111 event_ctx, &input->hdcp1); 112 } else if (is_in_hdcp2_states(hdcp)) { 113 status = mod_hdcp_hdcp2_execution(hdcp, event_ctx, &input->hdcp2); 114 } else if (is_in_hdcp2_dp_states(hdcp)) { 115 status = mod_hdcp_hdcp2_dp_execution(hdcp, 116 event_ctx, &input->hdcp2); 117 } 118 out: 119 return status; 120 } 121 122 static enum mod_hdcp_status transition(struct mod_hdcp *hdcp, 123 struct mod_hdcp_event_context *event_ctx, 124 union mod_hdcp_transition_input *input, 125 struct mod_hdcp_output *output) 126 { 127 enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS; 128 129 if (event_ctx->unexpected_event) 130 goto out; 131 132 if (is_in_initialized_state(hdcp)) { 133 if (is_dp_hdcp(hdcp)) 134 if (is_cp_desired_hdcp2(hdcp)) { 135 callback_in_ms(0, output); 136 set_state_id(hdcp, output, D2_A0_DETERMINE_RX_HDCP_CAPABLE); 137 } else if (is_cp_desired_hdcp1(hdcp)) { 138 callback_in_ms(0, output); 139 set_state_id(hdcp, output, D1_A0_DETERMINE_RX_HDCP_CAPABLE); 140 } else { 141 callback_in_ms(0, output); 142 set_state_id(hdcp, output, HDCP_CP_NOT_DESIRED); 143 } 144 else if (is_hdmi_dvi_sl_hdcp(hdcp)) 145 if (is_cp_desired_hdcp2(hdcp)) { 146 callback_in_ms(0, output); 147 set_state_id(hdcp, output, H2_A0_KNOWN_HDCP2_CAPABLE_RX); 148 } else if (is_cp_desired_hdcp1(hdcp)) { 149 callback_in_ms(0, output); 150 set_state_id(hdcp, output, H1_A0_WAIT_FOR_ACTIVE_RX); 151 } else { 152 callback_in_ms(0, output); 153 set_state_id(hdcp, output, HDCP_CP_NOT_DESIRED); 154 } 155 else { 156 callback_in_ms(0, output); 157 set_state_id(hdcp, output, HDCP_CP_NOT_DESIRED); 158 } 159 } else if (is_in_cp_not_desired_state(hdcp)) { 160 increment_stay_counter(hdcp); 161 } else if (is_in_hdcp1_states(hdcp)) { 162 status = mod_hdcp_hdcp1_transition(hdcp, 163 event_ctx, &input->hdcp1, output); 164 } else if (is_in_hdcp1_dp_states(hdcp)) { 165 status = mod_hdcp_hdcp1_dp_transition(hdcp, 166 event_ctx, &input->hdcp1, output); 167 } else if (is_in_hdcp2_states(hdcp)) { 168 status = mod_hdcp_hdcp2_transition(hdcp, 169 event_ctx, &input->hdcp2, output); 170 } else if (is_in_hdcp2_dp_states(hdcp)) { 171 status = mod_hdcp_hdcp2_dp_transition(hdcp, 172 event_ctx, &input->hdcp2, output); 173 } else { 174 status = MOD_HDCP_STATUS_INVALID_STATE; 175 } 176 out: 177 return status; 178 } 179 180 static enum mod_hdcp_status reset_authentication(struct mod_hdcp *hdcp, 181 struct mod_hdcp_output *output) 182 { 183 enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS; 184 185 if (is_hdcp1(hdcp)) { 186 if (hdcp->auth.trans_input.hdcp1.create_session != UNKNOWN) { 187 /* TODO - update psp to unify create session failure 188 * recovery between hdcp1 and 2. 189 */ 190 mod_hdcp_hdcp1_destroy_session(hdcp); 191 192 } 193 194 HDCP_TOP_RESET_AUTH_TRACE(hdcp); 195 memset(&hdcp->auth, 0, sizeof(struct mod_hdcp_authentication)); 196 memset(&hdcp->state, 0, sizeof(struct mod_hdcp_state)); 197 set_state_id(hdcp, output, HDCP_INITIALIZED); 198 } else if (is_hdcp2(hdcp)) { 199 if (hdcp->auth.trans_input.hdcp2.create_session == PASS) { 200 status = mod_hdcp_hdcp2_destroy_session(hdcp); 201 if (status != MOD_HDCP_STATUS_SUCCESS) { 202 output->callback_needed = 0; 203 output->watchdog_timer_needed = 0; 204 goto out; 205 } 206 } 207 208 HDCP_TOP_RESET_AUTH_TRACE(hdcp); 209 memset(&hdcp->auth, 0, sizeof(struct mod_hdcp_authentication)); 210 memset(&hdcp->state, 0, sizeof(struct mod_hdcp_state)); 211 set_state_id(hdcp, output, HDCP_INITIALIZED); 212 } else if (is_in_cp_not_desired_state(hdcp)) { 213 HDCP_TOP_RESET_AUTH_TRACE(hdcp); 214 memset(&hdcp->auth, 0, sizeof(struct mod_hdcp_authentication)); 215 memset(&hdcp->state, 0, sizeof(struct mod_hdcp_state)); 216 set_state_id(hdcp, output, HDCP_INITIALIZED); 217 } 218 219 out: 220 /* stop callback and watchdog requests from previous authentication*/ 221 output->watchdog_timer_stop = 1; 222 output->callback_stop = 1; 223 return status; 224 } 225 226 static enum mod_hdcp_status reset_connection(struct mod_hdcp *hdcp, 227 struct mod_hdcp_output *output) 228 { 229 enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS; 230 231 memset(output, 0, sizeof(struct mod_hdcp_output)); 232 233 status = reset_authentication(hdcp, output); 234 if (status != MOD_HDCP_STATUS_SUCCESS) 235 goto out; 236 237 if (current_state(hdcp) != HDCP_UNINITIALIZED) { 238 HDCP_TOP_RESET_CONN_TRACE(hdcp); 239 set_state_id(hdcp, output, HDCP_UNINITIALIZED); 240 } 241 memset(&hdcp->connection, 0, sizeof(hdcp->connection)); 242 out: 243 return status; 244 } 245 246 /* 247 * Implementation of functions in mod_hdcp.h 248 */ 249 size_t mod_hdcp_get_memory_size(void) 250 { 251 return sizeof(struct mod_hdcp); 252 } 253 254 enum mod_hdcp_status mod_hdcp_setup(struct mod_hdcp *hdcp, 255 struct mod_hdcp_config *config) 256 { 257 struct mod_hdcp_output output; 258 enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS; 259 260 memset(hdcp, 0, sizeof(struct mod_hdcp)); 261 memset(&output, 0, sizeof(output)); 262 hdcp->config = *config; 263 HDCP_TOP_INTERFACE_TRACE(hdcp); 264 status = reset_connection(hdcp, &output); 265 if (status != MOD_HDCP_STATUS_SUCCESS) 266 push_error_status(hdcp, status); 267 return status; 268 } 269 270 enum mod_hdcp_status mod_hdcp_teardown(struct mod_hdcp *hdcp) 271 { 272 enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS; 273 struct mod_hdcp_output output; 274 275 HDCP_TOP_INTERFACE_TRACE(hdcp); 276 memset(&output, 0, sizeof(output)); 277 status = reset_connection(hdcp, &output); 278 if (status == MOD_HDCP_STATUS_SUCCESS) 279 memset(hdcp, 0, sizeof(struct mod_hdcp)); 280 else 281 push_error_status(hdcp, status); 282 return status; 283 } 284 285 enum mod_hdcp_status mod_hdcp_add_display(struct mod_hdcp *hdcp, 286 struct mod_hdcp_link *link, struct mod_hdcp_display *display, 287 struct mod_hdcp_output *output) 288 { 289 enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS; 290 struct mod_hdcp_display *display_container = NULL; 291 292 HDCP_TOP_INTERFACE_TRACE_WITH_INDEX(hdcp, display->index); 293 memset(output, 0, sizeof(struct mod_hdcp_output)); 294 295 /* skip inactive display */ 296 if (display->state != MOD_HDCP_DISPLAY_ACTIVE) { 297 status = MOD_HDCP_STATUS_SUCCESS; 298 goto out; 299 } 300 301 /* check existing display container */ 302 if (get_active_display_at_index(hdcp, display->index)) { 303 status = MOD_HDCP_STATUS_SUCCESS; 304 goto out; 305 } 306 307 /* find an empty display container */ 308 display_container = get_empty_display_container(hdcp); 309 if (!display_container) { 310 status = MOD_HDCP_STATUS_DISPLAY_OUT_OF_BOUND; 311 goto out; 312 } 313 314 /* reset existing authentication status */ 315 status = reset_authentication(hdcp, output); 316 if (status != MOD_HDCP_STATUS_SUCCESS) 317 goto out; 318 319 /* reset retry counters */ 320 reset_retry_counts(hdcp); 321 322 /* reset error trace */ 323 memset(&hdcp->connection.trace, 0, sizeof(hdcp->connection.trace)); 324 325 /* add display to connection */ 326 hdcp->connection.link = *link; 327 *display_container = *display; 328 status = mod_hdcp_add_display_to_topology(hdcp, display->index); 329 if (status != MOD_HDCP_STATUS_SUCCESS) 330 goto out; 331 332 /* request authentication */ 333 if (current_state(hdcp) != HDCP_INITIALIZED) 334 set_state_id(hdcp, output, HDCP_INITIALIZED); 335 callback_in_ms(hdcp->connection.link.adjust.auth_delay * 1000, output); 336 out: 337 if (status != MOD_HDCP_STATUS_SUCCESS) 338 push_error_status(hdcp, status); 339 340 return status; 341 } 342 343 enum mod_hdcp_status mod_hdcp_remove_display(struct mod_hdcp *hdcp, 344 uint8_t index, struct mod_hdcp_output *output) 345 { 346 enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS; 347 struct mod_hdcp_display *display = NULL; 348 349 HDCP_TOP_INTERFACE_TRACE_WITH_INDEX(hdcp, index); 350 memset(output, 0, sizeof(struct mod_hdcp_output)); 351 352 /* find display in connection */ 353 display = get_active_display_at_index(hdcp, index); 354 if (!display) { 355 status = MOD_HDCP_STATUS_SUCCESS; 356 goto out; 357 } 358 359 /* stop current authentication */ 360 status = reset_authentication(hdcp, output); 361 if (status != MOD_HDCP_STATUS_SUCCESS) 362 goto out; 363 364 /* clear retry counters */ 365 reset_retry_counts(hdcp); 366 367 /* reset error trace */ 368 memset(&hdcp->connection.trace, 0, sizeof(hdcp->connection.trace)); 369 370 /* remove display */ 371 status = mod_hdcp_remove_display_from_topology(hdcp, index); 372 if (status != MOD_HDCP_STATUS_SUCCESS) 373 goto out; 374 display->state = MOD_HDCP_DISPLAY_INACTIVE; 375 376 /* request authentication when connection is not reset */ 377 if (current_state(hdcp) != HDCP_UNINITIALIZED) 378 callback_in_ms(hdcp->connection.link.adjust.auth_delay * 1000, 379 output); 380 out: 381 if (status != MOD_HDCP_STATUS_SUCCESS) 382 push_error_status(hdcp, status); 383 return status; 384 } 385 386 enum mod_hdcp_status mod_hdcp_query_display(struct mod_hdcp *hdcp, 387 uint8_t index, struct mod_hdcp_display_query *query) 388 { 389 enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS; 390 struct mod_hdcp_display *display = NULL; 391 392 /* find display in connection */ 393 display = get_active_display_at_index(hdcp, index); 394 if (!display) { 395 status = MOD_HDCP_STATUS_DISPLAY_NOT_FOUND; 396 goto out; 397 } 398 399 /* populate query */ 400 query->link = &hdcp->connection.link; 401 query->display = display; 402 query->trace = &hdcp->connection.trace; 403 query->encryption_status = MOD_HDCP_ENCRYPTION_STATUS_HDCP_OFF; 404 405 if (is_display_encryption_enabled(display)) { 406 if (is_hdcp1(hdcp)) { 407 query->encryption_status = MOD_HDCP_ENCRYPTION_STATUS_HDCP1_ON; 408 } else if (is_hdcp2(hdcp)) { 409 if (query->link->adjust.hdcp2.force_type == MOD_HDCP_FORCE_TYPE_0) 410 query->encryption_status = MOD_HDCP_ENCRYPTION_STATUS_HDCP2_TYPE0_ON; 411 else if (query->link->adjust.hdcp2.force_type == MOD_HDCP_FORCE_TYPE_1) 412 query->encryption_status = MOD_HDCP_ENCRYPTION_STATUS_HDCP2_TYPE1_ON; 413 else 414 query->encryption_status = MOD_HDCP_ENCRYPTION_STATUS_HDCP2_ON; 415 } 416 } else { 417 query->encryption_status = MOD_HDCP_ENCRYPTION_STATUS_HDCP_OFF; 418 } 419 420 out: 421 return status; 422 } 423 424 enum mod_hdcp_status mod_hdcp_reset_connection(struct mod_hdcp *hdcp, 425 struct mod_hdcp_output *output) 426 { 427 enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS; 428 429 HDCP_TOP_INTERFACE_TRACE(hdcp); 430 status = reset_connection(hdcp, output); 431 if (status != MOD_HDCP_STATUS_SUCCESS) 432 push_error_status(hdcp, status); 433 434 return status; 435 } 436 437 enum mod_hdcp_status mod_hdcp_process_event(struct mod_hdcp *hdcp, 438 enum mod_hdcp_event event, struct mod_hdcp_output *output) 439 { 440 enum mod_hdcp_status exec_status, trans_status, reset_status, status; 441 struct mod_hdcp_event_context event_ctx; 442 443 HDCP_EVENT_TRACE(hdcp, event); 444 memset(output, 0, sizeof(struct mod_hdcp_output)); 445 memset(&event_ctx, 0, sizeof(struct mod_hdcp_event_context)); 446 event_ctx.event = event; 447 448 /* execute and transition */ 449 exec_status = execution(hdcp, &event_ctx, &hdcp->auth.trans_input); 450 trans_status = transition( 451 hdcp, &event_ctx, &hdcp->auth.trans_input, output); 452 if (trans_status == MOD_HDCP_STATUS_SUCCESS) { 453 status = MOD_HDCP_STATUS_SUCCESS; 454 } else if (exec_status == MOD_HDCP_STATUS_SUCCESS) { 455 status = MOD_HDCP_STATUS_INTERNAL_POLICY_FAILURE; 456 push_error_status(hdcp, status); 457 } else { 458 status = exec_status; 459 push_error_status(hdcp, status); 460 } 461 462 /* reset authentication if needed */ 463 if (trans_status == MOD_HDCP_STATUS_RESET_NEEDED) { 464 HDCP_FULL_DDC_TRACE(hdcp); 465 reset_status = reset_authentication(hdcp, output); 466 if (reset_status != MOD_HDCP_STATUS_SUCCESS) 467 push_error_status(hdcp, reset_status); 468 } 469 return status; 470 } 471 472 enum mod_hdcp_operation_mode mod_hdcp_signal_type_to_operation_mode( 473 enum signal_type signal) 474 { 475 enum mod_hdcp_operation_mode mode = MOD_HDCP_MODE_OFF; 476 477 switch (signal) { 478 case SIGNAL_TYPE_DVI_SINGLE_LINK: 479 case SIGNAL_TYPE_HDMI_TYPE_A: 480 mode = MOD_HDCP_MODE_DEFAULT; 481 break; 482 case SIGNAL_TYPE_EDP: 483 case SIGNAL_TYPE_DISPLAY_PORT: 484 mode = MOD_HDCP_MODE_DP; 485 break; 486 case SIGNAL_TYPE_DISPLAY_PORT_MST: 487 mode = MOD_HDCP_MODE_DP_MST; 488 break; 489 default: 490 break; 491 } 492 493 return mode; 494 } 495