Home | History | Annotate | Line # | Download | only in hdcp
      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 
     33 static 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 
     52 static 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 
     72 static 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 
     93 static 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 	}
    124 out:
    125 	return status;
    126 }
    127 
    128 static 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 	}
    182 out:
    183 	return status;
    184 }
    185 
    186 static 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 
    245 out:
    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 
    252 static 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));
    268 out:
    269 	return status;
    270 }
    271 
    272 /*
    273  * Implementation of functions in mod_hdcp.h
    274  */
    275 size_t mod_hdcp_get_memory_size(void)
    276 {
    277 	return sizeof(struct mod_hdcp);
    278 }
    279 
    280 enum 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 
    296 enum 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 
    311 enum 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);
    359 out:
    360 	if (status != MOD_HDCP_STATUS_SUCCESS)
    361 		push_error_status(hdcp, status);
    362 
    363 	return status;
    364 }
    365 
    366 enum 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);
    400 out:
    401 	if (status != MOD_HDCP_STATUS_SUCCESS)
    402 		push_error_status(hdcp, status);
    403 	return status;
    404 }
    405 
    406 enum 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 
    440 out:
    441 	return status;
    442 }
    443 
    444 enum 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 
    457 enum 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 
    492 enum 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