Skip to content

Commit

Permalink
drm/amd/display: Add DP audio BW validation
Browse files Browse the repository at this point in the history
[Why]
Timings with small HBlank (such as CVT RBv2) can result in insufficient
HBlank bandwidth for audio SDP transmission when DSC is active. This
will cause some higher bandwidth audio modes to fail.

The combination of CVT RBv2 timings + DSC can commonly be encountered
in MST scenarios.

[How]
Add DP audio bandwidth validation for 8b/10b MST and 128b/132b SST/MST
cases and filter out modes that cannot be supported with the current
timing config.

Reviewed-by: Wenjing Liu <wenjing.liu@amd.com>
Acked-by: Alex Hung <alex.hung@amd.com>
Signed-off-by: George Shen <george.shen@amd.com>
Tested-by: Daniel Wheeler <daniel.wheeler@amd.com>
Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
  • Loading branch information
George Shen authored and Alex Deucher committed Jan 15, 2024
1 parent d451b53 commit 12f72a1
Show file tree
Hide file tree
Showing 5 changed files with 349 additions and 16 deletions.
288 changes: 275 additions & 13 deletions drivers/gpu/drm/amd/display/dc/dce/dce_audio.c
Original file line number Diff line number Diff line change
Expand Up @@ -239,27 +239,289 @@ static void check_audio_bandwidth_hdmi(
}
}
}
static struct fixed31_32 get_link_symbol_clk_freq_mhz(enum dc_link_rate link_rate)
{
switch (link_rate) {
case LINK_RATE_LOW:
return dc_fixpt_from_int(162); /* 162 MHz */
case LINK_RATE_HIGH:
return dc_fixpt_from_int(270); /* 270 MHz */
case LINK_RATE_HIGH2:
return dc_fixpt_from_int(540); /* 540 MHz */
case LINK_RATE_HIGH3:
return dc_fixpt_from_int(810); /* 810 MHz */
case LINK_RATE_UHBR10:
return dc_fixpt_from_fraction(3125, 10); /* 312.5 MHz */
case LINK_RATE_UHBR13_5:
return dc_fixpt_from_fraction(421875, 1000); /* 421.875 MHz */
case LINK_RATE_UHBR20:
return dc_fixpt_from_int(625); /* 625 MHz */
default:
/* Unexpected case, this requires debug if encountered. */
ASSERT(0);
return dc_fixpt_from_int(0);
}
}

struct dp_audio_layout_config {
uint8_t layouts_per_sample_denom;
uint8_t symbols_per_layout;
uint8_t max_layouts_per_audio_sdp;
};

static void get_audio_layout_config(
uint32_t channel_count,
enum dp_link_encoding encoding,
struct dp_audio_layout_config *output)
{
/* Assuming L-PCM audio. Current implementation uses max 1 layout per SDP,
* with each layout being the same size (8ch layout).
*/
if (encoding == DP_8b_10b_ENCODING) {
if (channel_count == 2) {
output->layouts_per_sample_denom = 4;
output->symbols_per_layout = 40;
output->max_layouts_per_audio_sdp = 1;
} else if (channel_count == 8) {
output->layouts_per_sample_denom = 1;
output->symbols_per_layout = 40;
output->max_layouts_per_audio_sdp = 1;
}
} else if (encoding == DP_128b_132b_ENCODING) {
if (channel_count == 2) {
output->layouts_per_sample_denom = 4;
output->symbols_per_layout = 10;
output->max_layouts_per_audio_sdp = 1;
} else if (channel_count == 8) {
output->layouts_per_sample_denom = 1;
output->symbols_per_layout = 10;
output->max_layouts_per_audio_sdp = 1;
}
}
}

