1/* $NetBSD: amdgpu_hdcp.c,v 1.2 2021/12/18 23:45:07 riastradh Exp $ */ 2 3/* 4 * Copyright 2019 Advanced Micro Devices, Inc. 5 * 6 * Permission is hereby granted, free of charge, to any person obtaining a 7 * copy of this software and associated documentation files (the "Software"), 8 * to deal in the Software without restriction, including without limitation 9 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 10 * and/or sell copies of the Software, and to permit persons to whom the 11 * Software is furnished to do so, subject to the following conditions: 12 * 13 * The above copyright notice and this permission notice shall be included in 14 * all copies or substantial portions of the Software. 15 * 16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 19 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 * OTHER DEALINGS IN THE SOFTWARE. 23 * 24 * Authors: AMD 25 * 26 */ 27 28#include <sys/cdefs.h> 29__KERNEL_RCSID(0, "$NetBSD: amdgpu_hdcp.c,v 1.2 2021/12/18 23:45:07 riastradh Exp $"); 30 31#include "hdcp.h" 32 33static void push_error_status(struct mod_hdcp *hdcp, 34 enum mod_hdcp_status status) 35{ 36 struct mod_hdcp_trace *trace = &hdcp->connection.trace; 37 38 if (trace->error_count < MAX_NUM_OF_ERROR_TRACE) { 39 trace->errors[trace->error_count].status = status; 40 trace->errors[trace->error_count].state_id = hdcp->state.id; 41 trace->error_count++; 42 HDCP_ERROR_TRACE(hdcp, status); 43 } 44 45 if (is_hdcp1(hdcp)) { 46 hdcp->connection.hdcp1_retry_count++; 47 } else if (is_hdcp2(hdcp)) { 48 hdcp->connection.hdcp2_retry_count++; 49 } 50} 51 52static uint8_t is_cp_desired_hdcp1(struct mod_hdcp *hdcp) 53{ 54 int i, is_auth_needed = 0; 55 56 /* if all displays on the link don't need authentication, 57 * hdcp is not desired 58 */ 59 for (i = 0; i < MAX_NUM_OF_DISPLAYS; i++) { 60 if (hdcp->connection.displays[i].state != MOD_HDCP_DISPLAY_INACTIVE && 61 !hdcp->connection.displays[i].adjust.disable) { 62 is_auth_needed = 1; 63 break; 64 } 65 } 66 67 return (hdcp->connection.hdcp1_retry_count < MAX_NUM_OF_ATTEMPTS) && 68 is_auth_needed && 69 !hdcp->connection.link.adjust.hdcp1.disable; 70} 71 72static uint8_t is_cp_desired_hdcp2(struct mod_hdcp *hdcp) 73{ 74 int i, is_auth_needed = 0; 75 76 /* if all displays on the link don't need authentication, 77 * hdcp is not desired 78 */ 79 for (i = 0; i < MAX_NUM_OF_DISPLAYS; i++) { 80 if (hdcp->connection.displays[i].state != MOD_HDCP_DISPLAY_INACTIVE && 81 !hdcp->connection.displays[i].adjust.disable) { 82 is_auth_needed = 1; 83 break; 84 } 85 } 86 87 return (hdcp->connection.hdcp2_retry_count < MAX_NUM_OF_ATTEMPTS) && 88 is_auth_needed && 89 !hdcp->connection.link.adjust.hdcp2.disable && 90 !hdcp->connection.is_hdcp2_revoked; 91} 92 93static enum mod_hdcp_status execution(struct mod_hdcp *hdcp, 94 struct mod_hdcp_event_context *event_ctx, 95 union mod_hdcp_transition_input *input) 96{ 97 enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS; 98 99 if (is_in_initialized_state(hdcp)) { 100 if (event_ctx->event != MOD_HDCP_EVENT_CALLBACK) { 101 event_ctx->unexpected_event = 1; 102 goto out; 103 } 104 /* initialize transition input */ 105 memset(input, 0, sizeof(union mod_hdcp_transition_input)); 106 } else if (is_in_cp_not_desired_state(hdcp)) { 107 if (event_ctx->event != MOD_HDCP_EVENT_CALLBACK) { 108 event_ctx->unexpected_event = 1; 109 goto out; 110 } 111 /* update topology event if hdcp is not desired */ 112 status = mod_hdcp_add_display_topology(hdcp); 113 } else if (is_in_hdcp1_states(hdcp)) { 114 status = mod_hdcp_hdcp1_execution(hdcp, event_ctx, &input->hdcp1); 115 } else if (is_in_hdcp1_dp_states(hdcp)) { 116 status = mod_hdcp_hdcp1_dp_execution(hdcp, 117 event_ctx, &input->hdcp1); 118 } else if (is_in_hdcp2_states(hdcp)) { 119 status = mod_hdcp_hdcp2_execution(hdcp, event_ctx, &input->hdcp2); 120 } else if (is_in_hdcp2_dp_states(hdcp)) { 121 status = mod_hdcp_hdcp2_dp_execution(hdcp, 122 event_ctx, &input->hdcp2); 123 } 124out: 125 return status; 126} 127 128static enum mod_hdcp_status transition(struct mod_hdcp *hdcp, 129 struct mod_hdcp_event_context *event_ctx, 130 union mod_hdcp_transition_input *input, 131 struct mod_hdcp_output *output) 132{ 133 enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS; 134 135 if (event_ctx->unexpected_event) 136 goto out; 137 138 if (is_in_initialized_state(hdcp)) { 139 if (is_dp_hdcp(hdcp)) 140 if (is_cp_desired_hdcp2(hdcp)) { 141 callback_in_ms(0, output); 142 set_state_id(hdcp, output, D2_A0_DETERMINE_RX_HDCP_CAPABLE); 143 } else if (is_cp_desired_hdcp1(hdcp)) { 144 callback_in_ms(0, output); 145 set_state_id(hdcp, output, D1_A0_DETERMINE_RX_HDCP_CAPABLE); 146 } else { 147 callback_in_ms(0, output); 148 set_state_id(hdcp, output, HDCP_CP_NOT_DESIRED); 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 } 161 else { 162 callback_in_ms(0, output); 163 set_state_id(hdcp, output, HDCP_CP_NOT_DESIRED); 164 } 165 } else if (is_in_cp_not_desired_state(hdcp)) { 166 increment_stay_counter(hdcp); 167 } else if (is_in_hdcp1_states(hdcp)) { 168 status = mod_hdcp_hdcp1_transition(hdcp, 169 event_ctx, &input->hdcp1, output); 170 } else if (is_in_hdcp1_dp_states(hdcp)) { 171 status = mod_hdcp_hdcp1_dp_transition(hdcp, 172 event_ctx, &input->hdcp1, output); 173 } else if (is_in_hdcp2_states(hdcp)) { 174 status = mod_hdcp_hdcp2_transition(hdcp, 175 event_ctx, &input->hdcp2, output); 176 } else if (is_in_hdcp2_dp_states(hdcp)) { 177 status = mod_hdcp_hdcp2_dp_transition(hdcp, 178 event_ctx, &input->hdcp2, output); 179 } else { 180 status = MOD_HDCP_STATUS_INVALID_STATE; 181 } 182out: 183 return status; 184} 185 186static enum mod_hdcp_status reset_authentication(struct mod_hdcp *hdcp, 187 struct mod_hdcp_output *output) 188{ 189 enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS; 190 191 if (is_hdcp1(hdcp)) { 192 if (hdcp->auth.trans_input.hdcp1.create_session != UNKNOWN) { 193 /* TODO - update psp to unify create session failure 194 * recovery between hdcp1 and 2. 195 */ 196 mod_hdcp_hdcp1_destroy_session(hdcp); 197 198 } 199 if (hdcp->auth.trans_input.hdcp1.add_topology == PASS) { 200 status = mod_hdcp_remove_display_topology(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 HDCP_TOP_RESET_AUTH_TRACE(hdcp); 208 memset(&hdcp->auth, 0, sizeof(struct mod_hdcp_authentication)); 209 memset(&hdcp->state, 0, sizeof(struct mod_hdcp_state)); 210 set_state_id(hdcp, output, HDCP_INITIALIZED); 211 } else if (is_hdcp2(hdcp)) { 212 if (hdcp->auth.trans_input.hdcp2.create_session == PASS) { 213 status = mod_hdcp_hdcp2_destroy_session(hdcp); 214 if (status != MOD_HDCP_STATUS_SUCCESS) { 215 output->callback_needed = 0; 216 output->watchdog_timer_needed = 0; 217 goto out; 218 } 219 } 220 if (hdcp->auth.trans_input.hdcp2.add_topology == PASS) { 221 status = mod_hdcp_remove_display_topology(hdcp); 222 if (status != MOD_HDCP_STATUS_SUCCESS) { 223 output->callback_needed = 0; 224 output->watchdog_timer_needed = 0; 225 goto out; 226 } 227 } 228 HDCP_TOP_RESET_AUTH_TRACE(hdcp); 229 memset(&hdcp->auth, 0, sizeof(struct mod_hdcp_authentication)); 230 memset(&hdcp->state, 0, sizeof(struct mod_hdcp_state)); 231 set_state_id(hdcp, output, HDCP_INITIALIZED); 232 } else if (is_in_cp_not_desired_state(hdcp)) { 233 status = mod_hdcp_remove_display_topology(hdcp); 234 if (status != MOD_HDCP_STATUS_SUCCESS) { 235 output->callback_needed = 0; 236 output->watchdog_timer_needed = 0; 237 goto out; 238 } 239 HDCP_TOP_RESET_AUTH_TRACE(hdcp); 240 memset(&hdcp->auth, 0, sizeof(struct mod_hdcp_authentication)); 241 memset(&hdcp->state, 0, sizeof(struct mod_hdcp_state)); 242 set_state_id(hdcp, output, HDCP_INITIALIZED); 243 } 244 245out: 246 /* stop callback and watchdog requests from previous authentication*/ 247 output->watchdog_timer_stop = 1; 248 output->callback_stop = 1; 249 return status; 250} 251 252static enum mod_hdcp_status reset_connection(struct mod_hdcp *hdcp, 253 struct mod_hdcp_output *output) 254{ 255 enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS; 256 257 memset(output, 0, sizeof(struct mod_hdcp_output)); 258 259 status = reset_authentication(hdcp, output); 260 if (status != MOD_HDCP_STATUS_SUCCESS) 261 goto out; 262 263 if (current_state(hdcp) != HDCP_UNINITIALIZED) { 264 HDCP_TOP_RESET_CONN_TRACE(hdcp); 265 set_state_id(hdcp, output, HDCP_UNINITIALIZED); 266 } 267 memset(&hdcp->connection, 0, sizeof(hdcp->connection)); 268out: 269 return status; 270} 271 272/* 273 * Implementation of functions in mod_hdcp.h 274 */ 275size_t mod_hdcp_get_memory_size(void) 276{ 277 return sizeof(struct mod_hdcp); 278} 279 280enum mod_hdcp_status mod_hdcp_setup(struct mod_hdcp *hdcp, 281 struct mod_hdcp_config *config) 282{ 283 struct mod_hdcp_output output; 284 enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS; 285 286 memset(hdcp, 0, sizeof(struct mod_hdcp)); 287 memset(&output, 0, sizeof(output)); 288 hdcp->config = *config; 289 HDCP_TOP_INTERFACE_TRACE(hdcp); 290 status = reset_connection(hdcp, &output); 291 if (status != MOD_HDCP_STATUS_SUCCESS) 292 push_error_status(hdcp, status); 293 return status; 294} 295 296enum mod_hdcp_status mod_hdcp_teardown(struct mod_hdcp *hdcp) 297{ 298 enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS; 299 struct mod_hdcp_output output; 300 301 HDCP_TOP_INTERFACE_TRACE(hdcp); 302 memset(&output, 0, sizeof(output)); 303 status = reset_connection(hdcp, &output); 304 if (status == MOD_HDCP_STATUS_SUCCESS) 305 memset(hdcp, 0, sizeof(struct mod_hdcp)); 306 else 307 push_error_status(hdcp, status); 308 return status; 309} 310 311enum mod_hdcp_status mod_hdcp_add_display(struct mod_hdcp *hdcp, 312 struct mod_hdcp_link *link, struct mod_hdcp_display *display, 313 struct mod_hdcp_output *output) 314{ 315 enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS; 316 struct mod_hdcp_display *display_container = NULL; 317 318 HDCP_TOP_INTERFACE_TRACE_WITH_INDEX(hdcp, display->index); 319 memset(output, 0, sizeof(struct mod_hdcp_output)); 320 321 /* skip inactive display */ 322 if (display->state != MOD_HDCP_DISPLAY_ACTIVE) { 323 status = MOD_HDCP_STATUS_SUCCESS; 324 goto out; 325 } 326 327 /* check existing display container */ 328 if (get_active_display_at_index(hdcp, display->index)) { 329 status = MOD_HDCP_STATUS_SUCCESS; 330 goto out; 331 } 332 333 /* find an empty display container */ 334 display_container = get_empty_display_container(hdcp); 335 if (!display_container) { 336 status = MOD_HDCP_STATUS_DISPLAY_OUT_OF_BOUND; 337 goto out; 338 } 339 340 /* reset existing authentication status */ 341 status = reset_authentication(hdcp, output); 342 if (status != MOD_HDCP_STATUS_SUCCESS) 343 goto out; 344 345 /* add display to connection */ 346 hdcp->connection.link = *link; 347 *display_container = *display; 348 349 /* reset retry counters */ 350 reset_retry_counts(hdcp); 351 352 /* reset error trace */ 353 memset(&hdcp->connection.trace, 0, sizeof(hdcp->connection.trace)); 354 355 /* request authentication */ 356 if (current_state(hdcp) != HDCP_INITIALIZED) 357 set_state_id(hdcp, output, HDCP_INITIALIZED); 358 callback_in_ms(hdcp->connection.link.adjust.auth_delay * 1000, output); 359out: 360 if (status != MOD_HDCP_STATUS_SUCCESS) 361 push_error_status(hdcp, status); 362 363 return status; 364} 365 366enum mod_hdcp_status mod_hdcp_remove_display(struct mod_hdcp *hdcp, 367 uint8_t index, struct mod_hdcp_output *output) 368{ 369 enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS; 370 struct mod_hdcp_display *display = NULL; 371 372 HDCP_TOP_INTERFACE_TRACE_WITH_INDEX(hdcp, index); 373 memset(output, 0, sizeof(struct mod_hdcp_output)); 374 375 /* find display in connection */ 376 display = get_active_display_at_index(hdcp, index); 377 if (!display) { 378 status = MOD_HDCP_STATUS_SUCCESS; 379 goto out; 380 } 381 382 /* stop current authentication */ 383 status = reset_authentication(hdcp, output); 384 if (status != MOD_HDCP_STATUS_SUCCESS) 385 goto out; 386 387 /* remove display */ 388 display->state = MOD_HDCP_DISPLAY_INACTIVE; 389 390 /* clear retry counters */ 391 reset_retry_counts(hdcp); 392 393 /* reset error trace */ 394 memset(&hdcp->connection.trace, 0, sizeof(hdcp->connection.trace)); 395 396 /* request authentication for remaining displays*/ 397 if (get_active_display_count(hdcp) > 0) 398 callback_in_ms(hdcp->connection.link.adjust.auth_delay * 1000, 399 output); 400out: 401 if (status != MOD_HDCP_STATUS_SUCCESS) 402 push_error_status(hdcp, status); 403 return status; 404} 405 406enum mod_hdcp_status mod_hdcp_query_display(struct mod_hdcp *hdcp, 407 uint8_t index, struct mod_hdcp_display_query *query) 408{ 409 enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS; 410 struct mod_hdcp_display *display = NULL; 411 412 /* find display in connection */ 413 display = get_active_display_at_index(hdcp, index); 414 if (!display) { 415 status = MOD_HDCP_STATUS_DISPLAY_NOT_FOUND; 416 goto out; 417 } 418 419 /* populate query */ 420 query->link = &hdcp->connection.link; 421 query->display = display; 422 query->trace = &hdcp->connection.trace; 423 query->encryption_status = MOD_HDCP_ENCRYPTION_STATUS_HDCP_OFF; 424 425 if (is_display_encryption_enabled(display)) { 426 if (is_hdcp1(hdcp)) { 427 query->encryption_status = MOD_HDCP_ENCRYPTION_STATUS_HDCP1_ON; 428 } else if (is_hdcp2(hdcp)) { 429 if (query->link->adjust.hdcp2.force_type == MOD_HDCP_FORCE_TYPE_0) 430 query->encryption_status = MOD_HDCP_ENCRYPTION_STATUS_HDCP2_TYPE0_ON; 431 else if (query->link->adjust.hdcp2.force_type == MOD_HDCP_FORCE_TYPE_1) 432 query->encryption_status = MOD_HDCP_ENCRYPTION_STATUS_HDCP2_TYPE1_ON; 433 else 434 query->encryption_status = MOD_HDCP_ENCRYPTION_STATUS_HDCP2_ON; 435 } 436 } else { 437 query->encryption_status = MOD_HDCP_ENCRYPTION_STATUS_HDCP_OFF; 438 } 439 440out: 441 return status; 442} 443 444enum mod_hdcp_status mod_hdcp_reset_connection(struct mod_hdcp *hdcp, 445 struct mod_hdcp_output *output) 446{ 447 enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS; 448 449 HDCP_TOP_INTERFACE_TRACE(hdcp); 450 status = reset_connection(hdcp, output); 451 if (status != MOD_HDCP_STATUS_SUCCESS) 452 push_error_status(hdcp, status); 453 454 return status; 455} 456 457enum mod_hdcp_status mod_hdcp_process_event(struct mod_hdcp *hdcp, 458 enum mod_hdcp_event event, struct mod_hdcp_output *output) 459{ 460 enum mod_hdcp_status exec_status, trans_status, reset_status, status; 461 struct mod_hdcp_event_context event_ctx; 462 463 HDCP_EVENT_TRACE(hdcp, event); 464 memset(output, 0, sizeof(struct mod_hdcp_output)); 465 memset(&event_ctx, 0, sizeof(struct mod_hdcp_event_context)); 466 event_ctx.event = event; 467 468 /* execute and transition */ 469 exec_status = execution(hdcp, &event_ctx, &hdcp->auth.trans_input); 470 trans_status = transition( 471 hdcp, &event_ctx, &hdcp->auth.trans_input, output); 472 if (trans_status == MOD_HDCP_STATUS_SUCCESS) { 473 status = MOD_HDCP_STATUS_SUCCESS; 474 } else if (exec_status == MOD_HDCP_STATUS_SUCCESS) { 475 status = MOD_HDCP_STATUS_INTERNAL_POLICY_FAILURE; 476 push_error_status(hdcp, status); 477 } else { 478 status = exec_status; 479 push_error_status(hdcp, status); 480 } 481 482 /* reset authentication if needed */ 483 if (trans_status == MOD_HDCP_STATUS_RESET_NEEDED) { 484 HDCP_FULL_DDC_TRACE(hdcp); 485 reset_status = reset_authentication(hdcp, output); 486 if (reset_status != MOD_HDCP_STATUS_SUCCESS) 487 push_error_status(hdcp, reset_status); 488 } 489 return status; 490} 491 492enum mod_hdcp_operation_mode mod_hdcp_signal_type_to_operation_mode( 493 enum signal_type signal) 494{ 495 enum mod_hdcp_operation_mode mode = MOD_HDCP_MODE_OFF; 496 497 switch (signal) { 498 case SIGNAL_TYPE_DVI_SINGLE_LINK: 499 case SIGNAL_TYPE_HDMI_TYPE_A: 500 mode = MOD_HDCP_MODE_DEFAULT; 501 break; 502 case SIGNAL_TYPE_EDP: 503 case SIGNAL_TYPE_DISPLAY_PORT: 504 mode = MOD_HDCP_MODE_DP; 505 break; 506 case SIGNAL_TYPE_DISPLAY_PORT_MST: 507 mode = MOD_HDCP_MODE_DP_MST; 508 break; 509 default: 510 break; 511 } 512 513 return mode; 514} 515