From 55c057b0cebe9aa1018c92d3199921bba803631b Mon Sep 17 00:00:00 2001 From: Ivan Lipski Date: Wed, 13 May 2026 17:53:57 -0400 Subject: [PATCH 01/12] [FROM-UPSTREAM] drm/amd/display: Initialize dsc_caps to 0 [Why&How] If we don't do that we make DSC decisions based on random inputs, which might result in disallowing DSC when the monitor and HW support it. Reviewed-by: Harry Wentland Signed-off-by: Ivan Lipski Signed-off-by: Harry Wentland Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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..3dbbf4bc1dbd8 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c @@ -7345,7 +7345,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; From 60903e0a240e998d7b5e911b672759ab09800501 Mon Sep 17 00:00:00 2001 From: Harry Wentland Date: Fri, 24 Apr 2026 11:38:00 -0400 Subject: [PATCH 02/12] [FROM-UPSTREAM] drm/amd/display: Add HDMI FRL definitions to includes This patch adds all relevant includes in DC that are used by the HDMI FRL implementation in DC. Signed-off-by: Harry Wentland Reviewed-by: Fangzhi Zuo Tested-by: Dan Wheeler Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/display/dc/dc.h | 121 ++++++++++ .../gpu/drm/amd/display/dc/dc_hdmi_types.h | 162 ++++++++++++- drivers/gpu/drm/amd/display/dc/dc_hw_types.h | 8 + drivers/gpu/drm/amd/display/dc/dc_types.h | 24 ++ drivers/gpu/drm/amd/display/dc/dm_cp_psp.h | 1 + .../drm/amd/display/dc/dm_services_types.h | 1 + .../drm/amd/display/dc/dml/dml1_frl_cap_chk.h | 173 +++++++++++++ .../gpu/drm/amd/display/dc/inc/core_status.h | 2 + .../gpu/drm/amd/display/dc/inc/core_types.h | 12 + drivers/gpu/drm/amd/display/dc/inc/hw/dccg.h | 10 + .../gpu/drm/amd/display/dc/inc/hw/hw_shared.h | 1 + .../drm/amd/display/dc/inc/hw/link_encoder.h | 176 ++++++++++++++ .../amd/display/dc/inc/hw/stream_encoder.h | 227 ++++++++++++++++++ .../amd/display/dc/inc/hw/timing_generator.h | 6 + .../gpu/drm/amd/display/dc/inc/link_service.h | 15 ++ drivers/gpu/drm/amd/display/dc/inc/resource.h | 7 + .../gpu/drm/amd/display/include/audio_types.h | 1 + .../amd/display/include/bios_parser_types.h | 14 +- .../drm/amd/display/include/logger_types.h | 2 + .../drm/amd/display/include/signal_types.h | 12 +- 20 files changed, 966 insertions(+), 9 deletions(-) create mode 100644 drivers/gpu/drm/amd/display/dc/dml/dml1_frl_cap_chk.h diff --git a/drivers/gpu/drm/amd/display/dc/dc.h b/drivers/gpu/drm/amd/display/dc/dc.h index 37714d4371fba..62df8a696f34a 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,49 @@ 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 */ + 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 +377,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 +583,8 @@ struct dc_config { int smart_mux_version; bool ignore_dpref_ss; bool enable_mipi_converter_optimization; + 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 +1143,18 @@ 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_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 +1726,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 +1741,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 +1787,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 +2124,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 +2346,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 +2376,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 +2433,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 +2488,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 +2547,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 +3230,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_hdmi_types.h b/drivers/gpu/drm/amd/display/dc/dc_hdmi_types.h index fcd3ab4b00459..afd21eb6bbcd1 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,19 @@ 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; + } 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 +164,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 +190,94 @@ 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_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/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/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/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..1800b1929b16b 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,223 @@ 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 { + /** + * @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..cf124838f354b 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,11 @@ 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_vstartup_dsc_frl)(struct timing_generator *optc); 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/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) From b79b1d6d265a43a0d0aede808b4251254a832a3a Mon Sep 17 00:00:00 2001 From: Harry Wentland Date: Fri, 24 Apr 2026 11:48:09 -0400 Subject: [PATCH 03/12] [FROM-UPSTREAM] drm/amd/display: Add DML changes to support HDMI FRL This adds DML support for HDMI FRL. Signed-off-by: Harry Wentland Reviewed-by: Fangzhi Zuo Tested-by: Dan Wheeler Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/display/dc/dml/Makefile | 3 + .../drm/amd/display/dc/dml/dcn20/dcn20_fpu.c | 5 + .../drm/amd/display/dc/dml/dcn30/dcn30_fpu.c | 89 +++ .../drm/amd/display/dc/dml/dcn30/dcn30_fpu.h | 22 + .../dc/dml/dcn30/display_mode_vba_30.c | 277 +++++++- .../amd/display/dc/dml/dcn302/dcn302_fpu.c | 1 + .../amd/display/dc/dml/dcn303/dcn303_fpu.c | 1 + .../dc/dml/dcn31/display_mode_vba_31.c | 240 +++++++ .../dc/dml/dcn314/display_mode_vba_314.c | 238 +++++++ .../drm/amd/display/dc/dml/dcn32/dcn32_fpu.c | 10 + .../drm/amd/display/dc/dml/dcn32/dcn32_fpu.h | 2 + .../dc/dml/dcn32/display_mode_vba_32.c | 16 +- .../dc/dml/dcn32/display_mode_vba_util_32.c | 124 +++- .../amd/display/dc/dml/display_mode_enums.h | 1 + .../drm/amd/display/dc/dml/dml1_frl_cap_chk.c | 589 ++++++++++++++++++ .../drm/amd/display/dc/dml/dml1_frl_cap_chk.h | 9 - 16 files changed, 1612 insertions(+), 15 deletions(-) create mode 100644 drivers/gpu/drm/amd/display/dc/dml/dml1_frl_cap_chk.c 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..921152a1d22a0 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,89 @@ 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; + } + + 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; + } 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 -5; +} \ 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..b54d24cdfbe23 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,38 @@ 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; + 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 +3385,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 +3436,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 +4079,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 +4254,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 +4262,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 +6493,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..96fdb3f6fa37d 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,38 @@ 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; + 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 +3693,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 +3745,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 +4173,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 +4598,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 +4736,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 +4748,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 +4760,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 +7263,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..50c217413abfd 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,38 @@ 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; + 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 +3799,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 +3851,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 +4263,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 +4685,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 +4823,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 +4835,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 +4847,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 +7348,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..530f6fdc8df35 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,27 @@ 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; + 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 +1717,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 +1729,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 +1737,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 +1794,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..9dde4e56f2378 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dml/dml1_frl_cap_chk.c @@ -0,0 +1,589 @@ +// 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; +} + +enum frl_cap_chk_result dml1_frl_cap_chk(struct frl_cap_chk_params *params) +{ + struct frl_cap_chk_intermediates inter; + + 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 index debe4c1dc0f74..545f498ea396c 100644 --- a/drivers/gpu/drm/amd/display/dc/dml/dml1_frl_cap_chk.h +++ b/drivers/gpu/drm/amd/display/dc/dml/dml1_frl_cap_chk.h @@ -119,13 +119,6 @@ struct frl_cap_chk_params { bool compressed; /* set to true if DSC is enabled */ bool bypass_hc_target_calc; /* debug only */ - bool allow_all_bpp; /* dsc_all_bpp */ - - /* DSC parameters */ - int slices; - int slice_width; - double bpp_target; - bool is_ovt; int layout; int acat; /* not supported */ @@ -145,8 +138,6 @@ enum frl_cap_chk_result dml1_frl_cap_chk_common(struct frl_cap_chk_intermediates enum frl_cap_chk_result dml1_frl_cap_chk_uncompressed(struct frl_cap_chk_params *params, struct frl_cap_chk_intermediates *inter); -enum frl_cap_chk_result dml1_frl_cap_chk_compressed(struct frl_cap_chk_params *params, - struct frl_cap_chk_intermediates *inter); #endif void frl_modified_pix_clock_for_dsc_padding(const int hc_active_target, From 7c478e369e84d04a436a3256ea53a9b0789494c5 Mon Sep 17 00:00:00 2001 From: Harry Wentland Date: Fri, 8 May 2026 10:43:43 -0400 Subject: [PATCH 04/12] [FROM-UPSTREAM] drm/amd/display: add HDMI 2.1 FRL base support to DML 2.0 Add HDMI FRL bits to DML 2.0 Signed-off-by: Harry Wentland Reviewed-by: Fangzhi Zuo Tested-by: Dan Wheeler Signed-off-by: Alex Deucher --- .../gpu/drm/amd/display/dc/dml2_0/Makefile | 2 + .../amd/display/dc/dml2_0/display_mode_core.c | 104 ++++- .../amd/display/dc/dml2_0/display_mode_util.c | 3 + .../dml2_0/dml21/dml21_translation_helper.c | 4 + .../dml21/src/dml2_core/dml2_core_dcn4.c | 1 + .../src/dml2_core/dml2_core_dcn4_calcs.c | 29 +- .../src/dml2_core/dml2_core_shared_types.h | 3 + .../lib_frl_cap_check.c | 396 +++++++++++++++++ .../lib_frl_cap_check.h | 90 ++++ .../dc/dml2_0/dml2_translation_helper.c | 4 + .../drm/amd/display/dc/dml2_0/dml2_utils.c | 2 + .../amd/display/dc/dml2_0/dml_frl_cap_chk.c | 413 ++++++++++++++++++ .../amd/display/dc/dml2_0/dml_frl_cap_chk.h | 109 +++++ 13 files changed, 1153 insertions(+), 7 deletions(-) create mode 100644 drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_standalone_libraries/lib_frl_cap_check.c create mode 100644 drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_standalone_libraries/lib_frl_cap_check.h create mode 100644 drivers/gpu/drm/amd/display/dc/dml2_0/dml_frl_cap_chk.c create mode 100644 drivers/gpu/drm/amd/display/dc/dml2_0/dml_frl_cap_chk.h 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 From 90b24e548726fa2738fe03122685d1885d3275c0 Mon Sep 17 00:00:00 2001 From: Harry Wentland Date: Fri, 24 Apr 2026 12:17:35 -0400 Subject: [PATCH 05/12] [FROM-UPSTREAM] drm/amd/display: Add DCCG DIO, HPO, OPP, and OPTC support for FRL This adds support to HW block programming for the core blocks for HDMI FRL: - DIO - HPO - OPP - OPTC Signed-off-by: Harry Wentland Reviewed-by: Fangzhi Zuo Tested-by: Dan Wheeler Signed-off-by: Alex Deucher --- .../amd/display/dc/dccg/dcn30/dcn30_dccg.c | 40 + .../amd/display/dc/dccg/dcn30/dcn30_dccg.h | 8 + .../amd/display/dc/dccg/dcn31/dcn31_dccg.c | 88 ++ .../amd/display/dc/dccg/dcn31/dcn31_dccg.h | 13 + .../amd/display/dc/dccg/dcn314/dcn314_dccg.c | 25 + .../amd/display/dc/dccg/dcn32/dcn32_dccg.c | 68 ++ .../amd/display/dc/dccg/dcn35/dcn35_dccg.c | 321 ++++++ .../amd/display/dc/dccg/dcn401/dcn401_dccg.c | 73 ++ .../amd/display/dc/dccg/dcn401/dcn401_dccg.h | 4 + .../amd/display/dc/dccg/dcn42/dcn42_dccg.c | 33 + .../display/dc/dio/dcn10/dcn10_link_encoder.c | 3 +- .../display/dc/dio/dcn20/dcn20_link_encoder.h | 1 + .../dc/dio/dcn30/dcn30_dio_link_encoder.c | 250 +++++ .../dc/dio/dcn30/dcn30_dio_link_encoder.h | 17 + .../dc/dio/dcn301/dcn301_dio_link_encoder.c | 18 + .../dc/dio/dcn31/dcn31_dio_link_encoder.c | 114 +++ .../dc/dio/dcn31/dcn31_dio_link_encoder.h | 8 + .../dc/dio/dcn32/dcn32_dio_link_encoder.c | 214 ++++ .../dc/dio/dcn32/dcn32_dio_link_encoder.h | 16 + .../dc/dio/dcn321/dcn321_dio_link_encoder.c | 20 + .../dc/dio/dcn35/dcn35_dio_link_encoder.c | 21 + .../dc/dio/dcn401/dcn401_dio_link_encoder.c | 220 ++++ .../dc/dio/dcn401/dcn401_dio_link_encoder.h | 16 + .../dc/dio/dcn42/dcn42_dio_link_encoder.c | 21 + drivers/gpu/drm/amd/display/dc/hpo/Makefile | 10 + .../dc/hpo/dcn30/dcn30_hpo_frl_link_encoder.c | 286 ++++++ .../dc/hpo/dcn30/dcn30_hpo_frl_link_encoder.h | 146 +++ .../hpo/dcn30/dcn30_hpo_frl_stream_encoder.c | 938 ++++++++++++++++++ .../hpo/dcn30/dcn30_hpo_frl_stream_encoder.h | 436 ++++++++ .../dcn401/dcn401_hpo_frl_stream_encoder.c | 906 +++++++++++++++++ .../dcn401/dcn401_hpo_frl_stream_encoder.h | 335 +++++++ .../hpo/dcn42/dcn42_hpo_frl_stream_encoder.c | 206 ++++ .../hpo/dcn42/dcn42_hpo_frl_stream_encoder.h | 59 ++ .../drm/amd/display/dc/opp/dcn10/dcn10_opp.c | 1 + .../amd/display/dc/optc/dcn10/dcn10_optc.h | 5 +- .../amd/display/dc/optc/dcn30/dcn30_optc.h | 8 +- .../amd/display/dc/optc/dcn31/dcn31_optc.c | 2 + .../amd/display/dc/optc/dcn401/dcn401_optc.c | 2 + 38 files changed, 4948 insertions(+), 4 deletions(-) create mode 100644 drivers/gpu/drm/amd/display/dc/hpo/dcn30/dcn30_hpo_frl_link_encoder.c create mode 100644 drivers/gpu/drm/amd/display/dc/hpo/dcn30/dcn30_hpo_frl_link_encoder.h create mode 100644 drivers/gpu/drm/amd/display/dc/hpo/dcn30/dcn30_hpo_frl_stream_encoder.c create mode 100644 drivers/gpu/drm/amd/display/dc/hpo/dcn30/dcn30_hpo_frl_stream_encoder.h create mode 100644 drivers/gpu/drm/amd/display/dc/hpo/dcn401/dcn401_hpo_frl_stream_encoder.c create mode 100644 drivers/gpu/drm/amd/display/dc/hpo/dcn401/dcn401_hpo_frl_stream_encoder.h create mode 100644 drivers/gpu/drm/amd/display/dc/hpo/dcn42/dcn42_hpo_frl_stream_encoder.c create mode 100644 drivers/gpu/drm/amd/display/dc/hpo/dcn42/dcn42_hpo_frl_stream_encoder.h 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/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..cd7d2bb661e52 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/hpo/dcn30/dcn30_hpo_frl_stream_encoder.c @@ -0,0 +1,938 @@ +/* + * 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; + } + + /* 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_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; + } + 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; + /* 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, + .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..27f7ffd896299 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/hpo/dcn401/dcn401_hpo_frl_stream_encoder.c @@ -0,0 +1,906 @@ +/* + * 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, + .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..0ec386347f800 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/hpo/dcn42/dcn42_hpo_frl_stream_encoder.c @@ -0,0 +1,206 @@ +// 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, + .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/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.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); } From 613f21c35e000e52d26ee09754951672d9037c0a Mon Sep 17 00:00:00 2001 From: Harry Wentland Date: Fri, 24 Apr 2026 12:22:18 -0400 Subject: [PATCH 06/12] [FROM-UPSTREAM] drm/amd/display: Add FRL support to clk_mgr, dsc, hdcp This adds a few, relatively minor, changes for FRL to clk_mgr, DSC, and HDCP blocks. Signed-off-by: Harry Wentland Reviewed-by: Fangzhi Zuo Tested-by: Dan Wheeler Signed-off-by: Alex Deucher --- .../display/dc/clk_mgr/dcn31/dcn31_clk_mgr.c | 3 +++ .../dc/clk_mgr/dcn314/dcn314_clk_mgr.c | 3 +++ .../dc/clk_mgr/dcn315/dcn315_clk_mgr.c | 3 +++ .../dc/clk_mgr/dcn316/dcn316_clk_mgr.c | 3 +++ .../display/dc/clk_mgr/dcn32/dcn32_clk_mgr.c | 12 +++++++++++ .../display/dc/clk_mgr/dcn35/dcn35_clk_mgr.c | 14 +++++++++++++ .../dc/clk_mgr/dcn401/dcn401_clk_mgr.c | 20 ++++++++++++++++++- drivers/gpu/drm/amd/display/dc/dsc/dsc.h | 5 +++++ .../gpu/drm/amd/display/dc/hdcp/hdcp_msg.c | 1 + 9 files changed, 63 insertions(+), 1 deletion(-) 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/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: From b2a00a0fc92d8932d32a6e9ba2a66981896f6c38 Mon Sep 17 00:00:00 2001 From: Harry Wentland Date: Fri, 24 Apr 2026 12:27:02 -0400 Subject: [PATCH 07/12] [FROM-UPSTREAM] drm/amd/display: Tie FRL programming together in HWSS This patch adds HW Sequencer support for FRL programming, which ties the HW programming for the different blocks together for FRL. Signed-off-by: Harry Wentland Reviewed-by: Fangzhi Zuo Tested-by: Dan Wheeler Signed-off-by: Alex Deucher --- .../amd/display/dc/hwss/dce110/dce110_hwseq.c | 103 +++++- .../amd/display/dc/hwss/dcn10/dcn10_hwseq.c | 37 +++ .../amd/display/dc/hwss/dcn20/dcn20_hwseq.c | 19 +- .../amd/display/dc/hwss/dcn30/dcn30_hwseq.c | 49 ++- .../amd/display/dc/hwss/dcn30/dcn30_hwseq.h | 16 + .../amd/display/dc/hwss/dcn30/dcn30_init.c | 1 + .../amd/display/dc/hwss/dcn31/dcn31_hwseq.c | 13 +- .../amd/display/dc/hwss/dcn31/dcn31_init.c | 1 + .../amd/display/dc/hwss/dcn314/dcn314_hwseq.c | 3 +- .../amd/display/dc/hwss/dcn314/dcn314_init.c | 1 + .../amd/display/dc/hwss/dcn32/dcn32_hwseq.c | 13 +- .../amd/display/dc/hwss/dcn32/dcn32_init.c | 3 + .../amd/display/dc/hwss/dcn35/dcn35_hwseq.c | 54 ++- .../amd/display/dc/hwss/dcn35/dcn35_hwseq.h | 2 + .../amd/display/dc/hwss/dcn35/dcn35_init.c | 2 + .../amd/display/dc/hwss/dcn351/dcn351_init.c | 2 + .../amd/display/dc/hwss/dcn401/dcn401_hwseq.c | 13 + .../amd/display/dc/hwss/dcn401/dcn401_init.c | 1 + .../amd/display/dc/hwss/dcn42/dcn42_hwseq.c | 38 ++- .../amd/display/dc/hwss/dcn42/dcn42_init.c | 2 + .../drm/amd/display/dc/hwss/hw_sequencer.h | 312 ++++++++++++++++++ .../display/dc/hwss/hw_sequencer_private.h | 3 + 22 files changed, 661 insertions(+), 27 deletions(-) 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..2306354e90af6 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,22 @@ 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); + +void dcn30_hw_set_vstartup_dsc_frl(struct dc *dc, + struct pipe_ctx *pipe_ctx); + 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..35e5371b9bfe7 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,42 @@ 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_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 +2259,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, From f4d704a4cf320fada28a4240797632044118e4df Mon Sep 17 00:00:00 2001 From: Harry Wentland Date: Fri, 24 Apr 2026 14:01:32 -0400 Subject: [PATCH 08/12] [FROM-UPSTREAM] drm/amd/display: Add DC resource support for FRL Add support for FRL in DC resources. This is mostly the register macros, encoder creation, and HW capabilities. Signed-off-by: Harry Wentland Reviewed-by: Fangzhi Zuo Tested-by: Dan Wheeler Signed-off-by: Alex Deucher --- .../dc/resource/dce112/dce112_resource.c | 3 + .../dc/resource/dcn30/dcn30_resource.c | 128 ++++++++++++++++- .../dc/resource/dcn301/dcn301_resource.c | 1 + .../dc/resource/dcn302/dcn302_resource.c | 111 ++++++++++++++- .../dc/resource/dcn303/dcn303_resource.c | 111 ++++++++++++++- .../dc/resource/dcn31/dcn31_resource.c | 127 +++++++++++++++++ .../dc/resource/dcn314/dcn314_resource.c | 127 +++++++++++++++++ .../dc/resource/dcn315/dcn315_resource.c | 128 +++++++++++++++++ .../dc/resource/dcn316/dcn316_resource.c | 126 +++++++++++++++++ .../dc/resource/dcn32/dcn32_resource.c | 131 +++++++++++++++++ .../dc/resource/dcn32/dcn32_resource.h | 79 +++++++++-- .../dc/resource/dcn321/dcn321_resource.c | 132 ++++++++++++++++++ .../dc/resource/dcn35/dcn35_resource.c | 121 ++++++++++++++++ .../dc/resource/dcn351/dcn351_resource.c | 121 ++++++++++++++++ .../dc/resource/dcn36/dcn36_resource.c | 121 ++++++++++++++++ .../dc/resource/dcn401/dcn401_resource.c | 121 ++++++++++++++++ .../dc/resource/dcn42/dcn42_resource.c | 121 ++++++++++++++++ 17 files changed, 1792 insertions(+), 17 deletions(-) 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; From 04d04f0d3b7b24f9069e68fe03befd6543bcb287 Mon Sep 17 00:00:00 2001 From: Harry Wentland Date: Fri, 24 Apr 2026 14:14:58 -0400 Subject: [PATCH 09/12] [FROM-UPSTREAM] drm/amd/display: Add DC link support for FRL Start bringing in the protocol layer for FRL in DC link. This includes FRL training, timing validation, and other protocol bits. Signed-off-by: Harry Wentland Reviewed-by: Fangzhi Zuo Tested-by: Dan Wheeler Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/display/dc/link/Makefile | 2 + .../display/dc/link/hwss/link_hwss_hpo_frl.c | 113 ++ .../display/dc/link/hwss/link_hwss_hpo_frl.h | 34 + .../drm/amd/display/dc/link/link_detection.c | 111 ++ .../gpu/drm/amd/display/dc/link/link_dpms.c | 189 ++- .../gpu/drm/amd/display/dc/link/link_dpms.h | 1 + .../drm/amd/display/dc/link/link_factory.c | 44 + .../drm/amd/display/dc/link/link_validation.c | 298 +++++ .../drm/amd/display/dc/link/link_validation.h | 13 + .../amd/display/dc/link/protocols/link_ddc.c | 90 ++ .../amd/display/dc/link/protocols/link_ddc.h | 4 + .../display/dc/link/protocols/link_hdmi_frl.c | 1128 +++++++++++++++++ .../display/dc/link/protocols/link_hdmi_frl.h | 53 + .../amd/display/dc/link/protocols/link_hpd.c | 1 + 14 files changed, 2077 insertions(+), 4 deletions(-) create mode 100644 drivers/gpu/drm/amd/display/dc/link/hwss/link_hwss_hpo_frl.c create mode 100644 drivers/gpu/drm/amd/display/dc/link/hwss/link_hwss_hpo_frl.h create mode 100644 drivers/gpu/drm/amd/display/dc/link/protocols/link_hdmi_frl.c create mode 100644 drivers/gpu/drm/amd/display/dc/link/protocols/link_hdmi_frl.h 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..c38908c3ce53f 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 @@ -549,15 +554,22 @@ static void update_psp_stream_config(struct pipe_ctx *pipe_ctx, bool dpms_off) 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 +589,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 +601,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 +640,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); } @@ -1813,6 +1861,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 +1893,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 +2236,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 +2347,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 +2361,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); @@ -2295,6 +2465,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 +2481,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 +2497,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 +2595,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); @@ -2437,6 +2617,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..0cb287825fb9c --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/link/protocols/link_hdmi_frl.c @@ -0,0 +1,1128 @@ +/* + * 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; + } +} + +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.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->local_sink->edid_caps.panel_patch.hdmi_comp_auto) { + link->frl_flags.force_frl_max = 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); + + 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; + } + } +} + +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->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; +} + +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; From facfbbf63d920f2e696f4ed87eab79ad602b18e3 Mon Sep 17 00:00:00 2001 From: Fangzhi Zuo Date: Tue, 28 Apr 2026 16:53:29 -0400 Subject: [PATCH 10/12] [FROM-UPSTREAM] drm/amd/display: add HDMI 2.1 Compliance Support Add force yuv format from igt for compliance test. Signed-off-by: Fangzhi Zuo Reviewed-by: Harry Wentland Tested-by: Dan Wheeler Signed-off-by: Alex Deucher --- .../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 16 ++++++++--- .../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h | 1 + .../amd/display/amdgpu_dm/amdgpu_dm_debugfs.c | 28 +++++++++++++++++++ 3 files changed, 41 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c index 3dbbf4bc1dbd8..bc4b8a7b10398 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c @@ -6790,18 +6790,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; 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..fd7ee8a98d516 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 */ 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} From 13251eb1168c28214829bf26249db8e8f0fbd06f Mon Sep 17 00:00:00 2001 From: Harry Wentland Date: Thu, 23 Apr 2026 17:40:22 -0400 Subject: [PATCH 11/12] [FROM-UPSTREAM] drm/amd/display: add HDMI 2.1 DSC over FRL support Add all the bits to enable DSC over FRL. Signed-off-by: Harry Wentland Reviewed-by: Fangzhi Zuo Tested-by: Dan Wheeler Signed-off-by: Alex Deucher --- .../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 74 +- .../amd/display/amdgpu_dm/amdgpu_dm_helpers.c | 68 +- drivers/gpu/drm/amd/display/dc/core/dc.c | 417 +++++++++++ .../drm/amd/display/dc/core/dc_hw_sequencer.c | 268 +++++++ .../gpu/drm/amd/display/dc/core/dc_resource.c | 56 ++ .../gpu/drm/amd/display/dc/core/dc_stream.c | 34 + drivers/gpu/drm/amd/display/dc/dc.h | 7 + drivers/gpu/drm/amd/display/dc/dc_dsc.h | 8 + .../gpu/drm/amd/display/dc/dc_hdmi_types.h | 2 + .../drm/amd/display/dc/dml/dcn30/dcn30_fpu.c | 40 +- .../dc/dml/dcn30/display_mode_vba_30.c | 3 + .../dc/dml/dcn31/display_mode_vba_31.c | 3 + .../dc/dml/dcn314/display_mode_vba_314.c | 3 + .../dc/dml/dcn32/display_mode_vba_util_32.c | 3 + .../drm/amd/display/dc/dml/dml1_frl_cap_chk.c | 201 +++++ .../drm/amd/display/dc/dml/dml1_frl_cap_chk.h | 9 + drivers/gpu/drm/amd/display/dc/dsc/dc_dsc.c | 704 ++++++++++++++++++ .../hpo/dcn30/dcn30_hpo_frl_stream_encoder.c | 105 +++ .../dcn401/dcn401_hpo_frl_stream_encoder.c | 1 + .../hpo/dcn42/dcn42_hpo_frl_stream_encoder.c | 1 + .../amd/display/dc/hwss/dcn30/dcn30_hwseq.h | 3 - .../drm/amd/display/dc/hwss/hw_sequencer.h | 2 + .../amd/display/dc/inc/hw/stream_encoder.h | 4 + .../amd/display/dc/inc/hw/timing_generator.h | 1 - .../gpu/drm/amd/display/dc/link/link_dpms.c | 38 + .../display/dc/link/protocols/link_hdmi_frl.c | 59 ++ .../amd/display/dc/optc/dcn30/dcn30_optc.c | 8 +- 27 files changed, 2109 insertions(+), 13 deletions(-) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c index bc4b8a7b10398..495f3bd08a174 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c @@ -7179,12 +7179,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); } } @@ -7258,6 +7276,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 = @@ -7296,8 +7318,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 && @@ -7310,11 +7342,41 @@ static void apply_dsc_policy_for_stream(struct amdgpu_dm_connector *aconnector, dc_link_get_highest_encoding_format(aconnector->dc_link), &stream->timing.dsc_cfg)) { stream->timing.flags.DSC = 1; - drm_dbg_driver(drm_connector->dev, "%s: SST_DSC [%s] DSC is selected from DP-HDMI PCON\n", - __func__, drm_connector->name); + drm_dbg_driver(drm_connector->dev, "%s: SST_DSC [%s] DSC is selected from %s\n", + __func__, drm_connector->name, + (dsc_caps->is_frl == 1) ? "HDMI FRL RX" : "DP-HDMI PCON"); } } } + else if (aconnector->dc_link && sink->sink_signal == SIGNAL_TYPE_HDMI_FRL) { + struct dc_dsc_policy dsc_policy = {0}; + + frl_verified_link_cap = dc_link_get_frl_link_cap(stream->link); + if (frl_verified_link_cap->frl_link_rate != HDMI_FRL_LINK_RATE_DISABLE && + aconnector->dc_link->frl_flags.force_frl_dsc) { + dc_dsc_policy_set_enable_dsc_when_not_needed(true); + dc_dsc_get_policy_for_timing(&stream->timing, 0, &dsc_policy, dc_link_get_highest_encoding_format(stream->link)); + } + + timing_bw_in_kbps = dc_bandwidth_in_kbps_from_timing(&stream->timing, DC_LINK_ENCODING_HDMI_FRL); + link_bandwidth_kbps = dc_link_frl_bandwidth_kbps(stream->link, frl_verified_link_cap->frl_link_rate); + dsc_sink_bw_in_kbps = dc_link_bw_kbps_from_raw_frl_link_rate_data(dc, sink->edid_caps.frl_dsc_max_frl_rate); + + if ((timing_bw_in_kbps > link_bandwidth_kbps && dsc_sink_bw_in_kbps > 0) || + (dsc_policy.enable_dsc_when_not_needed || dsc_options.force_dsc_when_not_needed)) { + if (dc_dsc_compute_config(aconnector->dc_link->ctx->dc->res_pool->dscs[0], + dsc_caps, + &dsc_options, + dsc_sink_bw_in_kbps, + &stream->timing, + dc_link_get_highest_encoding_format(aconnector->dc_link), + &stream->timing.dsc_cfg)) { + stream->timing.flags.DSC = 1; + drm_dbg_driver(drm_connector->dev, "%s: HDMI_FRL_DSC [%s] DSC is selected from HDMI FRL RX\n", + __func__, drm_connector->name); + } + } + } /* Overwrite the stream flag if DSC is enabled through debugfs */ if (aconnector->dsc_settings.dsc_force_enable == DSC_CLK_FORCE_ENABLE) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c index 7e1e9ff3fe0c4..3beb322043913 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c @@ -178,8 +178,15 @@ enum dc_edid_status dm_helpers_parse_edid_caps( edid_caps->edid_hdmi = connector->display_info.is_hdmi; - if (edid_caps->edid_hdmi) + if (edid_caps->edid_hdmi) { populate_hdmi_info_from_connector(&connector->display_info.hdmi, edid_caps); + drm_dbg_driver(connector->dev, "%s: HDMI_FRL [%s] max_frl_rate %d\n", __func__, connector->name, edid_caps->max_frl_rate); + if (edid_caps->frl_dsc_support) + drm_dbg_driver(connector->dev, "%s: HDMI_FRL_DSC [%s] frl_dsc_10bpc %d, frl_dsc_12bpc %d, frl_dsc_all_bpp %d, frl_dsc_native_420 %d, frl_dsc_max_slices %d, frl_dsc_max_frl_rate %d, frl_dsc_total_chunk_kbytes %d\n", + __func__, connector->name, edid_caps->frl_dsc_10bpc, edid_caps->frl_dsc_12bpc, \ + edid_caps->frl_dsc_all_bpp, edid_caps->frl_dsc_native_420, edid_caps->frl_dsc_max_slices, \ + edid_caps->frl_dsc_max_frl_rate, edid_caps->frl_dsc_total_chunk_kbytes); + } apply_edid_quirks(dev, edid_buf, edid_caps); @@ -1071,9 +1078,68 @@ dm_helpers_read_vbios_hardcoded_edid(struct dc_link *link, struct amdgpu_dm_conn return edid; } +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(struct drm_hdmi_info *hdmi, struct dc_edid_caps *edid_caps) { edid_caps->scdc_present = hdmi->scdc.supported; + edid_caps->max_frl_rate = get_max_frl_rate(hdmi->max_lanes, hdmi->max_frl_rate_per_lane); + edid_caps->frl_dsc_support = hdmi->dsc_cap.v_1p2; + if (edid_caps->frl_dsc_support) { + if (hdmi->dsc_cap.bpc_supported == 10) + edid_caps->frl_dsc_10bpc = true; + else if (hdmi->dsc_cap.bpc_supported == 12) + edid_caps->frl_dsc_12bpc = true; + edid_caps->frl_dsc_all_bpp = hdmi->dsc_cap.all_bpp; + edid_caps->frl_dsc_native_420 = hdmi->dsc_cap.native_420; + edid_caps->frl_dsc_max_slices = get_dsc_max_slices(hdmi->dsc_cap.max_slices, hdmi->dsc_cap.clk_per_slice); + edid_caps->frl_dsc_max_frl_rate = get_max_frl_rate(hdmi->dsc_cap.max_lanes, hdmi->dsc_cap.max_frl_rate_per_lane); + edid_caps->frl_dsc_total_chunk_kbytes = hdmi->dsc_cap.total_chunk_kbytes; + } } enum dc_edid_status dm_helpers_read_local_edid( diff --git a/drivers/gpu/drm/amd/display/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 62df8a696f34a..7c191ec0f7115 100644 --- a/drivers/gpu/drm/amd/display/dc/dc.h +++ b/drivers/gpu/drm/amd/display/dc/dc.h @@ -121,6 +121,12 @@ struct frl_cap_chk_params_fixed31_32 { bool compressed; /* set to true if DSC is enabled */ bool bypass_hc_target_calc; /* debug only */ + bool allow_all_bpp; /* dsc_all_bpp */ + + /* DSC parameters */ + int slices; + int slice_width; + struct fixed31_32 bpp_target; int layout; int acat; /* not supported */ @@ -1151,6 +1157,7 @@ struct dc_debug_options { int select_ffe; int limit_ffe; bool force_frl_always; + bool force_frl_dsc; bool force_frl_max; bool apply_vsdb_rcc_wa; bool enable_hdmi_idcc; diff --git a/drivers/gpu/drm/amd/display/dc/dc_dsc.h b/drivers/gpu/drm/amd/display/dc/dc_dsc.h index 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 afd21eb6bbcd1..5923a5f112a99 100644 --- a/drivers/gpu/drm/amd/display/dc/dc_hdmi_types.h +++ b/drivers/gpu/drm/amd/display/dc/dc_hdmi_types.h @@ -138,6 +138,7 @@ union hdmi_scdc_status_flags_data { uint8_t LANE3_LOCKED:1; uint8_t RESERVED:1; uint8_t FLT_READY:1; + uint8_t DSC_DECODEFAIL:1; } fields; }; @@ -270,6 +271,7 @@ struct dc_hdmi_frl_flags { int select_ffe; int limit_ffe; bool force_frl_always; + bool force_frl_dsc; bool force_frl_max; bool apply_vsdb_rcc_wa; }; diff --git a/drivers/gpu/drm/amd/display/dc/dml/dcn30/dcn30_fpu.c b/drivers/gpu/drm/amd/display/dc/dml/dcn30/dcn30_fpu.c index 921152a1d22a0..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 @@ -780,6 +780,38 @@ void hpo_fpu_enc3_validate_hdmi_frl_output_link(struct hpo_frl_stream_encoder *e break; } + if (timing->flags.DSC && + (unsigned int)frl_link_settings->frl_link_rate > dsc_max_rate) { + if (dsc_max_rate < HDMI_FRL_LINK_RATE_6GBPS_4LANE) { + frl_params->lanes = 3; + } else { + frl_params->lanes = 4; + } + + switch (dsc_max_rate) { + case HDMI_FRL_LINK_RATE_3GBPS: + frl_params->r_bit_nominal = 3.0e9; + break; + case HDMI_FRL_LINK_RATE_6GBPS: + case HDMI_FRL_LINK_RATE_6GBPS_4LANE: + frl_params->r_bit_nominal = 6.0e9; + break; + case HDMI_FRL_LINK_RATE_8GBPS: + frl_params->r_bit_nominal = 8.0e9; + break; + case HDMI_FRL_LINK_RATE_10GBPS: + default: + frl_params->r_bit_nominal = 10.0e9; + break; + case HDMI_FRL_LINK_RATE_12GBPS: + frl_params->r_bit_nominal = 12.0e9; + break; + } + } + + if (timing->flags.DSC && timing->rid > 0) + frl_params->is_ovt = true; + frl_params->f_pixel_clock_nominal = (double)timing->pix_clk_100hz * 100; frl_params->h_active = timing->h_addressable + timing->h_border_left + timing->h_border_right; frl_params->h_blank = timing->h_total - frl_params->h_active; @@ -795,6 +827,12 @@ void hpo_fpu_enc3_validate_hdmi_frl_output_timing( if (timing->flags.DSC) { frl_params->compressed = true; + frl_params->slices = timing->dsc_cfg.num_slices_h; + frl_params->slice_width = (frl_params->h_active + frl_params->slices - 1) / frl_params->slices; + // If YCBCR420 slice width must be even + if (timing->pixel_encoding == PIXEL_ENCODING_YCBCR420 && frl_params->slice_width % 2 != 0) + frl_params->slice_width++; + frl_params->bpp_target = timing->dsc_cfg.bits_per_pixel / 16.0; } else { frl_params->compressed = false; } @@ -826,5 +864,5 @@ enum frl_cap_chk_result frl_fpu_cap_chk_compressed(struct hpo_frl_stream_encoder struct frl_cap_chk_intermediates *inter) { (void)enc; - return -5; + return dml1_frl_cap_chk_compressed(params, inter); } \ No newline at end of file diff --git a/drivers/gpu/drm/amd/display/dc/dml/dcn30/display_mode_vba_30.c b/drivers/gpu/drm/amd/display/dc/dml/dcn30/display_mode_vba_30.c index b54d24cdfbe23..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 @@ -3358,6 +3358,9 @@ static double TruncToValidBPP( hdmifrlparams.h_active = HActive; hdmifrlparams.h_blank = HTotal - HActive; hdmifrlparams.compressed = DSCEnable; + hdmifrlparams.slices = DSCSlices; + hdmifrlparams.slice_width = (int)dml_ceil((double) HActive / DSCSlices, 1.0); + hdmifrlparams.bpp_target = DesiredBPP; if (Format == dm_420) { NonDSCBPP0 = 12; diff --git a/drivers/gpu/drm/amd/display/dc/dml/dcn31/display_mode_vba_31.c b/drivers/gpu/drm/amd/display/dc/dml/dcn31/display_mode_vba_31.c index 96fdb3f6fa37d..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 @@ -3665,6 +3665,9 @@ static double TruncToValidBPP( hdmifrlparams.h_active = HActive; hdmifrlparams.h_blank = HTotal - HActive; hdmifrlparams.compressed = DSCEnable; + hdmifrlparams.slices = DSCSlices; + hdmifrlparams.slice_width = (int)dml_ceil((double) HActive / DSCSlices, 1.0); + hdmifrlparams.bpp_target = DesiredBPP; if (Format == dm_420) { NonDSCBPP0 = 12; diff --git a/drivers/gpu/drm/amd/display/dc/dml/dcn314/display_mode_vba_314.c b/drivers/gpu/drm/amd/display/dc/dml/dcn314/display_mode_vba_314.c index 50c217413abfd..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 @@ -3771,6 +3771,9 @@ static double TruncToValidBPP( hdmifrlparams.h_active = HActive; hdmifrlparams.h_blank = HTotal - HActive; hdmifrlparams.compressed = DSCEnable; + hdmifrlparams.slices = DSCSlices; + hdmifrlparams.slice_width = (int)dml_ceil((double) HActive / DSCSlices, 1.0); + hdmifrlparams.bpp_target = DesiredBPP; if (Format == dm_420) { NonDSCBPP0 = 12; diff --git a/drivers/gpu/drm/amd/display/dc/dml/dcn32/display_mode_vba_util_32.c b/drivers/gpu/drm/amd/display/dc/dml/dcn32/display_mode_vba_util_32.c index 530f6fdc8df35..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 @@ -1702,6 +1702,9 @@ double dml32_TruncToValidBPP( hdmifrlparams.h_blank = HTotal - HActive; hdmifrlparams.bpc = (int)(DesiredBPP / 3); hdmifrlparams.compressed = DSCEnable; + hdmifrlparams.slices = DSCSlices; + hdmifrlparams.slice_width = (int)dml_ceil((double) HActive / DSCSlices, 1.0); + hdmifrlparams.bpp_target = DesiredBPP; if (Format == dm_420) { NonDSCBPP0 = 12; diff --git a/drivers/gpu/drm/amd/display/dc/dml/dml1_frl_cap_chk.c b/drivers/gpu/drm/amd/display/dc/dml/dml1_frl_cap_chk.c index 9dde4e56f2378..260f58a97fc7a 100644 --- a/drivers/gpu/drm/amd/display/dc/dml/dml1_frl_cap_chk.c +++ b/drivers/gpu/drm/amd/display/dc/dml/dml1_frl_cap_chk.c @@ -430,10 +430,211 @@ enum frl_cap_chk_result dml1_frl_cap_chk_uncompressed(struct frl_cap_chk_params 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); } diff --git a/drivers/gpu/drm/amd/display/dc/dml/dml1_frl_cap_chk.h b/drivers/gpu/drm/amd/display/dc/dml/dml1_frl_cap_chk.h index 545f498ea396c..debe4c1dc0f74 100644 --- a/drivers/gpu/drm/amd/display/dc/dml/dml1_frl_cap_chk.h +++ b/drivers/gpu/drm/amd/display/dc/dml/dml1_frl_cap_chk.h @@ -119,6 +119,13 @@ struct frl_cap_chk_params { bool compressed; /* set to true if DSC is enabled */ bool bypass_hc_target_calc; /* debug only */ + bool allow_all_bpp; /* dsc_all_bpp */ + + /* DSC parameters */ + int slices; + int slice_width; + double bpp_target; + bool is_ovt; int layout; int acat; /* not supported */ @@ -138,6 +145,8 @@ enum frl_cap_chk_result dml1_frl_cap_chk_common(struct frl_cap_chk_intermediates enum frl_cap_chk_result dml1_frl_cap_chk_uncompressed(struct frl_cap_chk_params *params, struct frl_cap_chk_intermediates *inter); +enum frl_cap_chk_result dml1_frl_cap_chk_compressed(struct frl_cap_chk_params *params, + struct frl_cap_chk_intermediates *inter); #endif void frl_modified_pix_clock_for_dsc_padding(const int hc_active_target, diff --git a/drivers/gpu/drm/amd/display/dc/dsc/dc_dsc.c b/drivers/gpu/drm/amd/display/dc/dsc/dc_dsc.c index 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/hpo/dcn30/dcn30_hpo_frl_stream_encoder.c b/drivers/gpu/drm/amd/display/dc/hpo/dcn30/dcn30_hpo_frl_stream_encoder.c index cd7d2bb661e52..cff5c95a771c4 100644 --- a/drivers/gpu/drm/amd/display/dc/hpo/dcn30/dcn30_hpo_frl_stream_encoder.c +++ b/drivers/gpu/drm/amd/display/dc/hpo/dcn30/dcn30_hpo_frl_stream_encoder.c @@ -216,6 +216,16 @@ void hpo_enc3_set_hdmi_stream_attribute(struct hpo_frl_stream_encoder *enc, break; } + /* When compression active, CD/PP/Phase field shall be zero in GCP */ + if (crtc_timing->flags.DSC) { + REG_UPDATE_2(HDMI_TB_ENC_PIXEL_FORMAT, + HDMI_DEEP_COLOR_DEPTH, 0, + HDMI_DEEP_COLOR_ENABLE, 0); + } else { + REG_UPDATE(HDMI_TB_ENC_PIXEL_FORMAT, + HDMI_DSC_MODE, 0); + } + /* Configure ODM combine mode */ switch (odm_combine_num_segments) { case 1: @@ -457,6 +467,94 @@ void hpo_enc3_update_hdmi_info_packet(struct dcn30_hpo_frl_stream_encoder *enc3, } } +void hpo_enc3_hdmi_set_dsc_config( + struct hpo_frl_stream_encoder *enc, + struct dc_crtc_timing *timing, + uint8_t *dsc_packed_pps) +{ + struct dcn30_hpo_frl_stream_encoder *enc3 = DCN30_HPO_FRL_STRENC_FROM_HPO_FRL_STRENC(enc); + enum optc_dsc_mode dsc_mode = OPTC_DSC_DISABLED; + uint8_t i; + + if (dsc_packed_pps) { + if (timing->pixel_encoding == PIXEL_ENCODING_YCBCR420 || + (timing->pixel_encoding == PIXEL_ENCODING_YCBCR422 + && !timing->dsc_cfg.ycbcr422_simple)) + dsc_mode = OPTC_DSC_ENABLED_NATIVE_SUBSAMPLED; + else + dsc_mode = OPTC_DSC_ENABLED_444; + } + + REG_UPDATE(HDMI_STREAM_ENC_CLOCK_RAMP_ADJUSTER_FIFO_STATUS_CONTROL0, + FIFO_DSC_MODE, dsc_mode); + + REG_UPDATE(HDMI_TB_ENC_PIXEL_FORMAT, + HDMI_DSC_MODE, dsc_mode); + + /* 5 packets for hdmi 2.1, use generic packets 5-10 to transmit*/ + /* TODO: do we change new bit to 0 after first transmission? Do we set End bit when exiting dsc? */ + if (dsc_mode != OPTC_DSC_DISABLED) { + struct dc_info_packet emp_packet = {0}; + uint32_t dsc_pic_width = timing->h_addressable + timing->h_border_left + timing->h_border_right; + uint32_t h_back = timing->h_total - dsc_pic_width - timing->h_sync_width - timing->h_front_porch; + /* HCactivebytes = Slices * ceil(SliceWidth * bpp/8) + * use ... + 15) / 16 to achieve ceil since bpp is stored as 16x actual value + */ + uint32_t h_cactive_bytes = timing->dsc_cfg.num_slices_h * ( + (dsc_pic_width / timing->dsc_cfg.num_slices_h * timing->dsc_cfg.bits_per_pixel / 8 + 15) / 16); + + /* Packet 0 */ + emp_packet.valid = true; + emp_packet.hb0 = 0x7F; /* Default */ + emp_packet.hb1 = (1 << 7); /* First */ + emp_packet.hb2 = 0; /* Sequence index */ + emp_packet.sb[0] = (1 << 1) | (1 << 2) | (1 << 7); /* Sync[1] = 1, VFR[2] = 1, New[7] = 1*/ + emp_packet.sb[2] = 1; /* Organization_ID = 1 (Vesa spec)*/ + emp_packet.sb[4] = 2; /* Data_Set_Tag(LSB) = 2*/ + emp_packet.sb[6] = 136; /* Data_Set_Length(LSB) = 136*/ + memcpy(&emp_packet.sb[7], dsc_packed_pps, 21); + hpo_enc3_update_hdmi_info_packet(enc3, 5, &emp_packet); + + /* Packets 1-3 */ + emp_packet.hb1 = 0; /* Not first or last*/ + for (i = 1; i < 4; i++) { + emp_packet.hb2 = i; /* Sequence index */ + memcpy(&emp_packet.sb[0], &dsc_packed_pps[21 + 28 * (i - 1)], 28); + hpo_enc3_update_hdmi_info_packet(enc3, 5 + i, &emp_packet); + } + + /* Packet 4 */ + emp_packet.hb2 = 4; /* Sequence index */ + memcpy(&emp_packet.sb[0], &dsc_packed_pps[105], 23); + emp_packet.sb[23] = (uint8_t)timing->h_front_porch; /* Hfront[7:0] */ + emp_packet.sb[24] = (uint8_t)(timing->h_front_porch >> 8); /* Hfront[15:8] */ + emp_packet.sb[25] = (uint8_t)timing->h_sync_width; /* Hsync[7:0] */ + emp_packet.sb[26] = (uint8_t)(timing->h_sync_width >> 8); /* Hsync[15:8] */ + emp_packet.sb[27] = (uint8_t)h_back; /* Hback[7:0] */ + hpo_enc3_update_hdmi_info_packet(enc3, 9, &emp_packet); + + /* Packet 5 */ + emp_packet.hb1 = (1 << 6); /* Last */ + emp_packet.hb2 = 5; + emp_packet.sb[0] = (uint8_t)(h_back >> 8); /* Hback[15:8] */ + emp_packet.sb[1] = (uint8_t)h_cactive_bytes; /* HCactive_bytes[7:0] */ + emp_packet.sb[2] = (uint8_t)(h_cactive_bytes >> 8); /* HCactive_bytes[15:8] */ + hpo_enc3_update_hdmi_info_packet(enc3, 10, &emp_packet); + + /* Packet 0 - Clear New[7] */ + emp_packet.valid = true; + emp_packet.hb0 = 0x7F; /* Default */ + emp_packet.hb1 = (1 << 7); /* First */ + emp_packet.hb2 = 0; /* Sequence index */ + emp_packet.sb[0] = (1 << 1) | (1 << 2); /* Sync[1] = 1, VFR[2] = 1*/ + emp_packet.sb[2] = 1; /* Organization_ID = 1 (Vesa spec)*/ + emp_packet.sb[4] = 2; /* Data_Set_Tag(LSB) = 2*/ + emp_packet.sb[6] = 136; /* Data_Set_Length(LSB) = 136*/ + memcpy(&emp_packet.sb[7], dsc_packed_pps, 21); + hpo_enc3_update_hdmi_info_packet(enc3, 5, &emp_packet); + } +} + void hpo_enc3_stop_hdmi_info_packets( struct hpo_frl_stream_encoder *enc) { @@ -777,12 +875,18 @@ bool hpo_enc3_validate_hdmi_frl_output( default: break; } + frl_params.allow_all_bpp = timing->dsc_cfg.is_vic_all_bpp; if (timing->pixel_encoding == PIXEL_ENCODING_YCBCR420) frl_params.pixel_encoding = HDMI_FRL_PIXEL_ENCODING_420; else if (timing->pixel_encoding == PIXEL_ENCODING_YCBCR422) frl_params.pixel_encoding = HDMI_FRL_PIXEL_ENCODING_422; else frl_params.pixel_encoding = HDMI_FRL_PIXEL_ENCODING_444; + /* DSC parameters */ + frl_params.bypass_hc_target_calc = false; + DC_FP_START(); + hpo_fpu_enc3_validate_hdmi_frl_output_timing(timing, audio, &frl_params); + DC_FP_END(); /* Audio parameters */ /* TODO: set Audio parameters */ @@ -910,6 +1014,7 @@ static const struct hpo_frl_stream_encoder_funcs dcn30_str_enc_funcs = { .set_avmute = enc3_stream_encoder_set_avmute, .validate_hdmi_frl_output = hpo_enc3_validate_hdmi_frl_output, .read_state = hpo_enc3_read_state, + .hdmi_frl_set_dsc_config = hpo_enc3_hdmi_set_dsc_config, .set_dynamic_metadata = hpo_enc3_set_dynamic_metadata, .hdmi_frl_fifo_odm_enabled = hpo_enc3_fifo_odm_enabled, }; diff --git a/drivers/gpu/drm/amd/display/dc/hpo/dcn401/dcn401_hpo_frl_stream_encoder.c b/drivers/gpu/drm/amd/display/dc/hpo/dcn401/dcn401_hpo_frl_stream_encoder.c index 27f7ffd896299..28cb14dc87b09 100644 --- a/drivers/gpu/drm/amd/display/dc/hpo/dcn401/dcn401_hpo_frl_stream_encoder.c +++ b/drivers/gpu/drm/amd/display/dc/hpo/dcn401/dcn401_hpo_frl_stream_encoder.c @@ -879,6 +879,7 @@ static const struct hpo_frl_stream_encoder_funcs dcn401_str_enc_funcs = { .hdmi_audio_disable = hpo_enc401_hdmi_audio_disable, .set_avmute = enc401_stream_encoder_set_avmute, .read_state = hpo_enc401_read_state, + .hdmi_frl_set_dsc_config = hpo_enc401_hdmi_set_dsc_config, .set_dynamic_metadata = hpo_enc401_set_dynamic_metadata, }; diff --git a/drivers/gpu/drm/amd/display/dc/hpo/dcn42/dcn42_hpo_frl_stream_encoder.c b/drivers/gpu/drm/amd/display/dc/hpo/dcn42/dcn42_hpo_frl_stream_encoder.c index 0ec386347f800..d4f66e62c729d 100644 --- a/drivers/gpu/drm/amd/display/dc/hpo/dcn42/dcn42_hpo_frl_stream_encoder.c +++ b/drivers/gpu/drm/amd/display/dc/hpo/dcn42/dcn42_hpo_frl_stream_encoder.c @@ -179,6 +179,7 @@ static const struct hpo_frl_stream_encoder_funcs dcn42_str_enc_funcs = { .hdmi_audio_disable = hpo_enc42_hdmi_audio_disable, .set_avmute = enc401_stream_encoder_set_avmute, .read_state = hpo_enc401_read_state, + .hdmi_frl_set_dsc_config = hpo_enc401_hdmi_set_dsc_config, .set_dynamic_metadata = hpo_enc401_set_dynamic_metadata, }; diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn30/dcn30_hwseq.h b/drivers/gpu/drm/amd/display/dc/hwss/dcn30/dcn30_hwseq.h index 2306354e90af6..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 @@ -85,9 +85,6 @@ int dcn30_hw_get_max_fva_factor(struct dc *dc, struct dc_crtc_timing *timing, unsigned int max_pixel_clock); -void dcn30_hw_set_vstartup_dsc_frl(struct dc *dc, - struct pipe_ctx *pipe_ctx); - 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/hw_sequencer.h b/drivers/gpu/drm/amd/display/dc/hwss/hw_sequencer.h index 35e5371b9bfe7..9e7a163c61e17 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/hw_sequencer.h +++ b/drivers/gpu/drm/amd/display/dc/hwss/hw_sequencer.h @@ -1642,6 +1642,8 @@ void hwss_hpo_dp_stream_enc_dp_set_dsc_pps_info_packet(union block_sequence_para void hwss_stream_enc_dp_set_dsc_pps_info_packet(union block_sequence_params *params); +void hwss_hpo_frl_stream_enc_set_dsc_config(union block_sequence_params *params); + void hwss_set_dmdata_attributes(union block_sequence_params *params); void hwss_dp_trace_source_sequence(union block_sequence_params *params); diff --git a/drivers/gpu/drm/amd/display/dc/inc/hw/stream_encoder.h b/drivers/gpu/drm/amd/display/dc/inc/hw/stream_encoder.h index 1800b1929b16b..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 @@ -368,6 +368,10 @@ struct hpo_frl_stream_encoder { * stream interfaces for setup the FRL stream encoder. */ struct hpo_frl_stream_encoder_funcs { + void (*hdmi_frl_set_dsc_config)( + struct hpo_frl_stream_encoder *enc, + struct dc_crtc_timing *timing, + uint8_t *dsc_packed_pps); /** * @hdmi_frl_enable: * diff --git a/drivers/gpu/drm/amd/display/dc/inc/hw/timing_generator.h b/drivers/gpu/drm/amd/display/dc/inc/hw/timing_generator.h index cf124838f354b..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 @@ -498,7 +498,6 @@ struct timing_generator_funcs { int (*get_max_hw_supported_fva_factor)(struct timing_generator *optc, struct dc_crtc_timing *timing, unsigned int max_pixclk_100hz); - void (*set_vstartup_dsc_frl)(struct timing_generator *optc); 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/link/link_dpms.c b/drivers/gpu/drm/amd/display/dc/link/link_dpms.c index c38908c3ce53f..434062a60b795 100644 --- a/drivers/gpu/drm/amd/display/dc/link/link_dpms.c +++ b/drivers/gpu/drm/amd/display/dc/link/link_dpms.c @@ -549,6 +549,8 @@ static void update_psp_stream_config(struct pipe_ctx *pipe_ctx, bool dpms_off) /* dig front end */ config.dig_fe = (uint8_t) pipe_ctx->stream_res.stream_enc->stream_enc_inst; + if (dc_is_hdmi_frl_signal(pipe_ctx->stream->signal)) + config.dig_fe = (uint8_t)pipe_ctx->stream_res.hpo_frl_stream_enc->stream_enc_inst; /* stream encoder index */ config.stream_enc_idx = pipe_ctx->stream_res.stream_enc->id - ENGINE_ID_DIGA; @@ -785,6 +787,22 @@ void link_set_dsc_on_stream(struct pipe_ctx *pipe_ctx, bool enable) /* PPS SDP is set elsewhere because it has to be done after DIG FE is connected to DIG BE */ } + else if (dc_is_hdmi_frl_signal(stream->signal)) { + uint8_t dsc_packed_pps[128]; + struct dc_crtc_timing patched_crtc_timing = stream->timing; + + DC_LOG_DSC("Setting stream encoder DSC config for engine %d:", (int)pipe_ctx->stream_res.hpo_frl_stream_enc->id); + dsc_optc_config_log(dsc, &dsc_optc_cfg); + + /* if we are borrowing from hblank, h_addressable and pic_width need to be adjusted */ + if (dc->debug.enable_hblank_borrow) { + dsc_cfg.pic_width = stream->timing.h_addressable; + } + + dsc->funcs->dsc_get_packed_pps(dsc, &dsc_cfg, &dsc_packed_pps[0]); + pipe_ctx->stream_res.hpo_frl_stream_enc->funcs->hdmi_frl_set_dsc_config( + pipe_ctx->stream_res.hpo_frl_stream_enc, &patched_crtc_timing, &dsc_packed_pps[0]); + } /* Enable DSC in OPTC */ DC_LOG_DSC("Setting optc DSC config for tg instance %d:", pipe_ctx->stream_res.tg->inst); @@ -816,6 +834,9 @@ void link_set_dsc_on_stream(struct pipe_ctx *pipe_ctx, bool enable) pipe_ctx->stream_res.stream_enc, false, NULL, true); } } + else if (dc_is_hdmi_frl_signal(stream->signal)) + pipe_ctx->stream_res.hpo_frl_stream_enc->funcs->hdmi_frl_set_dsc_config( + pipe_ctx->stream_res.hpo_frl_stream_enc, &stream->timing, NULL); /* disable DSC block */ for (odm_pipe = pipe_ctx; odm_pipe; odm_pipe = odm_pipe->next_odm_pipe) { @@ -903,6 +924,12 @@ bool link_set_dsc_pps_packet(struct pipe_ctx *pipe_ctx, bool enable, bool immedi &dsc_packed_pps[0], immediate_update); } + else if (dc_is_hdmi_frl_signal(stream->signal)) { + //TODO: bring HDMI FRL in line with DP + DC_LOG_DSC("Setting stream encoder DSC PPS SDP for engine %d\n", (int)pipe_ctx->stream_res.hpo_frl_stream_enc->id); + pipe_ctx->stream_res.hpo_frl_stream_enc->funcs->hdmi_frl_set_dsc_config( + pipe_ctx->stream_res.hpo_frl_stream_enc, &stream->timing, &dsc_packed_pps[0]); + } } else { /* disable DSC PPS in stream encoder */ memset(&stream->dsc_packed_pps[0], 0, sizeof(stream->dsc_packed_pps)); @@ -917,6 +944,10 @@ bool link_set_dsc_pps_packet(struct pipe_ctx *pipe_ctx, bool enable, bool immedi pipe_ctx->stream_res.stream_enc->funcs->dp_set_dsc_pps_info_packet( pipe_ctx->stream_res.stream_enc, false, NULL, true); } + else if (dc_is_hdmi_frl_signal(stream->signal)) + //TODO: bring HDMI FRL in line with DP + pipe_ctx->stream_res.hpo_frl_stream_enc->funcs->hdmi_frl_set_dsc_config( + pipe_ctx->stream_res.hpo_frl_stream_enc, &stream->timing, NULL); } return true; @@ -2426,6 +2457,8 @@ void link_set_dpms_off(struct pipe_ctx *pipe_ctx) if (pipe_ctx->stream->timing.flags.DSC) { if (dc_is_dp_signal(pipe_ctx->stream->signal)) link_set_dsc_enable(pipe_ctx, false); + else if (dc_is_hdmi_frl_signal(pipe_ctx->stream->signal)) + link_set_dsc_on_stream(pipe_ctx, false); } if (dp_is_128b_132b_signal(pipe_ctx)) { if (pipe_ctx->stream_res.tg->funcs->set_out_mux) @@ -2606,6 +2639,11 @@ void link_set_dpms_on( } } + if (pipe_ctx->stream->timing.flags.DSC && + dc_is_hdmi_frl_signal(pipe_ctx->stream->signal)) + //TODO: bring HDMI FRL in line with DP + link_set_dsc_on_stream(pipe_ctx, true); + /* turn off otg test pattern if enable */ if (pipe_ctx->stream_res.tg->funcs->set_test_pattern) pipe_ctx->stream_res.tg->funcs->set_test_pattern(pipe_ctx->stream_res.tg, diff --git a/drivers/gpu/drm/amd/display/dc/link/protocols/link_hdmi_frl.c b/drivers/gpu/drm/amd/display/dc/link/protocols/link_hdmi_frl.c index 0cb287825fb9c..c35074177a369 100644 --- a/drivers/gpu/drm/amd/display/dc/link/protocols/link_hdmi_frl.c +++ b/drivers/gpu/drm/amd/display/dc/link/protocols/link_hdmi_frl.c @@ -92,6 +92,25 @@ static void hdmi_return_preeshoot_and_deemphasis(struct dc_link *link, } } +static bool hdmi_frl_test_dsc_max_rate(struct ddc_service *ddc_service) +{ + uint8_t slave_address = HDMI_SCDC_ADDRESS; + uint8_t offset = HDMI_SCDC_SOURCE_TEST_REQ; + union hdmi_scdc_source_test_req test_req = {0}; + + DC_LOGGER_INIT(ddc_service->link->ctx->logger); + + link_query_ddc_data(ddc_service, slave_address, + &offset, sizeof(offset), &test_req.byte, + sizeof(test_req.byte)); + if (test_req.fields.DSC_FRL_MAX) { + FRL_INFO("FRL TEST REQ: DSC_FRL_MAX = 1"); + return true; + } + + return false; +} + enum clock_source_id hdmi_frl_find_matching_phypll( struct dc_link *link) { @@ -767,6 +786,9 @@ void hdmi_frl_verify_link_cap(struct dc_link *link, link->preferred_hdmi_frl_settings.force_frl_max || link->ctx->dc->debug.force_frl_max ? true : hdmi_frl_test_max_rate(link->ddc); + link->frl_flags.force_frl_dsc = + link->ctx->dc->debug.force_frl_dsc ? true : + hdmi_frl_test_dsc_max_rate(link->ddc); link->frl_flags.apply_vsdb_rcc_wa = link->ctx->dc->debug.apply_vsdb_rcc_wa; @@ -778,8 +800,16 @@ void hdmi_frl_verify_link_cap(struct dc_link *link, link->frl_flags.force_frl_always = true; if (!link->frl_flags.force_frl_max && + !link->frl_flags.force_frl_dsc && link->local_sink->edid_caps.panel_patch.hdmi_comp_auto) { link->frl_flags.force_frl_max = true; + link->frl_flags.force_frl_dsc = true; + } + + if (link->frl_flags.force_frl_max && + !link->frl_flags.force_frl_dsc && + link->local_sink->edid_caps.panel_patch.hdmi_comp_auto) { + link->frl_flags.force_frl_dsc = true; } if (link->local_sink && @@ -996,6 +1026,9 @@ void hdmi_frl_set_preferred_link_settings(struct dc *dc, resource_build_info_frame(pipe); link_stream->ctx->dc->hwss.update_info_frame(pipe); + if (link_stream->timing.flags.DSC) + link_set_dsc_on_stream(pipe, true); + link_stream->ctx->dc->hwss.enable_audio_stream(pipe); link_stream->ctx->dc->hwss.enable_stream(pipe); link_stream->ctx->dc->hwss.unblank_stream(pipe, @@ -1019,6 +1052,27 @@ void hdmi_frl_set_preferred_link_settings(struct dc *dc, } } +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) @@ -1085,6 +1139,10 @@ void hdmi_frl_decide_link_settings(struct dc_stream_state *stream, *frl_link_settings = stream->link->frl_verified_link_cap; return; } + if (stream->link->frl_flags.force_frl_dsc) { + *frl_link_settings = stream->link->frl_verified_link_cap; + return; + } if (stream->link->local_sink) if (stream->link->local_sink->edid_caps.panel_patch.hdmi_spe_handling) { @@ -1107,6 +1165,7 @@ void hdmi_frl_decide_link_settings(struct dc_stream_state *stream, } while (!success); *frl_link_settings = temp_settings; + update_borrow_mode_from_dsc_padding(dsc_padding_params, &stream->timing, frl_link_settings); } void hdmi_frl_write_read_request_enable(struct ddc_service *ddc_service) diff --git a/drivers/gpu/drm/amd/display/dc/optc/dcn30/dcn30_optc.c b/drivers/gpu/drm/amd/display/dc/optc/dcn30/dcn30_optc.c index 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, From c45d25943e2c5dae6499c504f66b340e223cb33a Mon Sep 17 00:00:00 2001 From: Fangzhi Zuo Date: Wed, 13 May 2026 17:02:03 -0400 Subject: [PATCH 12/12] [FROM-UPSTREAM] drm/amd/display: Disable FRL and add module param to enable it FRL links don't yet support VRR. If we enable it by default users will see a functional regression when connected to an FRL capable display as the driver will now default to FRL and not allow VRR. VRR support will come soon, so instead of making an elaborate TMDS fallback mechanism simply default FRL to disabled, but provide a dcfeaturemask of 0x400 to enable it if anyone wants to already try it out. Reviewed-by: Harry Wentland Signed-off-by: Fangzhi Zuo Signed-off-by: Harry Wentland Signed-off-by: Alex Deucher --- .../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 3 ++ .../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h | 2 +- .../amd/display/amdgpu_dm/amdgpu_dm_helpers.c | 30 ++++++++++--------- drivers/gpu/drm/amd/display/dc/dc.h | 1 + drivers/gpu/drm/amd/include/amd_shared.h | 4 +++ 5 files changed, 25 insertions(+), 15 deletions(-) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c index 495f3bd08a174..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)) { 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 fd7ee8a98d516..693b9a99e08f6 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h @@ -1099,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_helpers.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c index 3beb322043913..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 @@ -179,7 +179,7 @@ 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); + 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", @@ -1124,21 +1124,23 @@ static uint8_t get_dsc_max_slices(uint8_t max_slices, int clk_per_slice) return dsc_max_slices; } -void populate_hdmi_info_from_connector(struct drm_hdmi_info *hdmi, struct dc_edid_caps *edid_caps) +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; - 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; + 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; + } } } diff --git a/drivers/gpu/drm/amd/display/dc/dc.h b/drivers/gpu/drm/amd/display/dc/dc.h index 7c191ec0f7115..be0431088cd01 100644 --- a/drivers/gpu/drm/amd/display/dc/dc.h +++ b/drivers/gpu/drm/amd/display/dc/dc.h @@ -589,6 +589,7 @@ 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; 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), }; /**