/*For DP SST, calculate if specified sample rates can fit into a given timing */
static void check_audio_bandwidth_dpsst(
static uint32_t get_av_stream_map_lane_count(
enum dp_link_encoding encoding,
enum dc_lane_count lane_count,
bool is_mst)
{
uint32_t av_stream_map_lane_count = 0;

if (encoding == DP_8b_10b_ENCODING) {
if (!is_mst)
av_stream_map_lane_count = lane_count;
else
av_stream_map_lane_count = 4;
} else if (encoding == DP_128b_132b_ENCODING) {
av_stream_map_lane_count = 4;
}

ASSERT(av_stream_map_lane_count != 0);

return av_stream_map_lane_count;
}

static uint32_t get_audio_sdp_overhead(
enum dp_link_encoding encoding,
enum dc_lane_count lane_count,
bool is_mst)
{
uint32_t audio_sdp_overhead = 0;

if (encoding == DP_8b_10b_ENCODING) {
if (is_mst)
audio_sdp_overhead = 16; /* 4 * 2 + 8 */
else
audio_sdp_overhead = lane_count * 2 + 8;
} else if (encoding == DP_128b_132b_ENCODING) {
audio_sdp_overhead = 10; /* 4 x 2.5 */
}

ASSERT(audio_sdp_overhead != 0);

return audio_sdp_overhead;
}

static uint32_t calculate_required_audio_bw_in_symbols(
const struct audio_crtc_info *crtc_info,
const struct dp_audio_layout_config *layout_config,
uint32_t channel_count,
union audio_sample_rates *sample_rates)
uint32_t sample_rate_hz,
uint32_t av_stream_map_lane_count,
uint32_t audio_sdp_overhead)
{
/* do nothing */
/* DP spec recommends between 1.05 to 1.1 safety margin to prevent sample under-run */
struct fixed31_32 audio_sdp_margin = dc_fixpt_from_fraction(110, 100);
struct fixed31_32 horizontal_line_freq_khz = dc_fixpt_from_fraction(
crtc_info->requested_pixel_clock_100Hz, crtc_info->h_total * 10);
struct fixed31_32 samples_per_line;
struct fixed31_32 layouts_per_line;
struct fixed31_32 symbols_per_sdp_max_layout;
struct fixed31_32 remainder;
uint32_t num_sdp_with_max_layouts;
uint32_t required_symbols_per_hblank;

samples_per_line = dc_fixpt_from_fraction(sample_rate_hz, 1000);
samples_per_line = dc_fixpt_div(samples_per_line, horizontal_line_freq_khz);
layouts_per_line = dc_fixpt_div_int(samples_per_line, layout_config->layouts_per_sample_denom);

num_sdp_with_max_layouts = dc_fixpt_floor(
dc_fixpt_div_int(layouts_per_line, layout_config->max_layouts_per_audio_sdp));
symbols_per_sdp_max_layout = dc_fixpt_from_int(
layout_config->max_layouts_per_audio_sdp * layout_config->symbols_per_layout);
symbols_per_sdp_max_layout = dc_fixpt_add_int(symbols_per_sdp_max_layout, audio_sdp_overhead);
symbols_per_sdp_max_layout = dc_fixpt_mul(symbols_per_sdp_max_layout, audio_sdp_margin);
required_symbols_per_hblank = num_sdp_with_max_layouts;
required_symbols_per_hblank *= ((dc_fixpt_ceil(symbols_per_sdp_max_layout) + av_stream_map_lane_count) /
av_stream_map_lane_count) * av_stream_map_lane_count;

if (num_sdp_with_max_layouts != dc_fixpt_ceil(
dc_fixpt_div_int(layouts_per_line, layout_config->max_layouts_per_audio_sdp))) {
remainder = dc_fixpt_sub_int(layouts_per_line,
num_sdp_with_max_layouts * layout_config->max_layouts_per_audio_sdp);
remainder = dc_fixpt_mul_int(remainder, layout_config->symbols_per_layout);
remainder = dc_fixpt_add_int(remainder, audio_sdp_overhead);
remainder = dc_fixpt_mul(remainder, audio_sdp_margin);
required_symbols_per_hblank += ((dc_fixpt_ceil(remainder) + av_stream_map_lane_count) /
av_stream_map_lane_count) * av_stream_map_lane_count;
}

return required_symbols_per_hblank;
}

