2 * Copyright 2019 Advanced Micro Devices, Inc.
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:
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
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.
28 static void push_error_status(struct mod_hdcp
*hdcp
,
29 enum mod_hdcp_status status
)
31 struct mod_hdcp_trace
*trace
= &hdcp
->connection
.trace
;
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
;
37 HDCP_ERROR_TRACE(hdcp
, status
);
41 hdcp
->connection
.hdcp1_retry_count
++;
42 } else if (is_hdcp2(hdcp
)) {
43 hdcp
->connection
.hdcp2_retry_count
++;
47 static uint8_t is_cp_desired_hdcp1(struct mod_hdcp
*hdcp
)
49 int i
, is_auth_needed
= 0;
51 /* if all displays on the link don't need authentication,
54 for (i
= 0; i
< MAX_NUM_OF_DISPLAYS
; i
++) {
55 if (hdcp
->connection
.displays
[i
].state
!= MOD_HDCP_DISPLAY_INACTIVE
&&
56 !hdcp
->connection
.displays
[i
].adjust
.disable
) {
62 return (hdcp
->connection
.hdcp1_retry_count
< MAX_NUM_OF_ATTEMPTS
) &&
64 !hdcp
->connection
.link
.adjust
.hdcp1
.disable
;
67 static uint8_t is_cp_desired_hdcp2(struct mod_hdcp
*hdcp
)
69 int i
, is_auth_needed
= 0;
71 /* if all displays on the link don't need authentication,
74 for (i
= 0; i
< MAX_NUM_OF_DISPLAYS
; i
++) {
75 if (hdcp
->connection
.displays
[i
].state
!= MOD_HDCP_DISPLAY_INACTIVE
&&
76 !hdcp
->connection
.displays
[i
].adjust
.disable
) {
82 return (hdcp
->connection
.hdcp2_retry_count
< MAX_NUM_OF_ATTEMPTS
) &&
84 !hdcp
->connection
.link
.adjust
.hdcp2
.disable
&&
85 !hdcp
->connection
.is_hdcp2_revoked
;
88 static enum mod_hdcp_status
execution(struct mod_hdcp
*hdcp
,
89 struct mod_hdcp_event_context
*event_ctx
,
90 union mod_hdcp_transition_input
*input
)
92 enum mod_hdcp_status status
= MOD_HDCP_STATUS_SUCCESS
;
94 if (is_in_initialized_state(hdcp
)) {
95 if (event_ctx
->event
!= MOD_HDCP_EVENT_CALLBACK
) {
96 event_ctx
->unexpected_event
= 1;
99 /* initialize transition input */
100 memset(input
, 0, sizeof(union mod_hdcp_transition_input
));
101 } else if (is_in_cp_not_desired_state(hdcp
)) {
102 if (event_ctx
->event
!= MOD_HDCP_EVENT_CALLBACK
) {
103 event_ctx
->unexpected_event
= 1;
106 /* update topology event if hdcp is not desired */
107 status
= mod_hdcp_add_display_topology(hdcp
);
108 } else if (is_in_hdcp1_states(hdcp
)) {
109 status
= mod_hdcp_hdcp1_execution(hdcp
, event_ctx
, &input
->hdcp1
);
110 } else if (is_in_hdcp1_dp_states(hdcp
)) {
111 status
= mod_hdcp_hdcp1_dp_execution(hdcp
,
112 event_ctx
, &input
->hdcp1
);
113 } else if (is_in_hdcp2_states(hdcp
)) {
114 status
= mod_hdcp_hdcp2_execution(hdcp
, event_ctx
, &input
->hdcp2
);
115 } else if (is_in_hdcp2_dp_states(hdcp
)) {
116 status
= mod_hdcp_hdcp2_dp_execution(hdcp
,
117 event_ctx
, &input
->hdcp2
);
123 static enum mod_hdcp_status
transition(struct mod_hdcp
*hdcp
,
124 struct mod_hdcp_event_context
*event_ctx
,
125 union mod_hdcp_transition_input
*input
,
126 struct mod_hdcp_output
*output
)
128 enum mod_hdcp_status status
= MOD_HDCP_STATUS_SUCCESS
;
130 if (event_ctx
->unexpected_event
)
133 if (is_in_initialized_state(hdcp
)) {
134 if (is_dp_hdcp(hdcp
))
135 if (is_cp_desired_hdcp2(hdcp
)) {
136 callback_in_ms(0, output
);
137 set_state_id(hdcp
, output
, D2_A0_DETERMINE_RX_HDCP_CAPABLE
);
138 } else if (is_cp_desired_hdcp1(hdcp
)) {
139 callback_in_ms(0, output
);
140 set_state_id(hdcp
, output
, D1_A0_DETERMINE_RX_HDCP_CAPABLE
);
142 callback_in_ms(0, output
);
143 set_state_id(hdcp
, output
, HDCP_CP_NOT_DESIRED
);
145 else if (is_hdmi_dvi_sl_hdcp(hdcp
))
146 if (is_cp_desired_hdcp2(hdcp
)) {
147 callback_in_ms(0, output
);
148 set_state_id(hdcp
, output
, H2_A0_KNOWN_HDCP2_CAPABLE_RX
);
149 } else if (is_cp_desired_hdcp1(hdcp
)) {
150 callback_in_ms(0, output
);
151 set_state_id(hdcp
, output
, H1_A0_WAIT_FOR_ACTIVE_RX
);
153 callback_in_ms(0, output
);
154 set_state_id(hdcp
, output
, HDCP_CP_NOT_DESIRED
);
157 callback_in_ms(0, output
);
158 set_state_id(hdcp
, output
, HDCP_CP_NOT_DESIRED
);
160 } else if (is_in_cp_not_desired_state(hdcp
)) {
161 increment_stay_counter(hdcp
);
162 } else if (is_in_hdcp1_states(hdcp
)) {
163 status
= mod_hdcp_hdcp1_transition(hdcp
,
164 event_ctx
, &input
->hdcp1
, output
);
165 } else if (is_in_hdcp1_dp_states(hdcp
)) {
166 status
= mod_hdcp_hdcp1_dp_transition(hdcp
,
167 event_ctx
, &input
->hdcp1
, output
);
168 } else if (is_in_hdcp2_states(hdcp
)) {
169 status
= mod_hdcp_hdcp2_transition(hdcp
,
170 event_ctx
, &input
->hdcp2
, output
);
171 } else if (is_in_hdcp2_dp_states(hdcp
)) {
172 status
= mod_hdcp_hdcp2_dp_transition(hdcp
,
173 event_ctx
, &input
->hdcp2
, output
);
175 status
= MOD_HDCP_STATUS_INVALID_STATE
;
181 static enum mod_hdcp_status
reset_authentication(struct mod_hdcp
*hdcp
,
182 struct mod_hdcp_output
*output
)
184 enum mod_hdcp_status status
= MOD_HDCP_STATUS_SUCCESS
;
186 if (is_hdcp1(hdcp
)) {
187 if (hdcp
->auth
.trans_input
.hdcp1
.create_session
!= UNKNOWN
) {
188 /* TODO - update psp to unify create session failure
189 * recovery between hdcp1 and 2.
191 mod_hdcp_hdcp1_destroy_session(hdcp
);
194 if (hdcp
->auth
.trans_input
.hdcp1
.add_topology
== PASS
) {
195 status
= mod_hdcp_remove_display_topology(hdcp
);
196 if (status
!= MOD_HDCP_STATUS_SUCCESS
) {
197 output
->callback_needed
= 0;
198 output
->watchdog_timer_needed
= 0;
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;
215 if (hdcp
->auth
.trans_input
.hdcp2
.add_topology
== PASS
) {
216 status
= mod_hdcp_remove_display_topology(hdcp
);
217 if (status
!= MOD_HDCP_STATUS_SUCCESS
) {
218 output
->callback_needed
= 0;
219 output
->watchdog_timer_needed
= 0;
223 HDCP_TOP_RESET_AUTH_TRACE(hdcp
);
224 memset(&hdcp
->auth
, 0, sizeof(struct mod_hdcp_authentication
));
225 memset(&hdcp
->state
, 0, sizeof(struct mod_hdcp_state
));
226 set_state_id(hdcp
, output
, HDCP_INITIALIZED
);
227 } else if (is_in_cp_not_desired_state(hdcp
)) {
228 status
= mod_hdcp_remove_display_topology(hdcp
);
229 if (status
!= MOD_HDCP_STATUS_SUCCESS
) {
230 output
->callback_needed
= 0;
231 output
->watchdog_timer_needed
= 0;
234 HDCP_TOP_RESET_AUTH_TRACE(hdcp
);
235 memset(&hdcp
->auth
, 0, sizeof(struct mod_hdcp_authentication
));
236 memset(&hdcp
->state
, 0, sizeof(struct mod_hdcp_state
));
237 set_state_id(hdcp
, output
, HDCP_INITIALIZED
);
241 /* stop callback and watchdog requests from previous authentication*/
242 output
->watchdog_timer_stop
= 1;
243 output
->callback_stop
= 1;
247 static enum mod_hdcp_status
reset_connection(struct mod_hdcp
*hdcp
,
248 struct mod_hdcp_output
*output
)
250 enum mod_hdcp_status status
= MOD_HDCP_STATUS_SUCCESS
;
252 memset(output
, 0, sizeof(struct mod_hdcp_output
));
254 status
= reset_authentication(hdcp
, output
);
255 if (status
!= MOD_HDCP_STATUS_SUCCESS
)
258 if (current_state(hdcp
) != HDCP_UNINITIALIZED
) {
259 HDCP_TOP_RESET_CONN_TRACE(hdcp
);
260 set_state_id(hdcp
, output
, HDCP_UNINITIALIZED
);
262 memset(&hdcp
->connection
, 0, sizeof(hdcp
->connection
));
268 * Implementation of functions in mod_hdcp.h
270 size_t mod_hdcp_get_memory_size(void)
272 return sizeof(struct mod_hdcp
);
275 enum mod_hdcp_status
mod_hdcp_setup(struct mod_hdcp
*hdcp
,
276 struct mod_hdcp_config
*config
)
278 struct mod_hdcp_output output
;
279 enum mod_hdcp_status status
= MOD_HDCP_STATUS_SUCCESS
;
281 memset(hdcp
, 0, sizeof(struct mod_hdcp
));
282 memset(&output
, 0, sizeof(output
));
283 hdcp
->config
= *config
;
284 HDCP_TOP_INTERFACE_TRACE(hdcp
);
285 status
= reset_connection(hdcp
, &output
);
286 if (status
!= MOD_HDCP_STATUS_SUCCESS
)
287 push_error_status(hdcp
, status
);
291 enum mod_hdcp_status
mod_hdcp_teardown(struct mod_hdcp
*hdcp
)
293 enum mod_hdcp_status status
= MOD_HDCP_STATUS_SUCCESS
;
294 struct mod_hdcp_output output
;
296 HDCP_TOP_INTERFACE_TRACE(hdcp
);
297 memset(&output
, 0, sizeof(output
));
298 status
= reset_connection(hdcp
, &output
);
299 if (status
== MOD_HDCP_STATUS_SUCCESS
)
300 memset(hdcp
, 0, sizeof(struct mod_hdcp
));
302 push_error_status(hdcp
, status
);
306 enum mod_hdcp_status
mod_hdcp_add_display(struct mod_hdcp
*hdcp
,
307 struct mod_hdcp_link
*link
, struct mod_hdcp_display
*display
,
308 struct mod_hdcp_output
*output
)
310 enum mod_hdcp_status status
= MOD_HDCP_STATUS_SUCCESS
;
311 struct mod_hdcp_display
*display_container
= NULL
;
313 HDCP_TOP_INTERFACE_TRACE_WITH_INDEX(hdcp
, display
->index
);
314 memset(output
, 0, sizeof(struct mod_hdcp_output
));
316 /* skip inactive display */
317 if (display
->state
!= MOD_HDCP_DISPLAY_ACTIVE
) {
318 status
= MOD_HDCP_STATUS_SUCCESS
;
322 /* check existing display container */
323 if (get_active_display_at_index(hdcp
, display
->index
)) {
324 status
= MOD_HDCP_STATUS_SUCCESS
;
328 /* find an empty display container */
329 display_container
= get_empty_display_container(hdcp
);
330 if (!display_container
) {
331 status
= MOD_HDCP_STATUS_DISPLAY_OUT_OF_BOUND
;
335 /* reset existing authentication status */
336 status
= reset_authentication(hdcp
, output
);
337 if (status
!= MOD_HDCP_STATUS_SUCCESS
)
340 /* add display to connection */
341 hdcp
->connection
.link
= *link
;
342 *display_container
= *display
;
344 /* reset retry counters */
345 reset_retry_counts(hdcp
);
347 /* reset error trace */
348 memset(&hdcp
->connection
.trace
, 0, sizeof(hdcp
->connection
.trace
));
350 /* request authentication */
351 if (current_state(hdcp
) != HDCP_INITIALIZED
)
352 set_state_id(hdcp
, output
, HDCP_INITIALIZED
);
353 callback_in_ms(hdcp
->connection
.link
.adjust
.auth_delay
* 1000, output
);
355 if (status
!= MOD_HDCP_STATUS_SUCCESS
)
356 push_error_status(hdcp
, status
);
361 enum mod_hdcp_status
mod_hdcp_remove_display(struct mod_hdcp
*hdcp
,
362 uint8_t index
, struct mod_hdcp_output
*output
)
364 enum mod_hdcp_status status
= MOD_HDCP_STATUS_SUCCESS
;
365 struct mod_hdcp_display
*display
= NULL
;
367 HDCP_TOP_INTERFACE_TRACE_WITH_INDEX(hdcp
, index
);
368 memset(output
, 0, sizeof(struct mod_hdcp_output
));
370 /* find display in connection */
371 display
= get_active_display_at_index(hdcp
, index
);
373 status
= MOD_HDCP_STATUS_SUCCESS
;
377 /* stop current authentication */
378 status
= reset_authentication(hdcp
, output
);
379 if (status
!= MOD_HDCP_STATUS_SUCCESS
)
383 display
->state
= MOD_HDCP_DISPLAY_INACTIVE
;
385 /* clear retry counters */
386 reset_retry_counts(hdcp
);
388 /* reset error trace */
389 memset(&hdcp
->connection
.trace
, 0, sizeof(hdcp
->connection
.trace
));
391 /* request authentication for remaining displays*/
392 if (get_active_display_count(hdcp
) > 0)
393 callback_in_ms(hdcp
->connection
.link
.adjust
.auth_delay
* 1000,
396 if (status
!= MOD_HDCP_STATUS_SUCCESS
)
397 push_error_status(hdcp
, status
);
401 enum mod_hdcp_status
mod_hdcp_query_display(struct mod_hdcp
*hdcp
,
402 uint8_t index
, struct mod_hdcp_display_query
*query
)
404 enum mod_hdcp_status status
= MOD_HDCP_STATUS_SUCCESS
;
405 struct mod_hdcp_display
*display
= NULL
;
407 /* find display in connection */
408 display
= get_active_display_at_index(hdcp
, index
);
410 status
= MOD_HDCP_STATUS_DISPLAY_NOT_FOUND
;
415 query
->link
= &hdcp
->connection
.link
;
416 query
->display
= display
;
417 query
->trace
= &hdcp
->connection
.trace
;
418 query
->encryption_status
= MOD_HDCP_ENCRYPTION_STATUS_HDCP_OFF
;
420 if (is_display_encryption_enabled(display
)) {
421 if (is_hdcp1(hdcp
)) {
422 query
->encryption_status
= MOD_HDCP_ENCRYPTION_STATUS_HDCP1_ON
;
423 } else if (is_hdcp2(hdcp
)) {
424 if (query
->link
->adjust
.hdcp2
.force_type
== MOD_HDCP_FORCE_TYPE_0
)
425 query
->encryption_status
= MOD_HDCP_ENCRYPTION_STATUS_HDCP2_TYPE0_ON
;
426 else if (query
->link
->adjust
.hdcp2
.force_type
== MOD_HDCP_FORCE_TYPE_1
)
427 query
->encryption_status
= MOD_HDCP_ENCRYPTION_STATUS_HDCP2_TYPE1_ON
;
429 query
->encryption_status
= MOD_HDCP_ENCRYPTION_STATUS_HDCP2_ON
;
432 query
->encryption_status
= MOD_HDCP_ENCRYPTION_STATUS_HDCP_OFF
;
439 enum mod_hdcp_status
mod_hdcp_reset_connection(struct mod_hdcp
*hdcp
,
440 struct mod_hdcp_output
*output
)
442 enum mod_hdcp_status status
= MOD_HDCP_STATUS_SUCCESS
;
444 HDCP_TOP_INTERFACE_TRACE(hdcp
);
445 status
= reset_connection(hdcp
, output
);
446 if (status
!= MOD_HDCP_STATUS_SUCCESS
)
447 push_error_status(hdcp
, status
);
452 enum mod_hdcp_status
mod_hdcp_process_event(struct mod_hdcp
*hdcp
,
453 enum mod_hdcp_event event
, struct mod_hdcp_output
*output
)
455 enum mod_hdcp_status exec_status
, trans_status
, reset_status
, status
;
456 struct mod_hdcp_event_context event_ctx
;
458 HDCP_EVENT_TRACE(hdcp
, event
);
459 memset(output
, 0, sizeof(struct mod_hdcp_output
));
460 memset(&event_ctx
, 0, sizeof(struct mod_hdcp_event_context
));
461 event_ctx
.event
= event
;
463 /* execute and transition */
464 exec_status
= execution(hdcp
, &event_ctx
, &hdcp
->auth
.trans_input
);
465 trans_status
= transition(
466 hdcp
, &event_ctx
, &hdcp
->auth
.trans_input
, output
);
467 if (trans_status
== MOD_HDCP_STATUS_SUCCESS
) {
468 status
= MOD_HDCP_STATUS_SUCCESS
;
469 } else if (exec_status
== MOD_HDCP_STATUS_SUCCESS
) {
470 status
= MOD_HDCP_STATUS_INTERNAL_POLICY_FAILURE
;
471 push_error_status(hdcp
, status
);
473 status
= exec_status
;
474 push_error_status(hdcp
, status
);
477 /* reset authentication if needed */
478 if (trans_status
== MOD_HDCP_STATUS_RESET_NEEDED
) {
479 HDCP_FULL_DDC_TRACE(hdcp
);
480 reset_status
= reset_authentication(hdcp
, output
);
481 if (reset_status
!= MOD_HDCP_STATUS_SUCCESS
)
482 push_error_status(hdcp
, reset_status
);
487 enum mod_hdcp_operation_mode
mod_hdcp_signal_type_to_operation_mode(
488 enum signal_type signal
)
490 enum mod_hdcp_operation_mode mode
= MOD_HDCP_MODE_OFF
;
493 case SIGNAL_TYPE_DVI_SINGLE_LINK
:
494 case SIGNAL_TYPE_HDMI_TYPE_A
:
495 mode
= MOD_HDCP_MODE_DEFAULT
;
497 case SIGNAL_TYPE_EDP
:
498 case SIGNAL_TYPE_DISPLAY_PORT
:
499 mode
= MOD_HDCP_MODE_DP
;
501 case SIGNAL_TYPE_DISPLAY_PORT_MST
:
502 mode
= MOD_HDCP_MODE_DP_MST
;