On 2026-05-07 11:51, Harry Wentland wrote:
> Add all the bits to enable DSC over FRL.
>
> Signed-off-by: Harry Wentland <[email protected]>
> ---
> .../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 74 +-
> .../amd/display/amdgpu_dm/amdgpu_dm_helpers.c | 91 ++-
> .../display/amdgpu_dm/amdgpu_dm_mst_types.c | 12 +
> drivers/gpu/drm/amd/display/dc/core/dc.c | 6 +
> .../drm/amd/display/dc/core/dc_hw_sequencer.c | 13 +
> .../gpu/drm/amd/display/dc/core/dc_resource.c | 54 ++
> .../gpu/drm/amd/display/dc/core/dc_stream.c | 2 +-
> drivers/gpu/drm/amd/display/dc/dc.h | 7 +
> drivers/gpu/drm/amd/display/dc/dc_dsc.h | 8 +
> .../gpu/drm/amd/display/dc/dc_hdmi_types.h | 2 +
> .../drm/amd/display/dc/dml/dcn30/dcn30_fpu.c | 40 +-
> .../dc/dml/dcn30/display_mode_vba_30.c | 3 +
> .../dc/dml/dcn31/display_mode_vba_31.c | 3 +
> .../dc/dml/dcn314/display_mode_vba_314.c | 3 +
> .../dc/dml/dcn32/display_mode_vba_util_32.c | 3 +
> .../drm/amd/display/dc/dml/dml1_frl_cap_chk.c | 197 +++++
> .../drm/amd/display/dc/dml/dml1_frl_cap_chk.h | 9 +
> drivers/gpu/drm/amd/display/dc/dsc/dc_dsc.c | 704 ++++++++++++++++++
> .../hpo/dcn30/dcn30_hpo_frl_stream_encoder.c | 105 +++
> .../dcn401/dcn401_hpo_frl_stream_encoder.c | 1 +
> .../hpo/dcn42/dcn42_hpo_frl_stream_encoder.c | 1 +
> .../amd/display/dc/hwss/dcn30/dcn30_hwseq.c | 9 +
> .../amd/display/dc/hwss/dcn30/dcn30_init.c | 1 +
> .../amd/display/dc/hwss/dcn31/dcn31_init.c | 1 +
> .../amd/display/dc/hwss/dcn314/dcn314_init.c | 1 +
> .../amd/display/dc/hwss/dcn32/dcn32_init.c | 1 +
> .../amd/display/dc/hwss/dcn35/dcn35_init.c | 1 +
> .../amd/display/dc/hwss/dcn351/dcn351_init.c | 1 +
> .../amd/display/dc/hwss/dcn401/dcn401_init.c | 1 +
> .../amd/display/dc/hwss/dcn42/dcn42_init.c | 1 +
> .../drm/amd/display/dc/hwss/hw_sequencer.h | 5 +
> .../amd/display/dc/inc/hw/stream_encoder.h | 4 +
> .../gpu/drm/amd/display/dc/link/link_dpms.c | 38 +
> .../display/dc/link/protocols/link_hdmi_frl.c | 38 +
> .../amd/display/dc/optc/dcn30/dcn30_optc.c | 30 +-
> .../amd/display/dc/optc/dcn30/dcn30_optc.h | 1 +
> .../amd/display/dc/optc/dcn31/dcn31_optc.c | 1 +
> .../amd/display/dc/optc/dcn314/dcn314_optc.c | 1 +
> .../amd/display/dc/optc/dcn32/dcn32_optc.c | 1 +
> .../amd/display/dc/optc/dcn35/dcn35_optc.c | 1 +
> .../amd/display/dc/optc/dcn401/dcn401_optc.c | 1 +
> .../amd/display/dc/optc/dcn42/dcn42_optc.c | 1 +
> 42 files changed, 1460 insertions(+), 17 deletions(-)
>
> diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
> b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
> index be4b66b00be9..007f099ffbb5 100644
> --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
> +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
> @@ -7323,12 +7323,30 @@ static void update_dsc_caps(struct
> amdgpu_dm_connector *aconnector,
>
> if (aconnector->dc_link && (sink->sink_signal ==
> SIGNAL_TYPE_DISPLAY_PORT ||
> sink->sink_signal == SIGNAL_TYPE_EDP)) {
> - if (sink->link->dpcd_caps.dongle_type == DISPLAY_DONGLE_NONE ||
> - sink->link->dpcd_caps.dongle_type ==
> DISPLAY_DONGLE_DP_HDMI_CONVERTER)
> + if (sink->link->dpcd_caps.dongle_type == DISPLAY_DONGLE_NONE)
> dc_dsc_parse_dsc_dpcd(aconnector->dc_link->ctx->dc,
>
> aconnector->dc_link->dpcd_caps.dsc_caps.dsc_basic_caps.raw,
>
> aconnector->dc_link->dpcd_caps.dsc_caps.dsc_branch_decoder_caps.raw,
> dsc_caps);
> + else if (sink->link->dpcd_caps.dongle_type ==
> DISPLAY_DONGLE_DP_HDMI_CONVERTER) {
> + if
> (aconnector->dc_link->dpcd_caps.dsc_caps.dsc_basic_caps.fields.dsc_support.DSC_PASSTHROUGH_SUPPORT
> &&
> +
> !aconnector->dsc_settings.dsc_force_disable_passthrough &&
> +
> aconnector->dc_link->dpcd_caps.dongle_caps.dp_hdmi_frl_max_link_bw_in_kbps >
> 0 &&
> + sink->edid_caps.frl_dsc_support &&
> + sink->edid_caps.max_frl_rate > 0 &&
> + sink->edid_caps.frl_dsc_max_frl_rate >
> 0)
> +
> dc_dsc_parse_dsc_edid(aconnector->dc_link->ctx->dc, &sink->edid_caps,
> dsc_caps);
> + else
> +
> dc_dsc_parse_dsc_dpcd(aconnector->dc_link->ctx->dc,
> +
> aconnector->dc_link->dpcd_caps.dsc_caps.dsc_basic_caps.raw,
> +
> aconnector->dc_link->dpcd_caps.dsc_caps.dsc_branch_decoder_caps.raw,
> + dsc_caps);
> + }
> + } else if (aconnector->dc_link && sink->sink_signal ==
> SIGNAL_TYPE_HDMI_FRL) {
> + if (sink->edid_caps.frl_dsc_support &&
> + sink->edid_caps.max_frl_rate > 0 &&
> + sink->edid_caps.frl_dsc_max_frl_rate > 0)
> + dc_dsc_parse_dsc_edid(aconnector->dc_link->ctx->dc,
> &sink->edid_caps, dsc_caps);
> }
> }
>
> @@ -7402,6 +7420,10 @@ static void apply_dsc_policy_for_stream(struct
> amdgpu_dm_connector *aconnector,
> struct drm_connector *drm_connector = &aconnector->base;
> u32 link_bandwidth_kbps;
> struct dc *dc = sink->ctx->dc;
> + const struct dc_hdmi_frl_link_settings *frl_verified_link_cap = NULL;
> + u32 converter_bw_in_kbps;
> + u32 sink_bw_in_kbps;
> + u32 dsc_sink_bw_in_kbps;
> u32 max_supported_bw_in_kbps, timing_bw_in_kbps;
> u32 dsc_max_supported_bw_in_kbps;
> u32 max_dsc_target_bpp_limit_override =
> @@ -7440,8 +7462,18 @@ static void apply_dsc_policy_for_stream(struct
> amdgpu_dm_connector *aconnector,
> } else if (sink->link->dpcd_caps.dongle_type ==
> DISPLAY_DONGLE_DP_HDMI_CONVERTER) {
> timing_bw_in_kbps =
> dc_bandwidth_in_kbps_from_timing(&stream->timing,
>
> dc_link_get_highest_encoding_format(aconnector->dc_link));
> - max_supported_bw_in_kbps = link_bandwidth_kbps;
> - dsc_max_supported_bw_in_kbps = link_bandwidth_kbps;
> + converter_bw_in_kbps =
> aconnector->dc_link->dpcd_caps.dongle_caps.dp_hdmi_frl_max_link_bw_in_kbps;
> + sink_bw_in_kbps =
> dc_link_bw_kbps_from_raw_frl_link_rate_data(dc, sink->edid_caps.max_frl_rate);
> + dsc_sink_bw_in_kbps =
> dc_link_bw_kbps_from_raw_frl_link_rate_data(dc,
> sink->edid_caps.frl_dsc_max_frl_rate);
> +
> + if (dsc_caps->is_frl) {
> + max_supported_bw_in_kbps =
> min(link_bandwidth_kbps, converter_bw_in_kbps);
> + max_supported_bw_in_kbps =
> min(max_supported_bw_in_kbps, sink_bw_in_kbps);
> + dsc_max_supported_bw_in_kbps =
> min(max_supported_bw_in_kbps, dsc_sink_bw_in_kbps);
> + } else {
> + max_supported_bw_in_kbps = link_bandwidth_kbps;
> + dsc_max_supported_bw_in_kbps =
> link_bandwidth_kbps;
> + }
>
> if (timing_bw_in_kbps > max_supported_bw_in_kbps &&
> max_supported_bw_in_kbps > 0 &&
> @@ -7454,11 +7486,41 @@ static void apply_dsc_policy_for_stream(struct
> amdgpu_dm_connector *aconnector,
>
> dc_link_get_highest_encoding_format(aconnector->dc_link),
> &stream->timing.dsc_cfg)) {
> stream->timing.flags.DSC = 1;
> - drm_dbg_driver(drm_connector->dev, "%s:
> SST_DSC [%s] DSC is selected from DP-HDMI PCON\n",
> -
> __func__, drm_connector->name);
> + drm_dbg_driver(drm_connector->dev, "%s:
> SST_DSC [%s] DSC is selected from %s\n",
> + __func__,
> drm_connector->name,
> + (dsc_caps->is_frl == 1)
> ? "HDMI FRL RX" : "DP-HDMI PCON");
> }
> }
> }
> + else if (aconnector->dc_link && sink->sink_signal ==
> SIGNAL_TYPE_HDMI_FRL) {
> + struct dc_dsc_policy dsc_policy = {0};
> +
> + frl_verified_link_cap = dc_link_get_frl_link_cap(stream->link);
> + if (frl_verified_link_cap->frl_link_rate !=
> HDMI_FRL_LINK_RATE_DISABLE &&
> + aconnector->dc_link->frl_flags.force_frl_dsc) {
> + dc_dsc_policy_set_enable_dsc_when_not_needed(true);
> + dc_dsc_get_policy_for_timing(&stream->timing, 0,
> &dsc_policy, dc_link_get_highest_encoding_format(stream->link));
> + }
> +
> + timing_bw_in_kbps =
> dc_bandwidth_in_kbps_from_timing(&stream->timing, DC_LINK_ENCODING_HDMI_FRL);
> + link_bandwidth_kbps = dc_link_frl_bandwidth_kbps(stream->link,
> frl_verified_link_cap->frl_link_rate);
> + dsc_sink_bw_in_kbps =
> dc_link_bw_kbps_from_raw_frl_link_rate_data(dc,
> sink->edid_caps.frl_dsc_max_frl_rate);
> +
> + if ((timing_bw_in_kbps > link_bandwidth_kbps &&
> dsc_sink_bw_in_kbps > 0) ||
> + (dsc_policy.enable_dsc_when_not_needed ||
> dsc_options.force_dsc_when_not_needed)) {
> + if
> (dc_dsc_compute_config(aconnector->dc_link->ctx->dc->res_pool->dscs[0],
> + dsc_caps,
> + &dsc_options,
> + dsc_sink_bw_in_kbps,
> + &stream->timing,
> +
> dc_link_get_highest_encoding_format(aconnector->dc_link),
> + &stream->timing.dsc_cfg)) {
> + stream->timing.flags.DSC = 1;
> + drm_dbg_driver(drm_connector->dev, "%s:
> HDMI_FRL_DSC [%s] DSC is selected from HDMI FRL RX\n",
> + __func__, drm_connector->name);
> + }
> + }
> + }
>
> /* Overwrite the stream flag if DSC is enabled through debugfs */
> if (aconnector->dsc_settings.dsc_force_enable == DSC_CLK_FORCE_ENABLE)
> diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c
> b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c
> index 05f9a01f223c..e14980271c96 100644
> --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c
> +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c
> @@ -178,8 +178,15 @@ enum dc_edid_status dm_helpers_parse_edid_caps(
>
> edid_caps->edid_hdmi = connector->display_info.is_hdmi;
>
> - if (edid_caps->edid_hdmi)
> + if (edid_caps->edid_hdmi) {
>
> populate_hdmi_info_from_connector(&connector->display_info.hdmi, edid_caps);
> + drm_dbg_driver(connector->dev, "%s: HDMI_FRL [%s] max_frl_rate
> %d\n", __func__, connector->name, edid_caps->max_frl_rate);
> + if (edid_caps->frl_dsc_support)
> + drm_dbg_driver(connector->dev, "%s: HDMI_FRL_DSC [%s]
> frl_dsc_10bpc %d, frl_dsc_12bpc %d, frl_dsc_all_bpp %d, frl_dsc_native_420
> %d, frl_dsc_max_slices %d, frl_dsc_max_frl_rate %d,
> frl_dsc_total_chunk_kbytes %d\n",
> + __func__, connector->name,
> edid_caps->frl_dsc_10bpc, edid_caps->frl_dsc_12bpc, \
> + edid_caps->frl_dsc_all_bpp,
> edid_caps->frl_dsc_native_420, edid_caps->frl_dsc_max_slices, \
> + edid_caps->frl_dsc_max_frl_rate,
> edid_caps->frl_dsc_total_chunk_kbytes);
> + }
>
> apply_edid_quirks(dev, edid_buf, edid_caps);
>
> @@ -896,8 +903,23 @@ bool dm_helpers_dp_write_dsc_enable(
> port = aconnector->mst_output_port;
>
> if (enable) {
> - if (port->passthrough_aux) {
> - ret = drm_dp_dpcd_write(port->passthrough_aux,
> + if (port->passthrough_aux
> + || (stream->timing.dsc_cfg.is_frl &&
> + (!stream->sink->dsc_caps.dsc_dec_caps.is_frl ||
> + (stream->sink->dsc_caps.dsc_dec_caps.is_frl &&
> +
> aconnector->mst_downstream_port_present.fields.PORT_PRESENT &&
> +
> aconnector->mst_downstream_port_caps.bytes.byte0.bits.DWN_STRM_PORTX_TYPE ==
> + DOWN_STREAM_DETAILED_HDMI)))
> + ) {
> + if (!stream->sink->dsc_caps.dsc_dec_caps.is_frl)
> + /* DSC passthrough to DP receiver */
> + enable_passthrough = DSC_DECODING;
> + else if
> (aconnector->mst_downstream_port_present.fields.PORT_PRESENT &&
> +
> aconnector->mst_downstream_port_caps.bytes.byte0.bits.DWN_STRM_PORTX_TYPE ==
> + DOWN_STREAM_DETAILED_HDMI)
> + /* DSC passthrough to FRL sink */
> + enable_passthrough = DSC_PASSTHROUGH;
> + ret =
> drm_dp_dpcd_write(&aconnector->mst_output_port->aux,
> DP_DSC_ENABLE,
> &enable_passthrough, 1);
> drm_dbg_dp(dev,
> @@ -921,8 +943,13 @@ bool dm_helpers_dp_write_dsc_enable(
> "virtual dpcd",
> ret);
>
> - if (port->passthrough_aux) {
> - ret = drm_dp_dpcd_write(port->passthrough_aux,
> + if (port->passthrough_aux
> + || (stream->timing.dsc_cfg.is_frl &&
> + (!stream->sink->dsc_caps.dsc_dec_caps.is_frl ||
> +
> (aconnector->mst_downstream_port_present.fields.PORT_PRESENT &&
> +
> aconnector->mst_downstream_port_caps.bytes.byte0.bits.DWN_STRM_PORTX_TYPE ==
> + DOWN_STREAM_DETAILED_HDMI)))) {
> + ret =
> drm_dp_dpcd_write(&aconnector->mst_output_port->aux,
> DP_DSC_ENABLE,
> &enable_passthrough, 1);
> drm_dbg_dp(dev,
> @@ -939,10 +966,22 @@ bool dm_helpers_dp_write_dsc_enable(
> "SST_DSC Send DSC %s to SST RX\n",
> enable_dsc ? "enable" : "disable");
> } else if (stream->sink->link->dpcd_caps.dongle_type ==
> DISPLAY_DONGLE_DP_HDMI_CONVERTER) {
> + if (enable) {
> + if (stream->timing.dsc_cfg.is_frl) {
> + enable_dsc = DSC_PASSTHROUGH;
> + } else {
> + enable_dsc = DSC_DECODING;
> + }
> + drm_dbg_dp(dev,
> + "SST_DSC Sent DSC decoding enable to
> %s port, ret = %u\n",
> + (port->passthrough_aux) ?
> + "remote HDMI FRL RX" :
> + "DP-HDMI PCON", ret);
> + } else {
> + enable_dsc = DSC_DISABLE;
> + drm_dbg_dp(dev, "SST_DSC Send DSC disable to
> DP-HDMI PCON\n");
> + }
> ret = dm_helpers_dp_write_dpcd(ctx, stream->link,
> DP_DSC_ENABLE, &enable_dsc, 1);
> - drm_dbg_dp(dev,
> - "SST_DSC Send DSC %s to DP-HDMI PCON\n",
> - enable_dsc ? "enable" : "disable");
> }
> }
>
> @@ -1054,10 +1093,46 @@ static uint8_t get_max_frl_rate(uint8_t max_lanes,
> uint8_t max_rate_per_lane)
> return max_frl_rate;
> }
>
> +static uint8_t get_dsc_max_slices(uint8_t max_slices, int clk_per_slice)
> +{
> + uint8_t dsc_max_slices;
> +
> + if ((max_slices == 1) && (clk_per_slice == 340))
> + dsc_max_slices = 1;
> + else if ((max_slices == 2) && (clk_per_slice == 340))
> + dsc_max_slices = 2;
> + else if ((max_slices == 4) && (clk_per_slice == 340))
> + dsc_max_slices = 3;
> + else if ((max_slices == 8) && (clk_per_slice == 340))
> + dsc_max_slices = 4;
> + else if ((max_slices == 8) && (clk_per_slice == 400))
> + dsc_max_slices = 5;
> + else if ((max_slices == 12) && (clk_per_slice == 400))
> + dsc_max_slices = 6;
> + else if ((max_slices == 16) && (clk_per_slice == 400))
> + dsc_max_slices = 7;
> + else
> + dsc_max_slices = 0;
> +
> + return dsc_max_slices;
> +}
> +
> void populate_hdmi_info_from_connector(struct drm_hdmi_info *hdmi, struct
> dc_edid_caps *edid_caps)
> {
> edid_caps->scdc_present = hdmi->scdc.supported;
> edid_caps->max_frl_rate = get_max_frl_rate(hdmi->max_lanes,
> hdmi->max_frl_rate_per_lane);
> + edid_caps->frl_dsc_support = hdmi->dsc_cap.v_1p2;
> + if (edid_caps->frl_dsc_support) {
> + if (hdmi->dsc_cap.bpc_supported == 10)
> + edid_caps->frl_dsc_10bpc = true;
> + else if (hdmi->dsc_cap.bpc_supported == 12)
> + edid_caps->frl_dsc_12bpc = true;
> + edid_caps->frl_dsc_all_bpp = hdmi->dsc_cap.all_bpp;
> + edid_caps->frl_dsc_native_420 = hdmi->dsc_cap.native_420;
> + edid_caps->frl_dsc_max_slices =
> get_dsc_max_slices(hdmi->dsc_cap.max_slices, hdmi->dsc_cap.clk_per_slice);
> + edid_caps->frl_dsc_max_frl_rate =
> get_max_frl_rate(hdmi->dsc_cap.max_lanes,
> hdmi->dsc_cap.max_frl_rate_per_lane);
> + edid_caps->frl_dsc_total_chunk_kbytes =
> hdmi->dsc_cap.total_chunk_kbytes;
> + }
> }
>
> enum dc_edid_status dm_helpers_read_local_edid(
> diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c
> b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c
> index 8e2a8c2c1d84..bc5a9e1663ba 100644
> --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c
> +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c
> @@ -295,6 +295,17 @@ static bool validate_dsc_caps_on_connector(struct
> amdgpu_dm_connector *aconnecto
> if (!aconnector->dsc_aux)
> return false;
>
> + if (port->passthrough_aux &&
> + aconnector->dc_link->dc->caps.dp_hdmi21_pcon_support &&
> + dc_sink->edid_caps.frl_dsc_support &&
> + dc_sink->edid_caps.max_frl_rate > 0 &&
> + dc_sink->edid_caps.frl_dsc_max_frl_rate > 0) {
> + dc_dsc_parse_dsc_edid(aconnector->dc_link->ctx->dc,
> + &dc_sink->edid_caps,
> &dc_sink->dsc_caps.dsc_dec_caps);
> + DRM_DEBUG_DRIVER("%s: [%s] DSC is selected from FRL RX\n",
> __func__, aconnector->base.name);
> + return true;
> + } else {
> + DRM_DEBUG_DRIVER("%s: [%s] DSC is selected from DP-HDMI
> PCON\n", __func__, aconnector->base.name);
> if (drm_dp_dpcd_read(aconnector->dsc_aux, DP_DSC_SUPPORT, dsc_caps, 16)
> < 0)
> return false;
>
> @@ -307,6 +318,7 @@ static bool validate_dsc_caps_on_connector(struct
> amdgpu_dm_connector *aconnecto
> &dc_sink->dsc_caps.dsc_dec_caps))
> return false;
>
> + }
> return true;
> }
> #endif
> diff --git a/drivers/gpu/drm/amd/display/dc/core/dc.c
> b/drivers/gpu/drm/amd/display/dc/core/dc.c
> index b4ad3ec0f029..517844a99451 100644
> --- a/drivers/gpu/drm/amd/display/dc/core/dc.c
> +++ b/drivers/gpu/drm/amd/display/dc/core/dc.c
> @@ -4050,6 +4050,12 @@ static void add_link_update_dsc_config_sequence(
> pipe_ctx->stream_res.stream_enc,
> true, dsc_packed_pps, false);
> }
> + else if (dc_is_hdmi_frl_signal(stream->signal)) {
> + hwss_add_hpo_frl_stream_enc_set_dsc_config(seq_state,
> + pipe_ctx->stream_res.hpo_frl_stream_enc,
> + &stream->timing,
> + dsc_packed_pps);
> + }
> }
> }
>
> diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_hw_sequencer.c
> b/drivers/gpu/drm/amd/display/dc/core/dc_hw_sequencer.c
> index c0c35d6653a5..ad01ffdc73c3 100644
> --- a/drivers/gpu/drm/amd/display/dc/core/dc_hw_sequencer.c
> +++ b/drivers/gpu/drm/amd/display/dc/core/dc_hw_sequencer.c
> @@ -1642,6 +1642,9 @@ void hwss_execute_sequence(struct dc *dc,
> case STREAM_ENC_DP_SET_DSC_PPS_INFO_PACKET:
> hwss_stream_enc_dp_set_dsc_pps_info_packet(params);
> break;
> + case HPO_FRL_STREAM_ENC_SET_DSC_CONFIG:
> + hwss_hpo_frl_stream_enc_set_dsc_config(params);
> + break;
> case DP_TRACE_SOURCE_SEQUENCE:
> hwss_dp_trace_source_sequence(params);
> break;
> @@ -3734,6 +3737,16 @@ void hwss_stream_enc_dp_set_dsc_pps_info_packet(union
> block_sequence_params *par
>
> params->stream_enc_dp_set_dsc_pps_info_packet_params.pps_sdp_stream);
> }
>
> +void hwss_hpo_frl_stream_enc_set_dsc_config(union block_sequence_params
> *params)
> +{
> + if (params->hpo_frl_stream_enc_set_dsc_config_params.hpo_frl_stream_enc
> &&
> +
> params->hpo_frl_stream_enc_set_dsc_config_params.hpo_frl_stream_enc->funcs->hdmi_frl_set_dsc_config)
> +
> params->hpo_frl_stream_enc_set_dsc_config_params.hpo_frl_stream_enc->funcs->hdmi_frl_set_dsc_config(
> +
> params->hpo_frl_stream_enc_set_dsc_config_params.hpo_frl_stream_enc,
> + (struct dc_crtc_timing
> *)params->hpo_frl_stream_enc_set_dsc_config_params.timing,
> +
> params->hpo_frl_stream_enc_set_dsc_config_params.dsc_packed_pps);
> +}
> +
> void hwss_set_dmdata_attributes(union block_sequence_params *params)
> {
> struct hubp *hubp = params->set_dmdata_attributes_params.hubp;
> diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_resource.c
> b/drivers/gpu/drm/amd/display/dc/core/dc_resource.c
> index 2457b563e6c8..0815400468ed 100644
> --- a/drivers/gpu/drm/amd/display/dc/core/dc_resource.c
> +++ b/drivers/gpu/drm/amd/display/dc/core/dc_resource.c
> @@ -4592,6 +4592,38 @@ enum dc_status dc_validate_with_context(struct dc *dc,
> }
>
> #if defined(CONFIG_DRM_AMD_DC_FP)
> +/**
> + * dc_update_modified_pix_clock_for_dsc_with_padding() - update pix_clk for
> dsc with padding
> + *
> + * @dc_stream_state: Pointer to the stream structure.
> + * @dc_crtc_timing: Pointer to the stream dc_crtc_timing structure.
> + * @dsc_padding_params: Pointer to the DSC padding parameters structure.
> + *
> + * This function updated the pix_clk for dsc with padding stored in pipe_ctx
> + * such that the OTG h_active time fits withing the expected compressed
> active
> + * time calculated according to HDMI spec. H_total is then increased to
> + * maintain the same OTG line time as before the increased pix_clk.
> + */
> +static void dc_update_modified_pix_clock_for_dsc_with_padding(const struct
> dc_stream_state *stream,
> + const struct dc_crtc_timing *timing, struct dsc_padding_params
> *dsc_padding_params)
> +{
> + DC_FP_START();
> +
> frl_modified_pix_clock_for_dsc_padding(stream->link->frl_verified_link_cap.borrow_params.hc_active_target,
> + stream->link->frl_verified_link_cap.borrow_params.hc_blank_target,
> + stream->link->frl_verified_link_cap.frl_num_lanes,
> + timing->pix_clk_100hz,
> + stream->link->frl_verified_link_cap.frl_link_rate,
> + timing->h_addressable,
> + timing->h_border_left,
> + timing->h_border_right,
> + timing->h_total,
> + (timing->h_addressable + dsc_padding_params->dsc_hactive_padding),
> + &dsc_padding_params->dsc_pix_clk_100hz,
> + &dsc_padding_params->dsc_htotal_padding);
> + DC_FP_END();
> +
> + dsc_padding_params->dsc_htotal_padding =
> dsc_padding_params->dsc_htotal_padding - timing->h_total;
> +}
> #endif /* CONFIG_DRM_AMD_DC_FP */
>
> /**
> @@ -4618,6 +4650,28 @@ static void
> calculate_timing_params_for_dsc_with_padding(struct pipe_ctx *pipe_c
> if (stream)
> pipe_ctx->dsc_padding_params.dsc_pix_clk_100hz =
> stream->timing.pix_clk_100hz;
>
> + uint32_t hactive;
> + uint32_t ceil_slice_width;
> + if (stream && stream->timing.flags.DSC) {
> + hactive = stream->timing.h_addressable +
> stream->timing.h_border_left + stream->timing.h_border_right;
> +
> + /* Assume if determined slices does not divide Hactive evenly,
> Hborrow is needed for padding*/
> + if (hactive % stream->timing.dsc_cfg.num_slices_h != 0) {
> + ceil_slice_width = (hactive /
> stream->timing.dsc_cfg.num_slices_h) + 1;
> +
> + /* If YCBCR420 slice width must be even */
> + if (stream->timing.pixel_encoding ==
> PIXEL_ENCODING_YCBCR420 && ceil_slice_width % 2 != 0)
> + ceil_slice_width++;
> +
> + pipe_ctx->dsc_padding_params.dsc_hactive_padding =
> + (uint8_t)(ceil_slice_width *
> stream->timing.dsc_cfg.num_slices_h - hactive);
> +
> + if (stream->timing.h_total - hactive -
> pipe_ctx->dsc_padding_params.dsc_hactive_padding < 32)
> +
> pipe_ctx->dsc_padding_params.dsc_hactive_padding = 0;
> +
> +
> dc_update_modified_pix_clock_for_dsc_with_padding(stream, &stream->timing,
> &pipe_ctx->dsc_padding_params);
> + }
> + }
> }
>
> /**
> diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_stream.c
> b/drivers/gpu/drm/amd/display/dc/core/dc_stream.c
> index f694c6ad6e98..c1984ebe0ef1 100644
> --- a/drivers/gpu/drm/amd/display/dc/core/dc_stream.c
> +++ b/drivers/gpu/drm/amd/display/dc/core/dc_stream.c
> @@ -99,7 +99,7 @@ void update_stream_signal(struct dc_stream_state *stream,
> struct dc_sink *sink)
>
> if (stream->link->frl_flags.force_frl_always ||
> stream->link->frl_flags.force_frl_max
> - )
> + || stream->link->frl_flags.force_frl_dsc)
> stream->signal = SIGNAL_TYPE_HDMI_FRL;
> }
> }
> diff --git a/drivers/gpu/drm/amd/display/dc/dc.h
> b/drivers/gpu/drm/amd/display/dc/dc.h
> index 965fa9830355..4c75595a5742 100644
> --- a/drivers/gpu/drm/amd/display/dc/dc.h
> +++ b/drivers/gpu/drm/amd/display/dc/dc.h
> @@ -121,6 +121,12 @@ struct frl_cap_chk_params_fixed31_32 {
>
> bool compressed; /* set to true if DSC is enabled */
> bool bypass_hc_target_calc; /* debug only */
> + bool allow_all_bpp; /* dsc_all_bpp */
> +
> + /* DSC parameters */
> + int slices;
> + int slice_width;
> + struct fixed31_32 bpp_target;
> int layout;
> int acat; /* not supported */
>
> @@ -1151,6 +1157,7 @@ struct dc_debug_options {
> int select_ffe;
> int limit_ffe;
> bool force_frl_always;
> + bool force_frl_dsc;
> bool force_frl_max;
> bool apply_vsdb_rcc_wa;
> bool enable_hdmi_idcc;
> diff --git a/drivers/gpu/drm/amd/display/dc/dc_dsc.h
> b/drivers/gpu/drm/amd/display/dc/dc_dsc.h
> index 101bce6b8de6..c0564167c587 100644
> --- a/drivers/gpu/drm/amd/display/dc/dc_dsc.h
> +++ b/drivers/gpu/drm/amd/display/dc/dc_dsc.h
> @@ -63,11 +63,19 @@ struct dc_dsc_config_options {
> bool force_dsc_when_not_needed;
> };
>
> +struct dc_dsc_primary_bpp {
> + uint32_t vic;
> + uint32_t target_bpp;
> +};
> bool dc_dsc_parse_dsc_dpcd(const struct dc *dc,
> const uint8_t *dpcd_dsc_basic_data,
> const uint8_t *dpcd_dsc_ext_data,
> struct dsc_dec_dpcd_caps *dsc_sink_caps);
>
> +bool dc_dsc_parse_dsc_edid(const struct dc *dc,
> + const struct dc_edid_caps *edid_caps,
> + struct dsc_dec_dpcd_caps *dsc_sink_caps);
> +
> bool dc_dsc_compute_bandwidth_range(
> const struct display_stream_compressor *dsc,
> uint32_t dsc_min_slice_height_override,
> diff --git a/drivers/gpu/drm/amd/display/dc/dc_hdmi_types.h
> b/drivers/gpu/drm/amd/display/dc/dc_hdmi_types.h
> index afd21eb6bbcd..5923a5f112a9 100644
> --- a/drivers/gpu/drm/amd/display/dc/dc_hdmi_types.h
> +++ b/drivers/gpu/drm/amd/display/dc/dc_hdmi_types.h
> @@ -138,6 +138,7 @@ union hdmi_scdc_status_flags_data {
> uint8_t LANE3_LOCKED:1;
> uint8_t RESERVED:1;
> uint8_t FLT_READY:1;
> + uint8_t DSC_DECODEFAIL:1;
> } fields;
> };
>
> @@ -270,6 +271,7 @@ struct dc_hdmi_frl_flags {
> int select_ffe;
> int limit_ffe;
> bool force_frl_always;
> + bool force_frl_dsc;
> bool force_frl_max;
> bool apply_vsdb_rcc_wa;
> };
> diff --git a/drivers/gpu/drm/amd/display/dc/dml/dcn30/dcn30_fpu.c
> b/drivers/gpu/drm/amd/display/dc/dml/dcn30/dcn30_fpu.c
> index 58479bea9976..523d8261be94 100644
> --- a/drivers/gpu/drm/amd/display/dc/dml/dcn30/dcn30_fpu.c
> +++ b/drivers/gpu/drm/amd/display/dc/dml/dcn30/dcn30_fpu.c
> @@ -780,6 +780,38 @@ void hpo_fpu_enc3_validate_hdmi_frl_output_link(struct
> hpo_frl_stream_encoder *e
> break;
> }
>
> + if (timing->flags.DSC &&
> + (unsigned int)frl_link_settings->frl_link_rate >
> dsc_max_rate) {
> + if (dsc_max_rate < HDMI_FRL_LINK_RATE_6GBPS_4LANE) {
> + frl_params->lanes = 3;
> + } else {
> + frl_params->lanes = 4;
> + }
> +
> + switch (dsc_max_rate) {
> + case HDMI_FRL_LINK_RATE_3GBPS:
> + frl_params->r_bit_nominal = 3.0e9;
> + break;
> + case HDMI_FRL_LINK_RATE_6GBPS:
> + case HDMI_FRL_LINK_RATE_6GBPS_4LANE:
> + frl_params->r_bit_nominal = 6.0e9;
> + break;
> + case HDMI_FRL_LINK_RATE_8GBPS:
> + frl_params->r_bit_nominal = 8.0e9;
> + break;
> + case HDMI_FRL_LINK_RATE_10GBPS:
> + default:
> + frl_params->r_bit_nominal = 10.0e9;
> + break;
> + case HDMI_FRL_LINK_RATE_12GBPS:
> + frl_params->r_bit_nominal = 12.0e9;
> + break;
> + }
> + }
> +
> + if (timing->flags.DSC && timing->rid > 0)
> + frl_params->is_ovt = true;
> +
> frl_params->f_pixel_clock_nominal = (double)timing->pix_clk_100hz * 100;
> frl_params->h_active = timing->h_addressable + timing->h_border_left +
> timing->h_border_right;
> frl_params->h_blank = timing->h_total - frl_params->h_active;
> @@ -795,6 +827,12 @@ void hpo_fpu_enc3_validate_hdmi_frl_output_timing(
>
> if (timing->flags.DSC) {
> frl_params->compressed = true;
> + frl_params->slices = timing->dsc_cfg.num_slices_h;
> + frl_params->slice_width = (frl_params->h_active +
> frl_params->slices - 1) / frl_params->slices;
> + // If YCBCR420 slice width must be even
> + if (timing->pixel_encoding == PIXEL_ENCODING_YCBCR420 &&
> frl_params->slice_width % 2 != 0)
> + frl_params->slice_width++;
> + frl_params->bpp_target = timing->dsc_cfg.bits_per_pixel / 16.0;
> } else {
> frl_params->compressed = false;
> }
> @@ -826,5 +864,5 @@ enum frl_cap_chk_result frl_fpu_cap_chk_compressed(struct
> hpo_frl_stream_encoder
> struct
> frl_cap_chk_intermediates *inter)
> {
> (void)enc;
> - return -5;
> + return dml1_frl_cap_chk_compressed(params, inter);
> }
> \ No newline at end of file
> diff --git a/drivers/gpu/drm/amd/display/dc/dml/dcn30/display_mode_vba_30.c
> b/drivers/gpu/drm/amd/display/dc/dml/dcn30/display_mode_vba_30.c
> index b54d24cdfbe2..55db25437952 100644
> --- a/drivers/gpu/drm/amd/display/dc/dml/dcn30/display_mode_vba_30.c
> +++ b/drivers/gpu/drm/amd/display/dc/dml/dcn30/display_mode_vba_30.c
> @@ -3358,6 +3358,9 @@ static double TruncToValidBPP(
> hdmifrlparams.h_active = HActive;
> hdmifrlparams.h_blank = HTotal - HActive;
> hdmifrlparams.compressed = DSCEnable;
> + hdmifrlparams.slices = DSCSlices;
> + hdmifrlparams.slice_width = (int)dml_ceil((double) HActive / DSCSlices,
> 1.0);
> + hdmifrlparams.bpp_target = DesiredBPP;
>
> if (Format == dm_420) {
> NonDSCBPP0 = 12;
> diff --git a/drivers/gpu/drm/amd/display/dc/dml/dcn31/display_mode_vba_31.c
> b/drivers/gpu/drm/amd/display/dc/dml/dcn31/display_mode_vba_31.c
> index 96fdb3f6fa37..3216fd10ad04 100644
> --- a/drivers/gpu/drm/amd/display/dc/dml/dcn31/display_mode_vba_31.c
> +++ b/drivers/gpu/drm/amd/display/dc/dml/dcn31/display_mode_vba_31.c
> @@ -3665,6 +3665,9 @@ static double TruncToValidBPP(
> hdmifrlparams.h_active = HActive;
> hdmifrlparams.h_blank = HTotal - HActive;
> hdmifrlparams.compressed = DSCEnable;
> + hdmifrlparams.slices = DSCSlices;
> + hdmifrlparams.slice_width = (int)dml_ceil((double) HActive / DSCSlices,
> 1.0);
> + hdmifrlparams.bpp_target = DesiredBPP;
>
> if (Format == dm_420) {
> NonDSCBPP0 = 12;
> diff --git a/drivers/gpu/drm/amd/display/dc/dml/dcn314/display_mode_vba_314.c
> b/drivers/gpu/drm/amd/display/dc/dml/dcn314/display_mode_vba_314.c
> index 50c217413abf..2f412cee57c7 100644
> --- a/drivers/gpu/drm/amd/display/dc/dml/dcn314/display_mode_vba_314.c
> +++ b/drivers/gpu/drm/amd/display/dc/dml/dcn314/display_mode_vba_314.c
> @@ -3771,6 +3771,9 @@ static double TruncToValidBPP(
> hdmifrlparams.h_active = HActive;
> hdmifrlparams.h_blank = HTotal - HActive;
> hdmifrlparams.compressed = DSCEnable;
> + hdmifrlparams.slices = DSCSlices;
> + hdmifrlparams.slice_width = (int)dml_ceil((double) HActive / DSCSlices,
> 1.0);
> + hdmifrlparams.bpp_target = DesiredBPP;
>
> if (Format == dm_420) {
> NonDSCBPP0 = 12;
> diff --git
> a/drivers/gpu/drm/amd/display/dc/dml/dcn32/display_mode_vba_util_32.c
> b/drivers/gpu/drm/amd/display/dc/dml/dcn32/display_mode_vba_util_32.c
> index 530f6fdc8df3..2ddc30d08b73 100644
> --- a/drivers/gpu/drm/amd/display/dc/dml/dcn32/display_mode_vba_util_32.c
> +++ b/drivers/gpu/drm/amd/display/dc/dml/dcn32/display_mode_vba_util_32.c
> @@ -1702,6 +1702,9 @@ double dml32_TruncToValidBPP(
> hdmifrlparams.h_blank = HTotal - HActive;
> hdmifrlparams.bpc = (int)(DesiredBPP / 3);
> hdmifrlparams.compressed = DSCEnable;
> + hdmifrlparams.slices = DSCSlices;
> + hdmifrlparams.slice_width = (int)dml_ceil((double) HActive / DSCSlices,
> 1.0);
> + hdmifrlparams.bpp_target = DesiredBPP;
>
> if (Format == dm_420) {
> NonDSCBPP0 = 12;
> diff --git a/drivers/gpu/drm/amd/display/dc/dml/dml1_frl_cap_chk.c
> b/drivers/gpu/drm/amd/display/dc/dml/dml1_frl_cap_chk.c
> index 9dde4e56f237..327d89b66613 100644
> --- a/drivers/gpu/drm/amd/display/dc/dml/dml1_frl_cap_chk.c
> +++ b/drivers/gpu/drm/amd/display/dc/dml/dml1_frl_cap_chk.c
> @@ -430,10 +430,207 @@ enum frl_cap_chk_result
> dml1_frl_cap_chk_uncompressed(struct frl_cap_chk_params
> return FRL_CAP_CHK_OK;
> }
>
> +enum frl_cap_chk_result dml1_frl_cap_chk_compressed(struct
> frl_cap_chk_params *params,
> + struct
> frl_cap_chk_intermediates *inter)
> +{
> + enum frl_cap_chk_result res;
> + int c_frl_available;
> +#if defined(DEBUG_FRL_CAP_CHK)
> + int c_frl_active_available;
> + int c_frl_blank_available;
> +#endif
> + int bytes_target = 0;
> + int hc_active_target;
> + int hc_blank_target_est1;
> + int hc_blank_target_est2;
> + int hc_blank_target = 0;
> + int c_frl_actual_target_payload;
> + double utilization_targeted;
> + double margin_target;
> + double f_tb_average;
> + double t_active_ref;
> + double t_blank_ref;
> + double t_active_target;
> + double t_blank_target;
> + double tb_borrowed;
> +#ifdef DEBUG_FRL_CAP_CHK
> + double tb_delta;
> + double tb_delta_limit;
> + int tb_worst;
> +#endif
> + int table_size_444 = ARRAY_SIZE(prim_format_444);
> + int table_size_422 = ARRAY_SIZE(prim_format_422);
> + int table_size_420 = ARRAY_SIZE(prim_format_420);
> + int i;
> + bool hc_active_blank_predefined = false;
> +
> + dc_assert_fp_enabled();
> +
> + res = dml1_frl_cap_chk_common(inter, params);
> +
> + if (res != FRL_CAP_CHK_OK)
> + return res;
> +
> + c_frl_available = (int)dml_floor((1 - inter->overhead_max) *
> inter->c_frl_line, 1);
> +#if defined(DEBUG_FRL_CAP_CHK)
> + c_frl_active_available = dml_floor(c_frl_available *
> ((double)params->h_active / (params->h_active + params->h_blank)), 1);
> + c_frl_blank_available = dml_floor(c_frl_available *
> ((double)params->h_blank / (params->h_active + params->h_blank)), 1);
> +#endif
> + bytes_target = (int)(params->slices *
> dml_ceil(params->bpp_target * params->slice_width / 8.0, 1));
> +
> + if (!params->bypass_hc_target_calc)
> + hc_active_target = (int)dml_ceil(bytes_target / 3.0, 1);
> + else
> + hc_active_target = params->borrow_params.hc_active_target;
> +
> + if (!params->allow_all_bpp && params->vic != 0) {
> + if (params->pixel_encoding == HDMI_FRL_PIXEL_ENCODING_444) {
> + for (i = 0; i < table_size_444 ; i++) {
> + if (prim_format_444[i].vic == params->vic) {
> + params->borrow_params.hc_active_target
> = prim_format_444[i].hc_active;
> + params->borrow_params.hc_blank_target
> = prim_format_444[i].hc_blank;
> + hc_active_blank_predefined = true;
> + break;
> + }
> + }
> + } else if (params->pixel_encoding ==
> HDMI_FRL_PIXEL_ENCODING_422) {
> + for (i = 0; i < table_size_422 ; i++) {
> + if (prim_format_422[i].vic == params->vic) {
> + params->borrow_params.hc_active_target
> = prim_format_422[i].hc_active;
> + params->borrow_params.hc_blank_target
> = prim_format_422[i].hc_blank;
> + hc_active_blank_predefined = true;
> + break;
> + }
> + }
> + } else if (params->pixel_encoding ==
> HDMI_FRL_PIXEL_ENCODING_420) {
> + for (i = 0; i < table_size_420 ; i++) {
> + if (prim_format_420[i].vic == params->vic) {
> + params->borrow_params.hc_active_target
> = prim_format_420[i].hc_active;
> + params->borrow_params.hc_blank_target
> = prim_format_420[i].hc_blank;
> + hc_active_blank_predefined = true;
> + break;
> + }
> + }
> + }
> +
> + if (hc_active_blank_predefined) {
> + hc_active_target =
> params->borrow_params.hc_active_target;
> + hc_blank_target = params->borrow_params.hc_blank_target;
> + }
> + }
> +
> + hc_blank_target_est1 = (int)dml_ceil(hc_active_target *
> ((double)params->h_blank / params->h_active), 1);
> + hc_blank_target_est2 = (int)dml_max(hc_blank_target_est1,
> inter->blank_audio_min);
> +
> + if (!hc_active_blank_predefined) {
> + if (!params->bypass_hc_target_calc) {
> + hc_blank_target = (int)(4 *
> dml_floor(dml_min(hc_blank_target_est2, c_frl_available - 3.0 / 2.0 *
> hc_active_target) / 4.0, 1));
> +
> + params->borrow_params.hc_active_target =
> hc_active_target;
> + params->borrow_params.hc_blank_target =
> hc_blank_target;
> + } else {
> + hc_blank_target =
> params->borrow_params.hc_blank_target;
> + }
> + }
> +
> +#ifdef DEBUG_FRL_CAP_CHK
> + {
> + frl_dump_var("%i", c_frl_available);
> + frl_dump_var("%i", c_frl_active_available);
> + frl_dump_var("%i", c_frl_blank_available);
> + frl_dump_var("%i", bytes_target);
> + frl_dump_var("%i", hc_active_target);
> + frl_dump_var("%i", hc_blank_target_est1);
> + frl_dump_var("%i", hc_blank_target_est2);
> + frl_dump_var("%i", hc_blank_target);
> + }
> +#endif
> +
> + if (!(inter->blank_audio_min <= hc_blank_target)) {
> + frl_dump_var("%i", inter->blank_audio_min);
> + frl_dump_var("%i", hc_blank_target);
> + return FRL_CAP_CHK_ERROR_AUDIO_BW;
> + }
> +
> + f_tb_average = inter->f_pixel_clock_max / (params->h_active +
> params->h_blank) * (hc_active_target + hc_blank_target);
> + t_active_ref = inter->t_line * ((double)params->h_active /
> (params->h_active + params->h_blank));
> + t_blank_ref = inter->t_line - t_active_ref; // * ((double)
> params->h_blank / (params->h_active + params->h_blank));
> + t_active_target = dml_max((hc_active_target / f_tb_average),
> + (3.0 / 2.0 * hc_active_target) /
> + (params->lanes * inter->r_frl_char_min * (1.0
> - inter->overhead_max)));
> + t_blank_target = inter->t_line - t_active_target;
> +
> + tb_borrowed = t_active_target * f_tb_average - hc_active_target;
> +#ifdef DEBUG_FRL_CAP_CHK
> + tb_delta = dcn_bw_fabs(t_active_target - t_active_ref) *
> (hc_active_target + hc_blank_target_est1) / inter->t_line;
> +
> + {
> + frl_dump_var("%le", f_tb_average);
> + frl_dump_var("%le", t_active_ref);
> + frl_dump_var("%le", t_blank_ref);
> + frl_dump_var("%le", t_active_target);
> + frl_dump_var("%le", t_blank_target);
> + frl_dump_var("%le", tb_delta);
> + }
> +#endif
> +
> + if (t_blank_target - t_blank_ref > DBL_EPSILON) {
> +#ifdef DEBUG_FRL_CAP_CHK
> + tb_delta_limit = (t_active_ref - hc_active_target /
> f_tb_average) * (hc_active_target + hc_blank_target_est1) / inter->t_line;
> +#endif
> + params->borrow_params.borrow_mode = FRL_BORROW_MODE_FROM_ACTIVE;
> + } else if (t_active_target - t_active_ref > DBL_EPSILON) {
> +#ifdef DEBUG_FRL_CAP_CHK
> + tb_delta_limit = tb_delta;
> +#endif
> + params->borrow_params.borrow_mode = FRL_BORROW_MODE_FROM_BLANK;
> + } else {
> +#ifdef DEBUG_FRL_CAP_CHK
> + tb_delta_limit = 0;
> +#endif
> + params->borrow_params.borrow_mode = FRL_BORROW_MODE_NONE;
> + }
> +
> +#ifdef DEBUG_FRL_CAP_CHK
> + tb_worst = dml_ceil(dml_max(tb_borrowed, tb_delta_limit), 1);
> +
> + {
> + frl_dump_var("%le", tb_delta_limit);
> + frl_dump_var("%le", tb_borrowed);
> + frl_dump_var("%i", params->borrow_params.borrow_mode);
> + frl_dump_var("%i", tb_worst);
> + }
> +#endif
> +
> + if (!(tb_borrowed <= TB_BORROWED_MAX))
> + return FRL_CAP_CHK_ERROR_MAX_BORROW;
> +
> + c_frl_actual_target_payload = (int)(dml_ceil(3.0 / 2.0 *
> hc_active_target, 1) + hc_blank_target);
> + utilization_targeted = c_frl_actual_target_payload /
> inter->c_frl_line;
> + margin_target = 1.0 - (utilization_targeted +
> inter->overhead_max);
> +
> +#ifdef DEBUG_FRL_CAP_CHK
> + {
> + frl_dump_var("%i", c_frl_actual_target_payload);
> + frl_dump_var("%le", utilization_targeted);
> + frl_dump_var("%le", margin_target);
> + }
> +#endif
> +
> + // oversubscribed bandwidth relative to margin
> + if (margin_target < 0 && dcn_bw_fabs(margin_target) > EPSILON)
> + return FRL_CAP_CHK_ERROR_MARGIN;
> +
> + return FRL_CAP_CHK_OK;
> +}
> +
> enum frl_cap_chk_result dml1_frl_cap_chk(struct frl_cap_chk_params *params)
> {
> struct frl_cap_chk_intermediates inter;
>
> + if (params->compressed)
> + return dml1_frl_cap_chk_compressed(params, &inter);
> +
> return dml1_frl_cap_chk_inter(params, &inter);
> }
>
> diff --git a/drivers/gpu/drm/amd/display/dc/dml/dml1_frl_cap_chk.h
> b/drivers/gpu/drm/amd/display/dc/dml/dml1_frl_cap_chk.h
> index 545f498ea396..debe4c1dc0f7 100644
> --- a/drivers/gpu/drm/amd/display/dc/dml/dml1_frl_cap_chk.h
> +++ b/drivers/gpu/drm/amd/display/dc/dml/dml1_frl_cap_chk.h
> @@ -119,6 +119,13 @@ struct frl_cap_chk_params {
>
> bool compressed; /* set to true if DSC is enabled */
> bool bypass_hc_target_calc; /* debug only */
> + bool allow_all_bpp; /* dsc_all_bpp */
> +
> + /* DSC parameters */
> + int slices;
> + int slice_width;
> + double bpp_target;
> + bool is_ovt;
> int layout;
> int acat; /* not supported */
>
> @@ -138,6 +145,8 @@ enum frl_cap_chk_result dml1_frl_cap_chk_common(struct
> frl_cap_chk_intermediates
> enum frl_cap_chk_result dml1_frl_cap_chk_uncompressed(struct
> frl_cap_chk_params *params,
> struct
> frl_cap_chk_intermediates *inter);
>
> +enum frl_cap_chk_result dml1_frl_cap_chk_compressed(struct
> frl_cap_chk_params *params,
> + struct
> frl_cap_chk_intermediates *inter);
> #endif
>
> void frl_modified_pix_clock_for_dsc_padding(const int hc_active_target,
> diff --git a/drivers/gpu/drm/amd/display/dc/dsc/dc_dsc.c
> b/drivers/gpu/drm/amd/display/dc/dsc/dc_dsc.c
> index 8dfb6dd14eb2..c0f489ccf5e1 100644
> --- a/drivers/gpu/drm/amd/display/dc/dsc/dc_dsc.c
> +++ b/drivers/gpu/drm/amd/display/dc/dsc/dc_dsc.c
> @@ -151,6 +151,166 @@ uint32_t dc_bandwidth_in_kbps_from_timing(
> return kbps;
> }
>
> +const struct dc_dsc_primary_bpp prim_bpp_444[] = {
> + /* VIC/BPP */
> + {64, 192}, /* 1920x1080 @ 100 */
> + {77, 192}, /* 1920x1080 @ 100 */
> + {63, 192}, /* 1920x1080 @ 120 */
> + {78, 192}, /* 1920x1080 @ 120 */
> + {93, 192}, /* 3840x2160 @ 24 */
> + {103, 192}, /* 3840x2160 @ 24 */
> + {94, 192}, /* 3840x2160 @ 25 */
> + {104, 192}, /* 3840x2160 @ 25 */
> + {95, 192}, /* 3840x2160 @ 30 */
> + {105, 192}, /* 3840x2160 @ 30 */
> + {114, 192}, /* 3840x2160 @ 48 */
> + {116, 192}, /* 3840x2160 @ 48 */
> + {96, 192}, /* 3840x2160 @ 50 */
> + {106, 192}, /* 3840x2160 @ 50 */
> + {97, 192}, /* 3840x2160 @ 60 */
> + {107, 192}, /* 3840x2160 @ 60 */
> + {117, 192}, /* 3840x2160 @ 100 */
> + {119, 192}, /* 3840x2160 @ 100 */
> + {118, 192}, /* 3840x2160 @ 120 */
> + {120, 192}, /* 3840x2160 @ 120 */
> + {98, 192}, /* 4096x2160 @ 24 */
> + {99, 192}, /* 4096x2160 @ 25 */
> + {100, 192}, /* 4096x2160 @ 30 */
> + {115, 192}, /* 4096x2160 @ 48 */
> + {101, 192}, /* 4096x2160 @ 50 */
> + {102, 192}, /* 4096x2160 @ 60 */
> + {218, 192}, /* 4096x2160 @ 100 */
> + {219, 192}, /* 4096x2160 @ 120 */
> + {121, 192}, /* 5120x2160 @ 24 */
> + {122, 192}, /* 5120x2160 @ 25 */
> + {123, 192}, /* 5120x2160 @ 30 */
> + {124, 192}, /* 5120x2160 @ 48 */
> + {125, 192}, /* 5120x2160 @ 50 */
> + {126, 173}, /* 5120x2160 @ 60 */
> + {127, 192}, /* 5120x2160 @ 100 */
> + {193, 175}, /* 5120x2160 @ 120 */
> + {194, 192}, /* 7680x2160 @ 24 */
> + {202, 192}, /* 7680x2160 @ 24 */
> + {195, 192}, /* 7680x2160 @ 25 */
> + {203, 192}, /* 7680x2160 @ 25 */
> + {196, 192}, /* 7680x2160 @ 30 */
> + {204, 192}, /* 7680x2160 @ 30 */
> + {197, 157}, /* 7680x2160 @ 48 */
> + {205, 157}, /* 7680x2160 @ 48 */
> + {198, 157}, /* 7680x2160 @ 50 */
> + {206, 157}, /* 7680x2160 @ 50 */
> + {199, 159}, /* 7680x2160 @ 60 */
> + {207, 159}, /* 7680x2160 @ 60 */
> + {200, 134}, /* 7680x2160 @ 100 */
> + {208, 134}, /* 7680x2160 @ 100 */
> + {201, 130}, /* 7680x2160 @ 120 */
> + {209, 130}, /* 7680x2160 @ 120 */
> + {210, 182}, /* 10240x4320 @ 24 */
> + {211, 181}, /* 10240x4320 @ 25 */
> + {212, 177}, /* 10240x4320 @ 30 */
> + {213, 163}, /* 10240x4320 @ 48 */
> + {214, 162}, /* 10240x4320 @ 50 */
> + {215, 157}, /* 10240x4320 @ 60 */
> +};
> +const struct dc_dsc_primary_bpp prim_bpp_422[] = {
> + /* VIC/BPP */
> + {114, 192}, /* 3840x2160 @ 48 */
> + {116, 192}, /* 3840x2160 @ 48 */
> + {96, 192}, /* 3840x2160 @ 50 */
> + {106, 192}, /* 3840x2160 @ 50 */
> + {97, 192}, /* 3840x2160 @ 60 */
> + {107, 192}, /* 3840x2160 @ 60 */
> + {117, 137}, /* 3840x2160 @ 100 */
> + {119, 137}, /* 3840x2160 @ 100 */
> + {118, 113}, /* 3840x2160 @ 120 */
> + {120, 113}, /* 3840x2160 @ 120 */
> + {115, 192}, /* 4096x2160 @ 48 */
> + {101, 192}, /* 4096x2160 @ 50 */
> + {102, 192}, /* 4096x2160 @ 60 */
> + {218, 192}, /* 4096x2160 @ 100 */
> + {219, 192}, /* 4096x2160 @ 120 */
> + {121, 192}, /* 5120x2160 @ 24 */
> + {122, 192}, /* 5120x2160 @ 25 */
> + {123, 192}, /* 5120x2160 @ 30 */
> + {124, 192}, /* 5120x2160 @ 48 */
> + {125, 192}, /* 5120x2160 @ 50 */
> + {126, 173}, /* 5120x2160 @ 60 */
> + {127, 192}, /* 5120x2160 @ 100 */
> + {193, 175}, /* 5120x2160 @ 120 */
> + {194, 123}, /* 7680x2160 @ 24 */
> + {202, 123}, /* 7680x2160 @ 24 */
> + {195, 123}, /* 7680x2160 @ 25 */
> + {203, 123}, /* 7680x2160 @ 25 */
> + {196, 118}, /* 7680x2160 @ 30 */
> + {204, 118}, /* 7680x2160 @ 30 */
> + {197, 123}, /* 7680x2160 @ 48 */
> + {205, 123}, /* 7680x2160 @ 48 */
> + {198, 123}, /* 7680x2160 @ 50 */
> + {206, 123}, /* 7680x2160 @ 50 */
> + {199, 119}, /* 7680x2160 @ 60 */
> + {207, 119}, /* 7680x2160 @ 60 */
> + {200, 134}, /* 7680x2160 @ 100 */
> + {208, 134}, /* 7680x2160 @ 100 */
> + {201, 130}, /* 7680x2160 @ 120 */
> + {209, 130}, /* 7680x2160 @ 120 */
> + {210, 182}, /* 10240x4320 @ 24 */
> + {211, 181}, /* 10240x4320 @ 25 */
> + {212, 177}, /* 10240x4320 @ 30 */
> + {213, 126}, /* 10240x4320 @ 48 */
> + {214, 125}, /* 10240x4320 @ 50 */
> + {215, 117}, /* 10240x4320 @ 60 */
> + {216, 125}, /* 10240x4320 @ 100 */
> + {217, 117}, /* 10240x4320 @ 120 */
> +};
> +
> +const struct dc_dsc_primary_bpp prim_bpp_420[] = {
> + /* VIC/BPP */
> + {114, 192}, /* 3840x2160 @ 48 */
> + {116, 192}, /* 3840x2160 @ 48 */
> + {96, 192}, /* 3840x2160 @ 50 */
> + {106, 192}, /* 3840x2160 @ 50 */
> + {97, 192}, /* 3840x2160 @ 60 */
> + {107, 192}, /* 3840x2160 @ 60 */
> + {117, 137}, /* 3840x2160 @ 100 */
> + {119, 137}, /* 3840x2160 @ 100 */
> + {118, 113}, /* 3840x2160 @ 120 */
> + {120, 113}, /* 3840x2160 @ 120 */
> + {115, 192}, /* 4096x2160 @ 48 */
> + {101, 192}, /* 4096x2160 @ 50 */
> + {102, 192}, /* 4096x2160 @ 60 */
> + {218, 129}, /* 4096x2160 @ 100 */
> + {219, 106}, /* 4096x2160 @ 120 */
> + {124, 192}, /* 5120x2160 @ 48 */
> + {125, 192}, /* 5120x2160 @ 50 */
> + {126, 173}, /* 5120x2160 @ 60 */
> + {127, 192}, /* 5120x2160 @ 100 */
> + {193, 175}, /* 5120x2160 @ 120 */
> + {194, 123}, /* 7680x4320 @ 24 */
> + {202, 123}, /* 7680x4320 @ 24 */
> + {195, 123}, /* 7680x4320 @ 25 */
> + {203, 123}, /* 7680x4320 @ 25 */
> + {196, 118}, /* 7680x4320 @ 30 */
> + {204, 118}, /* 7680x4320 @ 30 */
> + {197, 123}, /* 7680x4320 @ 48 */
> + {205, 123}, /* 7680x4320 @ 48 */
> + {198, 123}, /* 7680x4320 @ 50 */
> + {206, 123}, /* 7680x4320 @ 50 */
> + {199, 119}, /* 7680x4320 @ 60 */
> + {207, 119}, /* 7680x4320 @ 60 */
> + {200, 112}, /* 7680x4320 @ 100 */
> + {208, 112}, /* 7680x4320 @ 100 */
> + {201, 103}, /* 7680x4320 @ 120 */
> + {209, 103}, /* 7680x4320 @ 120 */
> + {210, 98}, /* 10240x4320 @ 24 */
> + {211, 98}, /* 10240x4320 @ 25 */
> + {212, 177}, /* 10240x4320 @ 30 */
> + {213, 98}, /* 10240x4320 @ 48 */
> + {214, 125}, /* 10240x4320 @ 50 */
> + {215, 117}, /* 10240x4320 @ 60 */
> + {216, 107}, /* 10240x4320 @ 100 */
> + {217, 97}, /* 10240x4320 @ 120 */
> +};
> +
> /* Forward Declerations */
> static unsigned int get_min_dsc_slice_count_for_odm(
> const struct display_stream_compressor *dsc,
> @@ -194,6 +354,15 @@ static bool setup_dsc_config(
> int min_slice_count,
> struct dc_dsc_config *dsc_cfg);
>
> +static bool convert_bandwidth_to_frl_params(
> + int bandwidth_kbps,
> + int *num_lanes,
> + int *frl_rate);
> +
> +static uint32_t compute_bpp_x16_from_frl_params(
> + const struct dc_crtc_timing *timing,
> + const uint32_t num_slices_h,
> + const struct dsc_enc_caps *dsc_caps);
> static bool dsc_buff_block_size_from_dpcd(int dpcd_buff_block_size, int
> *buff_block_size)
> {
>
> @@ -328,6 +497,207 @@ static bool dsc_bpp_increment_div_from_dpcd(uint8_t
> bpp_increment_dpcd, uint32_t
>
>
>
> +static bool get_vic_preset_bpp(
> + const struct dc_crtc_timing *timing,
> + int *preset_bpp)
> +{
> + bool preset_found = false;
> + uint32_t table_size_444 = ARRAY_SIZE(prim_bpp_444);
> + uint32_t table_size_422 = ARRAY_SIZE(prim_bpp_422);
> + uint32_t table_size_420 = ARRAY_SIZE(prim_bpp_420);
> + uint32_t i;
> + uint32_t vid_id;
> +
> + if (timing->vic == 0 && timing->hdmi_vic == 0)
> + return false;
> +
> + vid_id = timing->vic;
> + switch (timing->hdmi_vic) {
> + case 1:
> + vid_id = 95;
> + break;
> + case 2:
> + vid_id = 94;
> + break;
> + case 3:
> + vid_id = 93;
> + break;
> + case 4:
> + vid_id = 98;
> + break;
> + default:
> + break;
> + }
> +
> + if (timing->pixel_encoding == PIXEL_ENCODING_RGB ||
> + timing->pixel_encoding == PIXEL_ENCODING_YCBCR444) {
> + for (i = 0; i < table_size_444 ; i++) {
> + if (prim_bpp_444[i].vic == vid_id) {
> + preset_found = true;
> + *preset_bpp = prim_bpp_444[i].target_bpp;
> + break;
> + }
> + }
> + } else if (timing->pixel_encoding == PIXEL_ENCODING_YCBCR422) {
> + for (i = 0; i < table_size_422 ; i++) {
> + if (prim_bpp_422[i].vic == vid_id) {
> + preset_found = true;
> + *preset_bpp = prim_bpp_422[i].target_bpp;
> + break;
> + }
> + }
> + } else if (timing->pixel_encoding == PIXEL_ENCODING_YCBCR420) {
> + for (i = 0; i < table_size_420 ; i++) {
> + if (prim_bpp_420[i].vic == vid_id) {
> + preset_found = true;
> + *preset_bpp = prim_bpp_420[i].target_bpp;
> + break;
> + }
> + }
> + } else {
> + return false;
> + }
> +
> + return preset_found;
> +}
> +
> +static int hdmi_dsc_get_num_slices(const struct dc_crtc_timing *timing)
> +{
> + int k_slice_adjust = 1;
> + int adj_pix_clk_mhz;
> + int min_slices;
> + int slice_target;
> + int slice_width = timing->h_addressable;
> + int h_ratio_adj_pix_clk_mhz;
> +
> + if (timing->pixel_encoding == PIXEL_ENCODING_RGB ||
> + timing->pixel_encoding == PIXEL_ENCODING_YCBCR444)
> + k_slice_adjust = 2;
> +
> + adj_pix_clk_mhz = k_slice_adjust * timing->pix_clk_100hz / 10000 / 2;
> + h_ratio_adj_pix_clk_mhz = adj_pix_clk_mhz * timing->h_addressable /
> timing->h_total;
> + if (adj_pix_clk_mhz <= 2720) {
> + min_slices = adj_pix_clk_mhz / 340;
> + if (adj_pix_clk_mhz % 340 != 0)
> + min_slices++;
> + } else if (adj_pix_clk_mhz <= 4800) {
> + min_slices = adj_pix_clk_mhz / 400;
> + if (adj_pix_clk_mhz % 400 != 0)
> + min_slices++;
> + } else if (h_ratio_adj_pix_clk_mhz <= 4800) {
> + min_slices = h_ratio_adj_pix_clk_mhz / 600;
> + if (h_ratio_adj_pix_clk_mhz % 600 != 0)
> + min_slices++;
> + } else {
> + min_slices = h_ratio_adj_pix_clk_mhz / 900;
> + if (h_ratio_adj_pix_clk_mhz % 900 != 0)
> + min_slices++;
> + }
> +
> + do {
> + if (min_slices <= 1)
> + slice_target = 1;
> + else if (min_slices <= 2)
> + slice_target = 2;
> + else if (min_slices <= 4)
> + slice_target = 4;
> + else if (min_slices <= 8)
> + slice_target = 8;
> + else if (min_slices <= 12)
> + slice_target = 12;
> + else if (min_slices <= 16)
> + slice_target = 16;
> + else
> + return 0;
> +
> + slice_width = timing->h_addressable / slice_target;
> + min_slices++;
> + } while (slice_width > 2720);
> +
> + return slice_target;
> +}
> +
> +static int hdmi_dsc_get_bpp(const struct dc_crtc_timing *timing,
> + const struct dsc_enc_caps *dsc_common_caps)
> +{
> + int max_dsc_bpp, min_dsc_bpp;
> + int target_bytes;
> + bool bpp_found = false;
> + int bpp_decrement_x16;
> + int src_fractional_bpp = dsc_common_caps->bpp_increment_div;
> + int bpp_target;
> + int bpp_target_x16;
> + int bpc_factor = 8;
> + int slice_width;
> + int num_slices;
> + bool hdmi_all_bpp = dsc_common_caps->is_vic_all_bpp;
> + int hdmi_max_chunk_bytes = dsc_common_caps->total_chunk_kbytes;
> +
> + int preset_bpp;
> + bool preset_found = false;
> +
> + if (timing->display_color_depth == COLOR_DEPTH_101010)
> + bpc_factor = 10;
> + if (timing->display_color_depth == COLOR_DEPTH_121212)
> + bpc_factor = 12;
> +
> + /* Assuming: bpc as 8*/
> + if (timing->pixel_encoding == PIXEL_ENCODING_RGB ||
> + timing->pixel_encoding == PIXEL_ENCODING_YCBCR444) {
> + min_dsc_bpp = 8;
> + max_dsc_bpp = 3 * bpc_factor;
> + } else if (timing->pixel_encoding == PIXEL_ENCODING_YCBCR422) {
> + min_dsc_bpp = 7;
> + max_dsc_bpp = 2 * bpc_factor;
> + } else {
> + min_dsc_bpp = 6;
> + max_dsc_bpp = 3 * bpc_factor / 2;
> + }
> +
> + if (!hdmi_all_bpp)
> + max_dsc_bpp = min(max_dsc_bpp, 12);
> +
> +
> + num_slices = hdmi_dsc_get_num_slices(timing);
> + if (num_slices == 0)
> + return 0;
> +
> + slice_width = timing->h_addressable / num_slices;
> +
> + bpp_target = max_dsc_bpp;
> + bpp_decrement_x16 = 16 / src_fractional_bpp;
> + bpp_target_x16 = (bpp_target * 16) - bpp_decrement_x16;
> + if (!hdmi_all_bpp)
> + bpp_target_x16 = (bpp_target * 16);
> +
> + /* check if byte target is below allowed Kbytes */
> + while (bpp_target_x16 > (min_dsc_bpp * 16)) {
> + target_bytes = num_slices * slice_width * bpp_target_x16 / 16 /
> 8;
> + if (target_bytes <= hdmi_max_chunk_bytes) {
> + bpp_found = true;
> + break;
> + }
> + bpp_target_x16 -= bpp_decrement_x16;
> + }
> +
> + if (bpp_found) {
> + if (!hdmi_all_bpp) {
> + /* Get preset bpp for CTA modes */
> + preset_found = get_vic_preset_bpp(timing, &preset_bpp);
> + if (preset_found) {
> + bpp_target_x16 = preset_bpp;
> + target_bytes =
> + num_slices * slice_width *
> bpp_target_x16 / 16 / 8;
> + if (target_bytes > hdmi_max_chunk_bytes)
> + return 0;
> + }
> + }
> + return bpp_target_x16;
> + }
> +
> + return 0;
> +}
> +
> bool dc_dsc_parse_dsc_dpcd(const struct dc *dc,
> const uint8_t *dpcd_dsc_basic_data,
> const uint8_t *dpcd_dsc_branch_decoder_caps,
> @@ -450,6 +820,104 @@ bool dc_dsc_parse_dsc_dpcd(const struct dc *dc,
> dsc_sink_caps->is_dp = true;
> return true;
> }
> +bool dc_dsc_parse_dsc_edid(const struct dc *dc, const struct dc_edid_caps
> *edid_caps,
> + struct dsc_dec_dpcd_caps *dsc_sink_caps)
> +{
> + (void)dc;
> + dsc_sink_caps->is_dsc_supported = edid_caps->frl_dsc_support;
> + if (!edid_caps->frl_dsc_support)
> + return false;
> +
> + dsc_sink_caps->dsc_version = 0x21;
> + dsc_sink_caps->is_frl = true;
> + dsc_sink_caps->is_dp = false;
> +
> + switch (edid_caps->frl_dsc_max_slices) {
> + case 0:
> + break;
> + case 1:
> + dsc_sink_caps->slice_caps1.bits.NUM_SLICES_1 = 1;
> + dsc_sink_caps->throughput_mode_0_mps = 340;
> + dsc_sink_caps->throughput_mode_1_mps = 680;
> + break;
> + case 2:
> + dsc_sink_caps->slice_caps1.bits.NUM_SLICES_1 = 1;
> + dsc_sink_caps->slice_caps1.bits.NUM_SLICES_2 = 1;
> + dsc_sink_caps->throughput_mode_0_mps = 340;
> + dsc_sink_caps->throughput_mode_1_mps = 680;
> + break;
> + case 3:
> + dsc_sink_caps->slice_caps1.bits.NUM_SLICES_1 = 1;
> + dsc_sink_caps->slice_caps1.bits.NUM_SLICES_2 = 1;
> + dsc_sink_caps->slice_caps1.bits.NUM_SLICES_4 = 1;
> + dsc_sink_caps->throughput_mode_0_mps = 340;
> + dsc_sink_caps->throughput_mode_1_mps = 680;
> + break;
> + case 4:
> + dsc_sink_caps->slice_caps1.bits.NUM_SLICES_1 = 1;
> + dsc_sink_caps->slice_caps1.bits.NUM_SLICES_2 = 1;
> + dsc_sink_caps->slice_caps1.bits.NUM_SLICES_4 = 1;
> + dsc_sink_caps->slice_caps1.bits.NUM_SLICES_8 = 1;
> + dsc_sink_caps->throughput_mode_0_mps = 340;
> + dsc_sink_caps->throughput_mode_1_mps = 680;
> + break;
> + case 5:
> + dsc_sink_caps->slice_caps1.bits.NUM_SLICES_1 = 1;
> + dsc_sink_caps->slice_caps1.bits.NUM_SLICES_2 = 1;
> + dsc_sink_caps->slice_caps1.bits.NUM_SLICES_4 = 1;
> + dsc_sink_caps->slice_caps1.bits.NUM_SLICES_8 = 1;
> + dsc_sink_caps->throughput_mode_0_mps = 400;
> + dsc_sink_caps->throughput_mode_1_mps = 800;
> + break;
> + case 6:
> + dsc_sink_caps->slice_caps1.bits.NUM_SLICES_1 = 1;
> + dsc_sink_caps->slice_caps1.bits.NUM_SLICES_2 = 1;
> + dsc_sink_caps->slice_caps1.bits.NUM_SLICES_4 = 1;
> + dsc_sink_caps->slice_caps1.bits.NUM_SLICES_8 = 1;
> + dsc_sink_caps->slice_caps1.bits.NUM_SLICES_12 = 1;
> + dsc_sink_caps->throughput_mode_0_mps = 400;
> + dsc_sink_caps->throughput_mode_1_mps = 800;
> + break;
> + case 7:
> + dsc_sink_caps->slice_caps1.bits.NUM_SLICES_1 = 1;
> + dsc_sink_caps->slice_caps1.bits.NUM_SLICES_2 = 1;
> + dsc_sink_caps->slice_caps1.bits.NUM_SLICES_4 = 1;
> + dsc_sink_caps->slice_caps1.bits.NUM_SLICES_8 = 1;
> + dsc_sink_caps->slice_caps1.bits.NUM_SLICES_12 = 1;
> + dsc_sink_caps->throughput_mode_0_mps = 600;
> + dsc_sink_caps->throughput_mode_1_mps = 1200;
> + break;
> + case 8:
> + default:
> + dsc_sink_caps->slice_caps1.bits.NUM_SLICES_1 = 1;
> + dsc_sink_caps->slice_caps1.bits.NUM_SLICES_2 = 1;
> + dsc_sink_caps->slice_caps1.bits.NUM_SLICES_4 = 1;
> + dsc_sink_caps->slice_caps1.bits.NUM_SLICES_8 = 1;
> + dsc_sink_caps->slice_caps1.bits.NUM_SLICES_12 = 1;
> + dsc_sink_caps->throughput_mode_0_mps = 900;
> + dsc_sink_caps->throughput_mode_1_mps = 1800;
> + break;
> + }
> + dsc_sink_caps->lb_bit_depth = 13; //Table 7-25
> + dsc_sink_caps->is_block_pred_supported = true; //Table 7-25
> + dsc_sink_caps->color_formats.bits.RGB = 1;
> + dsc_sink_caps->color_formats.bits.YCBCR_444 = 1;
> + dsc_sink_caps->color_formats.bits.YCBCR_NATIVE_422 = 1;
> + dsc_sink_caps->color_formats.bits.YCBCR_NATIVE_420 =
> + (edid_caps->frl_dsc_native_420 == true) ? 1 : 0;
> + dsc_sink_caps->color_depth.bits.COLOR_DEPTH_8_BPC = 1;
> + dsc_sink_caps->color_depth.bits.COLOR_DEPTH_10_BPC =
> + (edid_caps->frl_dsc_10bpc == true) ? 1 : 0;
> + dsc_sink_caps->color_depth.bits.COLOR_DEPTH_12_BPC =
> + (edid_caps->frl_dsc_12bpc == true) ? 1 : 0;
> + dsc_sink_caps->max_slice_width = 2560;
> + dsc_sink_caps->bpp_increment_div = 16; /* bpp increment divisor, e.g.
> if 16, it's 1/16th of a bit */
> + dsc_sink_caps->is_vic_all_bpp = edid_caps->frl_dsc_all_bpp;
> + dsc_sink_caps->total_chunk_kbytes =
> + 1024 * (1 + edid_caps->frl_dsc_total_chunk_kbytes);
> +
> + return true;
> +}
>
> /* If DSC is possbile, get DSC bandwidth range based on [min_bpp, max_bpp]
> target bitrate range and
> * timing's pixel clock and uncompressed bandwidth.
> @@ -757,6 +1225,9 @@ static bool intersect_dsc_caps(
> if (pixel_encoding == PIXEL_ENCODING_YCBCR422 || pixel_encoding ==
> PIXEL_ENCODING_YCBCR420)
> dsc_common_caps->bpp_increment_div =
> min(dsc_common_caps->bpp_increment_div, (uint32_t)8);
>
> + dsc_common_caps->is_frl = dsc_sink_caps->is_frl;
> + dsc_common_caps->is_vic_all_bpp = dsc_sink_caps->is_vic_all_bpp;
> + dsc_common_caps->total_chunk_kbytes = dsc_sink_caps->total_chunk_kbytes;
> dsc_common_caps->edp_sink_max_bits_per_pixel =
> dsc_sink_caps->edp_max_bits_per_pixel;
> dsc_common_caps->is_dp = dsc_sink_caps->is_dp;
> return true;
> @@ -786,6 +1257,191 @@ static uint32_t compute_bpp_x16_from_target_bandwidth(
> return dc_fixpt_floor(bpp_x16);
> }
>
> +static bool convert_bandwidth_to_frl_params(
> + int bandwidth_kbps,
> + int *num_lanes,
> + int *frl_rate)
> +{
> + if (bandwidth_kbps == 0)
> + return false;
> +
> + switch (bandwidth_kbps) {
> + case 9000000:
> + *num_lanes = 3;
> + *frl_rate = 3000;
> + break;
> + case 18000000:
> + *num_lanes = 3;
> + *frl_rate = 6000;
> + break;
> + case 24000000:
> + *num_lanes = 4;
> + *frl_rate = 6000;
> + break;
> + case 32000000:
> + *num_lanes = 4;
> + *frl_rate = 8000;
> + break;
> + case 40000000:
> + *num_lanes = 4;
> + *frl_rate = 10000;
> + break;
> + case 48000000:
> + *num_lanes = 4;
> + *frl_rate = 12000;
> + break;
> + default:
> + return false;
> + }
> + return true;
> +}
> +
> +static uint32_t compute_bpp_x16_from_frl_params(
> + const struct dc_crtc_timing *timing,
> + const uint32_t num_slices_h,
> + const struct dsc_enc_caps *dsc_caps)
> +{
> + struct fixed31_32 pixel_clock;
> + uint32_t num_lanes = dsc_caps->num_lanes;
> + uint32_t frl_rate = dsc_caps->frl_rate;
> + uint32_t h_active = timing->h_addressable;
> + uint32_t h_blank = timing->h_total - timing->h_addressable;
> + uint32_t bpp_target_x16;
> + struct fixed31_32 r_bit;
> + uint32_t f_audio = 48000;
> + struct fixed31_32 pixel_rate_tolerance;
> + uint32_t audio_tolerance = 1000;
> + uint32_t frl_bit_tolerance = 300;
> + uint32_t acr_rate_max = 1500;
> + uint32_t c_frl_cb = 510;
> + uint32_t c_frl_sb;
> + struct fixed31_32 overhead_sb;
> + struct fixed31_32 overhead_rs;
> + struct fixed31_32 overhead_map;
> + struct fixed31_32 overhead_min;
> + struct fixed31_32 overhead_m;
> + struct fixed31_32 overhead_max;
> + struct fixed31_32 pixel_clock_max;
> + struct fixed31_32 t_line;
> + struct fixed31_32 r_bit_min;
> + struct fixed31_32 r_frl_char_min;
> + struct fixed31_32 c_frl_line;
> + uint32_t c_frl_line_int;
> + struct fixed31_32 c_frl_available;
> + uint32_t c_frl_av_int;
> + struct fixed31_32 c_frl_active_av;
> + struct fixed31_32 c_frl_blank_av;
> + uint32_t acat_ap = 4;
> + struct fixed31_32 r_ap;
> + struct fixed31_32 max_audio_tol_rate;
> + struct fixed31_32 avg_audio_packets_line;
> + uint32_t avg_audio_packets_line_int;
> + int hc_blank_audio_min;
> + uint32_t bytes_target;
> + uint32_t hc_active_target;
> + uint32_t hc_blank_target_est1;
> + uint32_t hc_blank_target_est2;
> + struct fixed31_32 hc_blank_target_bandwidth;
> + int hc_blank_target;
> + uint32_t bpc_factor = 8;
> + uint32_t min_dsc_bpp_x16;
> + uint32_t max_dsc_bpp_x16;
> + bool hdmi_all_bpp = dsc_caps->is_vic_all_bpp;
> + uint32_t slice_width;
> +
> + if (timing->display_color_depth == COLOR_DEPTH_101010)
> + bpc_factor = 10;
> + if (timing->display_color_depth == COLOR_DEPTH_121212)
> + bpc_factor = 12;
> +
> + /* Assuming: bpc as 8*/
> + if (timing->pixel_encoding == PIXEL_ENCODING_RGB ||
> + timing->pixel_encoding == PIXEL_ENCODING_YCBCR444) {
> + min_dsc_bpp_x16 = 8 * 16;
> + max_dsc_bpp_x16 = 3 * 16 * bpc_factor;
> + } else if (timing->pixel_encoding == PIXEL_ENCODING_YCBCR422) {
> + min_dsc_bpp_x16 = 7 * 16;
> + max_dsc_bpp_x16 = 2 * 16 * bpc_factor;
> + } else {
> + min_dsc_bpp_x16 = 6 * 16;
> + max_dsc_bpp_x16 = 3 * 16 * bpc_factor / 2;
> + }
> +
> + max_dsc_bpp_x16 = MIN(max_dsc_bpp_x16, 256);
> + if (!hdmi_all_bpp)
> + max_dsc_bpp_x16 = MIN(max_dsc_bpp_x16, 192);
> +
> + c_frl_sb = 4 * c_frl_cb + num_lanes;
> + pixel_clock =
> dc_fixpt_div_int(dc_fixpt_from_int(timing->pix_clk_100hz), 10000);
> + r_bit = dc_fixpt_from_int(frl_rate);
> + pixel_rate_tolerance = dc_fixpt_div_int(dc_fixpt_from_int(5), 1000);
> + overhead_sb = dc_fixpt_div_int(dc_fixpt_from_int(num_lanes), c_frl_sb);
> + overhead_rs = dc_fixpt_div_int(dc_fixpt_from_int(32), c_frl_sb);
> + overhead_map = dc_fixpt_div_int(dc_fixpt_from_int(25), (c_frl_sb * 10));
> + overhead_min = dc_fixpt_add(overhead_sb, overhead_rs);
> + overhead_min = dc_fixpt_add(overhead_min, overhead_map);
> + overhead_m = dc_fixpt_div_int(dc_fixpt_from_int(3), 1000);
> + overhead_max = dc_fixpt_add(overhead_min, overhead_m);
> + pixel_rate_tolerance = dc_fixpt_add_int(pixel_rate_tolerance, 1);
> + pixel_clock_max = dc_fixpt_mul(pixel_clock, pixel_rate_tolerance);
> + t_line = dc_fixpt_div(dc_fixpt_from_int(h_active + h_blank),
> pixel_clock_max);
> + r_bit_min = dc_fixpt_div_int(dc_fixpt_from_int(frl_bit_tolerance),
> 1000000);
> + r_bit_min = dc_fixpt_sub(dc_fixpt_from_int(1), r_bit_min);
> + r_bit_min = dc_fixpt_mul(r_bit, r_bit_min);
> + r_frl_char_min = dc_fixpt_div_int(r_bit_min, 18);
> + c_frl_line = dc_fixpt_mul(t_line, r_frl_char_min);
> + c_frl_line = dc_fixpt_mul_int(c_frl_line, num_lanes);
> + c_frl_line_int = dc_fixpt_floor(c_frl_line);
> + c_frl_available = dc_fixpt_sub(dc_fixpt_from_int(1), overhead_max);
> + c_frl_available = dc_fixpt_mul_int(c_frl_available, c_frl_line_int);
> + c_frl_av_int = dc_fixpt_floor(c_frl_available);
> + c_frl_active_av = dc_fixpt_mul_int(dc_fixpt_from_int(c_frl_av_int),
> h_active);
> + c_frl_active_av = dc_fixpt_div_int(c_frl_active_av, (h_active +
> h_blank));
> + c_frl_blank_av = dc_fixpt_mul_int(dc_fixpt_from_int(c_frl_av_int),
> h_blank);
> + c_frl_blank_av = dc_fixpt_div_int(c_frl_blank_av, (h_active +
> h_blank));
> + r_ap = dc_fixpt_max(dc_fixpt_from_int(192000),
> + dc_fixpt_from_int(f_audio * acat_ap));
> + r_ap = dc_fixpt_add(r_ap, dc_fixpt_from_int(2 * acr_rate_max));
> + max_audio_tol_rate =
> dc_fixpt_div_int(dc_fixpt_from_int(audio_tolerance), 1000000);
> + max_audio_tol_rate = dc_fixpt_add(dc_fixpt_from_int(1),
> max_audio_tol_rate);
> + r_ap = dc_fixpt_mul(r_ap, max_audio_tol_rate);
> + avg_audio_packets_line = dc_fixpt_mul(r_ap, t_line);
> + avg_audio_packets_line = dc_fixpt_div_int(avg_audio_packets_line,
> 1000000);
> + avg_audio_packets_line_int = dc_fixpt_ceil(avg_audio_packets_line);
> + hc_blank_audio_min = 32 + 32 * avg_audio_packets_line_int;
> + slice_width = dc_fixpt_ceil(dc_fixpt_div_int(
> + dc_fixpt_from_int(h_active), num_slices_h));
> +
> + /* Slice width for 420 must be even */
> + if (timing->pixel_encoding == PIXEL_ENCODING_YCBCR420 && slice_width %
> 2 != 0) {
> + slice_width++;
> + }
> +
> + for (uint32_t i = max_dsc_bpp_x16; i >= min_dsc_bpp_x16; i--) {
> + bpp_target_x16 = i;
> + bytes_target = num_slices_h * dc_fixpt_ceil(dc_fixpt_div_int(
> + dc_fixpt_from_int(bpp_target_x16 *
> slice_width), 8 * 16));
> + hc_active_target = dc_fixpt_ceil(dc_fixpt_div_int(
> + dc_fixpt_from_int(bytes_target), 3));
> + hc_blank_target_est1 = dc_fixpt_ceil(dc_fixpt_div_int(
> + dc_fixpt_from_int(hc_active_target * h_blank),
> h_active));
> + hc_blank_target_est2 = dc_fixpt_floor(dc_fixpt_max(
> + dc_fixpt_from_int(hc_blank_target_est1),
> + dc_fixpt_from_int(hc_blank_audio_min)));
> + hc_blank_target_bandwidth =
> dc_fixpt_div_int(dc_fixpt_from_int(3), 2);
> + hc_blank_target_bandwidth =
> dc_fixpt_mul(hc_blank_target_bandwidth,
> + dc_fixpt_from_int(hc_active_target));
> + hc_blank_target_bandwidth =
> dc_fixpt_sub(dc_fixpt_from_int(c_frl_av_int),
> + hc_blank_target_bandwidth);
> + hc_blank_target_bandwidth =
> dc_fixpt_min(hc_blank_target_bandwidth,
> + dc_fixpt_from_int(hc_blank_target_est2));
> + hc_blank_target_bandwidth =
> dc_fixpt_div_int(hc_blank_target_bandwidth, 4);
> + hc_blank_target = dc_fixpt_floor(hc_blank_target_bandwidth) * 4;
> + if (hc_blank_target >= hc_blank_audio_min)
> + return bpp_target_x16;
> + }
> + return 0;
> +}
> /* Decide DSC bandwidth range based on signal, timing, specs specific and
> input min and max
> * requirements.
> * The range output includes decided min/max target bpp, the respective
> bandwidth requirements
> @@ -812,6 +1468,30 @@ static bool decide_dsc_bandwidth_range(
> range->min_target_bpp_x16 = preferred_bpp_x16;
> }
> }
> + else if (dsc_caps->is_frl) {
> + uint32_t specs_preferred_bpp_x16 = hdmi_dsc_get_bpp(timing,
> dsc_caps);
> + uint32_t specs_calculated_bpp_x16 = 0;
> +
> + if (timing->vic) {
> + /* For CTA timing, we should strictly follow HDMI spec.
> */
> + range->max_target_bpp_x16 = specs_preferred_bpp_x16;
> + if (dsc_caps->is_vic_all_bpp || dsc_caps->is_dp)
> + range->min_target_bpp_x16 = min_bpp_x16;
> + else
> + range->min_target_bpp_x16 =
> specs_preferred_bpp_x16;
> + } else {
> + if (timing->vic == 0 && timing->hdmi_vic == 0)
> + specs_calculated_bpp_x16 =
> compute_bpp_x16_from_frl_params(
> + timing, num_slices_h, dsc_caps);
> +
> + if (specs_calculated_bpp_x16 != 0)
> + specs_preferred_bpp_x16 =
> MIN(specs_calculated_bpp_x16,
> + specs_preferred_bpp_x16);
> +
> + range->max_target_bpp_x16 = MIN(max_bpp_x16,
> specs_preferred_bpp_x16);
> + range->min_target_bpp_x16 = min_bpp_x16;
> + }
> + }
> /* TODO - make this value generic to all signal types */
> else if (dsc_caps->edp_sink_max_bits_per_pixel) {
> /* apply max bpp limitation from edp sink */
> @@ -877,6 +1557,10 @@ static bool decide_dsc_target_bpp_x16(
> dsc_common_caps->bpp_increment_div,
> dsc_common_caps->is_dp);
> }
> + /* Assign minimum bpp and validate TB borrow scenario later */
> + if (target_bandwidth_kbps < range.min_kbps)
> + if (dsc_common_caps->is_frl)
> + *target_bpp_x16 = range.min_target_bpp_x16;
> }
>
> return *target_bpp_x16 != 0;
> @@ -1061,6 +1745,8 @@ static bool setup_dsc_config(
> int pic_height;
> int slice_height;
> struct dc_dsc_policy policy;
> + int num_lanes;
> + int frl_rate;
>
> memset(dsc_cfg, 0, sizeof(struct dc_dsc_config));
>
> @@ -1078,6 +1764,11 @@ static bool setup_dsc_config(
> is_dsc_possible = intersect_dsc_caps(dsc_sink_caps, dsc_enc_caps,
> timing->pixel_encoding, &dsc_common_caps);
> if (!is_dsc_possible)
> goto done;
> + if (convert_bandwidth_to_frl_params(
> + target_bandwidth_kbps, &num_lanes,
> &frl_rate)) {
> + dsc_common_caps.num_lanes = num_lanes;
> + dsc_common_caps.frl_rate = frl_rate;
> + }
>
> sink_per_slice_throughput_mps = 0;
>
> @@ -1201,6 +1892,8 @@ static bool setup_dsc_config(
> else
> is_dsc_possible = false;
> }
> + if (dsc_sink_caps->is_frl)
> + num_slices_h = hdmi_dsc_get_num_slices(timing);
> // When we force ODM, num dsc h slices must be divisible by num odm h
> slices
> switch (options->dsc_force_odm_hslice_override) {
> case 0:
> @@ -1279,6 +1972,11 @@ static bool setup_dsc_config(
> dsc_cfg->block_pred_enable = dsc_common_caps.is_block_pred_supported;
> dsc_cfg->linebuf_depth = dsc_common_caps.lb_bit_depth;
> dsc_cfg->version_minor = (dsc_common_caps.dsc_version & 0xf0) >> 4;
> + dsc_cfg->is_frl = dsc_sink_caps->is_frl;
> + if (dsc_cfg->is_frl)
> + dsc_cfg->num_slices_h = num_slices_h;
> + dsc_cfg->is_vic_all_bpp = dsc_sink_caps->is_vic_all_bpp;
> + dsc_cfg->total_chunk_kbytes = dsc_sink_caps->total_chunk_kbytes;
> dsc_cfg->is_dp = dsc_sink_caps->is_dp;
>
> done:
> @@ -1405,6 +2103,12 @@ void dc_dsc_get_policy_for_timing(const struct
> dc_crtc_timing *timing,
> /* DP specs limits to 3 x bpc */
> policy->max_target_bpp = 3 * bpc;
> policy->ycbcr422_simple = true;
> + if (timing->pixel_encoding == PIXEL_ENCODING_YCBCR422 &&
> link_encoding == DC_LINK_ENCODING_HDMI_FRL) {
> + /* HDMI FRL YCbCr422 native support */
> + policy->min_target_bpp = 7;
> + policy->max_target_bpp = 2 * bpc;
> + policy->ycbcr422_simple = false;
> + }
> break;
> case PIXEL_ENCODING_YCBCR420:
> /* DP specs limits to 6 */
> diff --git
> a/drivers/gpu/drm/amd/display/dc/hpo/dcn30/dcn30_hpo_frl_stream_encoder.c
> b/drivers/gpu/drm/amd/display/dc/hpo/dcn30/dcn30_hpo_frl_stream_encoder.c
> index cd7d2bb661e5..cff5c95a771c 100644
> --- a/drivers/gpu/drm/amd/display/dc/hpo/dcn30/dcn30_hpo_frl_stream_encoder.c
> +++ b/drivers/gpu/drm/amd/display/dc/hpo/dcn30/dcn30_hpo_frl_stream_encoder.c
> @@ -216,6 +216,16 @@ void hpo_enc3_set_hdmi_stream_attribute(struct
> hpo_frl_stream_encoder *enc,
> break;
> }
>
> + /* When compression active, CD/PP/Phase field shall be zero in GCP */
> + if (crtc_timing->flags.DSC) {
> + REG_UPDATE_2(HDMI_TB_ENC_PIXEL_FORMAT,
> + HDMI_DEEP_COLOR_DEPTH, 0,
> + HDMI_DEEP_COLOR_ENABLE, 0);
> + } else {
> + REG_UPDATE(HDMI_TB_ENC_PIXEL_FORMAT,
> + HDMI_DSC_MODE, 0);
> + }
> +
> /* Configure ODM combine mode */
> switch (odm_combine_num_segments) {
> case 1:
> @@ -457,6 +467,94 @@ void hpo_enc3_update_hdmi_info_packet(struct
> dcn30_hpo_frl_stream_encoder *enc3,
> }
> }
>
> +void hpo_enc3_hdmi_set_dsc_config(
> + struct hpo_frl_stream_encoder *enc,
> + struct dc_crtc_timing *timing,
> + uint8_t *dsc_packed_pps)
> +{
> + struct dcn30_hpo_frl_stream_encoder *enc3 =
> DCN30_HPO_FRL_STRENC_FROM_HPO_FRL_STRENC(enc);
> + enum optc_dsc_mode dsc_mode = OPTC_DSC_DISABLED;
> + uint8_t i;
> +
> + if (dsc_packed_pps) {
> + if (timing->pixel_encoding == PIXEL_ENCODING_YCBCR420 ||
> + (timing->pixel_encoding ==
> PIXEL_ENCODING_YCBCR422
> + && !timing->dsc_cfg.ycbcr422_simple))
> + dsc_mode = OPTC_DSC_ENABLED_NATIVE_SUBSAMPLED;
> + else
> + dsc_mode = OPTC_DSC_ENABLED_444;
> + }
> +
> + REG_UPDATE(HDMI_STREAM_ENC_CLOCK_RAMP_ADJUSTER_FIFO_STATUS_CONTROL0,
> + FIFO_DSC_MODE, dsc_mode);
> +
> + REG_UPDATE(HDMI_TB_ENC_PIXEL_FORMAT,
> + HDMI_DSC_MODE, dsc_mode);
> +
> + /* 5 packets for hdmi 2.1, use generic packets 5-10 to transmit*/
> + /* TODO: do we change new bit to 0 after first transmission? Do we set
> End bit when exiting dsc? */
> + if (dsc_mode != OPTC_DSC_DISABLED) {
> + struct dc_info_packet emp_packet = {0};
> + uint32_t dsc_pic_width = timing->h_addressable +
> timing->h_border_left + timing->h_border_right;
> + uint32_t h_back = timing->h_total - dsc_pic_width -
> timing->h_sync_width - timing->h_front_porch;
> + /* HCactivebytes = Slices * ceil(SliceWidth * bpp/8)
> + * use ... + 15) / 16 to achieve ceil since bpp is stored as
> 16x actual value
> + */
> + uint32_t h_cactive_bytes = timing->dsc_cfg.num_slices_h * (
> + (dsc_pic_width / timing->dsc_cfg.num_slices_h *
> timing->dsc_cfg.bits_per_pixel / 8 + 15) / 16);
> +
> + /* Packet 0 */
> + emp_packet.valid = true;
> + emp_packet.hb0 = 0x7F; /* Default */
> + emp_packet.hb1 = (1 << 7); /* First */
> + emp_packet.hb2 = 0; /* Sequence index */
> + emp_packet.sb[0] = (1 << 1) | (1 << 2) | (1 << 7); /* Sync[1] =
> 1, VFR[2] = 1, New[7] = 1*/
> + emp_packet.sb[2] = 1; /* Organization_ID = 1 (Vesa spec)*/
> + emp_packet.sb[4] = 2; /* Data_Set_Tag(LSB) = 2*/
> + emp_packet.sb[6] = 136; /* Data_Set_Length(LSB) = 136*/
> + memcpy(&emp_packet.sb[7], dsc_packed_pps, 21);
> + hpo_enc3_update_hdmi_info_packet(enc3, 5, &emp_packet);
> +
> + /* Packets 1-3 */
> + emp_packet.hb1 = 0; /* Not first or last*/
> + for (i = 1; i < 4; i++) {
> + emp_packet.hb2 = i; /* Sequence index */
> + memcpy(&emp_packet.sb[0], &dsc_packed_pps[21 + 28 * (i
> - 1)], 28);
> + hpo_enc3_update_hdmi_info_packet(enc3, 5 + i,
> &emp_packet);
> + }
> +
> + /* Packet 4 */
> + emp_packet.hb2 = 4; /* Sequence index */
> + memcpy(&emp_packet.sb[0], &dsc_packed_pps[105], 23);
> + emp_packet.sb[23] = (uint8_t)timing->h_front_porch; /*
> Hfront[7:0] */
> + emp_packet.sb[24] = (uint8_t)(timing->h_front_porch >> 8); /*
> Hfront[15:8] */
> + emp_packet.sb[25] = (uint8_t)timing->h_sync_width; /*
> Hsync[7:0] */
> + emp_packet.sb[26] = (uint8_t)(timing->h_sync_width >> 8); /*
> Hsync[15:8] */
> + emp_packet.sb[27] = (uint8_t)h_back; /* Hback[7:0] */
> + hpo_enc3_update_hdmi_info_packet(enc3, 9, &emp_packet);
> +
> + /* Packet 5 */
> + emp_packet.hb1 = (1 << 6); /* Last */
> + emp_packet.hb2 = 5;
> + emp_packet.sb[0] = (uint8_t)(h_back >> 8); /* Hback[15:8] */
> + emp_packet.sb[1] = (uint8_t)h_cactive_bytes; /*
> HCactive_bytes[7:0] */
> + emp_packet.sb[2] = (uint8_t)(h_cactive_bytes >> 8); /*
> HCactive_bytes[15:8] */
> + hpo_enc3_update_hdmi_info_packet(enc3, 10, &emp_packet);
> +
> + /* Packet 0 - Clear New[7] */
> + emp_packet.valid = true;
> + emp_packet.hb0 = 0x7F; /* Default */
> + emp_packet.hb1 = (1 << 7); /* First */
> + emp_packet.hb2 = 0; /* Sequence index */
> + emp_packet.sb[0] = (1 << 1) | (1 << 2); /* Sync[1] = 1, VFR[2]
> = 1*/
> + emp_packet.sb[2] = 1; /* Organization_ID = 1 (Vesa spec)*/
> + emp_packet.sb[4] = 2; /* Data_Set_Tag(LSB) = 2*/
> + emp_packet.sb[6] = 136; /* Data_Set_Length(LSB) = 136*/
> + memcpy(&emp_packet.sb[7], dsc_packed_pps, 21);
> + hpo_enc3_update_hdmi_info_packet(enc3, 5, &emp_packet);
> + }
> +}
> +
> void hpo_enc3_stop_hdmi_info_packets(
> struct hpo_frl_stream_encoder *enc)
> {
> @@ -777,12 +875,18 @@ bool hpo_enc3_validate_hdmi_frl_output(
> default:
> break;
> }
> + frl_params.allow_all_bpp = timing->dsc_cfg.is_vic_all_bpp;
> if (timing->pixel_encoding == PIXEL_ENCODING_YCBCR420)
> frl_params.pixel_encoding = HDMI_FRL_PIXEL_ENCODING_420;
> else if (timing->pixel_encoding == PIXEL_ENCODING_YCBCR422)
> frl_params.pixel_encoding = HDMI_FRL_PIXEL_ENCODING_422;
> else
> frl_params.pixel_encoding = HDMI_FRL_PIXEL_ENCODING_444;
> + /* DSC parameters */
> + frl_params.bypass_hc_target_calc = false;
> + DC_FP_START();
> + hpo_fpu_enc3_validate_hdmi_frl_output_timing(timing, audio,
> &frl_params);
> + DC_FP_END();
> /* Audio parameters */
> /* TODO: set Audio parameters */
>
> @@ -910,6 +1014,7 @@ static const struct hpo_frl_stream_encoder_funcs
> dcn30_str_enc_funcs = {
> .set_avmute = enc3_stream_encoder_set_avmute,
> .validate_hdmi_frl_output = hpo_enc3_validate_hdmi_frl_output,
> .read_state = hpo_enc3_read_state,
> + .hdmi_frl_set_dsc_config = hpo_enc3_hdmi_set_dsc_config,
> .set_dynamic_metadata = hpo_enc3_set_dynamic_metadata,
> .hdmi_frl_fifo_odm_enabled = hpo_enc3_fifo_odm_enabled,
> };
> diff --git
> a/drivers/gpu/drm/amd/display/dc/hpo/dcn401/dcn401_hpo_frl_stream_encoder.c
> b/drivers/gpu/drm/amd/display/dc/hpo/dcn401/dcn401_hpo_frl_stream_encoder.c
> index 27f7ffd89629..28cb14dc87b0 100644
> ---
> a/drivers/gpu/drm/amd/display/dc/hpo/dcn401/dcn401_hpo_frl_stream_encoder.c
> +++
> b/drivers/gpu/drm/amd/display/dc/hpo/dcn401/dcn401_hpo_frl_stream_encoder.c
> @@ -879,6 +879,7 @@ static const struct hpo_frl_stream_encoder_funcs
> dcn401_str_enc_funcs = {
> .hdmi_audio_disable = hpo_enc401_hdmi_audio_disable,
> .set_avmute = enc401_stream_encoder_set_avmute,
> .read_state = hpo_enc401_read_state,
> + .hdmi_frl_set_dsc_config = hpo_enc401_hdmi_set_dsc_config,
> .set_dynamic_metadata = hpo_enc401_set_dynamic_metadata,
> };
>
> diff --git
> a/drivers/gpu/drm/amd/display/dc/hpo/dcn42/dcn42_hpo_frl_stream_encoder.c
> b/drivers/gpu/drm/amd/display/dc/hpo/dcn42/dcn42_hpo_frl_stream_encoder.c
> index 0ec386347f80..d4f66e62c729 100644
> --- a/drivers/gpu/drm/amd/display/dc/hpo/dcn42/dcn42_hpo_frl_stream_encoder.c
> +++ b/drivers/gpu/drm/amd/display/dc/hpo/dcn42/dcn42_hpo_frl_stream_encoder.c
> @@ -179,6 +179,7 @@ static const struct hpo_frl_stream_encoder_funcs
> dcn42_str_enc_funcs = {
> .hdmi_audio_disable = hpo_enc42_hdmi_audio_disable,
> .set_avmute = enc401_stream_encoder_set_avmute,
> .read_state = hpo_enc401_read_state,
> + .hdmi_frl_set_dsc_config = hpo_enc401_hdmi_set_dsc_config,
> .set_dynamic_metadata = hpo_enc401_set_dynamic_metadata,
> };
>
> diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn30/dcn30_hwseq.c
> b/drivers/gpu/drm/amd/display/dc/hwss/dcn30/dcn30_hwseq.c
> index cfca7af6cd1f..a416efcc9325 100644
> --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn30/dcn30_hwseq.c
> +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn30/dcn30_hwseq.c
> @@ -955,6 +955,15 @@ enum dc_status dcn30_setup_hdmi_frl_link(
> return status;
> }
>
> +void dcn30_hw_set_vstartup_dsc_frl(struct dc *dc,
> + struct pipe_ctx *pipe_ctx)
> +{
> + (void)dc;
> + if (pipe_ctx->stream_res.tg->funcs->set_vstartup_dsc_frl)
> + pipe_ctx->stream_res.tg->funcs->set_vstartup_dsc_frl(
> + pipe_ctx->stream_res.tg);
> +}
And these aren't needed here. I'll send a v3.
Harry
> +
> bool dcn30_apply_idle_power_optimizations(struct dc *dc, bool enable)
> {
> union dmub_rb_cmd cmd;
> diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn30/dcn30_init.c
> b/drivers/gpu/drm/amd/display/dc/hwss/dcn30/dcn30_init.c
> index 26c7386a8a36..cd151c75f59e 100644
> --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn30/dcn30_init.c
> +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn30/dcn30_init.c
> @@ -106,6 +106,7 @@ static const struct hw_sequencer_funcs dcn30_funcs = {
> .enable_dp_link_output = dce110_enable_dp_link_output,
> .disable_link_output = dce110_disable_link_output,
> .setup_hdmi_frl_link = dcn30_setup_hdmi_frl_link,
> + .set_vstartup_dsc_frl = dcn30_hw_set_vstartup_dsc_frl,
> .set_disp_pattern_generator = dcn30_set_disp_pattern_generator,
> .get_dcc_en_bits = dcn10_get_dcc_en_bits,
> .update_visual_confirm_color = dcn10_update_visual_confirm_color,
> diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn31/dcn31_init.c
> b/drivers/gpu/drm/amd/display/dc/hwss/dcn31/dcn31_init.c
> index 23b30d6f3956..69b8e7030fa4 100644
> --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn31/dcn31_init.c
> +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn31/dcn31_init.c
> @@ -99,6 +99,7 @@ static const struct hw_sequencer_funcs dcn31_funcs = {
> .get_vupdate_offset_from_vsync = dcn10_get_vupdate_offset_from_vsync,
> .calc_vupdate_position = dcn10_calc_vupdate_position,
> .setup_hdmi_frl_link = dcn30_setup_hdmi_frl_link,
> + .set_vstartup_dsc_frl = dcn30_hw_set_vstartup_dsc_frl,
> .set_backlight_level = dcn21_set_backlight_level,
> .set_abm_immediate_disable = dcn21_set_abm_immediate_disable,
> .set_pipe = dcn21_set_pipe,
> diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn314/dcn314_init.c
> b/drivers/gpu/drm/amd/display/dc/hwss/dcn314/dcn314_init.c
> index 98771fc443c7..8c50604a4895 100644
> --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn314/dcn314_init.c
> +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn314/dcn314_init.c
> @@ -101,6 +101,7 @@ static const struct hw_sequencer_funcs dcn314_funcs = {
> .get_vupdate_offset_from_vsync = dcn10_get_vupdate_offset_from_vsync,
> .calc_vupdate_position = dcn10_calc_vupdate_position,
> .setup_hdmi_frl_link = dcn30_setup_hdmi_frl_link,
> + .set_vstartup_dsc_frl = dcn30_hw_set_vstartup_dsc_frl,
> .set_backlight_level = dcn21_set_backlight_level,
> .set_abm_immediate_disable = dcn21_set_abm_immediate_disable,
> .set_pipe = dcn21_set_pipe,
> diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn32/dcn32_init.c
> b/drivers/gpu/drm/amd/display/dc/hwss/dcn32/dcn32_init.c
> index 0b3e8512ebf1..203c4f7ecd2b 100644
> --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn32/dcn32_init.c
> +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn32/dcn32_init.c
> @@ -98,6 +98,7 @@ static const struct hw_sequencer_funcs dcn32_funcs = {
> .get_vupdate_offset_from_vsync = dcn10_get_vupdate_offset_from_vsync,
> .calc_vupdate_position = dcn10_calc_vupdate_position,
> .setup_hdmi_frl_link = dcn30_setup_hdmi_frl_link,
> + .set_vstartup_dsc_frl = dcn30_hw_set_vstartup_dsc_frl,
> .get_max_dispclk_mhz = dcn32_get_max_dispclk_mhz,
> .apply_idle_power_optimizations = dcn32_apply_idle_power_optimizations,
> .does_plane_fit_in_mall = NULL,
> diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn35/dcn35_init.c
> b/drivers/gpu/drm/amd/display/dc/hwss/dcn35/dcn35_init.c
> index fc18d2207711..c0cefeff6e85 100644
> --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn35/dcn35_init.c
> +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn35/dcn35_init.c
> @@ -108,6 +108,7 @@ static const struct hw_sequencer_funcs dcn35_funcs = {
> .get_vupdate_offset_from_vsync = dcn10_get_vupdate_offset_from_vsync,
> .calc_vupdate_position = dcn10_calc_vupdate_position,
> .setup_hdmi_frl_link = dcn30_setup_hdmi_frl_link,
> + .set_vstartup_dsc_frl = dcn30_hw_set_vstartup_dsc_frl,
> .set_backlight_level = dcn31_set_backlight_level,
> .set_abm_immediate_disable = dcn21_set_abm_immediate_disable,
> .set_pipe = dcn21_set_pipe,
> diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn351/dcn351_init.c
> b/drivers/gpu/drm/amd/display/dc/hwss/dcn351/dcn351_init.c
> index 19ec5b4edfdc..a852a2345296 100644
> --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn351/dcn351_init.c
> +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn351/dcn351_init.c
> @@ -101,6 +101,7 @@ static const struct hw_sequencer_funcs dcn351_funcs = {
> .get_vupdate_offset_from_vsync = dcn10_get_vupdate_offset_from_vsync,
> .calc_vupdate_position = dcn10_calc_vupdate_position,
> .setup_hdmi_frl_link = dcn30_setup_hdmi_frl_link,
> + .set_vstartup_dsc_frl = dcn30_hw_set_vstartup_dsc_frl,
> .set_backlight_level = dcn31_set_backlight_level,
> .set_abm_immediate_disable = dcn21_set_abm_immediate_disable,
> .set_pipe = dcn21_set_pipe,
> diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_init.c
> b/drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_init.c
> index d24a352937b4..e1cc16aa207c 100644
> --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_init.c
> +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_init.c
> @@ -85,6 +85,7 @@ static const struct hw_sequencer_funcs dcn401_funcs = {
> .get_vupdate_offset_from_vsync = dcn10_get_vupdate_offset_from_vsync,
> .calc_vupdate_position = dcn10_calc_vupdate_position,
> .setup_hdmi_frl_link = dcn30_setup_hdmi_frl_link,
> + .set_vstartup_dsc_frl = dcn30_hw_set_vstartup_dsc_frl,
> .apply_idle_power_optimizations = dcn401_apply_idle_power_optimizations,
> .does_plane_fit_in_mall = NULL,
> .set_backlight_level = dcn31_set_backlight_level,
> diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn42/dcn42_init.c
> b/drivers/gpu/drm/amd/display/dc/hwss/dcn42/dcn42_init.c
> index 49c13611a518..e54d814b3ea9 100644
> --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn42/dcn42_init.c
> +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn42/dcn42_init.c
> @@ -86,6 +86,7 @@ static const struct hw_sequencer_funcs dcn42_funcs = {
> .get_vupdate_offset_from_vsync = dcn10_get_vupdate_offset_from_vsync,
> .calc_vupdate_position = dcn10_calc_vupdate_position,
> .setup_hdmi_frl_link = dcn30_setup_hdmi_frl_link,
> + .set_vstartup_dsc_frl = dcn30_hw_set_vstartup_dsc_frl,
> .apply_idle_power_optimizations = dcn35_apply_idle_power_optimizations,
> .does_plane_fit_in_mall = NULL,
> .set_backlight_level = dcn31_set_backlight_level,
> diff --git a/drivers/gpu/drm/amd/display/dc/hwss/hw_sequencer.h
> b/drivers/gpu/drm/amd/display/dc/hwss/hw_sequencer.h
> index 7e65ccd11386..34e386e16d0c 100644
> --- a/drivers/gpu/drm/amd/display/dc/hwss/hw_sequencer.h
> +++ b/drivers/gpu/drm/amd/display/dc/hwss/hw_sequencer.h
> @@ -1437,6 +1437,9 @@ struct hw_sequencer_funcs {
> unsigned int (*get_max_dispclk_mhz)(struct dc *dc,
> struct dc_state *context);
>
> + void (*set_vstartup_dsc_frl)(struct dc *dc,
> + struct pipe_ctx *pipe_ctx);
> +
> /* Idle Optimization Related */
> bool (*apply_idle_power_optimizations)(struct dc *dc, bool enable);
>
> @@ -1710,6 +1713,8 @@ void
> hwss_hpo_dp_stream_enc_dp_set_dsc_pps_info_packet(union block_sequence_para
>
> void hwss_stream_enc_dp_set_dsc_pps_info_packet(union block_sequence_params
> *params);
>
> +void hwss_hpo_frl_stream_enc_set_dsc_config(union block_sequence_params
> *params);
> +
> void hwss_set_dmdata_attributes(union block_sequence_params *params);
>
> void hwss_dp_trace_source_sequence(union block_sequence_params *params);
> diff --git a/drivers/gpu/drm/amd/display/dc/inc/hw/stream_encoder.h
> b/drivers/gpu/drm/amd/display/dc/inc/hw/stream_encoder.h
> index 1800b1929b16..570c45c7830f 100644
> --- a/drivers/gpu/drm/amd/display/dc/inc/hw/stream_encoder.h
> +++ b/drivers/gpu/drm/amd/display/dc/inc/hw/stream_encoder.h
> @@ -368,6 +368,10 @@ struct hpo_frl_stream_encoder {
> * stream interfaces for setup the FRL stream encoder.
> */
> struct hpo_frl_stream_encoder_funcs {
> + void (*hdmi_frl_set_dsc_config)(
> + struct hpo_frl_stream_encoder *enc,
> + struct dc_crtc_timing *timing,
> + uint8_t *dsc_packed_pps);
> /**
> * @hdmi_frl_enable:
> *
> diff --git a/drivers/gpu/drm/amd/display/dc/link/link_dpms.c
> b/drivers/gpu/drm/amd/display/dc/link/link_dpms.c
> index ca56854dc9f1..1cd4a882f66d 100644
> --- a/drivers/gpu/drm/amd/display/dc/link/link_dpms.c
> +++ b/drivers/gpu/drm/amd/display/dc/link/link_dpms.c
> @@ -549,6 +549,8 @@ static void update_psp_stream_config(struct pipe_ctx
> *pipe_ctx, bool dpms_off)
>
> /* dig front end */
> config.dig_fe = (uint8_t)
> pipe_ctx->stream_res.stream_enc->stream_enc_inst;
> + if (dc_is_hdmi_frl_signal(pipe_ctx->stream->signal))
> + config.dig_fe =
> (uint8_t)pipe_ctx->stream_res.hpo_frl_stream_enc->stream_enc_inst;
>
> /* stream encoder index */
> config.stream_enc_idx = (uint8_t)(pipe_ctx->stream_res.stream_enc->id -
> ENGINE_ID_DIGA);
> @@ -785,6 +787,22 @@ void link_set_dsc_on_stream(struct pipe_ctx *pipe_ctx,
> bool enable)
>
> /* PPS SDP is set elsewhere because it has to be done
> after DIG FE is connected to DIG BE */
> }
> + else if (dc_is_hdmi_frl_signal(stream->signal)) {
> + uint8_t dsc_packed_pps[128];
> + struct dc_crtc_timing patched_crtc_timing =
> stream->timing;
> +
> + DC_LOG_DSC("Setting stream encoder DSC config for
> engine %d:", (int)pipe_ctx->stream_res.hpo_frl_stream_enc->id);
> + dsc_optc_config_log(dsc, &dsc_optc_cfg);
> +
> + /* if we are borrowing from hblank, h_addressable and
> pic_width need to be adjusted */
> + if (dc->debug.enable_hblank_borrow) {
> + dsc_cfg.pic_width =
> stream->timing.h_addressable;
> + }
> +
> + dsc->funcs->dsc_get_packed_pps(dsc, &dsc_cfg,
> &dsc_packed_pps[0]);
> +
> pipe_ctx->stream_res.hpo_frl_stream_enc->funcs->hdmi_frl_set_dsc_config(
> +
> pipe_ctx->stream_res.hpo_frl_stream_enc, &patched_crtc_timing,
> &dsc_packed_pps[0]);
> + }
>
> /* Enable DSC in OPTC */
> DC_LOG_DSC("Setting optc DSC config for tg instance %d:",
> pipe_ctx->stream_res.tg->inst);
> @@ -816,6 +834,9 @@ void link_set_dsc_on_stream(struct pipe_ctx *pipe_ctx,
> bool enable)
>
> pipe_ctx->stream_res.stream_enc, false, NULL, true);
> }
> }
> + else if (dc_is_hdmi_frl_signal(stream->signal))
> +
> pipe_ctx->stream_res.hpo_frl_stream_enc->funcs->hdmi_frl_set_dsc_config(
> +
> pipe_ctx->stream_res.hpo_frl_stream_enc, &stream->timing, NULL);
>
> /* disable DSC block */
> for (odm_pipe = pipe_ctx; odm_pipe; odm_pipe =
> odm_pipe->next_odm_pipe) {
> @@ -903,6 +924,12 @@ bool link_set_dsc_pps_packet(struct pipe_ctx *pipe_ctx,
> bool enable, bool immedi
> &dsc_packed_pps[0],
> immediate_update);
> }
> + else if (dc_is_hdmi_frl_signal(stream->signal)) {
> + //TODO: bring HDMI FRL in line with DP
> + DC_LOG_DSC("Setting stream encoder DSC PPS SDP for
> engine %d\n", (int)pipe_ctx->stream_res.hpo_frl_stream_enc->id);
> +
> pipe_ctx->stream_res.hpo_frl_stream_enc->funcs->hdmi_frl_set_dsc_config(
> +
> pipe_ctx->stream_res.hpo_frl_stream_enc, &stream->timing, &dsc_packed_pps[0]);
> + }
> } else {
> /* disable DSC PPS in stream encoder */
> memset(&stream->dsc_packed_pps[0], 0,
> sizeof(stream->dsc_packed_pps));
> @@ -917,6 +944,10 @@ bool link_set_dsc_pps_packet(struct pipe_ctx *pipe_ctx,
> bool enable, bool immedi
>
> pipe_ctx->stream_res.stream_enc->funcs->dp_set_dsc_pps_info_packet(
>
> pipe_ctx->stream_res.stream_enc, false, NULL, true);
> }
> + else if (dc_is_hdmi_frl_signal(stream->signal))
> + //TODO: bring HDMI FRL in line with DP
> +
> pipe_ctx->stream_res.hpo_frl_stream_enc->funcs->hdmi_frl_set_dsc_config(
> +
> pipe_ctx->stream_res.hpo_frl_stream_enc, &stream->timing, NULL);
> }
>
> return true;
> @@ -2426,6 +2457,8 @@ void link_set_dpms_off(struct pipe_ctx *pipe_ctx)
> if (pipe_ctx->stream->timing.flags.DSC) {
> if (dc_is_dp_signal(pipe_ctx->stream->signal))
> link_set_dsc_enable(pipe_ctx, false);
> + else if (dc_is_hdmi_frl_signal(pipe_ctx->stream->signal))
> + link_set_dsc_on_stream(pipe_ctx, false);
> }
> if (dp_is_128b_132b_signal(pipe_ctx)) {
> if (pipe_ctx->stream_res.tg->funcs->set_out_mux)
> @@ -2606,6 +2639,11 @@ void link_set_dpms_on(
> }
> }
>
> + if (pipe_ctx->stream->timing.flags.DSC &&
> + dc_is_hdmi_frl_signal(pipe_ctx->stream->signal))
> + //TODO: bring HDMI FRL in line with DP
> + link_set_dsc_on_stream(pipe_ctx, true);
> +
> /* turn off otg test pattern if enable */
> if (pipe_ctx->stream_res.tg->funcs->set_test_pattern)
>
> pipe_ctx->stream_res.tg->funcs->set_test_pattern(pipe_ctx->stream_res.tg,
> diff --git a/drivers/gpu/drm/amd/display/dc/link/protocols/link_hdmi_frl.c
> b/drivers/gpu/drm/amd/display/dc/link/protocols/link_hdmi_frl.c
> index 6db1f07fdb79..decc3061c58c 100644
> --- a/drivers/gpu/drm/amd/display/dc/link/protocols/link_hdmi_frl.c
> +++ b/drivers/gpu/drm/amd/display/dc/link/protocols/link_hdmi_frl.c
> @@ -92,6 +92,25 @@ static void hdmi_return_preeshoot_and_deemphasis(struct
> dc_link *link,
> }
> }
>
> +static bool hdmi_frl_test_dsc_max_rate(struct ddc_service *ddc_service)
> +{
> + uint8_t slave_address = HDMI_SCDC_ADDRESS;
> + uint8_t offset = HDMI_SCDC_SOURCE_TEST_REQ;
> + union hdmi_scdc_source_test_req test_req = {0};
> +
> + DC_LOGGER_INIT(ddc_service->link->ctx->logger);
> +
> + link_query_ddc_data(ddc_service, slave_address,
> + &offset, sizeof(offset), &test_req.byte,
> + sizeof(test_req.byte));
> + if (test_req.fields.DSC_FRL_MAX) {
> + FRL_INFO("FRL TEST REQ: DSC_FRL_MAX = 1");
> + return true;
> + }
> +
> + return false;
> +}
> +
> enum clock_source_id hdmi_frl_find_matching_phypll(
> struct dc_link *link)
> {
> @@ -767,6 +786,9 @@ void hdmi_frl_verify_link_cap(struct dc_link *link,
> link->preferred_hdmi_frl_settings.force_frl_max ||
> link->ctx->dc->debug.force_frl_max ? true :
> hdmi_frl_test_max_rate(link->ddc);
> + link->frl_flags.force_frl_dsc =
> + link->ctx->dc->debug.force_frl_dsc ? true :
> + hdmi_frl_test_dsc_max_rate(link->ddc);
> link->frl_flags.apply_vsdb_rcc_wa =
> link->ctx->dc->debug.apply_vsdb_rcc_wa;
>
> @@ -778,8 +800,16 @@ void hdmi_frl_verify_link_cap(struct dc_link *link,
> link->frl_flags.force_frl_always = true;
>
> if (!link->frl_flags.force_frl_max &&
> + !link->frl_flags.force_frl_dsc &&
> link->local_sink->edid_caps.panel_patch.hdmi_comp_auto)
> {
> link->frl_flags.force_frl_max = true;
> + link->frl_flags.force_frl_dsc = true;
> + }
> +
> + if (link->frl_flags.force_frl_max &&
> + !link->frl_flags.force_frl_dsc &&
> + link->local_sink->edid_caps.panel_patch.hdmi_comp_auto)
> {
> + link->frl_flags.force_frl_dsc = true;
> }
>
> if (link->local_sink &&
> @@ -996,6 +1026,9 @@ void hdmi_frl_set_preferred_link_settings(struct dc *dc,
> resource_build_info_frame(pipe);
> link_stream->ctx->dc->hwss.update_info_frame(pipe);
>
> + if (link_stream->timing.flags.DSC)
> + link_set_dsc_on_stream(pipe, true);
> +
> link_stream->ctx->dc->hwss.enable_audio_stream(pipe);
> link_stream->ctx->dc->hwss.enable_stream(pipe);
> link_stream->ctx->dc->hwss.unblank_stream(pipe,
> @@ -1104,6 +1137,10 @@ void hdmi_frl_decide_link_settings(struct
> dc_stream_state *stream,
> *frl_link_settings = stream->link->frl_verified_link_cap;
> return;
> }
> + if (stream->link->frl_flags.force_frl_dsc) {
> + *frl_link_settings = stream->link->frl_verified_link_cap;
> + return;
> + }
>
> if (stream->link->local_sink)
> if
> (stream->link->local_sink->edid_caps.panel_patch.hdmi_spe_handling) {
> @@ -1126,6 +1163,7 @@ void hdmi_frl_decide_link_settings(struct
> dc_stream_state *stream,
> } while (!success);
>
> *frl_link_settings = temp_settings;
> + update_borrow_mode_from_dsc_padding(dsc_padding_params,
> &stream->timing, frl_link_settings);
> }
>
> void hdmi_frl_write_read_request_enable(struct ddc_service *ddc_service)
> diff --git a/drivers/gpu/drm/amd/display/dc/optc/dcn30/dcn30_optc.c
> b/drivers/gpu/drm/amd/display/dc/optc/dcn30/dcn30_optc.c
> index d72574db1f07..78fd8d262de3 100644
> --- a/drivers/gpu/drm/amd/display/dc/optc/dcn30/dcn30_optc.c
> +++ b/drivers/gpu/drm/amd/display/dc/optc/dcn30/dcn30_optc.c
> @@ -189,7 +189,13 @@ void optc3_set_dsc_config(struct timing_generator *optc,
> struct optc *optc1 = DCN10TG_FROM_TG(optc);
>
> optc2_set_dsc_config(optc, dsc_mode, dsc_bytes_per_pixel,
> dsc_slice_width);
> - REG_UPDATE(OTG_V_SYNC_A_CNTL, OTG_V_SYNC_MODE, 0);
> +
> + if (dsc_mode != OPTC_DSC_DISABLED
> + && optc1->signal == SIGNAL_TYPE_HDMI_FRL) {
> + REG_UPDATE(OTG_V_SYNC_A_CNTL, OTG_V_SYNC_MODE, 1);
> + } else {
> + REG_UPDATE(OTG_V_SYNC_A_CNTL, OTG_V_SYNC_MODE, 0);
> + }
> }
>
> void optc3_set_odm_bypass(struct timing_generator *optc,
> @@ -314,6 +320,27 @@ bool optc3_get_pipe_update_pending(struct
> timing_generator *optc)
> return (flip_pending == 1 || dc_update_pending == 1);
> }
>
> +void optc3_set_vstartup_dsc_frl(struct timing_generator *optc)
> +{
> + struct optc *optc1 = DCN10TG_FROM_TG(optc);
> + unsigned int vblank_end = 0;
> + unsigned int vstartup_start = 0;
> +
> + REG_GET(OTG_V_BLANK_START_END, OTG_V_BLANK_END, &vblank_end);
> + REG_GET(OTG_VSTARTUP_PARAM, VSTARTUP_START, &vstartup_start);
> +
> + /* In FRL+DSC mode the VSYNC is generated in OTG at start of HBALNK
> + * before frame start (VCOUNT=0 and HCOUNT=0). We need to program
> + * VSTARTUP at least one line before frame start to ensure the VSYNC
> + * will be generated in VRR mode. We need to program
> + * VSTARTUP_START >= V_BLANK_END + 1.
> + * When fullscreen = false,
> + * global_sync will restore VSTARTUP_START to normal value
> + */
> + if (vblank_end >= vstartup_start)
> + REG_SET(OTG_VSTARTUP_PARAM, 0, VSTARTUP_START,
> + vblank_end+1);
> +}
> /**
> * optc3_set_timing_double_buffer() - DRR double buffering control
> *
> @@ -409,6 +436,7 @@ static const struct timing_generator_funcs dcn30_tg_funcs
> = {
> .get_optc_source = optc2_get_optc_source,
> .set_out_mux = optc3_set_out_mux,
> .set_drr_trigger_window = optc3_set_drr_trigger_window,
> + .set_vstartup_dsc_frl = optc3_set_vstartup_dsc_frl,
> .set_vtotal_change_limit = optc3_set_vtotal_change_limit,
> .set_gsl = optc2_set_gsl,
> .set_gsl_source_select = optc2_set_gsl_source_select,
> diff --git a/drivers/gpu/drm/amd/display/dc/optc/dcn30/dcn30_optc.h
> b/drivers/gpu/drm/amd/display/dc/optc/dcn30/dcn30_optc.h
> index 16c5610b49ac..8a88867f4361 100644
> --- a/drivers/gpu/drm/amd/display/dc/optc/dcn30/dcn30_optc.h
> +++ b/drivers/gpu/drm/amd/display/dc/optc/dcn30/dcn30_optc.h
> @@ -349,6 +349,7 @@ void optc3_triplebuffer_lock(struct timing_generator
> *optc);
> void optc3_program_blank_color(struct timing_generator *optc,
> const struct tg_color *blank_color);
>
> +void optc3_set_vstartup_dsc_frl(struct timing_generator *optc);
> void optc3_set_vtotal_change_limit(struct timing_generator *optc,
> uint32_t limit);
>
> diff --git a/drivers/gpu/drm/amd/display/dc/optc/dcn31/dcn31_optc.c
> b/drivers/gpu/drm/amd/display/dc/optc/dcn31/dcn31_optc.c
> index 98aaa22ce81c..bbf703777f72 100644
> --- a/drivers/gpu/drm/amd/display/dc/optc/dcn31/dcn31_optc.c
> +++ b/drivers/gpu/drm/amd/display/dc/optc/dcn31/dcn31_optc.c
> @@ -500,6 +500,7 @@ static const struct timing_generator_funcs dcn31_tg_funcs
> = {
> .get_optc_source = optc2_get_optc_source,
> .set_out_mux = optc3_set_out_mux,
> .set_drr_trigger_window = optc3_set_drr_trigger_window,
> + .set_vstartup_dsc_frl = optc3_set_vstartup_dsc_frl,
> .set_vtotal_change_limit = optc3_set_vtotal_change_limit,
> .set_gsl = optc2_set_gsl,
> .set_gsl_source_select = optc2_set_gsl_source_select,
> diff --git a/drivers/gpu/drm/amd/display/dc/optc/dcn314/dcn314_optc.c
> b/drivers/gpu/drm/amd/display/dc/optc/dcn314/dcn314_optc.c
> index a7cf34937b2f..4d4b517575e2 100644
> --- a/drivers/gpu/drm/amd/display/dc/optc/dcn314/dcn314_optc.c
> +++ b/drivers/gpu/drm/amd/display/dc/optc/dcn314/dcn314_optc.c
> @@ -244,6 +244,7 @@ static const struct timing_generator_funcs
> dcn314_tg_funcs = {
> .get_optc_source = optc2_get_optc_source,
> .set_out_mux = optc3_set_out_mux,
> .set_drr_trigger_window = optc3_set_drr_trigger_window,
> + .set_vstartup_dsc_frl = optc3_set_vstartup_dsc_frl,
> .set_vtotal_change_limit = optc3_set_vtotal_change_limit,
> .set_gsl = optc2_set_gsl,
> .set_gsl_source_select = optc2_set_gsl_source_select,
> diff --git a/drivers/gpu/drm/amd/display/dc/optc/dcn32/dcn32_optc.c
> b/drivers/gpu/drm/amd/display/dc/optc/dcn32/dcn32_optc.c
> index 60e546b69a05..99258b2fb14e 100644
> --- a/drivers/gpu/drm/amd/display/dc/optc/dcn32/dcn32_optc.c
> +++ b/drivers/gpu/drm/amd/display/dc/optc/dcn32/dcn32_optc.c
> @@ -354,6 +354,7 @@ static const struct timing_generator_funcs dcn32_tg_funcs
> = {
> .get_optc_source = optc2_get_optc_source,
> .set_out_mux = optc3_set_out_mux,
> .set_drr_trigger_window = optc3_set_drr_trigger_window,
> + .set_vstartup_dsc_frl = optc3_set_vstartup_dsc_frl,
> .set_vtotal_change_limit = optc3_set_vtotal_change_limit,
> .set_gsl = optc2_set_gsl,
> .set_gsl_source_select = optc2_set_gsl_source_select,
> diff --git a/drivers/gpu/drm/amd/display/dc/optc/dcn35/dcn35_optc.c
> b/drivers/gpu/drm/amd/display/dc/optc/dcn35/dcn35_optc.c
> index a880e4a6d165..f6183c2e78e4 100644
> --- a/drivers/gpu/drm/amd/display/dc/optc/dcn35/dcn35_optc.c
> +++ b/drivers/gpu/drm/amd/display/dc/optc/dcn35/dcn35_optc.c
> @@ -595,6 +595,7 @@ static const struct timing_generator_funcs dcn35_tg_funcs
> = {
> .set_h_timing_div_manual_mode =
> optc32_set_h_timing_div_manual_mode,
> .set_out_mux = optc3_set_out_mux,
> .set_drr_trigger_window = optc3_set_drr_trigger_window,
> + .set_vstartup_dsc_frl = optc3_set_vstartup_dsc_frl,
> .set_vtotal_change_limit = optc3_set_vtotal_change_limit,
> .set_gsl = optc2_set_gsl,
> .set_gsl_source_select = optc2_set_gsl_source_select,
> diff --git a/drivers/gpu/drm/amd/display/dc/optc/dcn401/dcn401_optc.c
> b/drivers/gpu/drm/amd/display/dc/optc/dcn401/dcn401_optc.c
> index a6d76f451cf8..581517c9cec4 100644
> --- a/drivers/gpu/drm/amd/display/dc/optc/dcn401/dcn401_optc.c
> +++ b/drivers/gpu/drm/amd/display/dc/optc/dcn401/dcn401_optc.c
> @@ -521,6 +521,7 @@ static const struct timing_generator_funcs
> dcn401_tg_funcs = {
> .get_optc_source = optc2_get_optc_source,
> .set_out_mux = optc401_set_out_mux,
> .set_drr_trigger_window = optc3_set_drr_trigger_window,
> + .set_vstartup_dsc_frl = optc3_set_vstartup_dsc_frl,
> .set_vtotal_change_limit = optc3_set_vtotal_change_limit,
> .set_gsl = optc2_set_gsl,
> .set_gsl_source_select = optc2_set_gsl_source_select,
> diff --git a/drivers/gpu/drm/amd/display/dc/optc/dcn42/dcn42_optc.c
> b/drivers/gpu/drm/amd/display/dc/optc/dcn42/dcn42_optc.c
> index ed66a2bbb8ae..f5280bc3c7ec 100644
> --- a/drivers/gpu/drm/amd/display/dc/optc/dcn42/dcn42_optc.c
> +++ b/drivers/gpu/drm/amd/display/dc/optc/dcn42/dcn42_optc.c
> @@ -250,6 +250,7 @@ static struct timing_generator_funcs dcn42_tg_funcs = {
> .wait_otg_disable = optc35_wait_otg_disable,
> .set_out_mux = optc401_set_out_mux,
> .set_drr_trigger_window = optc3_set_drr_trigger_window,
> + .set_vstartup_dsc_frl = optc3_set_vstartup_dsc_frl,
> .set_vtotal_change_limit = optc3_set_vtotal_change_limit,
> .set_gsl = optc2_set_gsl,
> .set_gsl_source_select = optc2_set_gsl_source_select,