Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
@@ -0,0 +1,31 @@
#
# 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.
#
#
# Makefile for the color sub-module of DAL.
#
MOD_COLOR = color_gamma.o color_table.o
AMD_DAL_MOD_COLOR = $(addprefix $(AMDDALPATH)/modules/color/,$(MOD_COLOR))
#$(info ************ DAL COLOR MODULE MAKEFILE ************)
AMD_DISPLAY_FILES += $(AMD_DAL_MOD_COLOR)
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,118 @@
/*
* Copyright 2016 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
*
*/
#ifndef COLOR_MOD_COLOR_GAMMA_H_
#define COLOR_MOD_COLOR_GAMMA_H_
#include "color_table.h"
struct dc_transfer_func;
struct dc_gamma;
struct dc_transfer_func_distributed_points;
struct dc_rgb_fixed;
struct dc_color_caps;
enum dc_transfer_func_predefined;
/* For SetRegamma ADL interface support
* Must match escape type
*/
union regamma_flags {
unsigned int raw;
struct {
unsigned int gammaRampArray :1; // RegammaRamp is in use
unsigned int gammaFromEdid :1; //gamma from edid is in use
unsigned int gammaFromEdidEx :1; //gamma from edid is in use , but only for Display Id 1.2
unsigned int gammaFromUser :1; //user custom gamma is used
unsigned int coeffFromUser :1; //coeff. A0-A3 from user is in use
unsigned int coeffFromEdid :1; //coeff. A0-A3 from edid is in use
unsigned int applyDegamma :1; //flag for additional degamma correction in driver
unsigned int gammaPredefinedSRGB :1; //flag for SRGB gamma
unsigned int gammaPredefinedPQ :1; //flag for PQ gamma
unsigned int gammaPredefinedPQ2084Interim :1; //flag for PQ gamma, lower max nits
unsigned int gammaPredefined36 :1; //flag for 3.6 gamma
unsigned int gammaPredefinedReset :1; //flag to return to previous gamma
} bits;
};
struct regamma_ramp {
unsigned short gamma[256*3]; // gamma ramp packed in same way as OS windows ,r , g & b
};
struct regamma_coeff {
int gamma[3];
int A0[3];
int A1[3];
int A2[3];
int A3[3];
};
struct regamma_lut {
union regamma_flags flags;
union {
struct regamma_ramp ramp;
struct regamma_coeff coeff;
};
};
struct hdr_tm_params {
unsigned int sdr_white_level;
unsigned int min_content; // luminance in 1/10000 nits
unsigned int max_content; // luminance in nits
unsigned int min_display; // luminance in 1/10000 nits
unsigned int max_display; // luminance in nits
unsigned int skip_tm; // skip tm
};
struct calculate_buffer {
int buffer_index;
struct fixed31_32 buffer[NUM_PTS_IN_REGION];
struct fixed31_32 gamma_of_2;
};
struct translate_from_linear_space_args {
struct fixed31_32 arg;
struct fixed31_32 a0;
struct fixed31_32 a1;
struct fixed31_32 a2;
struct fixed31_32 a3;
struct fixed31_32 gamma;
struct calculate_buffer *cal_buffer;
};
void setup_x_points_distribution(void);
void log_x_points_distribution(struct dal_logger *logger);
void precompute_pq(void);
void precompute_de_pq(void);
bool mod_color_calculate_regamma_params(struct dc_transfer_func *output_tf,
const struct dc_gamma *ramp, bool mapUserRamp, bool canRomBeUsed,
const struct hdr_tm_params *fs_params,
struct calculate_buffer *cal_buffer);
bool mod_color_calculate_degamma_params(struct dc_color_caps *dc_caps,
struct dc_transfer_func *output_tf,
const struct dc_gamma *ramp, bool mapUserRamp);
#endif /* COLOR_MOD_COLOR_GAMMA_H_ */
@@ -0,0 +1,64 @@
/*
* Copyright 2019 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 "color_table.h"
static struct fixed31_32 pq_table[MAX_HW_POINTS + 2];
static struct fixed31_32 de_pq_table[MAX_HW_POINTS + 2];
static bool pq_initialized;
static bool de_pg_initialized;
bool mod_color_is_table_init(enum table_type type)
{
bool ret = false;
if (type == type_pq_table)
ret = pq_initialized;
if (type == type_de_pq_table)
ret = de_pg_initialized;
return ret;
}
struct fixed31_32 *mod_color_get_table(enum table_type type)
{
struct fixed31_32 *table = NULL;
if (type == type_pq_table)
table = pq_table;
if (type == type_de_pq_table)
table = de_pq_table;
return table;
}
void mod_color_set_table_init_state(enum table_type type, bool state)
{
if (type == type_pq_table)
pq_initialized = state;
if (type == type_de_pq_table)
de_pg_initialized = state;
}
@@ -0,0 +1,47 @@
/*
* Copyright 2016 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
*
*/
#ifndef COLOR_MOD_COLOR_TABLE_H_
#define COLOR_MOD_COLOR_TABLE_H_
#include "dc_types.h"
#define NUM_PTS_IN_REGION 16
#define NUM_REGIONS 32
#define MAX_HW_POINTS (NUM_PTS_IN_REGION*NUM_REGIONS)
enum table_type {
type_pq_table,
type_de_pq_table
};
bool mod_color_is_table_init(enum table_type type);
struct fixed31_32 *mod_color_get_table(enum table_type type);
void mod_color_set_table_init_state(enum table_type type, bool state);
#endif /* COLOR_MOD_COLOR_TABLE_H_ */
@@ -0,0 +1,51 @@
/*
* Copyright 2016 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
*
*/
#ifndef LUTS_1D_H
#define LUTS_1D_H
#include "hw_shared.h"
struct point_config {
uint32_t custom_float_x;
uint32_t custom_float_y;
uint32_t custom_float_slope;
};
struct lut_point {
uint32_t red;
uint32_t green;
uint32_t blue;
uint32_t delta_red;
uint32_t delta_green;
uint32_t delta_blue;
};
struct pwl_1dlut_parameter {
struct gamma_curve arr_curve_points[34];
struct point_config arr_points[2];
struct lut_point rgb_resulted[256];
uint32_t hw_points_num;
};
#endif // LUTS_1D_H
@@ -0,0 +1,31 @@
#
# Copyright 2017 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.
#
#
# Makefile for the 'freesync' sub-module of DAL.
#
FREESYNC = freesync.o
AMD_DAL_FREESYNC = $(addprefix $(AMDDALPATH)/modules/freesync/,$(FREESYNC))
#$(info ************ DAL-FREE SYNC_MAKEFILE ************)
AMD_DISPLAY_FILES += $(AMD_DAL_FREESYNC)
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,33 @@
#
# Copyright 2019 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.
#
#
# Makefile for the 'hdcp' sub-module of DAL.
#
HDCP = hdcp_ddc.o hdcp_log.o hdcp_psp.o hdcp.o \
hdcp1_execution.o hdcp1_transition.o \
hdcp2_execution.o hdcp2_transition.o
AMD_DAL_HDCP = $(addprefix $(AMDDALPATH)/modules/hdcp/,$(HDCP))
#$(info ************ DAL-HDCP_MAKEFILE ************)
AMD_DISPLAY_FILES += $(AMD_DAL_HDCP)
@@ -0,0 +1,601 @@
/*
* Copyright 2019 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 "hdcp.h"
static void push_error_status(struct mod_hdcp *hdcp,
enum mod_hdcp_status status)
{
struct mod_hdcp_trace *trace = &hdcp->connection.trace;
const uint8_t retry_limit = hdcp->connection.link.adjust.retry_limit;
if (trace->error_count < MAX_NUM_OF_ERROR_TRACE) {
trace->errors[trace->error_count].status = status;
trace->errors[trace->error_count].state_id = hdcp->state.id;
trace->error_count++;
HDCP_ERROR_TRACE(hdcp, status);
}
if (is_hdcp1(hdcp)) {
hdcp->connection.hdcp1_retry_count++;
if (hdcp->connection.hdcp1_retry_count == retry_limit)
hdcp->connection.link.adjust.hdcp1.disable = 1;
} else if (is_hdcp2(hdcp)) {
hdcp->connection.hdcp2_retry_count++;
if (hdcp->connection.hdcp2_retry_count == retry_limit)
hdcp->connection.link.adjust.hdcp2.disable = 1;
}
}
static uint8_t is_cp_desired_hdcp1(struct mod_hdcp *hdcp)
{
int i, is_auth_needed = 0;
/* if all displays on the link don't need authentication,
* hdcp is not desired
*/
for (i = 0; i < MAX_NUM_OF_DISPLAYS; i++) {
if (hdcp->displays[i].state != MOD_HDCP_DISPLAY_INACTIVE &&
hdcp->displays[i].adjust.disable != MOD_HDCP_DISPLAY_DISABLE_AUTHENTICATION) {
is_auth_needed = 1;
break;
}
}
return is_auth_needed &&
!hdcp->connection.link.adjust.hdcp1.disable &&
!is_frl_hdcp(hdcp) &&
!hdcp->connection.is_hdcp1_revoked;
}
static uint8_t is_cp_desired_hdcp2(struct mod_hdcp *hdcp)
{
int i, is_auth_needed = 0;
/* if all displays on the link don't need authentication,
* hdcp is not desired
*/
for (i = 0; i < MAX_NUM_OF_DISPLAYS; i++) {
if (hdcp->displays[i].state != MOD_HDCP_DISPLAY_INACTIVE &&
hdcp->displays[i].adjust.disable != MOD_HDCP_DISPLAY_DISABLE_AUTHENTICATION) {
is_auth_needed = 1;
break;
}
}
return is_auth_needed &&
!hdcp->connection.link.adjust.hdcp2.disable &&
!hdcp->connection.is_hdcp2_revoked;
}
static enum mod_hdcp_status execution(struct mod_hdcp *hdcp,
struct mod_hdcp_event_context *event_ctx,
union mod_hdcp_transition_input *input)
{
enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS;
if (is_in_initialized_state(hdcp)) {
if (event_ctx->event != MOD_HDCP_EVENT_CALLBACK) {
event_ctx->unexpected_event = 1;
goto out;
}
/* initialize transition input */
memset(input, 0, sizeof(union mod_hdcp_transition_input));
} else if (is_in_cp_not_desired_state(hdcp)) {
if (event_ctx->event != MOD_HDCP_EVENT_CALLBACK) {
event_ctx->unexpected_event = 1;
goto out;
}
} else if (is_in_hdcp1_states(hdcp)) {
status = mod_hdcp_hdcp1_execution(hdcp, event_ctx, &input->hdcp1);
} else if (is_in_hdcp1_dp_states(hdcp)) {
status = mod_hdcp_hdcp1_dp_execution(hdcp,
event_ctx, &input->hdcp1);
} else if (is_in_hdcp2_states(hdcp)) {
status = mod_hdcp_hdcp2_execution(hdcp, event_ctx, &input->hdcp2);
} else if (is_in_hdcp2_dp_states(hdcp)) {
status = mod_hdcp_hdcp2_dp_execution(hdcp,
event_ctx, &input->hdcp2);
} else {
event_ctx->unexpected_event = 1;
goto out;
}
out:
return status;
}
static enum mod_hdcp_status transition(struct mod_hdcp *hdcp,
struct mod_hdcp_event_context *event_ctx,
union mod_hdcp_transition_input *input,
struct mod_hdcp_output *output)
{
enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS;
if (event_ctx->unexpected_event)
goto out;
if (is_in_initialized_state(hdcp)) {
if (is_dp_hdcp(hdcp))
if (is_cp_desired_hdcp2(hdcp)) {
callback_in_ms(0, output);
set_state_id(hdcp, output, D2_A0_DETERMINE_RX_HDCP_CAPABLE);
} else if (is_cp_desired_hdcp1(hdcp)) {
callback_in_ms(0, output);
set_state_id(hdcp, output, D1_A0_DETERMINE_RX_HDCP_CAPABLE);
} else {
callback_in_ms(0, output);
set_state_id(hdcp, output, HDCP_CP_NOT_DESIRED);
set_auth_complete(hdcp, output);
}
else if (is_hdmi_dvi_sl_hdcp(hdcp))
if (is_cp_desired_hdcp2(hdcp)) {
callback_in_ms(0, output);
set_state_id(hdcp, output, H2_A0_KNOWN_HDCP2_CAPABLE_RX);
} else if (is_cp_desired_hdcp1(hdcp)) {
callback_in_ms(0, output);
set_state_id(hdcp, output, H1_A0_WAIT_FOR_ACTIVE_RX);
} else {
callback_in_ms(0, output);
set_state_id(hdcp, output, HDCP_CP_NOT_DESIRED);
set_auth_complete(hdcp, output);
}
else {
callback_in_ms(0, output);
set_state_id(hdcp, output, HDCP_CP_NOT_DESIRED);
set_auth_complete(hdcp, output);
}
} else if (is_in_cp_not_desired_state(hdcp)) {
increment_stay_counter(hdcp);
} else if (is_in_hdcp1_states(hdcp)) {
status = mod_hdcp_hdcp1_transition(hdcp,
event_ctx, &input->hdcp1, output);
} else if (is_in_hdcp1_dp_states(hdcp)) {
status = mod_hdcp_hdcp1_dp_transition(hdcp,
event_ctx, &input->hdcp1, output);
} else if (is_in_hdcp2_states(hdcp)) {
status = mod_hdcp_hdcp2_transition(hdcp,
event_ctx, &input->hdcp2, output);
} else if (is_in_hdcp2_dp_states(hdcp)) {
status = mod_hdcp_hdcp2_dp_transition(hdcp,
event_ctx, &input->hdcp2, output);
} else {
status = MOD_HDCP_STATUS_INVALID_STATE;
}
out:
return status;
}
static enum mod_hdcp_status reset_authentication(struct mod_hdcp *hdcp,
struct mod_hdcp_output *output)
{
enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS;
if (is_hdcp1(hdcp)) {
if (hdcp->auth.trans_input.hdcp1.create_session != UNKNOWN) {
/* TODO - update psp to unify create session failure
* recovery between hdcp1 and 2.
*/
mod_hdcp_hdcp1_destroy_session(hdcp);
}
HDCP_TOP_RESET_AUTH_TRACE(hdcp);
memset(&hdcp->auth, 0, sizeof(struct mod_hdcp_authentication));
memset(&hdcp->state, 0, sizeof(struct mod_hdcp_state));
set_state_id(hdcp, output, HDCP_INITIALIZED);
} else if (is_hdcp2(hdcp)) {
if (hdcp->auth.trans_input.hdcp2.create_session == PASS) {
status = mod_hdcp_hdcp2_destroy_session(hdcp);
if (status != MOD_HDCP_STATUS_SUCCESS) {
output->callback_needed = 0;
output->watchdog_timer_needed = 0;
goto out;
}
}
HDCP_TOP_RESET_AUTH_TRACE(hdcp);
memset(&hdcp->auth, 0, sizeof(struct mod_hdcp_authentication));
memset(&hdcp->state, 0, sizeof(struct mod_hdcp_state));
set_state_id(hdcp, output, HDCP_INITIALIZED);
} else if (is_in_cp_not_desired_state(hdcp)) {
HDCP_TOP_RESET_AUTH_TRACE(hdcp);
memset(&hdcp->auth, 0, sizeof(struct mod_hdcp_authentication));
memset(&hdcp->state, 0, sizeof(struct mod_hdcp_state));
set_state_id(hdcp, output, HDCP_INITIALIZED);
}
out:
/* stop callback and watchdog requests from previous authentication*/
output->watchdog_timer_stop = 1;
output->callback_stop = 1;
return status;
}
static enum mod_hdcp_status reset_connection(struct mod_hdcp *hdcp,
struct mod_hdcp_output *output)
{
enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS;
memset(output, 0, sizeof(struct mod_hdcp_output));
status = reset_authentication(hdcp, output);
if (status != MOD_HDCP_STATUS_SUCCESS)
goto out;
if (current_state(hdcp) != HDCP_UNINITIALIZED) {
HDCP_TOP_RESET_CONN_TRACE(hdcp);
set_state_id(hdcp, output, HDCP_UNINITIALIZED);
}
memset(&hdcp->connection, 0, sizeof(hdcp->connection));
out:
return status;
}
static enum mod_hdcp_status update_display_adjustments(struct mod_hdcp *hdcp,
struct mod_hdcp_display *display,
struct mod_hdcp_display_adjustment *adj)
{
enum mod_hdcp_status status = MOD_HDCP_STATUS_NOT_IMPLEMENTED;
if (is_in_authenticated_states(hdcp) &&
is_dp_mst_hdcp(hdcp) &&
display->adjust.disable == true &&
adj->disable == false) {
display->adjust.disable = false;
if (is_hdcp1(hdcp))
status = mod_hdcp_hdcp1_enable_dp_stream_encryption(hdcp);
else if (is_hdcp2(hdcp))
status = mod_hdcp_hdcp2_enable_dp_stream_encryption(hdcp);
if (status != MOD_HDCP_STATUS_SUCCESS)
display->adjust.disable = true;
}
if (status == MOD_HDCP_STATUS_SUCCESS &&
memcmp(adj, &display->adjust,
sizeof(struct mod_hdcp_display_adjustment)) != 0)
status = MOD_HDCP_STATUS_NOT_IMPLEMENTED;
return status;
}
/*
* Implementation of functions in mod_hdcp.h
*/
size_t mod_hdcp_get_memory_size(void)
{
return sizeof(struct mod_hdcp);
}
enum mod_hdcp_status mod_hdcp_setup(struct mod_hdcp *hdcp,
struct mod_hdcp_config *config)
{
struct mod_hdcp_output output;
enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS;
memset(&output, 0, sizeof(output));
hdcp->config = *config;
HDCP_TOP_INTERFACE_TRACE(hdcp);
status = reset_connection(hdcp, &output);
if (status != MOD_HDCP_STATUS_SUCCESS)
push_error_status(hdcp, status);
return status;
}
enum mod_hdcp_status mod_hdcp_teardown(struct mod_hdcp *hdcp)
{
enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS;
struct mod_hdcp_output output;
HDCP_TOP_INTERFACE_TRACE(hdcp);
memset(&output, 0, sizeof(output));
status = reset_connection(hdcp, &output);
if (status == MOD_HDCP_STATUS_SUCCESS)
memset(hdcp, 0, sizeof(struct mod_hdcp));
else
push_error_status(hdcp, status);
return status;
}
enum mod_hdcp_status mod_hdcp_add_display(struct mod_hdcp *hdcp,
struct mod_hdcp_link *link, struct mod_hdcp_display *display,
struct mod_hdcp_output *output)
{
enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS;
struct mod_hdcp_display *display_container = NULL;
HDCP_TOP_INTERFACE_TRACE_WITH_INDEX(hdcp, display->index);
memset(output, 0, sizeof(struct mod_hdcp_output));
/* skip inactive display */
if (display->state != MOD_HDCP_DISPLAY_ACTIVE) {
status = MOD_HDCP_STATUS_SUCCESS;
goto out;
}
/* check existing display container */
if (get_active_display_at_index(hdcp, display->index)) {
status = MOD_HDCP_STATUS_SUCCESS;
goto out;
}
/* find an empty display container */
display_container = get_empty_display_container(hdcp);
if (!display_container) {
status = MOD_HDCP_STATUS_DISPLAY_OUT_OF_BOUND;
goto out;
}
/* reset existing authentication status */
status = reset_authentication(hdcp, output);
if (status != MOD_HDCP_STATUS_SUCCESS)
goto out;
/* reset retry counters */
reset_retry_counts(hdcp);
/* reset trace */
memset(&hdcp->connection.trace, 0, sizeof(hdcp->connection.trace));
/* add display to connection */
hdcp->connection.link = *link;
*display_container = *display;
status = mod_hdcp_add_display_to_topology(hdcp, display_container);
if (status != MOD_HDCP_STATUS_SUCCESS)
goto out;
/* request authentication */
if (current_state(hdcp) != HDCP_INITIALIZED)
set_state_id(hdcp, output, HDCP_INITIALIZED);
callback_in_ms(hdcp->connection.link.adjust.auth_delay * 1000, output);
out:
if (status != MOD_HDCP_STATUS_SUCCESS)
push_error_status(hdcp, status);
return status;
}
enum mod_hdcp_status mod_hdcp_remove_display(struct mod_hdcp *hdcp,
uint8_t index, struct mod_hdcp_output *output)
{
enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS;
struct mod_hdcp_display *display = NULL;
HDCP_TOP_INTERFACE_TRACE_WITH_INDEX(hdcp, index);
memset(output, 0, sizeof(struct mod_hdcp_output));
/* find display in connection */
display = get_active_display_at_index(hdcp, index);
if (!display) {
status = MOD_HDCP_STATUS_SUCCESS;
goto out;
}
/* stop current authentication */
status = reset_authentication(hdcp, output);
if (status != MOD_HDCP_STATUS_SUCCESS)
goto out;
/* clear retry counters */
reset_retry_counts(hdcp);
/* reset trace */
memset(&hdcp->connection.trace, 0, sizeof(hdcp->connection.trace));
/* remove display */
status = mod_hdcp_remove_display_from_topology(hdcp, index);
if (status != MOD_HDCP_STATUS_SUCCESS)
goto out;
memset(display, 0, sizeof(struct mod_hdcp_display));
/* request authentication when connection is not reset */
if (current_state(hdcp) != HDCP_UNINITIALIZED)
callback_in_ms(hdcp->connection.link.adjust.auth_delay * 1000,
output);
out:
if (status != MOD_HDCP_STATUS_SUCCESS)
push_error_status(hdcp, status);
return status;
}
enum mod_hdcp_status mod_hdcp_update_display(struct mod_hdcp *hdcp,
uint8_t index,
struct mod_hdcp_link_adjustment *link_adjust,
struct mod_hdcp_display_adjustment *display_adjust,
struct mod_hdcp_output *output)
{
enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS;
struct mod_hdcp_display *display = NULL;
HDCP_TOP_INTERFACE_TRACE_WITH_INDEX(hdcp, index);
memset(output, 0, sizeof(struct mod_hdcp_output));
/* find display in connection */
display = get_active_display_at_index(hdcp, index);
if (!display) {
status = MOD_HDCP_STATUS_DISPLAY_NOT_FOUND;
goto out;
}
/* skip if no changes */
if (memcmp(link_adjust, &hdcp->connection.link.adjust,
sizeof(struct mod_hdcp_link_adjustment)) == 0 &&
memcmp(display_adjust, &display->adjust,
sizeof(struct mod_hdcp_display_adjustment)) == 0) {
status = MOD_HDCP_STATUS_SUCCESS;
goto out;
}
if (memcmp(link_adjust, &hdcp->connection.link.adjust,
sizeof(struct mod_hdcp_link_adjustment)) == 0 &&
memcmp(display_adjust, &display->adjust,
sizeof(struct mod_hdcp_display_adjustment)) != 0) {
status = update_display_adjustments(hdcp, display, display_adjust);
if (status != MOD_HDCP_STATUS_NOT_IMPLEMENTED)
goto out;
}
/* stop current authentication */
status = reset_authentication(hdcp, output);
if (status != MOD_HDCP_STATUS_SUCCESS)
goto out;
/* clear retry counters */
reset_retry_counts(hdcp);
/* reset trace */
memset(&hdcp->connection.trace, 0, sizeof(hdcp->connection.trace));
/* set new adjustment */
hdcp->connection.link.adjust = *link_adjust;
display->adjust = *display_adjust;
/* request authentication when connection is not reset */
if (current_state(hdcp) != HDCP_UNINITIALIZED)
/* wait 100ms to debounce simultaneous updates for different indices */
callback_in_ms(100, output);
out:
if (status != MOD_HDCP_STATUS_SUCCESS)
push_error_status(hdcp, status);
return status;
}
enum mod_hdcp_status mod_hdcp_query_display(struct mod_hdcp *hdcp,
uint8_t index, struct mod_hdcp_display_query *query)
{
enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS;
struct mod_hdcp_display *display = NULL;
/* find display in connection */
display = get_active_display_at_index(hdcp, index);
if (!display) {
status = MOD_HDCP_STATUS_DISPLAY_NOT_FOUND;
goto out;
}
/* populate query */
query->link = &hdcp->connection.link;
query->display = display;
query->trace = &hdcp->connection.trace;
query->encryption_status = MOD_HDCP_ENCRYPTION_STATUS_HDCP_OFF;
if (is_display_encryption_enabled(display)) {
if (is_hdcp1(hdcp)) {
query->encryption_status = MOD_HDCP_ENCRYPTION_STATUS_HDCP1_ON;
} else if (is_hdcp2(hdcp)) {
if (query->link->adjust.hdcp2.force_type == MOD_HDCP_FORCE_TYPE_0)
query->encryption_status = MOD_HDCP_ENCRYPTION_STATUS_HDCP2_TYPE0_ON;
else if (query->link->adjust.hdcp2.force_type == MOD_HDCP_FORCE_TYPE_1)
query->encryption_status = MOD_HDCP_ENCRYPTION_STATUS_HDCP2_TYPE1_ON;
else
query->encryption_status = MOD_HDCP_ENCRYPTION_STATUS_HDCP2_ON;
}
} else {
query->encryption_status = MOD_HDCP_ENCRYPTION_STATUS_HDCP_OFF;
}
out:
return status;
}
enum mod_hdcp_status mod_hdcp_reset_connection(struct mod_hdcp *hdcp,
struct mod_hdcp_output *output)
{
enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS;
HDCP_TOP_INTERFACE_TRACE(hdcp);
status = reset_connection(hdcp, output);
if (status != MOD_HDCP_STATUS_SUCCESS)
push_error_status(hdcp, status);
return status;
}
enum mod_hdcp_status mod_hdcp_process_event(struct mod_hdcp *hdcp,
enum mod_hdcp_event event, struct mod_hdcp_output *output)
{
enum mod_hdcp_status exec_status, trans_status, reset_status, status;
struct mod_hdcp_event_context event_ctx;
HDCP_EVENT_TRACE(hdcp, event);
memset(output, 0, sizeof(struct mod_hdcp_output));
memset(&event_ctx, 0, sizeof(struct mod_hdcp_event_context));
event_ctx.event = event;
/* execute and transition */
exec_status = execution(hdcp, &event_ctx, &hdcp->auth.trans_input);
trans_status = transition(
hdcp, &event_ctx, &hdcp->auth.trans_input, output);
if (trans_status == MOD_HDCP_STATUS_SUCCESS) {
status = MOD_HDCP_STATUS_SUCCESS;
} else if (exec_status == MOD_HDCP_STATUS_SUCCESS) {
status = MOD_HDCP_STATUS_INTERNAL_POLICY_FAILURE;
push_error_status(hdcp, status);
} else {
status = exec_status;
push_error_status(hdcp, status);
}
/* reset authentication if needed */
if (trans_status == MOD_HDCP_STATUS_RESET_NEEDED) {
mod_hdcp_log_ddc_trace(hdcp);
reset_status = reset_authentication(hdcp, output);
if (reset_status != MOD_HDCP_STATUS_SUCCESS)
push_error_status(hdcp, reset_status);
}
/* Clear CP_IRQ status if needed */
if (event_ctx.event == MOD_HDCP_EVENT_CPIRQ) {
status = mod_hdcp_clear_cp_irq_status(hdcp);
if (status != MOD_HDCP_STATUS_SUCCESS)
push_error_status(hdcp, status);
}
return status;
}
enum mod_hdcp_operation_mode mod_hdcp_signal_type_to_operation_mode(
enum signal_type signal)
{
enum mod_hdcp_operation_mode mode = MOD_HDCP_MODE_OFF;
switch (signal) {
case SIGNAL_TYPE_DVI_SINGLE_LINK:
case SIGNAL_TYPE_HDMI_TYPE_A:
case SIGNAL_TYPE_HDMI_FRL:
mode = MOD_HDCP_MODE_DEFAULT;
break;
case SIGNAL_TYPE_EDP:
case SIGNAL_TYPE_DISPLAY_PORT:
case SIGNAL_TYPE_DISPLAY_PORT_MST:
mode = MOD_HDCP_MODE_DP;
break;
default:
break;
}
return mode;
}
@@ -0,0 +1,593 @@
/*
* Copyright 2019 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
*
*/
#ifndef HDCP_H_
#define HDCP_H_
#include "mod_hdcp.h"
#include "hdcp_log.h"
#include <drm/display/drm_dp_helper.h>
#include <drm/display/drm_hdcp_helper.h>
enum mod_hdcp_trans_input_result {
UNKNOWN = 0,
PASS,
FAIL
};
struct mod_hdcp_transition_input_hdcp1 {
uint8_t bksv_read;
uint8_t bksv_validation;
uint8_t create_session;
uint8_t an_write;
uint8_t aksv_write;
uint8_t ainfo_write;
uint8_t bcaps_read;
uint8_t r0p_read;
uint8_t rx_validation;
uint8_t encryption;
uint8_t link_maintenance;
uint8_t ready_check;
uint8_t bstatus_read;
uint8_t max_cascade_check;
uint8_t max_devs_check;
uint8_t device_count_check;
uint8_t ksvlist_read;
uint8_t vp_read;
uint8_t ksvlist_vp_validation;
uint8_t hdcp_capable_dp;
uint8_t binfo_read_dp;
uint8_t r0p_available_dp;
uint8_t link_integrity_check;
uint8_t reauth_request_check;
uint8_t stream_encryption_dp;
};
struct mod_hdcp_transition_input_hdcp2 {
uint8_t hdcp2version_read;
uint8_t hdcp2_capable_check;
uint8_t create_session;
uint8_t ake_init_prepare;
uint8_t ake_init_write;
uint8_t rxstatus_read;
uint8_t ake_cert_available;
uint8_t ake_cert_read;
uint8_t ake_cert_validation;
uint8_t stored_km_write;
uint8_t no_stored_km_write;
uint8_t h_prime_available;
uint8_t h_prime_read;
uint8_t pairing_available;
uint8_t pairing_info_read;
uint8_t h_prime_validation;
uint8_t lc_init_prepare;
uint8_t lc_init_write;
uint8_t l_prime_available_poll;
uint8_t l_prime_read;
uint8_t l_prime_combo_read;
uint8_t l_prime_validation;
uint8_t eks_prepare;
uint8_t eks_write;
uint8_t enable_encryption;
uint8_t reauth_request_check;
uint8_t rx_id_list_read;
uint8_t device_count_check;
uint8_t rx_id_list_validation;
uint8_t repeater_auth_ack_write;
uint8_t prepare_stream_manage;
uint8_t stream_manage_write;
uint8_t stream_ready_available;
uint8_t stream_ready_read;
uint8_t stream_ready_validation;
uint8_t rx_caps_read_dp;
uint8_t content_stream_type_write;
uint8_t link_integrity_check_dp;
uint8_t stream_encryption_dp;
};
union mod_hdcp_transition_input {
struct mod_hdcp_transition_input_hdcp1 hdcp1;
struct mod_hdcp_transition_input_hdcp2 hdcp2;
};
struct mod_hdcp_message_hdcp1 {
uint8_t an[8];
uint8_t aksv[5];
uint8_t ainfo;
uint8_t bksv[5];
uint16_t r0p;
uint8_t bcaps;
uint16_t bstatus;
uint8_t ksvlist[635];
uint16_t ksvlist_size;
uint8_t vp[20];
uint16_t binfo_dp;
};
struct mod_hdcp_message_hdcp2 {
uint8_t hdcp2version_hdmi;
uint8_t rxcaps_dp[3];
uint8_t rxstatus[2];
uint8_t ake_init[12];
uint8_t ake_cert[534];
uint8_t ake_no_stored_km[129];
uint8_t ake_stored_km[33];
uint8_t ake_h_prime[33];
uint8_t ake_pairing_info[17];
uint8_t lc_init[9];
uint8_t lc_l_prime[33];
uint8_t ske_eks[25];
uint8_t rx_id_list[177]; // 22 + 5 * 31
uint16_t rx_id_list_size;
uint8_t repeater_auth_ack[17];
uint8_t repeater_auth_stream_manage[68]; // 6 + 2 * 31
uint16_t stream_manage_size;
uint8_t repeater_auth_stream_ready[33];
uint8_t rxstatus_dp;
uint8_t content_stream_type_dp[2];
};
union mod_hdcp_message {
struct mod_hdcp_message_hdcp1 hdcp1;
struct mod_hdcp_message_hdcp2 hdcp2;
};
struct mod_hdcp_auth_counters {
uint8_t stream_management_retry_count;
};
/* contains values per connection */
struct mod_hdcp_connection {
struct mod_hdcp_link link;
uint8_t is_repeater;
uint8_t is_km_stored;
uint8_t is_hdcp1_revoked;
uint8_t is_hdcp2_revoked;
struct mod_hdcp_trace trace;
uint8_t hdcp1_retry_count;
uint8_t hdcp2_retry_count;
};
/* contains values per authentication cycle */
struct mod_hdcp_authentication {
uint32_t id;
union mod_hdcp_message msg;
union mod_hdcp_transition_input trans_input;
struct mod_hdcp_auth_counters count;
};
/* contains values per state change */
struct mod_hdcp_state {
uint8_t id;
uint32_t stay_count;
};
/* per event in a state */
struct mod_hdcp_event_context {
enum mod_hdcp_event event;
uint8_t rx_id_list_ready;
uint8_t unexpected_event;
};
struct mod_hdcp {
/* per link */
struct mod_hdcp_config config;
/* per connection */
struct mod_hdcp_connection connection;
/* per displays */
struct mod_hdcp_display displays[MAX_NUM_OF_DISPLAYS];
/* per authentication attempt */
struct mod_hdcp_authentication auth;
/* per state in an authentication */
struct mod_hdcp_state state;
/* reserved memory buffer */
uint8_t buf[2025];
};
enum mod_hdcp_initial_state_id {
HDCP_UNINITIALIZED = 0x0,
HDCP_INITIAL_STATE_START = HDCP_UNINITIALIZED,
HDCP_INITIALIZED,
HDCP_CP_NOT_DESIRED,
HDCP_INITIAL_STATE_END = HDCP_CP_NOT_DESIRED
};
enum mod_hdcp_hdcp1_state_id {
HDCP1_STATE_START = HDCP_INITIAL_STATE_END,
H1_A0_WAIT_FOR_ACTIVE_RX,
H1_A1_EXCHANGE_KSVS,
H1_A2_COMPUTATIONS_A3_VALIDATE_RX_A6_TEST_FOR_REPEATER,
H1_A45_AUTHENTICATED,
H1_A8_WAIT_FOR_READY,
H1_A9_READ_KSV_LIST,
HDCP1_STATE_END = H1_A9_READ_KSV_LIST
};
enum mod_hdcp_hdcp1_dp_state_id {
HDCP1_DP_STATE_START = HDCP1_STATE_END,
D1_A0_DETERMINE_RX_HDCP_CAPABLE,
D1_A1_EXCHANGE_KSVS,
D1_A23_WAIT_FOR_R0_PRIME,
D1_A2_COMPUTATIONS_A3_VALIDATE_RX_A5_TEST_FOR_REPEATER,
D1_A4_AUTHENTICATED,
D1_A6_WAIT_FOR_READY,
D1_A7_READ_KSV_LIST,
HDCP1_DP_STATE_END = D1_A7_READ_KSV_LIST,
};
enum mod_hdcp_hdcp2_state_id {
HDCP2_STATE_START = HDCP1_DP_STATE_END,
H2_A0_KNOWN_HDCP2_CAPABLE_RX,
H2_A1_SEND_AKE_INIT,
H2_A1_VALIDATE_AKE_CERT,
H2_A1_SEND_NO_STORED_KM,
H2_A1_READ_H_PRIME,
H2_A1_READ_PAIRING_INFO_AND_VALIDATE_H_PRIME,
H2_A1_SEND_STORED_KM,
H2_A1_VALIDATE_H_PRIME,
H2_A2_LOCALITY_CHECK,
H2_A3_EXCHANGE_KS_AND_TEST_FOR_REPEATER,
H2_ENABLE_ENCRYPTION,
H2_A5_AUTHENTICATED,
H2_A6_WAIT_FOR_RX_ID_LIST,
H2_A78_VERIFY_RX_ID_LIST_AND_SEND_ACK,
H2_A9_SEND_STREAM_MANAGEMENT,
H2_A9_VALIDATE_STREAM_READY,
HDCP2_STATE_END = H2_A9_VALIDATE_STREAM_READY,
};
enum mod_hdcp_hdcp2_dp_state_id {
HDCP2_DP_STATE_START = HDCP2_STATE_END,
D2_A0_DETERMINE_RX_HDCP_CAPABLE,
D2_A1_SEND_AKE_INIT,
D2_A1_VALIDATE_AKE_CERT,
D2_A1_SEND_NO_STORED_KM,
D2_A1_READ_H_PRIME,
D2_A1_READ_PAIRING_INFO_AND_VALIDATE_H_PRIME,
D2_A1_SEND_STORED_KM,
D2_A1_VALIDATE_H_PRIME,
D2_A2_LOCALITY_CHECK,
D2_A34_EXCHANGE_KS_AND_TEST_FOR_REPEATER,
D2_SEND_CONTENT_STREAM_TYPE,
D2_ENABLE_ENCRYPTION,
D2_A5_AUTHENTICATED,
D2_A6_WAIT_FOR_RX_ID_LIST,
D2_A78_VERIFY_RX_ID_LIST_AND_SEND_ACK,
D2_A9_SEND_STREAM_MANAGEMENT,
D2_A9_VALIDATE_STREAM_READY,
HDCP2_DP_STATE_END = D2_A9_VALIDATE_STREAM_READY,
HDCP_STATE_END = HDCP2_DP_STATE_END,
};
/* hdcp1 executions and transitions */
typedef enum mod_hdcp_status (*mod_hdcp_action)(struct mod_hdcp *hdcp);
uint8_t mod_hdcp_execute_and_set(
mod_hdcp_action func, uint8_t *flag,
enum mod_hdcp_status *status, struct mod_hdcp *hdcp, char *str);
enum mod_hdcp_status mod_hdcp_hdcp1_execution(struct mod_hdcp *hdcp,
struct mod_hdcp_event_context *event_ctx,
struct mod_hdcp_transition_input_hdcp1 *input);
enum mod_hdcp_status mod_hdcp_hdcp1_dp_execution(struct mod_hdcp *hdcp,
struct mod_hdcp_event_context *event_ctx,
struct mod_hdcp_transition_input_hdcp1 *input);
enum mod_hdcp_status 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);
enum mod_hdcp_status 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);
/* hdcp2 executions and transitions */
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 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 mod_hdcp_hdcp2_transition(struct mod_hdcp *hdcp,
struct mod_hdcp_event_context *event_ctx,
struct mod_hdcp_transition_input_hdcp2 *input,
struct mod_hdcp_output *output);
enum mod_hdcp_status mod_hdcp_hdcp2_dp_transition(struct mod_hdcp *hdcp,
struct mod_hdcp_event_context *event_ctx,
struct mod_hdcp_transition_input_hdcp2 *input,
struct mod_hdcp_output *output);
/* log functions */
void mod_hdcp_dump_binary_message(uint8_t *msg, uint32_t msg_size,
uint8_t *buf, uint32_t buf_size);
void mod_hdcp_log_ddc_trace(struct mod_hdcp *hdcp);
/* TODO: add adjustment log */
/* psp functions */
enum mod_hdcp_status mod_hdcp_add_display_to_topology(
struct mod_hdcp *hdcp, struct mod_hdcp_display *display);
enum mod_hdcp_status mod_hdcp_remove_display_from_topology(
struct mod_hdcp *hdcp, uint8_t index);
enum mod_hdcp_status mod_hdcp_hdcp1_create_session(struct mod_hdcp *hdcp);
enum mod_hdcp_status mod_hdcp_hdcp1_destroy_session(struct mod_hdcp *hdcp);
enum mod_hdcp_status mod_hdcp_hdcp1_validate_rx(struct mod_hdcp *hdcp);
enum mod_hdcp_status mod_hdcp_hdcp1_enable_encryption(struct mod_hdcp *hdcp);
enum mod_hdcp_status mod_hdcp_hdcp1_validate_ksvlist_vp(struct mod_hdcp *hdcp);
enum mod_hdcp_status mod_hdcp_hdcp1_enable_dp_stream_encryption(
struct mod_hdcp *hdcp);
enum mod_hdcp_status mod_hdcp_hdcp1_link_maintenance(struct mod_hdcp *hdcp);
enum mod_hdcp_status mod_hdcp_hdcp2_create_session(struct mod_hdcp *hdcp);
enum mod_hdcp_status mod_hdcp_hdcp2_destroy_session(struct mod_hdcp *hdcp);
enum mod_hdcp_status mod_hdcp_hdcp2_prepare_ake_init(struct mod_hdcp *hdcp);
enum mod_hdcp_status mod_hdcp_hdcp2_validate_ake_cert(struct mod_hdcp *hdcp);
enum mod_hdcp_status mod_hdcp_hdcp2_validate_h_prime(struct mod_hdcp *hdcp);
enum mod_hdcp_status mod_hdcp_hdcp2_prepare_lc_init(struct mod_hdcp *hdcp);
enum mod_hdcp_status mod_hdcp_hdcp2_validate_l_prime(struct mod_hdcp *hdcp);
enum mod_hdcp_status mod_hdcp_hdcp2_prepare_eks(struct mod_hdcp *hdcp);
enum mod_hdcp_status mod_hdcp_hdcp2_enable_encryption(struct mod_hdcp *hdcp);
enum mod_hdcp_status mod_hdcp_hdcp2_validate_rx_id_list(struct mod_hdcp *hdcp);
enum mod_hdcp_status mod_hdcp_hdcp2_enable_dp_stream_encryption(
struct mod_hdcp *hdcp);
enum mod_hdcp_status mod_hdcp_hdcp2_prepare_stream_management(
struct mod_hdcp *hdcp);
enum mod_hdcp_status mod_hdcp_hdcp2_validate_stream_ready(
struct mod_hdcp *hdcp);
/* ddc functions */
enum mod_hdcp_status mod_hdcp_read_bksv(struct mod_hdcp *hdcp);
enum mod_hdcp_status mod_hdcp_read_bcaps(struct mod_hdcp *hdcp);
enum mod_hdcp_status mod_hdcp_read_bstatus(struct mod_hdcp *hdcp);
enum mod_hdcp_status mod_hdcp_read_r0p(struct mod_hdcp *hdcp);
enum mod_hdcp_status mod_hdcp_read_ksvlist(struct mod_hdcp *hdcp);
enum mod_hdcp_status mod_hdcp_read_vp(struct mod_hdcp *hdcp);
enum mod_hdcp_status mod_hdcp_read_binfo(struct mod_hdcp *hdcp);
enum mod_hdcp_status mod_hdcp_write_aksv(struct mod_hdcp *hdcp);
enum mod_hdcp_status mod_hdcp_write_ainfo(struct mod_hdcp *hdcp);
enum mod_hdcp_status mod_hdcp_write_an(struct mod_hdcp *hdcp);
enum mod_hdcp_status mod_hdcp_read_hdcp2version(struct mod_hdcp *hdcp);
enum mod_hdcp_status mod_hdcp_read_rxcaps(struct mod_hdcp *hdcp);
enum mod_hdcp_status mod_hdcp_read_rxstatus(struct mod_hdcp *hdcp);
enum mod_hdcp_status mod_hdcp_read_ake_cert(struct mod_hdcp *hdcp);
enum mod_hdcp_status mod_hdcp_read_h_prime(struct mod_hdcp *hdcp);
enum mod_hdcp_status mod_hdcp_read_pairing_info(struct mod_hdcp *hdcp);
enum mod_hdcp_status mod_hdcp_read_l_prime(struct mod_hdcp *hdcp);
enum mod_hdcp_status mod_hdcp_read_rx_id_list(struct mod_hdcp *hdcp);
enum mod_hdcp_status mod_hdcp_read_stream_ready(struct mod_hdcp *hdcp);
enum mod_hdcp_status mod_hdcp_write_ake_init(struct mod_hdcp *hdcp);
enum mod_hdcp_status mod_hdcp_write_no_stored_km(struct mod_hdcp *hdcp);
enum mod_hdcp_status mod_hdcp_write_stored_km(struct mod_hdcp *hdcp);
enum mod_hdcp_status mod_hdcp_write_lc_init(struct mod_hdcp *hdcp);
enum mod_hdcp_status mod_hdcp_write_eks(struct mod_hdcp *hdcp);
enum mod_hdcp_status mod_hdcp_write_repeater_auth_ack(struct mod_hdcp *hdcp);
enum mod_hdcp_status mod_hdcp_write_stream_manage(struct mod_hdcp *hdcp);
enum mod_hdcp_status mod_hdcp_write_content_type(struct mod_hdcp *hdcp);
enum mod_hdcp_status mod_hdcp_clear_cp_irq_status(struct mod_hdcp *hdcp);
enum mod_hdcp_status mod_hdcp_write_poll_read_lc_fw(struct mod_hdcp *hdcp);
/* hdcp version helpers */
static inline uint8_t is_dp_hdcp(struct mod_hdcp *hdcp)
{
return (hdcp->connection.link.mode == MOD_HDCP_MODE_DP);
}
static inline uint8_t is_dp_mst_hdcp(struct mod_hdcp *hdcp)
{
return (hdcp->connection.link.mode == MOD_HDCP_MODE_DP &&
hdcp->connection.link.dp.mst_enabled);
}
static inline uint8_t is_hdmi_dvi_sl_hdcp(struct mod_hdcp *hdcp)
{
return (hdcp->connection.link.mode == MOD_HDCP_MODE_DEFAULT);
}
static inline uint8_t is_frl_hdcp(struct mod_hdcp *hdcp)
{
return (hdcp->connection.link.mode == MOD_HDCP_MODE_DEFAULT &&
hdcp->connection.link.hdmi.frl_enabled);
}
/* hdcp state helpers */
static inline uint8_t current_state(struct mod_hdcp *hdcp)
{
return hdcp->state.id;
}
static inline void set_state_id(struct mod_hdcp *hdcp,
struct mod_hdcp_output *output, uint8_t id)
{
memset(&hdcp->state, 0, sizeof(hdcp->state));
hdcp->state.id = id;
/* callback timer should be reset per state */
output->callback_stop = 1;
output->watchdog_timer_stop = 1;
HDCP_NEXT_STATE_TRACE(hdcp, id, output);
}
static inline uint8_t is_in_hdcp1_states(struct mod_hdcp *hdcp)
{
return (current_state(hdcp) > HDCP1_STATE_START &&
current_state(hdcp) <= HDCP1_STATE_END);
}
static inline uint8_t is_in_hdcp1_dp_states(struct mod_hdcp *hdcp)
{
return (current_state(hdcp) > HDCP1_DP_STATE_START &&
current_state(hdcp) <= HDCP1_DP_STATE_END);
}
static inline uint8_t is_in_hdcp2_states(struct mod_hdcp *hdcp)
{
return (current_state(hdcp) > HDCP2_STATE_START &&
current_state(hdcp) <= HDCP2_STATE_END);
}
static inline uint8_t is_in_hdcp2_dp_states(struct mod_hdcp *hdcp)
{
return (current_state(hdcp) > HDCP2_DP_STATE_START &&
current_state(hdcp) <= HDCP2_DP_STATE_END);
}
static inline uint8_t is_in_authenticated_states(struct mod_hdcp *hdcp)
{
return (current_state(hdcp) == D1_A4_AUTHENTICATED ||
current_state(hdcp) == H1_A45_AUTHENTICATED ||
current_state(hdcp) == D2_A5_AUTHENTICATED ||
current_state(hdcp) == H2_A5_AUTHENTICATED);
}
static inline uint8_t is_hdcp1(struct mod_hdcp *hdcp)
{
return (is_in_hdcp1_states(hdcp) || is_in_hdcp1_dp_states(hdcp));
}
static inline uint8_t is_hdcp2(struct mod_hdcp *hdcp)
{
return (is_in_hdcp2_states(hdcp) || is_in_hdcp2_dp_states(hdcp));
}
static inline uint8_t is_in_cp_not_desired_state(struct mod_hdcp *hdcp)
{
return current_state(hdcp) == HDCP_CP_NOT_DESIRED;
}
static inline uint8_t is_in_initialized_state(struct mod_hdcp *hdcp)
{
return current_state(hdcp) == HDCP_INITIALIZED;
}
/* transition operation helpers */
static inline void increment_stay_counter(struct mod_hdcp *hdcp)
{
hdcp->state.stay_count++;
}
static inline void fail_and_restart_in_ms(uint16_t time,
enum mod_hdcp_status *status,
struct mod_hdcp_output *output)
{
output->callback_needed = 1;
output->callback_delay = time;
output->watchdog_timer_needed = 0;
output->watchdog_timer_delay = 0;
*status = MOD_HDCP_STATUS_RESET_NEEDED;
}
static inline void callback_in_ms(uint16_t time, struct mod_hdcp_output *output)
{
output->callback_needed = 1;
output->callback_delay = time;
}
static inline void set_watchdog_in_ms(struct mod_hdcp *hdcp, uint16_t time,
struct mod_hdcp_output *output)
{
(void)hdcp;
output->watchdog_timer_needed = 1;
output->watchdog_timer_delay = time;
}
static inline void set_auth_complete(struct mod_hdcp *hdcp,
struct mod_hdcp_output *output)
{
output->auth_complete = 1;
HDCP_AUTH_COMPLETE_TRACE(hdcp);
}
/* connection topology helpers */
static inline uint8_t is_display_active(struct mod_hdcp_display *display)
{
return display->state >= MOD_HDCP_DISPLAY_ACTIVE;
}
static inline uint8_t is_display_encryption_enabled(struct mod_hdcp_display *display)
{
return display->state >= MOD_HDCP_DISPLAY_ENCRYPTION_ENABLED;
}
static inline uint8_t get_active_display_count(struct mod_hdcp *hdcp)
{
uint8_t active_count = 0;
uint8_t i;
for (i = 0; i < MAX_NUM_OF_DISPLAYS; i++)
if (is_display_active(&hdcp->displays[i]))
active_count++;
return active_count;
}
static inline struct mod_hdcp_display *get_first_active_display(
struct mod_hdcp *hdcp)
{
uint8_t i;
struct mod_hdcp_display *display = NULL;
for (i = 0; i < MAX_NUM_OF_DISPLAYS; i++)
if (is_display_active(&hdcp->displays[i])) {
display = &hdcp->displays[i];
break;
}
return display;
}
static inline struct mod_hdcp_display *get_active_display_at_index(
struct mod_hdcp *hdcp, uint8_t index)
{
uint8_t i;
struct mod_hdcp_display *display = NULL;
for (i = 0; i < MAX_NUM_OF_DISPLAYS; i++)
if (hdcp->displays[i].index == index &&
is_display_active(&hdcp->displays[i])) {
display = &hdcp->displays[i];
break;
}
return display;
}
static inline struct mod_hdcp_display *get_empty_display_container(
struct mod_hdcp *hdcp)
{
uint8_t i;
struct mod_hdcp_display *display = NULL;
for (i = 0; i < MAX_NUM_OF_DISPLAYS; i++)
if (!is_display_active(&hdcp->displays[i])) {
display = &hdcp->displays[i];
break;
}
return display;
}
static inline void reset_retry_counts(struct mod_hdcp *hdcp)
{
hdcp->connection.hdcp1_retry_count = 0;
hdcp->connection.hdcp2_retry_count = 0;
}
#endif /* HDCP_H_ */
@@ -0,0 +1,544 @@
/*
* Copyright 2019 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 "hdcp.h"
static inline enum mod_hdcp_status validate_bksv(struct mod_hdcp *hdcp)
{
uint64_t n = 0;
uint8_t count = 0;
enum mod_hdcp_status status;
u8 bksv[sizeof(n)] = { };
memcpy(bksv, hdcp->auth.msg.hdcp1.bksv, sizeof(hdcp->auth.msg.hdcp1.bksv));
n = *(uint64_t *)bksv;
while (n) {
count++;
n &= (n - 1);
}
if (count == 20) {
hdcp->connection.trace.hdcp1.attempt_count++;
status = MOD_HDCP_STATUS_SUCCESS;
} else {
status = MOD_HDCP_STATUS_HDCP1_INVALID_BKSV;
}
return status;
}
static inline enum mod_hdcp_status check_ksv_ready(struct mod_hdcp *hdcp)
{
if (is_dp_hdcp(hdcp))
return (hdcp->auth.msg.hdcp1.bstatus & DP_BSTATUS_READY) ?
MOD_HDCP_STATUS_SUCCESS :
MOD_HDCP_STATUS_HDCP1_KSV_LIST_NOT_READY;
return (hdcp->auth.msg.hdcp1.bcaps & DRM_HDCP_DDC_BCAPS_KSV_FIFO_READY) ?
MOD_HDCP_STATUS_SUCCESS :
MOD_HDCP_STATUS_HDCP1_KSV_LIST_NOT_READY;
}
static inline enum mod_hdcp_status check_hdcp_capable_dp(struct mod_hdcp *hdcp)
{
return (hdcp->auth.msg.hdcp1.bcaps & DP_BCAPS_HDCP_CAPABLE) ?
MOD_HDCP_STATUS_SUCCESS :
MOD_HDCP_STATUS_HDCP1_NOT_CAPABLE;
}
static inline enum mod_hdcp_status check_r0p_available_dp(struct mod_hdcp *hdcp)
{
enum mod_hdcp_status status;
if (is_dp_hdcp(hdcp)) {
status = (hdcp->auth.msg.hdcp1.bstatus &
DP_BSTATUS_R0_PRIME_READY) ?
MOD_HDCP_STATUS_SUCCESS :
MOD_HDCP_STATUS_HDCP1_R0_PRIME_PENDING;
} else {
status = MOD_HDCP_STATUS_INVALID_OPERATION;
}
return status;
}
static inline enum mod_hdcp_status check_link_integrity_dp(
struct mod_hdcp *hdcp)
{
return (hdcp->auth.msg.hdcp1.bstatus &
DP_BSTATUS_LINK_FAILURE) ?
MOD_HDCP_STATUS_HDCP1_LINK_INTEGRITY_FAILURE :
MOD_HDCP_STATUS_SUCCESS;
}
static inline enum mod_hdcp_status check_no_reauthentication_request_dp(
struct mod_hdcp *hdcp)
{
return (hdcp->auth.msg.hdcp1.bstatus & DP_BSTATUS_REAUTH_REQ) ?
MOD_HDCP_STATUS_HDCP1_REAUTH_REQUEST_ISSUED :
MOD_HDCP_STATUS_SUCCESS;
}
static inline enum mod_hdcp_status check_no_max_cascade(struct mod_hdcp *hdcp)
{
enum mod_hdcp_status status;
if (is_dp_hdcp(hdcp))
status = DRM_HDCP_MAX_CASCADE_EXCEEDED(hdcp->auth.msg.hdcp1.binfo_dp >> 8)
? MOD_HDCP_STATUS_HDCP1_MAX_CASCADE_EXCEEDED_FAILURE
: MOD_HDCP_STATUS_SUCCESS;
else
status = DRM_HDCP_MAX_CASCADE_EXCEEDED(hdcp->auth.msg.hdcp1.bstatus >> 8)
? MOD_HDCP_STATUS_HDCP1_MAX_CASCADE_EXCEEDED_FAILURE
: MOD_HDCP_STATUS_SUCCESS;
return status;
}
static inline enum mod_hdcp_status check_no_max_devs(struct mod_hdcp *hdcp)
{
enum mod_hdcp_status status;
if (is_dp_hdcp(hdcp))
status = DRM_HDCP_MAX_DEVICE_EXCEEDED(hdcp->auth.msg.hdcp1.binfo_dp) ?
MOD_HDCP_STATUS_HDCP1_MAX_DEVS_EXCEEDED_FAILURE :
MOD_HDCP_STATUS_SUCCESS;
else
status = DRM_HDCP_MAX_DEVICE_EXCEEDED(hdcp->auth.msg.hdcp1.bstatus) ?
MOD_HDCP_STATUS_HDCP1_MAX_DEVS_EXCEEDED_FAILURE :
MOD_HDCP_STATUS_SUCCESS;
return status;
}
static inline uint8_t get_device_count(struct mod_hdcp *hdcp)
{
return is_dp_hdcp(hdcp) ?
DRM_HDCP_NUM_DOWNSTREAM(hdcp->auth.msg.hdcp1.binfo_dp) :
DRM_HDCP_NUM_DOWNSTREAM(hdcp->auth.msg.hdcp1.bstatus);
}
static inline enum mod_hdcp_status check_device_count(struct mod_hdcp *hdcp)
{
/* Avoid device count == 0 to do authentication */
if (get_device_count(hdcp) == 0)
return MOD_HDCP_STATUS_HDCP1_DEVICE_COUNT_MISMATCH_FAILURE;
hdcp->connection.trace.hdcp1.downstream_device_count = get_device_count(hdcp);
/* Some MST display may choose to report the internal panel as an HDCP RX.
* To update this condition with 1(because the immediate repeater's internal
* panel is possibly not included in DEVICE_COUNT) + get_device_count(hdcp).
* Device count must be greater than or equal to tracked hdcp displays.
*/
return ((1 + get_device_count(hdcp)) < get_active_display_count(hdcp)) ?
MOD_HDCP_STATUS_HDCP1_DEVICE_COUNT_MISMATCH_FAILURE :
MOD_HDCP_STATUS_SUCCESS;
}
static enum mod_hdcp_status wait_for_active_rx(struct mod_hdcp *hdcp,
struct mod_hdcp_event_context *event_ctx,
struct mod_hdcp_transition_input_hdcp1 *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_bksv,
&input->bksv_read, &status,
hdcp, "bksv_read"))
goto out;
if (!mod_hdcp_execute_and_set(mod_hdcp_read_bcaps,
&input->bcaps_read, &status,
hdcp, "bcaps_read"))
goto out;
out:
return status;
}
static enum mod_hdcp_status exchange_ksvs(struct mod_hdcp *hdcp,
struct mod_hdcp_event_context *event_ctx,
struct mod_hdcp_transition_input_hdcp1 *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_hdcp1_create_session,
&input->create_session, &status,
hdcp, "create_session"))
goto out;
if (!mod_hdcp_execute_and_set(mod_hdcp_write_an,
&input->an_write, &status,
hdcp, "an_write"))
goto out;
if (!mod_hdcp_execute_and_set(mod_hdcp_write_aksv,
&input->aksv_write, &status,
hdcp, "aksv_write"))
goto out;
if (!mod_hdcp_execute_and_set(mod_hdcp_read_bksv,
&input->bksv_read, &status,
hdcp, "bksv_read"))
goto out;
if (!mod_hdcp_execute_and_set(validate_bksv,
&input->bksv_validation, &status,
hdcp, "bksv_validation"))
goto out;
if (hdcp->auth.msg.hdcp1.ainfo) {
if (!mod_hdcp_execute_and_set(mod_hdcp_write_ainfo,
&input->ainfo_write, &status,
hdcp, "ainfo_write"))
goto out;
}
out:
return status;
}
static enum mod_hdcp_status computations_validate_rx_test_for_repeater(
struct mod_hdcp *hdcp,
struct mod_hdcp_event_context *event_ctx,
struct mod_hdcp_transition_input_hdcp1 *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_r0p,
&input->r0p_read, &status,
hdcp, "r0p_read"))
goto out;
if (!mod_hdcp_execute_and_set(mod_hdcp_hdcp1_validate_rx,
&input->rx_validation, &status,
hdcp, "rx_validation"))
goto out;
if (hdcp->connection.is_repeater) {
if (!hdcp->connection.link.adjust.hdcp1.postpone_encryption)
if (!mod_hdcp_execute_and_set(
mod_hdcp_hdcp1_enable_encryption,
&input->encryption, &status,
hdcp, "encryption"))
goto out;
} else {
if (!mod_hdcp_execute_and_set(mod_hdcp_hdcp1_enable_encryption,
&input->encryption, &status,
hdcp, "encryption"))
goto out;
if (is_dp_mst_hdcp(hdcp))
if (!mod_hdcp_execute_and_set(
mod_hdcp_hdcp1_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_hdcp1 *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;
}
mod_hdcp_execute_and_set(mod_hdcp_hdcp1_link_maintenance,
&input->link_maintenance, &status,
hdcp, "link_maintenance");
out:
return status;
}
static enum mod_hdcp_status wait_for_ready(struct mod_hdcp *hdcp,
struct mod_hdcp_event_context *event_ctx,
struct mod_hdcp_transition_input_hdcp1 *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 (is_dp_hdcp(hdcp)) {
if (!mod_hdcp_execute_and_set(mod_hdcp_read_bstatus,
&input->bstatus_read, &status,
hdcp, "bstatus_read"))
goto out;
if (!mod_hdcp_execute_and_set(check_link_integrity_dp,
&input->link_integrity_check, &status,
hdcp, "link_integrity_check"))
goto out;
if (!mod_hdcp_execute_and_set(check_no_reauthentication_request_dp,
&input->reauth_request_check, &status,
hdcp, "reauth_request_check"))
goto out;
} else {
if (!mod_hdcp_execute_and_set(mod_hdcp_read_bcaps,
&input->bcaps_read, &status,
hdcp, "bcaps_read"))
goto out;
}
if (!mod_hdcp_execute_and_set(check_ksv_ready,
&input->ready_check, &status,
hdcp, "ready_check"))
goto out;
out:
return status;
}
static enum mod_hdcp_status read_ksv_list(struct mod_hdcp *hdcp,
struct mod_hdcp_event_context *event_ctx,
struct mod_hdcp_transition_input_hdcp1 *input)
{
enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS;
uint8_t device_count;
if (event_ctx->event != MOD_HDCP_EVENT_CALLBACK) {
event_ctx->unexpected_event = 1;
goto out;
}
if (is_dp_hdcp(hdcp)) {
if (!mod_hdcp_execute_and_set(mod_hdcp_read_binfo,
&input->binfo_read_dp, &status,
hdcp, "binfo_read_dp"))
goto out;
} else {
if (!mod_hdcp_execute_and_set(mod_hdcp_read_bstatus,
&input->bstatus_read, &status,
hdcp, "bstatus_read"))
goto out;
}
if (!mod_hdcp_execute_and_set(check_no_max_cascade,
&input->max_cascade_check, &status,
hdcp, "max_cascade_check"))
goto out;
if (!mod_hdcp_execute_and_set(check_no_max_devs,
&input->max_devs_check, &status,
hdcp, "max_devs_check"))
goto out;
if (!mod_hdcp_execute_and_set(check_device_count,
&input->device_count_check, &status,
hdcp, "device_count_check"))
goto out;
device_count = get_device_count(hdcp);
hdcp->auth.msg.hdcp1.ksvlist_size = device_count*5;
if (!mod_hdcp_execute_and_set(mod_hdcp_read_ksvlist,
&input->ksvlist_read, &status,
hdcp, "ksvlist_read"))
goto out;
if (!mod_hdcp_execute_and_set(mod_hdcp_read_vp,
&input->vp_read, &status,
hdcp, "vp_read"))
goto out;
if (!mod_hdcp_execute_and_set(mod_hdcp_hdcp1_validate_ksvlist_vp,
&input->ksvlist_vp_validation, &status,
hdcp, "ksvlist_vp_validation"))
goto out;
if (input->encryption != PASS)
if (!mod_hdcp_execute_and_set(mod_hdcp_hdcp1_enable_encryption,
&input->encryption, &status,
hdcp, "encryption"))
goto out;
if (is_dp_mst_hdcp(hdcp))
if (!mod_hdcp_execute_and_set(
mod_hdcp_hdcp1_enable_dp_stream_encryption,
&input->stream_encryption_dp, &status,
hdcp, "stream_encryption_dp"))
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_hdcp1 *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_bcaps,
&input->bcaps_read, &status,
hdcp, "bcaps_read"))
goto out;
if (!mod_hdcp_execute_and_set(check_hdcp_capable_dp,
&input->hdcp_capable_dp, &status,
hdcp, "hdcp_capable_dp"))
goto out;
out:
return status;
}
static enum mod_hdcp_status wait_for_r0_prime_dp(struct mod_hdcp *hdcp,
struct mod_hdcp_event_context *event_ctx,
struct mod_hdcp_transition_input_hdcp1 *input)
{
enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS;
if (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(mod_hdcp_read_bstatus,
&input->bstatus_read, &status,
hdcp, "bstatus_read"))
goto out;
if (!mod_hdcp_execute_and_set(check_r0p_available_dp,
&input->r0p_available_dp, &status,
hdcp, "r0p_available_dp"))
goto out;
out:
return status;
}
static enum mod_hdcp_status authenticated_dp(struct mod_hdcp *hdcp,
struct mod_hdcp_event_context *event_ctx,
struct mod_hdcp_transition_input_hdcp1 *input)
{
enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS;
if (event_ctx->event != MOD_HDCP_EVENT_CPIRQ) {
event_ctx->unexpected_event = 1;
goto out;
}
mod_hdcp_execute_and_set(mod_hdcp_read_bstatus,
&input->bstatus_read, &status,
hdcp, "bstatus_read");
mod_hdcp_execute_and_set(check_link_integrity_dp,
&input->link_integrity_check, &status,
hdcp, "link_integrity_check");
mod_hdcp_execute_and_set(check_no_reauthentication_request_dp,
&input->reauth_request_check, &status,
hdcp, "reauth_request_check");
out:
return status;
}
uint8_t mod_hdcp_execute_and_set(
mod_hdcp_action func, uint8_t *flag,
enum mod_hdcp_status *status, struct mod_hdcp *hdcp, char *str)
{
*status = func(hdcp);
if (*status == MOD_HDCP_STATUS_SUCCESS && *flag != PASS) {
HDCP_INPUT_PASS_TRACE(hdcp, str);
*flag = PASS;
} else if (*status != MOD_HDCP_STATUS_SUCCESS && *flag != FAIL) {
HDCP_INPUT_FAIL_TRACE(hdcp, str);
*flag = FAIL;
}
return (*status == MOD_HDCP_STATUS_SUCCESS);
}
enum mod_hdcp_status mod_hdcp_hdcp1_execution(struct mod_hdcp *hdcp,
struct mod_hdcp_event_context *event_ctx,
struct mod_hdcp_transition_input_hdcp1 *input)
{
enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS;
switch (current_state(hdcp)) {
case H1_A0_WAIT_FOR_ACTIVE_RX:
status = wait_for_active_rx(hdcp, event_ctx, input);
break;
case H1_A1_EXCHANGE_KSVS:
status = exchange_ksvs(hdcp, event_ctx, input);
break;
case H1_A2_COMPUTATIONS_A3_VALIDATE_RX_A6_TEST_FOR_REPEATER:
status = computations_validate_rx_test_for_repeater(hdcp,
event_ctx, input);
break;
case H1_A45_AUTHENTICATED:
status = authenticated(hdcp, event_ctx, input);
break;
case H1_A8_WAIT_FOR_READY:
status = wait_for_ready(hdcp, event_ctx, input);
break;
case H1_A9_READ_KSV_LIST:
status = read_ksv_list(hdcp, event_ctx, input);
break;
default:
status = MOD_HDCP_STATUS_INVALID_STATE;
break;
}
return status;
}
enum mod_hdcp_status mod_hdcp_hdcp1_dp_execution(struct mod_hdcp *hdcp,
struct mod_hdcp_event_context *event_ctx,
struct mod_hdcp_transition_input_hdcp1 *input)
{
enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS;
switch (current_state(hdcp)) {
case D1_A0_DETERMINE_RX_HDCP_CAPABLE:
status = determine_rx_hdcp_capable_dp(hdcp, event_ctx, input);
break;
case D1_A1_EXCHANGE_KSVS:
status = exchange_ksvs(hdcp, event_ctx, input);
break;
case D1_A23_WAIT_FOR_R0_PRIME:
status = wait_for_r0_prime_dp(hdcp, event_ctx, input);
break;
case D1_A2_COMPUTATIONS_A3_VALIDATE_RX_A5_TEST_FOR_REPEATER:
status = computations_validate_rx_test_for_repeater(
hdcp, event_ctx, input);
break;
case D1_A4_AUTHENTICATED:
status = authenticated_dp(hdcp, event_ctx, input);
break;
case D1_A6_WAIT_FOR_READY:
status = wait_for_ready(hdcp, event_ctx, input);
break;
case D1_A7_READ_KSV_LIST:
status = read_ksv_list(hdcp, event_ctx, input);
break;
default:
status = MOD_HDCP_STATUS_INVALID_STATE;
break;
}
return status;
}
@@ -0,0 +1,322 @@
/*
* Copyright 2019 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 "hdcp.h"
enum mod_hdcp_status 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)
{
enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS;
struct mod_hdcp_connection *conn = &hdcp->connection;
struct mod_hdcp_link_adjustment *adjust = &hdcp->connection.link.adjust;
switch (current_state(hdcp)) {
case H1_A0_WAIT_FOR_ACTIVE_RX:
if (input->bksv_read != PASS || input->bcaps_read != PASS) {
/* 1A-04: repeatedly attempts on port access failure */
callback_in_ms(500, output);
increment_stay_counter(hdcp);
break;
}
callback_in_ms(0, output);
set_state_id(hdcp, output, H1_A1_EXCHANGE_KSVS);
break;
case H1_A1_EXCHANGE_KSVS:
if (input->create_session != PASS) {
/* out of sync with psp state */
adjust->hdcp1.disable = 1;
fail_and_restart_in_ms(0, &status, output);
break;
} else if (input->an_write != PASS ||
input->aksv_write != PASS ||
input->bksv_read != PASS ||
input->bksv_validation != PASS ||
input->ainfo_write == FAIL) {
/* 1A-05: consider invalid bksv a failure */
fail_and_restart_in_ms(0, &status, output);
break;
}
callback_in_ms(300, output);
set_state_id(hdcp, output,
H1_A2_COMPUTATIONS_A3_VALIDATE_RX_A6_TEST_FOR_REPEATER);
break;
case H1_A2_COMPUTATIONS_A3_VALIDATE_RX_A6_TEST_FOR_REPEATER:
if (input->bcaps_read != PASS ||
input->r0p_read != PASS) {
fail_and_restart_in_ms(0, &status, output);
break;
} else if (input->rx_validation != PASS) {
/* 1A-06: consider invalid r0' a failure */
/* 1A-08: consider bksv listed in SRM a failure */
/*
* some slow RX will fail rx validation when it is
* not ready. give it more time to react before retry.
*/
fail_and_restart_in_ms(1000, &status, output);
break;
} else if (!conn->is_repeater && input->encryption != PASS) {
fail_and_restart_in_ms(0, &status, output);
break;
}
if (conn->is_repeater) {
callback_in_ms(0, output);
set_watchdog_in_ms(hdcp, 5000, output);
set_state_id(hdcp, output, H1_A8_WAIT_FOR_READY);
} else {
callback_in_ms(0, output);
set_state_id(hdcp, output, H1_A45_AUTHENTICATED);
set_auth_complete(hdcp, output);
}
break;
case H1_A45_AUTHENTICATED:
if (input->link_maintenance == FAIL) {
/* 1A-07: consider invalid ri' a failure */
/* 1A-07a: consider read ri' not returned a failure */
fail_and_restart_in_ms(0, &status, output);
break;
}
callback_in_ms(500, output);
increment_stay_counter(hdcp);
break;
case H1_A8_WAIT_FOR_READY:
if (input->ready_check != PASS) {
if (event_ctx->event ==
MOD_HDCP_EVENT_WATCHDOG_TIMEOUT) {
/* 1B-03: fail hdcp on ksv list READY timeout */
/* prevent black screen in next attempt */
adjust->hdcp1.postpone_encryption = 1;
fail_and_restart_in_ms(0, &status, output);
} else {
/* continue ksv list READY polling*/
callback_in_ms(500, output);
increment_stay_counter(hdcp);
}
break;
}
callback_in_ms(0, output);
set_state_id(hdcp, output, H1_A9_READ_KSV_LIST);
break;
case H1_A9_READ_KSV_LIST:
if (input->bstatus_read != PASS ||
input->max_cascade_check != PASS ||
input->max_devs_check != PASS ||
input->device_count_check != PASS ||
input->ksvlist_read != PASS ||
input->vp_read != PASS ||
input->ksvlist_vp_validation != PASS ||
input->encryption != PASS) {
/* 1B-06: consider MAX_CASCADE_EXCEEDED a failure */
/* 1B-05: consider MAX_DEVS_EXCEEDED a failure */
/* 1B-04: consider invalid v' a failure */
fail_and_restart_in_ms(0, &status, output);
break;
}
callback_in_ms(0, output);
set_state_id(hdcp, output, H1_A45_AUTHENTICATED);
set_auth_complete(hdcp, output);
break;
default:
status = MOD_HDCP_STATUS_INVALID_STATE;
fail_and_restart_in_ms(0, &status, output);
break;
}
return status;
}
enum mod_hdcp_status 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)
{
enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS;
struct mod_hdcp_connection *conn = &hdcp->connection;
struct mod_hdcp_link_adjustment *adjust = &hdcp->connection.link.adjust;
switch (current_state(hdcp)) {
case D1_A0_DETERMINE_RX_HDCP_CAPABLE:
if (input->bcaps_read != PASS) {
/* 1A-04: no authentication on bcaps read failure */
fail_and_restart_in_ms(0, &status, output);
break;
} else if (input->hdcp_capable_dp != PASS) {
adjust->hdcp1.disable = 1;
fail_and_restart_in_ms(0, &status, output);
break;
}
callback_in_ms(0, output);
set_state_id(hdcp, output, D1_A1_EXCHANGE_KSVS);
break;
case D1_A1_EXCHANGE_KSVS:
if (input->create_session != PASS) {
/* out of sync with psp state */
adjust->hdcp1.disable = 1;
fail_and_restart_in_ms(0, &status, output);
break;
} else if (input->an_write != PASS ||
input->aksv_write != PASS ||
input->bksv_read != PASS ||
input->bksv_validation != PASS ||
input->ainfo_write == FAIL) {
/* 1A-05: consider invalid bksv a failure */
fail_and_restart_in_ms(0, &status, output);
break;
}
set_watchdog_in_ms(hdcp, 100, output);
set_state_id(hdcp, output, D1_A23_WAIT_FOR_R0_PRIME);
break;
case D1_A23_WAIT_FOR_R0_PRIME:
if (input->bstatus_read != PASS) {
fail_and_restart_in_ms(0, &status, output);
break;
} else if (input->r0p_available_dp != PASS) {
if (event_ctx->event == MOD_HDCP_EVENT_WATCHDOG_TIMEOUT)
fail_and_restart_in_ms(0, &status, output);
else
increment_stay_counter(hdcp);
break;
}
callback_in_ms(0, output);
set_state_id(hdcp, output, D1_A2_COMPUTATIONS_A3_VALIDATE_RX_A5_TEST_FOR_REPEATER);
break;
case D1_A2_COMPUTATIONS_A3_VALIDATE_RX_A5_TEST_FOR_REPEATER:
if (input->r0p_read != PASS) {
fail_and_restart_in_ms(0, &status, output);
break;
} else if (input->rx_validation != PASS) {
if (hdcp->state.stay_count < 2 &&
!hdcp->connection.is_hdcp1_revoked) {
/* allow 2 additional retries */
callback_in_ms(0, output);
increment_stay_counter(hdcp);
} else {
/*
* 1A-06: consider invalid r0' a failure
* after 3 attempts.
* 1A-08: consider bksv listed in SRM a failure
*/
/*
* some slow RX will fail rx validation when it is
* not ready. give it more time to react before retry.
*/
fail_and_restart_in_ms(1000, &status, output);
}
break;
} else if ((!conn->is_repeater && input->encryption != PASS) ||
(!conn->is_repeater && is_dp_mst_hdcp(hdcp) && input->stream_encryption_dp != PASS)) {
fail_and_restart_in_ms(0, &status, output);
break;
} else if (conn->hdcp1_retry_count < conn->link.adjust.hdcp1.min_auth_retries_wa) {
fail_and_restart_in_ms(200, &status, output);
break;
}
if (conn->is_repeater) {
set_watchdog_in_ms(hdcp, 5000, output);
set_state_id(hdcp, output, D1_A6_WAIT_FOR_READY);
} else {
set_state_id(hdcp, output, D1_A4_AUTHENTICATED);
set_auth_complete(hdcp, output);
}
break;
case D1_A4_AUTHENTICATED:
if (input->link_integrity_check == FAIL ||
input->reauth_request_check == FAIL) {
/* 1A-07: restart hdcp on a link integrity failure */
fail_and_restart_in_ms(0, &status, output);
break;
}
break;
case D1_A6_WAIT_FOR_READY:
if (input->link_integrity_check == FAIL ||
input->reauth_request_check == FAIL) {
fail_and_restart_in_ms(0, &status, output);
break;
} else if (input->ready_check != PASS) {
if (event_ctx->event ==
MOD_HDCP_EVENT_WATCHDOG_TIMEOUT) {
/* 1B-04: fail hdcp on ksv list READY timeout */
/* prevent black screen in next attempt */
adjust->hdcp1.postpone_encryption = 1;
fail_and_restart_in_ms(0, &status, output);
} else {
increment_stay_counter(hdcp);
}
break;
}
callback_in_ms(0, output);
set_state_id(hdcp, output, D1_A7_READ_KSV_LIST);
break;
case D1_A7_READ_KSV_LIST:
if (input->binfo_read_dp != PASS ||
input->max_cascade_check != PASS ||
input->max_devs_check != PASS) {
/* 1B-06: consider MAX_DEVS_EXCEEDED a failure */
/* 1B-07: consider MAX_CASCADE_EXCEEDED a failure */
fail_and_restart_in_ms(0, &status, output);
break;
} else if (input->device_count_check != PASS) {
/*
* some slow dongle doesn't update
* device count as soon as downstream is connected.
* give it more time to react.
*/
adjust->hdcp1.postpone_encryption = 1;
fail_and_restart_in_ms(1000, &status, output);
break;
} else if (input->ksvlist_read != PASS ||
input->vp_read != PASS) {
fail_and_restart_in_ms(0, &status, output);
break;
} else if (input->ksvlist_vp_validation != PASS) {
if (hdcp->state.stay_count < 2 &&
!hdcp->connection.is_hdcp1_revoked) {
/* allow 2 additional retries */
callback_in_ms(0, output);
increment_stay_counter(hdcp);
} else {
/*
* 1B-05: consider invalid v' a failure
* after 3 attempts.
*/
fail_and_restart_in_ms(0, &status, output);
}
break;
} else if (input->encryption != PASS ||
(is_dp_mst_hdcp(hdcp) && input->stream_encryption_dp != PASS)) {
fail_and_restart_in_ms(0, &status, output);
break;
}
set_state_id(hdcp, output, D1_A4_AUTHENTICATED);
set_auth_complete(hdcp, output);
break;
default:
fail_and_restart_in_ms(0, &status, output);
break;
}
return status;
}
@@ -0,0 +1,902 @@
/*
* 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 uint16_t get_hdmi_rxstatus_msg_size(const uint8_t rxstatus[2])
{
return HDCP_2_2_HDMI_RXSTATUS_MSG_SZ_HI(rxstatus[1]) << 8 | rxstatus[0];
}
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]) &&
get_hdmi_rxstatus_msg_size(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;
struct mod_hdcp_trace *trace = &hdcp->connection.trace;
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;
if (status == MOD_HDCP_STATUS_SUCCESS)
trace->hdcp2.attempt_count++;
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;
if (is_dp_hdcp(hdcp)) {
status = MOD_HDCP_STATUS_SUCCESS;
} else {
status = mod_hdcp_read_rxstatus(hdcp);
if (status == MOD_HDCP_STATUS_SUCCESS) {
const uint16_t size = get_hdmi_rxstatus_msg_size(hdcp->auth.msg.hdcp2.rxstatus);
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;
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 {
const uint16_t size = get_hdmi_rxstatus_msg_size(hdcp->auth.msg.hdcp2.rxstatus);
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;
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 {
const uint16_t size = get_hdmi_rxstatus_msg_size(hdcp->auth.msg.hdcp2.rxstatus);
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 = MOD_HDCP_STATUS_FAILURE;
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;
const uint16_t size = get_hdmi_rxstatus_msg_size(hdcp->auth.msg.hdcp2.rxstatus);
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;
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;
const uint16_t size = get_hdmi_rxstatus_msg_size(hdcp->auth.msg.hdcp2.rxstatus);
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)
{
struct mod_hdcp_trace *trace = &hdcp->connection.trace;
/* Avoid device count == 0 to do authentication */
if (get_device_count(hdcp) == 0)
return MOD_HDCP_STATUS_HDCP1_DEVICE_COUNT_MISMATCH_FAILURE;
trace->hdcp2.downstream_device_count = get_device_count(hdcp);
trace->hdcp2.hdcp1_device_downstream =
HDCP_2_2_HDCP1_DEVICE_CONNECTED(hdcp->auth.msg.hdcp2.rx_id_list[2]);
trace->hdcp2.hdcp2_legacy_device_downstream =
HDCP_2_2_HDCP_2_0_REP_CONNECTED(hdcp->auth.msg.hdcp2.rx_id_list[2]);
/* Some MST display may choose to report the internal panel as an HDCP RX. */
/* To update this condition with 1(because the immediate repeater's internal */
/* panel is possibly not included in DEVICE_COUNT) + get_device_count(hdcp). */
/* Device count must be greater than or equal to tracked hdcp displays. */
return ((1 + 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 =
get_hdmi_rxstatus_msg_size(hdcp->auth.msg.hdcp2.rxstatus);
}
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 (hdcp->connection.link.adjust.hdcp2.use_fw_locality_check) {
if (!mod_hdcp_execute_and_set(mod_hdcp_write_poll_read_lc_fw,
&input->l_prime_combo_read, &status,
hdcp, "l_prime_combo_read"))
goto out;
} else {
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;
}
process_rxstatus(hdcp, event_ctx, input, &status);
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;
}
@@ -0,0 +1,704 @@
/*
* 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 "hdcp.h"
enum mod_hdcp_status mod_hdcp_hdcp2_transition(struct mod_hdcp *hdcp,
struct mod_hdcp_event_context *event_ctx,
struct mod_hdcp_transition_input_hdcp2 *input,
struct mod_hdcp_output *output)
{
enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS;
struct mod_hdcp_connection *conn = &hdcp->connection;
struct mod_hdcp_link_adjustment *adjust = &hdcp->connection.link.adjust;
switch (current_state(hdcp)) {
case H2_A0_KNOWN_HDCP2_CAPABLE_RX:
if (input->hdcp2version_read != PASS ||
input->hdcp2_capable_check != PASS) {
adjust->hdcp2.disable = 1;
callback_in_ms(0, output);
set_state_id(hdcp, output, HDCP_INITIALIZED);
} else {
callback_in_ms(0, output);
set_state_id(hdcp, output, H2_A1_SEND_AKE_INIT);
}
break;
case H2_A1_SEND_AKE_INIT:
if (input->create_session != PASS ||
input->ake_init_prepare != PASS) {
/* out of sync with psp state */
adjust->hdcp2.disable = 1;
fail_and_restart_in_ms(0, &status, output);
break;
} else if (input->ake_init_write != PASS) {
fail_and_restart_in_ms(0, &status, output);
break;
}
set_watchdog_in_ms(hdcp, 100, output);
callback_in_ms(0, output);
set_state_id(hdcp, output, H2_A1_VALIDATE_AKE_CERT);
break;
case H2_A1_VALIDATE_AKE_CERT:
if (input->ake_cert_available != PASS) {
if (event_ctx->event ==
MOD_HDCP_EVENT_WATCHDOG_TIMEOUT) {
/* 1A-08: consider ake timeout a failure */
/* some hdmi receivers are not ready for HDCP
* immediately after video becomes active,
* delay 1s before retry on first HDCP message
* timeout.
*/
fail_and_restart_in_ms(1000, &status, output);
} else {
/* continue ake cert polling*/
callback_in_ms(10, output);
increment_stay_counter(hdcp);
}
break;
} else if (input->ake_cert_read != PASS ||
input->ake_cert_validation != PASS) {
/*
* 1A-09: consider invalid ake cert a failure
* 1A-10: consider receiver id listed in SRM a failure
*/
fail_and_restart_in_ms(0, &status, output);
break;
}
if (conn->is_km_stored &&
!adjust->hdcp2.force_no_stored_km) {
callback_in_ms(0, output);
set_state_id(hdcp, output, H2_A1_SEND_STORED_KM);
} else {
callback_in_ms(0, output);
set_state_id(hdcp, output, H2_A1_SEND_NO_STORED_KM);
}
break;
case H2_A1_SEND_NO_STORED_KM:
if (input->no_stored_km_write != PASS) {
fail_and_restart_in_ms(0, &status, output);
break;
}
if (adjust->hdcp2.increase_h_prime_timeout)
set_watchdog_in_ms(hdcp, 2000, output);
else
set_watchdog_in_ms(hdcp, 1000, output);
callback_in_ms(0, output);
set_state_id(hdcp, output, H2_A1_READ_H_PRIME);
break;
case H2_A1_READ_H_PRIME:
if (input->h_prime_available != PASS) {
if (event_ctx->event ==
MOD_HDCP_EVENT_WATCHDOG_TIMEOUT) {
/* 1A-11-3: consider h' timeout a failure */
fail_and_restart_in_ms(1000, &status, output);
} else {
/* continue h' polling */
callback_in_ms(100, output);
increment_stay_counter(hdcp);
}
break;
} else if (input->h_prime_read != PASS) {
fail_and_restart_in_ms(0, &status, output);
break;
}
set_watchdog_in_ms(hdcp, 200, output);
callback_in_ms(0, output);
set_state_id(hdcp, output, H2_A1_READ_PAIRING_INFO_AND_VALIDATE_H_PRIME);
break;
case H2_A1_READ_PAIRING_INFO_AND_VALIDATE_H_PRIME:
if (input->pairing_available != PASS) {
if (event_ctx->event ==
MOD_HDCP_EVENT_WATCHDOG_TIMEOUT) {
/* 1A-12: consider pairing info timeout
* a failure
*/
fail_and_restart_in_ms(0, &status, output);
} else {
/* continue pairing info polling */
callback_in_ms(20, output);
increment_stay_counter(hdcp);
}
break;
} else if (input->pairing_info_read != PASS ||
input->h_prime_validation != PASS) {
/* 1A-11-1: consider invalid h' a failure */
fail_and_restart_in_ms(0, &status, output);
break;
}
callback_in_ms(0, output);
set_state_id(hdcp, output, H2_A2_LOCALITY_CHECK);
break;
case H2_A1_SEND_STORED_KM:
if (input->stored_km_write != PASS) {
fail_and_restart_in_ms(0, &status, output);
break;
}
set_watchdog_in_ms(hdcp, 200, output);
callback_in_ms(0, output);
set_state_id(hdcp, output, H2_A1_VALIDATE_H_PRIME);
break;
case H2_A1_VALIDATE_H_PRIME:
if (input->h_prime_available != PASS) {
if (event_ctx->event ==
MOD_HDCP_EVENT_WATCHDOG_TIMEOUT) {
/* 1A-11-2: consider h' timeout a failure */
fail_and_restart_in_ms(1000, &status, output);
} else {
/* continue h' polling */
callback_in_ms(20, output);
increment_stay_counter(hdcp);
}
break;
} else if (input->h_prime_read != PASS) {
fail_and_restart_in_ms(0, &status, output);
break;
} else if (input->h_prime_validation != PASS) {
/* 1A-11-1: consider invalid h' a failure */
adjust->hdcp2.force_no_stored_km = 1;
fail_and_restart_in_ms(0, &status, output);
break;
}
callback_in_ms(0, output);
set_state_id(hdcp, output, H2_A2_LOCALITY_CHECK);
break;
case H2_A2_LOCALITY_CHECK:
/* 1A-05: consider disconnection after LC init a failure */
if (hdcp->state.stay_count > 10 ||
input->lc_init_prepare != PASS) {
fail_and_restart_in_ms(0, &status, output);
break;
} else if (adjust->hdcp2.use_fw_locality_check &&
input->l_prime_combo_read != PASS) {
/* 1A-13-2: consider l' timeout a failure */
if (adjust->hdcp2.use_sw_locality_fallback) {
/* switch to software locality check */
adjust->hdcp2.use_fw_locality_check = 0;
callback_in_ms(0, output);
increment_stay_counter(hdcp);
break;
}
fail_and_restart_in_ms(0, &status, output);
break;
} else if (!adjust->hdcp2.use_fw_locality_check &&
(input->lc_init_write != PASS ||
input->l_prime_available_poll != PASS ||
input->l_prime_read != PASS)) {
/* 1A-13-2: consider l' timeout a failure */
fail_and_restart_in_ms(0, &status, output);
break;
} else if (input->l_prime_validation != PASS) {
/* 1A-13-1: consider invalid l' a failure */
callback_in_ms(0, output);
increment_stay_counter(hdcp);
break;
}
callback_in_ms(0, output);
set_state_id(hdcp, output, H2_A3_EXCHANGE_KS_AND_TEST_FOR_REPEATER);
break;
case H2_A3_EXCHANGE_KS_AND_TEST_FOR_REPEATER:
if (input->eks_prepare != PASS ||
input->eks_write != PASS) {
fail_and_restart_in_ms(0, &status, output);
break;
}
if (conn->is_repeater) {
set_watchdog_in_ms(hdcp, 3000, output);
callback_in_ms(0, output);
set_state_id(hdcp, output, H2_A6_WAIT_FOR_RX_ID_LIST);
} else {
/* some CTS equipment requires a delay GREATER than
* 200 ms, so delay 210 ms instead of 200 ms
*/
callback_in_ms(210, output);
set_state_id(hdcp, output, H2_ENABLE_ENCRYPTION);
}
break;
case H2_ENABLE_ENCRYPTION:
if (input->rxstatus_read != PASS ||
input->reauth_request_check != PASS) {
/*
* 1A-07: restart hdcp on REAUTH_REQ
* 1B-08: restart hdcp on REAUTH_REQ
*/
fail_and_restart_in_ms(0, &status, output);
break;
} else if (event_ctx->rx_id_list_ready && conn->is_repeater) {
callback_in_ms(0, output);
set_state_id(hdcp, output, H2_A78_VERIFY_RX_ID_LIST_AND_SEND_ACK);
break;
} else if (input->enable_encryption != PASS) {
fail_and_restart_in_ms(0, &status, output);
break;
}
callback_in_ms(0, output);
set_state_id(hdcp, output, H2_A5_AUTHENTICATED);
set_auth_complete(hdcp, output);
break;
case H2_A5_AUTHENTICATED:
if (input->rxstatus_read == FAIL ||
input->reauth_request_check == FAIL) {
fail_and_restart_in_ms(0, &status, output);
break;
} else if (event_ctx->rx_id_list_ready && conn->is_repeater) {
callback_in_ms(0, output);
set_state_id(hdcp, output, H2_A78_VERIFY_RX_ID_LIST_AND_SEND_ACK);
break;
}
callback_in_ms(500, output);
increment_stay_counter(hdcp);
break;
case H2_A6_WAIT_FOR_RX_ID_LIST:
if (input->rxstatus_read != PASS ||
input->reauth_request_check != PASS) {
fail_and_restart_in_ms(0, &status, output);
break;
} else if (!event_ctx->rx_id_list_ready) {
if (event_ctx->event == MOD_HDCP_EVENT_WATCHDOG_TIMEOUT) {
/* 1B-02: consider rx id list timeout a failure */
/* some CTS equipment's actual timeout
* measurement is slightly greater than 3000 ms.
* Delay 100 ms to ensure it is fully timeout
* before re-authentication.
*/
fail_and_restart_in_ms(100, &status, output);
} else {
callback_in_ms(300, output);
increment_stay_counter(hdcp);
}
break;
}
callback_in_ms(0, output);
set_state_id(hdcp, output, H2_A78_VERIFY_RX_ID_LIST_AND_SEND_ACK);
break;
case H2_A78_VERIFY_RX_ID_LIST_AND_SEND_ACK:
if (input->rxstatus_read != PASS ||
input->reauth_request_check != PASS ||
input->rx_id_list_read != PASS ||
input->device_count_check != PASS ||
input->rx_id_list_validation != PASS ||
input->repeater_auth_ack_write != PASS) {
/* 1B-03: consider invalid v' a failure
* 1B-04: consider MAX_DEVS_EXCEEDED a failure
* 1B-05: consider MAX_CASCADE_EXCEEDED a failure
* 1B-06: consider invalid seq_num_V a failure
* 1B-09: consider seq_num_V rollover a failure
*/
fail_and_restart_in_ms(0, &status, output);
break;
}
callback_in_ms(0, output);
set_state_id(hdcp, output, H2_A9_SEND_STREAM_MANAGEMENT);
break;
case H2_A9_SEND_STREAM_MANAGEMENT:
if (input->rxstatus_read != PASS ||
input->reauth_request_check != PASS) {
fail_and_restart_in_ms(0, &status, output);
break;
} else if (event_ctx->rx_id_list_ready && conn->is_repeater) {
callback_in_ms(0, output);
set_state_id(hdcp, output, H2_A78_VERIFY_RX_ID_LIST_AND_SEND_ACK);
break;
} else if (input->prepare_stream_manage != PASS ||
input->stream_manage_write != PASS) {
fail_and_restart_in_ms(0, &status, output);
break;
}
set_watchdog_in_ms(hdcp, 100, output);
callback_in_ms(0, output);
set_state_id(hdcp, output, H2_A9_VALIDATE_STREAM_READY);
break;
case H2_A9_VALIDATE_STREAM_READY:
if (input->rxstatus_read != PASS ||
input->reauth_request_check != PASS) {
fail_and_restart_in_ms(0, &status, output);
break;
} else if (event_ctx->rx_id_list_ready && conn->is_repeater) {
callback_in_ms(0, output);
set_state_id(hdcp, output, H2_A78_VERIFY_RX_ID_LIST_AND_SEND_ACK);
break;
} else if (input->stream_ready_available != PASS) {
if (event_ctx->event == MOD_HDCP_EVENT_WATCHDOG_TIMEOUT) {
/* 1B-10-2: restart content stream management on
* stream ready timeout
*/
hdcp->auth.count.stream_management_retry_count++;
callback_in_ms(0, output);
set_state_id(hdcp, output, H2_A9_SEND_STREAM_MANAGEMENT);
} else {
callback_in_ms(10, output);
increment_stay_counter(hdcp);
}
break;
} else if (input->stream_ready_read != PASS ||
input->stream_ready_validation != PASS) {
/*
* 1B-10-1: restart content stream management
* on invalid M'
*/
if (hdcp->auth.count.stream_management_retry_count > 10) {
fail_and_restart_in_ms(0, &status, output);
} else {
hdcp->auth.count.stream_management_retry_count++;
callback_in_ms(0, output);
set_state_id(hdcp, output, H2_A9_SEND_STREAM_MANAGEMENT);
}
break;
}
callback_in_ms(200, output);
set_state_id(hdcp, output, H2_ENABLE_ENCRYPTION);
break;
default:
status = MOD_HDCP_STATUS_INVALID_STATE;
fail_and_restart_in_ms(0, &status, output);
break;
}
return status;
}
enum mod_hdcp_status mod_hdcp_hdcp2_dp_transition(struct mod_hdcp *hdcp,
struct mod_hdcp_event_context *event_ctx,
struct mod_hdcp_transition_input_hdcp2 *input,
struct mod_hdcp_output *output)
{
enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS;
struct mod_hdcp_connection *conn = &hdcp->connection;
struct mod_hdcp_link_adjustment *adjust = &hdcp->connection.link.adjust;
switch (current_state(hdcp)) {
case D2_A0_DETERMINE_RX_HDCP_CAPABLE:
if (input->rx_caps_read_dp != PASS ||
input->hdcp2_capable_check != PASS) {
adjust->hdcp2.disable = 1;
callback_in_ms(0, output);
set_state_id(hdcp, output, HDCP_INITIALIZED);
} else {
callback_in_ms(0, output);
set_state_id(hdcp, output, D2_A1_SEND_AKE_INIT);
}
break;
case D2_A1_SEND_AKE_INIT:
if (input->create_session != PASS ||
input->ake_init_prepare != PASS) {
/* out of sync with psp state */
adjust->hdcp2.disable = 1;
fail_and_restart_in_ms(0, &status, output);
break;
} else if (input->ake_init_write != PASS) {
/* possibly display not ready */
fail_and_restart_in_ms(0, &status, output);
break;
}
callback_in_ms(100, output);
set_state_id(hdcp, output, D2_A1_VALIDATE_AKE_CERT);
break;
case D2_A1_VALIDATE_AKE_CERT:
if (input->ake_cert_read != PASS ||
input->ake_cert_validation != PASS) {
/*
* 1A-08: consider invalid ake cert a failure
* 1A-09: consider receiver id listed in SRM a failure
*/
fail_and_restart_in_ms(0, &status, output);
break;
}
if (conn->is_km_stored &&
!adjust->hdcp2.force_no_stored_km) {
callback_in_ms(0, output);
set_state_id(hdcp, output, D2_A1_SEND_STORED_KM);
} else {
callback_in_ms(0, output);
set_state_id(hdcp, output, D2_A1_SEND_NO_STORED_KM);
}
break;
case D2_A1_SEND_NO_STORED_KM:
if (input->no_stored_km_write != PASS) {
fail_and_restart_in_ms(0, &status, output);
break;
}
if (adjust->hdcp2.increase_h_prime_timeout)
set_watchdog_in_ms(hdcp, 2000, output);
else
set_watchdog_in_ms(hdcp, 1000, output);
set_state_id(hdcp, output, D2_A1_READ_H_PRIME);
break;
case D2_A1_READ_H_PRIME:
if (input->h_prime_available != PASS) {
if (event_ctx->event ==
MOD_HDCP_EVENT_WATCHDOG_TIMEOUT)
/* 1A-10-3: consider h' timeout a failure */
fail_and_restart_in_ms(1000, &status, output);
else
increment_stay_counter(hdcp);
break;
} else if (input->h_prime_read != PASS) {
fail_and_restart_in_ms(0, &status, output);
break;
}
set_watchdog_in_ms(hdcp, 200, output);
set_state_id(hdcp, output, D2_A1_READ_PAIRING_INFO_AND_VALIDATE_H_PRIME);
break;
case D2_A1_READ_PAIRING_INFO_AND_VALIDATE_H_PRIME:
if (input->pairing_available != PASS) {
if (event_ctx->event ==
MOD_HDCP_EVENT_WATCHDOG_TIMEOUT)
/*
* 1A-11: consider pairing info timeout
* a failure
*/
fail_and_restart_in_ms(0, &status, output);
else
increment_stay_counter(hdcp);
break;
} else if (input->pairing_info_read != PASS ||
input->h_prime_validation != PASS) {
/* 1A-10-1: consider invalid h' a failure */
fail_and_restart_in_ms(0, &status, output);
break;
}
callback_in_ms(0, output);
set_state_id(hdcp, output, D2_A2_LOCALITY_CHECK);
break;
case D2_A1_SEND_STORED_KM:
if (input->stored_km_write != PASS) {
fail_and_restart_in_ms(0, &status, output);
break;
}
set_watchdog_in_ms(hdcp, 200, output);
set_state_id(hdcp, output, D2_A1_VALIDATE_H_PRIME);
break;
case D2_A1_VALIDATE_H_PRIME:
if (input->h_prime_available != PASS) {
if (event_ctx->event ==
MOD_HDCP_EVENT_WATCHDOG_TIMEOUT)
/* 1A-10-2: consider h' timeout a failure */
fail_and_restart_in_ms(1000, &status, output);
else
increment_stay_counter(hdcp);
break;
} else if (input->h_prime_read != PASS) {
fail_and_restart_in_ms(0, &status, output);
break;
} else if (input->h_prime_validation != PASS) {
/* 1A-10-1: consider invalid h' a failure */
adjust->hdcp2.force_no_stored_km = 1;
fail_and_restart_in_ms(0, &status, output);
break;
}
callback_in_ms(0, output);
set_state_id(hdcp, output, D2_A2_LOCALITY_CHECK);
break;
case D2_A2_LOCALITY_CHECK:
if (hdcp->state.stay_count > 10 ||
input->lc_init_prepare != PASS) {
fail_and_restart_in_ms(0, &status, output);
break;
} else if (adjust->hdcp2.use_fw_locality_check &&
input->l_prime_combo_read != PASS) {
if (adjust->hdcp2.use_sw_locality_fallback) {
/* switch to software locality check */
adjust->hdcp2.use_fw_locality_check = 0;
callback_in_ms(0, output);
increment_stay_counter(hdcp);
break;
}
fail_and_restart_in_ms(0, &status, output);
break;
} else if (!adjust->hdcp2.use_fw_locality_check &&
(input->lc_init_write != PASS ||
input->l_prime_read != PASS)) {
fail_and_restart_in_ms(0, &status, output);
break;
} else if (input->l_prime_validation != PASS) {
/* 1A-12: consider invalid l' a failure */
callback_in_ms(0, output);
increment_stay_counter(hdcp);
break;
}
callback_in_ms(0, output);
set_state_id(hdcp, output, D2_A34_EXCHANGE_KS_AND_TEST_FOR_REPEATER);
break;
case D2_A34_EXCHANGE_KS_AND_TEST_FOR_REPEATER:
if (input->eks_prepare != PASS ||
input->eks_write != PASS) {
fail_and_restart_in_ms(0, &status, output);
break;
}
if (conn->is_repeater) {
set_watchdog_in_ms(hdcp, 3000, output);
set_state_id(hdcp, output, D2_A6_WAIT_FOR_RX_ID_LIST);
} else {
callback_in_ms(1, output);
set_state_id(hdcp, output, D2_SEND_CONTENT_STREAM_TYPE);
}
break;
case D2_SEND_CONTENT_STREAM_TYPE:
if (input->rxstatus_read != PASS ||
input->reauth_request_check != PASS ||
input->link_integrity_check_dp != PASS ||
input->content_stream_type_write != PASS) {
fail_and_restart_in_ms(0, &status, output);
break;
}
callback_in_ms(210, output);
set_state_id(hdcp, output, D2_ENABLE_ENCRYPTION);
break;
case D2_ENABLE_ENCRYPTION:
if (input->rxstatus_read != PASS ||
input->reauth_request_check != PASS ||
input->link_integrity_check_dp != PASS) {
/*
* 1A-07: restart hdcp on REAUTH_REQ
* 1B-08: restart hdcp on REAUTH_REQ
*/
fail_and_restart_in_ms(0, &status, output);
break;
} else if (event_ctx->rx_id_list_ready && conn->is_repeater) {
callback_in_ms(0, output);
set_state_id(hdcp, output, D2_A78_VERIFY_RX_ID_LIST_AND_SEND_ACK);
break;
} else if (input->enable_encryption != PASS ||
(is_dp_mst_hdcp(hdcp) && input->stream_encryption_dp != PASS)) {
fail_and_restart_in_ms(0, &status, output);
break;
}
set_state_id(hdcp, output, D2_A5_AUTHENTICATED);
set_auth_complete(hdcp, output);
break;
case D2_A5_AUTHENTICATED:
if (input->rxstatus_read == FAIL ||
input->reauth_request_check == FAIL) {
fail_and_restart_in_ms(100, &status, output);
break;
} else if (input->link_integrity_check_dp == FAIL) {
if (hdcp->connection.hdcp2_retry_count >= 1)
adjust->hdcp2.force_type = MOD_HDCP_FORCE_TYPE_0;
fail_and_restart_in_ms(0, &status, output);
break;
} else if (event_ctx->rx_id_list_ready && conn->is_repeater) {
callback_in_ms(0, output);
set_state_id(hdcp, output, D2_A78_VERIFY_RX_ID_LIST_AND_SEND_ACK);
break;
}
increment_stay_counter(hdcp);
break;
case D2_A6_WAIT_FOR_RX_ID_LIST:
if (input->rxstatus_read != PASS ||
input->reauth_request_check != PASS ||
input->link_integrity_check_dp != PASS) {
fail_and_restart_in_ms(0, &status, output);
break;
} else if (!event_ctx->rx_id_list_ready) {
if (event_ctx->event == MOD_HDCP_EVENT_WATCHDOG_TIMEOUT)
/* 1B-02: consider rx id list timeout a failure */
fail_and_restart_in_ms(0, &status, output);
else
increment_stay_counter(hdcp);
break;
}
callback_in_ms(0, output);
set_state_id(hdcp, output, D2_A78_VERIFY_RX_ID_LIST_AND_SEND_ACK);
break;
case D2_A78_VERIFY_RX_ID_LIST_AND_SEND_ACK:
if (input->rxstatus_read != PASS ||
input->reauth_request_check != PASS ||
input->link_integrity_check_dp != PASS ||
input->rx_id_list_read != PASS ||
input->device_count_check != PASS ||
input->rx_id_list_validation != PASS ||
input->repeater_auth_ack_write != PASS) {
/*
* 1B-03: consider invalid v' a failure
* 1B-04: consider MAX_DEVS_EXCEEDED a failure
* 1B-05: consider MAX_CASCADE_EXCEEDED a failure
* 1B-06: consider invalid seq_num_V a failure
* 1B-09: consider seq_num_V rollover a failure
*/
fail_and_restart_in_ms(0, &status, output);
break;
}
callback_in_ms(0, output);
set_state_id(hdcp, output, D2_A9_SEND_STREAM_MANAGEMENT);
break;
case D2_A9_SEND_STREAM_MANAGEMENT:
if (input->rxstatus_read != PASS ||
input->reauth_request_check != PASS ||
input->link_integrity_check_dp != PASS) {
fail_and_restart_in_ms(0, &status, output);
break;
} else if (event_ctx->rx_id_list_ready) {
callback_in_ms(0, output);
set_state_id(hdcp, output, D2_A78_VERIFY_RX_ID_LIST_AND_SEND_ACK);
break;
} else if (input->prepare_stream_manage != PASS ||
input->stream_manage_write != PASS) {
if (event_ctx->event == MOD_HDCP_EVENT_CALLBACK)
fail_and_restart_in_ms(0, &status, output);
else
increment_stay_counter(hdcp);
break;
}
callback_in_ms(100, output);
set_state_id(hdcp, output, D2_A9_VALIDATE_STREAM_READY);
break;
case D2_A9_VALIDATE_STREAM_READY:
if (input->rxstatus_read != PASS ||
input->reauth_request_check != PASS ||
input->link_integrity_check_dp != PASS) {
fail_and_restart_in_ms(0, &status, output);
break;
} else if (event_ctx->rx_id_list_ready) {
callback_in_ms(0, output);
set_state_id(hdcp, output, D2_A78_VERIFY_RX_ID_LIST_AND_SEND_ACK);
break;
} else if (input->stream_ready_read != PASS ||
input->stream_ready_validation != PASS) {
/*
* 1B-10-1: restart content stream management
* on invalid M'
* 1B-10-2: consider stream ready timeout a failure
*/
if (hdcp->auth.count.stream_management_retry_count > 10) {
fail_and_restart_in_ms(0, &status, output);
} else if (event_ctx->event == MOD_HDCP_EVENT_CALLBACK) {
hdcp->auth.count.stream_management_retry_count++;
callback_in_ms(0, output);
set_state_id(hdcp, output, D2_A9_SEND_STREAM_MANAGEMENT);
} else {
increment_stay_counter(hdcp);
}
break;
}
callback_in_ms(200, output);
set_state_id(hdcp, output, D2_ENABLE_ENCRYPTION);
break;
default:
status = MOD_HDCP_STATUS_INVALID_STATE;
fail_and_restart_in_ms(0, &status, output);
break;
}
return status;
}
@@ -0,0 +1,764 @@
/*
* Copyright 2019 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 "hdcp.h"
#ifndef MIN
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#endif
#define HDCP_I2C_ADDR 0x3a /* 0x74 >> 1*/
#define KSV_READ_SIZE 0xf /* 0x6803b - 0x6802c */
#define HDCP_MAX_AUX_TRANSACTION_SIZE 16
#define DP_CP_IRQ (1 << 2)
enum mod_hdcp_ddc_message_id {
MOD_HDCP_MESSAGE_ID_INVALID = -1,
/* HDCP 1.4 */
MOD_HDCP_MESSAGE_ID_READ_BKSV = 0,
MOD_HDCP_MESSAGE_ID_READ_RI_R0,
MOD_HDCP_MESSAGE_ID_WRITE_AKSV,
MOD_HDCP_MESSAGE_ID_WRITE_AINFO,
MOD_HDCP_MESSAGE_ID_WRITE_AN,
MOD_HDCP_MESSAGE_ID_READ_VH_X,
MOD_HDCP_MESSAGE_ID_READ_VH_0,
MOD_HDCP_MESSAGE_ID_READ_VH_1,
MOD_HDCP_MESSAGE_ID_READ_VH_2,
MOD_HDCP_MESSAGE_ID_READ_VH_3,
MOD_HDCP_MESSAGE_ID_READ_VH_4,
MOD_HDCP_MESSAGE_ID_READ_BCAPS,
MOD_HDCP_MESSAGE_ID_READ_BSTATUS,
MOD_HDCP_MESSAGE_ID_READ_KSV_FIFO,
MOD_HDCP_MESSAGE_ID_READ_BINFO,
/* HDCP 2.2 */
MOD_HDCP_MESSAGE_ID_HDCP2VERSION,
MOD_HDCP_MESSAGE_ID_RX_CAPS,
MOD_HDCP_MESSAGE_ID_WRITE_AKE_INIT,
MOD_HDCP_MESSAGE_ID_READ_AKE_SEND_CERT,
MOD_HDCP_MESSAGE_ID_WRITE_AKE_NO_STORED_KM,
MOD_HDCP_MESSAGE_ID_WRITE_AKE_STORED_KM,
MOD_HDCP_MESSAGE_ID_READ_AKE_SEND_H_PRIME,
MOD_HDCP_MESSAGE_ID_READ_AKE_SEND_PAIRING_INFO,
MOD_HDCP_MESSAGE_ID_WRITE_LC_INIT,
MOD_HDCP_MESSAGE_ID_READ_LC_SEND_L_PRIME,
MOD_HDCP_MESSAGE_ID_WRITE_SKE_SEND_EKS,
MOD_HDCP_MESSAGE_ID_READ_REPEATER_AUTH_SEND_RECEIVERID_LIST,
MOD_HDCP_MESSAGE_ID_READ_REPEATER_AUTH_SEND_RECEIVERID_LIST_PART2,
MOD_HDCP_MESSAGE_ID_WRITE_REPEATER_AUTH_SEND_ACK,
MOD_HDCP_MESSAGE_ID_WRITE_REPEATER_AUTH_STREAM_MANAGE,
MOD_HDCP_MESSAGE_ID_READ_REPEATER_AUTH_STREAM_READY,
MOD_HDCP_MESSAGE_ID_READ_RXSTATUS,
MOD_HDCP_MESSAGE_ID_WRITE_CONTENT_STREAM_TYPE,
MOD_HDCP_MESSAGE_ID_MAX
};
static const uint8_t hdcp_i2c_offsets[] = {
[MOD_HDCP_MESSAGE_ID_READ_BKSV] = 0x0,
[MOD_HDCP_MESSAGE_ID_READ_RI_R0] = 0x8,
[MOD_HDCP_MESSAGE_ID_WRITE_AKSV] = 0x10,
[MOD_HDCP_MESSAGE_ID_WRITE_AINFO] = 0x15,
[MOD_HDCP_MESSAGE_ID_WRITE_AN] = 0x18,
[MOD_HDCP_MESSAGE_ID_READ_VH_X] = 0x20,
[MOD_HDCP_MESSAGE_ID_READ_VH_0] = 0x20,
[MOD_HDCP_MESSAGE_ID_READ_VH_1] = 0x24,
[MOD_HDCP_MESSAGE_ID_READ_VH_2] = 0x28,
[MOD_HDCP_MESSAGE_ID_READ_VH_3] = 0x2C,
[MOD_HDCP_MESSAGE_ID_READ_VH_4] = 0x30,
[MOD_HDCP_MESSAGE_ID_READ_BCAPS] = 0x40,
[MOD_HDCP_MESSAGE_ID_READ_BSTATUS] = 0x41,
[MOD_HDCP_MESSAGE_ID_READ_KSV_FIFO] = 0x43,
[MOD_HDCP_MESSAGE_ID_READ_BINFO] = 0xFF,
[MOD_HDCP_MESSAGE_ID_HDCP2VERSION] = 0x50,
[MOD_HDCP_MESSAGE_ID_WRITE_AKE_INIT] = 0x60,
[MOD_HDCP_MESSAGE_ID_READ_AKE_SEND_CERT] = 0x80,
[MOD_HDCP_MESSAGE_ID_WRITE_AKE_NO_STORED_KM] = 0x60,
[MOD_HDCP_MESSAGE_ID_WRITE_AKE_STORED_KM] = 0x60,
[MOD_HDCP_MESSAGE_ID_READ_AKE_SEND_H_PRIME] = 0x80,
[MOD_HDCP_MESSAGE_ID_READ_AKE_SEND_PAIRING_INFO] = 0x80,
[MOD_HDCP_MESSAGE_ID_WRITE_LC_INIT] = 0x60,
[MOD_HDCP_MESSAGE_ID_READ_LC_SEND_L_PRIME] = 0x80,
[MOD_HDCP_MESSAGE_ID_WRITE_SKE_SEND_EKS] = 0x60,
[MOD_HDCP_MESSAGE_ID_READ_REPEATER_AUTH_SEND_RECEIVERID_LIST] = 0x80,
[MOD_HDCP_MESSAGE_ID_READ_REPEATER_AUTH_SEND_RECEIVERID_LIST_PART2] = 0x80,
[MOD_HDCP_MESSAGE_ID_WRITE_REPEATER_AUTH_SEND_ACK] = 0x60,
[MOD_HDCP_MESSAGE_ID_WRITE_REPEATER_AUTH_STREAM_MANAGE] = 0x60,
[MOD_HDCP_MESSAGE_ID_READ_REPEATER_AUTH_STREAM_READY] = 0x80,
[MOD_HDCP_MESSAGE_ID_READ_RXSTATUS] = 0x70,
[MOD_HDCP_MESSAGE_ID_WRITE_CONTENT_STREAM_TYPE] = 0x0
};
static const uint32_t hdcp_dpcd_addrs[] = {
[MOD_HDCP_MESSAGE_ID_READ_BKSV] = 0x68000,
[MOD_HDCP_MESSAGE_ID_READ_RI_R0] = 0x68005,
[MOD_HDCP_MESSAGE_ID_WRITE_AKSV] = 0x68007,
[MOD_HDCP_MESSAGE_ID_WRITE_AINFO] = 0x6803B,
[MOD_HDCP_MESSAGE_ID_WRITE_AN] = 0x6800c,
[MOD_HDCP_MESSAGE_ID_READ_VH_X] = 0x68014,
[MOD_HDCP_MESSAGE_ID_READ_VH_0] = 0x68014,
[MOD_HDCP_MESSAGE_ID_READ_VH_1] = 0x68018,
[MOD_HDCP_MESSAGE_ID_READ_VH_2] = 0x6801c,
[MOD_HDCP_MESSAGE_ID_READ_VH_3] = 0x68020,
[MOD_HDCP_MESSAGE_ID_READ_VH_4] = 0x68024,
[MOD_HDCP_MESSAGE_ID_READ_BCAPS] = 0x68028,
[MOD_HDCP_MESSAGE_ID_READ_BSTATUS] = 0x68029,
[MOD_HDCP_MESSAGE_ID_READ_KSV_FIFO] = 0x6802c,
[MOD_HDCP_MESSAGE_ID_READ_BINFO] = 0x6802a,
[MOD_HDCP_MESSAGE_ID_RX_CAPS] = 0x6921d,
[MOD_HDCP_MESSAGE_ID_WRITE_AKE_INIT] = 0x69000,
[MOD_HDCP_MESSAGE_ID_READ_AKE_SEND_CERT] = 0x6900b,
[MOD_HDCP_MESSAGE_ID_WRITE_AKE_NO_STORED_KM] = 0x69220,
[MOD_HDCP_MESSAGE_ID_WRITE_AKE_STORED_KM] = 0x692a0,
[MOD_HDCP_MESSAGE_ID_READ_AKE_SEND_H_PRIME] = 0x692c0,
[MOD_HDCP_MESSAGE_ID_READ_AKE_SEND_PAIRING_INFO] = 0x692e0,
[MOD_HDCP_MESSAGE_ID_WRITE_LC_INIT] = 0x692f0,
[MOD_HDCP_MESSAGE_ID_READ_LC_SEND_L_PRIME] = 0x692f8,
[MOD_HDCP_MESSAGE_ID_WRITE_SKE_SEND_EKS] = 0x69318,
[MOD_HDCP_MESSAGE_ID_READ_REPEATER_AUTH_SEND_RECEIVERID_LIST] = 0x69330,
[MOD_HDCP_MESSAGE_ID_READ_REPEATER_AUTH_SEND_RECEIVERID_LIST_PART2] = 0x69340,
[MOD_HDCP_MESSAGE_ID_WRITE_REPEATER_AUTH_SEND_ACK] = 0x693e0,
[MOD_HDCP_MESSAGE_ID_WRITE_REPEATER_AUTH_STREAM_MANAGE] = 0x693f0,
[MOD_HDCP_MESSAGE_ID_READ_REPEATER_AUTH_STREAM_READY] = 0x69473,
[MOD_HDCP_MESSAGE_ID_READ_RXSTATUS] = 0x69493,
[MOD_HDCP_MESSAGE_ID_WRITE_CONTENT_STREAM_TYPE] = 0x69494
};
static enum mod_hdcp_status read(struct mod_hdcp *hdcp,
enum mod_hdcp_ddc_message_id msg_id,
uint8_t *buf,
uint32_t buf_len)
{
bool success = true;
uint32_t cur_size = 0;
uint32_t data_offset = 0;
if (msg_id == MOD_HDCP_MESSAGE_ID_INVALID ||
msg_id >= MOD_HDCP_MESSAGE_ID_MAX)
return MOD_HDCP_STATUS_DDC_FAILURE;
if (is_dp_hdcp(hdcp)) {
int num_dpcd_addrs = ARRAY_SIZE(hdcp_dpcd_addrs);
if (msg_id >= num_dpcd_addrs)
return MOD_HDCP_STATUS_DDC_FAILURE;
while (buf_len > 0) {
cur_size = MIN(buf_len, HDCP_MAX_AUX_TRANSACTION_SIZE);
success = hdcp->config.ddc.funcs.read_dpcd(hdcp->config.ddc.handle,
hdcp_dpcd_addrs[msg_id] + data_offset,
buf + data_offset,
cur_size);
if (!success)
break;
buf_len -= cur_size;
data_offset += cur_size;
}
} else {
int num_i2c_offsets = ARRAY_SIZE(hdcp_i2c_offsets);
if (msg_id >= num_i2c_offsets)
return MOD_HDCP_STATUS_DDC_FAILURE;
success = hdcp->config.ddc.funcs.read_i2c(
hdcp->config.ddc.handle,
HDCP_I2C_ADDR,
hdcp_i2c_offsets[msg_id],
buf,
(uint32_t)buf_len);
}
return success ? MOD_HDCP_STATUS_SUCCESS : MOD_HDCP_STATUS_DDC_FAILURE;
}
static enum mod_hdcp_status read_repeatedly(struct mod_hdcp *hdcp,
enum mod_hdcp_ddc_message_id msg_id,
uint8_t *buf,
uint32_t buf_len,
uint8_t read_size)
{
enum mod_hdcp_status status = MOD_HDCP_STATUS_DDC_FAILURE;
uint32_t cur_size = 0;
uint32_t data_offset = 0;
while (buf_len > 0) {
cur_size = MIN(buf_len, read_size);
status = read(hdcp, msg_id, buf + data_offset, cur_size);
if (status != MOD_HDCP_STATUS_SUCCESS)
break;
buf_len -= cur_size;
data_offset += cur_size;
}
return status;
}
static enum mod_hdcp_status write(struct mod_hdcp *hdcp,
enum mod_hdcp_ddc_message_id msg_id,
uint8_t *buf,
uint32_t buf_len)
{
bool success = true;
uint32_t cur_size = 0;
uint32_t data_offset = 0;
if (msg_id == MOD_HDCP_MESSAGE_ID_INVALID ||
msg_id >= MOD_HDCP_MESSAGE_ID_MAX)
return MOD_HDCP_STATUS_DDC_FAILURE;
if (is_dp_hdcp(hdcp)) {
int num_dpcd_addrs = ARRAY_SIZE(hdcp_dpcd_addrs);
if (msg_id >= num_dpcd_addrs)
return MOD_HDCP_STATUS_DDC_FAILURE;
while (buf_len > 0) {
cur_size = MIN(buf_len, HDCP_MAX_AUX_TRANSACTION_SIZE);
success = hdcp->config.ddc.funcs.write_dpcd(
hdcp->config.ddc.handle,
hdcp_dpcd_addrs[msg_id] + data_offset,
buf + data_offset,
cur_size);
if (!success)
break;
buf_len -= cur_size;
data_offset += cur_size;
}
} else {
int num_i2c_offsets = ARRAY_SIZE(hdcp_i2c_offsets);
if (msg_id >= num_i2c_offsets)
return MOD_HDCP_STATUS_DDC_FAILURE;
hdcp->buf[0] = hdcp_i2c_offsets[msg_id];
memmove(&hdcp->buf[1], buf, buf_len);
success = hdcp->config.ddc.funcs.write_i2c(
hdcp->config.ddc.handle,
HDCP_I2C_ADDR,
hdcp->buf,
(uint32_t)(buf_len+1));
}
return success ? MOD_HDCP_STATUS_SUCCESS : MOD_HDCP_STATUS_DDC_FAILURE;
}
enum mod_hdcp_status mod_hdcp_read_bksv(struct mod_hdcp *hdcp)
{
return read(hdcp, MOD_HDCP_MESSAGE_ID_READ_BKSV,
hdcp->auth.msg.hdcp1.bksv,
sizeof(hdcp->auth.msg.hdcp1.bksv));
}
enum mod_hdcp_status mod_hdcp_read_bcaps(struct mod_hdcp *hdcp)
{
return read(hdcp, MOD_HDCP_MESSAGE_ID_READ_BCAPS,
&hdcp->auth.msg.hdcp1.bcaps,
sizeof(hdcp->auth.msg.hdcp1.bcaps));
}
enum mod_hdcp_status mod_hdcp_read_bstatus(struct mod_hdcp *hdcp)
{
enum mod_hdcp_status status;
if (is_dp_hdcp(hdcp))
status = read(hdcp, MOD_HDCP_MESSAGE_ID_READ_BSTATUS,
(uint8_t *)&hdcp->auth.msg.hdcp1.bstatus,
1);
else
status = read(hdcp, MOD_HDCP_MESSAGE_ID_READ_BSTATUS,
(uint8_t *)&hdcp->auth.msg.hdcp1.bstatus,
sizeof(hdcp->auth.msg.hdcp1.bstatus));
return status;
}
enum mod_hdcp_status mod_hdcp_read_r0p(struct mod_hdcp *hdcp)
{
return read(hdcp, MOD_HDCP_MESSAGE_ID_READ_RI_R0,
(uint8_t *)&hdcp->auth.msg.hdcp1.r0p,
sizeof(hdcp->auth.msg.hdcp1.r0p));
}
/* special case, reading repeatedly at the same address, don't use read() */
enum mod_hdcp_status mod_hdcp_read_ksvlist(struct mod_hdcp *hdcp)
{
enum mod_hdcp_status status;
if (is_dp_hdcp(hdcp))
status = read_repeatedly(hdcp, MOD_HDCP_MESSAGE_ID_READ_KSV_FIFO,
hdcp->auth.msg.hdcp1.ksvlist,
hdcp->auth.msg.hdcp1.ksvlist_size,
KSV_READ_SIZE);
else
status = read(hdcp, MOD_HDCP_MESSAGE_ID_READ_KSV_FIFO,
(uint8_t *)&hdcp->auth.msg.hdcp1.ksvlist,
hdcp->auth.msg.hdcp1.ksvlist_size);
return status;
}
enum mod_hdcp_status mod_hdcp_read_vp(struct mod_hdcp *hdcp)
{
enum mod_hdcp_status status;
status = read(hdcp, MOD_HDCP_MESSAGE_ID_READ_VH_0,
&hdcp->auth.msg.hdcp1.vp[0], 4);
if (status != MOD_HDCP_STATUS_SUCCESS)
goto out;
status = read(hdcp, MOD_HDCP_MESSAGE_ID_READ_VH_1,
&hdcp->auth.msg.hdcp1.vp[4], 4);
if (status != MOD_HDCP_STATUS_SUCCESS)
goto out;
status = read(hdcp, MOD_HDCP_MESSAGE_ID_READ_VH_2,
&hdcp->auth.msg.hdcp1.vp[8], 4);
if (status != MOD_HDCP_STATUS_SUCCESS)
goto out;
status = read(hdcp, MOD_HDCP_MESSAGE_ID_READ_VH_3,
&hdcp->auth.msg.hdcp1.vp[12], 4);
if (status != MOD_HDCP_STATUS_SUCCESS)
goto out;
status = read(hdcp, MOD_HDCP_MESSAGE_ID_READ_VH_4,
&hdcp->auth.msg.hdcp1.vp[16], 4);
out:
return status;
}
enum mod_hdcp_status mod_hdcp_read_binfo(struct mod_hdcp *hdcp)
{
enum mod_hdcp_status status;
if (is_dp_hdcp(hdcp))
status = read(hdcp, MOD_HDCP_MESSAGE_ID_READ_BINFO,
(uint8_t *)&hdcp->auth.msg.hdcp1.binfo_dp,
sizeof(hdcp->auth.msg.hdcp1.binfo_dp));
else
status = MOD_HDCP_STATUS_INVALID_OPERATION;
return status;
}
enum mod_hdcp_status mod_hdcp_write_aksv(struct mod_hdcp *hdcp)
{
return write(hdcp, MOD_HDCP_MESSAGE_ID_WRITE_AKSV,
hdcp->auth.msg.hdcp1.aksv,
sizeof(hdcp->auth.msg.hdcp1.aksv));
}
enum mod_hdcp_status mod_hdcp_write_ainfo(struct mod_hdcp *hdcp)
{
return write(hdcp, MOD_HDCP_MESSAGE_ID_WRITE_AINFO,
&hdcp->auth.msg.hdcp1.ainfo,
sizeof(hdcp->auth.msg.hdcp1.ainfo));
}
enum mod_hdcp_status mod_hdcp_write_an(struct mod_hdcp *hdcp)
{
return write(hdcp, MOD_HDCP_MESSAGE_ID_WRITE_AN,
hdcp->auth.msg.hdcp1.an,
sizeof(hdcp->auth.msg.hdcp1.an));
}
enum mod_hdcp_status mod_hdcp_read_hdcp2version(struct mod_hdcp *hdcp)
{
enum mod_hdcp_status status;
if (is_dp_hdcp(hdcp))
status = MOD_HDCP_STATUS_INVALID_OPERATION;
else
status = read(hdcp, MOD_HDCP_MESSAGE_ID_HDCP2VERSION,
&hdcp->auth.msg.hdcp2.hdcp2version_hdmi,
sizeof(hdcp->auth.msg.hdcp2.hdcp2version_hdmi));
return status;
}
enum mod_hdcp_status mod_hdcp_read_rxcaps(struct mod_hdcp *hdcp)
{
enum mod_hdcp_status status;
if (!is_dp_hdcp(hdcp))
status = MOD_HDCP_STATUS_INVALID_OPERATION;
else
status = read(hdcp, MOD_HDCP_MESSAGE_ID_RX_CAPS,
hdcp->auth.msg.hdcp2.rxcaps_dp,
sizeof(hdcp->auth.msg.hdcp2.rxcaps_dp));
return status;
}
enum mod_hdcp_status mod_hdcp_read_rxstatus(struct mod_hdcp *hdcp)
{
enum mod_hdcp_status status;
if (is_dp_hdcp(hdcp)) {
status = read(hdcp, MOD_HDCP_MESSAGE_ID_READ_RXSTATUS,
&hdcp->auth.msg.hdcp2.rxstatus_dp,
1);
} else {
status = read(hdcp, MOD_HDCP_MESSAGE_ID_READ_RXSTATUS,
(uint8_t *)&hdcp->auth.msg.hdcp2.rxstatus,
sizeof(hdcp->auth.msg.hdcp2.rxstatus));
}
return status;
}
enum mod_hdcp_status mod_hdcp_read_ake_cert(struct mod_hdcp *hdcp)
{
enum mod_hdcp_status status;
if (is_dp_hdcp(hdcp)) {
hdcp->auth.msg.hdcp2.ake_cert[0] = HDCP_2_2_AKE_SEND_CERT;
status = read(hdcp, MOD_HDCP_MESSAGE_ID_READ_AKE_SEND_CERT,
hdcp->auth.msg.hdcp2.ake_cert+1,
sizeof(hdcp->auth.msg.hdcp2.ake_cert)-1);
} else {
status = read(hdcp, MOD_HDCP_MESSAGE_ID_READ_AKE_SEND_CERT,
hdcp->auth.msg.hdcp2.ake_cert,
sizeof(hdcp->auth.msg.hdcp2.ake_cert));
}
return status;
}
enum mod_hdcp_status mod_hdcp_read_h_prime(struct mod_hdcp *hdcp)
{
enum mod_hdcp_status status;
if (is_dp_hdcp(hdcp)) {
hdcp->auth.msg.hdcp2.ake_h_prime[0] = HDCP_2_2_AKE_SEND_HPRIME;
status = read(hdcp, MOD_HDCP_MESSAGE_ID_READ_AKE_SEND_H_PRIME,
hdcp->auth.msg.hdcp2.ake_h_prime+1,
sizeof(hdcp->auth.msg.hdcp2.ake_h_prime)-1);
} else {
status = read(hdcp, MOD_HDCP_MESSAGE_ID_READ_AKE_SEND_H_PRIME,
hdcp->auth.msg.hdcp2.ake_h_prime,
sizeof(hdcp->auth.msg.hdcp2.ake_h_prime));
}
return status;
}
enum mod_hdcp_status mod_hdcp_read_pairing_info(struct mod_hdcp *hdcp)
{
enum mod_hdcp_status status;
if (is_dp_hdcp(hdcp)) {
hdcp->auth.msg.hdcp2.ake_pairing_info[0] = HDCP_2_2_AKE_SEND_PAIRING_INFO;
status = read(hdcp, MOD_HDCP_MESSAGE_ID_READ_AKE_SEND_PAIRING_INFO,
hdcp->auth.msg.hdcp2.ake_pairing_info+1,
sizeof(hdcp->auth.msg.hdcp2.ake_pairing_info)-1);
} else {
status = read(hdcp, MOD_HDCP_MESSAGE_ID_READ_AKE_SEND_PAIRING_INFO,
hdcp->auth.msg.hdcp2.ake_pairing_info,
sizeof(hdcp->auth.msg.hdcp2.ake_pairing_info));
}
return status;
}
enum mod_hdcp_status mod_hdcp_read_l_prime(struct mod_hdcp *hdcp)
{
enum mod_hdcp_status status;
if (is_dp_hdcp(hdcp)) {
hdcp->auth.msg.hdcp2.lc_l_prime[0] = HDCP_2_2_LC_SEND_LPRIME;
status = read(hdcp, MOD_HDCP_MESSAGE_ID_READ_LC_SEND_L_PRIME,
hdcp->auth.msg.hdcp2.lc_l_prime+1,
sizeof(hdcp->auth.msg.hdcp2.lc_l_prime)-1);
} else {
status = read(hdcp, MOD_HDCP_MESSAGE_ID_READ_LC_SEND_L_PRIME,
hdcp->auth.msg.hdcp2.lc_l_prime,
sizeof(hdcp->auth.msg.hdcp2.lc_l_prime));
}
return status;
}
enum mod_hdcp_status mod_hdcp_read_rx_id_list(struct mod_hdcp *hdcp)
{
enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS;
if (is_dp_hdcp(hdcp)) {
uint32_t device_count = 0;
uint32_t rx_id_list_size = 0;
uint32_t bytes_read = 0;
hdcp->auth.msg.hdcp2.rx_id_list[0] = HDCP_2_2_REP_SEND_RECVID_LIST;
status = read(hdcp, MOD_HDCP_MESSAGE_ID_READ_REPEATER_AUTH_SEND_RECEIVERID_LIST,
hdcp->auth.msg.hdcp2.rx_id_list+1,
HDCP_MAX_AUX_TRANSACTION_SIZE);
if (status == MOD_HDCP_STATUS_SUCCESS) {
bytes_read = HDCP_MAX_AUX_TRANSACTION_SIZE;
device_count = 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);
rx_id_list_size = MIN((21 + 5 * device_count),
(sizeof(hdcp->auth.msg.hdcp2.rx_id_list) - 1));
status = read(hdcp, MOD_HDCP_MESSAGE_ID_READ_REPEATER_AUTH_SEND_RECEIVERID_LIST_PART2,
hdcp->auth.msg.hdcp2.rx_id_list + 1 + bytes_read,
(rx_id_list_size - 1) / HDCP_MAX_AUX_TRANSACTION_SIZE * HDCP_MAX_AUX_TRANSACTION_SIZE);
}
} else {
status = read(hdcp, MOD_HDCP_MESSAGE_ID_READ_REPEATER_AUTH_SEND_RECEIVERID_LIST,
hdcp->auth.msg.hdcp2.rx_id_list,
MIN(hdcp->auth.msg.hdcp2.rx_id_list_size,
sizeof(hdcp->auth.msg.hdcp2.rx_id_list)));
}
return status;
}
enum mod_hdcp_status mod_hdcp_read_stream_ready(struct mod_hdcp *hdcp)
{
enum mod_hdcp_status status;
if (is_dp_hdcp(hdcp)) {
hdcp->auth.msg.hdcp2.repeater_auth_stream_ready[0] = HDCP_2_2_REP_STREAM_READY;
status = read(hdcp, MOD_HDCP_MESSAGE_ID_READ_REPEATER_AUTH_STREAM_READY,
hdcp->auth.msg.hdcp2.repeater_auth_stream_ready+1,
sizeof(hdcp->auth.msg.hdcp2.repeater_auth_stream_ready)-1);
} else {
status = read(hdcp, MOD_HDCP_MESSAGE_ID_READ_REPEATER_AUTH_STREAM_READY,
hdcp->auth.msg.hdcp2.repeater_auth_stream_ready,
sizeof(hdcp->auth.msg.hdcp2.repeater_auth_stream_ready));
}
return status;
}
enum mod_hdcp_status mod_hdcp_write_ake_init(struct mod_hdcp *hdcp)
{
enum mod_hdcp_status status;
if (is_dp_hdcp(hdcp))
status = write(hdcp, MOD_HDCP_MESSAGE_ID_WRITE_AKE_INIT,
hdcp->auth.msg.hdcp2.ake_init+1,
sizeof(hdcp->auth.msg.hdcp2.ake_init)-1);
else
status = write(hdcp, MOD_HDCP_MESSAGE_ID_WRITE_AKE_INIT,
hdcp->auth.msg.hdcp2.ake_init,
sizeof(hdcp->auth.msg.hdcp2.ake_init));
return status;
}
enum mod_hdcp_status mod_hdcp_write_no_stored_km(struct mod_hdcp *hdcp)
{
enum mod_hdcp_status status;
if (is_dp_hdcp(hdcp))
status = write(hdcp, MOD_HDCP_MESSAGE_ID_WRITE_AKE_NO_STORED_KM,
hdcp->auth.msg.hdcp2.ake_no_stored_km+1,
sizeof(hdcp->auth.msg.hdcp2.ake_no_stored_km)-1);
else
status = write(hdcp, MOD_HDCP_MESSAGE_ID_WRITE_AKE_NO_STORED_KM,
hdcp->auth.msg.hdcp2.ake_no_stored_km,
sizeof(hdcp->auth.msg.hdcp2.ake_no_stored_km));
return status;
}
enum mod_hdcp_status mod_hdcp_write_stored_km(struct mod_hdcp *hdcp)
{
enum mod_hdcp_status status;
if (is_dp_hdcp(hdcp))
status = write(hdcp, MOD_HDCP_MESSAGE_ID_WRITE_AKE_STORED_KM,
hdcp->auth.msg.hdcp2.ake_stored_km+1,
sizeof(hdcp->auth.msg.hdcp2.ake_stored_km)-1);
else
status = write(hdcp, MOD_HDCP_MESSAGE_ID_WRITE_AKE_STORED_KM,
hdcp->auth.msg.hdcp2.ake_stored_km,
sizeof(hdcp->auth.msg.hdcp2.ake_stored_km));
return status;
}
enum mod_hdcp_status mod_hdcp_write_lc_init(struct mod_hdcp *hdcp)
{
enum mod_hdcp_status status;
if (is_dp_hdcp(hdcp))
status = write(hdcp, MOD_HDCP_MESSAGE_ID_WRITE_LC_INIT,
hdcp->auth.msg.hdcp2.lc_init+1,
sizeof(hdcp->auth.msg.hdcp2.lc_init)-1);
else
status = write(hdcp, MOD_HDCP_MESSAGE_ID_WRITE_LC_INIT,
hdcp->auth.msg.hdcp2.lc_init,
sizeof(hdcp->auth.msg.hdcp2.lc_init));
return status;
}
enum mod_hdcp_status mod_hdcp_write_eks(struct mod_hdcp *hdcp)
{
enum mod_hdcp_status status;
if (is_dp_hdcp(hdcp))
status = write(hdcp,
MOD_HDCP_MESSAGE_ID_WRITE_SKE_SEND_EKS,
hdcp->auth.msg.hdcp2.ske_eks+1,
sizeof(hdcp->auth.msg.hdcp2.ske_eks)-1);
else
status = write(hdcp,
MOD_HDCP_MESSAGE_ID_WRITE_SKE_SEND_EKS,
hdcp->auth.msg.hdcp2.ske_eks,
sizeof(hdcp->auth.msg.hdcp2.ske_eks));
return status;
}
enum mod_hdcp_status mod_hdcp_write_repeater_auth_ack(struct mod_hdcp *hdcp)
{
enum mod_hdcp_status status;
if (is_dp_hdcp(hdcp))
status = write(hdcp, MOD_HDCP_MESSAGE_ID_WRITE_REPEATER_AUTH_SEND_ACK,
hdcp->auth.msg.hdcp2.repeater_auth_ack+1,
sizeof(hdcp->auth.msg.hdcp2.repeater_auth_ack)-1);
else
status = write(hdcp, MOD_HDCP_MESSAGE_ID_WRITE_REPEATER_AUTH_SEND_ACK,
hdcp->auth.msg.hdcp2.repeater_auth_ack,
sizeof(hdcp->auth.msg.hdcp2.repeater_auth_ack));
return status;
}
enum mod_hdcp_status mod_hdcp_write_stream_manage(struct mod_hdcp *hdcp)
{
enum mod_hdcp_status status;
if (is_dp_hdcp(hdcp))
status = write(hdcp,
MOD_HDCP_MESSAGE_ID_WRITE_REPEATER_AUTH_STREAM_MANAGE,
hdcp->auth.msg.hdcp2.repeater_auth_stream_manage+1,
hdcp->auth.msg.hdcp2.stream_manage_size-1);
else
status = write(hdcp,
MOD_HDCP_MESSAGE_ID_WRITE_REPEATER_AUTH_STREAM_MANAGE,
hdcp->auth.msg.hdcp2.repeater_auth_stream_manage,
hdcp->auth.msg.hdcp2.stream_manage_size);
return status;
}
enum mod_hdcp_status mod_hdcp_write_content_type(struct mod_hdcp *hdcp)
{
enum mod_hdcp_status status;
if (is_dp_hdcp(hdcp))
status = write(hdcp, MOD_HDCP_MESSAGE_ID_WRITE_CONTENT_STREAM_TYPE,
hdcp->auth.msg.hdcp2.content_stream_type_dp+1,
sizeof(hdcp->auth.msg.hdcp2.content_stream_type_dp)-1);
else
status = MOD_HDCP_STATUS_INVALID_OPERATION;
return status;
}
enum mod_hdcp_status mod_hdcp_clear_cp_irq_status(struct mod_hdcp *hdcp)
{
uint8_t clear_cp_irq_bit = DP_CP_IRQ;
uint32_t size = 1;
if (is_dp_hdcp(hdcp)) {
uint32_t cp_irq_addrs = (hdcp->connection.link.dp.rev >= 0x14)
? DP_DEVICE_SERVICE_IRQ_VECTOR_ESI0:DP_DEVICE_SERVICE_IRQ_VECTOR;
return hdcp->config.ddc.funcs.write_dpcd(hdcp->config.ddc.handle, cp_irq_addrs,
&clear_cp_irq_bit, size) ? MOD_HDCP_STATUS_SUCCESS : MOD_HDCP_STATUS_DDC_FAILURE;
}
return MOD_HDCP_STATUS_INVALID_OPERATION;
}
static bool write_stall_read_lc_fw_aux(struct mod_hdcp *hdcp)
{
struct mod_hdcp_message_hdcp2 *hdcp2 = &hdcp->auth.msg.hdcp2;
struct mod_hdcp_atomic_op_aux write = {
hdcp_dpcd_addrs[MOD_HDCP_MESSAGE_ID_WRITE_LC_INIT],
hdcp2->lc_init + 1,
sizeof(hdcp2->lc_init) - 1,
};
struct mod_hdcp_atomic_op_aux stall = { 0, NULL, 0, };
struct mod_hdcp_atomic_op_aux read = {
hdcp_dpcd_addrs[MOD_HDCP_MESSAGE_ID_READ_LC_SEND_L_PRIME],
hdcp2->lc_l_prime + 1,
sizeof(hdcp2->lc_l_prime) - 1,
};
hdcp2->lc_l_prime[0] = HDCP_2_2_LC_SEND_LPRIME;
return hdcp->config.ddc.funcs.atomic_write_poll_read_aux(
hdcp->config.ddc.handle,
&write,
&stall,
&read,
16 * 1000,
0
);
}
static bool write_poll_read_lc_fw_i2c(struct mod_hdcp *hdcp)
{
struct mod_hdcp_message_hdcp2 *hdcp2 = &hdcp->auth.msg.hdcp2;
uint8_t expected_rxstatus[2] = { sizeof(hdcp2->lc_l_prime) };
hdcp->buf[0] = hdcp_i2c_offsets[MOD_HDCP_MESSAGE_ID_WRITE_LC_INIT];
memmove(&hdcp->buf[1], hdcp2->lc_init, sizeof(hdcp2->lc_init));
struct mod_hdcp_atomic_op_i2c write = {
HDCP_I2C_ADDR,
0,
hdcp->buf,
sizeof(hdcp2->lc_init) + 1,
};
struct mod_hdcp_atomic_op_i2c poll = {
HDCP_I2C_ADDR,
hdcp_i2c_offsets[MOD_HDCP_MESSAGE_ID_READ_RXSTATUS],
expected_rxstatus,
sizeof(expected_rxstatus),
};
struct mod_hdcp_atomic_op_i2c read = {
HDCP_I2C_ADDR,
hdcp_i2c_offsets[MOD_HDCP_MESSAGE_ID_READ_LC_SEND_L_PRIME],
hdcp2->lc_l_prime,
sizeof(hdcp2->lc_l_prime),
};
return hdcp->config.ddc.funcs.atomic_write_poll_read_i2c(
hdcp->config.ddc.handle,
&write,
&poll,
&read,
20 * 1000,
6
);
}
enum mod_hdcp_status mod_hdcp_write_poll_read_lc_fw(struct mod_hdcp *hdcp)
{
const bool success = (is_dp_hdcp(hdcp) ? write_stall_read_lc_fw_aux : write_poll_read_lc_fw_i2c)(hdcp);
return success ? MOD_HDCP_STATUS_SUCCESS : MOD_HDCP_STATUS_HDCP2_LOCALITY_COMBO_READ_FAILURE;
}
@@ -0,0 +1,249 @@
/*
* Copyright 2019 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 "hdcp.h"
void mod_hdcp_dump_binary_message(uint8_t *msg, uint32_t msg_size,
uint8_t *buf, uint32_t buf_size)
{
const uint8_t bytes_per_line = 16,
byte_size = 3,
newline_size = 1,
terminator_size = 1;
uint32_t buf_pos = 0;
uint32_t i = 0;
/* Need room for at least the terminator. */
if (buf_size < terminator_size)
return;
for (i = 0; i < msg_size; i++) {
uint32_t needed = byte_size + terminator_size;
if (i % bytes_per_line == 0)
needed += newline_size;
if (buf_pos + needed > buf_size)
break;
if (i % bytes_per_line == 0)
buf[buf_pos++] = '\n';
sprintf((char *)&buf[buf_pos], "%02X ", msg[i]);
buf_pos += byte_size;
}
buf[buf_pos++] = '\0';
}
void mod_hdcp_log_ddc_trace(struct mod_hdcp *hdcp)
{
if (is_hdcp1(hdcp)) {
HDCP_DDC_READ_TRACE(hdcp, "BKSV", hdcp->auth.msg.hdcp1.bksv,
sizeof(hdcp->auth.msg.hdcp1.bksv));
HDCP_DDC_READ_TRACE(hdcp, "BCAPS", &hdcp->auth.msg.hdcp1.bcaps,
sizeof(hdcp->auth.msg.hdcp1.bcaps));
HDCP_DDC_READ_TRACE(hdcp, "BSTATUS",
(uint8_t *)&hdcp->auth.msg.hdcp1.bstatus,
sizeof(hdcp->auth.msg.hdcp1.bstatus));
HDCP_DDC_WRITE_TRACE(hdcp, "AN", hdcp->auth.msg.hdcp1.an,
sizeof(hdcp->auth.msg.hdcp1.an));
HDCP_DDC_WRITE_TRACE(hdcp, "AKSV", hdcp->auth.msg.hdcp1.aksv,
sizeof(hdcp->auth.msg.hdcp1.aksv));
HDCP_DDC_WRITE_TRACE(hdcp, "AINFO", &hdcp->auth.msg.hdcp1.ainfo,
sizeof(hdcp->auth.msg.hdcp1.ainfo));
HDCP_DDC_READ_TRACE(hdcp, "RI' / R0'",
(uint8_t *)&hdcp->auth.msg.hdcp1.r0p,
sizeof(hdcp->auth.msg.hdcp1.r0p));
HDCP_DDC_READ_TRACE(hdcp, "BINFO",
(uint8_t *)&hdcp->auth.msg.hdcp1.binfo_dp,
sizeof(hdcp->auth.msg.hdcp1.binfo_dp));
HDCP_DDC_READ_TRACE(hdcp, "KSVLIST", hdcp->auth.msg.hdcp1.ksvlist,
hdcp->auth.msg.hdcp1.ksvlist_size);
HDCP_DDC_READ_TRACE(hdcp, "V'", hdcp->auth.msg.hdcp1.vp,
sizeof(hdcp->auth.msg.hdcp1.vp));
} else if (is_hdcp2(hdcp)) {
HDCP_DDC_READ_TRACE(hdcp, "HDCP2Version",
&hdcp->auth.msg.hdcp2.hdcp2version_hdmi,
sizeof(hdcp->auth.msg.hdcp2.hdcp2version_hdmi));
HDCP_DDC_READ_TRACE(hdcp, "Rx Caps", hdcp->auth.msg.hdcp2.rxcaps_dp,
sizeof(hdcp->auth.msg.hdcp2.rxcaps_dp));
HDCP_DDC_WRITE_TRACE(hdcp, "AKE Init", hdcp->auth.msg.hdcp2.ake_init,
sizeof(hdcp->auth.msg.hdcp2.ake_init));
HDCP_DDC_READ_TRACE(hdcp, "AKE Cert", hdcp->auth.msg.hdcp2.ake_cert,
sizeof(hdcp->auth.msg.hdcp2.ake_cert));
HDCP_DDC_WRITE_TRACE(hdcp, "Stored KM",
hdcp->auth.msg.hdcp2.ake_stored_km,
sizeof(hdcp->auth.msg.hdcp2.ake_stored_km));
HDCP_DDC_WRITE_TRACE(hdcp, "No Stored KM",
hdcp->auth.msg.hdcp2.ake_no_stored_km,
sizeof(hdcp->auth.msg.hdcp2.ake_no_stored_km));
HDCP_DDC_READ_TRACE(hdcp, "H'", hdcp->auth.msg.hdcp2.ake_h_prime,
sizeof(hdcp->auth.msg.hdcp2.ake_h_prime));
HDCP_DDC_READ_TRACE(hdcp, "Pairing Info",
hdcp->auth.msg.hdcp2.ake_pairing_info,
sizeof(hdcp->auth.msg.hdcp2.ake_pairing_info));
HDCP_DDC_WRITE_TRACE(hdcp, "LC Init", hdcp->auth.msg.hdcp2.lc_init,
sizeof(hdcp->auth.msg.hdcp2.lc_init));
HDCP_DDC_READ_TRACE(hdcp, "L'", hdcp->auth.msg.hdcp2.lc_l_prime,
sizeof(hdcp->auth.msg.hdcp2.lc_l_prime));
HDCP_DDC_WRITE_TRACE(hdcp, "Exchange KS", hdcp->auth.msg.hdcp2.ske_eks,
sizeof(hdcp->auth.msg.hdcp2.ske_eks));
HDCP_DDC_READ_TRACE(hdcp, "Rx Status",
(uint8_t *)&hdcp->auth.msg.hdcp2.rxstatus,
sizeof(hdcp->auth.msg.hdcp2.rxstatus));
HDCP_DDC_READ_TRACE(hdcp, "Rx Id List",
hdcp->auth.msg.hdcp2.rx_id_list,
hdcp->auth.msg.hdcp2.rx_id_list_size);
HDCP_DDC_WRITE_TRACE(hdcp, "Rx Id List Ack",
hdcp->auth.msg.hdcp2.repeater_auth_ack,
sizeof(hdcp->auth.msg.hdcp2.repeater_auth_ack));
HDCP_DDC_WRITE_TRACE(hdcp, "Content Stream Management",
hdcp->auth.msg.hdcp2.repeater_auth_stream_manage,
hdcp->auth.msg.hdcp2.stream_manage_size);
HDCP_DDC_READ_TRACE(hdcp, "Stream Ready",
hdcp->auth.msg.hdcp2.repeater_auth_stream_ready,
sizeof(hdcp->auth.msg.hdcp2.repeater_auth_stream_ready));
HDCP_DDC_WRITE_TRACE(hdcp, "Content Stream Type",
hdcp->auth.msg.hdcp2.content_stream_type_dp,
sizeof(hdcp->auth.msg.hdcp2.content_stream_type_dp));
}
}
#define CASE_FORMAT(entry) case entry: return #entry;
char *mod_hdcp_status_to_str(int32_t status)
{
switch (status) {
MOD_HDCP_STATUS_LIST(CASE_FORMAT)
default:
return "MOD_HDCP_STATUS_UNKNOWN";
}
}
char *mod_hdcp_state_id_to_str(int32_t id)
{
switch (id) {
case HDCP_UNINITIALIZED:
return "HDCP_UNINITIALIZED";
case HDCP_INITIALIZED:
return "HDCP_INITIALIZED";
case HDCP_CP_NOT_DESIRED:
return "HDCP_CP_NOT_DESIRED";
case H1_A0_WAIT_FOR_ACTIVE_RX:
return "H1_A0_WAIT_FOR_ACTIVE_RX";
case H1_A1_EXCHANGE_KSVS:
return "H1_A1_EXCHANGE_KSVS";
case H1_A2_COMPUTATIONS_A3_VALIDATE_RX_A6_TEST_FOR_REPEATER:
return "H1_A2_COMPUTATIONS_A3_VALIDATE_RX_A6_TEST_FOR_REPEATER";
case H1_A45_AUTHENTICATED:
return "H1_A45_AUTHENTICATED";
case H1_A8_WAIT_FOR_READY:
return "H1_A8_WAIT_FOR_READY";
case H1_A9_READ_KSV_LIST:
return "H1_A9_READ_KSV_LIST";
case D1_A0_DETERMINE_RX_HDCP_CAPABLE:
return "D1_A0_DETERMINE_RX_HDCP_CAPABLE";
case D1_A1_EXCHANGE_KSVS:
return "D1_A1_EXCHANGE_KSVS";
case D1_A23_WAIT_FOR_R0_PRIME:
return "D1_A23_WAIT_FOR_R0_PRIME";
case D1_A2_COMPUTATIONS_A3_VALIDATE_RX_A5_TEST_FOR_REPEATER:
return "D1_A2_COMPUTATIONS_A3_VALIDATE_RX_A5_TEST_FOR_REPEATER";
case D1_A4_AUTHENTICATED:
return "D1_A4_AUTHENTICATED";
case D1_A6_WAIT_FOR_READY:
return "D1_A6_WAIT_FOR_READY";
case D1_A7_READ_KSV_LIST:
return "D1_A7_READ_KSV_LIST";
case H2_A0_KNOWN_HDCP2_CAPABLE_RX:
return "H2_A0_KNOWN_HDCP2_CAPABLE_RX";
case H2_A1_SEND_AKE_INIT:
return "H2_A1_SEND_AKE_INIT";
case H2_A1_VALIDATE_AKE_CERT:
return "H2_A1_VALIDATE_AKE_CERT";
case H2_A1_SEND_NO_STORED_KM:
return "H2_A1_SEND_NO_STORED_KM";
case H2_A1_READ_H_PRIME:
return "H2_A1_READ_H_PRIME";
case H2_A1_READ_PAIRING_INFO_AND_VALIDATE_H_PRIME:
return "H2_A1_READ_PAIRING_INFO_AND_VALIDATE_H_PRIME";
case H2_A1_SEND_STORED_KM:
return "H2_A1_SEND_STORED_KM";
case H2_A1_VALIDATE_H_PRIME:
return "H2_A1_VALIDATE_H_PRIME";
case H2_A2_LOCALITY_CHECK:
return "H2_A2_LOCALITY_CHECK";
case H2_A3_EXCHANGE_KS_AND_TEST_FOR_REPEATER:
return "H2_A3_EXCHANGE_KS_AND_TEST_FOR_REPEATER";
case H2_ENABLE_ENCRYPTION:
return "H2_ENABLE_ENCRYPTION";
case H2_A5_AUTHENTICATED:
return "H2_A5_AUTHENTICATED";
case H2_A6_WAIT_FOR_RX_ID_LIST:
return "H2_A6_WAIT_FOR_RX_ID_LIST";
case H2_A78_VERIFY_RX_ID_LIST_AND_SEND_ACK:
return "H2_A78_VERIFY_RX_ID_LIST_AND_SEND_ACK";
case H2_A9_SEND_STREAM_MANAGEMENT:
return "H2_A9_SEND_STREAM_MANAGEMENT";
case H2_A9_VALIDATE_STREAM_READY:
return "H2_A9_VALIDATE_STREAM_READY";
case D2_A0_DETERMINE_RX_HDCP_CAPABLE:
return "D2_A0_DETERMINE_RX_HDCP_CAPABLE";
case D2_A1_SEND_AKE_INIT:
return "D2_A1_SEND_AKE_INIT";
case D2_A1_VALIDATE_AKE_CERT:
return "D2_A1_VALIDATE_AKE_CERT";
case D2_A1_SEND_NO_STORED_KM:
return "D2_A1_SEND_NO_STORED_KM";
case D2_A1_READ_H_PRIME:
return "D2_A1_READ_H_PRIME";
case D2_A1_READ_PAIRING_INFO_AND_VALIDATE_H_PRIME:
return "D2_A1_READ_PAIRING_INFO_AND_VALIDATE_H_PRIME";
case D2_A1_SEND_STORED_KM:
return "D2_A1_SEND_STORED_KM";
case D2_A1_VALIDATE_H_PRIME:
return "D2_A1_VALIDATE_H_PRIME";
case D2_A2_LOCALITY_CHECK:
return "D2_A2_LOCALITY_CHECK";
case D2_A34_EXCHANGE_KS_AND_TEST_FOR_REPEATER:
return "D2_A34_EXCHANGE_KS_AND_TEST_FOR_REPEATER";
case D2_SEND_CONTENT_STREAM_TYPE:
return "D2_SEND_CONTENT_STREAM_TYPE";
case D2_ENABLE_ENCRYPTION:
return "D2_ENABLE_ENCRYPTION";
case D2_A5_AUTHENTICATED:
return "D2_A5_AUTHENTICATED";
case D2_A6_WAIT_FOR_RX_ID_LIST:
return "D2_A6_WAIT_FOR_RX_ID_LIST";
case D2_A78_VERIFY_RX_ID_LIST_AND_SEND_ACK:
return "D2_A78_VERIFY_RX_ID_LIST_AND_SEND_ACK";
case D2_A9_SEND_STREAM_MANAGEMENT:
return "D2_A9_SEND_STREAM_MANAGEMENT";
case D2_A9_VALIDATE_STREAM_READY:
return "D2_A9_VALIDATE_STREAM_READY";
default:
return "UNKNOWN_STATE_ID";
}
}
@@ -0,0 +1,140 @@
/*
* Copyright 2019 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
*
*/
#ifndef MOD_HDCP_LOG_H_
#define MOD_HDCP_LOG_H_
#define HDCP_LOG_ERR(hdcp, ...) DRM_DEBUG_KMS(__VA_ARGS__)
#define HDCP_LOG_VER(hdcp, ...) DRM_DEBUG_KMS(__VA_ARGS__)
#define HDCP_LOG_FSM(hdcp, ...) DRM_DEBUG_KMS(__VA_ARGS__)
#define HDCP_LOG_TOP(hdcp, ...) pr_debug("[HDCP_TOP]:"__VA_ARGS__)
#define HDCP_LOG_DDC(hdcp, ...) pr_debug("[HDCP_DDC]:"__VA_ARGS__)
#define HDCP_LOG_TRA(hdcp) do {} while (0)
/* default logs */
#define HDCP_ERROR_TRACE(hdcp, status) \
HDCP_LOG_ERR(hdcp, \
"[Link %d] WARNING %s IN STATE %s STAY COUNT %d", \
hdcp->config.index, \
mod_hdcp_status_to_str(status), \
mod_hdcp_state_id_to_str(hdcp->state.id), \
hdcp->state.stay_count)
#define HDCP_HDCP1_ENABLED_TRACE(hdcp, displayIndex) \
HDCP_LOG_VER(hdcp, \
"[Link %d] HDCP 1.4 enabled on display %d", \
hdcp->config.index, displayIndex)
#define HDCP_HDCP2_ENABLED_TRACE(hdcp, displayIndex) \
HDCP_LOG_VER(hdcp, \
"[Link %d] HDCP 2.2 enabled on display %d", \
hdcp->config.index, displayIndex)
#define HDCP_HDCP1_DISABLED_TRACE(hdcp, displayIndex) \
HDCP_LOG_VER(hdcp, \
"[Link %d] HDCP 1.4 disabled on display %d", \
hdcp->config.index, displayIndex)
#define HDCP_HDCP2_DISABLED_TRACE(hdcp, displayIndex) \
HDCP_LOG_VER(hdcp, \
"[Link %d] HDCP 2.2 disabled on display %d", \
hdcp->config.index, displayIndex)
/* state machine logs */
#define HDCP_REMOVE_DISPLAY_TRACE(hdcp, displayIndex) \
HDCP_LOG_FSM(hdcp, \
"[Link %d] HDCP_REMOVE_DISPLAY index %d", \
hdcp->config.index, displayIndex)
#define HDCP_INPUT_PASS_TRACE(hdcp, str) \
HDCP_LOG_FSM(hdcp, \
"[Link %d]\tPASS %s", \
hdcp->config.index, str)
#define HDCP_INPUT_FAIL_TRACE(hdcp, str) \
HDCP_LOG_FSM(hdcp, \
"[Link %d]\tFAIL %s", \
hdcp->config.index, str)
#define HDCP_NEXT_STATE_TRACE(hdcp, id, output) do { \
if (output->watchdog_timer_needed) \
HDCP_LOG_FSM(hdcp, \
"[Link %d] > %s with %d ms watchdog", \
hdcp->config.index, \
mod_hdcp_state_id_to_str(id), output->watchdog_timer_delay); \
else \
HDCP_LOG_FSM(hdcp, \
"[Link %d] > %s", hdcp->config.index, \
mod_hdcp_state_id_to_str(id)); \
} while (0)
#define HDCP_TIMEOUT_TRACE(hdcp) \
HDCP_LOG_FSM(hdcp, "[Link %d] --> TIMEOUT", hdcp->config.index)
#define HDCP_CPIRQ_TRACE(hdcp) \
HDCP_LOG_FSM(hdcp, "[Link %d] --> CPIRQ", hdcp->config.index)
#define HDCP_EVENT_TRACE(hdcp, event) \
do { \
if (event == MOD_HDCP_EVENT_WATCHDOG_TIMEOUT) \
HDCP_TIMEOUT_TRACE(hdcp); \
else if (event == MOD_HDCP_EVENT_CPIRQ) \
HDCP_CPIRQ_TRACE(hdcp); \
} while (0)
/* TODO: find some way to tell if logging is off to save time */
#define HDCP_DDC_READ_TRACE(hdcp, msg_name, msg, msg_size) do { \
mod_hdcp_dump_binary_message(msg, msg_size, hdcp->buf, \
sizeof(hdcp->buf)); \
HDCP_LOG_DDC(hdcp, "[Link %d] Read %s%s", hdcp->config.index, \
msg_name, hdcp->buf); \
} while (0)
#define HDCP_DDC_WRITE_TRACE(hdcp, msg_name, msg, msg_size) do { \
mod_hdcp_dump_binary_message(msg, msg_size, hdcp->buf, \
sizeof(hdcp->buf)); \
HDCP_LOG_DDC(hdcp, "[Link %d] Write %s%s", \
hdcp->config.index, msg_name,\
hdcp->buf); \
} while (0)
#define HDCP_TOP_ADD_DISPLAY_TRACE(hdcp, i) \
HDCP_LOG_TOP(hdcp, "[Link %d]\tadd display %d", \
hdcp->config.index, i)
#define HDCP_TOP_REMOVE_DISPLAY_TRACE(hdcp, i) \
HDCP_LOG_TOP(hdcp, "[Link %d]\tremove display %d", \
hdcp->config.index, i)
#define HDCP_TOP_HDCP1_DESTROY_SESSION_TRACE(hdcp) \
HDCP_LOG_TOP(hdcp, "[Link %d]\tdestroy hdcp1 session", \
hdcp->config.index)
#define HDCP_TOP_HDCP2_DESTROY_SESSION_TRACE(hdcp) \
HDCP_LOG_TOP(hdcp, "[Link %d]\tdestroy hdcp2 session", \
hdcp->config.index)
#define HDCP_TOP_RESET_AUTH_TRACE(hdcp) \
HDCP_LOG_TOP(hdcp, "[Link %d]\treset authentication", hdcp->config.index)
#define HDCP_TOP_RESET_CONN_TRACE(hdcp) \
HDCP_LOG_TOP(hdcp, "[Link %d]\treset connection", hdcp->config.index)
#define HDCP_TOP_INTERFACE_TRACE(hdcp) do { \
HDCP_LOG_TOP(hdcp, "\n"); \
HDCP_LOG_TOP(hdcp, "[Link %d] %s", hdcp->config.index, __func__); \
} while (0)
#define HDCP_TOP_INTERFACE_TRACE_WITH_INDEX(hdcp, i) do { \
HDCP_LOG_TOP(hdcp, "\n"); \
HDCP_LOG_TOP(hdcp, "[Link %d] %s display %d", hdcp->config.index, __func__, i); \
} while (0)
#define HDCP_AUTH_COMPLETE_TRACE(hdcp) do { \
mod_hdcp_log_ddc_trace(hdcp); \
HDCP_LOG_TRA(hdcp); \
} while (0)
#endif // MOD_HDCP_LOG_H_
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,534 @@
/*
* Copyright 2019 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
*
*/
#ifndef MODULES_HDCP_HDCP_PSP_H_
#define MODULES_HDCP_HDCP_PSP_H_
/*
* NOTE: These parameters are a one-to-one copy of the
* parameters required by PSP
*/
enum bgd_security_hdcp_encryption_level {
HDCP_ENCRYPTION_LEVEL__INVALID = 0,
HDCP_ENCRYPTION_LEVEL__OFF,
HDCP_ENCRYPTION_LEVEL__ON
};
enum bgd_security_hdcp2_content_type {
HDCP2_CONTENT_TYPE__INVALID = 0,
HDCP2_CONTENT_TYPE__TYPE0,
HDCP2_CONTENT_TYPE__TYPE1
};
enum ta_dtm_command {
TA_DTM_COMMAND__UNUSED_1 = 1,
TA_DTM_COMMAND__TOPOLOGY_UPDATE_V2,
TA_DTM_COMMAND__TOPOLOGY_ASSR_ENABLE,
TA_DTM_COMMAND__TOPOLOGY_UPDATE_V3
};
/* DTM related enumerations */
/**********************************************************/
enum ta_dtm_status {
TA_DTM_STATUS__SUCCESS = 0x00,
TA_DTM_STATUS__GENERIC_FAILURE = 0x01,
TA_DTM_STATUS__INVALID_PARAMETER = 0x02,
TA_DTM_STATUS__NULL_POINTER = 0x3
};
/* input/output structures for DTM commands */
/**********************************************************/
/**
* Input structures
*/
enum ta_dtm_hdcp_version_max_supported {
TA_DTM_HDCP_VERSION_MAX_SUPPORTED__NONE = 0,
TA_DTM_HDCP_VERSION_MAX_SUPPORTED__1_x = 10,
TA_DTM_HDCP_VERSION_MAX_SUPPORTED__2_0 = 20,
TA_DTM_HDCP_VERSION_MAX_SUPPORTED__2_1 = 21,
TA_DTM_HDCP_VERSION_MAX_SUPPORTED__2_2 = 22,
TA_DTM_HDCP_VERSION_MAX_SUPPORTED__2_3 = 23
};
struct ta_dtm_topology_update_input_v2 {
/* display handle is unique across the driver and is used to identify a display */
/* for all security interfaces which reference displays such as HDCP */
uint32_t display_handle;
uint32_t is_active;
uint32_t is_miracast;
uint32_t controller;
uint32_t ddc_line;
uint32_t dig_be;
uint32_t dig_fe;
uint32_t dp_mst_vcid;
uint32_t is_assr;
/*uint32_t is_frl;*/ /*todo*/
uint32_t max_hdcp_supported_version;
};
/* For security reason/HW may change value, these encoder type enum values are not HW register values */
/* Security code will check real HW register values and these SW enum values */
enum ta_dtm_encoder_type {
TA_DTM_ENCODER_TYPE__INVALID = 0,
TA_DTM_ENCODER_TYPE__FRL = 0x20,
TA_DTM_ENCODER_TYPE__DIG = 0x10
};
/* @enum ta_dtm_dio_output_type
* This enum defines software value for dio_output_type
*/
typedef enum {
TA_DTM_DIO_OUTPUT_TYPE__INVALID,
TA_DTM_DIO_OUTPUT_TYPE__DIRECT,
TA_DTM_DIO_OUTPUT_TYPE__DPIA
} ta_dtm_dio_output_type;
struct ta_dtm_topology_update_input_v3 {
/* display handle is unique across the driver and is used to identify a display */
/* for all security interfaces which reference displays such as HDCP */
/* link_hdcp_cap means link is HDCP-capable for audio HDCP capable property(informational), not for other logic(e.g. Crossbar) */
uint32_t display_handle;
uint32_t is_active;
uint32_t is_miracast;
uint32_t controller;
uint32_t ddc_line;
uint32_t link_enc;
uint32_t stream_enc;
uint32_t dp_mst_vcid;
uint32_t is_assr;
uint32_t max_hdcp_supported_version;
enum ta_dtm_encoder_type encoder_type;
uint32_t phy_id;
uint32_t link_hdcp_cap;
ta_dtm_dio_output_type dio_output_type;
uint32_t dio_output_id;
};
struct ta_dtm_topology_assr_enable {
uint32_t display_topology_dig_be_index;
};
/**
* Output structures
*/
/* No output structures yet */
union ta_dtm_cmd_input {
struct ta_dtm_topology_update_input_v2 topology_update_v2;
struct ta_dtm_topology_assr_enable topology_assr_enable;
struct ta_dtm_topology_update_input_v3 topology_update_v3;
};
union ta_dtm_cmd_output {
uint32_t reserved;
};
struct ta_dtm_shared_memory {
uint32_t cmd_id;
uint32_t resp_id;
enum ta_dtm_status dtm_status;
uint32_t reserved;
union ta_dtm_cmd_input dtm_in_message;
union ta_dtm_cmd_output dtm_out_message;
};
int psp_cmd_submit_buf(struct psp_context *psp, struct amdgpu_firmware_info *ucode, struct psp_gfx_cmd_resp *cmd,
uint64_t fence_mc_addr);
enum { PSP_HDCP_SRM_FIRST_GEN_MAX_SIZE = 5120 };
enum ta_hdcp_command {
TA_HDCP_COMMAND__INITIALIZE,
TA_HDCP_COMMAND__HDCP1_CREATE_SESSION,
TA_HDCP_COMMAND__HDCP1_DESTROY_SESSION,
TA_HDCP_COMMAND__HDCP1_FIRST_PART_AUTHENTICATION,
TA_HDCP_COMMAND__HDCP1_SECOND_PART_AUTHENTICATION,
TA_HDCP_COMMAND__HDCP1_ENABLE_ENCRYPTION,
TA_HDCP_COMMAND__HDCP1_ENABLE_DP_STREAM_ENCRYPTION,
TA_HDCP_COMMAND__HDCP1_GET_ENCRYPTION_STATUS,
TA_HDCP_COMMAND__UNUSED_1,
TA_HDCP_COMMAND__HDCP2_DESTROY_SESSION,
TA_HDCP_COMMAND__UNUSED_2,
TA_HDCP_COMMAND__HDCP2_SET_ENCRYPTION,
TA_HDCP_COMMAND__HDCP2_GET_ENCRYPTION_STATUS,
TA_HDCP_COMMAND__UNUSED_3,
TA_HDCP_COMMAND__HDCP2_CREATE_SESSION_V2,
TA_HDCP_COMMAND__HDCP2_PREPARE_PROCESS_AUTHENTICATION_MSG_V2,
TA_HDCP_COMMAND__HDCP2_ENABLE_DP_STREAM_ENCRYPTION,
TA_HDCP_COMMAND__HDCP_DESTROY_ALL_SESSIONS,
TA_HDCP_COMMAND__HDCP_SET_SRM,
TA_HDCP_COMMAND__HDCP_GET_SRM
};
enum ta_hdcp2_msg_id {
TA_HDCP_HDCP2_MSG_ID__NULL_MESSAGE = 1,
TA_HDCP_HDCP2_MSG_ID__AKE_INIT = 2,
TA_HDCP_HDCP2_MSG_ID__AKE_SEND_CERT = 3,
TA_HDCP_HDCP2_MSG_ID__AKE_NO_STORED_KM = 4,
TA_HDCP_HDCP2_MSG_ID__AKE_STORED_KM = 5,
TA_HDCP_HDCP2_MSG_ID__AKE_SEND_RRX = 6,
TA_HDCP_HDCP2_MSG_ID__AKE_SEND_H_PRIME = 7,
TA_HDCP_HDCP2_MSG_ID__AKE_SEND_PAIRING_INFO = 8,
TA_HDCP_HDCP2_MSG_ID__LC_INIT = 9,
TA_HDCP_HDCP2_MSG_ID__LC_SEND_L_PRIME = 10,
TA_HDCP_HDCP2_MSG_ID__SKE_SEND_EKS = 11,
TA_HDCP_HDCP2_MSG_ID__REPEATERAUTH_SEND_RECEIVERID_LIST = 12,
TA_HDCP_HDCP2_MSG_ID__RTT_READY = 13,
TA_HDCP_HDCP2_MSG_ID__RTT_CHALLENGE = 14,
TA_HDCP_HDCP2_MSG_ID__REPEATERAUTH_SEND_ACK = 15,
TA_HDCP_HDCP2_MSG_ID__REPEATERAUTH_STREAM_MANAGE = 16,
TA_HDCP_HDCP2_MSG_ID__REPEATERAUTH_STREAM_READY = 17,
TA_HDCP_HDCP2_MSG_ID__RECEIVER_AUTH_STATUS = 18,
TA_HDCP_HDCP2_MSG_ID__AKE_TRANSMITTER_INFO = 19,
TA_HDCP_HDCP2_MSG_ID__AKE_RECEIVER_INFO = 20,
TA_HDCP_HDCP2_MSG_ID__SIGNAL_CONTENT_STREAM_TYPE_DP = 129
};
enum ta_hdcp2_hdcp2_msg_id_max_size {
TA_HDCP_HDCP2_MSG_ID_MAX_SIZE__NULL_MESSAGE = 0,
TA_HDCP_HDCP2_MSG_ID_MAX_SIZE__AKE_INIT = 12,
TA_HDCP_HDCP2_MSG_ID_MAX_SIZE__AKE_SEND_CERT = 534,
TA_HDCP_HDCP2_MSG_ID_MAX_SIZE__AKE_NO_STORED_KM = 129,
TA_HDCP_HDCP2_MSG_ID_MAX_SIZE__AKE_STORED_KM = 33,
TA_HDCP_HDCP2_MSG_ID_MAX_SIZE__AKE_SEND_RRX = 9,
TA_HDCP_HDCP2_MSG_ID_MAX_SIZE__AKE_SEND_H_PRIME = 33,
TA_HDCP_HDCP2_MSG_ID_MAX_SIZE__AKE_SEND_PAIRING_INFO = 17,
TA_HDCP_HDCP2_MSG_ID_MAX_SIZE__LC_INIT = 9,
TA_HDCP_HDCP2_MSG_ID_MAX_SIZE__LC_SEND_L_PRIME = 33,
TA_HDCP_HDCP2_MSG_ID_MAX_SIZE__SKE_SEND_EKS = 25,
TA_HDCP_HDCP2_MSG_ID_MAX_SIZE__REPEATERAUTH_SEND_RECEIVERID_LIST = 181,
TA_HDCP_HDCP2_MSG_ID_MAX_SIZE__RTT_READY = 1,
TA_HDCP_HDCP2_MSG_ID_MAX_SIZE__RTT_CHALLENGE = 17,
TA_HDCP_HDCP2_MSG_ID_MAX_SIZE__REPEATERAUTH_SEND_RACK = 17,
TA_HDCP_HDCP2_MSG_ID_MAX_SIZE__REPEATERAUTH_STREAM_MANAGE = 13,
TA_HDCP_HDCP2_MSG_ID_MAX_SIZE__REPEATERAUTH_STREAM_READY = 33,
TA_HDCP_HDCP2_MSG_ID_MAX_SIZE__RECEIVER_AUTH_STATUS = 4,
TA_HDCP_HDCP2_MSG_ID_MAX_SIZE__AKE_TRANSMITTER_INFO = 6,
TA_HDCP_HDCP2_MSG_ID_MAX_SIZE__AKE_RECEIVER_INFO = 6,
TA_HDCP_HDCP2_MSG_ID_MAX_SIZE__SIGNAL_CONTENT_STREAM_TYPE_DP = 1
};
/* HDCP related enumerations */
/**********************************************************/
#define TA_HDCP__INVALID_SESSION 0xFFFF
#define TA_HDCP__HDCP1_AN_SIZE 8
#define TA_HDCP__HDCP1_KSV_SIZE 5
#define TA_HDCP__HDCP1_KSV_LIST_MAX_ENTRIES 127
#define TA_HDCP__HDCP1_V_PRIME_SIZE 20
#define TA_HDCP__HDCP2_TX_BUF_MAX_SIZE \
(TA_HDCP_HDCP2_MSG_ID_MAX_SIZE__AKE_NO_STORED_KM + TA_HDCP_HDCP2_MSG_ID_MAX_SIZE__AKE_STORED_KM + 6)
// 64 bits boundaries
#define TA_HDCP__HDCP2_RX_BUF_MAX_SIZE \
(TA_HDCP_HDCP2_MSG_ID_MAX_SIZE__AKE_SEND_CERT + TA_HDCP_HDCP2_MSG_ID_MAX_SIZE__AKE_RECEIVER_INFO + 4)
enum ta_hdcp_status {
TA_HDCP_STATUS__SUCCESS = 0x00,
TA_HDCP_STATUS__GENERIC_FAILURE = 0x01,
TA_HDCP_STATUS__NULL_POINTER = 0x02,
TA_HDCP_STATUS__FAILED_ALLOCATING_SESSION = 0x03,
TA_HDCP_STATUS__FAILED_SETUP_TX = 0x04,
TA_HDCP_STATUS__INVALID_PARAMETER = 0x05,
TA_HDCP_STATUS__VHX_ERROR = 0x06,
TA_HDCP_STATUS__SESSION_NOT_CLOSED_PROPERLY = 0x07,
TA_HDCP_STATUS__SRM_FAILURE = 0x08,
TA_HDCP_STATUS__MST_AUTHENTICATED_ALREADY_STARTED = 0x09,
TA_HDCP_STATUS__AKE_SEND_CERT_FAILURE = 0x0A,
TA_HDCP_STATUS__AKE_NO_STORED_KM_FAILURE = 0x0B,
TA_HDCP_STATUS__AKE_SEND_HPRIME_FAILURE = 0x0C,
TA_HDCP_STATUS__LC_SEND_LPRIME_FAILURE = 0x0D,
TA_HDCP_STATUS__SKE_SEND_EKS_FAILURE = 0x0E,
TA_HDCP_STATUS__REPAUTH_SEND_RXIDLIST_FAILURE = 0x0F,
TA_HDCP_STATUS__REPAUTH_STREAM_READY_FAILURE = 0x10,
TA_HDCP_STATUS__ASD_GENERIC_FAILURE = 0x11,
TA_HDCP_STATUS__UNWRAP_SECRET_FAILURE = 0x12,
TA_HDCP_STATUS__ENABLE_ENCR_FAILURE = 0x13,
TA_HDCP_STATUS__DISABLE_ENCR_FAILURE = 0x14,
TA_HDCP_STATUS__NOT_ENOUGH_MEMORY_FAILURE = 0x15,
TA_HDCP_STATUS__UNKNOWN_MESSAGE = 0x16,
TA_HDCP_STATUS__TOO_MANY_STREAM = 0x17
};
enum ta_hdcp_authentication_status {
TA_HDCP_AUTHENTICATION_STATUS__NOT_STARTED = 0x00,
TA_HDCP_AUTHENTICATION_STATUS__HDCP1_FIRST_PART_FAILED = 0x01,
TA_HDCP_AUTHENTICATION_STATUS__HDCP1_FIRST_PART_COMPLETE = 0x02,
TA_HDCP_AUTHENTICATION_STATUS__HDCP1_SECOND_PART_FAILED = 0x03,
TA_HDCP_AUTHENTICATION_STATUS__HDCP1_AUTHENTICATED = 0x04,
TA_HDCP_AUTHENTICATION_STATUS__HDCP22_AUTHENTICATION_PENDING = 0x06,
TA_HDCP_AUTHENTICATION_STATUS__HDCP22_AUTHENTICATION_FAILED = 0x07,
TA_HDCP_AUTHENTICATION_STATUS__HDCP22_AUTHENTICATED = 0x08,
TA_HDCP_AUTHENTICATION_STATUS__HDCP1_KSV_VALIDATION_FAILED = 0x09,
TA_HDCP_AUTHENTICATION_STATUS__HDCP1_KSV_REVOKED = 0x0A
};
enum ta_hdcp2_msg_authentication_status {
TA_HDCP2_MSG_AUTHENTICATION_STATUS__SUCCESS = 0,
TA_HDCP2_MSG_AUTHENTICATION_STATUS__KM_NOT_AVAILABLE,
TA_HDCP2_MSG_AUTHENTICATION_STATUS__UNUSED,
TA_HDCP2_MSG_AUTHENTICATION_STATUS__INVALID = 100, // everything above does not fail the request
TA_HDCP2_MSG_AUTHENTICATION_STATUS__NOT_ENOUGH_MEMORY,
TA_HDCP2_MSG_AUTHENTICATION_STATUS__NOT_EXPECTED_MSG,
TA_HDCP2_MSG_AUTHENTICATION_STATUS__SIGNATURE_CERTIFICAT_ERROR,
TA_HDCP2_MSG_AUTHENTICATION_STATUS__INCORRECT_HDCP_VERSION,
TA_HDCP2_MSG_AUTHENTICATION_STATUS__UNKNOWN_MESSAGE,
TA_HDCP2_MSG_AUTHENTICATION_STATUS__INVALID_HMAC,
TA_HDCP2_MSG_AUTHENTICATION_STATUS__INVALID_TOPOLOGY,
TA_HDCP2_MSG_AUTHENTICATION_STATUS__INVALID_SEQ_NUM,
TA_HDCP2_MSG_AUTHENTICATION_STATUS__INVALID_SIZE,
TA_HDCP2_MSG_AUTHENTICATION_STATUS__INVALID_LENGTH,
TA_HDCP2_MSG_AUTHENTICATION_STATUS__REAUTH_REQUEST,
TA_HDCP2_MSG_AUTHENTICATION_STATUS__RECEIVERID_REVOKED
};
enum ta_hdcp_content_type {
TA_HDCP2_CONTENT_TYPE__TYPE0 = 1,
TA_HDCP2_CONTENT_TYPE__TYPE1,
};
enum ta_hdcp_content_type_negotiation_type {
TA_HDCP2_CONTENT_TYPE_NEGOTIATION_TYPE__FORCE_TYPE0 = 1,
TA_HDCP2_CONTENT_TYPE_NEGOTIATION_TYPE__FORCE_TYPE1,
TA_HDCP2_CONTENT_TYPE_NEGOTIATION_TYPE__MAX_SUPPORTED
};
enum ta_hdcp2_version {
TA_HDCP2_VERSION_UNKNOWN = 0,
TA_HDCP2_VERSION_2_0 = 20,
TA_HDCP2_VERSION_2_1 = 21,
TA_HDCP2_VERSION_2_2 = 22,
TA_HDCP2_VERSION_2_3 = 23,
};
/* input/output structures for HDCP commands */
/**********************************************************/
struct ta_hdcp_cmd_hdcp1_create_session_input {
uint8_t display_handle;
};
struct ta_hdcp_cmd_hdcp1_create_session_output {
uint32_t session_handle;
uint8_t an_primary[TA_HDCP__HDCP1_AN_SIZE];
uint8_t aksv_primary[TA_HDCP__HDCP1_KSV_SIZE];
uint8_t ainfo_primary;
uint8_t an_secondary[TA_HDCP__HDCP1_AN_SIZE];
uint8_t aksv_secondary[TA_HDCP__HDCP1_KSV_SIZE];
uint8_t ainfo_secondary;
};
struct ta_hdcp_cmd_hdcp1_destroy_session_input {
uint32_t session_handle;
};
struct ta_hdcp_cmd_hdcp1_first_part_authentication_input {
uint32_t session_handle;
uint8_t bksv_primary[TA_HDCP__HDCP1_KSV_SIZE];
uint8_t bksv_secondary[TA_HDCP__HDCP1_KSV_SIZE];
uint8_t bcaps;
uint16_t r0_prime_primary;
uint16_t r0_prime_secondary;
};
struct ta_hdcp_cmd_hdcp1_first_part_authentication_output {
enum ta_hdcp_authentication_status authentication_status;
};
struct ta_hdcp_cmd_hdcp1_second_part_authentication_input {
uint32_t session_handle;
uint16_t bstatus_binfo;
uint8_t ksv_list[TA_HDCP__HDCP1_KSV_LIST_MAX_ENTRIES][TA_HDCP__HDCP1_KSV_SIZE];
uint32_t ksv_list_size;
uint8_t pj_prime;
uint8_t v_prime[TA_HDCP__HDCP1_V_PRIME_SIZE];
};
struct ta_hdcp_cmd_hdcp1_second_part_authentication_output {
enum ta_hdcp_authentication_status authentication_status;
};
struct ta_hdcp_cmd_hdcp1_enable_encryption_input {
uint32_t session_handle;
};
struct ta_hdcp_cmd_hdcp1_enable_dp_stream_encryption_input {
uint32_t session_handle;
uint32_t display_handle;
};
struct ta_hdcp_cmd_hdcp1_get_encryption_status_input {
uint32_t session_handle;
};
struct ta_hdcp_cmd_hdcp1_get_encryption_status_output {
uint32_t protection_level;
};
struct ta_hdcp_cmd_hdcp2_create_session_input_v2 {
uint32_t display_handle;
enum ta_hdcp_content_type_negotiation_type negotiate_content_type;
};
struct ta_hdcp_cmd_hdcp2_create_session_output_v2 {
uint32_t session_handle;
};
struct ta_hdcp_cmd_hdcp2_destroy_session_input {
uint32_t session_handle;
};
struct ta_hdcp_cmd_hdcp2_authentication_message_v2 {
enum ta_hdcp2_msg_id msg_id;
uint32_t msg_size;
};
struct ta_hdcp_cmd_hdcp2_process_authentication_message_input_v2 {
struct ta_hdcp_cmd_hdcp2_authentication_message_v2 msg1_desc;
struct ta_hdcp_cmd_hdcp2_authentication_message_v2 msg2_desc;
struct ta_hdcp_cmd_hdcp2_authentication_message_v2 msg3_desc;
uint8_t receiver_message[TA_HDCP__HDCP2_RX_BUF_MAX_SIZE];
};
struct ta_hdcp_cmd_hdcp2_process_authentication_message_output_v2 {
uint32_t hdcp_version;
uint32_t is_km_stored;
uint32_t is_locality_precompute_support;
uint32_t is_repeater;
enum ta_hdcp2_msg_authentication_status msg1_status;
enum ta_hdcp2_msg_authentication_status msg2_status;
enum ta_hdcp2_msg_authentication_status msg3_status;
};
struct ta_hdcp_cmd_hdcp2_prepare_authentication_message_input_v2 {
enum ta_hdcp2_msg_id msg1_id;
enum ta_hdcp2_msg_id msg2_id;
};
struct ta_hdcp_cmd_hdcp2_prepare_authentication_message_output_v2 {
enum ta_hdcp2_msg_authentication_status msg1_status;
enum ta_hdcp2_msg_authentication_status msg2_status;
struct ta_hdcp_cmd_hdcp2_authentication_message_v2 msg1_desc;
struct ta_hdcp_cmd_hdcp2_authentication_message_v2 msg2_desc;
uint8_t transmitter_message[TA_HDCP__HDCP2_TX_BUF_MAX_SIZE];
};
struct ta_hdcp_cmd_hdcp2_process_prepare_authentication_message_input_v2 {
uint32_t session_handle;
struct ta_hdcp_cmd_hdcp2_process_authentication_message_input_v2 process;
struct ta_hdcp_cmd_hdcp2_prepare_authentication_message_input_v2 prepare;
};
struct ta_hdcp_cmd_hdcp2_process_prepare_authentication_message_output_v2 {
uint32_t authentication_status;
struct ta_hdcp_cmd_hdcp2_process_authentication_message_output_v2 process;
struct ta_hdcp_cmd_hdcp2_prepare_authentication_message_output_v2 prepare;
};
struct ta_hdcp_cmd_hdcp2_set_encryption_input {
uint32_t session_handle;
};
struct ta_hdcp_cmd_hdcp2_get_encryption_status_input {
uint32_t session_handle;
};
struct ta_hdcp_cmd_hdcp2_get_encryption_status_output {
enum ta_hdcp_content_type hdcp2_type;
uint32_t protection_level;
};
struct ta_hdcp_cmd_hdcp2_enable_dp_stream_encryption_input {
uint32_t session_handle;
uint32_t display_handle;
};
struct ta_hdcp_cmd_set_srm_input {
uint32_t srm_buf_size;
uint8_t srm_buf[PSP_HDCP_SRM_FIRST_GEN_MAX_SIZE];
};
struct ta_hdcp_cmd_set_srm_output {
uint8_t valid_signature;
uint32_t srm_version;
};
struct ta_hdcp_cmd_get_srm_output {
uint32_t srm_version;
uint32_t srm_buf_size;
uint8_t srm_buf[PSP_HDCP_SRM_FIRST_GEN_MAX_SIZE];
};
/**********************************************************/
/* Common input structure for HDCP callbacks */
union ta_hdcp_cmd_input {
struct ta_hdcp_cmd_hdcp1_create_session_input hdcp1_create_session;
struct ta_hdcp_cmd_hdcp1_destroy_session_input hdcp1_destroy_session;
struct ta_hdcp_cmd_hdcp1_first_part_authentication_input hdcp1_first_part_authentication;
struct ta_hdcp_cmd_hdcp1_second_part_authentication_input hdcp1_second_part_authentication;
struct ta_hdcp_cmd_hdcp1_enable_encryption_input hdcp1_enable_encryption;
struct ta_hdcp_cmd_hdcp1_enable_dp_stream_encryption_input hdcp1_enable_dp_stream_encryption;
struct ta_hdcp_cmd_hdcp1_get_encryption_status_input hdcp1_get_encryption_status;
struct ta_hdcp_cmd_hdcp2_destroy_session_input hdcp2_destroy_session;
struct ta_hdcp_cmd_hdcp2_set_encryption_input hdcp2_set_encryption;
struct ta_hdcp_cmd_hdcp2_get_encryption_status_input hdcp2_get_encryption_status;
struct ta_hdcp_cmd_hdcp2_create_session_input_v2 hdcp2_create_session_v2;
struct ta_hdcp_cmd_hdcp2_process_prepare_authentication_message_input_v2
hdcp2_prepare_process_authentication_message_v2;
struct ta_hdcp_cmd_hdcp2_enable_dp_stream_encryption_input hdcp2_enable_dp_stream_encryption;
struct ta_hdcp_cmd_set_srm_input hdcp_set_srm;
};
/* Common output structure for HDCP callbacks */
union ta_hdcp_cmd_output {
struct ta_hdcp_cmd_hdcp1_create_session_output hdcp1_create_session;
struct ta_hdcp_cmd_hdcp1_first_part_authentication_output hdcp1_first_part_authentication;
struct ta_hdcp_cmd_hdcp1_second_part_authentication_output hdcp1_second_part_authentication;
struct ta_hdcp_cmd_hdcp1_get_encryption_status_output hdcp1_get_encryption_status;
struct ta_hdcp_cmd_hdcp2_get_encryption_status_output hdcp2_get_encryption_status;
struct ta_hdcp_cmd_hdcp2_create_session_output_v2 hdcp2_create_session_v2;
struct ta_hdcp_cmd_hdcp2_process_prepare_authentication_message_output_v2
hdcp2_prepare_process_authentication_message_v2;
struct ta_hdcp_cmd_set_srm_output hdcp_set_srm;
struct ta_hdcp_cmd_get_srm_output hdcp_get_srm;
};
/**********************************************************/
struct ta_hdcp_shared_memory {
uint32_t cmd_id;
enum ta_hdcp_status hdcp_status;
uint32_t reserved;
union ta_hdcp_cmd_input in_msg;
union ta_hdcp_cmd_output out_msg;
};
enum psp_status {
PSP_STATUS__SUCCESS = 0,
PSP_STATUS__ERROR_INVALID_PARAMS,
PSP_STATUS__ERROR_GENERIC,
PSP_STATUS__ERROR_OUT_OF_MEMORY,
PSP_STATUS__ERROR_UNSUPPORTED_FEATURE
};
#endif /* MODULES_HDCP_HDCP_PSP_H_ */
@@ -0,0 +1,47 @@
/*
* Copyright 2019 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
*
*/
#ifndef MOD_COLOR_TYPES_H_
#define MOD_COLOR_TYPES_H_
enum predefined_gamut_type {
gamut_type_bt709,
gamut_type_bt601,
gamut_type_adobe_rgb,
gamut_type_srgb,
gamut_type_bt2020,
gamut_type_dcip3,
gamut_type_unknown,
};
enum predefined_white_point_type {
white_point_type_5000k_horizon,
white_point_type_6500k_noon,
white_point_type_7500k_north_sky,
white_point_type_9300k,
white_point_type_unknown,
};
#endif /* MOD_COLOR_TYPES_H_ */
@@ -0,0 +1,149 @@
/*
* Copyright 2016 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
*
*/
#ifndef MOD_FREESYNC_H_
#define MOD_FREESYNC_H_
#include "mod_shared.h"
// Access structures
struct mod_freesync {
int dummy;
};
// TODO: References to this should be removed
struct mod_freesync_caps {
bool supported;
unsigned int min_refresh_in_micro_hz;
unsigned int max_refresh_in_micro_hz;
};
enum mod_vrr_state {
VRR_STATE_UNSUPPORTED = 0,
VRR_STATE_DISABLED,
VRR_STATE_INACTIVE,
VRR_STATE_ACTIVE_VARIABLE,
VRR_STATE_ACTIVE_FIXED
};
struct mod_freesync_config {
enum mod_vrr_state state;
bool vsif_supported;
bool ramping;
bool btr;
unsigned int min_refresh_in_uhz;
unsigned int max_refresh_in_uhz;
unsigned int fixed_refresh_in_uhz;
};
struct mod_vrr_params_btr {
bool btr_enabled;
bool btr_active;
uint32_t mid_point_in_us;
uint32_t inserted_duration_in_us;
uint32_t frames_to_insert;
uint32_t frame_counter;
uint32_t margin_in_us;
};
struct mod_vrr_params_fixed_refresh {
bool fixed_active;
bool ramping_active;
bool ramping_done;
uint32_t target_refresh_in_uhz;
uint32_t frame_counter;
};
struct mod_vrr_params_flip_interval {
bool flip_interval_workaround_active;
bool program_flip_interval_workaround;
bool do_flip_interval_workaround_cleanup;
uint32_t flip_interval_detect_counter;
uint32_t vsyncs_between_flip;
uint32_t vsync_to_flip_in_us;
uint32_t v_update_timestamp_in_us;
};
struct mod_vrr_params {
bool supported;
bool send_info_frame;
bool m_const_engaged; // this is used when m_const is set up in OPTC so no overriding happens from FreeSync Module
enum mod_vrr_state state;
uint32_t min_refresh_in_uhz;
uint32_t max_duration_in_us;
uint32_t max_refresh_in_uhz;
uint32_t min_duration_in_us;
uint32_t fixed_refresh_in_uhz;
uint32_t m_const;
struct dc_crtc_timing_adjust adjust;
struct mod_vrr_params_fixed_refresh fixed;
struct mod_vrr_params_btr btr;
struct mod_vrr_params_flip_interval flip_interval;
};
struct mod_freesync *mod_freesync_create(struct dc *dc);
void mod_freesync_destroy(struct mod_freesync *mod_freesync);
void mod_freesync_build_vrr_infopacket(struct mod_freesync *mod_freesync,
const struct dc_stream_state *stream,
const struct mod_vrr_params *vrr,
enum vrr_packet_type packet_type,
enum color_transfer_func app_tf,
struct dc_info_packet *infopacket,
bool pack_sdp_v1_3);
void mod_freesync_build_vrr_params(struct mod_freesync *mod_freesync,
const struct dc_stream_state *stream,
struct mod_freesync_config *in_config,
struct mod_vrr_params *in_out_vrr);
void mod_freesync_handle_preflip(struct mod_freesync *mod_freesync,
const struct dc_plane_state *plane,
const struct dc_stream_state *stream,
unsigned int curr_time_stamp_in_us,
struct mod_vrr_params *in_out_vrr);
void mod_freesync_handle_v_update(struct mod_freesync *mod_freesync,
const struct dc_stream_state *stream,
struct mod_vrr_params *in_out_vrr);
unsigned long long mod_freesync_calc_nominal_field_rate(
const struct dc_stream_state *stream);
unsigned int mod_freesync_calc_v_total_from_refresh(
const struct dc_stream_state *stream,
unsigned int refresh_in_uhz);
// Returns true when FreeSync is supported and enabled (even if it is inactive)
bool mod_freesync_get_freesync_enabled(struct mod_vrr_params *pVrr);
#endif
@@ -0,0 +1,377 @@
/*
* 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
*
*/
#ifndef MOD_HDCP_H_
#define MOD_HDCP_H_
#include "os_types.h"
#include "signal_types.h"
/* Forward Declarations */
struct mod_hdcp;
#define MAX_NUM_OF_DISPLAYS 6
#define MAX_NUM_OF_ATTEMPTS 4
#define MAX_NUM_OF_ERROR_TRACE 10
#define MOD_HDCP_STATUS_LIST(FORMAT) \
FORMAT(MOD_HDCP_STATUS_SUCCESS) \
FORMAT(MOD_HDCP_STATUS_FAILURE) \
FORMAT(MOD_HDCP_STATUS_RESET_NEEDED) \
FORMAT(MOD_HDCP_STATUS_DISPLAY_OUT_OF_BOUND) \
FORMAT(MOD_HDCP_STATUS_DISPLAY_NOT_FOUND) \
FORMAT(MOD_HDCP_STATUS_INVALID_STATE) \
FORMAT(MOD_HDCP_STATUS_NOT_IMPLEMENTED) \
FORMAT(MOD_HDCP_STATUS_INTERNAL_POLICY_FAILURE) \
FORMAT(MOD_HDCP_STATUS_UPDATE_TOPOLOGY_FAILURE) \
FORMAT(MOD_HDCP_STATUS_CREATE_PSP_SERVICE_FAILURE) \
FORMAT(MOD_HDCP_STATUS_DESTROY_PSP_SERVICE_FAILURE) \
FORMAT(MOD_HDCP_STATUS_HDCP1_CREATE_SESSION_FAILURE) \
FORMAT(MOD_HDCP_STATUS_HDCP1_DESTROY_SESSION_FAILURE) \
FORMAT(MOD_HDCP_STATUS_HDCP1_VALIDATE_ENCRYPTION_FAILURE) \
FORMAT(MOD_HDCP_STATUS_HDCP1_NOT_HDCP_REPEATER) \
FORMAT(MOD_HDCP_STATUS_HDCP1_NOT_CAPABLE) \
FORMAT(MOD_HDCP_STATUS_HDCP1_R0_PRIME_PENDING) \
FORMAT(MOD_HDCP_STATUS_HDCP1_VALIDATE_RX_FAILURE) \
FORMAT(MOD_HDCP_STATUS_HDCP1_BKSV_REVOKED) \
FORMAT(MOD_HDCP_STATUS_HDCP1_KSV_LIST_NOT_READY) \
FORMAT(MOD_HDCP_STATUS_HDCP1_VALIDATE_KSV_LIST_FAILURE) \
FORMAT(MOD_HDCP_STATUS_HDCP1_KSV_LIST_REVOKED) \
FORMAT(MOD_HDCP_STATUS_HDCP1_ENABLE_ENCRYPTION_FAILURE) \
FORMAT(MOD_HDCP_STATUS_HDCP1_ENABLE_STREAM_ENCRYPTION_FAILURE) \
FORMAT(MOD_HDCP_STATUS_HDCP1_MAX_CASCADE_EXCEEDED_FAILURE) \
FORMAT(MOD_HDCP_STATUS_HDCP1_MAX_DEVS_EXCEEDED_FAILURE) \
FORMAT(MOD_HDCP_STATUS_HDCP1_DEVICE_COUNT_MISMATCH_FAILURE) \
FORMAT(MOD_HDCP_STATUS_HDCP1_LINK_INTEGRITY_FAILURE) \
FORMAT(MOD_HDCP_STATUS_HDCP1_REAUTH_REQUEST_ISSUED) \
FORMAT(MOD_HDCP_STATUS_HDCP1_LINK_MAINTENANCE_FAILURE) \
FORMAT(MOD_HDCP_STATUS_HDCP1_INVALID_BKSV) \
FORMAT(MOD_HDCP_STATUS_DDC_FAILURE) /* TODO: specific errors */ \
FORMAT(MOD_HDCP_STATUS_INVALID_OPERATION) \
FORMAT(MOD_HDCP_STATUS_HDCP2_NOT_CAPABLE) \
FORMAT(MOD_HDCP_STATUS_HDCP2_CREATE_SESSION_FAILURE) \
FORMAT(MOD_HDCP_STATUS_HDCP2_DESTROY_SESSION_FAILURE) \
FORMAT(MOD_HDCP_STATUS_HDCP2_PREP_AKE_INIT_FAILURE) \
FORMAT(MOD_HDCP_STATUS_HDCP2_AKE_CERT_PENDING) \
FORMAT(MOD_HDCP_STATUS_HDCP2_H_PRIME_PENDING) \
FORMAT(MOD_HDCP_STATUS_HDCP2_PAIRING_INFO_PENDING) \
FORMAT(MOD_HDCP_STATUS_HDCP2_VALIDATE_AKE_CERT_FAILURE) \
FORMAT(MOD_HDCP_STATUS_HDCP2_AKE_CERT_REVOKED) \
FORMAT(MOD_HDCP_STATUS_HDCP2_VALIDATE_H_PRIME_FAILURE) \
FORMAT(MOD_HDCP_STATUS_HDCP2_VALIDATE_PAIRING_INFO_FAILURE) \
FORMAT(MOD_HDCP_STATUS_HDCP2_PREP_LC_INIT_FAILURE) \
FORMAT(MOD_HDCP_STATUS_HDCP2_L_PRIME_PENDING) \
FORMAT(MOD_HDCP_STATUS_HDCP2_VALIDATE_L_PRIME_FAILURE) \
FORMAT(MOD_HDCP_STATUS_HDCP2_PREP_EKS_FAILURE) \
FORMAT(MOD_HDCP_STATUS_HDCP2_ENABLE_ENCRYPTION_FAILURE) \
FORMAT(MOD_HDCP_STATUS_HDCP2_RX_ID_LIST_NOT_READY) \
FORMAT(MOD_HDCP_STATUS_HDCP2_VALIDATE_RX_ID_LIST_FAILURE) \
FORMAT(MOD_HDCP_STATUS_HDCP2_RX_ID_LIST_REVOKED) \
FORMAT(MOD_HDCP_STATUS_HDCP2_ENABLE_STREAM_ENCRYPTION_FAILURE) \
FORMAT(MOD_HDCP_STATUS_HDCP2_STREAM_READY_PENDING) \
FORMAT(MOD_HDCP_STATUS_HDCP2_VALIDATE_STREAM_READY_FAILURE) \
FORMAT(MOD_HDCP_STATUS_HDCP2_PREPARE_STREAM_MANAGEMENT_FAILURE) \
FORMAT(MOD_HDCP_STATUS_HDCP2_REAUTH_REQUEST) \
FORMAT(MOD_HDCP_STATUS_HDCP2_REAUTH_LINK_INTEGRITY_FAILURE) \
FORMAT(MOD_HDCP_STATUS_HDCP2_DEVICE_COUNT_MISMATCH_FAILURE) \
FORMAT(MOD_HDCP_STATUS_UNSUPPORTED_PSP_VER_FAILURE) \
FORMAT(MOD_HDCP_STATUS_HDCP2_LOCALITY_COMBO_READ_FAILURE)
#define ENUM_FORMAT(entry) entry,
/* detailed return status */
enum mod_hdcp_status {
MOD_HDCP_STATUS_LIST(ENUM_FORMAT)
};
struct mod_hdcp_displayport {
uint8_t rev;
uint8_t assr_enabled;
uint8_t mst_enabled;
uint8_t dp2_enabled;
uint8_t usb4_enabled;
};
struct mod_hdcp_hdmi {
uint8_t frl_enabled;
uint8_t reserved;
};
enum mod_hdcp_operation_mode {
MOD_HDCP_MODE_OFF,
MOD_HDCP_MODE_DEFAULT,
MOD_HDCP_MODE_DP
};
enum mod_hdcp_display_state {
MOD_HDCP_DISPLAY_INACTIVE = 0,
MOD_HDCP_DISPLAY_ACTIVE,
MOD_HDCP_DISPLAY_ENCRYPTION_ENABLED
};
struct mod_hdcp_psp_caps {
uint8_t dtm_v3_supported;
};
enum mod_hdcp_display_disable_option {
MOD_HDCP_DISPLAY_NOT_DISABLE = 0,
MOD_HDCP_DISPLAY_DISABLE_AUTHENTICATION,
MOD_HDCP_DISPLAY_DISABLE_ENCRYPTION,
};
struct mod_hdcp_atomic_op_i2c {
uint8_t address;
uint8_t offset;
uint8_t *data;
uint32_t size;
};
struct mod_hdcp_atomic_op_aux {
uint32_t address;
uint8_t *data;
uint32_t size;
};
struct mod_hdcp_ddc {
void *handle;
struct mod_hdcp_ddc_funcs {
bool (*read_i2c)(void *handle,
uint32_t address,
uint8_t offset,
uint8_t *data,
uint32_t size);
bool (*write_i2c)(void *handle,
uint32_t address,
const uint8_t *data,
uint32_t size);
bool (*read_dpcd)(void *handle,
uint32_t address,
uint8_t *data,
uint32_t size);
bool (*write_dpcd)(void *handle,
uint32_t address,
const uint8_t *data,
uint32_t size);
bool (*atomic_write_poll_read_i2c)(
void *handle,
const struct mod_hdcp_atomic_op_i2c *write,
const struct mod_hdcp_atomic_op_i2c *poll,
struct mod_hdcp_atomic_op_i2c *read,
uint32_t poll_timeout_us,
uint8_t poll_mask_msb
);
bool (*atomic_write_poll_read_aux)(
void *handle,
const struct mod_hdcp_atomic_op_aux *write,
const struct mod_hdcp_atomic_op_aux *poll,
struct mod_hdcp_atomic_op_aux *read,
uint32_t poll_timeout_us,
uint8_t poll_mask_msb
);
} funcs;
};
struct mod_hdcp_psp {
void *handle;
void *funcs;
struct mod_hdcp_psp_caps caps;
};
struct mod_hdcp_display_adjustment {
uint8_t disable : 2;
uint8_t reserved : 6;
};
struct mod_hdcp_link_adjustment_hdcp1 {
uint8_t disable : 1;
uint8_t postpone_encryption : 1;
uint8_t min_auth_retries_wa : 1;
uint8_t reserved : 5;
};
enum mod_hdcp_force_hdcp_type {
MOD_HDCP_FORCE_TYPE_MAX = 0,
MOD_HDCP_FORCE_TYPE_0,
MOD_HDCP_FORCE_TYPE_1
};
struct mod_hdcp_link_adjustment_hdcp2 {
uint8_t disable : 1;
uint8_t force_type : 2;
uint8_t force_no_stored_km : 1;
uint8_t increase_h_prime_timeout: 1;
uint8_t use_fw_locality_check : 1;
uint8_t use_sw_locality_fallback: 1;
uint8_t reserved : 1;
};
struct mod_hdcp_link_adjustment {
uint8_t auth_delay;
uint8_t retry_limit;
struct mod_hdcp_link_adjustment_hdcp1 hdcp1;
struct mod_hdcp_link_adjustment_hdcp2 hdcp2;
};
struct mod_hdcp_error {
enum mod_hdcp_status status;
uint8_t state_id;
};
struct mod_hdcp1_trace {
uint8_t attempt_count;
uint8_t downstream_device_count;
};
struct mod_hdcp2_trace {
uint8_t attempt_count;
uint8_t downstream_device_count;
uint8_t hdcp1_device_downstream;
uint8_t hdcp2_legacy_device_downstream;
};
struct mod_hdcp_trace {
struct mod_hdcp_error errors[MAX_NUM_OF_ERROR_TRACE];
uint8_t error_count;
struct mod_hdcp1_trace hdcp1;
struct mod_hdcp2_trace hdcp2;
};
enum mod_hdcp_encryption_status {
MOD_HDCP_ENCRYPTION_STATUS_HDCP_OFF = 0,
MOD_HDCP_ENCRYPTION_STATUS_HDCP1_ON,
MOD_HDCP_ENCRYPTION_STATUS_HDCP2_TYPE0_ON,
MOD_HDCP_ENCRYPTION_STATUS_HDCP2_TYPE1_ON,
MOD_HDCP_ENCRYPTION_STATUS_HDCP2_ON
};
/* per link events dm has to notify to hdcp module */
enum mod_hdcp_event {
MOD_HDCP_EVENT_CALLBACK = 0,
MOD_HDCP_EVENT_WATCHDOG_TIMEOUT,
MOD_HDCP_EVENT_CPIRQ
};
/* output flags from module requesting timer operations */
struct mod_hdcp_output {
uint8_t callback_needed;
uint8_t callback_stop;
uint8_t watchdog_timer_needed;
uint8_t watchdog_timer_stop;
uint16_t callback_delay;
uint16_t watchdog_timer_delay;
uint8_t auth_complete;
};
/* used to represent per display info */
struct mod_hdcp_display {
enum mod_hdcp_display_state state;
uint8_t index;
uint8_t controller;
uint8_t dig_fe;
uint8_t stream_enc_idx;
union {
uint8_t vc_id;
};
struct mod_hdcp_display_adjustment adjust;
};
/* used to represent per link info */
/* in case a link has multiple displays, they share the same link info */
struct mod_hdcp_link {
enum mod_hdcp_operation_mode mode;
uint8_t dig_be;
uint8_t ddc_line;
uint8_t link_enc_idx;
uint8_t phy_idx;
uint8_t dio_output_id;
uint8_t hdcp_supported_informational;
union {
struct mod_hdcp_displayport dp;
struct mod_hdcp_hdmi hdmi;
};
struct mod_hdcp_link_adjustment adjust;
};
/* a query structure for a display's hdcp information */
struct mod_hdcp_display_query {
const struct mod_hdcp_display *display;
const struct mod_hdcp_link *link;
const struct mod_hdcp_trace *trace;
enum mod_hdcp_encryption_status encryption_status;
};
/* contains values per on external display configuration change */
struct mod_hdcp_config {
struct mod_hdcp_psp psp;
struct mod_hdcp_ddc ddc;
uint8_t index;
};
/* dm allocates memory of mod_hdcp per dc_link on dm init based on memory size*/
size_t mod_hdcp_get_memory_size(void);
/* called per link on link creation */
enum mod_hdcp_status mod_hdcp_setup(struct mod_hdcp *hdcp,
struct mod_hdcp_config *config);
/* called per link on link destroy */
enum mod_hdcp_status mod_hdcp_teardown(struct mod_hdcp *hdcp);
/* called per display after stream is enabled */
enum mod_hdcp_status mod_hdcp_add_display(struct mod_hdcp *hdcp,
struct mod_hdcp_link *link, struct mod_hdcp_display *display,
struct mod_hdcp_output *output);
/* called per display before stream is disabled */
enum mod_hdcp_status mod_hdcp_remove_display(struct mod_hdcp *hdcp,
uint8_t index, struct mod_hdcp_output *output);
/* called per display to apply new authentication adjustment */
enum mod_hdcp_status mod_hdcp_update_display(struct mod_hdcp *hdcp,
uint8_t index,
struct mod_hdcp_link_adjustment *link_adjust,
struct mod_hdcp_display_adjustment *display_adjust,
struct mod_hdcp_output *output);
/* called to query hdcp information on a specific index */
enum mod_hdcp_status mod_hdcp_query_display(struct mod_hdcp *hdcp,
uint8_t index, struct mod_hdcp_display_query *query);
/* called per link on connectivity change */
enum mod_hdcp_status mod_hdcp_reset_connection(struct mod_hdcp *hdcp,
struct mod_hdcp_output *output);
/* called per link on events (i.e. callback, watchdog, CP_IRQ) */
enum mod_hdcp_status mod_hdcp_process_event(struct mod_hdcp *hdcp,
enum mod_hdcp_event event, struct mod_hdcp_output *output);
/* called to convert enum mod_hdcp_status to c string */
char *mod_hdcp_status_to_str(int32_t status);
/* called to convert state id to c string */
char *mod_hdcp_state_id_to_str(int32_t id);
/* called to convert signal type to operation mode */
enum mod_hdcp_operation_mode mod_hdcp_signal_type_to_operation_mode(
enum signal_type signal);
#endif /* MOD_HDCP_H_ */
@@ -0,0 +1,79 @@
/*
* 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
*
*/
#ifndef MOD_INFO_PACKET_H_
#define MOD_INFO_PACKET_H_
#include "dm_services.h"
#include "mod_info_packet_types.h"
#include "mod_shared.h"
//Forward Declarations
struct dc_stream_state;
struct dc_info_packet;
struct mod_vrr_params;
void set_vsc_packet_colorimetry_data(
const struct dc_stream_state *stream,
struct dc_info_packet *info_packet,
enum dc_color_space cs,
enum color_transfer_func tf);
void mod_build_vsc_infopacket(const struct dc_stream_state *stream,
struct dc_info_packet *info_packet,
enum dc_color_space cs,
enum color_transfer_func tf);
void mod_build_hf_vsif_infopacket(const struct dc_stream_state *stream,
struct dc_info_packet *info_packet, int ALLMEnabled, int ALLMValue);
enum adaptive_sync_sdp_version {
AS_SDP_VER_0 = 0x0,
AS_SDP_VER_1 = 0x1,
AS_SDP_VER_2 = 0x2,
};
#define AS_DP_SDP_LENGTH (9)
struct frame_duration_op {
bool support;
unsigned char frame_duration_hex;
};
struct AS_Df_params {
bool supportMode;
struct frame_duration_op increase;
struct frame_duration_op decrease;
};
void mod_build_adaptive_sync_infopacket(const struct dc_stream_state *stream,
enum adaptive_sync_type asType, const struct AS_Df_params *param,
struct dc_info_packet *info_packet);
void mod_build_adaptive_sync_infopacket_v2(const struct dc_stream_state *stream,
const struct AS_Df_params *param, struct dc_info_packet *info_packet);
void mod_build_adaptive_sync_infopacket_v1(struct dc_info_packet *info_packet);
#endif
@@ -0,0 +1,37 @@
/*
* 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
*
*/
#ifndef MOD_INFO_PACKET_TYPES_H_
#define MOD_INFO_PACKET_TYPES_H_
enum adaptive_sync_type {
ADAPTIVE_SYNC_TYPE_NONE = 0,
ADAPTIVE_SYNC_TYPE_DP = 1,
FREESYNC_TYPE_PCON_IN_WHITELIST = 2,
FREESYNC_TYPE_PCON_NOT_IN_WHITELIST = 3,
ADAPTIVE_SYNC_TYPE_EDP = 4,
};
#endif /* MOD_INFO_PACKET_TYPES_H_ */
@@ -0,0 +1,417 @@
/* Copyright (c) 2019 Advanced Micro Devices, Inc. All rights reserved. */
#ifndef MODULES_INC_MOD_POWER_H_
#define MODULES_INC_MOD_POWER_H_
#include "dm_services.h"
struct core_power;
struct mod_power_init_params {
bool disable_fractional_pwm;
/* Use nits based brightness instead of brightness percentage
*/
bool use_nits_based_brightness;
unsigned int panel_min_millinits;
unsigned int panel_max_millinits;
unsigned int min_backlight_pwm;
unsigned int max_backlight_pwm;
unsigned int min_abm_backlight;
unsigned int num_backlight_levels;
bool backlight_ramping_override;
unsigned int backlight_ramping_reduction;
unsigned int backlight_ramping_start;
bool def_varibright_enable;
unsigned int def_varibright_level;
unsigned int varibright_level;
unsigned int abm_config_setting;
bool allow_psr_smu_optimizations;
bool allow_psr_multi_disp_optimizations;
bool use_custom_backlight_caps;
unsigned int custom_backlight_caps_config_no;
bool use_linear_backlight_curve;
};
struct mod_power {
int dummy;
};
/* VariBright settings structure */
struct varibright_info {
unsigned int level;
bool enable;
bool activate;
};
struct mod_power_psr_context {
/* ddc line */
unsigned int channel;
/* Transmitter id */
unsigned int transmitter_id;
/* Engine Id is used for Dig Be source select */
unsigned int engine_id;
/* Controller Id used for Dig Fe source select */
unsigned int controller_id;
/* Pcie or Uniphy */
unsigned int phy_type;
/* Physical PHY Id used by SMU interpretation */
unsigned int smu_phy_id;
/* Vertical total pixels from crtc timing.
* This is used for static screen detection.
* ie. If we want to detect half a frame,
* we use this to determine the hyst lines.
*/
unsigned int crtc_timing_vertical_total;
/* PSR supported from panel capabilities and
* current display configuration
*/
bool psr_supported_display_config;
/* Whether fast link training is supported by the panel */
bool psr_exit_link_training_required;
/* If RFB setup time is greater than the total VBLANK time,
* it is not possible for the sink to capture the video frame
* in the same frame the SDP is sent. In this case,
* the frame capture indication bit should be set and an extra
* static frame should be transmitted to the sink.
*/
bool psr_frame_capture_indication_req;
/* Set the last possible line SDP may be transmitted without violating
* the RFB setup time or entering the active video frame.
*/
unsigned int sdp_transmit_line_num_deadline;
/* The VSync rate in Hz used to calculate the
* step size for smooth brightness feature
*/
unsigned int vsync_rate_hz;
unsigned int skip_psr_wait_for_pll_lock;
unsigned int number_of_controllers;
/* Unused, for future use. To indicate that first changed frame from
* state3 shouldn't result in psr_inactive, but rather to perform
* an automatic single frame rfb_update.
*/
bool rfb_update_auto_en;
/* Number of frame before entering static screen */
unsigned int timehyst_frames;
/* Partial frames before entering static screen */
unsigned int hyst_lines;
/* # of repeated AUX transaction attempts to make before
* indicating failure to the driver
*/
unsigned int aux_repeats;
/* Controls hw blocks to power down during PSR active state */
unsigned int psr_level;
/* Controls additional delay after remote frame capture before
* continuing powerd own
*/
unsigned int frame_delay;
bool allow_smu_optimizations;
bool allow_multi_disp_optimizations;
unsigned int line_time_in_us;
/* Panel self refresh 2 selective update granularity required */
bool su_granularity_required;
/* psr2 selective update y granularity capability */
uint8_t su_y_granularity;
uint8_t rate_control_caps;
bool os_request_force_ffu;
};
enum psr_event {
psr_event_invalid = 0x0,
psr_event_vsync = 0x1,
psr_event_full_screen = 0x2,
psr_event_defer_enable = 0x4,
psr_event_hw_programming = 0x8,
psr_event_test_harness_enable_psr = 0x10,
psr_event_test_harness_disable_psr = 0x20,
psr_event_mpo_video_selective_update = 0x40,
psr_event_edp_panel_off_disable_psr = 0x80,
psr_event_dynamic_display_switch = 0x100,
psr_event_big_screen_video = 0x200,
psr_event_dds_defer_stream_enable = 0x800,
psr_event_dynamic_link_rate_control = 0x1000,
psr_event_vrr_transition = 0x2000,
psr_event_pause = 0x4000,
psr_event_immediate_flip = 0x8000,
psr_event_os_request_disable = 0x10000,
psr_event_os_request_force_ffu = 0x20000,
psr_event_os_override_hold = 0x40000,
psr_event_crc_window_active = 0x80000,
};
enum replay_event {
replay_event_invalid = 0x0,
replay_event_vsync = 0x1,
replay_event_full_screen = 0x2,
replay_event_mpo_video_selective_update = 0x4,
replay_event_big_screen_video = 0x8,
replay_event_hw_programming = 0x10,
replay_event_edp_panel_off_disable_psr = 0x20,
replay_event_general_ui = 0x40,
replay_event_vrr = 0x80,
replay_event_prepare_vtotal = 0x100,
replay_event_test_harness_enable_replay = 0x200,
replay_event_test_harness_disable_replay = 0x400,
replay_event_test_harness_ultra_sleep = 0x800,
replay_event_immediate_flip = 0x1000,
replay_event_vrr_transition = 0x2000,
replay_event_pause = 0x4000,
replay_event_disable_replay_while_DPMS = 0x8000,
replay_event_test_harness_mode = 0x10000,
replay_event_cursor_updating = 0x20000,
replay_event_sleep_resume = 0x40000,
replay_event_disable_in_AC = 0x80000,
replay_event_disable_replay_while_detect_display = 0x100000,
replay_event_disable_replay_while_switching_mux = 0x400000,
replay_event_infopacket = 0x800000,
replay_event_os_request_disable = 0x1000000,
replay_event_os_request_force_ffu = 0x2000000,
replay_event_os_override_hold = 0x4000000,
replay_event_crc_window_active = 0x8000000,
};
enum replay_enable_option {
pr_enable_option_static_screen = 0x1,
pr_enable_option_mpo_video = 0x2,
pr_enable_option_full_screen_video = 0x4,
pr_enable_option_general_ui = 0x8,
pr_enable_option_full_screen = 0x10,
pr_enable_option_static_screen_coasting = 0x10000,
pr_enable_option_mpo_video_coasting = 0x20000,
pr_enable_option_full_screen_video_coasting = 0x40000,
pr_enable_option_full_screen_coasting = 0x100000,
};
struct mod_power *mod_power_create(struct dc *dc,
struct mod_power_init_params *init_params,
unsigned int edp_num);
void mod_power_destroy(struct mod_power *mod_power);
bool mod_power_hw_init(struct mod_power *mod_power);
bool mod_power_add_stream(struct mod_power *mod_power,
struct dc_stream_state *stream, struct psr_caps *caps);
bool mod_power_remove_stream(struct mod_power *mod_power,
const struct dc_stream_state *stream);
bool mod_power_replace_stream(struct mod_power *mod_power,
const struct dc_stream_state *current_stream,
struct dc_stream_state *new_stream,
struct psr_caps *new_caps);
bool mod_power_set_backlight_nits(struct mod_power *mod_power,
struct dc_stream_state *streams,
unsigned int backlight_millinit,
unsigned int transition_time_millisec,
bool skip_aux,
bool is_hdr);
bool mod_power_set_backlight_percent(struct mod_power *mod_power,
struct dc_stream_state *stream,
unsigned int backlight_millipercent,
unsigned int transition_time_millisec,
bool is_hdr);
void mod_power_update_backlight(struct mod_power *mod_power,
struct dc_stream_state *stream,
unsigned int backlight_millipercent);
void mod_power_update_backlight_nits(struct mod_power *mod_power,
struct dc_stream_state *stream,
unsigned int backlight_millinit);
bool mod_power_get_backlight_pwm(struct mod_power *mod_power,
unsigned int *backlight_pwm,
unsigned int inst);
bool mod_power_get_backlight_nits(struct mod_power *mod_power,
unsigned int *backlight_millinit,
unsigned int inst);
bool mod_power_get_backlight_percent(struct mod_power *mod_power,
unsigned int *backlight_millipercent,
unsigned int inst);
bool mod_power_get_hw_target_backlight_pwm_nits(
struct mod_power *mod_power,
const struct dc_link *link,
unsigned int *backlight_millinit,
unsigned int inst);
bool mod_power_get_hw_target_backlight_pwm_percent(
struct mod_power *mod_power,
const struct dc_link *link,
unsigned int *backlight_millipercent,
unsigned int inst);
bool mod_power_get_hw_target_backlight_pwm(
struct mod_power *mod_power,
const struct dc_link *link,
unsigned int *backlight_u16_16);
bool mod_power_get_hw_backlight_pwm(
struct mod_power *mod_power,
const struct dc_link *link,
unsigned int *backlight);
bool mod_power_get_hw_backlight_pwm_nits(
struct mod_power *mod_power,
const struct dc_link *link,
unsigned int *backlight_millinit,
unsigned int inst);
bool mod_power_get_hw_backlight_aux_nits(
struct mod_power *mod_power,
struct dc_stream_state **streams, int num_streams,
unsigned int *backlight_millinit_avg,
unsigned int *backlight_millinit_peak);
bool mod_power_get_hw_backlight_pwm_percent(
struct mod_power *mod_power,
const struct dc_link *link,
unsigned int *backlight_millipercent,
unsigned int inst);
void mod_power_initialize_backlight_caps
(struct mod_power *mod_power);
bool mod_power_get_panel_backlight_boundaries
(struct mod_power *mod_power,
unsigned int *out_min_backlight,
unsigned int *out_max_backlight,
unsigned int *out_ac_backlight_percent,
unsigned int *out_dc_backlight_percent,
unsigned int inst);
bool mod_power_set_smooth_brightness(struct mod_power *mod_power,
bool enable_brightness,
unsigned int inst);
bool mod_power_notify_mode_change(struct mod_power *mod_power,
const struct dc_stream_state *stream,
bool is_hdr);
bool mod_power_get_varibright_level(struct mod_power *mod_power,
unsigned int *varibright_level);
bool mod_power_get_varibright_hw_level(struct mod_power *mod_power,
unsigned int *varibright_level);
bool mod_power_get_varibright_default_level(struct mod_power *mod_power,
unsigned int *varibright_level);
bool mod_power_get_varibright_enable(struct mod_power *mod_power,
bool *varibright_enable);
bool mod_power_varibright_activate(struct mod_power *mod_power,
bool activate, struct dc_stream_update *stream_update);
bool mod_power_varibright_feature_enable(struct mod_power *mod_power,
bool enable, struct dc_stream_update *stream_update);
bool mod_power_varibright_set_level(struct mod_power *mod_power,
unsigned int level, struct dc_stream_update *stream_update);
bool mod_power_varibright_set_hw_level(struct mod_power *mod_power,
unsigned int level, struct dc_stream_update *stream_update);
bool mod_power_is_abm_active(struct mod_power *mod_power,
const struct dc_link *link,
unsigned int inst);
bool mod_power_set_psr_event(struct mod_power *mod_power,
struct dc_stream_state *stream, bool set_event,
enum psr_event event, bool wait);
bool mod_power_get_psr_event(struct mod_power *mod_power,
struct dc_stream_state *stream,
unsigned int *active_psr_events);
bool mod_power_get_psr_state(struct mod_power *mod_power,
const struct dc_stream_state *stream,
enum dc_psr_state *state);
bool mod_power_get_psr_enabled_status(struct mod_power *mod_power,
const struct dc_stream_state *stream,
bool *psr_enabled);
bool mod_power_set_replay_event(struct mod_power *mod_power,
struct dc_stream_state *stream, bool set_event,
enum replay_event event, bool wait_for_disable);
bool mod_power_get_replay_event(struct mod_power *mod_power,
struct dc_stream_state *stream,
unsigned int *active_replay_events);
bool mod_power_get_replay_active_status(const struct dc_stream_state *stream,
bool *replay_active);
bool mod_power_replay_set_coasting_vtotal(struct mod_power *mod_power,
const struct dc_stream_state *stream,
uint32_t coasting_vtotal, uint16_t frame_skip_number);
void mod_power_replay_residency(const struct dc_stream_state *stream,
unsigned int *residency, const bool is_start, const bool is_alpm);
bool mod_power_replay_set_power_opt_and_coasting_vtotal(struct mod_power *mod_power,
const struct dc_stream_state *stream, unsigned int active_replay_events, uint32_t coasting_vtotal,
bool is_ultra_sleep_mode, uint16_t frame_skip_number);
void mod_power_replay_set_timing_sync_supported(struct mod_power *mod_power,
const struct dc_stream_state *stream);
void mod_power_replay_disabled_adaptive_sync_sdp(struct mod_power *mod_power,
const struct dc_stream_state *stream, bool force_disabled);
void mod_power_replay_disabled_desync_error_detection(struct mod_power *mod_power,
const struct dc_stream_state *stream, bool force_disabled);
void mod_power_set_low_rr_activate(struct mod_power *mod_power,
const struct dc_stream_state *stream, bool low_rr_supported);
void mod_power_set_video_conferencing_activate(struct mod_power *mod_power,
const struct dc_stream_state *stream, bool video_conferencing_activate);
void mod_power_set_live_capture_with_cvt_activate(struct mod_power *mod_power,
const struct dc_stream_state *stream, bool live_capture_with_cvt_activate);
void mod_power_set_replay_continuously_resync(struct mod_power *mod_power,
const struct dc_stream_state *stream, bool enable);
void mod_power_set_coasting_vtotal_without_frame_update(struct mod_power *mod_power,
const struct dc_stream_state *stream, uint32_t coasting_vtotal);
void mod_power_psr_residency(struct mod_power *mod_power,
const struct dc_stream_state *stream,
unsigned int *residency,
const uint8_t mode);
bool mod_power_psr_get_active_psr_events(struct mod_power *mod_power,
const struct dc_stream_state *stream, unsigned int *active_psr_events);
bool mod_power_psr_set_sink_vtotal_in_psr_active(struct mod_power *mod_power,
const struct dc_stream_state *stream,
uint16_t psr_vtotal_idle,
uint16_t psr_vtotal_su);
bool mod_power_backlight_percent_to_nits(struct mod_power *mod_power,
struct dc_stream_state *stream,
unsigned int backlight_millipercent,
unsigned int *backlight_millinit);
bool mod_power_backlight_nits_to_percent(struct mod_power *mod_power,
struct dc_stream_state *stream,
unsigned int backlight_millinit,
unsigned int *backlight_millipercent);
#endif /* MODULES_INC_MOD_POWER_H_ */
@@ -0,0 +1,108 @@
/*
* Copyright 2016 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
*
*/
#ifndef MOD_SHARED_H_
#define MOD_SHARED_H_
enum color_transfer_func {
TRANSFER_FUNC_UNKNOWN,
TRANSFER_FUNC_SRGB,
TRANSFER_FUNC_BT709,
TRANSFER_FUNC_PQ2084,
TRANSFER_FUNC_PQ2084_INTERIM,
TRANSFER_FUNC_LINEAR_0_1,
TRANSFER_FUNC_LINEAR_0_125,
TRANSFER_FUNC_GAMMA_22,
TRANSFER_FUNC_GAMMA_26
};
enum vrr_packet_type {
PACKET_TYPE_VRR,
PACKET_TYPE_FS_V1,
PACKET_TYPE_FS_V2,
PACKET_TYPE_FS_V3,
PACKET_TYPE_VTEM
};
union lut3d_control_flags {
unsigned int raw;
struct {
unsigned int do_chroma_scale :1;
unsigned int spec_version :3;
unsigned int use_zero_display_black :1;
unsigned int use_zero_source_black :1;
unsigned int force_display_black :6;
unsigned int apply_display_gamma :1;
unsigned int exp_shaper_max :6;
unsigned int unity_3dlut :1;
unsigned int bypass_3dlut :1;
unsigned int use_3dlut :1;
unsigned int less_than_dcip3 :1;
unsigned int override_lum :1;
unsigned int use_gamut_map_lib :1;
unsigned int chromatic_adaptation_src :1;
unsigned int chromatic_adaptation_dst :1;
unsigned int do_blender_lut_degamma :1;
unsigned int reseved :4;
} bits;
};
enum tm_show_option_internal {
tm_show_option_internal_single_file = 0,/*flags2 not in use*/
tm_show_option_internal_duplicate_file, /*use flags2*/
tm_show_option_internal_duplicate_sidebyside/*use flags2*/
};
enum lut3d_control_gamut_map {
lut3d_control_gamut_map_none = 0,
lut3d_control_gamut_map_tonemap,
lut3d_control_gamut_map_chto,
lut3d_control_gamut_map_chso,
lut3d_control_gamut_map_chci
};
enum lut3d_control_rotation_mode {
lut3d_control_rotation_mode_none = 0,
lut3d_control_rotation_mode_hue,
lut3d_control_rotation_mode_cc,
lut3d_control_rotation_mode_hue_cc
};
struct lut3d_settings {
unsigned char version;
union lut3d_control_flags flags;
union lut3d_control_flags flags2;
enum tm_show_option_internal option;
unsigned int min_lum;/*multiplied by 100*/
unsigned int max_lum;
unsigned int min_lum2;
unsigned int max_lum2;
enum lut3d_control_gamut_map map;
enum lut3d_control_rotation_mode rotation;
enum lut3d_control_gamut_map map2;
enum lut3d_control_rotation_mode rotation2;
};
#endif /* MOD_SHARED_H_ */
@@ -0,0 +1,75 @@
/*
* Copyright 2016 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
*
*/
#ifndef MODULES_INC_MOD_STATS_H_
#define MODULES_INC_MOD_STATS_H_
#include "dm_services.h"
struct mod_stats {
int dummy;
};
struct mod_stats_caps {
bool dummy;
};
struct mod_stats_init_params {
unsigned int stats_enable;
unsigned int stats_entries;
};
struct mod_stats *mod_stats_create(struct dc *dc,
struct mod_stats_init_params *init_params);
void mod_stats_destroy(struct mod_stats *mod_stats);
bool mod_stats_init(struct mod_stats *mod_stats);
void mod_stats_dump(struct mod_stats *mod_stats);
void mod_stats_reset_data(struct mod_stats *mod_stats);
void mod_stats_update_event(struct mod_stats *mod_stats,
const char *event_string,
unsigned int length);
void mod_stats_update_flip(struct mod_stats *mod_stats,
unsigned long long timestamp_in_ns);
void mod_stats_update_vupdate(struct mod_stats *mod_stats,
unsigned long long timestamp_in_ns);
void mod_stats_update_freesync(struct mod_stats *mod_stats,
unsigned int v_total_min,
unsigned int v_total_max,
unsigned int event_triggers,
unsigned int window_min,
unsigned int window_max,
unsigned int lfc_mid_point_in_us,
unsigned int inserted_frames,
unsigned int inserted_frame_duration_in_us);
#endif /* MODULES_INC_MOD_STATS_H_ */
@@ -0,0 +1,46 @@
/*
* Copyright 2019 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
*
*/
#ifndef MOD_VMID_H_
#define MOD_VMID_H_
#define MAX_VMID 16
#include "dc.h"
struct mod_vmid {
int dummy;
};
uint8_t mod_vmid_get_for_ptb(struct mod_vmid *mod_vmid, uint64_t ptb);
void mod_vmid_reset(struct mod_vmid *mod_vmid);
struct mod_vmid *mod_vmid_create(
struct dc *dc,
unsigned int num_vmid,
struct dc_virtual_addr_space_config *va_config);
void mod_vmid_destroy(struct mod_vmid *mod_vmid);
#endif /* MOD_VMID_H_ */
@@ -0,0 +1,31 @@
#
# Copyright 2017 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.
#
#
# Makefile for the 'info_packet' sub-module of DAL.
#
INFO_PACKET = info_packet.o
AMD_DAL_INFO_PACKET = $(addprefix $(AMDDALPATH)/modules/info_packet/,$(INFO_PACKET))
#$(info ************ DAL INFO_PACKET MODULE MAKEFILE ************)
AMD_DISPLAY_FILES += $(AMD_DAL_INFO_PACKET)
@@ -0,0 +1,708 @@
/*
* 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 "mod_info_packet.h"
#include "core_types.h"
#include "dc_types.h"
#include "mod_shared.h"
#include "mod_freesync.h"
#include "dc.h"
enum vsc_packet_revision {
vsc_packet_undefined = 0,
//01h = VSC SDP supports only 3D stereo.
vsc_packet_rev1 = 1,
//02h = 3D stereo + PSR.
vsc_packet_rev2 = 2,
//03h = 3D stereo + PSR2.
vsc_packet_rev3 = 3,
//04h = 3D stereo + PSR/PSR2 + Y-coordinate.
vsc_packet_rev4 = 4,
//05h = 3D stereo + PSR/PSR2 + Y-coordinate + Pixel Encoding/Colorimetry Format
vsc_packet_rev5 = 5,
//06h = 3D stereo + PR + Y-coordinate
vsc_packet_rev6 = 6,
//07h = 3D stereo + PR + Y-coordinate + Pixel Encoding/Colorimetry Format
vsc_packet_rev7 = 7,
};
#define HDMI_INFOFRAME_TYPE_VENDOR 0x81
#define HF_VSIF_VERSION 1
// VTEM Byte Offset
#define VTEM_PB0 0
#define VTEM_PB1 1
#define VTEM_PB2 2
#define VTEM_PB3 3
#define VTEM_PB4 4
#define VTEM_PB5 5
#define VTEM_PB6 6
#define VTEM_MD0 7
#define VTEM_MD1 8
#define VTEM_MD2 9
#define VTEM_MD3 10
// VTEM Byte Masks
//PB0
#define MASK_VTEM_PB0__RESERVED0 0x01
#define MASK_VTEM_PB0__SYNC 0x02
#define MASK_VTEM_PB0__VFR 0x04
#define MASK_VTEM_PB0__AFR 0x08
#define MASK_VTEM_PB0__DS_TYPE 0x30
//0: Periodic pseudo-static EM Data Set
//1: Periodic dynamic EM Data Set
//2: Unique EM Data Set
//3: Reserved
#define MASK_VTEM_PB0__END 0x40
#define MASK_VTEM_PB0__NEW 0x80
//PB1
#define MASK_VTEM_PB1__RESERVED1 0xFF
//PB2
#define MASK_VTEM_PB2__ORGANIZATION_ID 0xFF
//0: This is a Vendor Specific EM Data Set
//1: This EM Data Set is defined by This Specification (HDMI 2.1 r102.clean)
//2: This EM Data Set is defined by CTA-861-G
//3: This EM Data Set is defined by VESA
//PB3
#define MASK_VTEM_PB3__DATA_SET_TAG_MSB 0xFF
//PB4
#define MASK_VTEM_PB4__DATA_SET_TAG_LSB 0xFF
//PB5
#define MASK_VTEM_PB5__DATA_SET_LENGTH_MSB 0xFF
//PB6
#define MASK_VTEM_PB6__DATA_SET_LENGTH_LSB 0xFF
//PB7-27 (20 bytes):
//PB7 = MD0
#define MASK_VTEM_MD0__VRR_EN 0x01
#define MASK_VTEM_MD0__M_CONST 0x02
#define MASK_VTEM_MD0__QMS_EN 0x04
#define MASK_VTEM_MD0__RESERVED2 0x08
#define MASK_VTEM_MD0__FVA_FACTOR_M1 0xF0
//MD1
#define MASK_VTEM_MD1__BASE_VFRONT 0xFF
//MD2
#define MASK_VTEM_MD2__BASE_REFRESH_RATE_98 0x03
#define MASK_VTEM_MD2__RB 0x04
#define MASK_VTEM_MD2__NEXT_TFR 0xF8
//MD3
#define MASK_VTEM_MD3__BASE_REFRESH_RATE_07 0xFF
enum ColorimetryRGBDP {
ColorimetryRGB_DP_sRGB = 0,
ColorimetryRGB_DP_AdobeRGB = 3,
ColorimetryRGB_DP_P3 = 4,
ColorimetryRGB_DP_CustomColorProfile = 5,
ColorimetryRGB_DP_ITU_R_BT2020RGB = 6,
};
enum ColorimetryYCCDP {
ColorimetryYCC_DP_ITU601 = 0,
ColorimetryYCC_DP_ITU709 = 1,
ColorimetryYCC_DP_AdobeYCC = 5,
ColorimetryYCC_DP_ITU2020YCC = 6,
ColorimetryYCC_DP_ITU2020YCbCr = 7,
};
/* Helper function to set VSC packet colorimetry data */
void set_vsc_packet_colorimetry_data(
const struct dc_stream_state *stream,
struct dc_info_packet *info_packet,
enum dc_color_space cs,
enum color_transfer_func tf)
{
/* Set VSC SDP fields for pixel encoding and colorimetry format from DP 1.3 specs
* Data Bytes DB 18~16
* Bits 3:0 (Colorimetry Format) | Bits 7:4 (Pixel Encoding)
* ----------------------------------------------------------------------------------------------------
* 0x0 = sRGB | 0 = RGB
* 0x1 = RGB Wide Gamut Fixed Point
* 0x2 = RGB Wide Gamut Floating Point
* 0x3 = AdobeRGB
* 0x4 = DCI-P3
* 0x5 = CustomColorProfile
* (others reserved)
* ----------------------------------------------------------------------------------------------------
* 0x0 = ITU-R BT.601 | 1 = YCbCr444
* 0x1 = ITU-R BT.709
* 0x2 = xvYCC601
* 0x3 = xvYCC709
* 0x4 = sYCC601
* 0x5 = AdobeYCC601
* 0x6 = ITU-R BT.2020 Y'cC'bcC'rc
* 0x7 = ITU-R BT.2020 Y'C'bC'r
* (others reserved)
* ----------------------------------------------------------------------------------------------------
* 0x0 = ITU-R BT.601 | 2 = YCbCr422
* 0x1 = ITU-R BT.709
* 0x2 = xvYCC601
* 0x3 = xvYCC709
* 0x4 = sYCC601
* 0x5 = AdobeYCC601
* 0x6 = ITU-R BT.2020 Y'cC'bcC'rc
* 0x7 = ITU-R BT.2020 Y'C'bC'r
* (others reserved)
* ----------------------------------------------------------------------------------------------------
* 0x0 = ITU-R BT.601 | 3 = YCbCr420
* 0x1 = ITU-R BT.709
* 0x2 = xvYCC601
* 0x3 = xvYCC709
* 0x4 = sYCC601
* 0x5 = AdobeYCC601
* 0x6 = ITU-R BT.2020 Y'cC'bcC'rc
* 0x7 = ITU-R BT.2020 Y'C'bC'r
* (others reserved)
* ----------------------------------------------------------------------------------------------------
* 0x0 =DICOM Part14 Grayscale | 4 = Yonly
* Display Function
* (others reserved)
*/
unsigned int pixelEncoding = 0;
unsigned int colorimetryFormat = 0;
/* Set Pixel Encoding */
switch (stream->timing.pixel_encoding) {
case PIXEL_ENCODING_RGB:
pixelEncoding = 0x0; /* RGB = 0h */
break;
case PIXEL_ENCODING_YCBCR444:
pixelEncoding = 0x1; /* YCbCr444 = 1h */
break;
case PIXEL_ENCODING_YCBCR422:
pixelEncoding = 0x2; /* YCbCr422 = 2h */
break;
case PIXEL_ENCODING_YCBCR420:
pixelEncoding = 0x3; /* YCbCr420 = 3h */
break;
default:
pixelEncoding = 0x0; /* default RGB = 0h */
break;
}
/* Set Colorimetry format based on pixel encoding */
switch (stream->timing.pixel_encoding) {
case PIXEL_ENCODING_RGB:
if ((cs == COLOR_SPACE_SRGB) ||
(cs == COLOR_SPACE_SRGB_LIMITED))
colorimetryFormat = ColorimetryRGB_DP_sRGB;
else if (cs == COLOR_SPACE_ADOBERGB)
colorimetryFormat = ColorimetryRGB_DP_AdobeRGB;
else if ((cs == COLOR_SPACE_2020_RGB_FULLRANGE) ||
(cs == COLOR_SPACE_2020_RGB_LIMITEDRANGE))
colorimetryFormat = ColorimetryRGB_DP_ITU_R_BT2020RGB;
break;
case PIXEL_ENCODING_YCBCR444:
case PIXEL_ENCODING_YCBCR422:
case PIXEL_ENCODING_YCBCR420:
/* Note: xvYCC probably not supported correctly here on DP since colorspace translation
* loses distinction between BT601 vs xvYCC601 in translation
*/
if (cs == COLOR_SPACE_YCBCR601)
colorimetryFormat = ColorimetryYCC_DP_ITU601;
else if (cs == COLOR_SPACE_YCBCR709)
colorimetryFormat = ColorimetryYCC_DP_ITU709;
else if (cs == COLOR_SPACE_ADOBERGB)
colorimetryFormat = ColorimetryYCC_DP_AdobeYCC;
else if (cs == COLOR_SPACE_2020_YCBCR_LIMITED)
colorimetryFormat = ColorimetryYCC_DP_ITU2020YCbCr;
if (cs == COLOR_SPACE_2020_YCBCR_LIMITED && tf == TRANSFER_FUNC_GAMMA_22)
colorimetryFormat = ColorimetryYCC_DP_ITU709;
break;
default:
colorimetryFormat = ColorimetryRGB_DP_sRGB;
break;
}
info_packet->sb[16] = (uint8_t)((pixelEncoding << 4) | colorimetryFormat);
/* Set color depth */
switch (stream->timing.display_color_depth) {
case COLOR_DEPTH_666:
/* NOTE: This is actually not valid for YCbCr pixel encoding to have 6 bpc
* as of DP1.4 spec, but value of 0 probably reserved here for potential future use.
*/
info_packet->sb[17] = 0;
break;
case COLOR_DEPTH_888:
info_packet->sb[17] = 1;
break;
case COLOR_DEPTH_101010:
info_packet->sb[17] = 2;
break;
case COLOR_DEPTH_121212:
info_packet->sb[17] = 3;
break;
/*case COLOR_DEPTH_141414: -- NO SUCH FORMAT IN DP SPEC */
case COLOR_DEPTH_161616:
info_packet->sb[17] = 4;
break;
default:
info_packet->sb[17] = 0;
break;
}
/* all YCbCr are always limited range */
if ((cs == COLOR_SPACE_SRGB_LIMITED) ||
(cs == COLOR_SPACE_2020_RGB_LIMITEDRANGE) ||
(pixelEncoding != 0x0)) {
info_packet->sb[17] |= 0x80; /* DB17 bit 7 set to 1 for CEA timing. */
}
/* Content Type (Bits 2:0)
* 0 = Not defined.
* 1 = Graphics.
* 2 = Photo.
* 3 = Video.
* 4 = Game.
*/
info_packet->sb[18] = 0;
}
void mod_build_vsc_infopacket(const struct dc_stream_state *stream,
struct dc_info_packet *info_packet,
enum dc_color_space cs,
enum color_transfer_func tf)
{
unsigned int vsc_packet_revision = vsc_packet_undefined;
unsigned int i;
bool stereo3dSupport = false;
if (stream->timing.timing_3d_format != TIMING_3D_FORMAT_NONE && stream->view_format != VIEW_3D_FORMAT_NONE) {
vsc_packet_revision = vsc_packet_rev1;
stereo3dSupport = true;
}
/* VSC packet set to 4 for PSR-SU, or 2 for PSR1 */
if (stream->link->psr_settings.psr_version == DC_PSR_VERSION_SU_1)
vsc_packet_revision = vsc_packet_rev4;
else if (stream->link->replay_settings.config.replay_supported)
vsc_packet_revision = vsc_packet_rev4;
else if (stream->link->psr_settings.psr_version == DC_PSR_VERSION_1)
vsc_packet_revision = vsc_packet_rev2;
/* Update to revision 5 for extended colorimetry support */
if (stream->use_vsc_sdp_for_colorimetry)
vsc_packet_revision = vsc_packet_rev5;
/* Check for Panel Replay (highest priority) */
if (stream->link->replay_settings.config.replay_version == DC_VESA_PANEL_REPLAY)
vsc_packet_revision = stream->use_vsc_sdp_for_colorimetry ? vsc_packet_rev7 : vsc_packet_rev6;
/* VSC packet not needed based on the features
* supported by this DP display
*/
if (vsc_packet_revision == vsc_packet_undefined)
return;
if (vsc_packet_revision == vsc_packet_rev6) {
/* Secondary-data Packet ID = 0*/
info_packet->hb0 = 0x00;
/* 07h - Packet Type Value indicating Video
* Stream Configuration packet
*/
info_packet->hb1 = 0x07;
/* 06h = VSC SDP supporting 3D stereo + PR
*/
info_packet->hb2 = 0x06;
/* 0Eh = VSC SDP supporting 3D stereo + PR
* (HB2 = 06h), with Y-coordinate of first scan
* line of the SU region
*/
info_packet->hb3 = 0x10;
for (i = 0; i < 28; i++)
info_packet->sb[i] = 0;
info_packet->valid = true;
}
if (vsc_packet_revision == vsc_packet_rev4) {
/* Secondary-data Packet ID = 0*/
info_packet->hb0 = 0x00;
/* 07h - Packet Type Value indicating Video
* Stream Configuration packet
*/
info_packet->hb1 = 0x07;
/* 04h = VSC SDP supporting 3D stereo + PSR/PSR2 + Y-coordinate
* (applies to eDP v1.4 or higher).
*/
info_packet->hb2 = 0x04;
/* 0Eh = VSC SDP supporting 3D stereo + PSR2
* (HB2 = 04h), with Y-coordinate of first scan
* line of the SU region
*/
info_packet->hb3 = 0x0E;
for (i = 0; i < 28; i++)
info_packet->sb[i] = 0;
info_packet->valid = true;
}
if (vsc_packet_revision == vsc_packet_rev2) {
/* Secondary-data Packet ID = 0*/
info_packet->hb0 = 0x00;
/* 07h - Packet Type Value indicating Video
* Stream Configuration packet
*/
info_packet->hb1 = 0x07;
/* 02h = VSC SDP supporting 3D stereo and PSR
* (applies to eDP v1.3 or higher).
*/
info_packet->hb2 = 0x02;
/* 08h = VSC packet supporting 3D stereo + PSR
* (HB2 = 02h).
*/
info_packet->hb3 = 0x08;
for (i = 0; i < 28; i++)
info_packet->sb[i] = 0;
info_packet->valid = true;
}
if (vsc_packet_revision == vsc_packet_rev1) {
info_packet->hb0 = 0x00; // Secondary-data Packet ID = 0
info_packet->hb1 = 0x07; // 07h = Packet Type Value indicating Video Stream Configuration packet
info_packet->hb2 = 0x01; // 01h = Revision number. VSC SDP supporting 3D stereo only
info_packet->hb3 = 0x01; // 01h = VSC SDP supporting 3D stereo only (HB2 = 01h).
info_packet->valid = true;
}
if (stereo3dSupport) {
/* ==============================================================================================================|
* A. STEREO 3D
* ==============================================================================================================|
* VSC Payload (1 byte) From DP1.2 spec
*
* Bits 3:0 (Stereo Interface Method Code) | Bits 7:4 (Stereo Interface Method Specific Parameter)
* -----------------------------------------------------------------------------------------------------
* 0 = Non Stereo Video | Must be set to 0x0
* -----------------------------------------------------------------------------------------------------
* 1 = Frame/Field Sequential | 0x0: L + R view indication based on MISC1 bit 2:1
* | 0x1: Right when Stereo Signal = 1
* | 0x2: Left when Stereo Signal = 1
* | (others reserved)
* -----------------------------------------------------------------------------------------------------
* 2 = Stacked Frame | 0x0: Left view is on top and right view on bottom
* | (others reserved)
* -----------------------------------------------------------------------------------------------------
* 3 = Pixel Interleaved | 0x0: horiz interleaved, right view pixels on even lines
* | 0x1: horiz interleaved, right view pixels on odd lines
* | 0x2: checker board, start with left view pixel
* | 0x3: vertical interleaved, start with left view pixels
* | 0x4: vertical interleaved, start with right view pixels
* | (others reserved)
* -----------------------------------------------------------------------------------------------------
* 4 = Side-by-side | 0x0: left half represents left eye view
* | 0x1: left half represents right eye view
*/
switch (stream->timing.timing_3d_format) {
case TIMING_3D_FORMAT_HW_FRAME_PACKING:
case TIMING_3D_FORMAT_SW_FRAME_PACKING:
case TIMING_3D_FORMAT_TOP_AND_BOTTOM:
case TIMING_3D_FORMAT_TB_SW_PACKED:
info_packet->sb[0] = 0x02; // Stacked Frame, Left view is on top and right view on bottom.
break;
case TIMING_3D_FORMAT_DP_HDMI_INBAND_FA:
case TIMING_3D_FORMAT_INBAND_FA:
info_packet->sb[0] = 0x01; // Frame/Field Sequential, L + R view indication based on MISC1 bit 2:1
break;
case TIMING_3D_FORMAT_SIDE_BY_SIDE:
case TIMING_3D_FORMAT_SBS_SW_PACKED:
info_packet->sb[0] = 0x04; // Side-by-side
break;
default:
info_packet->sb[0] = 0x00; // No Stereo Video, Shall be cleared to 0x0.
break;
}
}
/* 05h = VSC SDP supporting 3D stereo, PSR2, and Pixel Encoding/Colorimetry Format indication.
* Added in DP1.3, a DP Source device is allowed to indicate the pixel encoding/colorimetry
* format to the DP Sink device with VSC SDP only when the DP Sink device supports it
* (i.e., VSC_SDP_EXTENSION_FOR_COLORIMETRY_SUPPORTED bit in the DPRX_FEATURE_ENUMERATION_LIST
* register (DPCD Address 02210h, bit 3) is set to 1).
* (Requires VSC_SDP_EXTENSION_FOR_COLORIMETRY_SUPPORTED bit set to 1 in DPCD 02210h. This
* DPCD register is exposed in the new Extended Receiver Capability field for DPCD Rev. 1.4
* (and higher). When MISC1. bit 6. is Set to 1, a Source device uses a VSC SDP to indicate
* the Pixel Encoding/Colorimetry Format and that a Sink device must ignore MISC1, bit 7, and
* MISC0, bits 7:1 (MISC1, bit 7. and MISC0, bits 7:1 become "don't care").)
*/
if (vsc_packet_revision == vsc_packet_rev5) {
/* Secondary-data Packet ID = 0 */
info_packet->hb0 = 0x00;
/* 07h - Packet Type Value indicating Video Stream Configuration packet */
info_packet->hb1 = 0x07;
/* 05h = VSC SDP supporting 3D stereo, PSR2, and Pixel Encoding/Colorimetry Format indication. */
info_packet->hb2 = 0x05;
/* 13h = VSC SDP supporting 3D stereo, + PSR2, + Pixel Encoding/Colorimetry Format indication (HB2 = 05h). */
info_packet->hb3 = 0x13;
info_packet->valid = true;
set_vsc_packet_colorimetry_data(stream, info_packet, cs, tf);
}
if (vsc_packet_revision == vsc_packet_rev7) {
/* Secondary-data Packet ID = 0 */
info_packet->hb0 = 0x00;
/* 07h - Packet Type Value indicating Video Stream Configuration packet */
info_packet->hb1 = 0x07;
/* 07h = VSC SDP supporting 3D stereo, PR, and Pixel Encoding/Colorimetry Format indication. */
info_packet->hb2 = 0x07;
/* 13h = VSC SDP supporting 3D stereo, + PR, + Pixel Encoding/Colorimetry Format indication (HB2 = 07h). */
info_packet->hb3 = 0x13;
info_packet->valid = true;
set_vsc_packet_colorimetry_data(stream, info_packet, cs, tf);
}
}
/**
* mod_build_hf_vsif_infopacket - Prepare HDMI Vendor Specific info frame.
* Follows HDMI Spec to build up Vendor Specific info frame
*
* @stream: contains data we may need to construct VSIF (i.e. timing_3d_format, etc.)
* @info_packet: output structure where to store VSIF
* @ALLMEnabled: indicates whether ALLM HF-VSIF should be generated
* @ALLMValue: ALLM bit value to advertise in HF-VSIF
*/
void mod_build_hf_vsif_infopacket(const struct dc_stream_state *stream,
struct dc_info_packet *info_packet, int ALLMEnabled, int ALLMValue)
{
unsigned int length = 5;
bool hdmi_vic_mode = false;
uint8_t checksum = 0;
uint32_t i = 0;
enum dc_timing_3d_format format;
bool bALLM = (bool)ALLMEnabled;
bool bALLMVal = (bool)ALLMValue;
int CCBPC = 0;
info_packet->valid = false;
format = stream->timing.timing_3d_format;
if (stream->view_format == VIEW_3D_FORMAT_NONE)
format = TIMING_3D_FORMAT_NONE;
if (stream->timing.hdmi_vic != 0
&& stream->timing.h_total >= 3840
&& stream->timing.v_total >= 2160
&& format == TIMING_3D_FORMAT_NONE)
hdmi_vic_mode = true;
if ((format == TIMING_3D_FORMAT_NONE) && !hdmi_vic_mode && !bALLM)
return;
if (!bALLM) {
info_packet->sb[1] = 0x03;
info_packet->sb[2] = 0x0C;
info_packet->sb[3] = 0x00;
if (format != TIMING_3D_FORMAT_NONE)
info_packet->sb[4] = (2 << 5);
else if (hdmi_vic_mode)
info_packet->sb[4] = (1 << 5);
switch (format) {
case TIMING_3D_FORMAT_HW_FRAME_PACKING:
case TIMING_3D_FORMAT_SW_FRAME_PACKING:
info_packet->sb[5] = (0x0 << 4);
break;
case TIMING_3D_FORMAT_SIDE_BY_SIDE:
case TIMING_3D_FORMAT_SBS_SW_PACKED:
info_packet->sb[5] = (0x8 << 4);
length = 6;
break;
case TIMING_3D_FORMAT_TOP_AND_BOTTOM:
case TIMING_3D_FORMAT_TB_SW_PACKED:
info_packet->sb[5] = (0x6 << 4);
break;
default:
break;
}
if (hdmi_vic_mode) {
ASSERT(stream->timing.hdmi_vic <= 0xFF);
info_packet->sb[5] = (uint8_t)stream->timing.hdmi_vic;
}
} else {
info_packet->sb[1] = 0xD8;
info_packet->sb[2] = 0x5D;
info_packet->sb[3] = 0xC4;
info_packet->sb[4] = HF_VSIF_VERSION;
if (format != TIMING_3D_FORMAT_NONE) {
info_packet->sb[5] |= 0x01;
length = 6;
switch (format) {
case TIMING_3D_FORMAT_HW_FRAME_PACKING:
case TIMING_3D_FORMAT_SW_FRAME_PACKING:
info_packet->sb[6] = (0x0 << 4);
break;
case TIMING_3D_FORMAT_SIDE_BY_SIDE:
case TIMING_3D_FORMAT_SBS_SW_PACKED:
info_packet->sb[6] = (0x8 << 4);
break;
case TIMING_3D_FORMAT_TOP_AND_BOTTOM:
case TIMING_3D_FORMAT_TB_SW_PACKED:
info_packet->sb[6] = (0x6 << 4);
break;
default:
break;
}
}
info_packet->sb[5] = (info_packet->sb[5] & ~0x02) | (bALLMVal << 1);
switch (stream->timing.display_color_depth) {
case COLOR_DEPTH_888:
CCBPC = 1;
break;
case COLOR_DEPTH_101010:
CCBPC = 3;
break;
case COLOR_DEPTH_121212:
CCBPC = 5;
break;
case COLOR_DEPTH_161616:
CCBPC = 9;
break;
case COLOR_DEPTH_UNDEFINED:
case COLOR_DEPTH_666:
#ifdef CONFIG_DRM_AMD_DC_DCN2_0
case COLOR_DEPTH_999:
case COLOR_DEPTH_111111:
#endif
case COLOR_DEPTH_141414:
default:
break;
}
info_packet->sb[5] = (uint8_t)((info_packet->sb[5] & ~0xF0) | (CCBPC << 4));
}
info_packet->hb0 = HDMI_INFOFRAME_TYPE_VENDOR;
info_packet->hb1 = 0x01;
info_packet->hb2 = (uint8_t) (length);
checksum += info_packet->hb0;
checksum += info_packet->hb1;
checksum += info_packet->hb2;
for (i = 1; i <= length; i++)
checksum += info_packet->sb[i];
info_packet->sb[0] = (uint8_t) (0x100 - checksum);
info_packet->valid = true;
}
void mod_build_adaptive_sync_infopacket(const struct dc_stream_state *stream,
enum adaptive_sync_type asType,
const struct AS_Df_params *param,
struct dc_info_packet *info_packet)
{
info_packet->valid = false;
memset(info_packet, 0, sizeof(struct dc_info_packet));
switch (asType) {
case ADAPTIVE_SYNC_TYPE_DP:
if (stream != NULL)
mod_build_adaptive_sync_infopacket_v2(stream, param, info_packet);
break;
case FREESYNC_TYPE_PCON_IN_WHITELIST:
case ADAPTIVE_SYNC_TYPE_EDP:
if (stream && stream->link->replay_settings.config.replay_supported &&
stream->link->replay_settings.config.replay_version == DC_VESA_PANEL_REPLAY)
mod_build_adaptive_sync_infopacket_v2(stream, param, info_packet);
else
mod_build_adaptive_sync_infopacket_v1(info_packet);
break;
case ADAPTIVE_SYNC_TYPE_NONE:
case FREESYNC_TYPE_PCON_NOT_IN_WHITELIST:
default:
break;
}
}
void mod_build_adaptive_sync_infopacket_v1(struct dc_info_packet *info_packet)
{
info_packet->valid = true;
// HEADER {HB0, HB1, HB2, HB3} = {00, Type, Version, Length}
info_packet->hb0 = 0x00;
info_packet->hb1 = 0x22;
info_packet->hb2 = AS_SDP_VER_1;
info_packet->hb3 = 0x00;
}
void mod_build_adaptive_sync_infopacket_v2(const struct dc_stream_state *stream,
const struct AS_Df_params *param,
struct dc_info_packet *info_packet)
{
info_packet->valid = true;
// HEADER {HB0, HB1, HB2, HB3} = {00, Type, Version, Length}
info_packet->hb0 = 0x00;
info_packet->hb1 = 0x22;
info_packet->hb2 = AS_SDP_VER_2;
info_packet->hb3 = AS_DP_SDP_LENGTH;
if (param) {
//Payload
info_packet->sb[0] = param->supportMode; //1: AVT; 0: FAVT
info_packet->sb[1] = (stream->timing.v_total & 0x00FF);
info_packet->sb[2] = (stream->timing.v_total & 0xFF00) >> 8;
//info_packet->sb[3] = 0x00; Target RR, not use fot AVT
info_packet->sb[4] = (param->increase.support << 6 | param->decrease.support << 7);
info_packet->sb[5] = param->increase.frame_duration_hex;
info_packet->sb[6] = param->decrease.frame_duration_hex;
}
}
@@ -0,0 +1,31 @@
#
# Copyright 2017 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.
#
#
# Makefile for the 'power' sub-module of DAL.
#
MOD_POWER = power_helpers.o power.o power_abm.o power_psr.o power_replay.o
AMD_DAL_MOD_POWER = $(addprefix $(AMDDALPATH)/modules/power/,$(MOD_POWER))
#$(info ************ DAL POWER MODULE MAKEFILE ************)
AMD_DISPLAY_FILES += $(AMD_DAL_MOD_POWER)
@@ -0,0 +1,503 @@
/*
* Copyright 2016 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 "dm_services.h"
#include "dc.h"
#include "mod_power.h"
#include "core_types.h"
#include "dmcu.h"
#include "abm.h"
#include "power_helpers.h"
#include "dce/dmub_psr.h"
#include "dal_asic_id.h"
#include "link_service.h"
#include <linux/math.h>
#define DC_TRACE_LEVEL_MESSAGE(...) /* do nothing */
#define DC_TRACE_LEVEL_MESSAGEP(...) /* do nothing */
#define MOD_POWER_MAX_CONCURRENT_STREAMS 32
#define SMOOTH_BRIGHTNESS_ADJUSTMENT_TIME_IN_MS 500
#define LOW_REFRESH_RATE_DURATION_US_UPPER_BOUND 25000
/* If system or panel does not report some sort of brightness percent to nits
* mapping, we will use following default values so backlight control using
* nits based interfaces will still work, but might not describe panel
* correctly. In this case percentage based backlight control should ideally
* be used.
* Min = 5 nits
* Max = 300 nits
*/
static const unsigned int pwr_default_min_brightness_millinits = 1000;
static const unsigned int pwr_default_sdr_brightness_millinits = 270000;
static const unsigned int default_ac_backlight_percent = 100;
static const unsigned int default_dc_backlight_percent = 70;
#define MOD_POWER_TO_CORE(mod_power)\
container_of(mod_power, struct core_power, mod_public)
/* Given a specific dc_stream* this function finds its equivalent
* on the core_freesync->map and returns the corresponding index
*/
unsigned int map_index_from_stream(struct core_power *core_power,
const struct dc_stream_state *stream)
{
unsigned int index = 0;
for (index = 0; index < core_power->num_entities; index++) {
if (core_power->map[index].stream == stream)
return index;
}
/* Could not find stream requested, this is not trivial, fix when hit*/
DC_TRACE_LEVEL_MESSAGE(DAL_TRACE_LEVEL_ERROR,
WPP_BIT_FLAG_Firmware_PsrState,
"map index from stream: ERROR: core_power=%p stream=%p",
core_power,
stream);
ASSERT(false);
/* We come here only when we can't map stream index.
* In good cases, this would happen when we attempt to change
* brightness before stream creation, in which case we create a
* dummy stream with index 0.
* With external monitor connected, the index passed from this return
* is 1. Passing anything greater than 0 from here would always point
* to bad memory.
*/
return 0;
}
bool mod_power_hw_init(struct mod_power *mod_power)
{
/* Call backlight initialization */
return mod_power_hw_init_backlight(mod_power);
/* Future: Add other HW init here */
}
struct mod_power *mod_power_create(struct dc *dc,
struct mod_power_init_params *init_params,
unsigned int edp_num)
{
struct core_power *core_power = NULL;
int i = 0;
unsigned int abm_max_config = 0;
unsigned int inst = 0;
bool is_brightness_range_valid = false;
if (dc == NULL)
goto fail_dc_null;
core_power = kzalloc(sizeof(struct core_power), GFP_KERNEL);
if (core_power == NULL)
goto fail_alloc_context;
core_power->edp_num = edp_num;
core_power->map = kzalloc(sizeof(struct power_entity) * MOD_POWER_MAX_CONCURRENT_STREAMS,
GFP_KERNEL);
if (core_power->map == NULL)
goto fail_alloc_map;
for (i = 0; i < MOD_POWER_MAX_CONCURRENT_STREAMS; i++) {
core_power->map[i].stream = NULL;
}
for (i = 0; i < MOD_POWER_MAX_CONCURRENT_STREAMS; i++) {
core_power->map[i].psr_context =
kzalloc(sizeof(struct mod_power_psr_context),
GFP_KERNEL);
if (core_power->map[i].psr_context == NULL)
goto fail_construct;
}
core_power->psr_smu_optimizations_support = init_params->allow_psr_smu_optimizations;
core_power->multi_disp_optimizations_support = init_params->allow_psr_multi_disp_optimizations;
for (inst = 0; inst < edp_num; inst++) {
core_power->bl_prop[inst].min_abm_backlight =
init_params[inst].min_abm_backlight;
core_power->bl_prop[inst].disable_fractional_pwm =
init_params[inst].disable_fractional_pwm;
core_power->bl_prop[inst].use_linear_backlight_curve =
init_params[inst].use_linear_backlight_curve;
core_power->bl_prop[inst].use_nits_based_brightness =
init_params[inst].use_nits_based_brightness;
core_power->bl_prop[inst].backlight_ramping_override =
init_params[inst].backlight_ramping_override;
core_power->bl_prop[inst].backlight_ramping_reduction =
init_params[inst].backlight_ramping_reduction;
core_power->bl_prop[inst].backlight_ramping_start =
init_params[inst].backlight_ramping_start;
core_power->bl_prop[inst].use_custom_backlight_caps =
init_params[inst].use_custom_backlight_caps;
core_power->bl_prop[inst].custom_backlight_caps_config_no =
init_params[inst].custom_backlight_caps_config_no;
// Do not allow less than 101 backlight levels
if (init_params[inst].num_backlight_levels < 101)
core_power->bl_prop[inst].num_backlight_levels = 101;
else
core_power->bl_prop[inst].num_backlight_levels =
init_params[inst].num_backlight_levels;
core_power->bl_prop[inst].backlight_lut = (unsigned int *)
(kzalloc(sizeof(unsigned int) *
core_power->bl_prop[inst].num_backlight_levels, GFP_KERNEL));
if (core_power->bl_prop[inst].backlight_lut == NULL)
goto fail_alloc_backlight_array;
}
core_power->varibright_prop.varibright_active = false;
core_power->varibright_prop.varibright_user_enable =
init_params->def_varibright_enable;
// Table of ABM levels here is 1-4, but level 0 also exists as 'off'
if (init_params->varibright_level <= abm_defines_max_level) {
core_power->varibright_prop.varibright_level =
init_params->varibright_level;
} else {
core_power->varibright_prop.varibright_level = 3;
}
if (init_params->def_varibright_level <= abm_defines_max_level) {
core_power->varibright_prop.def_varibright_level =
init_params->def_varibright_level;
} else {
core_power->varibright_prop.def_varibright_level = 3;
}
// ABM used to contain 4 different configs. There is only 3 since ABM 2.3.
if ((dc->res_pool->dmcu != NULL) && (dc->res_pool->dmcu->dmcu_version.abm_version < 0x23))
abm_max_config = 4;
else
abm_max_config = 3;
if (init_params->abm_config_setting < abm_max_config)
core_power->varibright_prop.varibright_config_setting =
init_params->abm_config_setting;
else
core_power->varibright_prop.varibright_config_setting = 0;
for (inst = 0; inst < edp_num; inst++) {
core_power->bl_prop[inst].backlight_lut[0] = init_params[inst].min_backlight_pwm;
core_power->bl_prop[inst].backlight_lut[
core_power->bl_prop[inst].num_backlight_levels-1] =
init_params[inst].max_backlight_pwm;
core_power->bl_prop[inst].min_backlight_pwm = init_params[inst].min_backlight_pwm;
core_power->bl_prop[inst].max_backlight_pwm = init_params[inst].max_backlight_pwm;
core_power->bl_prop[inst].ac_backlight_percent =
default_ac_backlight_percent;
core_power->bl_prop[inst].dc_backlight_percent =
default_dc_backlight_percent;
core_power->bl_prop[inst].backlight_caps_valid = false;
if (core_power->bl_prop[inst].use_nits_based_brightness) {
core_power->bl_prop[inst].min_brightness_millinits =
init_params[inst].panel_min_millinits;
core_power->bl_prop[inst].max_brightness_millinits =
init_params[inst].panel_max_millinits;
} else {
core_power->bl_prop[inst].min_brightness_millinits =
pwr_default_min_brightness_millinits;
core_power->bl_prop[inst].max_brightness_millinits =
pwr_default_sdr_brightness_millinits;
}
core_power->bl_prop[inst].backlight_range =
core_power->bl_prop[inst].max_backlight_pwm-
core_power->bl_prop[inst].min_backlight_pwm;
core_power->bl_prop[inst].nits_range =
core_power->bl_prop[inst].max_brightness_millinits -
core_power->bl_prop[inst].min_brightness_millinits;
core_power->bl_state[inst].smooth_brightness_enabled = true;
}
/* Check if at least 1 instance in core_power is populated before failing */
for (inst = 0; inst < edp_num; inst++) {
if (core_power->bl_prop[inst].nits_range != 0 && core_power->bl_prop[inst].backlight_range != 0) {
is_brightness_range_valid = true;
break;
}
}
if (!is_brightness_range_valid)
goto fail_bad_brightness_range;
core_power->num_entities = 0;
core_power->dc = dc;
for (inst = 0; inst < edp_num; inst++) {
initialize_backlight_caps(core_power, inst);
core_power->bl_state[inst].backlight_millipercent =
core_power->bl_prop[inst].dc_backlight_percent * 1000;
core_power->bl_state[inst].backlight_pwm = backlight_millipercent_to_pwm(core_power,
core_power->bl_state[inst].backlight_millipercent, inst);
core_power->bl_state[inst].backlight_millinit = backlight_millipercent_to_millinit(core_power,
core_power->bl_state[inst].backlight_millipercent, inst);
}
return &core_power->mod_public;
fail_bad_brightness_range:
fail_alloc_backlight_array:
for (inst = 0; inst < edp_num; inst++)
if (core_power->bl_prop[inst].backlight_lut)
kfree(core_power->bl_prop[inst].backlight_lut);
fail_construct:
for (i = 0; i < MOD_POWER_MAX_CONCURRENT_STREAMS; i++) {
if (core_power->map[i].psr_context)
kfree(core_power->map[i].psr_context);
}
kfree(core_power->map);
fail_alloc_map:
kfree(core_power);
fail_alloc_context:
fail_dc_null:
return NULL;
}
void mod_power_destroy(struct mod_power *mod_power)
{
if (mod_power != NULL) {
unsigned int i;
struct core_power *core_power =
MOD_POWER_TO_CORE(mod_power);
for (i = 0; i < MOD_POWER_MAX_CONCURRENT_STREAMS; i++)
if (core_power->map[i].psr_context)
kfree(core_power->map[i].psr_context);
for (i = 0; i < core_power->num_entities; i++)
if (core_power->map[i].stream)
dc_stream_release(core_power->map[i].stream);
kfree(core_power->map);
for (i = 0; i < MAX_NUM_EDP; i++)
if (core_power->bl_prop[i].backlight_lut)
kfree(core_power->bl_prop[i].backlight_lut);
kfree(core_power);
}
}
bool mod_power_add_stream(struct mod_power *mod_power,
struct dc_stream_state *stream, struct psr_caps *caps)
{
struct core_power *core_power = NULL;
if (mod_power == NULL)
return false;
core_power = MOD_POWER_TO_CORE(mod_power);
if (core_power->num_entities < MOD_POWER_MAX_CONCURRENT_STREAMS) {
dc_stream_retain(stream);
core_power->map[core_power->num_entities].stream = stream;
core_power->map[core_power->num_entities].caps = caps;
// initialize cached PSR params to something "safe" (something that is
// consistent with disabled PSR state)
core_power->map[core_power->num_entities].psr_enabled = 0;
core_power->map[core_power->num_entities].psr_events = psr_event_vsync;
core_power->map[core_power->num_entities].psr_power_opt = 0;
core_power->num_entities++;
return true;
}
DC_TRACE_LEVEL_MESSAGE(DAL_TRACE_LEVEL_ERROR,
WPP_BIT_FLAG_Firmware_PsrState,
"mod_power: add_stream: ERROR: stream=%p num_entities=%u >= MOD_POWER_MAX_CONCURRENT_STREAMS",
stream,
core_power->num_entities);
return false;
}
bool mod_power_remove_stream(struct mod_power *mod_power,
const struct dc_stream_state *stream)
{
unsigned int i = 0;
struct core_power *core_power = NULL;
unsigned int index = 0;
if (mod_power == NULL)
return false;
core_power = MOD_POWER_TO_CORE(mod_power);
if (core_power->num_entities == 0) {
/* trying to remove a stream a second time or have not added yet */
BREAK_TO_DEBUGGER();
DC_TRACE_LEVEL_MESSAGE(DAL_TRACE_LEVEL_ERROR,
WPP_BIT_FLAG_Firmware_PsrState,
"mod_power: remove_stream: ERROR: num_entities=0 stream=%p",
stream);
return false;
}
index = map_index_from_stream(core_power, stream);
if (index >= core_power->num_entities) {
/* trying to remove a stream a second time or have not added yet */
BREAK_TO_DEBUGGER();
DC_TRACE_LEVEL_MESSAGE(DAL_TRACE_LEVEL_ERROR,
WPP_BIT_FLAG_Firmware_PsrState,
"mod_power: remove_stream: ERROR: index=%u >= num_entities=%u stream=%p",
index,
core_power->num_entities,
stream);
return false;
}
dc_stream_release(core_power->map[index].stream);
core_power->map[index].stream = NULL;
/* To remove this entity, shift everything after down */
for (i = index; i < core_power->num_entities - 1; i++) {
core_power->map[i].stream = core_power->map[i + 1].stream;
core_power->map[i].caps = core_power->map[i + 1].caps;
// copy over cached parameters in case they map to PSR capable display
core_power->map[i].psr_enabled = core_power->map[i + 1].psr_enabled;
core_power->map[i].psr_events = core_power->map[i + 1].psr_events;
core_power->map[i].psr_power_opt = core_power->map[i + 1].psr_power_opt;
memcpy(core_power->map[i].psr_context, core_power->map[i + 1].psr_context, sizeof(struct mod_power_psr_context));
memset(core_power->map[i + 1].psr_context, 0, sizeof(struct mod_power_psr_context));
}
core_power->num_entities--;
return true;
}
/*
* Replace_stream should be used when there is a mode set for existing
* display target with a valid stream. In this case might need to retain
* cached PSR state (events, power opt, en/dis) if we are dealing with PSR
* capable display. If mod_power_remove and mod_power_add are used instead,
* then stream may be assigned to a different slot and may end up with
* wrong cached PSR state. It is hard to tell which PSR events should
* persist through mode set or what psr_events should be initialized to, so
* it might be better just to retain them all.
*/
bool mod_power_replace_stream(struct mod_power *mod_power,
const struct dc_stream_state *current_stream,
struct dc_stream_state *new_stream,
struct psr_caps *new_caps)
{
struct core_power *core_power = NULL;
unsigned int index = 0;
if (mod_power == NULL)
return false;
core_power = MOD_POWER_TO_CORE(mod_power);
if (core_power->num_entities == 0) {
/* no streams exist in the table yet */
BREAK_TO_DEBUGGER();
DC_TRACE_LEVEL_MESSAGE(DAL_TRACE_LEVEL_ERROR,
WPP_BIT_FLAG_Firmware_PsrState,
"mod_power: replace_stream: ERROR: num_entities=0 stream=%p",
current_stream);
return false;
}
index = map_index_from_stream(core_power, current_stream);
if (index >= core_power->num_entities) {
/* trying to replace a non-existent stream */
BREAK_TO_DEBUGGER();
DC_TRACE_LEVEL_MESSAGE(DAL_TRACE_LEVEL_ERROR,
WPP_BIT_FLAG_Firmware_PsrState,
"mod_power: replace_stream: ERROR: index=%u >= num_entities=%u stream=%p",
index,
core_power->num_entities,
current_stream);
return false;
}
dc_stream_release(core_power->map[index].stream);
dc_stream_retain(new_stream);
core_power->map[index].stream = new_stream;
core_power->map[index].caps = new_caps;
memset(core_power->map[index].psr_context, 0, sizeof(struct mod_power_psr_context));
return true;
}
bool mod_power_notify_mode_change(struct mod_power *mod_power,
const struct dc_stream_state *stream,
bool is_hdr)
{
unsigned int stream_index = 0;
struct core_power *core_power = NULL;
struct dc_link *link = NULL;
struct dc *dc = NULL;
unsigned int panel_inst = 0;
uint8_t aux_inst = 0;
if ((mod_power == NULL) || (stream == NULL))
return false;
core_power = MOD_POWER_TO_CORE(mod_power);
if (core_power->num_entities == 0)
return false;
stream_index = map_index_from_stream(core_power, stream);
if (stream_index >= core_power->num_entities)
return false;
dc = core_power->dc;
link = dc_stream_get_link(stream);
if (link != NULL && dc_get_edp_link_panel_inst(dc, link, &panel_inst)) {
if (link->ctx->dc->config.dp_connector_no_native_i2c && link->no_ddc_pin) {
aux_inst = (uint8_t)link->aux_hw_inst;
} else {
ASSERT(link->ddc->ddc_pin->hw_info.ddc_channel <= 0xFF);
aux_inst = (uint8_t)link->ddc->ddc_pin->hw_info.ddc_channel;
}
mod_power_update_backlight_on_mode_change(core_power, link, panel_inst, aux_inst, is_hdr);
/* Handle PSR notification */
mod_power_psr_notify_mode_change(mod_power, stream, link, stream_index);
/* Handle Replay notification */
mod_power_replay_notify_mode_change(mod_power, dc, link, stream, stream_index);
}
return true;
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,39 @@
/* 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 "power_helpers.h"
#include "dc/inc/hw/dmcu.h"
#include "dc/inc/hw/abm.h"
#include "dc.h"
#include "core_types.h"
#include "dmub_cmd.h"
#define DIV_ROUNDUP(a, b) (((a)+((b)/2))/(b))
#define bswap16_based_on_endian(big_endian, value) \
((big_endian) ? cpu_to_be16(value) : cpu_to_le16(value))
bool mod_power_only_edp(const struct dc_state *context, const struct dc_stream_state *stream)
{
return context && context->stream_count == 1 && dc_is_embedded_signal(stream->signal);
}
@@ -0,0 +1,211 @@
/* 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
*
*/
#ifndef MODULES_POWER_POWER_HELPERS_H_
#define MODULES_POWER_POWER_HELPERS_H_
#include "dc/inc/hw/dmcu.h"
#include "dc/inc/hw/abm.h"
#include "dc/inc/core_types.h"
#include "mod_power.h"
struct resource_pool;
enum abm_defines {
abm_defines_max_level = 4,
abm_defines_max_config = 4,
};
struct dmcu_iram_parameters {
unsigned int *backlight_lut_array;
unsigned int backlight_lut_array_size;
bool backlight_ramping_override;
unsigned int backlight_ramping_reduction;
unsigned int backlight_ramping_start;
unsigned int min_abm_backlight;
unsigned int set;
};
struct backlight_state {
/* HW uses u16.16 format for backlight PWM */
unsigned int backlight_pwm;
/* DM may call power module to set backlight
* targeting percent brightness
*/
unsigned int backlight_millipercent;
/* DM may call power module to set backlight based on an explicit
* nits value.
*/
unsigned int backlight_millinit;
unsigned int frame_ramp;
bool smooth_brightness_enabled;
bool isHDR;
};
struct power_entity {
struct dc_stream_state *stream;
struct psr_caps *caps;
struct mod_power_psr_context *psr_context;
/*PSR cached properties*/
bool psr_enabled;
unsigned int psr_events;
unsigned int psr_power_opt;
unsigned int replay_events;
};
struct pwr_backlight_properties {
bool use_nits_based_brightness;
bool disable_fractional_pwm;
unsigned int min_abm_backlight;
unsigned int num_backlight_levels;
bool backlight_ramping_override;
unsigned int backlight_ramping_reduction;
unsigned int backlight_ramping_start;
/* Backlight cached properties */
unsigned int ac_backlight_percent;
unsigned int dc_backlight_percent;
/* backlight LUT stored in HW u16.16 format*/
unsigned int *backlight_lut;
unsigned int min_backlight_pwm;
unsigned int max_backlight_pwm;
unsigned int backlight_range;
/* Describes the panel's min and max luminance in millinits measured
* on full white screen, in min and max backlight settings.
*/
unsigned int min_brightness_millinits;
unsigned int max_brightness_millinits;
unsigned int nits_range;
bool backlight_caps_valid;
bool use_custom_backlight_caps;
unsigned int custom_backlight_caps_config_no;
bool use_linear_backlight_curve;
};
struct dmcu_varibright_cached_properties {
unsigned int varibright_config_setting;
unsigned int varibright_level;
unsigned int varibright_hw_level;
unsigned int def_varibright_level;
bool varibright_user_enable;
bool varibright_active;
};
struct core_power {
struct mod_power mod_public;
struct dc *dc;
struct power_entity *map;
struct dmcu_varibright_cached_properties varibright_prop;
struct pwr_backlight_properties bl_prop[MAX_NUM_EDP];
struct backlight_state bl_state[MAX_NUM_EDP];
unsigned int edp_num;
bool psr_smu_optimizations_support;
bool multi_disp_optimizations_support;
unsigned int num_entities;
};
union dmcu_abm_set_bl_params {
struct {
unsigned int gradual_change : 1; /* [0:0] */
unsigned int reserved : 15; /* [15:1] */
unsigned int frame_ramp : 16; /* [31:16] */
} bits;
unsigned int u32All;
};
bool dmcu_load_iram(struct dmcu *dmcu,
struct dmcu_iram_parameters params);
bool dmub_init_abm_config(struct resource_pool *res_pool,
struct dmcu_iram_parameters params,
unsigned int inst);
void init_replay_config(struct dc_link *link, struct replay_config *pr_config);
void set_replay_coasting_vtotal(struct dc_link *link,
enum replay_coasting_vtotal_type type,
uint32_t vtotal);
void set_replay_defer_update_coasting_vtotal(struct dc_link *link,
enum replay_coasting_vtotal_type type,
uint32_t vtotal);
void set_replay_frame_skip_number(struct dc_link *link,
enum replay_coasting_vtotal_type type,
uint32_t coasting_vtotal_refresh_rate_Mhz,
uint32_t flicker_free_refresh_rate_Mhz,
bool is_defer);
void update_replay_coasting_vtotal_from_defer(struct dc_link *link,
enum replay_coasting_vtotal_type type);
void set_replay_low_rr_full_screen_video_src_vtotal(struct dc_link *link, uint16_t vtotal);
void calculate_replay_link_off_frame_count(struct dc_link *link,
uint16_t vtotal, uint16_t htotal);
bool is_psr_su_specific_panel(struct dc_link *link);
void mod_power_calc_psr_configs(struct psr_config *psr_config,
struct dc_link *link,
const struct dc_stream_state *stream);
bool mod_power_only_edp(const struct dc_state *context,
const struct dc_stream_state *stream);
bool psr_su_set_dsc_slice_height(struct dc *dc, struct dc_link *link,
struct dc_stream_state *stream,
struct psr_config *config);
bool fill_custom_backlight_caps(unsigned int config_no,
struct dm_acpi_atif_backlight_caps *caps);
void reset_replay_dsync_error_count(struct dc_link *link);
void change_replay_to_psr(struct dc_link *link);
void change_psr_to_replay(struct dc_link *link);
void initialize_backlight_caps(struct core_power *core_power, unsigned int inst);
unsigned int backlight_millipercent_to_pwm(
struct core_power *core_power, unsigned int millipercent, unsigned int inst);
unsigned int backlight_millipercent_to_millinit(
struct core_power *core_power, unsigned int millipercent, unsigned int inst);
void fill_backlight_level_params(struct core_power *core_power,
struct set_backlight_level_params *backlight_level_params,
int panel_inst, uint8_t aux_inst, unsigned int backlight_pwm,
enum backlight_control_type backlight_control_type,
unsigned int backlight_millinit, unsigned int transition_time_millisec,
bool is_hdr);
bool mod_power_hw_init_backlight(struct mod_power *mod_power);
void mod_power_update_backlight_on_mode_change(
struct core_power *core_power,
struct dc_link *link,
unsigned int panel_inst,
uint8_t aux_inst,
bool is_hdr);
unsigned int map_index_from_stream(struct core_power *core_power,
const struct dc_stream_state *stream);
bool mod_power_psr_notify_mode_change(struct mod_power *mod_power,
const struct dc_stream_state *stream,
struct dc_link *link,
unsigned int stream_index);
void mod_power_replay_notify_mode_change(struct mod_power *mod_power,
struct dc *dc,
struct dc_link *link,
const struct dc_stream_state *stream,
unsigned int stream_index);
#endif /* MODULES_POWER_POWER_HELPERS_H_ */
@@ -0,0 +1,665 @@
// SPDX-License-Identifier: MIT
//
// Copyright 2026 Advanced Micro Devices, Inc.
#include "dm_services.h"
#include "dc.h"
#include "mod_power.h"
#include "core_types.h"
#include "dmcu.h"
#include "abm.h"
#include "power_helpers.h"
#include "dce/dmub_psr.h"
#include "dal_asic_id.h"
#include "link_service.h"
#include <linux/math.h>
#define DC_TRACE_LEVEL_MESSAGE(...) /* do nothing */
#define DC_TRACE_LEVEL_MESSAGEP(...) /* do nothing */
#include "dc/inc/hw/dmcu.h"
#include "dc/inc/hw/abm.h"
#include "dmub_cmd.h"
#define MOD_POWER_TO_CORE(mod_power)\
container_of(mod_power, struct core_power, mod_public)
static unsigned int calc_psr_num_static_frames(unsigned int vsync_rate_hz)
{
/* Initialize fail-safe to 2 static frames. */
unsigned int num_frames_static = 2;
/* Calculate number of frames such that at least 30 ms has passed.
* Round up to ensure the static period is not shorter than 30 ms.
*/
if (vsync_rate_hz != 0)
num_frames_static = DIV_ROUND_UP(30000 * vsync_rate_hz, 1000000);
return num_frames_static;
}
bool mod_power_psr_notify_mode_change(struct mod_power *mod_power,
const struct dc_stream_state *stream,
struct dc_link *link,
unsigned int stream_index)
{
struct core_power *core_power = NULL;
struct dc *dc = NULL;
struct psr_config psr_config = {0};
struct psr_context psr_context = {0};
int active_psr_events = 0;
if ((mod_power == NULL) || (stream == NULL) || (link == NULL))
return false;
core_power = MOD_POWER_TO_CORE(mod_power);
dc = core_power->dc;
// NO num_entities check here - already validated by caller
// stream_index is passed as validated parameter
active_psr_events = core_power->map[stream_index].psr_events;
/* Calculate PSR configurations */
mod_power_calc_psr_configs(&psr_config, link, stream);
psr_config.psr_exit_link_training_required =
core_power->map[stream_index].caps->psr_exit_link_training_required;
if (dc->ctx->asic_id.chip_family >= AMDGPU_FAMILY_GC_11_0_1)
psr_config.allow_smu_optimizations =
core_power->psr_smu_optimizations_support && dc_is_embedded_signal(stream->signal);
else
psr_config.allow_smu_optimizations =
core_power->psr_smu_optimizations_support &&
mod_power_only_edp(dc->current_state, stream);
psr_config.allow_multi_disp_optimizations = core_power->multi_disp_optimizations_support;
psr_config.rate_control_caps = core_power->map[stream_index].caps->rate_control_caps;
if (active_psr_events & psr_event_os_request_force_ffu)
psr_config.os_request_force_ffu = true;
/*
* DSC support:
* DSC slice height value must be 'mod' by su_y_granularity.
* According to Panel Vendor, there might be varied conditions to fulfill.
* Right now, DSC slice height value must be multiple of su_y_granularity.
*
* The value of DSC slice height is determined in DSC Driver but it does not
* propagated out here, so we need to calculate it as below 'slice_height'.
*/
psr_su_set_dsc_slice_height(dc, link,
(struct dc_stream_state *) stream,
&psr_config);
dc_link_setup_psr(link, stream, &psr_config, &psr_context);
return true;
}
static void mod_power_psr_set_power_opt(struct mod_power *mod_power,
struct dc_stream_state *stream,
unsigned int active_psr_events,
bool psr_enable_request)
{
(void)psr_enable_request;
struct core_power *core_power = NULL;
struct dc_link *link = NULL;
unsigned int stream_index = 0;
unsigned int power_opt = 0;
if (!stream)
return;
core_power = MOD_POWER_TO_CORE(mod_power);
stream_index = map_index_from_stream(core_power, stream);
if (!core_power->map[stream_index].caps->psr_version)
return;
link = dc_stream_get_link(stream);
if (active_psr_events == 0) {
/* Static Screen */
power_opt |= (psr_power_opt_smu_opt_static_screen | psr_power_opt_z10_static_screen |
psr_power_opt_ds_disable_allow);
}
/* psr_power_opt_flag is a configuration parameter into the module that determines
* which optimizations to enable during psr
*/
power_opt &= core_power->map[stream_index].caps->psr_power_opt_flag;
if (core_power->map[stream_index].psr_power_opt != power_opt) {
DC_TRACE_LEVEL_MESSAGE(DAL_TRACE_LEVEL_VERBOSE,
WPP_BIT_FLAG_Firmware_PsrState,
"mod_power set_power_opt: psr_power_opt=0x%04x, power_opt=0x%04x active_psr_events=0x%04x, psr_power_opt_flag=0x%04x",
core_power->map[stream_index].psr_power_opt,
power_opt,
active_psr_events,
core_power->map[stream_index].caps->psr_power_opt_flag);
dc_link_set_psr_allow_active(link, NULL, false, false, &power_opt);
core_power->map[stream_index].psr_power_opt = power_opt;
}
}
static bool set_psr_enable(struct mod_power *mod_power,
struct dc_stream_state *stream,
bool psr_enable,
bool wait,
bool force_static)
{
struct core_power *core_power = NULL;
enum dc_psr_state state = PSR_STATE0;
unsigned int retry_count;
const unsigned int max_retry = 1000;
struct dc_link *link = NULL;
if (mod_power == NULL)
return false;
core_power = MOD_POWER_TO_CORE(mod_power);
if (core_power->num_entities == 0) {
DC_TRACE_LEVEL_MESSAGE(DAL_TRACE_LEVEL_ERROR,
WPP_BIT_FLAG_Firmware_PsrState,
"set psr enable: ERROR: stream=%p num_entities=%u",
stream,
core_power->num_entities);
return false;
}
if (psr_enable) {
unsigned int vsync_rate_hz;
struct dc_static_screen_params params = {0};
vsync_rate_hz = (unsigned int)div_u64(div_u64((
stream->timing.pix_clk_100hz * 100),
stream->timing.v_total),
stream->timing.h_total);
params.triggers.cursor_update = true;
params.triggers.overlay_update = true;
params.triggers.surface_update = true;
params.num_frames = calc_psr_num_static_frames(vsync_rate_hz);
DC_TRACE_LEVEL_MESSAGE(DAL_TRACE_LEVEL_INFORMATION,
WPP_BIT_FLAG_Firmware_PsrState,
"set psr enable: CALCS: pix_clk_100hz=%u v_total=%u h_total=%u vsync_rate_hz=%u num_frames=%u",
stream->timing.pix_clk_100hz,
stream->timing.v_total,
stream->timing.h_total,
vsync_rate_hz,
params.num_frames);
dc_stream_set_static_screen_params(core_power->dc,
&stream, 1,
&params);
}
link = dc_stream_get_link(stream);
if (!dc_link_set_psr_allow_active(link, &psr_enable, false, force_static, NULL)) {
DC_TRACE_LEVEL_MESSAGE(DAL_TRACE_LEVEL_ERROR,
WPP_BIT_FLAG_Firmware_PsrState,
"set psr enable: ERROR: stream=%p link=%p psr_enable=%d",
stream,
link,
psr_enable);
return false;
}
if (wait == true) {
DC_TRACE_LEVEL_MESSAGE(DAL_TRACE_LEVEL_INFORMATION,
WPP_BIT_FLAG_Firmware_PsrState,
"set psr enable: BEGIN WAIT: psr_enable=%d",
(int)psr_enable);
for (retry_count = 0; retry_count <= max_retry; retry_count++) {
dc_link_get_psr_state(link, &state);
if (psr_enable) {
if (state != PSR_STATE0 &&
(!force_static || state == PSR_STATE3))
break;
} else {
if (state == PSR_STATE0)
break;
}
udelay(500);
}
DC_TRACE_LEVEL_MESSAGE(DAL_TRACE_LEVEL_INFORMATION,
WPP_BIT_FLAG_Firmware_PsrState,
"set psr enable: END WAIT: psr_enable=%d",
(int)psr_enable);
/* assert if max retry hit */
if (retry_count >= max_retry) {
ASSERT(0);
DC_TRACE_LEVEL_MESSAGE(DAL_TRACE_LEVEL_ERROR,
WPP_BIT_FLAG_Firmware_PsrState,
"set psr enable: ERROR: retry_count=%u: Unexpectedly long wait for PSR state change.",
retry_count);
}
} else {
DC_TRACE_LEVEL_MESSAGE(DAL_TRACE_LEVEL_INFORMATION,
WPP_BIT_FLAG_Firmware_PsrState,
"set psr enable: PSR state change initiated (wait=false): psr_enable=%d",
(int)psr_enable);
}
return true;
}
bool mod_power_get_psr_event(struct mod_power *mod_power,
struct dc_stream_state *stream,
unsigned int *active_psr_events)
{
struct core_power *core_power = NULL;
unsigned int stream_index = 0;
if (mod_power == NULL)
return false;
core_power = MOD_POWER_TO_CORE(mod_power);
if (core_power->num_entities == 0)
return false;
stream_index = map_index_from_stream(core_power, stream);
if (!core_power->map[stream_index].caps->psr_version)
return false;
*active_psr_events = core_power->map[stream_index].psr_events;
return true;
}
bool mod_power_set_psr_event(struct mod_power *mod_power,
struct dc_stream_state *stream, bool set_event,
enum psr_event event, bool wait)
{
struct core_power *core_power = NULL;
unsigned int stream_index = 0;
unsigned int active_psr_events = 0;
bool psr_enable_request = false;
bool force_static = false;
if (mod_power == NULL || stream == NULL)
return false;
core_power = MOD_POWER_TO_CORE(mod_power);
stream_index = map_index_from_stream(core_power, stream);
if (core_power->num_entities == 0) {
DC_TRACE_LEVEL_MESSAGE(DAL_TRACE_LEVEL_ERROR,
WPP_BIT_FLAG_Firmware_PsrState,
"mod_power set_psr_event: ERROR: stream=%p event=%d num_entities=%u",
stream,
(int)event,
core_power->num_entities);
return false;
}
if (!core_power->map[stream_index].caps->psr_version)
return false;
if (set_event)
core_power->map[stream_index].psr_events |= event;
else
core_power->map[stream_index].psr_events &= ~event;
active_psr_events = core_power->map[stream_index].psr_events;
// ignore other events when we're in forced psr enabled state
if (active_psr_events & psr_event_dynamic_display_switch &&
event != psr_event_dynamic_display_switch)
return false;
// ignore other events when we're in forced psr enabled state
if (active_psr_events & psr_event_os_override_hold &&
event != psr_event_os_override_hold)
return false;
// ignore other events when we're in forced psr enabled state
// dds events need to be processed while in dynamic_link_rate_control
if (active_psr_events & psr_event_dynamic_link_rate_control &&
event != psr_event_dynamic_link_rate_control &&
event != psr_event_dds_defer_stream_enable &&
event != psr_event_dynamic_display_switch)
return false;
if (active_psr_events & (psr_event_test_harness_disable_psr | psr_event_os_request_disable))
psr_enable_request = false;
else if (active_psr_events & psr_event_pause)
psr_enable_request = false;
else if (active_psr_events & psr_event_test_harness_enable_psr)
psr_enable_request = true;
else if (active_psr_events & psr_event_dynamic_display_switch) {
psr_enable_request = true;
force_static = true;
} else if (active_psr_events & psr_event_dynamic_link_rate_control) {
psr_enable_request = true;
force_static = true;
} else if (active_psr_events & psr_event_edp_panel_off_disable_psr)
psr_enable_request = false;
else if (active_psr_events & (psr_event_hw_programming |
psr_event_defer_enable |
psr_event_dds_defer_stream_enable |
psr_event_vrr_transition |
psr_event_immediate_flip))
psr_enable_request = false;
else if (active_psr_events & psr_event_big_screen_video)
psr_enable_request = true;
else if (active_psr_events & psr_event_full_screen)
psr_enable_request = false;
else if (active_psr_events & psr_event_mpo_video_selective_update)
psr_enable_request = true;
else if (active_psr_events & psr_event_vsync)
psr_enable_request = false;
else if (active_psr_events & psr_event_crc_window_active)
psr_enable_request = false;
else
psr_enable_request = true;
DC_TRACE_LEVEL_MESSAGE(DAL_TRACE_LEVEL_VERBOSE,
WPP_BIT_FLAG_Firmware_PsrState,
"mod_power set_psr_event: before: psr_enabled=%d -> request: set_event=%d event=0x%04x -> result: psr_events=0x%04x psr_enable_request=%d",
(int)core_power->map[stream_index].psr_enabled,
(int)set_event,
(unsigned int)event,
(unsigned int)core_power->map[stream_index].psr_events,
(int)psr_enable_request);
mod_power_psr_set_power_opt(mod_power, stream, active_psr_events, psr_enable_request);
if (core_power->map[stream_index].psr_enabled != psr_enable_request || force_static) {
if (set_psr_enable(mod_power, stream, psr_enable_request, wait, force_static))
core_power->map[stream_index].psr_enabled = psr_enable_request;
}
return true;
}
bool mod_power_get_psr_state(struct mod_power *mod_power,
const struct dc_stream_state *stream,
enum dc_psr_state *state)
{
struct core_power *core_power = NULL;
const struct dc_link *link = NULL;
if (!stream)
return false;
if (mod_power == NULL)
return false;
core_power = MOD_POWER_TO_CORE(mod_power);
if (core_power->num_entities == 0)
return false;
link = dc_stream_get_link(stream);
return dc_link_get_psr_state(link, state);
}
bool mod_power_get_psr_enabled_status(struct mod_power *mod_power,
const struct dc_stream_state *stream,
bool *psr_enabled)
{
struct core_power *core_power = NULL;
unsigned int stream_index = 0;
if (mod_power == NULL)
return false;
core_power = MOD_POWER_TO_CORE(mod_power);
if (core_power->num_entities == 0)
return false;
stream_index = map_index_from_stream(core_power, stream);
if (!core_power->map[stream_index].caps->psr_version)
return false;
*psr_enabled = core_power->map[stream_index].psr_enabled;
return true;
}
void mod_power_psr_residency(struct mod_power *mod_power,
const struct dc_stream_state *stream,
unsigned int *residency,
const uint8_t mode)
{
struct core_power *core_power = NULL;
const struct dc_link *link = NULL;
if (!stream)
return;
if (mod_power == NULL)
return;
core_power = MOD_POWER_TO_CORE(mod_power);
if (core_power->num_entities == 0)
return;
link = dc_stream_get_link(stream);
if (link != NULL)
link->dc->link_srv->edp_get_psr_residency(link, residency, mode);
}
bool mod_power_psr_get_active_psr_events(struct mod_power *mod_power,
const struct dc_stream_state *stream, unsigned int *active_psr_events)
{
struct core_power *core_power = NULL;
unsigned int stream_index = 0;
if (!stream)
return false;
if (mod_power == NULL)
return false;
if (active_psr_events == NULL)
return false;
core_power = MOD_POWER_TO_CORE(mod_power);
if (core_power->num_entities == 0)
return false;
stream_index = map_index_from_stream(core_power, stream);
*active_psr_events = core_power->map[stream_index].psr_events;
return true;
}
bool mod_power_psr_set_sink_vtotal_in_psr_active(struct mod_power *mod_power,
const struct dc_stream_state *stream,
uint16_t psr_vtotal_idle,
uint16_t psr_vtotal_su)
{
struct core_power *core_power = NULL;
unsigned int stream_index = 0;
const struct dc_link *link = NULL;
if (!stream)
return false;
if (mod_power == NULL)
return false;
core_power = MOD_POWER_TO_CORE(mod_power);
if (core_power->num_entities == 0)
return false;
stream_index = map_index_from_stream(core_power, stream);
if (!core_power->map[stream_index].caps->psr_version)
return false;
link = dc_stream_get_link(stream);
return link->dc->link_srv->edp_set_sink_vtotal_in_psr_active(
link, psr_vtotal_idle, psr_vtotal_su);
}
/*
* is_psr_su_specific_panel() - check if sink is AMD vendor-specific PSR-SU
* supported eDP device.
*
* @link: dc link pointer
*
* Return: true if AMDGPU vendor specific PSR-SU eDP panel
*/
bool is_psr_su_specific_panel(struct dc_link *link)
{
bool isPSRSUSupported = false;
struct dpcd_caps *dpcd_caps = &link->dpcd_caps;
if (dpcd_caps->edp_rev >= DP_EDP_14) {
if (dpcd_caps->psr_info.psr_version >= DP_PSR2_WITH_Y_COORD_ET_SUPPORTED)
isPSRSUSupported = true;
/*
* Some panels will report PSR capabilities over additional DPCD bits.
* Such panels are approved despite reporting only PSR v3, as long as
* the additional bits are reported.
*/
if (dpcd_caps->sink_dev_id == DP_BRANCH_DEVICE_ID_001CF8) {
/*
* This is the temporary workaround to disable PSRSU when system turned on
* DSC function on the sepcific sink.
*/
if (dpcd_caps->psr_info.psr_version < DP_PSR2_WITH_Y_COORD_IS_SUPPORTED)
isPSRSUSupported = false;
else if (dpcd_caps->dsc_caps.dsc_basic_caps.fields.dsc_support.DSC_SUPPORT &&
((dpcd_caps->sink_dev_id_str[1] == 0x08 && dpcd_caps->sink_dev_id_str[0] == 0x08) ||
(dpcd_caps->sink_dev_id_str[1] == 0x08 && dpcd_caps->sink_dev_id_str[0] == 0x07)))
isPSRSUSupported = false;
else if (dpcd_caps->sink_dev_id_str[1] == 0x08 && dpcd_caps->sink_dev_id_str[0] == 0x03)
isPSRSUSupported = false;
else if (dpcd_caps->sink_dev_id_str[1] == 0x08 && dpcd_caps->sink_dev_id_str[0] == 0x01)
isPSRSUSupported = false;
else if (dpcd_caps->psr_info.force_psrsu_cap == 0x1)
isPSRSUSupported = true;
}
}
return isPSRSUSupported;
}
/**
* mod_power_calc_psr_configs() - calculate/update generic psr configuration fields.
* @psr_config: [output], psr configuration structure to be updated
* @link: [input] dc link pointer
* @stream: [input] dc stream state pointer
*
* calculate and update the psr configuration fields that are not DM specific, i.e. such
* fields which are based on DPCD caps or timing information. To setup PSR in DMUB FW,
* this helper is assumed to be called before the call of the DC helper dc_link_setup_psr().
*
* PSR config fields to be updated within the helper:
* - psr_rfb_setup_time
* - psr_sdp_transmit_line_num_deadline
* - line_time_in_us
* - su_y_granularity
* - su_granularity_required
* - psr_frame_capture_indication_req
* - psr_exit_link_training_required
*
* PSR config fields that are DM specific and NOT updated within the helper:
* - allow_smu_optimizations
* - allow_multi_disp_optimizations
*/
void mod_power_calc_psr_configs(struct psr_config *psr_config,
struct dc_link *link,
const struct dc_stream_state *stream)
{
unsigned int num_vblank_lines = 0;
unsigned int vblank_time_in_us = 0;
unsigned int sdp_tx_deadline_in_us = 0;
unsigned int line_time_in_us = 0;
struct dpcd_caps *dpcd_caps = &link->dpcd_caps;
const int psr_setup_time_step_in_us = 55; /* refer to eDP spec DPCD 0x071h */
/* timing parameters */
num_vblank_lines = stream->timing.v_total -
stream->timing.v_addressable -
stream->timing.v_border_top -
stream->timing.v_border_bottom;
vblank_time_in_us = (stream->timing.h_total * num_vblank_lines * 1000) / (stream->timing.pix_clk_100hz / 10);
line_time_in_us = ((stream->timing.h_total * 1000) / (stream->timing.pix_clk_100hz / 10)) + 1;
/**
* psr configuration fields
*
* as per eDP 1.5 pg. 377 of 459, DPCD 0x071h bits [3:1], psr setup time bits interpreted as below
* 000b <--> 330 us (default)
* 001b <--> 275 us
* 010b <--> 220 us
* 011b <--> 165 us
* 100b <--> 110 us
* 101b <--> 055 us
* 110b <--> 000 us
*/
psr_config->psr_rfb_setup_time =
(6 - dpcd_caps->psr_info.psr_dpcd_caps.bits.PSR_SETUP_TIME) * psr_setup_time_step_in_us;
if (psr_config->psr_rfb_setup_time > vblank_time_in_us) {
link->psr_settings.psr_frame_capture_indication_req = true;
link->psr_settings.psr_sdp_transmit_line_num_deadline = num_vblank_lines;
} else {
sdp_tx_deadline_in_us = vblank_time_in_us - psr_config->psr_rfb_setup_time;
/* Set the last possible line SDP may be transmitted without violating the RFB setup time */
link->psr_settings.psr_frame_capture_indication_req = false;
link->psr_settings.psr_sdp_transmit_line_num_deadline = sdp_tx_deadline_in_us / line_time_in_us;
}
psr_config->psr_sdp_transmit_line_num_deadline = link->psr_settings.psr_sdp_transmit_line_num_deadline;
psr_config->line_time_in_us = line_time_in_us;
psr_config->su_y_granularity = dpcd_caps->psr_info.psr2_su_y_granularity_cap;
psr_config->su_granularity_required = dpcd_caps->psr_info.psr_dpcd_caps.bits.SU_GRANULARITY_REQUIRED;
psr_config->psr_frame_capture_indication_req = link->psr_settings.psr_frame_capture_indication_req;
psr_config->psr_exit_link_training_required =
!link->dpcd_caps.psr_info.psr_dpcd_caps.bits.LINK_TRAINING_ON_EXIT_NOT_REQUIRED;
}
bool psr_su_set_dsc_slice_height(struct dc *dc, struct dc_link *link,
struct dc_stream_state *stream,
struct psr_config *config)
{
uint32_t pic_height;
uint32_t slice_height;
config->dsc_slice_height = 0;
if (!(link->connector_signal & SIGNAL_TYPE_EDP) ||
!dc->caps.edp_dsc_support ||
link->panel_config.dsc.disable_dsc_edp ||
!link->dpcd_caps.dsc_caps.dsc_basic_caps.fields.dsc_support.DSC_SUPPORT ||
!stream->timing.dsc_cfg.num_slices_v)
return true;
pic_height = stream->timing.v_addressable +
stream->timing.v_border_top + stream->timing.v_border_bottom;
if (stream->timing.dsc_cfg.num_slices_v == 0)
return false;
slice_height = pic_height / stream->timing.dsc_cfg.num_slices_v;
config->dsc_slice_height = (uint16_t)slice_height;
if (slice_height) {
if (config->su_y_granularity &&
(slice_height % config->su_y_granularity)) {
ASSERT(0);
return false;
}
}
return true;
}
@@ -0,0 +1,911 @@
// SPDX-License-Identifier: MIT
//
// Copyright 2026 Advanced Micro Devices, Inc.
#include "dm_services.h"
#include "dc.h"
#include "mod_power.h"
#include "core_types.h"
#include "dmcu.h"
#include "abm.h"
#include "power_helpers.h"
#include "dce/dmub_psr.h"
#include "dal_asic_id.h"
#include "link_service.h"
#include <linux/math.h>
#define DC_TRACE_LEVEL_MESSAGE(...) /* do nothing */
#define DC_TRACE_LEVEL_MESSAGEP(...) /* do nothing */
#include "power_helpers.h"
#include "dc/inc/hw/dmcu.h"
#include "dc/inc/hw/abm.h"
#include "dc.h"
#include "core_types.h"
#include "dmub_cmd.h"
#define MOD_POWER_TO_CORE(mod_power)\
container_of(mod_power, struct core_power, mod_public)
#define LOW_REFRESH_RATE_DURATION_US_UPPER_BOUND 25000
static bool mod_power_set_replay_active(struct dc_stream_state *stream,
bool replay_active,
bool wait,
bool force_static)
{
uint64_t state;
unsigned int retry_count;
const unsigned int max_retry = 1000;
struct dc_link *link = NULL;
if (!stream)
return false;
link = dc_stream_get_link(stream);
if (!link)
return false;
if (!dc_link_set_replay_allow_active(link, &replay_active, false, force_static, NULL))
return false;
if (wait == true) {
for (retry_count = 0; retry_count <= max_retry; retry_count++) {
dc_link_get_replay_state(link, &state);
if (replay_active) {
if (state != REPLAY_STATE_0 &&
(!force_static || state == REPLAY_STATE_3))
break;
} else {
if (state == REPLAY_STATE_0)
break;
}
udelay(500);
}
/* assert if max retry hit */
if (retry_count >= max_retry)
ASSERT(0);
} else {
/* To-do: Add trace log */
}
return true;
}
static unsigned int mod_power_replay_setup_power_opt(struct dc_link *link,
unsigned int active_replay_events, bool is_ultra_sleep_mode)
{
unsigned int power_opt = 0;
if (is_ultra_sleep_mode) {
/* Static Screen */
power_opt |= (replay_power_opt_smu_opt_static_screen | replay_power_opt_z10_static_screen);
} else if (active_replay_events & replay_event_test_harness_ultra_sleep) {
power_opt |= replay_power_opt_z10_static_screen;
}
/* replay_power_opt_flag is a configuration parameter into the module that determines
* which optimizations to enable during replay
*/
power_opt &= link->replay_settings.config.replay_power_opt_supported;
return power_opt;
}
static bool mod_power_replay_set_power_opt(struct mod_power *mod_power,
struct dc_stream_state *stream,
unsigned int active_replay_events,
bool is_ultra_sleep_mode)
{
(void)mod_power;
struct dc_link *link = NULL;
unsigned int power_opt = 0;
if (!stream)
return false;
link = dc_stream_get_link(stream);
if (!link || !link->replay_settings.replay_feature_enabled)
return false;
power_opt = mod_power_replay_setup_power_opt(link, active_replay_events, is_ultra_sleep_mode);
if (!dc_link_set_replay_allow_active(link, NULL, false, false, &power_opt))
return false;
return true;
}
bool mod_power_get_replay_event(struct mod_power *mod_power,
struct dc_stream_state *stream,
unsigned int *active_replay_events)
{
struct core_power *core_power = NULL;
unsigned int stream_index = 0;
if (mod_power == NULL)
return false;
core_power = MOD_POWER_TO_CORE(mod_power);
if (core_power->num_entities == 0)
return false;
stream_index = map_index_from_stream(core_power, stream);
*active_replay_events = core_power->map[stream_index].replay_events;
return true;
}
static bool mod_power_update_replay_active_status(unsigned int active_replay_events,
struct dc_link *link, uint32_t *coasting_vtotal, bool *is_full_screen_video,
bool *is_ultra_sleep_mode, uint16_t *frame_skip_number, bool *is_video_playback)
{
if (!link || !coasting_vtotal || !is_full_screen_video || !is_video_playback)
return false;
// Check coasting_vtotal_table has been updated.
if (!link->replay_settings.coasting_vtotal_table[PR_COASTING_TYPE_STATIC]
|| !link->replay_settings.coasting_vtotal_table[PR_COASTING_TYPE_NOM])
return false;
unsigned int replay_enable_option =
link->replay_settings.config.replay_enable_option;
/* TODO: To support test harness and DDS event */
*coasting_vtotal = link->replay_settings.coasting_vtotal_table[PR_COASTING_TYPE_NOM];
ASSERT(link->replay_settings.frame_skip_number_table[PR_COASTING_TYPE_NOM] <= 0xFFFF);
*frame_skip_number = (uint16_t)link->replay_settings.frame_skip_number_table[PR_COASTING_TYPE_NOM];
link->replay_settings.config.replay_timing_sync_supported = false;
*is_full_screen_video = false;
*is_ultra_sleep_mode = false;
*is_video_playback = false;
/* DSAT test scenario */
if (active_replay_events & replay_event_test_harness_mode) {
if (link->replay_settings.coasting_vtotal_table[PR_COASTING_TYPE_TEST_HARNESS])
*coasting_vtotal =
link->replay_settings.coasting_vtotal_table[PR_COASTING_TYPE_TEST_HARNESS];
if (link->replay_settings.frame_skip_number_table[PR_COASTING_TYPE_TEST_HARNESS]) {
ASSERT(link->replay_settings.frame_skip_number_table[PR_COASTING_TYPE_TEST_HARNESS] <= 0xFFFF);
*frame_skip_number =
(uint16_t)link->replay_settings.frame_skip_number_table[PR_COASTING_TYPE_TEST_HARNESS];
}
/* During the ultra sleep mode testing, disable the timing sync in short vblank mode */
if (active_replay_events & (replay_event_test_harness_enable_replay)) {
if ((active_replay_events & replay_event_test_harness_ultra_sleep) &&
!link->replay_settings.config.replay_support_fast_resync_in_ultra_sleep_mode)
link->replay_settings.config.replay_timing_sync_supported = false;
return true;
} else
return false;
} else if (active_replay_events & (replay_event_test_harness_enable_replay)) {
if (link->replay_settings.coasting_vtotal_table[PR_COASTING_TYPE_TEST_HARNESS])
*coasting_vtotal = link->replay_settings.coasting_vtotal_table[PR_COASTING_TYPE_TEST_HARNESS];
if (link->replay_settings.frame_skip_number_table[PR_COASTING_TYPE_TEST_HARNESS]) {
uint32_t frame_skip_val =
link->replay_settings.frame_skip_number_table[PR_COASTING_TYPE_TEST_HARNESS];
ASSERT(frame_skip_val <= 0xFFFF);
*frame_skip_number = (uint16_t)frame_skip_val;
}
/* During the ultra sleep mode testing, disable the timing sync in short vblank mode */
if ((active_replay_events & replay_event_test_harness_ultra_sleep) &&
!link->replay_settings.config.replay_support_fast_resync_in_ultra_sleep_mode) {
link->replay_settings.config.replay_timing_sync_supported = false;
}
return true;
} else if (active_replay_events &
(replay_event_test_harness_disable_replay | replay_event_os_request_disable)) {
// set last set coasting vtotal
if (link->replay_settings.coasting_vtotal_table[PR_COASTING_TYPE_TEST_HARNESS])
*coasting_vtotal = link->replay_settings.coasting_vtotal_table[PR_COASTING_TYPE_TEST_HARNESS];
if (link->replay_settings.frame_skip_number_table[PR_COASTING_TYPE_TEST_HARNESS]) {
uint32_t frame_skip_val =
link->replay_settings.frame_skip_number_table[PR_COASTING_TYPE_TEST_HARNESS];
ASSERT(frame_skip_val <= 0xFFFF);
*frame_skip_number = (uint16_t)frame_skip_val;
}
return false;
}
/* Inactive conditions */
if (active_replay_events & (replay_event_edp_panel_off_disable_psr |
replay_event_hw_programming |
replay_event_vrr |
replay_event_immediate_flip |
replay_event_prepare_vtotal |
replay_event_vrr_transition |
replay_event_pause |
replay_event_disable_replay_while_DPMS |
replay_event_sleep_resume |
replay_event_disable_in_AC |
replay_event_disable_replay_while_detect_display |
replay_event_infopacket |
replay_event_crc_window_active))
return false;
// Full screen scenario
if (active_replay_events & replay_event_full_screen) {
if (!(replay_enable_option & pr_enable_option_full_screen))
return false;
}
/* Full screen video scenario */
if (active_replay_events & replay_event_big_screen_video) {
link->replay_settings.config.replay_timing_sync_supported = false;
if (replay_enable_option & pr_enable_option_full_screen_video_coasting) {
unsigned int fsn_vid =
link->replay_settings.frame_skip_number_table[PR_COASTING_TYPE_FULL_SCREEN_VIDEO];
*coasting_vtotal =
link->replay_settings.coasting_vtotal_table[PR_COASTING_TYPE_FULL_SCREEN_VIDEO];
ASSERT(fsn_vid <= 0xFFFF);
*frame_skip_number = (uint16_t)fsn_vid;
}
*is_video_playback = true;
if ((replay_enable_option & pr_enable_option_full_screen_video) &&
(replay_enable_option & pr_enable_option_full_screen_video_coasting)) {
*is_full_screen_video = true;
return true;
} else
return false;
}
/* MPO video scenario
* Some of the cases may contain a full screen UI layer in MPO video scenario which is
* not the expected case to enable Replay.
*/
if ((active_replay_events & replay_event_mpo_video_selective_update) &&
!(active_replay_events & replay_event_full_screen)) {
link->replay_settings.config.replay_timing_sync_supported = false;
if (replay_enable_option & pr_enable_option_mpo_video_coasting) {
*coasting_vtotal = link->replay_settings.coasting_vtotal_table[PR_COASTING_TYPE_NOM];
{
uint32_t frame_skip_val =
link->replay_settings.frame_skip_number_table[PR_COASTING_TYPE_NOM];
ASSERT(frame_skip_val <= 0xFFFF);
*frame_skip_number = (uint16_t)frame_skip_val;
}
}
*is_video_playback = true;
if (replay_enable_option & pr_enable_option_mpo_video)
return true;
else
return false;
}
/* Static screen scenario */
if (!(active_replay_events & replay_event_vsync)) {
if (replay_enable_option & pr_enable_option_static_screen_coasting) {
// Do not adjust eDP refresh rate if static screen + normal sleep mode
if ((!(link->replay_settings.config.replay_power_opt_supported &
replay_power_opt_z10_static_screen)) ||
(active_replay_events & replay_event_cursor_updating)) {
// normal sleep mode
*coasting_vtotal =
link->replay_settings.coasting_vtotal_table[PR_COASTING_TYPE_NOM];
{
uint32_t frame_skip_val =
link->replay_settings.frame_skip_number_table[PR_COASTING_TYPE_NOM];
ASSERT(frame_skip_val <= 0xFFFF);
*frame_skip_number = (uint16_t)frame_skip_val;
}
} else {
// ultra sleep mode
*coasting_vtotal =
link->replay_settings.coasting_vtotal_table[PR_COASTING_TYPE_STATIC];
{
uint32_t frame_skip_val =
link->replay_settings.frame_skip_number_table[PR_COASTING_TYPE_STATIC];
ASSERT(frame_skip_val <= 0xFFFF);
*frame_skip_number = (uint16_t)frame_skip_val;
}
*is_ultra_sleep_mode = true;
}
}
if (replay_enable_option & pr_enable_option_static_screen) {
if (!link->replay_settings.config.replay_support_fast_resync_in_ultra_sleep_mode)
link->replay_settings.config.replay_timing_sync_supported = false;
return true;
} else
return false;
}
/* General UI scenario */
if (active_replay_events & replay_event_general_ui) {
if (replay_enable_option & pr_enable_option_general_ui)
return true;
else
return false;
}
return false;
}
bool mod_power_replay_set_coasting_vtotal(struct mod_power *mod_power,
const struct dc_stream_state *stream,
uint32_t coasting_vtotal,
uint16_t frame_skip_number)
{
struct core_power *core_power = NULL;
struct dc_link *link = NULL;
if (!stream)
return false;
link = dc_stream_get_link(stream);
if (!link || !link->replay_settings.replay_feature_enabled)
return false;
if (mod_power == NULL)
return false;
core_power = MOD_POWER_TO_CORE(mod_power);
if (core_power->num_entities == 0)
return false;
return link->dc->link_srv->edp_set_coasting_vtotal(link, coasting_vtotal, frame_skip_number);
}
void mod_power_replay_set_timing_sync_supported(struct mod_power *mod_power,
const struct dc_stream_state *stream)
{
struct core_power *core_power = NULL;
struct dc_link *link = NULL;
unsigned int stream_index = 0;
union dmub_replay_cmd_set cmd_data = { 0 };
if (!stream || mod_power == NULL)
return;
core_power = MOD_POWER_TO_CORE(mod_power);
if (core_power->num_entities == 0)
return;
stream_index = map_index_from_stream(core_power, stream);
if (stream_index > core_power->num_entities) //invalid index
return;
link = dc_stream_get_link(stream);
if (!link || !link->replay_settings.replay_feature_enabled)
return;
cmd_data.sync_data.timing_sync_supported = link->replay_settings.config.replay_timing_sync_supported;
link->dc->link_srv->edp_send_replay_cmd(link, Replay_Set_Timing_Sync_Supported,
&cmd_data);
}
void mod_power_replay_disabled_adaptive_sync_sdp(struct mod_power *mod_power,
const struct dc_stream_state *stream, bool force_disabled)
{
struct core_power *core_power = NULL;
struct dc_link *link = NULL;
unsigned int stream_index = 0;
union dmub_replay_cmd_set cmd_data = { 0 };
if (!stream || mod_power == NULL)
return;
core_power = MOD_POWER_TO_CORE(mod_power);
if (core_power->num_entities == 0)
return;
stream_index = map_index_from_stream(core_power, stream);
if (stream_index > core_power->num_entities) //invalid index
return;
link = dc_stream_get_link(stream);
if (!link || !link->replay_settings.replay_feature_enabled)
return;
cmd_data.disabled_adaptive_sync_sdp_data.force_disabled = force_disabled;
link->dc->link_srv->edp_send_replay_cmd(link, Replay_Disabled_Adaptive_Sync_SDP,
&cmd_data);
}
static void mod_power_replay_set_general_cmd(struct mod_power *mod_power,
const struct dc_stream_state *stream,
const enum dmub_cmd_replay_general_subtype general_cmd_type,
const uint32_t param1, const uint32_t param2)
{
struct core_power *core_power = NULL;
struct dc_link *link = NULL;
unsigned int stream_index = 0;
union dmub_replay_cmd_set cmd_data = { 0 };
if (!stream || mod_power == NULL)
return;
core_power = MOD_POWER_TO_CORE(mod_power);
if (core_power->num_entities == 0)
return;
stream_index = map_index_from_stream(core_power, stream);
if (stream_index > core_power->num_entities) //invalid index
return;
link = dc_stream_get_link(stream);
if (!link || !link->replay_settings.replay_feature_enabled)
return;
cmd_data.set_general_cmd_data.subtype = general_cmd_type;
cmd_data.set_general_cmd_data.param1 = param1;
cmd_data.set_general_cmd_data.param2 = param2;
link->dc->link_srv->edp_send_replay_cmd(link, Replay_Set_General_Cmd,
&cmd_data);
}
void mod_power_replay_disabled_desync_error_detection(struct mod_power *mod_power,
const struct dc_stream_state *stream, bool force_disabled)
{
mod_power_replay_set_general_cmd(mod_power, stream,
REPLAY_GENERAL_CMD_DISABLED_DESYNC_ERROR_DETECTION,
force_disabled, 0);
}
static void mod_power_replay_set_pseudo_vtotal(struct mod_power *mod_power,
const struct dc_stream_state *stream, uint16_t vtotal)
{
struct core_power *core_power = NULL;
struct dc_link *link = NULL;
unsigned int stream_index = 0;
union dmub_replay_cmd_set cmd_data = { 0 };
if (!stream || mod_power == NULL)
return;
core_power = MOD_POWER_TO_CORE(mod_power);
if (core_power->num_entities == 0)
return;
stream_index = map_index_from_stream(core_power, stream);
if (stream_index > core_power->num_entities) //invalid index
return;
link = dc_stream_get_link(stream);
if (!link || !link->replay_settings.replay_feature_enabled)
return;
cmd_data.pseudo_vtotal_data.vtotal = vtotal;
if (link->replay_settings.last_pseudo_vtotal != vtotal) {
link->replay_settings.last_pseudo_vtotal = vtotal;
link->dc->link_srv->edp_send_replay_cmd(link, Replay_Set_Pseudo_VTotal, &cmd_data);
}
}
static void mod_power_update_error_status(struct mod_power *mod_power,
const struct dc_stream_state *stream)
{
struct dc_link *link = NULL;
union replay_debug_flags *pDebug = NULL;
if (mod_power == NULL || stream == NULL)
return;
link = dc_stream_get_link(stream);
if (!link)
return;
pDebug = (union replay_debug_flags *)&link->replay_settings.config.debug_flags;
if (pDebug->bitfields.enable_visual_confirm_debug == 0)
return;
mod_power_replay_set_general_cmd(mod_power, stream,
REPLAY_GENERAL_CMD_UPDATE_ERROR_STATUS,
link->replay_settings.config.replay_error_status.raw, 0);
}
void mod_power_set_low_rr_activate(struct mod_power *mod_power,
const struct dc_stream_state *stream, bool low_rr_supported)
{
struct dc_link *link = NULL;
if (mod_power == NULL || stream == NULL)
return;
link = dc_stream_get_link(stream);
if (!link)
return;
mod_power_replay_set_general_cmd(mod_power, stream,
REPLAY_GENERAL_CMD_SET_LOW_RR_ACTIVATE,
low_rr_supported, 0);
}
void mod_power_set_video_conferencing_activate(struct mod_power *mod_power,
const struct dc_stream_state *stream, bool video_conferencing_activate)
{
struct dc_link *link = NULL;
if (mod_power == NULL || stream == NULL)
return;
link = dc_stream_get_link(stream);
if (!link || !link->replay_settings.replay_feature_enabled)
return;
mod_power_replay_set_general_cmd(mod_power, stream,
REPLAY_GENERAL_CMD_VIDEO_CONFERENCING,
video_conferencing_activate, 0);
}
void mod_power_set_coasting_vtotal_without_frame_update(struct mod_power *mod_power,
const struct dc_stream_state *stream, uint32_t coasting_vtotal)
{
struct dc_link *link = NULL;
if (mod_power == NULL || stream == NULL)
return;
link = dc_stream_get_link(stream);
if (!link || !link->replay_settings.replay_feature_enabled)
return;
mod_power_replay_set_general_cmd(mod_power, stream,
REPLAY_GENERAL_CMD_SET_COASTING_VTOTAL_WITHOUT_FRAME_UPDATE,
coasting_vtotal, 0);
}
void mod_power_set_replay_continuously_resync(struct mod_power *mod_power,
const struct dc_stream_state *stream, bool enable)
{
struct dc_link *link = NULL;
if (mod_power == NULL || stream == NULL)
return;
link = dc_stream_get_link(stream);
if (!link || !link->replay_settings.replay_feature_enabled)
return;
mod_power_replay_set_general_cmd(mod_power, stream,
REPLAY_GENERAL_CMD_SET_CONTINUOUSLY_RESYNC,
enable, 0);
}
void mod_power_set_live_capture_with_cvt_activate(struct mod_power *mod_power,
const struct dc_stream_state *stream, bool live_capture_with_cvt_activate)
{
struct dc_link *link = NULL;
if (mod_power == NULL || stream == NULL)
return;
link = dc_stream_get_link(stream);
if (!link || !link->replay_settings.replay_feature_enabled)
return;
// Check if LIVE_CAPTURE_WITH_CVT bit is enabled in replay optimization config
if (!link->replay_settings.config.replay_optimization.bits.LIVE_CAPTURE_WITH_CVT)
return;
if (link->replay_settings.config.live_capture_with_cvt_activated != live_capture_with_cvt_activate) {
link->replay_settings.config.live_capture_with_cvt_activated = live_capture_with_cvt_activate;
mod_power_replay_set_general_cmd(mod_power, stream,
REPLAY_GENERAL_CMD_LIVE_CAPTURE_WITH_CVT,
live_capture_with_cvt_activate, 0);
}
}
bool mod_power_set_replay_event(struct mod_power *mod_power,
struct dc_stream_state *stream, bool set_event,
enum replay_event event, bool wait_for_disable)
{
struct core_power *core_power = NULL;
struct dc_link *link = NULL;
unsigned int stream_index = 0;
unsigned int active_replay_events = 0;
bool replay_active_request = false;
bool force_static = false;
uint32_t coasting_vtotal = 0;
bool current_timing_sync_status = false;
bool is_full_screen_video = false;
bool is_ultra_sleep_mode = false;
unsigned int sink_duration_us = 0;
bool low_rr_active = false;
uint16_t frame_skip_number = 0;
bool is_video_playback = false;
if (!stream)
return false;
if (mod_power == NULL)
return false;
core_power = MOD_POWER_TO_CORE(mod_power);
if (core_power->num_entities == 0)
return false;
stream_index = map_index_from_stream(core_power, stream);
if (set_event)
core_power->map[stream_index].replay_events |= event;
else
core_power->map[stream_index].replay_events &= ~event;
link = dc_stream_get_link(stream);
if (!link || !link->replay_settings.replay_feature_enabled)
return false;
if ((core_power->map[stream_index].replay_events & replay_event_disable_replay_while_switching_mux) != 0)
return false;
if ((core_power->map[stream_index].replay_events & replay_event_os_override_hold) != 0)
return false;
active_replay_events = core_power->map[stream_index].replay_events;
current_timing_sync_status =
link->replay_settings.config.replay_timing_sync_supported;
replay_active_request = mod_power_update_replay_active_status(active_replay_events,
link, &coasting_vtotal, &is_full_screen_video, &is_ultra_sleep_mode, &frame_skip_number, &is_video_playback);
if (is_full_screen_video)
mod_power_replay_set_pseudo_vtotal(mod_power, stream,
link->replay_settings.low_rr_full_screen_video_pseudo_vtotal);
else
mod_power_replay_set_pseudo_vtotal(mod_power, stream, 0);
//If timing_sync_status change, then re-enabled set timing_sync_supported value and re-enabled replay
if (current_timing_sync_status != link->replay_settings.config.replay_timing_sync_supported)
mod_power_replay_set_timing_sync_supported(mod_power, stream);
if (link->replay_settings.config.low_rr_supported) {
sink_duration_us =
(unsigned int)(div_u64(((unsigned long long)(coasting_vtotal)
* 10000) * stream->timing.h_total,
stream->timing.pix_clk_100hz));
low_rr_active = sink_duration_us < LOW_REFRESH_RATE_DURATION_US_UPPER_BOUND ? false : true;
if (low_rr_active != link->replay_settings.config.low_rr_activated) {
mod_power_set_low_rr_activate(mod_power, stream, low_rr_active);
link->replay_settings.config.low_rr_activated = low_rr_active;
}
}
// The function return fail when
// 1. DMUB function is not support (for backward compatible).
// 2. active_replay_events or coasting_vtotal is not updated in the same time
if (!mod_power_replay_set_power_opt_and_coasting_vtotal(mod_power,
stream, active_replay_events, coasting_vtotal, is_ultra_sleep_mode, frame_skip_number)) {
if (!mod_power_replay_set_power_opt(mod_power, stream, active_replay_events, is_ultra_sleep_mode))
return false;
if (!mod_power_replay_set_coasting_vtotal(mod_power, stream, coasting_vtotal, frame_skip_number))
return false;
}
mod_power_set_live_capture_with_cvt_activate(mod_power, stream, is_video_playback);
mod_power_update_error_status(mod_power, stream);
// If Replay is going to be enable (No matter is disable -> enable or enable -> enable), we don't need to wait.
// If Replay is going to be disable
// if disable -> disable
// -> Replay DMUB state should be state 0.
// So no matter wait_for_disable is true or not, it should makes no difference.
// if enable -> disable -> We should wait if wait_for_disable is true.
if (replay_active_request)
wait_for_disable = false;
if (!mod_power_set_replay_active(stream, replay_active_request, wait_for_disable, force_static))
return false;
return true;
}
bool mod_power_get_replay_active_status(const struct dc_stream_state *stream,
bool *replay_active)
{
const struct dc_link *link = NULL;
if (!stream)
return false;
link = dc_stream_get_link(stream);
*replay_active = link->replay_settings.replay_allow_active;
return true;
}
void mod_power_replay_residency(const struct dc_stream_state *stream,
unsigned int *residency, const bool is_start, const bool is_alpm)
{
const struct dc_link *link = NULL;
enum pr_residency_mode mode;
if (!stream)
return;
link = dc_stream_get_link(stream);
if (is_alpm)
mode = PR_RESIDENCY_MODE_ALPM;
else
mode = PR_RESIDENCY_MODE_PHY;
if (link && link->dc && link->dc->link_srv)
link->dc->link_srv->edp_replay_residency(link, residency, is_start, mode);
}
bool mod_power_replay_set_power_opt_and_coasting_vtotal(struct mod_power *mod_power,
const struct dc_stream_state *stream, unsigned int active_replay_events, uint32_t coasting_vtotal,
bool is_ultra_sleep_mode, uint16_t frame_skip_number)
{
struct core_power *core_power = NULL;
struct dc_link *link = NULL;
unsigned int power_opt = 0;
if (!stream)
return false;
if (mod_power == NULL)
return false;
core_power = MOD_POWER_TO_CORE(mod_power);
if (core_power->num_entities == 0)
return false;
link = dc_stream_get_link(stream);
if (!link || !link->replay_settings.replay_feature_enabled)
return false;
power_opt = mod_power_replay_setup_power_opt(link, active_replay_events, is_ultra_sleep_mode);
return link->dc->link_srv->edp_set_replay_power_opt_and_coasting_vtotal(link, &power_opt, coasting_vtotal, frame_skip_number);
}
void mod_power_replay_notify_mode_change(struct mod_power *mod_power,
struct dc *dc,
struct dc_link *link,
const struct dc_stream_state *stream,
unsigned int stream_index)
{
struct core_power *core_power = NULL;
int active_replay_events = 0;
if (!mod_power || !dc || !link || !stream)
return;
core_power = MOD_POWER_TO_CORE(mod_power);
active_replay_events = core_power->map[stream_index].replay_events;
link->replay_settings.replay_smu_opt_enable =
(link->replay_settings.config.replay_smu_opt_supported &&
mod_power_only_edp(dc->current_state, stream));
if (active_replay_events & replay_event_os_request_force_ffu) {
link->replay_settings.config.os_request_force_ffu = true;
}
if (dc_is_embedded_signal(stream->signal))
dc->link_srv->dp_setup_replay(link, stream);
}
void init_replay_config(struct dc_link *link, struct replay_config *pr_config)
{
link->replay_settings.config = *pr_config;
}
void set_replay_frame_skip_number(struct dc_link *link,
enum replay_coasting_vtotal_type type,
uint32_t coasting_vtotal_refresh_rate_uhz,
uint32_t flicker_free_refresh_rate_uhz,
bool is_defer)
{
uint32_t *frame_skip_number_array = NULL;
uint32_t frame_skip_number = 0;
if (link == NULL)
return;
if (link->replay_settings.config.frame_skip_supported == false)
return;
if (flicker_free_refresh_rate_uhz == 0 || coasting_vtotal_refresh_rate_uhz == 0)
return;
if (is_defer)
frame_skip_number_array = link->replay_settings.defer_frame_skip_number_table;
else
frame_skip_number_array = link->replay_settings.frame_skip_number_table;
if (frame_skip_number_array == NULL)
return;
frame_skip_number = (coasting_vtotal_refresh_rate_uhz + 500000) / flicker_free_refresh_rate_uhz;
if (frame_skip_number >= 1)
frame_skip_number_array[type] = frame_skip_number - 1;
else
frame_skip_number_array[type] = 0;
}
void set_replay_defer_update_coasting_vtotal(struct dc_link *link,
enum replay_coasting_vtotal_type type,
uint32_t vtotal)
{
link->replay_settings.defer_update_coasting_vtotal_table[type] = vtotal;
}
void update_replay_coasting_vtotal_from_defer(struct dc_link *link,
enum replay_coasting_vtotal_type type)
{
link->replay_settings.coasting_vtotal_table[type] =
link->replay_settings.defer_update_coasting_vtotal_table[type];
link->replay_settings.frame_skip_number_table[type] =
link->replay_settings.defer_frame_skip_number_table[type];
}
void set_replay_coasting_vtotal(struct dc_link *link,
enum replay_coasting_vtotal_type type,
uint32_t vtotal)
{
link->replay_settings.coasting_vtotal_table[type] = vtotal;
}
void set_replay_low_rr_full_screen_video_src_vtotal(struct dc_link *link, uint16_t vtotal)
{
link->replay_settings.low_rr_full_screen_video_pseudo_vtotal = vtotal;
}
void calculate_replay_link_off_frame_count(struct dc_link *link,
uint16_t vtotal, uint16_t htotal)
{
uint32_t max_link_off_frame_count = 0;
uint16_t max_deviation_line = 0, pixel_deviation_per_line = 0;
if (!link || link->replay_settings.config.replay_version != DC_FREESYNC_REPLAY)
return;
max_deviation_line = link->dpcd_caps.pr_info.max_deviation_line;
pixel_deviation_per_line = link->dpcd_caps.pr_info.pixel_deviation_per_line;
if (htotal != 0 && vtotal != 0 && pixel_deviation_per_line != 0)
max_link_off_frame_count = htotal * max_deviation_line / (pixel_deviation_per_line * vtotal);
else
ASSERT(0);
link->replay_settings.link_off_frame_count = max_link_off_frame_count;
}
void reset_replay_dsync_error_count(struct dc_link *link)
{
link->replay_settings.replay_desync_error_fail_count = 0;
}
@@ -0,0 +1,178 @@
/*
* Copyright 2019 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 "mod_vmid.h"
struct core_vmid {
struct mod_vmid public;
struct dc *dc;
unsigned int num_vmid;
unsigned int num_vmids_available;
uint64_t ptb_assigned_to_vmid[MAX_VMID];
struct dc_virtual_addr_space_config base_config;
};
#define MOD_VMID_TO_CORE(mod_vmid)\
container_of(mod_vmid, struct core_vmid, public)
static void add_ptb_to_table(struct core_vmid *core_vmid, unsigned int vmid, uint64_t ptb)
{
if (vmid < MAX_VMID) {
core_vmid->ptb_assigned_to_vmid[vmid] = ptb;
core_vmid->num_vmids_available--;
}
}
static void clear_entry_from_vmid_table(struct core_vmid *core_vmid, unsigned int vmid)
{
if (vmid < MAX_VMID) {
core_vmid->ptb_assigned_to_vmid[vmid] = 0;
core_vmid->num_vmids_available++;
}
}
static void evict_vmids(struct core_vmid *core_vmid)
{
unsigned int i;
int ord_int = dc_get_vmid_use_vector(core_vmid->dc);
ASSERT(ord_int >= 0 && ord_int <= 0xFFFF);
uint16_t ord = (uint16_t)ord_int;
// At this point any positions with value 0 are unused vmids, evict them
for (i = 1; i < core_vmid->num_vmid; i++) {
if (!(ord & (1u << i)))
clear_entry_from_vmid_table(core_vmid, i);
}
}
// Return value of -1 indicates vmid table uninitialized or ptb dne in the table
static int get_existing_vmid_for_ptb(struct core_vmid *core_vmid, uint64_t ptb)
{
unsigned int i;
for (i = 0; i < core_vmid->num_vmid; i++) {
if (core_vmid->ptb_assigned_to_vmid[i] == ptb)
return i;
}
return -1;
}
// Expected to be called only when there's an available vmid
static int get_next_available_vmid(struct core_vmid *core_vmid)
{
unsigned int i;
for (i = 1; i < core_vmid->num_vmid; i++) {
if (core_vmid->ptb_assigned_to_vmid[i] == 0)
return i;
}
return -1;
}
uint8_t mod_vmid_get_for_ptb(struct mod_vmid *mod_vmid, uint64_t ptb)
{
struct core_vmid *core_vmid = MOD_VMID_TO_CORE(mod_vmid);
int vmid = 0;
// Physical address gets vmid 0
if (ptb == 0)
return 0;
vmid = get_existing_vmid_for_ptb(core_vmid, ptb);
if (vmid == -1) {
struct dc_virtual_addr_space_config va_config = core_vmid->base_config;
va_config.page_table_base_addr = ptb;
if (core_vmid->num_vmids_available == 0)
evict_vmids(core_vmid);
vmid = get_next_available_vmid(core_vmid);
if (vmid != -1) {
add_ptb_to_table(core_vmid, vmid, ptb);
dc_setup_vm_context(core_vmid->dc, &va_config, vmid);
} else
ASSERT(0);
}
ASSERT(vmid >= 0 && vmid <= 0xFF);
return (uint8_t)vmid;
}
void mod_vmid_reset(struct mod_vmid *mod_vmid)
{
struct core_vmid *core_vmid = MOD_VMID_TO_CORE(mod_vmid);
core_vmid->num_vmids_available = core_vmid->num_vmid - 1;
memset(core_vmid->ptb_assigned_to_vmid, 0, sizeof(core_vmid->ptb_assigned_to_vmid[0]) * MAX_VMID);
}
struct mod_vmid *mod_vmid_create(
struct dc *dc,
unsigned int num_vmid,
struct dc_virtual_addr_space_config *va_config)
{
struct core_vmid *core_vmid;
if (num_vmid <= 1)
goto fail_no_vm_ctx;
if (dc == NULL)
goto fail_dc_null;
core_vmid = kzalloc_obj(struct core_vmid);
if (core_vmid == NULL)
goto fail_alloc_context;
core_vmid->dc = dc;
core_vmid->num_vmid = num_vmid;
core_vmid->num_vmids_available = num_vmid - 1;
core_vmid->base_config = *va_config;
memset(core_vmid->ptb_assigned_to_vmid, 0, sizeof(core_vmid->ptb_assigned_to_vmid[0]) * MAX_VMID);
return &core_vmid->public;
fail_no_vm_ctx:
fail_alloc_context:
fail_dc_null:
return NULL;
}
void mod_vmid_destroy(struct mod_vmid *mod_vmid)
{
if (mod_vmid != NULL) {
struct core_vmid *core_vmid = MOD_VMID_TO_CORE(mod_vmid);
kfree(core_vmid);
}
}