linux/drivers/gpu/drm/amd/display/modules/hdcp/hdcp2_execution.c
Isabel Zhang 2deade5ede drm/amd/display: Remove hdcp display state with mst fix
[Why]
Due to previous code changes, displays transition from active to active
and added state immediately, making it redundant to have both display
states. Previous change to fix this caused HDCP to get into a bad state
when monitor is connected to MST hub, this change fixes that issue.

[How]
Change code behavior so when a device is added successfully the state
remains as active and when addition is unsuccessful change state to
inactive. This removes need for added and active state.

Signed-off-by: Isabel Zhang <isabel.zhang@amd.com>
Reviewed-by: Wenjing Liu <Wenjing.Liu@amd.com>
Acked-by: Rodrigo Siqueira <Rodrigo.Siqueira@amd.com>
Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
2020-04-09 10:43:16 -04:00

885 lines
26 KiB
C

/*
* Copyright 2018 Advanced Micro Devices, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* Authors: AMD
*
*/
#include <linux/delay.h>
#include "hdcp.h"
static inline enum mod_hdcp_status check_receiver_id_list_ready(struct mod_hdcp *hdcp)
{
uint8_t is_ready = 0;
if (is_dp_hdcp(hdcp))
is_ready = HDCP_2_2_DP_RXSTATUS_READY(hdcp->auth.msg.hdcp2.rxstatus_dp) ? 1 : 0;
else
is_ready = (HDCP_2_2_HDMI_RXSTATUS_READY(hdcp->auth.msg.hdcp2.rxstatus[1]) &&
(HDCP_2_2_HDMI_RXSTATUS_MSG_SZ_HI(hdcp->auth.msg.hdcp2.rxstatus[1]) << 8 |
hdcp->auth.msg.hdcp2.rxstatus[0])) ? 1 : 0;
return is_ready ? MOD_HDCP_STATUS_SUCCESS :
MOD_HDCP_STATUS_HDCP2_RX_ID_LIST_NOT_READY;
}
static inline enum mod_hdcp_status check_hdcp2_capable(struct mod_hdcp *hdcp)
{
enum mod_hdcp_status status;
if (is_dp_hdcp(hdcp))
status = (hdcp->auth.msg.hdcp2.rxcaps_dp[0] == HDCP_2_2_RX_CAPS_VERSION_VAL) &&
HDCP_2_2_DP_HDCP_CAPABLE(hdcp->auth.msg.hdcp2.rxcaps_dp[2]) ?
MOD_HDCP_STATUS_SUCCESS :
MOD_HDCP_STATUS_HDCP2_NOT_CAPABLE;
else
status = (hdcp->auth.msg.hdcp2.hdcp2version_hdmi & HDCP_2_2_HDMI_SUPPORT_MASK) ?
MOD_HDCP_STATUS_SUCCESS :
MOD_HDCP_STATUS_HDCP2_NOT_CAPABLE;
return status;
}
static inline enum mod_hdcp_status check_reauthentication_request(
struct mod_hdcp *hdcp)
{
uint8_t ret = 0;
if (is_dp_hdcp(hdcp))
ret = HDCP_2_2_DP_RXSTATUS_REAUTH_REQ(hdcp->auth.msg.hdcp2.rxstatus_dp) ?
MOD_HDCP_STATUS_HDCP2_REAUTH_REQUEST :
MOD_HDCP_STATUS_SUCCESS;
else
ret = HDCP_2_2_HDMI_RXSTATUS_REAUTH_REQ(hdcp->auth.msg.hdcp2.rxstatus[1]) ?
MOD_HDCP_STATUS_HDCP2_REAUTH_REQUEST :
MOD_HDCP_STATUS_SUCCESS;
return ret;
}
static inline enum mod_hdcp_status check_link_integrity_failure_dp(
struct mod_hdcp *hdcp)
{
return HDCP_2_2_DP_RXSTATUS_LINK_FAILED(hdcp->auth.msg.hdcp2.rxstatus_dp) ?
MOD_HDCP_STATUS_HDCP2_REAUTH_LINK_INTEGRITY_FAILURE :
MOD_HDCP_STATUS_SUCCESS;
}
static enum mod_hdcp_status check_ake_cert_available(struct mod_hdcp *hdcp)
{
enum mod_hdcp_status status;
uint16_t size;
if (is_dp_hdcp(hdcp)) {
status = MOD_HDCP_STATUS_SUCCESS;
} else {
status = mod_hdcp_read_rxstatus(hdcp);
if (status == MOD_HDCP_STATUS_SUCCESS) {
size = HDCP_2_2_HDMI_RXSTATUS_MSG_SZ_HI(hdcp->auth.msg.hdcp2.rxstatus[1]) << 8 |
hdcp->auth.msg.hdcp2.rxstatus[0];
status = (size == sizeof(hdcp->auth.msg.hdcp2.ake_cert)) ?
MOD_HDCP_STATUS_SUCCESS :
MOD_HDCP_STATUS_HDCP2_AKE_CERT_PENDING;
}
}
return status;
}
static enum mod_hdcp_status check_h_prime_available(struct mod_hdcp *hdcp)
{
enum mod_hdcp_status status;
uint8_t size;
status = mod_hdcp_read_rxstatus(hdcp);
if (status != MOD_HDCP_STATUS_SUCCESS)
goto out;
if (is_dp_hdcp(hdcp)) {
status = HDCP_2_2_DP_RXSTATUS_H_PRIME(hdcp->auth.msg.hdcp2.rxstatus_dp) ?
MOD_HDCP_STATUS_SUCCESS :
MOD_HDCP_STATUS_HDCP2_H_PRIME_PENDING;
} else {
size = HDCP_2_2_HDMI_RXSTATUS_MSG_SZ_HI(hdcp->auth.msg.hdcp2.rxstatus[1]) << 8 |
hdcp->auth.msg.hdcp2.rxstatus[0];
status = (size == sizeof(hdcp->auth.msg.hdcp2.ake_h_prime)) ?
MOD_HDCP_STATUS_SUCCESS :
MOD_HDCP_STATUS_HDCP2_H_PRIME_PENDING;
}
out:
return status;
}
static enum mod_hdcp_status check_pairing_info_available(struct mod_hdcp *hdcp)
{
enum mod_hdcp_status status;
uint8_t size;
status = mod_hdcp_read_rxstatus(hdcp);
if (status != MOD_HDCP_STATUS_SUCCESS)
goto out;
if (is_dp_hdcp(hdcp)) {
status = HDCP_2_2_DP_RXSTATUS_PAIRING(hdcp->auth.msg.hdcp2.rxstatus_dp) ?
MOD_HDCP_STATUS_SUCCESS :
MOD_HDCP_STATUS_HDCP2_PAIRING_INFO_PENDING;
} else {
size = HDCP_2_2_HDMI_RXSTATUS_MSG_SZ_HI(hdcp->auth.msg.hdcp2.rxstatus[1]) << 8 |
hdcp->auth.msg.hdcp2.rxstatus[0];
status = (size == sizeof(hdcp->auth.msg.hdcp2.ake_pairing_info)) ?
MOD_HDCP_STATUS_SUCCESS :
MOD_HDCP_STATUS_HDCP2_PAIRING_INFO_PENDING;
}
out:
return status;
}
static enum mod_hdcp_status poll_l_prime_available(struct mod_hdcp *hdcp)
{
enum mod_hdcp_status status;
uint8_t size;
uint16_t max_wait = 20; // units of ms
uint16_t num_polls = 5;
uint16_t wait_time = max_wait / num_polls;
if (is_dp_hdcp(hdcp))
status = MOD_HDCP_STATUS_INVALID_OPERATION;
else
for (; num_polls; num_polls--) {
msleep(wait_time);
status = mod_hdcp_read_rxstatus(hdcp);
if (status != MOD_HDCP_STATUS_SUCCESS)
break;
size = HDCP_2_2_HDMI_RXSTATUS_MSG_SZ_HI(hdcp->auth.msg.hdcp2.rxstatus[1]) << 8 |
hdcp->auth.msg.hdcp2.rxstatus[0];
status = (size == sizeof(hdcp->auth.msg.hdcp2.lc_l_prime)) ?
MOD_HDCP_STATUS_SUCCESS :
MOD_HDCP_STATUS_HDCP2_L_PRIME_PENDING;
if (status == MOD_HDCP_STATUS_SUCCESS)
break;
}
return status;
}
static enum mod_hdcp_status check_stream_ready_available(struct mod_hdcp *hdcp)
{
enum mod_hdcp_status status;
uint8_t size;
if (is_dp_hdcp(hdcp)) {
status = MOD_HDCP_STATUS_INVALID_OPERATION;
} else {
status = mod_hdcp_read_rxstatus(hdcp);
if (status != MOD_HDCP_STATUS_SUCCESS)
goto out;
size = HDCP_2_2_HDMI_RXSTATUS_MSG_SZ_HI(hdcp->auth.msg.hdcp2.rxstatus[1]) << 8 |
hdcp->auth.msg.hdcp2.rxstatus[0];
status = (size == sizeof(hdcp->auth.msg.hdcp2.repeater_auth_stream_ready)) ?
MOD_HDCP_STATUS_SUCCESS :
MOD_HDCP_STATUS_HDCP2_STREAM_READY_PENDING;
}
out:
return status;
}
static inline uint8_t get_device_count(struct mod_hdcp *hdcp)
{
return HDCP_2_2_DEV_COUNT_LO(hdcp->auth.msg.hdcp2.rx_id_list[2]) +
(HDCP_2_2_DEV_COUNT_HI(hdcp->auth.msg.hdcp2.rx_id_list[1]) << 4);
}
static enum mod_hdcp_status check_device_count(struct mod_hdcp *hdcp)
{
/* device count must be greater than or equal to tracked hdcp displays */
return (get_device_count(hdcp) < get_active_display_count(hdcp)) ?
MOD_HDCP_STATUS_HDCP2_DEVICE_COUNT_MISMATCH_FAILURE :
MOD_HDCP_STATUS_SUCCESS;
}
static uint8_t process_rxstatus(struct mod_hdcp *hdcp,
struct mod_hdcp_event_context *event_ctx,
struct mod_hdcp_transition_input_hdcp2 *input,
enum mod_hdcp_status *status)
{
if (!mod_hdcp_execute_and_set(mod_hdcp_read_rxstatus,
&input->rxstatus_read, status,
hdcp, "rxstatus_read"))
goto out;
if (!mod_hdcp_execute_and_set(check_reauthentication_request,
&input->reauth_request_check, status,
hdcp, "reauth_request_check"))
goto out;
if (is_dp_hdcp(hdcp)) {
if (!mod_hdcp_execute_and_set(check_link_integrity_failure_dp,
&input->link_integrity_check_dp, status,
hdcp, "link_integrity_check_dp"))
goto out;
}
if (hdcp->connection.is_repeater)
if (check_receiver_id_list_ready(hdcp) ==
MOD_HDCP_STATUS_SUCCESS) {
HDCP_INPUT_PASS_TRACE(hdcp, "rx_id_list_ready");
event_ctx->rx_id_list_ready = 1;
if (is_dp_hdcp(hdcp))
hdcp->auth.msg.hdcp2.rx_id_list_size =
sizeof(hdcp->auth.msg.hdcp2.rx_id_list);
else
hdcp->auth.msg.hdcp2.rx_id_list_size =
HDCP_2_2_HDMI_RXSTATUS_MSG_SZ_HI(hdcp->auth.msg.hdcp2.rxstatus[1]) << 8 |
hdcp->auth.msg.hdcp2.rxstatus[0];
}
out:
return (*status == MOD_HDCP_STATUS_SUCCESS);
}
static enum mod_hdcp_status known_hdcp2_capable_rx(struct mod_hdcp *hdcp,
struct mod_hdcp_event_context *event_ctx,
struct mod_hdcp_transition_input_hdcp2 *input)
{
enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS;
if (event_ctx->event != MOD_HDCP_EVENT_CALLBACK) {
event_ctx->unexpected_event = 1;
goto out;
}
if (!mod_hdcp_execute_and_set(mod_hdcp_read_hdcp2version,
&input->hdcp2version_read, &status,
hdcp, "hdcp2version_read"))
goto out;
if (!mod_hdcp_execute_and_set(check_hdcp2_capable,
&input->hdcp2_capable_check, &status,
hdcp, "hdcp2_capable"))
goto out;
out:
return status;
}
static enum mod_hdcp_status send_ake_init(struct mod_hdcp *hdcp,
struct mod_hdcp_event_context *event_ctx,
struct mod_hdcp_transition_input_hdcp2 *input)
{
enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS;
if (event_ctx->event != MOD_HDCP_EVENT_CALLBACK) {
event_ctx->unexpected_event = 1;
goto out;
}
if (!mod_hdcp_execute_and_set(mod_hdcp_hdcp2_create_session,
&input->create_session, &status,
hdcp, "create_session"))
goto out;
if (!mod_hdcp_execute_and_set(mod_hdcp_hdcp2_prepare_ake_init,
&input->ake_init_prepare, &status,
hdcp, "ake_init_prepare"))
goto out;
if (!mod_hdcp_execute_and_set(mod_hdcp_write_ake_init,
&input->ake_init_write, &status,
hdcp, "ake_init_write"))
goto out;
out:
return status;
}
static enum mod_hdcp_status validate_ake_cert(struct mod_hdcp *hdcp,
struct mod_hdcp_event_context *event_ctx,
struct mod_hdcp_transition_input_hdcp2 *input)
{
enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS;
if (event_ctx->event != MOD_HDCP_EVENT_CALLBACK &&
event_ctx->event != MOD_HDCP_EVENT_WATCHDOG_TIMEOUT) {
event_ctx->unexpected_event = 1;
goto out;
}
if (is_hdmi_dvi_sl_hdcp(hdcp))
if (!mod_hdcp_execute_and_set(check_ake_cert_available,
&input->ake_cert_available, &status,
hdcp, "ake_cert_available"))
goto out;
if (!mod_hdcp_execute_and_set(mod_hdcp_read_ake_cert,
&input->ake_cert_read, &status,
hdcp, "ake_cert_read"))
goto out;
if (!mod_hdcp_execute_and_set(mod_hdcp_hdcp2_validate_ake_cert,
&input->ake_cert_validation, &status,
hdcp, "ake_cert_validation"))
goto out;
out:
return status;
}
static enum mod_hdcp_status send_no_stored_km(struct mod_hdcp *hdcp,
struct mod_hdcp_event_context *event_ctx,
struct mod_hdcp_transition_input_hdcp2 *input)
{
enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS;
if (event_ctx->event != MOD_HDCP_EVENT_CALLBACK) {
event_ctx->unexpected_event = 1;
goto out;
}
if (!mod_hdcp_execute_and_set(mod_hdcp_write_no_stored_km,
&input->no_stored_km_write, &status,
hdcp, "no_stored_km_write"))
goto out;
out:
return status;
}
static enum mod_hdcp_status read_h_prime(struct mod_hdcp *hdcp,
struct mod_hdcp_event_context *event_ctx,
struct mod_hdcp_transition_input_hdcp2 *input)
{
enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS;
if (event_ctx->event != MOD_HDCP_EVENT_CALLBACK &&
event_ctx->event != MOD_HDCP_EVENT_CPIRQ &&
event_ctx->event != MOD_HDCP_EVENT_WATCHDOG_TIMEOUT) {
event_ctx->unexpected_event = 1;
goto out;
}
if (!mod_hdcp_execute_and_set(check_h_prime_available,
&input->h_prime_available, &status,
hdcp, "h_prime_available"))
goto out;
if (!mod_hdcp_execute_and_set(mod_hdcp_read_h_prime,
&input->h_prime_read, &status,
hdcp, "h_prime_read"))
goto out;
out:
return status;
}
static enum mod_hdcp_status read_pairing_info_and_validate_h_prime(
struct mod_hdcp *hdcp,
struct mod_hdcp_event_context *event_ctx,
struct mod_hdcp_transition_input_hdcp2 *input)
{
enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS;
if (event_ctx->event != MOD_HDCP_EVENT_CALLBACK &&
event_ctx->event != MOD_HDCP_EVENT_CPIRQ &&
event_ctx->event != MOD_HDCP_EVENT_WATCHDOG_TIMEOUT) {
event_ctx->unexpected_event = 1;
goto out;
}
if (!mod_hdcp_execute_and_set(check_pairing_info_available,
&input->pairing_available, &status,
hdcp, "pairing_available"))
goto out;
if (!mod_hdcp_execute_and_set(mod_hdcp_read_pairing_info,
&input->pairing_info_read, &status,
hdcp, "pairing_info_read"))
goto out;
if (!mod_hdcp_execute_and_set(mod_hdcp_hdcp2_validate_h_prime,
&input->h_prime_validation, &status,
hdcp, "h_prime_validation"))
goto out;
out:
return status;
}
static enum mod_hdcp_status send_stored_km(struct mod_hdcp *hdcp,
struct mod_hdcp_event_context *event_ctx,
struct mod_hdcp_transition_input_hdcp2 *input)
{
enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS;
if (event_ctx->event != MOD_HDCP_EVENT_CALLBACK) {
event_ctx->unexpected_event = 1;
goto out;
}
if (!mod_hdcp_execute_and_set(mod_hdcp_write_stored_km,
&input->stored_km_write, &status,
hdcp, "stored_km_write"))
goto out;
out:
return status;
}
static enum mod_hdcp_status validate_h_prime(struct mod_hdcp *hdcp,
struct mod_hdcp_event_context *event_ctx,
struct mod_hdcp_transition_input_hdcp2 *input)
{
enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS;
if (event_ctx->event != MOD_HDCP_EVENT_CALLBACK &&
event_ctx->event != MOD_HDCP_EVENT_CPIRQ &&
event_ctx->event != MOD_HDCP_EVENT_WATCHDOG_TIMEOUT) {
event_ctx->unexpected_event = 1;
goto out;
}
if (!mod_hdcp_execute_and_set(check_h_prime_available,
&input->h_prime_available, &status,
hdcp, "h_prime_available"))
goto out;
if (!mod_hdcp_execute_and_set(mod_hdcp_read_h_prime,
&input->h_prime_read, &status,
hdcp, "h_prime_read"))
goto out;
if (!mod_hdcp_execute_and_set(mod_hdcp_hdcp2_validate_h_prime,
&input->h_prime_validation, &status,
hdcp, "h_prime_validation"))
goto out;
out:
return status;
}
static enum mod_hdcp_status locality_check(struct mod_hdcp *hdcp,
struct mod_hdcp_event_context *event_ctx,
struct mod_hdcp_transition_input_hdcp2 *input)
{
enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS;
if (event_ctx->event != MOD_HDCP_EVENT_CALLBACK) {
event_ctx->unexpected_event = 1;
goto out;
}
if (!mod_hdcp_execute_and_set(mod_hdcp_hdcp2_prepare_lc_init,
&input->lc_init_prepare, &status,
hdcp, "lc_init_prepare"))
goto out;
if (!mod_hdcp_execute_and_set(mod_hdcp_write_lc_init,
&input->lc_init_write, &status,
hdcp, "lc_init_write"))
goto out;
if (is_dp_hdcp(hdcp))
msleep(16);
else
if (!mod_hdcp_execute_and_set(poll_l_prime_available,
&input->l_prime_available_poll, &status,
hdcp, "l_prime_available_poll"))
goto out;
if (!mod_hdcp_execute_and_set(mod_hdcp_read_l_prime,
&input->l_prime_read, &status,
hdcp, "l_prime_read"))
goto out;
if (!mod_hdcp_execute_and_set(mod_hdcp_hdcp2_validate_l_prime,
&input->l_prime_validation, &status,
hdcp, "l_prime_validation"))
goto out;
out:
return status;
}
static enum mod_hdcp_status exchange_ks_and_test_for_repeater(struct mod_hdcp *hdcp,
struct mod_hdcp_event_context *event_ctx,
struct mod_hdcp_transition_input_hdcp2 *input)
{
enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS;
if (event_ctx->event != MOD_HDCP_EVENT_CALLBACK) {
event_ctx->unexpected_event = 1;
goto out;
}
if (!mod_hdcp_execute_and_set(mod_hdcp_hdcp2_prepare_eks,
&input->eks_prepare, &status,
hdcp, "eks_prepare"))
goto out;
if (!mod_hdcp_execute_and_set(mod_hdcp_write_eks,
&input->eks_write, &status,
hdcp, "eks_write"))
goto out;
out:
return status;
}
static enum mod_hdcp_status enable_encryption(struct mod_hdcp *hdcp,
struct mod_hdcp_event_context *event_ctx,
struct mod_hdcp_transition_input_hdcp2 *input)
{
enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS;
if (event_ctx->event != MOD_HDCP_EVENT_CALLBACK &&
event_ctx->event != MOD_HDCP_EVENT_CPIRQ) {
event_ctx->unexpected_event = 1;
goto out;
}
if (event_ctx->event == MOD_HDCP_EVENT_CPIRQ) {
process_rxstatus(hdcp, event_ctx, input, &status);
goto out;
}
if (is_hdmi_dvi_sl_hdcp(hdcp)) {
if (!process_rxstatus(hdcp, event_ctx, input, &status))
goto out;
if (event_ctx->rx_id_list_ready)
goto out;
}
if (!mod_hdcp_execute_and_set(mod_hdcp_hdcp2_enable_encryption,
&input->enable_encryption, &status,
hdcp, "enable_encryption"))
goto out;
if (is_dp_mst_hdcp(hdcp)) {
if (!mod_hdcp_execute_and_set(
mod_hdcp_hdcp2_enable_dp_stream_encryption,
&input->stream_encryption_dp, &status,
hdcp, "stream_encryption_dp"))
goto out;
}
out:
return status;
}
static enum mod_hdcp_status authenticated(struct mod_hdcp *hdcp,
struct mod_hdcp_event_context *event_ctx,
struct mod_hdcp_transition_input_hdcp2 *input)
{
enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS;
if (event_ctx->event != MOD_HDCP_EVENT_CALLBACK &&
event_ctx->event != MOD_HDCP_EVENT_CPIRQ) {
event_ctx->unexpected_event = 1;
goto out;
}
if (!process_rxstatus(hdcp, event_ctx, input, &status))
goto out;
if (event_ctx->rx_id_list_ready)
goto out;
out:
return status;
}
static enum mod_hdcp_status wait_for_rx_id_list(struct mod_hdcp *hdcp,
struct mod_hdcp_event_context *event_ctx,
struct mod_hdcp_transition_input_hdcp2 *input)
{
enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS;
if (event_ctx->event != MOD_HDCP_EVENT_CALLBACK &&
event_ctx->event != MOD_HDCP_EVENT_CPIRQ &&
event_ctx->event != MOD_HDCP_EVENT_WATCHDOG_TIMEOUT) {
event_ctx->unexpected_event = 1;
goto out;
}
if (!process_rxstatus(hdcp, event_ctx, input, &status))
goto out;
if (!event_ctx->rx_id_list_ready) {
status = MOD_HDCP_STATUS_HDCP2_RX_ID_LIST_NOT_READY;
goto out;
}
out:
return status;
}
static enum mod_hdcp_status verify_rx_id_list_and_send_ack(struct mod_hdcp *hdcp,
struct mod_hdcp_event_context *event_ctx,
struct mod_hdcp_transition_input_hdcp2 *input)
{
enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS;
if (event_ctx->event != MOD_HDCP_EVENT_CALLBACK &&
event_ctx->event != MOD_HDCP_EVENT_CPIRQ) {
event_ctx->unexpected_event = 1;
goto out;
}
if (event_ctx->event == MOD_HDCP_EVENT_CPIRQ) {
process_rxstatus(hdcp, event_ctx, input, &status);
goto out;
}
if (!mod_hdcp_execute_and_set(mod_hdcp_read_rx_id_list,
&input->rx_id_list_read,
&status, hdcp, "receiver_id_list_read"))
goto out;
if (!mod_hdcp_execute_and_set(check_device_count,
&input->device_count_check,
&status, hdcp, "device_count_check"))
goto out;
if (!mod_hdcp_execute_and_set(mod_hdcp_hdcp2_validate_rx_id_list,
&input->rx_id_list_validation,
&status, hdcp, "rx_id_list_validation"))
goto out;
if (!mod_hdcp_execute_and_set(mod_hdcp_write_repeater_auth_ack,
&input->repeater_auth_ack_write,
&status, hdcp, "repeater_auth_ack_write"))
goto out;
out:
return status;
}
static enum mod_hdcp_status send_stream_management(struct mod_hdcp *hdcp,
struct mod_hdcp_event_context *event_ctx,
struct mod_hdcp_transition_input_hdcp2 *input)
{
enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS;
if (event_ctx->event != MOD_HDCP_EVENT_CALLBACK &&
event_ctx->event != MOD_HDCP_EVENT_CPIRQ) {
event_ctx->unexpected_event = 1;
goto out;
}
if (event_ctx->event == MOD_HDCP_EVENT_CPIRQ) {
process_rxstatus(hdcp, event_ctx, input, &status);
goto out;
}
if (is_hdmi_dvi_sl_hdcp(hdcp)) {
if (!process_rxstatus(hdcp, event_ctx, input, &status))
goto out;
if (event_ctx->rx_id_list_ready)
goto out;
}
if (!mod_hdcp_execute_and_set(mod_hdcp_hdcp2_prepare_stream_management,
&input->prepare_stream_manage,
&status, hdcp, "prepare_stream_manage"))
goto out;
if (!mod_hdcp_execute_and_set(mod_hdcp_write_stream_manage,
&input->stream_manage_write,
&status, hdcp, "stream_manage_write"))
goto out;
out:
return status;
}
static enum mod_hdcp_status validate_stream_ready(struct mod_hdcp *hdcp,
struct mod_hdcp_event_context *event_ctx,
struct mod_hdcp_transition_input_hdcp2 *input)
{
enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS;
if (event_ctx->event != MOD_HDCP_EVENT_CALLBACK &&
event_ctx->event != MOD_HDCP_EVENT_CPIRQ &&
event_ctx->event != MOD_HDCP_EVENT_WATCHDOG_TIMEOUT) {
event_ctx->unexpected_event = 1;
goto out;
}
if (event_ctx->event == MOD_HDCP_EVENT_CPIRQ) {
process_rxstatus(hdcp, event_ctx, input, &status);
goto out;
}
if (is_hdmi_dvi_sl_hdcp(hdcp)) {
if (!process_rxstatus(hdcp, event_ctx, input, &status))
goto out;
if (event_ctx->rx_id_list_ready) {
goto out;
}
}
if (is_hdmi_dvi_sl_hdcp(hdcp))
if (!mod_hdcp_execute_and_set(check_stream_ready_available,
&input->stream_ready_available,
&status, hdcp, "stream_ready_available"))
goto out;
if (!mod_hdcp_execute_and_set(mod_hdcp_read_stream_ready,
&input->stream_ready_read,
&status, hdcp, "stream_ready_read"))
goto out;
if (!mod_hdcp_execute_and_set(mod_hdcp_hdcp2_validate_stream_ready,
&input->stream_ready_validation,
&status, hdcp, "stream_ready_validation"))
goto out;
out:
return status;
}
static enum mod_hdcp_status determine_rx_hdcp_capable_dp(struct mod_hdcp *hdcp,
struct mod_hdcp_event_context *event_ctx,
struct mod_hdcp_transition_input_hdcp2 *input)
{
enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS;
if (event_ctx->event != MOD_HDCP_EVENT_CALLBACK) {
event_ctx->unexpected_event = 1;
goto out;
}
if (!mod_hdcp_execute_and_set(mod_hdcp_read_rxcaps,
&input->rx_caps_read_dp,
&status, hdcp, "rx_caps_read_dp"))
goto out;
if (!mod_hdcp_execute_and_set(check_hdcp2_capable,
&input->hdcp2_capable_check, &status,
hdcp, "hdcp2_capable_check"))
goto out;
out:
return status;
}
static enum mod_hdcp_status send_content_stream_type_dp(struct mod_hdcp *hdcp,
struct mod_hdcp_event_context *event_ctx,
struct mod_hdcp_transition_input_hdcp2 *input)
{
enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS;
if (event_ctx->event != MOD_HDCP_EVENT_CALLBACK &&
event_ctx->event != MOD_HDCP_EVENT_CPIRQ) {
event_ctx->unexpected_event = 1;
goto out;
}
if (!process_rxstatus(hdcp, event_ctx, input, &status))
goto out;
if (!mod_hdcp_execute_and_set(mod_hdcp_write_content_type,
&input->content_stream_type_write, &status,
hdcp, "content_stream_type_write"))
goto out;
out:
return status;
}
enum mod_hdcp_status mod_hdcp_hdcp2_execution(struct mod_hdcp *hdcp,
struct mod_hdcp_event_context *event_ctx,
struct mod_hdcp_transition_input_hdcp2 *input)
{
enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS;
switch (current_state(hdcp)) {
case H2_A0_KNOWN_HDCP2_CAPABLE_RX:
status = known_hdcp2_capable_rx(hdcp, event_ctx, input);
break;
case H2_A1_SEND_AKE_INIT:
status = send_ake_init(hdcp, event_ctx, input);
break;
case H2_A1_VALIDATE_AKE_CERT:
status = validate_ake_cert(hdcp, event_ctx, input);
break;
case H2_A1_SEND_NO_STORED_KM:
status = send_no_stored_km(hdcp, event_ctx, input);
break;
case H2_A1_READ_H_PRIME:
status = read_h_prime(hdcp, event_ctx, input);
break;
case H2_A1_READ_PAIRING_INFO_AND_VALIDATE_H_PRIME:
status = read_pairing_info_and_validate_h_prime(hdcp,
event_ctx, input);
break;
case H2_A1_SEND_STORED_KM:
status = send_stored_km(hdcp, event_ctx, input);
break;
case H2_A1_VALIDATE_H_PRIME:
status = validate_h_prime(hdcp, event_ctx, input);
break;
case H2_A2_LOCALITY_CHECK:
status = locality_check(hdcp, event_ctx, input);
break;
case H2_A3_EXCHANGE_KS_AND_TEST_FOR_REPEATER:
status = exchange_ks_and_test_for_repeater(hdcp, event_ctx, input);
break;
case H2_ENABLE_ENCRYPTION:
status = enable_encryption(hdcp, event_ctx, input);
break;
case H2_A5_AUTHENTICATED:
status = authenticated(hdcp, event_ctx, input);
break;
case H2_A6_WAIT_FOR_RX_ID_LIST:
status = wait_for_rx_id_list(hdcp, event_ctx, input);
break;
case H2_A78_VERIFY_RX_ID_LIST_AND_SEND_ACK:
status = verify_rx_id_list_and_send_ack(hdcp, event_ctx, input);
break;
case H2_A9_SEND_STREAM_MANAGEMENT:
status = send_stream_management(hdcp, event_ctx, input);
break;
case H2_A9_VALIDATE_STREAM_READY:
status = validate_stream_ready(hdcp, event_ctx, input);
break;
default:
status = MOD_HDCP_STATUS_INVALID_STATE;
break;
}
return status;
}
enum mod_hdcp_status mod_hdcp_hdcp2_dp_execution(struct mod_hdcp *hdcp,
struct mod_hdcp_event_context *event_ctx,
struct mod_hdcp_transition_input_hdcp2 *input)
{
enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS;
switch (current_state(hdcp)) {
case D2_A0_DETERMINE_RX_HDCP_CAPABLE:
status = determine_rx_hdcp_capable_dp(hdcp, event_ctx, input);
break;
case D2_A1_SEND_AKE_INIT:
status = send_ake_init(hdcp, event_ctx, input);
break;
case D2_A1_VALIDATE_AKE_CERT:
status = validate_ake_cert(hdcp, event_ctx, input);
break;
case D2_A1_SEND_NO_STORED_KM:
status = send_no_stored_km(hdcp, event_ctx, input);
break;
case D2_A1_READ_H_PRIME:
status = read_h_prime(hdcp, event_ctx, input);
break;
case D2_A1_READ_PAIRING_INFO_AND_VALIDATE_H_PRIME:
status = read_pairing_info_and_validate_h_prime(hdcp,
event_ctx, input);
break;
case D2_A1_SEND_STORED_KM:
status = send_stored_km(hdcp, event_ctx, input);
break;
case D2_A1_VALIDATE_H_PRIME:
status = validate_h_prime(hdcp, event_ctx, input);
break;
case D2_A2_LOCALITY_CHECK:
status = locality_check(hdcp, event_ctx, input);
break;
case D2_A34_EXCHANGE_KS_AND_TEST_FOR_REPEATER:
status = exchange_ks_and_test_for_repeater(hdcp,
event_ctx, input);
break;
case D2_SEND_CONTENT_STREAM_TYPE:
status = send_content_stream_type_dp(hdcp, event_ctx, input);
break;
case D2_ENABLE_ENCRYPTION:
status = enable_encryption(hdcp, event_ctx, input);
break;
case D2_A5_AUTHENTICATED:
status = authenticated(hdcp, event_ctx, input);
break;
case D2_A6_WAIT_FOR_RX_ID_LIST:
status = wait_for_rx_id_list(hdcp, event_ctx, input);
break;
case D2_A78_VERIFY_RX_ID_LIST_AND_SEND_ACK:
status = verify_rx_id_list_and_send_ack(hdcp, event_ctx, input);
break;
case D2_A9_SEND_STREAM_MANAGEMENT:
status = send_stream_management(hdcp, event_ctx, input);
break;
case D2_A9_VALIDATE_STREAM_READY:
status = validate_stream_ready(hdcp, event_ctx, input);
break;
default:
status = MOD_HDCP_STATUS_INVALID_STATE;
break;
}
return status;
}