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
push_error_status(struct mod_hdcp * hdcp,enum mod_hdcp_status status)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
is_cp_desired_hdcp1(struct mod_hdcp * hdcp)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
is_cp_desired_hdcp2(struct mod_hdcp * hdcp)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
execution(struct mod_hdcp * hdcp,struct mod_hdcp_event_context * event_ctx,union mod_hdcp_transition_input * input)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
transition(struct mod_hdcp * hdcp,struct mod_hdcp_event_context * event_ctx,union mod_hdcp_transition_input * input,struct mod_hdcp_output * output)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
reset_authentication(struct mod_hdcp * hdcp,struct mod_hdcp_output * output)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
reset_connection(struct mod_hdcp * hdcp,struct mod_hdcp_output * output)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 */
mod_hdcp_get_memory_size(void)275 size_t mod_hdcp_get_memory_size(void)
276 {
277 return sizeof(struct mod_hdcp);
278 }
279
mod_hdcp_setup(struct mod_hdcp * hdcp,struct mod_hdcp_config * config)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
mod_hdcp_teardown(struct mod_hdcp * hdcp)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
mod_hdcp_add_display(struct mod_hdcp * hdcp,struct mod_hdcp_link * link,struct mod_hdcp_display * display,struct mod_hdcp_output * output)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
mod_hdcp_remove_display(struct mod_hdcp * hdcp,uint8_t index,struct mod_hdcp_output * output)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
mod_hdcp_query_display(struct mod_hdcp * hdcp,uint8_t index,struct mod_hdcp_display_query * query)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
mod_hdcp_reset_connection(struct mod_hdcp * hdcp,struct mod_hdcp_output * output)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
mod_hdcp_process_event(struct mod_hdcp * hdcp,enum mod_hdcp_event event,struct mod_hdcp_output * output)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
mod_hdcp_signal_type_to_operation_mode(enum signal_type signal)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