Path: blob/master/drivers/gpu/drm/amd/display/modules/hdcp/hdcp.c
29299 views
/*1* Copyright 2019 Advanced Micro Devices, Inc.2*3* Permission is hereby granted, free of charge, to any person obtaining a4* copy of this software and associated documentation files (the "Software"),5* to deal in the Software without restriction, including without limitation6* the rights to use, copy, modify, merge, publish, distribute, sublicense,7* and/or sell copies of the Software, and to permit persons to whom the8* Software is furnished to do so, subject to the following conditions:9*10* The above copyright notice and this permission notice shall be included in11* all copies or substantial portions of the Software.12*13* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR14* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,15* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL16* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR17* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,18* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR19* OTHER DEALINGS IN THE SOFTWARE.20*21* Authors: AMD22*23*/2425#include "hdcp.h"2627static void push_error_status(struct mod_hdcp *hdcp,28enum mod_hdcp_status status)29{30struct mod_hdcp_trace *trace = &hdcp->connection.trace;31const uint8_t retry_limit = hdcp->connection.link.adjust.retry_limit;3233if (trace->error_count < MAX_NUM_OF_ERROR_TRACE) {34trace->errors[trace->error_count].status = status;35trace->errors[trace->error_count].state_id = hdcp->state.id;36trace->error_count++;37HDCP_ERROR_TRACE(hdcp, status);38}3940if (is_hdcp1(hdcp)) {41hdcp->connection.hdcp1_retry_count++;42if (hdcp->connection.hdcp1_retry_count == retry_limit)43hdcp->connection.link.adjust.hdcp1.disable = 1;44} else if (is_hdcp2(hdcp)) {45hdcp->connection.hdcp2_retry_count++;46if (hdcp->connection.hdcp2_retry_count == retry_limit)47hdcp->connection.link.adjust.hdcp2.disable = 1;48}49}5051static uint8_t is_cp_desired_hdcp1(struct mod_hdcp *hdcp)52{53int i, is_auth_needed = 0;5455/* if all displays on the link don't need authentication,56* hdcp is not desired57*/58for (i = 0; i < MAX_NUM_OF_DISPLAYS; i++) {59if (hdcp->displays[i].state != MOD_HDCP_DISPLAY_INACTIVE &&60hdcp->displays[i].adjust.disable != MOD_HDCP_DISPLAY_DISABLE_AUTHENTICATION) {61is_auth_needed = 1;62break;63}64}6566return is_auth_needed &&67!hdcp->connection.link.adjust.hdcp1.disable &&68!hdcp->connection.is_hdcp1_revoked;69}7071static uint8_t is_cp_desired_hdcp2(struct mod_hdcp *hdcp)72{73int i, is_auth_needed = 0;7475/* if all displays on the link don't need authentication,76* hdcp is not desired77*/78for (i = 0; i < MAX_NUM_OF_DISPLAYS; i++) {79if (hdcp->displays[i].state != MOD_HDCP_DISPLAY_INACTIVE &&80hdcp->displays[i].adjust.disable != MOD_HDCP_DISPLAY_DISABLE_AUTHENTICATION) {81is_auth_needed = 1;82break;83}84}8586return is_auth_needed &&87!hdcp->connection.link.adjust.hdcp2.disable &&88!hdcp->connection.is_hdcp2_revoked;89}9091static enum mod_hdcp_status execution(struct mod_hdcp *hdcp,92struct mod_hdcp_event_context *event_ctx,93union mod_hdcp_transition_input *input)94{95enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS;9697if (is_in_initialized_state(hdcp)) {98if (event_ctx->event != MOD_HDCP_EVENT_CALLBACK) {99event_ctx->unexpected_event = 1;100goto out;101}102/* initialize transition input */103memset(input, 0, sizeof(union mod_hdcp_transition_input));104} else if (is_in_cp_not_desired_state(hdcp)) {105if (event_ctx->event != MOD_HDCP_EVENT_CALLBACK) {106event_ctx->unexpected_event = 1;107goto out;108}109} else if (is_in_hdcp1_states(hdcp)) {110status = mod_hdcp_hdcp1_execution(hdcp, event_ctx, &input->hdcp1);111} else if (is_in_hdcp1_dp_states(hdcp)) {112status = mod_hdcp_hdcp1_dp_execution(hdcp,113event_ctx, &input->hdcp1);114} else if (is_in_hdcp2_states(hdcp)) {115status = mod_hdcp_hdcp2_execution(hdcp, event_ctx, &input->hdcp2);116} else if (is_in_hdcp2_dp_states(hdcp)) {117status = mod_hdcp_hdcp2_dp_execution(hdcp,118event_ctx, &input->hdcp2);119} else {120event_ctx->unexpected_event = 1;121goto out;122}123out:124return status;125}126127static enum mod_hdcp_status transition(struct mod_hdcp *hdcp,128struct mod_hdcp_event_context *event_ctx,129union mod_hdcp_transition_input *input,130struct mod_hdcp_output *output)131{132enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS;133134if (event_ctx->unexpected_event)135goto out;136137if (is_in_initialized_state(hdcp)) {138if (is_dp_hdcp(hdcp))139if (is_cp_desired_hdcp2(hdcp)) {140callback_in_ms(0, output);141set_state_id(hdcp, output, D2_A0_DETERMINE_RX_HDCP_CAPABLE);142} else if (is_cp_desired_hdcp1(hdcp)) {143callback_in_ms(0, output);144set_state_id(hdcp, output, D1_A0_DETERMINE_RX_HDCP_CAPABLE);145} else {146callback_in_ms(0, output);147set_state_id(hdcp, output, HDCP_CP_NOT_DESIRED);148set_auth_complete(hdcp, output);149}150else if (is_hdmi_dvi_sl_hdcp(hdcp))151if (is_cp_desired_hdcp2(hdcp)) {152callback_in_ms(0, output);153set_state_id(hdcp, output, H2_A0_KNOWN_HDCP2_CAPABLE_RX);154} else if (is_cp_desired_hdcp1(hdcp)) {155callback_in_ms(0, output);156set_state_id(hdcp, output, H1_A0_WAIT_FOR_ACTIVE_RX);157} else {158callback_in_ms(0, output);159set_state_id(hdcp, output, HDCP_CP_NOT_DESIRED);160set_auth_complete(hdcp, output);161}162else {163callback_in_ms(0, output);164set_state_id(hdcp, output, HDCP_CP_NOT_DESIRED);165set_auth_complete(hdcp, output);166}167} else if (is_in_cp_not_desired_state(hdcp)) {168increment_stay_counter(hdcp);169} else if (is_in_hdcp1_states(hdcp)) {170status = mod_hdcp_hdcp1_transition(hdcp,171event_ctx, &input->hdcp1, output);172} else if (is_in_hdcp1_dp_states(hdcp)) {173status = mod_hdcp_hdcp1_dp_transition(hdcp,174event_ctx, &input->hdcp1, output);175} else if (is_in_hdcp2_states(hdcp)) {176status = mod_hdcp_hdcp2_transition(hdcp,177event_ctx, &input->hdcp2, output);178} else if (is_in_hdcp2_dp_states(hdcp)) {179status = mod_hdcp_hdcp2_dp_transition(hdcp,180event_ctx, &input->hdcp2, output);181} else {182status = MOD_HDCP_STATUS_INVALID_STATE;183}184out:185return status;186}187188static enum mod_hdcp_status reset_authentication(struct mod_hdcp *hdcp,189struct mod_hdcp_output *output)190{191enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS;192193if (is_hdcp1(hdcp)) {194if (hdcp->auth.trans_input.hdcp1.create_session != UNKNOWN) {195/* TODO - update psp to unify create session failure196* recovery between hdcp1 and 2.197*/198mod_hdcp_hdcp1_destroy_session(hdcp);199200}201202HDCP_TOP_RESET_AUTH_TRACE(hdcp);203memset(&hdcp->auth, 0, sizeof(struct mod_hdcp_authentication));204memset(&hdcp->state, 0, sizeof(struct mod_hdcp_state));205set_state_id(hdcp, output, HDCP_INITIALIZED);206} else if (is_hdcp2(hdcp)) {207if (hdcp->auth.trans_input.hdcp2.create_session == PASS) {208status = mod_hdcp_hdcp2_destroy_session(hdcp);209if (status != MOD_HDCP_STATUS_SUCCESS) {210output->callback_needed = 0;211output->watchdog_timer_needed = 0;212goto out;213}214}215216HDCP_TOP_RESET_AUTH_TRACE(hdcp);217memset(&hdcp->auth, 0, sizeof(struct mod_hdcp_authentication));218memset(&hdcp->state, 0, sizeof(struct mod_hdcp_state));219set_state_id(hdcp, output, HDCP_INITIALIZED);220} else if (is_in_cp_not_desired_state(hdcp)) {221HDCP_TOP_RESET_AUTH_TRACE(hdcp);222memset(&hdcp->auth, 0, sizeof(struct mod_hdcp_authentication));223memset(&hdcp->state, 0, sizeof(struct mod_hdcp_state));224set_state_id(hdcp, output, HDCP_INITIALIZED);225}226227out:228/* stop callback and watchdog requests from previous authentication*/229output->watchdog_timer_stop = 1;230output->callback_stop = 1;231return status;232}233234static enum mod_hdcp_status reset_connection(struct mod_hdcp *hdcp,235struct mod_hdcp_output *output)236{237enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS;238239memset(output, 0, sizeof(struct mod_hdcp_output));240241status = reset_authentication(hdcp, output);242if (status != MOD_HDCP_STATUS_SUCCESS)243goto out;244245if (current_state(hdcp) != HDCP_UNINITIALIZED) {246HDCP_TOP_RESET_CONN_TRACE(hdcp);247set_state_id(hdcp, output, HDCP_UNINITIALIZED);248}249memset(&hdcp->connection, 0, sizeof(hdcp->connection));250out:251return status;252}253254static enum mod_hdcp_status update_display_adjustments(struct mod_hdcp *hdcp,255struct mod_hdcp_display *display,256struct mod_hdcp_display_adjustment *adj)257{258enum mod_hdcp_status status = MOD_HDCP_STATUS_NOT_IMPLEMENTED;259260if (is_in_authenticated_states(hdcp) &&261is_dp_mst_hdcp(hdcp) &&262display->adjust.disable == true &&263adj->disable == false) {264display->adjust.disable = false;265if (is_hdcp1(hdcp))266status = mod_hdcp_hdcp1_enable_dp_stream_encryption(hdcp);267else if (is_hdcp2(hdcp))268status = mod_hdcp_hdcp2_enable_dp_stream_encryption(hdcp);269270if (status != MOD_HDCP_STATUS_SUCCESS)271display->adjust.disable = true;272}273274if (status == MOD_HDCP_STATUS_SUCCESS &&275memcmp(adj, &display->adjust,276sizeof(struct mod_hdcp_display_adjustment)) != 0)277status = MOD_HDCP_STATUS_NOT_IMPLEMENTED;278279return status;280}281/*282* Implementation of functions in mod_hdcp.h283*/284size_t mod_hdcp_get_memory_size(void)285{286return sizeof(struct mod_hdcp);287}288289enum mod_hdcp_status mod_hdcp_setup(struct mod_hdcp *hdcp,290struct mod_hdcp_config *config)291{292struct mod_hdcp_output output;293enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS;294295memset(&output, 0, sizeof(output));296hdcp->config = *config;297HDCP_TOP_INTERFACE_TRACE(hdcp);298status = reset_connection(hdcp, &output);299if (status != MOD_HDCP_STATUS_SUCCESS)300push_error_status(hdcp, status);301return status;302}303304enum mod_hdcp_status mod_hdcp_teardown(struct mod_hdcp *hdcp)305{306enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS;307struct mod_hdcp_output output;308309HDCP_TOP_INTERFACE_TRACE(hdcp);310memset(&output, 0, sizeof(output));311status = reset_connection(hdcp, &output);312if (status == MOD_HDCP_STATUS_SUCCESS)313memset(hdcp, 0, sizeof(struct mod_hdcp));314else315push_error_status(hdcp, status);316return status;317}318319enum mod_hdcp_status mod_hdcp_add_display(struct mod_hdcp *hdcp,320struct mod_hdcp_link *link, struct mod_hdcp_display *display,321struct mod_hdcp_output *output)322{323enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS;324struct mod_hdcp_display *display_container = NULL;325326HDCP_TOP_INTERFACE_TRACE_WITH_INDEX(hdcp, display->index);327memset(output, 0, sizeof(struct mod_hdcp_output));328329/* skip inactive display */330if (display->state != MOD_HDCP_DISPLAY_ACTIVE) {331status = MOD_HDCP_STATUS_SUCCESS;332goto out;333}334335/* check existing display container */336if (get_active_display_at_index(hdcp, display->index)) {337status = MOD_HDCP_STATUS_SUCCESS;338goto out;339}340341/* find an empty display container */342display_container = get_empty_display_container(hdcp);343if (!display_container) {344status = MOD_HDCP_STATUS_DISPLAY_OUT_OF_BOUND;345goto out;346}347348/* reset existing authentication status */349status = reset_authentication(hdcp, output);350if (status != MOD_HDCP_STATUS_SUCCESS)351goto out;352353/* reset retry counters */354reset_retry_counts(hdcp);355356/* reset error trace */357memset(&hdcp->connection.trace, 0, sizeof(hdcp->connection.trace));358359/* add display to connection */360hdcp->connection.link = *link;361*display_container = *display;362status = mod_hdcp_add_display_to_topology(hdcp, display_container);363364if (status != MOD_HDCP_STATUS_SUCCESS)365goto out;366367/* request authentication */368if (current_state(hdcp) != HDCP_INITIALIZED)369set_state_id(hdcp, output, HDCP_INITIALIZED);370callback_in_ms(hdcp->connection.link.adjust.auth_delay * 1000, output);371out:372if (status != MOD_HDCP_STATUS_SUCCESS)373push_error_status(hdcp, status);374375return status;376}377378enum mod_hdcp_status mod_hdcp_remove_display(struct mod_hdcp *hdcp,379uint8_t index, struct mod_hdcp_output *output)380{381enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS;382struct mod_hdcp_display *display = NULL;383384HDCP_TOP_INTERFACE_TRACE_WITH_INDEX(hdcp, index);385memset(output, 0, sizeof(struct mod_hdcp_output));386387/* find display in connection */388display = get_active_display_at_index(hdcp, index);389if (!display) {390status = MOD_HDCP_STATUS_SUCCESS;391goto out;392}393394/* stop current authentication */395status = reset_authentication(hdcp, output);396if (status != MOD_HDCP_STATUS_SUCCESS)397goto out;398399/* clear retry counters */400reset_retry_counts(hdcp);401402/* reset error trace */403memset(&hdcp->connection.trace, 0, sizeof(hdcp->connection.trace));404405/* remove display */406status = mod_hdcp_remove_display_from_topology(hdcp, index);407if (status != MOD_HDCP_STATUS_SUCCESS)408goto out;409memset(display, 0, sizeof(struct mod_hdcp_display));410411/* request authentication when connection is not reset */412if (current_state(hdcp) != HDCP_UNINITIALIZED)413callback_in_ms(hdcp->connection.link.adjust.auth_delay * 1000,414output);415out:416if (status != MOD_HDCP_STATUS_SUCCESS)417push_error_status(hdcp, status);418return status;419}420421enum mod_hdcp_status mod_hdcp_update_display(struct mod_hdcp *hdcp,422uint8_t index,423struct mod_hdcp_link_adjustment *link_adjust,424struct mod_hdcp_display_adjustment *display_adjust,425struct mod_hdcp_output *output)426{427enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS;428struct mod_hdcp_display *display = NULL;429430HDCP_TOP_INTERFACE_TRACE_WITH_INDEX(hdcp, index);431memset(output, 0, sizeof(struct mod_hdcp_output));432433/* find display in connection */434display = get_active_display_at_index(hdcp, index);435if (!display) {436status = MOD_HDCP_STATUS_DISPLAY_NOT_FOUND;437goto out;438}439440/* skip if no changes */441if (memcmp(link_adjust, &hdcp->connection.link.adjust,442sizeof(struct mod_hdcp_link_adjustment)) == 0 &&443memcmp(display_adjust, &display->adjust,444sizeof(struct mod_hdcp_display_adjustment)) == 0) {445status = MOD_HDCP_STATUS_SUCCESS;446goto out;447}448449if (memcmp(link_adjust, &hdcp->connection.link.adjust,450sizeof(struct mod_hdcp_link_adjustment)) == 0 &&451memcmp(display_adjust, &display->adjust,452sizeof(struct mod_hdcp_display_adjustment)) != 0) {453status = update_display_adjustments(hdcp, display, display_adjust);454if (status != MOD_HDCP_STATUS_NOT_IMPLEMENTED)455goto out;456}457458/* stop current authentication */459status = reset_authentication(hdcp, output);460if (status != MOD_HDCP_STATUS_SUCCESS)461goto out;462463/* clear retry counters */464reset_retry_counts(hdcp);465466/* reset error trace */467memset(&hdcp->connection.trace, 0, sizeof(hdcp->connection.trace));468469/* set new adjustment */470hdcp->connection.link.adjust = *link_adjust;471display->adjust = *display_adjust;472473/* request authentication when connection is not reset */474if (current_state(hdcp) != HDCP_UNINITIALIZED)475/* wait 100ms to debounce simultaneous updates for different indices */476callback_in_ms(100, output);477478out:479if (status != MOD_HDCP_STATUS_SUCCESS)480push_error_status(hdcp, status);481return status;482}483484enum mod_hdcp_status mod_hdcp_query_display(struct mod_hdcp *hdcp,485uint8_t index, struct mod_hdcp_display_query *query)486{487enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS;488struct mod_hdcp_display *display = NULL;489490/* find display in connection */491display = get_active_display_at_index(hdcp, index);492if (!display) {493status = MOD_HDCP_STATUS_DISPLAY_NOT_FOUND;494goto out;495}496497/* populate query */498query->link = &hdcp->connection.link;499query->display = display;500query->trace = &hdcp->connection.trace;501query->encryption_status = MOD_HDCP_ENCRYPTION_STATUS_HDCP_OFF;502503if (is_display_encryption_enabled(display)) {504if (is_hdcp1(hdcp)) {505query->encryption_status = MOD_HDCP_ENCRYPTION_STATUS_HDCP1_ON;506} else if (is_hdcp2(hdcp)) {507if (query->link->adjust.hdcp2.force_type == MOD_HDCP_FORCE_TYPE_0)508query->encryption_status = MOD_HDCP_ENCRYPTION_STATUS_HDCP2_TYPE0_ON;509else if (query->link->adjust.hdcp2.force_type == MOD_HDCP_FORCE_TYPE_1)510query->encryption_status = MOD_HDCP_ENCRYPTION_STATUS_HDCP2_TYPE1_ON;511else512query->encryption_status = MOD_HDCP_ENCRYPTION_STATUS_HDCP2_ON;513}514} else {515query->encryption_status = MOD_HDCP_ENCRYPTION_STATUS_HDCP_OFF;516}517518out:519return status;520}521522enum mod_hdcp_status mod_hdcp_reset_connection(struct mod_hdcp *hdcp,523struct mod_hdcp_output *output)524{525enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS;526527HDCP_TOP_INTERFACE_TRACE(hdcp);528status = reset_connection(hdcp, output);529if (status != MOD_HDCP_STATUS_SUCCESS)530push_error_status(hdcp, status);531532return status;533}534535enum mod_hdcp_status mod_hdcp_process_event(struct mod_hdcp *hdcp,536enum mod_hdcp_event event, struct mod_hdcp_output *output)537{538enum mod_hdcp_status exec_status, trans_status, reset_status, status;539struct mod_hdcp_event_context event_ctx;540541HDCP_EVENT_TRACE(hdcp, event);542memset(output, 0, sizeof(struct mod_hdcp_output));543memset(&event_ctx, 0, sizeof(struct mod_hdcp_event_context));544event_ctx.event = event;545546/* execute and transition */547exec_status = execution(hdcp, &event_ctx, &hdcp->auth.trans_input);548trans_status = transition(549hdcp, &event_ctx, &hdcp->auth.trans_input, output);550if (trans_status == MOD_HDCP_STATUS_SUCCESS) {551status = MOD_HDCP_STATUS_SUCCESS;552} else if (exec_status == MOD_HDCP_STATUS_SUCCESS) {553status = MOD_HDCP_STATUS_INTERNAL_POLICY_FAILURE;554push_error_status(hdcp, status);555} else {556status = exec_status;557push_error_status(hdcp, status);558}559560/* reset authentication if needed */561if (trans_status == MOD_HDCP_STATUS_RESET_NEEDED) {562mod_hdcp_log_ddc_trace(hdcp);563reset_status = reset_authentication(hdcp, output);564if (reset_status != MOD_HDCP_STATUS_SUCCESS)565push_error_status(hdcp, reset_status);566}567568/* Clear CP_IRQ status if needed */569if (event_ctx.event == MOD_HDCP_EVENT_CPIRQ) {570status = mod_hdcp_clear_cp_irq_status(hdcp);571if (status != MOD_HDCP_STATUS_SUCCESS)572push_error_status(hdcp, status);573}574575return status;576}577578enum mod_hdcp_operation_mode mod_hdcp_signal_type_to_operation_mode(579enum signal_type signal)580{581enum mod_hdcp_operation_mode mode = MOD_HDCP_MODE_OFF;582583switch (signal) {584case SIGNAL_TYPE_DVI_SINGLE_LINK:585case SIGNAL_TYPE_HDMI_TYPE_A:586mode = MOD_HDCP_MODE_DEFAULT;587break;588case SIGNAL_TYPE_EDP:589case SIGNAL_TYPE_DISPLAY_PORT:590case SIGNAL_TYPE_DISPLAY_PORT_MST:591mode = MOD_HDCP_MODE_DP;592break;593default:594break;595}596597return mode;598}599600601