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