Home | History | Annotate | Line # | Download | only in hdcp
      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 
     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 
    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