diff options
Diffstat (limited to 'drivers/gpu/drm/amd/display/modules')
22 files changed, 7134 insertions, 613 deletions
diff --git a/drivers/gpu/drm/amd/display/modules/color/color_gamma.c b/drivers/gpu/drm/amd/display/modules/color/color_gamma.c index 88898935a5e6..cac09d500fda 100644 --- a/drivers/gpu/drm/amd/display/modules/color/color_gamma.c +++ b/drivers/gpu/drm/amd/display/modules/color/color_gamma.c @@ -30,7 +30,6 @@ #include "opp.h" #include "color_gamma.h" - #define NUM_PTS_IN_REGION 16 #define NUM_REGIONS 32 #define MAX_HW_POINTS (NUM_PTS_IN_REGION*NUM_REGIONS) @@ -40,6 +39,33 @@ static struct hw_x_point coordinates_x[MAX_HW_POINTS + 2]; static struct fixed31_32 pq_table[MAX_HW_POINTS + 2]; static struct fixed31_32 de_pq_table[MAX_HW_POINTS + 2]; +// these are helpers for calculations to reduce stack usage +// do not depend on these being preserved across calls +static struct fixed31_32 scratch_1; +static struct fixed31_32 scratch_2; +static struct translate_from_linear_space_args scratch_gamma_args; + +/* Helper to optimize gamma calculation, only use in translate_from_linear, in + * particular the dc_fixpt_pow function which is very expensive + * The idea is that our regions for X points are exponential and currently they all use + * the same number of points (NUM_PTS_IN_REGION) and in each region every point + * is exactly 2x the one at the same index in the previous region. In other words + * X[i] = 2 * X[i-NUM_PTS_IN_REGION] for i>=16 + * The other fact is that (2x)^gamma = 2^gamma * x^gamma + * So we compute and save x^gamma for the first 16 regions, and for every next region + * just multiply with 2^gamma which can be computed once, and save the result so we + * recursively compute all the values. + */ +static struct fixed31_32 pow_buffer[NUM_PTS_IN_REGION]; +static struct fixed31_32 gamma_of_2; // 2^gamma +int pow_buffer_ptr = -1; + /*sRGB 709 2.2 2.4 P3*/ +static const int32_t gamma_numerator01[] = { 31308, 180000, 0, 0, 0}; +static const int32_t gamma_numerator02[] = { 12920, 4500, 0, 0, 0}; +static const int32_t gamma_numerator03[] = { 55, 99, 0, 0, 0}; +static const int32_t gamma_numerator04[] = { 55, 99, 0, 0, 0}; +static const int32_t gamma_numerator05[] = { 2400, 2200, 2200, 2400, 2600}; + static bool pq_initialized; /* = false; */ static bool de_pq_initialized; /* = false; */ @@ -71,6 +97,18 @@ void setup_x_points_distribution(void) } } +void log_x_points_distribution(struct dal_logger *logger) +{ + int i = 0; + + if (logger != NULL) { + LOG_GAMMA_WRITE("Log X Distribution\n"); + + for (i = 0; i < MAX_HW_POINTS; i++) + LOG_GAMMA_WRITE("%llu\n", coordinates_x[i].x.value); + } +} + static void compute_pq(struct fixed31_32 in_x, struct fixed31_32 *out_y) { /* consts for PQ gamma formula. */ @@ -116,6 +154,7 @@ static void compute_de_pq(struct fixed31_32 in_x, struct fixed31_32 *out_y) struct fixed31_32 l_pow_m1; struct fixed31_32 base, div; + struct fixed31_32 base2; if (dc_fixpt_lt(in_x, dc_fixpt_zero)) @@ -125,69 +164,80 @@ static void compute_de_pq(struct fixed31_32 in_x, struct fixed31_32 *out_y) dc_fixpt_div(dc_fixpt_one, m2)); base = dc_fixpt_sub(l_pow_m1, c1); - if (dc_fixpt_lt(base, dc_fixpt_zero)) - base = dc_fixpt_zero; - div = dc_fixpt_sub(c2, dc_fixpt_mul(c3, l_pow_m1)); - *out_y = dc_fixpt_pow(dc_fixpt_div(base, div), - dc_fixpt_div(dc_fixpt_one, m1)); + base2 = dc_fixpt_div(base, div); + //avoid complex numbers + if (dc_fixpt_lt(base2, dc_fixpt_zero)) + base2 = dc_fixpt_sub(dc_fixpt_zero, base2); + + + *out_y = dc_fixpt_pow(base2, dc_fixpt_div(dc_fixpt_one, m1)); } + /*de gamma, none linear to linear*/ -static void compute_hlg_oetf(struct fixed31_32 in_x, bool is_light0_12, struct fixed31_32 *out_y) +static void compute_hlg_eotf(struct fixed31_32 in_x, + struct fixed31_32 *out_y, + uint32_t sdr_white_level, uint32_t max_luminance_nits) { struct fixed31_32 a; struct fixed31_32 b; struct fixed31_32 c; struct fixed31_32 threshold; - struct fixed31_32 reference_white_level; + struct fixed31_32 x; + struct fixed31_32 scaling_factor = + dc_fixpt_from_fraction(max_luminance_nits, sdr_white_level); a = dc_fixpt_from_fraction(17883277, 100000000); - if (is_light0_12) { - /*light 0-12*/ - b = dc_fixpt_from_fraction(28466892, 100000000); - c = dc_fixpt_from_fraction(55991073, 100000000); - threshold = dc_fixpt_one; - reference_white_level = dc_fixpt_half; + b = dc_fixpt_from_fraction(28466892, 100000000); + c = dc_fixpt_from_fraction(55991073, 100000000); + threshold = dc_fixpt_from_fraction(1, 2); + + if (dc_fixpt_lt(in_x, threshold)) { + x = dc_fixpt_mul(in_x, in_x); + x = dc_fixpt_div_int(x, 3); } else { - /*light 0-1*/ - b = dc_fixpt_from_fraction(2372241, 100000000); - c = dc_fixpt_add(dc_fixpt_one, dc_fixpt_from_fraction(429347, 100000000)); - threshold = dc_fixpt_from_fraction(1, 12); - reference_white_level = dc_fixpt_pow(dc_fixpt_from_fraction(3, 1), dc_fixpt_half); + x = dc_fixpt_sub(in_x, c); + x = dc_fixpt_div(x, a); + x = dc_fixpt_exp(x); + x = dc_fixpt_add(x, b); + x = dc_fixpt_div_int(x, 12); } - if (dc_fixpt_lt(threshold, in_x)) - *out_y = dc_fixpt_add(c, dc_fixpt_mul(a, dc_fixpt_log(dc_fixpt_sub(in_x, b)))); - else - *out_y = dc_fixpt_mul(dc_fixpt_pow(in_x, dc_fixpt_half), reference_white_level); + *out_y = dc_fixpt_mul(x, scaling_factor); + } /*re gamma, linear to none linear*/ -static void compute_hlg_eotf(struct fixed31_32 in_x, bool is_light0_12, struct fixed31_32 *out_y) +static void compute_hlg_oetf(struct fixed31_32 in_x, struct fixed31_32 *out_y, + uint32_t sdr_white_level, uint32_t max_luminance_nits) { struct fixed31_32 a; struct fixed31_32 b; struct fixed31_32 c; - struct fixed31_32 reference_white_level; + struct fixed31_32 threshold; + struct fixed31_32 x; + struct fixed31_32 scaling_factor = + dc_fixpt_from_fraction(sdr_white_level, max_luminance_nits); a = dc_fixpt_from_fraction(17883277, 100000000); - if (is_light0_12) { - /*light 0-12*/ - b = dc_fixpt_from_fraction(28466892, 100000000); - c = dc_fixpt_from_fraction(55991073, 100000000); - reference_white_level = dc_fixpt_from_fraction(4, 1); + b = dc_fixpt_from_fraction(28466892, 100000000); + c = dc_fixpt_from_fraction(55991073, 100000000); + threshold = dc_fixpt_from_fraction(1, 12); + x = dc_fixpt_mul(in_x, scaling_factor); + + + if (dc_fixpt_lt(x, threshold)) { + x = dc_fixpt_mul(x, dc_fixpt_from_fraction(3, 1)); + *out_y = dc_fixpt_pow(x, dc_fixpt_half); } else { - /*light 0-1*/ - b = dc_fixpt_from_fraction(2372241, 100000000); - c = dc_fixpt_add(dc_fixpt_one, dc_fixpt_from_fraction(429347, 100000000)); - reference_white_level = dc_fixpt_from_fraction(1, 3); + x = dc_fixpt_mul(x, dc_fixpt_from_fraction(12, 1)); + x = dc_fixpt_sub(x, b); + x = dc_fixpt_log(x); + x = dc_fixpt_mul(a, x); + *out_y = dc_fixpt_add(x, c); } - if (dc_fixpt_lt(dc_fixpt_half, in_x)) - *out_y = dc_fixpt_add(dc_fixpt_exp(dc_fixpt_div(dc_fixpt_sub(in_x, c), a)), b); - else - *out_y = dc_fixpt_mul(dc_fixpt_pow(in_x, dc_fixpt_from_fraction(2, 1)), reference_white_level); } @@ -243,95 +293,144 @@ struct dividers { struct fixed31_32 divider3; }; -enum gamma_type_index { - gamma_type_index_2_4, - gamma_type_index_2_2, - gamma_type_index_2_2_flat -}; -static void build_coefficients(struct gamma_coefficients *coefficients, enum gamma_type_index type) +static bool build_coefficients(struct gamma_coefficients *coefficients, enum dc_transfer_func_predefined type) { - static const int32_t numerator01[] = { 31308, 180000, 0}; - static const int32_t numerator02[] = { 12920, 4500, 0}; - static const int32_t numerator03[] = { 55, 99, 0}; - static const int32_t numerator04[] = { 55, 99, 0}; - static const int32_t numerator05[] = { 2400, 2200, 2200}; uint32_t i = 0; uint32_t index = 0; + bool ret = true; - if (type == gamma_type_index_2_2) + if (type == TRANSFER_FUNCTION_SRGB) + index = 0; + else if (type == TRANSFER_FUNCTION_BT709) index = 1; - else if (type == gamma_type_index_2_2_flat) + else if (type == TRANSFER_FUNCTION_GAMMA22) index = 2; + else if (type == TRANSFER_FUNCTION_GAMMA24) + index = 3; + else if (type == TRANSFER_FUNCTION_GAMMA26) + index = 4; + else { + ret = false; + goto release; + } do { coefficients->a0[i] = dc_fixpt_from_fraction( - numerator01[index], 10000000); + gamma_numerator01[index], 10000000); coefficients->a1[i] = dc_fixpt_from_fraction( - numerator02[index], 1000); + gamma_numerator02[index], 1000); coefficients->a2[i] = dc_fixpt_from_fraction( - numerator03[index], 1000); + gamma_numerator03[index], 1000); coefficients->a3[i] = dc_fixpt_from_fraction( - numerator04[index], 1000); + gamma_numerator04[index], 1000); coefficients->user_gamma[i] = dc_fixpt_from_fraction( - numerator05[index], 1000); + gamma_numerator05[index], 1000); ++i; } while (i != ARRAY_SIZE(coefficients->a0)); +release: + return ret; } static struct fixed31_32 translate_from_linear_space( - 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 translate_from_linear_space_args *args) { const struct fixed31_32 one = dc_fixpt_from_int(1); - if (dc_fixpt_lt(one, arg)) + if (dc_fixpt_le(one, args->arg)) return one; - if (dc_fixpt_le(arg, dc_fixpt_neg(a0))) + if (dc_fixpt_le(args->arg, dc_fixpt_neg(args->a0))) { + scratch_1 = dc_fixpt_add(one, args->a3); + scratch_2 = dc_fixpt_pow( + dc_fixpt_neg(args->arg), + dc_fixpt_recip(args->gamma)); + scratch_1 = dc_fixpt_mul(scratch_1, scratch_2); + scratch_1 = dc_fixpt_sub(args->a2, scratch_1); + + return scratch_1; + } else if (dc_fixpt_le(args->a0, args->arg)) { + if (pow_buffer_ptr == 0) { + gamma_of_2 = dc_fixpt_pow(dc_fixpt_from_int(2), + dc_fixpt_recip(args->gamma)); + } + scratch_1 = dc_fixpt_add(one, args->a3); + if (pow_buffer_ptr < 16) + scratch_2 = dc_fixpt_pow(args->arg, + dc_fixpt_recip(args->gamma)); + else + scratch_2 = dc_fixpt_mul(gamma_of_2, + pow_buffer[pow_buffer_ptr%16]); + + if (pow_buffer_ptr != -1) { + pow_buffer[pow_buffer_ptr%16] = scratch_2; + pow_buffer_ptr++; + } + + scratch_1 = dc_fixpt_mul(scratch_1, scratch_2); + scratch_1 = dc_fixpt_sub(scratch_1, args->a2); + + return scratch_1; + } + else + return dc_fixpt_mul(args->arg, args->a1); +} + + +static struct fixed31_32 translate_from_linear_space_long( + struct translate_from_linear_space_args *args) +{ + const struct fixed31_32 one = dc_fixpt_from_int(1); + + if (dc_fixpt_lt(one, args->arg)) + return one; + + if (dc_fixpt_le(args->arg, dc_fixpt_neg(args->a0))) return dc_fixpt_sub( - a2, + args->a2, dc_fixpt_mul( dc_fixpt_add( one, - a3), + args->a3), dc_fixpt_pow( - dc_fixpt_neg(arg), - dc_fixpt_recip(gamma)))); - else if (dc_fixpt_le(a0, arg)) + dc_fixpt_neg(args->arg), + dc_fixpt_recip(args->gamma)))); + else if (dc_fixpt_le(args->a0, args->arg)) return dc_fixpt_sub( dc_fixpt_mul( dc_fixpt_add( one, - a3), + args->a3), dc_fixpt_pow( - arg, - dc_fixpt_recip(gamma))), - a2); + args->arg, + dc_fixpt_recip(args->gamma))), + args->a2); else return dc_fixpt_mul( - arg, - a1); + args->arg, + args->a1); } -static struct fixed31_32 calculate_gamma22(struct fixed31_32 arg) +static struct fixed31_32 calculate_gamma22(struct fixed31_32 arg, bool use_eetf) { struct fixed31_32 gamma = dc_fixpt_from_fraction(22, 10); - return translate_from_linear_space(arg, - dc_fixpt_zero, - dc_fixpt_zero, - dc_fixpt_zero, - dc_fixpt_zero, - gamma); + scratch_gamma_args.arg = arg; + scratch_gamma_args.a0 = dc_fixpt_zero; + scratch_gamma_args.a1 = dc_fixpt_zero; + scratch_gamma_args.a2 = dc_fixpt_zero; + scratch_gamma_args.a3 = dc_fixpt_zero; + scratch_gamma_args.gamma = gamma; + + if (use_eetf) + return translate_from_linear_space_long(&scratch_gamma_args); + + return translate_from_linear_space(&scratch_gamma_args); } + static struct fixed31_32 translate_to_linear_space( struct fixed31_32 arg, struct fixed31_32 a0, @@ -365,18 +464,19 @@ static struct fixed31_32 translate_to_linear_space( return linear; } -static inline struct fixed31_32 translate_from_linear_space_ex( +static struct fixed31_32 translate_from_linear_space_ex( struct fixed31_32 arg, struct gamma_coefficients *coeff, uint32_t color_index) { - return translate_from_linear_space( - arg, - coeff->a0[color_index], - coeff->a1[color_index], - coeff->a2[color_index], - coeff->a3[color_index], - coeff->user_gamma[color_index]); + scratch_gamma_args.arg = arg; + scratch_gamma_args.a0 = coeff->a0[color_index]; + scratch_gamma_args.a1 = coeff->a1[color_index]; + scratch_gamma_args.a2 = coeff->a2[color_index]; + scratch_gamma_args.a3 = coeff->a3[color_index]; + scratch_gamma_args.gamma = coeff->user_gamma[color_index]; + + return translate_from_linear_space(&scratch_gamma_args); } @@ -709,30 +809,42 @@ static void build_de_pq(struct pwl_float_data_ex *de_pq, } } -static void build_regamma(struct pwl_float_data_ex *rgb_regamma, +static bool build_regamma(struct pwl_float_data_ex *rgb_regamma, uint32_t hw_points_num, - const struct hw_x_point *coordinate_x, enum gamma_type_index type) + const struct hw_x_point *coordinate_x, enum dc_transfer_func_predefined type) { uint32_t i; + bool ret = false; - struct gamma_coefficients coeff; + struct gamma_coefficients *coeff; struct pwl_float_data_ex *rgb = rgb_regamma; const struct hw_x_point *coord_x = coordinate_x; - build_coefficients(&coeff, type); + coeff = kvzalloc(sizeof(*coeff), GFP_KERNEL); + if (!coeff) + goto release; + + if (!build_coefficients(coeff, type)) + goto release; + memset(pow_buffer, 0, NUM_PTS_IN_REGION * sizeof(struct fixed31_32)); + pow_buffer_ptr = 0; // see variable definition for more info i = 0; - - while (i != hw_points_num + 1) { + while (i <= hw_points_num) { /*TODO use y vs r,g,b*/ rgb->r = translate_from_linear_space_ex( - coord_x->x, &coeff, 0); + coord_x->x, coeff, 0); rgb->g = rgb->r; rgb->b = rgb->r; ++coord_x; ++rgb; ++i; } + pow_buffer_ptr = -1; // reset back to no optimize + ret = true; +release: + kfree(coeff); + return ret; } static void hermite_spline_eetf(struct fixed31_32 input_x, @@ -830,7 +942,6 @@ static bool build_freesync_hdr(struct pwl_float_data_ex *rgb_regamma, struct fixed31_32 max_display; struct fixed31_32 min_display; struct fixed31_32 max_content; - struct fixed31_32 min_content; struct fixed31_32 clip = dc_fixpt_one; struct fixed31_32 output; bool use_eetf = false; @@ -844,7 +955,6 @@ static bool build_freesync_hdr(struct pwl_float_data_ex *rgb_regamma, max_display = dc_fixpt_from_int(fs_params->max_display); min_display = dc_fixpt_from_fraction(fs_params->min_display, 10000); max_content = dc_fixpt_from_int(fs_params->max_content); - min_content = dc_fixpt_from_fraction(fs_params->min_content, 10000); sdr_white_level = dc_fixpt_from_int(fs_params->sdr_white_level); if (fs_params->min_display > 1000) // cap at 0.1 at the bottom @@ -852,16 +962,14 @@ static bool build_freesync_hdr(struct pwl_float_data_ex *rgb_regamma, if (fs_params->max_display < 100) // cap at 100 at the top max_display = dc_fixpt_from_int(100); - if (fs_params->min_content < fs_params->min_display) - use_eetf = true; - else - min_content = min_display; - + // only max used, we don't adjust min luminance if (fs_params->max_content > fs_params->max_display) use_eetf = true; else max_content = max_display; + if (!use_eetf) + pow_buffer_ptr = 0; // see var definition for more info rgb += 32; // first 32 points have problems with fixed point, too small coord_x += 32; for (i = 32; i <= hw_points_num; i++) { @@ -880,7 +988,7 @@ static bool build_freesync_hdr(struct pwl_float_data_ex *rgb_regamma, if (dc_fixpt_lt(scaledX, dc_fixpt_zero)) output = dc_fixpt_zero; else - output = calculate_gamma22(scaledX); + output = calculate_gamma22(scaledX, use_eetf); rgb->r = output; rgb->g = output; @@ -900,19 +1008,23 @@ static bool build_freesync_hdr(struct pwl_float_data_ex *rgb_regamma, ++coord_x; ++rgb; } + pow_buffer_ptr = -1; return true; } -static void build_degamma(struct pwl_float_data_ex *curve, +static bool build_degamma(struct pwl_float_data_ex *curve, uint32_t hw_points_num, - const struct hw_x_point *coordinate_x, enum gamma_type_index type) + const struct hw_x_point *coordinate_x, enum dc_transfer_func_predefined type) { uint32_t i; struct gamma_coefficients coeff; uint32_t begin_index, end_index; + bool ret = false; + + if (!build_coefficients(&coeff, type)) + goto release; - build_coefficients(&coeff, type); i = 0; /* X points is 2^-25 to 2^7 @@ -941,11 +1053,19 @@ static void build_degamma(struct pwl_float_data_ex *curve, curve[i].b = dc_fixpt_one; i++; } + ret = true; +release: + return ret; } + + + + static void build_hlg_degamma(struct pwl_float_data_ex *degamma, uint32_t hw_points_num, - const struct hw_x_point *coordinate_x, bool is_light0_12) + const struct hw_x_point *coordinate_x, + uint32_t sdr_white_level, uint32_t max_luminance_nits) { uint32_t i; @@ -953,9 +1073,9 @@ static void build_hlg_degamma(struct pwl_float_data_ex *degamma, const struct hw_x_point *coord_x = coordinate_x; i = 0; - + //check when i == 434 while (i != hw_points_num + 1) { - compute_hlg_oetf(coord_x->x, is_light0_12, &rgb->r); + compute_hlg_eotf(coord_x->x, &rgb->r, sdr_white_level, max_luminance_nits); rgb->g = rgb->r; rgb->b = rgb->r; ++coord_x; @@ -964,9 +1084,11 @@ static void build_hlg_degamma(struct pwl_float_data_ex *degamma, } } + static void build_hlg_regamma(struct pwl_float_data_ex *regamma, uint32_t hw_points_num, - const struct hw_x_point *coordinate_x, bool is_light0_12) + const struct hw_x_point *coordinate_x, + uint32_t sdr_white_level, uint32_t max_luminance_nits) { uint32_t i; @@ -975,8 +1097,9 @@ static void build_hlg_regamma(struct pwl_float_data_ex *regamma, i = 0; + //when i == 471 while (i != hw_points_num + 1) { - compute_hlg_eotf(coord_x->x, is_light0_12, &rgb->r); + compute_hlg_oetf(coord_x->x, &rgb->r, sdr_white_level, max_luminance_nits); rgb->g = rgb->r; rgb->b = rgb->r; ++coord_x; @@ -1550,124 +1673,6 @@ static bool map_regamma_hw_to_x_user( #define _EXTRA_POINTS 3 -bool mod_color_calculate_regamma_params(struct dc_transfer_func *output_tf, - const struct dc_gamma *ramp, bool mapUserRamp, bool canRomBeUsed, - const struct freesync_hdr_tf_params *fs_params) -{ - struct dc_transfer_func_distributed_points *tf_pts = &output_tf->tf_pts; - struct dividers dividers; - - struct pwl_float_data *rgb_user = NULL; - struct pwl_float_data_ex *rgb_regamma = NULL; - struct gamma_pixel *axis_x = NULL; - struct pixel_gamma_point *coeff = NULL; - enum dc_transfer_func_predefined tf = TRANSFER_FUNCTION_SRGB; - bool ret = false; - - if (output_tf->type == TF_TYPE_BYPASS) - return false; - - /* we can use hardcoded curve for plain SRGB TF */ - if (output_tf->type == TF_TYPE_PREDEFINED && canRomBeUsed == true && - output_tf->tf == TRANSFER_FUNCTION_SRGB) { - if (ramp == NULL) - return true; - if ((ramp->is_logical_identity) || - (!mapUserRamp && ramp->type == GAMMA_RGB_256)) - return true; - } - - output_tf->type = TF_TYPE_DISTRIBUTED_POINTS; - - if (ramp && (mapUserRamp || ramp->type != GAMMA_RGB_256)) { - rgb_user = kvcalloc(ramp->num_entries + _EXTRA_POINTS, - sizeof(*rgb_user), - GFP_KERNEL); - if (!rgb_user) - goto rgb_user_alloc_fail; - - axis_x = kvcalloc(ramp->num_entries + 3, sizeof(*axis_x), - GFP_KERNEL); - if (!axis_x) - goto axis_x_alloc_fail; - - dividers.divider1 = dc_fixpt_from_fraction(3, 2); - dividers.divider2 = dc_fixpt_from_int(2); - dividers.divider3 = dc_fixpt_from_fraction(5, 2); - - build_evenly_distributed_points( - axis_x, - ramp->num_entries, - dividers); - - if (ramp->type == GAMMA_RGB_256 && mapUserRamp) - scale_gamma(rgb_user, ramp, dividers); - else if (ramp->type == GAMMA_RGB_FLOAT_1024) - scale_gamma_dx(rgb_user, ramp, dividers); - } - - rgb_regamma = kvcalloc(MAX_HW_POINTS + _EXTRA_POINTS, - sizeof(*rgb_regamma), - GFP_KERNEL); - if (!rgb_regamma) - goto rgb_regamma_alloc_fail; - - coeff = kvcalloc(MAX_HW_POINTS + _EXTRA_POINTS, sizeof(*coeff), - GFP_KERNEL); - if (!coeff) - goto coeff_alloc_fail; - - tf = output_tf->tf; - if (tf == TRANSFER_FUNCTION_PQ) { - tf_pts->end_exponent = 7; - tf_pts->x_point_at_y1_red = 125; - tf_pts->x_point_at_y1_green = 125; - tf_pts->x_point_at_y1_blue = 125; - - build_pq(rgb_regamma, - MAX_HW_POINTS, - coordinates_x, - output_tf->sdr_ref_white_level); - } else if (tf == TRANSFER_FUNCTION_GAMMA22 && - fs_params != NULL && fs_params->skip_tm == 0) { - build_freesync_hdr(rgb_regamma, - MAX_HW_POINTS, - coordinates_x, - fs_params); - } else { - tf_pts->end_exponent = 0; - tf_pts->x_point_at_y1_red = 1; - tf_pts->x_point_at_y1_green = 1; - tf_pts->x_point_at_y1_blue = 1; - - build_regamma(rgb_regamma, - MAX_HW_POINTS, - coordinates_x, tf == TRANSFER_FUNCTION_SRGB ? gamma_type_index_2_4 : - tf == TRANSFER_FUNCTION_GAMMA22 ? - gamma_type_index_2_2_flat : gamma_type_index_2_2); - } - map_regamma_hw_to_x_user(ramp, coeff, rgb_user, - coordinates_x, axis_x, rgb_regamma, - MAX_HW_POINTS, tf_pts, - (mapUserRamp || (ramp && ramp->type != GAMMA_RGB_256)) && - (ramp && ramp->type != GAMMA_CS_TFM_1D)); - - if (ramp && ramp->type == GAMMA_CS_TFM_1D) - apply_lut_1d(ramp, MAX_HW_POINTS, tf_pts); - - ret = true; - - kvfree(coeff); -coeff_alloc_fail: - kvfree(rgb_regamma); -rgb_regamma_alloc_fail: - kvfree(axis_x); -axis_x_alloc_fail: - kvfree(rgb_user); -rgb_user_alloc_fail: - return ret; -} - bool calculate_user_regamma_coeff(struct dc_transfer_func *output_tf, const struct regamma_lut *regamma) { @@ -1845,13 +1850,19 @@ bool mod_color_calculate_degamma_params(struct dc_transfer_func *input_tf, MAX_HW_POINTS, coordinates_x); else if (tf == TRANSFER_FUNCTION_SRGB || - tf == TRANSFER_FUNCTION_BT709) + tf == TRANSFER_FUNCTION_BT709 || + tf == TRANSFER_FUNCTION_GAMMA22 || + tf == TRANSFER_FUNCTION_GAMMA24 || + tf == TRANSFER_FUNCTION_GAMMA26) build_degamma(curve, MAX_HW_POINTS, coordinates_x, - tf == TRANSFER_FUNCTION_SRGB ? - gamma_type_index_2_4 : tf == TRANSFER_FUNCTION_GAMMA22 ? - gamma_type_index_2_2_flat : gamma_type_index_2_2); + tf); + else if (tf == TRANSFER_FUNCTION_HLG) + build_hlg_degamma(curve, + MAX_HW_POINTS, + coordinates_x, + 80, 1000); else if (tf == TRANSFER_FUNCTION_LINEAR) { // just copy coordinates_x into curve i = 0; @@ -1869,10 +1880,28 @@ bool mod_color_calculate_degamma_params(struct dc_transfer_func *input_tf, tf_pts->x_point_at_y1_green = 1; tf_pts->x_point_at_y1_blue = 1; - map_regamma_hw_to_x_user(ramp, coeff, rgb_user, - coordinates_x, axis_x, curve, - MAX_HW_POINTS, tf_pts, - mapUserRamp && ramp && ramp->type == GAMMA_RGB_256); + if (input_tf->tf == TRANSFER_FUNCTION_PQ) { + /* just copy current rgb_regamma into tf_pts */ + struct pwl_float_data_ex *curvePt = curve; + int i = 0; + + while (i <= MAX_HW_POINTS) { + tf_pts->red[i] = curvePt->r; + tf_pts->green[i] = curvePt->g; + tf_pts->blue[i] = curvePt->b; + ++curvePt; + ++i; + } + } else { + //clamps to 0-1 + map_regamma_hw_to_x_user(ramp, coeff, rgb_user, + coordinates_x, axis_x, curve, + MAX_HW_POINTS, tf_pts, + mapUserRamp && ramp && ramp->type == GAMMA_RGB_256); + } + + + if (ramp->type == GAMMA_CUSTOM) apply_lut_1d(ramp, MAX_HW_POINTS, tf_pts); @@ -1891,14 +1920,14 @@ rgb_user_alloc_fail: return ret; } - -bool mod_color_calculate_curve(enum dc_transfer_func_predefined trans, +static bool calculate_curve(enum dc_transfer_func_predefined trans, struct dc_transfer_func_distributed_points *points, + struct pwl_float_data_ex *rgb_regamma, + const struct freesync_hdr_tf_params *fs_params, uint32_t sdr_ref_white_level) { uint32_t i; bool ret = false; - struct pwl_float_data_ex *rgb_regamma = NULL; if (trans == TRANSFER_FUNCTION_UNITY || trans == TRANSFER_FUNCTION_LINEAR) { @@ -1908,42 +1937,50 @@ bool mod_color_calculate_curve(enum dc_transfer_func_predefined trans, points->x_point_at_y1_blue = 1; for (i = 0; i <= MAX_HW_POINTS ; i++) { - points->red[i] = coordinates_x[i].x; - points->green[i] = coordinates_x[i].x; - points->blue[i] = coordinates_x[i].x; + rgb_regamma[i].r = coordinates_x[i].x; + rgb_regamma[i].g = coordinates_x[i].x; + rgb_regamma[i].b = coordinates_x[i].x; } + ret = true; } else if (trans == TRANSFER_FUNCTION_PQ) { - rgb_regamma = kvcalloc(MAX_HW_POINTS + _EXTRA_POINTS, - sizeof(*rgb_regamma), - GFP_KERNEL); - if (!rgb_regamma) - goto rgb_regamma_alloc_fail; points->end_exponent = 7; points->x_point_at_y1_red = 125; points->x_point_at_y1_green = 125; points->x_point_at_y1_blue = 125; - build_pq(rgb_regamma, MAX_HW_POINTS, coordinates_x, sdr_ref_white_level); - for (i = 0; i <= MAX_HW_POINTS ; i++) { - points->red[i] = rgb_regamma[i].r; - points->green[i] = rgb_regamma[i].g; - points->blue[i] = rgb_regamma[i].b; - } + ret = true; + } else if (trans == TRANSFER_FUNCTION_GAMMA22 && + fs_params != NULL && fs_params->skip_tm == 0) { + build_freesync_hdr(rgb_regamma, + MAX_HW_POINTS, + coordinates_x, + fs_params); - kvfree(rgb_regamma); - } else if (trans == TRANSFER_FUNCTION_SRGB || - trans == TRANSFER_FUNCTION_BT709) { - rgb_regamma = kvcalloc(MAX_HW_POINTS + _EXTRA_POINTS, - sizeof(*rgb_regamma), - GFP_KERNEL); - if (!rgb_regamma) - goto rgb_regamma_alloc_fail; + ret = true; + } else if (trans == TRANSFER_FUNCTION_HLG) { + points->end_exponent = 4; + points->x_point_at_y1_red = 12; + points->x_point_at_y1_green = 12; + points->x_point_at_y1_blue = 12; + + build_hlg_regamma(rgb_regamma, + MAX_HW_POINTS, + coordinates_x, + 80, 1000); + + ret = true; + } else { + // trans == TRANSFER_FUNCTION_SRGB + // trans == TRANSFER_FUNCTION_BT709 + // trans == TRANSFER_FUNCTION_GAMMA22 + // trans == TRANSFER_FUNCTION_GAMMA24 + // trans == TRANSFER_FUNCTION_GAMMA26 points->end_exponent = 0; points->x_point_at_y1_red = 1; points->x_point_at_y1_green = 1; @@ -1952,42 +1989,112 @@ bool mod_color_calculate_curve(enum dc_transfer_func_predefined trans, build_regamma(rgb_regamma, MAX_HW_POINTS, coordinates_x, - trans == TRANSFER_FUNCTION_SRGB ? - gamma_type_index_2_4 : trans == TRANSFER_FUNCTION_GAMMA22 ? - gamma_type_index_2_2_flat : gamma_type_index_2_2); - for (i = 0; i <= MAX_HW_POINTS ; i++) { - points->red[i] = rgb_regamma[i].r; - points->green[i] = rgb_regamma[i].g; - points->blue[i] = rgb_regamma[i].b; - } + trans); + ret = true; + } - kvfree(rgb_regamma); - } else if (trans == TRANSFER_FUNCTION_HLG || - trans == TRANSFER_FUNCTION_HLG12) { - rgb_regamma = kvcalloc(MAX_HW_POINTS + _EXTRA_POINTS, - sizeof(*rgb_regamma), - GFP_KERNEL); - if (!rgb_regamma) - goto rgb_regamma_alloc_fail; + return ret; +} - build_hlg_regamma(rgb_regamma, - MAX_HW_POINTS, - coordinates_x, - trans == TRANSFER_FUNCTION_HLG12 ? true:false); - for (i = 0; i <= MAX_HW_POINTS ; i++) { - points->red[i] = rgb_regamma[i].r; - points->green[i] = rgb_regamma[i].g; - points->blue[i] = rgb_regamma[i].b; - } - ret = true; - kvfree(rgb_regamma); +bool mod_color_calculate_regamma_params(struct dc_transfer_func *output_tf, + const struct dc_gamma *ramp, bool mapUserRamp, bool canRomBeUsed, + const struct freesync_hdr_tf_params *fs_params) +{ + struct dc_transfer_func_distributed_points *tf_pts = &output_tf->tf_pts; + struct dividers dividers; + + struct pwl_float_data *rgb_user = NULL; + struct pwl_float_data_ex *rgb_regamma = NULL; + struct gamma_pixel *axis_x = NULL; + struct pixel_gamma_point *coeff = NULL; + enum dc_transfer_func_predefined tf = TRANSFER_FUNCTION_SRGB; + bool ret = false; + + if (output_tf->type == TF_TYPE_BYPASS) + return false; + + /* we can use hardcoded curve for plain SRGB TF */ + if (output_tf->type == TF_TYPE_PREDEFINED && canRomBeUsed == true && + output_tf->tf == TRANSFER_FUNCTION_SRGB) { + if (ramp == NULL) + return true; + if ((ramp->is_identity && ramp->type != GAMMA_CS_TFM_1D) || + (!mapUserRamp && ramp->type == GAMMA_RGB_256)) + return true; + } + + output_tf->type = TF_TYPE_DISTRIBUTED_POINTS; + + if (ramp && ramp->type != GAMMA_CS_TFM_1D && + (mapUserRamp || ramp->type != GAMMA_RGB_256)) { + rgb_user = kvcalloc(ramp->num_entries + _EXTRA_POINTS, + sizeof(*rgb_user), + GFP_KERNEL); + if (!rgb_user) + goto rgb_user_alloc_fail; + + axis_x = kvcalloc(ramp->num_entries + 3, sizeof(*axis_x), + GFP_KERNEL); + if (!axis_x) + goto axis_x_alloc_fail; + + dividers.divider1 = dc_fixpt_from_fraction(3, 2); + dividers.divider2 = dc_fixpt_from_int(2); + dividers.divider3 = dc_fixpt_from_fraction(5, 2); + + build_evenly_distributed_points( + axis_x, + ramp->num_entries, + dividers); + + if (ramp->type == GAMMA_RGB_256 && mapUserRamp) + scale_gamma(rgb_user, ramp, dividers); + else if (ramp->type == GAMMA_RGB_FLOAT_1024) + scale_gamma_dx(rgb_user, ramp, dividers); + } + + rgb_regamma = kvcalloc(MAX_HW_POINTS + _EXTRA_POINTS, + sizeof(*rgb_regamma), + GFP_KERNEL); + if (!rgb_regamma) + goto rgb_regamma_alloc_fail; + + coeff = kvcalloc(MAX_HW_POINTS + _EXTRA_POINTS, sizeof(*coeff), + GFP_KERNEL); + if (!coeff) + goto coeff_alloc_fail; + + tf = output_tf->tf; + + ret = calculate_curve(tf, + tf_pts, + rgb_regamma, + fs_params, + output_tf->sdr_ref_white_level); + + if (ret) { + map_regamma_hw_to_x_user(ramp, coeff, rgb_user, + coordinates_x, axis_x, rgb_regamma, + MAX_HW_POINTS, tf_pts, + (mapUserRamp || (ramp && ramp->type != GAMMA_RGB_256)) && + (ramp && ramp->type != GAMMA_CS_TFM_1D)); + + if (ramp && ramp->type == GAMMA_CS_TFM_1D) + apply_lut_1d(ramp, MAX_HW_POINTS, tf_pts); } + + kvfree(coeff); +coeff_alloc_fail: + kvfree(rgb_regamma); rgb_regamma_alloc_fail: + kvfree(axis_x); +axis_x_alloc_fail: + kvfree(rgb_user); +rgb_user_alloc_fail: return ret; } - bool mod_color_calculate_degamma_curve(enum dc_transfer_func_predefined trans, struct dc_transfer_func_distributed_points *points) { @@ -2024,8 +2131,10 @@ bool mod_color_calculate_degamma_curve(enum dc_transfer_func_predefined trans, kvfree(rgb_degamma); } else if (trans == TRANSFER_FUNCTION_SRGB || - trans == TRANSFER_FUNCTION_BT709 || - trans == TRANSFER_FUNCTION_GAMMA22) { + trans == TRANSFER_FUNCTION_BT709 || + trans == TRANSFER_FUNCTION_GAMMA22 || + trans == TRANSFER_FUNCTION_GAMMA24 || + trans == TRANSFER_FUNCTION_GAMMA26) { rgb_degamma = kvcalloc(MAX_HW_POINTS + _EXTRA_POINTS, sizeof(*rgb_degamma), GFP_KERNEL); @@ -2035,9 +2144,7 @@ bool mod_color_calculate_degamma_curve(enum dc_transfer_func_predefined trans, build_degamma(rgb_degamma, MAX_HW_POINTS, coordinates_x, - trans == TRANSFER_FUNCTION_SRGB ? - gamma_type_index_2_4 : trans == TRANSFER_FUNCTION_GAMMA22 ? - gamma_type_index_2_2_flat : gamma_type_index_2_2); + trans); for (i = 0; i <= MAX_HW_POINTS ; i++) { points->red[i] = rgb_degamma[i].r; points->green[i] = rgb_degamma[i].g; @@ -2046,8 +2153,7 @@ bool mod_color_calculate_degamma_curve(enum dc_transfer_func_predefined trans, ret = true; kvfree(rgb_degamma); - } else if (trans == TRANSFER_FUNCTION_HLG || - trans == TRANSFER_FUNCTION_HLG12) { + } else if (trans == TRANSFER_FUNCTION_HLG) { rgb_degamma = kvcalloc(MAX_HW_POINTS + _EXTRA_POINTS, sizeof(*rgb_degamma), GFP_KERNEL); @@ -2057,7 +2163,7 @@ bool mod_color_calculate_degamma_curve(enum dc_transfer_func_predefined trans, build_hlg_degamma(rgb_degamma, MAX_HW_POINTS, coordinates_x, - trans == TRANSFER_FUNCTION_HLG12 ? true:false); + 80, 1000); for (i = 0; i <= MAX_HW_POINTS ; i++) { points->red[i] = rgb_degamma[i].r; points->green[i] = rgb_degamma[i].g; @@ -2074,5 +2180,3 @@ bool mod_color_calculate_degamma_curve(enum dc_transfer_func_predefined trans, rgb_degamma_alloc_fail: return ret; } - - diff --git a/drivers/gpu/drm/amd/display/modules/color/color_gamma.h b/drivers/gpu/drm/amd/display/modules/color/color_gamma.h index 369953fafadf..9994817a9a03 100644 --- a/drivers/gpu/drm/amd/display/modules/color/color_gamma.h +++ b/drivers/gpu/drm/amd/display/modules/color/color_gamma.h @@ -82,7 +82,17 @@ struct freesync_hdr_tf_params { unsigned int skip_tm; // skip tm }; +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; +}; + void setup_x_points_distribution(void); +void log_x_points_distribution(struct dal_logger *logger); void precompute_pq(void); void precompute_de_pq(void); @@ -93,10 +103,6 @@ bool mod_color_calculate_regamma_params(struct dc_transfer_func *output_tf, bool mod_color_calculate_degamma_params(struct dc_transfer_func *output_tf, const struct dc_gamma *ramp, bool mapUserRamp); -bool mod_color_calculate_curve(enum dc_transfer_func_predefined trans, - struct dc_transfer_func_distributed_points *points, - uint32_t sdr_ref_white_level); - bool mod_color_calculate_degamma_curve(enum dc_transfer_func_predefined trans, struct dc_transfer_func_distributed_points *points); diff --git a/drivers/gpu/drm/amd/display/modules/freesync/freesync.c b/drivers/gpu/drm/amd/display/modules/freesync/freesync.c index 7c20171a3b6d..b9992ebf77a6 100644 --- a/drivers/gpu/drm/amd/display/modules/freesync/freesync.c +++ b/drivers/gpu/drm/amd/display/modules/freesync/freesync.c @@ -37,8 +37,8 @@ #define STATIC_SCREEN_RAMP_DELTA_REFRESH_RATE_PER_FRAME ((1000 / 60) * 65) /* Number of elements in the render times cache array */ #define RENDER_TIMES_MAX_COUNT 10 -/* Threshold to exit BTR (to avoid frequent enter-exits at the lower limit) */ -#define BTR_EXIT_MARGIN 2000 +/* Threshold to exit/exit BTR (to avoid frequent enter-exits at the lower limit) */ +#define BTR_MAX_MARGIN 2500 /* Threshold to change BTR multiplier (to avoid frequent changes) */ #define BTR_DRIFT_MARGIN 2000 /*Threshold to exit fixed refresh rate*/ @@ -52,93 +52,6 @@ struct core_freesync { struct dc *dc; }; -void setFieldWithMask(unsigned char *dest, unsigned int mask, unsigned int value) -{ - unsigned int shift = 0; - - if (!mask || !dest) - return; - - while (!((mask >> shift) & 1)) - shift++; - - //reset - *dest = *dest & ~mask; - //set - //dont let value span past mask - value = value & (mask >> shift); - //insert value - *dest = *dest | (value << shift); -} - -// VTEM Byte Offset -#define VRR_VTEM_PB0 0 -#define VRR_VTEM_PB1 1 -#define VRR_VTEM_PB2 2 -#define VRR_VTEM_PB3 3 -#define VRR_VTEM_PB4 4 -#define VRR_VTEM_PB5 5 -#define VRR_VTEM_PB6 6 - -#define VRR_VTEM_MD0 7 -#define VRR_VTEM_MD1 8 -#define VRR_VTEM_MD2 9 -#define VRR_VTEM_MD3 10 - - -// VTEM Byte Masks -//PB0 -#define MASK__VRR_VTEM_PB0__RESERVED0 0x01 -#define MASK__VRR_VTEM_PB0__SYNC 0x02 -#define MASK__VRR_VTEM_PB0__VFR 0x04 -#define MASK__VRR_VTEM_PB0__AFR 0x08 -#define MASK__VRR_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__VRR_VTEM_PB0__END 0x40 -#define MASK__VRR_VTEM_PB0__NEW 0x80 - -//PB1 -#define MASK__VRR_VTEM_PB1__RESERVED1 0xFF - -//PB2 -#define MASK__VRR_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__VRR_VTEM_PB3__DATA_SET_TAG_MSB 0xFF -//PB4 -#define MASK__VRR_VTEM_PB4__DATA_SET_TAG_LSB 0xFF -//PB5 -#define MASK__VRR_VTEM_PB5__DATA_SET_LENGTH_MSB 0xFF -//PB6 -#define MASK__VRR_VTEM_PB6__DATA_SET_LENGTH_LSB 0xFF - - - -//PB7-27 (20 bytes): -//PB7 = MD0 -#define MASK__VRR_VTEM_MD0__VRR_EN 0x01 -#define MASK__VRR_VTEM_MD0__M_CONST 0x02 -#define MASK__VRR_VTEM_MD0__RESERVED2 0x0C -#define MASK__VRR_VTEM_MD0__FVA_FACTOR_M1 0xF0 - -//MD1 -#define MASK__VRR_VTEM_MD1__BASE_VFRONT 0xFF - -//MD2 -#define MASK__VRR_VTEM_MD2__BASE_REFRESH_RATE_98 0x03 -#define MASK__VRR_VTEM_MD2__RB 0x04 -#define MASK__VRR_VTEM_MD2__RESERVED3 0xF8 - -//MD3 -#define MASK__VRR_VTEM_MD3__BASE_REFRESH_RATE_07 0xFF - - #define MOD_FREESYNC_TO_CORE(mod_freesync)\ container_of(mod_freesync, struct core_freesync, public) @@ -209,7 +122,7 @@ static unsigned int calc_v_total_from_refresh( const struct dc_stream_state *stream, unsigned int refresh_in_uhz) { - unsigned int v_total = stream->timing.v_total; + unsigned int v_total; unsigned int frame_duration_in_ns; frame_duration_in_ns = @@ -321,6 +234,10 @@ static void update_v_total_for_static_ramp( current_duration_in_us) * (stream->timing.pix_clk_100hz / 10)), stream->timing.h_total), 1000); + /* v_total cannot be less than nominal */ + if (v_total < stream->timing.v_total) + v_total = stream->timing.v_total; + in_out_vrr->adjust.v_total_min = v_total; in_out_vrr->adjust.v_total_max = v_total; } @@ -337,24 +254,22 @@ static void apply_below_the_range(struct core_freesync *core_freesync, unsigned int delta_from_mid_point_in_us_1 = 0xFFFFFFFF; unsigned int delta_from_mid_point_in_us_2 = 0xFFFFFFFF; unsigned int frames_to_insert = 0; - unsigned int min_frame_duration_in_ns = 0; - unsigned int max_render_time_in_us = in_out_vrr->max_duration_in_us; unsigned int delta_from_mid_point_delta_in_us; - - min_frame_duration_in_ns = ((unsigned int) (div64_u64( - (1000000000ULL * 1000000), - in_out_vrr->max_refresh_in_uhz))); + unsigned int max_render_time_in_us = + in_out_vrr->max_duration_in_us - in_out_vrr->btr.margin_in_us; /* Program BTR */ - if (last_render_time_in_us + BTR_EXIT_MARGIN < max_render_time_in_us) { + if ((last_render_time_in_us + in_out_vrr->btr.margin_in_us / 2) < max_render_time_in_us) { /* Exit Below the Range */ if (in_out_vrr->btr.btr_active) { in_out_vrr->btr.frame_counter = 0; in_out_vrr->btr.btr_active = false; } - } else if (last_render_time_in_us > max_render_time_in_us) { + } else if (last_render_time_in_us > (max_render_time_in_us + in_out_vrr->btr.margin_in_us / 2)) { /* Enter Below the Range */ - in_out_vrr->btr.btr_active = true; + if (!in_out_vrr->btr.btr_active) { + in_out_vrr->btr.btr_active = true; + } } /* BTR set to "not active" so disengage */ @@ -410,7 +325,9 @@ static void apply_below_the_range(struct core_freesync *core_freesync, /* Choose number of frames to insert based on how close it * can get to the mid point of the variable range. */ - if (delta_from_mid_point_in_us_1 < delta_from_mid_point_in_us_2) { + if ((frame_time_in_us / mid_point_frames_ceil) > in_out_vrr->min_duration_in_us && + (delta_from_mid_point_in_us_1 < delta_from_mid_point_in_us_2 || + mid_point_frames_floor < 2)) { frames_to_insert = mid_point_frames_ceil; delta_from_mid_point_delta_in_us = delta_from_mid_point_in_us_2 - delta_from_mid_point_in_us_1; @@ -426,7 +343,7 @@ static void apply_below_the_range(struct core_freesync *core_freesync, if (in_out_vrr->btr.frames_to_insert != 0 && delta_from_mid_point_delta_in_us < BTR_DRIFT_MARGIN) { if (((last_render_time_in_us / in_out_vrr->btr.frames_to_insert) < - in_out_vrr->max_duration_in_us) && + max_render_time_in_us) && ((last_render_time_in_us / in_out_vrr->btr.frames_to_insert) > in_out_vrr->min_duration_in_us)) frames_to_insert = in_out_vrr->btr.frames_to_insert; @@ -435,6 +352,12 @@ static void apply_below_the_range(struct core_freesync *core_freesync, /* Either we've calculated the number of frames to insert, * or we need to insert min duration frames */ + if (last_render_time_in_us / frames_to_insert < + in_out_vrr->min_duration_in_us){ + frames_to_insert -= (frames_to_insert > 1) ? + 1 : 0; + } + if (frames_to_insert > 0) inserted_frame_duration_in_us = last_render_time_in_us / frames_to_insert; @@ -458,7 +381,7 @@ static void apply_fixed_refresh(struct core_freesync *core_freesync, bool update = false; unsigned int max_render_time_in_us = in_out_vrr->max_duration_in_us; - //Compute the exit refresh rate and exit frame duration + /* Compute the exit refresh rate and exit frame duration */ unsigned int exit_refresh_rate_in_milli_hz = ((1000000000/max_render_time_in_us) + (1000*FIXED_REFRESH_EXIT_MARGIN_IN_HZ)); unsigned int exit_frame_duration_in_us = 1000000000/exit_refresh_rate_in_milli_hz; @@ -568,22 +491,64 @@ bool mod_freesync_get_v_position(struct mod_freesync *mod_freesync, return false; } -static void build_vrr_infopacket_header_vtem(enum signal_type signal, +static void build_vrr_infopacket_data(const struct mod_vrr_params *vrr, + struct dc_info_packet *infopacket) +{ + /* PB1 = 0x1A (24bit AMD IEEE OUI (0x00001A) - Byte 0) */ + infopacket->sb[1] = 0x1A; + + /* PB2 = 0x00 (24bit AMD IEEE OUI (0x00001A) - Byte 1) */ + infopacket->sb[2] = 0x00; + + /* PB3 = 0x00 (24bit AMD IEEE OUI (0x00001A) - Byte 2) */ + infopacket->sb[3] = 0x00; + + /* PB4 = Reserved */ + + /* PB5 = Reserved */ + + /* PB6 = [Bits 7:3 = Reserved] */ + + /* PB6 = [Bit 0 = FreeSync Supported] */ + if (vrr->state != VRR_STATE_UNSUPPORTED) + infopacket->sb[6] |= 0x01; + + /* PB6 = [Bit 1 = FreeSync Enabled] */ + if (vrr->state != VRR_STATE_DISABLED && + vrr->state != VRR_STATE_UNSUPPORTED) + infopacket->sb[6] |= 0x02; + + /* PB6 = [Bit 2 = FreeSync Active] */ + if (vrr->state == VRR_STATE_ACTIVE_VARIABLE || + vrr->state == VRR_STATE_ACTIVE_FIXED) + infopacket->sb[6] |= 0x04; + + /* PB7 = FreeSync Minimum refresh rate (Hz) */ + infopacket->sb[7] = (unsigned char)(vrr->min_refresh_in_uhz / 1000000); + + /* PB8 = FreeSync Maximum refresh rate (Hz) + * Note: We should never go above the field rate of the mode timing set. + */ + infopacket->sb[8] = (unsigned char)(vrr->max_refresh_in_uhz / 1000000); + + + //FreeSync HDR + infopacket->sb[9] = 0; + infopacket->sb[10] = 0; +} + +static void build_vrr_infopacket_fs2_data(enum color_transfer_func app_tf, struct dc_info_packet *infopacket) { - // HEADER - - // HB0, HB1, HB2 indicates PacketType VTEMPacket - infopacket->hb0 = 0x7F; - infopacket->hb1 = 0xC0; - infopacket->hb2 = 0x00; //sequence_index - - setFieldWithMask(&infopacket->sb[VRR_VTEM_PB0], MASK__VRR_VTEM_PB0__VFR, 1); - setFieldWithMask(&infopacket->sb[VRR_VTEM_PB2], MASK__VRR_VTEM_PB2__ORGANIZATION_ID, 1); - setFieldWithMask(&infopacket->sb[VRR_VTEM_PB3], MASK__VRR_VTEM_PB3__DATA_SET_TAG_MSB, 0); - setFieldWithMask(&infopacket->sb[VRR_VTEM_PB4], MASK__VRR_VTEM_PB4__DATA_SET_TAG_LSB, 1); - setFieldWithMask(&infopacket->sb[VRR_VTEM_PB5], MASK__VRR_VTEM_PB5__DATA_SET_LENGTH_MSB, 0); - setFieldWithMask(&infopacket->sb[VRR_VTEM_PB6], MASK__VRR_VTEM_PB6__DATA_SET_LENGTH_LSB, 4); + if (app_tf != TRANSFER_FUNC_UNKNOWN) { + infopacket->valid = true; + + infopacket->sb[6] |= 0x08; // PB6 = [Bit 3 = Native Color Active] + + if (app_tf == TRANSFER_FUNC_GAMMA_22) { + infopacket->sb[9] |= 0x04; // PB6 = [Bit 2 = Gamma 2.2 EOTF Active] + } + } } static void build_vrr_infopacket_header_v1(enum signal_type signal, @@ -684,105 +649,6 @@ static void build_vrr_infopacket_header_v2(enum signal_type signal, } } -static void build_vrr_vtem_infopacket_data(const struct dc_stream_state *stream, - const struct mod_vrr_params *vrr, - struct dc_info_packet *infopacket) -{ - unsigned int fieldRateInHz; - - if (vrr->state == VRR_STATE_ACTIVE_VARIABLE || - vrr->state == VRR_STATE_ACTIVE_FIXED) { - setFieldWithMask(&infopacket->sb[VRR_VTEM_MD0], MASK__VRR_VTEM_MD0__VRR_EN, 1); - } else { - setFieldWithMask(&infopacket->sb[VRR_VTEM_MD0], MASK__VRR_VTEM_MD0__VRR_EN, 0); - } - - if (!stream->timing.vic) { - setFieldWithMask(&infopacket->sb[VRR_VTEM_MD1], MASK__VRR_VTEM_MD1__BASE_VFRONT, - stream->timing.v_front_porch); - - - /* TODO: In dal2, we check mode flags for a reduced blanking timing. - * Need a way to relay that information to this function. - * if("ReducedBlanking") - * { - * setFieldWithMask(&infopacket->sb[VRR_VTEM_MD2], MASK__VRR_VTEM_MD2__RB, 1; - * } - */ - - //TODO: DAL2 does FixPoint and rounding. Here we might need to account for that - fieldRateInHz = (stream->timing.pix_clk_100hz * 100)/ - (stream->timing.h_total * stream->timing.v_total); - - setFieldWithMask(&infopacket->sb[VRR_VTEM_MD2], MASK__VRR_VTEM_MD2__BASE_REFRESH_RATE_98, - fieldRateInHz >> 8); - setFieldWithMask(&infopacket->sb[VRR_VTEM_MD3], MASK__VRR_VTEM_MD3__BASE_REFRESH_RATE_07, - fieldRateInHz); - - } - infopacket->valid = true; -} - -static void build_vrr_infopacket_data(const struct mod_vrr_params *vrr, - struct dc_info_packet *infopacket) -{ - /* PB1 = 0x1A (24bit AMD IEEE OUI (0x00001A) - Byte 0) */ - infopacket->sb[1] = 0x1A; - - /* PB2 = 0x00 (24bit AMD IEEE OUI (0x00001A) - Byte 1) */ - infopacket->sb[2] = 0x00; - - /* PB3 = 0x00 (24bit AMD IEEE OUI (0x00001A) - Byte 2) */ - infopacket->sb[3] = 0x00; - - /* PB4 = Reserved */ - - /* PB5 = Reserved */ - - /* PB6 = [Bits 7:3 = Reserved] */ - - /* PB6 = [Bit 0 = FreeSync Supported] */ - if (vrr->state != VRR_STATE_UNSUPPORTED) - infopacket->sb[6] |= 0x01; - - /* PB6 = [Bit 1 = FreeSync Enabled] */ - if (vrr->state != VRR_STATE_DISABLED && - vrr->state != VRR_STATE_UNSUPPORTED) - infopacket->sb[6] |= 0x02; - - /* PB6 = [Bit 2 = FreeSync Active] */ - if (vrr->state == VRR_STATE_ACTIVE_VARIABLE || - vrr->state == VRR_STATE_ACTIVE_FIXED) - infopacket->sb[6] |= 0x04; - - /* PB7 = FreeSync Minimum refresh rate (Hz) */ - infopacket->sb[7] = (unsigned char)(vrr->min_refresh_in_uhz / 1000000); - - /* PB8 = FreeSync Maximum refresh rate (Hz) - * Note: We should never go above the field rate of the mode timing set. - */ - infopacket->sb[8] = (unsigned char)(vrr->max_refresh_in_uhz / 1000000); - - - //FreeSync HDR - infopacket->sb[9] = 0; - infopacket->sb[10] = 0; -} - -static void build_vrr_infopacket_fs2_data(enum color_transfer_func app_tf, - struct dc_info_packet *infopacket) -{ - if (app_tf != TRANSFER_FUNC_UNKNOWN) { - infopacket->valid = true; - - infopacket->sb[6] |= 0x08; // PB6 = [Bit 3 = Native Color Active] - - if (app_tf == TRANSFER_FUNC_GAMMA_22) { - infopacket->sb[9] |= 0x04; // PB6 = [Bit 2 = Gamma 2.2 EOTF Active] - } - } -} - static void build_vrr_infopacket_checksum(unsigned int *payload_size, struct dc_info_packet *infopacket) { @@ -835,21 +701,6 @@ static void build_vrr_infopacket_v2(enum signal_type signal, infopacket->valid = true; } -static void build_vrr_infopacket_vtem(const struct dc_stream_state *stream, - const struct mod_vrr_params *vrr, - struct dc_info_packet *infopacket) -{ - //VTEM info packet for HdmiVrr - - memset(infopacket, 0, sizeof(struct dc_info_packet)); - - //VTEM Packet is structured differently - build_vrr_infopacket_header_vtem(stream->signal, infopacket); - build_vrr_vtem_infopacket_data(stream, vrr, infopacket); - - infopacket->valid = true; -} - void mod_freesync_build_vrr_infopacket(struct mod_freesync *mod_freesync, const struct dc_stream_state *stream, const struct mod_vrr_params *vrr, @@ -862,16 +713,13 @@ void mod_freesync_build_vrr_infopacket(struct mod_freesync *mod_freesync, * Check if Freesync is supported. Return if false. If true, * set the corresponding bit in the info packet */ - if (!vrr->supported || (!vrr->send_info_frame && packet_type != PACKET_TYPE_VTEM)) + if (!vrr->supported || (!vrr->send_info_frame)) return; switch (packet_type) { case PACKET_TYPE_FS2: build_vrr_infopacket_v2(stream->signal, vrr, app_tf, infopacket); break; - case PACKET_TYPE_VTEM: - build_vrr_infopacket_vtem(stream, vrr, infopacket); - break; case PACKET_TYPE_VRR: case PACKET_TYPE_FS1: default: @@ -887,8 +735,8 @@ void mod_freesync_build_vrr_params(struct mod_freesync *mod_freesync, struct core_freesync *core_freesync = NULL; unsigned long long nominal_field_rate_in_uhz = 0; unsigned int refresh_range = 0; - unsigned int min_refresh_in_uhz = 0; - unsigned int max_refresh_in_uhz = 0; + unsigned long long min_refresh_in_uhz = 0; + unsigned long long max_refresh_in_uhz = 0; if (mod_freesync == NULL) return; @@ -899,6 +747,10 @@ void mod_freesync_build_vrr_params(struct mod_freesync *mod_freesync, nominal_field_rate_in_uhz = mod_freesync_calc_nominal_field_rate(stream); + /* Rounded to the nearest Hz */ + nominal_field_rate_in_uhz = 1000000ULL * + div_u64(nominal_field_rate_in_uhz + 500000, 1000000); + min_refresh_in_uhz = in_config->min_refresh_in_uhz; max_refresh_in_uhz = in_config->max_refresh_in_uhz; @@ -915,7 +767,7 @@ void mod_freesync_build_vrr_params(struct mod_freesync *mod_freesync, min_refresh_in_uhz = nominal_field_rate_in_uhz; if (!vrr_settings_require_update(core_freesync, - in_config, min_refresh_in_uhz, max_refresh_in_uhz, + in_config, (unsigned int)min_refresh_in_uhz, (unsigned int)max_refresh_in_uhz, in_out_vrr)) return; @@ -931,36 +783,45 @@ void mod_freesync_build_vrr_params(struct mod_freesync *mod_freesync, return; } else { - in_out_vrr->min_refresh_in_uhz = min_refresh_in_uhz; + in_out_vrr->min_refresh_in_uhz = (unsigned int)min_refresh_in_uhz; in_out_vrr->max_duration_in_us = calc_duration_in_us_from_refresh_in_uhz( - min_refresh_in_uhz); + (unsigned int)min_refresh_in_uhz); - in_out_vrr->max_refresh_in_uhz = max_refresh_in_uhz; + in_out_vrr->max_refresh_in_uhz = (unsigned int)max_refresh_in_uhz; in_out_vrr->min_duration_in_us = calc_duration_in_us_from_refresh_in_uhz( - max_refresh_in_uhz); + (unsigned int)max_refresh_in_uhz); refresh_range = in_out_vrr->max_refresh_in_uhz - in_out_vrr->min_refresh_in_uhz; + in_out_vrr->btr.margin_in_us = in_out_vrr->max_duration_in_us - + 2 * in_out_vrr->min_duration_in_us; + if (in_out_vrr->btr.margin_in_us > BTR_MAX_MARGIN) + in_out_vrr->btr.margin_in_us = BTR_MAX_MARGIN; + in_out_vrr->supported = true; } in_out_vrr->fixed.ramping_active = in_config->ramping; in_out_vrr->btr.btr_enabled = in_config->btr; + if (in_out_vrr->max_refresh_in_uhz < 2 * in_out_vrr->min_refresh_in_uhz) in_out_vrr->btr.btr_enabled = false; + in_out_vrr->btr.btr_active = false; in_out_vrr->btr.inserted_duration_in_us = 0; in_out_vrr->btr.frames_to_insert = 0; in_out_vrr->btr.frame_counter = 0; + in_out_vrr->fixed.fixed_active = false; + in_out_vrr->fixed.target_refresh_in_uhz = 0; + in_out_vrr->btr.mid_point_in_us = - in_out_vrr->min_duration_in_us + - (in_out_vrr->max_duration_in_us - - in_out_vrr->min_duration_in_us) / 2; + (in_out_vrr->min_duration_in_us + + in_out_vrr->max_duration_in_us) / 2; if (in_out_vrr->state == VRR_STATE_UNSUPPORTED) { in_out_vrr->adjust.v_total_min = stream->timing.v_total; @@ -973,6 +834,7 @@ void mod_freesync_build_vrr_params(struct mod_freesync *mod_freesync, in_out_vrr->adjust.v_total_max = stream->timing.v_total; } else if (in_out_vrr->state == VRR_STATE_ACTIVE_VARIABLE && refresh_range >= MIN_REFRESH_RANGE_IN_US) { + in_out_vrr->adjust.v_total_min = calc_v_total_from_refresh(stream, in_out_vrr->max_refresh_in_uhz); @@ -1130,13 +992,9 @@ void mod_freesync_get_settings(struct mod_freesync *mod_freesync, unsigned int *inserted_frames, unsigned int *inserted_duration_in_us) { - struct core_freesync *core_freesync = NULL; - if (mod_freesync == NULL) return; - core_freesync = MOD_FREESYNC_TO_CORE(mod_freesync); - if (vrr->supported) { *v_total_min = vrr->adjust.v_total_min; *v_total_max = vrr->adjust.v_total_max; @@ -1151,14 +1009,13 @@ unsigned long long mod_freesync_calc_nominal_field_rate( const struct dc_stream_state *stream) { unsigned long long nominal_field_rate_in_uhz = 0; + unsigned int total = stream->timing.h_total * stream->timing.v_total; - /* Calculate nominal field rate for stream */ + /* Calculate nominal field rate for stream, rounded up to nearest integer */ nominal_field_rate_in_uhz = stream->timing.pix_clk_100hz / 10; nominal_field_rate_in_uhz *= 1000ULL * 1000ULL * 1000ULL; - nominal_field_rate_in_uhz = div_u64(nominal_field_rate_in_uhz, - stream->timing.h_total); - nominal_field_rate_in_uhz = div_u64(nominal_field_rate_in_uhz, - stream->timing.v_total); + + nominal_field_rate_in_uhz = div_u64(nominal_field_rate_in_uhz, total); return nominal_field_rate_in_uhz; } diff --git a/drivers/gpu/drm/amd/display/modules/hdcp/Makefile b/drivers/gpu/drm/amd/display/modules/hdcp/Makefile new file mode 100644 index 000000000000..904424da01b5 --- /dev/null +++ b/drivers/gpu/drm/amd/display/modules/hdcp/Makefile @@ -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) diff --git a/drivers/gpu/drm/amd/display/modules/hdcp/hdcp.c b/drivers/gpu/drm/amd/display/modules/hdcp/hdcp.c new file mode 100644 index 000000000000..8aa528e874c4 --- /dev/null +++ b/drivers/gpu/drm/amd/display/modules/hdcp/hdcp.c @@ -0,0 +1,509 @@ +/* + * 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; + + 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++; + } else if (is_hdcp2(hdcp)) { + hdcp->connection.hdcp2_retry_count++; + } +} + +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->connection.displays[i].state != MOD_HDCP_DISPLAY_INACTIVE && + !hdcp->connection.displays[i].adjust.disable) { + is_auth_needed = 1; + break; + } + } + + return (hdcp->connection.hdcp1_retry_count < MAX_NUM_OF_ATTEMPTS) && + is_auth_needed && + !hdcp->connection.link.adjust.hdcp1.disable; +} + +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->connection.displays[i].state != MOD_HDCP_DISPLAY_INACTIVE && + !hdcp->connection.displays[i].adjust.disable) { + is_auth_needed = 1; + break; + } + } + + return (hdcp->connection.hdcp2_retry_count < MAX_NUM_OF_ATTEMPTS) && + 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; + } + /* update topology event if hdcp is not desired */ + status = mod_hdcp_add_display_topology(hdcp); + } 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); + } +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); + } + 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); + } + else { + callback_in_ms(0, output); + set_state_id(hdcp, output, HDCP_CP_NOT_DESIRED); + } + } 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); + + } + if (hdcp->auth.trans_input.hdcp1.add_topology == PASS) { + status = mod_hdcp_remove_display_topology(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_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; + } + } + if (hdcp->auth.trans_input.hdcp2.add_topology == PASS) { + status = mod_hdcp_remove_display_topology(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)) { + status = mod_hdcp_remove_display_topology(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); + } + +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; +} + +/* + * 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(hdcp, 0, sizeof(struct mod_hdcp)); + 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; + + /* add display to connection */ + hdcp->connection.link = *link; + *display_container = *display; + + /* reset retry counters */ + reset_retry_counts(hdcp); + + /* reset error trace */ + memset(&hdcp->connection.trace, 0, sizeof(hdcp->connection.trace)); + + /* 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; + + /* remove display */ + display->state = MOD_HDCP_DISPLAY_INACTIVE; + + /* clear retry counters */ + reset_retry_counts(hdcp); + + /* reset error trace */ + memset(&hdcp->connection.trace, 0, sizeof(hdcp->connection.trace)); + + /* request authentication for remaining displays*/ + if (get_active_display_count(hdcp) > 0) + 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_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) { + HDCP_FULL_DDC_TRACE(hdcp); + reset_status = reset_authentication(hdcp, output); + if (reset_status != MOD_HDCP_STATUS_SUCCESS) + push_error_status(hdcp, reset_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: + mode = MOD_HDCP_MODE_DEFAULT; + break; + case SIGNAL_TYPE_EDP: + case SIGNAL_TYPE_DISPLAY_PORT: + mode = MOD_HDCP_MODE_DP; + break; + case SIGNAL_TYPE_DISPLAY_PORT_MST: + mode = MOD_HDCP_MODE_DP_MST; + break; + default: + break; + } + + return mode; +} diff --git a/drivers/gpu/drm/amd/display/modules/hdcp/hdcp.h b/drivers/gpu/drm/amd/display/modules/hdcp/hdcp.h new file mode 100644 index 000000000000..af78e4f1be68 --- /dev/null +++ b/drivers/gpu/drm/amd/display/modules/hdcp/hdcp.h @@ -0,0 +1,587 @@ +/* + * 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/drm_hdcp.h> +#include <drm/drm_dp_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 add_topology; + 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 add_topology; + 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_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; + struct mod_hdcp_display displays[MAX_NUM_OF_DISPLAYS]; + uint8_t is_repeater; + uint8_t is_km_stored; + 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 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); +/* TODO: add adjustment log */ + +/* psp functions */ +enum mod_hdcp_status mod_hdcp_add_display_topology( + struct mod_hdcp *hdcp); +enum mod_hdcp_status mod_hdcp_remove_display_topology( + struct mod_hdcp *hdcp); +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_hdcp1_get_link_encryption_status(struct mod_hdcp *hdcp, + enum mod_hdcp_encryption_status *encryption_status); +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); +enum mod_hdcp_status mod_hdcp_hdcp2_get_link_encryption_status(struct mod_hdcp *hdcp, + enum mod_hdcp_encryption_status *encryption_status); + +/* 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); + +/* hdcp version helpers */ +static inline uint8_t is_dp_hdcp(struct mod_hdcp *hdcp) +{ + return (hdcp->connection.link.mode == MOD_HDCP_MODE_DP || + hdcp->connection.link.mode == MOD_HDCP_MODE_DP_MST); +} + +static inline uint8_t is_dp_mst_hdcp(struct mod_hdcp *hdcp) +{ + return (hdcp->connection.link.mode == MOD_HDCP_MODE_DP_MST); +} + +static inline uint8_t is_hdmi_dvi_sl_hdcp(struct mod_hdcp *hdcp) +{ + return (hdcp->connection.link.mode == MOD_HDCP_MODE_DEFAULT); +} + +/* 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_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) +{ + output->watchdog_timer_needed = 1; + output->watchdog_timer_delay = time; +} + +/* 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_added(struct mod_hdcp_display *display) +{ + return display->state >= MOD_HDCP_DISPLAY_ACTIVE_AND_ADDED; +} + +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 added_count = 0; + uint8_t i; + + for (i = 0; i < MAX_NUM_OF_DISPLAYS; i++) + if (is_display_active(&hdcp->connection.displays[i])) + added_count++; + return added_count; +} + +static inline uint8_t get_added_display_count(struct mod_hdcp *hdcp) +{ + uint8_t added_count = 0; + uint8_t i; + + for (i = 0; i < MAX_NUM_OF_DISPLAYS; i++) + if (is_display_added(&hdcp->connection.displays[i])) + added_count++; + return added_count; +} + +static inline struct mod_hdcp_display *get_first_added_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_added(&hdcp->connection.displays[i])) { + display = &hdcp->connection.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->connection.displays[i].index == index && + is_display_active(&hdcp->connection.displays[i])) { + display = &hdcp->connection.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->connection.displays[i])) { + display = &hdcp->connection.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_ */ diff --git a/drivers/gpu/drm/amd/display/modules/hdcp/hdcp1_execution.c b/drivers/gpu/drm/amd/display/modules/hdcp/hdcp1_execution.c new file mode 100644 index 000000000000..37670db64855 --- /dev/null +++ b/drivers/gpu/drm/amd/display/modules/hdcp/hdcp1_execution.c @@ -0,0 +1,529 @@ +/* + * 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; + + memcpy(&n, hdcp->auth.msg.hdcp1.bksv, sizeof(uint64_t)); + + while (n) { + count++; + n &= (n - 1); + } + return (count == 20) ? MOD_HDCP_STATUS_SUCCESS : + MOD_HDCP_STATUS_HDCP1_INVALID_BKSV; +} + +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) +{ + /* device count must be greater than or equal to tracked hdcp displays */ + return (get_device_count(hdcp) < get_added_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_add_display_topology, + &input->add_topology, &status, + hdcp, "add_topology")) + 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; + } + + if (!mod_hdcp_execute_and_set(mod_hdcp_hdcp1_link_maintenance, + &input->link_maintenance, &status, + hdcp, "link_maintenance")) + goto out; +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; + } + + 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; +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; +} + +extern 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; +} diff --git a/drivers/gpu/drm/amd/display/modules/hdcp/hdcp1_transition.c b/drivers/gpu/drm/amd/display/modules/hdcp/hdcp1_transition.c new file mode 100644 index 000000000000..76edcbe51f71 --- /dev/null +++ b/drivers/gpu/drm/amd/display/modules/hdcp/hdcp1_transition.c @@ -0,0 +1,319 @@ +/* + * 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->add_topology != PASS || + 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); + HDCP_FULL_DDC_TRACE(hdcp); + } + break; + case H1_A45_AUTHENTICATED: + if (input->link_maintenance != PASS) { + /* 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); + HDCP_FULL_DDC_TRACE(hdcp); + 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->add_topology != PASS || + 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) { + /* 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; + } + 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); + HDCP_FULL_DDC_TRACE(hdcp); + } + break; + case D1_A4_AUTHENTICATED: + if (input->link_integrity_check != PASS || + input->reauth_request_check != PASS) { + /* 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) { + /* 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); + HDCP_FULL_DDC_TRACE(hdcp); + break; + default: + fail_and_restart_in_ms(0, &status, output); + break; + } + + return status; +} diff --git a/drivers/gpu/drm/amd/display/modules/hdcp/hdcp2_execution.c b/drivers/gpu/drm/amd/display/modules/hdcp/hdcp2_execution.c new file mode 100644 index 000000000000..f730b94ac3c0 --- /dev/null +++ b/drivers/gpu/drm/amd/display/modules/hdcp/hdcp2_execution.c @@ -0,0 +1,886 @@ +/* + * Copyright 2018 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include <linux/delay.h> + +#include "hdcp.h" + +static inline enum mod_hdcp_status check_receiver_id_list_ready(struct mod_hdcp *hdcp) +{ + uint8_t is_ready = 0; + + if (is_dp_hdcp(hdcp)) + is_ready = HDCP_2_2_DP_RXSTATUS_READY(hdcp->auth.msg.hdcp2.rxstatus_dp) ? 1 : 0; + else + is_ready = (HDCP_2_2_HDMI_RXSTATUS_READY(hdcp->auth.msg.hdcp2.rxstatus[0]) && + (HDCP_2_2_HDMI_RXSTATUS_MSG_SZ_HI(hdcp->auth.msg.hdcp2.rxstatus[1]) << 8 | + hdcp->auth.msg.hdcp2.rxstatus[0])) ? 1 : 0; + return is_ready ? MOD_HDCP_STATUS_SUCCESS : + MOD_HDCP_STATUS_HDCP2_RX_ID_LIST_NOT_READY; +} + +static inline enum mod_hdcp_status check_hdcp2_capable(struct mod_hdcp *hdcp) +{ + enum mod_hdcp_status status; + + if (is_dp_hdcp(hdcp)) + status = (hdcp->auth.msg.hdcp2.rxcaps_dp[2] & HDCP_2_2_RX_CAPS_VERSION_VAL) && + HDCP_2_2_DP_HDCP_CAPABLE(hdcp->auth.msg.hdcp2.rxcaps_dp[0]) ? + MOD_HDCP_STATUS_SUCCESS : + MOD_HDCP_STATUS_HDCP2_NOT_CAPABLE; + else + status = (hdcp->auth.msg.hdcp2.hdcp2version_hdmi & HDCP_2_2_HDMI_SUPPORT_MASK) ? + MOD_HDCP_STATUS_SUCCESS : + MOD_HDCP_STATUS_HDCP2_NOT_CAPABLE; + return status; +} + +static inline enum mod_hdcp_status check_reauthentication_request( + struct mod_hdcp *hdcp) +{ + uint8_t ret = 0; + + if (is_dp_hdcp(hdcp)) + ret = HDCP_2_2_DP_RXSTATUS_REAUTH_REQ(hdcp->auth.msg.hdcp2.rxstatus_dp) ? + MOD_HDCP_STATUS_HDCP2_REAUTH_REQUEST : + MOD_HDCP_STATUS_SUCCESS; + else + ret = HDCP_2_2_HDMI_RXSTATUS_REAUTH_REQ(hdcp->auth.msg.hdcp2.rxstatus[0]) ? + MOD_HDCP_STATUS_HDCP2_REAUTH_REQUEST : + MOD_HDCP_STATUS_SUCCESS; + return ret; +} + +static inline enum mod_hdcp_status check_link_integrity_failure_dp( + struct mod_hdcp *hdcp) +{ + return HDCP_2_2_DP_RXSTATUS_LINK_FAILED(hdcp->auth.msg.hdcp2.rxstatus_dp) ? + MOD_HDCP_STATUS_HDCP2_REAUTH_LINK_INTEGRITY_FAILURE : + MOD_HDCP_STATUS_SUCCESS; +} + +static enum mod_hdcp_status check_ake_cert_available(struct mod_hdcp *hdcp) +{ + enum mod_hdcp_status status; + uint16_t size; + + if (is_dp_hdcp(hdcp)) { + status = MOD_HDCP_STATUS_SUCCESS; + } else { + status = mod_hdcp_read_rxstatus(hdcp); + if (status == MOD_HDCP_STATUS_SUCCESS) { + size = HDCP_2_2_HDMI_RXSTATUS_MSG_SZ_HI(hdcp->auth.msg.hdcp2.rxstatus[1]) << 8 | + hdcp->auth.msg.hdcp2.rxstatus[0]; + status = (size == sizeof(hdcp->auth.msg.hdcp2.ake_cert)) ? + MOD_HDCP_STATUS_SUCCESS : + MOD_HDCP_STATUS_HDCP2_AKE_CERT_PENDING; + } + } + return status; +} + +static enum mod_hdcp_status check_h_prime_available(struct mod_hdcp *hdcp) +{ + enum mod_hdcp_status status; + uint8_t size; + + status = mod_hdcp_read_rxstatus(hdcp); + if (status != MOD_HDCP_STATUS_SUCCESS) + goto out; + + if (is_dp_hdcp(hdcp)) { + status = HDCP_2_2_DP_RXSTATUS_H_PRIME(hdcp->auth.msg.hdcp2.rxstatus_dp) ? + MOD_HDCP_STATUS_SUCCESS : + MOD_HDCP_STATUS_HDCP2_H_PRIME_PENDING; + } else { + size = HDCP_2_2_HDMI_RXSTATUS_MSG_SZ_HI(hdcp->auth.msg.hdcp2.rxstatus[1]) << 8 | + hdcp->auth.msg.hdcp2.rxstatus[0]; + status = (size == sizeof(hdcp->auth.msg.hdcp2.ake_h_prime)) ? + MOD_HDCP_STATUS_SUCCESS : + MOD_HDCP_STATUS_HDCP2_H_PRIME_PENDING; + } +out: + return status; +} + +static enum mod_hdcp_status check_pairing_info_available(struct mod_hdcp *hdcp) +{ + enum mod_hdcp_status status; + uint8_t size; + + status = mod_hdcp_read_rxstatus(hdcp); + if (status != MOD_HDCP_STATUS_SUCCESS) + goto out; + + if (is_dp_hdcp(hdcp)) { + status = HDCP_2_2_DP_RXSTATUS_PAIRING(hdcp->auth.msg.hdcp2.rxstatus_dp) ? + MOD_HDCP_STATUS_SUCCESS : + MOD_HDCP_STATUS_HDCP2_PAIRING_INFO_PENDING; + } else { + size = HDCP_2_2_HDMI_RXSTATUS_MSG_SZ_HI(hdcp->auth.msg.hdcp2.rxstatus[1]) << 8 | + hdcp->auth.msg.hdcp2.rxstatus[0]; + status = (size == sizeof(hdcp->auth.msg.hdcp2.ake_pairing_info)) ? + MOD_HDCP_STATUS_SUCCESS : + MOD_HDCP_STATUS_HDCP2_PAIRING_INFO_PENDING; + } +out: + return status; +} + +static enum mod_hdcp_status poll_l_prime_available(struct mod_hdcp *hdcp) +{ + enum mod_hdcp_status status; + uint8_t size; + uint16_t max_wait = 20; // units of ms + uint16_t num_polls = 5; + uint16_t wait_time = max_wait / num_polls; + + if (is_dp_hdcp(hdcp)) + status = MOD_HDCP_STATUS_INVALID_OPERATION; + else + for (; num_polls; num_polls--) { + msleep(wait_time); + + status = mod_hdcp_read_rxstatus(hdcp); + if (status != MOD_HDCP_STATUS_SUCCESS) + break; + + size = HDCP_2_2_HDMI_RXSTATUS_MSG_SZ_HI(hdcp->auth.msg.hdcp2.rxstatus[1]) << 8 | + hdcp->auth.msg.hdcp2.rxstatus[0]; + status = (size == sizeof(hdcp->auth.msg.hdcp2.lc_l_prime)) ? + MOD_HDCP_STATUS_SUCCESS : + MOD_HDCP_STATUS_HDCP2_L_PRIME_PENDING; + if (status == MOD_HDCP_STATUS_SUCCESS) + break; + } + return status; +} + +static enum mod_hdcp_status check_stream_ready_available(struct mod_hdcp *hdcp) +{ + enum mod_hdcp_status status; + uint8_t size; + + if (is_dp_hdcp(hdcp)) { + status = MOD_HDCP_STATUS_INVALID_OPERATION; + } else { + status = mod_hdcp_read_rxstatus(hdcp); + if (status != MOD_HDCP_STATUS_SUCCESS) + goto out; + size = HDCP_2_2_HDMI_RXSTATUS_MSG_SZ_HI(hdcp->auth.msg.hdcp2.rxstatus[1]) << 8 | + hdcp->auth.msg.hdcp2.rxstatus[0]; + status = (size == sizeof(hdcp->auth.msg.hdcp2.repeater_auth_stream_ready)) ? + MOD_HDCP_STATUS_SUCCESS : + MOD_HDCP_STATUS_HDCP2_STREAM_READY_PENDING; + } +out: + return status; +} + +static inline uint8_t get_device_count(struct mod_hdcp *hdcp) +{ + return HDCP_2_2_DEV_COUNT_LO(hdcp->auth.msg.hdcp2.rx_id_list[2]) + + (HDCP_2_2_DEV_COUNT_HI(hdcp->auth.msg.hdcp2.rx_id_list[1]) << 4); +} + +static enum mod_hdcp_status check_device_count(struct mod_hdcp *hdcp) +{ + /* device count must be greater than or equal to tracked hdcp displays */ + return (get_device_count(hdcp) < get_added_display_count(hdcp)) ? + MOD_HDCP_STATUS_HDCP2_DEVICE_COUNT_MISMATCH_FAILURE : + MOD_HDCP_STATUS_SUCCESS; +} + +static uint8_t process_rxstatus(struct mod_hdcp *hdcp, + struct mod_hdcp_event_context *event_ctx, + struct mod_hdcp_transition_input_hdcp2 *input, + enum mod_hdcp_status *status) +{ + if (!mod_hdcp_execute_and_set(mod_hdcp_read_rxstatus, + &input->rxstatus_read, status, + hdcp, "rxstatus_read")) + goto out; + if (!mod_hdcp_execute_and_set(check_reauthentication_request, + &input->reauth_request_check, status, + hdcp, "reauth_request_check")) + goto out; + if (is_dp_hdcp(hdcp)) { + if (!mod_hdcp_execute_and_set(check_link_integrity_failure_dp, + &input->link_integrity_check_dp, status, + hdcp, "link_integrity_check_dp")) + goto out; + } + if (hdcp->connection.is_repeater) + if (check_receiver_id_list_ready(hdcp) == + MOD_HDCP_STATUS_SUCCESS) { + HDCP_INPUT_PASS_TRACE(hdcp, "rx_id_list_ready"); + event_ctx->rx_id_list_ready = 1; + if (is_dp_hdcp(hdcp)) + hdcp->auth.msg.hdcp2.rx_id_list_size = + sizeof(hdcp->auth.msg.hdcp2.rx_id_list); + else + hdcp->auth.msg.hdcp2.rx_id_list_size = + HDCP_2_2_HDMI_RXSTATUS_MSG_SZ_HI(hdcp->auth.msg.hdcp2.rxstatus[1]) << 8 | + hdcp->auth.msg.hdcp2.rxstatus[0]; + } +out: + return (*status == MOD_HDCP_STATUS_SUCCESS); +} + +static enum mod_hdcp_status known_hdcp2_capable_rx(struct mod_hdcp *hdcp, + struct mod_hdcp_event_context *event_ctx, + struct mod_hdcp_transition_input_hdcp2 *input) +{ + enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS; + + if (event_ctx->event != MOD_HDCP_EVENT_CALLBACK) { + event_ctx->unexpected_event = 1; + goto out; + } + if (!mod_hdcp_execute_and_set(mod_hdcp_read_hdcp2version, + &input->hdcp2version_read, &status, + hdcp, "hdcp2version_read")) + goto out; + if (!mod_hdcp_execute_and_set(check_hdcp2_capable, + &input->hdcp2_capable_check, &status, + hdcp, "hdcp2_capable")) + goto out; +out: + return status; +} + +static enum mod_hdcp_status send_ake_init(struct mod_hdcp *hdcp, + struct mod_hdcp_event_context *event_ctx, + struct mod_hdcp_transition_input_hdcp2 *input) +{ + enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS; + + if (event_ctx->event != MOD_HDCP_EVENT_CALLBACK) { + event_ctx->unexpected_event = 1; + goto out; + } + if (!mod_hdcp_execute_and_set(mod_hdcp_add_display_topology, + &input->add_topology, &status, + hdcp, "add_topology")) + goto out; + if (!mod_hdcp_execute_and_set(mod_hdcp_hdcp2_create_session, + &input->create_session, &status, + hdcp, "create_session")) + goto out; + if (!mod_hdcp_execute_and_set(mod_hdcp_hdcp2_prepare_ake_init, + &input->ake_init_prepare, &status, + hdcp, "ake_init_prepare")) + goto out; + if (!mod_hdcp_execute_and_set(mod_hdcp_write_ake_init, + &input->ake_init_write, &status, + hdcp, "ake_init_write")) + goto out; +out: + return status; +} + +static enum mod_hdcp_status validate_ake_cert(struct mod_hdcp *hdcp, + struct mod_hdcp_event_context *event_ctx, + struct mod_hdcp_transition_input_hdcp2 *input) +{ + enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS; + + + if (event_ctx->event != MOD_HDCP_EVENT_CALLBACK && + event_ctx->event != MOD_HDCP_EVENT_WATCHDOG_TIMEOUT) { + event_ctx->unexpected_event = 1; + goto out; + } + + if (is_hdmi_dvi_sl_hdcp(hdcp)) + if (!mod_hdcp_execute_and_set(check_ake_cert_available, + &input->ake_cert_available, &status, + hdcp, "ake_cert_available")) + goto out; + if (!mod_hdcp_execute_and_set(mod_hdcp_read_ake_cert, + &input->ake_cert_read, &status, + hdcp, "ake_cert_read")) + goto out; + if (!mod_hdcp_execute_and_set(mod_hdcp_hdcp2_validate_ake_cert, + &input->ake_cert_validation, &status, + hdcp, "ake_cert_validation")) + goto out; +out: + return status; +} + +static enum mod_hdcp_status send_no_stored_km(struct mod_hdcp *hdcp, + struct mod_hdcp_event_context *event_ctx, + struct mod_hdcp_transition_input_hdcp2 *input) +{ + enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS; + + if (event_ctx->event != MOD_HDCP_EVENT_CALLBACK) { + event_ctx->unexpected_event = 1; + goto out; + } + + if (!mod_hdcp_execute_and_set(mod_hdcp_write_no_stored_km, + &input->no_stored_km_write, &status, + hdcp, "no_stored_km_write")) + goto out; +out: + return status; +} + +static enum mod_hdcp_status read_h_prime(struct mod_hdcp *hdcp, + struct mod_hdcp_event_context *event_ctx, + struct mod_hdcp_transition_input_hdcp2 *input) +{ + enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS; + + if (event_ctx->event != MOD_HDCP_EVENT_CALLBACK && + event_ctx->event != MOD_HDCP_EVENT_CPIRQ && + event_ctx->event != MOD_HDCP_EVENT_WATCHDOG_TIMEOUT) { + event_ctx->unexpected_event = 1; + goto out; + } + + if (!mod_hdcp_execute_and_set(check_h_prime_available, + &input->h_prime_available, &status, + hdcp, "h_prime_available")) + goto out; + + if (!mod_hdcp_execute_and_set(mod_hdcp_read_h_prime, + &input->h_prime_read, &status, + hdcp, "h_prime_read")) + goto out; +out: + return status; +} + +static enum mod_hdcp_status read_pairing_info_and_validate_h_prime( + struct mod_hdcp *hdcp, + struct mod_hdcp_event_context *event_ctx, + struct mod_hdcp_transition_input_hdcp2 *input) +{ + enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS; + + if (event_ctx->event != MOD_HDCP_EVENT_CALLBACK && + event_ctx->event != MOD_HDCP_EVENT_CPIRQ && + event_ctx->event != MOD_HDCP_EVENT_WATCHDOG_TIMEOUT) { + event_ctx->unexpected_event = 1; + goto out; + } + + if (!mod_hdcp_execute_and_set(check_pairing_info_available, + &input->pairing_available, &status, + hdcp, "pairing_available")) + goto out; + if (!mod_hdcp_execute_and_set(mod_hdcp_read_pairing_info, + &input->pairing_info_read, &status, + hdcp, "pairing_info_read")) + goto out; + if (!mod_hdcp_execute_and_set(mod_hdcp_hdcp2_validate_h_prime, + &input->h_prime_validation, &status, + hdcp, "h_prime_validation")) + goto out; +out: + return status; +} + +static enum mod_hdcp_status send_stored_km(struct mod_hdcp *hdcp, + struct mod_hdcp_event_context *event_ctx, + struct mod_hdcp_transition_input_hdcp2 *input) +{ + enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS; + + if (event_ctx->event != MOD_HDCP_EVENT_CALLBACK) { + event_ctx->unexpected_event = 1; + goto out; + } + + if (!mod_hdcp_execute_and_set(mod_hdcp_write_stored_km, + &input->stored_km_write, &status, + hdcp, "stored_km_write")) + goto out; +out: + return status; +} + +static enum mod_hdcp_status validate_h_prime(struct mod_hdcp *hdcp, + struct mod_hdcp_event_context *event_ctx, + struct mod_hdcp_transition_input_hdcp2 *input) +{ + enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS; + + if (event_ctx->event != MOD_HDCP_EVENT_CALLBACK && + event_ctx->event != MOD_HDCP_EVENT_CPIRQ && + event_ctx->event != MOD_HDCP_EVENT_WATCHDOG_TIMEOUT) { + event_ctx->unexpected_event = 1; + goto out; + } + + if (!mod_hdcp_execute_and_set(check_h_prime_available, + &input->h_prime_available, &status, + hdcp, "h_prime_available")) + goto out; + if (!mod_hdcp_execute_and_set(mod_hdcp_read_h_prime, + &input->h_prime_read, &status, + hdcp, "h_prime_read")) + goto out; + if (!mod_hdcp_execute_and_set(mod_hdcp_hdcp2_validate_h_prime, + &input->h_prime_validation, &status, + hdcp, "h_prime_validation")) + goto out; +out: + return status; +} + +static enum mod_hdcp_status locality_check(struct mod_hdcp *hdcp, + struct mod_hdcp_event_context *event_ctx, + struct mod_hdcp_transition_input_hdcp2 *input) +{ + enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS; + + if (event_ctx->event != MOD_HDCP_EVENT_CALLBACK) { + event_ctx->unexpected_event = 1; + goto out; + } + + if (!mod_hdcp_execute_and_set(mod_hdcp_hdcp2_prepare_lc_init, + &input->lc_init_prepare, &status, + hdcp, "lc_init_prepare")) + goto out; + if (!mod_hdcp_execute_and_set(mod_hdcp_write_lc_init, + &input->lc_init_write, &status, + hdcp, "lc_init_write")) + goto out; + if (is_dp_hdcp(hdcp)) + msleep(16); + else + if (!mod_hdcp_execute_and_set(poll_l_prime_available, + &input->l_prime_available_poll, &status, + hdcp, "l_prime_available_poll")) + goto out; + if (!mod_hdcp_execute_and_set(mod_hdcp_read_l_prime, + &input->l_prime_read, &status, + hdcp, "l_prime_read")) + goto out; + if (!mod_hdcp_execute_and_set(mod_hdcp_hdcp2_validate_l_prime, + &input->l_prime_validation, &status, + hdcp, "l_prime_validation")) + goto out; +out: + return status; +} + +static enum mod_hdcp_status exchange_ks_and_test_for_repeater(struct mod_hdcp *hdcp, + struct mod_hdcp_event_context *event_ctx, + struct mod_hdcp_transition_input_hdcp2 *input) +{ + enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS; + + if (event_ctx->event != MOD_HDCP_EVENT_CALLBACK) { + event_ctx->unexpected_event = 1; + goto out; + } + + if (!mod_hdcp_execute_and_set(mod_hdcp_hdcp2_prepare_eks, + &input->eks_prepare, &status, + hdcp, "eks_prepare")) + goto out; + if (!mod_hdcp_execute_and_set(mod_hdcp_write_eks, + &input->eks_write, &status, + hdcp, "eks_write")) + goto out; +out: + return status; +} + +static enum mod_hdcp_status enable_encryption(struct mod_hdcp *hdcp, + struct mod_hdcp_event_context *event_ctx, + struct mod_hdcp_transition_input_hdcp2 *input) +{ + enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS; + + if (event_ctx->event != MOD_HDCP_EVENT_CALLBACK && + event_ctx->event != MOD_HDCP_EVENT_CPIRQ) { + event_ctx->unexpected_event = 1; + goto out; + } + if (event_ctx->event == MOD_HDCP_EVENT_CPIRQ) { + process_rxstatus(hdcp, event_ctx, input, &status); + goto out; + } + + if (is_hdmi_dvi_sl_hdcp(hdcp)) { + if (!process_rxstatus(hdcp, event_ctx, input, &status)) + goto out; + if (event_ctx->rx_id_list_ready) + goto out; + } + if (!mod_hdcp_execute_and_set(mod_hdcp_hdcp2_enable_encryption, + &input->enable_encryption, &status, + hdcp, "enable_encryption")) + goto out; + if (is_dp_mst_hdcp(hdcp)) { + if (!mod_hdcp_execute_and_set( + mod_hdcp_hdcp2_enable_dp_stream_encryption, + &input->stream_encryption_dp, &status, + hdcp, "stream_encryption_dp")) + goto out; + } +out: + return status; +} + +static enum mod_hdcp_status authenticated(struct mod_hdcp *hdcp, + struct mod_hdcp_event_context *event_ctx, + struct mod_hdcp_transition_input_hdcp2 *input) +{ + enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS; + + if (event_ctx->event != MOD_HDCP_EVENT_CALLBACK && + event_ctx->event != MOD_HDCP_EVENT_CPIRQ) { + event_ctx->unexpected_event = 1; + goto out; + } + + if (!process_rxstatus(hdcp, event_ctx, input, &status)) + goto out; + if (event_ctx->rx_id_list_ready) + goto out; +out: + return status; +} + +static enum mod_hdcp_status wait_for_rx_id_list(struct mod_hdcp *hdcp, + struct mod_hdcp_event_context *event_ctx, + struct mod_hdcp_transition_input_hdcp2 *input) +{ + enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS; + + if (event_ctx->event != MOD_HDCP_EVENT_CALLBACK && + event_ctx->event != MOD_HDCP_EVENT_CPIRQ && + event_ctx->event != MOD_HDCP_EVENT_WATCHDOG_TIMEOUT) { + event_ctx->unexpected_event = 1; + goto out; + } + + if (!process_rxstatus(hdcp, event_ctx, input, &status)) + goto out; + if (!event_ctx->rx_id_list_ready) { + status = MOD_HDCP_STATUS_HDCP2_RX_ID_LIST_NOT_READY; + goto out; + } +out: + return status; +} + +static enum mod_hdcp_status verify_rx_id_list_and_send_ack(struct mod_hdcp *hdcp, + struct mod_hdcp_event_context *event_ctx, + struct mod_hdcp_transition_input_hdcp2 *input) +{ + enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS; + + if (event_ctx->event != MOD_HDCP_EVENT_CALLBACK && + event_ctx->event != MOD_HDCP_EVENT_CPIRQ) { + event_ctx->unexpected_event = 1; + goto out; + } + if (event_ctx->event == MOD_HDCP_EVENT_CPIRQ) { + process_rxstatus(hdcp, event_ctx, input, &status); + goto out; + } + + if (!mod_hdcp_execute_and_set(mod_hdcp_read_rx_id_list, + &input->rx_id_list_read, + &status, hdcp, "receiver_id_list_read")) + goto out; + if (!mod_hdcp_execute_and_set(check_device_count, + &input->device_count_check, + &status, hdcp, "device_count_check")) + goto out; + if (!mod_hdcp_execute_and_set(mod_hdcp_hdcp2_validate_rx_id_list, + &input->rx_id_list_validation, + &status, hdcp, "rx_id_list_validation")) + goto out; + if (!mod_hdcp_execute_and_set(mod_hdcp_write_repeater_auth_ack, + &input->repeater_auth_ack_write, + &status, hdcp, "repeater_auth_ack_write")) + goto out; +out: + return status; +} + +static enum mod_hdcp_status send_stream_management(struct mod_hdcp *hdcp, + struct mod_hdcp_event_context *event_ctx, + struct mod_hdcp_transition_input_hdcp2 *input) +{ + enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS; + + if (event_ctx->event != MOD_HDCP_EVENT_CALLBACK && + event_ctx->event != MOD_HDCP_EVENT_CPIRQ) { + event_ctx->unexpected_event = 1; + goto out; + } + if (event_ctx->event == MOD_HDCP_EVENT_CPIRQ) { + process_rxstatus(hdcp, event_ctx, input, &status); + goto out; + } + + if (is_hdmi_dvi_sl_hdcp(hdcp)) { + if (!process_rxstatus(hdcp, event_ctx, input, &status)) + goto out; + if (event_ctx->rx_id_list_ready) + goto out; + } + if (!mod_hdcp_execute_and_set(mod_hdcp_hdcp2_prepare_stream_management, + &input->prepare_stream_manage, + &status, hdcp, "prepare_stream_manage")) + goto out; + + if (!mod_hdcp_execute_and_set(mod_hdcp_write_stream_manage, + &input->stream_manage_write, + &status, hdcp, "stream_manage_write")) + goto out; +out: + return status; +} + +static enum mod_hdcp_status validate_stream_ready(struct mod_hdcp *hdcp, + struct mod_hdcp_event_context *event_ctx, + struct mod_hdcp_transition_input_hdcp2 *input) +{ + enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS; + + if (event_ctx->event != MOD_HDCP_EVENT_CALLBACK && + event_ctx->event != MOD_HDCP_EVENT_CPIRQ && + event_ctx->event != MOD_HDCP_EVENT_WATCHDOG_TIMEOUT) { + event_ctx->unexpected_event = 1; + goto out; + } + if (event_ctx->event == MOD_HDCP_EVENT_CPIRQ) { + process_rxstatus(hdcp, event_ctx, input, &status); + goto out; + } + + if (is_hdmi_dvi_sl_hdcp(hdcp)) { + if (!process_rxstatus(hdcp, event_ctx, input, &status)) + goto out; + if (event_ctx->rx_id_list_ready) { + goto out; + } + } + if (is_hdmi_dvi_sl_hdcp(hdcp)) + if (!mod_hdcp_execute_and_set(check_stream_ready_available, + &input->stream_ready_available, + &status, hdcp, "stream_ready_available")) + goto out; + if (!mod_hdcp_execute_and_set(mod_hdcp_read_stream_ready, + &input->stream_ready_read, + &status, hdcp, "stream_ready_read")) + goto out; + if (!mod_hdcp_execute_and_set(mod_hdcp_hdcp2_validate_stream_ready, + &input->stream_ready_validation, + &status, hdcp, "stream_ready_validation")) + goto out; + +out: + return status; +} + +static enum mod_hdcp_status determine_rx_hdcp_capable_dp(struct mod_hdcp *hdcp, + struct mod_hdcp_event_context *event_ctx, + struct mod_hdcp_transition_input_hdcp2 *input) +{ + enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS; + + if (event_ctx->event != MOD_HDCP_EVENT_CALLBACK) { + event_ctx->unexpected_event = 1; + goto out; + } + + if (!mod_hdcp_execute_and_set(mod_hdcp_read_rxcaps, + &input->rx_caps_read_dp, + &status, hdcp, "rx_caps_read_dp")) + goto out; + if (!mod_hdcp_execute_and_set(check_hdcp2_capable, + &input->hdcp2_capable_check, &status, + hdcp, "hdcp2_capable_check")) + goto out; +out: + return status; +} + +static enum mod_hdcp_status send_content_stream_type_dp(struct mod_hdcp *hdcp, + struct mod_hdcp_event_context *event_ctx, + struct mod_hdcp_transition_input_hdcp2 *input) +{ + enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS; + + if (event_ctx->event != MOD_HDCP_EVENT_CALLBACK && + event_ctx->event != MOD_HDCP_EVENT_CPIRQ) { + event_ctx->unexpected_event = 1; + goto out; + } + + if (!process_rxstatus(hdcp, event_ctx, input, &status)) + goto out; + if (!mod_hdcp_execute_and_set(mod_hdcp_write_content_type, + &input->content_stream_type_write, &status, + hdcp, "content_stream_type_write")) + goto out; +out: + return status; +} + +enum mod_hdcp_status mod_hdcp_hdcp2_execution(struct mod_hdcp *hdcp, + struct mod_hdcp_event_context *event_ctx, + struct mod_hdcp_transition_input_hdcp2 *input) +{ + enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS; + + switch (current_state(hdcp)) { + case H2_A0_KNOWN_HDCP2_CAPABLE_RX: + status = known_hdcp2_capable_rx(hdcp, event_ctx, input); + break; + case H2_A1_SEND_AKE_INIT: + status = send_ake_init(hdcp, event_ctx, input); + break; + case H2_A1_VALIDATE_AKE_CERT: + status = validate_ake_cert(hdcp, event_ctx, input); + break; + case H2_A1_SEND_NO_STORED_KM: + status = send_no_stored_km(hdcp, event_ctx, input); + break; + case H2_A1_READ_H_PRIME: + status = read_h_prime(hdcp, event_ctx, input); + break; + case H2_A1_READ_PAIRING_INFO_AND_VALIDATE_H_PRIME: + status = read_pairing_info_and_validate_h_prime(hdcp, + event_ctx, input); + break; + case H2_A1_SEND_STORED_KM: + status = send_stored_km(hdcp, event_ctx, input); + break; + case H2_A1_VALIDATE_H_PRIME: + status = validate_h_prime(hdcp, event_ctx, input); + break; + case H2_A2_LOCALITY_CHECK: + status = locality_check(hdcp, event_ctx, input); + break; + case H2_A3_EXCHANGE_KS_AND_TEST_FOR_REPEATER: + status = exchange_ks_and_test_for_repeater(hdcp, event_ctx, input); + break; + case H2_ENABLE_ENCRYPTION: + status = enable_encryption(hdcp, event_ctx, input); + break; + case H2_A5_AUTHENTICATED: + status = authenticated(hdcp, event_ctx, input); + break; + case H2_A6_WAIT_FOR_RX_ID_LIST: + status = wait_for_rx_id_list(hdcp, event_ctx, input); + break; + case H2_A78_VERIFY_RX_ID_LIST_AND_SEND_ACK: + status = verify_rx_id_list_and_send_ack(hdcp, event_ctx, input); + break; + case H2_A9_SEND_STREAM_MANAGEMENT: + status = send_stream_management(hdcp, event_ctx, input); + break; + case H2_A9_VALIDATE_STREAM_READY: + status = validate_stream_ready(hdcp, event_ctx, input); + break; + default: + status = MOD_HDCP_STATUS_INVALID_STATE; + break; + } + + return status; +} + +enum mod_hdcp_status mod_hdcp_hdcp2_dp_execution(struct mod_hdcp *hdcp, + struct mod_hdcp_event_context *event_ctx, + struct mod_hdcp_transition_input_hdcp2 *input) +{ + enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS; + + switch (current_state(hdcp)) { + case D2_A0_DETERMINE_RX_HDCP_CAPABLE: + status = determine_rx_hdcp_capable_dp(hdcp, event_ctx, input); + break; + case D2_A1_SEND_AKE_INIT: + status = send_ake_init(hdcp, event_ctx, input); + break; + case D2_A1_VALIDATE_AKE_CERT: + status = validate_ake_cert(hdcp, event_ctx, input); + break; + case D2_A1_SEND_NO_STORED_KM: + status = send_no_stored_km(hdcp, event_ctx, input); + break; + case D2_A1_READ_H_PRIME: + status = read_h_prime(hdcp, event_ctx, input); + break; + case D2_A1_READ_PAIRING_INFO_AND_VALIDATE_H_PRIME: + status = read_pairing_info_and_validate_h_prime(hdcp, + event_ctx, input); + break; + case D2_A1_SEND_STORED_KM: + status = send_stored_km(hdcp, event_ctx, input); + break; + case D2_A1_VALIDATE_H_PRIME: + status = validate_h_prime(hdcp, event_ctx, input); + break; + case D2_A2_LOCALITY_CHECK: + status = locality_check(hdcp, event_ctx, input); + break; + case D2_A34_EXCHANGE_KS_AND_TEST_FOR_REPEATER: + status = exchange_ks_and_test_for_repeater(hdcp, + event_ctx, input); + break; + case D2_SEND_CONTENT_STREAM_TYPE: + status = send_content_stream_type_dp(hdcp, event_ctx, input); + break; + case D2_ENABLE_ENCRYPTION: + status = enable_encryption(hdcp, event_ctx, input); + break; + case D2_A5_AUTHENTICATED: + status = authenticated(hdcp, event_ctx, input); + break; + case D2_A6_WAIT_FOR_RX_ID_LIST: + status = wait_for_rx_id_list(hdcp, event_ctx, input); + break; + case D2_A78_VERIFY_RX_ID_LIST_AND_SEND_ACK: + status = verify_rx_id_list_and_send_ack(hdcp, event_ctx, input); + break; + case D2_A9_SEND_STREAM_MANAGEMENT: + status = send_stream_management(hdcp, event_ctx, input); + break; + case D2_A9_VALIDATE_STREAM_READY: + status = validate_stream_ready(hdcp, event_ctx, input); + break; + default: + status = MOD_HDCP_STATUS_INVALID_STATE; + break; + } + + return status; +} diff --git a/drivers/gpu/drm/amd/display/modules/hdcp/hdcp2_transition.c b/drivers/gpu/drm/amd/display/modules/hdcp/hdcp2_transition.c new file mode 100644 index 000000000000..8cae3e3aacd5 --- /dev/null +++ b/drivers/gpu/drm/amd/display/modules/hdcp/hdcp2_transition.c @@ -0,0 +1,679 @@ +/* + * 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->add_topology != PASS || + 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: + if (hdcp->state.stay_count > 10 || + input->lc_init_prepare != PASS || + input->lc_init_write != PASS || + input->l_prime_available_poll != PASS || + input->l_prime_read != PASS) { + /* + * 1A-05: consider disconnection after LC init a failure + * 1A-13-1: consider invalid l' a failure + * 1A-13-2: consider l' timeout a failure + */ + fail_and_restart_in_ms(0, &status, output); + break; + } else if (input->l_prime_validation != PASS) { + 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); + HDCP_FULL_DDC_TRACE(hdcp); + break; + case H2_A5_AUTHENTICATED: + 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; + } + 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->add_topology != PASS || + 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 || + input->lc_init_write != PASS || + input->l_prime_read != PASS) { + /* 1A-12: consider invalid l' a failure */ + fail_and_restart_in_ms(0, &status, output); + break; + } else if (input->l_prime_validation != PASS) { + 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(0, 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); + HDCP_FULL_DDC_TRACE(hdcp); + break; + case D2_A5_AUTHENTICATED: + if (input->rxstatus_read != PASS || + input->reauth_request_check != PASS) { + fail_and_restart_in_ms(0, &status, output); + break; + } else if (input->link_integrity_check_dp != PASS) { + 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; +} diff --git a/drivers/gpu/drm/amd/display/modules/hdcp/hdcp_ddc.c b/drivers/gpu/drm/amd/display/modules/hdcp/hdcp_ddc.c new file mode 100644 index 000000000000..ff9d54812e62 --- /dev/null +++ b/drivers/gpu/drm/amd/display/modules/hdcp/hdcp_ddc.c @@ -0,0 +1,631 @@ +/* + * 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" + +#define MIN(a, b) ((a) < (b) ? (a) : (b)) +#define HDCP_I2C_ADDR 0x3a /* 0x74 >> 1*/ +#define KSV_READ_SIZE 0xf /* 0x6803b - 0x6802c */ +#define HDCP_MAX_AUX_TRANSACTION_SIZE 16 + +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_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_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_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 (is_dp_hdcp(hdcp)) { + 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 { + 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 (is_dp_hdcp(hdcp)) { + 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 { + 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] = 3; + 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] = 7; + 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] = 8; + 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] = 10; + 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; + + if (is_dp_hdcp(hdcp)) { + hdcp->auth.msg.hdcp2.rx_id_list[0] = 12; + status = read(hdcp, MOD_HDCP_MESSAGE_ID_READ_REPEATER_AUTH_SEND_RECEIVERID_LIST, + hdcp->auth.msg.hdcp2.rx_id_list+1, + sizeof(hdcp->auth.msg.hdcp2.rx_id_list)-1); + + } else { + status = read(hdcp, MOD_HDCP_MESSAGE_ID_READ_REPEATER_AUTH_SEND_RECEIVERID_LIST, + hdcp->auth.msg.hdcp2.rx_id_list, + hdcp->auth.msg.hdcp2.rx_id_list_size); + } + 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] = 17; + 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; +} diff --git a/drivers/gpu/drm/amd/display/modules/hdcp/hdcp_log.c b/drivers/gpu/drm/amd/display/modules/hdcp/hdcp_log.c new file mode 100644 index 000000000000..724ebcee9a19 --- /dev/null +++ b/drivers/gpu/drm/amd/display/modules/hdcp/hdcp_log.c @@ -0,0 +1,281 @@ +/* + * 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 line_count = msg_size / bytes_per_line, + trailing_bytes = msg_size % bytes_per_line; + uint32_t target_size = (byte_size * bytes_per_line + newline_size) * line_count + + byte_size * trailing_bytes + newline_size + terminator_size; + uint32_t buf_pos = 0; + uint32_t i = 0; + + if (buf_size >= target_size) { + for (i = 0; i < msg_size; i++) { + if (i % bytes_per_line == 0) + buf[buf_pos++] = '\n'; + sprintf(&buf[buf_pos], "%02X ", msg[i]); + buf_pos += byte_size; + } + buf[buf_pos++] = '\0'; + } +} + +char *mod_hdcp_status_to_str(int32_t status) +{ + switch (status) { + case MOD_HDCP_STATUS_SUCCESS: + return "MOD_HDCP_STATUS_SUCCESS"; + case MOD_HDCP_STATUS_FAILURE: + return "MOD_HDCP_STATUS_FAILURE"; + case MOD_HDCP_STATUS_RESET_NEEDED: + return "MOD_HDCP_STATUS_RESET_NEEDED"; + case MOD_HDCP_STATUS_DISPLAY_OUT_OF_BOUND: + return "MOD_HDCP_STATUS_DISPLAY_OUT_OF_BOUND"; + case MOD_HDCP_STATUS_DISPLAY_NOT_FOUND: + return "MOD_HDCP_STATUS_DISPLAY_NOT_FOUND"; + case MOD_HDCP_STATUS_INVALID_STATE: + return "MOD_HDCP_STATUS_INVALID_STATE"; + case MOD_HDCP_STATUS_NOT_IMPLEMENTED: + return "MOD_HDCP_STATUS_NOT_IMPLEMENTED"; + case MOD_HDCP_STATUS_INTERNAL_POLICY_FAILURE: + return "MOD_HDCP_STATUS_INTERNAL_POLICY_FAILURE"; + case MOD_HDCP_STATUS_UPDATE_TOPOLOGY_FAILURE: + return "MOD_HDCP_STATUS_UPDATE_TOPOLOGY_FAILURE"; + case MOD_HDCP_STATUS_CREATE_PSP_SERVICE_FAILURE: + return "MOD_HDCP_STATUS_CREATE_PSP_SERVICE_FAILURE"; + case MOD_HDCP_STATUS_DESTROY_PSP_SERVICE_FAILURE: + return "MOD_HDCP_STATUS_DESTROY_PSP_SERVICE_FAILURE"; + case MOD_HDCP_STATUS_HDCP1_CREATE_SESSION_FAILURE: + return "MOD_HDCP_STATUS_HDCP1_CREATE_SESSION_FAILURE"; + case MOD_HDCP_STATUS_HDCP1_DESTROY_SESSION_FAILURE: + return "MOD_HDCP_STATUS_HDCP1_DESTROY_SESSION_FAILURE"; + case MOD_HDCP_STATUS_HDCP1_VALIDATE_ENCRYPTION_FAILURE: + return "MOD_HDCP_STATUS_HDCP1_VALIDATE_ENCRYPTION_FAILURE"; + case MOD_HDCP_STATUS_HDCP1_NOT_HDCP_REPEATER: + return "MOD_HDCP_STATUS_HDCP1_NOT_HDCP_REPEATER"; + case MOD_HDCP_STATUS_HDCP1_NOT_CAPABLE: + return "MOD_HDCP_STATUS_HDCP1_NOT_CAPABLE"; + case MOD_HDCP_STATUS_HDCP1_R0_PRIME_PENDING: + return "MOD_HDCP_STATUS_HDCP1_R0_PRIME_PENDING"; + case MOD_HDCP_STATUS_HDCP1_VALIDATE_RX_FAILURE: + return "MOD_HDCP_STATUS_HDCP1_VALIDATE_RX_FAILURE"; + case MOD_HDCP_STATUS_HDCP1_KSV_LIST_NOT_READY: + return "MOD_HDCP_STATUS_HDCP1_KSV_LIST_NOT_READY"; + case MOD_HDCP_STATUS_HDCP1_VALIDATE_KSV_LIST_FAILURE: + return "MOD_HDCP_STATUS_HDCP1_VALIDATE_KSV_LIST_FAILURE"; + case MOD_HDCP_STATUS_HDCP1_ENABLE_ENCRYPTION: + return "MOD_HDCP_STATUS_HDCP1_ENABLE_ENCRYPTION"; + case MOD_HDCP_STATUS_HDCP1_ENABLE_STREAM_ENCRYPTION_FAILURE: + return "MOD_HDCP_STATUS_HDCP1_ENABLE_STREAM_ENCRYPTION_FAILURE"; + case MOD_HDCP_STATUS_HDCP1_MAX_CASCADE_EXCEEDED_FAILURE: + return "MOD_HDCP_STATUS_HDCP1_MAX_CASCADE_EXCEEDED_FAILURE"; + case MOD_HDCP_STATUS_HDCP1_MAX_DEVS_EXCEEDED_FAILURE: + return "MOD_HDCP_STATUS_HDCP1_MAX_DEVS_EXCEEDED_FAILURE"; + case MOD_HDCP_STATUS_HDCP1_DEVICE_COUNT_MISMATCH_FAILURE: + return "MOD_HDCP_STATUS_HDCP1_DEVICE_COUNT_MISMATCH_FAILURE"; + case MOD_HDCP_STATUS_HDCP1_LINK_INTEGRITY_FAILURE: + return "MOD_HDCP_STATUS_HDCP1_LINK_INTEGRITY_FAILURE"; + case MOD_HDCP_STATUS_HDCP1_REAUTH_REQUEST_ISSUED: + return "MOD_HDCP_STATUS_HDCP1_REAUTH_REQUEST_ISSUED"; + case MOD_HDCP_STATUS_HDCP1_LINK_MAINTENANCE_FAILURE: + return "MOD_HDCP_STATUS_HDCP1_LINK_MAINTENANCE_FAILURE"; + case MOD_HDCP_STATUS_HDCP1_INVALID_BKSV: + return "MOD_HDCP_STATUS_HDCP1_INVALID_BKSV"; + case MOD_HDCP_STATUS_DDC_FAILURE: + return "MOD_HDCP_STATUS_DDC_FAILURE"; + case MOD_HDCP_STATUS_INVALID_OPERATION: + return "MOD_HDCP_STATUS_INVALID_OPERATION"; + case MOD_HDCP_STATUS_HDCP2_NOT_CAPABLE: + return "MOD_HDCP_STATUS_HDCP2_NOT_CAPABLE"; + case MOD_HDCP_STATUS_HDCP2_CREATE_SESSION_FAILURE: + return "MOD_HDCP_STATUS_HDCP2_CREATE_SESSION_FAILURE"; + case MOD_HDCP_STATUS_HDCP2_DESTROY_SESSION_FAILURE: + return "MOD_HDCP_STATUS_HDCP2_DESTROY_SESSION_FAILURE"; + case MOD_HDCP_STATUS_HDCP2_PREP_AKE_INIT_FAILURE: + return "MOD_HDCP_STATUS_HDCP2_PREP_AKE_INIT_FAILURE"; + case MOD_HDCP_STATUS_HDCP2_AKE_CERT_PENDING: + return "MOD_HDCP_STATUS_HDCP2_AKE_CERT_PENDING"; + case MOD_HDCP_STATUS_HDCP2_H_PRIME_PENDING: + return "MOD_HDCP_STATUS_HDCP2_H_PRIME_PENDING"; + case MOD_HDCP_STATUS_HDCP2_PAIRING_INFO_PENDING: + return "MOD_HDCP_STATUS_HDCP2_PAIRING_INFO_PENDING"; + case MOD_HDCP_STATUS_HDCP2_VALIDATE_AKE_CERT_FAILURE: + return "MOD_HDCP_STATUS_HDCP2_VALIDATE_AKE_CERT_FAILURE"; + case MOD_HDCP_STATUS_HDCP2_AKE_CERT_REVOKED: + return "MOD_HDCP_STATUS_HDCP2_AKE_CERT_REVOKED"; + case MOD_HDCP_STATUS_HDCP2_VALIDATE_H_PRIME_FAILURE: + return "MOD_HDCP_STATUS_HDCP2_VALIDATE_H_PRIME_FAILURE"; + case MOD_HDCP_STATUS_HDCP2_VALIDATE_PAIRING_INFO_FAILURE: + return "MOD_HDCP_STATUS_HDCP2_VALIDATE_PAIRING_INFO_FAILURE"; + case MOD_HDCP_STATUS_HDCP2_PREP_LC_INIT_FAILURE: + return "MOD_HDCP_STATUS_HDCP2_PREP_LC_INIT_FAILURE"; + case MOD_HDCP_STATUS_HDCP2_L_PRIME_PENDING: + return "MOD_HDCP_STATUS_HDCP2_L_PRIME_PENDING"; + case MOD_HDCP_STATUS_HDCP2_VALIDATE_L_PRIME_FAILURE: + return "MOD_HDCP_STATUS_HDCP2_VALIDATE_L_PRIME_FAILURE"; + case MOD_HDCP_STATUS_HDCP2_PREP_EKS_FAILURE: + return "MOD_HDCP_STATUS_HDCP2_PREP_EKS_FAILURE"; + case MOD_HDCP_STATUS_HDCP2_ENABLE_ENCRYPTION_FAILURE: + return "MOD_HDCP_STATUS_HDCP2_ENABLE_ENCRYPTION_FAILURE"; + case MOD_HDCP_STATUS_HDCP2_VALIDATE_RX_ID_LIST_FAILURE: + return "MOD_HDCP_STATUS_HDCP2_VALIDATE_RX_ID_LIST_FAILURE"; + case MOD_HDCP_STATUS_HDCP2_RX_ID_LIST_REVOKED: + return "MOD_HDCP_STATUS_HDCP2_RX_ID_LIST_REVOKED"; + case MOD_HDCP_STATUS_HDCP2_RX_ID_LIST_NOT_READY: + return "MOD_HDCP_STATUS_HDCP2_RX_ID_LIST_NOT_READY"; + case MOD_HDCP_STATUS_HDCP2_ENABLE_STREAM_ENCRYPTION: + return "MOD_HDCP_STATUS_HDCP2_ENABLE_STREAM_ENCRYPTION"; + case MOD_HDCP_STATUS_HDCP2_STREAM_READY_PENDING: + return "MOD_HDCP_STATUS_HDCP2_STREAM_READY_PENDING"; + case MOD_HDCP_STATUS_HDCP2_VALIDATE_STREAM_READY_FAILURE: + return "MOD_HDCP_STATUS_HDCP2_VALIDATE_STREAM_READY_FAILURE"; + case MOD_HDCP_STATUS_HDCP2_PREPARE_STREAM_MANAGEMENT_FAILURE: + return "MOD_HDCP_STATUS_HDCP2_PREPARE_STREAM_MANAGEMENT_FAILURE"; + case MOD_HDCP_STATUS_HDCP2_REAUTH_REQUEST: + return "MOD_HDCP_STATUS_HDCP2_REAUTH_REQUEST"; + case MOD_HDCP_STATUS_HDCP2_REAUTH_LINK_INTEGRITY_FAILURE: + return "MOD_HDCP_STATUS_HDCP2_REAUTH_LINK_INTEGRITY_FAILURE"; + case MOD_HDCP_STATUS_HDCP2_DEVICE_COUNT_MISMATCH_FAILURE: + return "MOD_HDCP_STATUS_HDCP2_DEVICE_COUNT_MISMATCH_FAILURE"; + 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"; + }; +} + diff --git a/drivers/gpu/drm/amd/display/modules/hdcp/hdcp_log.h b/drivers/gpu/drm/amd/display/modules/hdcp/hdcp_log.h new file mode 100644 index 000000000000..ff91373ebada --- /dev/null +++ b/drivers/gpu/drm/amd/display/modules/hdcp/hdcp_log.h @@ -0,0 +1,193 @@ +/* + * 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_ + +#ifdef CONFIG_DRM_AMD_DC_HDCP +#define HDCP_LOG_ERR(hdcp, ...) DRM_WARN(__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__) +#endif + +/* default logs */ +#define HDCP_ERROR_TRACE(hdcp, status) \ + HDCP_LOG_ERR(hdcp, \ + "[Link %d] WARNING %s IN STATE %s", \ + hdcp->config.index, \ + mod_hdcp_status_to_str(status), \ + mod_hdcp_state_id_to_str(hdcp->state.id)) +#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) +/* 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) \ + if (event == MOD_HDCP_EVENT_WATCHDOG_TIMEOUT) \ + HDCP_TIMEOUT_TRACE(hdcp); \ + else if (event == MOD_HDCP_EVENT_CPIRQ) \ + HDCP_CPIRQ_TRACE(hdcp) +/* 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_FULL_DDC_TRACE(hdcp) do { \ + 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_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 { \ + 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)); \ + } \ +} 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) + +#endif // MOD_HDCP_LOG_H_ diff --git a/drivers/gpu/drm/amd/display/modules/hdcp/hdcp_psp.c b/drivers/gpu/drm/amd/display/modules/hdcp/hdcp_psp.c new file mode 100644 index 000000000000..7911dc157d5a --- /dev/null +++ b/drivers/gpu/drm/amd/display/modules/hdcp/hdcp_psp.c @@ -0,0 +1,832 @@ +/* + * 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 + * + */ + +#define MAX_NUM_DISPLAYS 24 + + +#include "hdcp.h" + +#include "amdgpu.h" +#include "hdcp_psp.h" + +static void hdcp2_message_init(struct mod_hdcp *hdcp, + struct ta_hdcp_cmd_hdcp2_process_prepare_authentication_message_input_v2 *in) +{ + in->session_handle = hdcp->auth.id; + in->prepare.msg1_id = TA_HDCP_HDCP2_MSG_ID__NULL_MESSAGE; + in->prepare.msg2_id = TA_HDCP_HDCP2_MSG_ID__NULL_MESSAGE; + in->process.msg1_desc.msg_id = TA_HDCP_HDCP2_MSG_ID__NULL_MESSAGE; + in->process.msg1_desc.msg_size = 0; + in->process.msg2_desc.msg_id = TA_HDCP_HDCP2_MSG_ID__NULL_MESSAGE; + in->process.msg2_desc.msg_size = 0; + in->process.msg3_desc.msg_id = TA_HDCP_HDCP2_MSG_ID__NULL_MESSAGE; + in->process.msg3_desc.msg_size = 0; +} +enum mod_hdcp_status mod_hdcp_remove_display_topology(struct mod_hdcp *hdcp) +{ + + struct psp_context *psp = hdcp->config.psp.handle; + struct ta_dtm_shared_memory *dtm_cmd; + struct mod_hdcp_display *display = NULL; + uint8_t i; + + dtm_cmd = (struct ta_dtm_shared_memory *)psp->dtm_context.dtm_shared_buf; + + for (i = 0; i < MAX_NUM_OF_DISPLAYS; i++) { + if (is_display_added(&(hdcp->connection.displays[i]))) { + + memset(dtm_cmd, 0, sizeof(struct ta_dtm_shared_memory)); + + display = &hdcp->connection.displays[i]; + + dtm_cmd->cmd_id = TA_DTM_COMMAND__TOPOLOGY_UPDATE_V2; + dtm_cmd->dtm_in_message.topology_update_v2.display_handle = display->index; + dtm_cmd->dtm_in_message.topology_update_v2.is_active = 0; + dtm_cmd->dtm_status = TA_DTM_STATUS__GENERIC_FAILURE; + + psp_dtm_invoke(psp, dtm_cmd->cmd_id); + + if (dtm_cmd->dtm_status != TA_DTM_STATUS__SUCCESS) + return MOD_HDCP_STATUS_UPDATE_TOPOLOGY_FAILURE; + + display->state = MOD_HDCP_DISPLAY_ACTIVE; + HDCP_TOP_REMOVE_DISPLAY_TRACE(hdcp, display->index); + } + } + + return MOD_HDCP_STATUS_SUCCESS; +} + +enum mod_hdcp_status mod_hdcp_add_display_topology(struct mod_hdcp *hdcp) +{ + struct psp_context *psp = hdcp->config.psp.handle; + struct ta_dtm_shared_memory *dtm_cmd; + struct mod_hdcp_display *display = NULL; + struct mod_hdcp_link *link = &hdcp->connection.link; + uint8_t i; + + if (!psp->dtm_context.dtm_initialized) { + DRM_ERROR("Failed to add display topology, DTM TA is not initialized."); + return MOD_HDCP_STATUS_FAILURE; + } + + dtm_cmd = (struct ta_dtm_shared_memory *)psp->dtm_context.dtm_shared_buf; + + for (i = 0; i < MAX_NUM_OF_DISPLAYS; i++) { + if (hdcp->connection.displays[i].state == MOD_HDCP_DISPLAY_ACTIVE) { + display = &hdcp->connection.displays[i]; + + memset(dtm_cmd, 0, sizeof(struct ta_dtm_shared_memory)); + + dtm_cmd->cmd_id = TA_DTM_COMMAND__TOPOLOGY_UPDATE_V2; + dtm_cmd->dtm_in_message.topology_update_v2.display_handle = display->index; + dtm_cmd->dtm_in_message.topology_update_v2.is_active = 1; + dtm_cmd->dtm_in_message.topology_update_v2.controller = display->controller; + dtm_cmd->dtm_in_message.topology_update_v2.ddc_line = link->ddc_line; + dtm_cmd->dtm_in_message.topology_update_v2.dig_be = link->dig_be; + dtm_cmd->dtm_in_message.topology_update_v2.dig_fe = display->dig_fe; + dtm_cmd->dtm_in_message.topology_update_v2.dp_mst_vcid = display->vc_id; + dtm_cmd->dtm_in_message.topology_update_v2.max_hdcp_supported_version = + TA_DTM_HDCP_VERSION_MAX_SUPPORTED__2_2; + dtm_cmd->dtm_status = TA_DTM_STATUS__GENERIC_FAILURE; + + psp_dtm_invoke(psp, dtm_cmd->cmd_id); + + if (dtm_cmd->dtm_status != TA_DTM_STATUS__SUCCESS) + return MOD_HDCP_STATUS_UPDATE_TOPOLOGY_FAILURE; + + display->state = MOD_HDCP_DISPLAY_ACTIVE_AND_ADDED; + HDCP_TOP_ADD_DISPLAY_TRACE(hdcp, display->index); + } + } + + return MOD_HDCP_STATUS_SUCCESS; +} + +enum mod_hdcp_status mod_hdcp_hdcp1_create_session(struct mod_hdcp *hdcp) +{ + + struct psp_context *psp = hdcp->config.psp.handle; + struct mod_hdcp_display *display = get_first_added_display(hdcp); + struct ta_hdcp_shared_memory *hdcp_cmd; + + if (!psp->hdcp_context.hdcp_initialized) { + DRM_ERROR("Failed to create hdcp session. HDCP TA is not initialized."); + return MOD_HDCP_STATUS_FAILURE; + } + + hdcp_cmd = (struct ta_hdcp_shared_memory *)psp->hdcp_context.hdcp_shared_buf; + memset(hdcp_cmd, 0, sizeof(struct ta_hdcp_shared_memory)); + + hdcp_cmd->in_msg.hdcp1_create_session.display_handle = display->index; + hdcp_cmd->cmd_id = TA_HDCP_COMMAND__HDCP1_CREATE_SESSION; + + psp_hdcp_invoke(psp, hdcp_cmd->cmd_id); + + hdcp->auth.id = hdcp_cmd->out_msg.hdcp1_create_session.session_handle; + + if (hdcp_cmd->hdcp_status != TA_HDCP_STATUS__SUCCESS) + return MOD_HDCP_STATUS_HDCP1_CREATE_SESSION_FAILURE; + + hdcp->auth.msg.hdcp1.ainfo = hdcp_cmd->out_msg.hdcp1_create_session.ainfo_primary; + memcpy(hdcp->auth.msg.hdcp1.aksv, hdcp_cmd->out_msg.hdcp1_create_session.aksv_primary, + sizeof(hdcp->auth.msg.hdcp1.aksv)); + memcpy(hdcp->auth.msg.hdcp1.an, hdcp_cmd->out_msg.hdcp1_create_session.an_primary, + sizeof(hdcp->auth.msg.hdcp1.an)); + + return MOD_HDCP_STATUS_SUCCESS; +} + +enum mod_hdcp_status mod_hdcp_hdcp1_destroy_session(struct mod_hdcp *hdcp) +{ + + struct psp_context *psp = hdcp->config.psp.handle; + struct ta_hdcp_shared_memory *hdcp_cmd; + + hdcp_cmd = (struct ta_hdcp_shared_memory *)psp->hdcp_context.hdcp_shared_buf; + memset(hdcp_cmd, 0, sizeof(struct ta_hdcp_shared_memory)); + + hdcp_cmd->in_msg.hdcp1_destroy_session.session_handle = hdcp->auth.id; + hdcp_cmd->cmd_id = TA_HDCP_COMMAND__HDCP1_DESTROY_SESSION; + + psp_hdcp_invoke(psp, hdcp_cmd->cmd_id); + + if (hdcp_cmd->hdcp_status != TA_HDCP_STATUS__SUCCESS) + return MOD_HDCP_STATUS_HDCP1_DESTROY_SESSION_FAILURE; + + HDCP_TOP_HDCP1_DESTROY_SESSION_TRACE(hdcp); + + return MOD_HDCP_STATUS_SUCCESS; +} + +enum mod_hdcp_status mod_hdcp_hdcp1_validate_rx(struct mod_hdcp *hdcp) +{ + struct psp_context *psp = hdcp->config.psp.handle; + struct ta_hdcp_shared_memory *hdcp_cmd; + + hdcp_cmd = (struct ta_hdcp_shared_memory *)psp->hdcp_context.hdcp_shared_buf; + memset(hdcp_cmd, 0, sizeof(struct ta_hdcp_shared_memory)); + + hdcp_cmd->in_msg.hdcp1_first_part_authentication.session_handle = hdcp->auth.id; + + memcpy(hdcp_cmd->in_msg.hdcp1_first_part_authentication.bksv_primary, hdcp->auth.msg.hdcp1.bksv, + TA_HDCP__HDCP1_KSV_SIZE); + + hdcp_cmd->in_msg.hdcp1_first_part_authentication.r0_prime_primary = hdcp->auth.msg.hdcp1.r0p; + hdcp_cmd->in_msg.hdcp1_first_part_authentication.bcaps = hdcp->auth.msg.hdcp1.bcaps; + hdcp_cmd->cmd_id = TA_HDCP_COMMAND__HDCP1_FIRST_PART_AUTHENTICATION; + + psp_hdcp_invoke(psp, hdcp_cmd->cmd_id); + + if (hdcp_cmd->hdcp_status != TA_HDCP_STATUS__SUCCESS) + return MOD_HDCP_STATUS_HDCP1_VALIDATE_RX_FAILURE; + + if (hdcp_cmd->out_msg.hdcp1_first_part_authentication.authentication_status == + TA_HDCP_AUTHENTICATION_STATUS__HDCP1_FIRST_PART_COMPLETE) { + /* needs second part of authentication */ + hdcp->connection.is_repeater = 1; + } else if (hdcp_cmd->out_msg.hdcp1_first_part_authentication.authentication_status == + TA_HDCP_AUTHENTICATION_STATUS__HDCP1_AUTHENTICATED) { + hdcp->connection.is_repeater = 0; + } else + return MOD_HDCP_STATUS_HDCP1_VALIDATE_RX_FAILURE; + + + return MOD_HDCP_STATUS_SUCCESS; +} + +enum mod_hdcp_status mod_hdcp_hdcp1_enable_encryption(struct mod_hdcp *hdcp) +{ + struct psp_context *psp = hdcp->config.psp.handle; + struct ta_hdcp_shared_memory *hdcp_cmd; + struct mod_hdcp_display *display = get_first_added_display(hdcp); + + hdcp_cmd = (struct ta_hdcp_shared_memory *)psp->hdcp_context.hdcp_shared_buf; + memset(hdcp_cmd, 0, sizeof(struct ta_hdcp_shared_memory)); + + hdcp_cmd->in_msg.hdcp1_enable_encryption.session_handle = hdcp->auth.id; + hdcp_cmd->cmd_id = TA_HDCP_COMMAND__HDCP1_ENABLE_ENCRYPTION; + + psp_hdcp_invoke(psp, hdcp_cmd->cmd_id); + + if (hdcp_cmd->hdcp_status != TA_HDCP_STATUS__SUCCESS) + return MOD_HDCP_STATUS_HDCP1_ENABLE_ENCRYPTION; + + if (!is_dp_mst_hdcp(hdcp)) { + display->state = MOD_HDCP_DISPLAY_ENCRYPTION_ENABLED; + HDCP_HDCP1_ENABLED_TRACE(hdcp, display->index); + } + return MOD_HDCP_STATUS_SUCCESS; +} + +enum mod_hdcp_status mod_hdcp_hdcp1_validate_ksvlist_vp(struct mod_hdcp *hdcp) +{ + struct psp_context *psp = hdcp->config.psp.handle; + struct ta_hdcp_shared_memory *hdcp_cmd; + + hdcp_cmd = (struct ta_hdcp_shared_memory *)psp->hdcp_context.hdcp_shared_buf; + memset(hdcp_cmd, 0, sizeof(struct ta_hdcp_shared_memory)); + + hdcp_cmd->in_msg.hdcp1_second_part_authentication.session_handle = hdcp->auth.id; + + hdcp_cmd->in_msg.hdcp1_second_part_authentication.ksv_list_size = hdcp->auth.msg.hdcp1.ksvlist_size; + memcpy(hdcp_cmd->in_msg.hdcp1_second_part_authentication.ksv_list, hdcp->auth.msg.hdcp1.ksvlist, + hdcp->auth.msg.hdcp1.ksvlist_size); + + memcpy(hdcp_cmd->in_msg.hdcp1_second_part_authentication.v_prime, hdcp->auth.msg.hdcp1.vp, + sizeof(hdcp->auth.msg.hdcp1.vp)); + + hdcp_cmd->in_msg.hdcp1_second_part_authentication.bstatus_binfo = + is_dp_hdcp(hdcp) ? hdcp->auth.msg.hdcp1.binfo_dp : hdcp->auth.msg.hdcp1.bstatus; + hdcp_cmd->cmd_id = TA_HDCP_COMMAND__HDCP1_SECOND_PART_AUTHENTICATION; + + psp_hdcp_invoke(psp, hdcp_cmd->cmd_id); + + if (hdcp_cmd->hdcp_status != TA_HDCP_STATUS__SUCCESS) + return MOD_HDCP_STATUS_HDCP1_VALIDATE_KSV_LIST_FAILURE; + + return MOD_HDCP_STATUS_SUCCESS; +} + +enum mod_hdcp_status mod_hdcp_hdcp1_enable_dp_stream_encryption(struct mod_hdcp *hdcp) +{ + + struct psp_context *psp = hdcp->config.psp.handle; + struct ta_hdcp_shared_memory *hdcp_cmd; + int i = 0; + + hdcp_cmd = (struct ta_hdcp_shared_memory *)psp->hdcp_context.hdcp_shared_buf; + + for (i = 0; i < MAX_NUM_OF_DISPLAYS; i++) { + + if (hdcp->connection.displays[i].state != MOD_HDCP_DISPLAY_ACTIVE_AND_ADDED || + hdcp->connection.displays[i].adjust.disable) + continue; + + memset(hdcp_cmd, 0, sizeof(struct ta_hdcp_shared_memory)); + + hdcp_cmd->in_msg.hdcp1_enable_dp_stream_encryption.session_handle = hdcp->auth.id; + hdcp_cmd->in_msg.hdcp1_enable_dp_stream_encryption.display_handle = hdcp->connection.displays[i].index; + hdcp_cmd->cmd_id = TA_HDCP_COMMAND__HDCP1_ENABLE_DP_STREAM_ENCRYPTION; + + psp_hdcp_invoke(psp, hdcp_cmd->cmd_id); + + if (hdcp_cmd->hdcp_status != TA_HDCP_STATUS__SUCCESS) + return MOD_HDCP_STATUS_HDCP1_ENABLE_STREAM_ENCRYPTION_FAILURE; + + hdcp->connection.displays[i].state = MOD_HDCP_DISPLAY_ENCRYPTION_ENABLED; + HDCP_HDCP1_ENABLED_TRACE(hdcp, hdcp->connection.displays[i].index); + } + + return MOD_HDCP_STATUS_SUCCESS; +} + +enum mod_hdcp_status mod_hdcp_hdcp1_link_maintenance(struct mod_hdcp *hdcp) +{ + struct psp_context *psp = hdcp->config.psp.handle; + struct ta_hdcp_shared_memory *hdcp_cmd; + + hdcp_cmd = (struct ta_hdcp_shared_memory *)psp->hdcp_context.hdcp_shared_buf; + + memset(hdcp_cmd, 0, sizeof(struct ta_hdcp_shared_memory)); + + hdcp_cmd->in_msg.hdcp1_get_encryption_status.session_handle = hdcp->auth.id; + + hdcp_cmd->out_msg.hdcp1_get_encryption_status.protection_level = 0; + hdcp_cmd->cmd_id = TA_HDCP_COMMAND__HDCP1_GET_ENCRYPTION_STATUS; + + psp_hdcp_invoke(psp, hdcp_cmd->cmd_id); + + if (hdcp_cmd->hdcp_status != TA_HDCP_STATUS__SUCCESS) + return MOD_HDCP_STATUS_HDCP1_LINK_MAINTENANCE_FAILURE; + + return (hdcp_cmd->out_msg.hdcp1_get_encryption_status.protection_level == 1) + ? MOD_HDCP_STATUS_SUCCESS + : MOD_HDCP_STATUS_HDCP1_LINK_MAINTENANCE_FAILURE; +} + +enum mod_hdcp_status mod_hdcp_hdcp1_get_link_encryption_status(struct mod_hdcp *hdcp, + enum mod_hdcp_encryption_status *encryption_status) +{ + *encryption_status = MOD_HDCP_ENCRYPTION_STATUS_HDCP_OFF; + + if (mod_hdcp_hdcp1_link_maintenance(hdcp) != MOD_HDCP_STATUS_SUCCESS) + return MOD_HDCP_STATUS_FAILURE; + + *encryption_status = MOD_HDCP_ENCRYPTION_STATUS_HDCP1_ON; + + return MOD_HDCP_STATUS_SUCCESS; +} + +enum mod_hdcp_status mod_hdcp_hdcp2_create_session(struct mod_hdcp *hdcp) +{ + struct psp_context *psp = hdcp->config.psp.handle; + struct ta_hdcp_shared_memory *hdcp_cmd; + struct mod_hdcp_display *display = get_first_added_display(hdcp); + + if (!psp->hdcp_context.hdcp_initialized) { + DRM_ERROR("Failed to create hdcp session, HDCP TA is not initialized"); + return MOD_HDCP_STATUS_FAILURE; + } + + hdcp_cmd = (struct ta_hdcp_shared_memory *)psp->hdcp_context.hdcp_shared_buf; + memset(hdcp_cmd, 0, sizeof(struct ta_hdcp_shared_memory)); + + if (!display) + return MOD_HDCP_STATUS_DISPLAY_NOT_FOUND; + + hdcp_cmd->in_msg.hdcp2_create_session_v2.display_handle = display->index; + + if (hdcp->connection.link.adjust.hdcp2.force_type == MOD_HDCP_FORCE_TYPE_0) + hdcp_cmd->in_msg.hdcp2_create_session_v2.negotiate_content_type = + TA_HDCP2_CONTENT_TYPE_NEGOTIATION_TYPE__FORCE_TYPE0; + else if (hdcp->connection.link.adjust.hdcp2.force_type == MOD_HDCP_FORCE_TYPE_1) + hdcp_cmd->in_msg.hdcp2_create_session_v2.negotiate_content_type = + TA_HDCP2_CONTENT_TYPE_NEGOTIATION_TYPE__FORCE_TYPE1; + else if (hdcp->connection.link.adjust.hdcp2.force_type == MOD_HDCP_FORCE_TYPE_MAX) + hdcp_cmd->in_msg.hdcp2_create_session_v2.negotiate_content_type = + TA_HDCP2_CONTENT_TYPE_NEGOTIATION_TYPE__MAX_SUPPORTED; + + hdcp_cmd->cmd_id = TA_HDCP_COMMAND__HDCP2_CREATE_SESSION_V2; + + psp_hdcp_invoke(psp, hdcp_cmd->cmd_id); + + if (hdcp_cmd->hdcp_status != TA_HDCP_STATUS__SUCCESS) + return MOD_HDCP_STATUS_HDCP2_CREATE_SESSION_FAILURE; + + hdcp->auth.id = hdcp_cmd->out_msg.hdcp2_create_session_v2.session_handle; + + return MOD_HDCP_STATUS_SUCCESS; +} + +enum mod_hdcp_status mod_hdcp_hdcp2_destroy_session(struct mod_hdcp *hdcp) +{ + struct psp_context *psp = hdcp->config.psp.handle; + struct ta_hdcp_shared_memory *hdcp_cmd; + + hdcp_cmd = (struct ta_hdcp_shared_memory *)psp->hdcp_context.hdcp_shared_buf; + memset(hdcp_cmd, 0, sizeof(struct ta_hdcp_shared_memory)); + + hdcp_cmd->in_msg.hdcp2_destroy_session.session_handle = hdcp->auth.id; + hdcp_cmd->cmd_id = TA_HDCP_COMMAND__HDCP2_DESTROY_SESSION; + + psp_hdcp_invoke(psp, hdcp_cmd->cmd_id); + + if (hdcp_cmd->hdcp_status != TA_HDCP_STATUS__SUCCESS) + return MOD_HDCP_STATUS_HDCP2_DESTROY_SESSION_FAILURE; + + HDCP_TOP_HDCP2_DESTROY_SESSION_TRACE(hdcp); + + return MOD_HDCP_STATUS_SUCCESS; +} + +enum mod_hdcp_status mod_hdcp_hdcp2_prepare_ake_init(struct mod_hdcp *hdcp) +{ + struct psp_context *psp = hdcp->config.psp.handle; + struct ta_hdcp_shared_memory *hdcp_cmd; + struct ta_hdcp_cmd_hdcp2_process_prepare_authentication_message_input_v2 *msg_in; + struct ta_hdcp_cmd_hdcp2_process_prepare_authentication_message_output_v2 *msg_out; + + hdcp_cmd = (struct ta_hdcp_shared_memory *)psp->hdcp_context.hdcp_shared_buf; + memset(hdcp_cmd, 0, sizeof(struct ta_hdcp_shared_memory)); + + msg_in = &hdcp_cmd->in_msg.hdcp2_prepare_process_authentication_message_v2; + msg_out = &hdcp_cmd->out_msg.hdcp2_prepare_process_authentication_message_v2; + + hdcp2_message_init(hdcp, msg_in); + + hdcp_cmd->cmd_id = TA_HDCP_COMMAND__HDCP2_PREPARE_PROCESS_AUTHENTICATION_MSG_V2; + msg_in->prepare.msg1_id = TA_HDCP_HDCP2_MSG_ID__AKE_INIT; + + psp_hdcp_invoke(psp, hdcp_cmd->cmd_id); + + if (hdcp_cmd->hdcp_status != TA_HDCP_STATUS__SUCCESS) + return MOD_HDCP_STATUS_HDCP2_PREP_AKE_INIT_FAILURE; + + memcpy(&hdcp->auth.msg.hdcp2.ake_init[0], &msg_out->prepare.transmitter_message[0], + sizeof(hdcp->auth.msg.hdcp2.ake_init)); + + return MOD_HDCP_STATUS_SUCCESS; +} + +enum mod_hdcp_status mod_hdcp_hdcp2_validate_ake_cert(struct mod_hdcp *hdcp) +{ + struct psp_context *psp = hdcp->config.psp.handle; + struct ta_hdcp_shared_memory *hdcp_cmd; + struct ta_hdcp_cmd_hdcp2_process_prepare_authentication_message_input_v2 *msg_in; + struct ta_hdcp_cmd_hdcp2_process_prepare_authentication_message_output_v2 *msg_out; + + hdcp_cmd = (struct ta_hdcp_shared_memory *)psp->hdcp_context.hdcp_shared_buf; + memset(hdcp_cmd, 0, sizeof(struct ta_hdcp_shared_memory)); + + msg_in = &hdcp_cmd->in_msg.hdcp2_prepare_process_authentication_message_v2; + msg_out = &hdcp_cmd->out_msg.hdcp2_prepare_process_authentication_message_v2; + + hdcp2_message_init(hdcp, msg_in); + + msg_in->process.msg1_desc.msg_id = TA_HDCP_HDCP2_MSG_ID__AKE_SEND_CERT; + msg_in->process.msg1_desc.msg_size = TA_HDCP_HDCP2_MSG_ID_MAX_SIZE__AKE_SEND_CERT; + + memcpy(&msg_in->process.receiver_message[0], hdcp->auth.msg.hdcp2.ake_cert, + sizeof(hdcp->auth.msg.hdcp2.ake_cert)); + + msg_in->prepare.msg1_id = TA_HDCP_HDCP2_MSG_ID__AKE_NO_STORED_KM; + msg_in->prepare.msg2_id = TA_HDCP_HDCP2_MSG_ID__AKE_STORED_KM; + + hdcp_cmd->cmd_id = TA_HDCP_COMMAND__HDCP2_PREPARE_PROCESS_AUTHENTICATION_MSG_V2; + + psp_hdcp_invoke(psp, hdcp_cmd->cmd_id); + + if (hdcp_cmd->hdcp_status != TA_HDCP_STATUS__SUCCESS) + return MOD_HDCP_STATUS_HDCP2_VALIDATE_AKE_CERT_FAILURE; + + memcpy(hdcp->auth.msg.hdcp2.ake_no_stored_km, &msg_out->prepare.transmitter_message[0], + sizeof(hdcp->auth.msg.hdcp2.ake_no_stored_km)); + + memcpy(hdcp->auth.msg.hdcp2.ake_stored_km, + &msg_out->prepare.transmitter_message[sizeof(hdcp->auth.msg.hdcp2.ake_no_stored_km)], + sizeof(hdcp->auth.msg.hdcp2.ake_stored_km)); + + if (msg_out->process.msg1_status == TA_HDCP2_MSG_AUTHENTICATION_STATUS__SUCCESS) { + hdcp->connection.is_km_stored = msg_out->process.is_km_stored ? 1 : 0; + hdcp->connection.is_repeater = msg_out->process.is_repeater ? 1 : 0; + return MOD_HDCP_STATUS_SUCCESS; + } + + return MOD_HDCP_STATUS_FAILURE; +} + +enum mod_hdcp_status mod_hdcp_hdcp2_validate_h_prime(struct mod_hdcp *hdcp) +{ + struct psp_context *psp = hdcp->config.psp.handle; + struct ta_hdcp_shared_memory *hdcp_cmd; + struct ta_hdcp_cmd_hdcp2_process_prepare_authentication_message_input_v2 *msg_in; + struct ta_hdcp_cmd_hdcp2_process_prepare_authentication_message_output_v2 *msg_out; + + hdcp_cmd = (struct ta_hdcp_shared_memory *)psp->hdcp_context.hdcp_shared_buf; + memset(hdcp_cmd, 0, sizeof(struct ta_hdcp_shared_memory)); + + msg_in = &hdcp_cmd->in_msg.hdcp2_prepare_process_authentication_message_v2; + msg_out = &hdcp_cmd->out_msg.hdcp2_prepare_process_authentication_message_v2; + + hdcp2_message_init(hdcp, msg_in); + + msg_in->process.msg1_desc.msg_id = TA_HDCP_HDCP2_MSG_ID__AKE_SEND_H_PRIME; + msg_in->process.msg1_desc.msg_size = TA_HDCP_HDCP2_MSG_ID_MAX_SIZE__AKE_SEND_H_PRIME; + + memcpy(&msg_in->process.receiver_message[0], hdcp->auth.msg.hdcp2.ake_h_prime, + sizeof(hdcp->auth.msg.hdcp2.ake_h_prime)); + + if (!hdcp->connection.is_km_stored) { + msg_in->process.msg2_desc.msg_id = TA_HDCP_HDCP2_MSG_ID__AKE_SEND_PAIRING_INFO; + msg_in->process.msg2_desc.msg_size = TA_HDCP_HDCP2_MSG_ID_MAX_SIZE__AKE_SEND_PAIRING_INFO; + memcpy(&msg_in->process.receiver_message[sizeof(hdcp->auth.msg.hdcp2.ake_h_prime)], + hdcp->auth.msg.hdcp2.ake_pairing_info, sizeof(hdcp->auth.msg.hdcp2.ake_pairing_info)); + } + + hdcp_cmd->cmd_id = TA_HDCP_COMMAND__HDCP2_PREPARE_PROCESS_AUTHENTICATION_MSG_V2; + + psp_hdcp_invoke(psp, hdcp_cmd->cmd_id); + + if (hdcp_cmd->hdcp_status != TA_HDCP_STATUS__SUCCESS) + return MOD_HDCP_STATUS_HDCP2_VALIDATE_H_PRIME_FAILURE; + + if (msg_out->process.msg1_status != TA_HDCP2_MSG_AUTHENTICATION_STATUS__SUCCESS) + return MOD_HDCP_STATUS_HDCP2_VALIDATE_H_PRIME_FAILURE; + else if (!hdcp->connection.is_km_stored && + msg_out->process.msg2_status != TA_HDCP2_MSG_AUTHENTICATION_STATUS__SUCCESS) + return MOD_HDCP_STATUS_HDCP2_VALIDATE_PAIRING_INFO_FAILURE; + + + return MOD_HDCP_STATUS_SUCCESS; +} + +enum mod_hdcp_status mod_hdcp_hdcp2_prepare_lc_init(struct mod_hdcp *hdcp) +{ + struct psp_context *psp = hdcp->config.psp.handle; + struct ta_hdcp_shared_memory *hdcp_cmd; + struct ta_hdcp_cmd_hdcp2_process_prepare_authentication_message_input_v2 *msg_in; + struct ta_hdcp_cmd_hdcp2_process_prepare_authentication_message_output_v2 *msg_out; + + hdcp_cmd = (struct ta_hdcp_shared_memory *)psp->hdcp_context.hdcp_shared_buf; + memset(hdcp_cmd, 0, sizeof(struct ta_hdcp_shared_memory)); + + msg_in = &hdcp_cmd->in_msg.hdcp2_prepare_process_authentication_message_v2; + msg_out = &hdcp_cmd->out_msg.hdcp2_prepare_process_authentication_message_v2; + + hdcp2_message_init(hdcp, msg_in); + + msg_in->prepare.msg1_id = TA_HDCP_HDCP2_MSG_ID__LC_INIT; + + hdcp_cmd->cmd_id = TA_HDCP_COMMAND__HDCP2_PREPARE_PROCESS_AUTHENTICATION_MSG_V2; + + psp_hdcp_invoke(psp, hdcp_cmd->cmd_id); + + if (hdcp_cmd->hdcp_status != TA_HDCP_STATUS__SUCCESS) + return MOD_HDCP_STATUS_HDCP2_PREP_LC_INIT_FAILURE; + + memcpy(hdcp->auth.msg.hdcp2.lc_init, &msg_out->prepare.transmitter_message[0], + sizeof(hdcp->auth.msg.hdcp2.lc_init)); + + return MOD_HDCP_STATUS_SUCCESS; +} + +enum mod_hdcp_status mod_hdcp_hdcp2_validate_l_prime(struct mod_hdcp *hdcp) +{ + struct psp_context *psp = hdcp->config.psp.handle; + struct ta_hdcp_shared_memory *hdcp_cmd; + struct ta_hdcp_cmd_hdcp2_process_prepare_authentication_message_input_v2 *msg_in; + struct ta_hdcp_cmd_hdcp2_process_prepare_authentication_message_output_v2 *msg_out; + + hdcp_cmd = (struct ta_hdcp_shared_memory *)psp->hdcp_context.hdcp_shared_buf; + memset(hdcp_cmd, 0, sizeof(struct ta_hdcp_shared_memory)); + + msg_in = &hdcp_cmd->in_msg.hdcp2_prepare_process_authentication_message_v2; + msg_out = &hdcp_cmd->out_msg.hdcp2_prepare_process_authentication_message_v2; + + hdcp2_message_init(hdcp, msg_in); + + msg_in->process.msg1_desc.msg_id = TA_HDCP_HDCP2_MSG_ID__LC_SEND_L_PRIME; + msg_in->process.msg1_desc.msg_size = TA_HDCP_HDCP2_MSG_ID_MAX_SIZE__LC_SEND_L_PRIME; + + memcpy(&msg_in->process.receiver_message[0], hdcp->auth.msg.hdcp2.lc_l_prime, + sizeof(hdcp->auth.msg.hdcp2.lc_l_prime)); + + hdcp_cmd->cmd_id = TA_HDCP_COMMAND__HDCP2_PREPARE_PROCESS_AUTHENTICATION_MSG_V2; + + psp_hdcp_invoke(psp, hdcp_cmd->cmd_id); + + if (hdcp_cmd->hdcp_status != TA_HDCP_STATUS__SUCCESS) + return MOD_HDCP_STATUS_HDCP2_VALIDATE_L_PRIME_FAILURE; + + if (msg_out->process.msg1_status != TA_HDCP2_MSG_AUTHENTICATION_STATUS__SUCCESS) + return MOD_HDCP_STATUS_HDCP2_VALIDATE_L_PRIME_FAILURE; + + return MOD_HDCP_STATUS_SUCCESS; +} + +enum mod_hdcp_status mod_hdcp_hdcp2_prepare_eks(struct mod_hdcp *hdcp) +{ + struct psp_context *psp = hdcp->config.psp.handle; + struct ta_hdcp_shared_memory *hdcp_cmd; + struct ta_hdcp_cmd_hdcp2_process_prepare_authentication_message_input_v2 *msg_in; + struct ta_hdcp_cmd_hdcp2_process_prepare_authentication_message_output_v2 *msg_out; + + hdcp_cmd = (struct ta_hdcp_shared_memory *)psp->hdcp_context.hdcp_shared_buf; + memset(hdcp_cmd, 0, sizeof(struct ta_hdcp_shared_memory)); + + msg_in = &hdcp_cmd->in_msg.hdcp2_prepare_process_authentication_message_v2; + msg_out = &hdcp_cmd->out_msg.hdcp2_prepare_process_authentication_message_v2; + + hdcp2_message_init(hdcp, msg_in); + + msg_in->prepare.msg1_id = TA_HDCP_HDCP2_MSG_ID__SKE_SEND_EKS; + + if (is_dp_hdcp(hdcp)) + msg_in->prepare.msg2_id = TA_HDCP_HDCP2_MSG_ID__SIGNAL_CONTENT_STREAM_TYPE_DP; + + hdcp_cmd->cmd_id = TA_HDCP_COMMAND__HDCP2_PREPARE_PROCESS_AUTHENTICATION_MSG_V2; + psp_hdcp_invoke(psp, hdcp_cmd->cmd_id); + + if (hdcp_cmd->hdcp_status != TA_HDCP_STATUS__SUCCESS) + return MOD_HDCP_STATUS_HDCP2_PREP_EKS_FAILURE; + + memcpy(hdcp->auth.msg.hdcp2.ske_eks, &msg_out->prepare.transmitter_message[0], + sizeof(hdcp->auth.msg.hdcp2.ske_eks)); + msg_out->prepare.msg1_desc.msg_size = sizeof(hdcp->auth.msg.hdcp2.ske_eks); + + if (is_dp_hdcp(hdcp)) { + memcpy(hdcp->auth.msg.hdcp2.content_stream_type_dp, + &msg_out->prepare.transmitter_message[sizeof(hdcp->auth.msg.hdcp2.ske_eks)], + sizeof(hdcp->auth.msg.hdcp2.content_stream_type_dp)); + } + + return MOD_HDCP_STATUS_SUCCESS; +} + +enum mod_hdcp_status mod_hdcp_hdcp2_enable_encryption(struct mod_hdcp *hdcp) +{ + struct psp_context *psp = hdcp->config.psp.handle; + struct ta_hdcp_shared_memory *hdcp_cmd; + struct ta_hdcp_cmd_hdcp2_process_prepare_authentication_message_input_v2 *msg_in; + struct mod_hdcp_display *display = get_first_added_display(hdcp); + + hdcp_cmd = (struct ta_hdcp_shared_memory *)psp->hdcp_context.hdcp_shared_buf; + memset(hdcp_cmd, 0, sizeof(struct ta_hdcp_shared_memory)); + + msg_in = &hdcp_cmd->in_msg.hdcp2_prepare_process_authentication_message_v2; + + hdcp2_message_init(hdcp, msg_in); + + if (!display) + return MOD_HDCP_STATUS_DISPLAY_NOT_FOUND; + + hdcp_cmd->in_msg.hdcp1_enable_encryption.session_handle = hdcp->auth.id; + + hdcp_cmd->cmd_id = TA_HDCP_COMMAND__HDCP2_SET_ENCRYPTION; + psp_hdcp_invoke(psp, hdcp_cmd->cmd_id); + + if (hdcp_cmd->hdcp_status != TA_HDCP_STATUS__SUCCESS) + return MOD_HDCP_STATUS_HDCP2_ENABLE_ENCRYPTION_FAILURE; + + if (!is_dp_mst_hdcp(hdcp)) { + display->state = MOD_HDCP_DISPLAY_ENCRYPTION_ENABLED; + HDCP_HDCP2_ENABLED_TRACE(hdcp, display->index); + } + + return MOD_HDCP_STATUS_SUCCESS; +} + +enum mod_hdcp_status mod_hdcp_hdcp2_validate_rx_id_list(struct mod_hdcp *hdcp) +{ + struct psp_context *psp = hdcp->config.psp.handle; + struct ta_hdcp_shared_memory *hdcp_cmd; + struct ta_hdcp_cmd_hdcp2_process_prepare_authentication_message_input_v2 *msg_in; + struct ta_hdcp_cmd_hdcp2_process_prepare_authentication_message_output_v2 *msg_out; + + hdcp_cmd = (struct ta_hdcp_shared_memory *)psp->hdcp_context.hdcp_shared_buf; + memset(hdcp_cmd, 0, sizeof(struct ta_hdcp_shared_memory)); + + msg_in = &hdcp_cmd->in_msg.hdcp2_prepare_process_authentication_message_v2; + msg_out = &hdcp_cmd->out_msg.hdcp2_prepare_process_authentication_message_v2; + + hdcp2_message_init(hdcp, msg_in); + + msg_in->process.msg1_desc.msg_id = TA_HDCP_HDCP2_MSG_ID__REPEATERAUTH_SEND_RECEIVERID_LIST; + msg_in->process.msg1_desc.msg_size = sizeof(hdcp->auth.msg.hdcp2.rx_id_list); + memcpy(&msg_in->process.receiver_message[0], hdcp->auth.msg.hdcp2.rx_id_list, + sizeof(hdcp->auth.msg.hdcp2.rx_id_list)); + + msg_in->prepare.msg1_id = TA_HDCP_HDCP2_MSG_ID__REPEATERAUTH_SEND_ACK; + + hdcp_cmd->cmd_id = TA_HDCP_COMMAND__HDCP2_PREPARE_PROCESS_AUTHENTICATION_MSG_V2; + + psp_hdcp_invoke(psp, hdcp_cmd->cmd_id); + + if (hdcp_cmd->hdcp_status != TA_HDCP_STATUS__SUCCESS) + return MOD_HDCP_STATUS_HDCP2_VALIDATE_RX_ID_LIST_FAILURE; + + memcpy(hdcp->auth.msg.hdcp2.repeater_auth_ack, &msg_out->prepare.transmitter_message[0], + sizeof(hdcp->auth.msg.hdcp2.repeater_auth_ack)); + + if (msg_out->process.msg1_status == TA_HDCP2_MSG_AUTHENTICATION_STATUS__SUCCESS) { + hdcp->connection.is_km_stored = msg_out->process.is_km_stored ? 1 : 0; + hdcp->connection.is_repeater = msg_out->process.is_repeater ? 1 : 0; + return MOD_HDCP_STATUS_SUCCESS; + } + + + return MOD_HDCP_STATUS_HDCP2_VALIDATE_RX_ID_LIST_FAILURE; +} + +enum mod_hdcp_status mod_hdcp_hdcp2_enable_dp_stream_encryption(struct mod_hdcp *hdcp) +{ + struct psp_context *psp = hdcp->config.psp.handle; + struct ta_hdcp_shared_memory *hdcp_cmd; + struct ta_hdcp_cmd_hdcp2_process_prepare_authentication_message_input_v2 *msg_in; + uint8_t i; + + hdcp_cmd = (struct ta_hdcp_shared_memory *)psp->hdcp_context.hdcp_shared_buf; + memset(hdcp_cmd, 0, sizeof(struct ta_hdcp_shared_memory)); + + msg_in = &hdcp_cmd->in_msg.hdcp2_prepare_process_authentication_message_v2; + + hdcp2_message_init(hdcp, msg_in); + + + for (i = 0; i < MAX_NUM_OF_DISPLAYS; i++) { + if (hdcp->connection.displays[i].state != MOD_HDCP_DISPLAY_ACTIVE_AND_ADDED || + hdcp->connection.displays[i].adjust.disable) + continue; + hdcp_cmd->in_msg.hdcp2_enable_dp_stream_encryption.display_handle = hdcp->connection.displays[i].index; + hdcp_cmd->in_msg.hdcp2_enable_dp_stream_encryption.session_handle = hdcp->auth.id; + + hdcp_cmd->cmd_id = TA_HDCP_COMMAND__HDCP2_ENABLE_DP_STREAM_ENCRYPTION; + psp_hdcp_invoke(psp, hdcp_cmd->cmd_id); + + if (hdcp_cmd->hdcp_status != TA_HDCP_STATUS__SUCCESS) + break; + + hdcp->connection.displays[i].state = MOD_HDCP_DISPLAY_ENCRYPTION_ENABLED; + HDCP_HDCP2_ENABLED_TRACE(hdcp, hdcp->connection.displays[i].index); + } + + return (hdcp_cmd->hdcp_status == TA_HDCP_STATUS__SUCCESS) ? MOD_HDCP_STATUS_SUCCESS + : MOD_HDCP_STATUS_HDCP2_ENABLE_STREAM_ENCRYPTION; +} + +enum mod_hdcp_status mod_hdcp_hdcp2_prepare_stream_management(struct mod_hdcp *hdcp) +{ + + struct psp_context *psp = hdcp->config.psp.handle; + struct ta_hdcp_shared_memory *hdcp_cmd; + struct ta_hdcp_cmd_hdcp2_process_prepare_authentication_message_input_v2 *msg_in; + struct ta_hdcp_cmd_hdcp2_process_prepare_authentication_message_output_v2 *msg_out; + + hdcp_cmd = (struct ta_hdcp_shared_memory *)psp->hdcp_context.hdcp_shared_buf; + memset(hdcp_cmd, 0, sizeof(struct ta_hdcp_shared_memory)); + + msg_in = &hdcp_cmd->in_msg.hdcp2_prepare_process_authentication_message_v2; + msg_out = &hdcp_cmd->out_msg.hdcp2_prepare_process_authentication_message_v2; + + hdcp2_message_init(hdcp, msg_in); + + msg_in->prepare.msg1_id = TA_HDCP_HDCP2_MSG_ID__REPEATERAUTH_STREAM_MANAGE; + + + hdcp_cmd->cmd_id = TA_HDCP_COMMAND__HDCP2_PREPARE_PROCESS_AUTHENTICATION_MSG_V2; + psp_hdcp_invoke(psp, hdcp_cmd->cmd_id); + + if (hdcp_cmd->hdcp_status != TA_HDCP_STATUS__SUCCESS) + return MOD_HDCP_STATUS_HDCP2_PREPARE_STREAM_MANAGEMENT_FAILURE; + + hdcp->auth.msg.hdcp2.stream_manage_size = msg_out->prepare.msg1_desc.msg_size; + + memcpy(hdcp->auth.msg.hdcp2.repeater_auth_stream_manage, &msg_out->prepare.transmitter_message[0], + sizeof(hdcp->auth.msg.hdcp2.repeater_auth_stream_manage)); + + return MOD_HDCP_STATUS_SUCCESS; +} + +enum mod_hdcp_status mod_hdcp_hdcp2_validate_stream_ready(struct mod_hdcp *hdcp) +{ + struct psp_context *psp = hdcp->config.psp.handle; + struct ta_hdcp_shared_memory *hdcp_cmd; + struct ta_hdcp_cmd_hdcp2_process_prepare_authentication_message_input_v2 *msg_in; + struct ta_hdcp_cmd_hdcp2_process_prepare_authentication_message_output_v2 *msg_out; + + hdcp_cmd = (struct ta_hdcp_shared_memory *)psp->hdcp_context.hdcp_shared_buf; + memset(hdcp_cmd, 0, sizeof(struct ta_hdcp_shared_memory)); + + msg_in = &hdcp_cmd->in_msg.hdcp2_prepare_process_authentication_message_v2; + msg_out = &hdcp_cmd->out_msg.hdcp2_prepare_process_authentication_message_v2; + + hdcp2_message_init(hdcp, msg_in); + + msg_in->process.msg1_desc.msg_id = TA_HDCP_HDCP2_MSG_ID__REPEATERAUTH_STREAM_READY; + + msg_in->process.msg1_desc.msg_size = sizeof(hdcp->auth.msg.hdcp2.repeater_auth_stream_ready); + + memcpy(&msg_in->process.receiver_message[0], hdcp->auth.msg.hdcp2.repeater_auth_stream_ready, + sizeof(hdcp->auth.msg.hdcp2.repeater_auth_stream_ready)); + + hdcp_cmd->cmd_id = TA_HDCP_COMMAND__HDCP2_PREPARE_PROCESS_AUTHENTICATION_MSG_V2; + psp_hdcp_invoke(psp, hdcp_cmd->cmd_id); + + return (hdcp_cmd->hdcp_status == TA_HDCP_STATUS__SUCCESS) && + (msg_out->process.msg1_status == TA_HDCP2_MSG_AUTHENTICATION_STATUS__SUCCESS) + ? MOD_HDCP_STATUS_SUCCESS + : MOD_HDCP_STATUS_HDCP2_VALIDATE_STREAM_READY_FAILURE; +} + +enum mod_hdcp_status mod_hdcp_hdcp2_get_link_encryption_status(struct mod_hdcp *hdcp, + enum mod_hdcp_encryption_status *encryption_status) +{ + struct psp_context *psp = hdcp->config.psp.handle; + struct ta_hdcp_shared_memory *hdcp_cmd; + + hdcp_cmd = (struct ta_hdcp_shared_memory *)psp->hdcp_context.hdcp_shared_buf; + + memset(hdcp_cmd, 0, sizeof(struct ta_hdcp_shared_memory)); + + hdcp_cmd->in_msg.hdcp2_get_encryption_status.session_handle = hdcp->auth.id; + hdcp_cmd->out_msg.hdcp2_get_encryption_status.protection_level = 0; + hdcp_cmd->cmd_id = TA_HDCP_COMMAND__HDCP2_GET_ENCRYPTION_STATUS; + *encryption_status = MOD_HDCP_ENCRYPTION_STATUS_HDCP_OFF; + + psp_hdcp_invoke(psp, hdcp_cmd->cmd_id); + + if (hdcp_cmd->hdcp_status != TA_HDCP_STATUS__SUCCESS) + return MOD_HDCP_STATUS_FAILURE; + + if (hdcp_cmd->out_msg.hdcp2_get_encryption_status.protection_level == 1) { + if (hdcp_cmd->out_msg.hdcp2_get_encryption_status.hdcp2_type == TA_HDCP2_CONTENT_TYPE__TYPE1) + *encryption_status = MOD_HDCP_ENCRYPTION_STATUS_HDCP2_TYPE1_ON; + else + *encryption_status = MOD_HDCP_ENCRYPTION_STATUS_HDCP2_TYPE0_ON; + } + + return MOD_HDCP_STATUS_SUCCESS; +} diff --git a/drivers/gpu/drm/amd/display/modules/hdcp/hdcp_psp.h b/drivers/gpu/drm/amd/display/modules/hdcp/hdcp_psp.h new file mode 100644 index 000000000000..82a5e997d573 --- /dev/null +++ b/drivers/gpu/drm/amd/display/modules/hdcp/hdcp_psp.h @@ -0,0 +1,466 @@ +/* + * 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 +}; + +/* 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 max_hdcp_supported_version; +}; + +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; +}; + +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 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 +}; + +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 +}; + +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 +}; + +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 +}; + +/* 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; +}; + +/**********************************************************/ +/* 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; +}; + +/* 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_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_ */ diff --git a/drivers/gpu/drm/amd/display/modules/inc/mod_freesync.h b/drivers/gpu/drm/amd/display/modules/inc/mod_freesync.h index dcef85994c45..dbe7835aabcf 100644 --- a/drivers/gpu/drm/amd/display/modules/inc/mod_freesync.h +++ b/drivers/gpu/drm/amd/display/modules/inc/mod_freesync.h @@ -92,6 +92,7 @@ struct mod_vrr_params_btr { 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 { @@ -173,4 +174,6 @@ bool mod_freesync_is_valid_range(struct mod_freesync *mod_freesync, uint32_t min_refresh_request_in_uhz, uint32_t max_refresh_request_in_uhz); + + #endif diff --git a/drivers/gpu/drm/amd/display/modules/inc/mod_hdcp.h b/drivers/gpu/drm/amd/display/modules/inc/mod_hdcp.h new file mode 100644 index 000000000000..f2a0e1a064da --- /dev/null +++ b/drivers/gpu/drm/amd/display/modules/inc/mod_hdcp.h @@ -0,0 +1,298 @@ +/* + * 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_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 + +/* detailed return status */ +enum mod_hdcp_status { + MOD_HDCP_STATUS_SUCCESS = 0, + MOD_HDCP_STATUS_FAILURE, + MOD_HDCP_STATUS_RESET_NEEDED, + MOD_HDCP_STATUS_DISPLAY_OUT_OF_BOUND, + MOD_HDCP_STATUS_DISPLAY_NOT_FOUND, + MOD_HDCP_STATUS_INVALID_STATE, + MOD_HDCP_STATUS_NOT_IMPLEMENTED, + MOD_HDCP_STATUS_INTERNAL_POLICY_FAILURE, + MOD_HDCP_STATUS_UPDATE_TOPOLOGY_FAILURE, + MOD_HDCP_STATUS_CREATE_PSP_SERVICE_FAILURE, + MOD_HDCP_STATUS_DESTROY_PSP_SERVICE_FAILURE, + MOD_HDCP_STATUS_HDCP1_CREATE_SESSION_FAILURE, + MOD_HDCP_STATUS_HDCP1_DESTROY_SESSION_FAILURE, + MOD_HDCP_STATUS_HDCP1_VALIDATE_ENCRYPTION_FAILURE, + MOD_HDCP_STATUS_HDCP1_NOT_HDCP_REPEATER, + MOD_HDCP_STATUS_HDCP1_NOT_CAPABLE, + MOD_HDCP_STATUS_HDCP1_R0_PRIME_PENDING, + MOD_HDCP_STATUS_HDCP1_VALIDATE_RX_FAILURE, + MOD_HDCP_STATUS_HDCP1_KSV_LIST_NOT_READY, + MOD_HDCP_STATUS_HDCP1_VALIDATE_KSV_LIST_FAILURE, + MOD_HDCP_STATUS_HDCP1_ENABLE_ENCRYPTION, + MOD_HDCP_STATUS_HDCP1_ENABLE_STREAM_ENCRYPTION_FAILURE, + MOD_HDCP_STATUS_HDCP1_MAX_CASCADE_EXCEEDED_FAILURE, + MOD_HDCP_STATUS_HDCP1_MAX_DEVS_EXCEEDED_FAILURE, + MOD_HDCP_STATUS_HDCP1_DEVICE_COUNT_MISMATCH_FAILURE, + MOD_HDCP_STATUS_HDCP1_LINK_INTEGRITY_FAILURE, + MOD_HDCP_STATUS_HDCP1_REAUTH_REQUEST_ISSUED, + MOD_HDCP_STATUS_HDCP1_LINK_MAINTENANCE_FAILURE, + MOD_HDCP_STATUS_HDCP1_INVALID_BKSV, + MOD_HDCP_STATUS_DDC_FAILURE, /* TODO: specific errors */ + MOD_HDCP_STATUS_INVALID_OPERATION, + MOD_HDCP_STATUS_HDCP2_NOT_CAPABLE, + MOD_HDCP_STATUS_HDCP2_CREATE_SESSION_FAILURE, + MOD_HDCP_STATUS_HDCP2_DESTROY_SESSION_FAILURE, + MOD_HDCP_STATUS_HDCP2_PREP_AKE_INIT_FAILURE, + MOD_HDCP_STATUS_HDCP2_AKE_CERT_PENDING, + MOD_HDCP_STATUS_HDCP2_H_PRIME_PENDING, + MOD_HDCP_STATUS_HDCP2_PAIRING_INFO_PENDING, + MOD_HDCP_STATUS_HDCP2_VALIDATE_AKE_CERT_FAILURE, + MOD_HDCP_STATUS_HDCP2_AKE_CERT_REVOKED, + MOD_HDCP_STATUS_HDCP2_VALIDATE_H_PRIME_FAILURE, + MOD_HDCP_STATUS_HDCP2_VALIDATE_PAIRING_INFO_FAILURE, + MOD_HDCP_STATUS_HDCP2_PREP_LC_INIT_FAILURE, + MOD_HDCP_STATUS_HDCP2_L_PRIME_PENDING, + MOD_HDCP_STATUS_HDCP2_VALIDATE_L_PRIME_FAILURE, + MOD_HDCP_STATUS_HDCP2_PREP_EKS_FAILURE, + MOD_HDCP_STATUS_HDCP2_ENABLE_ENCRYPTION_FAILURE, + MOD_HDCP_STATUS_HDCP2_RX_ID_LIST_NOT_READY, + MOD_HDCP_STATUS_HDCP2_VALIDATE_RX_ID_LIST_FAILURE, + MOD_HDCP_STATUS_HDCP2_RX_ID_LIST_REVOKED, + MOD_HDCP_STATUS_HDCP2_ENABLE_STREAM_ENCRYPTION, + MOD_HDCP_STATUS_HDCP2_STREAM_READY_PENDING, + MOD_HDCP_STATUS_HDCP2_VALIDATE_STREAM_READY_FAILURE, + MOD_HDCP_STATUS_HDCP2_PREPARE_STREAM_MANAGEMENT_FAILURE, + MOD_HDCP_STATUS_HDCP2_REAUTH_REQUEST, + MOD_HDCP_STATUS_HDCP2_REAUTH_LINK_INTEGRITY_FAILURE, + MOD_HDCP_STATUS_HDCP2_DEVICE_COUNT_MISMATCH_FAILURE, +}; + +struct mod_hdcp_displayport { + uint8_t rev; + uint8_t assr_supported; +}; + +struct mod_hdcp_hdmi { + uint8_t reserved; +}; +enum mod_hdcp_operation_mode { + MOD_HDCP_MODE_OFF, + MOD_HDCP_MODE_DEFAULT, + MOD_HDCP_MODE_DP, + MOD_HDCP_MODE_DP_MST +}; + +enum mod_hdcp_display_state { + MOD_HDCP_DISPLAY_INACTIVE = 0, + MOD_HDCP_DISPLAY_ACTIVE, + MOD_HDCP_DISPLAY_ACTIVE_AND_ADDED, + MOD_HDCP_DISPLAY_ENCRYPTION_ENABLED +}; + +struct mod_hdcp_ddc { + void *handle; + struct { + 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); + } funcs; +}; + +struct mod_hdcp_psp { + void *handle; + void *funcs; +}; + +struct mod_hdcp_display_adjustment { + uint8_t disable : 1; + uint8_t reserved : 7; +}; + +struct mod_hdcp_link_adjustment_hdcp1 { + uint8_t disable : 1; + uint8_t postpone_encryption : 1; + uint8_t reserved : 6; +}; + +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 reserved : 3; +}; + +struct mod_hdcp_link_adjustment { + uint8_t auth_delay; + 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_hdcp_trace { + struct mod_hdcp_error errors[MAX_NUM_OF_ERROR_TRACE]; + uint8_t error_count; +}; + +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; +}; + +/* 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; + 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; + 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; +}; + +struct mod_hdcp; + +/* 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 on cp_desired set to true */ +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 on cp_desired set to false */ +enum mod_hdcp_status mod_hdcp_remove_display(struct mod_hdcp *hdcp, + uint8_t index, 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_ */ diff --git a/drivers/gpu/drm/amd/display/modules/inc/mod_info_packet.h b/drivers/gpu/drm/amd/display/modules/inc/mod_info_packet.h index 5b1c9a4c7643..42cbeffac640 100644 --- a/drivers/gpu/drm/amd/display/modules/inc/mod_info_packet.h +++ b/drivers/gpu/drm/amd/display/modules/inc/mod_info_packet.h @@ -26,13 +26,18 @@ #ifndef MOD_INFO_PACKET_H_ #define MOD_INFO_PACKET_H_ +#include "dm_services.h" #include "mod_shared.h" - //Forward Declarations struct dc_stream_state; struct dc_info_packet; +struct mod_vrr_params; void mod_build_vsc_infopacket(const struct dc_stream_state *stream, - struct dc_info_packet *info_packet); + struct dc_info_packet *info_packet, + bool *use_vsc_sdp_for_colorimetry); + +void mod_build_hf_vsif_infopacket(const struct dc_stream_state *stream, + struct dc_info_packet *info_packet, int ALLMEnabled, int ALLMValue); #endif diff --git a/drivers/gpu/drm/amd/display/modules/inc/mod_shared.h b/drivers/gpu/drm/amd/display/modules/inc/mod_shared.h index b45f7d65e76a..fe2117904329 100644 --- a/drivers/gpu/drm/amd/display/modules/inc/mod_shared.h +++ b/drivers/gpu/drm/amd/display/modules/inc/mod_shared.h @@ -45,7 +45,6 @@ enum vrr_packet_type { PACKET_TYPE_VTEM }; -#if defined(CONFIG_DRM_AMD_DC_DCN2_0) union lut3d_control_flags { unsigned int raw; struct { @@ -104,6 +103,5 @@ struct lut3d_settings { enum lut3d_control_gamut_map map2; enum lut3d_control_rotation_mode rotation2; }; -#endif #endif /* MOD_SHARED_H_ */ diff --git a/drivers/gpu/drm/amd/display/modules/info_packet/info_packet.c b/drivers/gpu/drm/amd/display/modules/info_packet/info_packet.c index bc13c552797f..6a8a056424b8 100644 --- a/drivers/gpu/drm/amd/display/modules/info_packet/info_packet.c +++ b/drivers/gpu/drm/amd/display/modules/info_packet/info_packet.c @@ -27,8 +27,92 @@ #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, +}; #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__RESERVED2 0x0C +#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__RESERVED3 0xF8 + +//MD3 +#define MASK_VTEM_MD3__BASE_REFRESH_RATE_07 0xFF enum ColorimetryRGBDP { ColorimetryRGB_DP_sRGB = 0, @@ -46,35 +130,41 @@ enum ColorimetryYCCDP { }; void mod_build_vsc_infopacket(const struct dc_stream_state *stream, - struct dc_info_packet *info_packet) + struct dc_info_packet *info_packet, + bool *use_vsc_sdp_for_colorimetry) { - unsigned int vscPacketRevision = 0; + unsigned int vsc_packet_revision = vsc_packet_undefined; unsigned int i; unsigned int pixelEncoding = 0; unsigned int colorimetryFormat = 0; bool stereo3dSupport = false; + /* Initialize first, later if infopacket is valid determine if VSC SDP + * should be used to signal colorimetry format and pixel encoding. + */ + *use_vsc_sdp_for_colorimetry = false; + if (stream->timing.timing_3d_format != TIMING_3D_FORMAT_NONE && stream->view_format != VIEW_3D_FORMAT_NONE) { - vscPacketRevision = 1; + vsc_packet_revision = vsc_packet_rev1; stereo3dSupport = true; } /*VSC packet set to 2 when DP revision >= 1.2*/ if (stream->psr_version != 0) - vscPacketRevision = 2; + vsc_packet_revision = vsc_packet_rev2; /* Update to revision 5 for extended colorimetry support for DPCD 1.4+ */ if (stream->link->dpcd_caps.dpcd_rev.raw >= 0x14 && stream->link->dpcd_caps.dprx_feature.bits.VSC_SDP_COLORIMETRY_SUPPORTED) - vscPacketRevision = 5; + vsc_packet_revision = vsc_packet_rev5; /* VSC packet not needed based on the features * supported by this DP display */ - if (vscPacketRevision == 0) + if (vsc_packet_revision == vsc_packet_undefined) return; - if (vscPacketRevision == 0x2) { + if (vsc_packet_revision == vsc_packet_rev2) { /* Secondary-data Packet ID = 0*/ info_packet->hb0 = 0x00; /* 07h - Packet Type Value indicating Video @@ -96,7 +186,7 @@ void mod_build_vsc_infopacket(const struct dc_stream_state *stream, info_packet->valid = true; } - if (vscPacketRevision == 0x1) { + 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 @@ -167,7 +257,7 @@ void mod_build_vsc_infopacket(const struct dc_stream_state *stream, * 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 (vscPacketRevision == 0x5) { + 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 */ @@ -179,6 +269,13 @@ void mod_build_vsc_infopacket(const struct dc_stream_state *stream, info_packet->valid = true; + /* If we are using VSC SDP revision 05h, use this to signal for + * colorimetry format and pixel encoding. HW should later be + * programmed to set MSA MISC1 bit 6 to indicate ignore + * colorimetry format and pixel encoding in the MSA. + */ + *use_vsc_sdp_for_colorimetry = true; + /* 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) @@ -323,6 +420,102 @@ void mod_build_vsc_infopacket(const struct dc_stream_state *stream, */ info_packet->sb[18] = 0; } +} + +/** + ***************************************************************************** + * Function: mod_build_hf_vsif_infopacket + * + * @brief + * Prepare HDMI Vendor Specific info frame. + * Follows HDMI Spec to build up Vendor Specific info frame + * + * @param [in] stream: contains data we may need to construct VSIF (i.e. timing_3d_format, etc.) + * @param [out] info_packet: output structure where to store 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; + + 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; + + info_packet->sb[1] = 0x03; + info_packet->sb[2] = 0x0C; + info_packet->sb[3] = 0x00; + if (bALLM) { + 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[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) + info_packet->sb[5] = stream->timing.hdmi_vic; + + info_packet->hb0 = HDMI_INFOFRAME_TYPE_VENDOR; + info_packet->hb1 = 0x01; + info_packet->hb2 = (uint8_t) (length); + + if (bALLM) + info_packet->sb[5] = (info_packet->sb[5] & ~0x02) | (bALLMVal << 1); + + 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; } diff --git a/drivers/gpu/drm/amd/display/modules/power/power_helpers.c b/drivers/gpu/drm/amd/display/modules/power/power_helpers.c index b3810b864676..e75a4bb94488 100644 --- a/drivers/gpu/drm/amd/display/modules/power/power_helpers.c +++ b/drivers/gpu/drm/amd/display/modules/power/power_helpers.c @@ -66,6 +66,39 @@ static const unsigned char abm_config[abm_defines_max_config][abm_defines_max_le { 3, 6, 10, 12 }, /* Alt #3 - Super aggressiveness */ }; +struct abm_parameters { + unsigned char min_reduction; + unsigned char max_reduction; + unsigned char bright_pos_gain; + unsigned char dark_pos_gain; + unsigned char brightness_gain; + unsigned char contrast_factor; + unsigned char deviation_gain; + unsigned char min_knee; + unsigned char max_knee; +}; + +static const struct abm_parameters abm_settings_config0[abm_defines_max_level] = { +// min_red max_red bright_pos dark_pos brightness_gain contrast deviation min_knee max_knee + {0xff, 0xbf, 0x20, 0x00, 0xff, 0x99, 0xb3, 0x40, 0xe0}, + {0xde, 0x85, 0x20, 0x00, 0xff, 0x90, 0xa8, 0x40, 0xdf}, + {0xb0, 0x50, 0x20, 0x00, 0xc0, 0x88, 0x78, 0x70, 0xa0}, + {0x82, 0x40, 0x20, 0x00, 0x00, 0xff, 0xb3, 0x70, 0x70}, +}; + +static const struct abm_parameters abm_settings_config1[abm_defines_max_level] = { +// min_red max_red bright_pos dark_pos brightness_gain contrast deviation min_knee max_knee + {0xf0, 0xd9, 0x20, 0x00, 0x00, 0xff, 0xb3, 0x70, 0x70}, + {0xcd, 0xa5, 0x20, 0x00, 0x00, 0xff, 0xb3, 0x70, 0x70}, + {0x99, 0x65, 0x20, 0x00, 0x00, 0xff, 0xb3, 0x70, 0x70}, + {0x82, 0x4d, 0x20, 0x00, 0x00, 0xff, 0xb3, 0x70, 0x70}, +}; + +static const struct abm_parameters * const abm_settings[] = { + abm_settings_config0, + abm_settings_config1, +}; + #define NUM_AMBI_LEVEL 5 #define NUM_AGGR_LEVEL 4 #define NUM_POWER_FN_SEGS 8 @@ -82,7 +115,7 @@ static const unsigned char abm_config[abm_defines_max_config][abm_defines_max_le /* NOTE: iRAM is 256B in size */ struct iram_table_v_2 { /* flags */ - uint16_t flags; /* 0x00 U16 */ + uint16_t min_abm_backlight; /* 0x00 U16 */ /* parameters for ABM2.0 algorithm */ uint8_t min_reduction[NUM_AMBI_LEVEL][NUM_AGGR_LEVEL]; /* 0x02 U0.8 */ @@ -107,10 +140,10 @@ struct iram_table_v_2 { /* For reading PSR State directly from IRAM */ uint8_t psr_state; /* 0xf0 */ - uint8_t dmcu_mcp_interface_version; /* 0xf1 */ - uint8_t dmcu_abm_feature_version; /* 0xf2 */ - uint8_t dmcu_psr_feature_version; /* 0xf3 */ - uint16_t dmcu_version; /* 0xf4 */ + uint8_t dmcu_mcp_interface_version; /* 0xf1 */ + uint8_t dmcu_abm_feature_version; /* 0xf2 */ + uint8_t dmcu_psr_feature_version; /* 0xf3 */ + uint16_t dmcu_version; /* 0xf4 */ uint8_t dmcu_state; /* 0xf6 */ uint16_t blRampReduction; /* 0xf7 */ @@ -131,40 +164,43 @@ struct iram_table_v_2_2 { uint8_t max_reduction[NUM_AMBI_LEVEL][NUM_AGGR_LEVEL]; /* 0x16 U0.8 */ uint8_t bright_pos_gain[NUM_AMBI_LEVEL][NUM_AGGR_LEVEL]; /* 0x2a U2.6 */ uint8_t dark_pos_gain[NUM_AMBI_LEVEL][NUM_AGGR_LEVEL]; /* 0x3e U2.6 */ - uint8_t hybridFactor[NUM_AGGR_LEVEL]; /* 0x52 U0.8 */ - uint8_t contrastFactor[NUM_AGGR_LEVEL]; /* 0x56 U0.8 */ - uint8_t deviation_gain[NUM_AGGR_LEVEL]; /* 0x5a U0.8 */ - uint8_t iir_curve[NUM_AMBI_LEVEL]; /* 0x5e U0.8 */ - uint8_t pad[29]; /* 0x63 U0.8 */ + uint8_t hybrid_factor[NUM_AGGR_LEVEL]; /* 0x52 U0.8 */ + uint8_t contrast_factor[NUM_AGGR_LEVEL]; /* 0x56 U0.8 */ + uint8_t deviation_gain[NUM_AGGR_LEVEL]; /* 0x5a U0.8 */ + uint8_t iir_curve[NUM_AMBI_LEVEL]; /* 0x5e U0.8 */ + uint8_t min_knee[NUM_AGGR_LEVEL]; /* 0x63 U0.8 */ + uint8_t max_knee[NUM_AGGR_LEVEL]; /* 0x67 U0.8 */ + uint16_t min_abm_backlight; /* 0x6b U16 */ + uint8_t pad[19]; /* 0x6d U0.8 */ /* parameters for crgb conversion */ - uint16_t crgb_thresh[NUM_POWER_FN_SEGS]; /* 0x80 U3.13 */ - uint16_t crgb_offset[NUM_POWER_FN_SEGS]; /* 0x90 U1.15 */ - uint16_t crgb_slope[NUM_POWER_FN_SEGS]; /* 0xa0 U4.12 */ + uint16_t crgb_thresh[NUM_POWER_FN_SEGS]; /* 0x80 U3.13 */ + uint16_t crgb_offset[NUM_POWER_FN_SEGS]; /* 0x90 U1.15 */ + uint16_t crgb_slope[NUM_POWER_FN_SEGS]; /* 0xa0 U4.12 */ /* parameters for custom curve */ /* thresholds for brightness --> backlight */ - uint16_t backlight_thresholds[NUM_BL_CURVE_SEGS]; /* 0xb0 U16.0 */ + uint16_t backlight_thresholds[NUM_BL_CURVE_SEGS]; /* 0xb0 U16.0 */ /* offsets for brightness --> backlight */ - uint16_t backlight_offsets[NUM_BL_CURVE_SEGS]; /* 0xd0 U16.0 */ + uint16_t backlight_offsets[NUM_BL_CURVE_SEGS]; /* 0xd0 U16.0 */ /* For reading PSR State directly from IRAM */ - uint8_t psr_state; /* 0xf0 */ - uint8_t dmcu_mcp_interface_version; /* 0xf1 */ - uint8_t dmcu_abm_feature_version; /* 0xf2 */ - uint8_t dmcu_psr_feature_version; /* 0xf3 */ - uint16_t dmcu_version; /* 0xf4 */ - uint8_t dmcu_state; /* 0xf6 */ - - uint8_t dummy1; /* 0xf7 */ - uint8_t dummy2; /* 0xf8 */ - uint8_t dummy3; /* 0xf9 */ - uint8_t dummy4; /* 0xfa */ - uint8_t dummy5; /* 0xfb */ - uint8_t dummy6; /* 0xfc */ - uint8_t dummy7; /* 0xfd */ - uint8_t dummy8; /* 0xfe */ - uint8_t dummy9; /* 0xff */ + uint8_t psr_state; /* 0xf0 */ + uint8_t dmcu_mcp_interface_version; /* 0xf1 */ + uint8_t dmcu_abm_feature_version; /* 0xf2 */ + uint8_t dmcu_psr_feature_version; /* 0xf3 */ + uint16_t dmcu_version; /* 0xf4 */ + uint8_t dmcu_state; /* 0xf6 */ + + uint8_t dummy1; /* 0xf7 */ + uint8_t dummy2; /* 0xf8 */ + uint8_t dummy3; /* 0xf9 */ + uint8_t dummy4; /* 0xfa */ + uint8_t dummy5; /* 0xfb */ + uint8_t dummy6; /* 0xfc */ + uint8_t dummy7; /* 0xfd */ + uint8_t dummy8; /* 0xfe */ + uint8_t dummy9; /* 0xff */ }; #pragma pack(pop) @@ -236,7 +272,8 @@ void fill_iram_v_2(struct iram_table_v_2 *ram_table, struct dmcu_iram_parameters { unsigned int set = params.set; - ram_table->flags = 0x0; + ram_table->min_abm_backlight = + cpu_to_be16(params.min_abm_backlight); ram_table->deviation_gain = 0xb3; ram_table->blRampReduction = @@ -410,6 +447,9 @@ void fill_iram_v_2_2(struct iram_table_v_2_2 *ram_table, struct dmcu_iram_parame ram_table->flags = 0x0; + ram_table->min_abm_backlight = + cpu_to_be16(params.min_abm_backlight); + ram_table->deviation_gain[0] = 0xb3; ram_table->deviation_gain[1] = 0xa8; ram_table->deviation_gain[2] = 0x98; @@ -501,15 +541,76 @@ void fill_iram_v_2_2(struct iram_table_v_2_2 *ram_table, struct dmcu_iram_parame ram_table->dark_pos_gain[4][2] = 0x00; ram_table->dark_pos_gain[4][3] = 0x00; - ram_table->hybridFactor[0] = 0xff; - ram_table->hybridFactor[1] = 0xff; - ram_table->hybridFactor[2] = 0xff; - ram_table->hybridFactor[3] = 0xc0; + ram_table->hybrid_factor[0] = 0xff; + ram_table->hybrid_factor[1] = 0xff; + ram_table->hybrid_factor[2] = 0xff; + ram_table->hybrid_factor[3] = 0xc0; - ram_table->contrastFactor[0] = 0x99; - ram_table->contrastFactor[1] = 0x99; - ram_table->contrastFactor[2] = 0x90; - ram_table->contrastFactor[3] = 0x80; + ram_table->contrast_factor[0] = 0x99; + ram_table->contrast_factor[1] = 0x99; + ram_table->contrast_factor[2] = 0x90; + ram_table->contrast_factor[3] = 0x80; + + ram_table->iir_curve[0] = 0x65; + ram_table->iir_curve[1] = 0x65; + ram_table->iir_curve[2] = 0x65; + ram_table->iir_curve[3] = 0x65; + ram_table->iir_curve[4] = 0x65; + + //Gamma 2.2 + ram_table->crgb_thresh[0] = cpu_to_be16(0x127c); + ram_table->crgb_thresh[1] = cpu_to_be16(0x151b); + ram_table->crgb_thresh[2] = cpu_to_be16(0x17d5); + ram_table->crgb_thresh[3] = cpu_to_be16(0x1a56); + ram_table->crgb_thresh[4] = cpu_to_be16(0x1c83); + ram_table->crgb_thresh[5] = cpu_to_be16(0x1e72); + ram_table->crgb_thresh[6] = cpu_to_be16(0x20f0); + ram_table->crgb_thresh[7] = cpu_to_be16(0x232b); + ram_table->crgb_offset[0] = cpu_to_be16(0x2999); + ram_table->crgb_offset[1] = cpu_to_be16(0x3999); + ram_table->crgb_offset[2] = cpu_to_be16(0x4666); + ram_table->crgb_offset[3] = cpu_to_be16(0x5999); + ram_table->crgb_offset[4] = cpu_to_be16(0x6333); + ram_table->crgb_offset[5] = cpu_to_be16(0x7800); + ram_table->crgb_offset[6] = cpu_to_be16(0x8c00); + ram_table->crgb_offset[7] = cpu_to_be16(0xa000); + ram_table->crgb_slope[0] = cpu_to_be16(0x3609); + ram_table->crgb_slope[1] = cpu_to_be16(0x2dfa); + ram_table->crgb_slope[2] = cpu_to_be16(0x27ea); + ram_table->crgb_slope[3] = cpu_to_be16(0x235d); + ram_table->crgb_slope[4] = cpu_to_be16(0x2042); + ram_table->crgb_slope[5] = cpu_to_be16(0x1dc3); + ram_table->crgb_slope[6] = cpu_to_be16(0x1b1a); + ram_table->crgb_slope[7] = cpu_to_be16(0x1910); + + fill_backlight_transform_table_v_2_2( + params, ram_table); +} + +void fill_iram_v_2_3(struct iram_table_v_2_2 *ram_table, struct dmcu_iram_parameters params) +{ + unsigned int i, j; + unsigned int set = params.set; + + ram_table->flags = 0x0; + + ram_table->min_abm_backlight = + cpu_to_be16(params.min_abm_backlight); + + for (i = 0; i < NUM_AGGR_LEVEL; i++) { + ram_table->hybrid_factor[i] = abm_settings[set][i].brightness_gain; + ram_table->contrast_factor[i] = abm_settings[set][i].contrast_factor; + ram_table->deviation_gain[i] = abm_settings[set][i].deviation_gain; + ram_table->min_knee[i] = abm_settings[set][i].min_knee; + ram_table->max_knee[i] = abm_settings[set][i].max_knee; + + for (j = 0; j < NUM_AMBI_LEVEL; j++) { + ram_table->min_reduction[j][i] = abm_settings[set][i].min_reduction; + ram_table->max_reduction[j][i] = abm_settings[set][i].max_reduction; + ram_table->bright_pos_gain[j][i] = abm_settings[set][i].bright_pos_gain; + ram_table->dark_pos_gain[j][i] = abm_settings[set][i].dark_pos_gain; + } + } ram_table->iir_curve[0] = 0x65; ram_table->iir_curve[1] = 0x65; @@ -561,7 +662,16 @@ bool dmcu_load_iram(struct dmcu *dmcu, memset(&ram_table, 0, sizeof(ram_table)); - if (dmcu->dmcu_version.abm_version == 0x22) { + if (dmcu->dmcu_version.abm_version == 0x24) { + fill_iram_v_2_3((struct iram_table_v_2_2 *)ram_table, params); + result = dmcu->funcs->load_iram( + dmcu, 0, (char *)(&ram_table), IRAM_RESERVE_AREA_START_V2_2); + } else if (dmcu->dmcu_version.abm_version == 0x23) { + fill_iram_v_2_3((struct iram_table_v_2_2 *)ram_table, params); + + result = dmcu->funcs->load_iram( + dmcu, 0, (char *)(&ram_table), IRAM_RESERVE_AREA_START_V2_2); + } else if (dmcu->dmcu_version.abm_version == 0x22) { fill_iram_v_2_2((struct iram_table_v_2_2 *)ram_table, params); result = dmcu->funcs->load_iram( @@ -581,3 +691,4 @@ bool dmcu_load_iram(struct dmcu *dmcu, return result; } + diff --git a/drivers/gpu/drm/amd/display/modules/power/power_helpers.h b/drivers/gpu/drm/amd/display/modules/power/power_helpers.h index da5df00fedce..e54157026330 100644 --- a/drivers/gpu/drm/amd/display/modules/power/power_helpers.h +++ b/drivers/gpu/drm/amd/display/modules/power/power_helpers.h @@ -38,6 +38,7 @@ struct dmcu_iram_parameters { unsigned int backlight_lut_array_size; unsigned int backlight_ramping_reduction; unsigned int backlight_ramping_start; + unsigned int min_abm_backlight; unsigned int set; }; |