xref: /linux/drivers/gpu/drm/amd/display/modules/hdcp/hdcp.c (revision 8a79db5e83a5d52c74e6f3c40d6f312cf899213e)
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