xref: /netbsd-src/sys/external/bsd/drm2/dist/drm/amd/display/modules/hdcp/amdgpu_hdcp1_transition.c (revision 41ec02673d281bbb3d38e6c78504ce6e30c228c1)
1 /*	$NetBSD: amdgpu_hdcp1_transition.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_hdcp1_transition.c,v 1.2 2021/12/18 23:45:07 riastradh Exp $");
30 
31 #include "hdcp.h"
32 
mod_hdcp_hdcp1_transition(struct mod_hdcp * hdcp,struct mod_hdcp_event_context * event_ctx,struct mod_hdcp_transition_input_hdcp1 * input,struct mod_hdcp_output * output)33 enum mod_hdcp_status mod_hdcp_hdcp1_transition(struct mod_hdcp *hdcp,
34 		struct mod_hdcp_event_context *event_ctx,
35 		struct mod_hdcp_transition_input_hdcp1 *input,
36 		struct mod_hdcp_output *output)
37 {
38 	enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS;
39 	struct mod_hdcp_connection *conn = &hdcp->connection;
40 	struct mod_hdcp_link_adjustment *adjust = &hdcp->connection.link.adjust;
41 
42 	switch (current_state(hdcp)) {
43 	case H1_A0_WAIT_FOR_ACTIVE_RX:
44 		if (input->bksv_read != PASS || input->bcaps_read != PASS) {
45 			/* 1A-04: repeatedly attempts on port access failure */
46 			callback_in_ms(500, output);
47 			increment_stay_counter(hdcp);
48 			break;
49 		}
50 		callback_in_ms(0, output);
51 		set_state_id(hdcp, output, H1_A1_EXCHANGE_KSVS);
52 		break;
53 	case H1_A1_EXCHANGE_KSVS:
54 		if (input->add_topology != PASS ||
55 				input->create_session != PASS) {
56 			/* out of sync with psp state */
57 			adjust->hdcp1.disable = 1;
58 			fail_and_restart_in_ms(0, &status, output);
59 			break;
60 		} else if (input->an_write != PASS ||
61 				input->aksv_write != PASS ||
62 				input->bksv_read != PASS ||
63 				input->bksv_validation != PASS ||
64 				input->ainfo_write == FAIL) {
65 			/* 1A-05: consider invalid bksv a failure */
66 			fail_and_restart_in_ms(0, &status, output);
67 			break;
68 		}
69 		callback_in_ms(300, output);
70 		set_state_id(hdcp, output,
71 			H1_A2_COMPUTATIONS_A3_VALIDATE_RX_A6_TEST_FOR_REPEATER);
72 		break;
73 	case H1_A2_COMPUTATIONS_A3_VALIDATE_RX_A6_TEST_FOR_REPEATER:
74 		if (input->bcaps_read != PASS ||
75 				input->r0p_read != PASS) {
76 			fail_and_restart_in_ms(0, &status, output);
77 			break;
78 		} else if (input->rx_validation != PASS) {
79 			/* 1A-06: consider invalid r0' a failure */
80 			/* 1A-08: consider bksv listed in SRM a failure */
81 			/*
82 			 * some slow RX will fail rx validation when it is
83 			 * not ready. give it more time to react before retry.
84 			 */
85 			fail_and_restart_in_ms(1000, &status, output);
86 			break;
87 		} else if (!conn->is_repeater && input->encryption != PASS) {
88 			fail_and_restart_in_ms(0, &status, output);
89 			break;
90 		}
91 		if (conn->is_repeater) {
92 			callback_in_ms(0, output);
93 			set_watchdog_in_ms(hdcp, 5000, output);
94 			set_state_id(hdcp, output, H1_A8_WAIT_FOR_READY);
95 		} else {
96 			callback_in_ms(0, output);
97 			set_state_id(hdcp, output, H1_A45_AUTHENTICATED);
98 			HDCP_FULL_DDC_TRACE(hdcp);
99 		}
100 		break;
101 	case H1_A45_AUTHENTICATED:
102 		if (input->link_maintenance != PASS) {
103 			/* 1A-07: consider invalid ri' a failure */
104 			/* 1A-07a: consider read ri' not returned a failure */
105 			fail_and_restart_in_ms(0, &status, output);
106 			break;
107 		}
108 		callback_in_ms(500, output);
109 		increment_stay_counter(hdcp);
110 		break;
111 	case H1_A8_WAIT_FOR_READY:
112 		if (input->ready_check != PASS) {
113 			if (event_ctx->event ==
114 					MOD_HDCP_EVENT_WATCHDOG_TIMEOUT) {
115 				/* 1B-03: fail hdcp on ksv list READY timeout */
116 				/* prevent black screen in next attempt */
117 				adjust->hdcp1.postpone_encryption = 1;
118 				fail_and_restart_in_ms(0, &status, output);
119 			} else {
120 				/* continue ksv list READY polling*/
121 				callback_in_ms(500, output);
122 				increment_stay_counter(hdcp);
123 			}
124 			break;
125 		}
126 		callback_in_ms(0, output);
127 		set_state_id(hdcp, output, H1_A9_READ_KSV_LIST);
128 		break;
129 	case H1_A9_READ_KSV_LIST:
130 		if (input->bstatus_read != PASS ||
131 				input->max_cascade_check != PASS ||
132 				input->max_devs_check != PASS ||
133 				input->device_count_check != PASS ||
134 				input->ksvlist_read != PASS ||
135 				input->vp_read != PASS ||
136 				input->ksvlist_vp_validation != PASS ||
137 				input->encryption != PASS) {
138 			/* 1B-06: consider MAX_CASCADE_EXCEEDED a failure */
139 			/* 1B-05: consider MAX_DEVS_EXCEEDED a failure */
140 			/* 1B-04: consider invalid v' a failure */
141 			fail_and_restart_in_ms(0, &status, output);
142 			break;
143 		}
144 		callback_in_ms(0, output);
145 		set_state_id(hdcp, output, H1_A45_AUTHENTICATED);
146 		HDCP_FULL_DDC_TRACE(hdcp);
147 		break;
148 	default:
149 		status = MOD_HDCP_STATUS_INVALID_STATE;
150 		fail_and_restart_in_ms(0, &status, output);
151 		break;
152 	}
153 
154 	return status;
155 }
156 
mod_hdcp_hdcp1_dp_transition(struct mod_hdcp * hdcp,struct mod_hdcp_event_context * event_ctx,struct mod_hdcp_transition_input_hdcp1 * input,struct mod_hdcp_output * output)157 enum mod_hdcp_status mod_hdcp_hdcp1_dp_transition(struct mod_hdcp *hdcp,
158 		struct mod_hdcp_event_context *event_ctx,
159 		struct mod_hdcp_transition_input_hdcp1 *input,
160 		struct mod_hdcp_output *output)
161 {
162 	enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS;
163 	struct mod_hdcp_connection *conn = &hdcp->connection;
164 	struct mod_hdcp_link_adjustment *adjust = &hdcp->connection.link.adjust;
165 
166 	switch (current_state(hdcp)) {
167 	case D1_A0_DETERMINE_RX_HDCP_CAPABLE:
168 		if (input->bcaps_read != PASS) {
169 			/* 1A-04: no authentication on bcaps read failure */
170 			fail_and_restart_in_ms(0, &status, output);
171 			break;
172 		} else if (input->hdcp_capable_dp != PASS) {
173 			adjust->hdcp1.disable = 1;
174 			fail_and_restart_in_ms(0, &status, output);
175 			break;
176 		}
177 		callback_in_ms(0, output);
178 		set_state_id(hdcp, output, D1_A1_EXCHANGE_KSVS);
179 		break;
180 	case D1_A1_EXCHANGE_KSVS:
181 		if (input->add_topology != PASS ||
182 				input->create_session != PASS) {
183 			/* out of sync with psp state */
184 			adjust->hdcp1.disable = 1;
185 			fail_and_restart_in_ms(0, &status, output);
186 			break;
187 		} else if (input->an_write != PASS ||
188 				input->aksv_write != PASS ||
189 				input->bksv_read != PASS ||
190 				input->bksv_validation != PASS ||
191 				input->ainfo_write == FAIL) {
192 			/* 1A-05: consider invalid bksv a failure */
193 			fail_and_restart_in_ms(0, &status, output);
194 			break;
195 		}
196 		set_watchdog_in_ms(hdcp, 100, output);
197 		set_state_id(hdcp, output, D1_A23_WAIT_FOR_R0_PRIME);
198 		break;
199 	case D1_A23_WAIT_FOR_R0_PRIME:
200 		if (input->bstatus_read != PASS) {
201 			fail_and_restart_in_ms(0, &status, output);
202 			break;
203 		} else if (input->r0p_available_dp != PASS) {
204 			if (event_ctx->event == MOD_HDCP_EVENT_WATCHDOG_TIMEOUT)
205 				fail_and_restart_in_ms(0, &status, output);
206 			else
207 				increment_stay_counter(hdcp);
208 			break;
209 		}
210 		callback_in_ms(0, output);
211 		set_state_id(hdcp, output, D1_A2_COMPUTATIONS_A3_VALIDATE_RX_A5_TEST_FOR_REPEATER);
212 		break;
213 	case D1_A2_COMPUTATIONS_A3_VALIDATE_RX_A5_TEST_FOR_REPEATER:
214 		if (input->r0p_read != PASS) {
215 			fail_and_restart_in_ms(0, &status, output);
216 			break;
217 		} else if (input->rx_validation != PASS) {
218 			if (hdcp->state.stay_count < 2) {
219 				/* allow 2 additional retries */
220 				callback_in_ms(0, output);
221 				increment_stay_counter(hdcp);
222 			} else {
223 				/*
224 				 * 1A-06: consider invalid r0' a failure
225 				 * after 3 attempts.
226 				 * 1A-08: consider bksv listed in SRM a failure
227 				 */
228 				/*
229 				 * some slow RX will fail rx validation when it is
230 				 * not ready. give it more time to react before retry.
231 				 */
232 				fail_and_restart_in_ms(1000, &status, output);
233 			}
234 			break;
235 		} else if ((!conn->is_repeater && input->encryption != PASS) ||
236 				(!conn->is_repeater && is_dp_mst_hdcp(hdcp) && input->stream_encryption_dp != PASS)) {
237 			fail_and_restart_in_ms(0, &status, output);
238 			break;
239 		}
240 		if (conn->is_repeater) {
241 			set_watchdog_in_ms(hdcp, 5000, output);
242 			set_state_id(hdcp, output, D1_A6_WAIT_FOR_READY);
243 		} else {
244 			set_state_id(hdcp, output, D1_A4_AUTHENTICATED);
245 			HDCP_FULL_DDC_TRACE(hdcp);
246 		}
247 		break;
248 	case D1_A4_AUTHENTICATED:
249 		if (input->link_integrity_check != PASS ||
250 				input->reauth_request_check != PASS) {
251 			/* 1A-07: restart hdcp on a link integrity failure */
252 			fail_and_restart_in_ms(0, &status, output);
253 			break;
254 		}
255 		break;
256 	case D1_A6_WAIT_FOR_READY:
257 		if (input->link_integrity_check == FAIL ||
258 				input->reauth_request_check == FAIL) {
259 			fail_and_restart_in_ms(0, &status, output);
260 			break;
261 		} else if (input->ready_check != PASS) {
262 			if (event_ctx->event ==
263 					MOD_HDCP_EVENT_WATCHDOG_TIMEOUT) {
264 				/* 1B-04: fail hdcp on ksv list READY timeout */
265 				/* prevent black screen in next attempt */
266 				adjust->hdcp1.postpone_encryption = 1;
267 				fail_and_restart_in_ms(0, &status, output);
268 			} else {
269 				increment_stay_counter(hdcp);
270 			}
271 			break;
272 		}
273 		callback_in_ms(0, output);
274 		set_state_id(hdcp, output, D1_A7_READ_KSV_LIST);
275 		break;
276 	case D1_A7_READ_KSV_LIST:
277 		if (input->binfo_read_dp != PASS ||
278 				input->max_cascade_check != PASS ||
279 				input->max_devs_check != PASS) {
280 			/* 1B-06: consider MAX_DEVS_EXCEEDED a failure */
281 			/* 1B-07: consider MAX_CASCADE_EXCEEDED a failure */
282 			fail_and_restart_in_ms(0, &status, output);
283 			break;
284 		} else if (input->device_count_check != PASS) {
285 			/*
286 			 * some slow dongle doesn't update
287 			 * device count as soon as downstream is connected.
288 			 * give it more time to react.
289 			 */
290 			adjust->hdcp1.postpone_encryption = 1;
291 			fail_and_restart_in_ms(1000, &status, output);
292 			break;
293 		} else if (input->ksvlist_read != PASS ||
294 				input->vp_read != PASS) {
295 			fail_and_restart_in_ms(0, &status, output);
296 			break;
297 		} else if (input->ksvlist_vp_validation != PASS) {
298 			if (hdcp->state.stay_count < 2) {
299 				/* allow 2 additional retries */
300 				callback_in_ms(0, output);
301 				increment_stay_counter(hdcp);
302 			} else {
303 				/*
304 				 * 1B-05: consider invalid v' a failure
305 				 * after 3 attempts.
306 				 */
307 				fail_and_restart_in_ms(0, &status, output);
308 			}
309 			break;
310 		} else if (input->encryption != PASS ||
311 				(is_dp_mst_hdcp(hdcp) && input->stream_encryption_dp != PASS)) {
312 			fail_and_restart_in_ms(0, &status, output);
313 			break;
314 		}
315 		set_state_id(hdcp, output, D1_A4_AUTHENTICATED);
316 		HDCP_FULL_DDC_TRACE(hdcp);
317 		break;
318 	default:
319 		fail_and_restart_in_ms(0, &status, output);
320 		break;
321 	}
322 
323 	return status;
324 }
325