/*For DP MST, calculate if specified sample rates can fit into a given timing */
static void check_audio_bandwidth_dpmst(
/* Current calculation only applicable for 8b/10b MST and 128b/132b SST/MST.
*/
static uint32_t calculate_available_hblank_bw_in_symbols(
const struct audio_crtc_info *crtc_info,
const struct audio_dp_link_info *dp_link_info)
{
uint64_t hblank = crtc_info->h_total - crtc_info->h_active;
struct fixed31_32 hblank_time_msec =
dc_fixpt_from_fraction(hblank * 10, crtc_info->requested_pixel_clock_100Hz);
struct fixed31_32 lsclkfreq_mhz =
get_link_symbol_clk_freq_mhz(dp_link_info->link_rate);
struct fixed31_32 average_stream_sym_bw_frac;
struct fixed31_32 peak_stream_bw_kbps;
struct fixed31_32 bits_per_pixel;
struct fixed31_32 link_bw_kbps;
struct fixed31_32 available_stream_sym_count;
uint32_t available_hblank_bw = 0; /* in stream symbols */

if (crtc_info->dsc_bits_per_pixel) {
bits_per_pixel = dc_fixpt_from_fraction(crtc_info->dsc_bits_per_pixel, 16);
} else {
switch (crtc_info->color_depth) {
case COLOR_DEPTH_666:
bits_per_pixel = dc_fixpt_from_int(6);
break;
case COLOR_DEPTH_888:
bits_per_pixel = dc_fixpt_from_int(8);
break;
case COLOR_DEPTH_101010:
bits_per_pixel = dc_fixpt_from_int(10);
break;
case COLOR_DEPTH_121212:
bits_per_pixel = dc_fixpt_from_int(12);
break;
default:
/* Default to commonly supported color depth. */
bits_per_pixel = dc_fixpt_from_int(8);
break;
}

bits_per_pixel = dc_fixpt_mul_int(bits_per_pixel, 3);

if (crtc_info->pixel_encoding == PIXEL_ENCODING_YCBCR422) {
bits_per_pixel = dc_fixpt_div_int(bits_per_pixel, 3);
bits_per_pixel = dc_fixpt_mul_int(bits_per_pixel, 2);
} else if (crtc_info->pixel_encoding == PIXEL_ENCODING_YCBCR420) {
bits_per_pixel = dc_fixpt_div_int(bits_per_pixel, 2);
}
}

/* Use simple stream BW calculation because mainlink overhead is
* accounted for separately in the audio BW calculations.
*/
peak_stream_bw_kbps = dc_fixpt_from_fraction(crtc_info->requested_pixel_clock_100Hz, 10);
peak_stream_bw_kbps = dc_fixpt_mul(peak_stream_bw_kbps, bits_per_pixel);
link_bw_kbps = dc_fixpt_from_int(dp_link_info->link_bandwidth_kbps);
average_stream_sym_bw_frac = dc_fixpt_div(peak_stream_bw_kbps, link_bw_kbps);

available_stream_sym_count = dc_fixpt_mul_int(hblank_time_msec, 1000);
available_stream_sym_count = dc_fixpt_mul(available_stream_sym_count, lsclkfreq_mhz);
available_stream_sym_count = dc_fixpt_mul(available_stream_sym_count, average_stream_sym_bw_frac);
available_hblank_bw = dc_fixpt_floor(available_stream_sym_count);
available_hblank_bw *= dp_link_info->lane_count;
available_hblank_bw -= crtc_info->dsc_num_slices * 4; /* EOC overhead */

if (available_hblank_bw < dp_link_info->hblank_min_symbol_width)
available_hblank_bw = dp_link_info->hblank_min_symbol_width;

if (available_hblank_bw < 12)
available_hblank_bw = 0;
else
available_hblank_bw -= 12; /* Main link overhead */

return available_hblank_bw;
}

static void check_audio_bandwidth_dp(
const struct audio_crtc_info *crtc_info,
const struct audio_dp_link_info *dp_link_info,
uint32_t channel_count,
union audio_sample_rates *sample_rates)
{
/* do nothing */
struct dp_audio_layout_config layout_config = {0};
uint32_t available_hblank_bw;
uint32_t av_stream_map_lane_count;
uint32_t audio_sdp_overhead;

/* TODO: Add validation for SST 8b/10 case */
if (!dp_link_info->is_mst && dp_link_info->encoding == DP_8b_10b_ENCODING)
return;

available_hblank_bw = calculate_available_hblank_bw_in_symbols(
crtc_info, dp_link_info);
av_stream_map_lane_count = get_av_stream_map_lane_count(
dp_link_info->encoding, dp_link_info->lane_count, dp_link_info->is_mst);
audio_sdp_overhead = get_audio_sdp_overhead(
dp_link_info->encoding, dp_link_info->lane_count, dp_link_info->is_mst);
get_audio_layout_config(
channel_count, dp_link_info->encoding, &layout_config);

if (available_hblank_bw < calculate_required_audio_bw_in_symbols(
crtc_info, &layout_config, channel_count, 192000,
av_stream_map_lane_count, audio_sdp_overhead))
sample_rates->rate.RATE_192 = 0;
if (available_hblank_bw < calculate_required_audio_bw_in_symbols(
crtc_info, &layout_config, channel_count, 176400,
av_stream_map_lane_count, audio_sdp_overhead))
sample_rates->rate.RATE_176_4 = 0;
if (available_hblank_bw < calculate_required_audio_bw_in_symbols(
crtc_info, &layout_config, channel_count, 96000,
av_stream_map_lane_count, audio_sdp_overhead))
sample_rates->rate.RATE_96 = 0;
if (available_hblank_bw < calculate_required_audio_bw_in_symbols(
crtc_info, &layout_config, channel_count, 88200,
av_stream_map_lane_count, audio_sdp_overhead))
sample_rates->rate.RATE_88_2 = 0;
if (available_hblank_bw < calculate_required_audio_bw_in_symbols(
crtc_info, &layout_config, channel_count, 48000,
av_stream_map_lane_count, audio_sdp_overhead))
sample_rates->rate.RATE_48 = 0;
if (available_hblank_bw < calculate_required_audio_bw_in_symbols(
crtc_info, &layout_config, channel_count, 44100,
av_stream_map_lane_count, audio_sdp_overhead))
sample_rates->rate.RATE_44_1 = 0;
if (available_hblank_bw < calculate_required_audio_bw_in_symbols(
crtc_info, &layout_config, channel_count, 32000,
av_stream_map_lane_count, audio_sdp_overhead))
sample_rates->rate.RATE_32 = 0;
}

static void check_audio_bandwidth(
const struct audio_crtc_info *crtc_info,
const struct audio_dp_link_info *dp_link_info,
uint32_t channel_count,
enum signal_type signal,
union audio_sample_rates *sample_rates)
Expand All @@ -271,12 +533,9 @@ static void check_audio_bandwidth(
break;
case SIGNAL_TYPE_EDP:
case SIGNAL_TYPE_DISPLAY_PORT:
check_audio_bandwidth_dpsst(
crtc_info, channel_count, sample_rates);
break;
case SIGNAL_TYPE_DISPLAY_PORT_MST:
check_audio_bandwidth_dpmst(
crtc_info, channel_count, sample_rates);
check_audio_bandwidth_dp(
crtc_info, dp_link_info, channel_count, sample_rates);
break;
default:
break;
Expand Down Expand Up @@ -394,7 +653,8 @@ void dce_aud_az_configure(
struct audio *audio,
enum signal_type signal,
const struct audio_crtc_info *crtc_info,
const struct audio_info *audio_info)
const struct audio_info *audio_info,
const struct audio_dp_link_info *dp_link_info)
{
struct dce_audio *aud = DCE_AUD(audio);

Expand Down Expand Up @@ -529,6 +789,7 @@ void dce_aud_az_configure(

check_audio_bandwidth(
crtc_info,
dp_link_info,
channel_count,
signal,
&sample_rates);
Expand Down Expand Up @@ -588,6 +849,7 @@ void dce_aud_az_configure(

check_audio_bandwidth(
crtc_info,
dp_link_info,
8,
signal,
&sample_rate);
Expand Down
3 changes: 2 additions & 1 deletion drivers/gpu/drm/amd/display/dc/dce/dce_audio.h
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,8 @@ void dce_aud_az_disable(struct audio *audio);
void dce_aud_az_configure(struct audio *audio,
enum signal_type signal,
const struct audio_crtc_info *crtc_info,
const struct audio_info *audio_info);
const struct audio_info *audio_info,
const struct audio_dp_link_info *dp_link_info);

void dce_aud_wall_dto_setup(struct audio *audio,
enum signal_type signal,
Expand Down
Loading

0 comments on commit 12f72a1

Please sign in to comment.