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 f8c13bad4ac23..9fba78fb1c30e 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c @@ -1983,6 +1983,9 @@ static int amdgpu_dm_init(struct amdgpu_device *adev) if (amdgpu_dc_feature_mask & DC_DISABLE_LTTPR_DP2_0) init_data.flags.allow_lttpr_non_transparent_mode.bits.DP2_0 = true; + if (amdgpu_dc_feature_mask & DC_FRL_MASK) + init_data.flags.enable_frl = true; + init_data.flags.seamless_boot_edp_requested = false; if (amdgpu_device_seamless_boot_supported(adev)) { @@ -6790,18 +6793,26 @@ static void fill_stream_properties_from_drm_display_mode( timing_out->v_border_bottom = 0; /* TODO: un-hardcode */ if (drm_mode_is_420_only(info, mode_in) - && stream->signal == SIGNAL_TYPE_HDMI_TYPE_A) + && (stream->signal == SIGNAL_TYPE_HDMI_TYPE_A || + stream->signal == SIGNAL_TYPE_HDMI_FRL) + && aconnector + && aconnector->force_yuv_pixel_format == PIXEL_ENCODING_YCBCR420) timing_out->pixel_encoding = PIXEL_ENCODING_YCBCR420; else if (drm_mode_is_420_also(info, mode_in) && aconnector - && aconnector->force_yuv420_output) + && (aconnector->force_yuv_pixel_format == PIXEL_ENCODING_YCBCR420 + || aconnector->force_yuv420_output)) timing_out->pixel_encoding = PIXEL_ENCODING_YCBCR420; else if ((connector->display_info.color_formats & BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR422)) && aconnector - && aconnector->force_yuv422_output) + && (aconnector->force_yuv_pixel_format == PIXEL_ENCODING_YCBCR422 + || aconnector->force_yuv422_output)) timing_out->pixel_encoding = PIXEL_ENCODING_YCBCR422; else if ((connector->display_info.color_formats & BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR444)) - && stream->signal == SIGNAL_TYPE_HDMI_TYPE_A) + && (stream->signal == SIGNAL_TYPE_HDMI_TYPE_A || + stream->signal == SIGNAL_TYPE_HDMI_FRL) + && aconnector + && aconnector->force_yuv_pixel_format == PIXEL_ENCODING_YCBCR444) timing_out->pixel_encoding = PIXEL_ENCODING_YCBCR444; else timing_out->pixel_encoding = PIXEL_ENCODING_RGB; @@ -7171,12 +7182,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); } } @@ -7250,6 +7279,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 = @@ -7288,8 +7321,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 && @@ -7302,11 +7345,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) @@ -7345,7 +7418,7 @@ create_stream_for_sink(struct drm_connector *connector, int preferred_refresh = 0; enum color_transfer_func tf = TRANSFER_FUNC_UNKNOWN; #if defined(CONFIG_DRM_AMD_DC_FP) - struct dsc_dec_dpcd_caps dsc_caps; + struct dsc_dec_dpcd_caps dsc_caps = {0}; #endif struct dc_link *link = NULL; struct dc_sink *sink = NULL; diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h index 74a8fe1a1999f..693b9a99e08f6 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h @@ -834,6 +834,7 @@ struct amdgpu_dm_connector { bool fake_enable; bool force_yuv420_output; bool force_yuv422_output; + uint8_t force_yuv_pixel_format; struct dsc_preferred_settings dsc_settings; union dp_downstream_port_present mst_downstream_port_present; /* Cached display modes */ @@ -1098,7 +1099,7 @@ int amdgpu_dm_update_plane_color_mgmt(struct dm_crtc_state *crtc, void amdgpu_dm_update_connector_after_detect( struct amdgpu_dm_connector *aconnector); -void populate_hdmi_info_from_connector(struct drm_hdmi_info *info, +void populate_hdmi_info_from_connector(bool enable_frl, struct drm_hdmi_info *info, struct dc_edid_caps *edid_caps); extern const struct drm_encoder_helper_funcs amdgpu_dm_encoder_helper_funcs; diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_debugfs.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_debugfs.c index 3a3d01ce0d424..0dedaf23d694d 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_debugfs.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_debugfs.c @@ -3141,6 +3141,7 @@ static int force_yuv420_output_set(void *data, u64 val) struct amdgpu_dm_connector *connector = data; connector->force_yuv420_output = (bool)val; + connector->force_yuv_pixel_format = PIXEL_ENCODING_YCBCR420; return 0; } @@ -3160,6 +3161,31 @@ static int force_yuv420_output_get(void *data, u64 *val) DEFINE_DEBUGFS_ATTRIBUTE(force_yuv420_output_fops, force_yuv420_output_get, force_yuv420_output_set, "%llu\n"); +static int force_yuv422_output_set(void *data, u64 val) +{ + struct amdgpu_dm_connector *connector = data; + + connector->force_yuv422_output = (bool)val; + connector->force_yuv_pixel_format = PIXEL_ENCODING_YCBCR422; + + return 0; +} + +DEFINE_DEBUGFS_ATTRIBUTE(force_yuv422_output_fops, NULL, + force_yuv422_output_set, "%llu\n"); + +static int force_yuv444_output_set(void *data, u64 val) +{ + struct amdgpu_dm_connector *connector = data; + + connector->force_yuv_pixel_format = PIXEL_ENCODING_YCBCR444; + + return 0; +} + +DEFINE_DEBUGFS_ATTRIBUTE(force_yuv444_output_fops, NULL, + force_yuv444_output_set, "%llu\n"); + /* * Read Replay state */ @@ -3500,6 +3526,8 @@ static const struct { const struct file_operations *fops; } connector_debugfs_entries[] = { {"force_yuv420_output", &force_yuv420_output_fops}, + {"force_yuv422_output", &force_yuv422_output_fops}, + {"force_yuv444_output", &force_yuv444_output_fops}, {"trigger_hotplug", &trigger_hotplug_debugfs_fops}, {"internal_display", &internal_display_fops}, {"odm_combine_segments", &odm_combine_segments_fops} 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 7e1e9ff3fe0c4..ee6c48e729249 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) - populate_hdmi_info_from_connector(&connector->display_info.hdmi, edid_caps); + if (edid_caps->edid_hdmi) { + populate_hdmi_info_from_connector(link->dc->config.enable_frl, &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); @@ -1071,9 +1078,70 @@ dm_helpers_read_vbios_hardcoded_edid(struct dc_link *link, struct amdgpu_dm_conn return edid; } -void populate_hdmi_info_from_connector(struct drm_hdmi_info *hdmi, struct dc_edid_caps *edid_caps) +static uint8_t get_max_frl_rate(uint8_t max_lanes, uint8_t max_rate_per_lane) +{ + uint8_t max_frl_rate; + + if ((max_lanes == 3) && (max_rate_per_lane == 3)) + max_frl_rate = 1; + else if ((max_lanes == 3) && (max_rate_per_lane == 6)) + max_frl_rate = 2; + else if ((max_lanes == 4) && (max_rate_per_lane == 6)) + max_frl_rate = 3; + else if ((max_lanes == 4) && (max_rate_per_lane == 8)) + max_frl_rate = 4; + else if ((max_lanes == 4) && (max_rate_per_lane == 10)) + max_frl_rate = 5; + else if ((max_lanes == 4) && (max_rate_per_lane == 12)) + max_frl_rate = 6; + else + max_frl_rate = 0; + + 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(bool enable_frl, struct drm_hdmi_info *hdmi, struct dc_edid_caps *edid_caps) { edid_caps->scdc_present = hdmi->scdc.supported; + if (enable_frl) { + 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/dc/clk_mgr/dcn31/dcn31_clk_mgr.c b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn31/dcn31_clk_mgr.c index 89fc482947eff..bc14e65be8244 100644 --- a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn31/dcn31_clk_mgr.c +++ b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn31/dcn31_clk_mgr.c @@ -93,6 +93,9 @@ static int dcn31_get_active_display_cnt_wa( if (dc_is_dp_signal(stream->signal) && !stream->dpms_off) display_count++; + /* FRL can't be tracked by DIG enablement */ + if (dc_is_hdmi_frl_signal(stream->signal)) + display_count++; } for (i = 0; i < dc->link_count; i++) { diff --git a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn314/dcn314_clk_mgr.c b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn314/dcn314_clk_mgr.c index b08a70a2f571d..c7c7c7ee76d43 100644 --- a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn314/dcn314_clk_mgr.c +++ b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn314/dcn314_clk_mgr.c @@ -127,6 +127,9 @@ static int dcn314_get_active_display_cnt_wa( if (dc_is_dp_signal(stream->signal) && !stream->dpms_off) display_count++; + /* FRL can't be tracked by DIG enablement */ + if (dc_is_hdmi_frl_signal(stream->signal)) + display_count++; } for (i = 0; i < dc->link_count; i++) { diff --git a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn315/dcn315_clk_mgr.c b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn315/dcn315_clk_mgr.c index 3a651c1a866d9..e715fc5f88dae 100644 --- a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn315/dcn315_clk_mgr.c +++ b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn315/dcn315_clk_mgr.c @@ -69,6 +69,9 @@ static int dcn315_get_active_display_cnt_wa( stream->signal == SIGNAL_TYPE_DVI_SINGLE_LINK || stream->signal == SIGNAL_TYPE_DVI_DUAL_LINK) tmds_present = true; + /* FRL can't be tracked by DIG enablement */ + if (dc_is_hdmi_frl_signal(stream->signal)) + display_count++; } for (i = 0; i < dc->link_count; i++) { diff --git a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn316/dcn316_clk_mgr.c b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn316/dcn316_clk_mgr.c index e9d492d8c8d49..d621fc2191066 100644 --- a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn316/dcn316_clk_mgr.c +++ b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn316/dcn316_clk_mgr.c @@ -81,6 +81,9 @@ static int dcn316_get_active_display_cnt_wa( stream->signal == SIGNAL_TYPE_DVI_SINGLE_LINK || stream->signal == SIGNAL_TYPE_DVI_DUAL_LINK) tmds_present = true; + /* FRL can't be tracked by DIG enablement */ + if (dc_is_hdmi_frl_signal(stream->signal)) + display_count++; } for (i = 0; i < dc->link_count; i++) { diff --git a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn32/dcn32_clk_mgr.c b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn32/dcn32_clk_mgr.c index fda6cade30a8d..b123bbf142c34 100644 --- a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn32/dcn32_clk_mgr.c +++ b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn32/dcn32_clk_mgr.c @@ -283,6 +283,18 @@ static void dcn32_update_clocks_update_dtb_dto(struct clk_mgr_internal *clk_mgr, dto_params.otg_inst = pipe_ctx->stream_res.tg->inst; dto_params.ref_dtbclk_khz = ref_dtbclk_khz; + if (dc_is_hdmi_frl_signal(pipe_ctx->stream->signal) || + dccg->ctx->dc->link_srv->dp_is_128b_132b_signal(pipe_ctx)) { + dto_params.pixclk_khz = pipe_ctx->stream->timing.pix_clk_100hz / 10; + + if (pipe_ctx->stream_res.audio != NULL) + dto_params.req_audio_dtbclk_khz = 24000; + } + + if (dc_is_hdmi_signal(pipe_ctx->stream->signal) || + dc_is_dvi_signal(pipe_ctx->stream->signal)) + dto_params.is_hdmi = true; + dccg->funcs->set_dtbclk_dto(clk_mgr->dccg, &dto_params); //dccg->funcs->set_audio_dtbclk_dto(clk_mgr->dccg, &dto_params); } diff --git a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn35/dcn35_clk_mgr.c b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn35/dcn35_clk_mgr.c index 2798088842f41..35bc1fb409426 100644 --- a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn35/dcn35_clk_mgr.c +++ b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn35/dcn35_clk_mgr.c @@ -232,6 +232,8 @@ void dcn35_disable_otg_wa(struct clk_mgr *clk_mgr_base, struct dc_state *context if (old_pipe->stream && new_pipe->stream && old_pipe->stream == new_pipe->stream) { has_active_hpo = dccg->ctx->dc->link_srv->dp_is_128b_132b_signal(old_pipe) && dccg->ctx->dc->link_srv->dp_is_128b_132b_signal(new_pipe); + has_active_hpo = has_active_hpo || (old_pipe->stream->signal == SIGNAL_TYPE_HDMI_FRL && + new_pipe->stream->signal == SIGNAL_TYPE_HDMI_FRL); } @@ -271,6 +273,18 @@ static void dcn35_update_clocks_update_dtb_dto(struct clk_mgr_internal *clk_mgr, dto_params.otg_inst = pipe_ctx->stream_res.tg->inst; dto_params.ref_dtbclk_khz = ref_dtbclk_khz; + if (dc_is_hdmi_frl_signal(pipe_ctx->stream->signal) || + dccg->ctx->dc->link_srv->dp_is_128b_132b_signal(pipe_ctx)) { + dto_params.pixclk_khz = pipe_ctx->stream->timing.pix_clk_100hz / 10; + + if (pipe_ctx->stream_res.audio != NULL) + dto_params.req_audio_dtbclk_khz = 24000; + } + + if (dc_is_hdmi_signal(pipe_ctx->stream->signal) || + dc_is_dvi_signal(pipe_ctx->stream->signal)) + dto_params.is_hdmi = true; + dccg->funcs->set_dtbclk_dto(clk_mgr->dccg, &dto_params); //dccg->funcs->set_audio_dtbclk_dto(clk_mgr->dccg, &dto_params); } diff --git a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn401/dcn401_clk_mgr.c b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn401/dcn401_clk_mgr.c index 82c1a55a22710..54c917ecad529 100644 --- a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn401/dcn401_clk_mgr.c +++ b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn401/dcn401_clk_mgr.c @@ -542,6 +542,7 @@ static void dcn401_update_clocks_update_dtb_dto(struct clk_mgr_internal *clk_mgr ASSERT(otg_master->stream_res.pix_clk_params.controller_id >= CONTROLLER_ID_D0); use_hpo_encoder = dccg->ctx->dc->link_srv->dp_is_128b_132b_signal(otg_master); + use_hpo_encoder |= dc_is_hdmi_frl_signal(otg_master->stream->signal); if (!use_hpo_encoder) continue; @@ -1092,6 +1093,9 @@ static unsigned int dcn401_build_update_display_clocks_sequence( bool update_dispclk = false; bool update_dppclk = false; bool dppclk_lowered = false; + struct pipe_ctx *otg_master; + bool frl_present = false; + unsigned int i; unsigned int num_steps = 0; @@ -1123,7 +1127,21 @@ static unsigned int dcn401_build_update_display_clocks_sequence( dcn401_is_ppclk_dpm_enabled(clk_mgr_internal, PPCLK_DTBCLK)) { /* DCCG requires KHz precision for DTBCLK */ block_sequence[num_steps].params.update_hardmin_params.ppclk = PPCLK_DTBCLK; - block_sequence[num_steps].params.update_hardmin_params.freq_mhz = khz_to_mhz_ceil(new_clocks->ref_dtbclk_khz); + block_sequence[num_steps].params.update_hardmin_params.freq_mhz = (uint16_t)khz_to_mhz_ceil(new_clocks->ref_dtbclk_khz); + for (i = 0; i < context->stream_count; i++) { + otg_master = resource_get_otg_master_for_stream( + &context->res_ctx, context->streams[i]); + if (otg_master != NULL && + otg_master->stream != NULL && + dc_is_hdmi_frl_signal(otg_master->stream->signal)) { + frl_present = true; + break; + } + } + if (frl_present) + block_sequence[num_steps].params.update_hardmin_params.freq_mhz = + (uint16_t)clk_mgr_base->bw_params->clk_table.entries[ + clk_mgr_base->bw_params->clk_table.num_entries_per_clk.num_dtbclk_levels - 1].dtbclk_mhz; block_sequence[num_steps].params.update_hardmin_params.response = &clk_mgr_base->clks.ref_dtbclk_khz; block_sequence[num_steps].func = CLK_MGR401_UPDATE_HARDMIN_PPCLK; num_steps++; diff --git a/drivers/gpu/drm/amd/display/dc/core/dc.c b/drivers/gpu/drm/amd/display/dc/core/dc.c index b3530fbf32f7f..bcd665fbca126 100644 --- a/drivers/gpu/drm/amd/display/dc/core/dc.c +++ b/drivers/gpu/drm/amd/display/dc/core/dc.c @@ -3743,6 +3743,423 @@ static bool update_planes_and_stream_state(struct dc *dc, } +static void program_cursor_attributes_sequence( + struct dc *dc, + struct dc_stream_state *stream, + struct dc_state *context, + struct block_sequence_state *seq_state) +{ + int k; + struct pipe_ctx *pipe_to_program = NULL; + bool enable_cursor_offload = dc_dmub_srv_is_cursor_offload_enabled(dc); + + for (k = 0; k < (int)dc->res_pool->pipe_count; k++) { + struct pipe_ctx *tmp_pipe = &context->res_ctx.pipe_ctx[k]; + + if (tmp_pipe->stream != stream) + continue; + + if (!pipe_to_program) { + pipe_to_program = tmp_pipe; + + if (enable_cursor_offload && dc->hwss.begin_cursor_offload_update) { + hwss_add_begin_cursor_offload_update(seq_state, dc, tmp_pipe); + } else { + hwss_add_cursor_lock(seq_state, dc, pipe_to_program, true); + if (pipe_to_program->next_odm_pipe) + hwss_add_cursor_lock(seq_state, dc, pipe_to_program->next_odm_pipe, true); + } + } + + hwss_add_set_cursor_attribute(seq_state, dc, tmp_pipe); + if (dc->ctx->dmub_srv) + hwss_add_send_update_cursor_info_to_dmu(seq_state, tmp_pipe, k); + if (dc->hwss.set_cursor_sdr_white_level) + hwss_add_set_cursor_sdr_white_level(seq_state, dc, tmp_pipe); + if (enable_cursor_offload && dc->hwss.update_cursor_offload_pipe) + hwss_add_update_cursor_offload_pipe(seq_state, dc, tmp_pipe); + } + + if (pipe_to_program) { + if (enable_cursor_offload && dc->hwss.commit_cursor_offload_update) { + hwss_add_commit_cursor_offload_update(seq_state, dc, pipe_to_program); + } else { + hwss_add_cursor_lock(seq_state, dc, pipe_to_program, false); + if (pipe_to_program->next_odm_pipe) + hwss_add_cursor_lock(seq_state, dc, pipe_to_program->next_odm_pipe, false); + } + } +} + +static void program_cursor_position_sequence( + struct dc *dc, + struct dc_stream_state *stream, + struct dc_state *context, + struct block_sequence_state *seq_state) +{ + int k; + struct pipe_ctx *pipe_to_program = NULL; + bool enable_cursor_offload = dc_dmub_srv_is_cursor_offload_enabled(dc); + + for (k = 0; k < (int)dc->res_pool->pipe_count; k++) { + struct pipe_ctx *tmp_pipe = &context->res_ctx.pipe_ctx[k]; + + if (tmp_pipe->stream != stream || + (!tmp_pipe->plane_res.mi && !tmp_pipe->plane_res.hubp) || + !tmp_pipe->plane_state || + (!tmp_pipe->plane_res.xfm && !tmp_pipe->plane_res.dpp) || + (!tmp_pipe->plane_res.ipp && !tmp_pipe->plane_res.dpp)) + continue; + + if (!pipe_to_program) { + pipe_to_program = tmp_pipe; + + if (enable_cursor_offload && dc->hwss.begin_cursor_offload_update) + hwss_add_begin_cursor_offload_update(seq_state, dc, tmp_pipe); + else + hwss_add_cursor_lock(seq_state, dc, pipe_to_program, true); + } + + hwss_add_set_cursor_position(seq_state, dc, tmp_pipe); + if (enable_cursor_offload && dc->hwss.update_cursor_offload_pipe) + hwss_add_update_cursor_offload_pipe(seq_state, dc, tmp_pipe); + + if (dc->ctx->dmub_srv) + hwss_add_send_update_cursor_info_to_dmu(seq_state, tmp_pipe, k); + } + + if (pipe_to_program) { + if (enable_cursor_offload && dc->hwss.commit_cursor_offload_update) + hwss_add_commit_cursor_offload_update(seq_state, dc, pipe_to_program); + else + hwss_add_cursor_lock(seq_state, dc, pipe_to_program, false); + } +} + +static void add_update_info_frame_sequence( + struct block_sequence_state *seq_state, + struct pipe_ctx *pipe_ctx) +{ + bool is_hdmi_tmds; + bool is_dp; + bool is_hdmi_frl; + + if (!pipe_ctx || !pipe_ctx->stream) + return; + + if (pipe_ctx->stream_res.stream_enc == NULL && + pipe_ctx->stream_res.hpo_frl_stream_enc == NULL) + return; + + is_hdmi_tmds = dc_is_hdmi_tmds_signal(pipe_ctx->stream->signal); + is_dp = dc_is_dp_signal(pipe_ctx->stream->signal); + + is_hdmi_frl = dc_is_hdmi_frl_signal(pipe_ctx->stream->signal); + if (!is_hdmi_tmds && !is_dp && !is_hdmi_frl) + return; + + if (is_hdmi_tmds) { + hwss_add_stream_enc_update_hdmi_info_packets(seq_state, pipe_ctx); + return; + } + + if (is_hdmi_frl) { + hwss_add_hpo_frl_stream_enc_update_hdmi_info_packets(seq_state, pipe_ctx); + return; + } + + if (is_dp) { + if (dp_is_128b_132b_signal(pipe_ctx)) { + hwss_add_hpo_dp_stream_enc_update_dp_info_packets_sdp_line_num(seq_state, pipe_ctx); + hwss_add_hpo_dp_stream_enc_update_dp_info_packets(seq_state, pipe_ctx); + } else { + hwss_add_stream_enc_update_dp_info_packets_sdp_line_num(seq_state, pipe_ctx); + hwss_add_stream_enc_update_dp_info_packets(seq_state, pipe_ctx); + } + } +} + +static void add_link_update_dsc_config_sequence( + struct block_sequence_state *seq_state, + struct pipe_ctx *pipe_ctx, + struct dsc_config *dsc_cfg, + struct dsc_optc_config *dsc_optc_cfg) +{ + struct display_stream_compressor *dsc = pipe_ctx->stream_res.dsc; + struct dc_stream_state *stream = pipe_ctx->stream; + struct dc *dc = stream->ctx->dc; + struct dccg *dccg = dc->res_pool->dccg; + struct pipe_ctx *top_pipe = pipe_ctx; + struct pipe_ctx *odm_pipe = NULL; + int opp_cnt = 1; + bool should_use_dto_dscclk = false; + struct dsc_config dsc_pps_cfg; + uint8_t *dsc_packed_pps = stream->dsc_packed_pps; + int last_dsc_set_config_step = 0; + + if (!stream->timing.flags.DSC || !dsc) + return; + + while (top_pipe->prev_odm_pipe) + top_pipe = top_pipe->prev_odm_pipe; + + for (odm_pipe = top_pipe->next_odm_pipe; odm_pipe; odm_pipe = odm_pipe->next_odm_pipe) + opp_cnt++; + + memset(dsc_cfg, 0, sizeof(*dsc_cfg)); + memset(dsc_optc_cfg, 0, sizeof(*dsc_optc_cfg)); + + dsc_cfg->pic_width = (stream->timing.h_addressable + + top_pipe->dsc_padding_params.dsc_hactive_padding + + stream->timing.h_border_left + + stream->timing.h_border_right) / opp_cnt; + dsc_cfg->pic_height = stream->timing.v_addressable + + stream->timing.v_border_top + + stream->timing.v_border_bottom; + dsc_cfg->pixel_encoding = stream->timing.pixel_encoding; + dsc_cfg->color_depth = stream->timing.display_color_depth; + dsc_cfg->is_odm = top_pipe->next_odm_pipe ? true : false; + dsc_cfg->dc_dsc_cfg = stream->timing.dsc_cfg; + ASSERT(dsc_cfg->dc_dsc_cfg.num_slices_h % opp_cnt == 0); + dsc_cfg->dc_dsc_cfg.num_slices_h /= opp_cnt; + dsc_cfg->dsc_padding = 0; + + if (dccg && dccg->funcs->set_dto_dscclk && + stream->timing.pix_clk_100hz > 480000) + should_use_dto_dscclk = true; + + if (should_use_dto_dscclk) + hwss_add_dccg_set_dto_dscclk(seq_state, dccg, dsc->inst, + dsc_cfg->dc_dsc_cfg.num_slices_h); + + last_dsc_set_config_step = *seq_state->num_steps; + hwss_add_dsc_set_config(seq_state, dsc, dsc_cfg, dsc_optc_cfg); + hwss_add_dsc_enable_with_opp(seq_state, top_pipe); + + for (odm_pipe = top_pipe->next_odm_pipe; odm_pipe; odm_pipe = odm_pipe->next_odm_pipe) { + struct display_stream_compressor *odm_dsc = odm_pipe->stream_res.dsc; + + if (should_use_dto_dscclk) + hwss_add_dccg_set_dto_dscclk(seq_state, dccg, odm_dsc->inst, + dsc_cfg->dc_dsc_cfg.num_slices_h); + + last_dsc_set_config_step = *seq_state->num_steps; + hwss_add_dsc_set_config(seq_state, odm_dsc, dsc_cfg, dsc_optc_cfg); + hwss_add_dsc_enable_with_opp(seq_state, odm_pipe); + } + + if (dc_is_dp_signal(stream->signal) && !dp_is_128b_132b_signal(pipe_ctx)) + hwss_add_stream_enc_dp_set_dsc_config(seq_state, + pipe_ctx->stream_res.stream_enc, + &seq_state->steps[last_dsc_set_config_step].params.dsc_set_config_simple_params.dsc_optc_cfg); + + hwss_add_tg_set_dsc_config(seq_state, top_pipe->stream_res.tg, + &seq_state->steps[last_dsc_set_config_step].params.dsc_set_config_simple_params.dsc_optc_cfg, true); + + memset(&dsc_pps_cfg, 0, sizeof(dsc_pps_cfg)); + dsc_pps_cfg.pic_width = stream->timing.h_addressable + + stream->timing.h_border_left + stream->timing.h_border_right; + dsc_pps_cfg.pic_height = stream->timing.v_addressable + + stream->timing.v_border_top + stream->timing.v_border_bottom; + dsc_pps_cfg.pixel_encoding = stream->timing.pixel_encoding; + dsc_pps_cfg.color_depth = stream->timing.display_color_depth; + dsc_pps_cfg.is_odm = top_pipe->next_odm_pipe ? true : false; + dsc_pps_cfg.dc_dsc_cfg = stream->timing.dsc_cfg; + dsc_pps_cfg.dsc_padding = top_pipe->dsc_padding_params.dsc_hactive_padding; + + if (dsc->funcs->dsc_get_packed_pps) { + dsc->funcs->dsc_get_packed_pps(dsc, &dsc_pps_cfg, dsc_packed_pps); + + if (dc_is_dp_signal(stream->signal)) { + if (dp_is_128b_132b_signal(pipe_ctx)) + hwss_add_hpo_dp_stream_enc_dp_set_dsc_pps_info_packet(seq_state, + pipe_ctx->stream_res.hpo_dp_stream_enc, + true, dsc_packed_pps, false); + else + hwss_add_stream_enc_dp_set_dsc_pps_info_packet(seq_state, + 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); + } + } +} + +static void commit_planes_do_stream_update_sequence(struct dc *dc, + struct dc_stream_state *stream, + struct dc_stream_update *stream_update, + enum surface_update_type update_type, + struct dc_state *context, + struct block_sequence block_sequence[MAX_HWSS_BLOCK_SEQUENCE_SIZE], + unsigned int *num_steps) +{ + int j; + struct block_sequence_state seq_state = { .steps = block_sequence, .num_steps = num_steps }; + struct dsc_config dsc_cfgs[MAX_PIPES]; + struct dsc_optc_config dsc_optc_cfgs[MAX_PIPES]; + unsigned int dsc_cfg_index = 0; + *num_steps = 0; // Initialize to 0 + + // Stream updates + for (j = 0; j < (int)dc->res_pool->pipe_count; j++) { + struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[j]; + + if (resource_is_pipe_type(pipe_ctx, OTG_MASTER) && pipe_ctx->stream == stream) { + + if (stream_update->periodic_interrupt && dc->hwss.setup_periodic_interrupt) + hwss_add_setup_periodic_interrupt(&seq_state, dc, pipe_ctx); + + if ((stream_update->hdr_static_metadata && !stream->use_dynamic_meta) || + stream_update->vrr_infopacket || + stream_update->vsc_infopacket || + stream_update->vsp_infopacket || + stream_update->hfvsif_infopacket || + stream_update->adaptive_sync_infopacket || + stream_update->vtem_infopacket || + stream_update->avi_infopacket) { + resource_build_info_frame(pipe_ctx); + add_update_info_frame_sequence(&seq_state, pipe_ctx); + + if (dc_is_dp_signal(pipe_ctx->stream->signal)) + hwss_add_dp_trace_source_sequence(&seq_state, + pipe_ctx->stream->link, + DPCD_SOURCE_SEQ_AFTER_UPDATE_INFO_FRAME); + } + + if (stream_update->hdr_static_metadata && + stream->use_dynamic_meta && + dc->hwss.set_dmdata_attributes && + pipe_ctx->stream->dmdata_address.quad_part != 0) + hwss_add_set_dmdata_attributes(&seq_state, pipe_ctx); + + if (stream_update->gamut_remap) + hwss_add_dpp_program_gamut_remap(&seq_state, pipe_ctx); + + if (stream_update->output_csc_transform) + hwss_add_program_output_csc(&seq_state, dc, pipe_ctx, + stream->output_color_space, + stream->csc_color_matrix.matrix, + pipe_ctx->stream_res.opp->inst); + + if (stream_update->dither_option) { + struct pipe_ctx *odm_pipe = pipe_ctx->next_odm_pipe; + resource_build_bit_depth_reduction_params(pipe_ctx->stream, + &pipe_ctx->stream->bit_depth_params); + hwss_add_opp_program_fmt(&seq_state, pipe_ctx->stream_res.opp, + &stream->bit_depth_params, + &stream->clamping); + while (odm_pipe) { + hwss_add_opp_program_fmt(&seq_state, odm_pipe->stream_res.opp, + &stream->bit_depth_params, + &stream->clamping); + odm_pipe = odm_pipe->next_odm_pipe; + } + } + + if (stream_update->cursor_attributes) + program_cursor_attributes_sequence(dc, stream, context, &seq_state); + + if (stream_update->cursor_position) + program_cursor_position_sequence(dc, stream, context, &seq_state); + + /* Full fe update*/ + if (update_type == UPDATE_TYPE_FAST) + continue; + + if (stream_update->dsc_config) + if (dsc_cfg_index < MAX_PIPES) { + add_link_update_dsc_config_sequence(&seq_state, + pipe_ctx, + &dsc_cfgs[dsc_cfg_index], + &dsc_optc_cfgs[dsc_cfg_index]); + dsc_cfg_index++; + } + + if (stream_update->mst_bw_update) { + if (stream_update->mst_bw_update->is_increase) + hwss_add_link_increase_mst_payload(&seq_state, + pipe_ctx, + stream_update->mst_bw_update->mst_stream_bw); + else + hwss_add_link_reduce_mst_payload(&seq_state, + pipe_ctx, + stream_update->mst_bw_update->mst_stream_bw); + } + + if (stream_update->pending_test_pattern) { + /* + * test pattern params depends on ODM topology + * changes that we could be applying to front + * end. Since at the current stage front end + * changes are not yet applied. We can only + * apply test pattern in hw based on current + * state and populate the final test pattern + * params in new state. If current and new test + * pattern params are different as result of + * different ODM topology being used, it will be + * detected and handle during front end + * programming update. + */ + hwss_add_dp_set_test_pattern(&seq_state, + stream->link, + stream->test_pattern.type, + stream->test_pattern.color_space, + stream->test_pattern.p_link_settings, + stream->test_pattern.p_custom_pattern, + stream->test_pattern.cust_pattern_size); + resource_build_test_pattern_params(&context->res_ctx, pipe_ctx); + } + + if (stream_update->dpms_off) { + // DPMS should not use partially updated pipe context + struct pipe_ctx *dpms_pipe_ctx = &dc->current_state->res_ctx.pipe_ctx[j]; + + if (*stream_update->dpms_off) { + hwss_add_link_set_dpms_off(&seq_state, dpms_pipe_ctx); + /* for dpms, keep acquired resources*/ + if (dpms_pipe_ctx->stream_res.audio && !dc->debug.az_endpoint_mute_only) + hwss_add_disable_audio_stream(&seq_state, dpms_pipe_ctx); + + hwss_add_dc_set_optimized_required(&seq_state, dc, true); + + } else { + if (get_seamless_boot_stream_count(context) == 0) + hwss_add_prepare_bandwidth(&seq_state, dc, dc->current_state); + hwss_add_link_set_dpms_on(&seq_state, dc->current_state, dpms_pipe_ctx); + } + } else if (pipe_ctx->stream->link->wa_flags.blank_stream_on_ocs_change && stream_update->output_color_space + && !stream->dpms_off && dc_is_dp_signal(pipe_ctx->stream->signal)) { + /* + * Workaround for firmware issue in some receivers where they don't pick up + * correct output color space unless DP link is disabled/re-enabled + */ + hwss_add_link_set_dpms_on(&seq_state, dc->current_state, pipe_ctx); + } + + if (stream_update->abm_level && pipe_ctx->stream_res.abm) { + bool should_program_abm = true; + + // if otg funcs defined check if blanked before programming + if (pipe_ctx->stream_res.tg->funcs->is_blanked) + if (pipe_ctx->stream_res.tg->funcs->is_blanked(pipe_ctx->stream_res.tg)) + should_program_abm = false; + + if (should_program_abm) { + if (*stream_update->abm_level == ABM_LEVEL_IMMEDIATE_DISABLE) { + hwss_add_abm_set_immediate_disable(&seq_state, dc, pipe_ctx); + } else { + hwss_add_abm_set_level(&seq_state, pipe_ctx->stream_res.abm, stream->abm_level); + } + } + } + } + } +} + static void commit_planes_do_stream_update(struct dc *dc, struct dc_stream_state *stream, struct dc_stream_update *stream_update, 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 7333f5905330c..594764b01ab35 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 @@ -1329,6 +1329,63 @@ void hwss_execute_sequence(struct dc *dc, case PHANTOM_HUBP_POST_ENABLE: hwss_phantom_hubp_post_enable(params); break; + case STREAM_ENC_UPDATE_HDMI_INFO_PACKETS: + hwss_stream_enc_update_hdmi_info_packets(params); + break; + case HPO_FRL_STREAM_ENC_UPDATE_HDMI_INFO_PACKETS: + hwss_hpo_frl_stream_enc_update_hdmi_info_packets(params); + break; + case HPO_DP_STREAM_ENC_UPDATE_DP_INFO_PACKETS_SDP_LINE_NUM: + hwss_hpo_dp_stream_enc_update_dp_info_packets_sdp_line_num(params); + break; + case HPO_DP_STREAM_ENC_UPDATE_DP_INFO_PACKETS: + hwss_hpo_dp_stream_enc_update_dp_info_packets(params); + break; + case STREAM_ENC_UPDATE_DP_INFO_PACKETS_SDP_LINE_NUM: + hwss_stream_enc_update_dp_info_packets_sdp_line_num(params); + break; + case STREAM_ENC_UPDATE_DP_INFO_PACKETS: + hwss_stream_enc_update_dp_info_packets(params); + break; + case DSC_SET_CONFIG_SIMPLE: + hwss_dsc_set_config_simple(params); + break; + case STREAM_ENC_DP_SET_DSC_CONFIG: + hwss_stream_enc_dp_set_dsc_config(params); + break; + case HPO_DP_STREAM_ENC_DP_SET_DSC_PPS_INFO_PACKET: + hwss_hpo_dp_stream_enc_dp_set_dsc_pps_info_packet(params); + break; + 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; + case LINK_INCREASE_MST_PAYLOAD: + hwss_link_increase_mst_payload(params); + break; + case LINK_REDUCE_MST_PAYLOAD: + hwss_link_reduce_mst_payload(params); + break; + case DP_SET_TEST_PATTERN: + hwss_dp_set_test_pattern(params); + break; + case LINK_SET_DPMS_OFF: + hwss_link_set_dpms_off(params); + break; + case DISABLE_AUDIO_STREAM: + hwss_disable_audio_stream(dc, params); + break; + case PREPARE_BANDWIDTH: + hwss_prepare_bandwidth(dc, params); + break; + case LINK_SET_DPMS_ON: + hwss_link_set_dpms_on(params); + break; default: ASSERT(false); break; @@ -3169,6 +3226,217 @@ void hwss_phantom_hubp_post_enable(union block_sequence_params *params) hubp->funcs->phantom_hubp_post_enable(hubp); } +void hwss_stream_enc_update_hdmi_info_packets(union block_sequence_params *params) +{ + if (params->stream_enc_update_hdmi_info_packets_params.pipe_ctx->stream_res.stream_enc && + params->stream_enc_update_hdmi_info_packets_params.pipe_ctx->stream_res.stream_enc->funcs->update_hdmi_info_packets) + params->stream_enc_update_hdmi_info_packets_params.pipe_ctx->stream_res.stream_enc->funcs->update_hdmi_info_packets( + params->stream_enc_update_hdmi_info_packets_params.pipe_ctx->stream_res.stream_enc, + ¶ms->stream_enc_update_hdmi_info_packets_params.pipe_ctx->stream_res.encoder_info_frame); +} + +void hwss_hpo_frl_stream_enc_update_hdmi_info_packets(union block_sequence_params *params) +{ + if (params->hpo_frl_stream_enc_update_hdmi_info_packets_params.pipe_ctx->stream_res.hpo_frl_stream_enc && + params->hpo_frl_stream_enc_update_hdmi_info_packets_params.pipe_ctx->stream_res.hpo_frl_stream_enc->funcs->update_hdmi_info_packets) + params->hpo_frl_stream_enc_update_hdmi_info_packets_params.pipe_ctx->stream_res.hpo_frl_stream_enc->funcs->update_hdmi_info_packets( + params->hpo_frl_stream_enc_update_hdmi_info_packets_params.pipe_ctx->stream_res.hpo_frl_stream_enc, + ¶ms->hpo_frl_stream_enc_update_hdmi_info_packets_params.pipe_ctx->stream_res.encoder_info_frame); +} + +void hwss_hpo_dp_stream_enc_update_dp_info_packets_sdp_line_num(union block_sequence_params *params) +{ + if (params->hpo_dp_stream_enc_update_dp_info_packets_sdp_line_num_params.pipe_ctx->stream_res.hpo_dp_stream_enc && + params->hpo_dp_stream_enc_update_dp_info_packets_sdp_line_num_params.pipe_ctx->stream_res.hpo_dp_stream_enc->funcs->update_dp_info_packets_sdp_line_num) + params->hpo_dp_stream_enc_update_dp_info_packets_sdp_line_num_params.pipe_ctx->stream_res.hpo_dp_stream_enc->funcs->update_dp_info_packets_sdp_line_num( + params->hpo_dp_stream_enc_update_dp_info_packets_sdp_line_num_params.pipe_ctx->stream_res.hpo_dp_stream_enc, + ¶ms->hpo_dp_stream_enc_update_dp_info_packets_sdp_line_num_params.pipe_ctx->stream_res.encoder_info_frame); +} + +void hwss_hpo_dp_stream_enc_update_dp_info_packets(union block_sequence_params *params) +{ + if (params->hpo_dp_stream_enc_update_dp_info_packets_params.pipe_ctx->stream_res.hpo_dp_stream_enc && + params->hpo_dp_stream_enc_update_dp_info_packets_params.pipe_ctx->stream_res.hpo_dp_stream_enc->funcs->update_dp_info_packets) + params->hpo_dp_stream_enc_update_dp_info_packets_params.pipe_ctx->stream_res.hpo_dp_stream_enc->funcs->update_dp_info_packets( + params->hpo_dp_stream_enc_update_dp_info_packets_params.pipe_ctx->stream_res.hpo_dp_stream_enc, + ¶ms->hpo_dp_stream_enc_update_dp_info_packets_params.pipe_ctx->stream_res.encoder_info_frame); +} + +void hwss_stream_enc_update_dp_info_packets_sdp_line_num(union block_sequence_params *params) +{ + if (params->stream_enc_update_dp_info_packets_sdp_line_num_params.pipe_ctx->stream_res.stream_enc && + params->stream_enc_update_dp_info_packets_sdp_line_num_params.pipe_ctx->stream_res.stream_enc->funcs->update_dp_info_packets_sdp_line_num) + params->stream_enc_update_dp_info_packets_sdp_line_num_params.pipe_ctx->stream_res.stream_enc->funcs->update_dp_info_packets_sdp_line_num( + params->stream_enc_update_dp_info_packets_sdp_line_num_params.pipe_ctx->stream_res.stream_enc, + ¶ms->stream_enc_update_dp_info_packets_sdp_line_num_params.pipe_ctx->stream_res.encoder_info_frame); +} + +void hwss_stream_enc_update_dp_info_packets(union block_sequence_params *params) +{ + if (params->stream_enc_update_dp_info_packets_params.pipe_ctx->stream_res.stream_enc && + params->stream_enc_update_dp_info_packets_params.pipe_ctx->stream_res.stream_enc->funcs->update_dp_info_packets) + params->stream_enc_update_dp_info_packets_params.pipe_ctx->stream_res.stream_enc->funcs->update_dp_info_packets( + params->stream_enc_update_dp_info_packets_params.pipe_ctx->stream_res.stream_enc, + ¶ms->stream_enc_update_dp_info_packets_params.pipe_ctx->stream_res.encoder_info_frame); +} + +void hwss_stream_enc_dp_set_dsc_config(union block_sequence_params *params) +{ + if (params->stream_enc_dp_set_dsc_config_params.stream_enc && + params->stream_enc_dp_set_dsc_config_params.stream_enc->funcs->dp_set_dsc_config) { + enum optc_dsc_mode dsc_mode = OPTC_DSC_DISABLED; + uint32_t dsc_bytes_per_pixel = 0; + uint32_t dsc_slice_width = 0; + const struct dsc_optc_config *dsc_optc_cfg = params->stream_enc_dp_set_dsc_config_params.dsc_optc_cfg; + + if (dsc_optc_cfg) { + dsc_mode = dsc_optc_cfg->is_pixel_format_444 ? + OPTC_DSC_ENABLED_444 : OPTC_DSC_ENABLED_NATIVE_SUBSAMPLED; + dsc_bytes_per_pixel = dsc_optc_cfg->bytes_per_pixel; + dsc_slice_width = dsc_optc_cfg->slice_width; + } + + params->stream_enc_dp_set_dsc_config_params.stream_enc->funcs->dp_set_dsc_config( + params->stream_enc_dp_set_dsc_config_params.stream_enc, + dsc_mode, dsc_bytes_per_pixel, dsc_slice_width); + } +} + +void hwss_hpo_dp_stream_enc_dp_set_dsc_pps_info_packet(union block_sequence_params *params) +{ + if (params->hpo_dp_stream_enc_dp_set_dsc_pps_info_packet_params.hpo_dp_stream_enc && + params->hpo_dp_stream_enc_dp_set_dsc_pps_info_packet_params.hpo_dp_stream_enc->funcs->dp_set_dsc_pps_info_packet) + params->hpo_dp_stream_enc_dp_set_dsc_pps_info_packet_params.hpo_dp_stream_enc->funcs->dp_set_dsc_pps_info_packet( + params->hpo_dp_stream_enc_dp_set_dsc_pps_info_packet_params.hpo_dp_stream_enc, + params->hpo_dp_stream_enc_dp_set_dsc_pps_info_packet_params.immediate_update, + params->hpo_dp_stream_enc_dp_set_dsc_pps_info_packet_params.dsc_packed_pps, + params->hpo_dp_stream_enc_dp_set_dsc_pps_info_packet_params.pps_sdp_stream); +} + +void hwss_stream_enc_dp_set_dsc_pps_info_packet(union block_sequence_params *params) +{ + if (params->stream_enc_dp_set_dsc_pps_info_packet_params.stream_enc && + params->stream_enc_dp_set_dsc_pps_info_packet_params.stream_enc->funcs->dp_set_dsc_pps_info_packet) + params->stream_enc_dp_set_dsc_pps_info_packet_params.stream_enc->funcs->dp_set_dsc_pps_info_packet( + params->stream_enc_dp_set_dsc_pps_info_packet_params.stream_enc, + params->stream_enc_dp_set_dsc_pps_info_packet_params.immediate_update, + params->stream_enc_dp_set_dsc_pps_info_packet_params.dsc_packed_pps, + 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; + + if (!hubp || !hubp->funcs->dmdata_set_attributes) + return; + + hubp->funcs->dmdata_set_attributes(hubp, + ¶ms->set_dmdata_attributes_params.attr); +} + +void hwss_dp_trace_source_sequence(union block_sequence_params *params) +{ + struct dc_link *link = params->dp_trace_source_sequence_params.link; + enum dpcd_source_sequence source = params->dp_trace_source_sequence_params.source; + + if (link && link->dc && link->dc->link_srv && link->dc->link_srv->dp_trace_source_sequence) + link->dc->link_srv->dp_trace_source_sequence(link, source); +} + +void hwss_link_increase_mst_payload(union block_sequence_params *params) +{ + struct pipe_ctx *pipe_ctx = params->link_increase_mst_payload_params.pipe_ctx; + uint32_t mst_stream_bw = params->link_increase_mst_payload_params.mst_stream_bw; + + if (pipe_ctx && pipe_ctx->stream && pipe_ctx->stream->link && + pipe_ctx->stream->link->dc && pipe_ctx->stream->link->dc->link_srv && + pipe_ctx->stream->link->dc->link_srv->increase_mst_payload) + pipe_ctx->stream->link->dc->link_srv->increase_mst_payload(pipe_ctx, mst_stream_bw); +} + +void hwss_link_reduce_mst_payload(union block_sequence_params *params) +{ + struct pipe_ctx *pipe_ctx = params->link_reduce_mst_payload_params.pipe_ctx; + uint32_t mst_stream_bw = params->link_reduce_mst_payload_params.mst_stream_bw; + + if (pipe_ctx && pipe_ctx->stream && pipe_ctx->stream->link && + pipe_ctx->stream->link->dc && pipe_ctx->stream->link->dc->link_srv && + pipe_ctx->stream->link->dc->link_srv->reduce_mst_payload) + pipe_ctx->stream->link->dc->link_srv->reduce_mst_payload(pipe_ctx, mst_stream_bw); +} + +void hwss_dp_set_test_pattern(union block_sequence_params *params) +{ + struct dc_link *link = params->dp_set_test_pattern_params.link; + enum dp_test_pattern test_pattern = params->dp_set_test_pattern_params.test_pattern; + enum dp_test_pattern_color_space test_pattern_color_space = + params->dp_set_test_pattern_params.test_pattern_color_space; + const struct link_training_settings *p_link_settings = + params->dp_set_test_pattern_params.p_link_settings; + const unsigned char *p_custom_pattern = params->dp_set_test_pattern_params.p_custom_pattern; + unsigned int cust_pattern_size = params->dp_set_test_pattern_params.cust_pattern_size; + + if (link && link->dc && link->dc->link_srv && link->dc->link_srv->dp_set_test_pattern) + link->dc->link_srv->dp_set_test_pattern(link, test_pattern, test_pattern_color_space, + p_link_settings, p_custom_pattern, cust_pattern_size); +} + +void hwss_link_set_dpms_off(union block_sequence_params *params) +{ + struct pipe_ctx *pipe_ctx = params->link_set_dpms_off_params.pipe_ctx; + + if (pipe_ctx && pipe_ctx->stream && pipe_ctx->stream->link && + pipe_ctx->stream->link->dc && pipe_ctx->stream->link->dc->link_srv && + pipe_ctx->stream->link->dc->link_srv->set_dpms_off) + pipe_ctx->stream->link->dc->link_srv->set_dpms_off(pipe_ctx); +} + +void hwss_disable_audio_stream(struct dc *dc, union block_sequence_params *params) +{ + if (dc->hwss.disable_audio_stream) + dc->hwss.disable_audio_stream( + params->disable_audio_stream_params.pipe_ctx); +} + +void hwss_prepare_bandwidth(struct dc *dc, union block_sequence_params *params) +{ + if (dc && dc->hwss.prepare_bandwidth) + dc->hwss.prepare_bandwidth(dc, + params->prepare_bandwidth_params.context); +} + +void hwss_link_set_dpms_on(union block_sequence_params *params) +{ + struct dc_state *state = params->link_set_dpms_on_params.state; + struct pipe_ctx *pipe_ctx = params->link_set_dpms_on_params.pipe_ctx; + + if (pipe_ctx && pipe_ctx->stream && pipe_ctx->stream->link && + pipe_ctx->stream->link->dc && pipe_ctx->stream->link->dc->link_srv && + pipe_ctx->stream->link->dc->link_srv->set_dpms_on) + pipe_ctx->stream->link->dc->link_srv->set_dpms_on(state, pipe_ctx); +} + +void hwss_dsc_set_config_simple(union block_sequence_params *params) +{ + struct display_stream_compressor *dsc = params->dsc_set_config_simple_params.dsc; + struct dsc_config *dsc_cfg = ¶ms->dsc_set_config_simple_params.dsc_cfg; + struct dsc_optc_config *dsc_optc_cfg = ¶ms->dsc_set_config_simple_params.dsc_optc_cfg; + + if (dsc && dsc->funcs && dsc->funcs->dsc_set_config) + dsc->funcs->dsc_set_config(dsc, dsc_cfg, dsc_optc_cfg); +} + void hwss_add_dccg_set_dto_dscclk(struct block_sequence_state *seq_state, struct dccg *dccg, int inst, int num_slices_h) { 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 05991a10f8bf2..33157adb2f1f9 100644 --- a/drivers/gpu/drm/amd/display/dc/core/dc_resource.c +++ b/drivers/gpu/drm/amd/display/dc/core/dc_resource.c @@ -4375,6 +4375,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 */ /** @@ -4401,6 +4433,30 @@ 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; +#if defined(CONFIG_DRM_AMD_DC_FP) + 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); + } + } +#endif } /** 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 e16de323f39c0..aabfcf0f8310d 100644 --- a/drivers/gpu/drm/amd/display/dc/core/dc_stream.c +++ b/drivers/gpu/drm/amd/display/dc/core/dc_stream.c @@ -67,6 +67,40 @@ void update_stream_signal(struct dc_stream_state *stream, struct dc_sink *sink) else stream->signal = SIGNAL_TYPE_DVI_SINGLE_LINK; } + if (dc_is_hdmi_frl_signal(stream->signal)) { + pix_clk = stream->timing.pix_clk_100hz / 10; + if (stream->timing.pixel_encoding == PIXEL_ENCODING_YCBCR420) + pix_clk /= 2; + + // YCbCr422 to use assume 12-bit interface always, clock stays the same + if (stream->timing.pixel_encoding != PIXEL_ENCODING_YCBCR422) { + switch (stream->timing.display_color_depth) { + case COLOR_DEPTH_666: + case COLOR_DEPTH_888: + break; + case COLOR_DEPTH_101010: + pix_clk = pix_clk * 10 / 8; + break; + case COLOR_DEPTH_121212: + pix_clk = pix_clk * 12 / 8; + break; + default: + break; + } + } + if (pix_clk != 0 && pix_clk < HDMI2_TMDS_MAX_PIXEL_CLOCK) + stream->signal = SIGNAL_TYPE_HDMI_TYPE_A; + if (stream->timing.pixel_encoding == PIXEL_ENCODING_YCBCR420 && + stream->timing.h_addressable > 4096) + stream->signal = SIGNAL_TYPE_HDMI_FRL; + if (stream->timing.rid != 0) + stream->signal = SIGNAL_TYPE_HDMI_FRL; + + 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; + } } bool dc_stream_construct(struct dc_stream_state *stream, diff --git a/drivers/gpu/drm/amd/display/dc/dc.h b/drivers/gpu/drm/amd/display/dc/dc.h index 37714d4371fba..be0431088cd01 100644 --- a/drivers/gpu/drm/amd/display/dc/dc.h +++ b/drivers/gpu/drm/amd/display/dc/dc.h @@ -46,6 +46,8 @@ #include "dmub/inc/dmub_cmd.h" +#include "dml/dml1_frl_cap_chk.h" + #include "sspl/dc_spl_types.h" struct abm_save_restore; @@ -84,6 +86,55 @@ struct dcn_dccg_reg_state; #define NUM_FAST_FLIPS_TO_STEADY_STATE 20 +struct frl_cap_chk_intermediates_fixed31_32 { + int 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_max; + struct fixed31_32 f_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; + struct fixed31_32 ap; + struct fixed31_32 r_ap; + struct fixed31_32 avg_audio_packets_line; + struct fixed31_32 margin; + int audio_packets_line; + int blank_audio_min; +}; + +struct frl_cap_chk_params_fixed31_32 { + int lanes; + struct fixed31_32 f_pixel_clock_nominal; /* Pixel Clock rate (Hz) */ + struct fixed31_32 r_bit_nominal; /* FRL bitrate (bps) */ + int audio_packet_type; + struct fixed31_32 f_audio; /* Audio rate (Hz) */ + int h_active; /* Active pixels per line */ + int h_blank; /* Blanking pixels per line */ + int bpc; /* Bits per component */ + int vic; /* Video Identification Code */ + + enum hdmi_frl_pixel_encoding pixel_encoding; + + 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 */ + + /* outputs */ + struct frl_dml_borrow_params borrow_params; + int average_tribyte_rate; +}; + /* Display Core Interfaces */ struct dc_versions { const char *dc_ver; @@ -332,6 +383,7 @@ struct dc_caps { bool ips_v2_support; uint32_t num_of_internal_disp; enum dp_protocol_version max_dp_protocol_version; + bool hdmi_hpo; unsigned int mall_size_per_mem_channel; unsigned int mall_size_total; unsigned int cursor_cache_size; @@ -537,6 +589,9 @@ struct dc_config { int smart_mux_version; bool ignore_dpref_ss; bool enable_mipi_converter_optimization; + bool enable_frl; + bool force_hdmi21_frl_enc_enable; + bool skip_frl_pretraining; bool use_default_clock_table; bool force_bios_enable_lttpr; uint8_t force_bios_fixed_vs; @@ -1095,6 +1150,19 @@ struct dc_debug_options { bool validate_dml_output; bool enable_dmcub_surface_flip; bool usbc_combo_phy_reset_wa; + bool force_vrr; + bool force_fva; + int max_frl_rate; + int force_frl_rate; + bool ignore_ffe; + 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; + bool enable_dram_clock_change_one_display_vactive; /* TODO - remove once tested */ bool legacy_dp2_lt; @@ -1666,6 +1734,7 @@ struct dc_scratch_space { bool edp_sink_present; struct dp_trace dp_trace; + volatile bool is_link_locked; /* caps is the same as reported_link_cap. link_traing use * reported_link_cap. Will clean up. TODO @@ -1680,6 +1749,7 @@ struct dc_scratch_space { * management of the override pointers. */ struct dc_link_training_overrides preferred_training_settings; + struct dc_hdmi_frl_link_training_overrides preferred_hdmi_frl_settings; struct dp_audio_test_data audio_test_data; enum gpio_ddc_line ddc_hw_inst; @@ -1725,6 +1795,12 @@ struct dc_scratch_space { struct panel_cntl *panel_cntl; struct link_encoder *link_enc; + struct hpo_frl_link_encoder *hpo_frl_link_enc; + struct dc_hdmi_frl_link_settings frl_reported_link_cap; + struct dc_hdmi_frl_link_settings frl_verified_link_cap; + struct dc_hdmi_frl_link_settings frl_link_settings; + struct dc_hdmi_frl_flags frl_flags; + union hdmi_idcc_cable_id hdmi_cable_id; struct graphics_object_id link_id; /* External encoder eg. NUTMEG or TRAVIS used on CIK APUs. */ @@ -2056,6 +2132,17 @@ struct dc_plane_state *dc_get_surface_for_mpcc(struct dc *dc, uint32_t dc_get_opp_for_plane(struct dc *dc, struct dc_plane_state *plane); +bool dc_set_fva_vrr_adjust(struct dc *dc, + struct dc_stream_state *stream, + struct fva_adj *fva_adj, + struct dc_crtc_timing_adjust *vrr_adj); + +int dc_get_hw_max_fva_factor(struct dc *dc, + struct dc_stream_state *stream, + unsigned int max_pixel_clock); + +void dc_set_vstartup_start(struct dc *dc, + struct dc_stream_state *stream); void dc_set_disable_128b_132b_stream_overhead(bool disable); @@ -2267,6 +2354,12 @@ enum dc_status dc_link_dp_read_hpd_rx_irq_data( struct dc_link *link, union hpd_irq_data *irq_data); +bool dc_link_frl_poll_status_flag(struct dc_link *link); +bool dc_link_frl_margin_check_uncompressed_video( + const struct dc_link *link, + struct frl_cap_chk_params_fixed31_32 *params, + struct frl_cap_chk_intermediates_fixed31_32 *inter); + /* The function clears recorded DP RX states in the link. DM should call this * function when it is resuming from S3 power state to previously connected links. * @@ -2291,6 +2384,12 @@ uint32_t dc_link_bandwidth_kbps( const struct dc_link *link, const struct dc_link_settings *link_setting); +/* The function returns effective HDMI FRL bandwidth given link rate. + * return - total effective link bandwidth in kbps. + */ +uint32_t dc_link_frl_bandwidth_kbps(const struct dc_link *link, + enum hdmi_frl_link_rate link_rate); + struct dp_audio_bandwidth_params { const struct dc_crtc_timing *crtc_timing; enum dp_link_encoding link_encoding; @@ -2342,6 +2441,8 @@ void dc_get_cur_link_res_map(const struct dc *dc, uint32_t *map); */ void dc_restore_link_res_map(const struct dc *dc, uint32_t *map); +void dc_link_wait_for_unlocked(struct dc_link *link); + /* TODO: this is not meant to be exposed to DM. Should switch to stream update * interface i.e stream_update->dsc_config */ @@ -2395,6 +2496,16 @@ const struct dc_link_settings *dc_link_get_link_cap(const struct dc_link *link); */ enum dc_link_encoding_format dc_link_get_highest_encoding_format(const struct dc_link *link); +/* get max frl link settings the link can enable with all things considered. + * (i.e TX/RX capabilities and link verification result. + * + * @link - a link with FRL RX connection + * return - max frl link settings the link can enable. + * + */ +struct dc_hdmi_frl_link_settings *dc_link_get_frl_link_cap( + struct dc_link *link); + /* Check if a RX (ex. DP sink, MST hub, passive or active dongle) is connected * to a link with dp connector signal type. * @link - a link with dp connector signal type @@ -2444,6 +2555,16 @@ void dc_link_set_preferred_link_settings(struct dc *dc, struct dc_link_settings *link_setting, struct dc_link *link); +/* Force FRL link settings to always use a specific value until reboot to a + * specific link. If link has already been enabled, the interface will also + * switch to desired link settings immediately. This is a debug interface to + * generic FRL issue trouble shooting. + */ +void dc_link_set_preferred_frl_link_settings(struct dc *dc, + struct dc_hdmi_frl_link_settings *link_setting, + struct dc_hdmi_frl_link_training_overrides *lt_overrides, + struct dc_link *link); + /* Force DP link to customize a specific link training behavior by overriding to * standard DP specs defined protocol. This is a debug interface to trouble shoot * display specific link training issues or apply some display specific @@ -3117,6 +3238,14 @@ struct dc_register_software_state { uint32_t symclk32_le_enable[2]; /* SYMCLK32_LE_CNTL->SYMCLK32_LE0_EN from dccg31_enable_symclk32_le() enable */ uint32_t symclk32_le_gate_disable[2]; /* DCCG_GATE_DISABLE_CNTL3->SYMCLK32_LE0_GATE_DISABLE from debug.root_clock_optimization.bits.symclk32_le */ + /* HDMI Clock Control */ + uint32_t hdmicharclk_enable; /* HDMICHARCLK0_CLOCK_CNTL->HDMICHARCLK0_EN from dccg31_enable_hdmicharclk() */ + uint32_t hdmicharclk_src_sel; /* HDMICHARCLK0_CLOCK_CNTL->HDMICHARCLK0_SRC_SEL from dccg31_enable_hdmicharclk() phypll_inst source */ + uint32_t hdmistreamclk_src_sel; /* HDMISTREAMCLK_CNTL->HDMISTREAMCLK0_SRC_SEL from dccg31_set_hdmistreamclk() src selection */ + uint32_t hdmistreamclk_dto_force_dis; /* HDMISTREAMCLK_CNTL->HDMISTREAMCLK0_DTO_FORCE_DIS from dccg31_set_hdmistreamclk() DTO force bypass */ + uint32_t hdmistreamclk_dto_phase; /* HDMISTREAMCLK0_DTO_PARAM->HDMISTREAMCLK0_DTO_PHASE from dccg31_disable_hdmistreamclk() */ + uint32_t hdmistreamclk_dto_modulo; /* HDMISTREAMCLK0_DTO_PARAM->HDMISTREAMCLK0_DTO_MODULO from dccg31_disable_hdmistreamclk() */ + /* DPIA Clock Control */ uint32_t dpiaclk_540m_dto_modulo; /* DPIACLK_540M_DTO_MODULO->DPIA 540MHz DTO modulo */ uint32_t dpiaclk_540m_dto_phase; /* DPIACLK_540M_DTO_PHASE->DPIA 540MHz DTO phase */ diff --git a/drivers/gpu/drm/amd/display/dc/dc_dsc.h b/drivers/gpu/drm/amd/display/dc/dc_dsc.h index 101bce6b8de64..c0564167c5872 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 fcd3ab4b00459..5923a5f112a99 100644 --- a/drivers/gpu/drm/amd/display/dc/dc_hdmi_types.h +++ b/drivers/gpu/drm/amd/display/dc/dc_hdmi_types.h @@ -65,19 +65,65 @@ struct dp_hdmi_dongle_signature_data { #define HDMI_SCDC_CONFIG_1 0x31 #define HDMI_SCDC_SOURCE_TEST_REQ 0x35 #define HDMI_SCDC_STATUS_FLAGS 0x40 +#define HDMI_SCDC_LTP_REQ 0x41 #define HDMI_SCDC_ERR_DETECT 0x50 #define HDMI_SCDC_TEST_CONFIG 0xC0 #define HDMI_SCDC_MANUFACTURER_OUI 0xD0 #define HDMI_SCDC_DEVICE_ID 0xDB +/* IDCC defines (HDMI 2.0) */ +#define HDMI_IDCC_ADDRESS 0x50 +#define HDMI_IDCC_MARKER0 0xAE +#define HDMI_IDCC_MARKER1 0x6E +#define HDMI_IDCC_MARKER2 0x60 + +enum hdmi_idcc_scope { + HDMI_IDCC_SCOPE_WRITE = 0x00, + HDMI_IDCC_SCOPE_RW_CA = 0x01, + HDMI_IDCC_SCOPE_RW_SINK = 0x02, +}; + +union hdmi_idcc_source_id { + struct { + uint8_t SI_PCA_n:1; + uint8_t AC_n:1; + uint8_t RESERVED:6; + } bits; + uint8_t raw; +}; + +union hdmi_idcc_cable_id { + struct { + uint8_t Cat1_n:1; + uint8_t Cat2_n:1; + uint8_t Cat3_n:1; + uint8_t Cat4_n:1; + uint8_t RESERVED:4; + uint8_t HEAC_n:1; + uint8_t PCA_DEP_n:1; + uint8_t MonoDir_n:1; + uint8_t MonoDirErr_n:1; + uint8_t PCA_ON_n:1; + uint8_t no_DeEmphasis_n:1; + uint8_t no_PreShoot_n:1; + uint8_t RESERVED2:1; + uint8_t RND_bits_7_0:8; + uint8_t RND_bits_15_8:8; + } bits; + uint8_t raw[4]; +}; union hdmi_scdc_update_read_data { uint8_t byte[2]; struct { uint8_t STATUS_UPDATE:1; uint8_t CED_UPDATE:1; uint8_t RR_TEST:1; - uint8_t RESERVED:5; + uint8_t SOURCE_TEST_UPDATE:1; + uint8_t FRL_START:1; + uint8_t FLT_UPDATE:1; + uint8_t RSED_UPDATE:1; + uint8_t RESERVED:1; uint8_t RESERVED2:8; } fields; }; @@ -89,7 +135,20 @@ union hdmi_scdc_status_flags_data { uint8_t CH0_LOCKED:1; uint8_t CH1_LOCKED:1; uint8_t CH2_LOCKED:1; - uint8_t RESERVED:4; + uint8_t LANE3_LOCKED:1; + uint8_t RESERVED:1; + uint8_t FLT_READY:1; + uint8_t DSC_DECODEFAIL:1; + } fields; +}; + +union hdmi_scdc_LTP_req_data { + uint8_t byte[2]; + struct { + uint8_t LN0_LTP_REQ:4; + uint8_t LN1_LTP_REQ:4; + uint8_t LN2_LTP_REQ:4; + uint8_t LN3_LTP_REQ:4; } fields; }; @@ -106,10 +165,12 @@ union hdmi_scdc_ced_data { uint8_t CH2_7HIGH:7; uint8_t CH2_VALID:1; uint8_t CHECKSUM:8; - uint8_t RESERVED:8; - uint8_t RESERVED2:8; - uint8_t RESERVED3:8; - uint8_t RESERVED4:4; + uint8_t LN3_8LOW:8; + uint8_t LN3_7HIGH:7; + uint8_t LN3_VALID:1; + uint8_t RSC_8LOW:8; + uint8_t RSC_7HIGH:7; + uint8_t RSC_VALID:1; } fields; }; @@ -130,4 +191,95 @@ union hdmi_scdc_device_id_data { } fields; }; +union hdmi_scdc_configuration { + uint8_t byte[2]; + struct { + uint8_t RR_ENABLE:1; + uint8_t FLT_NO_RETRAIN:1; + uint8_t RESERVED:6; + uint8_t FRL_RATE:4; + uint8_t FFE_LEVELS:4; + } fields; +}; + +union hdmi_scdc_source_test_req { + uint8_t byte; + struct { + uint8_t RESERVED:1; + uint8_t TXFFE_PRESHOOT:1; + uint8_t TXFFE_DEEMPHASIS:1; + uint8_t TXFFE_NOFFE:1; + uint8_t RESERVED2:1; + uint8_t FLT_NO_TIMEOUT:1; + uint8_t DSC_FRL_MAX:1; + uint8_t FRL_MAX:1; + } fields; +}; + +union hdmi_scdc_test_config_Data { + uint8_t byte; + struct { + uint8_t TEST_READ_REQUEST_DELAY:7; + uint8_t TEST_READ_REQUEST: 1; + } fields; +}; + +enum hdmi_frl_borrow_mode { + BORROW_MODE_NONE, + BORROW_MODE_FROM_ACTIVE, + BORROW_MODE_FROM_BLANK +}; + +enum link_result { + LINK_RESULT_UNKNOWN = 0, + LINK_RESULT_SUCCESS, + LINK_RESULT_LOWER_LINKRATE, + LINK_RESULT_TIMEOUT, + LINK_RESULT_FALLBACK +}; + +enum hdmi_frl_link_rate { + HDMI_FRL_LINK_RATE_DISABLE = 0, + HDMI_FRL_LINK_RATE_3GBPS, + HDMI_FRL_LINK_RATE_6GBPS, + HDMI_FRL_LINK_RATE_6GBPS_4LANE, + HDMI_FRL_LINK_RATE_8GBPS, + HDMI_FRL_LINK_RATE_10GBPS, + HDMI_FRL_LINK_RATE_12GBPS, + HDMI_FRL_LINK_RATE_16GBPS, + HDMI_FRL_LINK_RATE_20GBPS, + HDMI_FRL_LINK_RATE_24GBPS +}; + +struct frl_borrow_params { + int audio_packets_line; + int hc_active_target; + int hc_blank_target; + enum hdmi_frl_borrow_mode borrow_mode; +}; + +struct dc_hdmi_frl_link_settings { + enum hdmi_frl_link_rate frl_link_rate; + uint8_t frl_num_lanes; + struct frl_borrow_params borrow_params; + int average_tribyte_rate; +}; + +struct dc_hdmi_frl_flags { + int force_frl_rate; + bool ignore_ffe; + int select_ffe; + int limit_ffe; + bool force_frl_always; + bool force_frl_dsc; + bool force_frl_max; + bool apply_vsdb_rcc_wa; +}; + +struct dc_hdmi_frl_link_training_overrides { + bool force_frl_always; + bool force_frl_max; + uint8_t max_retries; + bool valid; +}; #endif /* DC_HDMI_TYPES_H */ diff --git a/drivers/gpu/drm/amd/display/dc/dc_hw_types.h b/drivers/gpu/drm/amd/display/dc/dc_hw_types.h index c2ca08d26e371..c13a824c9d352 100644 --- a/drivers/gpu/drm/amd/display/dc/dc_hw_types.h +++ b/drivers/gpu/drm/amd/display/dc/dc_hw_types.h @@ -886,6 +886,8 @@ struct dc_dsc_config { bool ycbcr422_simple; /* Tell DSC engine to convert YCbCr 4:2:2 to 'YCbCr 4:2:2 simple'. */ int32_t rc_buffer_size; /* DSC RC buffer block size in bytes */ bool is_frl; /* indicate if DSC is applied based on HDMI FRL sink's capability */ + bool is_vic_all_bpp; /* indicate of DSC_ALL_BPP = 1 */ + uint32_t total_chunk_kbytes; /* total chunk kbytes in EDID */ bool is_dp; /* indicate if DSC is applied based on DP's capability */ uint32_t mst_pbn; /* pbn of display on dsc mst hub */ const struct dc_dsc_rc_params_override *rc_params_ovrd; /* DM owned memory. If not NULL, apply custom dsc rc params */ @@ -1162,6 +1164,12 @@ struct tg_color { uint16_t color_b_cb; }; +struct fva_adj { + unsigned int pixel_clock_100hz; + unsigned int max_pixel_clock_100hz; + unsigned int fva_factor; +}; + enum symclk_state { SYMCLK_OFF_TX_OFF, SYMCLK_ON_TX_ON, diff --git a/drivers/gpu/drm/amd/display/dc/dc_types.h b/drivers/gpu/drm/amd/display/dc/dc_types.h index c08d5c005df6e..6ee36321281e0 100644 --- a/drivers/gpu/drm/amd/display/dc/dc_types.h +++ b/drivers/gpu/drm/amd/display/dc/dc_types.h @@ -77,6 +77,7 @@ struct dc_perf_trace { }; #define NUM_PIXEL_FORMATS 10 +#define DTBCLK_LIMIT 2920 enum tiling_mode { TILING_MODE_INVALID, @@ -174,6 +175,14 @@ struct dc_panel_patch { unsigned int max_dsc_target_bpp_limit; unsigned int embedded_tiled_slave; unsigned int disable_fams; + unsigned int hdmi_spe_handling; + unsigned int block_420_Freesync; + unsigned int block_10g; + unsigned int hdmi_comp_manual; + unsigned int hdmi_comp_auto; + unsigned int force_frl; + unsigned int vsdb_rcc_wa; + unsigned int delay_hdmi_link_training; unsigned int skip_avmute; unsigned int skip_audio_sab_check; unsigned int mst_start_top_delay; @@ -221,6 +230,17 @@ struct dc_edid_caps { bool scdc_present; bool analog; + /*HDMI 2.1 caps*/ + uint8_t max_frl_rate; + bool frl_dsc_support; + bool frl_dsc_10bpc; + bool frl_dsc_12bpc; + bool frl_dsc_all_bpp; + bool frl_dsc_native_420; + uint8_t frl_dsc_max_slices; + uint8_t frl_dsc_max_frl_rate; + uint8_t frl_dsc_total_chunk_kbytes; + struct dc_panel_patch panel_patch; }; @@ -545,6 +565,7 @@ struct audio_info { struct audio_check { unsigned int audio_packet_type; unsigned int max_audiosample_rate; + unsigned int max_channel_count; unsigned int acat; }; enum dc_infoframe_type { @@ -899,6 +920,9 @@ struct dsc_dec_dpcd_caps { uint32_t branch_overall_throughput_0_mps; /* In MPs */ uint32_t branch_overall_throughput_1_mps; /* In MPs */ uint32_t branch_max_line_width; + bool is_frl; /* Decoded format */ + bool is_vic_all_bpp; + uint32_t total_chunk_kbytes; bool is_dp; /* Decoded format */ }; diff --git a/drivers/gpu/drm/amd/display/dc/dccg/dcn30/dcn30_dccg.c b/drivers/gpu/drm/amd/display/dc/dccg/dcn30/dcn30_dccg.c index ca947d710ad15..9df06df6c7f1b 100644 --- a/drivers/gpu/drm/amd/display/dc/dccg/dcn30/dcn30_dccg.c +++ b/drivers/gpu/drm/amd/display/dc/dccg/dcn30/dcn30_dccg.c @@ -42,8 +42,48 @@ #define DC_LOGGER \ dccg->ctx->logger +void dccg3_enable_hdmicharclk(struct dccg *dccg, int hpo_inst, int phypll_inst) +{ + struct dcn_dccg *dccg_dcn = TO_DCN_DCCG(dccg); + + ASSERT(hpo_inst >= 0 && phypll_inst >= 0); + REG_UPDATE_2(HDMICHARCLK_CLOCK_CNTL[hpo_inst], + HDMICHARCLK0_EN, 1, + HDMICHARCLK0_SRC_SEL, phypll_inst); + + /* Enable FORCE_EN for SYMCLK */ + switch (phypll_inst) { + case 0: + REG_UPDATE_2(PHYASYMCLK_CLOCK_CNTL, + PHYASYMCLK_FORCE_EN, 1, + PHYASYMCLK_FORCE_SRC_SEL, 1); + break; + case 1: + REG_UPDATE_2(PHYBSYMCLK_CLOCK_CNTL, + PHYBSYMCLK_FORCE_EN, 1, + PHYBSYMCLK_FORCE_SRC_SEL, 1); + break; + case 2: + REG_UPDATE_2(PHYCSYMCLK_CLOCK_CNTL, + PHYCSYMCLK_FORCE_EN, 1, + PHYCSYMCLK_FORCE_SRC_SEL, 1); + break; + default: + BREAK_TO_DEBUGGER(); + return; + } +} + +void dccg3_disable_hdmicharclk(struct dccg *dccg, int hpo_inst) +{ + struct dcn_dccg *dccg_dcn = TO_DCN_DCCG(dccg); + + REG_WRITE(HDMICHARCLK_CLOCK_CNTL[hpo_inst], 0); +} static const struct dccg_funcs dccg3_funcs = { + .enable_hdmicharclk = dccg3_enable_hdmicharclk, + .disable_hdmicharclk = dccg3_disable_hdmicharclk, .update_dpp_dto = dccg2_update_dpp_dto, .get_dccg_ref_freq = dccg2_get_dccg_ref_freq, .set_fifo_errdet_ovr_en = dccg2_set_fifo_errdet_ovr_en, diff --git a/drivers/gpu/drm/amd/display/dc/dccg/dcn30/dcn30_dccg.h b/drivers/gpu/drm/amd/display/dc/dccg/dcn30/dcn30_dccg.h index 3f1da7f3a91cb..379fe94734ba8 100644 --- a/drivers/gpu/drm/amd/display/dc/dccg/dcn30/dcn30_dccg.h +++ b/drivers/gpu/drm/amd/display/dc/dccg/dcn30/dcn30_dccg.h @@ -63,4 +63,12 @@ struct dccg *dccg30_create( const struct dccg_shift *dccg_shift, const struct dccg_mask *dccg_mask); +void dccg3_enable_hdmicharclk( + struct dccg *dccg, + int hpo_inst, + int phypll_inst); + +void dccg3_disable_hdmicharclk( + struct dccg *dccg, + int hpo_inst); #endif //__DCN30_DCCG_H__ diff --git a/drivers/gpu/drm/amd/display/dc/dccg/dcn31/dcn31_dccg.c b/drivers/gpu/drm/amd/display/dc/dccg/dcn31/dcn31_dccg.c index a72bf413fad6b..94e9a24172a46 100644 --- a/drivers/gpu/drm/amd/display/dc/dccg/dcn31/dcn31_dccg.c +++ b/drivers/gpu/drm/amd/display/dc/dccg/dcn31/dcn31_dccg.c @@ -664,6 +664,87 @@ void dccg31_set_dispclk_change_mode( change_mode == DISPCLK_CHANGE_MODE_RAMPING ? 2 : 0); } +void dccg31_set_hdmistreamclk( + struct dccg *dccg, + enum streamclk_source src, + uint32_t otg_inst) +{ + (void)otg_inst; + struct dcn_dccg *dccg_dcn = TO_DCN_DCCG(dccg); + + if (src == REFCLK) { + REG_UPDATE_2(HDMISTREAMCLK_CNTL, + HDMISTREAMCLK0_SRC_SEL, 0, /* SEL_REFCLK0 */ + HDMISTREAMCLK0_DTO_FORCE_DIS, 1); /* DTO_FORCE_BYPASS */ + } else { + REG_UPDATE_2(HDMISTREAMCLK_CNTL, + HDMISTREAMCLK0_SRC_SEL, 1, /* SEL_DTBCLK0 */ + HDMISTREAMCLK0_DTO_FORCE_DIS, 1); /* DTO_FORCE_BYPASS */ + } +} + +static void dccg31_disable_hdmistreamclk(struct dccg *dccg) +{ + struct dcn_dccg *dccg_dcn = TO_DCN_DCCG(dccg); + + if (dccg->ctx->dc->debug.root_clock_optimization.bits.hdmistream) + REG_UPDATE_2(HDMISTREAMCLK0_DTO_PARAM, + HDMISTREAMCLK0_DTO_PHASE, 0, + HDMISTREAMCLK0_DTO_MODULO, 1); +} + +void dccg31_enable_hdmicharclk(struct dccg *dccg, int hpo_inst, int phypll_inst) +{ + struct dcn_dccg *dccg_dcn = TO_DCN_DCCG(dccg); + + ASSERT(hpo_inst >= 0 && phypll_inst >= 0); + if (dccg->ctx->dc->debug.root_clock_optimization.bits.hdmichar) { + REG_UPDATE(DCCG_GATE_DISABLE_CNTL4, + HDMICHARCLK0_ROOT_GATE_DISABLE, 1); + REG_UPDATE(DCCG_GATE_DISABLE_CNTL2, + HDMICHARCLK0_GATE_DISABLE, 1); + } + + REG_UPDATE_2(HDMICHARCLK_CLOCK_CNTL[hpo_inst], + HDMICHARCLK0_EN, 1, + HDMICHARCLK0_SRC_SEL, phypll_inst); + + /* Enable FORCE_EN for SYMCLK */ + switch (phypll_inst) { + case 0: + REG_UPDATE_2(PHYASYMCLK_CLOCK_CNTL, + PHYASYMCLK_FORCE_EN, 1, + PHYASYMCLK_FORCE_SRC_SEL, 1); + break; + case 1: + REG_UPDATE_2(PHYBSYMCLK_CLOCK_CNTL, + PHYBSYMCLK_FORCE_EN, 1, + PHYBSYMCLK_FORCE_SRC_SEL, 1); + break; + case 2: + REG_UPDATE_2(PHYCSYMCLK_CLOCK_CNTL, + PHYCSYMCLK_FORCE_EN, 1, + PHYCSYMCLK_FORCE_SRC_SEL, 1); + break; + default: + BREAK_TO_DEBUGGER(); + return; + } +} + +void dccg31_disable_hdmicharclk(struct dccg *dccg, int hpo_inst) +{ + struct dcn_dccg *dccg_dcn = TO_DCN_DCCG(dccg); + + REG_WRITE(HDMICHARCLK_CLOCK_CNTL[hpo_inst], 0); + if (dccg->ctx->dc->debug.root_clock_optimization.bits.hdmichar) { + REG_UPDATE(DCCG_GATE_DISABLE_CNTL2, + HDMICHARCLK0_GATE_DISABLE, 0); + REG_UPDATE(DCCG_GATE_DISABLE_CNTL4, + HDMICHARCLK0_ROOT_GATE_DISABLE, 0); + } +} + void dccg31_init(struct dccg *dccg) { /* Set HPO stream encoder to use refclk to avoid case where PHY is @@ -692,6 +773,10 @@ void dccg31_init(struct dccg *dccg) dccg31_set_physymclk(dccg, 3, PHYSYMCLK_FORCE_SRC_SYMCLK, false); dccg31_set_physymclk(dccg, 4, PHYSYMCLK_FORCE_SRC_SYMCLK, false); } + dccg31_disable_hdmistreamclk(dccg); + + if (dccg->ctx->dc->debug.root_clock_optimization.bits.hdmichar) + dccg31_disable_hdmicharclk(dccg, 0); } void dccg31_otg_add_pixel(struct dccg *dccg, @@ -835,6 +920,9 @@ void dccg31_read_reg_state(struct dccg *dccg, struct dcn_dccg_reg_state *dccg_re } static const struct dccg_funcs dccg31_funcs = { + .enable_hdmicharclk = dccg31_enable_hdmicharclk, + .disable_hdmicharclk = dccg31_disable_hdmicharclk, + .set_hdmistreamclk = dccg31_set_hdmistreamclk, .update_dpp_dto = dccg31_update_dpp_dto, .get_dccg_ref_freq = dccg31_get_dccg_ref_freq, .dccg_init = dccg31_init, diff --git a/drivers/gpu/drm/amd/display/dc/dccg/dcn31/dcn31_dccg.h b/drivers/gpu/drm/amd/display/dc/dccg/dcn31/dcn31_dccg.h index b5e3849ef12a8..2c2d682a82c21 100644 --- a/drivers/gpu/drm/amd/display/dc/dccg/dcn31/dcn31_dccg.h +++ b/drivers/gpu/drm/amd/display/dc/dccg/dcn31/dcn31_dccg.h @@ -202,6 +202,19 @@ void dccg31_set_physymclk( void dccg31_set_audio_dtbclk_dto( struct dccg *dccg, const struct dtbclk_dto_params *params); +void dccg31_enable_hdmicharclk( + struct dccg *dccg, + int hpo_inst, + int phypll_inst); + +void dccg31_disable_hdmicharclk( + struct dccg *dccg, + int hpo_inst); + +void dccg31_set_hdmistreamclk( + struct dccg *dccg, + enum streamclk_source src, + uint32_t otg_inst); void dccg31_update_dpp_dto( struct dccg *dccg, diff --git a/drivers/gpu/drm/amd/display/dc/dccg/dcn314/dcn314_dccg.c b/drivers/gpu/drm/amd/display/dc/dccg/dcn314/dcn314_dccg.c index ac6a909187c05..1a4d4cd58ad88 100644 --- a/drivers/gpu/drm/amd/display/dc/dccg/dcn314/dcn314_dccg.c +++ b/drivers/gpu/drm/amd/display/dc/dccg/dcn314/dcn314_dccg.c @@ -286,6 +286,28 @@ void dccg314_set_dpstreamclk( } } +static void dccg314_set_hdmistreamclk( + struct dccg *dccg, + enum streamclk_source src, + uint32_t otg_inst) +{ + struct dcn_dccg *dccg_dcn = TO_DCN_DCCG(dccg); + + /* set the dtbclk_p source */ + dccg314_set_dtbclk_p_src(dccg, src, otg_inst); + + if (src == REFCLK) { + REG_UPDATE_2(HDMISTREAMCLK_CNTL, + HDMISTREAMCLK0_EN, 0, /* SEL_REFCLK */ + HDMISTREAMCLK0_DTO_FORCE_DIS, 1); /* DTO_FORCE_BYPASS */ + } else { + REG_UPDATE_3(HDMISTREAMCLK_CNTL, + HDMISTREAMCLK0_EN, 1, /* selects one of the dtbclk_p as per HDMISTREAMCLK0_SRC_SEL */ + HDMISTREAMCLK0_SRC_SEL, otg_inst, /* Selects dtbclk_p as source for hdmistreamclk */ + HDMISTREAMCLK0_DTO_FORCE_DIS, 1); /* DTO_FORCE_BYPASS */ + } +} + static void dccg314_init(struct dccg *dccg) { int otg_inst; @@ -355,6 +377,9 @@ static void dccg314_dpp_root_clock_control( } static const struct dccg_funcs dccg314_funcs = { + .enable_hdmicharclk = dccg31_enable_hdmicharclk, + .disable_hdmicharclk = dccg31_disable_hdmicharclk, + .set_hdmistreamclk = dccg314_set_hdmistreamclk, .update_dpp_dto = dccg31_update_dpp_dto, .dpp_root_clock_control = dccg314_dpp_root_clock_control, .get_dccg_ref_freq = dccg31_get_dccg_ref_freq, diff --git a/drivers/gpu/drm/amd/display/dc/dccg/dcn32/dcn32_dccg.c b/drivers/gpu/drm/amd/display/dc/dccg/dcn32/dcn32_dccg.c index 18b9c5ceed437..f75c5aa2d23ed 100644 --- a/drivers/gpu/drm/amd/display/dc/dccg/dcn32/dcn32_dccg.c +++ b/drivers/gpu/drm/amd/display/dc/dccg/dcn32/dcn32_dccg.c @@ -329,7 +329,75 @@ static void dccg32_otg_drop_pixel(struct dccg *dccg, OTG_DROP_PIXEL[otg_inst], 1); } +static void dccg32_set_hdmistreamclk(struct dccg *dccg, + enum streamclk_source src, + uint32_t otg_inst) +{ + struct dcn_dccg *dccg_dcn = TO_DCN_DCCG(dccg); + + /* set the dtbclk_p source */ + /* always program refclk as DTBCLK. No use-case expected to require DPREFCLK as refclk */ + dccg32_set_dtbclk_p_src(dccg, DTBCLK0, otg_inst); + + if (src == REFCLK) { + REG_UPDATE_2(HDMISTREAMCLK_CNTL, + HDMISTREAMCLK0_EN, 0, /* SEL_REFCLK */ + HDMISTREAMCLK0_DTO_FORCE_DIS, 1); /* DTO_FORCE_BYPASS */ + } else { + REG_UPDATE_3(HDMISTREAMCLK_CNTL, + HDMISTREAMCLK0_EN, 1, /* selects one of the dtbclk_p as per HDMISTREAMCLK0_SRC_SEL */ + HDMISTREAMCLK0_SRC_SEL, otg_inst, /* Selects dtbclk_p as source for hdmistreamclk */ + HDMISTREAMCLK0_DTO_FORCE_DIS, 1); /* DTO_FORCE_BYPASS */ + } +} + +static void dccg32_enable_hdmicharclk(struct dccg *dccg, int hpo_inst, + int phypll_inst) +{ + struct dcn_dccg *dccg_dcn = TO_DCN_DCCG(dccg); + + ASSERT(hpo_inst >= 0 && phypll_inst >= 0); + REG_UPDATE_2(HDMICHARCLK_CLOCK_CNTL[hpo_inst], + HDMICHARCLK0_EN, 1, + HDMICHARCLK0_SRC_SEL, phypll_inst); + + /* Enable FORCE_EN for SYMCLK */ + switch (phypll_inst) { + case 0: + REG_UPDATE_2(PHYASYMCLK_CLOCK_CNTL, + PHYASYMCLK_FORCE_EN, 1, + PHYASYMCLK_FORCE_SRC_SEL, 1); + break; + case 1: + REG_UPDATE_2(PHYBSYMCLK_CLOCK_CNTL, + PHYBSYMCLK_FORCE_EN, 1, + PHYBSYMCLK_FORCE_SRC_SEL, 1); + break; + case 2: + REG_UPDATE_2(PHYCSYMCLK_CLOCK_CNTL, + PHYCSYMCLK_FORCE_EN, 1, + PHYCSYMCLK_FORCE_SRC_SEL, 1); + break; + case 3: + REG_UPDATE_2(PHYDSYMCLK_CLOCK_CNTL, + PHYDSYMCLK_FORCE_EN, 1, + PHYDSYMCLK_FORCE_SRC_SEL, 1); + break; + case 4: + REG_UPDATE_2(PHYESYMCLK_CLOCK_CNTL, + PHYESYMCLK_FORCE_EN, 1, + PHYESYMCLK_FORCE_SRC_SEL, 1); + break; + default: + BREAK_TO_DEBUGGER(); + return; + } +} + static const struct dccg_funcs dccg32_funcs = { + .enable_hdmicharclk = dccg32_enable_hdmicharclk, + .disable_hdmicharclk = dccg3_disable_hdmicharclk, + .set_hdmistreamclk = dccg32_set_hdmistreamclk, .update_dpp_dto = dccg2_update_dpp_dto, .get_dccg_ref_freq = dccg32_get_dccg_ref_freq, .dccg_init = dccg31_init, diff --git a/drivers/gpu/drm/amd/display/dc/dccg/dcn35/dcn35_dccg.c b/drivers/gpu/drm/amd/display/dc/dccg/dcn35/dcn35_dccg.c index efac64165ccd6..483cd9ab7eb76 100644 --- a/drivers/gpu/drm/amd/display/dc/dccg/dcn35/dcn35_dccg.c +++ b/drivers/gpu/drm/amd/display/dc/dccg/dcn35/dcn35_dccg.c @@ -495,6 +495,92 @@ static void dccg35_set_smclk32_se_rcg( } } +static void dccg35_set_hdmistreamclk_rcg( + struct dccg *dccg, + int inst, + bool enable) +{ + struct dcn_dccg *dccg_dcn = TO_DCN_DCCG(dccg); + + if (!dccg->ctx->dc->debug.root_clock_optimization.bits.hdmistream && enable) + return; + + switch (inst) { + case 0: + REG_UPDATE(DCCG_GATE_DISABLE_CNTL3, HDMISTREAMCLK0_GATE_DISABLE, enable ? 0 : 1); + REG_UPDATE(DCCG_GATE_DISABLE_CNTL6, HDMISTREAMCLK0_ROOT_GATE_DISABLE, enable ? 0 : 1); + break; + default: + BREAK_TO_DEBUGGER(); + return; + } +} + +static void dccg35_set_hdmi_char_clk_rcg( + struct dccg *dccg, + int inst, + bool enable) +{ + struct dcn_dccg *dccg_dcn = TO_DCN_DCCG(dccg); + + /* Only 1 HPO in DCN35 */ + if (!dccg->ctx->dc->debug.root_clock_optimization.bits.hdmichar && enable) + return; + + switch (inst) { + case 0: + REG_UPDATE(DCCG_GATE_DISABLE_CNTL2, HDMICHARCLK0_GATE_DISABLE, enable ? 0 : 1); + REG_UPDATE(DCCG_GATE_DISABLE_CNTL4, HDMICHARCLK0_ROOT_GATE_DISABLE, enable ? 0 : 1); + break; + default: + BREAK_TO_DEBUGGER(); + return; + } +} + +static void dccg35_set_hdmi_char_clk_src_new( + struct dccg *dccg, + enum hdmi_char_clk src, + int inst) +{ + struct dcn_dccg *dccg_dcn = TO_DCN_DCCG(dccg); + + /* If DTBCLK_P#_EN is 0 refclock is selected as functional clock + * If DTBCLK_P#_EN is 1 functional clock is selected as DTBCLK_P#_SRC_SEL + */ + + switch (inst) { + case 0: + REG_UPDATE_2(HDMICHARCLK_CLOCK_CNTL[0], + HDMICHARCLK0_SRC_SEL, (src == HDMI_CHAR_REFCLK) ? 0 : src, + HDMICHARCLK0_EN, (src == HDMI_CHAR_REFCLK) ? 0 : 1); + break; + default: + BREAK_TO_DEBUGGER(); + return; + } +} + +static void dccg35_set_hdmistreamclk_src_new( + struct dccg *dccg, + enum hdmi_stream_clk_source src, + int inst) +{ + struct dcn_dccg *dccg_dcn = TO_DCN_DCCG(dccg); + + switch (inst) { + case 0: + REG_UPDATE_2(HDMISTREAMCLK_CNTL, HDMISTREAMCLK0_EN, + (src == HDMI_STREAM_REFCLK) ? 0 : 1, + DPSTREAMCLK0_SRC_SEL, + (src == HDMI_STREAM_REFCLK) ? 0 : src); + break; + default: + BREAK_TO_DEBUGGER(); + return; + } +} + static void dccg35_set_dsc_clk_src_new(struct dccg *dccg, int inst, enum dsc_clk_source src) { struct dcn_dccg *dccg_dcn = TO_DCN_DCCG(dccg); @@ -1107,6 +1193,35 @@ static void dccg35_enable_dpstreamclk_new(struct dccg *dccg, dccg35_set_dpstreamclk_src_new(dccg, src, inst); } +static void dccg35_disable_hdmistreamclk_new( + struct dccg *dccg, + int inst) +{ + dccg35_set_hdmistreamclk_src_new(dccg, HDMI_STREAM_REFCLK, inst); + dccg35_set_hdmistreamclk_rcg(dccg, inst, true); +} + +static void dccg35_enable_hdmistreamclk_new(struct dccg *dccg, + enum hdmi_stream_clk_source src, + int inst) +{ + dccg35_set_hdmistreamclk_rcg(dccg, inst, false); + dccg35_set_hdmistreamclk_src_new(dccg, src, inst); +} + +static void dccg35_enable_hdmicharclk_new(struct dccg *dccg, enum hdmi_char_clk src, + int inst) +{ + dccg35_set_hdmi_char_clk_rcg(dccg, inst, false); + dccg35_set_hdmi_char_clk_src_new(dccg, src, inst); +} + +static void dccg35_disable_hdmicharclk_new(struct dccg *dccg, int inst) +{ + dccg35_set_hdmi_char_clk_src_new(dccg, HDMI_CHAR_REFCLK, inst); + dccg35_set_hdmi_char_clk_rcg(dccg, inst, true); +} + void dccg35_trigger_dio_fifo_resync(struct dccg *dccg) { struct dcn_dccg *dccg_dcn = TO_DCN_DCCG(dccg); @@ -1539,6 +1654,88 @@ void dccg35_set_dpstreamclk_root_clock_gating(struct dccg *dccg, int dp_hpo_inst +static void dccg35_set_hdmistreamclk( + struct dccg *dccg, + enum streamclk_source src, + uint32_t otg_inst) +{ + struct dcn_dccg *dccg_dcn = TO_DCN_DCCG(dccg); + + /* set the dtbclk_p source */ + dccg35_set_dtbclk_p_src(dccg, src, otg_inst); + + if (src == REFCLK) { + REG_UPDATE(HDMISTREAMCLK_CNTL, + HDMISTREAMCLK0_EN, 0); /* SEL_REFCLK */ + } else { + REG_UPDATE_2(HDMISTREAMCLK_CNTL, + HDMISTREAMCLK0_EN, 1, /* selects one of the dtbclk_p as per HDMISTREAMCLK0_SRC_SEL */ + HDMISTREAMCLK0_SRC_SEL, otg_inst); /* Selects dtbclk_p as source for hdmistreamclk */ + } + DC_LOG_DEBUG("%s: OTG%d HDMISTREAMCLK_EN = %d, HDMISTREAMCLK_SRC_SEL = %d\n", + __func__, otg_inst, (src == REFCLK) ? 0 : 1, otg_inst); +} + +void dccg35_set_hdmistreamclk_root_clock_gating(struct dccg *dccg, bool enable) +{ + struct dcn_dccg *dccg_dcn = TO_DCN_DCCG(dccg); + + if (dccg->ctx->dc->debug.root_clock_optimization.bits.hdmistream) + REG_UPDATE(DCCG_GATE_DISABLE_CNTL6, HDMISTREAMCLK0_ROOT_GATE_DISABLE, enable ? 1 : 0); + + DC_LOG_DEBUG("%s: HDMISTREAMCLK0_ROOT_GATE_DISABLE = %d\n", __func__, enable ? 1 : 0); +} + +static void dccg35_enable_hdmicharclk(struct dccg *dccg, int hpo_inst, + int phypll_inst) +{ + struct dcn_dccg *dccg_dcn = TO_DCN_DCCG(dccg); + + if (dccg->ctx->dc->debug.root_clock_optimization.bits.hdmichar) { + REG_UPDATE(DCCG_GATE_DISABLE_CNTL4, + HDMICHARCLK0_ROOT_GATE_DISABLE, 1); + REG_UPDATE(DCCG_GATE_DISABLE_CNTL2, + HDMICHARCLK0_GATE_DISABLE, 1); + } + + ASSERT(hpo_inst >= 0 && phypll_inst >= 0); + REG_UPDATE_2(HDMICHARCLK_CLOCK_CNTL[hpo_inst], + HDMICHARCLK0_EN, 1, + HDMICHARCLK0_SRC_SEL, phypll_inst); + + /* Enable FORCE_EN for SYMCLK */ + switch (phypll_inst) { + case 0: + REG_UPDATE_2(PHYASYMCLK_CLOCK_CNTL, + PHYASYMCLK_EN, 1, + PHYASYMCLK_SRC_SEL, 1); + break; + case 1: + REG_UPDATE_2(PHYBSYMCLK_CLOCK_CNTL, + PHYBSYMCLK_EN, 1, + PHYBSYMCLK_SRC_SEL, 1); + break; + case 2: + REG_UPDATE_2(PHYCSYMCLK_CLOCK_CNTL, + PHYCSYMCLK_EN, 1, + PHYCSYMCLK_SRC_SEL, 1); + break; + case 3: + REG_UPDATE_2(PHYDSYMCLK_CLOCK_CNTL, + PHYDSYMCLK_EN, 1, + PHYDSYMCLK_SRC_SEL, 1); + break; + case 4: + REG_UPDATE_2(PHYESYMCLK_CLOCK_CNTL, + PHYESYMCLK_EN, 1, + PHYESYMCLK_SRC_SEL, 1); + break; + default: + BREAK_TO_DEBUGGER(); + return; + } +} + static void dccg35_set_physymclk_root_clock_gating( struct dccg *dccg, int phy_inst, @@ -1756,6 +1953,33 @@ void dccg35_disable_symclk32_se(struct dccg *dccg, int hpo_se_inst) } +static void dccg35_disable_hdmistreamclk(struct dccg *dccg) +{ + struct dcn_dccg *dccg_dcn = TO_DCN_DCCG(dccg); + + if (dccg->ctx->dc->debug.root_clock_optimization.bits.hdmistream) { +// REG_UPDATE_2(HDMISTREAMCLK0_DTO_PARAM, +// HDMISTREAMCLK0_DTO_PHASE, 0, +// HDMISTREAMCLK0_DTO_MODULO, 1); + REG_UPDATE(DCCG_GATE_DISABLE_CNTL6, HDMISTREAMCLK0_ROOT_GATE_DISABLE, 0); + + } +} + +static void dccg35_disable_hdmicharclk(struct dccg *dccg, int hpo_inst) +{ + struct dcn_dccg *dccg_dcn = TO_DCN_DCCG(dccg); + + REG_WRITE(HDMICHARCLK_CLOCK_CNTL[hpo_inst], 0); + if (dccg->ctx->dc->debug.root_clock_optimization.bits.hdmichar) { + REG_UPDATE(DCCG_GATE_DISABLE_CNTL2, + HDMICHARCLK0_GATE_DISABLE, 0); + REG_UPDATE(DCCG_GATE_DISABLE_CNTL4, + HDMICHARCLK0_ROOT_GATE_DISABLE, 0); + DC_LOG_DEBUG("%s: HDMICHARCLK0_ROOT_GATE_DISABLE = 0\n", __func__); + } +} + static void dccg35_init_cb(struct dccg *dccg) { (void)dccg; @@ -1793,6 +2017,9 @@ void dccg35_init(struct dccg *dccg) __func__, otg_inst); } + dccg35_disable_hdmistreamclk(dccg); + if (dccg->ctx->dc->debug.root_clock_optimization.bits.hdmichar) + dccg35_disable_hdmicharclk(dccg, 0); /* dccg35_enable_global_fgcg_rep( dccg, dccg->ctx->dc->debug.enable_fine_grain_clock_gating.bits @@ -2088,6 +2315,92 @@ void dccg35_disable_symclk_se(struct dccg *dccg, uint32_t stream_enc_inst, uint3 } } +static void dccg35_enable_hdmicharclk_cb(struct dccg *dccg, int hpo_inst, int phypll_inst) +{ + ASSERT(hpo_inst >= 0 && phypll_inst >= 0); + + /* Note that this is now done in DMU as part of phy fsm enable sequence, + * this function does not get called on x86 + */ + enum hdmi_char_clk src = (enum hdmi_char_clk) phypll_inst; + + dccg35_enable_hdmicharclk_new(dccg, src, hpo_inst); + + /* Select PHYD18 as final SYMCLK going to the PHY from DCCG */ + dccg35_set_physymclk_src_new(dccg, phypll_inst, PHYSYMCLK_PHYD18CLK); +} + +static void dccg35_disable_hdmicharclk_cb(struct dccg *dccg, int hpo_inst) +{ + ASSERT(hpo_inst >= 0); + + /* Note that this is now done in DMU as part of phy fsm disable sequence, + * this function does not get called on x86 + */ + dccg35_disable_hdmicharclk_new(dccg, hpo_inst); + + /* TBD Optionally check if SYMCLK is active and disable it */ +} + +static void dccg35_set_hdmistreamclk_cb( + struct dccg *dccg, + enum streamclk_source src, + uint32_t otg_inst) +{ + enum dtbclk_source dtb_clk_src; + enum hdmi_stream_clk_source hdmi_stream_clk_src; + + switch (src) { + case REFCLK: + dtb_clk_src = DTBCLK_REFCLK; + hdmi_stream_clk_src = HDMI_STREAM_REFCLK; + break; + case DPREFCLK: + dtb_clk_src = DTBCLK_DPREFCLK; + hdmi_stream_clk_src = (enum hdmi_stream_clk_source)otg_inst; + break; + case DTBCLK0: + dtb_clk_src = DTBCLK_DTBCLK0; + hdmi_stream_clk_src = (enum hdmi_stream_clk_source)otg_inst; + break; + default: + BREAK_TO_DEBUGGER(); + return; + } + + if (dtb_clk_src == DTBCLK_REFCLK && + hdmi_stream_clk_src == HDMI_STREAM_REFCLK) { + dccg35_disable_dtbclk_p_new(dccg, otg_inst); + dccg35_disable_hdmistreamclk_new(dccg, 0); + } else { + dccg35_enable_dtbclk_p_new(dccg, dtb_clk_src, otg_inst); + dccg35_enable_hdmistreamclk_new(dccg, + hdmi_stream_clk_src, + 0); + } +} + +static void dccg35_set_hdmistreamclk_root_clock_gating_cb( + struct dccg *dccg, + bool power_on) +{ + /* power_on set indicates we need to ungate + * Currently called from optimize_bandwidth and prepare_bandwidth calls + * Since clock source is not passed restore to refclock on ungate + * Instance 0 is implied here since only one streamclock resource + * Redundant as gating when enabled is acheived through set_hdmistreamclk + */ + if (power_on) { + dccg35_enable_hdmicharclk_new(dccg, HDMI_CHAR_REFCLK, 0); + dccg35_enable_hdmistreamclk_new(dccg, + HDMI_STREAM_REFCLK, + 0); + } else { + dccg35_disable_hdmistreamclk_new(dccg, 0); + dccg35_disable_hdmicharclk_new(dccg, 0); + } +} + static void dccg35_set_dpstreamclk_cb( struct dccg *dccg, enum streamclk_source src, @@ -2386,6 +2699,10 @@ void dccg35_root_gate_disable_control(struct dccg *dccg, uint32_t pipe_idx, uint } static const struct dccg_funcs dccg35_funcs_new = { + .enable_hdmicharclk = dccg35_enable_hdmicharclk_cb, + .disable_hdmicharclk = dccg35_disable_hdmicharclk_cb, + .set_hdmistreamclk = dccg35_set_hdmistreamclk_cb, + .set_hdmistreamclk_root_clock_gating = dccg35_set_hdmistreamclk_root_clock_gating_cb, .update_dpp_dto = dccg35_update_dpp_dto_cb, .dpp_root_clock_control = dccg35_dpp_root_clock_control_cb, .get_dccg_ref_freq = dccg31_get_dccg_ref_freq, @@ -2421,6 +2738,10 @@ static const struct dccg_funcs dccg35_funcs_new = { }; static const struct dccg_funcs dccg35_funcs = { + .enable_hdmicharclk = dccg35_enable_hdmicharclk, + .disable_hdmicharclk = dccg35_disable_hdmicharclk, + .set_hdmistreamclk = dccg35_set_hdmistreamclk, + .set_hdmistreamclk_root_clock_gating = dccg35_set_hdmistreamclk_root_clock_gating, .update_dpp_dto = dccg35_update_dpp_dto, .dpp_root_clock_control = dccg35_dpp_root_clock_control, .get_dccg_ref_freq = dccg31_get_dccg_ref_freq, diff --git a/drivers/gpu/drm/amd/display/dc/dccg/dcn401/dcn401_dccg.c b/drivers/gpu/drm/amd/display/dc/dccg/dcn401/dcn401_dccg.c index 97605a416031b..fef51174ffad5 100644 --- a/drivers/gpu/drm/amd/display/dc/dccg/dcn401/dcn401_dccg.c +++ b/drivers/gpu/drm/amd/display/dc/dccg/dcn401/dcn401_dccg.c @@ -381,6 +381,72 @@ static void dccg401_otg_drop_pixel(struct dccg *dccg, OTG_DROP_PIXEL[otg_inst], 1); } +void dccg401_set_hdmistreamclk( + struct dccg *dccg, + enum streamclk_source src, + uint32_t otg_inst) +{ + struct dcn_dccg *dccg_dcn = TO_DCN_DCCG(dccg); + + if (src == REFCLK) { + REG_UPDATE(HDMISTREAMCLK_CNTL, + HDMISTREAMCLK0_EN, 0); /* SEL_REFCLK */ + } else { + REG_UPDATE_2(HDMISTREAMCLK_CNTL, + HDMISTREAMCLK0_EN, 1, /* selects one of the dtbclk_p as per HDMISTREAMCLK0_SRC_SEL */ + HDMISTREAMCLK0_SRC_SEL, otg_inst); /* Selects dtbclk_p as source for hdmistreamclk */ + } +} + +void dccg401_enable_hdmicharclk(struct dccg *dccg, int hpo_inst, int phypll_inst) +{ + struct dcn_dccg *dccg_dcn = TO_DCN_DCCG(dccg); + + ASSERT(hpo_inst >= 0 && phypll_inst >= 0); + if (dccg->ctx->dc->debug.root_clock_optimization.bits.hdmichar) { + REG_UPDATE(DCCG_GATE_DISABLE_CNTL4, + HDMICHARCLK0_ROOT_GATE_DISABLE, 1); + REG_UPDATE(DCCG_GATE_DISABLE_CNTL2, + HDMICHARCLK0_GATE_DISABLE, 1); + } + + REG_UPDATE_2(HDMICHARCLK_CLOCK_CNTL[hpo_inst], + HDMICHARCLK0_EN, 1, + HDMICHARCLK0_SRC_SEL, phypll_inst); + + /* Enable FORCE_EN for SYMCLK */ + dccg401_set_physymclk(dccg, phypll_inst, PHYSYMCLK_FORCE_SRC_PHYD18CLK, true); +} + +void dccg401_disable_hdmicharclk(struct dccg *dccg, int hpo_inst) +{ + struct dcn_dccg *dccg_dcn = TO_DCN_DCCG(dccg); + //int phypll_inst = 0; + + ASSERT(hpo_inst >= 0); + //REG_GET(dccg, HDMICHARCLK_CLOCK_CNTL[hpo_inst], HDMICHARCLK0_SRC_SEL, &phypll_inst); + REG_WRITE(HDMICHARCLK_CLOCK_CNTL[hpo_inst], 0); + + /* TODO should we also disable physymclk? */ + /* Disable FORCE_EN for SYMCLK */ + //dccg401_set_physymclk(dccg, phypll_inst, PHYSYMCLK_FORCE_SRC_PHYD18CLK, true); + + if (dccg->ctx->dc->debug.root_clock_optimization.bits.hdmichar) { + REG_UPDATE(DCCG_GATE_DISABLE_CNTL2, + HDMICHARCLK0_GATE_DISABLE, 0); + REG_UPDATE(DCCG_GATE_DISABLE_CNTL4, + HDMICHARCLK0_ROOT_GATE_DISABLE, 0); + } +} + +static void dccg401_disable_hdmistreamclk(struct dccg *dccg) +{ + struct dcn_dccg *dccg_dcn = TO_DCN_DCCG(dccg); + + REG_UPDATE_2(HDMISTREAMCLK_CNTL, + HDMISTREAMCLK0_EN, 0, + HDMISTREAMCLK0_SRC_SEL, 0); +} void dccg401_enable_symclk32_le( struct dccg *dccg, int hpo_le_inst, @@ -719,6 +785,10 @@ void dccg401_init(struct dccg *dccg) dccg401_set_physymclk(dccg, 2, PHYSYMCLK_FORCE_SRC_SYMCLK, false); dccg401_set_physymclk(dccg, 3, PHYSYMCLK_FORCE_SRC_SYMCLK, false); } + dccg401_disable_hdmistreamclk(dccg); + + if (dccg->ctx->dc->debug.root_clock_optimization.bits.hdmichar) + dccg401_disable_hdmicharclk(dccg, 0); } void dccg401_set_dto_dscclk(struct dccg *dccg, uint32_t inst, uint32_t num_slices_h) @@ -879,6 +949,9 @@ void dccg401_disable_symclk_se(struct dccg *dccg, uint32_t stream_enc_inst, uint } static const struct dccg_funcs dccg401_funcs = { + .enable_hdmicharclk = dccg401_enable_hdmicharclk, + .disable_hdmicharclk = dccg401_disable_hdmicharclk, + .set_hdmistreamclk = dccg401_set_hdmistreamclk, .update_dpp_dto = dccg401_update_dpp_dto, .get_dccg_ref_freq = dccg401_get_dccg_ref_freq, .dccg_init = dccg401_init, diff --git a/drivers/gpu/drm/amd/display/dc/dccg/dcn401/dcn401_dccg.h b/drivers/gpu/drm/amd/display/dc/dccg/dcn401/dcn401_dccg.h index 5947a35363aac..95f0602466d3c 100644 --- a/drivers/gpu/drm/amd/display/dc/dccg/dcn401/dcn401_dccg.h +++ b/drivers/gpu/drm/amd/display/dc/dccg/dcn401/dcn401_dccg.h @@ -246,4 +246,8 @@ void dccg401_set_physymclk( enum physymclk_clock_source clk_src, bool force_enable); +void dccg401_set_hdmistreamclk(struct dccg *dccg, enum streamclk_source src, uint32_t otg_inst); +void dccg401_enable_hdmicharclk(struct dccg *dccg, int hpo_inst, int phypll_inst); +void dccg401_disable_hdmicharclk(struct dccg *dccg, int hpo_inst); + #endif //__DCN401_DCCG_H__ diff --git a/drivers/gpu/drm/amd/display/dc/dccg/dcn42/dcn42_dccg.c b/drivers/gpu/drm/amd/display/dc/dccg/dcn42/dcn42_dccg.c index b813310763e51..293786b41f5fd 100644 --- a/drivers/gpu/drm/amd/display/dc/dccg/dcn42/dcn42_dccg.c +++ b/drivers/gpu/drm/amd/display/dc/dccg/dcn42/dcn42_dccg.c @@ -180,6 +180,32 @@ void dccg42_set_physymclk( } } +static void dccg42_disable_hdmistreamclk(struct dccg *dccg) +{ + struct dcn_dccg *dccg_dcn = TO_DCN_DCCG(dccg); + + REG_UPDATE_2(HDMISTREAMCLK_CNTL, + HDMISTREAMCLK0_EN, 0, + HDMISTREAMCLK0_SRC_SEL, 0); + + REG_UPDATE(DCCG_GATE_DISABLE_CNTL6, + HDMISTREAMCLK0_ROOT_GATE_DISABLE, + dccg->ctx->dc->debug.root_clock_optimization.bits.hdmistream ? 0 : 1); +} + +static void dccg42_disable_hdmicharclk(struct dccg *dccg, int hpo_inst) +{ + struct dcn_dccg *dccg_dcn = TO_DCN_DCCG(dccg); + + ASSERT(hpo_inst >= 0); + REG_WRITE(HDMICHARCLK_CLOCK_CNTL[hpo_inst], 0); + + REG_UPDATE(DCCG_GATE_DISABLE_CNTL2, + HDMICHARCLK0_GATE_DISABLE, 0); + REG_UPDATE(DCCG_GATE_DISABLE_CNTL4, + HDMICHARCLK0_ROOT_GATE_DISABLE, dccg->ctx->dc->debug.root_clock_optimization.bits.hdmichar ? 0 : 1); +} + void dccg42_set_pixel_rate_div( struct dccg *dccg, uint32_t otg_inst, @@ -269,10 +295,17 @@ static void dccg42_init(struct dccg *dccg) PHYDSYMCLK_ROOT_GATE_DISABLE, 1, PHYESYMCLK_ROOT_GATE_DISABLE, 1); } + dccg42_disable_hdmistreamclk(dccg); + if (dccg->ctx->dc->debug.root_clock_optimization.bits.hdmichar) + dccg42_disable_hdmicharclk(dccg, 0); } static const struct dccg_funcs dccg42_funcs = { + .enable_hdmicharclk = dccg401_enable_hdmicharclk, + .disable_hdmicharclk = dccg42_disable_hdmicharclk, + .set_hdmistreamclk = dccg401_set_hdmistreamclk, + .set_hdmistreamclk_root_clock_gating = dccg35_set_hdmistreamclk_root_clock_gating, .update_dpp_dto = dccg35_update_dpp_dto, .dpp_root_clock_control = dccg35_dpp_root_clock_control, .get_dccg_ref_freq = dccg401_get_dccg_ref_freq, diff --git a/drivers/gpu/drm/amd/display/dc/dio/dcn10/dcn10_link_encoder.c b/drivers/gpu/drm/amd/display/dc/dio/dcn10/dcn10_link_encoder.c index 13e14aad3daac..f97ec72f8abad 100644 --- a/drivers/gpu/drm/amd/display/dc/dio/dcn10/dcn10_link_encoder.c +++ b/drivers/gpu/drm/amd/display/dc/dio/dcn10/dcn10_link_encoder.c @@ -1065,7 +1065,8 @@ void dcn10_link_encoder_disable_output( struct bp_transmitter_control cntl = { 0 }; enum bp_result result; - if (enc->funcs->is_dig_enabled && !enc->funcs->is_dig_enabled(enc)) { + if (!dc_is_hdmi_frl_signal(signal) && + (enc->funcs->is_dig_enabled && !enc->funcs->is_dig_enabled(enc))) { /* OF_SKIP_POWER_DOWN_INACTIVE_ENCODER */ /*in DP_Alt_No_Connect case, we turn off the dig already, after excuation the PHY w/a sequence, not allow touch PHY any more*/ diff --git a/drivers/gpu/drm/amd/display/dc/dio/dcn20/dcn20_link_encoder.h b/drivers/gpu/drm/amd/display/dc/dio/dcn20/dcn20_link_encoder.h index 762c579fcb44d..1beba9c7d3f99 100644 --- a/drivers/gpu/drm/amd/display/dc/dio/dcn20/dcn20_link_encoder.h +++ b/drivers/gpu/drm/amd/display/dc/dio/dcn20/dcn20_link_encoder.h @@ -291,6 +291,7 @@ struct dpcssys_phy_seq_cfg { bool use_calibration_setting; struct mpll_cfg mpll_cfg; bool load_sram_fw; + bool tx_hdmi_frl_mode; #if 0 bool hdmimode_enable; diff --git a/drivers/gpu/drm/amd/display/dc/dio/dcn30/dcn30_dio_link_encoder.c b/drivers/gpu/drm/amd/display/dc/dio/dcn30/dcn30_dio_link_encoder.c index 57b9ae5fca1dc..8c4010c5b2e7c 100644 --- a/drivers/gpu/drm/amd/display/dc/dio/dcn30/dcn30_dio_link_encoder.c +++ b/drivers/gpu/drm/amd/display/dc/dio/dcn30/dcn30_dio_link_encoder.c @@ -48,11 +48,240 @@ (enc10->link_regs->index) +static bool dcn30_link_encoder_validate_hdmi_frl_output( + const struct dcn10_link_encoder *enc10, + const struct dc_crtc_timing *crtc_timing) +{ + enum dc_color_depth max_deep_color = + enc10->base.features.max_hdmi_deep_color; + + if (!enc10->base.features.flags.bits.IS_HDMI_FRL_CAPABLE) + return false; + + if (max_deep_color < crtc_timing->display_color_depth) + return false; + + if (crtc_timing->display_color_depth < COLOR_DEPTH_888) + return false; + + /* TODO: check if hdmi_charclk is above ASIC cap (10 GBS for DCN3AG) */ + + return true; +} + bool dcn30_link_encoder_validate_output_with_stream( struct link_encoder *enc, const struct dc_stream_state *stream) { + if (dc_is_hdmi_frl_signal(stream->signal)) { + struct dcn10_link_encoder *enc10 = TO_DCN10_LINK_ENC(enc); + + return dcn30_link_encoder_validate_hdmi_frl_output(enc10, &stream->timing); + } else { return dcn10_link_encoder_validate_output_with_stream(enc, stream); + } +} + +//--------------------------------------------------- +// Task: Program EQ setting +// Note: +// EQ setting can be dont during P2 state or P0 state +// If set in P0 state, The values are latched in a single +// cycle of txX_clk but will take maximum of 40 txX_clk symbols +// to be reflected on the output. During this period the +// analog serial lines might have a transitional behavior. +//--------------------------------------------------- +void dpcs30_program_eq_setting( + struct link_encoder *enc, + uint8_t FFE_Level, + bool de_emphasis_only, + bool pre_shoot_only, + bool no_ffe, + const struct dc_hdmi_frl_link_settings *link_settings) +{ + (void)link_settings; + struct dcn10_link_encoder *enc10 = TO_DCN10_LINK_ENC(enc); + /* EQ setting for DP lane0 */ + uint32_t eq_main; + uint32_t eq_pre; + uint32_t eq_post; + + if (enc10->base.ctx->dc->debug.ignore_ffe) + return; + + if (FFE_Level < 0x5) + enc10->base.txffe_state = FFE_Level; + + if (FFE_Level == 0xEE) { + enc10->base.txffe_state++; + if (enc10->base.txffe_state > 3) + enc10->base.txffe_state = 0; + } + + switch (enc10->base.txffe_state) { + case 0: + eq_main = 0x31; + if (de_emphasis_only) + eq_main = 0x36; + if (pre_shoot_only) + eq_main = 0x39; + eq_pre = 0x5; + eq_post = 0x8; + break; + case 1: + eq_main = 0x2F; + if (de_emphasis_only) + eq_main = 0x34; + if (pre_shoot_only) + eq_main = 0x39; + eq_pre = 0x5; + eq_post = 0xA; + break; + case 2: + eq_main = 0x2C; + if (de_emphasis_only) + eq_main = 0x31; + if (pre_shoot_only) + eq_main = 0x39; + eq_pre = 0x5; + eq_post = 0xD; + break; + case 3: + eq_main = 0x29; + if (de_emphasis_only) + eq_main = 0x2E; + if (pre_shoot_only) + eq_main = 0x39; + eq_pre = 0x5; + eq_post = 0x10; + break; + default: + return; + } + + eq_pre = de_emphasis_only ? 0 : eq_pre; + eq_post = pre_shoot_only ? 0 : eq_post; + + if (no_ffe) { + eq_pre = 0; + eq_post = 0; + eq_main = 0x3E; + } + + REG_UPDATE_3(RDPCSTX_PHY_FUSE0, + RDPCS_PHY_DP_TX0_EQ_MAIN, eq_main, + RDPCS_PHY_DP_TX0_EQ_PRE, eq_pre, + RDPCS_PHY_DP_TX0_EQ_POST, eq_post); + + REG_UPDATE_3(RDPCSTX_PHY_FUSE1, + RDPCS_PHY_DP_TX1_EQ_MAIN, eq_main, + RDPCS_PHY_DP_TX1_EQ_PRE, eq_pre, + RDPCS_PHY_DP_TX1_EQ_POST, eq_post); + + REG_UPDATE_3(RDPCSTX_PHY_FUSE2, + RDPCS_PHY_DP_TX2_EQ_MAIN, eq_main, + RDPCS_PHY_DP_TX2_EQ_PRE, eq_pre, + RDPCS_PHY_DP_TX2_EQ_POST, eq_post); + + REG_UPDATE_3(RDPCSTX_PHY_FUSE3, + RDPCS_PHY_DP_TX3_EQ_MAIN, eq_main, + RDPCS_PHY_DP_TX3_EQ_PRE, eq_pre, + RDPCS_PHY_DP_TX3_EQ_POST, eq_post); +} + +void dpcs30_get_txffe( + struct link_encoder *enc, + struct frl_txffe *lane_settings) +{ + struct dcn10_link_encoder *enc10 = TO_DCN10_LINK_ENC(enc); + /* EQ setting for DP lane0 */ + uint32_t eq_main; + uint32_t eq_pre; + uint32_t eq_post; + + REG_GET_3(RDPCSTX_PHY_FUSE0, + RDPCS_PHY_DP_TX0_EQ_MAIN, &eq_main, + RDPCS_PHY_DP_TX0_EQ_PRE, &eq_pre, + RDPCS_PHY_DP_TX0_EQ_POST, &eq_post); + + lane_settings->amplitude[0] = eq_main; + lane_settings->pre_emphasis[0] = eq_pre; + lane_settings->post_emphasis[0] = eq_post; + + REG_GET_3(RDPCSTX_PHY_FUSE1, + RDPCS_PHY_DP_TX1_EQ_MAIN, &eq_main, + RDPCS_PHY_DP_TX1_EQ_PRE, &eq_pre, + RDPCS_PHY_DP_TX1_EQ_POST, &eq_post); + + lane_settings->amplitude[1] = eq_main; + lane_settings->pre_emphasis[1] = eq_pre; + lane_settings->post_emphasis[1] = eq_post; + + REG_GET_3(RDPCSTX_PHY_FUSE2, + RDPCS_PHY_DP_TX2_EQ_MAIN, &eq_main, + RDPCS_PHY_DP_TX2_EQ_PRE, &eq_pre, + RDPCS_PHY_DP_TX2_EQ_POST, &eq_post); + + lane_settings->amplitude[2] = eq_main; + lane_settings->pre_emphasis[2] = eq_pre; + lane_settings->post_emphasis[2] = eq_post; + + REG_GET_3(RDPCSTX_PHY_FUSE3, + RDPCS_PHY_DP_TX3_EQ_MAIN, &eq_main, + RDPCS_PHY_DP_TX3_EQ_PRE, &eq_pre, + RDPCS_PHY_DP_TX3_EQ_POST, &eq_post); + + lane_settings->amplitude[3] = eq_main; + lane_settings->pre_emphasis[3] = eq_pre; + lane_settings->post_emphasis[3] = eq_post; + +} + +void dpcs30_set_txffe( + struct link_encoder *enc, + struct frl_txffe *lane_settings) +{ + struct dcn10_link_encoder *enc10 = TO_DCN10_LINK_ENC(enc); + /* EQ setting for DP lane0 */ + uint32_t eq_main; + uint32_t eq_pre; + uint32_t eq_post; + + eq_main = lane_settings->amplitude[0]; + eq_pre = lane_settings->pre_emphasis[0]; + eq_post = lane_settings->post_emphasis[0]; + + REG_UPDATE_3(RDPCSTX_PHY_FUSE0, + RDPCS_PHY_DP_TX0_EQ_MAIN, eq_main, + RDPCS_PHY_DP_TX0_EQ_PRE, eq_pre, + RDPCS_PHY_DP_TX0_EQ_POST, eq_post); + + eq_main = lane_settings->amplitude[1]; + eq_pre = lane_settings->pre_emphasis[1]; + eq_post = lane_settings->post_emphasis[1]; + + REG_UPDATE_3(RDPCSTX_PHY_FUSE1, + RDPCS_PHY_DP_TX1_EQ_MAIN, eq_main, + RDPCS_PHY_DP_TX1_EQ_PRE, eq_pre, + RDPCS_PHY_DP_TX1_EQ_POST, eq_post); + + eq_main = lane_settings->amplitude[2]; + eq_pre = lane_settings->pre_emphasis[2]; + eq_post = lane_settings->post_emphasis[2]; + + REG_UPDATE_3(RDPCSTX_PHY_FUSE2, + RDPCS_PHY_DP_TX2_EQ_MAIN, eq_main, + RDPCS_PHY_DP_TX2_EQ_PRE, eq_pre, + RDPCS_PHY_DP_TX2_EQ_POST, eq_post); + + eq_main = lane_settings->amplitude[3]; + eq_pre = lane_settings->pre_emphasis[3]; + eq_post = lane_settings->post_emphasis[3]; + + REG_UPDATE_3(RDPCSTX_PHY_FUSE3, + RDPCS_PHY_DP_TX3_EQ_MAIN, eq_main, + RDPCS_PHY_DP_TX3_EQ_PRE, eq_pre, + RDPCS_PHY_DP_TX3_EQ_POST, eq_post); } static const struct link_encoder_funcs dcn30_link_enc_funcs = { @@ -84,6 +313,15 @@ static const struct link_encoder_funcs dcn30_link_enc_funcs = { .get_dig_mode = dcn10_get_dig_mode, .is_in_alt_mode = dcn20_link_encoder_is_in_alt_mode, .get_max_link_cap = dcn20_link_encoder_get_max_link_cap, + .dpcstx_set_order_invert_18_bit = NULL, + .set_phy_source = NULL, + .dpcs_initialize_phy = NULL, + .dpcs_configure_phypll = NULL, + .dpcs_configure_dpcs = NULL, + .dpcs_enable_dpcs = NULL, + .prog_eq_setting = dpcs30_program_eq_setting, + .get_txffe = dpcs30_get_txffe, + .set_txffe = dpcs30_set_txffe, .get_hpd_state = dcn10_get_hpd_state, .program_hpd_filter = dcn10_program_hpd_filter, }; @@ -198,6 +436,12 @@ void dcn30_link_encoder_construct( enc10->base.features.flags.bits.IS_UHBR20_CAPABLE = bp_cap_info.DP_UHBR20_EN; enc10->base.features.flags.bits.DP_IS_USB_C = bp_cap_info.DP_IS_USB_C; + + enc10->base.features.flags.bits.IS_HDMI_FRL_CAPABLE = bp_cap_info.IS_HDMI_FRL_CAPABLE; + enc10->base.features.flags.bits.IS_FRL_8G_CAPABLE = bp_cap_info.FRL_8G_EN; + enc10->base.features.flags.bits.IS_FRL_10G_CAPABLE = bp_cap_info.FRL_10G_EN; + enc10->base.features.flags.bits.IS_FRL_12G_CAPABLE = bp_cap_info.FRL_12G_EN; + enc10->base.txffe_state = 0; } else { DC_LOG_WARNING("%s: Failed to get encoder_cap_info from VBIOS with error code %d!\n", __func__, @@ -206,6 +450,12 @@ void dcn30_link_encoder_construct( if (enc10->base.ctx->dc->debug.hdmi20_disable) { enc10->base.features.flags.bits.HDMI_6GB_EN = 0; } + if (enc10->base.ctx->dc->config.force_hdmi21_frl_enc_enable) { + enc10->base.features.flags.bits.IS_HDMI_FRL_CAPABLE = 1; + enc10->base.features.flags.bits.IS_FRL_8G_CAPABLE = 1; + enc10->base.features.flags.bits.IS_FRL_10G_CAPABLE = 1; + enc10->base.features.flags.bits.IS_FRL_12G_CAPABLE = 1; + } } #define AUX_REG(reg)\ diff --git a/drivers/gpu/drm/amd/display/dc/dio/dcn30/dcn30_dio_link_encoder.h b/drivers/gpu/drm/amd/display/dc/dio/dcn30/dcn30_dio_link_encoder.h index 5b6177c2ae981..4fc0915a5dc51 100644 --- a/drivers/gpu/drm/amd/display/dc/dio/dcn30/dcn30_dio_link_encoder.h +++ b/drivers/gpu/drm/amd/display/dc/dio/dcn30/dcn30_dio_link_encoder.h @@ -60,6 +60,8 @@ #define DPCS_DCN3_MASK_SH_LIST(mask_sh)\ DPCS_DCN2_MASK_SH_LIST(mask_sh),\ + LE_SF(DPCSTX0_DPCSTX_TX_CNTL, DPCS_TX_HDMI_FRL_MODE, mask_sh),\ + LE_SF(DPCSTX0_DPCSTX_TX_CNTL, DPCS_TX_DATA_SWAP_10_BIT, mask_sh),\ LE_SF(DPCSTX0_DPCSTX_TX_CNTL, DPCS_TX_DATA_ORDER_INVERT_18_BIT, mask_sh),\ LE_SF(RDPCSTX0_RDPCSTX_PHY_CNTL0, RDPCS_PHY_TX_VBOOST_LVL, mask_sh),\ LE_SF(RDPCSTX0_RDPCSTX_CLOCK_CNTL, RDPCS_TX_CLK_EN, mask_sh),\ @@ -83,4 +85,19 @@ bool dcn30_link_encoder_validate_output_with_stream( struct link_encoder *enc, const struct dc_stream_state *stream); +void dpcs30_program_eq_setting( + struct link_encoder *enc, + uint8_t FFE_Level, + bool de_emphasis_only, + bool pre_shoot_only, + bool no_ffe, + const struct dc_hdmi_frl_link_settings *link_settings); + +void dpcs30_get_txffe( + struct link_encoder *enc, + struct frl_txffe *lane_settings); + +void dpcs30_set_txffe( + struct link_encoder *enc, + struct frl_txffe *lane_settings); #endif /* __DC_LINK_ENCODER__DCN30_H__ */ diff --git a/drivers/gpu/drm/amd/display/dc/dio/dcn301/dcn301_dio_link_encoder.c b/drivers/gpu/drm/amd/display/dc/dio/dcn301/dcn301_dio_link_encoder.c index 47d84a2a48ce8..02fb844670f9d 100644 --- a/drivers/gpu/drm/amd/display/dc/dio/dcn301/dcn301_dio_link_encoder.c +++ b/drivers/gpu/drm/amd/display/dc/dio/dcn301/dcn301_dio_link_encoder.c @@ -73,6 +73,13 @@ static const struct link_encoder_funcs dcn301_link_enc_funcs = { .get_dig_mode = dcn10_get_dig_mode, .is_in_alt_mode = dcn20_link_encoder_is_in_alt_mode, .get_max_link_cap = dcn20_link_encoder_get_max_link_cap, + .dpcstx_set_order_invert_18_bit = NULL, + .set_phy_source = NULL, + .dpcs_initialize_phy = NULL, + .dpcs_configure_phypll = NULL, + .dpcs_configure_dpcs = NULL, + .dpcs_enable_dpcs = NULL, + .prog_eq_setting = NULL, .get_hpd_state = dcn10_get_hpd_state, .program_hpd_filter = dcn10_program_hpd_filter, }; @@ -183,6 +190,11 @@ void dcn301_link_encoder_construct( enc10->base.features.flags.bits.HDMI_6GB_EN = bp_cap_info.HDMI_6GB_EN; enc10->base.features.flags.bits.DP_IS_USB_C = bp_cap_info.DP_IS_USB_C; + enc10->base.features.flags.bits.IS_HDMI_FRL_CAPABLE = bp_cap_info.IS_HDMI_FRL_CAPABLE; + enc10->base.features.flags.bits.IS_FRL_8G_CAPABLE = bp_cap_info.FRL_8G_EN; + enc10->base.features.flags.bits.IS_FRL_10G_CAPABLE = bp_cap_info.FRL_10G_EN; + enc10->base.features.flags.bits.IS_FRL_12G_CAPABLE = bp_cap_info.FRL_12G_EN; + enc10->base.txffe_state = 0; } else { DC_LOG_WARNING("%s: Failed to get encoder_cap_info from VBIOS with error code %d!\n", __func__, @@ -191,4 +203,10 @@ void dcn301_link_encoder_construct( if (enc10->base.ctx->dc->debug.hdmi20_disable) { enc10->base.features.flags.bits.HDMI_6GB_EN = 0; } + if (enc10->base.ctx->dc->config.force_hdmi21_frl_enc_enable) { + enc10->base.features.flags.bits.IS_HDMI_FRL_CAPABLE = 1; + enc10->base.features.flags.bits.IS_FRL_8G_CAPABLE = 1; + enc10->base.features.flags.bits.IS_FRL_10G_CAPABLE = 1; + enc10->base.features.flags.bits.IS_FRL_12G_CAPABLE = 1; + } } diff --git a/drivers/gpu/drm/amd/display/dc/dio/dcn31/dcn31_dio_link_encoder.c b/drivers/gpu/drm/amd/display/dc/dio/dcn31/dcn31_dio_link_encoder.c index 07d362ef0daf3..bcb791d741894 100644 --- a/drivers/gpu/drm/amd/display/dc/dio/dcn31/dcn31_dio_link_encoder.c +++ b/drivers/gpu/drm/amd/display/dc/dio/dcn31/dcn31_dio_link_encoder.c @@ -67,6 +67,17 @@ #define MIN(X, Y) ((X) < (Y) ? (X) : (Y)) #endif +// HDMI FRL EQ Setting masks/shifts +// EQ level 0-32 bits[0:1] +#define HDMI_FRL_EQ__LEVEL__SHIFT 0x0 +#define HDMI_FRL_EQ__LEVEL__MASK 0x3 +// Enable no preshoot bit[5] +#define HDMI_FRL_EQ__NO_PRE__SHIFT 0x5 +// Enable no demphasis bit[6] +#define HDMI_FRL_EQ__NO_DEMPH__SHIFT 0x6 +// Enable no FFE bit[4] +#define HDMI_FRL_EQ__NO_FFE__SHIFT 0x4 + static uint8_t phy_id_from_transmitter(enum transmitter t) { uint8_t phy_id; @@ -246,6 +257,89 @@ void enc31_hw_init(struct link_encoder *enc) dcn10_aux_initialize(enc10); } +static enum bp_result link_transmitter_control( + struct dcn10_link_encoder *enc10, + struct bp_transmitter_control *cntl) +{ + enum bp_result result; + struct dc_bios *bp = enc10->base.ctx->dc_bios; + + result = bp->funcs->transmitter_control(bp, cntl); + + return result; +} +//--------------------------------------------------- +// Task: Program EQ setting in HDMI FRL mode +// Note: +// EQ setting can be dont during P2 state or P0 state +// If set in P0 state, The values are latched in a single +// cycle of txX_clk but will take maximum of 40 txX_clk symbols +// to be reflected on the output. During this period the +// analog serial lines might have a transitional behavior. +//--------------------------------------------------- +void dpcs31_program_eq_setting( + struct link_encoder *enc, + uint8_t FFE_Level, + bool de_emphasis_only, + bool pre_shoot_only, + bool no_ffe, + const struct dc_hdmi_frl_link_settings *link_settings) +{ + struct dcn10_link_encoder *enc10 = TO_DCN10_LINK_ENC(enc); + struct bp_transmitter_control cntl = { 0 }; + /* EQ setting for DP lane0 */ + + if (enc10->base.ctx->dc->debug.ignore_ffe) + return; + + if (FFE_Level < 0x5) + enc10->base.txffe_state = FFE_Level; + + if (FFE_Level == 0xEE) { + enc10->base.txffe_state++; + if (enc10->base.txffe_state > 3) + enc10->base.txffe_state = 0; + } + + if (no_ffe) { + de_emphasis_only = true; + pre_shoot_only = true; + } + /* Pass on the input params to DMCUB for proper calc of eq settings */ + cntl.lane_settings = ((de_emphasis_only ? 1 : 0) << HDMI_FRL_EQ__NO_PRE__SHIFT) | + ((pre_shoot_only ? 1 : 0) << HDMI_FRL_EQ__NO_DEMPH__SHIFT) | + ((enc10->base.txffe_state & HDMI_FRL_EQ__LEVEL__MASK) + << HDMI_FRL_EQ__LEVEL__SHIFT); + cntl.lane_select = 0; + cntl.action = TRANSMITTER_CONTROL_SET_VOLTAGE_AND_PREEMPASIS; + cntl.transmitter = enc10->base.transmitter; + cntl.connector_obj_id = enc10->base.connector; + cntl.lanes_number = link_settings->frl_num_lanes; + cntl.hpd_sel = enc10->base.hpd_source; + /* Use below or dc_link_frl_bandwidth_kbps()? */ + switch (link_settings->frl_link_rate) { + case HDMI_FRL_LINK_RATE_3GBPS: + cntl.pixel_clock = 166667 / 10; + break; + case HDMI_FRL_LINK_RATE_6GBPS: + case HDMI_FRL_LINK_RATE_6GBPS_4LANE: + cntl.pixel_clock = 333333 / 10; + break; + case HDMI_FRL_LINK_RATE_8GBPS: + cntl.pixel_clock = 444444 / 10; + break; + case HDMI_FRL_LINK_RATE_10GBPS: + cntl.pixel_clock = 555555 / 10; + break; + case HDMI_FRL_LINK_RATE_12GBPS: + default: + cntl.pixel_clock = 666667 / 10; + break; + } + /* call VBIOS table to set eq settings - voltage swing and pre-emphasis */ + link_transmitter_control(enc10, &cntl); +} + static const struct link_encoder_funcs dcn31_link_enc_funcs = { .read_state = link_enc2_read_state, .validate_output_with_stream = @@ -275,6 +369,15 @@ static const struct link_encoder_funcs dcn31_link_enc_funcs = { .get_dig_mode = dcn10_get_dig_mode, .is_in_alt_mode = dcn31_link_encoder_is_in_alt_mode, .get_max_link_cap = dcn31_link_encoder_get_max_link_cap, + .dpcstx_set_order_invert_18_bit = NULL, + .set_phy_source = NULL, + .dpcs_initialize_phy = NULL, + .dpcs_configure_phypll = NULL, + .dpcs_configure_dpcs = NULL, + .dpcs_enable_dpcs = NULL, + .prog_eq_setting = dpcs31_program_eq_setting, + .get_txffe = dpcs30_get_txffe, + .set_txffe = dpcs30_set_txffe, .set_dio_phy_mux = dcn31_link_encoder_set_dio_phy_mux, .get_hpd_state = dcn10_get_hpd_state, .program_hpd_filter = dcn10_program_hpd_filter, @@ -387,6 +490,11 @@ void dcn31_link_encoder_construct( enc10->base.features.flags.bits.IS_UHBR20_CAPABLE = bp_cap_info.DP_UHBR20_EN; enc10->base.features.flags.bits.DP_IS_USB_C = bp_cap_info.DP_IS_USB_C; + enc10->base.features.flags.bits.IS_HDMI_FRL_CAPABLE = bp_cap_info.IS_HDMI_FRL_CAPABLE; + enc10->base.features.flags.bits.IS_FRL_8G_CAPABLE = bp_cap_info.FRL_8G_EN; + enc10->base.features.flags.bits.IS_FRL_10G_CAPABLE = bp_cap_info.FRL_10G_EN; + enc10->base.features.flags.bits.IS_FRL_12G_CAPABLE = bp_cap_info.FRL_12G_EN; + enc10->base.txffe_state = 0; } else { DC_LOG_WARNING("%s: Failed to get encoder_cap_info from VBIOS with error code %d!\n", __func__, @@ -395,6 +503,12 @@ void dcn31_link_encoder_construct( if (enc10->base.ctx->dc->debug.hdmi20_disable) { enc10->base.features.flags.bits.HDMI_6GB_EN = 0; } + if (enc10->base.ctx->dc->config.force_hdmi21_frl_enc_enable) { + enc10->base.features.flags.bits.IS_HDMI_FRL_CAPABLE = 1; + enc10->base.features.flags.bits.IS_FRL_8G_CAPABLE = 1; + enc10->base.features.flags.bits.IS_FRL_10G_CAPABLE = 1; + enc10->base.features.flags.bits.IS_FRL_12G_CAPABLE = 1; + } } void dcn31_link_encoder_construct_minimal( diff --git a/drivers/gpu/drm/amd/display/dc/dio/dcn31/dcn31_dio_link_encoder.h b/drivers/gpu/drm/amd/display/dc/dio/dcn31/dcn31_dio_link_encoder.h index ee78ba80797c8..3cf5875279916 100644 --- a/drivers/gpu/drm/amd/display/dc/dio/dcn31/dcn31_dio_link_encoder.h +++ b/drivers/gpu/drm/amd/display/dc/dio/dcn31/dcn31_dio_link_encoder.h @@ -285,6 +285,14 @@ bool dcn31_link_encoder_is_in_alt_mode( void dcn31_link_encoder_get_max_link_cap(struct link_encoder *enc, struct dc_link_settings *link_settings); +void dpcs31_program_eq_setting( + struct link_encoder *enc, + uint8_t FFE_Level, + bool de_emphasis_only, + bool pre_shoot_only, + bool no_ffe, + const struct dc_hdmi_frl_link_settings *link_settings); + void enc31_hw_init(struct link_encoder *enc); #endif /* __DC_LINK_ENCODER__DCN31_H__ */ diff --git a/drivers/gpu/drm/amd/display/dc/dio/dcn32/dcn32_dio_link_encoder.c b/drivers/gpu/drm/amd/display/dc/dio/dcn32/dcn32_dio_link_encoder.c index 65d28cb07b046..68e0dc93ae7d6 100644 --- a/drivers/gpu/drm/amd/display/dc/dio/dcn32/dcn32_dio_link_encoder.c +++ b/drivers/gpu/drm/amd/display/dc/dio/dcn32/dcn32_dio_link_encoder.c @@ -62,6 +62,17 @@ #define AUX_REG_WRITE(reg_name, val) \ dm_write_reg(CTX, AUX_REG(reg_name), val) +// HDMI FRL EQ Setting masks/shifts +// EQ level 0-32 bits[0:1] +#define HDMI_FRL_EQ__LEVEL__SHIFT 0x0 +#define HDMI_FRL_EQ__LEVEL__MASK 0x3 +// Enable no preshoot bit[5] +#define HDMI_FRL_EQ__NO_PRE__SHIFT 0x5 +// Enable no demphasis bit[6] +#define HDMI_FRL_EQ__NO_DEMPH__SHIFT 0x6 +// Enable no FFE bit[4] +#define HDMI_FRL_EQ__NO_FFE__SHIFT 0x4 + static uint8_t phy_id_from_transmitter(enum transmitter t) { uint8_t phy_id; @@ -203,6 +214,188 @@ void dcn32_link_encoder_get_max_link_cap(struct link_encoder *enc, } +static enum bp_result link_transmitter_control( + struct dcn10_link_encoder *enc10, + struct bp_transmitter_control *cntl) +{ + enum bp_result result; + struct dc_bios *bp = enc10->base.ctx->dc_bios; + + result = bp->funcs->transmitter_control(bp, cntl); + + return result; +} +//--------------------------------------------------- +// Task: Program EQ setting +// Note: +// EQ setting can be dont during P2 state or P0 state +// If set in P0 state, The values are latched in a single +// cycle of txX_clk but will take maximum of 40 txX_clk symbols +// to be reflected on the output. During this period the +// analog serial lines might have a transitional behavior. +//--------------------------------------------------- +void dpcs32_program_eq_setting( + struct link_encoder *enc, + uint8_t FFE_Level, + bool de_emphasis_only, + bool pre_shoot_only, + bool no_ffe, + const struct dc_hdmi_frl_link_settings *link_settings) +{ + struct dcn10_link_encoder *enc10 = TO_DCN10_LINK_ENC(enc); + struct bp_transmitter_control cntl = { 0 }; + /* EQ setting for DP lane0 */ + + if (enc10->base.ctx->dc->debug.ignore_ffe) + return; + + if (FFE_Level < 0x5) + enc10->base.txffe_state = FFE_Level; + + if (FFE_Level == 0xEE) { + enc10->base.txffe_state++; + if (enc10->base.txffe_state > 3) + enc10->base.txffe_state = 0; + } + + if (no_ffe) { + de_emphasis_only = true; + pre_shoot_only = true; + } + /* Pass on the input params to DMCUB for proper calc of eq settings */ + cntl.lane_settings = ((de_emphasis_only ? 1 : 0) << HDMI_FRL_EQ__NO_PRE__SHIFT) | + ((pre_shoot_only ? 1 : 0) << HDMI_FRL_EQ__NO_DEMPH__SHIFT) | + ((enc10->base.txffe_state & HDMI_FRL_EQ__LEVEL__MASK) + << HDMI_FRL_EQ__LEVEL__SHIFT); + cntl.lane_select = 0; + cntl.action = TRANSMITTER_CONTROL_SET_VOLTAGE_AND_PREEMPASIS; + cntl.transmitter = enc10->base.transmitter; + cntl.connector_obj_id = enc10->base.connector; + cntl.lanes_number = link_settings->frl_num_lanes; + cntl.hpd_sel = enc10->base.hpd_source; + /* Use below or dc_link_frl_bandwidth_kbps()? */ + switch (link_settings->frl_link_rate) { + case HDMI_FRL_LINK_RATE_3GBPS: + cntl.pixel_clock = 166667 / 10; + break; + case HDMI_FRL_LINK_RATE_6GBPS: + case HDMI_FRL_LINK_RATE_6GBPS_4LANE: + cntl.pixel_clock = 333333 / 10; + break; + case HDMI_FRL_LINK_RATE_8GBPS: + cntl.pixel_clock = 444444 / 10; + break; + case HDMI_FRL_LINK_RATE_10GBPS: + cntl.pixel_clock = 555555 / 10; + break; + case HDMI_FRL_LINK_RATE_12GBPS: + default: + cntl.pixel_clock = 666667 / 10; + break; + } + /* call VBIOS table to set eq settings - voltage swing and pre-emphasis */ + link_transmitter_control(enc10, &cntl); +} + +void dpcs32_get_txffe( + struct link_encoder *enc, + struct frl_txffe *lane_settings) +{ + (void)enc; + /* EQ setting for DP lane0 */ + uint32_t eq_main = 0; + uint32_t eq_pre = 0; + uint32_t eq_post = 0; + + /* TODO */ + //REG_GET_3(RDPCSTX_PHY_FUSE0, + // RDPCS_PHY_DP_TX0_EQ_MAIN, &eq_main, + // RDPCS_PHY_DP_TX0_EQ_PRE, &eq_pre, + // RDPCS_PHY_DP_TX0_EQ_POST, &eq_post); + + lane_settings->amplitude[0] = eq_main; + lane_settings->pre_emphasis[0] = eq_pre; + lane_settings->post_emphasis[0] = eq_post; + + //REG_GET_3(RDPCSTX_PHY_FUSE1, + // RDPCS_PHY_DP_TX1_EQ_MAIN, &eq_main, + // RDPCS_PHY_DP_TX1_EQ_PRE, &eq_pre, + // RDPCS_PHY_DP_TX1_EQ_POST, &eq_post); + + lane_settings->amplitude[1] = eq_main; + lane_settings->pre_emphasis[1] = eq_pre; + lane_settings->post_emphasis[1] = eq_post; + + //REG_GET_3(RDPCSTX_PHY_FUSE2, + // RDPCS_PHY_DP_TX2_EQ_MAIN, &eq_main, + // RDPCS_PHY_DP_TX2_EQ_PRE, &eq_pre, + // RDPCS_PHY_DP_TX2_EQ_POST, &eq_post); + + lane_settings->amplitude[2] = eq_main; + lane_settings->pre_emphasis[2] = eq_pre; + lane_settings->post_emphasis[2] = eq_post; + + //REG_GET_3(RDPCSTX_PHY_FUSE3, + // RDPCS_PHY_DP_TX3_EQ_MAIN, &eq_main, + // RDPCS_PHY_DP_TX3_EQ_PRE, &eq_pre, + // RDPCS_PHY_DP_TX3_EQ_POST, &eq_post); + + lane_settings->amplitude[3] = eq_main; + lane_settings->pre_emphasis[3] = eq_pre; + lane_settings->post_emphasis[3] = eq_post; + +} + +void dpcs32_set_txffe( + struct link_encoder *enc, + struct frl_txffe *lane_settings) +{ + (void)enc; + (void)lane_settings; + /* EQ setting for DP lane0 */ + //uint32_t eq_main; + //uint32_t eq_pre; + //uint32_t eq_post; + + //eq_main = lane_settings->amplitude[0]; + //eq_pre = lane_settings->pre_emphasis[0]; + //eq_post = lane_settings->post_emphasis[0]; + + /* TODO */ + //REG_UPDATE_3(RDPCSTX_PHY_FUSE0, + // RDPCS_PHY_DP_TX0_EQ_MAIN, eq_main, + // RDPCS_PHY_DP_TX0_EQ_PRE, eq_pre, + // RDPCS_PHY_DP_TX0_EQ_POST, eq_post); + + //eq_main = lane_settings->amplitude[1]; + //eq_pre = lane_settings->pre_emphasis[1]; + //eq_post = lane_settings->post_emphasis[1]; + + //REG_UPDATE_3(RDPCSTX_PHY_FUSE1, + // RDPCS_PHY_DP_TX1_EQ_MAIN, eq_main, + // RDPCS_PHY_DP_TX1_EQ_PRE, eq_pre, + // RDPCS_PHY_DP_TX1_EQ_POST, eq_post); + + //eq_main = lane_settings->amplitude[2]; + //eq_pre = lane_settings->pre_emphasis[2]; + //eq_post = lane_settings->post_emphasis[2]; + + //REG_UPDATE_3(RDPCSTX_PHY_FUSE2, + // RDPCS_PHY_DP_TX2_EQ_MAIN, eq_main, + // RDPCS_PHY_DP_TX2_EQ_PRE, eq_pre, + // RDPCS_PHY_DP_TX2_EQ_POST, eq_post); + + //1eq_main = lane_settings->amplitude[3]; + //eq_pre = lane_settings->pre_emphasis[3]; + //eq_post = lane_settings->post_emphasis[3]; + + //REG_UPDATE_3(RDPCSTX_PHY_FUSE3, + // RDPCS_PHY_DP_TX3_EQ_MAIN, eq_main, + // RDPCS_PHY_DP_TX3_EQ_PRE, eq_pre, + // RDPCS_PHY_DP_TX3_EQ_POST, eq_post); +} + + static const struct link_encoder_funcs dcn32_link_enc_funcs = { .read_state = link_enc2_read_state, .validate_output_with_stream = @@ -232,6 +425,15 @@ static const struct link_encoder_funcs dcn32_link_enc_funcs = { .get_dig_mode = dcn10_get_dig_mode, .is_in_alt_mode = dcn32_link_encoder_is_in_alt_mode, .get_max_link_cap = dcn32_link_encoder_get_max_link_cap, + .dpcstx_set_order_invert_18_bit = NULL, + .set_phy_source = NULL, + .dpcs_initialize_phy = NULL, + .dpcs_configure_phypll = NULL, + .dpcs_configure_dpcs = NULL, + .dpcs_enable_dpcs = NULL, + .prog_eq_setting = dpcs32_program_eq_setting, + .get_txffe = dpcs32_get_txffe, + .set_txffe = dpcs32_set_txffe, .set_dio_phy_mux = dcn31_link_encoder_set_dio_phy_mux, .get_hpd_state = dcn10_get_hpd_state, .program_hpd_filter = dcn10_program_hpd_filter, @@ -329,6 +531,12 @@ void dcn32_link_encoder_construct( enc10->base.features.flags.bits.IS_UHBR10_CAPABLE = bp_cap_info.DP_UHBR10_EN; enc10->base.features.flags.bits.IS_UHBR13_5_CAPABLE = bp_cap_info.DP_UHBR13_5_EN; enc10->base.features.flags.bits.IS_UHBR20_CAPABLE = bp_cap_info.DP_UHBR20_EN; + + enc10->base.features.flags.bits.IS_HDMI_FRL_CAPABLE = 1; + enc10->base.features.flags.bits.IS_FRL_8G_CAPABLE = bp_cap_info.FRL_8G_EN; + enc10->base.features.flags.bits.IS_FRL_10G_CAPABLE = bp_cap_info.FRL_10G_EN; + enc10->base.features.flags.bits.IS_FRL_12G_CAPABLE = bp_cap_info.FRL_12G_EN; + enc10->base.txffe_state = 0; } else { DC_LOG_WARNING("%s: Failed to get encoder_cap_info from VBIOS with error code %d!\n", __func__, @@ -337,4 +545,10 @@ void dcn32_link_encoder_construct( if (enc10->base.ctx->dc->debug.hdmi20_disable) { enc10->base.features.flags.bits.HDMI_6GB_EN = 0; } + if (enc10->base.ctx->dc->config.force_hdmi21_frl_enc_enable) { + enc10->base.features.flags.bits.IS_HDMI_FRL_CAPABLE = 1; + enc10->base.features.flags.bits.IS_FRL_8G_CAPABLE = 1; + enc10->base.features.flags.bits.IS_FRL_10G_CAPABLE = 1; + enc10->base.features.flags.bits.IS_FRL_12G_CAPABLE = 1; + } } diff --git a/drivers/gpu/drm/amd/display/dc/dio/dcn32/dcn32_dio_link_encoder.h b/drivers/gpu/drm/amd/display/dc/dio/dcn32/dcn32_dio_link_encoder.h index 35d23d9db45e1..262796819a634 100644 --- a/drivers/gpu/drm/amd/display/dc/dio/dcn32/dcn32_dio_link_encoder.h +++ b/drivers/gpu/drm/amd/display/dc/dio/dcn32/dcn32_dio_link_encoder.h @@ -50,4 +50,20 @@ bool dcn32_link_encoder_is_in_alt_mode(struct link_encoder *enc); void dcn32_link_encoder_get_max_link_cap(struct link_encoder *enc, struct dc_link_settings *link_settings); +void dpcs32_program_eq_setting( + struct link_encoder *enc, + uint8_t FFE_Level, + bool de_emphasis_only, + bool pre_shoot_only, + bool no_ffe, + const struct dc_hdmi_frl_link_settings *link_settings); + +void dpcs32_get_txffe( + struct link_encoder *enc, + struct frl_txffe *lane_settings); + +void dpcs32_set_txffe( + struct link_encoder *enc, + struct frl_txffe *lane_settings); + #endif /* __DC_LINK_ENCODER__DCN32_H__ */ diff --git a/drivers/gpu/drm/amd/display/dc/dio/dcn321/dcn321_dio_link_encoder.c b/drivers/gpu/drm/amd/display/dc/dio/dcn321/dcn321_dio_link_encoder.c index 968f89295b64c..4108fd0f43802 100644 --- a/drivers/gpu/drm/amd/display/dc/dio/dcn321/dcn321_dio_link_encoder.c +++ b/drivers/gpu/drm/amd/display/dc/dio/dcn321/dcn321_dio_link_encoder.c @@ -88,6 +88,15 @@ static const struct link_encoder_funcs dcn321_link_enc_funcs = { .get_dig_mode = dcn10_get_dig_mode, .is_in_alt_mode = dcn20_link_encoder_is_in_alt_mode, .get_max_link_cap = dcn20_link_encoder_get_max_link_cap, + .dpcstx_set_order_invert_18_bit = NULL, + .set_phy_source = NULL, + .dpcs_initialize_phy = NULL, + .dpcs_configure_phypll = NULL, + .dpcs_configure_dpcs = NULL, + .dpcs_enable_dpcs = NULL, + .prog_eq_setting = dpcs32_program_eq_setting, + .get_txffe = dpcs32_get_txffe, + .set_txffe = dpcs32_set_txffe, .set_dio_phy_mux = dcn31_link_encoder_set_dio_phy_mux, .get_hpd_state = dcn10_get_hpd_state, .program_hpd_filter = dcn10_program_hpd_filter, @@ -183,6 +192,11 @@ void dcn321_link_encoder_construct( enc10->base.features.flags.bits.IS_UHBR10_CAPABLE = bp_cap_info.DP_UHBR10_EN; enc10->base.features.flags.bits.IS_UHBR13_5_CAPABLE = bp_cap_info.DP_UHBR13_5_EN; enc10->base.features.flags.bits.IS_UHBR20_CAPABLE = bp_cap_info.DP_UHBR20_EN; + enc10->base.features.flags.bits.IS_HDMI_FRL_CAPABLE = 1; + enc10->base.features.flags.bits.IS_FRL_8G_CAPABLE = bp_cap_info.FRL_8G_EN; + enc10->base.features.flags.bits.IS_FRL_10G_CAPABLE = bp_cap_info.FRL_10G_EN; + enc10->base.features.flags.bits.IS_FRL_12G_CAPABLE = bp_cap_info.FRL_12G_EN; + enc10->base.txffe_state = 0; } else { DC_LOG_WARNING("%s: Failed to get encoder_cap_info from VBIOS with error code %d!\n", __func__, @@ -190,4 +204,10 @@ void dcn321_link_encoder_construct( } if (enc10->base.ctx->dc->debug.hdmi20_disable) enc10->base.features.flags.bits.HDMI_6GB_EN = 0; + if (enc10->base.ctx->dc->config.force_hdmi21_frl_enc_enable) { + enc10->base.features.flags.bits.IS_HDMI_FRL_CAPABLE = 1; + enc10->base.features.flags.bits.IS_FRL_8G_CAPABLE = 1; + enc10->base.features.flags.bits.IS_FRL_10G_CAPABLE = 1; + enc10->base.features.flags.bits.IS_FRL_12G_CAPABLE = 1; + } } diff --git a/drivers/gpu/drm/amd/display/dc/dio/dcn35/dcn35_dio_link_encoder.c b/drivers/gpu/drm/amd/display/dc/dio/dcn35/dcn35_dio_link_encoder.c index 20bf04dac609a..8fa9b8c500f20 100644 --- a/drivers/gpu/drm/amd/display/dc/dio/dcn35/dcn35_dio_link_encoder.c +++ b/drivers/gpu/drm/amd/display/dc/dio/dcn35/dcn35_dio_link_encoder.c @@ -158,6 +158,15 @@ static const struct link_encoder_funcs dcn35_link_enc_funcs = { .get_dig_mode = dcn35_get_dig_mode, .is_in_alt_mode = dcn31_link_encoder_is_in_alt_mode, .get_max_link_cap = dcn31_link_encoder_get_max_link_cap, + .dpcstx_set_order_invert_18_bit = NULL, + .set_phy_source = NULL, + .dpcs_initialize_phy = NULL, + .dpcs_configure_phypll = NULL, + .dpcs_configure_dpcs = NULL, + .dpcs_enable_dpcs = NULL, + .prog_eq_setting = dpcs32_program_eq_setting, + .get_txffe = dpcs32_get_txffe, + .set_txffe = dpcs32_set_txffe, .set_dio_phy_mux = dcn31_link_encoder_set_dio_phy_mux, .enable_dpia_output = dcn35_link_encoder_enable_dpia_output, .disable_dpia_output = dcn35_link_encoder_disable_dpia_output, @@ -261,6 +270,12 @@ void dcn35_link_encoder_construct( enc10->base.features.flags.bits.IS_UHBR13_5_CAPABLE = bp_cap_info.DP_UHBR13_5_EN; enc10->base.features.flags.bits.IS_UHBR20_CAPABLE = bp_cap_info.DP_UHBR20_EN; + enc10->base.features.flags.bits.IS_HDMI_FRL_CAPABLE = + bp_cap_info.FRL_8G_EN || bp_cap_info.FRL_10G_EN || bp_cap_info.FRL_12G_EN; + enc10->base.features.flags.bits.IS_FRL_8G_CAPABLE = bp_cap_info.FRL_8G_EN; + enc10->base.features.flags.bits.IS_FRL_10G_CAPABLE = bp_cap_info.FRL_10G_EN; + enc10->base.features.flags.bits.IS_FRL_12G_CAPABLE = bp_cap_info.FRL_12G_EN; + enc10->base.txffe_state = 0; } else { DC_LOG_WARNING("%s: Failed to get encoder_cap_info from VBIOS with error code %d!\n", __func__, @@ -268,6 +283,12 @@ void dcn35_link_encoder_construct( } if (enc10->base.ctx->dc->debug.hdmi20_disable) enc10->base.features.flags.bits.HDMI_6GB_EN = 0; + if (enc10->base.ctx->dc->config.force_hdmi21_frl_enc_enable) { + enc10->base.features.flags.bits.IS_HDMI_FRL_CAPABLE = 1; + enc10->base.features.flags.bits.IS_FRL_8G_CAPABLE = 1; + enc10->base.features.flags.bits.IS_FRL_10G_CAPABLE = 1; + enc10->base.features.flags.bits.IS_FRL_12G_CAPABLE = 1; + } } diff --git a/drivers/gpu/drm/amd/display/dc/dio/dcn401/dcn401_dio_link_encoder.c b/drivers/gpu/drm/amd/display/dc/dio/dcn401/dcn401_dio_link_encoder.c index e1f0a1bf10756..2aaf06d487719 100644 --- a/drivers/gpu/drm/amd/display/dc/dio/dcn401/dcn401_dio_link_encoder.c +++ b/drivers/gpu/drm/amd/display/dc/dio/dcn401/dcn401_dio_link_encoder.c @@ -65,6 +65,17 @@ #define MIN(X, Y) ((X) < (Y) ? (X) : (Y)) #endif +// HDMI FRL EQ Setting masks/shifts +// EQ level 0-32 bits[0:1] +#define HDMI_FRL_EQ__LEVEL__SHIFT 0x0 +#define HDMI_FRL_EQ__LEVEL__MASK 0x3 +// Enable no preshoot bit[5] +#define HDMI_FRL_EQ__NO_PRE__SHIFT 0x5 +// Enable no demphasis bit[6] +#define HDMI_FRL_EQ__NO_DEMPH__SHIFT 0x6 +// Enable no FFE bit[4] +#define HDMI_FRL_EQ__NO_FFE__SHIFT 0x4 + void enc401_hw_init(struct link_encoder *enc) { struct dcn10_link_encoder *enc10 = TO_DCN10_LINK_ENC(enc); @@ -120,6 +131,194 @@ void dcn401_link_encoder_enable_dp_output( } } +static enum bp_result link_transmitter_control( + struct dcn10_link_encoder *enc10, + struct bp_transmitter_control *cntl) +{ + enum bp_result result; + struct dc_bios *bp = enc10->base.ctx->dc_bios; + + result = bp->funcs->transmitter_control(bp, cntl); + + return result; +} +//--------------------------------------------------- +// Task: Program EQ setting +// Note: +// EQ setting can be dont during P2 state or P0 state +// If set in P0 state, The values are latched in a single +// cycle of txX_clk but will take maximum of 40 txX_clk symbols +// to be reflected on the output. During this period the +// analog serial lines might have a transitional behavior. +//--------------------------------------------------- + +void dpcs401_program_eq_setting( + struct link_encoder *enc, + uint8_t FFE_Level, + bool de_emphasis_only, + bool pre_shoot_only, + bool no_ffe, + const struct dc_hdmi_frl_link_settings *link_settings) +{ + struct dcn10_link_encoder *enc10 = TO_DCN10_LINK_ENC(enc); + struct bp_transmitter_control cntl = { 0 }; + + if (enc10->base.ctx->dc->debug.ignore_ffe) + return; + + if (FFE_Level < 0x5) + enc10->base.txffe_state = FFE_Level; + + if (enc10->base.ctx->dc->debug.select_ffe) + enc10->base.txffe_state = + (uint8_t)enc10->base.ctx->dc->debug.select_ffe; + + if (FFE_Level == 0xEE) { + enc10->base.txffe_state++; + if (enc10->base.txffe_state > 3) + enc10->base.txffe_state = 0; + } + + if (no_ffe) { + de_emphasis_only = true; + pre_shoot_only = true; + } + /* Pass on the input params to DMCUB for proper calc of eq settings */ + cntl.lane_settings = ((de_emphasis_only ? 1 : 0) << HDMI_FRL_EQ__NO_PRE__SHIFT) | + ((pre_shoot_only ? 1 : 0) << HDMI_FRL_EQ__NO_DEMPH__SHIFT) | + ((enc10->base.txffe_state & HDMI_FRL_EQ__LEVEL__MASK) + << HDMI_FRL_EQ__LEVEL__SHIFT); + cntl.lane_select = 0; + cntl.action = TRANSMITTER_CONTROL_SET_VOLTAGE_AND_PREEMPASIS; + cntl.transmitter = enc10->base.transmitter; + cntl.connector_obj_id = enc10->base.connector; + cntl.lanes_number = link_settings->frl_num_lanes; + cntl.hpd_sel = enc10->base.hpd_source; + /* Use below or dc_link_frl_bandwidth_kbps()? */ + switch (link_settings->frl_link_rate) { + case HDMI_FRL_LINK_RATE_3GBPS: + cntl.pixel_clock = 166667 / 10; + break; + case HDMI_FRL_LINK_RATE_6GBPS: + case HDMI_FRL_LINK_RATE_6GBPS_4LANE: + cntl.pixel_clock = 333333 / 10; + break; + case HDMI_FRL_LINK_RATE_8GBPS: + cntl.pixel_clock = 444444 / 10; + break; + case HDMI_FRL_LINK_RATE_10GBPS: + cntl.pixel_clock = 555555 / 10; + break; + case HDMI_FRL_LINK_RATE_12GBPS: + default: + cntl.pixel_clock = 666667 / 10; + break; + } + /* call VBIOS table to set eq settings - voltage swing and pre-emphasis */ + link_transmitter_control(enc10, &cntl); +} + +void dpcs401_get_txffe( + struct link_encoder *enc, + struct frl_txffe *lane_settings) +{ + (void)enc; + /* EQ setting for DP lane0 */ + uint32_t eq_main = 0; + uint32_t eq_pre = 0; + uint32_t eq_post = 0; + + /* TODO */ + //REG_GET_3(RDPCSTX_PHY_FUSE0, + // RDPCS_PHY_DP_TX0_EQ_MAIN, &eq_main, + // RDPCS_PHY_DP_TX0_EQ_PRE, &eq_pre, + // RDPCS_PHY_DP_TX0_EQ_POST, &eq_post); + + lane_settings->amplitude[0] = eq_main; + lane_settings->pre_emphasis[0] = eq_pre; + lane_settings->post_emphasis[0] = eq_post; + + //REG_GET_3(RDPCSTX_PHY_FUSE1, + // RDPCS_PHY_DP_TX1_EQ_MAIN, &eq_main, + // RDPCS_PHY_DP_TX1_EQ_PRE, &eq_pre, + // RDPCS_PHY_DP_TX1_EQ_POST, &eq_post); + + lane_settings->amplitude[1] = eq_main; + lane_settings->pre_emphasis[1] = eq_pre; + lane_settings->post_emphasis[1] = eq_post; + + //REG_GET_3(RDPCSTX_PHY_FUSE2, + // RDPCS_PHY_DP_TX2_EQ_MAIN, &eq_main, + // RDPCS_PHY_DP_TX2_EQ_PRE, &eq_pre, + // RDPCS_PHY_DP_TX2_EQ_POST, &eq_post); + + lane_settings->amplitude[2] = eq_main; + lane_settings->pre_emphasis[2] = eq_pre; + lane_settings->post_emphasis[2] = eq_post; + + //REG_GET_3(RDPCSTX_PHY_FUSE3, + // RDPCS_PHY_DP_TX3_EQ_MAIN, &eq_main, + // RDPCS_PHY_DP_TX3_EQ_PRE, &eq_pre, + // RDPCS_PHY_DP_TX3_EQ_POST, &eq_post); + + lane_settings->amplitude[3] = eq_main; + lane_settings->pre_emphasis[3] = eq_pre; + lane_settings->post_emphasis[3] = eq_post; + +} + +void dpcs401_set_txffe( + struct link_encoder *enc, + struct frl_txffe *lane_settings) +{ + (void)enc; + (void)lane_settings; + //struct dcn10_link_encoder *enc10 = TO_DCN10_LINK_ENC(enc); + /* EQ setting for DP lane0 */ + //TODO: Unused + //uint32_t eq_main; + //uint32_t eq_pre; + //uint32_t eq_post; + + //eq_main = lane_settings->amplitude[0]; + //eq_pre = lane_settings->pre_emphasis[0]; + //eq_post = lane_settings->post_emphasis[0]; + + /* TODO */ + //REG_UPDATE_3(RDPCSTX_PHY_FUSE0, + // RDPCS_PHY_DP_TX0_EQ_MAIN, eq_main, + // RDPCS_PHY_DP_TX0_EQ_PRE, eq_pre, + // RDPCS_PHY_DP_TX0_EQ_POST, eq_post); + + //eq_main = lane_settings->amplitude[1]; + //eq_pre = lane_settings->pre_emphasis[1]; + //eq_post = lane_settings->post_emphasis[1]; + + //REG_UPDATE_3(RDPCSTX_PHY_FUSE1, + // RDPCS_PHY_DP_TX1_EQ_MAIN, eq_main, + // RDPCS_PHY_DP_TX1_EQ_PRE, eq_pre, + // RDPCS_PHY_DP_TX1_EQ_POST, eq_post); + + //eq_main = lane_settings->amplitude[2]; + //eq_pre = lane_settings->pre_emphasis[2]; + //eq_post = lane_settings->post_emphasis[2]; + + //REG_UPDATE_3(RDPCSTX_PHY_FUSE2, + // RDPCS_PHY_DP_TX2_EQ_MAIN, eq_main, + // RDPCS_PHY_DP_TX2_EQ_PRE, eq_pre, + // RDPCS_PHY_DP_TX2_EQ_POST, eq_post); + + //eq_main = lane_settings->amplitude[3]; + //eq_pre = lane_settings->pre_emphasis[3]; + //eq_post = lane_settings->post_emphasis[3]; + + //REG_UPDATE_3(RDPCSTX_PHY_FUSE3, + // RDPCS_PHY_DP_TX3_EQ_MAIN, eq_main, + // RDPCS_PHY_DP_TX3_EQ_PRE, eq_pre, + // RDPCS_PHY_DP_TX3_EQ_POST, eq_post); +} + + void dcn401_link_encoder_setup( struct link_encoder *enc, enum signal_type signal) @@ -214,6 +413,15 @@ static const struct link_encoder_funcs dcn401_link_enc_funcs = { .get_dig_mode = dcn401_get_dig_mode, .is_in_alt_mode = dcn32_link_encoder_is_in_alt_mode, .get_max_link_cap = dcn32_link_encoder_get_max_link_cap, + .dpcstx_set_order_invert_18_bit = NULL, + .set_phy_source = NULL, + .dpcs_initialize_phy = NULL, + .dpcs_configure_phypll = NULL, + .dpcs_configure_dpcs = NULL, + .dpcs_enable_dpcs = NULL, + .prog_eq_setting = dpcs401_program_eq_setting, + .get_txffe = dpcs401_get_txffe, + .set_txffe = dpcs401_set_txffe, .set_dio_phy_mux = dcn31_link_encoder_set_dio_phy_mux, .get_hpd_state = dcn10_get_hpd_state, .program_hpd_filter = dcn10_program_hpd_filter, @@ -314,6 +522,12 @@ void dcn401_link_encoder_construct( enc10->base.features.flags.bits.IS_UHBR10_CAPABLE = bp_cap_info.DP_UHBR10_EN; enc10->base.features.flags.bits.IS_UHBR13_5_CAPABLE = bp_cap_info.DP_UHBR13_5_EN; enc10->base.features.flags.bits.IS_UHBR20_CAPABLE = bp_cap_info.DP_UHBR20_EN; + enc10->base.features.flags.bits.IS_HDMI_FRL_CAPABLE = + bp_cap_info.FRL_8G_EN || bp_cap_info.FRL_10G_EN || bp_cap_info.FRL_12G_EN; + enc10->base.features.flags.bits.IS_FRL_8G_CAPABLE = bp_cap_info.FRL_8G_EN; + enc10->base.features.flags.bits.IS_FRL_10G_CAPABLE = bp_cap_info.FRL_10G_EN; + enc10->base.features.flags.bits.IS_FRL_12G_CAPABLE = bp_cap_info.FRL_12G_EN; + enc10->base.txffe_state = 0; } else { DC_LOG_WARNING("%s: Failed to get encoder_cap_info from VBIOS with error code %d!\n", __func__, @@ -322,4 +536,10 @@ void dcn401_link_encoder_construct( if (enc10->base.ctx->dc->debug.hdmi20_disable) { enc10->base.features.flags.bits.HDMI_6GB_EN = 0; } + if (enc10->base.ctx->dc->config.force_hdmi21_frl_enc_enable) { + enc10->base.features.flags.bits.IS_HDMI_FRL_CAPABLE = 1; + enc10->base.features.flags.bits.IS_FRL_8G_CAPABLE = 1; + enc10->base.features.flags.bits.IS_FRL_10G_CAPABLE = 1; + enc10->base.features.flags.bits.IS_FRL_12G_CAPABLE = 1; + } } diff --git a/drivers/gpu/drm/amd/display/dc/dio/dcn401/dcn401_dio_link_encoder.h b/drivers/gpu/drm/amd/display/dc/dio/dcn401/dcn401_dio_link_encoder.h index 6baab8302b813..a40c479fada88 100644 --- a/drivers/gpu/drm/amd/display/dc/dio/dcn401/dcn401_dio_link_encoder.h +++ b/drivers/gpu/drm/amd/display/dc/dio/dcn401/dcn401_dio_link_encoder.h @@ -121,6 +121,22 @@ void dcn401_link_encoder_enable_dp_output( const struct dc_link_settings *link_settings, enum clock_source_id clock_source); +void dpcs401_program_eq_setting( + struct link_encoder *enc, + uint8_t FFE_Level, + bool de_emphasis_only, + bool pre_shoot_only, + bool no_ffe, + const struct dc_hdmi_frl_link_settings *link_settings); + +void dpcs401_get_txffe( + struct link_encoder *enc, + struct frl_txffe *lane_settings); + +void dpcs401_set_txffe( + struct link_encoder *enc, + struct frl_txffe *lane_settings); + void dcn401_link_encoder_setup( struct link_encoder *enc, enum signal_type signal); diff --git a/drivers/gpu/drm/amd/display/dc/dio/dcn42/dcn42_dio_link_encoder.c b/drivers/gpu/drm/amd/display/dc/dio/dcn42/dcn42_dio_link_encoder.c index 35dfe3bb0c555..fae49b3b170e7 100644 --- a/drivers/gpu/drm/amd/display/dc/dio/dcn42/dcn42_dio_link_encoder.c +++ b/drivers/gpu/drm/amd/display/dc/dio/dcn42/dcn42_dio_link_encoder.c @@ -93,6 +93,15 @@ static const struct link_encoder_funcs dcn42_link_enc_funcs = { .get_dig_mode = dcn401_get_dig_mode, .is_in_alt_mode = dcn32_link_encoder_is_in_alt_mode, .get_max_link_cap = dcn32_link_encoder_get_max_link_cap, + .dpcstx_set_order_invert_18_bit = NULL, + .set_phy_source = NULL, + .dpcs_initialize_phy = NULL, + .dpcs_configure_phypll = NULL, + .dpcs_configure_dpcs = NULL, + .dpcs_enable_dpcs = NULL, + .prog_eq_setting = dpcs401_program_eq_setting, + .get_txffe = dpcs401_get_txffe, + .set_txffe = dpcs401_set_txffe, .set_dio_phy_mux = dcn31_link_encoder_set_dio_phy_mux, .enable_dpia_output = dcn35_link_encoder_enable_dpia_output, .disable_dpia_output = dcn35_link_encoder_disable_dpia_output, @@ -194,6 +203,12 @@ void dcn42_link_encoder_construct( enc10->base.features.flags.bits.IS_UHBR10_CAPABLE = bp_cap_info.DP_UHBR10_EN; enc10->base.features.flags.bits.IS_UHBR13_5_CAPABLE = bp_cap_info.DP_UHBR13_5_EN; enc10->base.features.flags.bits.IS_UHBR20_CAPABLE = bp_cap_info.DP_UHBR20_EN; + enc10->base.features.flags.bits.IS_HDMI_FRL_CAPABLE = + bp_cap_info.FRL_8G_EN || bp_cap_info.FRL_10G_EN || bp_cap_info.FRL_12G_EN; + enc10->base.features.flags.bits.IS_FRL_8G_CAPABLE = bp_cap_info.FRL_8G_EN; + enc10->base.features.flags.bits.IS_FRL_10G_CAPABLE = bp_cap_info.FRL_10G_EN; + enc10->base.features.flags.bits.IS_FRL_12G_CAPABLE = bp_cap_info.FRL_12G_EN; + enc10->base.txffe_state = 0; } else { DC_LOG_WARNING("%s: Failed to get encoder_cap_info from VBIOS with error code %d!\n", __func__, @@ -202,4 +217,10 @@ void dcn42_link_encoder_construct( if (enc10->base.ctx->dc->debug.hdmi20_disable) { enc10->base.features.flags.bits.HDMI_6GB_EN = 0; } + if (enc10->base.ctx->dc->config.force_hdmi21_frl_enc_enable) { + enc10->base.features.flags.bits.IS_HDMI_FRL_CAPABLE = 1; + enc10->base.features.flags.bits.IS_FRL_8G_CAPABLE = 1; + enc10->base.features.flags.bits.IS_FRL_10G_CAPABLE = 1; + enc10->base.features.flags.bits.IS_FRL_12G_CAPABLE = 1; + } } diff --git a/drivers/gpu/drm/amd/display/dc/dm_cp_psp.h b/drivers/gpu/drm/amd/display/dc/dm_cp_psp.h index f4d3f04ec8579..cb20bfe9c1c81 100644 --- a/drivers/gpu/drm/amd/display/dc/dm_cp_psp.h +++ b/drivers/gpu/drm/amd/display/dc/dm_cp_psp.h @@ -41,6 +41,7 @@ struct cp_psp_stream_config { uint8_t phy_idx; uint8_t assr_enabled; uint8_t mst_enabled; + uint8_t frl_enabled; uint8_t dp2_enabled; uint8_t usb4_enabled; void *dm_stream_ctx; diff --git a/drivers/gpu/drm/amd/display/dc/dm_services_types.h b/drivers/gpu/drm/amd/display/dc/dm_services_types.h index 3b093b8699abd..e676e395cabf3 100644 --- a/drivers/gpu/drm/amd/display/dc/dm_services_types.h +++ b/drivers/gpu/drm/amd/display/dc/dm_services_types.h @@ -292,6 +292,7 @@ struct dm_process_phy_transition_init_params { enum dc_link_rate display_port_link_rate; uint32_t transition_bitmask; uint8_t hdmi_frl_num_lanes; + enum hdmi_frl_link_rate hdmi_frl_link_rate; }; struct dm_process_phy_transition_input_params { diff --git a/drivers/gpu/drm/amd/display/dc/dml/Makefile b/drivers/gpu/drm/amd/display/dc/dml/Makefile index 268b5fbdb48bd..10d4ace04d4f1 100644 --- a/drivers/gpu/drm/amd/display/dc/dml/Makefile +++ b/drivers/gpu/drm/amd/display/dc/dml/Makefile @@ -78,6 +78,7 @@ CFLAGS_$(AMDDALPATH)/dc/dml/calcs/dcn_calcs.o := $(dml_ccflags) CFLAGS_$(AMDDALPATH)/dc/dml/calcs/dcn_calc_auto.o := $(dml_ccflags) CFLAGS_$(AMDDALPATH)/dc/dml/calcs/dcn_calc_math.o := $(dml_ccflags) -Wno-tautological-compare CFLAGS_REMOVE_$(AMDDALPATH)/dc/dml/display_mode_vba.o := $(dml_rcflags) +CFLAGS_$(AMDDALPATH)/dc/dml/dml1_frl_cap_chk.o := $(dml_ccflags) CFLAGS_REMOVE_$(AMDDALPATH)/dc/dml/dcn20/display_mode_vba_20.o := $(dml_rcflags) CFLAGS_REMOVE_$(AMDDALPATH)/dc/dml/dcn20/display_rq_dlg_calc_20.o := $(dml_rcflags) CFLAGS_REMOVE_$(AMDDALPATH)/dc/dml/dcn20/display_mode_vba_20v2.o := $(dml_rcflags) @@ -117,9 +118,11 @@ CFLAGS_REMOVE_$(AMDDALPATH)/dc/dml/display_rq_dlg_helpers.o := $(dml_rcflags) CFLAGS_REMOVE_$(AMDDALPATH)/dc/dml/calcs/dcn_calcs.o := $(dml_rcflags) CFLAGS_REMOVE_$(AMDDALPATH)/dc/dml/calcs/dcn_calc_auto.o := $(dml_rcflags) CFLAGS_REMOVE_$(AMDDALPATH)/dc/dml/calcs/dcn_calc_math.o := $(dml_rcflags) +CFLAGS_REMOVE_$(AMDDALPATH)/dc/dml/dml1_frl_cap_chk.o := $(dml_rcflags) ifdef CONFIG_DRM_AMD_DC_FP DML += display_mode_lib.o display_rq_dlg_helpers.o dml1_display_rq_dlg_calc.o +DML += dml1_frl_cap_chk.o DML += dcn10/dcn10_fpu.o DML += dcn20/dcn20_fpu.o DML += display_mode_vba.o dcn20/display_rq_dlg_calc_20.o dcn20/display_mode_vba_20.o diff --git a/drivers/gpu/drm/amd/display/dc/dml/dcn20/dcn20_fpu.c b/drivers/gpu/drm/amd/display/dc/dml/dcn20/dcn20_fpu.c index e82f2d5312118..2a60c4af06e12 100644 --- a/drivers/gpu/drm/amd/display/dc/dml/dcn20/dcn20_fpu.c +++ b/drivers/gpu/drm/amd/display/dc/dml/dcn20/dcn20_fpu.c @@ -1048,6 +1048,8 @@ static bool is_dtbclk_required(struct dc *dc, struct dc_state *context) for (i = 0; i < dc->res_pool->pipe_count; i++) { if (!context->res_ctx.pipe_ctx[i].stream) continue; + if (dc_is_hdmi_frl_signal(context->res_ctx.pipe_ctx[i].stream->signal)) + return true; if (dc->link_srv->dp_is_128b_132b_signal(&context->res_ctx.pipe_ctx[i])) return true; } @@ -1463,6 +1465,9 @@ int dcn20_populate_dml_pipes_from_context(struct dc *dc, case SIGNAL_TYPE_DVI_DUAL_LINK: pipes[pipe_cnt].dout.output_type = dm_hdmi; break; + case SIGNAL_TYPE_HDMI_FRL: + pipes[pipe_cnt].dout.output_type = dm_hdmifrl; + break; default: /* In case there is no signal, set dp with 4 lanes to allow max config */ pipes[pipe_cnt].dout.is_virtual = 1; 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 0cdd60869ce1c..715c0d375c53f 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 @@ -33,6 +33,8 @@ #include "display_mode_vba_30.h" #include "dcn30_fpu.h" +#include "../dml1_frl_cap_chk.h" + #define REG(reg)\ optc1->tg_regs->reg @@ -131,6 +133,7 @@ struct _vcs_dpi_soc_bounding_box_st dcn3_0_soc = { .phyclk_mhz = 300.0, .phyclk_d18_mhz = 667.0, .dscclk_mhz = 405.6, + .dtbclk_mhz = 1217.0, }, }, @@ -739,3 +742,127 @@ void patch_dcn30_soc_bounding_box(struct dc *dc, struct _vcs_dpi_soc_bounding_bo } } } + +#undef DC_LOGGER +#define DC_LOGGER \ + enc3->base.ctx->logger + +#define hdmi_frl_print(str, ...) {DC_LOG_HDMI_FRL(str, ##__VA_ARGS__); } + +#define DEBUG_FRL_CAP_CHK 1 + +void hpo_fpu_enc3_validate_hdmi_frl_output_link(struct hpo_frl_stream_encoder *enc, + struct dc_hdmi_frl_link_settings *frl_link_settings, + struct frl_cap_chk_params *frl_params, + const struct dc_crtc_timing *timing, + unsigned int dsc_max_rate) +{ + (void)enc; + dc_assert_fp_enabled(); + + switch (frl_link_settings->frl_link_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 && + (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; + frl_params->vic = timing->vic; +} + +void hpo_fpu_enc3_validate_hdmi_frl_output_timing( + const struct dc_crtc_timing *timing, + const struct audio_check *audio, + struct frl_cap_chk_params *frl_params) +{ + dc_assert_fp_enabled(); + + 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; + } + + frl_params->audio_packet_type = audio->audio_packet_type; + frl_params->f_audio = audio->max_audiosample_rate; + frl_params->acat = audio->acat; +} + +enum frl_cap_chk_result frl_fpu_cap_chk_common(struct hpo_frl_stream_encoder *enc, + struct frl_cap_chk_intermediates *inter, + struct frl_cap_chk_params *params) +{ + (void)enc; + return dml1_frl_cap_chk_common(inter, params); +} + + +enum frl_cap_chk_result frl_fpu_cap_chk_uncompressed(struct hpo_frl_stream_encoder *enc, + struct frl_cap_chk_params *params, + struct frl_cap_chk_intermediates *inter) +{ + (void)enc; + return dml1_frl_cap_chk_uncompressed(params, inter); +} + +enum frl_cap_chk_result frl_fpu_cap_chk_compressed(struct hpo_frl_stream_encoder *enc, + struct frl_cap_chk_params *params, + struct frl_cap_chk_intermediates *inter) +{ + (void)enc; + 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/dcn30_fpu.h b/drivers/gpu/drm/amd/display/dc/dml/dcn30/dcn30_fpu.h index e3b6ad6a87848..e3a46915d168d 100644 --- a/drivers/gpu/drm/amd/display/dc/dml/dcn30/dcn30_fpu.h +++ b/drivers/gpu/drm/amd/display/dc/dml/dcn30/dcn30_fpu.h @@ -70,4 +70,26 @@ void dcn3_fpu_build_wm_range_table(struct clk_mgr *base); void patch_dcn30_soc_bounding_box(struct dc *dc, struct _vcs_dpi_soc_bounding_box_st *dcn3_0_ip); +void hpo_fpu_enc3_validate_hdmi_frl_output_link(struct hpo_frl_stream_encoder *enc, + struct dc_hdmi_frl_link_settings *frl_link_settings, + struct frl_cap_chk_params *frl_params, + const struct dc_crtc_timing *timing, + unsigned int dsc_max_rate); + +void hpo_fpu_enc3_validate_hdmi_frl_output_timing(const struct dc_crtc_timing *timing, + const struct audio_check *audio, + struct frl_cap_chk_params *frl_params); + +enum frl_cap_chk_result frl_fpu_cap_chk_common(struct hpo_frl_stream_encoder *enc, + struct frl_cap_chk_intermediates *inter, + struct frl_cap_chk_params *params); + +enum frl_cap_chk_result frl_fpu_cap_chk_uncompressed(struct hpo_frl_stream_encoder *enc, + struct frl_cap_chk_params *params, + struct frl_cap_chk_intermediates *inter); + +enum frl_cap_chk_result frl_fpu_cap_chk_compressed(struct hpo_frl_stream_encoder *enc, + struct frl_cap_chk_params *params, + struct frl_cap_chk_intermediates *inter); + #endif /* __DCN30_FPU_H__*/ 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 634982173190e..55db254379520 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 @@ -28,6 +28,8 @@ #include "display_mode_vba_30.h" #include "../dml_inline_defs.h" +#include "../dml1_frl_cap_chk.h" + /* * NOTE: @@ -324,6 +326,17 @@ static void CalculateUrgentBurstFactor( double *UrgentBurstFactorChroma, bool *NotEnoughUrgentLatencyHiding); +static double RequiredDTBCLK( + bool DSCEnable, + double PixelClock, + enum output_format_class OutputFormat, + double OutputBPP, + int DSCSlices, + long HTotal, + long HActive, + int AudioRate, + int AudioLayoutSingle); + static void UseMinimumDCFCLK( struct display_mode_lib *mode_lib, struct vba_vars_st *v, @@ -626,6 +639,8 @@ static unsigned int dscceComputeDelay( pixelsPerClock = 1; else if (pixelFormat == dm_n422) pixelsPerClock = 2; + else if (Output == dm_hdmifrl) + pixelsPerClock = 2; else pixelsPerClock = 1; @@ -646,6 +661,8 @@ static unsigned int dscceComputeDelay( //422 mode has an additional cycle of delay if (pixelFormat == dm_420 || pixelFormat == dm_444 || pixelFormat == dm_n422) s = 0; + else if (Output == dm_hdmifrl) + s = 0; else s = 1; @@ -716,6 +733,25 @@ static unsigned int dscComputeDelay(enum output_format_class pixelFormat, enum o Delay = Delay + 1; // sft Delay = Delay + 1; + } else if (Output == dm_hdmifrl && pixelFormat != dm_444) { + // sfr + Delay = Delay + 2; + // dsccif + Delay = Delay + 1; + // dscc - input deserializer + Delay = Delay + 5; + // dscc - input cdc fifo + Delay = Delay + 25; + // dscc - cdc uncertainty + Delay = Delay + 2; + // dscc - output cdc fifo + Delay = Delay + 10; + // dscc - cdc uncertainty + Delay = Delay + 2; + // dscc - output serializer + Delay = Delay + 1; + // sft + Delay = Delay + 1; } else { // sfr Delay = Delay + 2; @@ -2016,6 +2052,8 @@ static void DISPCLKDPPCLKDCFCLKDeepSleepPrefetchParametersWatermarksAndPerforman v->DSCFormatFactor = 1; else if (v->OutputFormat[k] == dm_n422) v->DSCFormatFactor = 2; + else if (v->Output[k] == dm_hdmifrl) + v->DSCFormatFactor = 2; else v->DSCFormatFactor = 1; if (v->ODMCombineEnabled[k] == dm_odm_combine_mode_4to1) @@ -3308,19 +3346,41 @@ static double TruncToValidBPP( int NonDSCBPP1 = 0; int NonDSCBPP2 = 0; + enum frl_cap_chk_result hdmifrlresult = { 0 }; + struct frl_cap_chk_params hdmifrlparams = { 0 }; + struct frl_cap_chk_intermediates hdmifrlinter = { 0 }; + + hdmifrlparams.lanes = Lanes; + hdmifrlparams.f_pixel_clock_nominal = PixelClock * 1000000; + hdmifrlparams.r_bit_nominal = LinkBitRate * 1000000; + hdmifrlparams.layout = AudioLayout; + hdmifrlparams.f_audio = AudioRate * 1000; + 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; NonDSCBPP1 = 15; NonDSCBPP2 = 18; MinDSCBPP = 6; MaxDSCBPP = 1.5 * DSCInputBitPerComponent - 1.0 / 16; + hdmifrlparams.pixel_encoding = HDMI_FRL_PIXEL_ENCODING_420; + hdmifrlparams.bpc = (int)(DesiredBPP * 2 / 3); } else if (Format == dm_444) { NonDSCBPP0 = 24; NonDSCBPP1 = 30; NonDSCBPP2 = 36; MinDSCBPP = 8; MaxDSCBPP = 3 * DSCInputBitPerComponent - 1.0 / 16; + hdmifrlparams.pixel_encoding = HDMI_FRL_PIXEL_ENCODING_444; + hdmifrlparams.bpc = (int)(DesiredBPP / 3); } else { + hdmifrlparams.pixel_encoding = HDMI_FRL_PIXEL_ENCODING_422; + hdmifrlparams.bpc = (int)(DesiredBPP / 2); NonDSCBPP0 = 16; NonDSCBPP1 = 20; NonDSCBPP2 = 24; @@ -3328,12 +3388,20 @@ static double TruncToValidBPP( if (Format == dm_n422) { MinDSCBPP = 7; MaxDSCBPP = 2 * DSCInputBitPerComponent - 1.0 / 16.0; + } else if (Output == dm_hdmifrl) { + MinDSCBPP = 7; + MaxDSCBPP = 2 * DSCInputBitPerComponent - 1.0 / 16.0; } else { MinDSCBPP = 8; MaxDSCBPP = 3 * DSCInputBitPerComponent - 1.0 / 16.0; } } + if (Output == dm_hdmifrl) { + hdmifrlresult = dml1_frl_cap_chk_inter(&hdmifrlparams, &hdmifrlinter); + MaxLinkBPP = (1 - hdmifrlinter.overhead_max) * dml_min(hdmifrlinter.r_frl_char_min * 16 * Lanes / hdmifrlinter.f_pixel_clock_max + 24 * TB_BORROWED_MAX / HActive, + (hdmifrlinter.r_frl_char_min * 16 * Lanes / hdmifrlinter.f_pixel_clock_max * HTotal - 16 * hdmifrlinter.blank_audio_min) / HActive); + } else if (DSCEnable && Output == dm_dp) { MaxLinkBPP = LinkBitRate / 10 * 8 * Lanes / PixelClock * (1 - 2.4 / 100); } else { @@ -3371,6 +3439,8 @@ static double TruncToValidBPP( if (!((DSCEnable == false && (DesiredBPP == NonDSCBPP2 || DesiredBPP == NonDSCBPP1 || DesiredBPP == NonDSCBPP0 || DesiredBPP == 18)) || (DSCEnable && DesiredBPP >= MinDSCBPP && DesiredBPP <= MaxDSCBPP))) { return BPP_INVALID; + } else if ((Output == dm_hdmifrl && hdmifrlresult != FRL_CAP_CHK_OK) || (Output != dm_hdmifrl && MaxLinkBPP < DesiredBPP)) { + return BPP_INVALID; } else { return DesiredBPP; } @@ -4012,6 +4082,172 @@ void dml30_ModeSupportAndSystemConfigurationFull(struct display_mode_lib *mode_l // TODO: Need some other way to handle this nonsense // v->OutputTypeAndRatePerState[i][k] = v->Output[k] & " HBR3" } + } else if (v->Output[k] == dm_hdmifrl) { + if (v->DSCEnable[k] == true || v->ODMCombineEnablePerState[i][k] == dm_odm_combine_mode_4to1) { + v->RequiresDSC[i][k] = true; + v->LinkDSCEnable = true; + v->RequiresFEC[i][k] = true; + } else { + v->RequiresDSC[i][k] = false; + v->LinkDSCEnable = false; + v->RequiresFEC[i][k] = false; + } + v->Outbpp = BPP_INVALID; + if (v->PHYCLKD18PerState[i] >= 3000.0 / 18) { + v->Outbpp = TruncToValidBPP( + 3000, + 3, + v->HTotal[k], + v->HActive[k], + v->PixelClockBackEnd[k], + v->ForcedOutputLinkBPP[k], + v->LinkDSCEnable, + v->Output[k], + v->OutputFormat[k], + v->DSCInputBitPerComponent[k], + v->NumberOfDSCSlices[k], + v->AudioSampleRate[k], + v->AudioSampleLayout[k], + v->ODMCombineEnablePerState[i][k]); + v->OutputBppPerState[i][k] = v->Outbpp; + //v->OutputTypeAndRatePerState[i][k] = v->Output[k] & "3x3"; + } + if (v->Outbpp == BPP_INVALID && v->PHYCLKD18PerState[i] >= 6000.0 / 18) { + v->Outbpp = TruncToValidBPP( + 6000, + 3, + v->HTotal[k], + v->HActive[k], + v->PixelClockBackEnd[k], + v->ForcedOutputLinkBPP[k], + v->LinkDSCEnable, + v->Output[k], + v->OutputFormat[k], + v->DSCInputBitPerComponent[k], + v->NumberOfDSCSlices[k], + v->AudioSampleRate[k], + v->AudioSampleLayout[k], + v->ODMCombineEnablePerState[i][k]); + v->OutputBppPerState[i][k] = v->Outbpp; + //v->OutputTypeAndRatePerState[i][k] = v->Output[k] & "6x3"; + } + if (v->Outbpp == BPP_INVALID && v->PHYCLKD18PerState[i] >= 6000.0 / 18) { + v->Outbpp = TruncToValidBPP( + 6000, + 4, + v->HTotal[k], + v->HActive[k], + v->PixelClockBackEnd[k], + v->ForcedOutputLinkBPP[k], + v->LinkDSCEnable, + v->Output[k], + v->OutputFormat[k], + v->DSCInputBitPerComponent[k], + v->NumberOfDSCSlices[k], + v->AudioSampleRate[k], + v->AudioSampleLayout[k], + v->ODMCombineEnablePerState[i][k]); + v->OutputBppPerState[i][k] = v->Outbpp; + //v->OutputTypeAndRatePerState[i][k] = v->Output[k] & "6x4"; + } + if (v->Outbpp == BPP_INVALID && v->PHYCLKD18PerState[i] >= 8000.0 / 18) { + v->Outbpp = TruncToValidBPP( + 8000, + 4, + v->HTotal[k], + v->HActive[k], + v->PixelClockBackEnd[k], + v->ForcedOutputLinkBPP[k], + v->LinkDSCEnable, + v->Output[k], + v->OutputFormat[k], + v->DSCInputBitPerComponent[k], + v->NumberOfDSCSlices[k], + v->AudioSampleRate[k], + v->AudioSampleLayout[k], + v->ODMCombineEnablePerState[i][k]); + v->OutputBppPerState[i][k] = v->Outbpp; + //v->OutputTypeAndRatePerState(i, k) = v->Output[k] & "8x4"; + } + if (v->Outbpp == BPP_INVALID && v->PHYCLKD18PerState[i] >= 10000.0 / 18) { + v->Outbpp = TruncToValidBPP( + 10000, + 4, + v->HTotal[k], + v->HActive[k], + v->PixelClockBackEnd[k], + v->ForcedOutputLinkBPP[k], + v->LinkDSCEnable, + v->Output[k], + v->OutputFormat[k], + v->DSCInputBitPerComponent[k], + v->NumberOfDSCSlices[k], + v->AudioSampleRate[k], + v->AudioSampleLayout[k], + v->ODMCombineEnablePerState[i][k]); + if (v->Outbpp == BPP_INVALID && v->ForcedOutputLinkBPP[k] == 0 + && v->PHYCLKD18PerState[i] < 12000.0 / 18) { + v->RequiresDSC[i][k] = true; + v->LinkDSCEnable = true; + v->RequiresFEC[i][k] = true; + v->Outbpp = TruncToValidBPP( + 10000, + 4, + v->HTotal[k], + v->HActive[k], + v->PixelClockBackEnd[k], + v->ForcedOutputLinkBPP[k], + v->LinkDSCEnable, + v->Output[k], + v->OutputFormat[k], + v->DSCInputBitPerComponent[k], + v->NumberOfDSCSlices[k], + v->AudioSampleRate[k], + v->AudioSampleLayout[k], + v->ODMCombineEnablePerState[i][k]); + } + v->OutputBppPerState[i][k] = v->Outbpp; + //v->OutputTypeAndRatePerState[i][k] = v->Output[k] & "10x4"; + } + if (v->Outbpp == BPP_INVALID && v->PHYCLKD18PerState[i] >= 12000.0 / 18) { + v->Outbpp = TruncToValidBPP( + 12000, + 4, + v->HTotal[k], + v->HActive[k], + v->PixelClockBackEnd[k], + v->ForcedOutputLinkBPP[k], + v->LinkDSCEnable, + v->Output[k], + v->OutputFormat[k], + v->DSCInputBitPerComponent[k], + v->NumberOfDSCSlices[k], + v->AudioSampleRate[k], + v->AudioSampleLayout[k], + v->ODMCombineEnablePerState[i][k]); + if (v->Outbpp == BPP_INVALID && v->ForcedOutputLinkBPP[k] == 0) { + v->RequiresDSC[i][k] = true; + v->LinkDSCEnable = true; + v->RequiresFEC[i][k] = true; + v->Outbpp = TruncToValidBPP( + 12000, + 4, + v->HTotal[k], + v->HActive[k], + v->PixelClockBackEnd[k], + v->ForcedOutputLinkBPP[k], + v->LinkDSCEnable, + v->Output[k], + v->OutputFormat[k], + v->DSCInputBitPerComponent[k], + v->NumberOfDSCSlices[k], + v->AudioSampleRate[k], + v->AudioSampleLayout[k], + v->ODMCombineEnablePerState[i][k]); + } + v->OutputBppPerState[i][k] = v->Outbpp; + //v->OutputTypeAndRatePerState[i][k] = v->Output[k] & "12x4"; + } } } else { v->OutputBppPerState[i][k] = 0; @@ -4021,7 +4257,7 @@ void dml30_ModeSupportAndSystemConfigurationFull(struct display_mode_lib *mode_l for (i = start_state; i < v->soc.num_states; i++) { v->DIOSupport[i] = true; for (k = 0; k <= v->NumberOfActivePlanes - 1; k++) { - if (!v->skip_dio_check[k] && v->BlendingAndTiming[k] == k && (v->Output[k] == dm_dp || v->Output[k] == dm_edp || v->Output[k] == dm_hdmi) + if (!v->skip_dio_check[k] && v->BlendingAndTiming[k] == k && (v->Output[k] == dm_dp || v->Output[k] == dm_edp || v->Output[k] == dm_hdmi || v->Output[k] == dm_hdmifrl) && (v->OutputBppPerState[i][k] == 0 || (v->OutputFormat[k] == dm_420 && v->Interlace[k] == true && v->ProgressiveToInterlaceUnitInOPP == true))) { v->DIOSupport[i] = false; @@ -4029,6 +4265,25 @@ void dml30_ModeSupportAndSystemConfigurationFull(struct display_mode_lib *mode_l } } + for (i = start_state; i < v->soc.num_states; ++i) { + v->DTBCLKRequiredMoreThanSupported[i] = false; + for (k = 0; k < v->NumberOfActivePlanes; ++k) { + if (v->BlendingAndTiming[k] == k && v->Output[k] == dm_hdmifrl + && RequiredDTBCLK( + v->RequiresDSC[i][k], + v->PixelClockBackEnd[k], + v->OutputFormat[k], + v->OutputBppPerState[i][k], + v->NumberOfDSCSlices[k], + v->HTotal[k], + v->HActive[k], + v->AudioSampleRate[k], + v->AudioSampleLayout[k]) > v->DTBCLKPerState[i]) { + v->DTBCLKRequiredMoreThanSupported[i] = true; + } + } + } + for (i = start_state; i < v->soc.num_states; ++i) { v->ODMCombine4To1SupportCheckOK[i] = true; for (k = 0; k < v->NumberOfActivePlanes; ++k) { @@ -6241,6 +6496,29 @@ static double CalculateUrgentLatency( return ret; } +static double RequiredDTBCLK( + bool DSCEnable, + double PixelClock, + enum output_format_class OutputFormat, + double OutputBPP, + int DSCSlices, + long HTotal, + long HActive, + int AudioRate, + int AudioLayout) +{ + if (DSCEnable != true) { + return dml_max(PixelClock / 4.0 * OutputBPP / 24.0, 25.0); + } else { + double PixelWordRate = PixelClock / (OutputFormat == dm_444 ? 1 : 2); + double HCActive = dml_ceil(DSCSlices * dml_ceil(OutputBPP * dml_ceil(HActive / DSCSlices, 1) / 8.0, 1) / 3.0, 1); + double HCBlank = 64 + 32 * dml_ceil((double)AudioRate * (AudioLayout == 1 ? 1.0 : 0.25) * HTotal / (PixelClock * 1000), 1); + double AverageTribyteRate = PixelWordRate * (HCActive + HCBlank) / HTotal; + double HActiveTribyteRate = PixelWordRate * HCActive / HActive; + return dml_max4(PixelWordRate / 4.0, AverageTribyteRate / 4.0, HActiveTribyteRate / 4.0, 25.0) * 1.002; + } +} + static noinline_for_stack void UseMinimumDCFCLK( struct display_mode_lib *mode_lib, struct vba_vars_st *v, diff --git a/drivers/gpu/drm/amd/display/dc/dml/dcn302/dcn302_fpu.c b/drivers/gpu/drm/amd/display/dc/dml/dcn302/dcn302_fpu.c index 8d7c59ec701dc..5eec0eb1bbd4c 100644 --- a/drivers/gpu/drm/amd/display/dc/dml/dcn302/dcn302_fpu.c +++ b/drivers/gpu/drm/amd/display/dc/dml/dcn302/dcn302_fpu.c @@ -119,6 +119,7 @@ struct _vcs_dpi_soc_bounding_box_st dcn3_02_soc = { .phyclk_mhz = 300.0, .phyclk_d18_mhz = 667.0, .dscclk_mhz = 405.6, + .dtbclk_mhz = 1217.0, }, }, diff --git a/drivers/gpu/drm/amd/display/dc/dml/dcn303/dcn303_fpu.c b/drivers/gpu/drm/amd/display/dc/dml/dcn303/dcn303_fpu.c index b5d3fd4c3694e..d24843ef093b0 100644 --- a/drivers/gpu/drm/amd/display/dc/dml/dcn303/dcn303_fpu.c +++ b/drivers/gpu/drm/amd/display/dc/dml/dcn303/dcn303_fpu.c @@ -118,6 +118,7 @@ struct _vcs_dpi_soc_bounding_box_st dcn3_03_soc = { .phyclk_mhz = 300.0, .phyclk_d18_mhz = 667.0, .dscclk_mhz = 405.6, + .dtbclk_mhz = 1217.0, }, }, 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 9833467722b93..3216fd10ad04b 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 @@ -29,6 +29,8 @@ #include "display_mode_vba_31.h" #include "../dml_inline_defs.h" +#include "../dml1_frl_cap_chk.h" + /* * NOTE: * This file is gcc-parsable HW gospel, coming straight from HW engineers. @@ -357,6 +359,17 @@ static void CalculateUrgentBurstFactor( double *UrgentBurstFactorChroma, bool *NotEnoughUrgentLatencyHiding); +static double RequiredDTBCLK( + bool DSCEnable, + double PixelClock, + enum output_format_class OutputFormat, + double OutputBPP, + int DSCSlices, + int HTotal, + int HActive, + int AudioRate, + int AudioLayoutSingle); + static void UseMinimumDCFCLK( struct display_mode_lib *mode_lib, int MaxPrefetchMode, @@ -695,6 +708,8 @@ static unsigned int dscceComputeDelay( pixelsPerClock = 1; else if (pixelFormat == dm_n422) pixelsPerClock = 2; + else if (Output == dm_hdmifrl) + pixelsPerClock = 2; // #all other modes operate at 1 pixel per clock else pixelsPerClock = 1; @@ -716,6 +731,8 @@ static unsigned int dscceComputeDelay( //422 mode has an additional cycle of delay if (pixelFormat == dm_420 || pixelFormat == dm_444 || pixelFormat == dm_n422) s = 0; + else if (Output == dm_hdmifrl) + s = 0; else s = 1; @@ -786,6 +803,25 @@ static unsigned int dscComputeDelay(enum output_format_class pixelFormat, enum o Delay = Delay + 1; // sft Delay = Delay + 1; + } else if (Output == dm_hdmifrl && pixelFormat != dm_444) { + // sfr + Delay = Delay + 2; + // dsccif + Delay = Delay + 1; + // dscc - input deserializer + Delay = Delay + 5; + // dscc - input cdc fifo + Delay = Delay + 25; + // dscc - cdc uncertainty + Delay = Delay + 2; + // dscc - output cdc fifo + Delay = Delay + 10; + // dscc - cdc uncertainty + Delay = Delay + 2; + // dscc - output serializer + Delay = Delay + 1; + // sft + Delay = Delay + 1; } else { // sfr Delay = Delay + 2; @@ -2243,6 +2279,8 @@ static void DISPCLKDPPCLKDCFCLKDeepSleepPrefetchParametersWatermarksAndPerforman v->DSCFormatFactor = 1; else if (v->OutputFormat[k] == dm_n422) v->DSCFormatFactor = 2; + else if (v->Output[k] == dm_hdmifrl) + v->DSCFormatFactor = 2; else v->DSCFormatFactor = 1; if (v->ODMCombineEnabled[k] == dm_odm_combine_mode_4to1) @@ -3615,19 +3653,41 @@ static double TruncToValidBPP( int NonDSCBPP1; int NonDSCBPP2; + enum frl_cap_chk_result hdmifrlresult = FRL_CAP_CHK_OK; + struct frl_cap_chk_params hdmifrlparams = { 0 }; + struct frl_cap_chk_intermediates hdmifrlinter = { 0 }; + + hdmifrlparams.lanes = Lanes; + hdmifrlparams.f_pixel_clock_nominal = PixelClock * 1000000; + hdmifrlparams.r_bit_nominal = LinkBitRate * 1000000; + hdmifrlparams.layout = AudioLayout; + hdmifrlparams.f_audio = AudioRate * 1000; + 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; NonDSCBPP1 = 15; NonDSCBPP2 = 18; MinDSCBPP = 6; MaxDSCBPP = 1.5 * DSCInputBitPerComponent - 1.0 / 16; + hdmifrlparams.pixel_encoding = HDMI_FRL_PIXEL_ENCODING_420; + hdmifrlparams.bpc = (int)(DesiredBPP * 2 / 3); } else if (Format == dm_444) { NonDSCBPP0 = 24; NonDSCBPP1 = 30; NonDSCBPP2 = 36; MinDSCBPP = 8; MaxDSCBPP = 3 * DSCInputBitPerComponent - 1.0 / 16; + hdmifrlparams.pixel_encoding = HDMI_FRL_PIXEL_ENCODING_444; + hdmifrlparams.bpc = (int)(DesiredBPP / 3); } else { + hdmifrlparams.pixel_encoding = HDMI_FRL_PIXEL_ENCODING_422; + hdmifrlparams.bpc = (int)(DesiredBPP / 2); NonDSCBPP0 = 16; NonDSCBPP1 = 20; @@ -3636,12 +3696,22 @@ static double TruncToValidBPP( if (Format == dm_n422) { MinDSCBPP = 7; MaxDSCBPP = 2 * DSCInputBitPerComponent - 1.0 / 16.0; + } else if (Output == dm_hdmifrl) { + MinDSCBPP = 7; + MaxDSCBPP = 2 * DSCInputBitPerComponent - 1.0 / 16.0; } else { MinDSCBPP = 8; MaxDSCBPP = 3 * DSCInputBitPerComponent - 1.0 / 16.0; } } + if (Output == dm_hdmifrl) { + hdmifrlresult = dml1_frl_cap_chk_inter(&hdmifrlparams, &hdmifrlinter); + MaxLinkBPP = (1 - hdmifrlinter.overhead_max) + * dml_min( + hdmifrlinter.r_frl_char_min * 16 * Lanes / hdmifrlinter.f_pixel_clock_max + 24 * TB_BORROWED_MAX / HActive, + (hdmifrlinter.r_frl_char_min * 16 * Lanes / hdmifrlinter.f_pixel_clock_max * HTotal - 16 * hdmifrlinter.blank_audio_min) / HActive); + } else if (DSCEnable && Output == dm_dp) { MaxLinkBPP = LinkBitRate / 10 * 8 * Lanes / PixelClock * (1 - 2.4 / 100); } else { @@ -3678,6 +3748,8 @@ static double TruncToValidBPP( if (!((DSCEnable == false && (DesiredBPP == NonDSCBPP2 || DesiredBPP == NonDSCBPP1 || DesiredBPP <= NonDSCBPP0)) || (DSCEnable && DesiredBPP >= MinDSCBPP && DesiredBPP <= MaxDSCBPP))) { return BPP_INVALID; + } else if ((Output == dm_hdmifrl && hdmifrlresult != FRL_CAP_CHK_OK) || (Output != dm_hdmifrl && MaxLinkBPP < DesiredBPP)) { + return BPP_INVALID; } else { return DesiredBPP; } @@ -4104,6 +4176,7 @@ void dml31_ModeSupportAndSystemConfigurationFull(struct display_mode_lib *mode_l if (v->ODMCombinePolicy == dm_odm_combine_policy_none || !(v->Output[k] == dm_dp || + v->Output[k] == dm_hdmifrl || v->Output[k] == dm_dp2p0 || v->Output[k] == dm_edp)) { v->ODMCombineEnablePerState[i][k] = dm_odm_combine_mode_disabled; @@ -4528,6 +4601,131 @@ void dml31_ModeSupportAndSystemConfigurationFull(struct display_mode_lib *mode_l // v->OutputTypeAndRatePerState[i][k] = v->Output[k] & " HBR3" } } + } else if (v->Output[k] == dm_hdmifrl) { + if (v->DSCEnable[k] == true) { + v->RequiresDSC[i][k] = true; + v->LinkDSCEnable = true; + v->RequiresFEC[i][k] = true; + } else { + v->RequiresDSC[i][k] = false; + v->LinkDSCEnable = false; + v->RequiresFEC[i][k] = false; + } + v->Outbpp = BPP_INVALID; + if (v->PHYCLKD18PerState[i] >= 3000.0 / 18) { + v->Outbpp = TruncToValidBPP( + 3000, + 3, + v->HTotal[k], + v->HActive[k], + v->PixelClockBackEnd[k], + v->ForcedOutputLinkBPP[k], + v->LinkDSCEnable, + v->Output[k], + v->OutputFormat[k], + v->DSCInputBitPerComponent[k], + v->NumberOfDSCSlices[k], + v->AudioSampleRate[k], + v->AudioSampleLayout[k], + v->ODMCombineEnablePerState[i][k]); + v->OutputBppPerState[i][k] = v->Outbpp; + //v->OutputTypeAndRatePerState[i][k] = v->Output[k] & "3x3"; + } + if (v->Outbpp == BPP_INVALID && v->PHYCLKD18PerState[i] >= 6000.0 / 18) { + v->Outbpp = TruncToValidBPP( + 6000, + 3, + v->HTotal[k], + v->HActive[k], + v->PixelClockBackEnd[k], + v->ForcedOutputLinkBPP[k], + v->LinkDSCEnable, + v->Output[k], + v->OutputFormat[k], + v->DSCInputBitPerComponent[k], + v->NumberOfDSCSlices[k], + v->AudioSampleRate[k], + v->AudioSampleLayout[k], + v->ODMCombineEnablePerState[i][k]); + v->OutputBppPerState[i][k] = v->Outbpp; + //v->OutputTypeAndRatePerState[i][k] = v->Output[k] & "6x3"; + } + if (v->Outbpp == BPP_INVALID && v->PHYCLKD18PerState[i] >= 6000.0 / 18) { + v->Outbpp = TruncToValidBPP( + 6000, + 4, + v->HTotal[k], + v->HActive[k], + v->PixelClockBackEnd[k], + v->ForcedOutputLinkBPP[k], + v->LinkDSCEnable, + v->Output[k], + v->OutputFormat[k], + v->DSCInputBitPerComponent[k], + v->NumberOfDSCSlices[k], + v->AudioSampleRate[k], + v->AudioSampleLayout[k], + v->ODMCombineEnablePerState[i][k]); + v->OutputBppPerState[i][k] = v->Outbpp; + //v->OutputTypeAndRatePerState[i][k] = v->Output[k] & "6x4"; + } + if (v->Outbpp == BPP_INVALID && v->PHYCLKD18PerState[i] >= 8000.0 / 18) { + v->Outbpp = TruncToValidBPP( + 8000, + 4, + v->HTotal[k], + v->HActive[k], + v->PixelClockBackEnd[k], + v->ForcedOutputLinkBPP[k], + v->LinkDSCEnable, + v->Output[k], + v->OutputFormat[k], + v->DSCInputBitPerComponent[k], + v->NumberOfDSCSlices[k], + v->AudioSampleRate[k], + v->AudioSampleLayout[k], + v->ODMCombineEnablePerState[i][k]); + v->OutputBppPerState[i][k] = v->Outbpp; + //v->OutputTypeAndRatePerState(i, k) = v->Output[k] & "8x4"; + } + if (v->Outbpp == BPP_INVALID && v->PHYCLKD18PerState[i] >= 10000.0 / 18) { + v->Outbpp = TruncToValidBPP( + 10000, + 4, + v->HTotal[k], + v->HActive[k], + v->PixelClockBackEnd[k], + v->ForcedOutputLinkBPP[k], + v->LinkDSCEnable, + v->Output[k], + v->OutputFormat[k], + v->DSCInputBitPerComponent[k], + v->NumberOfDSCSlices[k], + v->AudioSampleRate[k], + v->AudioSampleLayout[k], + v->ODMCombineEnablePerState[i][k]); + v->OutputBppPerState[i][k] = v->Outbpp; + //v->OutputTypeAndRatePerState[i][k] = v->Output[k] & "10x4"; + } + if (v->Outbpp == BPP_INVALID && v->PHYCLKD18PerState[i] >= 12000.0 / 18) { + v->Outbpp = TruncToValidBPP( + 12000, + 4, + v->HTotal[k], + v->HActive[k], + v->PixelClockBackEnd[k], + v->ForcedOutputLinkBPP[k], + v->LinkDSCEnable, + v->Output[k], + v->OutputFormat[k], + v->DSCInputBitPerComponent[k], + v->NumberOfDSCSlices[k], + v->AudioSampleRate[k], + v->AudioSampleLayout[k], + v->ODMCombineEnablePerState[i][k]); + v->OutputBppPerState[i][k] = v->Outbpp; + //v->OutputTypeAndRatePerState[i][k] = v->Output[k] & "12x4"; + } } } else { v->OutputBppPerState[i][k] = 0; @@ -4541,6 +4739,7 @@ void dml31_ModeSupportAndSystemConfigurationFull(struct display_mode_lib *mode_l if (v->BlendingAndTiming[k] == k && (v->Output[k] == dm_dp || v->Output[k] == dm_edp || + v->Output[k] == dm_hdmifrl || v->Output[k] == dm_hdmi) && v->OutputBppPerState[i][k] == 0) { v->LinkCapacitySupport[i] = false; } @@ -4552,6 +4751,7 @@ void dml31_ModeSupportAndSystemConfigurationFull(struct display_mode_lib *mode_l if (v->BlendingAndTiming[k] == k && (v->Output[k] == dm_dp || v->Output[k] == dm_edp || + v->Output[k] == dm_hdmifrl || v->Output[k] == dm_hdmi)) { if (v->OutputFormat[k] == dm_420 && v->Interlace[k] == 1 && v->ProgressiveToInterlaceUnitInOPP == true) { P2IWith420 = true; @@ -4563,11 +4763,31 @@ void dml31_ModeSupportAndSystemConfigurationFull(struct display_mode_lib *mode_l } } + for (i = 0; i < v->soc.num_states; ++i) { + v->DTBCLKRequiredMoreThanSupported[i] = false; + for (k = 0; k < v->NumberOfActivePlanes; ++k) { + if (v->BlendingAndTiming[k] == k && v->Output[k] == dm_hdmifrl + && RequiredDTBCLK( + v->RequiresDSC[i][k], + v->PixelClockBackEnd[k], + v->OutputFormat[k], + v->OutputBppPerState[i][k], + v->NumberOfDSCSlices[k], + v->HTotal[k], + v->HActive[k], + v->AudioSampleRate[k], + v->AudioSampleLayout[k]) > v->DTBCLKPerState[i]) { + v->DTBCLKRequiredMoreThanSupported[i] = true; + } + } + } + for (i = 0; i < v->soc.num_states; ++i) { v->ODMCombine4To1SupportCheckOK[i] = true; for (k = 0; k < v->NumberOfActivePlanes; ++k) { if (v->BlendingAndTiming[k] == k && v->ODMCombineEnablePerState[i][k] == dm_odm_combine_mode_4to1 && (v->ODMCombine4To1Supported == false || v->Output[k] == dm_dp || v->Output[k] == dm_edp + || (v->Output[k] == dm_hdmifrl && v->DSCEnable[k] == false) || v->Output[k] == dm_hdmi)) { v->ODMCombine4To1SupportCheckOK[i] = false; } @@ -7046,6 +7266,29 @@ static double CalculateUrgentLatency( return ret; } +static double RequiredDTBCLK( + bool DSCEnable, + double PixelClock, + enum output_format_class OutputFormat, + double OutputBPP, + int DSCSlices, + int HTotal, + int HActive, + int AudioRate, + int AudioLayout) +{ + if (DSCEnable != true) { + return dml_max(PixelClock / 4.0 * OutputBPP / 24.0, 25.0); + } else { + double PixelWordRate = PixelClock / (OutputFormat == dm_444 ? 1 : 2); + double HCActive = dml_ceil(DSCSlices * dml_ceil(OutputBPP * dml_ceil(HActive / DSCSlices, 1) / 8.0, 1) / 3.0, 1); + double HCBlank = 64 + 32 * dml_ceil(AudioRate * (AudioLayout == 1 ? 1.0 : 0.25) * HTotal / (PixelClock * 1000), 1); + double AverageTribyteRate = PixelWordRate * (HCActive + HCBlank) / HTotal; + double HActiveTribyteRate = PixelWordRate * HCActive / HActive; + return dml_max4(PixelWordRate / 4.0, AverageTribyteRate / 4.0, HActiveTribyteRate / 4.0, 25.0) * 1.002; + } +} + static noinline_for_stack void UseMinimumDCFCLK( struct display_mode_lib *mode_lib, int MaxPrefetchMode, 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 033fde774337f..2f412cee57c76 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 @@ -29,6 +29,8 @@ #include "display_mode_vba_314.h" #include "../dml_inline_defs.h" +#include "../dml1_frl_cap_chk.h" + /* * NOTE: * This file is gcc-parsable HW gospel, coming straight from HW engineers. @@ -366,6 +368,17 @@ static void CalculateUrgentBurstFactor( double *UrgentBurstFactorChroma, bool *NotEnoughUrgentLatencyHiding); +static double RequiredDTBCLK( + bool DSCEnable, + double PixelClock, + enum output_format_class OutputFormat, + double OutputBPP, + int DSCSlices, + int HTotal, + int HActive, + int AudioRate, + int AudioLayoutSingle); + static void UseMinimumDCFCLK( struct display_mode_lib *mode_lib, int MaxPrefetchMode, @@ -713,6 +726,8 @@ static unsigned int dscceComputeDelay( pixelsPerClock = 1; else if (pixelFormat == dm_n422) pixelsPerClock = 2; + else if (Output == dm_hdmifrl) + pixelsPerClock = 2; // #all other modes operate at 1 pixel per clock else pixelsPerClock = 1; @@ -734,6 +749,8 @@ static unsigned int dscceComputeDelay( //422 mode has an additional cycle of delay if (pixelFormat == dm_420 || pixelFormat == dm_444 || pixelFormat == dm_n422) s = 0; + else if (Output == dm_hdmifrl) + s = 0; else s = 1; @@ -804,6 +821,25 @@ static unsigned int dscComputeDelay(enum output_format_class pixelFormat, enum o Delay = Delay + 1; // sft Delay = Delay + 1; + } else if (Output == dm_hdmifrl && pixelFormat != dm_444) { + // sfr + Delay = Delay + 2; + // dsccif + Delay = Delay + 1; + // dscc - input deserializer + Delay = Delay + 5; + // dscc - input cdc fifo + Delay = Delay + 25; + // dscc - cdc uncertainty + Delay = Delay + 2; + // dscc - output cdc fifo + Delay = Delay + 10; + // dscc - cdc uncertainty + Delay = Delay + 2; + // dscc - output serializer + Delay = Delay + 1; + // sft + Delay = Delay + 1; } else { // sfr Delay = Delay + 2; @@ -2261,6 +2297,8 @@ static void DISPCLKDPPCLKDCFCLKDeepSleepPrefetchParametersWatermarksAndPerforman v->DSCFormatFactor = 1; else if (v->OutputFormat[k] == dm_n422) v->DSCFormatFactor = 2; + else if (v->Output[k] == dm_hdmifrl) + v->DSCFormatFactor = 2; else v->DSCFormatFactor = 1; if (v->ODMCombineEnabled[k] == dm_odm_combine_mode_4to1) @@ -3721,19 +3759,41 @@ static double TruncToValidBPP( int NonDSCBPP1; int NonDSCBPP2; + enum frl_cap_chk_result hdmifrlresult = FRL_CAP_CHK_OK; + struct frl_cap_chk_params hdmifrlparams = { 0 }; + struct frl_cap_chk_intermediates hdmifrlinter = { 0 }; + + hdmifrlparams.lanes = Lanes; + hdmifrlparams.f_pixel_clock_nominal = PixelClock * 1000000; + hdmifrlparams.r_bit_nominal = LinkBitRate * 1000000; + hdmifrlparams.layout = AudioLayout; + hdmifrlparams.f_audio = AudioRate * 1000; + 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; NonDSCBPP1 = 15; NonDSCBPP2 = 18; MinDSCBPP = 6; MaxDSCBPP = 1.5 * DSCInputBitPerComponent - 1.0 / 16; + hdmifrlparams.pixel_encoding = HDMI_FRL_PIXEL_ENCODING_420; + hdmifrlparams.bpc = (int)(DesiredBPP * 2 / 3); } else if (Format == dm_444) { NonDSCBPP0 = 24; NonDSCBPP1 = 30; NonDSCBPP2 = 36; MinDSCBPP = 8; MaxDSCBPP = 3 * DSCInputBitPerComponent - 1.0 / 16; + hdmifrlparams.pixel_encoding = HDMI_FRL_PIXEL_ENCODING_444; + hdmifrlparams.bpc = (int)(DesiredBPP / 3); } else { + hdmifrlparams.pixel_encoding = HDMI_FRL_PIXEL_ENCODING_422; + hdmifrlparams.bpc = (int)(DesiredBPP / 2); NonDSCBPP0 = 16; NonDSCBPP1 = 20; @@ -3742,12 +3802,22 @@ static double TruncToValidBPP( if (Format == dm_n422) { MinDSCBPP = 7; MaxDSCBPP = 2 * DSCInputBitPerComponent - 1.0 / 16.0; + } else if (Output == dm_hdmifrl) { + MinDSCBPP = 7; + MaxDSCBPP = 2 * DSCInputBitPerComponent - 1.0 / 16.0; } else { MinDSCBPP = 8; MaxDSCBPP = 3 * DSCInputBitPerComponent - 1.0 / 16.0; } } + if (Output == dm_hdmifrl) { + hdmifrlresult = dml1_frl_cap_chk_inter(&hdmifrlparams, &hdmifrlinter); + MaxLinkBPP = (1 - hdmifrlinter.overhead_max) + * dml_min( + hdmifrlinter.r_frl_char_min * 16 * Lanes / hdmifrlinter.f_pixel_clock_max + 24 * TB_BORROWED_MAX / HActive, + (hdmifrlinter.r_frl_char_min * 16 * Lanes / hdmifrlinter.f_pixel_clock_max * HTotal - 16 * hdmifrlinter.blank_audio_min) / HActive); + } else if (DSCEnable && Output == dm_dp) { MaxLinkBPP = LinkBitRate / 10 * 8 * Lanes / PixelClock * (1 - 2.4 / 100); } else { @@ -3784,6 +3854,8 @@ static double TruncToValidBPP( if (!((DSCEnable == false && (DesiredBPP == NonDSCBPP2 || DesiredBPP == NonDSCBPP1 || DesiredBPP <= NonDSCBPP0)) || (DSCEnable && DesiredBPP >= MinDSCBPP && DesiredBPP <= MaxDSCBPP))) { return BPP_INVALID; + } else if ((Output == dm_hdmifrl && hdmifrlresult != FRL_CAP_CHK_OK) || (Output != dm_hdmifrl && MaxLinkBPP < DesiredBPP)) { + return BPP_INVALID; } else { return DesiredBPP; } @@ -4194,6 +4266,7 @@ void dml314_ModeSupportAndSystemConfigurationFull(struct display_mode_lib *mode_ if (v->ODMCombinePolicy == dm_odm_combine_policy_none || !(v->Output[k] == dm_dp || + v->Output[k] == dm_hdmifrl || v->Output[k] == dm_dp2p0 || v->Output[k] == dm_edp)) { v->ODMCombineEnablePerState[i][k] = dm_odm_combine_mode_disabled; @@ -4615,6 +4688,131 @@ void dml314_ModeSupportAndSystemConfigurationFull(struct display_mode_lib *mode_ // v->OutputTypeAndRatePerState[i][k] = v->Output[k] & " HBR3" } } + } else if (v->Output[k] == dm_hdmifrl) { + if (v->DSCEnable[k] == true) { + v->RequiresDSC[i][k] = true; + v->LinkDSCEnable = true; + v->RequiresFEC[i][k] = true; + } else { + v->RequiresDSC[i][k] = false; + v->LinkDSCEnable = false; + v->RequiresFEC[i][k] = false; + } + v->Outbpp = BPP_INVALID; + if (v->PHYCLKD18PerState[i] >= 3000.0 / 18) { + v->Outbpp = TruncToValidBPP( + 3000, + 3, + v->HTotal[k], + v->HActive[k], + v->PixelClockBackEnd[k], + v->ForcedOutputLinkBPP[k], + v->LinkDSCEnable, + v->Output[k], + v->OutputFormat[k], + v->DSCInputBitPerComponent[k], + v->NumberOfDSCSlices[k], + v->AudioSampleRate[k], + v->AudioSampleLayout[k], + v->ODMCombineEnablePerState[i][k]); + v->OutputBppPerState[i][k] = v->Outbpp; + //v->OutputTypeAndRatePerState[i][k] = v->Output[k] & "3x3"; + } + if (v->Outbpp == BPP_INVALID && v->PHYCLKD18PerState[i] >= 6000.0 / 18) { + v->Outbpp = TruncToValidBPP( + 6000, + 3, + v->HTotal[k], + v->HActive[k], + v->PixelClockBackEnd[k], + v->ForcedOutputLinkBPP[k], + v->LinkDSCEnable, + v->Output[k], + v->OutputFormat[k], + v->DSCInputBitPerComponent[k], + v->NumberOfDSCSlices[k], + v->AudioSampleRate[k], + v->AudioSampleLayout[k], + v->ODMCombineEnablePerState[i][k]); + v->OutputBppPerState[i][k] = v->Outbpp; + //v->OutputTypeAndRatePerState[i][k] = v->Output[k] & "6x3"; + } + if (v->Outbpp == BPP_INVALID && v->PHYCLKD18PerState[i] >= 6000.0 / 18) { + v->Outbpp = TruncToValidBPP( + 6000, + 4, + v->HTotal[k], + v->HActive[k], + v->PixelClockBackEnd[k], + v->ForcedOutputLinkBPP[k], + v->LinkDSCEnable, + v->Output[k], + v->OutputFormat[k], + v->DSCInputBitPerComponent[k], + v->NumberOfDSCSlices[k], + v->AudioSampleRate[k], + v->AudioSampleLayout[k], + v->ODMCombineEnablePerState[i][k]); + v->OutputBppPerState[i][k] = v->Outbpp; + //v->OutputTypeAndRatePerState[i][k] = v->Output[k] & "6x4"; + } + if (v->Outbpp == BPP_INVALID && v->PHYCLKD18PerState[i] >= 8000.0 / 18) { + v->Outbpp = TruncToValidBPP( + 8000, + 4, + v->HTotal[k], + v->HActive[k], + v->PixelClockBackEnd[k], + v->ForcedOutputLinkBPP[k], + v->LinkDSCEnable, + v->Output[k], + v->OutputFormat[k], + v->DSCInputBitPerComponent[k], + v->NumberOfDSCSlices[k], + v->AudioSampleRate[k], + v->AudioSampleLayout[k], + v->ODMCombineEnablePerState[i][k]); + v->OutputBppPerState[i][k] = v->Outbpp; + //v->OutputTypeAndRatePerState(i, k) = v->Output[k] & "8x4"; + } + if (v->Outbpp == BPP_INVALID && v->PHYCLKD18PerState[i] >= 10000.0 / 18) { + v->Outbpp = TruncToValidBPP( + 10000, + 4, + v->HTotal[k], + v->HActive[k], + v->PixelClockBackEnd[k], + v->ForcedOutputLinkBPP[k], + v->LinkDSCEnable, + v->Output[k], + v->OutputFormat[k], + v->DSCInputBitPerComponent[k], + v->NumberOfDSCSlices[k], + v->AudioSampleRate[k], + v->AudioSampleLayout[k], + v->ODMCombineEnablePerState[i][k]); + v->OutputBppPerState[i][k] = v->Outbpp; + //v->OutputTypeAndRatePerState[i][k] = v->Output[k] & "10x4"; + } + if (v->Outbpp == BPP_INVALID && v->PHYCLKD18PerState[i] >= 12000.0 / 18) { + v->Outbpp = TruncToValidBPP( + 12000, + 4, + v->HTotal[k], + v->HActive[k], + v->PixelClockBackEnd[k], + v->ForcedOutputLinkBPP[k], + v->LinkDSCEnable, + v->Output[k], + v->OutputFormat[k], + v->DSCInputBitPerComponent[k], + v->NumberOfDSCSlices[k], + v->AudioSampleRate[k], + v->AudioSampleLayout[k], + v->ODMCombineEnablePerState[i][k]); + v->OutputBppPerState[i][k] = v->Outbpp; + //v->OutputTypeAndRatePerState[i][k] = v->Output[k] & "12x4"; + } } } else { v->OutputBppPerState[i][k] = 0; @@ -4628,6 +4826,7 @@ void dml314_ModeSupportAndSystemConfigurationFull(struct display_mode_lib *mode_ if (v->BlendingAndTiming[k] == k && (v->Output[k] == dm_dp || v->Output[k] == dm_edp || + v->Output[k] == dm_hdmifrl || v->Output[k] == dm_hdmi) && v->OutputBppPerState[i][k] == 0) { v->LinkCapacitySupport[i] = false; } @@ -4639,6 +4838,7 @@ void dml314_ModeSupportAndSystemConfigurationFull(struct display_mode_lib *mode_ if (v->BlendingAndTiming[k] == k && (v->Output[k] == dm_dp || v->Output[k] == dm_edp || + v->Output[k] == dm_hdmifrl || v->Output[k] == dm_hdmi)) { if (v->OutputFormat[k] == dm_420 && v->Interlace[k] == 1 && v->ProgressiveToInterlaceUnitInOPP == true) { P2IWith420 = true; @@ -4650,6 +4850,24 @@ void dml314_ModeSupportAndSystemConfigurationFull(struct display_mode_lib *mode_ } } + for (i = 0; i < v->soc.num_states; ++i) { + v->DTBCLKRequiredMoreThanSupported[i] = false; + for (k = 0; k < v->NumberOfActivePlanes; ++k) { + if (v->BlendingAndTiming[k] == k && v->Output[k] == dm_hdmifrl + && RequiredDTBCLK( + v->RequiresDSC[i][k], + v->PixelClockBackEnd[k], + v->OutputFormat[k], + v->OutputBppPerState[i][k], + v->NumberOfDSCSlices[k], + v->HTotal[k], + v->HActive[k], + v->AudioSampleRate[k], + v->AudioSampleLayout[k]) > v->DTBCLKPerState[i]) { + v->DTBCLKRequiredMoreThanSupported[i] = true; + } + } + } for (i = 0; i < v->soc.num_states; ++i) { v->ODMCombine4To1SupportCheckOK[i] = true; @@ -7133,6 +7351,29 @@ static double CalculateUrgentLatency( return ret; } +static double RequiredDTBCLK( + bool DSCEnable, + double PixelClock, + enum output_format_class OutputFormat, + double OutputBPP, + int DSCSlices, + int HTotal, + int HActive, + int AudioRate, + int AudioLayout) +{ + if (DSCEnable != true) { + return dml_max(PixelClock / 4.0 * OutputBPP / 24.0, 25.0); + } else { + double PixelWordRate = PixelClock / (OutputFormat == dm_444 ? 1 : 2); + double HCActive = dml_ceil(DSCSlices * dml_ceil(OutputBPP * dml_ceil(HActive / DSCSlices, 1) / 8.0, 1) / 3.0, 1); + double HCBlank = 64 + 32 * dml_ceil(AudioRate * (AudioLayout == 1 ? 1.0 : 0.25) * HTotal / (PixelClock * 1000), 1); + double AverageTribyteRate = PixelWordRate * (HCActive + HCBlank) / HTotal; + double HActiveTribyteRate = PixelWordRate * HCActive / HActive; + return dml_max4(PixelWordRate / 4.0, AverageTribyteRate / 4.0, HActiveTribyteRate / 4.0, 25.0) * 1.002; + } +} + static noinline_for_stack void UseMinimumDCFCLK( struct display_mode_lib *mode_lib, int MaxPrefetchMode, diff --git a/drivers/gpu/drm/amd/display/dc/dml/dcn32/dcn32_fpu.c b/drivers/gpu/drm/amd/display/dc/dml/dcn32/dcn32_fpu.c index eb199215d298e..65f46141ebfda 100644 --- a/drivers/gpu/drm/amd/display/dc/dml/dcn32/dcn32_fpu.c +++ b/drivers/gpu/drm/amd/display/dc/dml/dcn32/dcn32_fpu.c @@ -1604,6 +1604,8 @@ static bool is_dtbclk_required(struct dc *dc, struct dc_state *context) for (i = 0; i < dc->res_pool->pipe_count; i++) { if (!context->res_ctx.pipe_ctx[i].stream) continue; + if (dc_is_hdmi_frl_signal(context->res_ctx.pipe_ctx[i].stream->signal)) + return true; if (dc->link_srv->dp_is_128b_132b_signal(&context->res_ctx.pipe_ctx[i])) return true; } @@ -3576,3 +3578,11 @@ void dcn32_override_min_req_memclk(struct dc *dc, struct dc_state *context) } } } + +unsigned int dcn32_get_max_dispclk_mhz(struct dc *dc, struct dc_state *context) +{ + (void)dc; + int max_level = context->bw_ctx.dml.soc.num_states; + + return (unsigned int) context->bw_ctx.dml.soc.clock_limits[max_level - 1].dispclk_mhz; +} diff --git a/drivers/gpu/drm/amd/display/dc/dml/dcn32/dcn32_fpu.h b/drivers/gpu/drm/amd/display/dc/dml/dcn32/dcn32_fpu.h index 273d2bd79d85b..ff83fbc811d3c 100644 --- a/drivers/gpu/drm/amd/display/dc/dml/dcn32/dcn32_fpu.h +++ b/drivers/gpu/drm/amd/display/dc/dml/dcn32/dcn32_fpu.h @@ -77,4 +77,6 @@ void dcn32_override_min_req_memclk(struct dc *dc, struct dc_state *context); void dcn32_set_clock_limits(const struct _vcs_dpi_soc_bounding_box_st *soc_bb); +unsigned int dcn32_get_max_dispclk_mhz(struct dc *dc, struct dc_state *context); + #endif diff --git a/drivers/gpu/drm/amd/display/dc/dml/dcn32/display_mode_vba_32.c b/drivers/gpu/drm/amd/display/dc/dml/dcn32/display_mode_vba_32.c index 0782a34689a00..6387b7d722f84 100644 --- a/drivers/gpu/drm/amd/display/dc/dml/dcn32/display_mode_vba_32.c +++ b/drivers/gpu/drm/amd/display/dc/dml/dcn32/display_mode_vba_32.c @@ -29,6 +29,8 @@ #include "../dml_inline_defs.h" #include "display_mode_vba_util_32.h" +#include "../dml1_frl_cap_chk.h" + void dml32_recalculate(struct display_mode_lib *mode_lib); static void DISPCLKDPPCLKDCFCLKDeepSleepPrefetchParametersWatermarksAndPerformanceCalculation( struct display_mode_lib *mode_lib); @@ -342,6 +344,8 @@ static void DISPCLKDPPCLKDCFCLKDeepSleepPrefetchParametersWatermarksAndPerforman mode_lib->vba.DSCFormatFactor = 1; else if (mode_lib->vba.OutputFormat[k] == dm_n422) mode_lib->vba.DSCFormatFactor = 2; + else if (mode_lib->vba.Output[k] == dm_hdmifrl) + mode_lib->vba.DSCFormatFactor = 2; else mode_lib->vba.DSCFormatFactor = 1; if (mode_lib->vba.ODMCombineEnabled[k] == dm_odm_combine_mode_4to1) @@ -2281,6 +2285,8 @@ void dml32_ModeSupportAndSystemConfigurationFull(struct display_mode_lib *mode_l for (k = 0; k < mode_lib->vba.NumberOfActiveSurfaces; ++k) { if (mode_lib->vba.BlendingAndTiming[k] == k) { v->dummy_vars.dml32_ModeSupportAndSystemConfigurationFull.TotalNumberOfActiveOTG = v->dummy_vars.dml32_ModeSupportAndSystemConfigurationFull.TotalNumberOfActiveOTG + 1; + if (mode_lib->vba.Output[k] == dm_hdmifrl) + v->dummy_vars.dml32_ModeSupportAndSystemConfigurationFull.TotalNumberOfActiveDP2p0 = v->dummy_vars.dml32_ModeSupportAndSystemConfigurationFull.TotalNumberOfActiveDP2p0 + 1; if (mode_lib->vba.Output[k] == dm_dp2p0) { v->dummy_vars.dml32_ModeSupportAndSystemConfigurationFull.TotalNumberOfActiveDP2p0 = v->dummy_vars.dml32_ModeSupportAndSystemConfigurationFull.TotalNumberOfActiveDP2p0 + 1; if (mode_lib->vba.OutputMultistreamId[k] @@ -2327,6 +2333,7 @@ void dml32_ModeSupportAndSystemConfigurationFull(struct display_mode_lib *mode_l if (mode_lib->vba.BlendingAndTiming[k] == k && (mode_lib->vba.Output[k] == dm_dp || mode_lib->vba.Output[k] == dm_dp2p0 || mode_lib->vba.Output[k] == dm_edp + || mode_lib->vba.Output[k] == dm_hdmifrl || mode_lib->vba.Output[k] == dm_hdmi) && mode_lib->vba.OutputBppPerState[i][k] == 0 && (mode_lib->vba.UsesMALLForPStateChange[k] != dm_use_mall_pstate_change_phantom_pipe)) { @@ -2352,6 +2359,7 @@ void dml32_ModeSupportAndSystemConfigurationFull(struct display_mode_lib *mode_l if (mode_lib->vba.BlendingAndTiming[k] == k && (mode_lib->vba.Output[k] == dm_dp || mode_lib->vba.Output[k] == dm_dp2p0 || mode_lib->vba.Output[k] == dm_edp + || mode_lib->vba.Output[k] == dm_hdmifrl || mode_lib->vba.Output[k] == dm_hdmi)) { if (mode_lib->vba.OutputFormat[k] == dm_420 && mode_lib->vba.Interlace[k] == 1 && @@ -2387,7 +2395,9 @@ void dml32_ModeSupportAndSystemConfigurationFull(struct display_mode_lib *mode_l } } - if ((mode_lib->vba.Output[k] == dm_edp || mode_lib->vba.Output[k] == dm_hdmi)) { + if ((mode_lib->vba.Output[k] == dm_edp + || mode_lib->vba.Output[k] == dm_hdmifrl + || mode_lib->vba.Output[k] == dm_hdmi)) { if (mode_lib->vba.OutputMultistreamEn[k] == true && mode_lib->vba.OutputMultistreamId[k] == k) mode_lib->vba.MultistreamWithHDMIOreDP = true; for (j = 0; j < mode_lib->vba.NumberOfActiveSurfaces; ++j) { @@ -2414,6 +2424,7 @@ void dml32_ModeSupportAndSystemConfigurationFull(struct display_mode_lib *mode_l mode_lib->vba.DTBCLKRequiredMoreThanSupported[i] = false; for (k = 0; k < mode_lib->vba.NumberOfActiveSurfaces; ++k) { if (mode_lib->vba.BlendingAndTiming[k] == k + && mode_lib->vba.Output[k] == dm_hdmifrl && dml32_RequiredDTBCLK(mode_lib->vba.RequiresDSC[i][k], mode_lib->vba.PixelClockBackEnd[k], mode_lib->vba.OutputFormat[k], @@ -2450,6 +2461,7 @@ void dml32_ModeSupportAndSystemConfigurationFull(struct display_mode_lib *mode_l for (k = 0; k <= mode_lib->vba.NumberOfActiveSurfaces - 1; k++) { if (mode_lib->vba.BlendingAndTiming[k] == k) { if (mode_lib->vba.Output[k] == dm_dp || mode_lib->vba.Output[k] == dm_dp2p0 + || mode_lib->vba.Output[k] == dm_hdmifrl || mode_lib->vba.Output[k] == dm_edp) { if (mode_lib->vba.OutputFormat[k] == dm_420) { mode_lib->vba.DSCFormatFactor = 2; @@ -2457,6 +2469,8 @@ void dml32_ModeSupportAndSystemConfigurationFull(struct display_mode_lib *mode_l mode_lib->vba.DSCFormatFactor = 1; } else if (mode_lib->vba.OutputFormat[k] == dm_n422) { mode_lib->vba.DSCFormatFactor = 2; + } else if (mode_lib->vba.Output[k] == dm_hdmifrl) { + mode_lib->vba.DSCFormatFactor = 2; } else { mode_lib->vba.DSCFormatFactor = 1; } 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 5e72966a8daf8..2ddc30d08b738 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 @@ -24,6 +24,7 @@ */ #include "display_mode_vba_util_32.h" #include "../dml_inline_defs.h" +#include "../dml1_frl_cap_chk.h" #include "display_mode_vba_32.h" #include "../display_mode_lib.h" @@ -55,6 +56,8 @@ unsigned int dml32_dscceComputeDelay( if (pixelFormat == dm_420) pixelsPerClock = 2; + else if (Output == dm_hdmifrl) + pixelsPerClock = 2; else if (pixelFormat == dm_n422) pixelsPerClock = 2; // #all other modes operate at 1 pixel per clock @@ -78,6 +81,8 @@ unsigned int dml32_dscceComputeDelay( //422 mode has an additional cycle of delay if (pixelFormat == dm_420 || pixelFormat == dm_444 || pixelFormat == dm_n422) s = 0; + else if (Output == dm_hdmifrl) + s = 0; else s = 1; @@ -140,7 +145,7 @@ unsigned int dml32_dscComputeDelay(enum output_format_class pixelFormat, enum ou Delay = Delay + 1; // sft Delay = Delay + 1; - } else if (pixelFormat == dm_n422 || (pixelFormat != dm_444)) { + } else if (pixelFormat == dm_n422 || (Output == dm_hdmifrl && pixelFormat != dm_444)) { // sfr Delay = Delay + 2; // dsccif @@ -1546,6 +1551,91 @@ void dml32_CalculateOutputLink( *OutputRate = dm_output_rate_dp_rate_hbr3; } } + } else if (Output == dm_hdmifrl) { + if (DSCEnable == true) { + *RequiresDSC = true; + LinkDSCEnable = true; + *RequiresFEC = true; + } else { + *RequiresDSC = false; + LinkDSCEnable = false; + *RequiresFEC = false; + } + *OutBpp = 0; + if (PHYCLKD18PerState >= 3000 / 18) { + *OutBpp = dml32_TruncToValidBPP(3000, 3, HTotal, HActive, PixelClockBackEnd, + ForcedOutputLinkBPP, LinkDSCEnable, Output, OutputFormat, + DSCInputBitPerComponent, NumberOfDSCSlices, (unsigned int)AudioSampleRate, + AudioSampleLayout, ODMModeNoDSC, ODMModeDSC, &dummy); + //OutputTypeAndRate = Output & "3x3"; + *OutputType = dm_output_type_hdmifrl; + *OutputRate = dm_output_rate_hdmi_rate_3x3; + } + if (*OutBpp == 0 && PHYCLKD18PerState >= 6000 / 18) { + *OutBpp = dml32_TruncToValidBPP(6000, 3, HTotal, HActive, PixelClockBackEnd, + ForcedOutputLinkBPP, LinkDSCEnable, Output, OutputFormat, + DSCInputBitPerComponent, NumberOfDSCSlices, (unsigned int)AudioSampleRate, + AudioSampleLayout, ODMModeNoDSC, ODMModeDSC, &dummy); + //OutputTypeAndRate = Output & "6x3"; + *OutputType = dm_output_type_hdmifrl; + *OutputRate = dm_output_rate_hdmi_rate_6x3; + } + if (*OutBpp == 0 && PHYCLKD18PerState >= 6000 / 18) { + *OutBpp = dml32_TruncToValidBPP(6000, 4, HTotal, HActive, PixelClockBackEnd, + ForcedOutputLinkBPP, LinkDSCEnable, Output, OutputFormat, + DSCInputBitPerComponent, NumberOfDSCSlices, (unsigned int)AudioSampleRate, + AudioSampleLayout, ODMModeNoDSC, ODMModeDSC, &dummy); + //OutputTypeAndRate = Output & "6x4"; + *OutputType = dm_output_type_hdmifrl; + *OutputRate = dm_output_rate_hdmi_rate_6x4; + } + if (*OutBpp == 0 && PHYCLKD18PerState >= 8000 / 18) { + *OutBpp = dml32_TruncToValidBPP(8000, 4, HTotal, HActive, PixelClockBackEnd, + ForcedOutputLinkBPP, LinkDSCEnable, Output, OutputFormat, + DSCInputBitPerComponent, NumberOfDSCSlices, (unsigned int)AudioSampleRate, + AudioSampleLayout, ODMModeNoDSC, ODMModeDSC, &dummy); + //OutputTypeAndRate = Output & "8x4"; + *OutputType = dm_output_type_hdmifrl; + *OutputRate = dm_output_rate_hdmi_rate_8x4; + } + if (*OutBpp == 0 && PHYCLKD18PerState >= 10000 / 18) { + *OutBpp = dml32_TruncToValidBPP(10000, 4, HTotal, HActive, PixelClockBackEnd, + ForcedOutputLinkBPP, LinkDSCEnable, Output, OutputFormat, + DSCInputBitPerComponent, NumberOfDSCSlices, (unsigned int)AudioSampleRate, + AudioSampleLayout, ODMModeNoDSC, ODMModeDSC, &dummy); + if (*OutBpp == 0 && DSCEnable == true && ForcedOutputLinkBPP == 0 && + PHYCLKD18PerState < 12000 / 18) { + *RequiresDSC = true; + LinkDSCEnable = true; + *RequiresFEC = true; + *OutBpp = dml32_TruncToValidBPP(10000, 4, HTotal, HActive, PixelClockBackEnd, + ForcedOutputLinkBPP, LinkDSCEnable, Output, OutputFormat, + DSCInputBitPerComponent, NumberOfDSCSlices, (unsigned int)AudioSampleRate, + AudioSampleLayout, ODMModeNoDSC, ODMModeDSC, &dummy); + } + //OutputTypeAndRate = Output & "10x4"; + *OutputType = dm_output_type_hdmifrl; + *OutputRate = dm_output_rate_hdmi_rate_10x4; + } + + if (*OutBpp == 0 && PHYCLKD18PerState >= 12000 / 18) { + *OutBpp = dml32_TruncToValidBPP(12000, 4, HTotal, HActive, PixelClockBackEnd, + ForcedOutputLinkBPP, LinkDSCEnable, Output, OutputFormat, + DSCInputBitPerComponent, NumberOfDSCSlices, (unsigned int)AudioSampleRate, + AudioSampleLayout, ODMModeNoDSC, ODMModeDSC, &dummy); + if (*OutBpp == 0 && DSCEnable == true && ForcedOutputLinkBPP == 0) { + *RequiresDSC = true; + LinkDSCEnable = true; + *RequiresFEC = true; + *OutBpp = dml32_TruncToValidBPP(12000, 4, HTotal, HActive, PixelClockBackEnd, + ForcedOutputLinkBPP, LinkDSCEnable, Output, OutputFormat, + DSCInputBitPerComponent, NumberOfDSCSlices, (unsigned int)AudioSampleRate, + AudioSampleLayout, ODMModeNoDSC, ODMModeDSC, &dummy); + } + //OutputTypeAndRate = Output & "12x4"; + *OutputType = dm_output_type_hdmifrl; + *OutputRate = dm_output_rate_hdmi_rate_12x4; + } } } } @@ -1599,12 +1689,30 @@ double dml32_TruncToValidBPP( unsigned int NonDSCBPP2; unsigned int NonDSCBPP3 = BPP_INVALID; + enum frl_cap_chk_result hdmifrlresult = FRL_CAP_CHK_OK; + struct frl_cap_chk_params hdmifrlparams = { 0 }; + struct frl_cap_chk_intermediates hdmifrlinter = { 0 }; + + hdmifrlparams.lanes = Lanes; + hdmifrlparams.f_pixel_clock_nominal = PixelClock * 1000000; + hdmifrlparams.r_bit_nominal = LinkBitRate * 1000000; + hdmifrlparams.layout = AudioLayout; + hdmifrlparams.f_audio = AudioRate * 1000; + hdmifrlparams.h_active = HActive; + 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; NonDSCBPP1 = 15; NonDSCBPP2 = 18; MinDSCBPP = 6; MaxDSCBPP = 1.5 * DSCInputBitPerComponent - 1.0 / 16; + hdmifrlparams.pixel_encoding = HDMI_FRL_PIXEL_ENCODING_420; } else if (Format == dm_444) { NonDSCBPP3 = 18; NonDSCBPP0 = 24; @@ -1612,8 +1720,10 @@ double dml32_TruncToValidBPP( NonDSCBPP2 = 36; MinDSCBPP = 8; MaxDSCBPP = 3 * DSCInputBitPerComponent - 1.0 / 16; + hdmifrlparams.pixel_encoding = HDMI_FRL_PIXEL_ENCODING_444; } else { - if (Output == dm_hdmi) { + hdmifrlparams.pixel_encoding = HDMI_FRL_PIXEL_ENCODING_422; + if (Output == dm_hdmi || Output == dm_hdmifrl) { NonDSCBPP0 = 24; NonDSCBPP1 = 24; NonDSCBPP2 = 24; @@ -1622,7 +1732,7 @@ double dml32_TruncToValidBPP( NonDSCBPP1 = 20; NonDSCBPP2 = 24; } - if (Format == dm_n422) { + if (Format == dm_n422 || Output == dm_hdmifrl) { MinDSCBPP = 7; MaxDSCBPP = 2 * DSCInputBitPerComponent - 1.0 / 16.0; } else { @@ -1630,7 +1740,13 @@ double dml32_TruncToValidBPP( MaxDSCBPP = 3 * DSCInputBitPerComponent - 1.0 / 16.0; } } - if (Output == dm_dp2p0) { + if (Output == dm_hdmifrl) { + hdmifrlresult = dml1_frl_cap_chk_inter(&hdmifrlparams, &hdmifrlinter); + MaxLinkBPP = (1 - hdmifrlinter.overhead_max) * dml_min(hdmifrlinter.r_frl_char_min * 16 * + Lanes / hdmifrlinter.f_pixel_clock_max + 24 * TB_BORROWED_MAX / HActive, + (hdmifrlinter.r_frl_char_min * 16 * Lanes / hdmifrlinter.f_pixel_clock_max * + HTotal - 16 * hdmifrlinter.blank_audio_min) / HActive); + } else if (Output == dm_dp2p0) { MaxLinkBPP = LinkBitRate * Lanes / PixelClock * 128 / 132 * 383 / 384 * 65536 / 65540; } else if (DSCEnable && Output == dm_dp) { MaxLinkBPP = LinkBitRate / 10 * 8 * Lanes / PixelClock * (1 - 2.4 / 100); @@ -1681,6 +1797,9 @@ double dml32_TruncToValidBPP( DesiredBPP <= NonDSCBPP0)) || (DSCEnable && DesiredBPP >= MinDSCBPP && DesiredBPP <= MaxDSCBPP))) return BPP_INVALID; + else if ((Output == dm_hdmifrl && hdmifrlresult != FRL_CAP_CHK_OK) || + (Output != dm_hdmifrl && MaxLinkBPP < DesiredBPP)) + return BPP_INVALID; else return DesiredBPP; } diff --git a/drivers/gpu/drm/amd/display/dc/dml/display_mode_enums.h b/drivers/gpu/drm/amd/display/dc/dml/display_mode_enums.h index d5831a34f5a19..42013fa5ad015 100644 --- a/drivers/gpu/drm/amd/display/dc/dml/display_mode_enums.h +++ b/drivers/gpu/drm/amd/display/dc/dml/display_mode_enums.h @@ -30,6 +30,7 @@ enum output_encoder_class { dm_hdmi = 1, dm_wb = 2, dm_edp = 3, + dm_hdmifrl = 4, dm_dp2p0 = 5, }; enum output_format_class { 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 new file mode 100644 index 0000000000000..260f58a97fc7a --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dml/dml1_frl_cap_chk.c @@ -0,0 +1,790 @@ +// SPDX-License-Identifier: MIT +/* + * Copyright 2023 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include "dml_logger.h" +#include "dml1_frl_cap_chk.h" +#include "dml_inline_defs.h" + +static const double __maybe_unused EPSILON = 0.01; +static const double __maybe_unused DBL_EPSILON = 2.2204460492503131e-16; +static const double __maybe_unused OVERHEAD_M = 0.003; /* % */ +static const double __maybe_unused TOLERANCE_PIXEL_CLOCK = 0.005; /* % */ +static const double __maybe_unused DML_TOLERANCE_AUDIO_CLOCK = 1000; /* ppm */ + +#define frl_dump_var(fmt, var) {} +#define frl_print(fmt, ...) {} + +const struct frl_primary_format prim_format_444[] = { +/* VIC/Rate/Lanes/HCactive/HCBlank */ + {64, 3, 3, 960, 360}, /* 1920x1080 @ 100 */ + {77, 3, 3, 960, 360}, /* 1920x1080 @ 100 */ + {63, 3, 3, 960, 140}, /* 1920x1080 @ 120 */ + {78, 3, 3, 960, 140}, /* 1920x1080 @ 120 */ + {93, 3, 3, 1920, 828}, /* 3840x2160 @ 24 */ + {103, 3, 3, 1920, 828}, /* 3840x2160 @ 24 */ + {94, 3, 3, 1920, 720}, /* 3840x2160 @ 25 */ + {104, 3, 3, 1920, 720}, /* 3840x2160 @ 25 */ + {95, 3, 3, 1920, 280}, /* 3840x2160 @ 30 */ + {105, 3, 3, 1920, 280}, /* 3840x2160 @ 30 */ + {114, 3, 3, 1920, 828}, /* 3840x2160 @ 48 */ + {116, 3, 3, 1920, 828}, /* 3840x2160 @ 48 */ + {96, 3, 3, 1920, 720}, /* 3840x2160 @ 50 */ + {106, 3, 3, 1920, 720}, /* 3840x2160 @ 50 */ + {97, 3, 3, 1920, 280}, /* 3840x2160 @ 60 */ + {107, 3, 3, 1920, 280}, /* 3840x2160 @ 60 */ + {117, 6, 3, 1920, 720}, /* 3840x2160 @ 100 */ + {119, 6, 3, 1920, 720}, /* 3840x2160 @ 100 */ + {118, 6, 3, 1920, 280}, /* 3840x2160 @ 120 */ + {120, 6, 3, 1920, 280}, /* 3840x2160 @ 120 */ + {98, 3, 3, 2048, 700}, /* 4096x2160 @ 24 */ + {99, 3, 3, 2048, 592}, /* 4096x2160 @ 25 */ + {100, 3, 3, 2048, 152}, /* 4096x2160 @ 30 */ + {115, 3, 3, 2048, 700}, /* 4096x2160 @ 48 */ + {101, 3, 3, 2048, 592}, /* 4096x2160 @ 50 */ + {102, 3, 3, 2048, 152}, /* 4096x2160 @ 60 */ + {218, 6, 3, 2048, 592}, /* 4096x2160 @ 100 */ + {219, 6, 3, 2048, 152}, /* 4096x2160 @ 120 */ + {121, 3, 3, 2560, 1188}, /* 5120x2160 @ 24 */ + {122, 3, 3, 2560, 1040}, /* 5120x2160 @ 25 */ + {123, 3, 3, 2560, 440}, /* 5120x2160 @ 30 */ + {124, 3, 3, 2560, 256}, /* 5120x2160 @ 48 */ + {125, 3, 3, 2560, 484}, /* 5120x2160 @ 50 */ + {126, 3, 3, 2307, 144}, /* 5120x2160 @ 60 */ + {127, 6, 3, 2560, 484}, /* 5120x2160 @ 100 */ + {193, 6, 3, 2334, 104}, /* 5120x2160 @ 120 */ + {194, 6, 3, 3840, 1660}, /* 7680x2160 @ 24 */ + {202, 6, 3, 3840, 1660}, /* 7680x2160 @ 24 */ + {195, 6, 3, 3840, 1560}, /* 7680x2160 @ 25 */ + {203, 6, 3, 3840, 1560}, /* 7680x2160 @ 25 */ + {196, 6, 3, 3840, 660}, /* 7680x2160 @ 30 */ + {204, 6, 3, 3840, 660}, /* 7680x2160 @ 30 */ + {197, 6, 4, 3142, 1292}, /* 7680x2160 @ 48 */ + {205, 6, 4, 3142, 1292}, /* 7680x2160 @ 48 */ + {198, 6, 4, 3142, 1180}, /* 7680x2160 @ 50 */ + {206, 6, 4, 3142, 1180}, /* 7680x2160 @ 50 */ + {199, 6, 4, 3182, 140}, /* 7680x2160 @ 60 */ + {207, 6, 4, 3182, 140}, /* 7680x2160 @ 60 */ + {200, 10, 4, 2680, 784}, /* 7680x2160 @ 100 */ + {208, 10, 4, 2680, 784}, /* 7680x2160 @ 100 */ + {201, 10, 4, 2600, 100}, /* 7680x2160 @ 120 */ + {209, 10, 4, 2600, 100}, /* 7680x2160 @ 120 */ + {210, 6, 3, 4854, 912}, /* 10240x4320 @ 24 */ + {211, 6, 3, 4827, 1536}, /* 10240x4320 @ 25 */ + {212, 6, 3, 4720, 128}, /* 10240x4320 @ 30 */ + {213, 8, 4, 4347, 756}, /* 10240x4320 @ 48 */ + {214, 8, 4, 4320, 1376}, /* 10240x4320 @ 50 */ + {215, 8, 4, 4187, 124}, /* 10240x4320 @ 60 */ +}; + +const struct frl_primary_format prim_format_422[] = { +/* VIC/Rate/Lanes/HCactive/HCBlank */ + {64, 3, 3, 960, 360}, /* 1920x1080 @ 100 */ + {77, 3, 3, 960, 360}, /* 1920x1080 @ 100 */ + {63, 3, 3, 960, 140}, /* 1920x1080 @ 120 */ + {78, 3, 3, 960, 140}, /* 1920x1080 @ 120 */ + {93, 3, 3, 1920, 828}, /* 3840x2160 @ 24 */ + {103, 3, 3, 1920, 828}, /* 3840x2160 @ 24 */ + {94, 3, 3, 1920, 720}, /* 3840x2160 @ 25 */ + {104, 3, 3, 1920, 720}, /* 3840x2160 @ 25 */ + {95, 3, 3, 1920, 280}, /* 3840x2160 @ 30 */ + {105, 3, 3, 1920, 280}, /* 3840x2160 @ 30 */ + {114, 3, 3, 1920, 828}, /* 3840x2160 @ 48 */ + {116, 3, 3, 1920, 828}, /* 3840x2160 @ 48 */ + {96, 3, 3, 1920, 720}, /* 3840x2160 @ 50 */ + {106, 3, 3, 1920, 720}, /* 3840x2160 @ 50 */ + {97, 3, 3, 1920, 280}, /* 3840x2160 @ 60 */ + {107, 3, 3, 1920, 280}, /* 3840x2160 @ 60 */ + {117, 3, 3, 1370, 104}, /* 3840x2160 @ 100 */ + {119, 3, 3, 1370, 104}, /* 3840x2160 @ 100 */ + {118, 3, 3, 1130, 104}, /* 3840x2160 @ 120 */ + {120, 3, 3, 1130, 104}, /* 3840x2160 @ 120 */ + {98, 3, 3, 2048, 700}, /* 4096x2160 @ 24 */ + {99, 3, 3, 2048, 592}, /* 4096x2160 @ 25 */ + {100, 3, 3, 2048, 152}, /* 4096x2160 @ 30 */ + {115, 3, 3, 2048, 700}, /* 4096x2160 @ 48 */ + {101, 3, 3, 2048, 592}, /* 4096x2160 @ 50 */ + {102, 3, 3, 2048, 152}, /* 4096x2160 @ 60 */ + {218, 6, 3, 2048, 592}, /* 4096x2160 @ 100 */ + {219, 6, 3, 2048, 152}, /* 4096x2160 @ 120 */ + {121, 3, 3, 2560, 1188}, /* 5120x2160 @ 24 */ + {122, 3, 3, 2560, 1040}, /* 5120x2160 @ 25 */ + {123, 3, 3, 2560, 440}, /* 5120x2160 @ 30 */ + {124, 3, 3, 2560, 256}, /* 5120x2160 @ 48 */ + {125, 3, 3, 2560, 484}, /* 5120x2160 @ 50 */ + {126, 3, 3, 2307, 144}, /* 5120x2160 @ 60 */ + {127, 6, 3, 2560, 484}, /* 5120x2160 @ 100 */ + {193, 6, 3, 2334, 104}, /* 5120x2160 @ 120 */ + {194, 3, 3, 2460, 816}, /* 7680x2160 @ 24 */ + {202, 3, 3, 2460, 816}, /* 7680x2160 @ 24 */ + {195, 3, 3, 2460, 732}, /* 7680x2160 @ 25 */ + {203, 3, 3, 2460, 732}, /* 7680x2160 @ 25 */ + {196, 3, 3, 2360, 144}, /* 7680x2160 @ 30 */ + {204, 3, 3, 2360, 144}, /* 7680x2160 @ 30 */ + {197, 6, 3, 2460, 816}, /* 7680x2160 @ 48 */ + {205, 6, 3, 2460, 816}, /* 7680x2160 @ 48 */ + {198, 6, 3, 2460, 732}, /* 7680x2160 @ 50 */ + {206, 6, 3, 2460, 732}, /* 7680x2160 @ 50 */ + {199, 6, 3, 2380, 116}, /* 7680x2160 @ 60 */ + {207, 6, 3, 2380, 116}, /* 7680x2160 @ 60 */ + {200, 10, 4, 2680, 784}, /* 7680x2160 @ 100 */ + {208, 10, 4, 2680, 784}, /* 7680x2160 @ 100 */ + {201, 10, 4, 2600, 100}, /* 7680x2160 @ 120 */ + {209, 10, 4, 2600, 100}, /* 7680x2160 @ 120 */ + {210, 6, 3, 4854, 912}, /* 10240x4320 @ 24 */ + {211, 6, 3, 4827, 1536}, /* 10240x4320 @ 25 */ + {212, 6, 3, 4720, 128}, /* 10240x4320 @ 30 */ + {213, 6, 4, 3360, 420}, /* 10240x4320 @ 48 */ + {214, 6, 4, 3334, 892}, /* 10240x4320 @ 50 */ + {215, 6, 4, 3120, 124}, /* 10240x4320 @ 60 */ + {216, 12, 4, 3334, 764}, /* 10240x4320 @ 100 */ + {217, 12, 4, 3120, 124}, /* 10240x4320 @ 120 */ +}; + +const struct frl_primary_format prim_format_420[] = { +/* VIC/Rate/Lanes/HCactive/HCBlank */ + {114, 3, 3, 1920, 828}, /* 3840x2160 @ 48 */ + {116, 3, 3, 1920, 828}, /* 3840x2160 @ 48 */ + {96, 3, 3, 1920, 720}, /* 3840x2160 @ 50 */ + {106, 3, 3, 1920, 720}, /* 3840x2160 @ 50 */ + {97, 3, 3, 1920, 280}, /* 3840x2160 @ 60 */ + {107, 3, 3, 1920, 280}, /* 3840x2160 @ 60 */ + {117, 3, 3, 1370, 104}, /* 3840x2160 @ 100 */ + {119, 3, 3, 1370, 104}, /* 3840x2160 @ 100 */ + {118, 3, 3, 1130, 104}, /* 3840x2160 @ 120 */ + {120, 3, 3, 1130, 104}, /* 3840x2160 @ 120 */ + {115, 3, 3, 2048, 700}, /* 4096x2160 @ 48 */ + {101, 3, 3, 2048, 592}, /* 4096x2160 @ 50 */ + {102, 3, 3, 2048, 152}, /* 4096x2160 @ 60 */ + {218, 3, 3, 1376, 96}, /* 4096x2160 @ 100 */ + {219, 3, 3, 1131, 84}, /* 4096x2160 @ 120 */ + {124, 3, 3, 2560, 256}, /* 5120x2160 @ 48 */ + {125, 3, 3, 2560, 484}, /* 5120x2160 @ 50 */ + {126, 3, 3, 2307, 144}, /* 5120x2160 @ 60 */ + {127, 6, 3, 2560, 484}, /* 5120x2160 @ 100 */ + {193, 6, 3, 2334, 104}, /* 5120x2160 @ 120 */ + {194, 3, 3, 2460, 816}, /* 7680x2160 @ 24 */ + {202, 3, 3, 2460, 816}, /* 7680x2160 @ 24 */ + {195, 3, 3, 2460, 732}, /* 7680x2160 @ 25 */ + {203, 3, 3, 2460, 732}, /* 7680x2160 @ 25 */ + {196, 3, 3, 2360, 144}, /* 7680x2160 @ 30 */ + {204, 3, 3, 2360, 144}, /* 7680x2160 @ 30 */ + {197, 6, 3, 2460, 816}, /* 7680x2160 @ 48 */ + {205, 6, 3, 2460, 816}, /* 7680x2160 @ 48 */ + {198, 6, 3, 2460, 732}, /* 7680x2160 @ 50 */ + {206, 6, 3, 2460, 732}, /* 7680x2160 @ 50 */ + {199, 6, 3, 2380, 116}, /* 7680x2160 @ 60 */ + {207, 6, 3, 2380, 116}, /* 7680x2160 @ 60 */ + {200, 8, 4, 2240, 480}, /* 7680x2160 @ 100 */ + {208, 8, 4, 2240, 480}, /* 7680x2160 @ 100 */ + {201, 8, 4, 2062, 108}, /* 7680x2160 @ 120 */ + {209, 8, 4, 2062, 108}, /* 7680x2160 @ 120 */ + {210, 3, 3, 2614, 172}, /* 10240x4320 @ 24 */ + {211, 3, 3, 2614, 500}, /* 10240x4320 @ 25 */ + {212, 6, 3, 4720, 128}, /* 10240x4320 @ 30 */ + {213, 6, 3, 2614, 172}, /* 10240x4320 @ 48 */ + {214, 6, 4, 3334, 892}, /* 10240x4320 @ 50 */ + {215, 6, 4, 3120, 124}, /* 10240x4320 @ 60 */ + {216, 10, 4, 2854, 520}, /* 10240x4320 @ 100 */ + {217, 10, 4, 2587, 120}, /* 10240x4320 @ 120 */ +}; + +enum frl_cap_chk_result dml1_frl_cap_chk_common(struct frl_cap_chk_intermediates *inter, + struct frl_cap_chk_params *params) +{ + double audio_bw_reserve = (params->compressed ? 192000.0 : 0.0); + + dc_assert_fp_enabled(); + +#ifdef DEBUG_FRL_CAP_CHK + { + printf("frl_cap_chk inputs:\n"); + printf("-------------------\n"); + frl_dump_var("%i", params->lanes); + frl_dump_var("%le", params->f_pixel_clock_nominal); + frl_dump_var("%le", params->r_bit_nominal); + frl_dump_var("%i", params->audio_packet_type); + frl_dump_var("%le", params->f_audio); + frl_dump_var("%i", params->h_active); + frl_dump_var("%i", params->h_blank); + frl_dump_var("%i", params->bpc); + frl_dump_var("%i", params->pixel_encoding); + frl_dump_var("%i", params->compressed); + frl_dump_var("%i", params->slices); + frl_dump_var("%i", params->slice_width); + frl_dump_var("%le", params->bpp_target); + frl_dump_var("%i", params->layout); + frl_dump_var("%i", params->acat); + printf("frl_cap_chk outputs:\n"); + printf("---------------------\n"); + } +#endif + + inter->c_frl_sb = 4 * C_FRL_CB + params->lanes; + inter->overhead_sb = (double)params->lanes / inter->c_frl_sb; + inter->overhead_rs = 8.0 * 4.0 / inter->c_frl_sb; + inter->overhead_map = 2.5 / inter->c_frl_sb; + inter->overhead_min = inter->overhead_sb + inter->overhead_rs + inter->overhead_map; + inter->overhead_max = inter->overhead_min + OVERHEAD_M; + inter->f_pixel_clock_max = params->f_pixel_clock_nominal * (1.0 + TOLERANCE_PIXEL_CLOCK); + inter->t_line = (params->h_active + params->h_blank) / inter->f_pixel_clock_max; + inter->r_bit_min = params->r_bit_nominal * (1.0 - TOLERANCE_FRL_BIT / 1000000.0); + inter->r_frl_char_min = inter->r_bit_min / 18.0; + inter->c_frl_line = dml_floor(inter->t_line * inter->r_frl_char_min * params->lanes, 1); + +#ifdef DEBUG_FRL_CAP_CHK + { + frl_dump_var("%i", inter->c_frl_sb); + frl_dump_var("%le", inter->overhead_sb); + frl_dump_var("%le", inter->overhead_rs); + frl_dump_var("%le", inter->overhead_map); + frl_dump_var("%le", inter->overhead_min); + frl_dump_var("%le", inter->overhead_max); + frl_dump_var("%le", inter->f_pixel_clock_max); + frl_dump_var("%le", inter->t_line); + frl_dump_var("%le", inter->r_bit_min); + frl_dump_var("%le", inter->r_frl_char_min); + frl_dump_var("%le", inter->c_frl_line); + } +#endif + + switch (params->audio_packet_type) { + case 0x02: + if (params->layout == 0) + inter->ap = 0.25; + else if (params->layout == 1) + inter->ap = 1.0; + break; + case 0x08: + inter->ap = 0.25; + break; + case 0x09: + inter->ap = 1.0; + break; + case 0x07: + case 0x0e: + case 0x0f: + case 0x0b: + case 0x0c: + /* Unsupported audio format */ + return FRL_CAP_CHK_ERROR_UNSUPPORTED_AUDIO; + default: + inter->ap = 0.0; + } + + inter->r_ap = (dml_max(audio_bw_reserve, params->f_audio * inter->ap) + 2 * ACR_RATE_MAX) * (1 + DML_TOLERANCE_AUDIO_CLOCK / 1000000.0); + inter->avg_audio_packets_line = inter->r_ap * inter->t_line; + inter->audio_packets_line = (int)dml_ceil(inter->avg_audio_packets_line, 1); + inter->blank_audio_min = 32 + 32 * inter->audio_packets_line; // h_blank_audio_min or hc_blank_audio_min + + params->borrow_params.audio_packets_line = inter->audio_packets_line; + +#ifdef DEBUG_FRL_CAP_CHK + { + frl_dump_var("%le", inter->ap); + frl_dump_var("%le", inter->r_ap); + frl_dump_var("%le", inter->avg_audio_packets_line); + frl_dump_var("%i", inter->audio_packets_line); + frl_dump_var("%i", inter->blank_audio_min); + } +#endif + + return FRL_CAP_CHK_OK; +} + +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 res; + int k_420; + double k_cd; + int c_frl_free; + int c_frl_rc_margin; + int c_frl_rc_savings; + int bpp; + double bytes_line; + int tb_active; + int tb_blank; + double f_tb_average; + double t_active_ref; + double t_blank_ref; + double t_active_min; + double t_blank_min; + double t_borrowed; + double tb_borrowed; + int c_frl_actual_payload; + double utilization; + double margin; + + dc_assert_fp_enabled(); + + res = dml1_frl_cap_chk_common(inter, params); + if (res != FRL_CAP_CHK_OK) + return res; + + k_420 = params->pixel_encoding == HDMI_FRL_PIXEL_ENCODING_420 ? 2 : 1; + k_cd = params->pixel_encoding == HDMI_FRL_PIXEL_ENCODING_422 ? 1.0 : params->bpc / 8.0; + c_frl_free = (int)dml_max(params->h_blank * k_cd / k_420 - 32 * (1 + inter->audio_packets_line) - 7, 0); + c_frl_rc_margin = 4; + c_frl_rc_savings = (int)dml_floor(dml_max(((7.0 / 8.0) * c_frl_free) - c_frl_rc_margin, 0.0), 1); + bpp = (int)(24 * k_cd / k_420); + bytes_line = bpp * params->h_active / 8.0; + tb_active = (int)dml_ceil(bytes_line / 3, 1); + tb_blank = (int)dml_ceil(params->h_blank * k_cd / k_420, 1); + +#ifdef DEBUG_FRL_CAP_CHK + { + frl_dump_var("%i", k_420); + frl_dump_var("%le", k_cd); + frl_dump_var("%i", c_frl_free); + frl_dump_var("%i", c_frl_rc_margin); + frl_dump_var("%i", c_frl_rc_savings); + frl_dump_var("%i", bpp); + frl_dump_var("%le", bytes_line); + frl_dump_var("%i", tb_active); + frl_dump_var("%i", tb_blank); + } +#endif + + if (!(inter->blank_audio_min <= tb_blank)) { + frl_dump_var("%i", inter->blank_audio_min); + frl_dump_var("%i", tb_blank); + return FRL_CAP_CHK_ERROR_AUDIO_BW; + } + + f_tb_average = (inter->f_pixel_clock_max / (params->h_active + params->h_blank)) * (tb_active + tb_blank); + t_active_ref = inter->t_line * ((double)params->h_active / (params->h_active + params->h_blank)); + t_blank_ref = inter->t_line * ((double)params->h_blank / (params->h_active + params->h_blank)); + t_active_min = (3.0 / 2.0) * tb_active / (params->lanes * inter->r_frl_char_min * (1.0 - inter->overhead_max)); + t_blank_min = tb_blank / (params->lanes * inter->r_frl_char_min * (1.0 - inter->overhead_max)); + +#ifdef DEBUG_FRL_CAP_CHK + { + 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_min); + frl_dump_var("%le", t_blank_min); + } +#endif + + if (t_active_ref >= t_active_min && t_blank_ref >= t_blank_min) { + t_borrowed = 0; + params->borrow_params.borrow_mode = FRL_BORROW_MODE_NONE; + } else if ((t_active_ref < t_active_min) && (t_blank_ref >= t_blank_min)) { + t_borrowed = t_active_min - t_active_ref; + params->borrow_params.borrow_mode = FRL_BORROW_MODE_FROM_BLANK; + } else { + return FRL_CAP_CHK_ERROR_BORROW; + } + + tb_borrowed = dml_ceil(t_borrowed * f_tb_average, 1); + +#ifdef DEBUG_FRL_CAP_CHK + { + frl_dump_var("%le", tb_borrowed); + frl_dump_var("%i", params->borrow_params.borrow_mode); + } +#endif + + if (!(tb_borrowed <= TB_BORROWED_MAX)) + return FRL_CAP_CHK_ERROR_MAX_BORROW; + + c_frl_actual_payload = (int)(dml_ceil((3.0 / 2.0) * tb_active, 1) + tb_blank - c_frl_rc_savings); + utilization = c_frl_actual_payload / inter->c_frl_line; + margin = 1.0 - (utilization + inter->overhead_max); + +#ifdef DEBUG_FRL_CAP_CHK + { + frl_dump_var("%i", c_frl_actual_payload); + frl_dump_var("%le", utilization); + frl_dump_var("%le", margin); + } +#endif + + if (margin < 0 && dcn_bw_fabs(margin) > EPSILON) + return FRL_CAP_CHK_ERROR_MARGIN; + + return FRL_CAP_CHK_OK; +} + +#if defined (CONFIG_DRM_AMD_DC_FP) +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; +} +#endif + +enum frl_cap_chk_result dml1_frl_cap_chk(struct frl_cap_chk_params *params) +{ + struct frl_cap_chk_intermediates inter; + +#if defined (CONFIG_DRM_AMD_DC_FP) + if (params->compressed) + return dml1_frl_cap_chk_compressed(params, &inter); +#endif + + return dml1_frl_cap_chk_inter(params, &inter); +} + +enum frl_cap_chk_result dml1_frl_cap_chk_inter(struct frl_cap_chk_params *params, + struct frl_cap_chk_intermediates *inter) +{ + return dml1_frl_cap_chk_uncompressed(params, inter); +} + +static double calculate_compressed_active_time(uint32_t h_active, + const uint32_t h_blank, + const int hc_active, + const int hc_blank, + const uint32_t frl_num_lanes, + const double pix_clk, + const int frl_link_rate) +{ + double f_tb_average; + double r_bit_nominal; + double r_bit_min; + double r_frl_char_min; + double t_active_est_1; + double t_active_est_2; + double t_active_target; + int c_frl_sb = 510; + int frl_bit_tolerance = 300; + double overhead_m = 0.003; + double overhead_sb; + double overhead_rs; + double overhead_map; + double overhead_min; + double overhead_max; + + switch (frl_link_rate) { + case FRL_LINK_RATE_3GBPS: + r_bit_nominal = 3.0e9; + break; + case FRL_LINK_RATE_6GBPS: + case FRL_LINK_RATE_6GBPS_4LANE: + r_bit_nominal = 6.0e9; + break; + case FRL_LINK_RATE_8GBPS: + r_bit_nominal = 8.0e9; + break; + case FRL_LINK_RATE_10GBPS: + default: + r_bit_nominal = 10.0e9; + break; + case FRL_LINK_RATE_12GBPS: + r_bit_nominal = 12.0e9; + break; + } + + f_tb_average = pix_clk / (h_active + h_blank) + * (hc_active + hc_blank); + + c_frl_sb = 4 * c_frl_sb + frl_num_lanes; + overhead_sb = (double)frl_num_lanes / c_frl_sb; + overhead_rs = 8.0 * 4.0 / c_frl_sb; + overhead_map = 2.5 / c_frl_sb; + overhead_min = overhead_sb + overhead_rs + overhead_map; + overhead_max = overhead_min + overhead_m; + + r_bit_min = r_bit_nominal * (1.0 - frl_bit_tolerance / 1000000.0); + r_frl_char_min = r_bit_min / 18.0; + t_active_est_1 = hc_active / f_tb_average; + t_active_est_2 = (3.0 / 2.0 * hc_active) / + (frl_num_lanes * r_frl_char_min * (1.0 - overhead_max)); + + if (t_active_est_1 > t_active_est_2) { + t_active_target = t_active_est_1; + } else { + t_active_target = t_active_est_2; + } + + return t_active_target; +} + +void frl_modified_pix_clock_for_dsc_padding(const int hc_active_target, + const int hc_blank_target, + const uint8_t frl_num_lanes, + const uint32_t pix_clk_100hz, + const int frl_link_rate, + const uint32_t h_addressable, + const uint32_t h_border_left, + const uint32_t h_border_right, + const uint32_t h_total, + const uint32_t h_addressable_otg, + uint32_t *pix_clk_100hz_otg, + uint32_t *h_total_otg) +{ + double pix_clk; + int h_active; + int h_blank; + double t_active_target; + double hw_pix_clk; + double h_total_otg_temp; + + pix_clk = (double)pix_clk_100hz * 100; + + h_active = h_addressable + h_border_left + h_border_right; + h_blank = h_total - h_active; + + t_active_target = calculate_compressed_active_time(h_active, h_blank, hc_active_target, hc_blank_target, frl_num_lanes, pix_clk, frl_link_rate); + + h_total_otg_temp = ((double)h_addressable_otg * (double)h_total) / ((double)pix_clk_100hz * 100.0 * t_active_target); + /* Htotal must be a multiple of 4, also take the ceiling */ + *h_total_otg = (uint32_t)dml_ceil(h_total_otg_temp, 4.0); + + hw_pix_clk = (double)(pix_clk_100hz * 100.0 * (double)*h_total_otg) / (double)h_total; + *pix_clk_100hz_otg = (uint32_t)(hw_pix_clk / 100.0); +} + +int frl_modify_borrow_mode_for_dsc_padding(const uint32_t pix_clk_100hz, + const uint32_t h_active, + const uint32_t h_active_padded, + const uint32_t h_blank, + const uint32_t h_blank_padded, + const int hc_active, + const int hc_blank, + const uint8_t frl_num_lanes, + const int frl_link_rate) +{ + double f_pixel_clock_max; + double t_line; + double t_active; + double t_blank; + double t_active_target; + double t_blank_target; + double pix_clk_tolerance = 0.005; + + enum frl_borrow_mode borrow_mode; + + f_pixel_clock_max = (double)pix_clk_100hz * (1.0 + pix_clk_tolerance); + t_line = (double)(h_active + h_blank) / f_pixel_clock_max; + + t_active_target = calculate_compressed_active_time(h_active, h_blank, hc_active, hc_blank, frl_num_lanes, f_pixel_clock_max, frl_link_rate); + + t_active = t_line * ((double)h_active_padded / (h_active_padded + h_blank_padded)); + t_blank = t_line - t_active; + + t_blank_target = t_line - t_active_target; + + if (t_blank_target - t_blank > DBL_EPSILON) { + borrow_mode = FRL_BORROW_MODE_FROM_ACTIVE; + } else if (t_active_target - t_active > DBL_EPSILON) { + borrow_mode = FRL_BORROW_MODE_FROM_BLANK; + } else { + borrow_mode = FRL_BORROW_MODE_NONE; + } + + return borrow_mode; +} 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 new file mode 100644 index 0000000000000..debe4c1dc0f74 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dml/dml1_frl_cap_chk.h @@ -0,0 +1,173 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright 2022 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DML1_FRL_CAP_CHK_H__ +#define __DML1_FRL_CAP_CHK_H__ + +#include "os_types.h" + +#define TB_BORROWED_MAX 400 +#define C_FRL_CB 510 +#define TOLERANCE_FRL_BIT 300 /* ppm */ +#define ACR_RATE_MAX 1500 + +enum hdmi_frl_pixel_encoding { + HDMI_FRL_PIXEL_ENCODING_444, + HDMI_FRL_PIXEL_ENCODING_422, + HDMI_FRL_PIXEL_ENCODING_420 +}; + +enum frl_cap_chk_result { + FRL_CAP_CHK_OK = 0, + + FRL_CAP_CHK_ERROR_AUDIO_BW = -1, + FRL_CAP_CHK_ERROR_BORROW = -2, + FRL_CAP_CHK_ERROR_MAX_BORROW = -3, + FRL_CAP_CHK_ERROR_MARGIN = -4, + + FRL_CAP_CHK_ERROR_UNSUPPORTED_AUDIO = -1000 +}; + +enum frl_borrow_mode { + FRL_BORROW_MODE_NONE, + FRL_BORROW_MODE_FROM_ACTIVE, + FRL_BORROW_MODE_FROM_BLANK +}; + +enum frl_link_rate { + FRL_LINK_RATE_DISABLE = 0, + FRL_LINK_RATE_3GBPS, + FRL_LINK_RATE_6GBPS, + FRL_LINK_RATE_6GBPS_4LANE, + FRL_LINK_RATE_8GBPS, + FRL_LINK_RATE_10GBPS, + FRL_LINK_RATE_12GBPS, + FRL_LINK_RATE_16GBPS, + FRL_LINK_RATE_20GBPS, + FRL_LINK_RATE_24GBPS +}; + +struct frl_dml_borrow_params { + int audio_packets_line; + int hc_active_target; + int hc_blank_target; + enum frl_borrow_mode borrow_mode; +}; + +struct frl_primary_format { + uint32_t vic; + uint32_t frl_rate; + uint32_t frl_lanes; + uint32_t hc_active; + uint32_t hc_blank; +}; + +struct frl_cap_chk_intermediates { + int c_frl_sb; + double overhead_sb; + double overhead_rs; + double overhead_map; + double overhead_min; + double overhead_max; + double f_pixel_clock_max; + double t_line; + double r_bit_min; + double r_frl_char_min; + double c_frl_line; + double ap; + double r_ap; + double avg_audio_packets_line; + int audio_packets_line; + int blank_audio_min; +}; + +struct frl_cap_chk_params { + int lanes; + double f_pixel_clock_nominal; /* Pixel Clock rate (Hz) */ + double r_bit_nominal; /* FRL bitrate (bps) */ + int audio_packet_type; + double f_audio; /* Audio rate (Hz) */ + int h_active; /* Active pixels per line */ + int h_blank; /* Blanking pixels per line */ + int bpc; /* Bits per component */ + int vic; /* Video Identification Code */ + + enum hdmi_frl_pixel_encoding pixel_encoding; + + 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 */ + + /* outputs */ + struct frl_dml_borrow_params borrow_params; + int average_tribyte_rate; +}; + +enum frl_cap_chk_result dml1_frl_cap_chk(struct frl_cap_chk_params *params); + +enum frl_cap_chk_result dml1_frl_cap_chk_inter(struct frl_cap_chk_params *params, + struct frl_cap_chk_intermediates *inter); + +enum frl_cap_chk_result dml1_frl_cap_chk_common(struct frl_cap_chk_intermediates *inter, + struct frl_cap_chk_params *params); + +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, + const int hc_blank_target, + const uint8_t frl_num_lanes, + const uint32_t pix_clk_100hz, + const int frl_link_rate, + const uint32_t h_addressable, + const uint32_t h_border_left, + const uint32_t h_border_right, + const uint32_t h_total, + const uint32_t h_addressable_otg, + uint32_t *pix_clk_100hz_otg, + uint32_t *h_total_otg); + +int frl_modify_borrow_mode_for_dsc_padding(const uint32_t pix_clk_100hz, + const uint32_t h_active, + const uint32_t h_active_padded, + const uint32_t h_blank, + const uint32_t h_blank_padded, + const int hc_active, + const int hc_blank, + const uint8_t frl_num_lanes, + const int frl_link_rate); diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/Makefile b/drivers/gpu/drm/amd/display/dc/dml2_0/Makefile index 8a451c36fdb3a..44e00c2b7ac79 100644 --- a/drivers/gpu/drm/amd/display/dc/dml2_0/Makefile +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/Makefile @@ -80,6 +80,7 @@ CFLAGS_REMOVE_$(AMDDALPATH)/dc/dml2_0/dml21/dml21_wrapper.o := $(dml2_ccflags) DML2 = display_mode_core.o display_mode_util.o dml2_wrapper_fpu.o dml2_wrapper.o \ dml2_utils.o dml2_policy.o dml2_translation_helper.o dml2_dc_resource_mgmt.o dml2_mall_phantom.o \ dml_display_rq_dlg_calc.o +DML2 += dml_frl_cap_chk.o AMD_DAL_DML2 = $(addprefix $(AMDDALPATH)/dc/dml2_0/,$(DML2)) @@ -102,6 +103,7 @@ DML21 += src/dml2_pmo/dml2_pmo_factory.o DML21 += src/dml2_pmo/dml2_pmo_dcn4_fams2.o DML21 += src/dml2_pmo/dml2_pmo_dcn42.o DML21 += src/dml2_standalone_libraries/lib_float_math.o +DML21 += src/dml2_standalone_libraries/lib_frl_cap_check.o DML21 += dml21_translation_helper.o DML21 += dml21_wrapper.o DML21 += dml21_wrapper_fpu.o diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/display_mode_core.c b/drivers/gpu/drm/amd/display/dc/dml2_0/display_mode_core.c index 698d62fb9cf72..72efb1e822ff4 100644 --- a/drivers/gpu/drm/amd/display/dc/dml2_0/display_mode_core.c +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/display_mode_core.c @@ -27,6 +27,8 @@ #include "display_mode_core.h" #include "display_mode_util.h" #include "display_mode_lib_defines.h" +#include "dml_frl_cap_chk.h" +#include "lib_frl_cap_check.h" #include "dml_assert.h" @@ -864,7 +866,7 @@ static dml_uint_t dscceComputeDelay( // #all other modes operate at 1 pixel per clock else if (pixelFormat == dml_444) pixelsPerClock = 1; - else if (pixelFormat == dml_n422) + else if (pixelFormat == dml_n422 || Output == dml_hdmifrl) pixelsPerClock = 2; else pixelsPerClock = 1; @@ -884,7 +886,7 @@ static dml_uint_t dscceComputeDelay( w = sliceWidth / pixelsPerClock; //422 mode has an additional cycle of delay - if (pixelFormat == dml_420 || pixelFormat == dml_444 || pixelFormat == dml_n422) + if (pixelFormat == dml_420 || pixelFormat == dml_444 || pixelFormat == dml_n422 || Output == dml_hdmifrl) s = 0; else s = 1; @@ -947,7 +949,7 @@ static dml_uint_t dscComputeDelay(enum dml_output_format_class pixelFormat, enum Delay = Delay + 1; // sft Delay = Delay + 1; - } else if (pixelFormat == dml_n422) { + } else if (pixelFormat == dml_n422 || (Output == dml_hdmifrl && pixelFormat != dml_444)) { // sfr Delay = Delay + 2; // dsccif @@ -2741,20 +2743,44 @@ static dml_float_t TruncToValidBPP( dml_uint_t NonDSCBPP1; dml_uint_t NonDSCBPP2; + frl_cap_chk_result hdmifrlresult = FRL_CAP_CHK_OK; + frl_cap_chk_params hdmifrlparams = { 0 }; + frl_cap_chk_intermediates hdmifrlinter = { 0 }; + + hdmifrlparams.lanes = Lanes; + hdmifrlparams.f_pixel_clock_nominal = PixelClock * 1000000; + hdmifrlparams.r_bit_nominal = LinkBitRate * 1000000; + hdmifrlparams.layout = AudioLayout; + hdmifrlparams.f_audio = AudioRate * 1000; + hdmifrlparams.h_active = HActive; + hdmifrlparams.h_blank = HTotal - HActive; + hdmifrlparams.bpc = (dml_uint_t)(DesiredBPP / 3); + hdmifrlparams.compressed = DSCEnable; + hdmifrlparams.slices = DSCSlices; + hdmifrlparams.slice_width = (dml_uint_t)(dml_ceil((dml_float_t) HActive / DSCSlices, 1.0)); + hdmifrlparams.bpp_target = DesiredBPP; + if (Format == dml_420) { NonDSCBPP0 = 12; NonDSCBPP1 = 15; NonDSCBPP2 = 18; MinDSCBPP = 6; MaxDSCBPP = 1.5 * DSCInputBitPerComponent - 1.0 / 16; + hdmifrlparams.pixel_encoding = PIXEL_ENCODING_420; + hdmifrlparams.bpc = (dml_uint_t) (DesiredBPP / 1.5); } else if (Format == dml_444) { NonDSCBPP0 = 24; NonDSCBPP1 = 30; NonDSCBPP2 = 36; MinDSCBPP = 8; MaxDSCBPP = 3 * DSCInputBitPerComponent - 1.0 / 16; + hdmifrlparams.pixel_encoding = PIXEL_ENCODING_444; + hdmifrlparams.bpc = (dml_uint_t) (DesiredBPP / 3.0); } else { - if (Output == dml_hdmi) { + hdmifrlparams.pixel_encoding = PIXEL_ENCODING_422; + hdmifrlparams.bpc = (dml_uint_t) (DesiredBPP / 2.0); + + if (Output == dml_hdmi || Output == dml_hdmifrl) { NonDSCBPP0 = 24; NonDSCBPP1 = 24; NonDSCBPP2 = 24; @@ -2763,7 +2789,7 @@ static dml_float_t TruncToValidBPP( NonDSCBPP1 = 20; NonDSCBPP2 = 24; } - if (Format == dml_n422) { + if (Format == dml_n422 || Output == dml_hdmifrl) { MinDSCBPP = 7; MaxDSCBPP = 2 * DSCInputBitPerComponent - 1.0 / 16.0; } else { @@ -2772,7 +2798,11 @@ static dml_float_t TruncToValidBPP( } } - if (Output == dml_dp2p0) { + if (Output == dml_hdmifrl) { + hdmifrlresult = frl_cap_chk_inter(&hdmifrlparams, &hdmifrlinter); + MaxLinkBPP = (1 - hdmifrlinter.overhead_max) * dml_min(hdmifrlinter.r_frl_char_min * 16.0 * (dml_float_t) Lanes / hdmifrlinter.f_pixel_clock_max + 24.0 * (dml_float_t) TB_BORROWED_MAX / (dml_float_t) HActive, + (hdmifrlinter.r_frl_char_min * 16.0 * (dml_float_t)Lanes / hdmifrlinter.f_pixel_clock_max * (dml_float_t) HTotal - 16.0 * (dml_float_t) hdmifrlinter.blank_audio_min) / (dml_float_t) HActive); + } else if (Output == dml_dp2p0) { MaxLinkBPP = LinkBitRate * Lanes / PixelClock * 128.0 / 132.0 * 383.0 / 384.0 * 65536.0 / 65540.0; } else if (DSCEnable && Output == dml_dp) { MaxLinkBPP = LinkBitRate / 10.0 * 8.0 * Lanes / PixelClock * (1 - 2.4 / 100); @@ -2824,6 +2854,8 @@ static dml_float_t TruncToValidBPP( if (!((DSCEnable == false && (DesiredBPP == NonDSCBPP2 || DesiredBPP == NonDSCBPP1 || DesiredBPP == NonDSCBPP0)) || (DSCEnable && DesiredBPP >= MinDSCBPP && DesiredBPP <= MaxDSCBPP))) { return __DML_DPP_INVALID__; + } else if ((Output == dml_hdmifrl && hdmifrlresult != FRL_CAP_CHK_OK) || (Output != dml_hdmifrl && MaxLinkBPP < DesiredBPP)) { + return __DML_DPP_INVALID__; } else { return DesiredBPP; } @@ -5516,6 +5548,66 @@ static void CalculateOutputLink( *OutputRate = dml_output_rate_dp_rate_hbr3; } } + } else if (Output == dml_hdmifrl) { + if (DSCEnable == dml_dsc_enable) { + *RequiresDSC = true; + LinkDSCEnable = true; + *RequiresFEC = true; + } else { + *RequiresDSC = false; + LinkDSCEnable = false; + *RequiresFEC = false; + } + *OutBpp = 0; + if (PHYCLKD18PerState >= 3000 / 18) { + *OutBpp = TruncToValidBPP(3000, 3, HTotal, HActive, PixelClockBackEnd, ForcedOutputLinkBPP, LinkDSCEnable, Output, OutputFormat, DSCInputBitPerComponent, NumberOfDSCSlices, (dml_uint_t)AudioSampleRate, AudioSampleLayout, ODMModeNoDSC, ODMModeDSC, &dummy); + //OutputTypeAndRate = Output & "3x3"; + *OutputType = dml_output_type_hdmifrl; + *OutputRate = dml_output_rate_hdmi_rate_3x3; + } + if (*OutBpp == 0 && PHYCLKD18PerState >= 6000 / 18) { + *OutBpp = TruncToValidBPP(6000, 3, HTotal, HActive, PixelClockBackEnd, ForcedOutputLinkBPP, LinkDSCEnable, Output, OutputFormat, DSCInputBitPerComponent, NumberOfDSCSlices, (dml_uint_t)AudioSampleRate, AudioSampleLayout, ODMModeNoDSC, ODMModeDSC, &dummy); + //OutputTypeAndRate = Output & "6x3"; + *OutputType = dml_output_type_hdmifrl; + *OutputRate = dml_output_rate_hdmi_rate_6x3; + } + if (*OutBpp == 0 && PHYCLKD18PerState >= 6000 / 18) { + *OutBpp = TruncToValidBPP(6000, 4, HTotal, HActive, PixelClockBackEnd, ForcedOutputLinkBPP, LinkDSCEnable, Output, OutputFormat, DSCInputBitPerComponent, NumberOfDSCSlices, (dml_uint_t)AudioSampleRate, AudioSampleLayout, ODMModeNoDSC, ODMModeDSC, &dummy); + //OutputTypeAndRate = Output & "6x4"; + *OutputType = dml_output_type_hdmifrl; + *OutputRate = dml_output_rate_hdmi_rate_6x4; + } + if (*OutBpp == 0 && PHYCLKD18PerState >= 8000 / 18) { + *OutBpp = TruncToValidBPP(8000, 4, HTotal, HActive, PixelClockBackEnd, ForcedOutputLinkBPP, LinkDSCEnable, Output, OutputFormat, DSCInputBitPerComponent, NumberOfDSCSlices, (dml_uint_t)AudioSampleRate, AudioSampleLayout, ODMModeNoDSC, ODMModeDSC, &dummy); + //OutputTypeAndRate = Output & "8x4"; + *OutputType = dml_output_type_hdmifrl; + *OutputRate = dml_output_rate_hdmi_rate_8x4; + } + if (*OutBpp == 0 && PHYCLKD18PerState >= 10000 / 18) { + *OutBpp = TruncToValidBPP(10000, 4, HTotal, HActive, PixelClockBackEnd, ForcedOutputLinkBPP, LinkDSCEnable, Output, OutputFormat, DSCInputBitPerComponent, NumberOfDSCSlices, (dml_uint_t)AudioSampleRate, AudioSampleLayout, ODMModeNoDSC, ODMModeDSC, &dummy); + if (*OutBpp == 0 && DSCEnable == dml_dsc_enable_if_necessary && ForcedOutputLinkBPP == 0 && PHYCLKD18PerState < 12000 / 18) { + *RequiresDSC = true; + LinkDSCEnable = true; + *RequiresFEC = true; + *OutBpp = TruncToValidBPP(10000, 4, HTotal, HActive, PixelClockBackEnd, ForcedOutputLinkBPP, LinkDSCEnable, Output, OutputFormat, DSCInputBitPerComponent, NumberOfDSCSlices, (dml_uint_t)AudioSampleRate, AudioSampleLayout, ODMModeNoDSC, ODMModeDSC, &dummy); + } + //OutputTypeAndRate = Output & "10x4"; + *OutputType = dml_output_type_hdmifrl; + *OutputRate = dml_output_rate_hdmi_rate_10x4; + } + + if (*OutBpp == 0 && PHYCLKD18PerState >= 12000 / 18) { + *OutBpp = TruncToValidBPP(12000, 4, HTotal, HActive, PixelClockBackEnd, ForcedOutputLinkBPP, LinkDSCEnable, Output, OutputFormat, DSCInputBitPerComponent, NumberOfDSCSlices, (dml_uint_t)AudioSampleRate, AudioSampleLayout, ODMModeNoDSC, ODMModeDSC, &dummy); + if (*OutBpp == 0 && DSCEnable == dml_dsc_enable_if_necessary && ForcedOutputLinkBPP == 0) { + *RequiresDSC = true; + LinkDSCEnable = true; + *RequiresFEC = true; + *OutBpp = TruncToValidBPP(12000, 4, HTotal, HActive, PixelClockBackEnd, ForcedOutputLinkBPP, LinkDSCEnable, Output, OutputFormat, DSCInputBitPerComponent, NumberOfDSCSlices, (dml_uint_t)AudioSampleRate, AudioSampleLayout, ODMModeNoDSC, ODMModeDSC, &dummy); + } + //OutputTypeAndRate = Output & "12x4"; + *OutputType = dml_output_type_hdmifrl; + *OutputRate = dml_output_rate_hdmi_rate_12x4; + } } } } diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/display_mode_util.c b/drivers/gpu/drm/amd/display/dc/dml2_0/display_mode_util.c index b2fada6c44c36..26adcbb6d4567 100644 --- a/drivers/gpu/drm/amd/display/dc/dml2_0/display_mode_util.c +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/display_mode_util.c @@ -393,6 +393,7 @@ void dml_print_mode_support(struct display_mode_lib_st *mode_lib, dml_uint_t j) dml_print("DML: MODE SUPPORT: DISPCLK DPPCLK Support : %s\n", mode_lib->ms.support.DISPCLK_DPPCLK_Support[j] == true ? "Supported" : "NOT Supported"); dml_print("DML: MODE SUPPORT: Total Available Pipes Support : %s\n", mode_lib->ms.support.TotalAvailablePipesSupport[j] == true ? "Supported" : "NOT Supported"); dml_print("DML: MODE SUPPORT: Number Of OTG Support : %s\n", mode_lib->ms.support.NumberOfOTGSupport == true ? "Supported" : "NOT Supported"); + dml_print("DML: MODE SUPPORT: Number Of HDMI FRL Support : %s\n", mode_lib->ms.support.NumberOfHDMIFRLSupport == true ? "Supported" : "NOT Supported"); dml_print("DML: MODE SUPPORT: Number Of DP2p0 Support : %s\n", mode_lib->ms.support.NumberOfDP2p0Support == true ? "Supported" : "NOT Supported"); dml_print("DML: MODE SUPPORT: Writeback Latency Support : %s\n", mode_lib->ms.support.WritebackLatencySupport == true ? "Supported" : "NOT Supported"); dml_print("DML: MODE SUPPORT: Writeback Scale Ratio And Taps Support : %s\n", mode_lib->ms.support.WritebackScaleRatioAndTapsSupport == true ? "Supported" : "NOT Supported"); @@ -451,6 +452,8 @@ void dml_print_dml_mode_support_info(const struct dml_mode_support_info_st *supp dml_print("DML: support: NotEnoughLanesForMSO = 0x%x\n", support->NotEnoughLanesForMSO); if (!fail_only || support->NumberOfOTGSupport == 0) dml_print("DML: support: NumberOfOTGSupport = 0x%x\n", support->NumberOfOTGSupport); + if (!fail_only || support->NumberOfHDMIFRLSupport == 0) + dml_print("DML: support: NumberOfHDMIFRLSupport = 0x%x\n", support->NumberOfHDMIFRLSupport); if (!fail_only || support->NumberOfDP2p0Support == 0) dml_print("DML: support: NumberOfDP2p0Support = 0x%x\n", support->NumberOfDP2p0Support); if (!fail_only || support->NonsupportedDSCInputBPC == 1) diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/dml21_translation_helper.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/dml21_translation_helper.c index 476030193f149..4f261814cef4c 100644 --- a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/dml21_translation_helper.c +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/dml21_translation_helper.c @@ -210,6 +210,9 @@ static void populate_dml21_output_config_from_stream_state(struct dml2_link_outp case SIGNAL_TYPE_DVI_DUAL_LINK: output->output_encoder = dml2_hdmi; break; + case SIGNAL_TYPE_HDMI_FRL: + output->output_encoder = dml2_hdmifrl; + break; default: output->output_encoder = dml2_dp; } @@ -244,6 +247,7 @@ static void populate_dml21_output_config_from_stream_state(struct dml2_link_outp case SIGNAL_TYPE_DISPLAY_PORT_MST: case SIGNAL_TYPE_EDP: case SIGNAL_TYPE_VIRTUAL: + case SIGNAL_TYPE_HDMI_FRL: default: output->output_dp_link_rate = dml2_dp_rate_na; break; diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn4.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn4.c index 858e7bbc511f5..c983869e0fa3a 100644 --- a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn4.c +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn4.c @@ -66,6 +66,7 @@ struct dml2_core_ip_params core_dcn4_ip_caps_base = { .cursor_64bpp_support = true, .dynamic_metadata_vm_enabled = false, + .max_num_hdmi_frl_outputs = 1, .max_num_dp2p0_outputs = 4, .max_num_dp2p0_streams = 4, .imall_supported = 1, diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn4_calcs.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn4_calcs.c index 827bd9143c879..f338e733318e1 100644 --- a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn4_calcs.c +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn4_calcs.c @@ -7,6 +7,7 @@ #include "dml2_core_dcn4_calcs.h" #include "dml2_debug.h" #include "lib_float_math.h" +#include "lib_frl_cap_check.h" #include "dml_top_types.h" #define DML2_MAX_FMT_420_BUFFER_WIDTH 4096 @@ -1294,19 +1295,39 @@ static double TruncToValidBPP( unsigned int NonDSCBPP2; enum dml2_odm_mode ODMMode; + enum lib_frl_cap_check_status hdmifrlresult = LIB_FRL_CAP_CHECK_OK; + + l->hdmifrlparams.lanes = (int)Lanes; + l->hdmifrlparams.f_pixel_clock_nominal = PixelClock * 1000000; + l->hdmifrlparams.r_bit_nominal = LinkBitRate * 1000000; + l->hdmifrlparams.layout = (int)AudioLayout; + l->hdmifrlparams.f_audio = AudioRate * 1000; + l->hdmifrlparams.h_active = (int)HActive; + l->hdmifrlparams.h_blank = (int)(HTotal - HActive); + l->hdmifrlparams.bpc = (int)(DesiredBPP / 3); + l->hdmifrlparams.compressed = DSCEnable; + l->hdmifrlparams.slices = (int)DSCSlices; + l->hdmifrlparams.slice_width = (int)(math_ceil2((double)HActive / DSCSlices, 1.0)); + l->hdmifrlparams.bpp_target = DesiredBPP; if (Format == dml2_420) { NonDSCBPP0 = 12; NonDSCBPP1 = 15; NonDSCBPP2 = 18; MinDSCBPP = 6; MaxDSCBPP = 16; + l->hdmifrlparams.pixel_encoding = LIB_FRL_CAP_CHECK_PIXEL_ENCODING_420; + l->hdmifrlparams.bpc = (int)(DesiredBPP / 1.5); } else if (Format == dml2_444) { NonDSCBPP0 = 24; NonDSCBPP1 = 30; NonDSCBPP2 = 36; MinDSCBPP = 8; MaxDSCBPP = 16; + l->hdmifrlparams.pixel_encoding = LIB_FRL_CAP_CHECK_PIXEL_ENCODING_444; + l->hdmifrlparams.bpc = (int)(DesiredBPP / 3.0); } else { + l->hdmifrlparams.pixel_encoding = LIB_FRL_CAP_CHECK_PIXEL_ENCODING_422; + l->hdmifrlparams.bpc = (int)(DesiredBPP / 2.0); if (Output == dml2_hdmi || Output == dml2_hdmifrl) { NonDSCBPP0 = 24; @@ -1326,7 +1347,11 @@ static double TruncToValidBPP( } } - if (Output == dml2_dp2p0) { + if (Output == dml2_hdmifrl) { + hdmifrlresult = frl_cap_check_intermediates(&l->hdmifrlparams, &l->hdmifrlinter); + MaxLinkBPP = (1 - l->hdmifrlinter.overhead_max) * math_min2(l->hdmifrlinter.r_frl_char_min * 16.0 * (double)Lanes / l->hdmifrlinter.f_pixel_clock_max + 24.0 * (double)DML2_FRL_CHK_TB_BORROWED_MAX / (double)HActive, + (l->hdmifrlinter.r_frl_char_min * 16.0 * (double)Lanes / l->hdmifrlinter.f_pixel_clock_max * (double)HTotal - 16.0 * (double)l->hdmifrlinter.blank_audio_min) / (double)HActive); + } else if (Output == dml2_dp2p0) { MaxLinkBPP = LinkBitRate * Lanes / PixelClock * 128.0 / 132.0 * 383.0 / 384.0 * 65536.0 / 65540.0; } else if (DSCEnable && Output == dml2_dp) { MaxLinkBPP = LinkBitRate / 10.0 * 8.0 * Lanes / PixelClock * (1 - 2.4 / 100); @@ -1364,6 +1389,8 @@ static double TruncToValidBPP( if (!((DSCEnable == false && (DesiredBPP == NonDSCBPP2 || DesiredBPP == NonDSCBPP1 || DesiredBPP == NonDSCBPP0)) || (DSCEnable && DesiredBPP >= MinDSCBPP && DesiredBPP <= MaxDSCBPP))) { return __DML2_CALCS_DPP_INVALID__; + } else if ((Output == dml2_hdmifrl && hdmifrlresult != LIB_FRL_CAP_CHECK_OK) || (Output != dml2_hdmifrl && MaxLinkBPP < DesiredBPP)) { + return __DML2_CALCS_DPP_INVALID__; } else { return DesiredBPP; } diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_shared_types.h b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_shared_types.h index 080bc3c3d2447..11e295253f72e 100644 --- a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_shared_types.h +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_shared_types.h @@ -8,6 +8,7 @@ #include "dml2_external_lib_deps.h" #include "dml_top_display_cfg_types.h" #include "dml_top_types.h" +#include "lib_frl_cap_check.h" #define __DML_VBA_DEBUG__ #define __DML2_CALCS_MAX_VRATIO_PRE_OTO__ 4.0 //compressed ? 192000.0 : 0.0); + /* + if (getenv("DEBUG_FRL_CAP_CHK")) + { + printf("frl_cap_chk inputs:\n"); + printf("-------------------\n"); + frl_dump_var("%i", params->lanes); + frl_dump_var("%le", params->f_pixel_clock_nominal); + frl_dump_var("%le", params->r_bit_nominal); + frl_dump_var("%i", params->audio_packet_type); + frl_dump_var("%le", params->f_audio); + frl_dump_var("%i", params->h_active); + frl_dump_var("%i", params->h_blank); + frl_dump_var("%i", params->bpc); + frl_dump_var("%i", params->pixel_encoding); + frl_dump_var("%i", params->compressed); + frl_dump_var("%i", params->slices); + frl_dump_var("%i", params->slice_width); + frl_dump_var("%le", params->bpp_target); + frl_dump_var("%i", params->layout); + frl_dump_var("%i", params->acat); + printf("frl_cap_chk outputs:\n"); + printf("---------------------\n"); + } + */ + inter->c_frl_sb = 4 * C_FRL_CB + params->lanes; + inter->overhead_sb = (double)params->lanes / inter->c_frl_sb; + inter->overhead_rs = 8.0 * 4.0 / inter->c_frl_sb; + inter->overhead_map = 2.5 / inter->c_frl_sb; + inter->overhead_min = inter->overhead_sb + inter->overhead_rs + inter->overhead_map; + inter->overhead_max = inter->overhead_min + OVERHEAD_M; + inter->f_pixel_clock_max = params->f_pixel_clock_nominal * (1.0 + TOLERANCE_PIXEL_CLOCK); + inter->t_line = (params->h_active + params->h_blank) / inter->f_pixel_clock_max; + inter->r_bit_min = params->r_bit_nominal * (1.0 - TOLERANCE_FRL_BIT / 1000000.0); + inter->r_frl_char_min = inter->r_bit_min / 18.0; + inter->c_frl_line = math_floor(inter->t_line * inter->r_frl_char_min * params->lanes); + /* + if (getenv("DEBUG_FRL_CAP_CHK")) + { + frl_dump_var("%i", inter->c_frl_sb); + frl_dump_var("%le", inter->overhead_sb); + frl_dump_var("%le", inter->overhead_rs); + frl_dump_var("%le", inter->overhead_map); + frl_dump_var("%le", inter->overhead_min); + frl_dump_var("%le", inter->overhead_max); + frl_dump_var("%le", inter->f_pixel_clock_max); + frl_dump_var("%le", inter->t_line); + frl_dump_var("%le", inter->r_bit_min); + frl_dump_var("%le", inter->r_frl_char_min); + frl_dump_var("%le", inter->c_frl_line); + } + */ + switch (params->audio_packet_type) { + case 0x02: + /* unsupported + case 0x07: + */ + if (params->layout == 0) + inter->ap = 0.25; + else if (params->layout == 1) + inter->ap = 1.0; + break; + case 0x08: + inter->ap = 0.25; + break; + case 0x09: + /* unsupported + case 0x0e: + case 0x0f: + */ + inter->ap = 1.0; + break; + /* unsupported + case 0x0b: + case 0x0c: + if (acat == 0x01) + ap = 2.0; + else if (acat == 0x02) + ap = 3.0; + else if (acat == 0x03) + ap = 4.0; + break; + */ + case 0x07: + case 0x0e: + case 0x0f: + case 0x0b: + case 0x0c: + // Unsupported audio format + return LIB_FRL_CAP_CHECK_ERROR_UNSUPPORTED_AUDIO; + default: + inter->ap = 0.0; + } + + inter->r_ap = (math_max2(audio_bw_reserve, params->f_audio * inter->ap) + 2 * ACR_RATE_MAX) * (1 + TOLERANCE_AUDIO_CLOCK / 1000000.0); + inter->avg_audio_packets_line = inter->r_ap * inter->t_line; + inter->audio_packets_line = (int)math_ceil(inter->avg_audio_packets_line); + inter->blank_audio_min = 32 + 32 * inter->audio_packets_line; // h_blank_audio_min or hc_blank_audio_min + + params->audio_packets_line = inter->audio_packets_line; + /* + if (getenv("DEBUG_FRL_CAP_CHK")) + { + frl_dump_var("%le", inter->ap); + frl_dump_var("%le", inter->r_ap); + frl_dump_var("%le", inter->avg_audio_packets_line); + frl_dump_var("%i", inter->audio_packets_line); + frl_dump_var("%i", inter->blank_audio_min); + } + */ + return LIB_FRL_CAP_CHECK_OK; +} + + +static enum lib_frl_cap_check_status frl_cap_check_uncompressed(struct lib_frl_cap_check_params *params, struct lib_frl_cap_check_intermediates *inter) +{ + enum lib_frl_cap_check_status res; + + int k_420; + double k_cd; + int c_frl_free; + int c_frl_rc_margin; + int c_frl_rc_savings; + int bpp; + double bytes_line; + int tb_active; + int tb_blank; + double f_tb_average; + double t_active_ref; + double t_blank_ref; + double t_active_min; + double t_blank_min; + double t_borrowed; + double tb_borrowed; + int c_frl_actual_payload; + double utilization; + double margin; + + res = frl_cap_check_common(inter, params); + if (res != LIB_FRL_CAP_CHECK_OK) { + return res; + } + + k_420 = params->pixel_encoding == LIB_FRL_CAP_CHECK_PIXEL_ENCODING_420 ? 2 : 1; + k_cd = params->pixel_encoding == LIB_FRL_CAP_CHECK_PIXEL_ENCODING_422 ? 1.0 : params->bpc / 8.0; + c_frl_free = (int)math_max2(params->h_blank * k_cd / k_420 - 32 * (1 + inter->audio_packets_line) - 7, 0); + c_frl_rc_margin = 4; + c_frl_rc_savings = (int)math_floor(math_max2(((7.0 / 8.0) * c_frl_free) - c_frl_rc_margin, 0.0)); + bpp = (int)(24 * k_cd / k_420); + bytes_line = bpp * params->h_active / 8.0; + tb_active = (int)math_ceil(bytes_line / 3); + tb_blank = (int)math_ceil(params->h_blank * k_cd / k_420); + /* + if (getenv("DEBUG_FRL_CAP_CHK")) + { + frl_dump_var("%i", k_420); + frl_dump_var("%le", k_cd); + frl_dump_var("%i", c_frl_free); + frl_dump_var("%i", c_frl_rc_margin); + frl_dump_var("%i", c_frl_rc_savings); + frl_dump_var("%i", bpp); + frl_dump_var("%le", bytes_line); + frl_dump_var("%i", tb_active); + frl_dump_var("%i", tb_blank); + } + */ + if (!(inter->blank_audio_min <= tb_blank)) { + frl_dump_var("%i", inter->blank_audio_min); + frl_dump_var("%i", tb_blank); + return LIB_FRL_CAP_CHECK_ERROR_AUDIO_BW; + } + + f_tb_average = (inter->f_pixel_clock_max / (params->h_active + params->h_blank)) * (tb_active + tb_blank); + t_active_ref = inter->t_line * ((double)params->h_active / (params->h_active + params->h_blank)); + t_blank_ref = inter->t_line * ((double)params->h_blank / (params->h_active + params->h_blank)); + t_active_min = (3.0 / 2.0) * tb_active / (params->lanes * inter->r_frl_char_min * (1.0 - inter->overhead_max)); + t_blank_min = tb_blank / (params->lanes * inter->r_frl_char_min * (1.0 - inter->overhead_max)); + /* + if (getenv("DEBUG_FRL_CAP_CHK")) + { + 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_min); + frl_dump_var("%le", t_blank_min); + } + */ + if ((t_active_ref >= t_active_min) && (t_blank_ref >= t_blank_min)) { + t_borrowed = 0; + params->borrow_mode = LIB_FRL_CAP_CHECK_BORROW_MODE_NONE; + } else if ((t_active_ref < t_active_min) && (t_blank_ref >= t_blank_min)) { + t_borrowed = t_active_min - t_active_ref; + params->borrow_mode = LIB_FRL_CAP_CHECK_BORROW_MODE_FROM_BLANK; + } else + return LIB_FRL_CAP_CHECK_ERROR_BORROW; + + tb_borrowed = math_ceil(t_borrowed * f_tb_average); + /* + if (getenv("DEBUG_FRL_CAP_CHK")) + { + frl_dump_var("%le", tb_borrowed); + frl_dump_var("%i", params->borrow_mode); + } + */ + if (!(tb_borrowed <= DML2_FRL_CHK_TB_BORROWED_MAX)) + return LIB_FRL_CAP_CHECK_ERROR_MAX_BORROW; + + c_frl_actual_payload = (int)math_ceil((3.0 / 2.0) * tb_active) + tb_blank - c_frl_rc_savings; + utilization = c_frl_actual_payload / inter->c_frl_line; + margin = 1.0 - (utilization + inter->overhead_max); + /* + if (getenv("DEBUG_FRL_CAP_CHK")) + { + frl_dump_var("%i", c_frl_actual_payload); + frl_dump_var("%le", utilization); + frl_dump_var("%le", margin); + } + */ + if (margin < 0 && math_fabs(margin) > EPSILON) + return LIB_FRL_CAP_CHECK_ERROR_MARGIN; + + return LIB_FRL_CAP_CHECK_OK; +} + +static enum lib_frl_cap_check_status frl_cap_check_compressed(struct lib_frl_cap_check_params *params, struct lib_frl_cap_check_intermediates *inter) +{ + enum lib_frl_cap_check_status res; + + int c_frl_available; + int c_frl_active_available; + int c_frl_blank_available; + int bytes_target; + int hc_active_target; + int hc_blank_target_est1; + int hc_blank_target_est2; + int hc_blank_target; + double f_tb_average; + double t_active_ref; + double t_blank_ref; + double t_active_target; + double t_blank_target; + double tb_borrowed; + int c_frl_actual_target_payload; + double utilization_targeted; + double margin_target; +#if defined(DEBUG_FRL_CAP_CHK) + double tb_delta; + double tb_delta_limit; + int tb_worst; +#endif + + res = frl_cap_check_common(inter, params); + if (res != LIB_FRL_CAP_CHECK_OK) + return res; + + c_frl_available = (int)math_floor((1 - inter->overhead_max) * inter->c_frl_line); + c_frl_active_available = (int)math_floor(c_frl_available * ((double)params->h_active / (params->h_active + params->h_blank))); + (void)c_frl_active_available; + c_frl_blank_available = (int)math_floor(c_frl_available * ((double)params->h_blank / (params->h_active + params->h_blank))); + (void)c_frl_blank_available; + bytes_target = params->slices * (int)math_ceil(params->bpp_target * params->slice_width / 8.0); + + if (!params->bypass_hc_target_calc) + hc_active_target = (int)math_ceil(bytes_target / 3.0); + else + hc_active_target = params->hc_active_target; + + hc_blank_target_est1 = (int)math_ceil(hc_active_target * ((double)params->h_blank / params->h_active)); + hc_blank_target_est2 = (int)math_max2(hc_blank_target_est1, inter->blank_audio_min); + + if (!params->bypass_hc_target_calc) { + hc_blank_target = 4 * (int)math_floor(math_min2(hc_blank_target_est2, c_frl_available - 3.0 / 2.0 * hc_active_target) / 4.0); + + params->hc_active_target = hc_active_target; + params->hc_blank_target = hc_blank_target; + } else { + hc_blank_target = params->hc_blank_target; + } + /* + if (getenv("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); + } + */ + if (!(inter->blank_audio_min <= hc_blank_target)) { + frl_dump_var("%i", inter->blank_audio_min); + frl_dump_var("%i", hc_blank_target); + return LIB_FRL_CAP_CHECK_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 = math_max2((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; +#if defined(DEBUG_FRL_CAP_CHK) + tb_delta = math_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) { +#if defined(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_mode = LIB_FRL_CAP_CHECK_BORROW_MODE_FROM_ACTIVE; + } else if (t_active_target - t_active_ref > DBL_EPSILON) { +#if defined(DEBUG_FRL_CAP_CHK) + tb_delta_limit = tb_delta; +#endif + params->borrow_mode = LIB_FRL_CAP_CHECK_BORROW_MODE_FROM_BLANK; + } else { +#if defined(DEBUG_FRL_CAP_CHK) + tb_delta_limit = 0; +#endif + params->borrow_mode = LIB_FRL_CAP_CHECK_BORROW_MODE_NONE; + } + +#if defined(DEBUG_FRL_CAP_CHK) + tb_worst = (int)math_ceil(math_max2(tb_borrowed, tb_delta_limit)); + + { + frl_dump_var("%le", tb_delta_limit); + frl_dump_var("%le", tb_borrowed); + frl_dump_var("%i", params->borrow_mode); + frl_dump_var("%i", tb_worst); + } +#endif + if (!(tb_borrowed <= DML2_FRL_CHK_TB_BORROWED_MAX)) + return LIB_FRL_CAP_CHECK_ERROR_MAX_BORROW; + + c_frl_actual_target_payload = (int)math_ceil(3.0 / 2.0 * hc_active_target) + hc_blank_target; + utilization_targeted = c_frl_actual_target_payload / inter->c_frl_line; + margin_target = 1.0 - (utilization_targeted + inter->overhead_max); +#if defined(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 && math_fabs(margin_target) > EPSILON) + return LIB_FRL_CAP_CHECK_ERROR_MARGIN; + + return LIB_FRL_CAP_CHECK_OK; +} + +enum lib_frl_cap_check_status frl_cap_check(struct lib_frl_cap_check_params *params) +{ + struct lib_frl_cap_check_intermediates inter; + return frl_cap_check_intermediates(params, &inter); +} + +enum lib_frl_cap_check_status frl_cap_check_intermediates(struct lib_frl_cap_check_params *params, struct lib_frl_cap_check_intermediates *inter) +{ + if (params->compressed) + return frl_cap_check_compressed(params, inter); + return frl_cap_check_uncompressed(params, inter); +} diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_standalone_libraries/lib_frl_cap_check.h b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_standalone_libraries/lib_frl_cap_check.h new file mode 100644 index 0000000000000..aa27648565460 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_standalone_libraries/lib_frl_cap_check.h @@ -0,0 +1,90 @@ +// SPDX-License-Identifier: MIT +// +// Copyright 2024 Advanced Micro Devices, Inc. + +#ifndef __LIB_FRL_CAP_CHECK_H__ +#define __LIB_FRL_CAP_CHECK_H__ + +#include "dml2_external_lib_deps.h" + +extern const int DML2_FRL_CHK_TB_BORROWED_MAX; + +enum lib_frl_cap_check_pixel_encoding { + LIB_FRL_CAP_CHECK_PIXEL_ENCODING_444, + LIB_FRL_CAP_CHECK_PIXEL_ENCODING_422, + LIB_FRL_CAP_CHECK_PIXEL_ENCODING_420 +}; + +enum lib_frl_cap_check_borrow_mode { + LIB_FRL_CAP_CHECK_BORROW_MODE_NONE, + LIB_FRL_CAP_CHECK_BORROW_MODE_FROM_ACTIVE, + LIB_FRL_CAP_CHECK_BORROW_MODE_FROM_BLANK +}; + +enum lib_frl_cap_check_status { + LIB_FRL_CAP_CHECK_OK = 0, + + LIB_FRL_CAP_CHECK_ERROR_AUDIO_BW = -1, + LIB_FRL_CAP_CHECK_ERROR_BORROW = -2, + LIB_FRL_CAP_CHECK_ERROR_MAX_BORROW = -3, + LIB_FRL_CAP_CHECK_ERROR_MARGIN = -4, + + LIB_FRL_CAP_CHECK_ERROR_UNSUPPORTED_AUDIO = -1000 +}; + +struct lib_frl_cap_check_intermediates { + int c_frl_sb; + double overhead_sb; + double overhead_rs; + double overhead_map; + double overhead_min; + double overhead_max; + double f_pixel_clock_max; + double t_line; + double r_bit_min; + double r_frl_char_min; + double c_frl_line; + double ap; + double r_ap; + double avg_audio_packets_line; + int audio_packets_line; + int blank_audio_min; +}; + +struct lib_frl_cap_check_params { + int lanes; + double f_pixel_clock_nominal; /* Pixel Clock rate (Hz) */ + double r_bit_nominal; /* FRL bitrate (bps) */ + int audio_packet_type; + double f_audio; /* Audio rate (Hz) */ + int h_active; /* Active pixels per line */ + int h_blank; /* Blanking pixels per line */ + int bpc; /* Bits per component */ + + enum lib_frl_cap_check_pixel_encoding pixel_encoding; + + bool compressed; + bool bypass_hc_target_calc; + + /* DSC parameters */ + int slices; + int slice_width; + double bpp_target; + + int layout; /* not supported */ + int acat; /* not supported */ + + /* outputs */ + int audio_packets_line; + + /* inputs or outputs */ + int hc_active_target; + int hc_blank_target; + + enum lib_frl_cap_check_borrow_mode borrow_mode; +}; + +enum lib_frl_cap_check_status frl_cap_check(struct lib_frl_cap_check_params *params); +enum lib_frl_cap_check_status frl_cap_check_intermediates(struct lib_frl_cap_check_params *params, struct lib_frl_cap_check_intermediates *inter); + +#endif diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml2_translation_helper.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml2_translation_helper.c index 8e0997441ee0e..54ff597413c55 100644 --- a/drivers/gpu/drm/amd/display/dc/dml2_0/dml2_translation_helper.c +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml2_translation_helper.c @@ -808,6 +808,9 @@ static void populate_dml_output_cfg_from_stream_state(struct dml_output_cfg_st * case SIGNAL_TYPE_DVI_DUAL_LINK: out->OutputEncoder[location] = dml_hdmi; break; + case SIGNAL_TYPE_HDMI_FRL: + out->OutputEncoder[location] = dml_hdmifrl; + break; default: out->OutputEncoder[location] = dml_dp; } @@ -883,6 +886,7 @@ static void populate_dml_output_cfg_from_stream_state(struct dml_output_cfg_st * case SIGNAL_TYPE_DISPLAY_PORT_MST: case SIGNAL_TYPE_EDP: case SIGNAL_TYPE_VIRTUAL: + case SIGNAL_TYPE_HDMI_FRL: default: out->OutputLinkDPRate[location] = dml_dp_rate_na; break; diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml2_utils.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml2_utils.c index 86567e2324150..4fa7298c6c7ba 100644 --- a/drivers/gpu/drm/amd/display/dc/dml2_0/dml2_utils.c +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml2_utils.c @@ -173,6 +173,8 @@ bool is_dtbclk_required(const struct dc *dc, struct dc_state *context) for (i = 0; i < dc->res_pool->pipe_count; i++) { if (!context->res_ctx.pipe_ctx[i].stream) continue; + if (dc_is_hdmi_frl_signal(context->res_ctx.pipe_ctx[i].stream->signal)) + return true; if (is_dp2p0_output_encoder(&context->res_ctx.pipe_ctx[i])) return true; } diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml_frl_cap_chk.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml_frl_cap_chk.c new file mode 100644 index 0000000000000..a638c0d6d7653 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml_frl_cap_chk.c @@ -0,0 +1,413 @@ +/* + * Copyright 2022 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include "dml_frl_cap_chk.h" +#include "display_mode_util.h" +#include "lib_frl_cap_check.h" + +#define frl_dump_var(fmt, var) {} +#define frl_print(fmt, ...) {} +#include "dcn_calc_math.h" +#define fabs(var) dcn_bw_fabs(var) +#define floor(var) dcn_bw_floor(var) +#define ceil(var) dcn_bw_ceil(var) + +#if !defined(TB_BORROWED_MAX) +#define TB_BORROWED_MAX 400 +#endif + +static const double EPSILON = 0.01; +static const double DBL_EPSILON = 2.2204460492503131e-16; +static const int C_FRL_CB = 510; +static const double OVERHEAD_M = 0.003; /* % */ +static const double TOLERANCE_PIXEL_CLOCK = 0.005; /* % */ +static const double TOLERANCE_AUDIO_CLOCK = 1000; /* ppm */ +static const int TOLERANCE_FRL_BIT = 300; /* ppm */ +static const int ACR_RATE_MAX = 1500; + +static frl_cap_chk_result frl_cap_chk_common(frl_cap_chk_intermediates *inter, frl_cap_chk_params *params) +{ + double audio_bw_reserve = (params->compressed ? 192000.0 : 0.0); +/* + if (getenv("DEBUG_FRL_CAP_CHK")) + { + printf("frl_cap_chk inputs:\n"); + printf("-------------------\n"); + frl_dump_var("%i", params->lanes); + frl_dump_var("%le", params->f_pixel_clock_nominal); + frl_dump_var("%le", params->r_bit_nominal); + frl_dump_var("%i", params->audio_packet_type); + frl_dump_var("%le", params->f_audio); + frl_dump_var("%i", params->h_active); + frl_dump_var("%i", params->h_blank); + frl_dump_var("%i", params->bpc); + frl_dump_var("%i", params->pixel_encoding); + frl_dump_var("%i", params->compressed); + frl_dump_var("%i", params->slices); + frl_dump_var("%i", params->slice_width); + frl_dump_var("%le", params->bpp_target); + frl_dump_var("%i", params->layout); + frl_dump_var("%i", params->acat); + printf("frl_cap_chk outputs:\n"); + printf("---------------------\n"); + } +*/ + inter->c_frl_sb = 4 * C_FRL_CB + params->lanes; + inter->overhead_sb = (double) params->lanes / inter->c_frl_sb; + inter->overhead_rs = 8.0 * 4.0 / inter->c_frl_sb; + inter->overhead_map = 2.5 / inter->c_frl_sb; + inter->overhead_min = inter->overhead_sb + inter->overhead_rs + inter->overhead_map; + inter->overhead_max = inter->overhead_min + OVERHEAD_M; + inter->f_pixel_clock_max = params->f_pixel_clock_nominal * (1.0 + TOLERANCE_PIXEL_CLOCK); + inter->t_line = (params->h_active + params->h_blank) / inter->f_pixel_clock_max; + inter->r_bit_min = params->r_bit_nominal * (1.0 - TOLERANCE_FRL_BIT / 1000000.0); + inter->r_frl_char_min = inter->r_bit_min / 18.0; + inter->c_frl_line = floor(inter->t_line * inter->r_frl_char_min * params->lanes); +/* + if (getenv("DEBUG_FRL_CAP_CHK")) + { + frl_dump_var("%i", inter->c_frl_sb); + frl_dump_var("%le", inter->overhead_sb); + frl_dump_var("%le", inter->overhead_rs); + frl_dump_var("%le", inter->overhead_map); + frl_dump_var("%le", inter->overhead_min); + frl_dump_var("%le", inter->overhead_max); + frl_dump_var("%le", inter->f_pixel_clock_max); + frl_dump_var("%le", inter->t_line); + frl_dump_var("%le", inter->r_bit_min); + frl_dump_var("%le", inter->r_frl_char_min); + frl_dump_var("%le", inter->c_frl_line); + } +*/ + switch (params->audio_packet_type) { + case 0x02: + /* unsupported + case 0x07: + */ + if (params->layout == 0) + inter->ap = 0.25; + else if (params->layout == 1) + inter->ap = 1.0; + break; + case 0x08: + inter->ap = 0.25; + break; + case 0x09: + /* unsupported + case 0x0e: + case 0x0f: + */ + inter->ap = 1.0; + break; + /* unsupported + case 0x0b: + case 0x0c: + if (acat == 0x01) + ap = 2.0; + else if (acat == 0x02) + ap = 3.0; + else if (acat == 0x03) + ap = 4.0; + break; + */ + case 0x07: + case 0x0e: + case 0x0f: + case 0x0b: + case 0x0c: + // Unsupported audio format + return FRL_CAP_CHK_ERROR_UNSUPPORTED_AUDIO; + default: + inter->ap = 0.0; + } + + inter->r_ap = (dml_max(audio_bw_reserve, params->f_audio * inter->ap) + 2 * ACR_RATE_MAX) * (1 + TOLERANCE_AUDIO_CLOCK / 1000000.0); + inter->avg_audio_packets_line = inter->r_ap * inter->t_line; + inter->audio_packets_line = (int)ceil(inter->avg_audio_packets_line); + inter->blank_audio_min = 32 + 32 * inter->audio_packets_line; // h_blank_audio_min or hc_blank_audio_min + + params->audio_packets_line = inter->audio_packets_line; +/* + if (getenv("DEBUG_FRL_CAP_CHK")) + { + frl_dump_var("%le", inter->ap); + frl_dump_var("%le", inter->r_ap); + frl_dump_var("%le", inter->avg_audio_packets_line); + frl_dump_var("%i", inter->audio_packets_line); + frl_dump_var("%i", inter->blank_audio_min); + } +*/ + return FRL_CAP_CHK_OK; +} + + +static frl_cap_chk_result frl_cap_chk_uncompressed(frl_cap_chk_params *params, frl_cap_chk_intermediates *inter) +{ + frl_cap_chk_result res; + + int k_420; + double k_cd; + int c_frl_free; + int c_frl_rc_margin; + int c_frl_rc_savings; + int bpp; + double bytes_line; + int tb_active; + int tb_blank ; + double f_tb_average; + double t_active_ref; + double t_blank_ref; + double t_active_min; + double t_blank_min; + double t_borrowed; + double tb_borrowed; + int c_frl_actual_payload; + double utilization; + double margin; + + res = frl_cap_chk_common(inter, params); + if (res != FRL_CAP_CHK_OK) { + return res; + } + + k_420 = params->pixel_encoding == PIXEL_ENCODING_420 ? 2 : 1; + k_cd = params->pixel_encoding == PIXEL_ENCODING_422 ? 1.0 : params->bpc / 8.0; + c_frl_free = (int)dml_max(params->h_blank * k_cd / k_420 - 32 * (1 + inter->audio_packets_line) - 7, 0); + c_frl_rc_margin = 4; + c_frl_rc_savings = (int)floor(dml_max(((7.0/8.0) * c_frl_free) - c_frl_rc_margin, 0.0)); + bpp = (int)(24 * k_cd / k_420); + bytes_line = bpp * params->h_active / 8.0; + tb_active = (int)ceil(bytes_line / 3); + tb_blank = (int)ceil(params->h_blank * k_cd / k_420); +/* + if (getenv("DEBUG_FRL_CAP_CHK")) + { + frl_dump_var("%i", k_420); + frl_dump_var("%le", k_cd); + frl_dump_var("%i", c_frl_free); + frl_dump_var("%i", c_frl_rc_margin); + frl_dump_var("%i", c_frl_rc_savings); + frl_dump_var("%i", bpp); + frl_dump_var("%le", bytes_line); + frl_dump_var("%i", tb_active); + frl_dump_var("%i", tb_blank); + } +*/ + if (!(inter->blank_audio_min <= tb_blank)) { + frl_dump_var("%i", inter->blank_audio_min); + frl_dump_var("%i", tb_blank); + return FRL_CAP_CHK_ERROR_AUDIO_BW; + } + + f_tb_average = (inter->f_pixel_clock_max / (params->h_active + params->h_blank)) * (tb_active + tb_blank); + t_active_ref = inter->t_line * ((double) params->h_active / (params->h_active + params->h_blank)); + t_blank_ref = inter->t_line * ((double) params->h_blank / (params->h_active + params->h_blank)); + t_active_min = (3.0 / 2.0) * tb_active / (params->lanes * inter->r_frl_char_min * (1.0 - inter->overhead_max)); + t_blank_min = tb_blank / (params->lanes * inter->r_frl_char_min * (1.0 - inter->overhead_max)); +/* + if (getenv("DEBUG_FRL_CAP_CHK")) + { + 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_min); + frl_dump_var("%le", t_blank_min); + } +*/ + if ((t_active_ref >= t_active_min) && (t_blank_ref >= t_blank_min)) { + t_borrowed = 0; + params->borrow_mode = BORROW_MODE_NONE; + } else if ((t_active_ref < t_active_min) && (t_blank_ref >= t_blank_min)) { + t_borrowed = t_active_min - t_active_ref; + params->borrow_mode = BORROW_MODE_FROM_BLANK; + } else + return FRL_CAP_CHK_ERROR_BORROW; + + tb_borrowed = ceil(t_borrowed * f_tb_average); +/* + if (getenv("DEBUG_FRL_CAP_CHK")) + { + frl_dump_var("%le", tb_borrowed); + frl_dump_var("%i", params->borrow_mode); +} +*/ + if (!(tb_borrowed <= TB_BORROWED_MAX)) + return FRL_CAP_CHK_ERROR_MAX_BORROW; + + c_frl_actual_payload = (int)ceil((3.0/2.0) * tb_active) + tb_blank - c_frl_rc_savings; + utilization = c_frl_actual_payload / inter->c_frl_line; + margin = 1.0 - (utilization + inter->overhead_max); +/* + if (getenv("DEBUG_FRL_CAP_CHK")) + { + frl_dump_var("%i", c_frl_actual_payload); + frl_dump_var("%le", utilization); + frl_dump_var("%le", margin); + } +*/ + if (margin < 0 && fabs(margin) > EPSILON) + return FRL_CAP_CHK_ERROR_MARGIN; + + return FRL_CAP_CHK_OK; +} + + + +static frl_cap_chk_result frl_cap_chk_compressed(frl_cap_chk_params *params, frl_cap_chk_intermediates *inter) +{ + frl_cap_chk_result res; + + int c_frl_available; + int c_frl_active_available; + int c_frl_blank_available; + int bytes_target; + int hc_active_target; + int hc_blank_target_est1; + int hc_blank_target_est2; + int hc_blank_target; + double f_tb_average; + double t_active_ref; + double t_blank_ref; + double t_active_target; + double t_blank_target; + double tb_borrowed; + int c_frl_actual_target_payload; + double utilization_targeted; + double margin_target; + + res = frl_cap_chk_common(inter, params); + if (res != FRL_CAP_CHK_OK) + return res; + + c_frl_available = (int)floor((1 - inter->overhead_max) * inter->c_frl_line); + c_frl_active_available = (int)floor(c_frl_available * ((double) params->h_active / (params->h_active + params->h_blank))); + (void) c_frl_active_available; + c_frl_blank_available = (int)floor(c_frl_available * ((double) params->h_blank / (params->h_active + params->h_blank))); + (void) c_frl_blank_available; + bytes_target = params->slices * (int)ceil(params->bpp_target * params->slice_width / 8.0); + + if (!params->bypass_hc_target_calc) + hc_active_target = (int)ceil(bytes_target / 3.0); + else + hc_active_target = params->hc_active_target; + + hc_blank_target_est1 = (int)ceil(hc_active_target * ((double) params->h_blank / params->h_active)); + hc_blank_target_est2 = (int)dml_max(hc_blank_target_est1, inter->blank_audio_min); + + if (!params->bypass_hc_target_calc) { + hc_blank_target = 4 * (int)floor(dml_min(hc_blank_target_est2, c_frl_available - 3.0/2.0 * hc_active_target) / 4.0); + + params->hc_active_target = hc_active_target; + params->hc_blank_target = hc_blank_target; + } else { + hc_blank_target = params->hc_blank_target; + } +/* + if (getenv("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); + } +*/ + 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; +/* + if (getenv("DEBUG_FRL_CAP_CHK")) + { + 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); + } +*/ + if (t_blank_target - t_blank_ref > DBL_EPSILON) { + params->borrow_mode = BORROW_MODE_FROM_ACTIVE; + } else if (t_active_target - t_active_ref > DBL_EPSILON) { + params->borrow_mode = BORROW_MODE_FROM_BLANK; + } else { + params->borrow_mode = BORROW_MODE_NONE; + } + +/* + if (getenv("DEBUG_FRL_CAP_CHK")) + { + frl_dump_var("%le", tb_delta_limit); + frl_dump_var("%le", tb_borrowed); + frl_dump_var("%i", params->borrow_mode); + frl_dump_var("%i", tb_worst); + } +*/ + if (!(tb_borrowed <= TB_BORROWED_MAX)) + return FRL_CAP_CHK_ERROR_MAX_BORROW; + + c_frl_actual_target_payload = (int)ceil(3.0/2.0 * hc_active_target) + hc_blank_target; + utilization_targeted = c_frl_actual_target_payload / inter->c_frl_line; + margin_target = 1.0 - (utilization_targeted + inter->overhead_max); +/* + if (getenv("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); + } +*/ + // oversubscribed bandwidth relative to margin + if (margin_target < 0 && fabs(margin_target) > EPSILON) + return FRL_CAP_CHK_ERROR_MARGIN; + + return FRL_CAP_CHK_OK; +} + +frl_cap_chk_result frl_cap_chk(frl_cap_chk_params *params) +{ + frl_cap_chk_intermediates inter; + return frl_cap_chk_inter(params, &inter); +} + +frl_cap_chk_result frl_cap_chk_inter(frl_cap_chk_params *params, frl_cap_chk_intermediates *inter) +{ + if (params->compressed) + return frl_cap_chk_compressed(params, inter); + return frl_cap_chk_uncompressed(params, inter); +} diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml_frl_cap_chk.h b/drivers/gpu/drm/amd/display/dc/dml2_0/dml_frl_cap_chk.h new file mode 100644 index 0000000000000..87ac9b94e98b2 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml_frl_cap_chk.h @@ -0,0 +1,109 @@ +/* + * Copyright 2022 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DML_FRL_CAP_CHK_H__ +#define __DML_FRL_CAP_CHK_H__ + +#include "os_types.h" + +typedef enum { + PIXEL_ENCODING_444, + PIXEL_ENCODING_422, + PIXEL_ENCODING_420 +} enum_pixel_encoding; + +typedef enum { + BORROW_MODE_NONE, + BORROW_MODE_FROM_ACTIVE, + BORROW_MODE_FROM_BLANK +} enum_borrow_mode; + +typedef enum { + FRL_CAP_CHK_OK = 0, + + FRL_CAP_CHK_ERROR_AUDIO_BW = -1, + FRL_CAP_CHK_ERROR_BORROW = -2, + FRL_CAP_CHK_ERROR_MAX_BORROW = -3, + FRL_CAP_CHK_ERROR_MARGIN = -4, + + FRL_CAP_CHK_ERROR_UNSUPPORTED_AUDIO = -1000 +} frl_cap_chk_result; + +typedef struct { + int c_frl_sb; + double overhead_sb; + double overhead_rs; + double overhead_map; + double overhead_min; + double overhead_max; + double f_pixel_clock_max; + double t_line; + double r_bit_min; + double r_frl_char_min; + double c_frl_line; + double ap; + double r_ap; + double avg_audio_packets_line; + int audio_packets_line; + int blank_audio_min; +} frl_cap_chk_intermediates; + +typedef struct { + int lanes; + double f_pixel_clock_nominal; /* Pixel Clock rate (Hz) */ + double r_bit_nominal; /* FRL bitrate (bps) */ + int audio_packet_type; + double f_audio; /* Audio rate (Hz) */ + int h_active; /* Active pixels per line */ + int h_blank; /* Blanking pixels per line */ + int bpc; /* Bits per component */ + + enum_pixel_encoding pixel_encoding; + + bool compressed; + bool bypass_hc_target_calc; + + /* DSC parameters */ + int slices; + int slice_width; + double bpp_target; + + int layout; /* not supported */ + int acat; /* not supported */ + + /* outputs */ + int audio_packets_line; + + /* inputs or outputs */ + int hc_active_target; + int hc_blank_target; + + enum_borrow_mode borrow_mode; +} frl_cap_chk_params; + +frl_cap_chk_result frl_cap_chk(frl_cap_chk_params *params); +frl_cap_chk_result frl_cap_chk_inter(frl_cap_chk_params *params, frl_cap_chk_intermediates *inter); + +#endif 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 8dfb6dd14eb29..c0f489ccf5e1e 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/dsc/dsc.h b/drivers/gpu/drm/amd/display/dc/dsc/dsc.h index a16c60d8532f0..ab37a7eaaf010 100644 --- a/drivers/gpu/drm/amd/display/dc/dsc/dsc.h +++ b/drivers/gpu/drm/amd/display/dc/dsc/dsc.h @@ -102,6 +102,11 @@ struct dsc_enc_caps { int32_t max_total_throughput_mps; /* Maximum total throughput with all the slices combined */ int32_t max_slice_width; uint32_t bpp_increment_div; /* bpp increment divisor, e.g. if 16, it's 1/16th of a bit */ + bool is_frl; + bool is_vic_all_bpp; + uint32_t total_chunk_kbytes; + uint32_t num_lanes; + uint32_t frl_rate; uint32_t edp_sink_max_bits_per_pixel; bool is_dp; }; diff --git a/drivers/gpu/drm/amd/display/dc/hdcp/hdcp_msg.c b/drivers/gpu/drm/amd/display/dc/hdcp/hdcp_msg.c index 73a1e6a037192..34fc9f56dbefc 100644 --- a/drivers/gpu/drm/amd/display/dc/hdcp/hdcp_msg.c +++ b/drivers/gpu/drm/amd/display/dc/hdcp/hdcp_msg.c @@ -365,6 +365,7 @@ static const struct protection_properties *get_protection_properties_by_signal( case SIGNAL_TYPE_DVI_SINGLE_LINK: case SIGNAL_TYPE_DVI_DUAL_LINK: case SIGNAL_TYPE_HDMI_TYPE_A: + case SIGNAL_TYPE_HDMI_FRL: return &hdmi_14_protection; //todo version2.2 case SIGNAL_TYPE_DISPLAY_PORT: case SIGNAL_TYPE_DISPLAY_PORT_MST: diff --git a/drivers/gpu/drm/amd/display/dc/hpo/Makefile b/drivers/gpu/drm/amd/display/dc/hpo/Makefile index 25b196714bd8c..db60564b9cfd6 100644 --- a/drivers/gpu/drm/amd/display/dc/hpo/Makefile +++ b/drivers/gpu/drm/amd/display/dc/hpo/Makefile @@ -27,6 +27,7 @@ ifdef CONFIG_DRM_AMD_DC_FP ############################################################################### # DCN30 ############################################################################### +HPO_DCN30 = dcn30_hpo_frl_link_encoder.o dcn30_hpo_frl_stream_encoder.o AMD_DAL_HPO_DCN30 = $(addprefix $(AMDDALPATH)/dc/hpo/dcn30/,$(HPO_DCN30)) @@ -48,10 +49,19 @@ AMD_DAL_HPO_DCN32 = $(addprefix $(AMDDALPATH)/dc/hpo/dcn32/,$(HPO_DCN32)) AMD_DISPLAY_FILES += $(AMD_DAL_HPO_DCN32) +############################################################################### +# DCN401 +############################################################################### +HPO_DCN401 = dcn401_hpo_frl_stream_encoder.o + +AMD_DAL_HPO_DCN401 = $(addprefix $(AMDDALPATH)/dc/hpo/dcn401/,$(HPO_DCN401)) + +AMD_DISPLAY_FILES += $(AMD_DAL_HPO_DCN401) ############################################################################### # DCN42 ############################################################################### HPO_DCN42 = dcn42_hpo_dp_link_encoder.o +HPO_DCN42 += dcn42_hpo_frl_stream_encoder.o AMD_DAL_HPO_DCN42 = $(addprefix $(AMDDALPATH)/dc/hpo/dcn42/,$(HPO_DCN42)) diff --git a/drivers/gpu/drm/amd/display/dc/hpo/dcn30/dcn30_hpo_frl_link_encoder.c b/drivers/gpu/drm/amd/display/dc/hpo/dcn30/dcn30_hpo_frl_link_encoder.c new file mode 100644 index 0000000000000..56e753d2f43da --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/hpo/dcn30/dcn30_hpo_frl_link_encoder.c @@ -0,0 +1,286 @@ +/* + * Copyright 2019 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + + +#include "core_types.h" +#include "dc_bios_types.h" +#include "dcn30_hpo_frl_link_encoder.h" +#include "reg_helper.h" +#include "dcn10/dcn10_link_encoder.h" + +#define DC_LOGGER enc3->base.ctx->logger + +#define REG(reg) (enc3->regs->reg) + +#undef FN +#define FN(reg_name, field_name) enc3->hpo_le_shift->field_name, enc3->hpo_le_mask->field_name + +#define CTX enc3->base.ctx + +void hpo_frl_link_enc3_setup_link_encoder(struct hpo_frl_link_encoder *enc, + int lane_count) +{ + struct dcn30_hpo_frl_link_encoder *enc3 = DCN30_HPO_FRL_LINK_ENC_FROM_HPO_FRL_LINK_ENC(enc); + + DC_LOG_DEBUG("Entering [%s]\n", __func__); + + if (enc->ctx->dc->caps.ips_v2_support) { + REG_UPDATE(HDMI_FRL_ENC_MEM_CTRL, + METERBUFFER_MEM_PWR_DIS, 1); + REG_WAIT(HDMI_FRL_ENC_MEM_CTRL, METERBUFFER_MEM_PWR_STATE, 0, 1, 100); + } + /* Enable Link encoder clock */ + REG_UPDATE(HDMI_LINK_ENC_CLK_CTRL, + HDMI_LINK_ENC_CLOCK_EN, 1); + + /* Configure lane count of FRL encoder */ + REG_UPDATE(HDMI_FRL_ENC_CONFIG, + HDMI_LINK_LANE_COUNT, lane_count == 3 ? 0 : 1); + + /* Reset link encoder */ + REG_UPDATE_2(HDMI_LINK_ENC_CONTROL, + HDMI_LINK_ENC_ENABLE, 0, + HDMI_LINK_ENC_SOFT_RESET, 1); + + REG_UPDATE(HDMI_LINK_ENC_CONTROL, + HDMI_LINK_ENC_SOFT_RESET, 0); + + /* Enable link encoder */ + REG_UPDATE(HDMI_LINK_ENC_CONTROL, + HDMI_LINK_ENC_ENABLE, 1); + + DC_LOG_HDMI_FRL("Exiting [%s]\n", __func__); +} + +void hpo_frl_link_enc3_set_training_pattern(struct hpo_frl_link_encoder *enc, + uint32_t lane0_pattern, + uint32_t lane1_pattern, + uint32_t lane2_pattern, + uint32_t lane3_pattern) +{ + struct dcn30_hpo_frl_link_encoder *enc3 = DCN30_HPO_FRL_LINK_ENC_FROM_HPO_FRL_LINK_ENC(enc); + + /* Configure lane count of FRL encoder */ + REG_UPDATE(HDMI_FRL_ENC_CONFIG, + HDMI_LINK_TRAINING_ENABLE, 1); + + if (lane0_pattern < 8) + REG_UPDATE(HDMI_FRL_ENC_CONFIG, + HDMI_LINK_LANE0_TRAINING_PATTERN, lane0_pattern); + + if (lane1_pattern < 8) + REG_UPDATE(HDMI_FRL_ENC_CONFIG, + HDMI_LINK_LANE1_TRAINING_PATTERN, lane1_pattern); + + if (lane2_pattern < 8) + REG_UPDATE(HDMI_FRL_ENC_CONFIG, + HDMI_LINK_LANE2_TRAINING_PATTERN, lane2_pattern); + + if (lane3_pattern < 8) + REG_UPDATE(HDMI_FRL_ENC_CONFIG, + HDMI_LINK_LANE3_TRAINING_PATTERN, lane3_pattern); +} + +void hpo_frl_link_enc3_get_training_pattern(struct hpo_frl_link_encoder *enc, + uint32_t *lane0_pattern, + uint32_t *lane1_pattern, + uint32_t *lane2_pattern, + uint32_t *lane3_pattern) +{ + struct dcn30_hpo_frl_link_encoder *enc3 = DCN30_HPO_FRL_LINK_ENC_FROM_HPO_FRL_LINK_ENC(enc); + + /* Configure lane count of FRL encoder */ + REG_GET_4(HDMI_FRL_ENC_CONFIG, + HDMI_LINK_LANE0_TRAINING_PATTERN, lane0_pattern, + HDMI_LINK_LANE1_TRAINING_PATTERN, lane1_pattern, + HDMI_LINK_LANE2_TRAINING_PATTERN, lane2_pattern, + HDMI_LINK_LANE3_TRAINING_PATTERN, lane3_pattern); +} + +static enum bp_result link_transmitter_control(struct dcn10_link_encoder *enc10, + struct bp_transmitter_control *cntl) +{ + struct dc_bios *bp = enc10->base.ctx->dc_bios; + + return bp->funcs->transmitter_control(bp, cntl); +} + +static void hpo_frl_link_enc3_enable_phy_output(struct hpo_frl_link_encoder *hpo_enc, + struct link_encoder *enc, + enum clock_source_id clock_source, + enum hdmi_frl_link_rate frl_link_rate) +{ + struct dcn30_hpo_frl_link_encoder *enc3 = DCN30_HPO_FRL_LINK_ENC_FROM_HPO_FRL_LINK_ENC(hpo_enc); + struct dcn10_link_encoder *enc10 = TO_DCN10_LINK_ENC(enc); + struct bp_transmitter_control cntl = { 0 }; + enum bp_result result; + + /* Enable the PHY */ + cntl.action = TRANSMITTER_CONTROL_ENABLE; + cntl.engine_id = enc->preferred_engine; + cntl.transmitter = enc10->base.transmitter; + cntl.pll_id = clock_source; + cntl.signal = SIGNAL_TYPE_HDMI_FRL; + cntl.hpd_sel = enc10->base.hpd_source; + + switch (frl_link_rate) { + case HDMI_FRL_LINK_RATE_3GBPS: + cntl.pixel_clock = 166667; + break; + case HDMI_FRL_LINK_RATE_6GBPS: + case HDMI_FRL_LINK_RATE_6GBPS_4LANE: + cntl.pixel_clock = 333333; + break; + case HDMI_FRL_LINK_RATE_8GBPS: + cntl.pixel_clock = 444444; + break; + case HDMI_FRL_LINK_RATE_10GBPS: + cntl.pixel_clock = 555555; + break; + case HDMI_FRL_LINK_RATE_12GBPS: + default: + cntl.pixel_clock = 666667; + break; + } + + cntl.hpo_engine_id = enc3->base.inst + ENGINE_ID_HPO_0; + + if (frl_link_rate <= HDMI_FRL_LINK_RATE_6GBPS) + cntl.lanes_number = 3; + else + cntl.lanes_number = 4; + + result = link_transmitter_control(enc10, &cntl); + + if (result != BP_RESULT_OK) { + DC_LOG_HDMI_FRL("%s: Failed to execute VBIOS command table!\n", __func__); + BREAK_TO_DEBUGGER(); + } +} + +void hpo_frl_link_enc3_enable_output(struct hpo_frl_link_encoder *enc) +{ + struct dcn30_hpo_frl_link_encoder *enc3 = DCN30_HPO_FRL_LINK_ENC_FROM_HPO_FRL_LINK_ENC(enc); + + DC_LOG_HDMI_FRL("Entering [%s]\n", __func__); + + /* Enable FRL packet transmission */ + REG_UPDATE(HDMI_FRL_ENC_CONFIG, + HDMI_LINK_TRAINING_ENABLE, 0); + DC_LOG_HDMI_FRL("Exiting [%s]\n", __func__); +} + +void hpo_frl_link_enc3_disable(struct hpo_frl_link_encoder *enc) +{ + struct dcn30_hpo_frl_link_encoder *enc3 = DCN30_HPO_FRL_LINK_ENC_FROM_HPO_FRL_LINK_ENC(enc); + + DC_LOG_HDMI_FRL("Entering [%s]\n", __func__); + + REG_UPDATE_5(HDMI_FRL_ENC_CONFIG, + HDMI_LINK_TRAINING_ENABLE, 1, + HDMI_LINK_LANE0_TRAINING_PATTERN, 0, + HDMI_LINK_LANE1_TRAINING_PATTERN, 0, + HDMI_LINK_LANE2_TRAINING_PATTERN, 0, + HDMI_LINK_LANE3_TRAINING_PATTERN, 0); + + /* Disable link encoder */ + REG_UPDATE(HDMI_LINK_ENC_CONTROL, + HDMI_LINK_ENC_ENABLE, 0); + + /* Disable Link encoder clock */ + REG_UPDATE(HDMI_LINK_ENC_CLK_CTRL, + HDMI_LINK_ENC_CLOCK_EN, 0); + REG_UPDATE(HDMI_FRL_ENC_CONFIG2, + HDMI_LINK_RC_COMPRESS_DISABLE, 0); + + DC_LOG_HDMI_FRL("Exiting [%s]\n", __func__); +} + +void hpo_frl_link_enc3_read_state(struct hpo_frl_link_encoder *enc, + struct hpo_frl_link_enc_state *state) +{ + struct dcn30_hpo_frl_link_encoder *enc3 = DCN30_HPO_FRL_LINK_ENC_FROM_HPO_FRL_LINK_ENC(enc); + unsigned int link_training_enabled; + unsigned int lane_count_field; + + ASSERT(state); + REG_GET(HDMI_LINK_ENC_CONTROL, + HDMI_LINK_ENC_ENABLE, &state->link_enc_enabled); + + REG_GET_2(HDMI_FRL_ENC_CONFIG, + HDMI_LINK_TRAINING_ENABLE, &link_training_enabled, + HDMI_LINK_LANE_COUNT, &lane_count_field); + + state->link_active = link_training_enabled == 1; + + if (lane_count_field == 1) + state->lane_count = 4; + else + state->lane_count = 3; +} + +void hpo_frl_link_enc3_destroy(struct hpo_frl_link_encoder **enc) +{ + kfree(DCN30_HPO_FRL_LINK_ENC_FROM_HPO_FRL_LINK_ENC(*enc)); + *enc = NULL; +} + +void hpo_frl_link_enc3_apply_vsdb_rcc_wa(struct hpo_frl_link_encoder *enc) +{ + struct dcn30_hpo_frl_link_encoder *enc3 = + DCN30_HPO_FRL_LINK_ENC_FROM_HPO_FRL_LINK_ENC(enc); + + REG_UPDATE(HDMI_FRL_ENC_CONFIG2, + HDMI_LINK_RC_COMPRESS_DISABLE, 1); +} + +static struct hpo_frl_link_encoder_funcs dcn30_hpo_frl_link_encoder_funcs = { + .setup_link_encoder = hpo_frl_link_enc3_setup_link_encoder, + .set_hdmi_training_pattern = hpo_frl_link_enc3_set_training_pattern, + .get_hdmi_training_pattern = hpo_frl_link_enc3_get_training_pattern, + .enable_frl_phy_output = hpo_frl_link_enc3_enable_phy_output, + .enable_output = hpo_frl_link_enc3_enable_output, + .disable_link_encoder = hpo_frl_link_enc3_disable, + .read_state = hpo_frl_link_enc3_read_state, + .destroy = hpo_frl_link_enc3_destroy, + .apply_vsdb_rcc_wa = hpo_frl_link_enc3_apply_vsdb_rcc_wa +}; + +void hpo_frl_link_encoder3_construct(struct dcn30_hpo_frl_link_encoder *enc3, + struct dc_context *ctx, + uint32_t inst, + const struct dcn30_hpo_frl_link_encoder_registers *hpo_le_regs, + const struct dcn30_hpo_frl_link_encoder_shift *hpo_le_shift, + const struct dcn30_hpo_frl_link_encoder_mask *hpo_le_mask) +{ + enc3->base.ctx = ctx; + + enc3->base.inst = inst; + enc3->base.funcs = &dcn30_hpo_frl_link_encoder_funcs; + + enc3->regs = hpo_le_regs; + enc3->hpo_le_shift = hpo_le_shift; + enc3->hpo_le_mask = hpo_le_mask; +} diff --git a/drivers/gpu/drm/amd/display/dc/hpo/dcn30/dcn30_hpo_frl_link_encoder.h b/drivers/gpu/drm/amd/display/dc/hpo/dcn30/dcn30_hpo_frl_link_encoder.h new file mode 100644 index 0000000000000..9626793812b8c --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/hpo/dcn30/dcn30_hpo_frl_link_encoder.h @@ -0,0 +1,146 @@ +/* + * Copyright 2019 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DAL_DCN30_HPO_FRL_LINK_ENCODER_H__ +#define __DAL_DCN30_HPO_FRL_LINK_ENCODER_H__ + +#include "link_encoder.h" + + +#define DCN30_HPO_FRL_LINK_ENC_FROM_HPO_FRL_LINK_ENC(hpo_frl_link_encoder)\ + container_of(hpo_frl_link_encoder, struct dcn30_hpo_frl_link_encoder, base) + + +#define DCN3_0_HPO_FRL_LINK_ENC_REG_LIST(id) \ + SR(HDMI_LINK_ENC_CLK_CTRL), \ + SR(HDMI_LINK_ENC_CONTROL), \ + SR(HDMI_FRL_ENC_CONFIG), \ + SR(HDMI_FRL_ENC_CONFIG2),\ + SR(HDMI_FRL_ENC_MEM_CTRL) + +struct dcn30_hpo_frl_link_encoder_registers { + uint32_t HDMI_LINK_ENC_CLK_CTRL; + uint32_t HDMI_LINK_ENC_CONTROL; + uint32_t HDMI_FRL_ENC_CONFIG; + uint32_t HDMI_FRL_ENC_CONFIG2; + uint32_t HDMI_FRL_ENC_MEM_CTRL; +}; + +#define DCN3_0_HPO_FRL_LINK_ENC_MASK_SH_LIST(mask_sh)\ + SE_SF(HDMI_LINK_ENC_CLK_CTRL, HDMI_LINK_ENC_CLOCK_EN, mask_sh),\ + SE_SF(HDMI_LINK_ENC_CONTROL, HDMI_LINK_ENC_ENABLE, mask_sh),\ + SE_SF(HDMI_LINK_ENC_CONTROL, HDMI_LINK_ENC_SOFT_RESET, mask_sh),\ + SE_SF(HDMI_FRL_ENC_MEM_CTRL, METERBUFFER_MEM_PWR_DIS, mask_sh),\ + SE_SF(HDMI_FRL_ENC_MEM_CTRL, METERBUFFER_MEM_PWR_FORCE, mask_sh),\ + SE_SF(HDMI_FRL_ENC_MEM_CTRL, METERBUFFER_MEM_PWR_STATE, mask_sh),\ + SE_SF(HDMI_FRL_ENC_MEM_CTRL, METERBUFFER_MEM_DEFAULT_MEM_LOW_POWER_STATE, mask_sh),\ + SE_SF(HDMI_FRL_ENC_CONFIG, HDMI_LINK_LANE_COUNT, mask_sh),\ + SE_SF(HDMI_FRL_ENC_CONFIG, HDMI_LINK_TRAINING_ENABLE, mask_sh),\ + SE_SF(HDMI_FRL_ENC_CONFIG, HDMI_LINK_LANE0_TRAINING_PATTERN, mask_sh),\ + SE_SF(HDMI_FRL_ENC_CONFIG, HDMI_LINK_LANE1_TRAINING_PATTERN, mask_sh),\ + SE_SF(HDMI_FRL_ENC_CONFIG, HDMI_LINK_LANE2_TRAINING_PATTERN, mask_sh),\ + SE_SF(HDMI_FRL_ENC_CONFIG, HDMI_LINK_LANE3_TRAINING_PATTERN, mask_sh),\ + SE_SF(HDMI_FRL_ENC_CONFIG2, HDMI_LINK_MAX_JITTER_VALUE, mask_sh),\ + SE_SF(HDMI_FRL_ENC_CONFIG2, HDMI_LINK_JITTER_THRESHOLD, mask_sh),\ + SE_SF(HDMI_FRL_ENC_CONFIG2, HDMI_LINK_JITTER_CAL_EN, mask_sh),\ + SE_SF(HDMI_FRL_ENC_CONFIG2, HDMI_LINK_RC_COMPRESS_DISABLE, mask_sh),\ + SE_SF(HDMI_FRL_ENC_CONFIG2, HDMI_FRL_HDMISTREAMCLK_DB_SEL, mask_sh),\ + SE_SF(HDMI_FRL_ENC_CONFIG2, HDMI_LINK_MAX_JITTER_VALUE_RESET, mask_sh),\ + SE_SF(HDMI_FRL_ENC_CONFIG2, HDMI_LINK_JITTER_EXCEED_STATUS, mask_sh),\ + SE_SF(HDMI_FRL_ENC_CONFIG2, HDMI_LINK_METER_BUFFER_OVERFLOW_STATUS, mask_sh) + +#define HPO_FRL_LINK_ENC_DCN3_REG_FIELD_LIST(type) \ + type HDMI_LINK_ENC_CLOCK_EN;\ + type HDMI_LINK_ENC_ENABLE;\ + type HDMI_LINK_ENC_SOFT_RESET;\ + type HDMI_LINK_LANE_COUNT;\ + type HDMI_LINK_TRAINING_ENABLE;\ + type HDMI_LINK_LANE0_TRAINING_PATTERN;\ + type HDMI_LINK_LANE1_TRAINING_PATTERN;\ + type HDMI_LINK_LANE2_TRAINING_PATTERN;\ + type HDMI_LINK_LANE3_TRAINING_PATTERN;\ + type HDMI_LINK_MAX_JITTER_VALUE;\ + type HDMI_LINK_JITTER_THRESHOLD;\ + type HDMI_LINK_JITTER_CAL_EN;\ + type HDMI_LINK_RC_COMPRESS_DISABLE;\ + type METERBUFFER_MEM_PWR_DIS;\ + type METERBUFFER_MEM_PWR_STATE;\ + type METERBUFFER_MEM_PWR_FORCE;\ + type METERBUFFER_MEM_DEFAULT_MEM_LOW_POWER_STATE;\ + type HDMI_FRL_HDMISTREAMCLK_DB_SEL;\ + type HDMI_LINK_MAX_JITTER_VALUE_RESET;\ + type HDMI_LINK_JITTER_EXCEED_STATUS;\ + type HDMI_LINK_METER_BUFFER_OVERFLOW_STATUS + + +struct dcn30_hpo_frl_link_encoder_shift { + HPO_FRL_LINK_ENC_DCN3_REG_FIELD_LIST(uint8_t); +}; + +struct dcn30_hpo_frl_link_encoder_mask { + HPO_FRL_LINK_ENC_DCN3_REG_FIELD_LIST(uint32_t); +}; + +struct dcn30_hpo_frl_link_encoder { + struct hpo_frl_link_encoder base; + const struct dcn30_hpo_frl_link_encoder_registers *regs; + const struct dcn30_hpo_frl_link_encoder_shift *hpo_le_shift; + const struct dcn30_hpo_frl_link_encoder_mask *hpo_le_mask; +}; + +void hpo_frl_link_enc3_setup_link_encoder(struct hpo_frl_link_encoder *enc, + int lane_count); + +void hpo_frl_link_enc3_set_training_pattern(struct hpo_frl_link_encoder *enc, + uint32_t lane0_pattern, + uint32_t lane1_pattern, + uint32_t lane2_pattern, + uint32_t lane3_pattern); + +void hpo_frl_link_enc3_get_training_pattern(struct hpo_frl_link_encoder *enc, + uint32_t *lane0_pattern, + uint32_t *lane1_pattern, + uint32_t *lane2_pattern, + uint32_t *lane3_pattern); + +void hpo_frl_link_enc3_enable_output(struct hpo_frl_link_encoder *enc); + +void hpo_frl_link_enc3_disable(struct hpo_frl_link_encoder *enc); + +void hpo_frl_link_enc3_read_state(struct hpo_frl_link_encoder *enc, + struct hpo_frl_link_enc_state *state); + +void hpo_frl_link_enc3_destroy(struct hpo_frl_link_encoder **enc); + +void hpo_frl_link_enc3_apply_vsdb_rcc_wa(struct hpo_frl_link_encoder *enc); + +void hpo_frl_link_encoder3_construct(struct dcn30_hpo_frl_link_encoder *enc3, + struct dc_context *ctx, + uint32_t inst, + const struct dcn30_hpo_frl_link_encoder_registers *hpo_le_regs, + const struct dcn30_hpo_frl_link_encoder_shift *hpo_le_shift, + const struct dcn30_hpo_frl_link_encoder_mask *hpo_le_mask); + +#endif 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 new file mode 100644 index 0000000000000..cff5c95a771c4 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/hpo/dcn30/dcn30_hpo_frl_stream_encoder.c @@ -0,0 +1,1043 @@ +/* + * Copyright 2019 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + + +#include "dc_bios_types.h" +#include "core_types.h" +#include "dcn30_hpo_frl_stream_encoder.h" +#include "reg_helper.h" +#include "hw_shared.h" +#include "dcn_calc_math.h" +#include "dml/dcn30/dcn30_fpu.h" + +#undef DC_LOGGER +#define DC_LOGGER enc3->base.ctx->logger + +#define DTRACE(str, ...) {DC_LOG_HDMI_FRL(str, ##__VA_ARGS__); } + +#define DEBUG_FRL_CAP_CHK 1 + +#define REG(reg) (enc3->regs->reg) + +#undef FN +#define FN(reg_name, field_name) enc3->hpo_se_shift->field_name, enc3->hpo_se_mask->field_name + +#define CTX enc3->base.ctx + +#define VBI_LINE_0 0 + +void hpo_enc3_enable(struct hpo_frl_stream_encoder *enc, int otg_inst) +{ + struct dcn30_hpo_frl_stream_encoder *enc3 = DCN30_HPO_FRL_STRENC_FROM_HPO_FRL_STRENC(enc); + + DC_LOG_HDMI_FRL("Entering [%s]\n", __func__); + + /* Enable DISPCLK, SOCCLK, and HDMISTREAMCLK */ + REG_UPDATE(HDMI_STREAM_ENC_CLOCK_CONTROL, + HDMI_STREAM_ENC_CLOCK_EN, 1); + + /* Reset */ + REG_UPDATE_2(HDMI_TB_ENC_CONTROL, + HDMI_RESET, 1, + HDMI_TB_ENC_EN, 0); + REG_WAIT(HDMI_TB_ENC_CONTROL, HDMI_RESET_DONE, + 1, 10, 100); + REG_UPDATE(HDMI_TB_ENC_CONTROL, + HDMI_RESET, 0); + + /* FOR DEBUG: enable CRC */ + REG_UPDATE_2(HDMI_TB_ENC_CRC_CNTL, + HDMI_CRC_EN, 1, + HDMI_CRC_CONT_EN, 1); + + REG_UPDATE(HDMI_STREAM_ENC_CLOCK_RAMP_ADJUSTER_FIFO_STATUS_CONTROL2, + FIFO_DB_DISABLE, 1); + + /* TODO: confirm if need to set HDMI_DB_DISABLE -- HW team only setting FIFO_DB_DISABLE */ + REG_UPDATE(HDMI_TB_ENC_DB_CONTROL, + HDMI_DB_DISABLE, 1); + + /* Set the input mux to select OTG source */ + REG_UPDATE(HDMI_STREAM_ENC_INPUT_MUX_CONTROL, + HDMI_STREAM_ENC_INPUT_MUX_SOURCE_SEL, otg_inst); + + DC_LOG_HDMI_FRL("Exiting [%s]\n", __func__); +} + +void hpo_enc3_unblank(struct hpo_frl_stream_encoder *enc, int otg_inst) +{ + (void)otg_inst; + struct dcn30_hpo_frl_stream_encoder *enc3 = DCN30_HPO_FRL_STRENC_FROM_HPO_FRL_STRENC(enc); + + DC_LOG_HDMI_FRL("Entering [%s]\n", __func__); + + /*make sure FIFO_VIDEO_STREAM_ACTIVE =1*/ + REG_UPDATE(HDMI_STREAM_ENC_CLOCK_RAMP_ADJUSTER_FIFO_STATUS_CONTROL0, + FIFO_ENABLE, 0); + + /* Reset */ + REG_UPDATE(HDMI_STREAM_ENC_CLOCK_RAMP_ADJUSTER_FIFO_STATUS_CONTROL0, + FIFO_RESET, 1); + REG_WAIT(HDMI_STREAM_ENC_CLOCK_RAMP_ADJUSTER_FIFO_STATUS_CONTROL0, FIFO_RESET_DONE, + 1, 10, 1000); + REG_UPDATE(HDMI_STREAM_ENC_CLOCK_RAMP_ADJUSTER_FIFO_STATUS_CONTROL0, + FIFO_RESET, 0); + REG_WAIT(HDMI_STREAM_ENC_CLOCK_RAMP_ADJUSTER_FIFO_STATUS_CONTROL0, FIFO_RESET_DONE, + 0, 10, 1000); + + /* Enable HDMI Tribyte Encoder */ + REG_UPDATE(HDMI_TB_ENC_CONTROL, + HDMI_TB_ENC_EN, 1); + + /* Enable Clock Ramp Adjuster FIFO */ + REG_UPDATE(HDMI_STREAM_ENC_CLOCK_RAMP_ADJUSTER_FIFO_STATUS_CONTROL0, + FIFO_ENABLE, 1); + + DC_LOG_HDMI_FRL("Exiting [%s]\n", __func__); +} + +bool hpo_enc3_fifo_odm_enabled(struct hpo_frl_stream_encoder *enc) +{ + struct dcn30_hpo_frl_stream_encoder *enc3 = DCN30_HPO_FRL_STRENC_FROM_HPO_FRL_STRENC(enc); + uint32_t fifo_odm_combine_mode; + + REG_GET(HDMI_STREAM_ENC_CLOCK_RAMP_ADJUSTER_FIFO_STATUS_CONTROL0, + FIFO_ODM_COMBINE_MODE, &fifo_odm_combine_mode); + + return (fifo_odm_combine_mode != 0); +} + +void hpo_enc3_blank(struct hpo_frl_stream_encoder *enc) +{ + struct dcn30_hpo_frl_stream_encoder *enc3 = DCN30_HPO_FRL_STRENC_FROM_HPO_FRL_STRENC(enc); + + /* Disable Clock Ramp Adjuster FIFO */ + REG_UPDATE_2(HDMI_STREAM_ENC_CLOCK_RAMP_ADJUSTER_FIFO_STATUS_CONTROL0, + FIFO_ENABLE, 0, + FIFO_ODM_COMBINE_MODE, 0); + + /* Disable HDMI Tribyte Encoder */ + REG_UPDATE(HDMI_TB_ENC_CONTROL, + HDMI_TB_ENC_EN, 0); + + /* Disable DISPCLK, SOCCLK, and HDMISTREAMCLK */ + REG_UPDATE(HDMI_STREAM_ENC_CLOCK_CONTROL, + HDMI_STREAM_ENC_CLOCK_EN, 0); +} + +/* Setup stream encoder in hdmi mode + * - Precondition: link is trained + */ +void hpo_enc3_set_hdmi_stream_attribute(struct hpo_frl_stream_encoder *enc, + struct dc_crtc_timing *crtc_timing, + struct frl_borrow_params *borrow_params, + int odm_combine_num_segments) +{ + struct dcn30_hpo_frl_stream_encoder *enc3 = DCN30_HPO_FRL_STRENC_FROM_HPO_FRL_STRENC(enc); + uint32_t h_active; + uint32_t h_blank; + + DC_LOG_HDMI_FRL("Entering [%s]\n", __func__); + + /* Configure pixel encoding */ + switch (crtc_timing->pixel_encoding) { + case PIXEL_ENCODING_YCBCR422: + REG_UPDATE(HDMI_TB_ENC_PIXEL_FORMAT, + HDMI_PIXEL_ENCODING, 1); + REG_UPDATE(HDMI_STREAM_ENC_CLOCK_RAMP_ADJUSTER_FIFO_STATUS_CONTROL0, + FIFO_PIXEL_ENCODING, 1); + break; + case PIXEL_ENCODING_YCBCR420: + REG_UPDATE(HDMI_TB_ENC_PIXEL_FORMAT, + HDMI_PIXEL_ENCODING, 2); + REG_UPDATE(HDMI_STREAM_ENC_CLOCK_RAMP_ADJUSTER_FIFO_STATUS_CONTROL0, + FIFO_PIXEL_ENCODING, 2); + break; + default: + REG_UPDATE(HDMI_TB_ENC_PIXEL_FORMAT, + HDMI_PIXEL_ENCODING, 0); + REG_UPDATE(HDMI_STREAM_ENC_CLOCK_RAMP_ADJUSTER_FIFO_STATUS_CONTROL0, + FIFO_PIXEL_ENCODING, 0); + break; + } + + /* Configure color depth */ + switch (crtc_timing->display_color_depth) { + case COLOR_DEPTH_888: + REG_UPDATE_2(HDMI_TB_ENC_PIXEL_FORMAT, + HDMI_DEEP_COLOR_DEPTH, 0, + HDMI_DEEP_COLOR_ENABLE, 0); + break; + case COLOR_DEPTH_101010: + if (crtc_timing->pixel_encoding == PIXEL_ENCODING_YCBCR422) { + REG_UPDATE_2(HDMI_TB_ENC_PIXEL_FORMAT, + HDMI_DEEP_COLOR_DEPTH, 1, + HDMI_DEEP_COLOR_ENABLE, 0); + } else { + REG_UPDATE_2(HDMI_TB_ENC_PIXEL_FORMAT, + HDMI_DEEP_COLOR_DEPTH, 1, + HDMI_DEEP_COLOR_ENABLE, 1); + } + break; + case COLOR_DEPTH_121212: + if (crtc_timing->pixel_encoding == PIXEL_ENCODING_YCBCR422) { + REG_UPDATE_2(HDMI_TB_ENC_PIXEL_FORMAT, + HDMI_DEEP_COLOR_DEPTH, 2, + HDMI_DEEP_COLOR_ENABLE, 0); + } else { + REG_UPDATE_2(HDMI_TB_ENC_PIXEL_FORMAT, + HDMI_DEEP_COLOR_DEPTH, 2, + HDMI_DEEP_COLOR_ENABLE, 1); + } + break; + default: + 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: + REG_UPDATE(HDMI_STREAM_ENC_CLOCK_RAMP_ADJUSTER_FIFO_STATUS_CONTROL0, + FIFO_ODM_COMBINE_MODE, 0); + + if (enc3->hpo_se_mask->HDMI_ODM_COMBINE_MODE) + REG_UPDATE(HDMI_TB_ENC_PIXEL_FORMAT, + HDMI_ODM_COMBINE_MODE, 0); + break; + case 2: + REG_UPDATE(HDMI_STREAM_ENC_CLOCK_RAMP_ADJUSTER_FIFO_STATUS_CONTROL0, + FIFO_ODM_COMBINE_MODE, 1); + + if (enc3->hpo_se_mask->HDMI_ODM_COMBINE_MODE) + REG_UPDATE(HDMI_TB_ENC_PIXEL_FORMAT, + HDMI_ODM_COMBINE_MODE, 1); + break; + case 4: + REG_UPDATE(HDMI_STREAM_ENC_CLOCK_RAMP_ADJUSTER_FIFO_STATUS_CONTROL0, + FIFO_ODM_COMBINE_MODE, 3); + break; + default: + break; + } + + /* Configure horizontal active and blank size */ + h_active = crtc_timing->h_addressable + crtc_timing->h_border_left + crtc_timing->h_border_right; + h_blank = crtc_timing->h_total - h_active; + + if (crtc_timing->pixel_encoding == PIXEL_ENCODING_YCBCR420 || + crtc_timing->pixel_encoding == PIXEL_ENCODING_YCBCR422) { + h_active /= 2; + h_blank /= 2; + } + + + REG_SET_2(HDMI_TB_ENC_H_ACTIVE_BLANK, 0, + HDMI_H_ACTIVE, h_active, + HDMI_H_BLANK, h_blank); + + /* Configure borrow parameters */ + REG_UPDATE(HDMI_TB_ENC_MODE, + HDMI_BORROW_MODE, borrow_params->borrow_mode); + + REG_UPDATE(HDMI_TB_ENC_PACKET_CONTROL, + HDMI_MAX_PACKETS_PER_LINE, borrow_params->audio_packets_line); + + REG_SET_2(HDMI_TB_ENC_HC_ACTIVE_BLANK, 0, + HDMI_HC_ACTIVE, borrow_params->hc_active_target, + HDMI_HC_BLANK, borrow_params->hc_blank_target); + + /* Enable transmission of General Control packet on every frame */ + REG_UPDATE_2(HDMI_TB_ENC_VBI_PACKET_CONTROL1, + HDMI_GC_CONT, 1, + HDMI_GC_SEND, 1); + + /* Disable Audio Content Protection packet transmission */ + /* TODO: review if this needs to be here */ + REG_UPDATE(HDMI_TB_ENC_VBI_PACKET_CONTROL1, + HDMI_ACP_SEND, 0); + + /* Enable Audio InfoFrame packet transmission. */ + REG_UPDATE(HDMI_TB_ENC_VBI_PACKET_CONTROL1, + HDMI_AUDIO_INFO_SEND, 1); + + /* update double-buffered AUDIO_INFO registers immediately */ + ASSERT(enc->afmt); + enc->afmt->funcs->audio_info_immediate_update(enc->afmt); + + /* Select line number on which to send Audio InfoFrame packets */ + REG_UPDATE(HDMI_TB_ENC_VBI_PACKET_CONTROL1, HDMI_AUDIO_INFO_LINE, + VBI_LINE_0 + 2); + + /* set HDMI GC AVMUTE */ + REG_UPDATE(HDMI_TB_ENC_GC_CONTROL, + HDMI_GC_AVMUTE, 0); + + DC_LOG_HDMI_FRL("Exiting [%s]\n", __func__); +} + +void hpo_enc3_update_hdmi_info_packets(struct hpo_frl_stream_encoder *enc, + const struct encoder_info_frame *info_frame) +{ + struct dcn30_hpo_frl_stream_encoder *enc3 = DCN30_HPO_FRL_STRENC_FROM_HPO_FRL_STRENC(enc); + + hpo_enc3_update_hdmi_info_packet(enc3, 0, &info_frame->avi); + hpo_enc3_update_hdmi_info_packet(enc3, 1, &info_frame->vendor); + hpo_enc3_update_hdmi_info_packet(enc3, 2, &info_frame->gamut); + hpo_enc3_update_hdmi_info_packet(enc3, 3, &info_frame->spd); + hpo_enc3_update_hdmi_info_packet(enc3, 4, &info_frame->hdrsmd); + + /* 5-10 used by dsc */ + hpo_enc3_update_hdmi_info_packet(enc3, 11, &info_frame->hfvsif); + hpo_enc3_update_hdmi_info_packet(enc3, 12, &info_frame->vtem); +} + +void hpo_enc3_update_hdmi_info_packet(struct dcn30_hpo_frl_stream_encoder *enc3, + uint32_t packet_index, + const struct dc_info_packet *info_packet) +{ + uint32_t cont, send, line; + + if (info_packet->valid) { + enc3->base.vpg->funcs->update_generic_info_packet( + enc3->base.vpg, + packet_index, + info_packet, + true); + + /* enable transmission of packet(s) - + * packet transmission begins on the next frame */ + cont = 1; + /* send packet(s) every frame */ + send = 1; + /* select line number to send packets on */ + /* TODO: check if line 2 is correct */ + line = 2; + } else { + cont = 0; + send = 0; + line = 0; + } + + /* TODO: set bit to indicate if packet is Extended Metadata Packet. */ + /* TODO: In DCN3, there are 0-14 generic packets */ + + /* choose which generic packet control to use */ + switch (packet_index) { + case 0: + REG_UPDATE_2(HDMI_TB_ENC_GENERIC_PACKET_CONTROL0, + HDMI_GENERIC0_CONT, cont, + HDMI_GENERIC0_SEND, send); + REG_UPDATE(HDMI_TB_ENC_GENERIC_PACKET0_1_LINE, + HDMI_GENERIC0_LINE, line); + break; + case 1: + REG_UPDATE_2(HDMI_TB_ENC_GENERIC_PACKET_CONTROL0, + HDMI_GENERIC1_CONT, cont, + HDMI_GENERIC1_SEND, send); + REG_UPDATE(HDMI_TB_ENC_GENERIC_PACKET0_1_LINE, + HDMI_GENERIC1_LINE, line); + break; + case 2: + REG_UPDATE_2(HDMI_TB_ENC_GENERIC_PACKET_CONTROL0, + HDMI_GENERIC2_CONT, cont, + HDMI_GENERIC2_SEND, send); + REG_UPDATE(HDMI_TB_ENC_GENERIC_PACKET2_3_LINE, + HDMI_GENERIC2_LINE, line); + break; + case 3: + REG_UPDATE_2(HDMI_TB_ENC_GENERIC_PACKET_CONTROL0, + HDMI_GENERIC3_CONT, cont, + HDMI_GENERIC3_SEND, send); + REG_UPDATE(HDMI_TB_ENC_GENERIC_PACKET2_3_LINE, + HDMI_GENERIC3_LINE, line); + break; + case 4: + REG_UPDATE_2(HDMI_TB_ENC_GENERIC_PACKET_CONTROL0, + HDMI_GENERIC4_CONT, cont, + HDMI_GENERIC4_SEND, send); + REG_UPDATE(HDMI_TB_ENC_GENERIC_PACKET4_5_LINE, + HDMI_GENERIC4_LINE, line); + break; + case 5: + REG_UPDATE_2(HDMI_TB_ENC_GENERIC_PACKET_CONTROL0, + HDMI_GENERIC5_CONT, cont, + HDMI_GENERIC5_SEND, send); + REG_UPDATE(HDMI_TB_ENC_GENERIC_PACKET4_5_LINE, + HDMI_GENERIC5_LINE, line); + break; + case 6: + REG_UPDATE_2(HDMI_TB_ENC_GENERIC_PACKET_CONTROL0, + HDMI_GENERIC6_CONT, cont, + HDMI_GENERIC6_SEND, send); + REG_UPDATE(HDMI_TB_ENC_GENERIC_PACKET6_7_LINE, + HDMI_GENERIC6_LINE, line); + break; + case 7: + REG_UPDATE_2(HDMI_TB_ENC_GENERIC_PACKET_CONTROL0, + HDMI_GENERIC7_CONT, cont, + HDMI_GENERIC7_SEND, send); + REG_UPDATE(HDMI_TB_ENC_GENERIC_PACKET6_7_LINE, + HDMI_GENERIC7_LINE, line); + break; + case 8: + REG_UPDATE_2(HDMI_TB_ENC_GENERIC_PACKET_CONTROL1, + HDMI_GENERIC8_CONT, cont, + HDMI_GENERIC8_SEND, send); + REG_UPDATE(HDMI_TB_ENC_GENERIC_PACKET8_9_LINE, + HDMI_GENERIC8_LINE, line); + break; + case 9: + REG_UPDATE_2(HDMI_TB_ENC_GENERIC_PACKET_CONTROL1, + HDMI_GENERIC9_CONT, cont, + HDMI_GENERIC9_SEND, send); + REG_UPDATE(HDMI_TB_ENC_GENERIC_PACKET8_9_LINE, + HDMI_GENERIC9_LINE, line); + break; + case 10: + REG_UPDATE_2(HDMI_TB_ENC_GENERIC_PACKET_CONTROL1, + HDMI_GENERIC10_CONT, cont, + HDMI_GENERIC10_SEND, send); + REG_UPDATE(HDMI_TB_ENC_GENERIC_PACKET10_11_LINE, + HDMI_GENERIC10_LINE, line); + break; + case 11: + REG_UPDATE_2(HDMI_TB_ENC_GENERIC_PACKET_CONTROL1, + HDMI_GENERIC11_CONT, cont, + HDMI_GENERIC11_SEND, send); + REG_UPDATE(HDMI_TB_ENC_GENERIC_PACKET10_11_LINE, + HDMI_GENERIC11_LINE, line); + break; + case 12: + REG_UPDATE_2(HDMI_TB_ENC_GENERIC_PACKET_CONTROL1, + HDMI_GENERIC12_CONT, cont, + HDMI_GENERIC12_SEND, send); + REG_UPDATE(HDMI_TB_ENC_GENERIC_PACKET12_13_LINE, + HDMI_GENERIC12_LINE, line); + break; + case 13: + REG_UPDATE_2(HDMI_TB_ENC_GENERIC_PACKET_CONTROL1, + HDMI_GENERIC13_CONT, cont, + HDMI_GENERIC13_SEND, send); + REG_UPDATE(HDMI_TB_ENC_GENERIC_PACKET12_13_LINE, + HDMI_GENERIC13_LINE, line); + break; + case 14: + REG_UPDATE_2(HDMI_TB_ENC_GENERIC_PACKET_CONTROL1, + HDMI_GENERIC14_CONT, cont, + HDMI_GENERIC14_SEND, send); + REG_UPDATE(HDMI_TB_ENC_GENERIC_PACKET14_LINE, + HDMI_GENERIC14_LINE, line); + break; + default: + /* invalid HW packet index */ + DC_LOG_WARNING("Invalid HW packet index: %s()\n", __func__); + return; + } +} + +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) +{ + struct dcn30_hpo_frl_stream_encoder *enc3 = DCN30_HPO_FRL_STRENC_FROM_HPO_FRL_STRENC(enc); + + /* TODO: should also set extended metadata packet bit back to 0? */ + + /* stop generic packets 0,1 on HDMI */ + REG_SET_4(HDMI_TB_ENC_GENERIC_PACKET_CONTROL0, 0, + HDMI_GENERIC0_CONT, 0, + HDMI_GENERIC0_SEND, 0, + HDMI_GENERIC1_CONT, 0, + HDMI_GENERIC1_SEND, 0); + REG_SET_2(HDMI_TB_ENC_GENERIC_PACKET0_1_LINE, 0, + HDMI_GENERIC0_LINE, 0, + HDMI_GENERIC1_LINE, 0); + + /* stop generic packets 2,3 on HDMI */ + REG_SET_4(HDMI_TB_ENC_GENERIC_PACKET_CONTROL0, 0, + HDMI_GENERIC2_CONT, 0, + HDMI_GENERIC2_SEND, 0, + HDMI_GENERIC3_CONT, 0, + HDMI_GENERIC3_SEND, 0); + REG_SET_2(HDMI_TB_ENC_GENERIC_PACKET2_3_LINE, 0, + HDMI_GENERIC2_LINE, 0, + HDMI_GENERIC3_LINE, 0); + + /* stop generic packets 4,5 on HDMI */ + REG_SET_4(HDMI_TB_ENC_GENERIC_PACKET_CONTROL0, 0, + HDMI_GENERIC4_CONT, 0, + HDMI_GENERIC4_SEND, 0, + HDMI_GENERIC5_CONT, 0, + HDMI_GENERIC5_SEND, 0); + REG_SET_2(HDMI_TB_ENC_GENERIC_PACKET4_5_LINE, 0, + HDMI_GENERIC4_LINE, 0, + HDMI_GENERIC5_LINE, 0); + + /* stop generic packets 6,7 on HDMI */ + REG_SET_4(HDMI_TB_ENC_GENERIC_PACKET_CONTROL0, 0, + HDMI_GENERIC6_CONT, 0, + HDMI_GENERIC6_SEND, 0, + HDMI_GENERIC7_CONT, 0, + HDMI_GENERIC7_SEND, 0); + REG_SET_2(HDMI_TB_ENC_GENERIC_PACKET6_7_LINE, 0, + HDMI_GENERIC6_LINE, 0, + HDMI_GENERIC7_LINE, 0); + + /* stop generic packets 8,9 on HDMI */ + REG_SET_4(HDMI_TB_ENC_GENERIC_PACKET_CONTROL1, 0, + HDMI_GENERIC8_CONT, 0, + HDMI_GENERIC8_SEND, 0, + HDMI_GENERIC9_CONT, 0, + HDMI_GENERIC9_SEND, 0); + REG_SET_2(HDMI_TB_ENC_GENERIC_PACKET8_9_LINE, 0, + HDMI_GENERIC8_LINE, 0, + HDMI_GENERIC9_LINE, 0); + + /* stop generic packets 10,11 on HDMI */ + REG_SET_4(HDMI_TB_ENC_GENERIC_PACKET_CONTROL1, 0, + HDMI_GENERIC10_CONT, 0, + HDMI_GENERIC10_SEND, 0, + HDMI_GENERIC11_CONT, 0, + HDMI_GENERIC11_SEND, 0); + REG_SET_2(HDMI_TB_ENC_GENERIC_PACKET10_11_LINE, 0, + HDMI_GENERIC10_LINE, 0, + HDMI_GENERIC11_LINE, 0); + + /* stop generic packets 12,13 on HDMI */ + REG_SET_4(HDMI_TB_ENC_GENERIC_PACKET_CONTROL1, 0, + HDMI_GENERIC12_CONT, 0, + HDMI_GENERIC12_SEND, 0, + HDMI_GENERIC13_CONT, 0, + HDMI_GENERIC13_SEND, 0); + REG_SET_2(HDMI_TB_ENC_GENERIC_PACKET12_13_LINE, 0, + HDMI_GENERIC12_LINE, 0, + HDMI_GENERIC13_LINE, 0); + + /* stop generic packets 14 on HDMI */ + REG_SET_2(HDMI_TB_ENC_GENERIC_PACKET_CONTROL1, 0, + HDMI_GENERIC14_CONT, 0, + HDMI_GENERIC14_SEND, 0); + REG_SET(HDMI_TB_ENC_GENERIC_PACKET14_LINE, 0, + HDMI_GENERIC14_LINE, 0); + +} + +//Covered both, rounding up or rounding down from FRL Link Rate /18. +static const struct frl_audio_clock_info frl_audio_clock_info_table[10] = { + {166666, 4224, 171875, 5292, 156250, 5760, 156250}, + {166667, 4224, 171875, 5292, 156250, 5760, 156250}, + {333333, 4032, 328125, 5292, 312500, 6048, 328125}, + {333334, 4032, 328125, 5292, 312500, 6048, 328125}, + {444444, 4032, 437500, 3969, 312500, 6048, 437500}, + {444445, 4032, 437500, 3969, 312500, 6048, 437500}, + {555555, 3456, 468750, 3969, 390625, 5184, 468750}, + {555556, 3456, 468750, 3969, 390625, 5184, 468750}, + {666666, 3072, 500000, 3969, 468750, 4752, 515625}, + {666667, 3072, 500000, 3969, 468750, 4752, 515625} +}; + +static void get_audio_clock_info( + enum dc_color_depth color_depth, + uint32_t frl_character_clock_kHz, + struct frl_audio_clock_info *audio_clock_info) +{ + (void)color_depth; + const struct frl_audio_clock_info *clock_info; + uint32_t index; + uint32_t audio_array_size; + + clock_info = frl_audio_clock_info_table; + audio_array_size = ARRAY_SIZE( + frl_audio_clock_info_table); + + if (clock_info != NULL) { + /* search for exact frl character clock in table */ + for (index = 0; index < audio_array_size; index++) { + if (clock_info[index].frl_character_clock_kHz > + frl_character_clock_kHz) + break; /* not match */ + else if (clock_info[index].frl_character_clock_kHz == + frl_character_clock_kHz) { + /* match found */ + *audio_clock_info = clock_info[index]; + return; + } + } + } + /*Only 3, 6, 8, 10 and 12 Gbps are used for FRL Link rates with character + *clocks of 166.667, 333.333, 444.444, 555.555 and 666.667 MHz are used + *so entry should be found in above table if no bugs */ + BREAK_TO_DEBUGGER(); +} + +void hpo_enc3_setup_hdmi_audio( + struct hpo_frl_stream_encoder *enc, + const struct audio_crtc_info *crtc_info) +{ + struct dcn30_hpo_frl_stream_encoder *enc3 = DCN30_HPO_FRL_STRENC_FROM_HPO_FRL_STRENC(enc); + struct frl_audio_clock_info audio_clock_info = {0}; + + DC_LOG_DEBUG("Entering [%s]\n", __func__); + + /* TODO: HDMI_AUDIO_DELAY_EN bit only in DIG -- not in HPO? */ + /* HDMI_AUDIO_PACKET_CONTROL */ + //REG_UPDATE(HDMI_AUDIO_PACKET_CONTROL, + // HDMI_AUDIO_DELAY_EN, 1); + + /* Setup audio in AFMT - program AFMT block associated with HPO */ + ASSERT (enc->afmt); + enc->afmt->funcs->setup_hdmi_audio(enc->afmt); + + /* TODO: Same programming, but using HDMI_TB_ENC register */ + /* HDMI_ACR_PACKET_CONTROL */ + REG_UPDATE_3(HDMI_TB_ENC_ACR_PACKET_CONTROL, + HDMI_ACR_AUTO_SEND, 1, + HDMI_ACR_SOURCE, 0, + HDMI_ACR_AUDIO_PRIORITY, 0); + + /* N/CTS computed relative to FRL rate instead of video rate (TMDS character clock). */ + /* Program audio clock sample/regeneration parameters */ + get_audio_clock_info(crtc_info->color_depth, + crtc_info->frl_character_clock_kHz, + &audio_clock_info); + DC_LOG_HW_AUDIO( + "\n%s:Input::requested_pixel_clock_100Hz = %d" \ + "calculated_pixel_clock_100Hz = %d \n", __func__, \ + crtc_info->requested_pixel_clock_100Hz, \ + crtc_info->calculated_pixel_clock_100Hz); + + /* Same register definition, but using HDMI_TB_ENC register */ + /* HDMI_ACR_32_0__HDMI_ACR_CTS_32_MASK */ + REG_UPDATE(HDMI_TB_ENC_ACR_32_0, HDMI_ACR_CTS_32, audio_clock_info.cts_32khz); + + /* HDMI_ACR_32_1__HDMI_ACR_N_32_MASK */ + REG_UPDATE(HDMI_TB_ENC_ACR_32_1, HDMI_ACR_N_32, audio_clock_info.n_32khz); + + /* HDMI_ACR_44_0__HDMI_ACR_CTS_44_MASK */ + REG_UPDATE(HDMI_TB_ENC_ACR_44_0, HDMI_ACR_CTS_44, audio_clock_info.cts_44khz); + + /* HDMI_ACR_44_1__HDMI_ACR_N_44_MASK */ + REG_UPDATE(HDMI_TB_ENC_ACR_44_1, HDMI_ACR_N_44, audio_clock_info.n_44khz); + + /* HDMI_ACR_48_0__HDMI_ACR_CTS_48_MASK */ + REG_UPDATE(HDMI_TB_ENC_ACR_48_0, HDMI_ACR_CTS_48, audio_clock_info.cts_48khz); + + /* HDMI_ACR_48_1__HDMI_ACR_N_48_MASK */ + REG_UPDATE(HDMI_TB_ENC_ACR_48_1, HDMI_ACR_N_48, audio_clock_info.n_48khz); + + + /* TODO: HDMI_TB_ENC_ACR_PACKET_CONTROL::ACR_N_MULTIPLE + * Same register definition, but using HDMI_TB_ENC register*/ + + /* Video driver cannot know in advance which sample rate will + * be used by HD Audio driver + * HDMI_ACR_PACKET_CONTROL__HDMI_ACR_N_MULTIPLE field is + * programmed below in interrupt callback + */ + DC_LOG_DEBUG("Exiting [%s]\n", __func__); +} + +void hpo_enc3_hdmi_audio_setup( + struct hpo_frl_stream_encoder *enc, + unsigned int az_inst, + struct audio_info *info, + struct audio_crtc_info *audio_crtc_info) +{ + hpo_enc3_setup_hdmi_audio(enc, audio_crtc_info); + ASSERT (enc->afmt); + enc->afmt->funcs->se_audio_setup(enc->afmt, az_inst, info); +} + +void hpo_enc3_hdmi_audio_disable( + struct hpo_frl_stream_encoder *enc) +{ + ASSERT(enc->afmt); + if (enc->afmt->funcs->afmt_powerdown) + enc->afmt->funcs->afmt_powerdown(enc->afmt); +} + +void hpo_enc3_audio_mute_control( + struct hpo_frl_stream_encoder *enc, + bool mute) +{ + ASSERT (enc->afmt); + enc->afmt->funcs->audio_mute_control(enc->afmt, mute); +} + +void enc3_stream_encoder_set_avmute( + struct hpo_frl_stream_encoder *enc, + bool enable) +{ + struct dcn30_hpo_frl_stream_encoder *enc3 = DCN30_HPO_FRL_STRENC_FROM_HPO_FRL_STRENC(enc); + unsigned int value = enable ? 1 : 0; + + REG_UPDATE(HDMI_TB_ENC_GC_CONTROL, HDMI_GC_AVMUTE, value); +} + +static enum frl_cap_chk_result frl_cap_chk_uncompressed( + struct hpo_frl_stream_encoder *enc, + struct frl_cap_chk_params *params, + struct frl_cap_chk_intermediates *inter) +{ + int res; + DC_FP_START(); + res = frl_fpu_cap_chk_uncompressed(enc, params, inter); + DC_FP_END(); + return res; +} + +static enum frl_cap_chk_result frl_cap_chk_compressed( + struct hpo_frl_stream_encoder *enc, + struct frl_cap_chk_params *params, + struct frl_cap_chk_intermediates *inter) +{ + int res; + DC_FP_START(); + res = frl_fpu_cap_chk_compressed(enc, params, inter); + DC_FP_END(); + return res; +} + +static bool hpo_enc3_frl_cap_chk( + struct hpo_frl_stream_encoder *enc, + struct frl_cap_chk_params *params) +{ + struct frl_cap_chk_intermediates inter; + enum frl_cap_chk_result res; + + if (params->compressed) + res = frl_cap_chk_compressed(enc, params, &inter); + else + res = frl_cap_chk_uncompressed(enc, params, &inter); + + return (res == FRL_CAP_CHK_OK); +} + +bool hpo_enc3_validate_hdmi_frl_output( + struct hpo_frl_stream_encoder *enc, + const struct dc_crtc_timing *timing, + const struct audio_check *audio, + struct dc_hdmi_frl_link_settings *frl_link_settings, + unsigned int dsc_max_rate) +{ + struct frl_cap_chk_params frl_params = {0}; + bool frl_check_res = false; + + /* Set inputs for FRL check */ + frl_params.lanes = frl_link_settings->frl_num_lanes; + DC_FP_START(); + hpo_fpu_enc3_validate_hdmi_frl_output_link(enc, + frl_link_settings, + &frl_params, + timing, + dsc_max_rate); + DC_FP_END(); + + if (timing->display_color_depth == COLOR_DEPTH_888) + frl_params.bpc = 8; + else if (timing->display_color_depth == COLOR_DEPTH_101010) + frl_params.bpc = 10; + else + frl_params.bpc = 12; + + switch (timing->hdmi_vic) { + case 1: + frl_params.vic = 95; + break; + case 2: + frl_params.vic = 94; + break; + case 3: + frl_params.vic = 93; + break; + case 4: + frl_params.vic = 98; + break; + 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 */ + + if (audio->audio_packet_type == 2) { + if (audio->max_channel_count <= 2 + || (timing->v_addressable + timing->v_border_top + timing->v_border_bottom) <= 576) + frl_params.layout = 0; + else + frl_params.layout = 1; + + } + + /* Check HDMI FRL Capacity and compute borrow parameters */ + frl_check_res = hpo_enc3_frl_cap_chk(enc, &frl_params); + /* Save borrow parameters and average tribyte rate (for capture sideband) */ + if (frl_check_res) { + frl_link_settings->borrow_params.audio_packets_line = + frl_params.borrow_params.audio_packets_line; + frl_link_settings->borrow_params.hc_active_target = + frl_params.borrow_params.hc_active_target; + frl_link_settings->borrow_params.hc_blank_target = + frl_params.borrow_params.hc_blank_target; + frl_link_settings->borrow_params.borrow_mode = + (unsigned int) frl_params.borrow_params.borrow_mode; + frl_link_settings->average_tribyte_rate = frl_params.average_tribyte_rate; + } + + return frl_check_res; +} + +void hpo_enc3_read_state( + struct hpo_frl_stream_encoder *enc, + struct hpo_frl_stream_encoder_state *state) +{ + int pixel_encoding; + int color_depth; + int odm_combine; + struct dcn30_hpo_frl_stream_encoder *enc3 = DCN30_HPO_FRL_STRENC_FROM_HPO_FRL_STRENC(enc); + + ASSERT(state); + + REG_GET(HDMI_TB_ENC_CONTROL, + HDMI_TB_ENC_EN, &state->stream_enc_enabled); + + REG_GET(HDMI_STREAM_ENC_INPUT_MUX_CONTROL, + HDMI_STREAM_ENC_INPUT_MUX_SOURCE_SEL, &state->otg_inst); + + REG_GET_2(HDMI_TB_ENC_PIXEL_FORMAT, + HDMI_PIXEL_ENCODING, &pixel_encoding, + HDMI_DEEP_COLOR_DEPTH, &color_depth); + + REG_GET(HDMI_STREAM_ENC_CLOCK_RAMP_ADJUSTER_FIFO_STATUS_CONTROL0, + FIFO_ODM_COMBINE_MODE, &odm_combine); + + REG_GET_2(HDMI_TB_ENC_H_ACTIVE_BLANK, + HDMI_H_ACTIVE, &state->h_active, + HDMI_H_BLANK, &state->h_blank); + + REG_GET(HDMI_TB_ENC_MODE, + HDMI_BORROW_MODE, &state->borrow_mode); + + if (pixel_encoding == 0) + state->pixel_format = PIXEL_ENCODING_YCBCR444; + else if (pixel_encoding == 1) + state->pixel_format = PIXEL_ENCODING_YCBCR422; + else + state->pixel_format = PIXEL_ENCODING_YCBCR420; + + if (color_depth == 0) + state->color_depth = 8; + else if (color_depth == 1) + state->color_depth = 10; + else + state->color_depth = 12; + + state->num_odm_segments = odm_combine + 1; +} + +/* Set Dynamic Metadata-configuration. + * enable_dme: TRUE: enables Dynamic Metadata Enfine, FALSE: disables DME + * hubp_requestor_id: HUBP physical instance that is the source of dynamic metadata + * only needs to be set when enable_dme is TRUE + * dmdata_mode: dynamic metadata packet type: DP, HDMI, or Dolby Vision + * + * Ensure the OTG master update lock is set when changing DME configuration. + */ +void hpo_enc3_set_dynamic_metadata(struct hpo_frl_stream_encoder *enc, + bool enable_dme, + uint32_t hubp_requestor_id, + enum dynamic_metadata_mode dmdata_mode) +{ + struct dcn30_hpo_frl_stream_encoder *enc3 = DCN30_HPO_FRL_STRENC_FROM_HPO_FRL_STRENC(enc); + + if (enable_dme) { + REG_UPDATE_2(DME_CONTROL, + METADATA_HUBP_REQUESTOR_ID, hubp_requestor_id, + METADATA_STREAM_TYPE, (dmdata_mode == dmdata_dolby_vision) ? 1 : 0); + + REG_UPDATE_3(HDMI_TB_ENC_METADATA_PACKET_CONTROL, + HDMI_METADATA_PACKET_ENABLE, 1, + HDMI_METADATA_PACKET_LINE_REFERENCE, 0, + HDMI_METADATA_PACKET_LINE, 2); + + REG_UPDATE(DME_CONTROL, + METADATA_ENGINE_EN, 1); + } else { + REG_UPDATE(DME_CONTROL, + METADATA_ENGINE_EN, 0); + + REG_UPDATE(HDMI_TB_ENC_METADATA_PACKET_CONTROL, + HDMI_METADATA_PACKET_ENABLE, 0); + } +} + +static const struct hpo_frl_stream_encoder_funcs dcn30_str_enc_funcs = { + .hdmi_frl_enable = hpo_enc3_enable, + .hdmi_frl_unblank = hpo_enc3_unblank, + .hdmi_frl_blank = hpo_enc3_blank, + .hdmi_frl_set_stream_attribute = hpo_enc3_set_hdmi_stream_attribute, + .update_hdmi_info_packets = hpo_enc3_update_hdmi_info_packets, + .stop_hdmi_info_packets = hpo_enc3_stop_hdmi_info_packets, + .audio_mute_control = hpo_enc3_audio_mute_control, + .hdmi_audio_setup = hpo_enc3_hdmi_audio_setup, + .hdmi_audio_disable = hpo_enc3_hdmi_audio_disable, + .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, +}; + +void dcn30_hpo_frl_stream_encoder_construct( + struct dcn30_hpo_frl_stream_encoder *enc3, + struct dc_context *ctx, + struct dc_bios *bp, + enum engine_id eng_id, + struct vpg *vpg, + struct afmt *afmt, + const struct dcn30_hpo_frl_stream_enc_registers *regs, + const struct dcn30_hpo_frl_stream_encoder_shift *hpo_se_shift, + const struct dcn30_hpo_frl_stream_encoder_mask *hpo_se_mask) +{ + enc3->base.funcs = &dcn30_str_enc_funcs; + enc3->base.ctx = ctx; + enc3->base.id = eng_id; + enc3->base.bp = bp; + enc3->base.vpg = vpg; + enc3->base.afmt = afmt; + enc3->regs = regs; + enc3->hpo_se_shift = hpo_se_shift; + enc3->hpo_se_mask = hpo_se_mask; + enc3->base.stream_enc_inst = vpg->inst; +} diff --git a/drivers/gpu/drm/amd/display/dc/hpo/dcn30/dcn30_hpo_frl_stream_encoder.h b/drivers/gpu/drm/amd/display/dc/hpo/dcn30/dcn30_hpo_frl_stream_encoder.h new file mode 100644 index 0000000000000..c00be852aa170 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/hpo/dcn30/dcn30_hpo_frl_stream_encoder.h @@ -0,0 +1,436 @@ +/* + * Copyright 2019 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DC_HPO_FRL_STREAM_ENCODER_DCN30_H__ +#define __DC_HPO_FRL_STREAM_ENCODER_DCN30_H__ + +#include "dcn30/dcn30_vpg.h" +#include "dcn30/dcn30_afmt.h" +#include "stream_encoder.h" +#include "dml/dml1_frl_cap_chk.h" + +#define DCN30_HPO_FRL_STRENC_FROM_HPO_FRL_STRENC(hpo_frl_stream_encoder)\ + container_of(hpo_frl_stream_encoder, struct dcn30_hpo_frl_stream_encoder, base) + +#define SE_SF(reg_name, field_name, post_fix)\ + .field_name = reg_name ## __ ## field_name ## post_fix + +#define DCN3_0_HDMI_STREAM_ENC_REG_LIST \ + SR(HDMI_STREAM_ENC_CLOCK_CONTROL), \ + SR(HDMI_STREAM_ENC_INPUT_MUX_CONTROL), \ + SR(HDMI_STREAM_ENC_CLOCK_RAMP_ADJUSTER_FIFO_STATUS_CONTROL0), \ + SR(HDMI_STREAM_ENC_CLOCK_RAMP_ADJUSTER_FIFO_STATUS_CONTROL2) + +#define DCN3_0_HDMI_TB_ENC_REG_LIST \ + SR(HDMI_TB_ENC_CONTROL), \ + SR(HDMI_TB_ENC_H_ACTIVE_BLANK), \ + SR(HDMI_TB_ENC_HC_ACTIVE_BLANK), \ + SR(HDMI_TB_ENC_MODE), \ + SR(HDMI_TB_ENC_PACKET_CONTROL), \ + SR(HDMI_TB_ENC_DB_CONTROL), \ + SR(HDMI_TB_ENC_PIXEL_FORMAT), \ + SR(HDMI_TB_ENC_VBI_PACKET_CONTROL1), \ + SR(HDMI_TB_ENC_GC_CONTROL), \ + SR(HDMI_TB_ENC_GENERIC_PACKET_CONTROL0), \ + SR(HDMI_TB_ENC_GENERIC_PACKET_CONTROL1), \ + SR(HDMI_TB_ENC_GENERIC_PACKET0_1_LINE), \ + SR(HDMI_TB_ENC_GENERIC_PACKET2_3_LINE), \ + SR(HDMI_TB_ENC_GENERIC_PACKET4_5_LINE), \ + SR(HDMI_TB_ENC_GENERIC_PACKET6_7_LINE), \ + SR(HDMI_TB_ENC_GENERIC_PACKET8_9_LINE), \ + SR(HDMI_TB_ENC_GENERIC_PACKET10_11_LINE), \ + SR(HDMI_TB_ENC_GENERIC_PACKET12_13_LINE), \ + SR(HDMI_TB_ENC_GENERIC_PACKET14_LINE), \ + SR(HDMI_TB_ENC_ACR_PACKET_CONTROL), \ + SR(HDMI_TB_ENC_ACR_32_0), \ + SR(HDMI_TB_ENC_ACR_32_1), \ + SR(HDMI_TB_ENC_ACR_44_0), \ + SR(HDMI_TB_ENC_ACR_44_1), \ + SR(HDMI_TB_ENC_ACR_48_0), \ + SR(HDMI_TB_ENC_ACR_48_1), \ + SR(HDMI_TB_ENC_CRC_CNTL),\ + SR(HDMI_TB_ENC_METADATA_PACKET_CONTROL) + +#define DCN3_0_HPO_FRL_STREAM_ENC_REG_LIST(id) \ + DCN3_0_HDMI_STREAM_ENC_REG_LIST, \ + DCN3_0_HDMI_TB_ENC_REG_LIST + +#define DCN3_0_HPO_STREAM_ENC_DME_REG_LIST(id, offset) \ + [id - offset]SRI(DME_CONTROL, DME, id) + + +struct dcn30_hpo_frl_stream_enc_registers { + uint32_t HDMI_STREAM_ENC_CLOCK_CONTROL; + uint32_t HDMI_STREAM_ENC_INPUT_MUX_CONTROL; + uint32_t HDMI_STREAM_ENC_CLOCK_RAMP_ADJUSTER_FIFO_STATUS_CONTROL0; + uint32_t HDMI_STREAM_ENC_CLOCK_RAMP_ADJUSTER_FIFO_STATUS_CONTROL2; + uint32_t HDMI_STREAM_ENC_AUDIO_CONTROL; + uint32_t HDMI_TB_ENC_CONTROL; + uint32_t HDMI_TB_ENC_MODE; + uint32_t HDMI_TB_ENC_H_ACTIVE_BLANK; + uint32_t HDMI_TB_ENC_HC_ACTIVE_BLANK; + uint32_t HDMI_TB_ENC_PACKET_CONTROL; + uint32_t HDMI_TB_ENC_DB_CONTROL; + uint32_t HDMI_TB_ENC_PIXEL_FORMAT; + uint32_t HDMI_TB_ENC_VBI_PACKET_CONTROL1; + uint32_t HDMI_TB_ENC_GC_CONTROL; + uint32_t HDMI_TB_ENC_GENERIC_PACKET_CONTROL0; + uint32_t HDMI_TB_ENC_GENERIC_PACKET_CONTROL1; + uint32_t HDMI_TB_ENC_GENERIC_PACKET0_1_LINE; + uint32_t HDMI_TB_ENC_GENERIC_PACKET2_3_LINE; + uint32_t HDMI_TB_ENC_GENERIC_PACKET4_5_LINE; + uint32_t HDMI_TB_ENC_GENERIC_PACKET6_7_LINE; + uint32_t HDMI_TB_ENC_GENERIC_PACKET8_9_LINE; + uint32_t HDMI_TB_ENC_GENERIC_PACKET10_11_LINE; + uint32_t HDMI_TB_ENC_GENERIC_PACKET12_13_LINE; + uint32_t HDMI_TB_ENC_GENERIC_PACKET14_LINE; + uint32_t HDMI_TB_ENC_ACR_PACKET_CONTROL; + uint32_t HDMI_TB_ENC_ACR_32_0; + uint32_t HDMI_TB_ENC_ACR_32_1; + uint32_t HDMI_TB_ENC_ACR_44_0; + uint32_t HDMI_TB_ENC_ACR_44_1; + uint32_t HDMI_TB_ENC_ACR_48_0; + uint32_t HDMI_TB_ENC_ACR_48_1; + uint32_t HDMI_TB_ENC_CRC_CNTL; + uint32_t DME_CONTROL; + uint32_t HDMI_TB_ENC_METADATA_PACKET_CONTROL; + uint32_t HDMI_TB_ENC_MEM_CTRL; + uint32_t HDMI_FRL_ENC_MEM_CTRL; +}; + +#define DCN3_0_HDMI_STREAM_ENC_MASK_SH_LIST(mask_sh)\ + SE_SF(HDMI_STREAM_ENC_INPUT_MUX_CONTROL, HDMI_STREAM_ENC_INPUT_MUX_SOURCE_SEL, mask_sh),\ + SE_SF(HDMI_STREAM_ENC_CLOCK_RAMP_ADJUSTER_FIFO_STATUS_CONTROL0, FIFO_ENABLE, mask_sh),\ + SE_SF(HDMI_STREAM_ENC_CLOCK_RAMP_ADJUSTER_FIFO_STATUS_CONTROL0, FIFO_RESET, mask_sh),\ + SE_SF(HDMI_STREAM_ENC_CLOCK_RAMP_ADJUSTER_FIFO_STATUS_CONTROL0, FIFO_RESET_DONE, mask_sh),\ + SE_SF(HDMI_STREAM_ENC_CLOCK_RAMP_ADJUSTER_FIFO_STATUS_CONTROL0, FIFO_PIXEL_ENCODING, mask_sh),\ + SE_SF(HDMI_STREAM_ENC_CLOCK_CONTROL, HDMI_STREAM_ENC_CLOCK_EN, mask_sh),\ + SE_SF(HDMI_STREAM_ENC_CLOCK_RAMP_ADJUSTER_FIFO_STATUS_CONTROL0, FIFO_ODM_COMBINE_MODE, mask_sh),\ + SE_SF(HDMI_STREAM_ENC_CLOCK_RAMP_ADJUSTER_FIFO_STATUS_CONTROL0, FIFO_DSC_MODE, mask_sh),\ + SE_SF(HDMI_STREAM_ENC_CLOCK_RAMP_ADJUSTER_FIFO_STATUS_CONTROL2, FIFO_DB_DISABLE, mask_sh),\ + SE_SF(HDMI_STREAM_ENC_CLOCK_RAMP_ADJUSTER_FIFO_STATUS_CONTROL2, FIFO_DB_DISABLE, mask_sh),\ + SE_SF(DME0_DME_CONTROL, METADATA_HUBP_REQUESTOR_ID, mask_sh),\ + SE_SF(DME0_DME_CONTROL, METADATA_ENGINE_EN, mask_sh),\ + SE_SF(DME0_DME_CONTROL, METADATA_STREAM_TYPE, mask_sh),\ + SE_SF(HDMI_TB_ENC_MEM_CTRL, BORROWBUFFER_MEM_PWR_DIS, mask_sh),\ + SE_SF(HDMI_TB_ENC_MEM_CTRL, BORROWBUFFER_MEM_PWR_FORCE, mask_sh),\ + SE_SF(HDMI_TB_ENC_MEM_CTRL, BORROWBUFFER_MEM_PWR_STATE, mask_sh),\ + SE_SF(HDMI_TB_ENC_MEM_CTRL, BORROWBUFFER_MEM_DEFAULT_MEM_LOW_POWER_STATE, mask_sh),\ + SE_SF(HDMI_TB_ENC_METADATA_PACKET_CONTROL, HDMI_METADATA_PACKET_ENABLE, mask_sh),\ + SE_SF(HDMI_TB_ENC_METADATA_PACKET_CONTROL, HDMI_METADATA_PACKET_LINE_REFERENCE, mask_sh),\ + SE_SF(HDMI_TB_ENC_METADATA_PACKET_CONTROL, HDMI_METADATA_PACKET_MISSED, mask_sh),\ + SE_SF(HDMI_TB_ENC_METADATA_PACKET_CONTROL, HDMI_METADATA_PACKET_LINE, mask_sh) + +#define DCN3_0_HDMI_TB_ENC_MASK_SH_LIST(mask_sh)\ + SE_SF(HDMI_TB_ENC_CONTROL, HDMI_TB_ENC_EN, mask_sh),\ + SE_SF(HDMI_TB_ENC_CONTROL, HDMI_RESET, mask_sh),\ + SE_SF(HDMI_TB_ENC_CONTROL, HDMI_RESET_DONE, mask_sh),\ + SE_SF(HDMI_TB_ENC_MODE, HDMI_BORROW_MODE, mask_sh),\ + SE_SF(HDMI_TB_ENC_H_ACTIVE_BLANK, HDMI_H_ACTIVE, mask_sh),\ + SE_SF(HDMI_TB_ENC_H_ACTIVE_BLANK, HDMI_H_BLANK, mask_sh),\ + SE_SF(HDMI_TB_ENC_HC_ACTIVE_BLANK, HDMI_HC_ACTIVE, mask_sh),\ + SE_SF(HDMI_TB_ENC_HC_ACTIVE_BLANK, HDMI_HC_BLANK, mask_sh),\ + SE_SF(HDMI_TB_ENC_PACKET_CONTROL, HDMI_MAX_PACKETS_PER_LINE, mask_sh),\ + SE_SF(HDMI_TB_ENC_DB_CONTROL, HDMI_DB_DISABLE, mask_sh),\ + SE_SF(HDMI_TB_ENC_PIXEL_FORMAT, HDMI_PIXEL_ENCODING, mask_sh),\ + SE_SF(HDMI_TB_ENC_PIXEL_FORMAT, HDMI_DEEP_COLOR_DEPTH, mask_sh),\ + SE_SF(HDMI_TB_ENC_PIXEL_FORMAT, HDMI_DEEP_COLOR_ENABLE, mask_sh),\ + SE_SF(HDMI_TB_ENC_PIXEL_FORMAT, HDMI_DSC_MODE, mask_sh),\ + SE_SF(HDMI_TB_ENC_VBI_PACKET_CONTROL1, HDMI_GC_CONT, mask_sh),\ + SE_SF(HDMI_TB_ENC_VBI_PACKET_CONTROL1, HDMI_GC_SEND, mask_sh),\ + SE_SF(HDMI_TB_ENC_VBI_PACKET_CONTROL1, HDMI_ACP_SEND, mask_sh),\ + SE_SF(HDMI_TB_ENC_VBI_PACKET_CONTROL1, HDMI_AUDIO_INFO_SEND, mask_sh),\ + SE_SF(HDMI_TB_ENC_VBI_PACKET_CONTROL1, HDMI_AUDIO_INFO_LINE, mask_sh),\ + SE_SF(HDMI_TB_ENC_GC_CONTROL, HDMI_GC_AVMUTE, mask_sh),\ + SE_SF(HDMI_TB_ENC_GENERIC_PACKET_CONTROL0, HDMI_GENERIC0_CONT, mask_sh),\ + SE_SF(HDMI_TB_ENC_GENERIC_PACKET_CONTROL0, HDMI_GENERIC1_CONT, mask_sh),\ + SE_SF(HDMI_TB_ENC_GENERIC_PACKET_CONTROL0, HDMI_GENERIC2_CONT, mask_sh),\ + SE_SF(HDMI_TB_ENC_GENERIC_PACKET_CONTROL0, HDMI_GENERIC3_CONT, mask_sh),\ + SE_SF(HDMI_TB_ENC_GENERIC_PACKET_CONTROL0, HDMI_GENERIC4_CONT, mask_sh),\ + SE_SF(HDMI_TB_ENC_GENERIC_PACKET_CONTROL0, HDMI_GENERIC5_CONT, mask_sh),\ + SE_SF(HDMI_TB_ENC_GENERIC_PACKET_CONTROL0, HDMI_GENERIC6_CONT, mask_sh),\ + SE_SF(HDMI_TB_ENC_GENERIC_PACKET_CONTROL0, HDMI_GENERIC7_CONT, mask_sh),\ + SE_SF(HDMI_TB_ENC_GENERIC_PACKET_CONTROL1, HDMI_GENERIC8_CONT, mask_sh),\ + SE_SF(HDMI_TB_ENC_GENERIC_PACKET_CONTROL1, HDMI_GENERIC9_CONT, mask_sh),\ + SE_SF(HDMI_TB_ENC_GENERIC_PACKET_CONTROL1, HDMI_GENERIC10_CONT, mask_sh),\ + SE_SF(HDMI_TB_ENC_GENERIC_PACKET_CONTROL1, HDMI_GENERIC11_CONT, mask_sh),\ + SE_SF(HDMI_TB_ENC_GENERIC_PACKET_CONTROL1, HDMI_GENERIC12_CONT, mask_sh),\ + SE_SF(HDMI_TB_ENC_GENERIC_PACKET_CONTROL1, HDMI_GENERIC13_CONT, mask_sh),\ + SE_SF(HDMI_TB_ENC_GENERIC_PACKET_CONTROL1, HDMI_GENERIC14_CONT, mask_sh),\ + SE_SF(HDMI_TB_ENC_GENERIC_PACKET_CONTROL0, HDMI_GENERIC0_SEND, mask_sh),\ + SE_SF(HDMI_TB_ENC_GENERIC_PACKET_CONTROL0, HDMI_GENERIC1_SEND, mask_sh),\ + SE_SF(HDMI_TB_ENC_GENERIC_PACKET_CONTROL0, HDMI_GENERIC2_SEND, mask_sh),\ + SE_SF(HDMI_TB_ENC_GENERIC_PACKET_CONTROL0, HDMI_GENERIC3_SEND, mask_sh),\ + SE_SF(HDMI_TB_ENC_GENERIC_PACKET_CONTROL0, HDMI_GENERIC4_SEND, mask_sh),\ + SE_SF(HDMI_TB_ENC_GENERIC_PACKET_CONTROL0, HDMI_GENERIC5_SEND, mask_sh),\ + SE_SF(HDMI_TB_ENC_GENERIC_PACKET_CONTROL0, HDMI_GENERIC6_SEND, mask_sh),\ + SE_SF(HDMI_TB_ENC_GENERIC_PACKET_CONTROL0, HDMI_GENERIC7_SEND, mask_sh),\ + SE_SF(HDMI_TB_ENC_GENERIC_PACKET_CONTROL1, HDMI_GENERIC8_SEND, mask_sh),\ + SE_SF(HDMI_TB_ENC_GENERIC_PACKET_CONTROL1, HDMI_GENERIC9_SEND, mask_sh),\ + SE_SF(HDMI_TB_ENC_GENERIC_PACKET_CONTROL1, HDMI_GENERIC10_SEND, mask_sh),\ + SE_SF(HDMI_TB_ENC_GENERIC_PACKET_CONTROL1, HDMI_GENERIC11_SEND, mask_sh),\ + SE_SF(HDMI_TB_ENC_GENERIC_PACKET_CONTROL1, HDMI_GENERIC12_SEND, mask_sh),\ + SE_SF(HDMI_TB_ENC_GENERIC_PACKET_CONTROL1, HDMI_GENERIC13_SEND, mask_sh),\ + SE_SF(HDMI_TB_ENC_GENERIC_PACKET_CONTROL1, HDMI_GENERIC14_SEND, mask_sh),\ + SE_SF(HDMI_TB_ENC_GENERIC_PACKET0_1_LINE, HDMI_GENERIC0_LINE, mask_sh),\ + SE_SF(HDMI_TB_ENC_GENERIC_PACKET0_1_LINE, HDMI_GENERIC1_LINE, mask_sh),\ + SE_SF(HDMI_TB_ENC_GENERIC_PACKET2_3_LINE, HDMI_GENERIC2_LINE, mask_sh),\ + SE_SF(HDMI_TB_ENC_GENERIC_PACKET2_3_LINE, HDMI_GENERIC3_LINE, mask_sh),\ + SE_SF(HDMI_TB_ENC_GENERIC_PACKET4_5_LINE, HDMI_GENERIC4_LINE, mask_sh),\ + SE_SF(HDMI_TB_ENC_GENERIC_PACKET4_5_LINE, HDMI_GENERIC5_LINE, mask_sh),\ + SE_SF(HDMI_TB_ENC_GENERIC_PACKET6_7_LINE, HDMI_GENERIC6_LINE, mask_sh),\ + SE_SF(HDMI_TB_ENC_GENERIC_PACKET6_7_LINE, HDMI_GENERIC7_LINE, mask_sh),\ + SE_SF(HDMI_TB_ENC_GENERIC_PACKET8_9_LINE, HDMI_GENERIC8_LINE, mask_sh),\ + SE_SF(HDMI_TB_ENC_GENERIC_PACKET8_9_LINE, HDMI_GENERIC9_LINE, mask_sh),\ + SE_SF(HDMI_TB_ENC_GENERIC_PACKET10_11_LINE, HDMI_GENERIC10_LINE, mask_sh),\ + SE_SF(HDMI_TB_ENC_GENERIC_PACKET10_11_LINE, HDMI_GENERIC11_LINE, mask_sh),\ + SE_SF(HDMI_TB_ENC_GENERIC_PACKET12_13_LINE, HDMI_GENERIC12_LINE, mask_sh),\ + SE_SF(HDMI_TB_ENC_GENERIC_PACKET12_13_LINE, HDMI_GENERIC13_LINE, mask_sh),\ + SE_SF(HDMI_TB_ENC_GENERIC_PACKET14_LINE, HDMI_GENERIC14_LINE, mask_sh),\ + SE_SF(HDMI_TB_ENC_ACR_PACKET_CONTROL, HDMI_ACR_AUTO_SEND, mask_sh),\ + SE_SF(HDMI_TB_ENC_ACR_PACKET_CONTROL, HDMI_ACR_SOURCE, mask_sh),\ + SE_SF(HDMI_TB_ENC_ACR_PACKET_CONTROL, HDMI_ACR_AUDIO_PRIORITY, mask_sh),\ + SE_SF(HDMI_TB_ENC_ACR_32_0, HDMI_ACR_CTS_32, mask_sh),\ + SE_SF(HDMI_TB_ENC_ACR_32_1, HDMI_ACR_N_32, mask_sh),\ + SE_SF(HDMI_TB_ENC_ACR_44_0, HDMI_ACR_CTS_44, mask_sh),\ + SE_SF(HDMI_TB_ENC_ACR_44_1, HDMI_ACR_N_44, mask_sh),\ + SE_SF(HDMI_TB_ENC_ACR_48_0, HDMI_ACR_CTS_48, mask_sh),\ + SE_SF(HDMI_TB_ENC_ACR_48_1, HDMI_ACR_N_48, mask_sh),\ + SE_SF(HDMI_TB_ENC_CRC_CNTL, HDMI_CRC_EN, mask_sh),\ + SE_SF(HDMI_TB_ENC_CRC_CNTL, HDMI_CRC_CONT_EN, mask_sh) + +#define DCN3_0_HPO_STREAM_ENC_MASK_SH_LIST(mask_sh)\ + DCN3_0_HDMI_STREAM_ENC_MASK_SH_LIST(mask_sh),\ + DCN3_0_HDMI_TB_ENC_MASK_SH_LIST(mask_sh) + + + + +#define DCN3_HDMI_TB_ENC_REG_FIELD_LIST(type) \ + type HDMI_TB_ENC_EN;\ + type HDMI_RESET;\ + type HDMI_RESET_DONE;\ + type HDMI_STREAM_ENC_CLOCK_EN;\ + type HDMI_STREAM_ENC_INPUT_MUX_SOURCE_SEL;\ + type HDMI_MAX_PACKETS_PER_LINE;\ + type FIFO_ENABLE;\ + type FIFO_RESET;\ + type FIFO_RESET_DONE;\ + type FIFO_PIXEL_ENCODING;\ + type FIFO_ODM_COMBINE_MODE;\ + type FIFO_DSC_MODE;\ + type FIFO_DB_DISABLE;\ + type HDMI_BORROW_MODE;\ + type HDMI_H_ACTIVE;\ + type HDMI_H_BLANK;\ + type HDMI_HC_ACTIVE;\ + type HDMI_HC_BLANK;\ + type HDMI_DB_DISABLE;\ + type HDMI_PIXEL_ENCODING;\ + type HDMI_DEEP_COLOR_DEPTH;\ + type HDMI_DEEP_COLOR_ENABLE;\ + type HDMI_ODM_COMBINE_MODE;\ + type HDMI_DSC_MODE;\ + type HDMI_GC_CONT;\ + type HDMI_GC_SEND;\ + type HDMI_ACP_SEND;\ + type HDMI_AUDIO_INFO_SEND;\ + type HDMI_AUDIO_INFO_LINE;\ + type HDMI_GC_AVMUTE;\ + type HDMI_GENERIC0_CONT;\ + type HDMI_GENERIC0_SEND;\ + type HDMI_GENERIC0_LINE;\ + type HDMI_GENERIC1_CONT;\ + type HDMI_GENERIC1_SEND;\ + type HDMI_GENERIC1_LINE;\ + type HDMI_GENERIC2_CONT;\ + type HDMI_GENERIC2_SEND;\ + type HDMI_GENERIC2_LINE;\ + type HDMI_GENERIC3_CONT;\ + type HDMI_GENERIC3_SEND;\ + type HDMI_GENERIC3_LINE;\ + type HDMI_GENERIC4_CONT;\ + type HDMI_GENERIC4_SEND;\ + type HDMI_GENERIC4_LINE;\ + type HDMI_GENERIC5_CONT;\ + type HDMI_GENERIC5_SEND;\ + type HDMI_GENERIC5_LINE;\ + type HDMI_GENERIC6_CONT;\ + type HDMI_GENERIC6_SEND;\ + type HDMI_GENERIC6_LINE;\ + type HDMI_GENERIC7_CONT;\ + type HDMI_GENERIC7_SEND;\ + type HDMI_GENERIC7_LINE;\ + type HDMI_GENERIC8_CONT;\ + type HDMI_GENERIC8_SEND;\ + type HDMI_GENERIC8_LINE;\ + type HDMI_GENERIC9_CONT;\ + type HDMI_GENERIC9_SEND;\ + type HDMI_GENERIC9_LINE;\ + type HDMI_GENERIC10_CONT;\ + type HDMI_GENERIC10_SEND;\ + type HDMI_GENERIC10_LINE;\ + type HDMI_GENERIC11_CONT;\ + type HDMI_GENERIC11_SEND;\ + type HDMI_GENERIC11_LINE;\ + type HDMI_GENERIC12_CONT;\ + type HDMI_GENERIC12_SEND;\ + type HDMI_GENERIC12_LINE;\ + type HDMI_GENERIC13_CONT;\ + type HDMI_GENERIC13_SEND;\ + type HDMI_GENERIC13_LINE;\ + type HDMI_GENERIC14_CONT;\ + type HDMI_GENERIC14_SEND;\ + type HDMI_GENERIC14_LINE;\ + type HDMI_ACR_AUTO_SEND;\ + type HDMI_ACR_SOURCE;\ + type HDMI_ACR_AUDIO_PRIORITY;\ + type HDMI_ACR_CTS_32;\ + type HDMI_ACR_N_32;\ + type HDMI_ACR_CTS_44;\ + type HDMI_ACR_N_44;\ + type HDMI_ACR_CTS_48;\ + type HDMI_ACR_N_48;\ + type HDMI_CRC_EN;\ + type HDMI_CRC_CONT_EN;\ + type METADATA_HUBP_REQUESTOR_ID;\ + type METADATA_ENGINE_EN;\ + type METADATA_STREAM_TYPE;\ + type HDMI_METADATA_PACKET_ENABLE;\ + type HDMI_METADATA_PACKET_LINE_REFERENCE;\ + type HDMI_METADATA_PACKET_MISSED;\ + type HDMI_METADATA_PACKET_LINE;\ + type BORROWBUFFER_MEM_PWR_DIS;\ + type BORROWBUFFER_MEM_PWR_FORCE;\ + type BORROWBUFFER_MEM_PWR_STATE;\ + type BORROWBUFFER_MEM_DEFAULT_MEM_LOW_POWER_STATE + + +struct dcn30_hpo_frl_stream_encoder_shift { + DCN3_HDMI_TB_ENC_REG_FIELD_LIST(uint8_t); +}; + +struct dcn30_hpo_frl_stream_encoder_mask { + DCN3_HDMI_TB_ENC_REG_FIELD_LIST(uint32_t); +}; + +struct dcn30_hpo_frl_stream_encoder { + struct hpo_frl_stream_encoder base; + const struct dcn30_hpo_frl_stream_enc_registers *regs; + const struct dcn30_hpo_frl_stream_encoder_shift *hpo_se_shift; + const struct dcn30_hpo_frl_stream_encoder_mask *hpo_se_mask; +}; + +void hpo_enc3_enable( + struct hpo_frl_stream_encoder *enc, + int otg_inst); + +void hpo_enc3_unblank( + struct hpo_frl_stream_encoder *enc, + int otg_inst); + +void hpo_enc3_read_state( + struct hpo_frl_stream_encoder *enc, + struct hpo_frl_stream_encoder_state *state); + +bool hpo_enc3_fifo_odm_enabled( + struct hpo_frl_stream_encoder *enc); + +void hpo_enc3_blank( + struct hpo_frl_stream_encoder *enc); + +void hpo_enc3_set_hdmi_stream_attribute( + struct hpo_frl_stream_encoder *enc, + struct dc_crtc_timing *crtc_timing, + struct frl_borrow_params *borrow_params, + int odm_combine_num_segments); + +void hpo_enc3_update_hdmi_info_packet( + struct dcn30_hpo_frl_stream_encoder *enc3, + uint32_t packet_index, + const struct dc_info_packet *info_packet); + +void hpo_enc3_update_hdmi_info_packets( + struct hpo_frl_stream_encoder *enc, + const struct encoder_info_frame *info_frame); + +void hpo_enc3_hdmi_set_dsc_config( + struct hpo_frl_stream_encoder *enc, + struct dc_crtc_timing *timing, + uint8_t *dsc_packed_pps); + +void hpo_enc3_stop_hdmi_info_packets( + struct hpo_frl_stream_encoder *enc); + +void hpo_enc3_setup_hdmi_audio( + struct hpo_frl_stream_encoder *enc, + const struct audio_crtc_info *crtc_info); + +void hpo_enc3_hdmi_audio_setup( + struct hpo_frl_stream_encoder *enc, + unsigned int az_inst, + struct audio_info *info, + struct audio_crtc_info *audio_crtc_info); + +void hpo_enc3_hdmi_audio_disable( + struct hpo_frl_stream_encoder *enc); + +void hpo_enc3_audio_mute_control( + struct hpo_frl_stream_encoder *enc, + bool mute); + +void enc3_stream_encoder_set_avmute( + struct hpo_frl_stream_encoder *enc, + bool enable); + +bool hpo_enc3_validate_hdmi_frl_output( + struct hpo_frl_stream_encoder *enc, + const struct dc_crtc_timing *timing, + const struct audio_check *audio, + struct dc_hdmi_frl_link_settings *frl_link_settings, + unsigned int dsc_max_rate); + +void hpo_enc3_set_dynamic_metadata( + struct hpo_frl_stream_encoder *enc, + bool enable_dme, + uint32_t hubp_requestor_id, + enum dynamic_metadata_mode dmdata_mode); + +void dcn30_hpo_frl_stream_encoder_construct( + struct dcn30_hpo_frl_stream_encoder *enc3, + struct dc_context *ctx, + struct dc_bios *bp, + enum engine_id eng_id, + struct vpg *vpg, + struct afmt *afmt, + const struct dcn30_hpo_frl_stream_enc_registers *regs, + const struct dcn30_hpo_frl_stream_encoder_shift *hpo_se_shift, + const struct dcn30_hpo_frl_stream_encoder_mask *hpo_se_mask); + +void convert_dc_info_packet_to_128( + const struct dc_info_packet *info_packet, + struct dc_info_packet_128 *info_packet_128); + +#endif 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 new file mode 100644 index 0000000000000..28cb14dc87b09 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/hpo/dcn401/dcn401_hpo_frl_stream_encoder.c @@ -0,0 +1,907 @@ +/* + * Copyright 2019 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + + +#include "dc_bios_types.h" +#include "core_types.h" +#include "dcn401_hpo_frl_stream_encoder.h" +#include "dcn30/dcn30_hpo_frl_stream_encoder.h" +#include "reg_helper.h" +#include "hw_shared.h" +#include "dcn_calc_math.h" +#include "dml/dcn30/dcn30_fpu.h" + +#undef DC_LOGGER +#define DC_LOGGER \ + enc401->base.ctx->logger + +#define DTRACE(str, ...) {DC_LOG_HDMI_FRL(str, ##__VA_ARGS__); } + +#define DEBUG_FRL_CAP_CHK 1 + +#define REG(reg)\ + (enc401->regs->reg) + +#undef FN +#define FN(reg_name, field_name) \ + enc401->hpo_se_shift->field_name, enc401->hpo_se_mask->field_name + + +#define CTX \ + enc401->base.ctx + + +#define VBI_LINE_0 0 + +void hpo_enc401_enable( + struct hpo_frl_stream_encoder *enc, + int otg_inst) +{ + struct dcn401_hpo_frl_stream_encoder *enc401 = DCN401_HPO_FRL_STRENC_FROM_HPO_FRL_STRENC(enc); + + DC_LOG_DEBUG("Entering [%s]\n", __func__); + + /* Enable DISPCLK, SOCCLK, and HDMISTREAMCLK */ + REG_UPDATE(HDMI_STREAM_ENC_CLOCK_CONTROL, HDMI_STREAM_ENC_CLOCK_EN, 1); + + /* Reset */ + REG_UPDATE_2(HDMI_TB_ENC_CONTROL, + HDMI_RESET, 1, + HDMI_TB_ENC_EN, 0); + REG_WAIT(HDMI_TB_ENC_CONTROL, HDMI_RESET_DONE, + 1, 10, 100); + REG_UPDATE(HDMI_TB_ENC_CONTROL, + HDMI_RESET, 0); + + /* FOR DEBUG: enable CRC */ + REG_UPDATE_2(HDMI_TB_ENC_CRC_CNTL, + HDMI_CRC_EN, 1, + HDMI_CRC_CONT_EN, 1); + +// REG_UPDATE(HDMI_STREAM_ENC_CLOCK_RAMP_ADJUSTER_FIFO_STATUS_CONTROL2, FIFO_DB_DISABLE, 1); + /* TODO: confirm if need to set HDMI_DB_DISABLE -- HW team only setting FIFO_DB_DISABLE */ + REG_UPDATE(HDMI_TB_ENC_DB_CONTROL, HDMI_DB_DISABLE, 1); + + /* Set the input mux to select OTG source */ + REG_UPDATE(HDMI_STREAM_ENC_INPUT_MUX_CONTROL, HDMI_STREAM_ENC_INPUT_MUX_SOURCE_SEL, otg_inst); + + DC_LOG_DEBUG("Exiting [%s]\n", __func__); +} + +void hpo_enc401_unblank(struct hpo_frl_stream_encoder *enc, int otg_inst) +{ + (void)otg_inst; + struct dcn401_hpo_frl_stream_encoder *enc401 = DCN401_HPO_FRL_STRENC_FROM_HPO_FRL_STRENC(enc); + + DC_LOG_HDMI_FRL("Entering [%s]\n", __func__); + + /*make sure FIFO_VIDEO_STREAM_ACTIVE =1*/ + REG_UPDATE(HDMI_STREAM_ENC_CLOCK_RAMP_ADJUSTER_FIFO_STATUS_CONTROL0, + FIFO_ENABLE, 0); + + /* Reset */ + REG_UPDATE(HDMI_STREAM_ENC_CLOCK_RAMP_ADJUSTER_FIFO_STATUS_CONTROL0, + FIFO_RESET, 1); + REG_WAIT(HDMI_STREAM_ENC_CLOCK_RAMP_ADJUSTER_FIFO_STATUS_CONTROL0, FIFO_RESET_DONE, + 1, 10, 1000); + REG_UPDATE(HDMI_STREAM_ENC_CLOCK_RAMP_ADJUSTER_FIFO_STATUS_CONTROL0, + FIFO_RESET, 0); + REG_WAIT(HDMI_STREAM_ENC_CLOCK_RAMP_ADJUSTER_FIFO_STATUS_CONTROL0, FIFO_RESET_DONE, + 0, 10, 1000); + + /* Enable HDMI Tribyte Encoder */ + REG_UPDATE(HDMI_TB_ENC_CONTROL, + HDMI_TB_ENC_EN, 1); + + /* Enable Clock Ramp Adjuster FIFO */ + REG_UPDATE(HDMI_STREAM_ENC_CLOCK_RAMP_ADJUSTER_FIFO_STATUS_CONTROL0, + FIFO_ENABLE, 1); + + DC_LOG_HDMI_FRL("Exiting [%s]\n", __func__); +} + +void hpo_enc401_blank(struct hpo_frl_stream_encoder *enc) +{ + struct dcn401_hpo_frl_stream_encoder *enc401 = DCN401_HPO_FRL_STRENC_FROM_HPO_FRL_STRENC(enc); + + /* Disable Clock Ramp Adjuster FIFO */ + REG_UPDATE(HDMI_STREAM_ENC_CLOCK_RAMP_ADJUSTER_FIFO_STATUS_CONTROL0, + FIFO_ENABLE, 0); + + /* Disable HDMI Tribyte Encoder */ + REG_UPDATE(HDMI_TB_ENC_CONTROL, + HDMI_TB_ENC_EN, 0); + + /* Disable DISPCLK, SOCCLK, and HDMISTREAMCLK */ + REG_UPDATE(HDMI_STREAM_ENC_CLOCK_CONTROL, + HDMI_STREAM_ENC_CLOCK_EN, 0); +} + +void hpo_enc401_read_state( + struct hpo_frl_stream_encoder *enc, + struct hpo_frl_stream_encoder_state *state) +{ + int pixel_encoding; + int color_depth; +// int odm_combine; + struct dcn401_hpo_frl_stream_encoder *enc401 = DCN401_HPO_FRL_STRENC_FROM_HPO_FRL_STRENC(enc); + + ASSERT(state); + + REG_GET(HDMI_TB_ENC_CONTROL, + HDMI_TB_ENC_EN, &state->stream_enc_enabled); + + REG_GET(HDMI_STREAM_ENC_INPUT_MUX_CONTROL, + HDMI_STREAM_ENC_INPUT_MUX_SOURCE_SEL, &state->otg_inst); + + REG_GET_2(HDMI_TB_ENC_PIXEL_FORMAT, + HDMI_PIXEL_ENCODING, &pixel_encoding, + HDMI_DEEP_COLOR_DEPTH, &color_depth); + +// REG_GET(HDMI_STREAM_ENC_CLOCK_RAMP_ADJUSTER_FIFO_STATUS_CONTROL0, + // FIFO_ODM_COMBINE_MODE, &odm_combine); + + REG_GET_2(HDMI_TB_ENC_H_ACTIVE_BLANK, + HDMI_H_ACTIVE, &state->h_active, + HDMI_H_BLANK, &state->h_blank); + + REG_GET(HDMI_TB_ENC_MODE, + HDMI_BORROW_MODE, &state->borrow_mode); + + if (pixel_encoding == 0) + state->pixel_format = PIXEL_ENCODING_YCBCR444; + else if (pixel_encoding == 1) + state->pixel_format = PIXEL_ENCODING_YCBCR422; + else + state->pixel_format = PIXEL_ENCODING_YCBCR420; + + if (color_depth == 0) + state->color_depth = 8; + else if (color_depth == 1) + state->color_depth = 10; + else + state->color_depth = 12; + +// state->num_odm_segments = odm_combine + 1; +} + +/* setup stream encoder in hdmi mode */ +/* Precondition: link is trained */ +void hpo_enc401_set_hdmi_stream_attribute( + struct hpo_frl_stream_encoder *enc, + struct dc_crtc_timing *crtc_timing, + struct frl_borrow_params *borrow_params, + int odm_combine_num_segments) +{ + (void)odm_combine_num_segments; + uint32_t h_active; + uint32_t h_blank; + struct dcn401_hpo_frl_stream_encoder *enc401 = DCN401_HPO_FRL_STRENC_FROM_HPO_FRL_STRENC(enc); + + DC_LOG_DEBUG("Entering [%s]\n", __func__); + + /* Configure pixel encoding */ + switch (crtc_timing->pixel_encoding) { + case PIXEL_ENCODING_YCBCR422: + REG_UPDATE(HDMI_TB_ENC_PIXEL_FORMAT, + HDMI_PIXEL_ENCODING, 1); + REG_UPDATE(HDMI_STREAM_ENC_CLOCK_RAMP_ADJUSTER_FIFO_STATUS_CONTROL0, + FIFO_UNCOMPRESSED_PIXEL_FORMAT, 0); + break; + case PIXEL_ENCODING_YCBCR420: + REG_UPDATE(HDMI_TB_ENC_PIXEL_FORMAT, + HDMI_PIXEL_ENCODING, 2); + REG_UPDATE(HDMI_STREAM_ENC_CLOCK_RAMP_ADJUSTER_FIFO_STATUS_CONTROL0, + FIFO_UNCOMPRESSED_PIXEL_FORMAT, 1); + break; + default: + REG_UPDATE(HDMI_TB_ENC_PIXEL_FORMAT, + HDMI_PIXEL_ENCODING, 0); + REG_UPDATE(HDMI_STREAM_ENC_CLOCK_RAMP_ADJUSTER_FIFO_STATUS_CONTROL0, + FIFO_UNCOMPRESSED_PIXEL_FORMAT, 0); + break; + } + + /* Configure color depth */ + switch (crtc_timing->display_color_depth) { + case COLOR_DEPTH_888: + REG_UPDATE_2(HDMI_TB_ENC_PIXEL_FORMAT, + HDMI_DEEP_COLOR_DEPTH, 0, + HDMI_DEEP_COLOR_ENABLE, 0); + break; + case COLOR_DEPTH_101010: + if (crtc_timing->pixel_encoding == PIXEL_ENCODING_YCBCR422) { + REG_UPDATE_2(HDMI_TB_ENC_PIXEL_FORMAT, + HDMI_DEEP_COLOR_DEPTH, 1, + HDMI_DEEP_COLOR_ENABLE, 0); + } else { + REG_UPDATE_2(HDMI_TB_ENC_PIXEL_FORMAT, + HDMI_DEEP_COLOR_DEPTH, 1, + HDMI_DEEP_COLOR_ENABLE, 1); + } + break; + case COLOR_DEPTH_121212: + if (crtc_timing->pixel_encoding == PIXEL_ENCODING_YCBCR422) { + REG_UPDATE_2(HDMI_TB_ENC_PIXEL_FORMAT, + HDMI_DEEP_COLOR_DEPTH, 2, + HDMI_DEEP_COLOR_ENABLE, 0); + } else { + REG_UPDATE_2(HDMI_TB_ENC_PIXEL_FORMAT, + HDMI_DEEP_COLOR_DEPTH, 2, + HDMI_DEEP_COLOR_ENABLE, 1); + } + break; + default: + 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); + } + + /* Configure horizontal active and blank size */ + h_active = crtc_timing->h_addressable + crtc_timing->h_border_left + crtc_timing->h_border_right; + h_blank = crtc_timing->h_total - h_active; + + if (crtc_timing->pixel_encoding == PIXEL_ENCODING_YCBCR420 || + crtc_timing->pixel_encoding == PIXEL_ENCODING_YCBCR422) { + h_active /= 2; + h_blank /= 2; + } + + + REG_SET_2(HDMI_TB_ENC_H_ACTIVE_BLANK, 0, + HDMI_H_ACTIVE, h_active, + HDMI_H_BLANK, h_blank); + + /* Configure borrow parameters */ + REG_UPDATE(HDMI_TB_ENC_MODE, + HDMI_BORROW_MODE, borrow_params->borrow_mode); + REG_UPDATE(HDMI_TB_ENC_PACKET_CONTROL, + HDMI_MAX_PACKETS_PER_LINE, borrow_params->audio_packets_line); + REG_SET_2(HDMI_TB_ENC_HC_ACTIVE_BLANK, 0, + HDMI_HC_ACTIVE, borrow_params->hc_active_target, + HDMI_HC_BLANK, borrow_params->hc_blank_target); + + /* Enable transmission of General Control packet on every frame */ + REG_UPDATE_2(HDMI_TB_ENC_VBI_PACKET_CONTROL1, + HDMI_GC_CONT, 1, + HDMI_GC_SEND, 1); + + /* Disable Audio Content Protection packet transmission */ + /* TODO: review if this needs to be here */ + REG_UPDATE(HDMI_TB_ENC_VBI_PACKET_CONTROL1, HDMI_ACP_SEND, 0); + + + /* Enable Audio InfoFrame packet transmission. */ + REG_UPDATE(HDMI_TB_ENC_VBI_PACKET_CONTROL1, HDMI_AUDIO_INFO_SEND, 1); + + /* update double-buffered AUDIO_INFO registers immediately */ + if (enc->afmt && enc->afmt->funcs->audio_info_immediate_update) + enc->afmt->funcs->audio_info_immediate_update(enc->afmt); + + /* Select line number on which to send Audio InfoFrame packets */ + REG_UPDATE(HDMI_TB_ENC_VBI_PACKET_CONTROL1, HDMI_AUDIO_INFO_LINE, + VBI_LINE_0 + 2); + + /* set HDMI GC AVMUTE */ + REG_UPDATE(HDMI_TB_ENC_GC_CONTROL, HDMI_GC_AVMUTE, 0); + + DC_LOG_DEBUG("Exiting [%s]\n", __func__); +} + +void hpo_enc401_update_hdmi_info_packet( + struct dcn401_hpo_frl_stream_encoder *enc401, + uint32_t packet_index, + const struct dc_info_packet *info_packet) +{ + uint32_t cont, send, line; + + if (info_packet->valid) { + enc401->base.vpg->funcs->update_generic_info_packet( + enc401->base.vpg, + packet_index, + info_packet, + true); + + /* enable transmission of packet(s) - + * packet transmission begins on the next frame + */ + cont = 1; + /* send packet(s) every frame */ + send = 1; + /* select line number to send packets on */ + /* TODO: check if line 2 is correct */ + line = 2; + } else { + cont = 0; + send = 0; + line = 0; + } + + /* TODO: set bit to indicate if packet is Extended Metadata Packet. */ + /* TODO: In DCN3, there are 0-14 generic packets */ + + /* choose which generic packet control to use */ + switch (packet_index) { + case 0: + REG_UPDATE_2(HDMI_TB_ENC_GENERIC_PACKET_CONTROL0, + HDMI_GENERIC0_CONT, cont, + HDMI_GENERIC0_SEND, send); + REG_UPDATE(HDMI_TB_ENC_GENERIC_PACKET0_1_LINE, + HDMI_GENERIC0_LINE, line); + break; + case 1: + REG_UPDATE_2(HDMI_TB_ENC_GENERIC_PACKET_CONTROL0, + HDMI_GENERIC1_CONT, cont, + HDMI_GENERIC1_SEND, send); + REG_UPDATE(HDMI_TB_ENC_GENERIC_PACKET0_1_LINE, + HDMI_GENERIC1_LINE, line); + break; + case 2: + REG_UPDATE_2(HDMI_TB_ENC_GENERIC_PACKET_CONTROL0, + HDMI_GENERIC2_CONT, cont, + HDMI_GENERIC2_SEND, send); + REG_UPDATE(HDMI_TB_ENC_GENERIC_PACKET2_3_LINE, + HDMI_GENERIC2_LINE, line); + break; + case 3: + REG_UPDATE_2(HDMI_TB_ENC_GENERIC_PACKET_CONTROL0, + HDMI_GENERIC3_CONT, cont, + HDMI_GENERIC3_SEND, send); + REG_UPDATE(HDMI_TB_ENC_GENERIC_PACKET2_3_LINE, + HDMI_GENERIC3_LINE, line); + break; + case 4: + REG_UPDATE_2(HDMI_TB_ENC_GENERIC_PACKET_CONTROL0, + HDMI_GENERIC4_CONT, cont, + HDMI_GENERIC4_SEND, send); + REG_UPDATE(HDMI_TB_ENC_GENERIC_PACKET4_5_LINE, + HDMI_GENERIC4_LINE, line); + break; + case 5: + REG_UPDATE_2(HDMI_TB_ENC_GENERIC_PACKET_CONTROL0, + HDMI_GENERIC5_CONT, cont, + HDMI_GENERIC5_SEND, send); + REG_UPDATE(HDMI_TB_ENC_GENERIC_PACKET4_5_LINE, + HDMI_GENERIC5_LINE, line); + break; + case 6: + REG_UPDATE_2(HDMI_TB_ENC_GENERIC_PACKET_CONTROL0, + HDMI_GENERIC6_CONT, cont, + HDMI_GENERIC6_SEND, send); + REG_UPDATE(HDMI_TB_ENC_GENERIC_PACKET6_7_LINE, + HDMI_GENERIC6_LINE, line); + break; + case 7: + REG_UPDATE_2(HDMI_TB_ENC_GENERIC_PACKET_CONTROL0, + HDMI_GENERIC7_CONT, cont, + HDMI_GENERIC7_SEND, send); + REG_UPDATE(HDMI_TB_ENC_GENERIC_PACKET6_7_LINE, + HDMI_GENERIC7_LINE, line); + break; + case 8: + REG_UPDATE_2(HDMI_TB_ENC_GENERIC_PACKET_CONTROL1, + HDMI_GENERIC8_CONT, cont, + HDMI_GENERIC8_SEND, send); + REG_UPDATE(HDMI_TB_ENC_GENERIC_PACKET8_9_LINE, + HDMI_GENERIC8_LINE, line); + break; + case 9: + REG_UPDATE_2(HDMI_TB_ENC_GENERIC_PACKET_CONTROL1, + HDMI_GENERIC9_CONT, cont, + HDMI_GENERIC9_SEND, send); + REG_UPDATE(HDMI_TB_ENC_GENERIC_PACKET8_9_LINE, + HDMI_GENERIC9_LINE, line); + break; + case 10: + REG_UPDATE_2(HDMI_TB_ENC_GENERIC_PACKET_CONTROL1, + HDMI_GENERIC10_CONT, cont, + HDMI_GENERIC10_SEND, send); + REG_UPDATE(HDMI_TB_ENC_GENERIC_PACKET10_11_LINE, + HDMI_GENERIC10_LINE, line); + break; + case 11: + REG_UPDATE_2(HDMI_TB_ENC_GENERIC_PACKET_CONTROL1, + HDMI_GENERIC11_CONT, cont, + HDMI_GENERIC11_SEND, send); + REG_UPDATE(HDMI_TB_ENC_GENERIC_PACKET10_11_LINE, + HDMI_GENERIC11_LINE, line); + break; + case 12: + REG_UPDATE_2(HDMI_TB_ENC_GENERIC_PACKET_CONTROL1, + HDMI_GENERIC12_CONT, cont, + HDMI_GENERIC12_SEND, send); + REG_UPDATE(HDMI_TB_ENC_GENERIC_PACKET12_13_LINE, + HDMI_GENERIC12_LINE, line); + break; + case 13: + REG_UPDATE_2(HDMI_TB_ENC_GENERIC_PACKET_CONTROL1, + HDMI_GENERIC13_CONT, cont, + HDMI_GENERIC13_SEND, send); + REG_UPDATE(HDMI_TB_ENC_GENERIC_PACKET12_13_LINE, + HDMI_GENERIC13_LINE, line); + break; + case 14: + REG_UPDATE_2(HDMI_TB_ENC_GENERIC_PACKET_CONTROL1, + HDMI_GENERIC14_CONT, cont, + HDMI_GENERIC14_SEND, send); + REG_UPDATE(HDMI_TB_ENC_GENERIC_PACKET14_LINE, + HDMI_GENERIC14_LINE, line); + break; + default: + /* invalid HW packet index */ + DC_LOG_WARNING( + "Invalid HW packet index: %s()\n", + __func__); + return; + } +} + +void hpo_enc401_update_hdmi_info_packets(struct hpo_frl_stream_encoder *enc, + const struct encoder_info_frame *info_frame) +{ + struct dcn401_hpo_frl_stream_encoder *enc401 = DCN401_HPO_FRL_STRENC_FROM_HPO_FRL_STRENC(enc); + + hpo_enc401_update_hdmi_info_packet(enc401, 0, &info_frame->avi); + hpo_enc401_update_hdmi_info_packet(enc401, 1, &info_frame->vendor); + hpo_enc401_update_hdmi_info_packet(enc401, 2, &info_frame->gamut); + hpo_enc401_update_hdmi_info_packet(enc401, 3, &info_frame->spd); + hpo_enc401_update_hdmi_info_packet(enc401, 4, &info_frame->hdrsmd); + + /* 5-10 used by dsc */ + hpo_enc401_update_hdmi_info_packet(enc401, 11, &info_frame->hfvsif); + hpo_enc401_update_hdmi_info_packet(enc401, 12, &info_frame->vtem); +} + +void hpo_enc401_hdmi_set_dsc_config( + struct hpo_frl_stream_encoder *enc, + struct dc_crtc_timing *timing, + uint8_t *dsc_packed_pps) +{ + struct dcn401_hpo_frl_stream_encoder *enc401 = DCN401_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; + } + + switch (dsc_mode) { + case OPTC_DSC_DISABLED: + REG_UPDATE_2(HDMI_STREAM_ENC_CLOCK_RAMP_ADJUSTER_FIFO_STATUS_CONTROL0, + FIFO_PIXEL_ENCODING_TYPE, 0, + FIFO_COMPRESSED_PIXEL_FORMAT, 0); + break; + case OPTC_DSC_ENABLED_NATIVE_SUBSAMPLED: + REG_UPDATE_2(HDMI_STREAM_ENC_CLOCK_RAMP_ADJUSTER_FIFO_STATUS_CONTROL0, + FIFO_PIXEL_ENCODING_TYPE, 1, + FIFO_COMPRESSED_PIXEL_FORMAT, 1); + break; + case OPTC_DSC_ENABLED_444: + REG_UPDATE_2(HDMI_STREAM_ENC_CLOCK_RAMP_ADJUSTER_FIFO_STATUS_CONTROL0, + FIFO_PIXEL_ENCODING_TYPE, 1, + FIFO_COMPRESSED_PIXEL_FORMAT, 0); + break; + } + + 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}; + /* Need to find the padded h_total to recover the expected h_back*/ + uint32_t h_active_padding = timing->h_addressable % timing->dsc_cfg.num_slices_h; + if (h_active_padding != 0) + h_active_padding = timing->dsc_cfg.num_slices_h - h_active_padding; + /* if YCBCR420, ensure slice width is even */ + uint32_t slice_width = (timing->h_addressable + h_active_padding) / timing->dsc_cfg.num_slices_h; + if (timing->pixel_encoding == PIXEL_ENCODING_YCBCR420 && + slice_width % 2 != 0) + h_active_padding += timing->dsc_cfg.num_slices_h; + uint32_t dsc_pic_width = timing->h_addressable + timing->h_border_left + timing->h_border_right + h_active_padding; + uint32_t h_back = timing->h_total - dsc_pic_width - timing->h_sync_width - timing->h_front_porch + h_active_padding; + /* HCactivebytes = Slices * ceil(SliceWidth * bpp/8) + * Since bpp is stored as 16x actual value do (sliceWidth * bpp + 127) / 128 to ceil + */ + 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 + 127) / 128); + + /* 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_enc401_update_hdmi_info_packet(enc401, 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_enc401_update_hdmi_info_packet(enc401, 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_enc401_update_hdmi_info_packet(enc401, 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_enc401_update_hdmi_info_packet(enc401, 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_enc401_update_hdmi_info_packet(enc401, 5, &emp_packet); + } +} + +void hpo_enc401_stop_hdmi_info_packets( + struct hpo_frl_stream_encoder *enc) +{ + struct dcn401_hpo_frl_stream_encoder *enc401 = DCN401_HPO_FRL_STRENC_FROM_HPO_FRL_STRENC(enc); + + /* TODO: should also set extended metadata packet bit back to 0? */ + + /* stop generic packets 0,1 on HDMI */ + REG_SET_4(HDMI_TB_ENC_GENERIC_PACKET_CONTROL0, 0, + HDMI_GENERIC0_CONT, 0, + HDMI_GENERIC0_SEND, 0, + HDMI_GENERIC1_CONT, 0, + HDMI_GENERIC1_SEND, 0); + REG_SET_2(HDMI_TB_ENC_GENERIC_PACKET0_1_LINE, 0, + HDMI_GENERIC0_LINE, 0, + HDMI_GENERIC1_LINE, 0); + + /* stop generic packets 2,3 on HDMI */ + REG_SET_4(HDMI_TB_ENC_GENERIC_PACKET_CONTROL0, 0, + HDMI_GENERIC2_CONT, 0, + HDMI_GENERIC2_SEND, 0, + HDMI_GENERIC3_CONT, 0, + HDMI_GENERIC3_SEND, 0); + REG_SET_2(HDMI_TB_ENC_GENERIC_PACKET2_3_LINE, 0, + HDMI_GENERIC2_LINE, 0, + HDMI_GENERIC3_LINE, 0); + + /* stop generic packets 4,5 on HDMI */ + REG_SET_4(HDMI_TB_ENC_GENERIC_PACKET_CONTROL0, 0, + HDMI_GENERIC4_CONT, 0, + HDMI_GENERIC4_SEND, 0, + HDMI_GENERIC5_CONT, 0, + HDMI_GENERIC5_SEND, 0); + REG_SET_2(HDMI_TB_ENC_GENERIC_PACKET4_5_LINE, 0, + HDMI_GENERIC4_LINE, 0, + HDMI_GENERIC5_LINE, 0); + + /* stop generic packets 6,7 on HDMI */ + REG_SET_4(HDMI_TB_ENC_GENERIC_PACKET_CONTROL0, 0, + HDMI_GENERIC6_CONT, 0, + HDMI_GENERIC6_SEND, 0, + HDMI_GENERIC7_CONT, 0, + HDMI_GENERIC7_SEND, 0); + REG_SET_2(HDMI_TB_ENC_GENERIC_PACKET6_7_LINE, 0, + HDMI_GENERIC6_LINE, 0, + HDMI_GENERIC7_LINE, 0); + + /* stop generic packets 8,9 on HDMI */ + REG_SET_4(HDMI_TB_ENC_GENERIC_PACKET_CONTROL1, 0, + HDMI_GENERIC8_CONT, 0, + HDMI_GENERIC8_SEND, 0, + HDMI_GENERIC9_CONT, 0, + HDMI_GENERIC9_SEND, 0); + REG_SET_2(HDMI_TB_ENC_GENERIC_PACKET8_9_LINE, 0, + HDMI_GENERIC8_LINE, 0, + HDMI_GENERIC9_LINE, 0); + + /* stop generic packets 10,11 on HDMI */ + REG_SET_4(HDMI_TB_ENC_GENERIC_PACKET_CONTROL1, 0, + HDMI_GENERIC10_CONT, 0, + HDMI_GENERIC10_SEND, 0, + HDMI_GENERIC11_CONT, 0, + HDMI_GENERIC11_SEND, 0); + REG_SET_2(HDMI_TB_ENC_GENERIC_PACKET10_11_LINE, 0, + HDMI_GENERIC10_LINE, 0, + HDMI_GENERIC11_LINE, 0); + + /* stop generic packets 12,13 on HDMI */ + REG_SET_4(HDMI_TB_ENC_GENERIC_PACKET_CONTROL1, 0, + HDMI_GENERIC12_CONT, 0, + HDMI_GENERIC12_SEND, 0, + HDMI_GENERIC13_CONT, 0, + HDMI_GENERIC13_SEND, 0); + REG_SET_2(HDMI_TB_ENC_GENERIC_PACKET12_13_LINE, 0, + HDMI_GENERIC12_LINE, 0, + HDMI_GENERIC13_LINE, 0); + + /* stop generic packets 14 on HDMI */ + REG_SET_2(HDMI_TB_ENC_GENERIC_PACKET_CONTROL1, 0, + HDMI_GENERIC14_CONT, 0, + HDMI_GENERIC14_SEND, 0); + REG_SET(HDMI_TB_ENC_GENERIC_PACKET14_LINE, 0, + HDMI_GENERIC14_LINE, 0); + +} + +//Covered both, rounding up or rounding down from FRL Link Rate /18. +static const struct frl_audio_clock_info frl_audio_clock_info_table[10] = { + {166666, 4224, 171875, 5292, 156250, 5760, 156250}, + {166667, 4224, 171875, 5292, 156250, 5760, 156250}, + {333333, 4032, 328125, 5292, 312500, 6048, 328125}, + {333334, 4032, 328125, 5292, 312500, 6048, 328125}, + {444444, 4032, 437500, 3969, 312500, 6048, 437500}, + {444445, 4032, 437500, 3969, 312500, 6048, 437500}, + {555555, 3456, 468750, 3969, 390625, 5184, 468750}, + {555556, 3456, 468750, 3969, 390625, 5184, 468750}, + {666666, 3072, 500000, 3969, 468750, 4752, 515625}, + {666667, 3072, 500000, 3969, 468750, 4752, 515625} +}; + +void frl_get_audio_clock_info( + enum dc_color_depth color_depth, + uint32_t frl_character_clock_kHz, + struct frl_audio_clock_info *audio_clock_info) +{ + (void)color_depth; + const struct frl_audio_clock_info *clock_info; + uint32_t index; + uint32_t audio_array_size; + + clock_info = frl_audio_clock_info_table; + audio_array_size = ARRAY_SIZE( + frl_audio_clock_info_table); + + if (clock_info != NULL) { + /* search for exact frl character clock in table */ + for (index = 0; index < audio_array_size; index++) { + if (clock_info[index].frl_character_clock_kHz > + frl_character_clock_kHz) + break; /* not match */ + else if (clock_info[index].frl_character_clock_kHz == + frl_character_clock_kHz) { + /* match found */ + *audio_clock_info = clock_info[index]; + return; + } + } + } + /*Only 3, 6, 8, 10 and 12 Gbps are used for FRL Link rates with character + *clocks of 166.667, 333.333, 444.444, 555.555 and 666.667 MHz are used + *so entry should be found in above table if no bugs */ + BREAK_TO_DEBUGGER(); +} + +void hpo_enc401_setup_hdmi_audio( + struct hpo_frl_stream_encoder *enc, + const struct audio_crtc_info *crtc_info) +{ + struct dcn401_hpo_frl_stream_encoder *enc401 = DCN401_HPO_FRL_STRENC_FROM_HPO_FRL_STRENC(enc); + struct frl_audio_clock_info audio_clock_info = {0}; + + DC_LOG_DEBUG("Entering [%s]\n", __func__); + + /* TODO: HDMI_AUDIO_DELAY_EN bit only in DIG -- not in HPO? */ + /* HDMI_AUDIO_PACKET_CONTROL */ + //REG_UPDATE(HDMI_AUDIO_PACKET_CONTROL, + // HDMI_AUDIO_DELAY_EN, 1); + + /* Setup audio in AFMT - program AFMT block associated with HPO */ + ASSERT (enc->afmt); + enc->afmt->funcs->setup_hdmi_audio(enc->afmt); + + /* TODO: Same programming, but using HDMI_TB_ENC register */ + /* HDMI_ACR_PACKET_CONTROL */ + REG_UPDATE_3(HDMI_TB_ENC_ACR_PACKET_CONTROL, + HDMI_ACR_AUTO_SEND, 1, + HDMI_ACR_SOURCE, 0, + HDMI_ACR_AUDIO_PRIORITY, 0); + + /* N/CTS computed relative to FRL rate instead of video rate (TMDS character clock). */ + /* Program audio clock sample/regeneration parameters */ + frl_get_audio_clock_info(crtc_info->color_depth, + crtc_info->frl_character_clock_kHz, + &audio_clock_info); + DC_LOG_HW_AUDIO( + "\n%s:Input::requested_pixel_clock_100Hz = %d" \ + "calculated_pixel_clock_100Hz = %d \n", __func__, \ + crtc_info->requested_pixel_clock_100Hz, \ + crtc_info->calculated_pixel_clock_100Hz); + + /* Same register definition, but using HDMI_TB_ENC register */ + /* HDMI_ACR_32_0__HDMI_ACR_CTS_32_MASK */ + REG_UPDATE(HDMI_TB_ENC_ACR_32_0, HDMI_ACR_CTS_32, audio_clock_info.cts_32khz); + + /* HDMI_ACR_32_1__HDMI_ACR_N_32_MASK */ + REG_UPDATE(HDMI_TB_ENC_ACR_32_1, HDMI_ACR_N_32, audio_clock_info.n_32khz); + + /* HDMI_ACR_44_0__HDMI_ACR_CTS_44_MASK */ + REG_UPDATE(HDMI_TB_ENC_ACR_44_0, HDMI_ACR_CTS_44, audio_clock_info.cts_44khz); + + /* HDMI_ACR_44_1__HDMI_ACR_N_44_MASK */ + REG_UPDATE(HDMI_TB_ENC_ACR_44_1, HDMI_ACR_N_44, audio_clock_info.n_44khz); + + /* HDMI_ACR_48_0__HDMI_ACR_CTS_48_MASK */ + REG_UPDATE(HDMI_TB_ENC_ACR_48_0, HDMI_ACR_CTS_48, audio_clock_info.cts_48khz); + + /* HDMI_ACR_48_1__HDMI_ACR_N_48_MASK */ + REG_UPDATE(HDMI_TB_ENC_ACR_48_1, HDMI_ACR_N_48, audio_clock_info.n_48khz); + + + /* TODO: HDMI_TB_ENC_ACR_PACKET_CONTROL::ACR_N_MULTIPLE + * Same register definition, but using HDMI_TB_ENC register*/ + + /* Video driver cannot know in advance which sample rate will + * be used by HD Audio driver + * HDMI_ACR_PACKET_CONTROL__HDMI_ACR_N_MULTIPLE field is + * programmed below in interrupt callback + */ + DC_LOG_DEBUG("Exiting [%s]\n", __func__); +} + +void hpo_enc401_hdmi_audio_setup( + struct hpo_frl_stream_encoder *enc, + unsigned int az_inst, + struct audio_info *info, + struct audio_crtc_info *audio_crtc_info) +{ + hpo_enc401_setup_hdmi_audio(enc, audio_crtc_info); + ASSERT (enc->afmt); + enc->afmt->funcs->se_audio_setup(enc->afmt, az_inst, info); +} + +void hpo_enc401_hdmi_audio_disable( + struct hpo_frl_stream_encoder *enc) +{ + ASSERT(enc->afmt); + if (enc->afmt->funcs->afmt_powerdown) + enc->afmt->funcs->afmt_powerdown(enc->afmt); +} + +void hpo_enc401_audio_mute_control( + struct hpo_frl_stream_encoder *enc, + bool mute) +{ + ASSERT (enc->afmt); + enc->afmt->funcs->audio_mute_control(enc->afmt, mute); +} + +void enc401_stream_encoder_set_avmute( + struct hpo_frl_stream_encoder *enc, + bool enable) +{ + struct dcn401_hpo_frl_stream_encoder *enc401 = DCN401_HPO_FRL_STRENC_FROM_HPO_FRL_STRENC(enc); + unsigned int value = enable ? 1 : 0; + + REG_UPDATE(HDMI_TB_ENC_GC_CONTROL, HDMI_GC_AVMUTE, value); +} + +/* Set Dynamic Metadata-configuration. + * enable_dme: TRUE: enables Dynamic Metadata Enfine, FALSE: disables DME + * hubp_requestor_id: HUBP physical instance that is the source of dynamic metadata + * only needs to be set when enable_dme is TRUE + * dmdata_mode: dynamic metadata packet type: DP, HDMI, or Dolby Vision + * + * Ensure the OTG master update lock is set when changing DME configuration. + */ +void hpo_enc401_set_dynamic_metadata(struct hpo_frl_stream_encoder *enc, + bool enable_dme, + uint32_t hubp_requestor_id, + enum dynamic_metadata_mode dmdata_mode) +{ + struct dcn401_hpo_frl_stream_encoder *enc401 = DCN401_HPO_FRL_STRENC_FROM_HPO_FRL_STRENC(enc); + + if (enable_dme) { + REG_UPDATE_2(DME_CONTROL, + METADATA_HUBP_REQUESTOR_ID, hubp_requestor_id, + METADATA_STREAM_TYPE, (dmdata_mode == dmdata_dolby_vision) ? 1 : 0); + + REG_UPDATE_3(HDMI_TB_ENC_METADATA_PACKET_CONTROL, + HDMI_METADATA_PACKET_ENABLE, 1, + HDMI_METADATA_PACKET_LINE_REFERENCE, 0, + HDMI_METADATA_PACKET_LINE, 2); + + REG_UPDATE(DME_CONTROL, + METADATA_ENGINE_EN, 1); + } else { + REG_UPDATE(DME_CONTROL, + METADATA_ENGINE_EN, 0); + + REG_UPDATE(HDMI_TB_ENC_METADATA_PACKET_CONTROL, + HDMI_METADATA_PACKET_ENABLE, 0); + } +} + +static const struct hpo_frl_stream_encoder_funcs dcn401_str_enc_funcs = { + .hdmi_frl_enable = hpo_enc401_enable, + .hdmi_frl_unblank = hpo_enc401_unblank, + .hdmi_frl_blank = hpo_enc401_blank, + .hdmi_frl_set_stream_attribute = hpo_enc401_set_hdmi_stream_attribute, + .validate_hdmi_frl_output = hpo_enc3_validate_hdmi_frl_output, + .update_hdmi_info_packets = hpo_enc401_update_hdmi_info_packets, + .stop_hdmi_info_packets = hpo_enc401_stop_hdmi_info_packets, + .audio_mute_control = hpo_enc401_audio_mute_control, + .hdmi_audio_setup = hpo_enc401_hdmi_audio_setup, + .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, +}; + +void dcn401_hpo_frl_stream_encoder_construct( + struct dcn401_hpo_frl_stream_encoder *enc401, + struct dc_context *ctx, + struct dc_bios *bp, + enum engine_id eng_id, + struct vpg *vpg, + struct afmt *afmt, + const struct dcn30_hpo_frl_stream_enc_registers *regs, + const struct dcn401_hpo_frl_stream_encoder_shift *hpo_se_shift, + const struct dcn401_hpo_frl_stream_encoder_mask *hpo_se_mask) +{ + enc401->base.funcs = &dcn401_str_enc_funcs; + enc401->base.ctx = ctx; + enc401->base.id = eng_id; + enc401->base.bp = bp; + enc401->base.vpg = vpg; + enc401->base.afmt = afmt; + enc401->regs = regs; + enc401->hpo_se_shift = hpo_se_shift; + enc401->hpo_se_mask = hpo_se_mask; + enc401->base.stream_enc_inst = vpg->inst; +} diff --git a/drivers/gpu/drm/amd/display/dc/hpo/dcn401/dcn401_hpo_frl_stream_encoder.h b/drivers/gpu/drm/amd/display/dc/hpo/dcn401/dcn401_hpo_frl_stream_encoder.h new file mode 100644 index 0000000000000..b47ba55b9a68b --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/hpo/dcn401/dcn401_hpo_frl_stream_encoder.h @@ -0,0 +1,335 @@ +/* + * Copyright 2023 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __DC_HPO_FRL_STREAM_ENCODER_DCN401_H__ +#define __DC_HPO_FRL_STREAM_ENCODER_DCN401_H__ + +#include "dcn30/dcn30_vpg.h" +#include "dcn30/dcn30_afmt.h" +#include "dcn30/dcn30_hpo_frl_stream_encoder.h" +#include "stream_encoder.h" +#include "dml/dml1_frl_cap_chk.h" + +#define DCN401_HPO_FRL_STRENC_FROM_HPO_FRL_STRENC(hpo_frl_stream_encoder)\ + container_of(hpo_frl_stream_encoder, struct dcn401_hpo_frl_stream_encoder, base) + +#define SE_SF(reg_name, field_name, post_fix)\ + .field_name = reg_name ## __ ## field_name ## post_fix + +#define DCN401_HDMI_STREAM_ENC_MASK_SH_LIST(mask_sh)\ + SE_SF(HDMI_STREAM_ENC_INPUT_MUX_CONTROL, HDMI_STREAM_ENC_INPUT_MUX_SOURCE_SEL, mask_sh),\ + SE_SF(HDMI_STREAM_ENC_CLOCK_RAMP_ADJUSTER_FIFO_STATUS_CONTROL0, FIFO_ENABLE, mask_sh),\ + SE_SF(HDMI_STREAM_ENC_CLOCK_RAMP_ADJUSTER_FIFO_STATUS_CONTROL0, FIFO_RESET, mask_sh),\ + SE_SF(HDMI_STREAM_ENC_CLOCK_RAMP_ADJUSTER_FIFO_STATUS_CONTROL0, FIFO_PIXEL_ENCODING_TYPE, mask_sh),\ + SE_SF(HDMI_STREAM_ENC_CLOCK_RAMP_ADJUSTER_FIFO_STATUS_CONTROL0, FIFO_UNCOMPRESSED_PIXEL_FORMAT, mask_sh),\ + SE_SF(HDMI_STREAM_ENC_CLOCK_RAMP_ADJUSTER_FIFO_STATUS_CONTROL0, FIFO_COMPRESSED_PIXEL_FORMAT, mask_sh),\ + SE_SF(HDMI_STREAM_ENC_CLOCK_RAMP_ADJUSTER_FIFO_STATUS_CONTROL0, FIFO_RESET_DONE, mask_sh),\ + SE_SF(HDMI_STREAM_ENC_CLOCK_CONTROL, HDMI_STREAM_ENC_CLOCK_EN, mask_sh),\ + SE_SF(DME0_DME_CONTROL, METADATA_HUBP_REQUESTOR_ID, mask_sh),\ + SE_SF(DME0_DME_CONTROL, METADATA_ENGINE_EN, mask_sh),\ + SE_SF(DME0_DME_CONTROL, METADATA_STREAM_TYPE, mask_sh),\ + SE_SF(HDMI_TB_ENC_METADATA_PACKET_CONTROL, HDMI_METADATA_PACKET_ENABLE, mask_sh),\ + SE_SF(HDMI_TB_ENC_METADATA_PACKET_CONTROL, HDMI_METADATA_PACKET_LINE_REFERENCE, mask_sh),\ + SE_SF(HDMI_TB_ENC_METADATA_PACKET_CONTROL, HDMI_METADATA_PACKET_MISSED, mask_sh),\ + SE_SF(HDMI_TB_ENC_METADATA_PACKET_CONTROL, HDMI_METADATA_PACKET_LINE, mask_sh) + +#define DCN401_HDMI_TB_ENC_MASK_SH_LIST(mask_sh)\ + SE_SF(HDMI_TB_ENC_CONTROL, HDMI_TB_ENC_EN, mask_sh),\ + SE_SF(HDMI_TB_ENC_CONTROL, HDMI_RESET, mask_sh),\ + SE_SF(HDMI_TB_ENC_CONTROL, HDMI_RESET_DONE, mask_sh),\ + SE_SF(HDMI_TB_ENC_MODE, HDMI_BORROW_MODE, mask_sh),\ + SE_SF(HDMI_TB_ENC_H_ACTIVE_BLANK, HDMI_H_ACTIVE, mask_sh),\ + SE_SF(HDMI_TB_ENC_H_ACTIVE_BLANK, HDMI_H_BLANK, mask_sh),\ + SE_SF(HDMI_TB_ENC_HC_ACTIVE_BLANK, HDMI_HC_ACTIVE, mask_sh),\ + SE_SF(HDMI_TB_ENC_HC_ACTIVE_BLANK, HDMI_HC_BLANK, mask_sh),\ + SE_SF(HDMI_TB_ENC_PACKET_CONTROL, HDMI_MAX_PACKETS_PER_LINE, mask_sh),\ + SE_SF(HDMI_TB_ENC_DB_CONTROL, HDMI_DB_DISABLE, mask_sh),\ + SE_SF(HDMI_TB_ENC_PIXEL_FORMAT, HDMI_PIXEL_ENCODING, mask_sh),\ + SE_SF(HDMI_TB_ENC_PIXEL_FORMAT, HDMI_DEEP_COLOR_DEPTH, mask_sh),\ + SE_SF(HDMI_TB_ENC_PIXEL_FORMAT, HDMI_DEEP_COLOR_ENABLE, mask_sh),\ + SE_SF(HDMI_TB_ENC_PIXEL_FORMAT, HDMI_DSC_MODE, mask_sh),\ + SE_SF(HDMI_TB_ENC_VBI_PACKET_CONTROL1, HDMI_GC_CONT, mask_sh),\ + SE_SF(HDMI_TB_ENC_VBI_PACKET_CONTROL1, HDMI_GC_SEND, mask_sh),\ + SE_SF(HDMI_TB_ENC_VBI_PACKET_CONTROL1, HDMI_ACP_SEND, mask_sh),\ + SE_SF(HDMI_TB_ENC_VBI_PACKET_CONTROL1, HDMI_AUDIO_INFO_SEND, mask_sh),\ + SE_SF(HDMI_TB_ENC_VBI_PACKET_CONTROL1, HDMI_AUDIO_INFO_LINE, mask_sh),\ + SE_SF(HDMI_TB_ENC_GC_CONTROL, HDMI_GC_AVMUTE, mask_sh),\ + SE_SF(HDMI_TB_ENC_GENERIC_PACKET_CONTROL0, HDMI_GENERIC0_CONT, mask_sh),\ + SE_SF(HDMI_TB_ENC_GENERIC_PACKET_CONTROL0, HDMI_GENERIC1_CONT, mask_sh),\ + SE_SF(HDMI_TB_ENC_GENERIC_PACKET_CONTROL0, HDMI_GENERIC2_CONT, mask_sh),\ + SE_SF(HDMI_TB_ENC_GENERIC_PACKET_CONTROL0, HDMI_GENERIC3_CONT, mask_sh),\ + SE_SF(HDMI_TB_ENC_GENERIC_PACKET_CONTROL0, HDMI_GENERIC4_CONT, mask_sh),\ + SE_SF(HDMI_TB_ENC_GENERIC_PACKET_CONTROL0, HDMI_GENERIC5_CONT, mask_sh),\ + SE_SF(HDMI_TB_ENC_GENERIC_PACKET_CONTROL0, HDMI_GENERIC6_CONT, mask_sh),\ + SE_SF(HDMI_TB_ENC_GENERIC_PACKET_CONTROL0, HDMI_GENERIC7_CONT, mask_sh),\ + SE_SF(HDMI_TB_ENC_GENERIC_PACKET_CONTROL1, HDMI_GENERIC8_CONT, mask_sh),\ + SE_SF(HDMI_TB_ENC_GENERIC_PACKET_CONTROL1, HDMI_GENERIC9_CONT, mask_sh),\ + SE_SF(HDMI_TB_ENC_GENERIC_PACKET_CONTROL1, HDMI_GENERIC10_CONT, mask_sh),\ + SE_SF(HDMI_TB_ENC_GENERIC_PACKET_CONTROL1, HDMI_GENERIC11_CONT, mask_sh),\ + SE_SF(HDMI_TB_ENC_GENERIC_PACKET_CONTROL1, HDMI_GENERIC12_CONT, mask_sh),\ + SE_SF(HDMI_TB_ENC_GENERIC_PACKET_CONTROL1, HDMI_GENERIC13_CONT, mask_sh),\ + SE_SF(HDMI_TB_ENC_GENERIC_PACKET_CONTROL1, HDMI_GENERIC14_CONT, mask_sh),\ + SE_SF(HDMI_TB_ENC_GENERIC_PACKET_CONTROL0, HDMI_GENERIC0_SEND, mask_sh),\ + SE_SF(HDMI_TB_ENC_GENERIC_PACKET_CONTROL0, HDMI_GENERIC1_SEND, mask_sh),\ + SE_SF(HDMI_TB_ENC_GENERIC_PACKET_CONTROL0, HDMI_GENERIC2_SEND, mask_sh),\ + SE_SF(HDMI_TB_ENC_GENERIC_PACKET_CONTROL0, HDMI_GENERIC3_SEND, mask_sh),\ + SE_SF(HDMI_TB_ENC_GENERIC_PACKET_CONTROL0, HDMI_GENERIC4_SEND, mask_sh),\ + SE_SF(HDMI_TB_ENC_GENERIC_PACKET_CONTROL0, HDMI_GENERIC5_SEND, mask_sh),\ + SE_SF(HDMI_TB_ENC_GENERIC_PACKET_CONTROL0, HDMI_GENERIC6_SEND, mask_sh),\ + SE_SF(HDMI_TB_ENC_GENERIC_PACKET_CONTROL0, HDMI_GENERIC7_SEND, mask_sh),\ + SE_SF(HDMI_TB_ENC_GENERIC_PACKET_CONTROL1, HDMI_GENERIC8_SEND, mask_sh),\ + SE_SF(HDMI_TB_ENC_GENERIC_PACKET_CONTROL1, HDMI_GENERIC9_SEND, mask_sh),\ + SE_SF(HDMI_TB_ENC_GENERIC_PACKET_CONTROL1, HDMI_GENERIC10_SEND, mask_sh),\ + SE_SF(HDMI_TB_ENC_GENERIC_PACKET_CONTROL1, HDMI_GENERIC11_SEND, mask_sh),\ + SE_SF(HDMI_TB_ENC_GENERIC_PACKET_CONTROL1, HDMI_GENERIC12_SEND, mask_sh),\ + SE_SF(HDMI_TB_ENC_GENERIC_PACKET_CONTROL1, HDMI_GENERIC13_SEND, mask_sh),\ + SE_SF(HDMI_TB_ENC_GENERIC_PACKET_CONTROL1, HDMI_GENERIC14_SEND, mask_sh),\ + SE_SF(HDMI_TB_ENC_GENERIC_PACKET0_1_LINE, HDMI_GENERIC0_LINE, mask_sh),\ + SE_SF(HDMI_TB_ENC_GENERIC_PACKET0_1_LINE, HDMI_GENERIC1_LINE, mask_sh),\ + SE_SF(HDMI_TB_ENC_GENERIC_PACKET2_3_LINE, HDMI_GENERIC2_LINE, mask_sh),\ + SE_SF(HDMI_TB_ENC_GENERIC_PACKET2_3_LINE, HDMI_GENERIC3_LINE, mask_sh),\ + SE_SF(HDMI_TB_ENC_GENERIC_PACKET4_5_LINE, HDMI_GENERIC4_LINE, mask_sh),\ + SE_SF(HDMI_TB_ENC_GENERIC_PACKET4_5_LINE, HDMI_GENERIC5_LINE, mask_sh),\ + SE_SF(HDMI_TB_ENC_GENERIC_PACKET6_7_LINE, HDMI_GENERIC6_LINE, mask_sh),\ + SE_SF(HDMI_TB_ENC_GENERIC_PACKET6_7_LINE, HDMI_GENERIC7_LINE, mask_sh),\ + SE_SF(HDMI_TB_ENC_GENERIC_PACKET8_9_LINE, HDMI_GENERIC8_LINE, mask_sh),\ + SE_SF(HDMI_TB_ENC_GENERIC_PACKET8_9_LINE, HDMI_GENERIC9_LINE, mask_sh),\ + SE_SF(HDMI_TB_ENC_GENERIC_PACKET10_11_LINE, HDMI_GENERIC10_LINE, mask_sh),\ + SE_SF(HDMI_TB_ENC_GENERIC_PACKET10_11_LINE, HDMI_GENERIC11_LINE, mask_sh),\ + SE_SF(HDMI_TB_ENC_GENERIC_PACKET12_13_LINE, HDMI_GENERIC12_LINE, mask_sh),\ + SE_SF(HDMI_TB_ENC_GENERIC_PACKET12_13_LINE, HDMI_GENERIC13_LINE, mask_sh),\ + SE_SF(HDMI_TB_ENC_GENERIC_PACKET14_LINE, HDMI_GENERIC14_LINE, mask_sh),\ + SE_SF(HDMI_TB_ENC_ACR_PACKET_CONTROL, HDMI_ACR_AUTO_SEND, mask_sh),\ + SE_SF(HDMI_TB_ENC_ACR_PACKET_CONTROL, HDMI_ACR_SOURCE, mask_sh),\ + SE_SF(HDMI_TB_ENC_ACR_PACKET_CONTROL, HDMI_ACR_AUDIO_PRIORITY, mask_sh),\ + SE_SF(HDMI_TB_ENC_ACR_32_0, HDMI_ACR_CTS_32, mask_sh),\ + SE_SF(HDMI_TB_ENC_ACR_32_1, HDMI_ACR_N_32, mask_sh),\ + SE_SF(HDMI_TB_ENC_ACR_44_0, HDMI_ACR_CTS_44, mask_sh),\ + SE_SF(HDMI_TB_ENC_ACR_44_1, HDMI_ACR_N_44, mask_sh),\ + SE_SF(HDMI_TB_ENC_ACR_48_0, HDMI_ACR_CTS_48, mask_sh),\ + SE_SF(HDMI_TB_ENC_ACR_48_1, HDMI_ACR_N_48, mask_sh),\ + SE_SF(HDMI_TB_ENC_CRC_CNTL, HDMI_CRC_EN, mask_sh),\ + SE_SF(HDMI_TB_ENC_CRC_CNTL, HDMI_CRC_CONT_EN, mask_sh) + +#define DCN401_HDMI_TB_ENC_REG_FIELD_LIST(type) \ + type HDMI_TB_ENC_EN;\ + type HDMI_RESET;\ + type HDMI_RESET_DONE;\ + type HDMI_STREAM_ENC_CLOCK_EN;\ + type HDMI_STREAM_ENC_INPUT_MUX_SOURCE_SEL;\ + type HDMI_MAX_PACKETS_PER_LINE;\ + type FIFO_ENABLE;\ + type FIFO_RESET;\ + type FIFO_PIXEL_ENCODING_TYPE;\ + type FIFO_UNCOMPRESSED_PIXEL_FORMAT;\ + type FIFO_COMPRESSED_PIXEL_FORMAT;\ + type FIFO_RESET_DONE;\ + type HDMI_BORROW_MODE;\ + type HDMI_H_ACTIVE;\ + type HDMI_H_BLANK;\ + type HDMI_HC_ACTIVE;\ + type HDMI_HC_BLANK;\ + type HDMI_DB_DISABLE;\ + type HDMI_PIXEL_ENCODING;\ + type HDMI_DEEP_COLOR_DEPTH;\ + type HDMI_DEEP_COLOR_ENABLE;\ + type HDMI_ODM_COMBINE_MODE;\ + type HDMI_DSC_MODE;\ + type HDMI_GC_CONT;\ + type HDMI_GC_SEND;\ + type HDMI_ACP_SEND;\ + type HDMI_AUDIO_INFO_SEND;\ + type HDMI_AUDIO_INFO_LINE;\ + type HDMI_GC_AVMUTE;\ + type HDMI_GENERIC0_CONT;\ + type HDMI_GENERIC0_SEND;\ + type HDMI_GENERIC0_LINE;\ + type HDMI_GENERIC1_CONT;\ + type HDMI_GENERIC1_SEND;\ + type HDMI_GENERIC1_LINE;\ + type HDMI_GENERIC2_CONT;\ + type HDMI_GENERIC2_SEND;\ + type HDMI_GENERIC2_LINE;\ + type HDMI_GENERIC3_CONT;\ + type HDMI_GENERIC3_SEND;\ + type HDMI_GENERIC3_LINE;\ + type HDMI_GENERIC4_CONT;\ + type HDMI_GENERIC4_SEND;\ + type HDMI_GENERIC4_LINE;\ + type HDMI_GENERIC5_CONT;\ + type HDMI_GENERIC5_SEND;\ + type HDMI_GENERIC5_LINE;\ + type HDMI_GENERIC6_CONT;\ + type HDMI_GENERIC6_SEND;\ + type HDMI_GENERIC6_LINE;\ + type HDMI_GENERIC7_CONT;\ + type HDMI_GENERIC7_SEND;\ + type HDMI_GENERIC7_LINE;\ + type HDMI_GENERIC8_CONT;\ + type HDMI_GENERIC8_SEND;\ + type HDMI_GENERIC8_LINE;\ + type HDMI_GENERIC9_CONT;\ + type HDMI_GENERIC9_SEND;\ + type HDMI_GENERIC9_LINE;\ + type HDMI_GENERIC10_CONT;\ + type HDMI_GENERIC10_SEND;\ + type HDMI_GENERIC10_LINE;\ + type HDMI_GENERIC11_CONT;\ + type HDMI_GENERIC11_SEND;\ + type HDMI_GENERIC11_LINE;\ + type HDMI_GENERIC12_CONT;\ + type HDMI_GENERIC12_SEND;\ + type HDMI_GENERIC12_LINE;\ + type HDMI_GENERIC13_CONT;\ + type HDMI_GENERIC13_SEND;\ + type HDMI_GENERIC13_LINE;\ + type HDMI_GENERIC14_CONT;\ + type HDMI_GENERIC14_SEND;\ + type HDMI_GENERIC14_LINE;\ + type HDMI_ACR_AUTO_SEND;\ + type HDMI_ACR_SOURCE;\ + type HDMI_ACR_AUDIO_PRIORITY;\ + type HDMI_ACR_CTS_32;\ + type HDMI_ACR_N_32;\ + type HDMI_ACR_CTS_44;\ + type HDMI_ACR_N_44;\ + type HDMI_ACR_CTS_48;\ + type HDMI_ACR_N_48;\ + type HDMI_CRC_EN;\ + type HDMI_CRC_CONT_EN;\ + type METADATA_HUBP_REQUESTOR_ID;\ + type METADATA_ENGINE_EN;\ + type METADATA_STREAM_TYPE;\ + type HDMI_METADATA_PACKET_ENABLE;\ + type HDMI_METADATA_PACKET_LINE_REFERENCE;\ + type HDMI_METADATA_PACKET_MISSED;\ + type HDMI_METADATA_PACKET_LINE + +#define DCN401_HPO_STREAM_ENC_MASK_SH_LIST(mask_sh)\ + DCN401_HDMI_STREAM_ENC_MASK_SH_LIST(mask_sh),\ + DCN401_HDMI_TB_ENC_MASK_SH_LIST(mask_sh) + +#define DCN42_HDMI_TB_ENC_REG_FIELD_LIST(type) \ + type HDMI_STREAM_ENC_INPUT_MUX_AUDIO_STREAM_SOURCE_SEL;\ + type HDMI_STREAM_ENC_APG_CLOCK_EN + +struct dcn401_hpo_frl_stream_encoder_shift { + DCN401_HDMI_TB_ENC_REG_FIELD_LIST(uint8_t); + DCN42_HDMI_TB_ENC_REG_FIELD_LIST(uint8_t); +}; + +struct dcn401_hpo_frl_stream_encoder_mask { + DCN401_HDMI_TB_ENC_REG_FIELD_LIST(uint32_t); + DCN42_HDMI_TB_ENC_REG_FIELD_LIST(uint32_t); +}; + +struct dcn401_hpo_frl_stream_encoder { + struct hpo_frl_stream_encoder base; + const struct dcn30_hpo_frl_stream_enc_registers *regs; + const struct dcn401_hpo_frl_stream_encoder_shift *hpo_se_shift; + const struct dcn401_hpo_frl_stream_encoder_mask *hpo_se_mask; +}; + +void hpo_enc401_enable( + struct hpo_frl_stream_encoder *enc, + int otg_inst); + +void hpo_enc401_unblank( + struct hpo_frl_stream_encoder *enc, + int otg_inst); + +void hpo_enc401_read_state( + struct hpo_frl_stream_encoder *enc, + struct hpo_frl_stream_encoder_state *state); + +void hpo_enc401_blank( + struct hpo_frl_stream_encoder *enc); + +void hpo_enc401_set_hdmi_stream_attribute( + struct hpo_frl_stream_encoder *enc, + struct dc_crtc_timing *crtc_timing, + struct frl_borrow_params *borrow_params, + int odm_combine_num_segments); + +void hpo_enc401_update_hdmi_info_packet( + struct dcn401_hpo_frl_stream_encoder *enc401, + uint32_t packet_index, + const struct dc_info_packet *info_packet); + +void hpo_enc401_update_hdmi_info_packets( + struct hpo_frl_stream_encoder *enc, + const struct encoder_info_frame *info_frame); + +void hpo_enc401_hdmi_set_dsc_config( + struct hpo_frl_stream_encoder *enc, + struct dc_crtc_timing *timing, + uint8_t *dsc_packed_pps); + +void hpo_enc401_stop_hdmi_info_packets( + struct hpo_frl_stream_encoder *enc); + +void hpo_enc401_setup_hdmi_audio( + struct hpo_frl_stream_encoder *enc, + const struct audio_crtc_info *crtc_info); + +void hpo_enc401_hdmi_audio_setup( + struct hpo_frl_stream_encoder *enc, + unsigned int az_inst, + struct audio_info *info, + struct audio_crtc_info *audio_crtc_info); + +void hpo_enc401_hdmi_audio_disable( + struct hpo_frl_stream_encoder *enc); + +void hpo_enc401_audio_mute_control( + struct hpo_frl_stream_encoder *enc, + bool mute); + +void enc401_stream_encoder_set_avmute( + struct hpo_frl_stream_encoder *enc, + bool enable); + +void hpo_enc401_set_dynamic_metadata( + struct hpo_frl_stream_encoder *enc, + bool enable_dme, + uint32_t hubp_requestor_id, + enum dynamic_metadata_mode dmdata_mode); +void frl_get_audio_clock_info( + enum dc_color_depth color_depth, + uint32_t frl_character_clock_kHz, + struct frl_audio_clock_info *audio_clock_info); + +void dcn401_hpo_frl_stream_encoder_construct( + struct dcn401_hpo_frl_stream_encoder *enc401, + struct dc_context *ctx, + struct dc_bios *bp, + enum engine_id eng_id, + struct vpg *vpg, + struct afmt *afmt, + const struct dcn30_hpo_frl_stream_enc_registers *regs, + const struct dcn401_hpo_frl_stream_encoder_shift *hpo_se_shift, + const struct dcn401_hpo_frl_stream_encoder_mask *hpo_se_mask); + +#endif 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 new file mode 100644 index 0000000000000..d4f66e62c729d --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/hpo/dcn42/dcn42_hpo_frl_stream_encoder.c @@ -0,0 +1,207 @@ +// SPDX-License-Identifier: MIT +// +// Copyright 2025 Advanced Micro Devices, Inc. + +#include "dc_bios_types.h" +#include "core_types.h" +#include "dcn42_hpo_frl_stream_encoder.h" +#include "dcn31/dcn31_apg.h" +#include "dcn401/dcn401_hpo_frl_stream_encoder.h" +#include "dcn30/dcn30_hpo_frl_stream_encoder.h" +#include "reg_helper.h" +#include "hw_shared.h" +#include "dcn_calc_math.h" +#include "dml/dcn30/dcn30_fpu.h" + +#undef DC_LOGGER +#define DC_LOGGER \ + enc401->base.ctx->logger + +#define DTRACE(str, ...) {DC_LOG_HDMI_FRL(str, ##__VA_ARGS__); } + +#define DEBUG_FRL_CAP_CHK 1 + +#define REG(reg)\ + (enc401->regs->reg) + +#undef FN +#define FN(reg_name, field_name) \ + enc401->hpo_se_shift->field_name, enc401->hpo_se_mask->field_name + +#define CTX \ + enc401->base.ctx + +#define VBI_LINE_0 0 + +void hpo_enc42_unblank(struct hpo_frl_stream_encoder *enc, int otg_inst) +{ + (void)otg_inst; + struct dcn401_hpo_frl_stream_encoder *enc401 = DCN401_HPO_FRL_STRENC_FROM_HPO_FRL_STRENC(enc); + + DC_LOG_HDMI_FRL("Entering [%s]\n", __func__); + + /*make sure FIFO_VIDEO_STREAM_ACTIVE =1*/ + REG_UPDATE(HDMI_STREAM_ENC_CLOCK_RAMP_ADJUSTER_FIFO_STATUS_CONTROL0, + FIFO_ENABLE, 0); + + /* Reset */ + REG_UPDATE(HDMI_STREAM_ENC_CLOCK_RAMP_ADJUSTER_FIFO_STATUS_CONTROL0, + FIFO_RESET, 1); + REG_WAIT(HDMI_STREAM_ENC_CLOCK_RAMP_ADJUSTER_FIFO_STATUS_CONTROL0, FIFO_RESET_DONE, + 1, 10, 1000); + REG_UPDATE(HDMI_STREAM_ENC_CLOCK_RAMP_ADJUSTER_FIFO_STATUS_CONTROL0, + FIFO_RESET, 0); + REG_WAIT(HDMI_STREAM_ENC_CLOCK_RAMP_ADJUSTER_FIFO_STATUS_CONTROL0, FIFO_RESET_DONE, + 0, 10, 1000); + + /* Enable HDMI Tribyte Encoder */ + REG_UPDATE(HDMI_TB_ENC_CONTROL, + HDMI_TB_ENC_EN, 1); + + /* Enable Clock Ramp Adjuster FIFO */ + REG_UPDATE(HDMI_STREAM_ENC_CLOCK_RAMP_ADJUSTER_FIFO_STATUS_CONTROL0, + FIFO_ENABLE, 1); + + DC_LOG_HDMI_FRL("Exiting [%s]\n", __func__); +} + +void hpo_enc42_setup_hdmi_audio( + struct hpo_frl_stream_encoder *enc, + const struct audio_crtc_info *crtc_info) +{ + struct dcn401_hpo_frl_stream_encoder *enc401 = DCN401_HPO_FRL_STRENC_FROM_HPO_FRL_STRENC(enc); + struct frl_audio_clock_info audio_clock_info = {0}; + + DC_LOG_DEBUG("Entering [%s]\n", __func__); + + /* TODO: HDMI_AUDIO_DELAY_EN bit only in DIG -- not in HPO? */ + /* HDMI_AUDIO_PACKET_CONTROL */ + //REG_UPDATE(HDMI_AUDIO_PACKET_CONTROL, + // HDMI_AUDIO_DELAY_EN, 1); + + /* Setup audio in APG - program APG block associated with HPO */ + ASSERT(enc->apg); + + /* HDMI_ACR_PACKET_CONTROL */ + REG_UPDATE_3(HDMI_TB_ENC_ACR_PACKET_CONTROL, + HDMI_ACR_AUTO_SEND, 1, + HDMI_ACR_SOURCE, 0, + HDMI_ACR_AUDIO_PRIORITY, 0); + + /* N/CTS computed relative to FRL rate instead of video rate (TMDS character clock). */ + /* Program audio clock sample/regeneration parameters */ + frl_get_audio_clock_info(crtc_info->color_depth, + crtc_info->frl_character_clock_kHz, + &audio_clock_info); + DC_LOG_HW_AUDIO( + "\n%s:Input::requested_pixel_clock_100Hz = %d" \ + "calculated_pixel_clock_100Hz = %d \n", __func__, \ + crtc_info->requested_pixel_clock_100Hz, \ + crtc_info->calculated_pixel_clock_100Hz); + + /* Same register definition, but using HDMI_TB_ENC register */ + /* HDMI_ACR_32_0__HDMI_ACR_CTS_32_MASK */ + REG_UPDATE(HDMI_TB_ENC_ACR_32_0, HDMI_ACR_CTS_32, audio_clock_info.cts_32khz); + + /* HDMI_ACR_32_1__HDMI_ACR_N_32_MASK */ + REG_UPDATE(HDMI_TB_ENC_ACR_32_1, HDMI_ACR_N_32, audio_clock_info.n_32khz); + + /* HDMI_ACR_44_0__HDMI_ACR_CTS_44_MASK */ + REG_UPDATE(HDMI_TB_ENC_ACR_44_0, HDMI_ACR_CTS_44, audio_clock_info.cts_44khz); + + /* HDMI_ACR_44_1__HDMI_ACR_N_44_MASK */ + REG_UPDATE(HDMI_TB_ENC_ACR_44_1, HDMI_ACR_N_44, audio_clock_info.n_44khz); + + /* HDMI_ACR_48_0__HDMI_ACR_CTS_48_MASK */ + REG_UPDATE(HDMI_TB_ENC_ACR_48_0, HDMI_ACR_CTS_48, audio_clock_info.cts_48khz); + + /* HDMI_ACR_48_1__HDMI_ACR_N_48_MASK */ + REG_UPDATE(HDMI_TB_ENC_ACR_48_1, HDMI_ACR_N_48, audio_clock_info.n_48khz); + + + /* TODO: HDMI_TB_ENC_ACR_PACKET_CONTROL::ACR_N_MULTIPLE + * Same register definition, but using HDMI_TB_ENC register*/ + + /* Video driver cannot know in advance which sample rate will + * be used by HD Audio driver + * HDMI_ACR_PACKET_CONTROL__HDMI_ACR_N_MULTIPLE field is + * programmed below in interrupt callback + */ + DC_LOG_DEBUG("Exiting [%s]\n", __func__); +} + +void hpo_enc42_hdmi_audio_setup( + struct hpo_frl_stream_encoder *enc, + unsigned int az_inst, + struct audio_info *info, + struct audio_crtc_info *audio_crtc_info) +{ + struct dcn401_hpo_frl_stream_encoder *enc401 = DCN401_HPO_FRL_STRENC_FROM_HPO_FRL_STRENC(enc); + + REG_UPDATE_2(HDMI_STREAM_ENC_AUDIO_CONTROL, + HDMI_STREAM_ENC_INPUT_MUX_AUDIO_STREAM_SOURCE_SEL, az_inst, + HDMI_STREAM_ENC_APG_CLOCK_EN, 1); + + hpo_enc42_setup_hdmi_audio(enc, audio_crtc_info); + ASSERT (enc->apg); + enc->apg->funcs->se_audio_setup(enc->apg, az_inst, info); +} + +void hpo_enc42_hdmi_audio_disable( + struct hpo_frl_stream_encoder *enc) +{ + ASSERT(enc->apg); + if (enc->apg->funcs->disable_apg) + enc->apg->funcs->disable_apg(enc->apg); +} + +void hpo_enc42_audio_mute_control( + struct hpo_frl_stream_encoder *enc, + bool mute) +{ + ASSERT (enc->apg); + if (mute) + enc->apg->funcs->disable_apg(enc->apg); + else + enc->apg->funcs->enable_apg(enc->apg); +} + +static const struct hpo_frl_stream_encoder_funcs dcn42_str_enc_funcs = { + .hdmi_frl_enable = hpo_enc401_enable, + .hdmi_frl_unblank = hpo_enc42_unblank, + .hdmi_frl_blank = hpo_enc401_blank, + .hdmi_frl_set_stream_attribute = hpo_enc401_set_hdmi_stream_attribute, + .validate_hdmi_frl_output = hpo_enc3_validate_hdmi_frl_output, + .update_hdmi_info_packets = hpo_enc401_update_hdmi_info_packets, + .stop_hdmi_info_packets = hpo_enc401_stop_hdmi_info_packets, + .audio_mute_control = hpo_enc42_audio_mute_control, + .hdmi_audio_setup = hpo_enc42_hdmi_audio_setup, + .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, +}; + +void dcn42_hpo_frl_stream_encoder_construct( + struct dcn42_hpo_frl_stream_encoder *enc42, + struct dc_context *ctx, + struct dc_bios *bp, + enum engine_id eng_id, + struct vpg *vpg, + struct apg *apg, + const struct dcn30_hpo_frl_stream_enc_registers *regs, + const struct dcn401_hpo_frl_stream_encoder_shift *hpo_se_shift, + const struct dcn401_hpo_frl_stream_encoder_mask *hpo_se_mask) +{ + enc42->base.funcs = &dcn42_str_enc_funcs; + enc42->base.ctx = ctx; + enc42->base.id = eng_id; + enc42->base.bp = bp; + enc42->base.vpg = vpg; + enc42->base.apg = apg; + enc42->regs = regs; + enc42->hpo_se_shift = hpo_se_shift; + enc42->hpo_se_mask = hpo_se_mask; + enc42->base.stream_enc_inst = vpg->inst; +} diff --git a/drivers/gpu/drm/amd/display/dc/hpo/dcn42/dcn42_hpo_frl_stream_encoder.h b/drivers/gpu/drm/amd/display/dc/hpo/dcn42/dcn42_hpo_frl_stream_encoder.h new file mode 100644 index 0000000000000..ff8218bed7530 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/hpo/dcn42/dcn42_hpo_frl_stream_encoder.h @@ -0,0 +1,59 @@ +// SPDX-License-Identifier: MIT +// +// Copyright 2025 Advanced Micro Devices, Inc. + +#ifndef __DC_HPO_FRL_STREAM_ENCODER_DCN42_H__ +#define __DC_HPO_FRL_STREAM_ENCODER_DCN42_H__ + +#include "dcn30/dcn30_vpg.h" +#include "dcn31/dcn31_apg.h" +#include "dcn30/dcn30_hpo_frl_stream_encoder.h" +#include "dcn401/dcn401_hpo_frl_stream_encoder.h" + +#include "stream_encoder.h" +#include "dml/dml1_frl_cap_chk.h" + +#define DCN42_HDMI_STREAM_ENC_MASK_SH_LIST(mask_sh)\ + DCN401_HPO_STREAM_ENC_MASK_SH_LIST(mask_sh),\ + SE_SF(HDMI_STREAM_ENC_AUDIO_CONTROL, HDMI_STREAM_ENC_INPUT_MUX_AUDIO_STREAM_SOURCE_SEL, mask_sh),\ + SE_SF(HDMI_STREAM_ENC_AUDIO_CONTROL, HDMI_STREAM_ENC_APG_CLOCK_EN, mask_sh) +struct dcn42_hpo_frl_stream_encoder { + struct hpo_frl_stream_encoder base; + const struct dcn30_hpo_frl_stream_enc_registers *regs; + const struct dcn401_hpo_frl_stream_encoder_shift *hpo_se_shift; + const struct dcn401_hpo_frl_stream_encoder_mask *hpo_se_mask; +}; + +void hpo_enc42_unblank( + struct hpo_frl_stream_encoder *enc, + int otg_inst); + +void hpo_enc42_setup_hdmi_audio( + struct hpo_frl_stream_encoder *enc, + const struct audio_crtc_info *crtc_info); + +void hpo_enc42_hdmi_audio_setup( + struct hpo_frl_stream_encoder *enc, + unsigned int az_inst, + struct audio_info *info, + struct audio_crtc_info *audio_crtc_info); + +void hpo_enc42_hdmi_audio_disable( + struct hpo_frl_stream_encoder *enc); + +void hpo_enc42_audio_mute_control( + struct hpo_frl_stream_encoder *enc, + bool mute); + +void dcn42_hpo_frl_stream_encoder_construct( + struct dcn42_hpo_frl_stream_encoder *enc42, + struct dc_context *ctx, + struct dc_bios *bp, + enum engine_id eng_id, + struct vpg *vpg, + struct apg *apg, + const struct dcn30_hpo_frl_stream_enc_registers *regs, + const struct dcn401_hpo_frl_stream_encoder_shift *hpo_se_shift, + const struct dcn401_hpo_frl_stream_encoder_mask *hpo_se_mask); + +#endif /* __DC_HPO_STREAM_ENCODER_DCN42_H__ */ diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dce110/dce110_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dce110/dce110_hwseq.c index f0abbb7c2cb24..f449d7fe7f78e 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dce110/dce110_hwseq.c +++ b/drivers/gpu/drm/amd/display/dc/hwss/dce110/dce110_hwseq.c @@ -1220,6 +1220,9 @@ void dce110_disable_stream(struct pipe_ctx *pipe_ctx) pipe_ctx->stream_res.stream_enc); } + if (dc_is_hdmi_frl_signal(pipe_ctx->stream->signal)) + pipe_ctx->stream_res.hpo_frl_stream_enc->funcs->stop_hdmi_info_packets( + pipe_ctx->stream_res.hpo_frl_stream_enc); if (dc->link_srv->dp_is_128b_132b_signal(pipe_ctx)) { pipe_ctx->stream_res.hpo_dp_stream_enc->funcs->stop_dp_info_packets( pipe_ctx->stream_res.hpo_dp_stream_enc); @@ -1244,6 +1247,7 @@ void dce110_disable_stream(struct pipe_ctx *pipe_ctx) } } } else if (dccg && dccg->funcs->disable_symclk_se) { + if (pipe_ctx->stream->signal != SIGNAL_TYPE_HDMI_FRL) dccg->funcs->disable_symclk_se(dccg, stream_enc->stream_enc_inst, link_enc->transmitter - TRANSMITTER_UNIPHY_A); } @@ -1315,6 +1319,18 @@ void dce110_blank_stream(struct pipe_ctx *pipe_ctx) link->dc->link_srv->edp_receiver_ready_T9(link); } + if (dc_is_hdmi_frl_signal(pipe_ctx->stream->signal)) { + pipe_ctx->stream_res.hpo_frl_stream_enc->funcs->hdmi_frl_blank(pipe_ctx->stream_res.hpo_frl_stream_enc); + + /* Set HDMISTREAMCLK source to REFCLK */ + if (link->dc->res_pool->dccg && + link->dc->res_pool->dccg->funcs->set_hdmistreamclk) { + link->dc->res_pool->dccg->funcs->set_hdmistreamclk( + link->dc->res_pool->dccg, + REFCLK, + pipe_ctx->stream_res.tg->inst); + } + } } @@ -1504,9 +1520,32 @@ void build_audio_output( } } + if (pipe_ctx->stream->signal == SIGNAL_TYPE_HDMI_FRL) { + switch (pipe_ctx->stream->link->frl_link_settings.frl_link_rate) { + case HDMI_FRL_LINK_RATE_3GBPS: + audio_output->crtc_info.frl_character_clock_kHz = 166667; + break; + case HDMI_FRL_LINK_RATE_6GBPS: + case HDMI_FRL_LINK_RATE_6GBPS_4LANE: + audio_output->crtc_info.frl_character_clock_kHz = 333333; + break; + case HDMI_FRL_LINK_RATE_8GBPS: + audio_output->crtc_info.frl_character_clock_kHz = 444444; + break; + case HDMI_FRL_LINK_RATE_10GBPS: + audio_output->crtc_info.frl_character_clock_kHz = 555555; + break; + case HDMI_FRL_LINK_RATE_12GBPS: + default: + audio_output->crtc_info.frl_character_clock_kHz = 666667; + break; + } + } else + audio_output->crtc_info.frl_character_clock_kHz = 0; if (state->clk_mgr && (pipe_ctx->stream->signal == SIGNAL_TYPE_DISPLAY_PORT || + pipe_ctx->stream->signal == SIGNAL_TYPE_HDMI_FRL || pipe_ctx->stream->signal == SIGNAL_TYPE_DISPLAY_PORT_MST)) { audio_output->pll_info.audio_dto_source_clock_in_khz = state->clk_mgr->funcs->get_dp_ref_clk_frequency( @@ -1731,7 +1770,8 @@ enum dc_status dce110_apply_single_controller_ctx_to_hw( pipe_ctx->stream_res.tg->funcs->set_static_screen_control( pipe_ctx->stream_res.tg, event_triggers, 2); - if (!dc_is_virtual_signal(pipe_ctx->stream->signal)) + if (!dc_is_virtual_signal(pipe_ctx->stream->signal) && + !dc_is_hdmi_frl_signal(pipe_ctx->stream->signal)) pipe_ctx->stream_res.stream_enc->funcs->dig_connect_to_otg( pipe_ctx->stream_res.stream_enc, pipe_ctx->stream_res.tg->inst); @@ -2481,7 +2521,8 @@ static void dce110_setup_audio_dto( if (pipe_ctx->top_pipe) continue; - if (pipe_ctx->stream->signal != SIGNAL_TYPE_HDMI_TYPE_A) + if (pipe_ctx->stream->signal != SIGNAL_TYPE_HDMI_TYPE_A && + pipe_ctx->stream->signal != SIGNAL_TYPE_HDMI_FRL) continue; if (pipe_ctx->stream_res.audio != NULL) { struct audio_output audio_output; @@ -2490,15 +2531,27 @@ static void dce110_setup_audio_dto( if (dc->res_pool->dccg && dc->res_pool->dccg->funcs->set_audio_dtbclk_dto) { struct dtbclk_dto_params dto_params = {0}; - - dc->res_pool->dccg->funcs->set_audio_dtbclk_dto( - dc->res_pool->dccg, &dto_params); - - pipe_ctx->stream_res.audio->funcs->wall_dto_setup( + dto_params.ref_dtbclk_khz = dc->clk_mgr->funcs->get_dtb_ref_clk_frequency(dc->clk_mgr); + + if (pipe_ctx->stream->signal == SIGNAL_TYPE_HDMI_FRL) { + /* For DCN3.1, audio to HPO FRL encoder is using audio DTBCLK DTO */ + /* set audio DTBCLK DTO to 24MHz */ + dto_params.req_audio_dtbclk_khz = 24000; + dc->res_pool->dccg->funcs->set_audio_dtbclk_dto( + dc->res_pool->dccg, + &dto_params); + } else { + /* Audio DTBCLK params default to disabled */ + dc->res_pool->dccg->funcs->set_audio_dtbclk_dto( + dc->res_pool->dccg, + &dto_params); + + pipe_ctx->stream_res.audio->funcs->wall_dto_setup( pipe_ctx->stream_res.audio, pipe_ctx->stream->signal, &audio_output.crtc_info, &audio_output.pll_info); + } } else pipe_ctx->stream_res.audio->funcs->wall_dto_setup( pipe_ctx->stream_res.audio, @@ -2528,11 +2581,37 @@ static void dce110_setup_audio_dto( build_audio_output(context, pipe_ctx, &audio_output); - pipe_ctx->stream_res.audio->funcs->wall_dto_setup( - pipe_ctx->stream_res.audio, - pipe_ctx->stream->signal, - &audio_output.crtc_info, - &audio_output.pll_info); + /* Audio to HPO DP encoder is using audio DTBCLK DTO */ + if (dc->res_pool->dccg && dc->res_pool->dccg->funcs->set_audio_dtbclk_dto) { + struct dtbclk_dto_params dto_params = {0}; + dto_params.ref_dtbclk_khz = + dc->clk_mgr->funcs->get_dtb_ref_clk_frequency(dc->clk_mgr); + + if (dc->link_srv->dp_is_128b_132b_signal(pipe_ctx)) { + /* set audio DTBCLK DTO to 24MHz */ + dto_params.req_audio_dtbclk_khz = 24000; + dc->res_pool->dccg->funcs->set_audio_dtbclk_dto( + dc->res_pool->dccg, + &dto_params); + } else { + /* Audio DTBCLK params default to disabled */ + dc->res_pool->dccg->funcs->set_audio_dtbclk_dto( + dc->res_pool->dccg, + &dto_params); + + pipe_ctx->stream_res.audio->funcs->wall_dto_setup( + pipe_ctx->stream_res.audio, + pipe_ctx->stream->signal, + &audio_output.crtc_info, + &audio_output.pll_info); + } + } else { + pipe_ctx->stream_res.audio->funcs->wall_dto_setup( + pipe_ctx->stream_res.audio, + pipe_ctx->stream->signal, + &audio_output.crtc_info, + &audio_output.pll_info); + } break; } } diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn10/dcn10_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn10/dcn10_hwseq.c index 756ce83795381..eef0ef7e2f893 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn10/dcn10_hwseq.c +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn10/dcn10_hwseq.c @@ -752,6 +752,43 @@ void dcn10_log_hw_state(struct dc *dc, log_mpc_crc(dc, log_ctx); + for (i = 0; i < pool->hpo_frl_stream_enc_count; i++) { + struct hpo_frl_stream_encoder_state hpo_se_state = {0}; + struct hpo_frl_link_enc_state hpo_le_state = {0}; + struct hpo_frl_stream_encoder *hpo_frl_stream_enc = pool->hpo_frl_stream_enc[i]; + struct hpo_frl_link_encoder *hpo_frl_link_enc = dc->links[i]->hpo_frl_link_enc; + bool printed_header = false; + + hpo_frl_stream_enc->funcs->read_state(hpo_frl_stream_enc, &hpo_se_state); + if (hpo_se_state.stream_enc_enabled) + hpo_frl_link_enc->funcs->read_state(hpo_frl_link_enc, &hpo_le_state); + + /* Only print if HPO link is enabled */ + if ((hpo_se_state.stream_enc_enabled == 0) + || (hpo_le_state.link_enc_enabled == 0)) + continue; + if (!printed_header) { + DTN_INFO("\n"); + DTN_INFO("HPO: OTG Inst Link Pixel Format Depth ODM Segments Lanes Borrow h_active h_blank\n"); + printed_header = true; + } + + DTN_INFO("[%d]: %10d %6s %10s %5d %5d %5d %6s %5d %5d\n", + hpo_frl_stream_enc->id - ENGINE_ID_HPO_0, + hpo_se_state.otg_inst, + hpo_le_state.link_active ? "Active" : "Training", + (hpo_se_state.pixel_format == PIXEL_ENCODING_YCBCR420) ? "4:2:0" : + ((hpo_se_state.pixel_format == PIXEL_ENCODING_YCBCR422) ? "4:2:2" : "4:4:4"), + hpo_se_state.color_depth, + hpo_se_state.num_odm_segments, + hpo_le_state.lane_count, + (hpo_se_state.borrow_mode == 0) ? "NONE" : + ((hpo_se_state.borrow_mode == 1) ? "ACTIVE" : "BLANK"), + hpo_se_state.h_active, + hpo_se_state.h_blank); + } + DTN_INFO("\n"); + { if (pool->hpo_dp_stream_enc_count > 0) { DTN_INFO("DP HPO S_ENC: Enabled OTG Format Depth Vid SDP Compressed Link\n"); diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn20/dcn20_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn20/dcn20_hwseq.c index 288e4edaa9a2b..f1853e973e8da 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn20/dcn20_hwseq.c +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn20/dcn20_hwseq.c @@ -917,6 +917,11 @@ enum dc_status dcn20_enable_stream_timing( pipe_ctx->stream->signal, true); + /* Must use manual div mode for FRL */ + if (pipe_ctx->stream_res.tg->funcs->set_h_timing_div_manual_mode) { + bool manual_mode = dc_is_hdmi_frl_signal(pipe_ctx->stream->signal) || !is_h_timing_divisible_by_2(stream) || dc_is_virtual_signal(pipe_ctx->stream->signal); + pipe_ctx->stream_res.tg->funcs->set_h_timing_div_manual_mode(pipe_ctx->stream_res.tg, manual_mode); + } rate_control_2x_pclk = rate_control_2x_pclk || opp_cnt > 1; flow_control.flow_ctrl_mode = 0; flow_control.flow_ctrl_cnt0 = 0x80; @@ -2784,6 +2789,16 @@ void dcn20_unblank_stream(struct pipe_ctx *pipe_ctx, pipe_ctx->stream_res.stream_enc->funcs->dp_unblank(link, pipe_ctx->stream_res.stream_enc, ¶ms); } + if (dc_is_hdmi_frl_signal(pipe_ctx->stream->signal)) { + if (params.opp_cnt == 4) + params.timing.pix_clk_100hz /= 4; + else if (is_two_pixels_per_container || params.opp_cnt > 1) + params.timing.pix_clk_100hz /= 2; + if (link->link_status.link_active && link->frl_link_settings.frl_link_rate != 0) + pipe_ctx->stream_res.hpo_frl_stream_enc->funcs->hdmi_frl_unblank( + pipe_ctx->stream_res.hpo_frl_stream_enc, + pipe_ctx->stream_res.tg->inst); + } if (link->local_sink && link->local_sink->sink_signal == SIGNAL_TYPE_EDP) { hws->funcs.edp_backlight_control(link, true); } @@ -3051,7 +3066,9 @@ void dcn20_enable_stream(struct pipe_ctx *pipe_ctx) dccg->funcs->enable_symclk32_se(dccg, dp_hpo_inst, phyd32clk); } } else { - if (dccg->funcs->enable_symclk_se && link_enc) { + if (dccg->funcs->enable_symclk_se + && link_enc + && pipe_ctx->stream->signal != SIGNAL_TYPE_HDMI_FRL) { if (link->ep_type == DISPLAY_ENDPOINT_USB4_DPIA && link->cur_link_settings.link_rate == LINK_RATE_UNKNOWN && !link->link_status.link_active) { 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 2aa0f1de81037..a0d049aebbe31 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 @@ -836,6 +836,10 @@ void dcn30_set_avmute(struct pipe_ctx *pipe_ctx, bool enable) if (pipe_ctx == NULL) return; + if (dc_is_hdmi_frl_signal(pipe_ctx->stream->signal) && pipe_ctx->stream_res.hpo_frl_stream_enc != NULL) + pipe_ctx->stream_res.hpo_frl_stream_enc->funcs->set_avmute( + pipe_ctx->stream_res.hpo_frl_stream_enc, + enable); if (dc_is_hdmi_signal(pipe_ctx->stream->signal) && pipe_ctx->stream_res.stream_enc != NULL) { pipe_ctx->stream_res.stream_enc->funcs->set_avmute( pipe_ctx->stream_res.stream_enc, @@ -856,22 +860,29 @@ void dcn30_update_info_frame(struct pipe_ctx *pipe_ctx) { bool is_hdmi_tmds; bool is_dp; + bool is_hdmi_frl; ASSERT(pipe_ctx->stream); - if (pipe_ctx->stream_res.stream_enc == NULL) + if (pipe_ctx->stream_res.stream_enc == NULL && + pipe_ctx->stream_res.hpo_frl_stream_enc == NULL) return; /* this is not root pipe */ is_hdmi_tmds = dc_is_hdmi_tmds_signal(pipe_ctx->stream->signal); is_dp = dc_is_dp_signal(pipe_ctx->stream->signal); - if (!is_hdmi_tmds && !is_dp) + is_hdmi_frl = dc_is_hdmi_frl_signal(pipe_ctx->stream->signal); + if (!is_hdmi_tmds && !is_dp && !is_hdmi_frl) return; if (is_hdmi_tmds) pipe_ctx->stream_res.stream_enc->funcs->update_hdmi_info_packets( pipe_ctx->stream_res.stream_enc, &pipe_ctx->stream_res.encoder_info_frame); + else if (is_hdmi_frl) + pipe_ctx->stream_res.hpo_frl_stream_enc->funcs->update_hdmi_info_packets( + pipe_ctx->stream_res.hpo_frl_stream_enc, + &pipe_ctx->stream_res.encoder_info_frame); else { if (pipe_ctx->stream_res.stream_enc->funcs->update_dp_info_packets_sdp_line_num) pipe_ctx->stream_res.stream_enc->funcs->update_dp_info_packets_sdp_line_num( @@ -890,6 +901,7 @@ void dcn30_program_dmdata_engine(struct pipe_ctx *pipe_ctx) struct hubp *hubp = pipe_ctx->plane_res.hubp; bool enable = false; struct stream_encoder *stream_enc = pipe_ctx->stream_res.stream_enc; + struct hpo_frl_stream_encoder *hpo_enc = pipe_ctx->stream_res.hpo_frl_stream_enc; enum dynamic_metadata_mode mode = dc_is_dp_signal(stream->signal) ? dmdata_dp : dmdata_hdmi; @@ -903,11 +915,44 @@ void dcn30_program_dmdata_engine(struct pipe_ctx *pipe_ctx) if (!hubp) return; + if (dc_is_hdmi_frl_signal(stream->signal)) { + ASSERT(mode == dmdata_hdmi); + + if (!hpo_enc || !hpo_enc->funcs->set_dynamic_metadata) + return; + + hpo_enc->funcs->set_dynamic_metadata(hpo_enc, enable, + hubp->inst, dmdata_hdmi); + } else { if (!stream_enc || !stream_enc->funcs->set_dynamic_metadata) return; stream_enc->funcs->set_dynamic_metadata(stream_enc, enable, hubp->inst, mode); + } +} +enum dc_status dcn30_setup_hdmi_frl_link( + struct dc_link *link, + int hpo_inst, + enum clock_source_id frl_phy_clock_source_id) +{ + (void)hpo_inst; + enum dc_status status = DC_OK; + struct dc *dc = link->ctx->dc; + + if ((!link->link_enc) || + (!link->hpo_frl_link_enc) || + (!dc->res_pool->dccg->funcs->enable_hdmicharclk)) + return DC_ERROR_UNEXPECTED; + + //Enable phy output for FRL case + link->hpo_frl_link_enc->funcs->enable_frl_phy_output( + link->hpo_frl_link_enc, + link->link_enc, + frl_phy_clock_source_id, + link->frl_link_settings.frl_link_rate); + link->phy_state.symclk_state = SYMCLK_ON_TX_ON; + return status; } bool dcn30_apply_idle_power_optimizations(struct dc *dc, bool enable) diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn30/dcn30_hwseq.h b/drivers/gpu/drm/amd/display/dc/hwss/dcn30/dcn30_hwseq.h index 40afbbfb5b9c3..a963d360a3680 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn30/dcn30_hwseq.h +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn30/dcn30_hwseq.h @@ -72,6 +72,19 @@ void dcn30_set_avmute(struct pipe_ctx *pipe_ctx, bool enable); void dcn30_update_info_frame(struct pipe_ctx *pipe_ctx); void dcn30_program_dmdata_engine(struct pipe_ctx *pipe_ctx); +enum dc_status dcn30_setup_hdmi_frl_link( + struct dc_link *link, + int hpo_inst, + enum clock_source_id frl_phy_clock_source_id); +void dcn30_hw_set_fva_vrr_adj(struct dc *dc, struct pipe_ctx **pipe_ctx, int num_pipes, + struct fva_adj *fva_adj, + struct dc_crtc_timing_adjust *vrr_adj); + +int dcn30_hw_get_max_fva_factor(struct dc *dc, + struct pipe_ctx *pipe_ctx, + struct dc_crtc_timing *timing, + unsigned int max_pixel_clock); + bool dcn30_does_plane_fit_in_mall(struct dc *dc, unsigned int pitch, unsigned int height, 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 5cbae0cdda962..26c7386a8a36c 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 @@ -105,6 +105,7 @@ static const struct hw_sequencer_funcs dcn30_funcs = { .enable_tmds_link_output = dce110_enable_tmds_link_output, .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_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_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn31/dcn31_hwseq.c index b4afb2bc44934..fd8311ad4fa68 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn31/dcn31_hwseq.c +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn31/dcn31_hwseq.c @@ -104,6 +104,8 @@ static void enable_memory_low_power(struct dc *dc) dc->res_pool->stream_enc[i]->vpg->funcs->vpg_powerdown(dc->res_pool->stream_enc[i]->vpg); for (i = 0; i < dc->res_pool->hpo_dp_stream_enc_count; i++) dc->res_pool->hpo_dp_stream_enc[i]->vpg->funcs->vpg_powerdown(dc->res_pool->hpo_dp_stream_enc[i]->vpg); + for (i = 0; i < dc->res_pool->hpo_frl_stream_enc_count; i++) + dc->res_pool->hpo_frl_stream_enc[i]->vpg->funcs->vpg_powerdown(dc->res_pool->hpo_frl_stream_enc[i]->vpg); } } @@ -377,22 +379,29 @@ void dcn31_update_info_frame(struct pipe_ctx *pipe_ctx) { bool is_hdmi_tmds; bool is_dp; + bool is_hdmi_frl; ASSERT(pipe_ctx->stream); - if (pipe_ctx->stream_res.stream_enc == NULL) + if (pipe_ctx->stream_res.stream_enc == NULL && + pipe_ctx->stream_res.hpo_frl_stream_enc == NULL) return; /* this is not root pipe */ is_hdmi_tmds = dc_is_hdmi_tmds_signal(pipe_ctx->stream->signal); is_dp = dc_is_dp_signal(pipe_ctx->stream->signal); - if (!is_hdmi_tmds && !is_dp) + is_hdmi_frl = dc_is_hdmi_frl_signal(pipe_ctx->stream->signal); + if (!is_hdmi_tmds && !is_dp && !is_hdmi_frl) return; if (is_hdmi_tmds) pipe_ctx->stream_res.stream_enc->funcs->update_hdmi_info_packets( pipe_ctx->stream_res.stream_enc, &pipe_ctx->stream_res.encoder_info_frame); + else if (is_hdmi_frl) + pipe_ctx->stream_res.hpo_frl_stream_enc->funcs->update_hdmi_info_packets( + pipe_ctx->stream_res.hpo_frl_stream_enc, + &pipe_ctx->stream_res.encoder_info_frame); else if (pipe_ctx->stream->ctx->dc->link_srv->dp_is_128b_132b_signal(pipe_ctx)) { if (pipe_ctx->stream_res.hpo_dp_stream_enc->funcs->update_dp_info_packets_sdp_line_num) pipe_ctx->stream_res.hpo_dp_stream_enc->funcs->update_dp_info_packets_sdp_line_num( 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 e56b9a46aecfe..23b30d6f39568 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 @@ -98,6 +98,7 @@ static const struct hw_sequencer_funcs dcn31_funcs = { .set_flip_control_gsl = dcn20_set_flip_control_gsl, .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_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_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn314/dcn314_hwseq.c index 858a06b03b577..d567151926565 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn314/dcn314_hwseq.c +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn314/dcn314_hwseq.c @@ -335,7 +335,8 @@ unsigned int dcn314_calculate_dccg_k1_k2_values(struct pipe_ctx *pipe_ctx, unsig two_pix_per_container = pipe_ctx->stream_res.tg->funcs->is_two_pixels_per_container(&stream->timing); odm_combine_factor = get_odm_config(pipe_ctx, NULL); - if (stream->ctx->dc->link_srv->dp_is_128b_132b_signal(pipe_ctx)) { + if (dc_is_hdmi_frl_signal(pipe_ctx->stream->signal) || + stream->ctx->dc->link_srv->dp_is_128b_132b_signal(pipe_ctx)) { *k1_div = PIXEL_RATE_DIV_BY_1; *k2_div = PIXEL_RATE_DIV_BY_1; } else if (dc_is_hdmi_tmds_signal(pipe_ctx->stream->signal) || dc_is_dvi_signal(pipe_ctx->stream->signal)) { 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 9900c87b4567f..98771fc443c75 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 @@ -100,6 +100,7 @@ static const struct hw_sequencer_funcs dcn314_funcs = { .set_flip_control_gsl = dcn20_set_flip_control_gsl, .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_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_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn32/dcn32_hwseq.c index b45ceb570a5ca..3e2a3ae6bc820 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn32/dcn32_hwseq.c +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn32/dcn32_hwseq.c @@ -1205,7 +1205,8 @@ unsigned int dcn32_calculate_dccg_k1_k2_values(struct pipe_ctx *pipe_ctx, unsign two_pix_per_container = pipe_ctx->stream_res.tg->funcs->is_two_pixels_per_container(&stream->timing); odm_combine_factor = get_odm_config(pipe_ctx, NULL); - if (stream->ctx->dc->link_srv->dp_is_128b_132b_signal(pipe_ctx)) { + if (dc_is_hdmi_frl_signal(pipe_ctx->stream->signal) || + stream->ctx->dc->link_srv->dp_is_128b_132b_signal(pipe_ctx)) { *k1_div = PIXEL_RATE_DIV_BY_1; *k2_div = PIXEL_RATE_DIV_BY_1; } else if (dc_is_hdmi_tmds_signal(stream->signal) || dc_is_dvi_signal(stream->signal)) { @@ -1351,6 +1352,16 @@ void dcn32_unblank_stream(struct pipe_ctx *pipe_ctx, pipe_ctx->stream_res.stream_enc->funcs->dp_unblank(link, pipe_ctx->stream_res.stream_enc, ¶ms); } + if (dc_is_hdmi_frl_signal(pipe_ctx->stream->signal)) { + if (params.opp_cnt == 4) + params.timing.pix_clk_100hz /= 4; + else if (pipe_ctx->stream_res.tg->funcs->is_two_pixels_per_container(&stream->timing) || params.opp_cnt > 1) + params.timing.pix_clk_100hz /= 2; + if (link->link_status.link_active && link->frl_link_settings.frl_link_rate != 0) + pipe_ctx->stream_res.hpo_frl_stream_enc->funcs->hdmi_frl_unblank( + pipe_ctx->stream_res.hpo_frl_stream_enc, + pipe_ctx->stream_res.tg->inst); + } if (link->local_sink && link->local_sink->sink_signal == SIGNAL_TYPE_EDP) hws->funcs.edp_backlight_control(link, true); } 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 849dae18b7384..0b3e8512ebf12 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 @@ -31,6 +31,7 @@ #include "dcn31/dcn31_hwseq.h" #include "dcn32/dcn32_hwseq.h" #include "dcn401/dcn401_hwseq.h" +#include "dml/dcn32/dcn32_fpu.h" #include "dcn32_init.h" static const struct hw_sequencer_funcs dcn32_funcs = { @@ -96,6 +97,8 @@ static const struct hw_sequencer_funcs dcn32_funcs = { .set_flip_control_gsl = dcn20_set_flip_control_gsl, .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, + .get_max_dispclk_mhz = dcn32_get_max_dispclk_mhz, .apply_idle_power_optimizations = dcn32_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/dcn35/dcn35_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn35/dcn35_hwseq.c index f133b52ea9583..61ca62e1392f0 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn35/dcn35_hwseq.c +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn35/dcn35_hwseq.c @@ -110,6 +110,8 @@ static void enable_memory_low_power(struct dc *dc) for (i = 0; i < dc->res_pool->hpo_dp_stream_enc_count; i++) dc->res_pool->hpo_dp_stream_enc[i]->vpg->funcs->vpg_powerdown(dc->res_pool->hpo_dp_stream_enc[i]->vpg); #endif + for (i = 0; i < dc->res_pool->hpo_frl_stream_enc_count; i++) + dc->res_pool->hpo_frl_stream_enc[i]->vpg->funcs->vpg_powerdown(dc->res_pool->hpo_frl_stream_enc[i]->vpg); } } @@ -439,6 +441,9 @@ void dcn35_update_odm(struct dc *dc, struct dc_state *context, struct pipe_ctx * opp_cnt = get_odm_config(pipe_ctx, opp_inst); + if (!(pipe_ctx->stream_res.hpo_frl_stream_enc && + pipe_ctx->stream_res.hpo_frl_stream_enc->funcs->hdmi_frl_fifo_odm_enabled && + pipe_ctx->stream_res.hpo_frl_stream_enc->funcs->hdmi_frl_fifo_odm_enabled(pipe_ctx->stream_res.hpo_frl_stream_enc))) { if (opp_cnt > 1) pipe_ctx->stream_res.tg->funcs->set_odm_combine( pipe_ctx->stream_res.tg, @@ -463,6 +468,7 @@ void dcn35_update_odm(struct dc *dc, struct dc_state *context, struct pipe_ctx * odm_pipe->stream_res.opp, true); } + } if (pipe_ctx->stream_res.dsc) { struct pipe_ctx *current_pipe_ctx = &dc->current_state->res_ctx.pipe_ctx[pipe_ctx->pipe_idx]; @@ -501,6 +507,17 @@ void dcn35_dpstream_root_clock_control(struct dce_hwseq *hws, unsigned int dp_hp } } +void dcn35_hdmistream_root_clock_control(struct dce_hwseq *hws, bool clock_on) +{ + if (!hws->ctx->dc->debug.root_clock_optimization.bits.hdmistream) + return; + + if (hws->ctx->dc->res_pool->dccg->funcs->set_hdmistreamclk_root_clock_gating) { + hws->ctx->dc->res_pool->dccg->funcs->set_hdmistreamclk_root_clock_gating( + hws->ctx->dc->res_pool->dccg, clock_on); + } +} + void dcn35_physymclk_root_clock_control(struct dce_hwseq *hws, unsigned int phy_inst, bool clock_on) { if (!hws->ctx->dc->debug.root_clock_optimization.bits.physymclk) @@ -928,9 +945,17 @@ void dcn35_calc_blocks_to_gate(struct dc *dc, struct dc_state *context, memset(update_state, 0, sizeof(struct pg_block_update)); - for (i = 0; i < dc->res_pool->hpo_dp_stream_enc_count; i++) { - if (context->res_ctx.is_hpo_dp_stream_enc_acquired[i] && - dc->res_pool->hpo_dp_stream_enc[i]) { + for (ui = 0; ui < dc->res_pool->hpo_frl_stream_enc_count; ui++) { + if (context->res_ctx.is_hpo_frl_stream_enc_acquired[ui] && + dc->res_pool->hpo_frl_stream_enc[ui]) { + hpo_frl_stream_enc_acquired = true; + break; + } + } + + for (ui = 0; ui < dc->res_pool->hpo_dp_stream_enc_count; ui++) { + if (context->res_ctx.is_hpo_dp_stream_enc_acquired[ui] && + dc->res_pool->hpo_dp_stream_enc[ui]) { hpo_dp_stream_enc_acquired = true; break; } @@ -983,6 +1008,9 @@ void dcn35_calc_blocks_to_gate(struct dc *dc, struct dc_state *context, update_state->pg_pipe_res_update[PG_DPSTREAM][pipe_ctx->stream_res.hpo_dp_stream_enc->inst] = false; } + if (hpo_frl_stream_enc_acquired) + update_state->pg_pipe_res_update[PG_HDMISTREAM][0] = false; + for (i = 0; i < dc->link_count; i++) { update_state->pg_pipe_res_update[PG_PHYSYMCLK][dc->links[i]->link_enc_hw_inst] = true; if (dc->links[i]->type != dc_connection_none) @@ -1104,9 +1132,17 @@ void dcn35_calc_blocks_to_ungate(struct dc *dc, struct dc_state *context, if (dc->links[i]->type != dc_connection_none) update_state->pg_pipe_res_update[PG_PHYSYMCLK][dc->links[i]->link_enc_hw_inst] = true; - for (i = 0; i < dc->res_pool->hpo_dp_stream_enc_count; i++) { - if (context->res_ctx.is_hpo_dp_stream_enc_acquired[i] && - dc->res_pool->hpo_dp_stream_enc[i]) { + for (ui = 0; ui < dc->res_pool->hpo_frl_stream_enc_count; ui++) { + if (context->res_ctx.is_hpo_frl_stream_enc_acquired[ui] && + dc->res_pool->hpo_frl_stream_enc[ui]) { + hpo_frl_stream_enc_acquired = true; + break; + } + } + + for (ui = 0; ui < dc->res_pool->hpo_dp_stream_enc_count; ui++) { + if (context->res_ctx.is_hpo_dp_stream_enc_acquired[ui] && + dc->res_pool->hpo_dp_stream_enc[ui]) { hpo_dp_stream_enc_acquired = true; break; } @@ -1320,6 +1356,9 @@ void dcn35_root_clock_control(struct dc *dc, if (dc->hwseq->funcs.physymclk_root_clock_control) dc->hwseq->funcs.physymclk_root_clock_control(dc->hwseq, i, power_on); + if (update_state->pg_pipe_res_update[PG_HDMISTREAM][0]) + if (dc->hwseq->funcs.hdmistream_root_clock_control) + dc->hwseq->funcs.hdmistream_root_clock_control(dc->hwseq, power_on); } for (i = 0; i < dc->res_pool->res_cap->num_dsc; i++) { if (update_state->pg_pipe_res_update[PG_DSC][i]) { @@ -1350,6 +1389,9 @@ void dcn35_root_clock_control(struct dc *dc, if (dc->hwseq->funcs.physymclk_root_clock_control) dc->hwseq->funcs.physymclk_root_clock_control(dc->hwseq, i, power_on); + if (update_state->pg_pipe_res_update[PG_HDMISTREAM][0]) + if (dc->hwseq->funcs.hdmistream_root_clock_control) + dc->hwseq->funcs.hdmistream_root_clock_control(dc->hwseq, power_on); } } diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn35/dcn35_hwseq.h b/drivers/gpu/drm/amd/display/dc/hwss/dcn35/dcn35_hwseq.h index e3459546a908a..235ebf00bd1f2 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn35/dcn35_hwseq.h +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn35/dcn35_hwseq.h @@ -39,6 +39,8 @@ void dcn35_dpp_root_clock_control(struct dce_hwseq *hws, unsigned int dpp_inst, void dcn35_dpstream_root_clock_control(struct dce_hwseq *hws, unsigned int dp_hpo_inst, bool clock_on); +void dcn35_hdmistream_root_clock_control(struct dce_hwseq *hws, bool clock_on); + void dcn35_physymclk_root_clock_control(struct dce_hwseq *hws, unsigned int phy_inst, bool clock_on); void dcn35_enable_power_gating_plane(struct dce_hwseq *hws, bool enable); 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 6ac8ad97cf135..fc18d2207711f 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 @@ -107,6 +107,7 @@ static const struct hw_sequencer_funcs dcn35_funcs = { .set_flip_control_gsl = dcn20_set_flip_control_gsl, .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_backlight_level = dcn31_set_backlight_level, .set_abm_immediate_disable = dcn21_set_abm_immediate_disable, .set_pipe = dcn21_set_pipe, @@ -158,6 +159,7 @@ static const struct hwseq_private_funcs dcn35_private_funcs = { //.hubp_pg_control = dcn35_hubp_pg_control, .dpp_root_clock_control = dcn35_dpp_root_clock_control, .dpstream_root_clock_control = dcn35_dpstream_root_clock_control, + .hdmistream_root_clock_control = dcn35_hdmistream_root_clock_control, .physymclk_root_clock_control = dcn35_physymclk_root_clock_control, .program_all_writeback_pipes_in_tree = dcn30_program_all_writeback_pipes_in_tree, .update_odm = dcn35_update_odm, 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 04c260015eec3..19ec5b4edfdc9 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 @@ -100,6 +100,7 @@ static const struct hw_sequencer_funcs dcn351_funcs = { .set_flip_control_gsl = dcn20_set_flip_control_gsl, .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_backlight_level = dcn31_set_backlight_level, .set_abm_immediate_disable = dcn21_set_abm_immediate_disable, .set_pipe = dcn21_set_pipe, @@ -147,6 +148,7 @@ static const struct hwseq_private_funcs dcn351_private_funcs = { //.hubp_pg_control = dcn35_hubp_pg_control, .dpp_root_clock_control = dcn35_dpp_root_clock_control, .dpstream_root_clock_control = dcn35_dpstream_root_clock_control, + .hdmistream_root_clock_control = dcn35_hdmistream_root_clock_control, .physymclk_root_clock_control = dcn35_physymclk_root_clock_control, .program_all_writeback_pipes_in_tree = dcn30_program_all_writeback_pipes_in_tree, .update_odm = dcn35_update_odm, diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_hwseq.c index 9768b8f6b6fb6..f1d8d944f3282 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_hwseq.c +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_hwseq.c @@ -638,6 +638,9 @@ static void enable_stream_timing_calc( stream->link->phy_state.symclk_state = SYMCLK_ON_TX_ON; } + if (pipe_ctx->stream_res.tg->funcs->set_h_timing_div_manual_mode) { + *manual_mode = !is_h_timing_divisible_by_2(stream); + } params->vertical_total_min = stream->adjust.v_total_min; params->vertical_total_max = stream->adjust.v_total_max; params->vertical_total_mid = stream->adjust.v_total_mid; @@ -695,6 +698,8 @@ enum dc_status dcn401_enable_stream_timing( if (dc->res_pool->dccg->funcs->set_dtbclk_p_src) { if (dc_is_dp_signal(stream->signal) || dc_is_virtual_signal(stream->signal)) { dc->res_pool->dccg->funcs->set_dtbclk_p_src(dc->res_pool->dccg, DPREFCLK, pipe_ctx->stream_res.tg->inst); + } else if (dc_is_hdmi_frl_signal(stream->signal)) { + dc->res_pool->dccg->funcs->set_dtbclk_p_src(dc->res_pool->dccg, DTBCLK0, pipe_ctx->stream_res.tg->inst); } } @@ -733,6 +738,8 @@ enum dc_status dcn401_enable_stream_timing( pipe_ctx->stream->signal, true); + if (pipe_ctx->stream_res.tg->funcs->set_h_timing_div_manual_mode) + pipe_ctx->stream_res.tg->funcs->set_h_timing_div_manual_mode(pipe_ctx->stream_res.tg, manual_mode); for (i = 0; i < opp_cnt; i++) { opp_heads[i]->stream_res.opp->funcs->opp_pipe_clock_control( opp_heads[i]->stream_res.opp, @@ -1687,6 +1694,12 @@ void dcn401_unblank_stream(struct pipe_ctx *pipe_ctx, pipe_ctx->stream_res.stream_enc->funcs->dp_unblank(link, pipe_ctx->stream_res.stream_enc, ¶ms); } + if (dc_is_hdmi_frl_signal(pipe_ctx->stream->signal)) { + if (link->link_status.link_active && link->frl_link_settings.frl_link_rate != 0) + pipe_ctx->stream_res.hpo_frl_stream_enc->funcs->hdmi_frl_unblank( + pipe_ctx->stream_res.hpo_frl_stream_enc, + pipe_ctx->stream_res.tg->inst); + } if (link->local_sink && link->local_sink->sink_signal == SIGNAL_TYPE_EDP) hws->funcs.edp_backlight_control(link, true); } 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 5d0dfb36f3e1b..d24a352937b4b 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 @@ -84,6 +84,7 @@ static const struct hw_sequencer_funcs dcn401_funcs = { .set_flip_control_gsl = dcn20_set_flip_control_gsl, .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, .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_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn42/dcn42_hwseq.c index 46f2f9833d9e5..31dca33fdf8fa 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn42/dcn42_hwseq.c +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn42/dcn42_hwseq.c @@ -546,6 +546,14 @@ void dcn42_calc_blocks_to_gate(struct dc *dc, struct dc_state *context, update_state->pg_res_update[PG_DIO] = true; + for (i = 0; i < dc->res_pool->hpo_frl_stream_enc_count; i++) { + if (context->res_ctx.is_hpo_frl_stream_enc_acquired[i] && + dc->res_pool->hpo_frl_stream_enc[i]) { + hpo_frl_stream_enc_acquired = true; + break; + } + } + for (i = 0; i < dc->res_pool->hpo_dp_stream_enc_count; i++) { if (context->res_ctx.is_hpo_dp_stream_enc_acquired[i] && dc->res_pool->hpo_dp_stream_enc[i]) { @@ -591,11 +599,14 @@ void dcn42_calc_blocks_to_gate(struct dc *dc, struct dc_state *context, if (pipe_ctx->link_res.dio_link_enc) { update_state->pg_res_update[PG_DIO] = false; } - if (pipe_ctx->link_res.hpo_dp_link_enc) { + if (pipe_ctx->link_res.hpo_dp_link_enc + || pipe_ctx->link_res.hpo_frl_link_enc) { update_state->pg_res_update[PG_HPO] = false; } } + if (hpo_frl_stream_enc_acquired) + update_state->pg_pipe_res_update[PG_HDMISTREAM][0] = false; for (i = 0; i < dc->link_count; i++) { update_state->pg_pipe_res_update[PG_PHYSYMCLK][dc->links[i]->link_enc_hw_inst] = true; @@ -615,6 +626,12 @@ void dcn42_calc_blocks_to_gate(struct dc *dc, struct dc_state *context, } } + for (i = 0; i < dc->res_pool->hpo_frl_stream_enc_count; i++) { + if (dc->current_state->res_ctx.is_hpo_frl_stream_enc_acquired[i]) { + update_state->pg_res_update[PG_HPO] = false; + break; + } + } } void dcn42_prepare_bandwidth( @@ -662,6 +679,7 @@ void dcn42_optimize_bandwidth(struct dc *dc, struct dc_state *context) void dcn42_calc_blocks_to_ungate(struct dc *dc, struct dc_state *context, struct pg_block_update *update_state) { + bool hpo_frl_stream_enc_acquired = false; bool hpo_dp_stream_enc_acquired = false; int i = 0, j = 0; @@ -747,6 +765,14 @@ void dcn42_calc_blocks_to_ungate(struct dc *dc, struct dc_state *context, break; } } + for (i = 0; i < dc->res_pool->hpo_frl_stream_enc_count; i++) { + if (context->res_ctx.is_hpo_frl_stream_enc_acquired[i] && + dc->res_pool->hpo_frl_stream_enc[i]) { + hpo_frl_stream_enc_acquired = true; + break; + } + } + for (i = 0; i < dc->res_pool->hpo_dp_stream_enc_count; i++) { if (context->res_ctx.is_hpo_dp_stream_enc_acquired[i] && dc->res_pool->hpo_dp_stream_enc[i]) { @@ -755,9 +781,11 @@ void dcn42_calc_blocks_to_ungate(struct dc *dc, struct dc_state *context, } } - if (hpo_dp_stream_enc_acquired) + if (hpo_frl_stream_enc_acquired || hpo_dp_stream_enc_acquired) update_state->pg_res_update[PG_HPO] = true; + if (hpo_frl_stream_enc_acquired) + update_state->pg_pipe_res_update[PG_HDMISTREAM][0] = true; if (count_active_streams(dc) > 0) { update_state->pg_res_update[PG_DCCG] = true; update_state->pg_res_update[PG_DCIO] = true; @@ -967,6 +995,9 @@ void dcn42_root_clock_control(struct dc *dc, if (dc->hwseq->funcs.physymclk_root_clock_control) dc->hwseq->funcs.physymclk_root_clock_control(dc->hwseq, i, power_on); + if (update_state->pg_pipe_res_update[PG_HDMISTREAM][0]) + if (dc->hwseq->funcs.hdmistream_root_clock_control) + dc->hwseq->funcs.hdmistream_root_clock_control(dc->hwseq, power_on); } for (i = 0; i < dc->res_pool->res_cap->num_dsc; i++) { if (update_state->pg_pipe_res_update[PG_DSC][i]) { @@ -997,6 +1028,9 @@ void dcn42_root_clock_control(struct dc *dc, if (dc->hwseq->funcs.physymclk_root_clock_control) dc->hwseq->funcs.physymclk_root_clock_control(dc->hwseq, i, power_on); + if (update_state->pg_pipe_res_update[PG_HDMISTREAM][0]) + if (dc->hwseq->funcs.hdmistream_root_clock_control) + dc->hwseq->funcs.hdmistream_root_clock_control(dc->hwseq, power_on); } } void dcn42_setup_stereo(struct pipe_ctx *pipe_ctx, struct dc *dc) 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 b324a2195e8a6..49c13611a5186 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 @@ -85,6 +85,7 @@ static const struct hw_sequencer_funcs dcn42_funcs = { .set_flip_control_gsl = dcn20_set_flip_control_gsl, .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, .apply_idle_power_optimizations = dcn35_apply_idle_power_optimizations, .does_plane_fit_in_mall = NULL, .set_backlight_level = dcn31_set_backlight_level, @@ -159,6 +160,7 @@ static const struct hwseq_private_funcs dcn42_private_funcs = { .program_cm_hist = dcn42_program_cm_hist, .dpp_root_clock_control = dcn35_dpp_root_clock_control, .dpstream_root_clock_control = dcn35_dpstream_root_clock_control, + .hdmistream_root_clock_control = dcn35_hdmistream_root_clock_control, .physymclk_root_clock_control = dcn35_physymclk_root_clock_control, .resync_fifo_dccg_dio = dcn314_resync_fifo_dccg_dio, .wait_for_pipe_update_if_needed = dcn10_wait_for_pipe_update_if_needed, 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 d1dba7ffcd9b3..9e7a163c61e17 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/hw_sequencer.h +++ b/drivers/gpu/drm/amd/display/dc/hwss/hw_sequencer.h @@ -739,6 +739,123 @@ struct phantom_hubp_post_enable_params { struct hubp *hubp; }; +struct begin_cursor_offload_update_params { + struct dc *dc; + struct pipe_ctx *pipe_ctx; +}; + +struct update_cursor_offload_pipe_params { + struct dc *dc; + struct pipe_ctx *pipe_ctx; +}; + +struct commit_cursor_offload_update_params { + struct dc *dc; + struct pipe_ctx *pipe_ctx; +}; + +struct stream_enc_update_hdmi_info_packets_params { + struct pipe_ctx *pipe_ctx; +}; + +struct hpo_frl_stream_enc_update_hdmi_info_packets_params { + struct pipe_ctx *pipe_ctx; +}; + +struct hpo_dp_stream_enc_update_dp_info_packets_sdp_line_num_params { + struct pipe_ctx *pipe_ctx; +}; + +struct hpo_dp_stream_enc_update_dp_info_packets_params { + struct pipe_ctx *pipe_ctx; +}; + +struct stream_enc_update_dp_info_packets_sdp_line_num_params { + struct pipe_ctx *pipe_ctx; +}; + +struct stream_enc_update_dp_info_packets_params { + struct pipe_ctx *pipe_ctx; +}; + +struct dsc_set_config_simple_params { + struct display_stream_compressor *dsc; + struct dsc_config dsc_cfg; + struct dsc_optc_config dsc_optc_cfg; +}; + +struct stream_enc_dp_set_dsc_config_params { + struct stream_encoder *stream_enc; + const struct dsc_optc_config *dsc_optc_cfg; +}; + +struct hpo_dp_stream_enc_dp_set_dsc_pps_info_packet_params { + struct hpo_dp_stream_encoder *hpo_dp_stream_enc; + bool immediate_update; + uint8_t *dsc_packed_pps; + bool pps_sdp_stream; +}; + +struct stream_enc_dp_set_dsc_pps_info_packet_params { + struct stream_encoder *stream_enc; + bool immediate_update; + uint8_t *dsc_packed_pps; + bool pps_sdp_stream; +}; + +struct hpo_frl_stream_enc_set_dsc_config_params { + struct hpo_frl_stream_encoder *hpo_frl_stream_enc; + const struct dc_crtc_timing *timing; + uint8_t *dsc_packed_pps; +}; + +struct dp_trace_source_sequence_params { + struct dc_link *link; + enum dpcd_source_sequence source; +}; + +struct set_dmdata_attributes_params { + struct hubp *hubp; + struct dc_dmdata_attributes attr; +}; + +struct link_increase_mst_payload_params { + struct pipe_ctx *pipe_ctx; + uint32_t mst_stream_bw; +}; + +struct link_reduce_mst_payload_params { + struct pipe_ctx *pipe_ctx; + uint32_t mst_stream_bw; +}; + +struct dp_set_test_pattern_params { + struct dc_link *link; + enum dp_test_pattern test_pattern; + enum dp_test_pattern_color_space test_pattern_color_space; + const struct link_training_settings *p_link_settings; + const unsigned char *p_custom_pattern; + unsigned int cust_pattern_size; +}; + +struct link_set_dpms_off_params { + struct pipe_ctx *pipe_ctx; +}; + +struct disable_audio_stream_params { + struct pipe_ctx *pipe_ctx; +}; + +struct prepare_bandwidth_params { + struct dc *dc; + struct dc_state *context; +}; + +struct link_set_dpms_on_params { + struct dc_state *state; + struct pipe_ctx *pipe_ctx; +}; + union block_sequence_params { struct update_plane_addr_params update_plane_addr_params; struct subvp_pipe_control_lock_fast_params subvp_pipe_control_lock_fast_params; @@ -861,6 +978,29 @@ union block_sequence_params { struct program_output_csc_params program_output_csc_params; struct hubp_set_blank_params hubp_set_blank_params; struct phantom_hubp_post_enable_params phantom_hubp_post_enable_params; + struct begin_cursor_offload_update_params begin_cursor_offload_update_params; + struct update_cursor_offload_pipe_params update_cursor_offload_pipe_params; + struct commit_cursor_offload_update_params commit_cursor_offload_update_params; + struct stream_enc_update_hdmi_info_packets_params stream_enc_update_hdmi_info_packets_params; + struct hpo_frl_stream_enc_update_hdmi_info_packets_params hpo_frl_stream_enc_update_hdmi_info_packets_params; + struct hpo_dp_stream_enc_update_dp_info_packets_sdp_line_num_params hpo_dp_stream_enc_update_dp_info_packets_sdp_line_num_params; + struct hpo_dp_stream_enc_update_dp_info_packets_params hpo_dp_stream_enc_update_dp_info_packets_params; + struct stream_enc_update_dp_info_packets_sdp_line_num_params stream_enc_update_dp_info_packets_sdp_line_num_params; + struct stream_enc_update_dp_info_packets_params stream_enc_update_dp_info_packets_params; + struct dsc_set_config_simple_params dsc_set_config_simple_params; + struct stream_enc_dp_set_dsc_config_params stream_enc_dp_set_dsc_config_params; + struct hpo_dp_stream_enc_dp_set_dsc_pps_info_packet_params hpo_dp_stream_enc_dp_set_dsc_pps_info_packet_params; + struct stream_enc_dp_set_dsc_pps_info_packet_params stream_enc_dp_set_dsc_pps_info_packet_params; + struct hpo_frl_stream_enc_set_dsc_config_params hpo_frl_stream_enc_set_dsc_config_params; + struct dp_trace_source_sequence_params dp_trace_source_sequence_params; + struct set_dmdata_attributes_params set_dmdata_attributes_params; + struct link_increase_mst_payload_params link_increase_mst_payload_params; + struct link_reduce_mst_payload_params link_reduce_mst_payload_params; + struct dp_set_test_pattern_params dp_set_test_pattern_params; + struct link_set_dpms_off_params link_set_dpms_off_params; + struct disable_audio_stream_params disable_audio_stream_params; + struct prepare_bandwidth_params prepare_bandwidth_params; + struct link_set_dpms_on_params link_set_dpms_on_params; }; enum block_sequence_func { @@ -982,6 +1122,25 @@ enum block_sequence_func { HUBP_SET_LEGACY_TILING_COMPAT_LEVEL, HUBP_SET_BLANK, PHANTOM_HUBP_POST_ENABLE, + STREAM_ENC_UPDATE_HDMI_INFO_PACKETS, + HPO_FRL_STREAM_ENC_UPDATE_HDMI_INFO_PACKETS, + HPO_DP_STREAM_ENC_UPDATE_DP_INFO_PACKETS_SDP_LINE_NUM, + HPO_DP_STREAM_ENC_UPDATE_DP_INFO_PACKETS, + STREAM_ENC_UPDATE_DP_INFO_PACKETS_SDP_LINE_NUM, + STREAM_ENC_UPDATE_DP_INFO_PACKETS, + DSC_SET_CONFIG_SIMPLE, + STREAM_ENC_DP_SET_DSC_CONFIG, + HPO_DP_STREAM_ENC_DP_SET_DSC_PPS_INFO_PACKET, + STREAM_ENC_DP_SET_DSC_PPS_INFO_PACKET, + HPO_FRL_STREAM_ENC_SET_DSC_CONFIG, + LINK_INCREASE_MST_PAYLOAD, + LINK_REDUCE_MST_PAYLOAD, + DP_TRACE_SOURCE_SEQUENCE, + DP_SET_TEST_PATTERN, + LINK_SET_DPMS_OFF, + DISABLE_AUDIO_STREAM, + PREPARE_BANDWIDTH, + LINK_SET_DPMS_ON, /* This must be the last value in this enum, add new ones above */ HWSS_BLOCK_SEQUENCE_FUNC_COUNT }; @@ -1202,6 +1361,14 @@ struct hw_sequencer_funcs { void (*get_dcc_en_bits)(struct dc *dc, int *dcc_en_bits); + enum dc_status (*setup_hdmi_frl_link)( + struct dc_link *link, + int hpo_inst, + enum clock_source_id frl_phy_clock_source_id); + + unsigned int (*get_max_dispclk_mhz)(struct dc *dc, + struct dc_state *context); + /* Idle Optimization Related */ bool (*apply_idle_power_optimizations)(struct dc *dc, bool enable); @@ -1455,6 +1622,44 @@ void hwss_dsc_calculate_and_set_config(union block_sequence_params *params); void hwss_dsc_enable_with_opp(union block_sequence_params *params); +void hwss_dsc_set_config_simple(union block_sequence_params *params); + +void hwss_stream_enc_update_hdmi_info_packets(union block_sequence_params *params); + +void hwss_hpo_frl_stream_enc_update_hdmi_info_packets(union block_sequence_params *params); + +void hwss_hpo_dp_stream_enc_update_dp_info_packets_sdp_line_num(union block_sequence_params *params); + +void hwss_hpo_dp_stream_enc_update_dp_info_packets(union block_sequence_params *params); + +void hwss_stream_enc_update_dp_info_packets_sdp_line_num(union block_sequence_params *params); + +void hwss_stream_enc_update_dp_info_packets(union block_sequence_params *params); + +void hwss_stream_enc_dp_set_dsc_config(union block_sequence_params *params); + +void hwss_hpo_dp_stream_enc_dp_set_dsc_pps_info_packet(union block_sequence_params *params); + +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); + +void hwss_link_increase_mst_payload(union block_sequence_params *params); + +void hwss_link_reduce_mst_payload(union block_sequence_params *params); + +void hwss_dp_set_test_pattern(union block_sequence_params *params); + +void hwss_link_set_dpms_off(union block_sequence_params *params); + +void hwss_prepare_bandwidth(struct dc *dc, union block_sequence_params *params); + +void hwss_link_set_dpms_on(union block_sequence_params *params); + void hwss_program_tg(union block_sequence_params *params); void hwss_tg_program_global_sync(union block_sequence_params *params); @@ -2056,4 +2261,113 @@ void hwss_add_opp_program_left_edge_extra_pixel(struct block_sequence_state *seq enum dc_pixel_encoding pixel_encoding, bool is_otg_master); +void hwss_add_hubp_enable_3dlut_fl(struct block_sequence_state *seq_state, + struct hubp *hubp); + +void hwss_add_begin_cursor_offload_update(struct block_sequence_state *seq_state, + struct dc *dc, + struct pipe_ctx *pipe_ctx); + +void hwss_add_cursor_lock(struct block_sequence_state *seq_state, + struct dc *dc, + struct pipe_ctx *pipe_ctx, + bool lock); + +void hwss_add_send_update_cursor_info_to_dmu(struct block_sequence_state *seq_state, + struct pipe_ctx *pipe_ctx, + int index); + +void hwss_add_update_cursor_offload_pipe(struct block_sequence_state *seq_state, + struct dc *dc, + struct pipe_ctx *pipe_ctx); + +void hwss_add_commit_cursor_offload_update(struct block_sequence_state *seq_state, + struct dc *dc, + struct pipe_ctx *pipe_ctx); + +void hwss_add_stream_enc_update_hdmi_info_packets(struct block_sequence_state *seq_state, + struct pipe_ctx *pipe_ctx); + +void hwss_add_hpo_frl_stream_enc_update_hdmi_info_packets(struct block_sequence_state *seq_state, + struct pipe_ctx *pipe_ctx); + +void hwss_add_hpo_dp_stream_enc_update_dp_info_packets_sdp_line_num(struct block_sequence_state *seq_state, + struct pipe_ctx *pipe_ctx); + +void hwss_add_hpo_dp_stream_enc_update_dp_info_packets(struct block_sequence_state *seq_state, + struct pipe_ctx *pipe_ctx); + +void hwss_add_stream_enc_update_dp_info_packets_sdp_line_num(struct block_sequence_state *seq_state, + struct pipe_ctx *pipe_ctx); + +void hwss_add_stream_enc_update_dp_info_packets(struct block_sequence_state *seq_state, + struct pipe_ctx *pipe_ctx); + +void hwss_add_dsc_set_config(struct block_sequence_state *seq_state, + struct display_stream_compressor *dsc, + const struct dsc_config *dsc_cfg, + const struct dsc_optc_config *dsc_optc_cfg); + +void hwss_add_stream_enc_dp_set_dsc_config(struct block_sequence_state *seq_state, + struct stream_encoder *stream_enc, + const struct dsc_optc_config *dsc_optc_cfg); + +void hwss_add_hpo_dp_stream_enc_dp_set_dsc_pps_info_packet(struct block_sequence_state *seq_state, + struct hpo_dp_stream_encoder *hpo_dp_stream_enc, + bool immediate_update, + uint8_t *dsc_packed_pps, + bool pps_sdp_stream); + +void hwss_add_stream_enc_dp_set_dsc_pps_info_packet(struct block_sequence_state *seq_state, + struct stream_encoder *stream_enc, + bool immediate_update, + uint8_t *dsc_packed_pps, + bool pps_sdp_stream); + +void hwss_add_hpo_frl_stream_enc_set_dsc_config(struct block_sequence_state *seq_state, + struct hpo_frl_stream_encoder *hpo_frl_stream_enc, + const struct dc_crtc_timing *timing, + uint8_t *dsc_packed_pps); + +void hwss_add_setup_periodic_interrupt(struct block_sequence_state *seq_state, + struct dc *dc, + struct pipe_ctx *pipe_ctx); + +void hwss_add_dp_trace_source_sequence(struct block_sequence_state *seq_state, + struct dc_link *link, + enum dpcd_source_sequence source); + +void hwss_add_set_dmdata_attributes(struct block_sequence_state *seq_state, + struct pipe_ctx *pipe_ctx); + +void hwss_add_link_increase_mst_payload(struct block_sequence_state *seq_state, + struct pipe_ctx *pipe_ctx, + uint32_t mst_stream_bw); + +void hwss_add_link_reduce_mst_payload(struct block_sequence_state *seq_state, + struct pipe_ctx *pipe_ctx, + uint32_t mst_stream_bw); + +void hwss_add_dp_set_test_pattern(struct block_sequence_state *seq_state, + struct dc_link *link, + enum dp_test_pattern test_pattern, + enum dp_test_pattern_color_space test_pattern_color_space, + const struct link_training_settings *p_link_settings, + const unsigned char *p_custom_pattern, + unsigned int cust_pattern_size); + +void hwss_add_link_set_dpms_off(struct block_sequence_state *seq_state, + struct pipe_ctx *pipe_ctx); + +void hwss_add_disable_audio_stream(struct block_sequence_state *seq_state, + struct pipe_ctx *pipe_ctx); + +void hwss_add_prepare_bandwidth(struct block_sequence_state *seq_state, + struct dc *dc, + struct dc_state *context); + +void hwss_add_link_set_dpms_on(struct block_sequence_state *seq_state, + struct dc_state *state, + struct pipe_ctx *pipe_ctx); + #endif /* __DC_HW_SEQUENCER_H__ */ diff --git a/drivers/gpu/drm/amd/display/dc/hwss/hw_sequencer_private.h b/drivers/gpu/drm/amd/display/dc/hwss/hw_sequencer_private.h index 8e3f54fb53fd6..63c6c841c681f 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/hw_sequencer_private.h +++ b/drivers/gpu/drm/amd/display/dc/hwss/hw_sequencer_private.h @@ -140,6 +140,9 @@ struct hwseq_private_funcs { struct dce_hwseq *hws, unsigned int dpp_inst, bool clock_on); + void (*hdmistream_root_clock_control)( + struct dce_hwseq *hws, + bool clock_on); void (*physymclk_root_clock_control)( struct dce_hwseq *hws, unsigned int phy_inst, diff --git a/drivers/gpu/drm/amd/display/dc/inc/core_status.h b/drivers/gpu/drm/amd/display/dc/inc/core_status.h index 82085d9c3f407..388f801f4582d 100644 --- a/drivers/gpu/drm/amd/display/dc/inc/core_status.h +++ b/drivers/gpu/drm/amd/display/dc/inc/core_status.h @@ -48,6 +48,8 @@ enum dc_status { DC_FAIL_DSC_VALIDATE = 16, DC_NO_DSC_RESOURCE = 17, DC_FAIL_UNSUPPORTED_1 = 18, + DC_FAIL_HDMI_FRL_LINK_TRAINING = 19, + DC_NO_HDMI_FRL_LINK_BANDWIDTH = 20, DC_FAIL_CLK_EXCEED_MAX = 21, DC_FAIL_CLK_BELOW_MIN = 22, /*THIS IS MIN PER IP*/ DC_FAIL_CLK_BELOW_CFG_REQUIRED = 23, /*THIS IS hard_min in PPLIB*/ diff --git a/drivers/gpu/drm/amd/display/dc/inc/core_types.h b/drivers/gpu/drm/amd/display/dc/inc/core_types.h index e960ca9062adc..ac33fa6c8da43 100644 --- a/drivers/gpu/drm/amd/display/dc/inc/core_types.h +++ b/drivers/gpu/drm/amd/display/dc/inc/core_types.h @@ -82,6 +82,9 @@ struct resource_funcs { /* Create a minimal link encoder object with no dc_link object * associated with it. */ struct link_encoder *(*link_enc_create_minimal)(struct dc_context *ctx, enum engine_id eng_id); + struct hpo_frl_link_encoder *(*hpo_frl_link_enc_create)( + enum engine_id eng_id, + struct dc_context *ctx); enum dc_status (*validate_bandwidth)( struct dc *dc, struct dc_state *context, @@ -285,6 +288,10 @@ struct resource_pool { /* Number of USB4 DPIA (DisplayPort Input Adapter) link objects created.*/ unsigned int usb4_dpia_count; + unsigned int hpo_frl_stream_enc_count; + struct hpo_frl_stream_encoder *hpo_frl_stream_enc[MAX_HDMI_FRL_ENCODERS]; + unsigned int hpo_frl_link_enc_count; + struct hpo_frl_link_encoder *hpo_frl_link_enc[MAX_HDMI_FRL_ENCODERS]; unsigned int hpo_dp_stream_enc_count; struct hpo_dp_stream_encoder *hpo_dp_stream_enc[MAX_HPO_DP2_ENCODERS]; unsigned int hpo_dp_link_enc_count; @@ -351,6 +358,7 @@ struct stream_resource { struct display_stream_compressor *dsc; struct timing_generator *tg; struct stream_encoder *stream_enc; + struct hpo_frl_stream_encoder *hpo_frl_stream_enc; struct hpo_dp_stream_encoder *hpo_dp_stream_enc; struct audio *audio; @@ -394,6 +402,7 @@ struct plane_resource { struct link_resource { struct link_encoder *dio_link_enc; struct hpo_dp_link_encoder *hpo_dp_link_enc; + struct hpo_frl_link_encoder *hpo_frl_link_enc; }; struct link_config { @@ -531,6 +540,9 @@ struct resource_context { struct link_enc_cfg_context link_enc_cfg_ctx; unsigned int dio_link_enc_to_link_idx[MAX_LINK_ENCODERS]; int dio_link_enc_ref_cnts[MAX_LINK_ENCODERS]; + bool is_hpo_frl_stream_enc_acquired[MAX_HDMI_FRL_ENCODERS]; + unsigned int hpo_frl_link_enc_to_link_idx[MAX_HDMI_FRL_ENCODERS]; + int hpo_frl_link_enc_ref_cnts[MAX_HDMI_FRL_ENCODERS]; bool is_hpo_dp_stream_enc_acquired[MAX_HPO_DP2_ENCODERS]; unsigned int hpo_dp_link_enc_to_link_idx[MAX_HPO_DP2_LINK_ENCODERS]; int hpo_dp_link_enc_ref_cnts[MAX_HPO_DP2_LINK_ENCODERS]; diff --git a/drivers/gpu/drm/amd/display/dc/inc/hw/dccg.h b/drivers/gpu/drm/amd/display/dc/inc/hw/dccg.h index d6f5e01a0b66c..6db7c8753081d 100644 --- a/drivers/gpu/drm/amd/display/dc/inc/hw/dccg.h +++ b/drivers/gpu/drm/amd/display/dc/inc/hw/dccg.h @@ -228,6 +228,16 @@ struct dccg_funcs { void (*allow_clock_gating)(struct dccg *dccg, bool allow); void (*enable_memory_low_power)(struct dccg *dccg, bool enable); bool (*is_s0i3_golden_init_wa_done)(struct dccg *dccg); + void (*enable_hdmicharclk)(struct dccg *dccg, int hpo_inst, int phypll_inst); + void (*disable_hdmicharclk)(struct dccg *dccg, int hpo_inst); + void (*set_hdmistreamclk)( + struct dccg *dccg, + enum streamclk_source src, + uint32_t otg_inst); + + void (*set_hdmistreamclk_root_clock_gating)( + struct dccg *dccg, + bool enable); void (*set_dpstreamclk_root_clock_gating)( struct dccg *dccg, int dp_hpo_inst, diff --git a/drivers/gpu/drm/amd/display/dc/inc/hw/hw_shared.h b/drivers/gpu/drm/amd/display/dc/inc/hw/hw_shared.h index 51581c10fd6bf..176d048a64e70 100644 --- a/drivers/gpu/drm/amd/display/dc/inc/hw/hw_shared.h +++ b/drivers/gpu/drm/amd/display/dc/inc/hw/hw_shared.h @@ -76,6 +76,7 @@ #define MAX_LINK_ENCODERS (MAX_DIG_LINK_ENCODERS + MAX_DAC_LINK_ENCODERS) #define MAX_DWB_PIPES 1 +#define MAX_HDMI_FRL_ENCODERS 2 #define MAX_HPO_DP2_ENCODERS 4 #define MAX_HPO_DP2_LINK_ENCODERS 4 diff --git a/drivers/gpu/drm/amd/display/dc/inc/hw/link_encoder.h b/drivers/gpu/drm/amd/display/dc/inc/hw/link_encoder.h index 5abbf485d273e..e7727ed3300cd 100644 --- a/drivers/gpu/drm/amd/display/dc/inc/hw/link_encoder.h +++ b/drivers/gpu/drm/amd/display/dc/inc/hw/link_encoder.h @@ -67,6 +67,13 @@ struct encoder_feature_support { uint32_t IS_UHBR13_5_CAPABLE:1; uint32_t IS_UHBR20_CAPABLE:1; uint32_t DP_IS_USB_C:1; + uint32_t IS_HDMI_FRL_CAPABLE:1; + uint32_t IS_FRL_8G_CAPABLE:1; + uint32_t IS_FRL_10G_CAPABLE:1; + uint32_t IS_FRL_12G_CAPABLE:1; + uint32_t IS_FRL_16G_CAPABLE:1; + uint32_t IS_FRL_20G_CAPABLE:1; + uint32_t IS_FRL_24G_CAPABLE:1; } bits; uint32_t raw; } flags; @@ -78,6 +85,11 @@ struct encoder_feature_support { bool fec_supported; }; +enum phy_source_select { + PHY_SOURCE_DIG, + PHY_SOURCE_HPO +}; + struct link_encoder { const struct link_encoder_funcs *funcs; int32_t aux_channel_offset; @@ -93,6 +105,7 @@ struct link_encoder { struct gpio *hpd_gpio; enum hpd_source_id hpd_source; bool usbc_combo_phy; + uint8_t txffe_state; }; struct link_enc_state { @@ -104,6 +117,12 @@ struct link_enc_state { }; +struct frl_txffe { + uint32_t amplitude[4]; + uint32_t pre_emphasis[4]; + uint32_t post_emphasis[4]; +}; + enum encoder_type_select { ENCODER_TYPE_DIG = 0, ENCODER_TYPE_HDMI_FRL = 1, @@ -172,6 +191,43 @@ struct link_encoder_funcs { enum signal_type (*get_dig_mode)( struct link_encoder *enc); + void (*dpcstx_set_order_invert_18_bit)( + struct link_encoder *enc, + bool invert); + + void (*set_phy_source)( + struct link_encoder *enc, + enum phy_source_select src_sel, + uint32_t hpo_inst); + + void (*dpcs_initialize_phy)( + struct link_encoder *enc, + uint32_t hpo_inst, + enum hdmi_frl_link_rate frl_link_rate); + + void (*dpcs_configure_phypll)( + struct link_encoder *enc, + uint32_t hpo_inst, + enum hdmi_frl_link_rate frl_link_rate); + + void (*dpcs_configure_dpcs)( + struct link_encoder *enc); + + void (*dpcs_enable_dpcs)( + struct link_encoder *enc); + void (*prog_eq_setting)( + struct link_encoder *enc, + uint8_t FFE_Level, + bool de_emphasis_only, + bool pre_shoot_only, + bool no_ffe, + const struct dc_hdmi_frl_link_settings *link_settings); + void (*get_txffe)( + struct link_encoder *enc, + struct frl_txffe *lane_settings); + void (*set_txffe)( + struct link_encoder *enc, + struct frl_txffe *lane_settings); void (*set_dio_phy_mux)( struct link_encoder *enc, enum encoder_type_select sel, @@ -207,6 +263,126 @@ enum link_enc_cfg_mode { LINK_ENC_CFG_TRANSIENT /* During commit state - use state to be committed. */ }; +struct hpo_frl_link_encoder { + const struct hpo_frl_link_encoder_funcs *funcs; + struct dc_context *ctx; + int inst; +}; + +/** + * @hpo_frl_link_enc_state - FRL data from the device + * + * This struct is used to store FRL information retrieved from the hardware. + * This is used as a parameter for the read_state function. + */ +struct hpo_frl_link_enc_state { + /** + * @link_enc_enabled: 0 means disable and 1 enabled. + */ + uint32_t link_enc_enabled; + + /** + * @link_active: + * + * If link training is enable this field should be set to 1. + */ + uint32_t link_active; + + /** + * @lane_count: FRL lane count. + */ + uint32_t lane_count; +}; + +/*** + * @hpo_frl_link_encoder_funcs - FRL encoder functions + * + * DC handles FRL as an encoder; each ASIC may have some peculiarities in + * setting FRL. Thus, this struct, adds all the necessary callbacks that each + * DCN version must implement. + */ +struct hpo_frl_link_encoder_funcs { + /** + * @setup_link_encoder: + * + * This function is responsible for setup the ASIC to use FRL, i.e., it + * contains a register configuration. This function implementation + * expects the enablement of the link clock, lane count configuration, + * any reset/cleanup, and, finally, the enablement of the link. + */ + void (*setup_link_encoder)(struct hpo_frl_link_encoder *enc, + int lane_count); + + /** + * @disable_link_encoder: + * + * Disable the FRL link. Note that this function must do the reverse of + * the setup_link_encoder. + */ + void (*disable_link_encoder)(struct hpo_frl_link_encoder *enc); + + /** + * @set_hdmi_training_pattern: + * + * Register level configuration for each lane. + */ + void (*set_hdmi_training_pattern)(struct hpo_frl_link_encoder *enc, + uint32_t lane0_pattern, + uint32_t lane1_pattern, + uint32_t lane2_pattern, + uint32_t lane3_pattern); + + /** + * @get_hdmi_training_pattern: + * + * Retrieve from the registers each of the lane pattern configurations. + */ + void (*get_hdmi_training_pattern)(struct hpo_frl_link_encoder *enc, + uint32_t *lane0_pattern, + uint32_t *lane1_pattern, + uint32_t *lane2_pattern, + uint32_t *lane3_pattern); + + /** + * @enable_frl_phy_output: + * + * Based on the parameters, this function should fill out the + * bp_transmitter_control struct and use it to enable the FRL PHY link + * via VBIOS. + */ + void (*enable_frl_phy_output)(struct hpo_frl_link_encoder *hpo_enc, + struct link_encoder *enc, + enum clock_source_id clock_source, + enum hdmi_frl_link_rate frl_link_rate); + + /** + * @enable_output: + * + * Enable FRL by sending the enable packet training. + */ + void (*enable_output)(struct hpo_frl_link_encoder *enc); + + /** + * @read_state: + * + * Get the FRL information from registers and fill it out in the + * hpo_frl_link_enc_state struct. + */ + void (*read_state)(struct hpo_frl_link_encoder *enc, + struct hpo_frl_link_enc_state *state); + + /** + * @destroy: + * + * Destroy encoder object. + */ + void (*destroy)(struct hpo_frl_link_encoder **enc); + + void (*apply_vsdb_rcc_wa)( + struct hpo_frl_link_encoder *enc); + +}; + enum dp2_link_mode { DP2_LINK_TRAINING_TPS1, DP2_LINK_TRAINING_TPS2, 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 27f950ae45ee5..570c45c7830f4 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 @@ -71,6 +71,16 @@ enum dynamic_metadata_mode { dmdata_dolby_vision }; +struct frl_audio_clock_info { + uint32_t frl_character_clock_kHz; + uint32_t n_32khz; + uint32_t cts_32khz; + uint32_t n_44khz; + uint32_t cts_44khz; + uint32_t n_48khz; + uint32_t cts_48khz; +}; + struct enc_sdp_line_num { /* Adaptive Sync SDP */ bool adaptive_sync_line_num_valid; @@ -277,6 +287,227 @@ struct stream_encoder_funcs { uint32_t (*get_pixels_per_cycle)(struct stream_encoder *enc); }; +/** + * @hpo_frl_stream_encoder_state - Stream encoder parameters + */ +struct hpo_frl_stream_encoder_state { + uint32_t stream_enc_enabled; + uint32_t otg_inst; + uint32_t color_depth; + uint32_t num_odm_segments; + uint32_t h_active; + uint32_t h_blank; + uint32_t borrow_mode; + enum dc_pixel_encoding pixel_format; +}; + +/** + * @hpo_frl_stream_encoder - Encoder stream instance + * + * This struct keeps the reference to the struct with the FRL stream encoder + * callbacks. Additionally, it has other references that simplify the stream + * configuration. + */ +struct hpo_frl_stream_encoder { + /** + * @funcs: callback functions for using FRL stream encoder. + */ + const struct hpo_frl_stream_encoder_funcs *funcs; + + /** + * @stream_enc_inst: Instance ID reference. + */ + uint32_t stream_enc_inst; + + /** + * @ctx: DC context. + */ + struct dc_context *ctx; + + /** + * @bp: Bios parser reference. + */ + struct dc_bios *bp; + + /** + * @id: ID to the Engine object type. + */ + enum engine_id id; + + /** + * @afmt: + * + * Audio Formatter (AFMT) reference used for select the correct audio + * reference. + */ + struct afmt *afmt; + + /** + * @vpg: + * + * The VBI Packet Generator (VPG) reference which is used for + * generating the HDMI data island packet headers for ISRC1 and generic + * packages. + */ + struct vpg *vpg; + + /** + * @afmt: + * + * Audio Pattern Generator (APG) reference used for select the correct audio + * reference. + */ + struct apg *apg; +}; + +/** + * @hpo_frl_stream_encoder_funcs - FRL stream encoder functions callbacks + * + * DC must set up the FRL encoder and the stream encoder; however, each ASIC + * may have some quirks in setting FRL. Thus, this struct, adds all the 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: + * + * This callback initializes a new FRL stream by enabling HDMI Dispclk, + * SOCCLK, and HDMI stream clock. Additionally, this callback is also + * used to initialize some debug options, such as the CRC validation. + * Finally, it must setup the OTG instance. + */ + void (*hdmi_frl_enable)(struct hpo_frl_stream_encoder *enc, + int otg_inst); + + /** + * @hdmi_frl_unblank: + * + * Ensure that the FIFO video stream is active, reset the necessary + * FIFO registers, enable HDMI tribyte encoder, and finally, adjust the + * clock ramp registers for FIFO. Notice that all the configuration + * made by this function it is at the register level. + */ + void (*hdmi_frl_unblank)(struct hpo_frl_stream_encoder *enc, + int otg_inst); + + /** + * @hdmi_frl_blank: + * + * This callback is a register-level configuration that must disable + * the clock ramp adjuster FIFO, disable the HDMI Tribyte encoder, and + * disable the stream clocks (dispclk, socclk, and hdmistreamclk). + */ + void (*hdmi_frl_blank)(struct hpo_frl_stream_encoder *enc); + + /** + * @hdmi_frl_fifo_odm_enabled: + * + * This callback checks if the FIFO ODM Combine mode is enabled + */ + bool (*hdmi_frl_fifo_odm_enabled)(struct hpo_frl_stream_encoder *enc); + + /** + * @hdmi_frl_set_stream_attribute: + * + * This callback should be invoked only after the link is trained. The + * implementation should configure the pixel encode, color depth, ODM + * mode, configure horizontal blank/active size, configure borrow + * parameters, enable general control packet, enable/setup audio, and + * AVMute. + */ + void (*hdmi_frl_set_stream_attribute)(struct hpo_frl_stream_encoder *enc, + struct dc_crtc_timing *crtc_timing, + struct frl_borrow_params *borrow_params, + int odm_combine_num_segments); + + /** + * @update_hdmi_info_packets: + * + * Update the HDMI packet control option. + */ + void (*update_hdmi_info_packets)(struct hpo_frl_stream_encoder *enc, + const struct encoder_info_frame *info_frame); + + /** + * @stop_hdmi_info_packets: + * + * Update HDMI info packet (avi, vendoer, gamut, spd, hdrsmd, hfvsif, + * vtem, etc). + */ + void (*stop_hdmi_info_packets)(struct hpo_frl_stream_encoder *enc); + + /** + * @audio_mute_control: + * + * Just mute the audio. + */ + void (*audio_mute_control)(struct hpo_frl_stream_encoder *enc, + bool mute); + + /** + * @hdmi_audio_setup: + * + * Setup HDMI audio based on the Azila info. + */ + void (*hdmi_audio_setup)(struct hpo_frl_stream_encoder *enc, + unsigned int az_inst, + struct audio_info *info, + struct audio_crtc_info *audio_crtc_info); + + /** + * @hdmi_audio_disable: + * + * Disable audio. + */ + void (*hdmi_audio_disable)(struct hpo_frl_stream_encoder *enc); + + /** + * @set_avmute: + * + * Disable AVmute at the register-level. + */ + void (*set_avmute)(struct hpo_frl_stream_encoder *enc, bool enable); + + /** + * @validate_hdmi_frl_output: + * + * Validate FRL inputs, DSC, audio parameters, FRL capacity, and borrow + * parameters. + */ + bool (*validate_hdmi_frl_output)(struct hpo_frl_stream_encoder *enc, + const struct dc_crtc_timing *timing, + const struct audio_check *audio, + struct dc_hdmi_frl_link_settings *frl_link_settings, + unsigned int dsc_max_rate); + + /** + * @read_state: + * + * Fill out the hpo_frl_stream_encoder_state with the info retrieved + * from ASIC registers. + */ + void (*read_state)(struct hpo_frl_stream_encoder *enc, + struct hpo_frl_stream_encoder_state *state); + + /** + * @set_dynamic_metadata: + * + * Metadata configuration that sets: + * - Using Enfine or disable DME. + * - HUBP setup for the physical instance that has the DME enabled. + * - Metadata packet type. + * - Ensure OTG master update locks in the changing DME configuration. + */ + void (*set_dynamic_metadata)(struct hpo_frl_stream_encoder *enc, + bool enable_dme, + uint32_t hubp_requestor_id, + enum dynamic_metadata_mode dmdata_mode); +}; + struct hpo_dp_stream_encoder_state { uint32_t stream_enc_enabled; uint32_t vid_stream_enabled; diff --git a/drivers/gpu/drm/amd/display/dc/inc/hw/timing_generator.h b/drivers/gpu/drm/amd/display/dc/inc/hw/timing_generator.h index 2f70bb476c97b..8f72322aab4bd 100644 --- a/drivers/gpu/drm/amd/display/dc/inc/hw/timing_generator.h +++ b/drivers/gpu/drm/amd/display/dc/inc/hw/timing_generator.h @@ -106,6 +106,7 @@ enum crc_selection { enum otg_out_mux_dest { OUT_MUX_DIO = 0, + OUT_MUX_HPO_FRL = 1, OUT_MUX_HPO_DP = 2, }; @@ -493,6 +494,10 @@ struct timing_generator_funcs { void (*set_out_mux)(struct timing_generator *tg, enum otg_out_mux_dest dest); void (*set_drr_trigger_window)(struct timing_generator *optc, uint32_t window_start, uint32_t window_end); + int (*set_fva_factor)(struct timing_generator *optc, struct fva_adj *fva_adj); + int (*get_max_hw_supported_fva_factor)(struct timing_generator *optc, + struct dc_crtc_timing *timing, + unsigned int max_pixclk_100hz); void (*set_vtotal_change_limit)(struct timing_generator *optc, uint32_t limit); void (*align_vblanks)(struct timing_generator *master_optc, diff --git a/drivers/gpu/drm/amd/display/dc/inc/link_service.h b/drivers/gpu/drm/amd/display/dc/inc/link_service.h index 57bb82e949427..aaef747552260 100644 --- a/drivers/gpu/drm/amd/display/dc/inc/link_service.h +++ b/drivers/gpu/drm/amd/display/dc/inc/link_service.h @@ -144,6 +144,10 @@ struct link_service { enum dc_status (*validate_dp_tunnel_bandwidth)( const struct dc *dc, const struct dc_state *new_ctx); + uint32_t (*frl_link_bandwidth_kbps)(enum hdmi_frl_link_rate link_rate); + bool (*frl_margin_check_uncompressed_video)( + struct frl_cap_chk_params_fixed31_32 *params, + struct frl_cap_chk_intermediates_fixed31_32 *inter); uint32_t (*dp_required_hblank_size_bytes)( const struct dc_link *link, @@ -164,6 +168,7 @@ struct link_service { void (*set_dsc_on_stream)(struct pipe_ctx *pipe_ctx, bool enable); bool (*set_dsc_enable)(struct pipe_ctx *pipe_ctx, bool enable); bool (*update_dsc_config)(struct pipe_ctx *pipe_ctx); + void (*wait_for_unlocked)(struct dc_link *link); /*************************** DDC **************************************/ @@ -311,6 +316,16 @@ struct link_service { void (*edp_set_panel_power)(struct dc_link *link, bool powerOn); + /*************************** HDMI FRL *********************************/ + bool (*hdmi_frl_poll_status_flag)(struct dc_link *link); + struct dc_hdmi_frl_link_settings *(*hdmi_frl_get_verified_link_cap)( + struct dc_link *link); + void (*hdmi_frl_set_preferred_link_settings)(struct dc *dc, + struct dc_hdmi_frl_link_settings *link_setting, + struct dc_hdmi_frl_link_training_overrides *lt_overrides, + struct dc_link *link); + + /*************************** DP CTS ************************************/ void (*dp_handle_automated_test)(struct dc_link *link); bool (*dp_set_test_pattern)( diff --git a/drivers/gpu/drm/amd/display/dc/inc/resource.h b/drivers/gpu/drm/amd/display/dc/inc/resource.h index cecd3282a29f7..6352e078b1323 100644 --- a/drivers/gpu/drm/amd/display/dc/inc/resource.h +++ b/drivers/gpu/drm/amd/display/dc/inc/resource.h @@ -57,6 +57,7 @@ struct resource_caps { int num_dsc; unsigned int num_dig_link_enc; // Total number of DIGs (digital encoders) in DIO (Display Input/Output). unsigned int num_usb4_dpia; // Total number of USB4 DPIA (DisplayPort Input Adapters). + int num_hpo_frl; int num_hpo_dp_stream_encoder; int num_hpo_dp_link_encoder; int num_mpc_3dlut; @@ -86,6 +87,10 @@ struct resource_create_funcs { struct stream_encoder *(*create_stream_encoder)( enum engine_id eng_id, struct dc_context *ctx); + struct hpo_frl_stream_encoder *(*create_hpo_frl_stream_encoder)( + enum engine_id eng_id, struct dc_context *ctx); + struct hpo_frl_link_encoder *(*create_hpo_frl_link_encoder)( + enum engine_id eng_id, struct dc_context *ctx); struct hpo_dp_stream_encoder *(*create_hpo_dp_stream_encoder)( enum engine_id eng_id, struct dc_context *ctx); struct hpo_dp_link_encoder *(*create_hpo_dp_link_encoder)( @@ -604,6 +609,8 @@ unsigned int resource_pixel_format_to_bpp(enum surface_pixel_format format); bool get_temp_dp_link_res(struct dc_link *link, struct link_resource *link_res, struct dc_link_settings *link_settings); +bool get_temp_frl_link_res(struct dc_link *link, + struct link_resource *link_res); void reset_syncd_pipes_from_disabled_pipes(struct dc *dc, struct dc_state *context); diff --git a/drivers/gpu/drm/amd/display/dc/link/Makefile b/drivers/gpu/drm/amd/display/dc/link/Makefile index 0f3670e302325..f558a20eceaa7 100644 --- a/drivers/gpu/drm/amd/display/dc/link/Makefile +++ b/drivers/gpu/drm/amd/display/dc/link/Makefile @@ -45,6 +45,7 @@ AMD_DISPLAY_FILES += $(AMD_DAL_LINK_ACCESSORIES) LINK_HWSS = link_hwss_dio.o link_hwss_dpia.o link_hwss_hpo_dp.o \ link_hwss_dio_fixed_vs_pe_retimer.o link_hwss_hpo_fixed_vs_pe_retimer_dp.o \ link_hwss_virtual.o +LINK_HWSS += link_hwss_hpo_frl.o AMD_DAL_LINK_HWSS = $(addprefix $(AMDDALPATH)/dc/link/hwss/, \ $(LINK_HWSS)) @@ -58,6 +59,7 @@ link_dp_training.o link_dp_training_8b_10b.o link_dp_training_128b_132b.o \ link_dp_training_dpia.o link_dp_training_auxless.o \ link_dp_training_fixed_vs_pe_retimer.o link_dp_phy.o link_dp_capability.o \ link_edp_panel_control.o link_dp_panel_replay.o link_dp_irq_handler.o link_dp_dpia_bw.o +LINK_PROTOCOLS += link_hdmi_frl.o AMD_DAL_LINK_PROTOCOLS = $(addprefix $(AMDDALPATH)/dc/link/protocols/, \ $(LINK_PROTOCOLS)) diff --git a/drivers/gpu/drm/amd/display/dc/link/hwss/link_hwss_hpo_frl.c b/drivers/gpu/drm/amd/display/dc/link/hwss/link_hwss_hpo_frl.c new file mode 100644 index 0000000000000..a2c0fa21ee948 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/link/hwss/link_hwss_hpo_frl.c @@ -0,0 +1,113 @@ +/* + * Copyright 2022 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ +#include "link_hwss_hpo_frl.h" +#include "core_types.h" +#include "link/hwss/link_hwss_virtual.h" + +static void setup_hpo_frl_stream_attribute(struct pipe_ctx *pipe_ctx) +{ + struct hpo_frl_stream_encoder *stream_enc = pipe_ctx->stream_res.hpo_frl_stream_enc; + struct dc_stream_state *stream = pipe_ctx->stream; + struct pipe_ctx *odm_pipe; + struct dc *dc = stream->link->ctx->dc; + struct dc_stream_state *temp_stream = &dc->scratch.temp_stream; + int odm_combine_num_segments = 1; + + memcpy(temp_stream, stream, sizeof(struct dc_stream_state)); + + /* Modify patched_crtc_timing as required for padding */ + if (pipe_ctx->dsc_padding_params.dsc_hactive_padding) { + temp_stream->timing.h_addressable = stream->timing.h_addressable + pipe_ctx->dsc_padding_params.dsc_hactive_padding; + temp_stream->timing.h_total = stream->timing.h_total + pipe_ctx->dsc_padding_params.dsc_htotal_padding; + } + + /* get number of ODM combine input segments */ + for (odm_pipe = pipe_ctx->next_odm_pipe; odm_pipe; odm_pipe = odm_pipe->next_odm_pipe) + odm_combine_num_segments++; + + stream_enc->funcs->hdmi_frl_set_stream_attribute( + stream_enc, + &temp_stream->timing, + &stream->link->frl_link_settings.borrow_params, + odm_combine_num_segments); +} + +static void disable_hpo_frl_link_output(struct dc_link *link, + const struct link_resource *link_res, + enum signal_type signal) +{ + (void)link_res; + if (dc_is_hdmi_frl_signal(signal)) + link->hpo_frl_link_enc->funcs->disable_link_encoder(link->hpo_frl_link_enc); + link->link_enc->funcs->disable_output(link->link_enc, signal); +} + +static void setup_hpo_frl_audio_output(struct pipe_ctx *pipe_ctx, + struct audio_output *audio_output, uint32_t audio_inst) +{ + pipe_ctx->stream_res.hpo_frl_stream_enc->funcs->hdmi_audio_setup( + pipe_ctx->stream_res.hpo_frl_stream_enc, + audio_inst, + &pipe_ctx->stream->audio_info, + &audio_output->crtc_info); +} + +static void enable_hpo_frl_audio_packet(struct pipe_ctx *pipe_ctx) +{ + pipe_ctx->stream_res.hpo_frl_stream_enc->funcs->audio_mute_control( + pipe_ctx->stream_res.hpo_frl_stream_enc, false); +} + +static void disable_hpo_frl_audio_packet(struct pipe_ctx *pipe_ctx) +{ + pipe_ctx->stream_res.hpo_frl_stream_enc->funcs->audio_mute_control( + pipe_ctx->stream_res.hpo_frl_stream_enc, true); + + if (pipe_ctx->stream_res.audio) + pipe_ctx->stream_res.hpo_frl_stream_enc->funcs->hdmi_audio_disable( + pipe_ctx->stream_res.hpo_frl_stream_enc); +} + +static const struct link_hwss hpo_frl_link_hwss = { + .setup_stream_encoder = virtual_setup_stream_encoder, + .reset_stream_encoder = virtual_reset_stream_encoder, + .setup_stream_attribute = setup_hpo_frl_stream_attribute, + .disable_link_output = disable_hpo_frl_link_output, + .setup_audio_output = setup_hpo_frl_audio_output, + .enable_audio_packet = enable_hpo_frl_audio_packet, + .disable_audio_packet = disable_hpo_frl_audio_packet, +}; + +bool can_use_hpo_frl_link_hwss(const struct dc_link *link, + const struct link_resource *link_res) +{ + (void)link; + return link_res->hpo_frl_link_enc != NULL; +} + +const struct link_hwss *get_hpo_frl_link_hwss(void) +{ + return &hpo_frl_link_hwss; +} diff --git a/drivers/gpu/drm/amd/display/dc/link/hwss/link_hwss_hpo_frl.h b/drivers/gpu/drm/amd/display/dc/link/hwss/link_hwss_hpo_frl.h new file mode 100644 index 0000000000000..ea8d9760132fd --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/link/hwss/link_hwss_hpo_frl.h @@ -0,0 +1,34 @@ +/* + * Copyright 2022 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ +#ifndef __LINK_HWSS_HPO_FRL_H__ +#define __LINK_HWSS_HPO_FRL_H__ + +#include "link_hwss.h" + +bool can_use_hpo_frl_link_hwss(const struct dc_link *link, + const struct link_resource *link_res); +const struct link_hwss *get_hpo_frl_link_hwss(void); + +#endif /* __LINK_HWSS_HPO_FRL_H__ */ diff --git a/drivers/gpu/drm/amd/display/dc/link/link_detection.c b/drivers/gpu/drm/amd/display/dc/link/link_detection.c index 794dd6a959183..78d50c3fbffe8 100644 --- a/drivers/gpu/drm/amd/display/dc/link/link_detection.c +++ b/drivers/gpu/drm/amd/display/dc/link/link_detection.c @@ -41,6 +41,7 @@ #include "protocols/link_dp_dpia.h" #include "protocols/link_dp_phy.h" #include "protocols/link_dp_training.h" +#include "protocols/link_hdmi_frl.h" #include "protocols/link_dp_dpia_bw.h" #include "accessories/link_dp_trace.h" @@ -78,6 +79,7 @@ static enum ddc_transaction_type get_ddc_transaction_type(enum signal_type sink_ case SIGNAL_TYPE_DVI_SINGLE_LINK: case SIGNAL_TYPE_DVI_DUAL_LINK: case SIGNAL_TYPE_HDMI_TYPE_A: + case SIGNAL_TYPE_HDMI_FRL: case SIGNAL_TYPE_LVDS: case SIGNAL_TYPE_RGB: transaction_type = DDC_TRANSACTION_TYPE_I2C; @@ -769,6 +771,60 @@ static bool should_prepare_phy_clocks_for_link_verification(const struct dc *dc, return !can_apply_seamless_boot && reason != DETECT_REASON_BOOT; } +static bool is_hdmi_frl_in_use(struct dc_link *link) +{ + int i; + unsigned int hdmi_conn_count = 0; + unsigned int hdmi_stream_count = 0; + bool hdmi_frl_in_use = false; + bool incoming_link_identical = false; + + /*Enumerate HDMI connector from all present links */ + for (i = 0; i < link->dc->link_count; i++) { + if (link->dc->links[i] != NULL && + dc_is_hdmi_signal(link->dc->links[i]->connector_signal)) + hdmi_conn_count++; + } + /* If less than 2 HDMI Connector, assume HPO is always available*/ + if (hdmi_conn_count < 2) + return false; + + /*Enumerate existing HDMI stream count*/ + for (i = 0; i < link->dc->current_state->stream_count; i++) { + if (dc_is_hdmi_signal(link->dc->current_state->streams[i]->signal)) + hdmi_stream_count++; + if (link == link->dc->current_state->streams[i]->link && + (dc_is_hdmi_frl_signal(link->dc->current_state->streams[i]->signal))) + incoming_link_identical = true; + } + + if (hdmi_stream_count > 1 || (hdmi_stream_count == 1 && !incoming_link_identical)) { + for (i = 0; i < link->dc->current_state->stream_count; i++) { + if (dc_is_hdmi_frl_signal( + link->dc->current_state->streams[i]->signal)) { + hdmi_frl_in_use = true; + break; + } + } + } + + /* Check if previous link already has been assigned with FRL*/ + if (!hdmi_frl_in_use && !incoming_link_identical) { + for (i = 0; i < link->dc->link_count; i++) { + if (link->dc->links[i] != NULL && + link->dc->links[i]->local_sink != NULL && + link != link->dc->links[i] && + dc_is_hdmi_frl_signal( + link->dc->links[i]->local_sink->sink_signal)) { + hdmi_frl_in_use = true; + break; + } + } + } + + return hdmi_frl_in_use; +} + static void prepare_phy_clocks_for_destructive_link_verification(const struct dc *dc) { dc_z10_restore(dc); @@ -796,6 +852,16 @@ static void verify_link_capability_destructive(struct dc_link *link, dp_verify_link_cap_with_retries( link, &known_limit_link_setting, LINK_TRAINING_MAX_VERIFY_RETRY); + } else if (dc_is_hdmi_signal(link->local_sink->sink_signal)) { + if (!is_hdmi_frl_in_use(link)) { + link_set_all_streams_dpms_off_for_link(link); + hdmi_frl_verify_link_cap(link, &link->frl_reported_link_cap); + link->local_sink->sink_signal = (link->frl_verified_link_cap.frl_link_rate != HDMI_FRL_LINK_RATE_DISABLE) + ? SIGNAL_TYPE_HDMI_FRL : SIGNAL_TYPE_HDMI_TYPE_A; + } else { + link->local_sink->sink_signal = SIGNAL_TYPE_HDMI_TYPE_A; + link->frl_verified_link_cap.frl_link_rate = HDMI_FRL_LINK_RATE_DISABLE; + } } else { ASSERT(0); } @@ -815,6 +881,13 @@ static void verify_link_capability_non_destructive(struct dc_link *link) link->verified_link_cap = link->reported_link_cap; else link->verified_link_cap = dp_get_max_link_cap(link); + } else if (dc_is_hdmi_signal(link->local_sink->sink_signal)) { + link->verified_link_cap = link->reported_link_cap; + + if (is_hdmi_frl_in_use(link)) { + link->local_sink->sink_signal = SIGNAL_TYPE_HDMI_TYPE_A; + link->frl_verified_link_cap.frl_link_rate = HDMI_FRL_LINK_RATE_DISABLE; + } } } @@ -850,6 +923,27 @@ static bool should_verify_link_capability_destructively(struct dc_link *link, destrictive = false; } } + } else if (dc_is_hdmi_signal(link->local_sink->sink_signal) && link->link_enc && + link->link_enc->features.flags.bits.IS_HDMI_FRL_CAPABLE && + link->local_sink->edid_caps.max_frl_rate != 0) { + int i = 0; + struct pipe_ctx *pipes = + link->dc->current_state->res_ctx.pipe_ctx; + + destrictive = true; + if (is_hdmi_frl_in_use(link)) { + destrictive = false; + } else if (link->dc->config.skip_frl_pretraining) { + for (i = 0; i < MAX_PIPES; i++) { + if (pipes[i].stream != NULL && + pipes[i].stream->link == link) { + /*If link is already active, skip PHY programming*/ + if (link->link_status.link_active) { + destrictive = false; + } + } + } + } } return destrictive; @@ -1007,6 +1101,7 @@ static bool detect_link_and_local_sink(struct dc_link *link, /* From Disconnected-to-Connected. */ switch (link->connector_signal) { + case SIGNAL_TYPE_HDMI_FRL: case SIGNAL_TYPE_HDMI_TYPE_A: { sink_caps.transaction_type = DDC_TRANSACTION_TYPE_I2C; if (aud_support->hdmi_audio_native) @@ -1234,6 +1329,13 @@ static bool detect_link_and_local_sink(struct dc_link *link, if (dc_is_hdmi_signal(link->connector_signal)) read_scdc_caps(link->ddc, link->local_sink); + if (dc_is_hdmi_signal(link->connector_signal) && dc->debug.enable_hdmi_idcc) { + memset(&link->hdmi_cable_id, 0, sizeof(union hdmi_idcc_cable_id)); + read_idcc_data(link->ddc, HDMI_IDCC_SCOPE_RW_CA, + link->hdmi_cable_id.raw, 0, 4); + } + if (sink->edid_caps.rr_capable) + hdmi_frl_write_read_request_enable(link->ddc); /* When FreeSync is toggled through OSD, * we see same EDID no matter what. Check MCCS caps * to see if we should update FreeSync caps now. @@ -1248,6 +1350,12 @@ static bool detect_link_and_local_sink(struct dc_link *link, same_edid = false; } + if (reason != DETECT_REASON_FALLBACK && dc_is_hdmi_signal(link->connector_signal) && + link->link_enc->features.flags.bits.IS_HDMI_FRL_CAPABLE && sink->edid_caps.max_frl_rate != 0) { + hdmi_frl_retrieve_link_cap(link, link->local_sink); + } + if (reason == DETECT_REASON_FALLBACK && sink->sink_signal == SIGNAL_TYPE_HDMI_FRL) + same_edid = false; if (link->connector_signal == SIGNAL_TYPE_DISPLAY_PORT && sink_caps.transaction_type == DDC_TRANSACTION_TYPE_I2C_OVER_AUX) { @@ -1262,6 +1370,8 @@ static bool detect_link_and_local_sink(struct dc_link *link, link_disconnect_remap(prev_sink, link); sink = prev_sink; prev_sink = NULL; + if (reason == DETECT_REASON_FALLBACK && sink->sink_signal == SIGNAL_TYPE_HDMI_FRL) + sink->sink_signal = SIGNAL_TYPE_HDMI_TYPE_A; } if (!sink->edid_caps.analog) @@ -1529,6 +1639,7 @@ bool link_is_hdcp22(struct dc_link *link, enum signal_type signal) case SIGNAL_TYPE_DVI_SINGLE_LINK: case SIGNAL_TYPE_DVI_DUAL_LINK: case SIGNAL_TYPE_HDMI_TYPE_A: + case SIGNAL_TYPE_HDMI_FRL: ret = (link->hdcp_caps.rx_caps.fields.version == 0x4) ? 1:0; break; default: 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 e12c25896364a..434062a60b795 100644 --- a/drivers/gpu/drm/amd/display/dc/link/link_dpms.c +++ b/drivers/gpu/drm/amd/display/dc/link/link_dpms.c @@ -48,6 +48,7 @@ #include "protocols/link_edp_panel_control.h" #include "protocols/link_dp_panel_replay.h" #include "protocols/link_dp_dpia_bw.h" +#include "link/protocols/link_hdmi_frl.h" #include "dm_helpers.h" #include "link_enc_cfg.h" @@ -71,6 +72,10 @@ DC_LOG_RETIMER_REDRIVER( \ __VA_ARGS__) +#define FRL_INFO(...) \ + DC_LOG_HDMI_FRL_LTP( \ + __VA_ARGS__) + #define MAX_MTP_SLOT_COUNT 64 #define LINK_TRAINING_ATTEMPTS 4 #define PEAK_FACTOR_X1000 1006 @@ -544,20 +549,29 @@ 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 = pipe_ctx->stream_res.stream_enc->id - ENGINE_ID_DIGA; if (dp_is_128b_132b_signal(pipe_ctx)) config.stream_enc_idx = - pipe_ctx->stream_res.hpo_dp_stream_enc->id - ENGINE_ID_HPO_DP_0; + (uint8_t)(pipe_ctx->stream_res.hpo_dp_stream_enc->id - ENGINE_ID_HPO_DP_0); + if (dc_is_hdmi_frl_signal(pipe_ctx->stream->signal)) + config.stream_enc_idx = + (uint8_t)(pipe_ctx->stream_res.hpo_frl_stream_enc->id - ENGINE_ID_HPO_0); /* dig back end */ config.dig_be = pipe_ctx->stream->link->link_enc_hw_inst; + if (dc_is_hdmi_frl_signal(pipe_ctx->stream->signal)) + config.dig_be = (uint8_t)pipe_ctx->stream_res.hpo_frl_stream_enc->stream_enc_inst; /* link encoder index */ config.link_enc_idx = link_enc->transmitter - TRANSMITTER_UNIPHY_A; if (dp_is_128b_132b_signal(pipe_ctx)) - config.link_enc_idx = pipe_ctx->link_res.hpo_dp_link_enc->inst; + config.link_enc_idx = (uint8_t)pipe_ctx->link_res.hpo_dp_link_enc->inst; + if (dc_is_hdmi_frl_signal(pipe_ctx->stream->signal)) + config.link_enc_idx = (uint8_t)pipe_ctx->stream->link->hpo_frl_link_enc->inst; /* dio output index is dpia index for DPIA endpoint & dcio index by default */ if (pipe_ctx->stream->link->ep_type == DISPLAY_ENDPOINT_USB4_DPIA) @@ -577,6 +591,7 @@ static void update_psp_stream_config(struct pipe_ctx *pipe_ctx, bool dpms_off) config.assr_enabled = (panel_mode == DP_PANEL_MODE_EDP) ? 1 : 0; config.mst_enabled = (pipe_ctx->stream->signal == SIGNAL_TYPE_DISPLAY_PORT_MST) ? 1 : 0; + config.frl_enabled = dc_is_hdmi_frl_signal(pipe_ctx->stream->signal) ? 1 : 0; config.dp2_enabled = dp_is_128b_132b_signal(pipe_ctx) ? 1 : 0; config.usb4_enabled = (pipe_ctx->stream->link->ep_type == DISPLAY_ENDPOINT_USB4_DPIA) ? 1 : 0; @@ -588,6 +603,38 @@ static void update_psp_stream_config(struct pipe_ctx *pipe_ctx, bool dpms_off) cp_psp->funcs.update_stream_config(cp_psp->handle, &config); } +void link_wait_for_unlocked(struct dc_link *link) +{ + unsigned long long enter_timestamp; + unsigned long long finish_timestamp; + unsigned long long time_taken_in_ns; + bool waited = false; + + DC_LOGGER_INIT(link->ctx->logger); + + enter_timestamp = dm_get_timestamp(link->ctx); + + while (link->is_link_locked) { // busy wait + if (!waited) { + DC_LOG_DC("%s: 0x%p ...", __func__, link); + + waited = true; + } + + udelay(1); + } + + if (!waited) + return; + + finish_timestamp = dm_get_timestamp(link->ctx); + time_taken_in_ns = dm_get_elapse_time_in_ns(link->ctx, + finish_timestamp, enter_timestamp); + + DC_LOG_DC("%s: 0x%p took %llu ms.", __func__, + link, div_u64(time_taken_in_ns, 1000000)); +} + static void set_avmute(struct pipe_ctx *pipe_ctx, bool enable) { struct dc *dc = pipe_ctx->stream->ctx->dc; @@ -595,6 +642,9 @@ static void set_avmute(struct pipe_ctx *pipe_ctx, bool enable) if (!dc_is_hdmi_signal(pipe_ctx->stream->signal)) return; + if (pipe_ctx->stream->timing.flags.DSC) + return; + dc->hwss.set_avmute(pipe_ctx, enable); } @@ -737,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); @@ -768,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) { @@ -855,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)); @@ -869,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; @@ -1813,6 +1892,13 @@ static void enable_link_hdmi(struct pipe_ctx *pipe_ctx) stream->phy_pix_clk, stream->timing.flags.LTE_340MCSC_SCRAMBLE); + if (dc->debug.enable_hdmi_idcc) { + union hdmi_idcc_source_id source_id; + + source_id.raw = 0xff; + write_idcc_data(stream->link->ddc, HDMI_IDCC_SCOPE_WRITE, + &source_id.raw, 0, 1); + } memset(&stream->link->cur_link_settings, 0, sizeof(struct dc_link_settings)); @@ -1838,6 +1924,109 @@ static void enable_link_hdmi(struct pipe_ctx *pipe_ctx) read_scdc_data(link->ddc); } +static enum dc_status enable_link_hdmi_frl(struct pipe_ctx *pipe_ctx) +{ + enum link_result link_stat = LINK_RESULT_UNKNOWN; + enum dc_status status = DC_OK; + struct dc_stream_state *stream = pipe_ctx->stream; + struct dc *core_dc = pipe_ctx->stream->ctx->dc; + enum clock_source_id frl_phy_clock_source_id; + bool frl_poll_start = true; + + DC_LOGGER_INIT(stream->ctx->logger); + + if ((!stream->link->link_enc) || + (!stream->link->hpo_frl_link_enc) || + (!core_dc->res_pool->dccg->funcs->enable_hdmicharclk) || + (!(pipe_ctx->stream_res.hpo_frl_stream_enc))) + return DC_ERROR_UNEXPECTED; + + /* get link settings for video mode timing */ + hdmi_frl_decide_link_settings(stream, &stream->link->frl_link_settings, &pipe_ctx->dsc_padding_params); + + switch (stream->link->frl_link_settings.frl_link_rate) { + case HDMI_FRL_LINK_RATE_3GBPS: + pipe_ctx->stream_res.pix_clk_params.requested_sym_clk = 166667; + break; + case HDMI_FRL_LINK_RATE_6GBPS: + case HDMI_FRL_LINK_RATE_6GBPS_4LANE: + pipe_ctx->stream_res.pix_clk_params.requested_sym_clk = 333333; + break; + case HDMI_FRL_LINK_RATE_8GBPS: + pipe_ctx->stream_res.pix_clk_params.requested_sym_clk = 444444; + break; + case HDMI_FRL_LINK_RATE_10GBPS: + pipe_ctx->stream_res.pix_clk_params.requested_sym_clk = 555555; + break; + case HDMI_FRL_LINK_RATE_12GBPS: + pipe_ctx->stream_res.pix_clk_params.requested_sym_clk = 666667; + break; + case HDMI_FRL_LINK_RATE_16GBPS: + pipe_ctx->stream_res.pix_clk_params.requested_sym_clk = 888889; + break; + case HDMI_FRL_LINK_RATE_20GBPS: + pipe_ctx->stream_res.pix_clk_params.requested_sym_clk = 1111111; + break; + case HDMI_FRL_LINK_RATE_24GBPS: + pipe_ctx->stream_res.pix_clk_params.requested_sym_clk = 1333333; + break; + default: + break; + } + + stream->phy_pix_clk = pipe_ctx->stream_res.pix_clk_params.requested_sym_clk; + + memset(&stream->link->cur_link_settings, 0, + sizeof(struct dc_link_settings)); + + /* Find proper clock source in HDMI FRL mode for phy used for DCCG */ + frl_phy_clock_source_id = hdmi_frl_find_matching_phypll(stream->link); + + FRL_INFO("FRL LINK TRAINING: LTS:P Start\n"); + /* Setup FRL PHY, enable HDMI character clock and HPO link encoder */ + core_dc->hwss.setup_hdmi_frl_link(stream->link, + (pipe_ctx->stream_res.hpo_frl_stream_enc->id - ENGINE_ID_HPO_0), + frl_phy_clock_source_id); + + link_stat = hdmi_frl_perform_link_training_with_retries(stream->link); + + if (core_dc->res_pool->dccg->funcs->set_valid_pixel_rate) + core_dc->res_pool->dccg->funcs->set_valid_pixel_rate( + core_dc->res_pool->dccg, + core_dc->clk_mgr->funcs->get_dtb_ref_clk_frequency(core_dc->clk_mgr), + pipe_ctx->stream_res.tg->inst, + (stream->timing.pix_clk_100hz / 10)); + + /* Enable FRL packet transmission */ + if (link_stat == LINK_RESULT_SUCCESS) { + stream->link->hpo_frl_link_enc->funcs->enable_output( + stream->link->hpo_frl_link_enc); + if (stream->link->frl_flags.apply_vsdb_rcc_wa) + stream->link->hpo_frl_link_enc->funcs->apply_vsdb_rcc_wa(stream->link->hpo_frl_link_enc); + if (frl_poll_start) + hdmi_frl_poll_start(stream->link->ddc); + + FRL_INFO("FRL LINK TRAINING: LTS:P Success\n"); + + /* Set HDMISTREAMCLK source to DTBCLK0 and bypass DTO */ + if (core_dc->res_pool->dccg->funcs->set_hdmistreamclk) { + core_dc->res_pool->dccg->funcs->set_hdmistreamclk( + core_dc->res_pool->dccg, + DTBCLK0, + pipe_ctx->stream_res.tg->inst); + } + + pipe_ctx->stream_res.hpo_frl_stream_enc->funcs->hdmi_frl_enable( + pipe_ctx->stream_res.hpo_frl_stream_enc, + pipe_ctx->stream_res.tg->inst); + } else { + status = DC_FAIL_HDMI_FRL_LINK_TRAINING; + stream->link->frl_link_settings.frl_link_rate = 0; + } + + return status; +} + static enum dc_status enable_link_dp(struct dc_state *state, struct pipe_ctx *pipe_ctx) { @@ -2078,6 +2267,14 @@ static enum dc_status enable_link( enable_link_hdmi(pipe_ctx); status = DC_OK; break; + case SIGNAL_TYPE_HDMI_FRL: + if (link->local_sink && + link->local_sink->edid_caps.panel_patch.delay_hdmi_link_training && + stream->timing.pix_clk_100hz == 6627500) { + msleep(link->local_sink->edid_caps.panel_patch.delay_hdmi_link_training); + } + status = enable_link_hdmi_frl(pipe_ctx); + break; case SIGNAL_TYPE_LVDS: enable_link_lvds(pipe_ctx); status = DC_OK; @@ -2181,6 +2378,8 @@ void link_set_dpms_off(struct pipe_ctx *pipe_ctx) if (dp_is_128b_132b_signal(pipe_ctx)) vpg = pipe_ctx->stream_res.hpo_dp_stream_enc->vpg; + if (dc_is_hdmi_frl_signal(pipe_ctx->stream->signal)) + vpg = pipe_ctx->stream_res.hpo_frl_stream_enc->vpg; if (dc_is_virtual_signal(pipe_ctx->stream->signal)) return; @@ -2193,6 +2392,8 @@ void link_set_dpms_off(struct pipe_ctx *pipe_ctx) } } + link_wait_for_unlocked(link); + if (!pipe_ctx->stream->sink->edid_caps.panel_patch.skip_avmute) { if (dc_is_hdmi_signal(pipe_ctx->stream->signal)) set_avmute(pipe_ctx, true); @@ -2256,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) @@ -2295,6 +2498,8 @@ void link_set_dpms_on( if (dp_is_128b_132b_signal(pipe_ctx)) vpg = pipe_ctx->stream_res.hpo_dp_stream_enc->vpg; + if (dc_is_hdmi_frl_signal(pipe_ctx->stream->signal)) + vpg = pipe_ctx->stream_res.hpo_frl_stream_enc->vpg; if (dc_is_virtual_signal(pipe_ctx->stream->signal)) return; @@ -2309,11 +2514,13 @@ void link_set_dpms_on( } } + link_wait_for_unlocked(stream->link); if (!dc->config.unify_link_enc_assignment) link_enc = link_enc_cfg_get_link_enc(link); ASSERT(link_enc); if (!dc_is_virtual_signal(pipe_ctx->stream->signal) + && !dc_is_hdmi_frl_signal(pipe_ctx->stream->signal) && !dp_is_128b_132b_signal(pipe_ctx)) { if (link_enc) link_enc->funcs->setup( @@ -2323,9 +2530,14 @@ void link_set_dpms_on( pipe_ctx->stream->link->link_state_valid = true; + if (dc_is_hdmi_frl_signal(pipe_ctx->stream->signal)) + hdmi_frl_decide_link_settings(stream, &stream->link->frl_link_settings, &pipe_ctx->dsc_padding_params); + if (pipe_ctx->stream_res.tg->funcs->set_out_mux) { if (dp_is_128b_132b_signal(pipe_ctx)) otg_out_dest = OUT_MUX_HPO_DP; + else if (dc_is_hdmi_frl_signal(pipe_ctx->stream->signal)) + otg_out_dest = OUT_MUX_HPO_FRL; else otg_out_dest = OUT_MUX_DIO; pipe_ctx->stream_res.tg->funcs->set_out_mux(pipe_ctx->stream_res.tg, otg_out_dest); @@ -2416,8 +2628,9 @@ void link_set_dpms_on( * show the stream anyway. But MST displays can't proceed * without link training. */ - if (status != DC_FAIL_DP_LINK_TRAINING || - pipe_ctx->stream->signal == SIGNAL_TYPE_DISPLAY_PORT_MST) { + if ((status != DC_FAIL_DP_LINK_TRAINING && + status != DC_FAIL_HDMI_FRL_LINK_TRAINING) || + pipe_ctx->stream->signal == SIGNAL_TYPE_DISPLAY_PORT_MST) { if (false == stream->link->link_status.link_active) disable_link(stream->link, &pipe_ctx->link_res, pipe_ctx->stream->signal); @@ -2426,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, @@ -2437,6 +2655,7 @@ void link_set_dpms_on( * from transmitter control. */ if (!(dc_is_virtual_signal(pipe_ctx->stream->signal) || + dc_is_hdmi_frl_signal(pipe_ctx->stream->signal) || dp_is_128b_132b_signal(pipe_ctx))) { if (link_enc) diff --git a/drivers/gpu/drm/amd/display/dc/link/link_dpms.h b/drivers/gpu/drm/amd/display/dc/link/link_dpms.h index bd6fc63064a3b..e8662147dd8e7 100644 --- a/drivers/gpu/drm/amd/display/dc/link/link_dpms.h +++ b/drivers/gpu/drm/amd/display/dc/link/link_dpms.h @@ -50,4 +50,5 @@ struct fixed31_32 link_calculate_sst_avg_time_slots_per_mtp( void link_set_dsc_on_stream(struct pipe_ctx *pipe_ctx, bool enable); bool link_set_dsc_enable(struct pipe_ctx *pipe_ctx, bool enable); bool link_update_dsc_config(struct pipe_ctx *pipe_ctx); +void link_wait_for_unlocked(struct dc_link *link); #endif /* __DC_LINK_DPMS_H__ */ diff --git a/drivers/gpu/drm/amd/display/dc/link/link_factory.c b/drivers/gpu/drm/amd/display/dc/link/link_factory.c index ae4c4ad05baa0..5ee4a1431bc6d 100644 --- a/drivers/gpu/drm/amd/display/dc/link/link_factory.c +++ b/drivers/gpu/drm/amd/display/dc/link/link_factory.c @@ -42,6 +42,7 @@ #include "protocols/link_dp_training.h" #include "protocols/link_edp_panel_control.h" #include "protocols/link_dp_panel_replay.h" +#include "protocols/link_hdmi_frl.h" #include "protocols/link_hpd.h" #include "gpio_service_interface.h" #include "atomfirmware.h" @@ -101,6 +102,8 @@ static void construct_link_service_validation(struct link_service *link_srv) link_srv->validate_mode_timing = link_validate_mode_timing; link_srv->dp_link_bandwidth_kbps = dp_link_bandwidth_kbps; link_srv->validate_dp_tunnel_bandwidth = link_validate_dp_tunnel_bandwidth; + link_srv->frl_link_bandwidth_kbps = frl_link_bandwidth_kbps; + link_srv->frl_margin_check_uncompressed_video = frl_capacity_computations_uncompressed_video; link_srv->dp_required_hblank_size_bytes = dp_required_hblank_size_bytes; } @@ -121,6 +124,7 @@ static void construct_link_service_dpms(struct link_service *link_srv) link_srv->set_dsc_on_stream = link_set_dsc_on_stream; link_srv->set_dsc_enable = link_set_dsc_enable; link_srv->update_dsc_config = link_update_dsc_config; + link_srv->wait_for_unlocked = link_wait_for_unlocked; } /* link ddc implements generic display communication protocols such as i2c, aux @@ -243,6 +247,25 @@ static void construct_link_service_dp_panel_replay(struct link_service *link_srv link_srv->dp_pr_get_state = dp_pr_get_state; } +/* link hdmi frl implements FRL link capability and link training related + * functions. FRL link is established by order of retrieve_link, verify_link, + * and poll_status. Other helper functions exist to obtain information required + * to maintain the correct sequence according to HDMI specification. Each + * sequence and state inside link training functions are timing sensitive and order sensitive. + * It is mandatory that these functions are debugged with FRL_LTP output message + * configurable in DSAT. Any changes in the LT sequence should follow the HDMI + * specification as much as possible and tested through HDMI electrical and + * link layer compliance. + */ +static void construct_link_service_hdmi_frl(struct link_service *link_srv) +{ + link_srv->hdmi_frl_poll_status_flag = hdmi_frl_poll_status_flag; + link_srv->hdmi_frl_get_verified_link_cap = + hdmi_frl_get_verified_link_cap; + link_srv->hdmi_frl_set_preferred_link_settings = + hdmi_frl_set_preferred_link_settings; +} + /* link dp cts implements dp compliance test automation protocols and manual * testing interfaces for debugging and certification purpose. */ @@ -296,6 +319,7 @@ static void construct_link_service(struct link_service *link_srv) construct_link_service_dp_irq_handler(link_srv); construct_link_service_edp_panel_control(link_srv); construct_link_service_dp_panel_replay(link_srv); + construct_link_service_hdmi_frl(link_srv); construct_link_service_dp_cts(link_srv); construct_link_service_dp_trace(link_srv); } @@ -401,6 +425,8 @@ static void link_destruct(struct dc_link *link) link->link_enc->funcs->destroy(&link->link_enc); } + if (link->hpo_frl_link_enc) + link->hpo_frl_link_enc->funcs->destroy(&link->hpo_frl_link_enc); if (link->local_sink) dc_sink_release(link->local_sink); @@ -622,6 +648,7 @@ static bool construct_phy(struct dc_link *link, DC_LOG_DC("BIOS object table - DP_IS_USB_C: %d", link->link_enc->features.flags.bits.DP_IS_USB_C); DC_LOG_DC("BIOS object table - IS_DP2_CAPABLE: %d", link->link_enc->features.flags.bits.IS_DP2_CAPABLE); + DC_LOG_DC("BIOS object table - IS_HDMI_FRL_CAPABLE: %d", link->link_enc->features.flags.bits.IS_HDMI_FRL_CAPABLE); switch (link->link_id.id) { case CONNECTOR_ID_HDMI_TYPE_A: @@ -825,12 +852,29 @@ static bool construct_phy(struct dc_link *link, */ program_hpd_filter(link); + /* If the connector is HDMI FRL capable, also create an HPO link encoder */ + if ((link->link_enc->features.flags.bits.IS_HDMI_FRL_CAPABLE) && + (!link->link_enc->features.flags.bits.DP_IS_USB_C) && + (link->dc->res_pool->funcs->hpo_frl_link_enc_create)) { + enum engine_id hpo_eng_id; + hpo_eng_id = ENGINE_ID_HPO_0; + + link->hpo_frl_link_enc = link->dc->res_pool->funcs->hpo_frl_link_enc_create( + hpo_eng_id, + dc_ctx); + if (link->hpo_frl_link_enc == NULL) { + DC_ERROR("Failed to create HPO link encoder!\n"); + goto hpo_enc_create_fail; + } + } + link->psr_settings.psr_vtotal_control_support = false; link->psr_settings.psr_version = DC_PSR_VERSION_UNSUPPORTED; link->replay_settings.config.replay_version = DC_REPLAY_VERSION_UNSUPPORTED; DC_LOG_DC("BIOS object table - %s finished successfully.\n", __func__); return true; +hpo_enc_create_fail: device_tag_fail: link_enc_create_fail: panel_cntl_create_fail: diff --git a/drivers/gpu/drm/amd/display/dc/link/link_validation.c b/drivers/gpu/drm/amd/display/dc/link/link_validation.c index eb791285ed06a..64930a1204012 100644 --- a/drivers/gpu/drm/amd/display/dc/link/link_validation.c +++ b/drivers/gpu/drm/amd/display/dc/link/link_validation.c @@ -31,6 +31,7 @@ #include "link_validation.h" #include "protocols/link_dp_capability.h" #include "protocols/link_dp_dpia_bw.h" +#include "protocols/link_hdmi_frl.h" #include "resource.h" #define DC_LOGGER_INIT(logger) @@ -263,6 +264,195 @@ uint32_t dp_link_bandwidth_kbps( return link_rate_per_lane_kbps * link_settings->lane_count / 10000 * total_data_bw_efficiency_x10000; } +uint32_t frl_link_bandwidth_kbps(enum hdmi_frl_link_rate link_rate) +{ + switch (link_rate) { + case HDMI_FRL_LINK_RATE_3GBPS: + return 9000000; + case HDMI_FRL_LINK_RATE_6GBPS: + return 18000000; + case HDMI_FRL_LINK_RATE_6GBPS_4LANE: + return 24000000; + case HDMI_FRL_LINK_RATE_8GBPS: + return 32000000; + case HDMI_FRL_LINK_RATE_10GBPS: + return 40000000; + case HDMI_FRL_LINK_RATE_12GBPS: + return 48000000; + case HDMI_FRL_LINK_RATE_16GBPS: + return 64000000; + case HDMI_FRL_LINK_RATE_20GBPS: + return 80000000; + case HDMI_FRL_LINK_RATE_24GBPS: + return 96000000; + default: + return 0; + } +} + +bool frl_capacity_computations_common(struct frl_cap_chk_params_fixed31_32 *params, + struct frl_cap_chk_intermediates_fixed31_32 *inter) +{ + struct fixed31_32 audio_bw_reserve = dc_fixpt_from_int((params->compressed ? 192000 : 0)); + struct fixed31_32 pixel_rate_tolerance = dc_fixpt_div_int(dc_fixpt_from_int(5), 1000); + struct fixed31_32 max_audio_tol_rate; + struct fixed31_32 overhead_m; + + inter->c_frl_sb = 4 * C_FRL_CB + params->lanes; + inter->overhead_sb = dc_fixpt_div_int(dc_fixpt_from_int(params->lanes), inter->c_frl_sb); + inter->overhead_rs = dc_fixpt_div_int(dc_fixpt_from_int(32), inter->c_frl_sb); + inter->overhead_map = dc_fixpt_div_int(dc_fixpt_from_int(25), (inter->c_frl_sb * 10)); + + inter->overhead_min = dc_fixpt_add(inter->overhead_sb, inter->overhead_rs); + inter->overhead_min = dc_fixpt_add(inter->overhead_min, inter->overhead_map); + overhead_m = dc_fixpt_div_int(dc_fixpt_from_int(3), 1000); + inter->overhead_max = dc_fixpt_add(inter->overhead_min, overhead_m); + + pixel_rate_tolerance = dc_fixpt_add_int(pixel_rate_tolerance, 1); + + inter->f_pixel_clock_max = dc_fixpt_mul(params->f_pixel_clock_nominal, pixel_rate_tolerance); + inter->t_line = dc_fixpt_div(dc_fixpt_from_int(params->h_active + params->h_blank), inter->f_pixel_clock_max); + inter->r_bit_min = dc_fixpt_div_int(dc_fixpt_from_int(TOLERANCE_FRL_BIT), 1000000); + inter->r_bit_min = dc_fixpt_sub(dc_fixpt_from_int(1), inter->r_bit_min); + inter->r_bit_min = dc_fixpt_mul(params->r_bit_nominal, inter->r_bit_min); + + inter->r_frl_char_min = dc_fixpt_div_int(inter->r_bit_min, 18); + inter->c_frl_line = dc_fixpt_mul(inter->t_line, inter->r_frl_char_min); + inter->c_frl_line = dc_fixpt_mul_int(inter->c_frl_line, params->lanes); + + switch (params->audio_packet_type) { + case 0x02: + if (params->layout == 0) + inter->ap = dc_fixpt_div_int(dc_fixpt_from_int(25), 100); + else if (params->layout == 1) + inter->ap = dc_fixpt_from_int(1); + break; + case 0x08: + inter->ap = dc_fixpt_div_int(dc_fixpt_from_int(25), 100); + break; + case 0x09: + inter->ap = dc_fixpt_from_int(1); + break; + case 0x07: + case 0x0e: + case 0x0f: + case 0x0b: + case 0x0c: + /* Unsupported audio format */ + return false; + default: + inter->ap = dc_fixpt_from_int(0); + } + + inter->r_ap = dc_fixpt_max(audio_bw_reserve, dc_fixpt_mul(params->f_audio, inter->ap)); + inter->r_ap = dc_fixpt_add(inter->r_ap, dc_fixpt_from_int(2 * ACR_RATE_MAX)); + max_audio_tol_rate = dc_fixpt_div_int(dc_fixpt_from_int(TOLERANCE_AUDIO_CLOCK), 1000000); + max_audio_tol_rate = dc_fixpt_add(dc_fixpt_from_int(1), max_audio_tol_rate); + inter->r_ap = dc_fixpt_mul(inter->r_ap, max_audio_tol_rate); + + inter->avg_audio_packets_line = dc_fixpt_mul(inter->r_ap, inter->t_line); + inter->avg_audio_packets_line = dc_fixpt_div_int(inter->avg_audio_packets_line, 1000000); + inter->audio_packets_line = dc_fixpt_ceil(inter->avg_audio_packets_line); + + inter->blank_audio_min = 32 + 32 * inter->audio_packets_line; + + params->borrow_params.audio_packets_line = inter->audio_packets_line; + + return true; +} + +bool frl_capacity_computations_uncompressed_video(struct frl_cap_chk_params_fixed31_32 *params, + struct frl_cap_chk_intermediates_fixed31_32 *inter) +{ + bool res; + int k_420; + struct fixed31_32 k_cd; + struct fixed31_32 c_frl_free; + int c_frl_free_int; + int c_frl_rc_margin; + struct fixed31_32 c_frl_rc_savings; + int c_frl_rc_savings_int; + int bpp; + struct fixed31_32 bytes_line; + int tb_active; + int tb_blank; + struct fixed31_32 f_tb_average; + struct fixed31_32 t_active_ref; + struct fixed31_32 t_blank_ref; + struct fixed31_32 t_active_min; + struct fixed31_32 t_blank_min; + int c_frl_actual_payload; + struct fixed31_32 utilization; + + res = frl_capacity_computations_common(params, inter); + if (res != true) + return res; + + k_420 = params->pixel_encoding == HDMI_FRL_PIXEL_ENCODING_420 ? 2 : 1; + if (params->pixel_encoding == HDMI_FRL_PIXEL_ENCODING_422) + k_cd = dc_fixpt_from_int(1); + else + k_cd = dc_fixpt_div_int(dc_fixpt_from_int(params->bpc), 8); + + c_frl_free = dc_fixpt_div_int(dc_fixpt_mul_int(k_cd, params->h_blank), k_420); + c_frl_free = dc_fixpt_sub_int(c_frl_free, 32 * (1 + inter->audio_packets_line) + 7); + c_frl_free = dc_fixpt_max(c_frl_free, dc_fixpt_from_int(0)); + c_frl_free_int = dc_fixpt_ceil(c_frl_free); + c_frl_rc_margin = 4; + c_frl_rc_savings = dc_fixpt_mul_int(dc_fixpt_div_int(dc_fixpt_from_int(7), 8), c_frl_free_int); + c_frl_rc_savings = dc_fixpt_sub_int(c_frl_rc_savings, c_frl_rc_margin); + c_frl_rc_savings_int = dc_fixpt_floor(dc_fixpt_max(c_frl_rc_savings, dc_fixpt_from_int(0))); + + bpp = dc_fixpt_ceil(dc_fixpt_mul_int(dc_fixpt_div_int(k_cd, k_420), 24)); + bytes_line = dc_fixpt_div_int(dc_fixpt_from_int(bpp * params->h_active), 8); + tb_active = dc_fixpt_ceil(dc_fixpt_div_int(bytes_line, 3)); + tb_blank = dc_fixpt_ceil(dc_fixpt_div_int(dc_fixpt_mul_int(k_cd, params->h_blank), k_420)); + + if (!(inter->blank_audio_min <= tb_blank)) { + return false; + } + + f_tb_average = dc_fixpt_div_int(inter->f_pixel_clock_max, (params->h_active + params->h_blank)); + f_tb_average = dc_fixpt_mul_int(f_tb_average, (tb_active + tb_blank)); + + t_active_ref = dc_fixpt_div(dc_fixpt_from_int(params->h_active), dc_fixpt_from_int(params->h_active + params->h_blank)); + t_active_ref = dc_fixpt_mul(inter->t_line, t_active_ref); + + t_blank_ref = dc_fixpt_div(dc_fixpt_from_int(params->h_blank), dc_fixpt_from_int(params->h_active + params->h_blank)); + t_blank_ref = dc_fixpt_mul(inter->t_line, t_blank_ref); + + t_active_min = dc_fixpt_sub(dc_fixpt_from_int(1), inter->overhead_max); + t_active_min = dc_fixpt_mul(t_active_min, inter->r_frl_char_min); + t_active_min = dc_fixpt_mul_int(t_active_min, params->lanes); + t_active_min = dc_fixpt_div_int(t_active_min, 1000); + t_blank_min = t_active_min; + + t_active_min = dc_fixpt_div(dc_fixpt_mul_int(dc_fixpt_div(dc_fixpt_from_int(3), dc_fixpt_from_int(2)), tb_active), t_active_min); + + t_blank_min = dc_fixpt_div(dc_fixpt_from_int(tb_blank), t_blank_min); + + if (dc_fixpt_le(t_active_min, t_active_ref) && dc_fixpt_le(t_blank_min, t_blank_ref)) { + params->borrow_params.borrow_mode = FRL_BORROW_MODE_NONE; + } else if (dc_fixpt_lt(t_active_ref, t_active_min) && dc_fixpt_le(t_blank_min, t_blank_ref)) { + params->borrow_params.borrow_mode = FRL_BORROW_MODE_FROM_BLANK; + } else { + return false; + } + + + c_frl_actual_payload = dc_fixpt_ceil(dc_fixpt_mul_int(dc_fixpt_div(dc_fixpt_from_int(3), dc_fixpt_from_int(2)), tb_active)) + tb_blank - c_frl_rc_savings_int; + + utilization = dc_fixpt_div(dc_fixpt_from_int(c_frl_actual_payload), inter->c_frl_line); + utilization = dc_fixpt_mul_int(utilization, 1000); + + inter->margin = dc_fixpt_sub(dc_fixpt_from_int(1), dc_fixpt_add(utilization, inter->overhead_max)); + + if (dc_fixpt_lt(inter->margin, dc_fixpt_from_int(0)) && dc_fixpt_lt(dc_fixpt_from_fraction(1, 100), dc_fixpt_abs(inter->margin))) + return false; + + return true; +} + static uint32_t dp_get_timing_bandwidth_kbps( const struct dc_crtc_timing *timing, const struct dc_link *link) @@ -329,6 +519,86 @@ static bool dp_validate_mode_timing( return false; } +bool frl_validate_mode_timing( + struct dc_link *link, + const struct dc_crtc_timing *timing, + struct dc_hdmi_frl_link_settings *frl_link_settings) +{ + uint32_t req_bw; + uint32_t max_bw; + enum engine_id hpo_eng_id; + unsigned int i; + unsigned int hpo_frl_stream_enc_index = 0; + bool frl_output_valid = false; + + if (!link) + return false; + if (!link->local_sink) + return false; + + req_bw = dc_bandwidth_in_kbps_from_timing(timing, dc_link_get_highest_encoding_format(link)); + max_bw = frl_link_bandwidth_kbps(frl_link_settings->frl_link_rate); + + /* Use Engine ID to determine which hpo stream + * encoder should be used. + */ + if (link->connector_signal == SIGNAL_TYPE_VIRTUAL) + frl_output_valid = true; + else { + struct audio_check audio_frl_check = {0}; + struct audio_info audio_info = {0}; + hpo_eng_id = ENGINE_ID_HPO_0; + + for (i = 0; i < link->dc->res_pool->hpo_frl_stream_enc_count; i++) { + if (link->dc->res_pool->hpo_frl_stream_enc[i]->id == hpo_eng_id) { + hpo_frl_stream_enc_index = i; + break; + } + } + /*add audio check*/ + for (i = 0; i < (link->local_sink->edid_caps.audio_mode_count); i++) { + audio_info.modes[i].channel_count = link->local_sink->edid_caps.audio_modes[i].channel_count; + audio_info.modes[i].format_code = link->local_sink->edid_caps.audio_modes[i].format_code; + audio_info.modes[i].sample_rates.all = link->local_sink->edid_caps.audio_modes[i].sample_rate; + audio_info.modes[i].sample_size = link->local_sink->edid_caps.audio_modes[i].sample_size; + } + audio_info.mode_count = link->local_sink->edid_caps.audio_mode_count; + get_audio_check(&audio_info, &audio_frl_check); + + frl_output_valid = + link->dc->res_pool->hpo_frl_stream_enc[hpo_frl_stream_enc_index]->funcs->validate_hdmi_frl_output( + link->dc->res_pool->hpo_frl_stream_enc[hpo_frl_stream_enc_index], + timing, &audio_frl_check, + frl_link_settings, + link->local_sink->edid_caps.frl_dsc_max_frl_rate); + } + + if (req_bw <= max_bw && frl_output_valid) { + /* remember the biggest mode here, during + * initial link training (to get + * verified_link_cap), LS sends event about + * cannot train at reported cap to upper + * layer and upper layer will re-enumerate modes. + * this is not necessary if the lower + * verified_link_cap is enough to drive + * all the modes + */ + + /* TODO: DYNAMIC_VALIDATION needs to be implemented */ + /* if (flags.DYNAMIC_VALIDATION == 1) + * dpsst->max_req_bw_for_verified_linkcap = dal_max( + * dpsst->max_req_bw_for_verified_linkcap, req_bw); + */ + return true; + } else if (frl_output_valid && timing->dsc_cfg.is_frl) { + /* HDMI DSC calculation is validated within frl_output_valid + * and req_bw may exceed max_bw + */ + return true; + } else + return false; +} + enum dc_status link_validate_mode_timing( const struct dc_stream_state *stream, struct dc_link *link, @@ -343,6 +613,15 @@ enum dc_status link_validate_mode_timing( if (link->remote_sinks[0] && link->remote_sinks[0]->sink_signal == SIGNAL_TYPE_VIRTUAL) return DC_OK; + /* If DSC is supported, but native 422 DSC is not supported, + * HDMI 2.1a specification requires that all 422 format be disabled (7.7.1) + */ + if (dc_is_hdmi_signal(stream->signal)) { + if (timing->pixel_encoding == PIXEL_ENCODING_YCBCR422 && link->dc->config.no_native422_support) { + return DC_SURFACE_PIXEL_FORMAT_UNSUPPORTED; + } + } + /* Passive Dongle */ if (max_pix_clk != 0 && get_tmds_output_pixel_clock_100hz(timing) > max_pix_clk) return DC_EXCEED_DONGLE_CAP; @@ -360,6 +639,25 @@ enum dc_status link_validate_mode_timing( return DC_NO_DP_LINK_BANDWIDTH; break; + case SIGNAL_TYPE_HDMI_FRL: + { + uint32_t pxl_clk_mhz; + /* Limit pixel clock to DTBCLK Limit (Base Pix > 4 * DTBCLK) */ + pxl_clk_mhz = (timing->pix_clk_100hz + 10000 - 1) / 10000 ; + if (timing->pixel_encoding == PIXEL_ENCODING_YCBCR420) + pxl_clk_mhz /= 2; + else if (timing->pixel_encoding == PIXEL_ENCODING_YCBCR422) + pxl_clk_mhz = pxl_clk_mhz * 2 / 3; + if (pxl_clk_mhz > DTBCLK_LIMIT && link->ctx->dce_version < DCN_VERSION_3_1) + return DC_NO_HDMI_FRL_LINK_BANDWIDTH; + } + + if (!frl_validate_mode_timing( + link, + timing, + hdmi_frl_get_verified_link_cap(link))) + return DC_NO_HDMI_FRL_LINK_BANDWIDTH; + break; default: break; } diff --git a/drivers/gpu/drm/amd/display/dc/link/link_validation.h b/drivers/gpu/drm/amd/display/dc/link/link_validation.h index 595774e764536..ccea329c556cb 100644 --- a/drivers/gpu/drm/amd/display/dc/link/link_validation.h +++ b/drivers/gpu/drm/amd/display/dc/link/link_validation.h @@ -26,6 +26,8 @@ #define __LINK_VALIDATION_H__ #include "link_service.h" +#define TOLERANCE_AUDIO_CLOCK 1000 + enum dc_status link_validate_mode_timing( const struct dc_stream_state *stream, struct dc_link *link, @@ -33,9 +35,20 @@ enum dc_status link_validate_mode_timing( enum dc_status link_validate_dp_tunnel_bandwidth( const struct dc *dc, const struct dc_state *new_ctx); +bool frl_validate_mode_timing( + struct dc_link *link, + const struct dc_crtc_timing *timing, + struct dc_hdmi_frl_link_settings *frl_link_settings); uint32_t dp_link_bandwidth_kbps( const struct dc_link *link, const struct dc_link_settings *link_settings); +uint32_t frl_link_bandwidth_kbps(enum hdmi_frl_link_rate link_rate); +uint32_t link_timing_bandwidth_kbps(const struct dc_crtc_timing *timing); +bool frl_capacity_computations_common(struct frl_cap_chk_params_fixed31_32 *params, + struct frl_cap_chk_intermediates_fixed31_32 *inter); +bool frl_capacity_computations_uncompressed_video( + struct frl_cap_chk_params_fixed31_32 *params, + struct frl_cap_chk_intermediates_fixed31_32 *inter); uint32_t dp_required_hblank_size_bytes( diff --git a/drivers/gpu/drm/amd/display/dc/link/protocols/link_ddc.c b/drivers/gpu/drm/amd/display/dc/link/protocols/link_ddc.c index a66217e54a095..11e3af9db064c 100644 --- a/drivers/gpu/drm/amd/display/dc/link/protocols/link_ddc.c +++ b/drivers/gpu/drm/amd/display/dc/link/protocols/link_ddc.c @@ -31,6 +31,7 @@ * link training. */ #include "link_ddc.h" +#include "link_hdmi_frl.h" #include "vector.h" #include "dce/dce_aux.h" #include "dal_asic_id.h" @@ -552,6 +553,8 @@ void write_scdc_data(struct ddc_service *ddc_service, (ddc_service->link->local_sink->edid_caps.panel_patch.skip_scdc_overwrite || !ddc_service->link->local_sink->edid_caps.scdc_present)) return; + hdmi_frl_LTS_clear_Link_Setting(ddc_service); + hdmi_frl_LTS_clear_Update_flag(ddc_service); link_query_ddc_data(ddc_service, slave_address, &offset, sizeof(offset), &sink_version, sizeof(sink_version)); @@ -602,3 +605,90 @@ void read_scdc_data(struct ddc_service *ddc_service) sizeof(status_data.byte)); } } +void write_idcc_data(struct ddc_service *ddc_service, enum hdmi_idcc_scope idcc_scope, + uint8_t *write_buf, uint8_t offset, uint8_t write_len) +{ + uint8_t slave_address = HDMI_IDCC_ADDRESS; + uint8_t idcc_header[5] = {0}; + uint8_t dummy_buf[1] = {0}; + uint8_t checksum = 0; + int i; + + idcc_header[0] = HDMI_IDCC_MARKER0; + idcc_header[1] = HDMI_IDCC_MARKER1; + idcc_header[2] = (uint8_t)(HDMI_IDCC_MARKER2 | idcc_scope); + idcc_header[3] = offset; + idcc_header[4] = write_len; + + /* Write the IDCC header */ + for (i = 0; i < sizeof(idcc_header); ++i) { + link_query_ddc_data(ddc_service, slave_address, + &idcc_header[i], 1, + &dummy_buf[0], 1); + checksum += idcc_header[i]; + } + + /* Write the payload */ + for (i = 0; i < write_len; ++i) { + link_query_ddc_data(ddc_service, slave_address, + &write_buf[i], 1, + &dummy_buf[0], 1); + checksum += write_buf[i]; + } + + /* Write the checksum */ + if (write_len > 0) { + checksum = 0xff - checksum + 1; + link_query_ddc_data(ddc_service, slave_address, + &checksum, 1, + &dummy_buf[0], 1); + } +} + +int read_idcc_data(struct ddc_service *ddc_service, enum hdmi_idcc_scope idcc_scope, + uint8_t *read_buf, uint8_t offset, uint8_t read_len) +{ + uint8_t slave_address = HDMI_IDCC_ADDRESS; + uint8_t idcc_header[5] = {0}; + uint8_t dummy_buf[1] = {0}; + uint8_t read_buf_local[6] = {0}; + uint8_t checksum = 0; + int i; + + idcc_header[0] = HDMI_IDCC_MARKER0; + idcc_header[1] = HDMI_IDCC_MARKER1; + idcc_header[2] = (uint8_t)(HDMI_IDCC_MARKER2 | idcc_scope); + idcc_header[3] = offset; + idcc_header[4] = read_len; + + /* Write the IDCC header */ + for (i = 0; i < sizeof(idcc_header); ++i) { + link_query_ddc_data(ddc_service, slave_address, + &idcc_header[i], 1, + &dummy_buf[0], 1); + checksum += idcc_header[i]; + } + + /* Read the payload */ + if (read_len > 0) { + dummy_buf[0] = 0x01; + if (read_len > 5) + read_len = 5; + link_query_ddc_data(ddc_service, slave_address, + &dummy_buf[0], 1, + &read_buf_local[0], read_len + 1); + + memcpy(read_buf, read_buf_local, read_len); + + /* Check checksum */ + checksum = read_buf_local[read_len]; + for (i = 0; i < 5; ++i) + checksum += idcc_header[i]; + for (i = 0; i < read_len; ++i) + checksum += read_buf_local[i]; + if (checksum != 0) + return -1; + } + + return read_len; +} diff --git a/drivers/gpu/drm/amd/display/dc/link/protocols/link_ddc.h b/drivers/gpu/drm/amd/display/dc/link/protocols/link_ddc.h index d3e6f01a6a90d..f2a80e12494ba 100644 --- a/drivers/gpu/drm/amd/display/dc/link/protocols/link_ddc.h +++ b/drivers/gpu/drm/amd/display/dc/link/protocols/link_ddc.h @@ -94,6 +94,10 @@ void write_scdc_data( void read_scdc_data( struct ddc_service *ddc_service); +void write_idcc_data(struct ddc_service *ddc_service, enum hdmi_idcc_scope idcc_scope, + uint8_t *write_buf, uint8_t offset, uint8_t write_len); +int read_idcc_data(struct ddc_service *ddc_service, enum hdmi_idcc_scope idcc_scope, + uint8_t *read_buf, uint8_t offset, uint8_t read_len); void set_dongle_type(struct ddc_service *ddc, enum display_dongle_type dongle_type); 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 new file mode 100644 index 0000000000000..c35074177a369 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/link/protocols/link_hdmi_frl.c @@ -0,0 +1,1187 @@ +/* + * Copyright 2022 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +/* FILE POLICY AND INTENDED USAGE: + * This file implements FRL link capability and link training related functions. + * FRL link is established by order of retrieve_link, verify_link, and poll_status. + * Other helper functions exist to obtain information required to maintain + * the correct sequence according to HDMI specification. Each sequence and state + * inside link training functions are timing sensitive and order sensitive. + * It is mandatory that these functions are debugged with FRL_LTP output message + * configurable in DSAT. Any changes in the LT sequence should follow the HDMI + * specification as much as possible and tested through HDMI electrical and + * link layer compliance. + */ +#include "link_hdmi_frl.h" +#include "link_ddc.h" +#include "link/link_dpms.h" +#include "link/link_validation.h" +#include "resource.h" +#include "dccg.h" + +#include "dml/dml1_frl_cap_chk.h" + +#define DC_LOGGER \ + dc_logger +#define DC_LOGGER_INIT(logger) \ + struct dal_logger *dc_logger = logger + +#define FRL_INFO(...) \ + DC_LOG_HDMI_FRL_LTP( \ + __VA_ARGS__) + +static bool hdmi_frl_test_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.FRL_MAX) { + FRL_INFO("FRL TEST REQ: FRL_MAX = 1"); + return true; + } + + return false; +} + +static void hdmi_return_preeshoot_and_deemphasis(struct dc_link *link, + union hdmi_scdc_source_test_req *test_req, bool *de_emphasis_only, + bool *pre_shoot_only, bool *no_ffe) +{ + /* check if cable_id is valid */ + if (link->hdmi_cable_id.raw[0] && link->frl_link_settings.frl_link_rate >= + HDMI_FRL_LINK_RATE_16GBPS) { + *de_emphasis_only = (test_req->fields.TXFFE_DEEMPHASIS && link->hdmi_cable_id.bits.no_DeEmphasis_n) || + !link->hdmi_cable_id.bits.no_PreShoot_n; + *pre_shoot_only = (test_req->fields.TXFFE_PRESHOOT && link->hdmi_cable_id.bits.no_PreShoot_n) || + !link->hdmi_cable_id.bits.no_DeEmphasis_n; + *no_ffe = test_req->fields.TXFFE_NOFFE || + (!link->hdmi_cable_id.bits.no_PreShoot_n && + !link->hdmi_cable_id.bits.no_DeEmphasis_n); + } else { + *de_emphasis_only = test_req->fields.TXFFE_DEEMPHASIS; + *pre_shoot_only = test_req->fields.TXFFE_PRESHOOT; + *no_ffe = test_req->fields.TXFFE_NOFFE; + } +} + +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) +{ + switch (link->link_enc->transmitter) { + case TRANSMITTER_UNIPHY_A: + return CLOCK_SOURCE_COMBO_PHY_PLL0; + case TRANSMITTER_UNIPHY_B: + return CLOCK_SOURCE_COMBO_PHY_PLL1; + case TRANSMITTER_UNIPHY_C: + return CLOCK_SOURCE_COMBO_PHY_PLL2; + case TRANSMITTER_UNIPHY_D: + return CLOCK_SOURCE_COMBO_PHY_PLL3; + case TRANSMITTER_UNIPHY_E: + return CLOCK_SOURCE_COMBO_PHY_PLL4; + case TRANSMITTER_UNIPHY_F: + return CLOCK_SOURCE_COMBO_PHY_PLL5; + default: + return CLOCK_SOURCE_ID_UNDEFINED; + }; +} + +void hdmi_frl_retrieve_link_cap(struct dc_link *link, struct dc_sink *sink) +{ + enum hdmi_frl_link_rate encoder_link_rate = HDMI_FRL_LINK_RATE_6GBPS_4LANE; + + if (link->link_enc->features.flags.bits.IS_FRL_8G_CAPABLE) + encoder_link_rate = HDMI_FRL_LINK_RATE_8GBPS; + + if (link->link_enc->features.flags.bits.IS_FRL_10G_CAPABLE) + encoder_link_rate = HDMI_FRL_LINK_RATE_10GBPS; + + if (link->link_enc->features.flags.bits.IS_FRL_12G_CAPABLE) + encoder_link_rate = HDMI_FRL_LINK_RATE_12GBPS; + + if (link->dc->debug.max_frl_rate != 0 && encoder_link_rate > link->dc->debug.max_frl_rate) + encoder_link_rate = link->dc->debug.max_frl_rate; + + if (link->frl_flags.force_frl_rate != 0 && + link->frl_flags.force_frl_rate != 0xF) + encoder_link_rate = link->frl_flags.force_frl_rate; + + link->frl_reported_link_cap.frl_link_rate = + (encoder_link_rate < sink->edid_caps.max_frl_rate) ? + encoder_link_rate : sink->edid_caps.max_frl_rate; + + if (sink->edid_caps.max_frl_rate < HDMI_FRL_LINK_RATE_6GBPS_4LANE) + link->frl_reported_link_cap.frl_num_lanes = 3; + else + link->frl_reported_link_cap.frl_num_lanes = 4; +} + +struct dc_hdmi_frl_link_settings *hdmi_frl_get_verified_link_cap( + struct dc_link *link) +{ + // TODO: rework hdmi_frl_get_verified_link_cap to be a const interface + return &link->frl_verified_link_cap; +} + + +void hdmi_frl_LTS_clear_Update_flag(struct ddc_service *ddc_service) +{ + uint8_t slave_address = HDMI_SCDC_ADDRESS; + uint8_t offset = HDMI_SCDC_UPDATE_0; + uint8_t write_buffer[2] = { 0 }; + union hdmi_scdc_update_read_data scdc_update = {0}; + DC_LOGGER_INIT(ddc_service->link->ctx->logger); + + /*Check FLT_update flag*/ + link_query_ddc_data(ddc_service, slave_address, + &offset, sizeof(offset), &scdc_update.byte[0], + sizeof(scdc_update.byte[0])); + + if (scdc_update.fields.FLT_UPDATE != 0) { + FRL_INFO("FRL LINK TRAINING: LTS:L Clear FLT_UPDATE.\n"); + write_buffer[0] = HDMI_SCDC_UPDATE_0; + /*FLT_update - bit 5*/ + write_buffer[1] = (scdc_update.fields.FLT_UPDATE << 5); + link_query_ddc_data(ddc_service, slave_address, + write_buffer, sizeof(write_buffer), NULL, 0); + } +} + + +bool hdmi_frl_poll_status_flag(struct dc_link *link) +{ + uint8_t slave_address = HDMI_SCDC_ADDRESS; + uint8_t offset = 0; + uint32_t ln0_pattern = 0; + uint32_t ln1_pattern = 0; + uint32_t ln2_pattern = 0; + uint32_t ln3_pattern = 0; + uint8_t write_buffer[2] = {0}; + union hdmi_scdc_update_read_data scdc_update = {0}; + union hdmi_scdc_source_test_req test_req = {0}; + union hdmi_scdc_LTP_req_data ltp_req = {0}; + struct hpo_frl_link_encoder *hpo_frl_link_enc = link->hpo_frl_link_enc; + struct link_encoder *dio_link_enc = link->link_enc; + bool flt_no_timeout = false; + bool link_update = false; + + /*Test Condition - FLT_no_timeout avoid link training*/ + offset = HDMI_SCDC_SOURCE_TEST_REQ; + link_query_ddc_data(link->ddc, slave_address, &offset, + sizeof(offset), &test_req.byte, sizeof(test_req.byte)); + if (test_req.fields.FLT_NO_TIMEOUT) + flt_no_timeout = true; + + offset = HDMI_SCDC_UPDATE_0; + + /*Check FLT_update flag*/ + link_query_ddc_data(link->ddc, slave_address, + &offset, sizeof(offset), &scdc_update.byte[0], + sizeof(scdc_update.byte[0])); + + if (scdc_update.fields.FRL_START == 1) { + write_buffer[0] = HDMI_SCDC_UPDATE_0; + /*FLT_START - bit 4*/ + write_buffer[1] = (scdc_update.fields.FRL_START << 4); + link_query_ddc_data(link->ddc, slave_address, + write_buffer, sizeof(write_buffer), NULL, 0); + } + if (scdc_update.fields.FLT_UPDATE) { + offset = HDMI_SCDC_LTP_REQ; + + link_query_ddc_data(link->ddc, slave_address, + &offset, sizeof(offset), ltp_req.byte, + sizeof(ltp_req.byte)); + + ln0_pattern = ltp_req.fields.LN0_LTP_REQ; + ln1_pattern = ltp_req.fields.LN1_LTP_REQ; + ln2_pattern = ltp_req.fields.LN2_LTP_REQ; + ln3_pattern = ltp_req.fields.LN3_LTP_REQ; + + if (ln0_pattern == 0x03 || ln1_pattern == 0x03 || + ln2_pattern == 0x03 || ln3_pattern == 0x03) { + if (flt_no_timeout) { + hpo_frl_link_enc->funcs->set_hdmi_training_pattern( + hpo_frl_link_enc, + ln0_pattern - 1, + ln1_pattern - 1, + ln2_pattern - 1, + ln3_pattern - 1); + } else + hpo_frl_link_enc->funcs->set_hdmi_training_pattern( + hpo_frl_link_enc, + (ln0_pattern == 0x03) ? 0xF : ln0_pattern - 1, + (ln1_pattern == 0x03) ? 0xF : ln1_pattern - 1, + (ln2_pattern == 0x03) ? 0xF : ln2_pattern - 1, + (ln3_pattern == 0x03) ? 0xF : ln3_pattern - 1); + } else + hpo_frl_link_enc->funcs->set_hdmi_training_pattern( + hpo_frl_link_enc, + ln0_pattern - 1, + ln1_pattern - 1, + ln2_pattern - 1, + ln3_pattern - 1); + if (ln0_pattern == 0x0E || ln1_pattern == 0x0E || + ln2_pattern == 0x0E || ln3_pattern == 0x0E) { + if (scdc_update.fields.FLT_UPDATE) { + if (link->dc->debug.limit_ffe == 0) + return false; + + bool de_emphasis_only = false; + bool pre_shoot_only = false; + bool no_ffe = false; + + hdmi_return_preeshoot_and_deemphasis(link, &test_req, + &de_emphasis_only, &pre_shoot_only, &no_ffe); + + dio_link_enc->funcs->prog_eq_setting(dio_link_enc, 0xEE, + de_emphasis_only, + pre_shoot_only, + no_ffe, + &link->frl_link_settings); + } + } else + if (!flt_no_timeout) { + hdmi_frl_LTS_clear_Link_Setting(link->ddc); + hdmi_frl_LTS_clear_Update_flag(link->ddc); + hpo_frl_link_enc->funcs->set_hdmi_training_pattern(hpo_frl_link_enc, 0, 0, 0, 0); + hdmi_frl_perform_link_training_with_retries(link); + link_update = true; + } + + write_buffer[0] = HDMI_SCDC_UPDATE_0; + /*Clear SCDC Update Flags*/ + write_buffer[1] = (scdc_update.fields.FLT_UPDATE << 5); + link_query_ddc_data(link->ddc, slave_address, + write_buffer, sizeof(write_buffer), NULL, 0); + } + if (scdc_update.fields.SOURCE_TEST_UPDATE) { + offset = HDMI_SCDC_SOURCE_TEST_REQ; + bool de_emphasis_only = false; + bool pre_shoot_only = false; + bool no_ffe = false; + + link_query_ddc_data(link->ddc, slave_address, + &offset, sizeof(offset), &test_req.byte, + sizeof(test_req.byte)); + + hdmi_return_preeshoot_and_deemphasis(link, &test_req, + &de_emphasis_only, &pre_shoot_only, &no_ffe); + + dio_link_enc->funcs->prog_eq_setting(dio_link_enc, 0xFF, + de_emphasis_only, + pre_shoot_only, + no_ffe, + &link->frl_link_settings); + + + write_buffer[0] = HDMI_SCDC_UPDATE_0; + /*Clear SCDC Update Flags*/ + write_buffer[1] = (scdc_update.fields.SOURCE_TEST_UPDATE << 3); + link_query_ddc_data(link->ddc, slave_address, + write_buffer, sizeof(write_buffer), NULL, 0); + + } + + return link_update; +} + +void hdmi_frl_poll_start(struct ddc_service *ddc_service) +{ + uint8_t slave_address = HDMI_SCDC_ADDRESS; + uint8_t offset = HDMI_SCDC_UPDATE_0; + uint8_t write_buffer[2] = {0}; + union hdmi_scdc_source_test_req test_req = {0}; + union hdmi_scdc_update_read_data scdc_update = {0}; + uint16_t num_polls = 100; + uint16_t wait_time = 2000; + bool flt_no_timeout = false; + + DC_LOGGER_INIT(ddc_service->link->ctx->logger); + + /*Test Condition - FLT_no_timeout avoid link training*/ + offset = HDMI_SCDC_SOURCE_TEST_REQ; + link_query_ddc_data(ddc_service, slave_address, &offset, + sizeof(offset), &test_req.byte, sizeof(test_req.byte)); + if (test_req.fields.FLT_NO_TIMEOUT) + flt_no_timeout = true; + + /*Test Condition - No need to poll FRL_START if FLT_no_timeout*/ + if (flt_no_timeout) + return; + + offset = HDMI_SCDC_UPDATE_0; + /*LTS:P: Check FRL_START, poll for 200ms */ + for (; num_polls; num_polls--) { + udelay(wait_time); + /*Check FLT_update flag*/ + link_query_ddc_data(ddc_service, slave_address, + &offset, sizeof(offset), &scdc_update.byte[0], + sizeof(scdc_update.byte[0])); + FRL_INFO("FRL LINK TRAINING: Read FRL_START = %d, FLT_UPDATE = %d. num_polls = %d\n", + scdc_update.fields.FRL_START, scdc_update.fields.FLT_UPDATE, num_polls); + if (scdc_update.fields.FRL_START == 1) { + write_buffer[0] = HDMI_SCDC_UPDATE_0; + /*FLT_update - bit 5*/ + write_buffer[1] = (scdc_update.fields.FRL_START << 4); + link_query_ddc_data(ddc_service, slave_address, + write_buffer, sizeof(write_buffer), NULL, 0); + break; + } + if (scdc_update.fields.FLT_UPDATE == 1) { + write_buffer[0] = HDMI_SCDC_UPDATE_0; + /*FLT_update - bit 5*/ + write_buffer[1] = (scdc_update.fields.FLT_UPDATE << 5); + link_query_ddc_data(ddc_service, slave_address, + write_buffer, sizeof(write_buffer), NULL, 0); + break; + } + } + return; +} + +void hdmi_frl_LTS_clear_Link_Setting(struct ddc_service *ddc_service) +{ + uint8_t slave_address = HDMI_SCDC_ADDRESS; + uint8_t offset = HDMI_SCDC_CONFIG_1; + uint8_t write_buffer[2] = { 0 }; + union hdmi_scdc_configuration scdc_config = {0}; + DC_LOGGER_INIT(ddc_service->link->ctx->logger); + + /*Check FRL_Rate for fallback*/ + link_query_ddc_data(ddc_service, slave_address, + &offset, sizeof(offset), &scdc_config.byte[1], + sizeof(scdc_config.byte[1])); + if (scdc_config.fields.FRL_RATE != 0) { + /*LTS:L FRL_Rate = 0*/ + write_buffer[0] = HDMI_SCDC_CONFIG_1; + /*FRL_RATE*/ + write_buffer[1] = 0; + link_query_ddc_data(ddc_service, slave_address, + write_buffer, sizeof(write_buffer), NULL, 0); + FRL_INFO("FRL LINK TRAINING: LTS:L - Clear FRL_Rate.\n"); + } + +} + +static enum link_result hdmi_frl_perform_link_training(struct ddc_service *ddc_service, + struct dc_hdmi_frl_link_settings *link_settings) +{ + enum link_result result = LINK_RESULT_UNKNOWN; + uint8_t slave_address = HDMI_SCDC_ADDRESS; + uint8_t offset = HDMI_SCDC_STATUS_FLAGS; + uint32_t ln0_pattern = 0, pre_ln0_pattern = 0; + uint32_t ln1_pattern = 0, pre_ln1_pattern = 0; + uint32_t ln2_pattern = 0, pre_ln2_pattern = 0; + uint32_t ln3_pattern = 0, pre_ln3_pattern = 0; + uint8_t write_buffer[2] = {0}; + union hdmi_scdc_update_read_data scdc_update = {0}; + union hdmi_scdc_status_flags_data status_data = {0}; + union hdmi_scdc_source_test_req test_req = {0}; + union hdmi_scdc_LTP_req_data ltp_req = {0}; + uint16_t num_polls = 0; + uint16_t max_polls = 105; + unsigned long long wait_time_ns = 2000000; + struct hpo_frl_link_encoder *hpo_frl_link_enc = ddc_service->link->hpo_frl_link_enc; + struct link_encoder *dio_link_enc = ddc_service->link->link_enc; + uint8_t sink_version = 0; + uint8_t FFE_Levels = (uint8_t)ddc_service->link->dc->debug.limit_ffe; + uint8_t current_FFE = 0; + bool override_FFE = false; + bool flt_no_timeout = false; + unsigned long long flt_poll_cur_time = 0, flt_poll_last_time = 0, flt_poll_elapsed_time_ns = 0; + + DC_LOGGER_INIT(ddc_service->link->ctx->logger); + FRL_INFO("FRL LINK TRAINING: Starting FRL Link Training.\n"); + + hdmi_frl_LTS_clear_Link_Setting(ddc_service); + + offset = HDMI_SCDC_SINK_VERSION; + link_query_ddc_data(ddc_service, slave_address, &offset, + sizeof(offset), &sink_version, sizeof(sink_version)); + + FRL_INFO("FRL LINK TRAINING: Read Sink Version = %d.\n", sink_version); + + if (sink_version == 0) { + FRL_INFO("FRL LINK TRAINING: SKIP - FRL not supported by sink.\n"); + return LINK_RESULT_FALLBACK; + } + + if (sink_version == 1) { + /*Source Version = 1*/ + write_buffer[0] = HDMI_SCDC_SOURCE_VERSION; + write_buffer[1] = 1; + link_query_ddc_data(ddc_service, slave_address, + write_buffer, sizeof(write_buffer), NULL, 0); + FRL_INFO("FRL LINK TRAINING: Set Source Version = 1.\n"); + } + + FRL_INFO("FRL LINK TRAINING: Poll for FLT_READY.\n"); + offset = HDMI_SCDC_STATUS_FLAGS; + + /*LTS:2: Check FLT Ready, poll for 200ms */ + while (num_polls < max_polls) { + flt_poll_cur_time = dm_get_timestamp(ddc_service->ctx); + flt_poll_elapsed_time_ns = dm_get_elapse_time_in_ns(ddc_service->ctx, flt_poll_cur_time, flt_poll_last_time); + if (flt_poll_elapsed_time_ns < wait_time_ns) + continue; + flt_poll_last_time = dm_get_timestamp(ddc_service->ctx); + num_polls++; + + link_query_ddc_data(ddc_service, slave_address, + &offset, sizeof(offset), &status_data.byte, + sizeof(status_data.byte)); + FRL_INFO("FRL LINK TRAINING: Read FLT_READY = %d. num_polls = %d\n", + status_data.fields.FLT_READY, num_polls); + if (status_data.fields.FLT_READY) { + /* Spec recommends to clear update flag, but QD980 has problem */ + hdmi_frl_LTS_clear_Update_flag(ddc_service); + dio_link_enc->funcs->prog_eq_setting(dio_link_enc, 0, + test_req.fields.TXFFE_DEEMPHASIS, + test_req.fields.TXFFE_PRESHOOT, + test_req.fields.TXFFE_NOFFE, + link_settings); + break; + } + } + + /*Test Condition - FLT_no_timeout avoid link training*/ + offset = HDMI_SCDC_SOURCE_TEST_REQ; + link_query_ddc_data(ddc_service, slave_address, &offset, + sizeof(offset), &test_req.byte, sizeof(test_req.byte)); + FRL_INFO("FRL TEST REQ: FLT_no_timeout = %d \n", test_req.fields.FLT_NO_TIMEOUT); + if (test_req.fields.FLT_NO_TIMEOUT) + flt_no_timeout = true; + + if (status_data.fields.FLT_READY) { + /*Specify FRL rate*/ + write_buffer[0] = HDMI_SCDC_CONFIG_1; + /*FRL_RATE*/ + write_buffer[1] = (uint8_t)(link_settings->frl_link_rate | (FFE_Levels << 4)); + link_query_ddc_data(ddc_service, slave_address, + write_buffer, sizeof(write_buffer), NULL, 0); + FRL_INFO("FRL LINK TRAINING: Write link rate = %d. Max FFE_Levels = %d\n", + link_settings->frl_link_rate, FFE_Levels); + + FRL_INFO("FRL LINK TRAINING: Poll for FLT_UPDATE.\n"); + /*LTS:3: Start Link Training*/ + /*Start FLT Timer = 200 ms*/ + num_polls = 0; + if (flt_no_timeout) + max_polls = 500; + + while (num_polls < max_polls) { + flt_poll_cur_time = dm_get_timestamp(ddc_service->ctx); + flt_poll_elapsed_time_ns = dm_get_elapse_time_in_ns(ddc_service->ctx, flt_poll_cur_time, flt_poll_last_time); + if (flt_poll_elapsed_time_ns < wait_time_ns) + continue; + flt_poll_last_time = flt_poll_cur_time; + + num_polls++; + + offset = HDMI_SCDC_UPDATE_0; + /*Check FLT_update flag*/ + link_query_ddc_data(ddc_service, slave_address, + &offset, sizeof(offset), &scdc_update.byte[0], + sizeof(scdc_update.byte[0])); + + FRL_INFO("FRL LINK TRAINING: Read FLT_UPDATE = %d. num_polls = %d\n", + scdc_update.fields.FLT_UPDATE, num_polls); + /*Set TxFFE = TxFFE0*/ + /*Program FFE_Levels - scdc_config has this field at 0 */ + if (override_FFE) { + if (flt_no_timeout) + current_FFE = 0; + if (current_FFE == 0) + dio_link_enc->funcs->prog_eq_setting(dio_link_enc, current_FFE, + test_req.fields.TXFFE_DEEMPHASIS, + test_req.fields.TXFFE_PRESHOOT, + test_req.fields.TXFFE_NOFFE, + link_settings); + else { + if (scdc_update.fields.FLT_UPDATE) + dio_link_enc->funcs->prog_eq_setting(dio_link_enc, current_FFE, + test_req.fields.TXFFE_DEEMPHASIS, + test_req.fields.TXFFE_PRESHOOT, + test_req.fields.TXFFE_NOFFE, + link_settings); + } + FRL_INFO("FRL LINK TRAINING: TxFFE = %d.\n", current_FFE); + override_FFE = false; + } + if (scdc_update.fields.FLT_UPDATE) { + offset = HDMI_SCDC_LTP_REQ; + link_query_ddc_data(ddc_service, slave_address, + &offset, sizeof(offset), ltp_req.byte, + sizeof(ltp_req.byte)); + + pre_ln0_pattern = ln0_pattern; + pre_ln1_pattern = ln1_pattern; + pre_ln2_pattern = ln2_pattern; + pre_ln3_pattern = ln3_pattern; + + ln0_pattern = ltp_req.fields.LN0_LTP_REQ; + ln1_pattern = ltp_req.fields.LN1_LTP_REQ; + ln2_pattern = ltp_req.fields.LN2_LTP_REQ; + ln3_pattern = ltp_req.fields.LN3_LTP_REQ; + + FRL_INFO("FRL LINK TRAINING: Read LN0_LTP_REQ = %d. LN1_LTP_REQ = %d\n", + ltp_req.fields.LN0_LTP_REQ, + ltp_req.fields.LN1_LTP_REQ); + FRL_INFO("FRL LINK TRAINING: Read LN2_LTP_REQ = %d. LN3_LTP_REQ = %d\n", + ltp_req.fields.LN2_LTP_REQ, + ltp_req.fields.LN3_LTP_REQ); + + /*Clear FLT_update flag*/ + FRL_INFO("FRL LINK TRAINING: Clear FLT_UPDATE flag.\n"); + write_buffer[0] = HDMI_SCDC_UPDATE_0; + /*FLT_update - bit 5*/ + write_buffer[1] = (scdc_update.fields.FLT_UPDATE << 5); + link_query_ddc_data(ddc_service, slave_address, + write_buffer, sizeof(write_buffer), NULL, 0); + + if (ln0_pattern == 0x03 || ln1_pattern == 0x03 || + ln2_pattern == 0x03 || ln3_pattern == 0x03) + if (!flt_no_timeout) + continue; + + if (link_settings->frl_num_lanes == 3) { + ln3_pattern = 1; + if (!ln0_pattern && !ln1_pattern && !ln2_pattern) { + /*Link Training is done*/ + FRL_INFO("FRL LINK TRAINING: PASSED\n"); + return LINK_RESULT_SUCCESS; + } + if (ln0_pattern == 0x0F || ln1_pattern == 0x0F || ln2_pattern == 0x0F) { + /*sink requesting to lower link rate*/ + FRL_INFO("FRL LINK TRAINING: Sink requesting lower link rate.\n"); + return LINK_RESULT_LOWER_LINKRATE; + } + if (ln0_pattern == 0x0E || ln1_pattern == 0x0E || ln2_pattern == 0x0E) { + /*sink requesting to next FFE*/ + FRL_INFO("FRL LINK TRAINING: Sink requesting next FFE.\n"); + if (ddc_service->link->dc->debug.limit_ffe == 0) { + return LINK_RESULT_LOWER_LINKRATE; + } + current_FFE++; + override_FFE = true; + if (current_FFE > 3) + current_FFE = 0; + if (flt_no_timeout) + current_FFE = 0; + } + } else { + if (!ln0_pattern && !ln1_pattern && !ln2_pattern && !ln3_pattern) { + /*Link Training is done*/ + FRL_INFO("FRL LINK TRAINING: PASSED\n"); + return LINK_RESULT_SUCCESS; + } + if (ln0_pattern == 0x0F || ln1_pattern == 0x0F || + ln2_pattern == 0x0F || ln3_pattern == 0x0F) { + /*sink requesting to lower link rate*/ + FRL_INFO("FRL LINK TRAINING: Sink requesting lower link rate.\n"); + return LINK_RESULT_LOWER_LINKRATE; + } + if (ln0_pattern == 0x0E || ln1_pattern == 0x0E || + ln2_pattern == 0x0E || ln3_pattern == 0x0E) { + /*sink requesting to next FFE*/ + FRL_INFO("FRL LINK TRAINING: Sink requesting next FFE.\n"); + if (ddc_service->link->dc->debug.limit_ffe == 0) { + return LINK_RESULT_LOWER_LINKRATE; + } + current_FFE++; + override_FFE = true; + if (current_FFE > 3) + current_FFE = 0; + if (flt_no_timeout) + current_FFE = 0; + } + } + + if (override_FFE) { + ln0_pattern = pre_ln0_pattern; + ln1_pattern = pre_ln1_pattern; + ln2_pattern = pre_ln2_pattern; + ln3_pattern = pre_ln3_pattern; + } + + FRL_INFO("FRL LINK TRAINING: Setting Training Pattern [ln0,ln1,ln2,ln3] = [%d,%d,%d,%d].\n", + ln0_pattern, ln1_pattern, ln2_pattern, ln3_pattern); + + hpo_frl_link_enc->funcs->set_hdmi_training_pattern( + hpo_frl_link_enc, + ln0_pattern - 1, + ln1_pattern - 1, + ln2_pattern - 1, + ln3_pattern - 1); + /* Workaround for DEDCN3AG-111 + * HDMI-FRL Incorrect Serialization Order for LTP4 + */ + } + + } + if (flt_no_timeout) { + return LINK_RESULT_SUCCESS; + } else { + FRL_INFO("FRL LINK TRAINING: FAILED - Timeout waiting for FLT_UPDATE to be set by sink.\n"); + write_buffer[0] = HDMI_SCDC_CONFIG_1; + /*FRL_RATE*/ + write_buffer[1] = HDMI_FRL_LINK_RATE_DISABLE | (0 << 4); + link_query_ddc_data(ddc_service, slave_address, + write_buffer, sizeof(write_buffer), NULL, 0); + hdmi_frl_LTS_clear_Link_Setting(ddc_service); + result = LINK_RESULT_TIMEOUT; + } + } else { + FRL_INFO("FRL LINK TRAINING: FAILED - FLT_READY not set by sink.\n"); + result = LINK_RESULT_TIMEOUT; + } + + return result; +} + +enum link_result hdmi_frl_perform_link_training_with_retries( + struct dc_link *link) +{ + enum link_result status = LINK_RESULT_UNKNOWN; + unsigned int retry_count = 0; + unsigned int max_retries = 3; + + DC_LOGGER_INIT(link->ctx->logger); + + if (link->preferred_hdmi_frl_settings.valid) + max_retries = link->preferred_hdmi_frl_settings.max_retries; + + /* FRL Link Training */ + while (status != LINK_RESULT_FALLBACK) { + if (status == LINK_RESULT_SUCCESS) { + break; + } else if (status == LINK_RESULT_LOWER_LINKRATE) { + FRL_INFO("FRL LINK TRAINING: Sink requested lower link rate during link enable. \n"); + break; + } else { + if (retry_count > 0) + msleep(200); + status = hdmi_frl_perform_link_training(link->ddc, + &link->frl_link_settings); + }; + retry_count++; + FRL_INFO("FRL LINK TRAINING: Retry count = %u out of %u\n", retry_count, max_retries); + if (retry_count > max_retries) { + status = LINK_RESULT_FALLBACK; + break; + } + } + + return status; +} + +enum link_result hdmi_frl_perform_link_training_with_fallback( + struct dc_link *link, struct link_resource *link_res, + enum clock_source_id frl_phy_clock_source_id) +{ + enum link_result status = LINK_RESULT_UNKNOWN; + + DC_LOGGER_INIT(link->ctx->logger); + + /* FRL Link Training */ + while (status != LINK_RESULT_FALLBACK) { + if (link->frl_link_settings.frl_link_rate == + HDMI_FRL_LINK_RATE_DISABLE) { + FRL_INFO("FRL LINK TRAINING: Cannot Link Train. Fall back to TMDS \n"); + status = LINK_RESULT_FALLBACK; + break; + } + + msleep(200); + + link->ctx->dc->hwss.setup_hdmi_frl_link(link, 0, + frl_phy_clock_source_id); + + status = hdmi_frl_perform_link_training(link->ddc, + &link->frl_link_settings); + + link->dc->hwss.disable_link_output(link, link_res, SIGNAL_TYPE_HDMI_FRL); + + if (status == LINK_RESULT_SUCCESS) + break; + + link->frl_link_settings.frl_link_rate--; + if (link->frl_link_settings.frl_link_rate < + HDMI_FRL_LINK_RATE_6GBPS_4LANE) + link->frl_link_settings.frl_num_lanes = 3; + } + + return status; +} + +void hdmi_frl_verify_link_cap(struct dc_link *link, + struct dc_hdmi_frl_link_settings *known_limit_link_setting) +{ + struct dc_hdmi_frl_link_settings cur_link_setting = {0}; + struct dc_hdmi_frl_link_settings *cur = &cur_link_setting; + bool success = false; + enum link_result status = LINK_RESULT_UNKNOWN; + enum clock_source_id frl_phy_clock_source_id; + unsigned int t_id = link->link_enc->transmitter; + struct link_resource link_res = {.hpo_frl_link_enc = link->hpo_frl_link_enc}; + struct dc_stream_state *link_stream = NULL; + struct dc_stream_state *stream = NULL; + int i; + + DC_LOGGER_INIT(link->ctx->logger); + + link->frl_flags.force_frl_rate = + link->ctx->dc->debug.force_frl_rate; + link->frl_flags.force_frl_always = + link->preferred_hdmi_frl_settings.force_frl_always || + link->ctx->dc->debug.force_frl_always; + link->frl_flags.force_frl_max = + 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; + + if (link->frl_flags.force_frl_rate == 0xF) + return; + + if (link->local_sink && + link->local_sink->edid_caps.panel_patch.force_frl) + 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 && + link->local_sink->edid_caps.panel_patch.vsdb_rcc_wa) + link->frl_flags.apply_vsdb_rcc_wa = true; + + frl_phy_clock_source_id = hdmi_frl_find_matching_phypll(link); + + cur_link_setting = *known_limit_link_setting; + + if (link->frl_flags.force_frl_rate != 0) { + cur->frl_link_rate = (cur_link_setting.frl_link_rate < + link->frl_flags.force_frl_rate) ? + cur_link_setting.frl_link_rate : + link->frl_flags.force_frl_rate; + link->frl_verified_link_cap = *cur; + return; + } + + if (link->local_sink) { + if (link->local_sink->edid_caps.panel_patch.hdmi_spe_handling) { + link->dc->hwss.disable_link_output(link, &link_res, link->connector_signal); + link->dc->res_pool->clock_sources[t_id]->funcs->cs_power_down( + link->dc->res_pool->clock_sources[t_id]); + link->frl_verified_link_cap = *cur; + return; + } + /* Monitor patch do decrease 10G to 8G*/ + if (link->local_sink->edid_caps.panel_patch.block_10g) { + if (cur->frl_link_rate == HDMI_FRL_LINK_RATE_10GBPS) + cur->frl_link_rate--; + } + } + + link->frl_link_settings = cur_link_setting; + /* disable PHY first for PNP */ + if (link->dc->ctx->dce_version <= DCN_VERSION_3_0) + link->dc->hwss.disable_link_output(link, &link_res, SIGNAL_TYPE_HDMI_FRL); + else + link->dc->hwss.disable_link_output(link, &link_res, link->connector_signal); + + link->dc->res_pool->clock_sources[t_id]->funcs->cs_power_down( + link->dc->res_pool->clock_sources[t_id]); + /*Either enable PHY ourselves or use VBIOS*/ + + FRL_INFO("FRL LINK TRAINING: Validation\n"); + + status = hdmi_frl_perform_link_training_with_fallback(link, &link_res, frl_phy_clock_source_id); + + if (status == LINK_RESULT_SUCCESS) { + cur->frl_link_rate = link->frl_link_settings.frl_link_rate; + cur->frl_num_lanes = link->frl_link_settings.frl_num_lanes; + success = true; + link->frl_verified_link_cap = *cur; + } + if (!success) { + link->frl_verified_link_cap.frl_link_rate = HDMI_FRL_LINK_RATE_DISABLE; + link->frl_verified_link_cap.frl_num_lanes = 3; + } + + for (i = 0; i < MAX_STREAMS; i++) { + stream = link->dc->current_state->streams[i]; + if (stream && stream->link == link) { + link_stream = stream; + break; + } + } + + if (link_stream) { + link->dc->hwss.disable_link_output(link, &link_res, link_stream->signal); + } +} + +void hdmi_frl_set_preferred_link_settings(struct dc *dc, + struct dc_hdmi_frl_link_settings *link_setting, + struct dc_hdmi_frl_link_training_overrides *lt_overrides, + struct dc_link *link) +{ + int i; + struct pipe_ctx *pipe; + struct dc_stream_state *link_stream = 0; + struct pipe_ctx *link_pipe = 0; + struct pipe_ctx *odm_pipe; + int opp_cnt = 1; + enum link_result link_stat = LINK_RESULT_UNKNOWN; + enum clock_source_id frl_phy_clock_source_id; + struct dc_stream_state *temp_stream = &dc->scratch.temp_stream; + + DC_LOGGER_INIT(link->ctx->logger); + + for (i = 0; i < MAX_PIPES; i++) { + pipe = &dc->current_state->res_ctx.pipe_ctx[i]; + if (pipe->stream && pipe->stream->link) { + if (pipe->stream->link == link) { + link_stream = pipe->stream; + link_pipe = pipe; + break; + } + } + } + + /* Stream not found */ + if (i == MAX_PIPES) + return; + + FRL_INFO("FRL LINK TRAINING: Preferred link Update = %d.\n", link_setting->frl_link_rate); + + frl_validate_mode_timing(link, &link_stream->timing, link_setting); + + if (lt_overrides) + link->preferred_hdmi_frl_settings = *lt_overrides; + else + memset(&link->preferred_hdmi_frl_settings, 0, sizeof(link->preferred_hdmi_frl_settings)); + + link_stream->link->frl_link_settings = *link_setting; + link_stream->link->frl_verified_link_cap = *link_setting; + + while (link_stat != LINK_RESULT_SUCCESS) { + link_set_dpms_off(pipe); + /* For DCN3.0, can also have 4:1 combine mode. + * TODO: Add function get_odm_combine_mode that has different + * implementation for DCN2/DCN3AG and DCN3.0 + */ + for (odm_pipe = pipe->next_odm_pipe; odm_pipe; odm_pipe = odm_pipe->next_odm_pipe) + opp_cnt++; + + memcpy(temp_stream, link_stream, sizeof(struct dc_stream_state)); + /* Modify patched_crtc_timing as required for padding */ + if (link_pipe->dsc_padding_params.dsc_hactive_padding) { + temp_stream->timing.h_addressable = link_stream->timing.h_addressable + link_pipe->dsc_padding_params.dsc_hactive_padding; + temp_stream->timing.h_total = link_stream->timing.h_total + link_pipe->dsc_padding_params.dsc_htotal_padding; + } + + pipe->stream_res.hpo_frl_stream_enc->funcs->hdmi_frl_set_stream_attribute( + pipe->stream_res.hpo_frl_stream_enc, + &temp_stream->timing, + &link_stream->link->frl_link_settings.borrow_params, + opp_cnt); + + if (pipe->stream_res.tg->funcs->set_out_mux) + pipe->stream_res.tg->funcs->set_out_mux(pipe->stream_res.tg, OUT_MUX_HPO_FRL); + + if ((!link_stream->link->link_enc) || + (!link_stream->link->hpo_frl_link_enc) || + (!link_stream->ctx->dc->res_pool->dccg->funcs->enable_hdmicharclk) || + (!(pipe->stream_res.hpo_frl_stream_enc))) + return; + + switch (link_stream->link->frl_link_settings.frl_link_rate) { + case HDMI_FRL_LINK_RATE_3GBPS: + pipe->stream_res.pix_clk_params.requested_sym_clk = 166667; + break; + case HDMI_FRL_LINK_RATE_6GBPS: + case HDMI_FRL_LINK_RATE_6GBPS_4LANE: + pipe->stream_res.pix_clk_params.requested_sym_clk = 333333; + break; + case HDMI_FRL_LINK_RATE_8GBPS: + pipe->stream_res.pix_clk_params.requested_sym_clk = 444444; + break; + case HDMI_FRL_LINK_RATE_10GBPS: + pipe->stream_res.pix_clk_params.requested_sym_clk = 555555; + break; + case HDMI_FRL_LINK_RATE_12GBPS: + pipe->stream_res.pix_clk_params.requested_sym_clk = 666667; + break; + case HDMI_FRL_LINK_RATE_16GBPS: + pipe->stream_res.pix_clk_params.requested_sym_clk = 888889; + break; + case HDMI_FRL_LINK_RATE_20GBPS: + pipe->stream_res.pix_clk_params.requested_sym_clk = 1111111; + break; + case HDMI_FRL_LINK_RATE_24GBPS: + pipe->stream_res.pix_clk_params.requested_sym_clk = 1333333; + break; + default: + break; + } + + link_stream->phy_pix_clk = pipe->stream_res.pix_clk_params.requested_sym_clk; + + memset(&link_stream->link->cur_link_settings, 0, + sizeof(struct dc_link_settings)); + + /* Find proper clock source in HDMI FRL mode for phy used for DCCG */ + frl_phy_clock_source_id = hdmi_frl_find_matching_phypll(link); + + dc->hwss.setup_hdmi_frl_link(link, + (pipe->stream_res.hpo_frl_stream_enc->id - ENGINE_ID_HPO_0), + frl_phy_clock_source_id); + + FRL_INFO("FRL LINK TRAINING: Start forced link training at %d. \n", + link_stream->link->frl_link_settings.frl_link_rate); + link_stat = hdmi_frl_perform_link_training_with_retries(link_stream->link); + + /* Enable FRL packet transmission */ + if (link_stat == LINK_RESULT_SUCCESS) { + link_stream->link->hpo_frl_link_enc->funcs->enable_output( + link_stream->link->hpo_frl_link_enc); + if (link_stream->link->frl_flags.apply_vsdb_rcc_wa) + link_stream->link->hpo_frl_link_enc->funcs->apply_vsdb_rcc_wa(link_stream->link->hpo_frl_link_enc); + hdmi_frl_poll_start(link_stream->link->ddc); + + /* Set HDMISTREAMCLK source to DTBCLK0 and bypass DTO */ + if (dc->res_pool->dccg->funcs->set_hdmistreamclk) { + dc->res_pool->dccg->funcs->set_hdmistreamclk( + dc->res_pool->dccg, + DTBCLK0, + pipe->stream_res.tg->inst); + } + + pipe->stream_res.hpo_frl_stream_enc->funcs->hdmi_frl_enable( + pipe->stream_res.hpo_frl_stream_enc, + pipe->stream_res.tg->inst); + 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, + &pipe->stream->link->cur_link_settings); + FRL_INFO("FRL LINK TRAINING: Forced link training successful. \n"); + } + if (link_stat == LINK_RESULT_LOWER_LINKRATE) { + link_stream->link->frl_link_settings.frl_link_rate--; + if (link_stream->link->frl_link_settings.frl_link_rate > + HDMI_FRL_LINK_RATE_6GBPS) + link_stream->link->frl_link_settings.frl_num_lanes = 4; + else + link_stream->link->frl_link_settings.frl_num_lanes = 3; + FRL_INFO("FRL LINK TRAINING: Lower link rate = %d.\n", + link_stream->link->frl_link_settings.frl_link_rate); + } + if (link_stat == LINK_RESULT_FALLBACK) { + FRL_INFO("FRL LINK TRAINING: Forced Link Training failed. Fallback to TMDS. \n"); + break; + } + } +} + +static void update_borrow_mode_from_dsc_padding(struct dsc_padding_params *dsc_padding_params, + struct dc_crtc_timing *timing, + struct dc_hdmi_frl_link_settings *frl_link_settings) +{ +#ifdef CONFIG_DRM_AMD_DC_FP + uint32_t h_active = timing->h_addressable + timing->h_border_left + timing->h_border_right; + uint32_t h_blank = timing->h_total - h_active; + struct frl_borrow_params *borrow_params = &frl_link_settings->borrow_params; + + borrow_params->borrow_mode = frl_modify_borrow_mode_for_dsc_padding(timing->pix_clk_100hz, + h_active, + h_active + dsc_padding_params->dsc_hactive_padding, + h_blank, + h_blank + dsc_padding_params->dsc_htotal_padding, + borrow_params->hc_active_target, + borrow_params->hc_blank_target, + frl_link_settings->frl_num_lanes, + frl_link_settings->frl_link_rate); +#endif +} + +void hdmi_frl_decide_link_settings(struct dc_stream_state *stream, + struct dc_hdmi_frl_link_settings *frl_link_settings, + struct dsc_padding_params *dsc_padding_params) +{ + bool success = false; + struct dc_hdmi_frl_link_settings temp_settings = {0}; + + temp_settings.frl_link_rate = HDMI_FRL_LINK_RATE_3GBPS; + temp_settings.frl_num_lanes = 3; + + /* Verify FRL and fill in borrow_params to verified_link_cap*/ + frl_validate_mode_timing( + stream->link, + &stream->timing, + &stream->link->frl_verified_link_cap); + + if (stream->link->frl_flags.force_frl_rate != 0 && + stream->link->frl_flags.force_frl_rate < stream->link->frl_verified_link_cap.frl_link_rate) { + switch (stream->link->frl_flags.force_frl_rate) { + case HDMI_FRL_LINK_RATE_3GBPS: + temp_settings.frl_link_rate = HDMI_FRL_LINK_RATE_3GBPS; + temp_settings.frl_num_lanes = 3; + break; + case HDMI_FRL_LINK_RATE_6GBPS: + temp_settings.frl_link_rate = HDMI_FRL_LINK_RATE_6GBPS; + temp_settings.frl_num_lanes = 3; + break; + case HDMI_FRL_LINK_RATE_6GBPS_4LANE: + temp_settings.frl_link_rate = HDMI_FRL_LINK_RATE_6GBPS; + temp_settings.frl_num_lanes = 4; + break; + case HDMI_FRL_LINK_RATE_8GBPS: + temp_settings.frl_link_rate = HDMI_FRL_LINK_RATE_8GBPS; + temp_settings.frl_num_lanes = 4; + break; + case HDMI_FRL_LINK_RATE_10GBPS: + temp_settings.frl_link_rate = HDMI_FRL_LINK_RATE_10GBPS; + temp_settings.frl_num_lanes = 4; + break; + case HDMI_FRL_LINK_RATE_12GBPS: + temp_settings.frl_link_rate = HDMI_FRL_LINK_RATE_12GBPS; + temp_settings.frl_num_lanes = 4; + break; + case HDMI_FRL_LINK_RATE_16GBPS: + temp_settings.frl_link_rate = HDMI_FRL_LINK_RATE_16GBPS; + temp_settings.frl_num_lanes = 4; + break; + case HDMI_FRL_LINK_RATE_20GBPS: + temp_settings.frl_link_rate = HDMI_FRL_LINK_RATE_20GBPS; + temp_settings.frl_num_lanes = 4; + break; + case HDMI_FRL_LINK_RATE_24GBPS: + temp_settings.frl_link_rate = HDMI_FRL_LINK_RATE_24GBPS; + temp_settings.frl_num_lanes = 4; + break; + default: + break; + } + *frl_link_settings = temp_settings; + return; + } + /*test equipment requires max rate, identified at FRL_Max = 1*/ + if (stream->link->frl_flags.force_frl_max) { + *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) { + *frl_link_settings = stream->link->frl_verified_link_cap; + return; + } + + do { + success = frl_validate_mode_timing( + stream->link, + &stream->timing, + &temp_settings); + if (temp_settings.frl_link_rate == + stream->link->frl_verified_link_cap.frl_link_rate) + break; + if (!success) + temp_settings.frl_link_rate++; + if (temp_settings.frl_link_rate > HDMI_FRL_LINK_RATE_6GBPS) + temp_settings.frl_num_lanes = 4; + } 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) +{ + uint8_t slave_address = HDMI_SCDC_ADDRESS; + uint8_t offset = HDMI_SCDC_CONFIG_0; + uint8_t scdc_config = 0; + uint8_t write_buffer[2] = {0}; + + link_query_ddc_data(ddc_service, slave_address, &offset, + sizeof(offset), &scdc_config, sizeof(scdc_config)); + + write_buffer[0] = HDMI_SCDC_CONFIG_0; + write_buffer[1] = scdc_config; + write_buffer[1] |= 0x1; + + link_query_ddc_data(ddc_service, slave_address, write_buffer, + sizeof(write_buffer), NULL, 0); +} diff --git a/drivers/gpu/drm/amd/display/dc/link/protocols/link_hdmi_frl.h b/drivers/gpu/drm/amd/display/dc/link/protocols/link_hdmi_frl.h new file mode 100644 index 0000000000000..abeec56c65281 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/link/protocols/link_hdmi_frl.h @@ -0,0 +1,53 @@ +/* + * Copyright 2023 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ +#ifndef __LINK_HDMI_FRL_H__ +#define __LINK_HDMI_FRL_H__ +#include "link_service.h" +enum clock_source_id hdmi_frl_find_matching_phypll( + struct dc_link *link); +void hdmi_frl_LTS_clear_Update_flag(struct ddc_service *ddc_service); +void hdmi_frl_poll_start(struct ddc_service *ddc_service); +void hdmi_frl_LTS_clear_Link_Setting(struct ddc_service *ddc_service); +void hdmi_frl_retrieve_link_cap(struct dc_link *link, struct dc_sink *sink); +enum link_result hdmi_frl_perform_link_training_with_retries( + struct dc_link *link); +enum link_result hdmi_frl_perform_link_training_with_fallback( + struct dc_link *link, struct link_resource *link_res, + enum clock_source_id frl_phy_clock_source_id); +void hdmi_frl_verify_link_cap(struct dc_link *link, + struct dc_hdmi_frl_link_settings *known_limit_link_setting); +void hdmi_frl_decide_link_settings(struct dc_stream_state *stream, + struct dc_hdmi_frl_link_settings *frl_link_settings, + struct dsc_padding_params *dsc_paddding_params); +bool hdmi_frl_poll_status_flag(struct dc_link *link); +struct dc_hdmi_frl_link_settings *hdmi_frl_get_verified_link_cap( + struct dc_link *link); +void hdmi_frl_set_preferred_link_settings(struct dc *dc, + struct dc_hdmi_frl_link_settings *link_setting, + struct dc_hdmi_frl_link_training_overrides *lt_overrides, + struct dc_link *link); +void hdmi_frl_write_read_request_enable( + struct ddc_service *ddc_service); +#endif /* __LINK_HDMI_FRL_H__ */ diff --git a/drivers/gpu/drm/amd/display/dc/link/protocols/link_hpd.c b/drivers/gpu/drm/amd/display/dc/link/protocols/link_hpd.c index b157d05b67ad2..cfb5ef0523a7f 100644 --- a/drivers/gpu/drm/amd/display/dc/link/protocols/link_hpd.c +++ b/drivers/gpu/drm/amd/display/dc/link/protocols/link_hpd.c @@ -80,6 +80,7 @@ bool program_hpd_filter(const struct dc_link *link) case SIGNAL_TYPE_DVI_SINGLE_LINK: case SIGNAL_TYPE_DVI_DUAL_LINK: case SIGNAL_TYPE_HDMI_TYPE_A: + case SIGNAL_TYPE_HDMI_FRL: /* Program hpd filter */ delay_on_connect_in_ms = 500; delay_on_disconnect_in_ms = 100; diff --git a/drivers/gpu/drm/amd/display/dc/opp/dcn10/dcn10_opp.c b/drivers/gpu/drm/amd/display/dc/opp/dcn10/dcn10_opp.c index b49bd155cad4c..98335a53ca521 100644 --- a/drivers/gpu/drm/amd/display/dc/opp/dcn10/dcn10_opp.c +++ b/drivers/gpu/drm/amd/display/dc/opp/dcn10/dcn10_opp.c @@ -263,6 +263,7 @@ void opp1_set_dyn_expansion( /*00 - 10-bit -> 12-bit dynamic expansion*/ /*01 - 8-bit -> 12-bit dynamic expansion*/ if (signal == SIGNAL_TYPE_HDMI_TYPE_A || + signal == SIGNAL_TYPE_HDMI_FRL || signal == SIGNAL_TYPE_DISPLAY_PORT || signal == SIGNAL_TYPE_DISPLAY_PORT_MST || signal == SIGNAL_TYPE_VIRTUAL) { diff --git a/drivers/gpu/drm/amd/display/dc/optc/dcn10/dcn10_optc.h b/drivers/gpu/drm/amd/display/dc/optc/dcn10/dcn10_optc.h index 138081e6cc977..bceefb5320ebd 100644 --- a/drivers/gpu/drm/amd/display/dc/optc/dcn10/dcn10_optc.h +++ b/drivers/gpu/drm/amd/display/dc/optc/dcn10/dcn10_optc.h @@ -266,6 +266,7 @@ struct dcn_optc_registers { OPTC_REG_VARIABLE_LIST_DCN; + uint32_t OTG_DRR_V_TOTAL_REACH_RANGE; OPTC_REG_VARIABLE_LIST_DCN42; }; @@ -595,7 +596,9 @@ struct dcn_optc_registers { type MANUAL_FLOW_CONTROL;\ type MANUAL_FLOW_CONTROL_SEL; -#define V_TOTAL_REGS(type) +#define V_TOTAL_REGS(type) \ + type OTG_DRR_V_TOTAL_REACH_LOWER_RANGE;\ + type OTG_DRR_V_TOTAL_REACH_UPPER_RANGE; #define TG_REG_FIELD_LIST(type) \ TG_REG_FIELD_LIST_DCN1_0(type)\ 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 d72574db1f07b..90ec4cb8a9dda 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, 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 e2303f9eaf13b..16c5610b49acc 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 @@ -28,7 +28,9 @@ #include "dcn20/dcn20_optc.h" -#define V_TOTAL_REGS_DCN30_SRI(inst) +#define V_TOTAL_REGS_DCN30_SRI(inst) \ + SRI(OTG_V_TOTAL_MID, OTG, inst),\ + SRI(OTG_DRR_V_TOTAL_REACH_RANGE, OTG, inst), #define OPTC_COMMON_REG_LIST_DCN3_BASE(inst) \ SRI(OTG_VSTARTUP_PARAM, OTG, inst),\ @@ -112,7 +114,9 @@ SR(DWB_SOURCE_SELECT),\ SRI(OTG_PIPE_UPDATE_STATUS, OTG, inst) -#define DCN30_VTOTAL_REGS_SF(mask_sh) +#define DCN30_VTOTAL_REGS_SF(mask_sh)\ + SF(OTG0_OTG_DRR_V_TOTAL_REACH_RANGE, OTG_DRR_V_TOTAL_REACH_LOWER_RANGE, mask_sh),\ + SF(OTG0_OTG_DRR_V_TOTAL_REACH_RANGE, OTG_DRR_V_TOTAL_REACH_UPPER_RANGE, mask_sh), #define OPTC_COMMON_MASK_SH_LIST_DCN3_BASE(mask_sh)\ SF(OTG0_OTG_VSTARTUP_PARAM, VSTARTUP_START, mask_sh),\ 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 5f53f8747812a..98aaa22ce81cf 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 @@ -320,6 +320,8 @@ void optc31_read_reg_state(struct timing_generator *optc, struct dcn_optc_reg_st { struct optc *optc1 = DCN10TG_FROM_TG(optc); + optc_reg_state->otg_drr_v_total_reach_range = REG_READ(OTG_DRR_V_TOTAL_REACH_RANGE); + optc_reg_state->optc_bytes_per_pixel = REG_READ(OPTC_BYTES_PER_PIXEL); optc_reg_state->optc_data_format_control = REG_READ(OPTC_DATA_FORMAT_CONTROL); optc_reg_state->optc_data_source_select = REG_READ(OPTC_DATA_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 a8e978d1fae87..a6d76f451cf8c 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 @@ -378,6 +378,8 @@ void optc401_set_out_mux(struct timing_generator *optc, enum otg_out_mux_dest de 01 - OTG_CONTROL_OTG_OUT_MUX_1 : Reserved. 02 - OTG_CONTROL_OTG_OUT_MUX_2 : Connects to HPO. */ + if (dest == OUT_MUX_HPO_FRL) + dest = OUT_MUX_HPO_DP; REG_UPDATE(OTG_CONTROL, OTG_OUT_MUX, dest); } diff --git a/drivers/gpu/drm/amd/display/dc/resource/dce112/dce112_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dce112/dce112_resource.c index 458b14e4cb977..df2e9ee3acc34 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dce112/dce112_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dce112/dce112_resource.c @@ -980,6 +980,9 @@ enum dc_status resource_map_phy_clock_resources( || dc_is_virtual_signal(pipe_ctx->stream->signal)) pipe_ctx->clock_source = dc->res_pool->dp_clock_source; + else if (pipe_ctx->stream->signal == SIGNAL_TYPE_HDMI_FRL) + pipe_ctx->clock_source = + dc->res_pool->dp_clock_source; else { if (stream && stream->link && stream->link->link_enc) pipe_ctx->clock_source = find_matching_pll( diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn30/dcn30_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn30/dcn30_resource.c index 4a864689c44f3..c35c849e6b593 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn30/dcn30_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn30/dcn30_resource.c @@ -50,6 +50,8 @@ #include "dcn30/dcn30_vpg.h" #include "dcn30/dcn30_afmt.h" #include "dcn30/dcn30_dio_stream_encoder.h" +#include "dcn30/dcn30_hpo_frl_stream_encoder.h" +#include "dcn30/dcn30_hpo_frl_link_encoder.h" #include "dcn30/dcn30_dio_link_encoder.h" #include "dce/dce_clock_source.h" #include "dce/dce_audio.h" @@ -400,6 +402,45 @@ static const struct dcn10_link_enc_mask le_mask = { }; +#define hpo_frl_stream_encoder_reg_list(id)\ +[id] = {\ + DCN3_0_HPO_FRL_STREAM_ENC_REG_LIST(id)\ +} + +#define hpo_frl_stream_encoder_dme_reg_list(id)\ + DCN3_0_HPO_STREAM_ENC_DME_REG_LIST(id, 6) + + +static const struct dcn30_hpo_frl_stream_enc_registers hpo_frl_stream_enc_regs[] = { + hpo_frl_stream_encoder_reg_list(0), + hpo_frl_stream_encoder_dme_reg_list(6), +}; + +static const struct dcn30_hpo_frl_stream_encoder_shift hpo_se_shift = { + DCN3_0_HPO_STREAM_ENC_MASK_SH_LIST(__SHIFT) +}; + +static const struct dcn30_hpo_frl_stream_encoder_mask hpo_se_mask = { + DCN3_0_HPO_STREAM_ENC_MASK_SH_LIST(_MASK) +}; + +#define hpo_frl_link_encoder_reg_list(id)\ +[id] = {\ + DCN3_0_HPO_FRL_LINK_ENC_REG_LIST(id)\ +} + +static const struct dcn30_hpo_frl_link_encoder_registers hpo_frl_link_enc_regs[] = { + hpo_frl_link_encoder_reg_list(0), +}; + +static const struct dcn30_hpo_frl_link_encoder_shift hpo_le_shift = { + DCN3_0_HPO_FRL_LINK_ENC_MASK_SH_LIST(__SHIFT) +}; + +static const struct dcn30_hpo_frl_link_encoder_mask hpo_le_mask = { + DCN3_0_HPO_FRL_LINK_ENC_MASK_SH_LIST(_MASK) +}; + static const struct dce_panel_cntl_registers panel_cntl_regs[] = { { DCN_PANEL_CNTL_REG_LIST() } }; @@ -673,6 +714,7 @@ static const struct resource_caps res_cap_dcn3 = { .num_video_plane = 6, .num_audio = 6, .num_stream_encoder = 6, + .num_hpo_frl = 1, .num_pll = 6, .num_dwb = 1, .num_ddc = 6, @@ -1073,6 +1115,69 @@ static struct stream_encoder *dcn30_stream_encoder_create(enum engine_id eng_id, return &enc1->base; } +static struct hpo_frl_stream_encoder *dcn30_hpo_frl_stream_encoder_create(enum engine_id eng_id, + struct dc_context *ctx) +{ + struct dcn30_hpo_frl_stream_encoder *hpo_enc3; + struct afmt *afmt; + struct vpg *vpg; + int afmt_inst; + int vpg_inst; + + /* Mapping of VPG, AFMT, DME register blocks to HPO block instance */ + if (eng_id == ENGINE_ID_HPO_0) { + vpg_inst = 6; + afmt_inst = 6; + } else { + return NULL; + } + + /* allocate HPO stream encoder and create VPG sub-block */ + hpo_enc3 = kzalloc(sizeof(struct dcn30_hpo_frl_stream_encoder), GFP_KERNEL); + vpg = dcn30_vpg_create(ctx, vpg_inst); + afmt = dcn30_afmt_create(ctx, afmt_inst); + + if (!hpo_enc3 || !vpg || !afmt) { + kfree(hpo_enc3); + kfree(vpg); + kfree(afmt); + return NULL; + } + + dcn30_hpo_frl_stream_encoder_construct(hpo_enc3, + ctx, + ctx->dc_bios, + eng_id, + vpg, + afmt, + &hpo_frl_stream_enc_regs[eng_id - ENGINE_ID_HPO_0], + &hpo_se_shift, &hpo_se_mask); + + return &hpo_enc3->base; +} + +static struct hpo_frl_link_encoder *dcn30_hpo_frl_link_encoder_create(enum engine_id eng_id, + struct dc_context *ctx) +{ + struct dcn30_hpo_frl_link_encoder *hpo_enc3; + + ASSERT((eng_id == ENGINE_ID_HPO_0) || (eng_id == ENGINE_ID_HPO_1)); + + /* allocate HPO link encoder */ + hpo_enc3 = kzalloc(sizeof(struct dcn30_hpo_frl_link_encoder), GFP_KERNEL); + if (!hpo_enc3) + return NULL; /* out of memory */ + + hpo_frl_link_encoder3_construct(hpo_enc3, + ctx, + eng_id-ENGINE_ID_HPO_0, + &hpo_frl_link_enc_regs[eng_id - ENGINE_ID_HPO_0], + &hpo_le_shift, + &hpo_le_mask); + + return &hpo_enc3->base; +} + static struct dce_hwseq *dcn30_hwseq_create(struct dc_context *ctx) { struct dce_hwseq *hws = kzalloc_obj(struct dce_hwseq); @@ -1089,6 +1194,7 @@ static const struct resource_create_funcs res_create_funcs = { .read_dce_straps = read_dce_straps, .create_audio = dcn30_create_audio, .create_stream_encoder = dcn30_stream_encoder_create, + .create_hpo_frl_stream_encoder = dcn30_hpo_frl_stream_encoder_create, .create_hwseq = dcn30_hwseq_create, }; @@ -1111,7 +1217,24 @@ static void dcn30_resource_destruct(struct dcn30_resource_pool *pool) } } - for (i = 0; i < pool->base.res_cap->num_dsc; i++) { + for (i = 0; i < pool->base.hpo_frl_stream_enc_count; i++) { + if (pool->base.hpo_frl_stream_enc[i] != NULL) { + if (pool->base.hpo_frl_stream_enc[i]->vpg != NULL) { + kfree(DCN30_VPG_FROM_VPG(pool->base.hpo_frl_stream_enc[i]->vpg)); + pool->base.hpo_frl_stream_enc[i]->vpg = NULL; + } + + if (pool->base.hpo_frl_stream_enc[i]->afmt != NULL) { + kfree(DCN30_AFMT_FROM_AFMT(pool->base.hpo_frl_stream_enc[i]->afmt)); + pool->base.hpo_frl_stream_enc[i]->afmt = NULL; + } + + kfree(DCN30_HPO_FRL_STRENC_FROM_HPO_FRL_STRENC(pool->base.hpo_frl_stream_enc[i])); + pool->base.hpo_frl_stream_enc[i] = NULL; + } + } + + for (i = 0; i < (unsigned int)pool->base.res_cap->num_dsc; i++) { if (pool->base.dscs[i] != NULL) dcn20_dsc_destroy(&pool->base.dscs[i]); } @@ -2268,6 +2391,7 @@ static const struct resource_funcs dcn30_res_pool_funcs = { .destroy = dcn30_destroy_resource_pool, .link_enc_create = dcn30_link_encoder_create, .panel_cntl_create = dcn30_panel_cntl_create, + .hpo_frl_link_enc_create = dcn30_hpo_frl_link_encoder_create, .validate_bandwidth = dcn30_validate_bandwidth, .calculate_wm_and_dlg = dcn30_calculate_wm_and_dlg, .update_soc_for_wm_a = dcn30_update_soc_for_wm_a, @@ -2351,6 +2475,8 @@ static bool dcn30_resource_construct( dc->caps.max_slave_rgb_planes = 2; dc->caps.post_blend_color_processing = true; dc->caps.force_dp_tps4_for_cp2520 = true; + dc->caps.hdmi_hpo = true; + dc->config.skip_frl_pretraining = true; dc->caps.extended_aux_timeout_support = true; dc->caps.dmcub_support = true; diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn301/dcn301_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn301/dcn301_resource.c index a74b150cbe9c4..730a1f3dc7782 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn301/dcn301_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn301/dcn301_resource.c @@ -644,6 +644,7 @@ static struct resource_caps res_cap_dcn301 = { .num_video_plane = 4, .num_audio = 4, .num_stream_encoder = 4, + .num_hpo_frl = 0, .num_pll = 4, .num_dwb = 1, .num_ddc = 4, diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn302/dcn302_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn302/dcn302_resource.c index 539c5aa6bffaa..d45d8a67f03df 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn302/dcn302_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn302/dcn302_resource.c @@ -32,6 +32,8 @@ #include "dcn30/dcn30_dio_stream_encoder.h" #include "dcn30/dcn30_dwb.h" #include "dcn30/dcn30_dpp.h" +#include "dcn30/dcn30_hpo_frl_link_encoder.h" +#include "dcn30/dcn30_hpo_frl_stream_encoder.h" #include "dcn30/dcn30_hubbub.h" #include "dcn30/dcn30_hubp.h" #include "dcn30/dcn30_mmhubbub.h" @@ -129,6 +131,7 @@ static const struct resource_caps res_cap_dcn302 = { .num_video_plane = 5, .num_audio = 5, .num_stream_encoder = 5, + .num_hpo_frl = 1, .num_dwb = 1, .num_ddc = 5, .num_vmid = 16, @@ -451,6 +454,91 @@ static struct stream_encoder *dcn302_stream_encoder_create(enum engine_id eng_id return &enc1->base; } +#define hpo_frl_stream_encoder_reg_list(id)\ + [id] = { DCN3_0_HPO_FRL_STREAM_ENC_REG_LIST(id) } + +#define hpo_frl_stream_encoder_dme_reg_list(id)\ + DCN3_0_HPO_STREAM_ENC_DME_REG_LIST(id, 5) + +static const struct dcn30_hpo_frl_stream_enc_registers hpo_frl_stream_enc_regs[] = { + hpo_frl_stream_encoder_reg_list(0), + hpo_frl_stream_encoder_dme_reg_list(5), +}; + +static const struct dcn30_hpo_frl_stream_encoder_shift hpo_se_shift = { + DCN3_0_HPO_STREAM_ENC_MASK_SH_LIST(__SHIFT) +}; + +static const struct dcn30_hpo_frl_stream_encoder_mask hpo_se_mask = { + DCN3_0_HPO_STREAM_ENC_MASK_SH_LIST(_MASK) +}; + +static struct hpo_frl_stream_encoder *dcn302_hpo_frl_stream_encoder_create(enum engine_id eng_id, + struct dc_context *ctx) +{ + struct dcn30_hpo_frl_stream_encoder *hpo_enc3; + struct vpg *vpg; + struct afmt *afmt; + int vpg_inst; + int afmt_inst; + + /* Mapping of VPG, AFMT, DME register blocks to HPO block instance */ + if (eng_id == ENGINE_ID_HPO_0) { + vpg_inst = 5; + afmt_inst = 5; + } else + return NULL; + + /* allocate HPO stream encoder and create VPG sub-block */ + hpo_enc3 = kzalloc(sizeof(struct dcn30_hpo_frl_stream_encoder), GFP_KERNEL); + vpg = dcn302_vpg_create(ctx, vpg_inst); + afmt = dcn302_afmt_create(ctx, afmt_inst); + + if (!hpo_enc3 || !vpg || !afmt) { + kfree(hpo_enc3); + kfree(vpg); + kfree(afmt); + return NULL; + } + + dcn30_hpo_frl_stream_encoder_construct(hpo_enc3, ctx, ctx->dc_bios, eng_id, vpg, afmt, + &hpo_frl_stream_enc_regs[eng_id-ENGINE_ID_HPO_0], &hpo_se_shift, &hpo_se_mask); + + return &hpo_enc3->base; +} + +#define hpo_frl_link_encoder_reg_list(id)\ + [id] = { DCN3_0_HPO_FRL_LINK_ENC_REG_LIST(id) } + +static const struct dcn30_hpo_frl_link_encoder_registers hpo_frl_link_enc_regs[] = { + hpo_frl_link_encoder_reg_list(0), +}; + +static const struct dcn30_hpo_frl_link_encoder_shift hpo_le_shift = { + DCN3_0_HPO_FRL_LINK_ENC_MASK_SH_LIST(__SHIFT) +}; + +static const struct dcn30_hpo_frl_link_encoder_mask hpo_le_mask = { + DCN3_0_HPO_FRL_LINK_ENC_MASK_SH_LIST(_MASK) +}; + +static struct hpo_frl_link_encoder *dcn302_hpo_frl_link_encoder_create(enum engine_id eng_id, struct dc_context *ctx) +{ + struct dcn30_hpo_frl_link_encoder *hpo_enc3; + + ASSERT((eng_id == ENGINE_ID_HPO_0) || (eng_id == ENGINE_ID_HPO_1)); + + /* allocate HPO link encoder */ + hpo_enc3 = kzalloc(sizeof(struct dcn30_hpo_frl_link_encoder), GFP_KERNEL); + if (!hpo_enc3) + return NULL; /* out of memory */ + + hpo_frl_link_encoder3_construct(hpo_enc3, ctx, eng_id-ENGINE_ID_HPO_0, + &hpo_frl_link_enc_regs[eng_id-ENGINE_ID_HPO_0], &hpo_le_shift, &hpo_le_mask); + + return &hpo_enc3->base; +} + #define clk_src_regs(index, pllid)\ [index] = { CS_COMMON_REG_LIST_DCN3_02(index, pllid) } @@ -970,6 +1058,7 @@ static const struct resource_create_funcs res_create_funcs = { .read_dce_straps = read_dce_straps, .create_audio = dcn302_create_audio, .create_stream_encoder = dcn302_stream_encoder_create, + .create_hpo_frl_stream_encoder = dcn302_hpo_frl_stream_encoder_create, .create_hwseq = dcn302_hwseq_create, }; @@ -1036,7 +1125,24 @@ static void dcn302_resource_destruct(struct resource_pool *pool) } } - for (i = 0; i < pool->res_cap->num_dsc; i++) { + for (i = 0; i < pool->hpo_frl_stream_enc_count; i++) { + if (pool->hpo_frl_stream_enc[i] != NULL) { + if (pool->hpo_frl_stream_enc[i]->vpg != NULL) { + kfree(DCN30_VPG_FROM_VPG(pool->hpo_frl_stream_enc[i]->vpg)); + pool->hpo_frl_stream_enc[i]->vpg = NULL; + } + + if (pool->hpo_frl_stream_enc[i]->afmt != NULL) { + kfree(DCN30_AFMT_FROM_AFMT(pool->hpo_frl_stream_enc[i]->afmt)); + pool->hpo_frl_stream_enc[i]->afmt = NULL; + } + + kfree(DCN30_HPO_FRL_STRENC_FROM_HPO_FRL_STRENC(pool->hpo_frl_stream_enc[i])); + pool->hpo_frl_stream_enc[i] = NULL; + } + } + + for (i = 0; i < (unsigned int)pool->res_cap->num_dsc; i++) { if (pool->dscs[i] != NULL) dcn20_dsc_destroy(&pool->dscs[i]); } @@ -1172,6 +1278,7 @@ static struct resource_funcs dcn302_res_pool_funcs = { .destroy = dcn302_destroy_resource_pool, .link_enc_create = dcn302_link_encoder_create, .panel_cntl_create = dcn302_panel_cntl_create, + .hpo_frl_link_enc_create = dcn302_hpo_frl_link_encoder_create, .validate_bandwidth = dcn30_validate_bandwidth, .calculate_wm_and_dlg = dcn30_calculate_wm_and_dlg, .update_soc_for_wm_a = dcn30_update_soc_for_wm_a, @@ -1270,6 +1377,8 @@ static bool dcn302_resource_construct( dc->caps.max_slave_rgb_planes = 2; dc->caps.post_blend_color_processing = true; dc->caps.force_dp_tps4_for_cp2520 = true; + dc->caps.hdmi_hpo = true; + dc->config.skip_frl_pretraining = true; dc->caps.extended_aux_timeout_support = true; dc->caps.dmcub_support = true; dc->caps.max_v_total = (1 << 15) - 1; diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn303/dcn303_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn303/dcn303_resource.c index 529eccb4ed3bf..88cafee4d2978 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn303/dcn303_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn303/dcn303_resource.c @@ -32,6 +32,8 @@ #include "dcn30/dcn30_dio_stream_encoder.h" #include "dcn30/dcn30_dpp.h" #include "dcn30/dcn30_dwb.h" +#include "dcn30/dcn30_hpo_frl_link_encoder.h" +#include "dcn30/dcn30_hpo_frl_stream_encoder.h" #include "dcn30/dcn30_hubbub.h" #include "dcn30/dcn30_hubp.h" #include "dcn30/dcn30_mmhubbub.h" @@ -126,6 +128,7 @@ static const struct resource_caps res_cap_dcn303 = { .num_video_plane = 2, .num_audio = 2, .num_stream_encoder = 2, + .num_hpo_frl = 1, .num_dwb = 1, .num_ddc = 2, .num_vmid = 16, @@ -438,6 +441,91 @@ static struct stream_encoder *dcn303_stream_encoder_create(enum engine_id eng_id return &enc1->base; } +#define hpo_frl_stream_encoder_reg_list(id)\ + [id] = { DCN3_0_HPO_FRL_STREAM_ENC_REG_LIST(id) } + +#define hpo_frl_stream_encoder_dme_reg_list(id)\ + DCN3_0_HPO_STREAM_ENC_DME_REG_LIST(id, 2) + +static const struct dcn30_hpo_frl_stream_enc_registers hpo_frl_stream_enc_regs[] = { + hpo_frl_stream_encoder_reg_list(0), + hpo_frl_stream_encoder_dme_reg_list(2), +}; + +static const struct dcn30_hpo_frl_stream_encoder_shift hpo_se_shift = { + DCN3_0_HPO_STREAM_ENC_MASK_SH_LIST(__SHIFT) +}; + +static const struct dcn30_hpo_frl_stream_encoder_mask hpo_se_mask = { + DCN3_0_HPO_STREAM_ENC_MASK_SH_LIST(_MASK) +}; + +static struct hpo_frl_stream_encoder *dcn303_hpo_frl_stream_encoder_create(enum engine_id eng_id, + struct dc_context *ctx) +{ + struct dcn30_hpo_frl_stream_encoder *hpo_enc3; + struct vpg *vpg; + struct afmt *afmt; + int vpg_inst; + int afmt_inst; + + /* Mapping of VPG, AFMT, DME register blocks to HPO block instance */ + if (eng_id == ENGINE_ID_HPO_0) { + vpg_inst = 2; + afmt_inst = 2; + } else + return NULL; + + /* allocate HPO stream encoder and create VPG sub-block */ + hpo_enc3 = kzalloc(sizeof(struct dcn30_hpo_frl_stream_encoder), GFP_KERNEL); + vpg = dcn303_vpg_create(ctx, vpg_inst); + afmt = dcn303_afmt_create(ctx, afmt_inst); + + if (!hpo_enc3 || !vpg || !afmt) { + kfree(hpo_enc3); + kfree(vpg); + kfree(afmt); + return NULL; + } + + dcn30_hpo_frl_stream_encoder_construct(hpo_enc3, ctx, ctx->dc_bios, eng_id, vpg, afmt, + &hpo_frl_stream_enc_regs[eng_id-ENGINE_ID_HPO_0], &hpo_se_shift, &hpo_se_mask); + + return &hpo_enc3->base; +} + +#define hpo_frl_link_encoder_reg_list(id)\ + [id] = { DCN3_0_HPO_FRL_LINK_ENC_REG_LIST(id) } + +static const struct dcn30_hpo_frl_link_encoder_registers hpo_frl_link_enc_regs[] = { + hpo_frl_link_encoder_reg_list(0), +}; + +static const struct dcn30_hpo_frl_link_encoder_shift hpo_le_shift = { + DCN3_0_HPO_FRL_LINK_ENC_MASK_SH_LIST(__SHIFT) +}; + +static const struct dcn30_hpo_frl_link_encoder_mask hpo_le_mask = { + DCN3_0_HPO_FRL_LINK_ENC_MASK_SH_LIST(_MASK) +}; + +static struct hpo_frl_link_encoder *dcn303_hpo_frl_link_encoder_create(enum engine_id eng_id, struct dc_context *ctx) +{ + struct dcn30_hpo_frl_link_encoder *hpo_enc3; + + ASSERT((eng_id == ENGINE_ID_HPO_0) || (eng_id == ENGINE_ID_HPO_1)); + + /* allocate HPO link encoder */ + hpo_enc3 = kzalloc(sizeof(struct dcn30_hpo_frl_link_encoder), GFP_KERNEL); + if (!hpo_enc3) + return NULL; /* out of memory */ + + hpo_frl_link_encoder3_construct(hpo_enc3, ctx, eng_id-ENGINE_ID_HPO_0, + &hpo_frl_link_enc_regs[eng_id-ENGINE_ID_HPO_0], &hpo_le_shift, &hpo_le_mask); + + return &hpo_enc3->base; +} + #define clk_src_regs(index, pllid)\ [index] = { CS_COMMON_REG_LIST_DCN3_03(index, pllid) } @@ -915,6 +1003,7 @@ static const struct resource_create_funcs res_create_funcs = { .read_dce_straps = read_dce_straps, .create_audio = dcn303_create_audio, .create_stream_encoder = dcn303_stream_encoder_create, + .create_hpo_frl_stream_encoder = dcn303_hpo_frl_stream_encoder_create, .create_hwseq = dcn303_hwseq_create, }; @@ -980,7 +1069,24 @@ static void dcn303_resource_destruct(struct resource_pool *pool) } } - for (i = 0; i < pool->res_cap->num_dsc; i++) { + for (i = 0; i < pool->hpo_frl_stream_enc_count; i++) { + if (pool->hpo_frl_stream_enc[i] != NULL) { + if (pool->hpo_frl_stream_enc[i]->vpg != NULL) { + kfree(DCN30_VPG_FROM_VPG(pool->hpo_frl_stream_enc[i]->vpg)); + pool->hpo_frl_stream_enc[i]->vpg = NULL; + } + + if (pool->hpo_frl_stream_enc[i]->afmt != NULL) { + kfree(DCN30_AFMT_FROM_AFMT(pool->hpo_frl_stream_enc[i]->afmt)); + pool->hpo_frl_stream_enc[i]->afmt = NULL; + } + + kfree(DCN30_HPO_FRL_STRENC_FROM_HPO_FRL_STRENC(pool->hpo_frl_stream_enc[i])); + pool->hpo_frl_stream_enc[i] = NULL; + } + } + + for (i = 0; i < (unsigned int)pool->res_cap->num_dsc; i++) { if (pool->dscs[i] != NULL) dcn20_dsc_destroy(&pool->dscs[i]); } @@ -1116,6 +1222,7 @@ static struct resource_funcs dcn303_res_pool_funcs = { .destroy = dcn303_destroy_resource_pool, .link_enc_create = dcn303_link_encoder_create, .panel_cntl_create = dcn303_panel_cntl_create, + .hpo_frl_link_enc_create = dcn303_hpo_frl_link_encoder_create, .validate_bandwidth = dcn30_validate_bandwidth, .calculate_wm_and_dlg = dcn30_calculate_wm_and_dlg, .update_soc_for_wm_a = dcn30_update_soc_for_wm_a, @@ -1214,6 +1321,8 @@ static bool dcn303_resource_construct( dc->caps.max_slave_rgb_planes = 1; dc->caps.post_blend_color_processing = true; dc->caps.force_dp_tps4_for_cp2520 = true; + dc->caps.hdmi_hpo = true; + dc->config.skip_frl_pretraining = true; dc->caps.extended_aux_timeout_support = true; dc->caps.dmcub_support = true; dc->caps.max_v_total = (1 << 15) - 1; diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn31/dcn31_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn31/dcn31_resource.c index 39944d90ea980..bdc945e71dd49 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn31/dcn31_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn31/dcn31_resource.c @@ -54,6 +54,8 @@ #include "dcn30/dcn30_vpg.h" #include "dcn30/dcn30_afmt.h" #include "dcn30/dcn30_dio_stream_encoder.h" +#include "dcn30/dcn30_hpo_frl_stream_encoder.h" +#include "dcn30/dcn30_hpo_frl_link_encoder.h" #include "dcn31/dcn31_hpo_dp_stream_encoder.h" #include "dcn31/dcn31_hpo_dp_link_encoder.h" #include "dcn31/dcn31_apg.h" @@ -431,6 +433,45 @@ static const struct dcn10_link_enc_mask le_mask = { DPCS_DCN31_MASK_SH_LIST(_MASK) }; +#define hpo_frl_stream_encoder_reg_list(id)\ +[id] = {\ + DCN3_0_HPO_FRL_STREAM_ENC_REG_LIST(id)\ +} + +#define hpo_frl_stream_encoder_dme_reg_list(id)\ + DCN3_0_HPO_STREAM_ENC_DME_REG_LIST(id, 6) + + +static const struct dcn30_hpo_frl_stream_enc_registers hpo_frl_stream_enc_regs[] = { + hpo_frl_stream_encoder_reg_list(0), + hpo_frl_stream_encoder_dme_reg_list(6), +}; + +static const struct dcn30_hpo_frl_stream_encoder_shift hpo_se_shift = { + DCN3_0_HPO_STREAM_ENC_MASK_SH_LIST(__SHIFT) +}; + +static const struct dcn30_hpo_frl_stream_encoder_mask hpo_se_mask = { + DCN3_0_HPO_STREAM_ENC_MASK_SH_LIST(_MASK) +}; + +#define hpo_frl_link_encoder_reg_list(id)\ +[id] = {\ + DCN3_0_HPO_FRL_LINK_ENC_REG_LIST(id)\ +} + +static const struct dcn30_hpo_frl_link_encoder_registers hpo_frl_link_enc_regs[] = { + hpo_frl_link_encoder_reg_list(0), +}; + +static const struct dcn30_hpo_frl_link_encoder_shift hpo_le_shift = { + DCN3_0_HPO_FRL_LINK_ENC_MASK_SH_LIST(__SHIFT) +}; + +static const struct dcn30_hpo_frl_link_encoder_mask hpo_le_mask = { + DCN3_0_HPO_FRL_LINK_ENC_MASK_SH_LIST(_MASK) +}; + #define hpo_dp_stream_encoder_reg_list(id)\ [id] = {\ DCN3_1_HPO_DP_STREAM_ENC_REG_LIST(id)\ @@ -833,6 +874,7 @@ static const struct resource_caps res_cap_dcn31 = { .num_audio = 5, .num_stream_encoder = 5, .num_dig_link_enc = 5, + .num_hpo_frl = 1, .num_hpo_dp_stream_encoder = 4, .num_hpo_dp_link_encoder = 2, .num_pll = 5, @@ -904,6 +946,7 @@ static const struct dc_debug_options debug_defaults_drv = { } }, .disable_z10 = true, + .max_frl_rate = HDMI_FRL_LINK_RATE_10GBPS, .enable_z9_disable_interface = true, /* Allow support for the PMFW interface for disable Z9*/ .dml_hostvm_override = DML_HOSTVM_OVERRIDE_FALSE, .using_dml2 = false, @@ -1281,6 +1324,69 @@ static struct stream_encoder *dcn31_stream_encoder_create( return &enc1->base; } +static struct hpo_frl_stream_encoder *dcn31_hpo_frl_stream_encoder_create(enum engine_id eng_id, + struct dc_context *ctx) +{ + struct dcn30_hpo_frl_stream_encoder *hpo_enc3; + struct afmt *afmt; + struct vpg *vpg; + int afmt_inst; + int vpg_inst; + + /* Mapping of VPG, AFMT, DME register blocks to HPO block instance */ + if (eng_id == ENGINE_ID_HPO_0) { + vpg_inst = 5; + afmt_inst = 5; + } else { + return NULL; + } + + /* allocate HPO stream encoder and create VPG, AFMT sub-blocks */ + hpo_enc3 = kzalloc(sizeof(struct dcn30_hpo_frl_stream_encoder), GFP_KERNEL); + vpg = dcn31_vpg_create(ctx, vpg_inst); + afmt = dcn31_afmt_create(ctx, afmt_inst); + + if (!hpo_enc3 || !vpg || !afmt) { + kfree(hpo_enc3); + kfree(vpg); + kfree(afmt); + return NULL; + } + + dcn30_hpo_frl_stream_encoder_construct(hpo_enc3, + ctx, + ctx->dc_bios, + eng_id, + vpg, + afmt, + &hpo_frl_stream_enc_regs[eng_id - ENGINE_ID_HPO_0], + &hpo_se_shift, &hpo_se_mask); + + return &hpo_enc3->base; +} + +static struct hpo_frl_link_encoder *dcn31_hpo_frl_link_encoder_create(enum engine_id eng_id, + struct dc_context *ctx) +{ + struct dcn30_hpo_frl_link_encoder *hpo_enc3; + + ASSERT((eng_id == ENGINE_ID_HPO_0) || (eng_id == ENGINE_ID_HPO_1)); + + /* allocate HPO link encoder */ + hpo_enc3 = kzalloc(sizeof(struct dcn30_hpo_frl_link_encoder), GFP_KERNEL); + if (!hpo_enc3) + return NULL; /* out of memory */ + + hpo_frl_link_encoder3_construct(hpo_enc3, + ctx, + eng_id - ENGINE_ID_HPO_0, + &hpo_frl_link_enc_regs[eng_id - ENGINE_ID_HPO_0], + &hpo_le_shift, + &hpo_le_mask); + + return &hpo_enc3->base; +} + static struct hpo_dp_stream_encoder *dcn31_hpo_dp_stream_encoder_create( enum engine_id eng_id, struct dc_context *ctx) @@ -1366,6 +1472,7 @@ static const struct resource_create_funcs res_create_funcs = { .read_dce_straps = read_dce_straps, .create_audio = dcn31_create_audio, .create_stream_encoder = dcn31_stream_encoder_create, + .create_hpo_frl_stream_encoder = dcn31_hpo_frl_stream_encoder_create, .create_hpo_dp_stream_encoder = dcn31_hpo_dp_stream_encoder_create, .create_hpo_dp_link_encoder = dcn31_hpo_dp_link_encoder_create, .create_hwseq = dcn31_hwseq_create, @@ -1390,6 +1497,23 @@ static void dcn31_resource_destruct(struct dcn31_resource_pool *pool) } } + for (i = 0; i < pool->base.hpo_frl_stream_enc_count; i++) { + if (pool->base.hpo_frl_stream_enc[i] != NULL) { + if (pool->base.hpo_frl_stream_enc[i]->vpg != NULL) { + kfree(DCN30_VPG_FROM_VPG(pool->base.hpo_frl_stream_enc[i]->vpg)); + pool->base.hpo_frl_stream_enc[i]->vpg = NULL; + } + + if (pool->base.hpo_frl_stream_enc[i]->afmt != NULL) { + kfree(DCN30_AFMT_FROM_AFMT(pool->base.hpo_frl_stream_enc[i]->afmt)); + pool->base.hpo_frl_stream_enc[i]->afmt = NULL; + } + + kfree(DCN30_HPO_FRL_STRENC_FROM_HPO_FRL_STRENC(pool->base.hpo_frl_stream_enc[i])); + pool->base.hpo_frl_stream_enc[i] = NULL; + } + } + for (i = 0; i < pool->base.hpo_dp_stream_enc_count; i++) { if (pool->base.hpo_dp_stream_enc[i] != NULL) { if (pool->base.hpo_dp_stream_enc[i]->vpg != NULL) { @@ -1867,6 +1991,7 @@ static struct resource_funcs dcn31_res_pool_funcs = { .link_enc_create_minimal = dcn31_link_enc_create_minimal, .link_encs_assign = link_enc_cfg_link_encs_assign, .link_enc_unassign = link_enc_cfg_link_enc_unassign, + .hpo_frl_link_enc_create = dcn31_hpo_frl_link_encoder_create, .panel_cntl_create = dcn31_panel_cntl_create, .validate_bandwidth = dcn31_validate_bandwidth, .calculate_wm_and_dlg = dcn31_calculate_wm_and_dlg, @@ -1951,6 +2076,8 @@ static bool dcn31_resource_construct( dc->caps.force_dp_tps4_for_cp2520 = true; if (dc->config.forceHBR2CP2520) dc->caps.force_dp_tps4_for_cp2520 = false; + dc->caps.hdmi_hpo = true; + dc->config.skip_frl_pretraining = true; dc->caps.dp_hpo = true; dc->caps.dp_hdmi21_pcon_support = true; dc->caps.edp_dsc_support = true; diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn314/dcn314_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn314/dcn314_resource.c index 5acc545bbe7f3..bfb799202851d 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn314/dcn314_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn314/dcn314_resource.c @@ -57,6 +57,8 @@ #include "dcn30/dcn30_afmt.h" #include "dcn31/dcn31_dio_link_encoder.h" #include "dcn314/dcn314_dio_stream_encoder.h" +#include "dcn30/dcn30_hpo_frl_stream_encoder.h" +#include "dcn30/dcn30_hpo_frl_link_encoder.h" #include "dcn31/dcn31_hpo_dp_stream_encoder.h" #include "dcn31/dcn31_hpo_dp_link_encoder.h" #include "dcn31/dcn31_apg.h" @@ -438,6 +440,45 @@ static const struct dcn10_link_enc_mask le_mask = { DPCS_DCN31_MASK_SH_LIST(_MASK) }; +#define hpo_frl_stream_encoder_reg_list(id)\ +[id] = {\ + DCN3_0_HPO_FRL_STREAM_ENC_REG_LIST(id)\ +} + +#define hpo_frl_stream_encoder_dme_reg_list(id)\ + DCN3_0_HPO_STREAM_ENC_DME_REG_LIST(id, 6) + + +static const struct dcn30_hpo_frl_stream_enc_registers hpo_frl_stream_enc_regs[] = { + hpo_frl_stream_encoder_reg_list(0), + hpo_frl_stream_encoder_dme_reg_list(6), +}; + +static const struct dcn30_hpo_frl_stream_encoder_shift hpo_se_shift = { + DCN3_0_HPO_STREAM_ENC_MASK_SH_LIST(__SHIFT) +}; + +static const struct dcn30_hpo_frl_stream_encoder_mask hpo_se_mask = { + DCN3_0_HPO_STREAM_ENC_MASK_SH_LIST(_MASK) +}; + +#define hpo_frl_link_encoder_reg_list(id)\ +[id] = {\ + DCN3_0_HPO_FRL_LINK_ENC_REG_LIST(id)\ +} + +static const struct dcn30_hpo_frl_link_encoder_registers hpo_frl_link_enc_regs[] = { + hpo_frl_link_encoder_reg_list(0), +}; + +static const struct dcn30_hpo_frl_link_encoder_shift hpo_le_shift = { + DCN3_0_HPO_FRL_LINK_ENC_MASK_SH_LIST(__SHIFT) +}; + +static const struct dcn30_hpo_frl_link_encoder_mask hpo_le_mask = { + DCN3_0_HPO_FRL_LINK_ENC_MASK_SH_LIST(_MASK) +}; + #define hpo_dp_stream_encoder_reg_list(id)\ [id] = {\ DCN3_1_HPO_DP_STREAM_ENC_REG_LIST(id)\ @@ -845,6 +886,7 @@ static const struct resource_caps res_cap_dcn314 = { .num_audio = 5, .num_stream_encoder = 5, .num_dig_link_enc = 5, + .num_hpo_frl = 1, .num_hpo_dp_stream_encoder = 4, .num_hpo_dp_link_encoder = 2, .num_pll = 5, @@ -1339,6 +1381,71 @@ static struct stream_encoder *dcn314_stream_encoder_create( return &enc1->base; } +static struct hpo_frl_stream_encoder *dcn31_hpo_frl_stream_encoder_create(enum engine_id eng_id, + struct dc_context *ctx) +{ + struct dcn30_hpo_frl_stream_encoder *hpo_enc3; + struct afmt *afmt; + struct vpg *vpg; + int afmt_inst; + int vpg_inst; + + /* Mapping of VPG, AFMT, DME register blocks to HPO block instance */ + if (eng_id == ENGINE_ID_HPO_0) { + //Maps to VPG INST 5, vpg_inst 5 reg offset padded to inst 9 + vpg_inst = 9; + afmt_inst = 5; + } else { + return NULL; + } + + /* allocate HPO stream encoder and create VPG, AFMT sub-blocks */ + hpo_enc3 = kzalloc(sizeof(struct dcn30_hpo_frl_stream_encoder), GFP_KERNEL); + vpg = dcn31_vpg_create(ctx, vpg_inst); + afmt = dcn31_afmt_create(ctx, afmt_inst); + + if (!hpo_enc3 || !vpg || !afmt) { + kfree(hpo_enc3); + kfree(vpg); + kfree(afmt); + return NULL; + } + + dcn30_hpo_frl_stream_encoder_construct(hpo_enc3, + ctx, + ctx->dc_bios, + eng_id, + vpg, + afmt, + &hpo_frl_stream_enc_regs[eng_id - ENGINE_ID_HPO_0], + &hpo_se_shift, + &hpo_se_mask); + + return &hpo_enc3->base; +} + +static struct hpo_frl_link_encoder *dcn31_hpo_frl_link_encoder_create(enum engine_id eng_id, + struct dc_context *ctx) +{ + struct dcn30_hpo_frl_link_encoder *hpo_enc3; + + ASSERT((eng_id == ENGINE_ID_HPO_0) || (eng_id == ENGINE_ID_HPO_1)); + + /* allocate HPO link encoder */ + hpo_enc3 = kzalloc(sizeof(struct dcn30_hpo_frl_link_encoder), GFP_KERNEL); + if (!hpo_enc3) + return NULL; /* out of memory */ + + hpo_frl_link_encoder3_construct(hpo_enc3, + ctx, + eng_id - ENGINE_ID_HPO_0, + &hpo_frl_link_enc_regs[eng_id - ENGINE_ID_HPO_0], + &hpo_le_shift, + &hpo_le_mask); + + return &hpo_enc3->base; +} + static struct hpo_dp_stream_encoder *dcn31_hpo_dp_stream_encoder_create( enum engine_id eng_id, struct dc_context *ctx) @@ -1425,6 +1532,7 @@ static const struct resource_create_funcs res_create_funcs = { .read_dce_straps = read_dce_straps, .create_audio = dcn31_create_audio, .create_stream_encoder = dcn314_stream_encoder_create, + .create_hpo_frl_stream_encoder = dcn31_hpo_frl_stream_encoder_create, .create_hpo_dp_stream_encoder = dcn31_hpo_dp_stream_encoder_create, .create_hpo_dp_link_encoder = dcn31_hpo_dp_link_encoder_create, .create_hwseq = dcn314_hwseq_create, @@ -1449,6 +1557,23 @@ static void dcn314_resource_destruct(struct dcn314_resource_pool *pool) } } + for (i = 0; i < pool->base.hpo_frl_stream_enc_count; i++) { + if (pool->base.hpo_frl_stream_enc[i] != NULL) { + if (pool->base.hpo_frl_stream_enc[i]->vpg != NULL) { + kfree(DCN30_VPG_FROM_VPG(pool->base.hpo_frl_stream_enc[i]->vpg)); + pool->base.hpo_frl_stream_enc[i]->vpg = NULL; + } + + if (pool->base.hpo_frl_stream_enc[i]->afmt != NULL) { + kfree(DCN30_AFMT_FROM_AFMT(pool->base.hpo_frl_stream_enc[i]->afmt)); + pool->base.hpo_frl_stream_enc[i]->afmt = NULL; + } + + kfree(DCN30_HPO_FRL_STRENC_FROM_HPO_FRL_STRENC(pool->base.hpo_frl_stream_enc[i])); + pool->base.hpo_frl_stream_enc[i] = NULL; + } + } + for (i = 0; i < pool->base.hpo_dp_stream_enc_count; i++) { if (pool->base.hpo_dp_stream_enc[i] != NULL) { if (pool->base.hpo_dp_stream_enc[i]->vpg != NULL) { @@ -1793,6 +1918,7 @@ static struct resource_funcs dcn314_res_pool_funcs = { .link_enc_create_minimal = dcn31_link_enc_create_minimal, .link_encs_assign = link_enc_cfg_link_encs_assign, .link_enc_unassign = link_enc_cfg_link_enc_unassign, + .hpo_frl_link_enc_create = dcn31_hpo_frl_link_encoder_create, .panel_cntl_create = dcn31_panel_cntl_create, .validate_bandwidth = dcn314_validate_bandwidth, .calculate_wm_and_dlg = dcn31_calculate_wm_and_dlg, @@ -1879,6 +2005,7 @@ static bool dcn314_resource_construct( dc->caps.force_dp_tps4_for_cp2520 = true; if (dc->config.forceHBR2CP2520) dc->caps.force_dp_tps4_for_cp2520 = false; + dc->caps.hdmi_hpo = true; dc->caps.dp_hpo = true; dc->caps.dp_hdmi21_pcon_support = true; dc->caps.edp_dsc_support = true; diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn315/dcn315_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn315/dcn315_resource.c index 975e14f3f5fa7..e9e99c2aa84a5 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn315/dcn315_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn315/dcn315_resource.c @@ -53,6 +53,8 @@ #include "dcn30/dcn30_vpg.h" #include "dcn30/dcn30_afmt.h" #include "dcn30/dcn30_dio_stream_encoder.h" +#include "dcn30/dcn30_hpo_frl_stream_encoder.h" +#include "dcn30/dcn30_hpo_frl_link_encoder.h" #include "dcn31/dcn31_hpo_dp_stream_encoder.h" #include "dcn31/dcn31_hpo_dp_link_encoder.h" #include "dcn31/dcn31_apg.h" @@ -433,6 +435,45 @@ static const struct dcn10_link_enc_mask le_mask = { DPCS_DCN31_MASK_SH_LIST(_MASK) }; +#define hpo_frl_stream_encoder_reg_list(id)\ +[id] = {\ + DCN3_0_HPO_FRL_STREAM_ENC_REG_LIST(id)\ +} + +#define hpo_frl_stream_encoder_dme_reg_list(id)\ + DCN3_0_HPO_STREAM_ENC_DME_REG_LIST(id, 6) + + +static const struct dcn30_hpo_frl_stream_enc_registers hpo_frl_stream_enc_regs[] = { + hpo_frl_stream_encoder_reg_list(0), + hpo_frl_stream_encoder_dme_reg_list(6), +}; + +static const struct dcn30_hpo_frl_stream_encoder_shift hpo_se_shift = { + DCN3_0_HPO_STREAM_ENC_MASK_SH_LIST(__SHIFT) +}; + +static const struct dcn30_hpo_frl_stream_encoder_mask hpo_se_mask = { + DCN3_0_HPO_STREAM_ENC_MASK_SH_LIST(_MASK) +}; + +#define hpo_frl_link_encoder_reg_list(id)\ +[id] = {\ + DCN3_0_HPO_FRL_LINK_ENC_REG_LIST(id)\ +} + +static const struct dcn30_hpo_frl_link_encoder_registers hpo_frl_link_enc_regs[] = { + hpo_frl_link_encoder_reg_list(0), +}; + +static const struct dcn30_hpo_frl_link_encoder_shift hpo_le_shift = { + DCN3_0_HPO_FRL_LINK_ENC_MASK_SH_LIST(__SHIFT) +}; + +static const struct dcn30_hpo_frl_link_encoder_mask hpo_le_mask = { + DCN3_0_HPO_FRL_LINK_ENC_MASK_SH_LIST(_MASK) +}; + #define hpo_dp_stream_encoder_reg_list(id)\ [id] = {\ DCN3_1_HPO_DP_STREAM_ENC_REG_LIST(id)\ @@ -832,6 +873,7 @@ static const struct resource_caps res_cap_dcn31 = { .num_audio = 5, .num_stream_encoder = 5, .num_dig_link_enc = 5, + .num_hpo_frl = 1, .num_hpo_dp_stream_encoder = 4, .num_hpo_dp_link_encoder = 2, .num_pll = 5, @@ -903,6 +945,7 @@ static const struct dc_debug_options debug_defaults_drv = { .afmt = true, } }, + .max_frl_rate = HDMI_FRL_LINK_RATE_12GBPS, .psr_power_use_phy_fsm = 0, .using_dml2 = false, .min_disp_clk_khz = 100000, @@ -1282,6 +1325,70 @@ static struct stream_encoder *dcn315_stream_encoder_create( return &enc1->base; } +static struct hpo_frl_stream_encoder *dcn31_hpo_frl_stream_encoder_create(enum engine_id eng_id, + struct dc_context *ctx) +{ + struct dcn30_hpo_frl_stream_encoder *hpo_enc3; + struct afmt *afmt; + struct vpg *vpg; + int afmt_inst; + int vpg_inst; + + /* Mapping of VPG, AFMT, DME register blocks to HPO block instance */ + if (eng_id == ENGINE_ID_HPO_0) { + vpg_inst = 5; + afmt_inst = 5; + } else { + return NULL; + } + + /* allocate HPO stream encoder and create VPG, AFMT sub-blocks */ + hpo_enc3 = kzalloc(sizeof(struct dcn30_hpo_frl_stream_encoder), GFP_KERNEL); + vpg = dcn31_vpg_create(ctx, vpg_inst); + afmt = dcn31_afmt_create(ctx, afmt_inst); + + if (!hpo_enc3 || !vpg || !afmt) { + kfree(hpo_enc3); + kfree(vpg); + kfree(afmt); + return NULL; + } + + dcn30_hpo_frl_stream_encoder_construct(hpo_enc3, + ctx, + ctx->dc_bios, + eng_id, + vpg, + afmt, + &hpo_frl_stream_enc_regs[eng_id - ENGINE_ID_HPO_0], + &hpo_se_shift, + &hpo_se_mask); + + return &hpo_enc3->base; +} + +static struct hpo_frl_link_encoder *dcn31_hpo_frl_link_encoder_create(enum engine_id eng_id, + struct dc_context *ctx) +{ + struct dcn30_hpo_frl_link_encoder *hpo_enc3; + + ASSERT((eng_id == ENGINE_ID_HPO_0) || (eng_id == ENGINE_ID_HPO_1)); + + /* allocate HPO link encoder */ + hpo_enc3 = kzalloc(sizeof(struct dcn30_hpo_frl_link_encoder), GFP_KERNEL); + if (!hpo_enc3) + return NULL; /* out of memory */ + + hpo_frl_link_encoder3_construct(hpo_enc3, + ctx, + eng_id - ENGINE_ID_HPO_0, + &hpo_frl_link_enc_regs[eng_id - ENGINE_ID_HPO_0], + &hpo_le_shift, + &hpo_le_mask); + + return &hpo_enc3->base; +} + static struct hpo_dp_stream_encoder *dcn31_hpo_dp_stream_encoder_create( enum engine_id eng_id, struct dc_context *ctx) @@ -1367,6 +1474,7 @@ static const struct resource_create_funcs res_create_funcs = { .read_dce_straps = read_dce_straps, .create_audio = dcn31_create_audio, .create_stream_encoder = dcn315_stream_encoder_create, + .create_hpo_frl_stream_encoder = dcn31_hpo_frl_stream_encoder_create, .create_hpo_dp_stream_encoder = dcn31_hpo_dp_stream_encoder_create, .create_hpo_dp_link_encoder = dcn31_hpo_dp_link_encoder_create, .create_hwseq = dcn31_hwseq_create, @@ -1391,6 +1499,23 @@ static void dcn315_resource_destruct(struct dcn315_resource_pool *pool) } } + for (i = 0; i < pool->base.hpo_frl_stream_enc_count; i++) { + if (pool->base.hpo_frl_stream_enc[i] != NULL) { + if (pool->base.hpo_frl_stream_enc[i]->vpg != NULL) { + kfree(DCN30_VPG_FROM_VPG(pool->base.hpo_frl_stream_enc[i]->vpg)); + pool->base.hpo_frl_stream_enc[i]->vpg = NULL; + } + + if (pool->base.hpo_frl_stream_enc[i]->afmt != NULL) { + kfree(DCN30_AFMT_FROM_AFMT(pool->base.hpo_frl_stream_enc[i]->afmt)); + pool->base.hpo_frl_stream_enc[i]->afmt = NULL; + } + + kfree(DCN30_HPO_FRL_STRENC_FROM_HPO_FRL_STRENC(pool->base.hpo_frl_stream_enc[i])); + pool->base.hpo_frl_stream_enc[i] = NULL; + } + } + for (i = 0; i < pool->base.hpo_dp_stream_enc_count; i++) { if (pool->base.hpo_dp_stream_enc[i] != NULL) { if (pool->base.hpo_dp_stream_enc[i]->vpg != NULL) { @@ -1862,6 +1987,7 @@ static struct resource_funcs dcn315_res_pool_funcs = { .link_enc_create_minimal = dcn31_link_enc_create_minimal, .link_encs_assign = link_enc_cfg_link_encs_assign, .link_enc_unassign = link_enc_cfg_link_enc_unassign, + .hpo_frl_link_enc_create = dcn31_hpo_frl_link_encoder_create, .panel_cntl_create = dcn31_panel_cntl_create, .validate_bandwidth = dcn31_validate_bandwidth, .calculate_wm_and_dlg = dcn31_calculate_wm_and_dlg, @@ -1925,6 +2051,8 @@ static bool dcn315_resource_construct( dc->caps.force_dp_tps4_for_cp2520 = true; if (dc->config.forceHBR2CP2520) dc->caps.force_dp_tps4_for_cp2520 = false; + dc->caps.hdmi_hpo = true; + dc->config.skip_frl_pretraining = true; dc->caps.dp_hpo = true; dc->caps.dp_hdmi21_pcon_support = true; dc->caps.edp_dsc_support = true; diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn316/dcn316_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn316/dcn316_resource.c index 914d91df174ca..8c21ded6f4cff 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn316/dcn316_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn316/dcn316_resource.c @@ -53,6 +53,8 @@ #include "dcn30/dcn30_vpg.h" #include "dcn30/dcn30_afmt.h" #include "dcn30/dcn30_dio_stream_encoder.h" +#include "dcn30/dcn30_hpo_frl_stream_encoder.h" +#include "dcn30/dcn30_hpo_frl_link_encoder.h" #include "dcn31/dcn31_hpo_dp_stream_encoder.h" #include "dcn31/dcn31_hpo_dp_link_encoder.h" #include "dcn31/dcn31_apg.h" @@ -421,6 +423,45 @@ static const struct dcn10_link_enc_mask le_mask = { DPCS_DCN31_MASK_SH_LIST(_MASK) }; +#define hpo_frl_stream_encoder_reg_list(id)\ +[id] = {\ + DCN3_0_HPO_FRL_STREAM_ENC_REG_LIST(id)\ +} + +#define hpo_frl_stream_encoder_dme_reg_list(id)\ + DCN3_0_HPO_STREAM_ENC_DME_REG_LIST(id, 6) + + +static const struct dcn30_hpo_frl_stream_enc_registers hpo_frl_stream_enc_regs[] = { + hpo_frl_stream_encoder_reg_list(0), + hpo_frl_stream_encoder_dme_reg_list(6), +}; + +static const struct dcn30_hpo_frl_stream_encoder_shift hpo_se_shift = { + DCN3_0_HPO_STREAM_ENC_MASK_SH_LIST(__SHIFT) +}; + +static const struct dcn30_hpo_frl_stream_encoder_mask hpo_se_mask = { + DCN3_0_HPO_STREAM_ENC_MASK_SH_LIST(_MASK) +}; + +#define hpo_frl_link_encoder_reg_list(id)\ +[id] = {\ + DCN3_0_HPO_FRL_LINK_ENC_REG_LIST(id)\ +} + +static const struct dcn30_hpo_frl_link_encoder_registers hpo_frl_link_enc_regs[] = { + hpo_frl_link_encoder_reg_list(0), +}; + +static const struct dcn30_hpo_frl_link_encoder_shift hpo_le_shift = { + DCN3_0_HPO_FRL_LINK_ENC_MASK_SH_LIST(__SHIFT) +}; + +static const struct dcn30_hpo_frl_link_encoder_mask hpo_le_mask = { + DCN3_0_HPO_FRL_LINK_ENC_MASK_SH_LIST(_MASK) +}; + #define hpo_dp_stream_encoder_reg_list(id)\ @@ -827,6 +868,7 @@ static const struct resource_caps res_cap_dcn31 = { .num_audio = 5, .num_stream_encoder = 5, .num_dig_link_enc = 5, + .num_hpo_frl = 1, .num_hpo_dp_stream_encoder = 4, .num_hpo_dp_link_encoder = 2, .num_pll = 5, @@ -898,6 +940,7 @@ static const struct dc_debug_options debug_defaults_drv = { .afmt = true, } }, + .max_frl_rate = HDMI_FRL_LINK_RATE_10GBPS, /*same as dcn3.1 for now*/ .using_dml2 = false, }; @@ -1275,6 +1318,70 @@ static struct stream_encoder *dcn316_stream_encoder_create( return &enc1->base; } +static struct hpo_frl_stream_encoder *dcn31_hpo_frl_stream_encoder_create(enum engine_id eng_id, + struct dc_context *ctx) +{ + struct dcn30_hpo_frl_stream_encoder *hpo_enc3; + struct afmt *afmt; + struct vpg *vpg; + int afmt_inst; + int vpg_inst; + + /* Mapping of VPG, AFMT, DME register blocks to HPO block instance */ + if (eng_id == ENGINE_ID_HPO_0) { + vpg_inst = 5; + afmt_inst = 5; + } else { + return NULL; + } + + /* allocate HPO stream encoder and create VPG, AFMT sub-blocks */ + hpo_enc3 = kzalloc(sizeof(struct dcn30_hpo_frl_stream_encoder), GFP_KERNEL); + vpg = dcn31_vpg_create(ctx, vpg_inst); + afmt = dcn31_afmt_create(ctx, afmt_inst); + + if (!hpo_enc3 || !vpg || !afmt) { + kfree(hpo_enc3); + kfree(vpg); + kfree(afmt); + return NULL; + } + + dcn30_hpo_frl_stream_encoder_construct(hpo_enc3, + ctx, + ctx->dc_bios, + eng_id, + vpg, + afmt, + &hpo_frl_stream_enc_regs[eng_id - ENGINE_ID_HPO_0], + &hpo_se_shift, + &hpo_se_mask); + + return &hpo_enc3->base; +} + +static struct hpo_frl_link_encoder *dcn31_hpo_frl_link_encoder_create(enum engine_id eng_id, + struct dc_context *ctx) +{ + struct dcn30_hpo_frl_link_encoder *hpo_enc3; + + ASSERT((eng_id == ENGINE_ID_HPO_0) || (eng_id == ENGINE_ID_HPO_1)); + + /* allocate HPO link encoder */ + hpo_enc3 = kzalloc(sizeof(struct dcn30_hpo_frl_link_encoder), GFP_KERNEL); + if (!hpo_enc3) + return NULL; /* out of memory */ + + hpo_frl_link_encoder3_construct(hpo_enc3, + ctx, + eng_id - ENGINE_ID_HPO_0, + &hpo_frl_link_enc_regs[eng_id - ENGINE_ID_HPO_0], + &hpo_le_shift, + &hpo_le_mask); + + return &hpo_enc3->base; +} + static struct hpo_dp_stream_encoder *dcn31_hpo_dp_stream_encoder_create( enum engine_id eng_id, @@ -1362,6 +1469,7 @@ static const struct resource_create_funcs res_create_funcs = { .read_dce_straps = read_dce_straps, .create_audio = dcn31_create_audio, .create_stream_encoder = dcn316_stream_encoder_create, + .create_hpo_frl_stream_encoder = dcn31_hpo_frl_stream_encoder_create, .create_hpo_dp_stream_encoder = dcn31_hpo_dp_stream_encoder_create, .create_hpo_dp_link_encoder = dcn31_hpo_dp_link_encoder_create, .create_hwseq = dcn31_hwseq_create, @@ -1386,6 +1494,21 @@ static void dcn316_resource_destruct(struct dcn316_resource_pool *pool) } } + for (i = 0; i < pool->base.hpo_frl_stream_enc_count; i++) { + if (pool->base.hpo_frl_stream_enc[i] != NULL) { + if (pool->base.hpo_frl_stream_enc[i]->vpg != NULL) { + kfree(DCN30_VPG_FROM_VPG(pool->base.hpo_frl_stream_enc[i]->vpg)); + pool->base.hpo_frl_stream_enc[i]->vpg = NULL; + } + if (pool->base.hpo_frl_stream_enc[i]->afmt != NULL) { + kfree(DCN30_AFMT_FROM_AFMT(pool->base.hpo_frl_stream_enc[i]->afmt)); + pool->base.hpo_frl_stream_enc[i]->afmt = NULL; + } + kfree(DCN30_HPO_FRL_STRENC_FROM_HPO_FRL_STRENC(pool->base.hpo_frl_stream_enc[i])); + pool->base.hpo_frl_stream_enc[i] = NULL; + } + } + for (i = 0; i < pool->base.hpo_dp_stream_enc_count; i++) { if (pool->base.hpo_dp_stream_enc[i] != NULL) { if (pool->base.hpo_dp_stream_enc[i]->vpg != NULL) { @@ -1738,6 +1861,7 @@ static struct resource_funcs dcn316_res_pool_funcs = { .link_enc_create_minimal = dcn31_link_enc_create_minimal, .link_encs_assign = link_enc_cfg_link_encs_assign, .link_enc_unassign = link_enc_cfg_link_enc_unassign, + .hpo_frl_link_enc_create = dcn31_hpo_frl_link_encoder_create, .panel_cntl_create = dcn31_panel_cntl_create, .validate_bandwidth = dcn31_validate_bandwidth, .calculate_wm_and_dlg = dcn31_calculate_wm_and_dlg, @@ -1801,6 +1925,8 @@ static bool dcn316_resource_construct( dc->caps.force_dp_tps4_for_cp2520 = true; if (dc->config.forceHBR2CP2520) dc->caps.force_dp_tps4_for_cp2520 = false; + dc->caps.hdmi_hpo = true; + dc->config.skip_frl_pretraining = true; dc->caps.dp_hpo = true; dc->caps.dp_hdmi21_pcon_support = true; dc->caps.edp_dsc_support = true; diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn32/dcn32_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn32/dcn32_resource.c index 3751f7a94a059..614554415c499 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn32/dcn32_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn32/dcn32_resource.c @@ -55,6 +55,8 @@ #include "dcn30/dcn30_afmt.h" #include "dcn30/dcn30_dio_stream_encoder.h" #include "dcn32/dcn32_dio_stream_encoder.h" +#include "dcn30/dcn30_hpo_frl_stream_encoder.h" +#include "dcn30/dcn30_hpo_frl_link_encoder.h" #include "dcn31/dcn31_hpo_dp_stream_encoder.h" #include "dcn31/dcn31_hpo_dp_link_encoder.h" #include "dcn32/dcn32_hpo_dp_link_encoder.h" @@ -144,6 +146,10 @@ enum dcn32_clk_src_array_id { REG_STRUCT[id-1].reg_name = BASE(reg ## block ## id ## _ ## reg_name ## _BASE_IDX) + \ reg ## block ## id ## _ ## reg_name +#define SRI_ARR_DME(reg_name, block, id, offset)\ + REG_STRUCT[id - offset].reg_name = BASE(reg ## block ## id ## _ ## reg_name ## _BASE_IDX) + \ + reg ## block ## id ## _ ## reg_name + #define SRI_ARR_ALPHABET(reg_name, block, index, id)\ REG_STRUCT[index].reg_name = BASE(reg ## block ## id ## _ ## reg_name ## _BASE_IDX) + \ reg ## block ## id ## _ ## reg_name @@ -338,6 +344,36 @@ static const struct dcn10_link_enc_mask le_mask = { //DPCS_DCN31_MASK_SH_LIST(_MASK) }; +#define hpo_frl_stream_encoder_reg_list(id)\ + DCN3_0_HPO_FRL_STREAM_ENC_REG_LIST_RI(id) + +#define hpo_frl_stream_encoder_dme_reg_list(id)\ + DCN3_0_HPO_STREAM_ENC_DME_REG_LIST_RI(id, 6) + + +static struct dcn30_hpo_frl_stream_enc_registers hpo_frl_stream_enc_regs[2]; + +static const struct dcn30_hpo_frl_stream_encoder_shift hpo_se_shift = { + DCN3_0_HPO_STREAM_ENC_MASK_SH_LIST(__SHIFT) +}; + +static const struct dcn30_hpo_frl_stream_encoder_mask hpo_se_mask = { + DCN3_0_HPO_STREAM_ENC_MASK_SH_LIST(_MASK) +}; + +#define hpo_frl_link_encoder_reg_list(id)\ + DCN3_0_HPO_FRL_LINK_ENC_REG_LIST_RI(id) + +static struct dcn30_hpo_frl_link_encoder_registers hpo_frl_link_enc_regs[1]; + +static const struct dcn30_hpo_frl_link_encoder_shift hpo_le_shift = { + DCN3_0_HPO_FRL_LINK_ENC_MASK_SH_LIST(__SHIFT) +}; + +static const struct dcn30_hpo_frl_link_encoder_mask hpo_le_mask = { + DCN3_0_HPO_FRL_LINK_ENC_MASK_SH_LIST(_MASK) +}; + #define hpo_dp_stream_encoder_reg_init(id)\ DCN3_1_HPO_DP_STREAM_ENC_REG_LIST_RI(id) @@ -668,6 +704,7 @@ static const struct resource_caps res_cap_dcn32 = { .num_video_plane = 4, .num_audio = 5, .num_stream_encoder = 5, + .num_hpo_frl = 1, .num_hpo_dp_stream_encoder = 4, .num_hpo_dp_link_encoder = 2, .num_pll = 5, @@ -1277,6 +1314,79 @@ static struct stream_encoder *dcn32_stream_encoder_create( return &enc1->base; } +static struct hpo_frl_stream_encoder *dcn32_hpo_frl_stream_encoder_create(enum engine_id eng_id, + struct dc_context *ctx) +{ + struct dcn30_hpo_frl_stream_encoder *hpo_enc3; + struct afmt *afmt; + struct vpg *vpg; + int afmt_inst; + int vpg_inst; + +#undef REG_STRUCT +#define REG_STRUCT hpo_frl_stream_enc_regs + hpo_frl_stream_encoder_reg_list(0), + hpo_frl_stream_encoder_dme_reg_list(6); + + /* Mapping of VPG, AFMT, DME register blocks to HPO block instance */ + if (eng_id == ENGINE_ID_HPO_0) { + vpg_inst = 5; + afmt_inst = 5; + } else { + return NULL; + } + + /* allocate HPO stream encoder and create VPG, AFMT sub-blocks */ + hpo_enc3 = kzalloc(sizeof(struct dcn30_hpo_frl_stream_encoder), GFP_KERNEL); + vpg = dcn32_vpg_create(ctx, vpg_inst); + afmt = dcn32_afmt_create(ctx, afmt_inst); + + if (!hpo_enc3 || !vpg || !afmt) { + kfree(hpo_enc3); + kfree(vpg); + kfree(afmt); + return NULL; + } + + dcn30_hpo_frl_stream_encoder_construct(hpo_enc3, + ctx, + ctx->dc_bios, + eng_id, + vpg, + afmt, + &hpo_frl_stream_enc_regs[eng_id - ENGINE_ID_HPO_0], + &hpo_se_shift, + &hpo_se_mask); + + return &hpo_enc3->base; +} + +static struct hpo_frl_link_encoder *dcn32_hpo_frl_link_encoder_create(enum engine_id eng_id, + struct dc_context *ctx) +{ + struct dcn30_hpo_frl_link_encoder *hpo_enc3; + + ASSERT((eng_id == ENGINE_ID_HPO_0) || (eng_id == ENGINE_ID_HPO_1)); + +#undef REG_STRUCT +#define REG_STRUCT hpo_frl_link_enc_regs + hpo_frl_link_encoder_reg_list(0); + + /* allocate HPO link encoder */ + hpo_enc3 = kzalloc(sizeof(struct dcn30_hpo_frl_link_encoder), GFP_KERNEL); + if (!hpo_enc3) + return NULL; /* out of memory */ + + hpo_frl_link_encoder3_construct(hpo_enc3, + ctx, + eng_id - ENGINE_ID_HPO_0, + &hpo_frl_link_enc_regs[eng_id - ENGINE_ID_HPO_0], + &hpo_le_shift, + &hpo_le_mask); + + return &hpo_enc3->base; +} + static struct hpo_dp_stream_encoder *dcn32_hpo_dp_stream_encoder_create( enum engine_id eng_id, struct dc_context *ctx) @@ -1378,6 +1488,7 @@ static const struct resource_create_funcs res_create_funcs = { .read_dce_straps = read_dce_straps, .create_audio = dcn32_create_audio, .create_stream_encoder = dcn32_stream_encoder_create, + .create_hpo_frl_stream_encoder = dcn32_hpo_frl_stream_encoder_create, .create_hpo_dp_stream_encoder = dcn32_hpo_dp_stream_encoder_create, .create_hpo_dp_link_encoder = dcn32_hpo_dp_link_encoder_create, .create_hwseq = dcn32_hwseq_create, @@ -1402,6 +1513,23 @@ static void dcn32_resource_destruct(struct dcn32_resource_pool *pool) } } + for (i = 0; i < pool->base.hpo_frl_stream_enc_count; i++) { + if (pool->base.hpo_frl_stream_enc[i] != NULL) { + if (pool->base.hpo_frl_stream_enc[i]->vpg != NULL) { + kfree(DCN30_VPG_FROM_VPG(pool->base.hpo_frl_stream_enc[i]->vpg)); + pool->base.hpo_frl_stream_enc[i]->vpg = NULL; + } + + if (pool->base.hpo_frl_stream_enc[i]->afmt != NULL) { + kfree(DCN30_AFMT_FROM_AFMT(pool->base.hpo_frl_stream_enc[i]->afmt)); + pool->base.hpo_frl_stream_enc[i]->afmt = NULL; + } + + kfree(DCN30_HPO_FRL_STRENC_FROM_HPO_FRL_STRENC(pool->base.hpo_frl_stream_enc[i])); + pool->base.hpo_frl_stream_enc[i] = NULL; + } + } + for (i = 0; i < pool->base.hpo_dp_stream_enc_count; i++) { if (pool->base.hpo_dp_stream_enc[i] != NULL) { if (pool->base.hpo_dp_stream_enc[i]->vpg != NULL) { @@ -2134,6 +2262,7 @@ static struct resource_funcs dcn32_res_pool_funcs = { .destroy = dcn32_destroy_resource_pool, .link_enc_create = dcn32_link_encoder_create, .link_enc_create_minimal = NULL, + .hpo_frl_link_enc_create = dcn32_hpo_frl_link_encoder_create, .panel_cntl_create = dcn32_panel_cntl_create, .validate_bandwidth = dcn32_validate_bandwidth, .calculate_wm_and_dlg = dcn32_calculate_wm_and_dlg, @@ -2274,6 +2403,8 @@ static bool dcn32_resource_construct( dc->caps.force_dp_tps4_for_cp2520 = true; if (dc->config.forceHBR2CP2520) dc->caps.force_dp_tps4_for_cp2520 = false; + dc->caps.hdmi_hpo = true; + dc->config.skip_frl_pretraining = true; dc->caps.dp_hpo = true; dc->caps.dp_hdmi21_pcon_support = true; dc->caps.edp_dsc_support = true; diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn32/dcn32_resource.h b/drivers/gpu/drm/amd/display/dc/resource/dcn32/dcn32_resource.h index 91be493e0bb6d..68e7140f1505d 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn32/dcn32_resource.h +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn32/dcn32_resource.h @@ -323,20 +323,27 @@ unsigned int dcn32_get_max_hw_cursor_size(const struct dc *dc, SRI_ARR(DC_HPD_TOGGLE_FILT_CNTL, HPD, id)) /* Link encoder */ -#define LE_DCN3_REG_LIST_RI(id) \ - SRI_ARR(DIG_BE_CNTL, DIG, id), SRI_ARR(DIG_BE_EN_CNTL, DIG, id), \ - SRI_ARR(TMDS_CTL_BITS, DIG, id), \ - SRI_ARR(TMDS_DCBALANCER_CONTROL, DIG, id), SRI_ARR(DP_CONFIG, DP, id), \ - SRI_ARR(DP_DPHY_CNTL, DP, id), SRI_ARR(DP_DPHY_PRBS_CNTL, DP, id), \ - SRI_ARR(DP_DPHY_SCRAM_CNTL, DP, id), SRI_ARR(DP_DPHY_SYM0, DP, id), \ - SRI_ARR(DP_DPHY_SYM1, DP, id), SRI_ARR(DP_DPHY_SYM2, DP, id), \ - SRI_ARR(DP_DPHY_TRAINING_PATTERN_SEL, DP, id), \ - SRI_ARR(DP_LINK_CNTL, DP, id), SRI_ARR(DP_LINK_FRAMING_CNTL, DP, id), \ - SRI_ARR(DP_MSE_SAT0, DP, id), SRI_ARR(DP_MSE_SAT1, DP, id), \ - SRI_ARR(DP_MSE_SAT2, DP, id), SRI_ARR(DP_MSE_SAT_UPDATE, DP, id), \ - SRI_ARR(DP_SEC_CNTL, DP, id), SRI_ARR(DP_VID_STREAM_CNTL, DP, id), \ - SRI_ARR(DP_DPHY_FAST_TRAINING, DP, id), SRI_ARR(DP_SEC_CNTL1, DP, id), \ - SRI_ARR(DP_DPHY_BS_SR_SWAP_CNTL, DP, id), \ +#define LE_DCN3_REG_LIST_RI(id) \ + SRI_ARR(DIG_BE_CNTL, DIG, id), \ + SRI_ARR(DIG_BE_EN_CNTL, DIG, id), \ + SRI_ARR(TMDS_CTL_BITS, DIG, id), \ + SRI_ARR(TMDS_DCBALANCER_CONTROL, DIG, id), \ + SRI_ARR(DP_CONFIG, DP, id), SRI_ARR(DP_DPHY_CNTL, DP, id), \ + SRI_ARR(DP_DPHY_PRBS_CNTL, DP, id), \ + SRI_ARR(DP_DPHY_SCRAM_CNTL, DP, id), \ + SRI_ARR(DP_DPHY_SYM0, DP, id), SRI_ARR(DP_DPHY_SYM1, DP, id), \ + SRI_ARR(DP_DPHY_SYM2, DP, id), \ + SRI_ARR(DP_DPHY_TRAINING_PATTERN_SEL, DP, id), \ + SRI_ARR(DP_LINK_CNTL, DP, id), \ + SRI_ARR(DP_LINK_FRAMING_CNTL, DP, id), \ + SRI_ARR(DP_MSE_SAT0, DP, id), SRI_ARR(DP_MSE_SAT1, DP, id), \ + SRI_ARR(DP_MSE_SAT2, DP, id), \ + SRI_ARR(DP_MSE_SAT_UPDATE, DP, id), \ + SRI_ARR(DP_SEC_CNTL, DP, id), \ + SRI_ARR(DP_VID_STREAM_CNTL, DP, id), \ + SRI_ARR(DP_DPHY_FAST_TRAINING, DP, id), \ + SRI_ARR(DP_SEC_CNTL1, DP, id), \ + SRI_ARR(DP_DPHY_BS_SR_SWAP_CNTL, DP, id), \ SRI_ARR(DP_DPHY_HBR2_PATTERN_CONTROL, DP, id) #define LE_DCN31_REG_LIST_RI(id) \ @@ -1281,4 +1288,48 @@ unsigned int dcn32_get_max_hw_cursor_size(const struct dc *dc, I2C_HW_ENGINE_COMMON_REG_LIST_RI(id), SR_ARR_I2C(DIO_MEM_PWR_CTRL, id), \ SR_ARR_I2C(DIO_MEM_PWR_STATUS, id) +#define DCN3_0_HDMI_STREAM_ENC_REG_LIST_RI(id) \ + SR_ARR(HDMI_STREAM_ENC_CLOCK_CONTROL, id), \ + SR_ARR(HDMI_STREAM_ENC_INPUT_MUX_CONTROL, id), \ + SR_ARR(HDMI_STREAM_ENC_CLOCK_RAMP_ADJUSTER_FIFO_STATUS_CONTROL0, id), \ + SR_ARR(HDMI_STREAM_ENC_CLOCK_RAMP_ADJUSTER_FIFO_STATUS_CONTROL2, id) + +#define DCN3_0_HDMI_TB_ENC_REG_LIST_RI(id) \ + SR_ARR(HDMI_TB_ENC_CONTROL, id), SR_ARR(HDMI_TB_ENC_H_ACTIVE_BLANK, id), \ + SR_ARR(HDMI_TB_ENC_HC_ACTIVE_BLANK, id), SR_ARR(HDMI_TB_ENC_MODE, id), \ + SR_ARR(HDMI_TB_ENC_PACKET_CONTROL, id), \ + SR_ARR(HDMI_TB_ENC_DB_CONTROL, id), \ + SR_ARR(HDMI_TB_ENC_PIXEL_FORMAT, id), \ + SR_ARR(HDMI_TB_ENC_VBI_PACKET_CONTROL1, id), \ + SR_ARR(HDMI_TB_ENC_GC_CONTROL, id), \ + SR_ARR(HDMI_TB_ENC_GENERIC_PACKET_CONTROL0, id), \ + SR_ARR(HDMI_TB_ENC_GENERIC_PACKET_CONTROL1, id), \ + SR_ARR(HDMI_TB_ENC_GENERIC_PACKET0_1_LINE, id), \ + SR_ARR(HDMI_TB_ENC_GENERIC_PACKET2_3_LINE, id), \ + SR_ARR(HDMI_TB_ENC_GENERIC_PACKET4_5_LINE, id), \ + SR_ARR(HDMI_TB_ENC_GENERIC_PACKET6_7_LINE, id), \ + SR_ARR(HDMI_TB_ENC_GENERIC_PACKET8_9_LINE, id), \ + SR_ARR(HDMI_TB_ENC_GENERIC_PACKET10_11_LINE, id), \ + SR_ARR(HDMI_TB_ENC_GENERIC_PACKET12_13_LINE, id), \ + SR_ARR(HDMI_TB_ENC_GENERIC_PACKET14_LINE, id), \ + SR_ARR(HDMI_TB_ENC_ACR_PACKET_CONTROL, id), \ + SR_ARR(HDMI_TB_ENC_ACR_32_0, id), SR_ARR(HDMI_TB_ENC_ACR_32_1, id), \ + SR_ARR(HDMI_TB_ENC_ACR_44_0, id), SR_ARR(HDMI_TB_ENC_ACR_44_1, id), \ + SR_ARR(HDMI_TB_ENC_ACR_48_0, id), SR_ARR(HDMI_TB_ENC_ACR_48_1, id), \ + SR_ARR(HDMI_TB_ENC_CRC_CNTL, id), \ + SR_ARR(HDMI_TB_ENC_METADATA_PACKET_CONTROL, id) + +#define DCN3_0_HPO_STREAM_ENC_DME_REG_LIST_RI(id, offset) \ + SRI_ARR_DME(DME_CONTROL, DME, id, offset) + +#define DCN3_0_HPO_FRL_STREAM_ENC_REG_LIST_RI(id) \ + DCN3_0_HDMI_STREAM_ENC_REG_LIST_RI(id), DCN3_0_HDMI_TB_ENC_REG_LIST_RI(id) + +#define DCN3_0_HPO_FRL_LINK_ENC_REG_LIST_RI(id) \ + SR_ARR(HDMI_LINK_ENC_CLK_CTRL, id), \ + SR_ARR(HDMI_LINK_ENC_CONTROL, id), \ + SR_ARR(HDMI_FRL_ENC_CONFIG, id), \ + SR_ARR(HDMI_FRL_ENC_CONFIG2, id),\ + SR_ARR(HDMI_FRL_ENC_MEM_CTRL, id) + #endif /* _DCN32_RESOURCE_H_ */ diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn321/dcn321_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn321/dcn321_resource.c index c8c81870d50ed..6f4ed761e2d1b 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn321/dcn321_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn321/dcn321_resource.c @@ -57,6 +57,8 @@ #include "dcn30/dcn30_afmt.h" #include "dcn30/dcn30_dio_stream_encoder.h" #include "dcn32/dcn32_dio_stream_encoder.h" +#include "dcn30/dcn30_hpo_frl_stream_encoder.h" +#include "dcn30/dcn30_hpo_frl_link_encoder.h" #include "dcn31/dcn31_hpo_dp_stream_encoder.h" #include "dcn31/dcn31_hpo_dp_link_encoder.h" #include "dcn32/dcn32_hpo_dp_link_encoder.h" @@ -139,6 +141,10 @@ enum dcn321_clk_src_array_id { REG_STRUCT[id-1].reg_name = BASE(reg ## block ## id ## _ ## reg_name ## _BASE_IDX) + \ reg ## block ## id ## _ ## reg_name +#define SRI_ARR_DME(reg_name, block, id, offset)\ + REG_STRUCT[id - offset].reg_name = BASE(reg ## block ## id ## _ ## reg_name ## _BASE_IDX) + \ + reg ## block ## id ## _ ## reg_name + #define SRI_ARR_ALPHABET(reg_name, block, index, id)\ REG_STRUCT[index].reg_name = BASE(reg ## block ## id ## _ ## reg_name ## _BASE_IDX) + \ reg ## block ## id ## _ ## reg_name @@ -332,6 +338,35 @@ static const struct dcn10_link_enc_mask le_mask = { // DPCS_DCN31_MASK_SH_LIST(_MASK) }; +#define hpo_frl_stream_encoder_reg_list(id)\ + DCN3_0_HPO_FRL_STREAM_ENC_REG_LIST_RI(id) + +#define hpo_frl_stream_encoder_dme_reg_list(id)\ + DCN3_0_HPO_STREAM_ENC_DME_REG_LIST_RI(id, 6) + +static struct dcn30_hpo_frl_stream_enc_registers hpo_frl_stream_enc_regs[2]; + +static const struct dcn30_hpo_frl_stream_encoder_shift hpo_se_shift = { + DCN3_0_HPO_STREAM_ENC_MASK_SH_LIST(__SHIFT) +}; + +static const struct dcn30_hpo_frl_stream_encoder_mask hpo_se_mask = { + DCN3_0_HPO_STREAM_ENC_MASK_SH_LIST(_MASK) +}; + +#define hpo_frl_link_encoder_reg_list(id)\ + DCN3_0_HPO_FRL_LINK_ENC_REG_LIST_RI(id) + +static struct dcn30_hpo_frl_link_encoder_registers hpo_frl_link_enc_regs[1]; + +static const struct dcn30_hpo_frl_link_encoder_shift hpo_le_shift = { + DCN3_0_HPO_FRL_LINK_ENC_MASK_SH_LIST(__SHIFT) +}; + +static const struct dcn30_hpo_frl_link_encoder_mask hpo_le_mask = { + DCN3_0_HPO_FRL_LINK_ENC_MASK_SH_LIST(_MASK) +}; + #define hpo_dp_stream_encoder_reg_init(id)\ DCN3_1_HPO_DP_STREAM_ENC_REG_LIST_RI(id) @@ -659,6 +694,7 @@ static const struct resource_caps res_cap_dcn321 = { .num_video_plane = 4, .num_audio = 5, .num_stream_encoder = 5, + .num_hpo_frl = 1, .num_hpo_dp_stream_encoder = 4, .num_hpo_dp_link_encoder = 2, .num_pll = 5, @@ -1253,6 +1289,79 @@ static struct stream_encoder *dcn321_stream_encoder_create( return &enc1->base; } +static struct hpo_frl_stream_encoder *dcn321_hpo_frl_stream_encoder_create(enum engine_id eng_id, + struct dc_context *ctx) +{ + struct dcn30_hpo_frl_stream_encoder *hpo_enc3; + struct afmt *afmt; + struct vpg *vpg; + int afmt_inst; + int vpg_inst; + +#undef REG_STRUCT +#define REG_STRUCT hpo_frl_stream_enc_regs + hpo_frl_stream_encoder_reg_list(0), + hpo_frl_stream_encoder_dme_reg_list(6); + + /* Mapping of VPG, AFMT, DME register blocks to HPO block instance */ + if (eng_id == ENGINE_ID_HPO_0) { + vpg_inst = 5; + afmt_inst = 5; + } else { + return NULL; + } + + /* allocate HPO stream encoder and create VPG, AFMT sub-blocks */ + hpo_enc3 = kzalloc(sizeof(struct dcn30_hpo_frl_stream_encoder), GFP_KERNEL); + vpg = dcn321_vpg_create(ctx, vpg_inst); + afmt = dcn321_afmt_create(ctx, afmt_inst); + + if (!hpo_enc3 || !vpg || !afmt) { + kfree(hpo_enc3); + kfree(vpg); + kfree(afmt); + return NULL; + } + + dcn30_hpo_frl_stream_encoder_construct(hpo_enc3, + ctx, + ctx->dc_bios, + eng_id, + vpg, + afmt, + &hpo_frl_stream_enc_regs[eng_id - ENGINE_ID_HPO_0], + &hpo_se_shift, + &hpo_se_mask); + + return &hpo_enc3->base; +} + +static struct hpo_frl_link_encoder *dcn321_hpo_frl_link_encoder_create(enum engine_id eng_id, + struct dc_context *ctx) +{ + struct dcn30_hpo_frl_link_encoder *hpo_enc3; + + ASSERT((eng_id == ENGINE_ID_HPO_0) || (eng_id == ENGINE_ID_HPO_1)); + +#undef REG_STRUCT +#define REG_STRUCT hpo_frl_link_enc_regs + hpo_frl_link_encoder_reg_list(0); + + /* allocate HPO link encoder */ + hpo_enc3 = kzalloc(sizeof(struct dcn30_hpo_frl_link_encoder), GFP_KERNEL); + if (!hpo_enc3) + return NULL; /* out of memory */ + + hpo_frl_link_encoder3_construct(hpo_enc3, + ctx, + eng_id - ENGINE_ID_HPO_0, + &hpo_frl_link_enc_regs[eng_id - ENGINE_ID_HPO_0], + &hpo_le_shift, + &hpo_le_mask); + + return &hpo_enc3->base; +} + static struct hpo_dp_stream_encoder *dcn321_hpo_dp_stream_encoder_create( enum engine_id eng_id, struct dc_context *ctx) @@ -1354,6 +1463,7 @@ static const struct resource_create_funcs res_create_funcs = { .read_dce_straps = read_dce_straps, .create_audio = dcn321_create_audio, .create_stream_encoder = dcn321_stream_encoder_create, + .create_hpo_frl_stream_encoder = dcn321_hpo_frl_stream_encoder_create, .create_hpo_dp_stream_encoder = dcn321_hpo_dp_stream_encoder_create, .create_hpo_dp_link_encoder = dcn321_hpo_dp_link_encoder_create, .create_hwseq = dcn321_hwseq_create, @@ -1378,6 +1488,23 @@ static void dcn321_resource_destruct(struct dcn321_resource_pool *pool) } } + for (i = 0; i < pool->base.hpo_frl_stream_enc_count; i++) { + if (pool->base.hpo_frl_stream_enc[i] != NULL) { + if (pool->base.hpo_frl_stream_enc[i]->vpg != NULL) { + kfree(DCN30_VPG_FROM_VPG(pool->base.hpo_frl_stream_enc[i]->vpg)); + pool->base.hpo_frl_stream_enc[i]->vpg = NULL; + } + + if (pool->base.hpo_frl_stream_enc[i]->afmt != NULL) { + kfree(DCN30_AFMT_FROM_AFMT(pool->base.hpo_frl_stream_enc[i]->afmt)); + pool->base.hpo_frl_stream_enc[i]->afmt = NULL; + } + + kfree(DCN30_HPO_FRL_STRENC_FROM_HPO_FRL_STRENC(pool->base.hpo_frl_stream_enc[i])); + pool->base.hpo_frl_stream_enc[i] = NULL; + } + } + for (i = 0; i < pool->base.hpo_dp_stream_enc_count; i++) { if (pool->base.hpo_dp_stream_enc[i] != NULL) { if (pool->base.hpo_dp_stream_enc[i]->vpg != NULL) { @@ -1631,6 +1758,7 @@ static struct resource_funcs dcn321_res_pool_funcs = { .destroy = dcn321_destroy_resource_pool, .link_enc_create = dcn321_link_encoder_create, .link_enc_create_minimal = NULL, + .hpo_frl_link_enc_create = dcn321_hpo_frl_link_encoder_create, .panel_cntl_create = dcn32_panel_cntl_create, .validate_bandwidth = dcn32_validate_bandwidth, .calculate_wm_and_dlg = dcn32_calculate_wm_and_dlg, @@ -1768,6 +1896,8 @@ static bool dcn321_resource_construct( dc->caps.max_slave_rgb_planes = 2; dc->caps.post_blend_color_processing = true; dc->caps.force_dp_tps4_for_cp2520 = true; + dc->caps.hdmi_hpo = true; + dc->config.skip_frl_pretraining = true; dc->caps.dp_hpo = true; dc->caps.dp_hdmi21_pcon_support = true; dc->caps.edp_dsc_support = true; @@ -1810,6 +1940,8 @@ static bool dcn321_resource_construct( dc->caps.color.mpc.ogam_rom_caps.hlg = 0; dc->caps.color.mpc.ocsc = 1; dc->caps.color.mpc.preblend = true; + /* HACK: Force FRL support until BIOS is ready. */ + dc->config.force_hdmi21_frl_enc_enable = true; /* Use pipe context based otg sync logic */ dc->config.use_pipe_ctx_sync_logic = true; diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn35/dcn35_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn35/dcn35_resource.c index 2a1b0b152501c..7521a9edbfde2 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn35/dcn35_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn35/dcn35_resource.c @@ -58,6 +58,8 @@ #include "dcn30/dcn30_afmt.h" #include "dcn31/dcn31_dio_link_encoder.h" #include "dcn35/dcn35_dio_stream_encoder.h" +#include "dcn30/dcn30_hpo_frl_stream_encoder.h" +#include "dcn30/dcn30_hpo_frl_link_encoder.h" #include "dcn31/dcn31_hpo_dp_stream_encoder.h" #include "dcn31/dcn31_hpo_dp_link_encoder.h" #include "dcn32/dcn32_hpo_dp_link_encoder.h" @@ -154,6 +156,10 @@ enum dcn35_clk_src_array_id { REG_STRUCT[id-1].reg_name = BASE(reg ## block ## id ## _ ## reg_name ## _BASE_IDX) + \ reg ## block ## id ## _ ## reg_name +#define SRI_ARR_DME(reg_name, block, id, offset)\ + REG_STRUCT[id - offset].reg_name = BASE(reg ## block ## id ## _ ## reg_name ## _BASE_IDX) + \ + reg ## block ## id ## _ ## reg_name + #define SRI_ARR_ALPHABET(reg_name, block, index, id)\ REG_STRUCT[index].reg_name = BASE(reg ## block ## id ## _ ## reg_name ## _BASE_IDX) + \ reg ## block ## id ## _ ## reg_name @@ -353,6 +359,35 @@ static const struct dcn10_link_enc_mask le_mask = { //DPCS_DCN31_MASK_SH_LIST(_MASK) }; +#define hpo_frl_stream_encoder_dme_reg_list(id)\ + DCN3_0_HPO_STREAM_ENC_DME_REG_LIST_RI(id, 6) + +#define hpo_frl_stream_encoder_reg_list(id)\ + DCN3_0_HPO_FRL_STREAM_ENC_REG_LIST_RI(id) + +static struct dcn30_hpo_frl_stream_enc_registers hpo_frl_stream_enc_regs[2]; + +static const struct dcn30_hpo_frl_stream_encoder_shift hpo_se_shift = { + DCN3_0_HPO_STREAM_ENC_MASK_SH_LIST(__SHIFT) +}; + +static const struct dcn30_hpo_frl_stream_encoder_mask hpo_se_mask = { + DCN3_0_HPO_STREAM_ENC_MASK_SH_LIST(_MASK) +}; + +#define hpo_frl_link_encoder_reg_list(id)\ + DCN3_0_HPO_FRL_LINK_ENC_REG_LIST_RI(id) + +static struct dcn30_hpo_frl_link_encoder_registers hpo_frl_link_enc_regs[1]; + +static const struct dcn30_hpo_frl_link_encoder_shift hpo_le_shift = { + DCN3_0_HPO_FRL_LINK_ENC_MASK_SH_LIST(__SHIFT) +}; + +static const struct dcn30_hpo_frl_link_encoder_mask hpo_le_mask = { + DCN3_0_HPO_FRL_LINK_ENC_MASK_SH_LIST(_MASK) +}; + #define hpo_dp_stream_encoder_reg_init(id)\ DCN3_1_HPO_DP_STREAM_ENC_REG_LIST_RI(id) @@ -685,6 +720,7 @@ static const struct resource_caps res_cap_dcn35 = { .num_audio = 5, .num_stream_encoder = 5, .num_dig_link_enc = 5, + .num_hpo_frl = 1, .num_hpo_dp_stream_encoder = 4, .num_hpo_dp_link_encoder = 2, .num_pll = 4,/*1 c10 edp, 3xc20 combo PHY*/ @@ -1336,6 +1372,72 @@ static struct stream_encoder *dcn35_stream_encoder_create( return &enc1->base; } +static struct hpo_frl_stream_encoder *dcn31_hpo_frl_stream_encoder_create( + enum engine_id eng_id, + struct dc_context *ctx) +{ + struct dcn30_hpo_frl_stream_encoder *hpo_enc3; + struct vpg *vpg; + struct afmt *afmt; + int vpg_inst; + int afmt_inst; + +#undef REG_STRUCT +#define REG_STRUCT hpo_frl_stream_enc_regs + hpo_frl_stream_encoder_reg_list(0), + hpo_frl_stream_encoder_dme_reg_list(6); + + /* Mapping of VPG, AFMT, DME register blocks to HPO block instance */ + if (eng_id == ENGINE_ID_HPO_0) { + vpg_inst = 5; + afmt_inst = 5; + } else + return NULL; + + /* allocate HPO stream encoder and create VPG, AFMT sub-blocks */ + hpo_enc3 = kzalloc(sizeof(struct dcn30_hpo_frl_stream_encoder), GFP_KERNEL); + vpg = dcn31_vpg_create(ctx, vpg_inst); + afmt = dcn31_afmt_create(ctx, afmt_inst); + + if (!hpo_enc3 || !vpg || !afmt) { + kfree(hpo_enc3); + kfree(vpg); + kfree(afmt); + return NULL; + } + + dcn30_hpo_frl_stream_encoder_construct(hpo_enc3, ctx, ctx->dc_bios, + eng_id, vpg, afmt, + &hpo_frl_stream_enc_regs[eng_id-ENGINE_ID_HPO_0], + &hpo_se_shift, &hpo_se_mask); + + return &hpo_enc3->base; +} + +static struct hpo_frl_link_encoder *dcn31_hpo_frl_link_encoder_create( + enum engine_id eng_id, + struct dc_context *ctx) +{ + struct dcn30_hpo_frl_link_encoder *hpo_enc3; + + ASSERT((eng_id == ENGINE_ID_HPO_0) || (eng_id == ENGINE_ID_HPO_1)); + +#undef REG_STRUCT +#define REG_STRUCT hpo_frl_link_enc_regs + hpo_frl_link_encoder_reg_list(0); + + /* allocate HPO link encoder */ + hpo_enc3 = kzalloc(sizeof(struct dcn30_hpo_frl_link_encoder), GFP_KERNEL); + if (!hpo_enc3) + return NULL; /* out of memory */ + + hpo_frl_link_encoder3_construct(hpo_enc3, ctx, eng_id-ENGINE_ID_HPO_0, + &hpo_frl_link_enc_regs[eng_id-ENGINE_ID_HPO_0], + &hpo_le_shift, &hpo_le_mask); + + return &hpo_enc3->base; +} + static struct hpo_dp_stream_encoder *dcn31_hpo_dp_stream_encoder_create( enum engine_id eng_id, struct dc_context *ctx) @@ -1437,6 +1539,7 @@ static const struct resource_create_funcs res_create_funcs = { .read_dce_straps = read_dce_straps, .create_audio = dcn31_create_audio, .create_stream_encoder = dcn35_stream_encoder_create, + .create_hpo_frl_stream_encoder = dcn31_hpo_frl_stream_encoder_create, .create_hpo_dp_stream_encoder = dcn31_hpo_dp_stream_encoder_create, .create_hpo_dp_link_encoder = dcn31_hpo_dp_link_encoder_create, .create_hwseq = dcn35_hwseq_create, @@ -1461,6 +1564,21 @@ static void dcn35_resource_destruct(struct dcn35_resource_pool *pool) } } + for (i = 0; i < pool->base.hpo_frl_stream_enc_count; i++) { + if (pool->base.hpo_frl_stream_enc[i] != NULL) { + if (pool->base.hpo_frl_stream_enc[i]->vpg != NULL) { + kfree(DCN30_VPG_FROM_VPG(pool->base.hpo_frl_stream_enc[i]->vpg)); + pool->base.hpo_frl_stream_enc[i]->vpg = NULL; + } + if (pool->base.hpo_frl_stream_enc[i]->afmt != NULL) { + kfree(DCN30_AFMT_FROM_AFMT(pool->base.hpo_frl_stream_enc[i]->afmt)); + pool->base.hpo_frl_stream_enc[i]->afmt = NULL; + } + kfree(DCN30_HPO_FRL_STRENC_FROM_HPO_FRL_STRENC(pool->base.hpo_frl_stream_enc[i])); + pool->base.hpo_frl_stream_enc[i] = NULL; + } + } + for (i = 0; i < pool->base.hpo_dp_stream_enc_count; i++) { if (pool->base.hpo_dp_stream_enc[i] != NULL) { if (pool->base.hpo_dp_stream_enc[i]->vpg != NULL) { @@ -1823,6 +1941,7 @@ static struct resource_funcs dcn35_res_pool_funcs = { .link_enc_create_minimal = dcn31_link_enc_create_minimal, .link_encs_assign = link_enc_cfg_link_encs_assign, .link_enc_unassign = link_enc_cfg_link_enc_unassign, + .hpo_frl_link_enc_create = dcn31_hpo_frl_link_encoder_create, .panel_cntl_create = dcn31_panel_cntl_create, .validate_bandwidth = dcn35_validate_bandwidth, .calculate_wm_and_dlg = NULL, @@ -1909,6 +2028,8 @@ static bool dcn35_resource_construct( dc->caps.force_dp_tps4_for_cp2520 = true; if (dc->config.forceHBR2CP2520) dc->caps.force_dp_tps4_for_cp2520 = false; + dc->caps.hdmi_hpo = true; + dc->config.skip_frl_pretraining = true; dc->caps.dp_hpo = true; dc->caps.dp_hdmi21_pcon_support = true; diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn351/dcn351_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn351/dcn351_resource.c index bfd1016aa7803..1a97d7f1994dd 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn351/dcn351_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn351/dcn351_resource.c @@ -37,6 +37,8 @@ #include "dcn31/dcn31_dio_link_encoder.h" #include "dcn35/dcn35_dio_stream_encoder.h" +#include "dcn30/dcn30_hpo_frl_stream_encoder.h" +#include "dcn30/dcn30_hpo_frl_link_encoder.h" #include "dcn31/dcn31_hpo_dp_stream_encoder.h" #include "dcn31/dcn31_hpo_dp_link_encoder.h" #include "dcn32/dcn32_hpo_dp_link_encoder.h" @@ -134,6 +136,10 @@ enum dcn351_clk_src_array_id { REG_STRUCT[id-1].reg_name = BASE(reg ## block ## id ## _ ## reg_name ## _BASE_IDX) + \ reg ## block ## id ## _ ## reg_name +#define SRI_ARR_DME(reg_name, block, id, offset)\ + REG_STRUCT[id - offset].reg_name = BASE(reg ## block ## id ## _ ## reg_name ## _BASE_IDX) + \ + reg ## block ## id ## _ ## reg_name + #define SRI_ARR_ALPHABET(reg_name, block, index, id)\ REG_STRUCT[index].reg_name = BASE(reg ## block ## id ## _ ## reg_name ## _BASE_IDX) + \ reg ## block ## id ## _ ## reg_name @@ -333,6 +339,35 @@ static const struct dcn10_link_enc_mask le_mask = { //DPCS_DCN31_MASK_SH_LIST(_MASK) }; +#define hpo_frl_stream_encoder_dme_reg_list(id)\ + DCN3_0_HPO_STREAM_ENC_DME_REG_LIST_RI(id, 6) + +#define hpo_frl_stream_encoder_reg_list(id)\ + DCN3_0_HPO_FRL_STREAM_ENC_REG_LIST_RI(id) + +static struct dcn30_hpo_frl_stream_enc_registers hpo_frl_stream_enc_regs[2]; + +static const struct dcn30_hpo_frl_stream_encoder_shift hpo_se_shift = { + DCN3_0_HPO_STREAM_ENC_MASK_SH_LIST(__SHIFT) +}; + +static const struct dcn30_hpo_frl_stream_encoder_mask hpo_se_mask = { + DCN3_0_HPO_STREAM_ENC_MASK_SH_LIST(_MASK) +}; + +#define hpo_frl_link_encoder_reg_list(id)\ + DCN3_0_HPO_FRL_LINK_ENC_REG_LIST_RI(id) + +static struct dcn30_hpo_frl_link_encoder_registers hpo_frl_link_enc_regs[1]; + +static const struct dcn30_hpo_frl_link_encoder_shift hpo_le_shift = { + DCN3_0_HPO_FRL_LINK_ENC_MASK_SH_LIST(__SHIFT) +}; + +static const struct dcn30_hpo_frl_link_encoder_mask hpo_le_mask = { + DCN3_0_HPO_FRL_LINK_ENC_MASK_SH_LIST(_MASK) +}; + #define hpo_dp_stream_encoder_reg_init(id)\ DCN3_1_HPO_DP_STREAM_ENC_REG_LIST_RI(id) @@ -665,6 +700,7 @@ static const struct resource_caps res_cap_dcn351 = { .num_audio = 5, .num_stream_encoder = 5, .num_dig_link_enc = 5, + .num_hpo_frl = 1, .num_hpo_dp_stream_encoder = 4, .num_hpo_dp_link_encoder = 2, .num_pll = 4,/*1 c10 edp, 3xc20 combo PHY*/ @@ -1316,6 +1352,72 @@ static struct stream_encoder *dcn35_stream_encoder_create( return &enc1->base; } +static struct hpo_frl_stream_encoder *dcn31_hpo_frl_stream_encoder_create( + enum engine_id eng_id, + struct dc_context *ctx) +{ + struct dcn30_hpo_frl_stream_encoder *hpo_enc3; + struct vpg *vpg; + struct afmt *afmt; + int vpg_inst; + int afmt_inst; + +#undef REG_STRUCT +#define REG_STRUCT hpo_frl_stream_enc_regs + hpo_frl_stream_encoder_reg_list(0), + hpo_frl_stream_encoder_dme_reg_list(6); + + /* Mapping of VPG, AFMT, DME register blocks to HPO block instance */ + if (eng_id == ENGINE_ID_HPO_0) { + vpg_inst = 5; + afmt_inst = 5; + } else + return NULL; + + /* allocate HPO stream encoder and create VPG, AFMT sub-blocks */ + hpo_enc3 = kzalloc(sizeof(struct dcn30_hpo_frl_stream_encoder), GFP_KERNEL); + vpg = dcn31_vpg_create(ctx, vpg_inst); + afmt = dcn31_afmt_create(ctx, afmt_inst); + + if (!hpo_enc3 || !vpg || !afmt) { + kfree(hpo_enc3); + kfree(vpg); + kfree(afmt); + return NULL; + } + + dcn30_hpo_frl_stream_encoder_construct(hpo_enc3, ctx, ctx->dc_bios, + eng_id, vpg, afmt, + &hpo_frl_stream_enc_regs[eng_id-ENGINE_ID_HPO_0], + &hpo_se_shift, &hpo_se_mask); + + return &hpo_enc3->base; +} + +static struct hpo_frl_link_encoder *dcn31_hpo_frl_link_encoder_create( + enum engine_id eng_id, + struct dc_context *ctx) +{ + struct dcn30_hpo_frl_link_encoder *hpo_enc3; + + ASSERT((eng_id == ENGINE_ID_HPO_0) || (eng_id == ENGINE_ID_HPO_1)); + +#undef REG_STRUCT +#define REG_STRUCT hpo_frl_link_enc_regs + hpo_frl_link_encoder_reg_list(0); + + /* allocate HPO link encoder */ + hpo_enc3 = kzalloc(sizeof(struct dcn30_hpo_frl_link_encoder), GFP_KERNEL); + if (!hpo_enc3) + return NULL; /* out of memory */ + + hpo_frl_link_encoder3_construct(hpo_enc3, ctx, eng_id-ENGINE_ID_HPO_0, + &hpo_frl_link_enc_regs[eng_id-ENGINE_ID_HPO_0], + &hpo_le_shift, &hpo_le_mask); + + return &hpo_enc3->base; +} + static struct hpo_dp_stream_encoder *dcn31_hpo_dp_stream_encoder_create( enum engine_id eng_id, struct dc_context *ctx) @@ -1417,6 +1519,7 @@ static const struct resource_create_funcs res_create_funcs = { .read_dce_straps = read_dce_straps, .create_audio = dcn31_create_audio, .create_stream_encoder = dcn35_stream_encoder_create, + .create_hpo_frl_stream_encoder = dcn31_hpo_frl_stream_encoder_create, .create_hpo_dp_stream_encoder = dcn31_hpo_dp_stream_encoder_create, .create_hpo_dp_link_encoder = dcn31_hpo_dp_link_encoder_create, .create_hwseq = dcn351_hwseq_create, @@ -1441,6 +1544,21 @@ static void dcn351_resource_destruct(struct dcn351_resource_pool *pool) } } + for (i = 0; i < pool->base.hpo_frl_stream_enc_count; i++) { + if (pool->base.hpo_frl_stream_enc[i] != NULL) { + if (pool->base.hpo_frl_stream_enc[i]->vpg != NULL) { + kfree(DCN30_VPG_FROM_VPG(pool->base.hpo_frl_stream_enc[i]->vpg)); + pool->base.hpo_frl_stream_enc[i]->vpg = NULL; + } + if (pool->base.hpo_frl_stream_enc[i]->afmt != NULL) { + kfree(DCN30_AFMT_FROM_AFMT(pool->base.hpo_frl_stream_enc[i]->afmt)); + pool->base.hpo_frl_stream_enc[i]->afmt = NULL; + } + kfree(DCN30_HPO_FRL_STRENC_FROM_HPO_FRL_STRENC(pool->base.hpo_frl_stream_enc[i])); + pool->base.hpo_frl_stream_enc[i] = NULL; + } + } + for (i = 0; i < pool->base.hpo_dp_stream_enc_count; i++) { if (pool->base.hpo_dp_stream_enc[i] != NULL) { if (pool->base.hpo_dp_stream_enc[i]->vpg != NULL) { @@ -1796,6 +1914,7 @@ static struct resource_funcs dcn351_res_pool_funcs = { .link_enc_create_minimal = dcn31_link_enc_create_minimal, .link_encs_assign = link_enc_cfg_link_encs_assign, .link_enc_unassign = link_enc_cfg_link_enc_unassign, + .hpo_frl_link_enc_create = dcn31_hpo_frl_link_encoder_create, .panel_cntl_create = dcn31_panel_cntl_create, .validate_bandwidth = dcn351_validate_bandwidth, .calculate_wm_and_dlg = NULL, @@ -1882,6 +2001,8 @@ static bool dcn351_resource_construct( dc->caps.force_dp_tps4_for_cp2520 = true; if (dc->config.forceHBR2CP2520) dc->caps.force_dp_tps4_for_cp2520 = false; + dc->caps.hdmi_hpo = true; + dc->config.skip_frl_pretraining = true; dc->caps.dp_hpo = true; dc->caps.dp_hdmi21_pcon_support = true; diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn36/dcn36_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn36/dcn36_resource.c index 07a6675a73294..bf8f568b1b42e 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn36/dcn36_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn36/dcn36_resource.c @@ -37,6 +37,8 @@ #include "dcn30/dcn30_afmt.h" #include "dcn31/dcn31_dio_link_encoder.h" #include "dcn35/dcn35_dio_stream_encoder.h" +#include "dcn30/dcn30_hpo_frl_stream_encoder.h" +#include "dcn30/dcn30_hpo_frl_link_encoder.h" #include "dcn31/dcn31_hpo_dp_stream_encoder.h" #include "dcn31/dcn31_hpo_dp_link_encoder.h" #include "dcn32/dcn32_hpo_dp_link_encoder.h" @@ -139,6 +141,10 @@ enum dcn36_clk_src_array_id { REG_STRUCT[id-1].reg_name = BASE(reg ## block ## id ## _ ## reg_name ## _BASE_IDX) + \ reg ## block ## id ## _ ## reg_name +#define SRI_ARR_DME(reg_name, block, id, offset)\ + REG_STRUCT[id - offset].reg_name = BASE(reg ## block ## id ## _ ## reg_name ## _BASE_IDX) + \ + reg ## block ## id ## _ ## reg_name + #define SRI_ARR_ALPHABET(reg_name, block, index, id)\ REG_STRUCT[index].reg_name = BASE(reg ## block ## id ## _ ## reg_name ## _BASE_IDX) + \ reg ## block ## id ## _ ## reg_name @@ -338,6 +344,35 @@ static const struct dcn10_link_enc_mask le_mask = { //DPCS_DCN31_MASK_SH_LIST(_MASK) }; +#define hpo_frl_stream_encoder_dme_reg_list(id)\ + DCN3_0_HPO_STREAM_ENC_DME_REG_LIST_RI(id, 6) + +#define hpo_frl_stream_encoder_reg_list(id)\ + DCN3_0_HPO_FRL_STREAM_ENC_REG_LIST_RI(id) + +static struct dcn30_hpo_frl_stream_enc_registers hpo_frl_stream_enc_regs[2]; + +static const struct dcn30_hpo_frl_stream_encoder_shift hpo_se_shift = { + DCN3_0_HPO_STREAM_ENC_MASK_SH_LIST(__SHIFT) +}; + +static const struct dcn30_hpo_frl_stream_encoder_mask hpo_se_mask = { + DCN3_0_HPO_STREAM_ENC_MASK_SH_LIST(_MASK) +}; + +#define hpo_frl_link_encoder_reg_list(id)\ + DCN3_0_HPO_FRL_LINK_ENC_REG_LIST_RI(id) + +static struct dcn30_hpo_frl_link_encoder_registers hpo_frl_link_enc_regs[1]; + +static const struct dcn30_hpo_frl_link_encoder_shift hpo_le_shift = { + DCN3_0_HPO_FRL_LINK_ENC_MASK_SH_LIST(__SHIFT) +}; + +static const struct dcn30_hpo_frl_link_encoder_mask hpo_le_mask = { + DCN3_0_HPO_FRL_LINK_ENC_MASK_SH_LIST(_MASK) +}; + #define hpo_dp_stream_encoder_reg_init(id)\ DCN3_1_HPO_DP_STREAM_ENC_REG_LIST_RI(id) @@ -672,6 +707,7 @@ static const struct resource_caps res_cap_dcn36 = { .num_audio = 5, .num_stream_encoder = 5, .num_dig_link_enc = 5, + .num_hpo_frl = 1, .num_hpo_dp_stream_encoder = 4, .num_hpo_dp_link_encoder = 2, .num_pll = 4,/*1 c10 edp, 3xc20 combo PHY*/ @@ -1323,6 +1359,72 @@ static struct stream_encoder *dcn35_stream_encoder_create( return &enc1->base; } +static struct hpo_frl_stream_encoder *dcn31_hpo_frl_stream_encoder_create( + enum engine_id eng_id, + struct dc_context *ctx) +{ + struct dcn30_hpo_frl_stream_encoder *hpo_enc3; + struct vpg *vpg; + struct afmt *afmt; + int vpg_inst; + int afmt_inst; + +#undef REG_STRUCT +#define REG_STRUCT hpo_frl_stream_enc_regs + hpo_frl_stream_encoder_reg_list(0), + hpo_frl_stream_encoder_dme_reg_list(6); + + /* Mapping of VPG, AFMT, DME register blocks to HPO block instance */ + if (eng_id == ENGINE_ID_HPO_0) { + vpg_inst = 5; + afmt_inst = 5; + } else + return NULL; + + /* allocate HPO stream encoder and create VPG, AFMT sub-blocks */ + hpo_enc3 = kzalloc(sizeof(struct dcn30_hpo_frl_stream_encoder), GFP_KERNEL); + vpg = dcn31_vpg_create(ctx, vpg_inst); + afmt = dcn31_afmt_create(ctx, afmt_inst); + + if (!hpo_enc3 || !vpg || !afmt) { + kfree(hpo_enc3); + kfree(vpg); + kfree(afmt); + return NULL; + } + + dcn30_hpo_frl_stream_encoder_construct(hpo_enc3, ctx, ctx->dc_bios, + eng_id, vpg, afmt, + &hpo_frl_stream_enc_regs[eng_id-ENGINE_ID_HPO_0], + &hpo_se_shift, &hpo_se_mask); + + return &hpo_enc3->base; +} + +static struct hpo_frl_link_encoder *dcn31_hpo_frl_link_encoder_create( + enum engine_id eng_id, + struct dc_context *ctx) +{ + struct dcn30_hpo_frl_link_encoder *hpo_enc3; + + ASSERT((eng_id == ENGINE_ID_HPO_0) || (eng_id == ENGINE_ID_HPO_1)); + +#undef REG_STRUCT +#define REG_STRUCT hpo_frl_link_enc_regs + hpo_frl_link_encoder_reg_list(0); + + /* allocate HPO link encoder */ + hpo_enc3 = kzalloc(sizeof(struct dcn30_hpo_frl_link_encoder), GFP_KERNEL); + if (!hpo_enc3) + return NULL; /* out of memory */ + + hpo_frl_link_encoder3_construct(hpo_enc3, ctx, eng_id-ENGINE_ID_HPO_0, + &hpo_frl_link_enc_regs[eng_id-ENGINE_ID_HPO_0], + &hpo_le_shift, &hpo_le_mask); + + return &hpo_enc3->base; +} + static struct hpo_dp_stream_encoder *dcn31_hpo_dp_stream_encoder_create( enum engine_id eng_id, struct dc_context *ctx) @@ -1424,6 +1526,7 @@ static const struct resource_create_funcs res_create_funcs = { .read_dce_straps = read_dce_straps, .create_audio = dcn31_create_audio, .create_stream_encoder = dcn35_stream_encoder_create, + .create_hpo_frl_stream_encoder = dcn31_hpo_frl_stream_encoder_create, .create_hpo_dp_stream_encoder = dcn31_hpo_dp_stream_encoder_create, .create_hpo_dp_link_encoder = dcn31_hpo_dp_link_encoder_create, .create_hwseq = dcn36_hwseq_create, @@ -1448,6 +1551,21 @@ static void dcn36_resource_destruct(struct dcn36_resource_pool *pool) } } + for (i = 0; i < pool->base.hpo_frl_stream_enc_count; i++) { + if (pool->base.hpo_frl_stream_enc[i] != NULL) { + if (pool->base.hpo_frl_stream_enc[i]->vpg != NULL) { + kfree(DCN30_VPG_FROM_VPG(pool->base.hpo_frl_stream_enc[i]->vpg)); + pool->base.hpo_frl_stream_enc[i]->vpg = NULL; + } + if (pool->base.hpo_frl_stream_enc[i]->afmt != NULL) { + kfree(DCN30_AFMT_FROM_AFMT(pool->base.hpo_frl_stream_enc[i]->afmt)); + pool->base.hpo_frl_stream_enc[i]->afmt = NULL; + } + kfree(DCN30_HPO_FRL_STRENC_FROM_HPO_FRL_STRENC(pool->base.hpo_frl_stream_enc[i])); + pool->base.hpo_frl_stream_enc[i] = NULL; + } + } + for (i = 0; i < pool->base.hpo_dp_stream_enc_count; i++) { if (pool->base.hpo_dp_stream_enc[i] != NULL) { if (pool->base.hpo_dp_stream_enc[i]->vpg != NULL) { @@ -1797,6 +1915,7 @@ static struct resource_funcs dcn36_res_pool_funcs = { .link_enc_create_minimal = dcn31_link_enc_create_minimal, .link_encs_assign = link_enc_cfg_link_encs_assign, .link_enc_unassign = link_enc_cfg_link_enc_unassign, + .hpo_frl_link_enc_create = dcn31_hpo_frl_link_encoder_create, .panel_cntl_create = dcn31_panel_cntl_create, .validate_bandwidth = dcn35_validate_bandwidth, .calculate_wm_and_dlg = NULL, @@ -1879,6 +1998,8 @@ static bool dcn36_resource_construct( dc->caps.force_dp_tps4_for_cp2520 = true; if (dc->config.forceHBR2CP2520) dc->caps.force_dp_tps4_for_cp2520 = false; + dc->caps.hdmi_hpo = true; + dc->config.skip_frl_pretraining = true; dc->caps.dp_hpo = true; dc->caps.dp_hdmi21_pcon_support = true; diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn401/dcn401_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn401/dcn401_resource.c index cb93bfbe9e9e8..6e06167309400 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn401/dcn401_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn401/dcn401_resource.c @@ -35,6 +35,8 @@ #include "dcn30/dcn30_afmt.h" #include "dcn30/dcn30_dio_stream_encoder.h" #include "dcn401/dcn401_dio_stream_encoder.h" +#include "dcn401/dcn401_hpo_frl_stream_encoder.h" +#include "dcn30/dcn30_hpo_frl_link_encoder.h" #include "dcn31/dcn31_hpo_dp_stream_encoder.h" #include "dcn31/dcn31_hpo_dp_link_encoder.h" #include "dcn32/dcn32_hpo_dp_link_encoder.h" @@ -126,6 +128,10 @@ enum dcn401_clk_src_array_id { REG_STRUCT[id-1].reg_name = BASE(reg ## block ## id ## _ ## reg_name ## _BASE_IDX) + \ reg ## block ## id ## _ ## reg_name +#define SRI_ARR_DME(reg_name, block, id, offset)\ + REG_STRUCT[id - offset].reg_name = BASE(reg ## block ## id ## _ ## reg_name ## _BASE_IDX) + \ + reg ## block ## id ## _ ## reg_name + #define SRI_ARR_ALPHABET(reg_name, block, index, id)\ REG_STRUCT[index].reg_name = BASE(reg ## block ## id ## _ ## reg_name ## _BASE_IDX) + \ reg ## block ## id ## _ ## reg_name @@ -313,6 +319,35 @@ static const struct dcn10_link_enc_mask le_mask = { }; +#define hpo_frl_stream_encoder_reg_list(id)\ + DCN3_0_HPO_FRL_STREAM_ENC_REG_LIST_RI(id) + +#define hpo_frl_stream_encoder_dme_reg_list(id)\ + DCN3_0_HPO_STREAM_ENC_DME_REG_LIST_RI(id, 4) + +static struct dcn30_hpo_frl_stream_enc_registers hpo_frl_stream_enc_regs[2]; + +static const struct dcn401_hpo_frl_stream_encoder_shift hpo_se_shift = { + DCN401_HPO_STREAM_ENC_MASK_SH_LIST(__SHIFT) +}; + +static const struct dcn401_hpo_frl_stream_encoder_mask hpo_se_mask = { + DCN401_HPO_STREAM_ENC_MASK_SH_LIST(_MASK) +}; + +#define hpo_frl_link_encoder_reg_list(id)\ + DCN3_0_HPO_FRL_LINK_ENC_REG_LIST_RI(id) + +static struct dcn30_hpo_frl_link_encoder_registers hpo_frl_link_enc_regs[1]; + +static const struct dcn30_hpo_frl_link_encoder_shift hpo_le_shift = { + DCN3_0_HPO_FRL_LINK_ENC_MASK_SH_LIST(__SHIFT) +}; + +static const struct dcn30_hpo_frl_link_encoder_mask hpo_le_mask = { + DCN3_0_HPO_FRL_LINK_ENC_MASK_SH_LIST(_MASK) +}; + #define hpo_dp_stream_encoder_reg_init(id)\ DCN3_1_HPO_DP_STREAM_ENC_REG_LIST_RI(id) @@ -657,6 +692,7 @@ static const struct resource_caps res_cap_dcn4_01 = { .num_video_plane = 4, .num_audio = 4, .num_stream_encoder = 4, + .num_hpo_frl = 1, .num_hpo_dp_stream_encoder = 4, .num_hpo_dp_link_encoder = 4, .num_pll = 4, @@ -1246,6 +1282,71 @@ static struct stream_encoder *dcn401_stream_encoder_create( return &enc1->base; } +static struct hpo_frl_stream_encoder *dcn401_hpo_frl_stream_encoder_create( + enum engine_id eng_id, + struct dc_context *ctx) +{ + struct dcn401_hpo_frl_stream_encoder *hpo_enc401; + struct vpg *vpg; + struct afmt *afmt; + int vpg_inst; + int afmt_inst; + +#undef REG_STRUCT +#define REG_STRUCT hpo_frl_stream_enc_regs + hpo_frl_stream_encoder_reg_list(0), + hpo_frl_stream_encoder_dme_reg_list(4); + + /* Mapping of VPG, AFMT, DME register blocks to HPO block instance */ + if (eng_id == ENGINE_ID_HPO_0) { + vpg_inst = 4; + afmt_inst = 4; + } else + return NULL; + + /* allocate HPO stream encoder and create VPG, AFMT sub-blocks */ + hpo_enc401 = kzalloc(sizeof(struct dcn401_hpo_frl_stream_encoder), GFP_KERNEL); + vpg = dcn401_vpg_create(ctx, vpg_inst); + afmt = dcn401_afmt_create(ctx, afmt_inst); + + if (!hpo_enc401 || !vpg || !afmt) { + kfree(hpo_enc401); + kfree(vpg); + kfree(afmt); + return NULL; + } + + dcn401_hpo_frl_stream_encoder_construct(hpo_enc401, ctx, ctx->dc_bios, + eng_id, vpg, afmt, + &hpo_frl_stream_enc_regs[eng_id-ENGINE_ID_HPO_0], + &hpo_se_shift, &hpo_se_mask); + + return &hpo_enc401->base; +} + +static struct hpo_frl_link_encoder *dcn401_hpo_frl_link_encoder_create( + enum engine_id eng_id, + struct dc_context *ctx) +{ + struct dcn30_hpo_frl_link_encoder *hpo_link_enc; + ASSERT((eng_id == ENGINE_ID_HPO_0) || (eng_id == ENGINE_ID_HPO_1)); + +#undef REG_STRUCT +#define REG_STRUCT hpo_frl_link_enc_regs + hpo_frl_link_encoder_reg_list(0); + + /* allocate HPO link encoder */ + hpo_link_enc = kzalloc(sizeof(struct dcn30_hpo_frl_link_encoder), GFP_KERNEL); + if (!hpo_link_enc) + return NULL; /* out of memory */ + + hpo_frl_link_encoder3_construct(hpo_link_enc, ctx, eng_id-ENGINE_ID_HPO_0, + &hpo_frl_link_enc_regs[eng_id-ENGINE_ID_HPO_0], + &hpo_le_shift, &hpo_le_mask); + + return &hpo_link_enc->base; +} + static struct hpo_dp_stream_encoder *dcn401_hpo_dp_stream_encoder_create( enum engine_id eng_id, struct dc_context *ctx) @@ -1373,6 +1474,7 @@ static const struct resource_create_funcs res_create_funcs = { .read_dce_straps = read_dce_straps, .create_audio = dcn401_create_audio, .create_stream_encoder = dcn401_stream_encoder_create, + .create_hpo_frl_stream_encoder = dcn401_hpo_frl_stream_encoder_create, .create_hpo_dp_stream_encoder = dcn401_hpo_dp_stream_encoder_create, .create_hpo_dp_link_encoder = dcn401_hpo_dp_link_encoder_create, .create_hwseq = dcn401_hwseq_create, @@ -1403,6 +1505,21 @@ static void dcn401_resource_destruct(struct dcn401_resource_pool *pool) } } + for (i = 0; i < pool->base.hpo_frl_stream_enc_count; i++) { + if (pool->base.hpo_frl_stream_enc[i] != NULL) { + if (pool->base.hpo_frl_stream_enc[i]->vpg != NULL) { + kfree(DCN31_VPG_FROM_VPG(pool->base.hpo_frl_stream_enc[i]->vpg)); + pool->base.hpo_frl_stream_enc[i]->vpg = NULL; + } + if (pool->base.hpo_frl_stream_enc[i]->afmt != NULL) { + kfree(DCN30_AFMT_FROM_AFMT(pool->base.hpo_frl_stream_enc[i]->afmt)); + pool->base.hpo_frl_stream_enc[i]->afmt = NULL; + } + kfree(DCN401_HPO_FRL_STRENC_FROM_HPO_FRL_STRENC(pool->base.hpo_frl_stream_enc[i])); + pool->base.hpo_frl_stream_enc[i] = NULL; + } + } + for (i = 0; i < pool->base.hpo_dp_stream_enc_count; i++) { if (pool->base.hpo_dp_stream_enc[i] != NULL) { if (pool->base.hpo_dp_stream_enc[i]->vpg != NULL) { @@ -1829,6 +1946,7 @@ static struct resource_funcs dcn401_res_pool_funcs = { .destroy = dcn401_destroy_resource_pool, .link_enc_create = dcn401_link_encoder_create, .link_enc_create_minimal = NULL, + .hpo_frl_link_enc_create = dcn401_hpo_frl_link_encoder_create, .panel_cntl_create = dcn32_panel_cntl_create, .validate_bandwidth = dcn401_validate_bandwidth, .calculate_wm_and_dlg = NULL, @@ -1966,6 +2084,7 @@ static bool dcn401_resource_construct( dc->caps.max_slave_rgb_planes = 3; dc->caps.post_blend_color_processing = true; dc->caps.force_dp_tps4_for_cp2520 = true; + dc->caps.hdmi_hpo = true; dc->caps.dp_hpo = true; dc->caps.dp_hdmi21_pcon_support = true; dc->caps.edp_dsc_support = true; @@ -2011,6 +2130,8 @@ static bool dcn401_resource_construct( dc->caps.color.mpc.ogam_rom_caps.hlg = 0; dc->caps.color.mpc.ocsc = 1; dc->caps.color.mpc.preblend = true; + /* HACK: Force FRL support until BIOS is ready. */ + dc->config.force_hdmi21_frl_enc_enable = true; dc->config.use_spl = true; dc->config.prefer_easf = true; diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn42/dcn42_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn42/dcn42_resource.c index c0d37f00fed91..0f8df5f770d2f 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn42/dcn42_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn42/dcn42_resource.c @@ -40,6 +40,9 @@ #include "dcn31/dcn31_vpg.h" #include "dcn42/dcn42_dio_stream_encoder.h" #include "dcn42/dcn42_pg_cntl.h" +#include "dcn401/dcn401_hpo_frl_stream_encoder.h" +#include "dcn42/dcn42_hpo_frl_stream_encoder.h" +#include "dcn30/dcn30_hpo_frl_link_encoder.h" #include "dcn31/dcn31_hpo_dp_stream_encoder.h" #include "dcn31/dcn31_hpo_dp_link_encoder.h" #include "dcn32/dcn32_hpo_dp_link_encoder.h" @@ -140,6 +143,9 @@ enum dcn401_clk_src_array_id { REG_STRUCT[id - 1].reg_name = BASE(reg##block##id##_##reg_name##_BASE_IDX) + \ reg##block##id##_##reg_name +#define SRI_ARR_DME(reg_name, block, id, offset) \ + REG_STRUCT[id - offset].reg_name = BASE(reg##block##id##_##reg_name##_BASE_IDX) + \ + reg##block##id##_##reg_name #define SRI_ARR_ALPHABET(reg_name, block, index, id) \ REG_STRUCT[index].reg_name = BASE(reg##block##id##_##reg_name##_BASE_IDX) + \ @@ -297,6 +303,34 @@ static const struct dcn10_link_enc_shift le_shift = { static const struct dcn10_link_enc_mask le_mask = { LINK_ENCODER_MASK_SH_LIST_DCN42(_MASK)}; +#define hpo_frl_stream_encoder_reg_list(id) \ + DCN42_HPO_FRL_STREAM_ENC_REG_LIST_RI(id) + +#define hpo_frl_stream_encoder_dme_reg_list(id) \ + DCN3_0_HPO_STREAM_ENC_DME_REG_LIST_RI(id, 6) + +static struct dcn30_hpo_frl_stream_enc_registers hpo_frl_stream_enc_regs[2]; + +static const struct dcn401_hpo_frl_stream_encoder_shift hpo_se_shift = { + DCN401_HPO_STREAM_ENC_MASK_SH_LIST(__SHIFT), + DCN42_HDMI_STREAM_ENC_MASK_SH_LIST(__SHIFT) +}; +static const struct dcn401_hpo_frl_stream_encoder_mask hpo_se_mask = { + DCN401_HPO_STREAM_ENC_MASK_SH_LIST(_MASK), + DCN42_HDMI_STREAM_ENC_MASK_SH_LIST(_MASK) +}; + +#define hpo_frl_link_encoder_reg_list(id) \ + DCN3_0_HPO_FRL_LINK_ENC_REG_LIST_RI(id) + +static struct dcn30_hpo_frl_link_encoder_registers hpo_frl_link_enc_regs[1]; + +static const struct dcn30_hpo_frl_link_encoder_shift hpo_le_shift = { + DCN3_0_HPO_FRL_LINK_ENC_MASK_SH_LIST(__SHIFT)}; + +static const struct dcn30_hpo_frl_link_encoder_mask hpo_le_mask = { + DCN3_0_HPO_FRL_LINK_ENC_MASK_SH_LIST(_MASK)}; + #define hpo_dp_stream_encoder_reg_init(id) \ DCN42_HPO_DP_STREAM_ENC_REG_LIST_RI(id) @@ -658,6 +692,7 @@ static const struct resource_caps res_cap_dcn42 = { .num_stream_encoder = 5, .num_dig_link_enc = 5, .num_usb4_dpia = 6, + .num_hpo_frl = 1, .num_hpo_dp_stream_encoder = 4, .num_hpo_dp_link_encoder = 4, .num_pll = 5, @@ -1257,6 +1292,74 @@ static struct stream_encoder *dcn42_stream_encoder_create( return &enc1->base; } +static struct hpo_frl_stream_encoder *dcn42_hpo_frl_stream_encoder_create( + enum engine_id eng_id, + struct dc_context *ctx) +{ + struct dcn42_hpo_frl_stream_encoder *hpo_enc42; + struct vpg *vpg; + struct apg *apg; + + uint32_t vpg_inst; + uint32_t apg_inst; + + +#undef REG_STRUCT +#define REG_STRUCT hpo_frl_stream_enc_regs + hpo_frl_stream_encoder_reg_list(0), + hpo_frl_stream_encoder_dme_reg_list(6); + + /* Mapping of VPG, DME register blocks to HPO block instance */ + if (eng_id == ENGINE_ID_HPO_0) { + vpg_inst = 9; /*hw hard wired to inst 9, ref to dcn header file*/ + apg_inst = 9; + } else + return NULL; + + /* allocate HPO stream encoder and create VPG sub-block */ + hpo_enc42 = kzalloc(sizeof(struct dcn42_hpo_frl_stream_encoder), GFP_KERNEL); + vpg = dcn42_vpg_create(ctx, vpg_inst); + apg = dcn42_apg_create(ctx, apg_inst); + + if (!hpo_enc42 || !vpg || !apg) { + kfree(hpo_enc42); + kfree(vpg); + kfree(apg); + return NULL; + } + + dcn42_hpo_frl_stream_encoder_construct(hpo_enc42, ctx, ctx->dc_bios, + eng_id, vpg, apg, + &hpo_frl_stream_enc_regs[eng_id - ENGINE_ID_HPO_0], + &hpo_se_shift, &hpo_se_mask); + + return &hpo_enc42->base; +} + +static struct hpo_frl_link_encoder *dcn42_hpo_frl_link_encoder_create( + enum engine_id eng_id, + struct dc_context *ctx) +{ + struct dcn30_hpo_frl_link_encoder *hpo_link_enc; + + ASSERT((eng_id == ENGINE_ID_HPO_0) || (eng_id == ENGINE_ID_HPO_1)); + +#undef REG_STRUCT +#define REG_STRUCT hpo_frl_link_enc_regs + hpo_frl_link_encoder_reg_list(0); + + /* allocate HPO link encoder */ + hpo_link_enc = kzalloc(sizeof(struct dcn30_hpo_frl_link_encoder), GFP_KERNEL); + if (!hpo_link_enc) + return NULL; /* out of memory */ + + hpo_frl_link_encoder3_construct(hpo_link_enc, ctx, eng_id - ENGINE_ID_HPO_0, + &hpo_frl_link_enc_regs[eng_id - ENGINE_ID_HPO_0], + &hpo_le_shift, &hpo_le_mask); + + return &hpo_link_enc->base; +} + static struct hpo_dp_stream_encoder *dcn42_hpo_dp_stream_encoder_create( enum engine_id eng_id, struct dc_context *ctx) @@ -1362,6 +1465,7 @@ static const struct resource_create_funcs res_create_funcs = { .read_dce_straps = read_dce_straps, .create_audio = dcn42_create_audio, .create_stream_encoder = dcn42_stream_encoder_create, + .create_hpo_frl_stream_encoder = dcn42_hpo_frl_stream_encoder_create, .create_hpo_dp_stream_encoder = dcn42_hpo_dp_stream_encoder_create, .create_hpo_dp_link_encoder = dcn42_hpo_dp_link_encoder_create, .create_hwseq = dcn42_hwseq_create, @@ -1392,6 +1496,21 @@ static void dcn42_resource_destruct(struct dcn42_resource_pool *pool) } } + for (i = 0; i < pool->base.hpo_frl_stream_enc_count; i++) { + if (pool->base.hpo_frl_stream_enc[i] != NULL) { + if (pool->base.hpo_frl_stream_enc[i]->vpg != NULL) { + kfree(DCN31_VPG_FROM_VPG(pool->base.hpo_frl_stream_enc[i]->vpg)); + pool->base.hpo_frl_stream_enc[i]->vpg = NULL; + } + if (pool->base.hpo_frl_stream_enc[i]->apg != NULL) { + kfree(DCN31_APG_FROM_APG(pool->base.hpo_frl_stream_enc[i]->apg)); + pool->base.hpo_frl_stream_enc[i]->apg = NULL; + } + kfree(DCN401_HPO_FRL_STRENC_FROM_HPO_FRL_STRENC(pool->base.hpo_frl_stream_enc[i])); + pool->base.hpo_frl_stream_enc[i] = NULL; + } + } + for (i = 0; i < pool->base.hpo_dp_stream_enc_count; i++) { if (pool->base.hpo_dp_stream_enc[i] != NULL) { if (pool->base.hpo_dp_stream_enc[i]->vpg != NULL) { @@ -1785,6 +1904,7 @@ static struct resource_funcs dcn42_res_pool_funcs = { .link_enc_create_minimal = dcn42_link_enc_create_minimal, .link_encs_assign = link_enc_cfg_link_encs_assign, .link_enc_unassign = link_enc_cfg_link_enc_unassign, + .hpo_frl_link_enc_create = dcn42_hpo_frl_link_encoder_create, .panel_cntl_create = dcn32_panel_cntl_create, .validate_bandwidth = dcn42_validate_bandwidth, .calculate_wm_and_dlg = NULL, @@ -1925,6 +2045,7 @@ static bool dcn42_resource_construct( dc->caps.force_dp_tps4_for_cp2520 = true; if (dc->config.forceHBR2CP2520) dc->caps.force_dp_tps4_for_cp2520 = false; + dc->caps.hdmi_hpo = true; dc->caps.dp_hdmi21_pcon_support = true; dc->caps.dp_hpo = true; dc->caps.edp_dsc_support = true; diff --git a/drivers/gpu/drm/amd/display/include/audio_types.h b/drivers/gpu/drm/amd/display/include/audio_types.h index 6699ad4fa825e..2a0f1a9c30a57 100644 --- a/drivers/gpu/drm/amd/display/include/audio_types.h +++ b/drivers/gpu/drm/amd/display/include/audio_types.h @@ -56,6 +56,7 @@ struct audio_crtc_info { uint16_t refresh_rate; uint8_t pixel_repetition; bool interlaced; + uint32_t frl_character_clock_kHz; /* in KHz */ }; struct azalia_clock_info { uint32_t pixel_clock_in_10khz; diff --git a/drivers/gpu/drm/amd/display/include/bios_parser_types.h b/drivers/gpu/drm/amd/display/include/bios_parser_types.h index b5d97b394131b..aeeac1f0c2c35 100644 --- a/drivers/gpu/drm/amd/display/include/bios_parser_types.h +++ b/drivers/gpu/drm/amd/display/include/bios_parser_types.h @@ -329,7 +329,11 @@ struct bp_encoder_cap_info { uint32_t DP_UHBR13_5_EN:1; uint32_t DP_UHBR20_EN:1; uint32_t DP_IS_USB_C:1; - uint32_t RESERVED:27; + uint32_t IS_HDMI_FRL_CAPABLE:1; + uint32_t FRL_8G_EN:1; + uint32_t FRL_10G_EN:1; + uint32_t FRL_12G_EN:1; + uint32_t RESERVED:19; }; struct bp_soc_bb_info { @@ -346,7 +350,13 @@ struct bp_connector_speed_cap_info { uint32_t DP_UHBR13_5_EN:1; uint32_t DP_UHBR20_EN:1; uint32_t DP_IS_USB_C:1; - uint32_t RESERVED:28; + uint32_t FRL_8G_EN:1; + uint32_t FRL_10G_EN:1; + uint32_t FRL_12G_EN:1; + uint32_t FRL_16G_EN:1; + uint32_t FRL_20G_EN:1; + uint32_t FRL_24G_EN:1; + uint32_t RESERVED:19; }; #endif /*__DAL_BIOS_PARSER_TYPES_H__ */ diff --git a/drivers/gpu/drm/amd/display/include/logger_types.h b/drivers/gpu/drm/amd/display/include/logger_types.h index 177acb0574f1c..a11bfb2e7cdda 100644 --- a/drivers/gpu/drm/amd/display/include/logger_types.h +++ b/drivers/gpu/drm/amd/display/include/logger_types.h @@ -62,6 +62,8 @@ #define DC_LOG_ALL_TF_CHANNELS(...) pr_debug("[GAMMA]:"__VA_ARGS__) #define DC_LOG_DSC(...) drm_dbg_dp((DC_LOGGER)->dev, __VA_ARGS__) #define DC_LOG_SMU(...) pr_debug("[SMU_MSG]:"__VA_ARGS__) +#define DC_LOG_HDMI_FRL(...) drm_dbg((DC_LOGGER)->dev, __VA_ARGS__) +#define DC_LOG_HDMI_FRL_LTP(...) drm_dbg((DC_LOGGER)->dev, __VA_ARGS__) #define DC_LOG_DWB(...) drm_dbg((DC_LOGGER)->dev, __VA_ARGS__) #define DC_LOG_DP2(...) drm_dbg_dp((DC_LOGGER)->dev, __VA_ARGS__) #define DC_LOG_AUTO_DPM_TEST(...) pr_debug("[AutoDPMTest]: "__VA_ARGS__) diff --git a/drivers/gpu/drm/amd/display/include/signal_types.h b/drivers/gpu/drm/amd/display/include/signal_types.h index 3a2c2d2fb6291..9b134aa3e2754 100644 --- a/drivers/gpu/drm/amd/display/include/signal_types.h +++ b/drivers/gpu/drm/amd/display/include/signal_types.h @@ -30,6 +30,8 @@ #define TMDS_MIN_PIXEL_CLOCK 25000 /* Maximum pixel clock, in KHz. For TMDS signal is 165.00 MHz */ #define TMDS_MAX_PIXEL_CLOCK 165000 +/* Maximum pixel clock, in KHz. For HDMI2 TMDS signal is 600 MHz */ +#define HDMI2_TMDS_MAX_PIXEL_CLOCK 600000 enum signal_type { SIGNAL_TYPE_NONE = 0L, /* no signal */ @@ -41,6 +43,7 @@ enum signal_type { SIGNAL_TYPE_DISPLAY_PORT = (1 << 5), SIGNAL_TYPE_DISPLAY_PORT_MST = (1 << 6), SIGNAL_TYPE_EDP = (1 << 7), + SIGNAL_TYPE_HDMI_FRL = (1 << 8), SIGNAL_TYPE_VIRTUAL = (1 << 9), /* Virtual Display */ }; @@ -65,6 +68,8 @@ static inline const char *signal_type_to_string(const int type) return "Display Port: MST"; case SIGNAL_TYPE_EDP: return "Embedded Display Port"; + case SIGNAL_TYPE_HDMI_FRL: + return "HDMI: FRL"; case SIGNAL_TYPE_VIRTUAL: return "Virtual"; default: @@ -78,9 +83,14 @@ static inline bool dc_is_hdmi_tmds_signal(enum signal_type signal) return (signal == SIGNAL_TYPE_HDMI_TYPE_A); } +static inline bool dc_is_hdmi_frl_signal(enum signal_type signal) +{ + return ((signal == SIGNAL_TYPE_HDMI_FRL)); +} + static inline bool dc_is_hdmi_signal(enum signal_type signal) { - return (signal == SIGNAL_TYPE_HDMI_TYPE_A); + return (dc_is_hdmi_tmds_signal(signal) || dc_is_hdmi_frl_signal(signal)); } static inline bool dc_is_dp_sst_signal(enum signal_type signal) diff --git a/drivers/gpu/drm/amd/include/amd_shared.h b/drivers/gpu/drm/amd/include/amd_shared.h index ac2d3701e2bdc..3fd38323a88ba 100644 --- a/drivers/gpu/drm/amd/include/amd_shared.h +++ b/drivers/gpu/drm/amd/include/amd_shared.h @@ -286,6 +286,10 @@ enum DC_FEATURE_MASK { * @DC_REPLAY_MASK: (0x200) disabled by default for DCN < 3.1.4 */ DC_REPLAY_MASK = (1 << 9), + /** + * @DC_FRL_MASK: (0x400) disabled by default + */ + DC_FRL_MASK = (1 << 10), }; /**