brintos

brintos / linux-shallow public Read only

0
0
Text · 17.7 KiB · 5e01c6e Raw
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