Path: blob/master/drivers/gpu/drm/amd/display/modules/hdcp/hdcp.c
26552 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;3132if (trace->error_count < MAX_NUM_OF_ERROR_TRACE) {33trace->errors[trace->error_count].status = status;34trace->errors[trace->error_count].state_id = hdcp->state.id;35trace->error_count++;36HDCP_ERROR_TRACE(hdcp, status);37}3839if (is_hdcp1(hdcp)) {40hdcp->connection.hdcp1_retry_count++;41if (hdcp->connection.hdcp1_retry_count == MAX_NUM_OF_ATTEMPTS)42hdcp->connection.link.adjust.hdcp1.disable = 1;43} else if (is_hdcp2(hdcp)) {44hdcp->connection.hdcp2_retry_count++;45if (hdcp->connection.hdcp2_retry_count == MAX_NUM_OF_ATTEMPTS)46hdcp->connection.link.adjust.hdcp2.disable = 1;47}48}4950static uint8_t is_cp_desired_hdcp1(struct mod_hdcp *hdcp)51{52int i, is_auth_needed = 0;5354/* if all displays on the link don't need authentication,55* hdcp is not desired56*/57for (i = 0; i < MAX_NUM_OF_DISPLAYS; i++) {58if (hdcp->displays[i].state != MOD_HDCP_DISPLAY_INACTIVE &&59hdcp->displays[i].adjust.disable != MOD_HDCP_DISPLAY_DISABLE_AUTHENTICATION) {60is_auth_needed = 1;61break;62}63}6465return is_auth_needed &&66!hdcp->connection.link.adjust.hdcp1.disable &&67!hdcp->connection.is_hdcp1_revoked;68}6970static uint8_t is_cp_desired_hdcp2(struct mod_hdcp *hdcp)71{72int i, is_auth_needed = 0;7374/* if all displays on the link don't need authentication,75* hdcp is not desired76*/77for (i = 0; i < MAX_NUM_OF_DISPLAYS; i++) {78if (hdcp->displays[i].state != MOD_HDCP_DISPLAY_INACTIVE &&79hdcp->displays[i].adjust.disable != MOD_HDCP_DISPLAY_DISABLE_AUTHENTICATION) {80is_auth_needed = 1;81break;82}83}8485return is_auth_needed &&86!hdcp->connection.link.adjust.hdcp2.disable &&87!hdcp->connection.is_hdcp2_revoked;88}8990static enum mod_hdcp_status execution(struct mod_hdcp *hdcp,91struct mod_hdcp_event_context *event_ctx,92union mod_hdcp_transition_input *input)93{94enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS;9596if (is_in_initialized_state(hdcp)) {97if (event_ctx->event != MOD_HDCP_EVENT_CALLBACK) {98event_ctx->unexpected_event = 1;99goto out;100}101/* initialize transition input */102memset(input, 0, sizeof(union mod_hdcp_transition_input));103} else if (is_in_cp_not_desired_state(hdcp)) {104if (event_ctx->event != MOD_HDCP_EVENT_CALLBACK) {105event_ctx->unexpected_event = 1;106goto out;107}108} else if (is_in_hdcp1_states(hdcp)) {109status = mod_hdcp_hdcp1_execution(hdcp, event_ctx, &input->hdcp1);110} else if (is_in_hdcp1_dp_states(hdcp)) {111status = mod_hdcp_hdcp1_dp_execution(hdcp,112event_ctx, &input->hdcp1);113} else if (is_in_hdcp2_states(hdcp)) {114status = mod_hdcp_hdcp2_execution(hdcp, event_ctx, &input->hdcp2);115} else if (is_in_hdcp2_dp_states(hdcp)) {116status = mod_hdcp_hdcp2_dp_execution(hdcp,117event_ctx, &input->hdcp2);118} else {119event_ctx->unexpected_event = 1;120goto out;121}122out:123return status;124}125126static enum mod_hdcp_status transition(struct mod_hdcp *hdcp,127struct mod_hdcp_event_context *event_ctx,128union mod_hdcp_transition_input *input,129struct mod_hdcp_output *output)130{131enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS;132133if (event_ctx->unexpected_event)134goto out;135136if (is_in_initialized_state(hdcp)) {137if (is_dp_hdcp(hdcp))138if (is_cp_desired_hdcp2(hdcp)) {139callback_in_ms(0, output);140set_state_id(hdcp, output, D2_A0_DETERMINE_RX_HDCP_CAPABLE);141} else if (is_cp_desired_hdcp1(hdcp)) {142callback_in_ms(0, output);143set_state_id(hdcp, output, D1_A0_DETERMINE_RX_HDCP_CAPABLE);144} else {145callback_in_ms(0, output);146set_state_id(hdcp, output, HDCP_CP_NOT_DESIRED);147set_auth_complete(hdcp, output);148}149else if (is_hdmi_dvi_sl_hdcp(hdcp))150if (is_cp_desired_hdcp2(hdcp)) {151callback_in_ms(0, output);152set_state_id(hdcp, output, H2_A0_KNOWN_HDCP2_CAPABLE_RX);153} else if (is_cp_desired_hdcp1(hdcp)) {154callback_in_ms(0, output);155set_state_id(hdcp, output, H1_A0_WAIT_FOR_ACTIVE_RX);156} else {157callback_in_ms(0, output);158set_state_id(hdcp, output, HDCP_CP_NOT_DESIRED);159set_auth_complete(hdcp, output);160}161else {162callback_in_ms(0, output);163set_state_id(hdcp, output, HDCP_CP_NOT_DESIRED);164set_auth_complete(hdcp, output);165}166} else if (is_in_cp_not_desired_state(hdcp)) {167increment_stay_counter(hdcp);168} else if (is_in_hdcp1_states(hdcp)) {169status = mod_hdcp_hdcp1_transition(hdcp,170event_ctx, &input->hdcp1, output);171} else if (is_in_hdcp1_dp_states(hdcp)) {172status = mod_hdcp_hdcp1_dp_transition(hdcp,173event_ctx, &input->hdcp1, output);174} else if (is_in_hdcp2_states(hdcp)) {175status = mod_hdcp_hdcp2_transition(hdcp,176event_ctx, &input->hdcp2, output);177} else if (is_in_hdcp2_dp_states(hdcp)) {178status = mod_hdcp_hdcp2_dp_transition(hdcp,179event_ctx, &input->hdcp2, output);180} else {181status = MOD_HDCP_STATUS_INVALID_STATE;182}183out:184return status;185}186187static enum mod_hdcp_status reset_authentication(struct mod_hdcp *hdcp,188struct mod_hdcp_output *output)189{190enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS;191192if (is_hdcp1(hdcp)) {193if (hdcp->auth.trans_input.hdcp1.create_session != UNKNOWN) {194/* TODO - update psp to unify create session failure195* recovery between hdcp1 and 2.196*/197mod_hdcp_hdcp1_destroy_session(hdcp);198199}200201HDCP_TOP_RESET_AUTH_TRACE(hdcp);202memset(&hdcp->auth, 0, sizeof(struct mod_hdcp_authentication));203memset(&hdcp->state, 0, sizeof(struct mod_hdcp_state));204set_state_id(hdcp, output, HDCP_INITIALIZED);205} else if (is_hdcp2(hdcp)) {206if (hdcp->auth.trans_input.hdcp2.create_session == PASS) {207status = mod_hdcp_hdcp2_destroy_session(hdcp);208if (status != MOD_HDCP_STATUS_SUCCESS) {209output->callback_needed = 0;210output->watchdog_timer_needed = 0;211goto out;212}213}214215HDCP_TOP_RESET_AUTH_TRACE(hdcp);216memset(&hdcp->auth, 0, sizeof(struct mod_hdcp_authentication));217memset(&hdcp->state, 0, sizeof(struct mod_hdcp_state));218set_state_id(hdcp, output, HDCP_INITIALIZED);219} else if (is_in_cp_not_desired_state(hdcp)) {220HDCP_TOP_RESET_AUTH_TRACE(hdcp);221memset(&hdcp->auth, 0, sizeof(struct mod_hdcp_authentication));222memset(&hdcp->state, 0, sizeof(struct mod_hdcp_state));223set_state_id(hdcp, output, HDCP_INITIALIZED);224}225226out:227/* stop callback and watchdog requests from previous authentication*/228output->watchdog_timer_stop = 1;229output->callback_stop = 1;230return status;231}232233static enum mod_hdcp_status reset_connection(struct mod_hdcp *hdcp,234struct mod_hdcp_output *output)235{236enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS;237238memset(output, 0, sizeof(struct mod_hdcp_output));239240status = reset_authentication(hdcp, output);241if (status != MOD_HDCP_STATUS_SUCCESS)242goto out;243244if (current_state(hdcp) != HDCP_UNINITIALIZED) {245HDCP_TOP_RESET_CONN_TRACE(hdcp);246set_state_id(hdcp, output, HDCP_UNINITIALIZED);247}248memset(&hdcp->connection, 0, sizeof(hdcp->connection));249out:250return status;251}252253static enum mod_hdcp_status update_display_adjustments(struct mod_hdcp *hdcp,254struct mod_hdcp_display *display,255struct mod_hdcp_display_adjustment *adj)256{257enum mod_hdcp_status status = MOD_HDCP_STATUS_NOT_IMPLEMENTED;258259if (is_in_authenticated_states(hdcp) &&260is_dp_mst_hdcp(hdcp) &&261display->adjust.disable == true &&262adj->disable == false) {263display->adjust.disable = false;264if (is_hdcp1(hdcp))265status = mod_hdcp_hdcp1_enable_dp_stream_encryption(hdcp);266else if (is_hdcp2(hdcp))267status = mod_hdcp_hdcp2_enable_dp_stream_encryption(hdcp);268269if (status != MOD_HDCP_STATUS_SUCCESS)270display->adjust.disable = true;271}272273if (status == MOD_HDCP_STATUS_SUCCESS &&274memcmp(adj, &display->adjust,275sizeof(struct mod_hdcp_display_adjustment)) != 0)276status = MOD_HDCP_STATUS_NOT_IMPLEMENTED;277278return status;279}280/*281* Implementation of functions in mod_hdcp.h282*/283size_t mod_hdcp_get_memory_size(void)284{285return sizeof(struct mod_hdcp);286}287288enum mod_hdcp_status mod_hdcp_setup(struct mod_hdcp *hdcp,289struct mod_hdcp_config *config)290{291struct mod_hdcp_output output;292enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS;293294memset(&output, 0, sizeof(output));295hdcp->config = *config;296HDCP_TOP_INTERFACE_TRACE(hdcp);297status = reset_connection(hdcp, &output);298if (status != MOD_HDCP_STATUS_SUCCESS)299push_error_status(hdcp, status);300return status;301}302303enum mod_hdcp_status mod_hdcp_teardown(struct mod_hdcp *hdcp)304{305enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS;306struct mod_hdcp_output output;307308HDCP_TOP_INTERFACE_TRACE(hdcp);309memset(&output, 0, sizeof(output));310status = reset_connection(hdcp, &output);311if (status == MOD_HDCP_STATUS_SUCCESS)312memset(hdcp, 0, sizeof(struct mod_hdcp));313else314push_error_status(hdcp, status);315return status;316}317318enum mod_hdcp_status mod_hdcp_add_display(struct mod_hdcp *hdcp,319struct mod_hdcp_link *link, struct mod_hdcp_display *display,320struct mod_hdcp_output *output)321{322enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS;323struct mod_hdcp_display *display_container = NULL;324325HDCP_TOP_INTERFACE_TRACE_WITH_INDEX(hdcp, display->index);326memset(output, 0, sizeof(struct mod_hdcp_output));327328/* skip inactive display */329if (display->state != MOD_HDCP_DISPLAY_ACTIVE) {330status = MOD_HDCP_STATUS_SUCCESS;331goto out;332}333334/* check existing display container */335if (get_active_display_at_index(hdcp, display->index)) {336status = MOD_HDCP_STATUS_SUCCESS;337goto out;338}339340/* find an empty display container */341display_container = get_empty_display_container(hdcp);342if (!display_container) {343status = MOD_HDCP_STATUS_DISPLAY_OUT_OF_BOUND;344goto out;345}346347/* reset existing authentication status */348status = reset_authentication(hdcp, output);349if (status != MOD_HDCP_STATUS_SUCCESS)350goto out;351352/* reset retry counters */353reset_retry_counts(hdcp);354355/* reset error trace */356memset(&hdcp->connection.trace, 0, sizeof(hdcp->connection.trace));357358/* add display to connection */359hdcp->connection.link = *link;360*display_container = *display;361status = mod_hdcp_add_display_to_topology(hdcp, display_container);362363if (status != MOD_HDCP_STATUS_SUCCESS)364goto out;365366/* request authentication */367if (current_state(hdcp) != HDCP_INITIALIZED)368set_state_id(hdcp, output, HDCP_INITIALIZED);369callback_in_ms(hdcp->connection.link.adjust.auth_delay * 1000, output);370out:371if (status != MOD_HDCP_STATUS_SUCCESS)372push_error_status(hdcp, status);373374return status;375}376377enum mod_hdcp_status mod_hdcp_remove_display(struct mod_hdcp *hdcp,378uint8_t index, struct mod_hdcp_output *output)379{380enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS;381struct mod_hdcp_display *display = NULL;382383HDCP_TOP_INTERFACE_TRACE_WITH_INDEX(hdcp, index);384memset(output, 0, sizeof(struct mod_hdcp_output));385386/* find display in connection */387display = get_active_display_at_index(hdcp, index);388if (!display) {389status = MOD_HDCP_STATUS_SUCCESS;390goto out;391}392393/* stop current authentication */394status = reset_authentication(hdcp, output);395if (status != MOD_HDCP_STATUS_SUCCESS)396goto out;397398/* clear retry counters */399reset_retry_counts(hdcp);400401/* reset error trace */402memset(&hdcp->connection.trace, 0, sizeof(hdcp->connection.trace));403404/* remove display */405status = mod_hdcp_remove_display_from_topology(hdcp, index);406if (status != MOD_HDCP_STATUS_SUCCESS)407goto out;408memset(display, 0, sizeof(struct mod_hdcp_display));409410/* request authentication when connection is not reset */411if (current_state(hdcp) != HDCP_UNINITIALIZED)412callback_in_ms(hdcp->connection.link.adjust.auth_delay * 1000,413output);414out:415if (status != MOD_HDCP_STATUS_SUCCESS)416push_error_status(hdcp, status);417return status;418}419420enum mod_hdcp_status mod_hdcp_update_display(struct mod_hdcp *hdcp,421uint8_t index,422struct mod_hdcp_link_adjustment *link_adjust,423struct mod_hdcp_display_adjustment *display_adjust,424struct mod_hdcp_output *output)425{426enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS;427struct mod_hdcp_display *display = NULL;428429HDCP_TOP_INTERFACE_TRACE_WITH_INDEX(hdcp, index);430memset(output, 0, sizeof(struct mod_hdcp_output));431432/* find display in connection */433display = get_active_display_at_index(hdcp, index);434if (!display) {435status = MOD_HDCP_STATUS_DISPLAY_NOT_FOUND;436goto out;437}438439/* skip if no changes */440if (memcmp(link_adjust, &hdcp->connection.link.adjust,441sizeof(struct mod_hdcp_link_adjustment)) == 0 &&442memcmp(display_adjust, &display->adjust,443sizeof(struct mod_hdcp_display_adjustment)) == 0) {444status = MOD_HDCP_STATUS_SUCCESS;445goto out;446}447448if (memcmp(link_adjust, &hdcp->connection.link.adjust,449sizeof(struct mod_hdcp_link_adjustment)) == 0 &&450memcmp(display_adjust, &display->adjust,451sizeof(struct mod_hdcp_display_adjustment)) != 0) {452status = update_display_adjustments(hdcp, display, display_adjust);453if (status != MOD_HDCP_STATUS_NOT_IMPLEMENTED)454goto out;455}456457/* stop current authentication */458status = reset_authentication(hdcp, output);459if (status != MOD_HDCP_STATUS_SUCCESS)460goto out;461462/* clear retry counters */463reset_retry_counts(hdcp);464465/* reset error trace */466memset(&hdcp->connection.trace, 0, sizeof(hdcp->connection.trace));467468/* set new adjustment */469hdcp->connection.link.adjust = *link_adjust;470display->adjust = *display_adjust;471472/* request authentication when connection is not reset */473if (current_state(hdcp) != HDCP_UNINITIALIZED)474/* wait 100ms to debounce simultaneous updates for different indices */475callback_in_ms(100, output);476477out:478if (status != MOD_HDCP_STATUS_SUCCESS)479push_error_status(hdcp, status);480return status;481}482483enum mod_hdcp_status mod_hdcp_query_display(struct mod_hdcp *hdcp,484uint8_t index, struct mod_hdcp_display_query *query)485{486enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS;487struct mod_hdcp_display *display = NULL;488489/* find display in connection */490display = get_active_display_at_index(hdcp, index);491if (!display) {492status = MOD_HDCP_STATUS_DISPLAY_NOT_FOUND;493goto out;494}495496/* populate query */497query->link = &hdcp->connection.link;498query->display = display;499query->trace = &hdcp->connection.trace;500query->encryption_status = MOD_HDCP_ENCRYPTION_STATUS_HDCP_OFF;501502if (is_display_encryption_enabled(display)) {503if (is_hdcp1(hdcp)) {504query->encryption_status = MOD_HDCP_ENCRYPTION_STATUS_HDCP1_ON;505} else if (is_hdcp2(hdcp)) {506if (query->link->adjust.hdcp2.force_type == MOD_HDCP_FORCE_TYPE_0)507query->encryption_status = MOD_HDCP_ENCRYPTION_STATUS_HDCP2_TYPE0_ON;508else if (query->link->adjust.hdcp2.force_type == MOD_HDCP_FORCE_TYPE_1)509query->encryption_status = MOD_HDCP_ENCRYPTION_STATUS_HDCP2_TYPE1_ON;510else511query->encryption_status = MOD_HDCP_ENCRYPTION_STATUS_HDCP2_ON;512}513} else {514query->encryption_status = MOD_HDCP_ENCRYPTION_STATUS_HDCP_OFF;515}516517out:518return status;519}520521enum mod_hdcp_status mod_hdcp_reset_connection(struct mod_hdcp *hdcp,522struct mod_hdcp_output *output)523{524enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS;525526HDCP_TOP_INTERFACE_TRACE(hdcp);527status = reset_connection(hdcp, output);528if (status != MOD_HDCP_STATUS_SUCCESS)529push_error_status(hdcp, status);530531return status;532}533534enum mod_hdcp_status mod_hdcp_process_event(struct mod_hdcp *hdcp,535enum mod_hdcp_event event, struct mod_hdcp_output *output)536{537enum mod_hdcp_status exec_status, trans_status, reset_status, status;538struct mod_hdcp_event_context event_ctx;539540HDCP_EVENT_TRACE(hdcp, event);541memset(output, 0, sizeof(struct mod_hdcp_output));542memset(&event_ctx, 0, sizeof(struct mod_hdcp_event_context));543event_ctx.event = event;544545/* execute and transition */546exec_status = execution(hdcp, &event_ctx, &hdcp->auth.trans_input);547trans_status = transition(548hdcp, &event_ctx, &hdcp->auth.trans_input, output);549if (trans_status == MOD_HDCP_STATUS_SUCCESS) {550status = MOD_HDCP_STATUS_SUCCESS;551} else if (exec_status == MOD_HDCP_STATUS_SUCCESS) {552status = MOD_HDCP_STATUS_INTERNAL_POLICY_FAILURE;553push_error_status(hdcp, status);554} else {555status = exec_status;556push_error_status(hdcp, status);557}558559/* reset authentication if needed */560if (trans_status == MOD_HDCP_STATUS_RESET_NEEDED) {561mod_hdcp_log_ddc_trace(hdcp);562reset_status = reset_authentication(hdcp, output);563if (reset_status != MOD_HDCP_STATUS_SUCCESS)564push_error_status(hdcp, reset_status);565}566567/* Clear CP_IRQ status if needed */568if (event_ctx.event == MOD_HDCP_EVENT_CPIRQ) {569status = mod_hdcp_clear_cp_irq_status(hdcp);570if (status != MOD_HDCP_STATUS_SUCCESS)571push_error_status(hdcp, status);572}573574return status;575}576577enum mod_hdcp_operation_mode mod_hdcp_signal_type_to_operation_mode(578enum signal_type signal)579{580enum mod_hdcp_operation_mode mode = MOD_HDCP_MODE_OFF;581582switch (signal) {583case SIGNAL_TYPE_DVI_SINGLE_LINK:584case SIGNAL_TYPE_HDMI_TYPE_A:585mode = MOD_HDCP_MODE_DEFAULT;586break;587case SIGNAL_TYPE_EDP:588case SIGNAL_TYPE_DISPLAY_PORT:589case SIGNAL_TYPE_DISPLAY_PORT_MST:590mode = MOD_HDCP_MODE_DP;591break;592default:593break;594}595596return mode;597}598599600