* [PATCH v5 00/13] HDMI FRL and DSC Support for amdgpu
@ 2026-05-12 15:52 Harry Wentland
2026-05-12 15:52 ` [PATCH v5 01/13] drm/amd/display: Add HDMI FRL definitions to includes Harry Wentland
` (12 more replies)
0 siblings, 13 replies; 15+ messages in thread
From: Harry Wentland @ 2026-05-12 15:52 UTC (permalink / raw)
To: amd-gfx; +Cc: Jerry.Zuo, Harry Wentland, Rodrigo Siqueira, Tomasz Pakuła
This patch series adds HDMI FRL and FRL DSC support to the amdgpu
display driver.
This work passed a representative subset of HDMI compliance and a full
compliance run on this branch is in the works. We don't expect the full
run to show any failures since it passes in other environments.
Thanks to Siqueira who prepared this work a couple years back and
unfortunately didn't manage to send them while he was still working
at AMD.
Thanks to Jerry who has been making this code solid on Linux and
running the compliance tests.
The first patch in the series isn't related to HDMI 2.1 but included
here because it moved the code around some key bits of the HDMI 2.1
stuff around too much. It will land with the next DC Patch series.
v5:
- One more missing FP compilation fix
v4:
- Disable FRL over DP MST due to lack of testing
- FP compilation fix
- squash fixup commit
v3:
- Add missing DML2 bits
- Merged register headers to asdn and removed from patchset
v2:
- Add missing function pointers on DCN 3.x
- Add DSC
Cc: Rodrigo Siqueira <siqueira@igalia.com>
Cc: Zuo, Jerry <Jerry.Zuo@amd.com>
Cc: Tomasz Pakuła <tomasz.pakula.oficjalny@gmail.com>
Fangzhi Zuo (1):
drm/amd/display: add HDMI 2.1 Compliance Support
Harry Wentland (12):
drm/amd/display: Add HDMI FRL definitions to includes
drm/amd/display: Add DML changes to support HDMI FRL
drm/amd/display: add HDMI 2.1 FRL base support to DML 2.0
drm/amd/display: Add DCCG DIO, HPO, OPP, and OPTC support for FRL
drm/amd/display: Add FRL support to clk_mgr, dsc, hdcp
drm/amd/display: Tie FRL programming together in HWSS
drm/amd/display: Add DC resource support for FRL
drm/amd/display Add DC link support for FRL
drm/amd/display: Add support for FRL to DC core
drm/amd/display: Update HDCP and info_packet modules for FRL
drm/amd/display: Tie FRL support into amdgpu_dm
drm/amd/display: add HDMI 2.1 DSC over FRL support
.../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 160 ++-
.../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h | 11 +
.../amd/display/amdgpu_dm/amdgpu_dm_debugfs.c | 28 +
.../amd/display/amdgpu_dm/amdgpu_dm_hdcp.c | 2 +
.../amd/display/amdgpu_dm/amdgpu_dm_helpers.c | 68 +-
.../display/amdgpu_dm/amdgpu_dm_mst_types.c | 79 ++
.../drm/amd/display/dc/bios/bios_parser2.c | 21 +
.../drm/amd/display/dc/bios/command_table2.c | 6 +
.../dce112/command_table_helper2_dce112.c | 3 +
.../bios/dce112/command_table_helper_dce112.c | 3 +
.../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 | 18 +
drivers/gpu/drm/amd/display/dc/core/dc.c | 32 +-
.../gpu/drm/amd/display/dc/core/dc_debug.c | 4 +
.../drm/amd/display/dc/core/dc_hw_sequencer.c | 49 +
.../drm/amd/display/dc/core/dc_link_enc_cfg.c | 3 +
.../drm/amd/display/dc/core/dc_link_exports.c | 45 +
.../gpu/drm/amd/display/dc/core/dc_resource.c | 289 ++++
.../gpu/drm/amd/display/dc/core/dc_stream.c | 35 +
drivers/gpu/drm/amd/display/dc/dc.h | 128 ++
drivers/gpu/drm/amd/display/dc/dc_dsc.h | 8 +
.../gpu/drm/amd/display/dc/dc_hdmi_types.h | 164 ++-
drivers/gpu/drm/amd/display/dc/dc_hw_types.h | 8 +
drivers/gpu/drm/amd/display/dc/dc_types.h | 24 +
.../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 +
.../gpu/drm/amd/display/dc/dce/dce_audio.c | 8 +
.../drm/amd/display/dc/dce/dce_clock_source.c | 30 +-
.../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/dm_cp_psp.h | 1 +
.../drm/amd/display/dc/dm_services_types.h | 1 +
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 | 127 ++
.../drm/amd/display/dc/dml/dcn30/dcn30_fpu.h | 22 +
.../dc/dml/dcn30/display_mode_vba_30.c | 280 +++-
.../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 | 243 ++++
.../dc/dml/dcn314/display_mode_vba_314.c | 241 ++++
.../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 | 127 +-
.../amd/display/dc/dml/display_mode_enums.h | 1 +
.../drm/amd/display/dc/dml/dml1_frl_cap_chk.c | 786 +++++++++++
.../drm/amd/display/dc/dml/dml1_frl_cap_chk.h | 173 +++
.../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 ++
drivers/gpu/drm/amd/display/dc/dsc/dc_dsc.c | 704 ++++++++++
drivers/gpu/drm/amd/display/dc/dsc/dsc.h | 5 +
.../gpu/drm/amd/display/dc/hdcp/hdcp_msg.c | 1 +
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 | 1043 +++++++++++++++
.../hpo/dcn30/dcn30_hpo_frl_stream_encoder.h | 436 ++++++
.../dcn401/dcn401_hpo_frl_stream_encoder.c | 907 +++++++++++++
.../dcn401/dcn401_hpo_frl_stream_encoder.h | 335 +++++
.../hpo/dcn42/dcn42_hpo_frl_stream_encoder.c | 207 +++
.../hpo/dcn42/dcn42_hpo_frl_stream_encoder.h | 59 +
.../amd/display/dc/hwss/dce110/dce110_hwseq.c | 99 +-
.../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 | 13 +
.../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 | 42 +
.../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 | 34 +
.../display/dc/hwss/hw_sequencer_private.h | 3 +
.../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 | 231 ++++
.../amd/display/dc/inc/hw/timing_generator.h | 5 +
.../gpu/drm/amd/display/dc/inc/link_service.h | 15 +
drivers/gpu/drm/amd/display/dc/inc/resource.h | 7 +
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 | 223 +++-
.../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 | 1187 +++++++++++++++++
.../display/dc/link/protocols/link_hdmi_frl.h | 53 +
.../amd/display/dc/link/protocols/link_hpd.c | 1 +
.../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.c | 8 +-
.../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 +
.../dc/resource/dce112/dce112_resource.c | 3 +
.../dc/resource/dcn30/dcn30_resource.c | 126 ++
.../dc/resource/dcn301/dcn301_resource.c | 1 +
.../dc/resource/dcn302/dcn302_resource.c | 109 ++
.../dc/resource/dcn303/dcn303_resource.c | 109 ++
.../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 ++
.../gpu/drm/amd/display/dmub/inc/dmub_cmd.h | 5 +-
.../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 +-
.../gpu/drm/amd/display/modules/hdcp/hdcp.c | 2 +
.../gpu/drm/amd/display/modules/hdcp/hdcp.h | 6 +
.../drm/amd/display/modules/hdcp/hdcp_psp.c | 2 +
.../drm/amd/display/modules/hdcp/hdcp_psp.h | 2 +
.../amd/display/modules/inc/mod_freesync.h | 3 +
.../drm/amd/display/modules/inc/mod_hdcp.h | 1 +
.../amd/display/modules/inc/mod_info_packet.h | 2 +-
.../display/modules/info_packet/info_packet.c | 122 +-
179 files changed, 15129 insertions(+), 113 deletions(-)
create mode 100644 drivers/gpu/drm/amd/display/dc/dml/dml1_frl_cap_chk.c
create mode 100644 drivers/gpu/drm/amd/display/dc/dml/dml1_frl_cap_chk.h
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
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
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
--
2.54.0
^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH v5 01/13] drm/amd/display: Add HDMI FRL definitions to includes
2026-05-12 15:52 [PATCH v5 00/13] HDMI FRL and DSC Support for amdgpu Harry Wentland
@ 2026-05-12 15:52 ` Harry Wentland
2026-05-12 15:52 ` [PATCH v5 02/13] drm/amd/display: Add DML changes to support HDMI FRL Harry Wentland
` (11 subsequent siblings)
12 siblings, 0 replies; 15+ messages in thread
From: Harry Wentland @ 2026-05-12 15:52 UTC (permalink / raw)
To: amd-gfx; +Cc: Jerry.Zuo, Harry Wentland
This patch adds all relevant includes in DC that are
used by the HDMI FRL implementation in DC.
Signed-off-by: Harry Wentland <harry.wentland@amd.com>
Reviewed-by: Fangzhi Zuo <Jerry.Zuo@amd.com>
---
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 d0b6fad65bc0..0cbb60325875 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;
@@ -1096,6 +1144,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;
@@ -1635,6 +1695,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
@@ -1649,6 +1710,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;
@@ -1694,6 +1756,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. */
@@ -2033,6 +2101,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);
@@ -2244,6 +2323,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.
*
@@ -2268,6 +2353,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;
@@ -2319,6 +2410,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
*/
@@ -2372,6 +2465,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
@@ -2421,6 +2524,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
@@ -3094,6 +3207,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 fcd3ab4b0045..afd21eb6bbcd 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 c2ca08d26e37..c13a824c9d35 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 3ec49f4e277f..4ed1efa17270 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 {
@@ -929,6 +950,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 f4d3f04ec857..cb20bfe9c1c8 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 3b093b8699ab..e676e395cabf 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 000000000000..debe4c1dc0f7
--- /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 82085d9c3f40..388f801f4582 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 a99e1937f8ce..cbbc1fb4b3dd 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 d6f5e01a0b66..6db7c8753081 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 51581c10fd6b..176d048a64e7 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 5abbf485d273..e7727ed3300c 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 a9bf960dacbc..22d5f291c7db 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;
@@ -278,6 +288,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 3a80369cde16..e4e17f630e97 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 d0609443af49..23202c2114bb 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 cecd3282a29f..6352e078b132 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 6699ad4fa825..2a0f1a9c30a5 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 b5d97b394131..aeeac1f0c2c3 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 177acb0574f1..a11bfb2e7cdd 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 3a2c2d2fb629..9b134aa3e275 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)
--
2.54.0
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH v5 02/13] drm/amd/display: Add DML changes to support HDMI FRL
2026-05-12 15:52 [PATCH v5 00/13] HDMI FRL and DSC Support for amdgpu Harry Wentland
2026-05-12 15:52 ` [PATCH v5 01/13] drm/amd/display: Add HDMI FRL definitions to includes Harry Wentland
@ 2026-05-12 15:52 ` Harry Wentland
2026-05-12 15:52 ` [PATCH v5 03/13] drm/amd/display: add HDMI 2.1 FRL base support to DML 2.0 Harry Wentland
` (10 subsequent siblings)
12 siblings, 0 replies; 15+ messages in thread
From: Harry Wentland @ 2026-05-12 15:52 UTC (permalink / raw)
To: amd-gfx; +Cc: Jerry.Zuo, Harry Wentland
This adds DML support for HDMI FRL.
Signed-off-by: Harry Wentland <harry.wentland@amd.com>
Reviewed-by: Fangzhi Zuo <Jerry.Zuo@amd.com>
---
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 268b5fbdb48b..10d4ace04d4f 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 ed7c989a5b13..5f088d113b9f 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
@@ -1049,6 +1049,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;
}
@@ -1467,6 +1469,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 0ba388c6aec1..a690324f78dc 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,
},
},
@@ -741,3 +744,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 e3b6ad6a8784..e3a46915d168 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 f0b1bfb408f6..2a309e86f60f 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;
@@ -2017,6 +2053,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)
@@ -3309,19 +3347,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;
@@ -3329,12 +3386,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 {
@@ -3372,6 +3437,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;
}
@@ -4013,6 +4080,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;
@@ -4022,7 +4255,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;
@@ -4030,6 +4263,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) {
@@ -6248,6 +6500,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 90a7b29ebe74..781bc429807f 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 e42419c7868d..e6d7d67f06a4 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 f9224a433220..261d90aaa3ad 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;
}
@@ -4105,6 +4174,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;
@@ -4529,6 +4599,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;
@@ -4542,6 +4737,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;
}
@@ -4553,6 +4749,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;
@@ -4564,11 +4761,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;
}
@@ -7055,6 +7272,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 dd9dc0c8cb43..674605d8d4fd 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;
}
@@ -4195,6 +4264,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;
@@ -4616,6 +4686,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;
@@ -4629,6 +4824,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;
}
@@ -4640,6 +4836,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;
@@ -4651,6 +4848,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;
@@ -7141,6 +7356,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 1b1ab6a6d53a..b5bd1358fdfd 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
@@ -1608,6 +1608,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;
}
@@ -3588,3 +3590,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 273d2bd79d85..ff83fbc811d3 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 505b5a5f212e..81e608b3cc74 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)
@@ -2282,6 +2286,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]
@@ -2328,6 +2334,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)) {
@@ -2353,6 +2360,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 &&
@@ -2388,7 +2396,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) {
@@ -2415,6 +2425,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],
@@ -2451,6 +2462,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;
@@ -2458,6 +2470,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 15f5248340a7..40a0a5815ca5 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 d5831a34f5a1..42013fa5ad01 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 000000000000..9dde4e56f237
--- /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 debe4c1dc0f7..545f498ea396 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,
--
2.54.0
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH v5 03/13] drm/amd/display: add HDMI 2.1 FRL base support to DML 2.0
2026-05-12 15:52 [PATCH v5 00/13] HDMI FRL and DSC Support for amdgpu Harry Wentland
2026-05-12 15:52 ` [PATCH v5 01/13] drm/amd/display: Add HDMI FRL definitions to includes Harry Wentland
2026-05-12 15:52 ` [PATCH v5 02/13] drm/amd/display: Add DML changes to support HDMI FRL Harry Wentland
@ 2026-05-12 15:52 ` Harry Wentland
2026-05-12 15:52 ` [PATCH v5 04/13] drm/amd/display: Add DCCG DIO, HPO, OPP, and OPTC support for FRL Harry Wentland
` (9 subsequent siblings)
12 siblings, 0 replies; 15+ messages in thread
From: Harry Wentland @ 2026-05-12 15:52 UTC (permalink / raw)
To: amd-gfx; +Cc: Jerry.Zuo, Harry Wentland
Add HDMI FRL bits to DML 2.0
Signed-off-by: Harry Wentland <harry.wentland@amd.com>
Reviewed-by: Fangzhi Zuo <Jerry.Zuo@amd.com>
---
.../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 8a451c36fdb3..44e00c2b7ac7 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 241406e9e85a..fb4ea2ee28b7 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 3939a0d8b835..a8519f547dce 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 25557c99a28e..c86b45bbeb2b 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
@@ -213,6 +213,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;
}
@@ -247,6 +250,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 858e7bbc511f..c983869e0fa3 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 827bd9143c87..f338e733318e 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 080bc3c3d244..11e295253f72 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 //<brief max vratio for one-to-one prefetch bw scheduling
@@ -1522,6 +1523,8 @@ struct dml2_core_shared_CalculateSwathAndDETConfiguration_locals {
};
struct dml2_core_shared_TruncToValidBPP_locals {
+ struct lib_frl_cap_check_params hdmifrlparams;
+ struct lib_frl_cap_check_intermediates hdmifrlinter;
};
struct dml2_core_shared_CalculateDETBufferSize_locals {
diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_standalone_libraries/lib_frl_cap_check.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_standalone_libraries/lib_frl_cap_check.c
new file mode 100644
index 000000000000..d62cdf8566cc
--- /dev/null
+++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_standalone_libraries/lib_frl_cap_check.c
@@ -0,0 +1,396 @@
+// SPDX-License-Identifier: MIT
+//
+// Copyright 2024 Advanced Micro Devices, Inc.
+
+#include "lib_float_math.h"
+#include "lib_frl_cap_check.h"
+
+#define frl_dump_var(fmt, var) {}
+#define frl_print(fmt, ...) {}
+
+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;
+const int DML2_FRL_CHK_TB_BORROWED_MAX = 400;
+
+static enum lib_frl_cap_check_status frl_cap_check_common(struct lib_frl_cap_check_intermediates *inter, struct lib_frl_cap_check_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 = 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 000000000000..aa2764856546
--- /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 0d8ff236c6d0..166f10b8862f 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 5ed14f694fb0..a3ce011612f7 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 000000000000..a638c0d6d765
--- /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 000000000000..87ac9b94e98b
--- /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
--
2.54.0
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH v5 04/13] drm/amd/display: Add DCCG DIO, HPO, OPP, and OPTC support for FRL
2026-05-12 15:52 [PATCH v5 00/13] HDMI FRL and DSC Support for amdgpu Harry Wentland
` (2 preceding siblings ...)
2026-05-12 15:52 ` [PATCH v5 03/13] drm/amd/display: add HDMI 2.1 FRL base support to DML 2.0 Harry Wentland
@ 2026-05-12 15:52 ` Harry Wentland
2026-05-12 15:52 ` [PATCH v5 05/13] drm/amd/display: Add FRL support to clk_mgr, dsc, hdcp Harry Wentland
` (8 subsequent siblings)
12 siblings, 0 replies; 15+ messages in thread
From: Harry Wentland @ 2026-05-12 15:52 UTC (permalink / raw)
To: amd-gfx; +Cc: Jerry.Zuo, Harry Wentland
This adds support to HW block programming for the core blocks
for HDMI FRL:
- DIO
- HPO
- OPP
- OPTC
Signed-off-by: Harry Wentland <harry.wentland@amd.com>
Reviewed-by: Fangzhi Zuo <Jerry.Zuo@amd.com>
---
.../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 adec7c3c2d49..180175693e0e 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 3f1da7f3a91c..379fe94734ba 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 1f5a4a8bf691..a4f6faaf97fb 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 b5e3849ef12a..2c2d682a82c2 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 2e9c4b13988a..410e914c720c 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 d2752dd8f81f..c81362876774 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 33418ee1fa62..83589777f1ca 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 d49689baa7ca..b9c88bc436e0 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,
@@ -721,6 +787,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)
@@ -881,6 +951,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 5947a35363aa..95f0602466d3 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 9612f4498ef6..e57242f8bc12 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
@@ -181,6 +181,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,
@@ -270,10 +296,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 59b68422334d..4b36c01a9e7a 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
@@ -1066,7 +1066,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 762c579fcb44..1beba9c7d3f9 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 57b9ae5fca1d..8c4010c5b2e7 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 5b6177c2ae98..4fc0915a5dc5 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 47d84a2a48ce..02fb844670f9 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 07d362ef0daf..bcb791d74189 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 ee78ba80797c..3cf587527991 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 65d28cb07b04..68e0dc93ae7d 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 35d23d9db45e..262796819a63 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 968f89295b64..4108fd0f4380 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 20bf04dac609..8fa9b8c500f2 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 e1f0a1bf1075..2aaf06d48771 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 6baab8302b81..a40c479fada8 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 35dfe3bb0c55..fae49b3b170e 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 25b196714bd8..db60564b9cfd 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 000000000000..56e753d2f43d
--- /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 000000000000..9626793812b8
--- /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 000000000000..cd7d2bb661e5
--- /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 000000000000..c00be852aa17
--- /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 000000000000..27f7ffd89629
--- /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 000000000000..b47ba55b9a68
--- /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 000000000000..0ec386347f80
--- /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 000000000000..ff8218bed753
--- /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 b49bd155cad4..98335a53ca52 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 138081e6cc97..bceefb5320eb 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 e2303f9eaf13..16c5610b49ac 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 5f53f8747812..98aaa22ce81c 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 a8e978d1fae8..a6d76f451cf8 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);
}
--
2.54.0
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH v5 05/13] drm/amd/display: Add FRL support to clk_mgr, dsc, hdcp
2026-05-12 15:52 [PATCH v5 00/13] HDMI FRL and DSC Support for amdgpu Harry Wentland
` (3 preceding siblings ...)
2026-05-12 15:52 ` [PATCH v5 04/13] drm/amd/display: Add DCCG DIO, HPO, OPP, and OPTC support for FRL Harry Wentland
@ 2026-05-12 15:52 ` Harry Wentland
2026-05-12 15:52 ` [PATCH v5 06/13] drm/amd/display: Tie FRL programming together in HWSS Harry Wentland
` (7 subsequent siblings)
12 siblings, 0 replies; 15+ messages in thread
From: Harry Wentland @ 2026-05-12 15:52 UTC (permalink / raw)
To: amd-gfx; +Cc: Jerry.Zuo, Harry Wentland
This adds a few, relatively minor, changes for FRL to
clk_mgr, DSC, and HDCP blocks.
Signed-off-by: Harry Wentland <harry.wentland@amd.com>
Reviewed-by: Fangzhi Zuo <Jerry.Zuo@amd.com>
---
.../display/dc/clk_mgr/dcn31/dcn31_clk_mgr.c | 3 +++
.../display/dc/clk_mgr/dcn314/dcn314_clk_mgr.c | 3 +++
.../display/dc/clk_mgr/dcn315/dcn315_clk_mgr.c | 3 +++
.../display/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 ++++++++++++++
.../display/dc/clk_mgr/dcn401/dcn401_clk_mgr.c | 18 ++++++++++++++++++
drivers/gpu/drm/amd/display/dc/dsc/dsc.h | 5 +++++
drivers/gpu/drm/amd/display/dc/hdcp/hdcp_msg.c | 1 +
9 files changed, 62 insertions(+)
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 dc7f50095a13..00c4be7c3aa4 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 5fe59adc862f..dd6f11ecb9c9 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 b6f26475ac16..75d39cb26dba 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 72a38f7a761c..c7fecbdfda2c 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 2dc244d5a55f..be0e3836a6c1 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 6c6848e375e1..103013e2a0de 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 7c2b716d5d2a..5f9398745a38 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;
@@ -1095,6 +1096,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;
@@ -1127,6 +1131,20 @@ static unsigned int dcn401_build_update_display_clocks_sequence(
/* 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 = (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 a16c60d8532f..ab37a7eaaf01 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 73a1e6a03719..34fc9f56dbef 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:
--
2.54.0
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH v5 06/13] drm/amd/display: Tie FRL programming together in HWSS
2026-05-12 15:52 [PATCH v5 00/13] HDMI FRL and DSC Support for amdgpu Harry Wentland
` (4 preceding siblings ...)
2026-05-12 15:52 ` [PATCH v5 05/13] drm/amd/display: Add FRL support to clk_mgr, dsc, hdcp Harry Wentland
@ 2026-05-12 15:52 ` Harry Wentland
2026-05-12 15:52 ` [PATCH v5 07/13] drm/amd/display: Add DC resource support for FRL Harry Wentland
` (6 subsequent siblings)
12 siblings, 0 replies; 15+ messages in thread
From: Harry Wentland @ 2026-05-12 15:52 UTC (permalink / raw)
To: amd-gfx; +Cc: Jerry.Zuo, Harry Wentland
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 <harry.wentland@amd.com>
Reviewed-by: Fangzhi Zuo <Jerry.Zuo@amd.com>
---
.../amd/display/dc/hwss/dce110/dce110_hwseq.c | 99 +++++++++++++++++--
.../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 | 42 ++++++++
.../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 | 32 ++++++
.../display/dc/hwss/hw_sequencer_private.h | 3 +
22 files changed, 373 insertions(+), 19 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 d81271b870bb..042602c50e35 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
@@ -1221,6 +1221,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);
@@ -1245,6 +1248,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);
}
@@ -1316,6 +1320,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);
+ }
+ }
}
@@ -1505,9 +1521,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(
@@ -1732,7 +1771,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);
@@ -2486,7 +2526,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;
@@ -2495,15 +2536,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};
+ dto_params.ref_dtbclk_khz = dc->clk_mgr->funcs->get_dtb_ref_clk_frequency(dc->clk_mgr);
- dc->res_pool->dccg->funcs->set_audio_dtbclk_dto(
- dc->res_pool->dccg, &dto_params);
+ 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->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,
@@ -2533,11 +2586,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 fc2587ca56ec..51001d761b5e 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
@@ -753,6 +753,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 07c53a8e73b5..e6a8206f8ce0 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
@@ -918,6 +918,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;
@@ -2789,6 +2794,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);
}
@@ -3056,7 +3071,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 3c70d685ba65..a7c85a2302ab 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
@@ -838,6 +838,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,
@@ -858,22 +862,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(
@@ -892,6 +903,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;
@@ -905,11 +917,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 40afbbfb5b9c..2306354e90af 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 5cbae0cdda96..26c7386a8a36 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 71643ccf23d9..43e4edfe9182 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 e56b9a46aecf..23b30d6f3956 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 09dfbb16dd29..6ddc678bacf9 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 9900c87b4567..98771fc443c7 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 415b3f875f0d..a3242e7521a4 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 849dae18b738..0b3e8512ebf1 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 1a0123338dfa..8f9038fec0f7 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)
@@ -936,6 +953,14 @@ void dcn35_calc_blocks_to_gate(struct dc *dc, struct dc_state *context,
memset(update_state, 0, sizeof(struct pg_block_update));
+ 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]) {
@@ -991,6 +1016,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)
@@ -1113,6 +1141,14 @@ 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 (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]) {
@@ -1330,6 +1366,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 < (unsigned int)dc->res_pool->res_cap->num_dsc; i++) {
if (update_state->pg_pipe_res_update[PG_DSC][i]) {
@@ -1360,6 +1399,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 e3459546a908..235ebf00bd1f 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 6ac8ad97cf13..fc18d2207711 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 04c260015eec..19ec5b4edfdc 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 124a1ccec741..ab6c85b7f893 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
@@ -760,6 +760,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;
@@ -817,6 +820,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);
}
}
@@ -855,6 +860,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,
@@ -1819,6 +1826,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 5d0dfb36f3e1..d24a352937b4 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 cbc20f214f9e..c56373a3cc60 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
@@ -973,6 +973,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]) {
@@ -1018,11 +1026,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;
@@ -1042,6 +1053,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(
@@ -1089,6 +1106,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;
unsigned int i = 0;
int j = 0;
@@ -1175,6 +1193,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]) {
@@ -1183,9 +1209,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;
@@ -1396,6 +1424,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 < (unsigned int)dc->res_pool->res_cap->num_dsc; i++) {
if (update_state->pg_pipe_res_update[PG_DSC][i]) {
@@ -1426,6 +1457,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 b324a2195e8a..49c13611a518 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 4b9fcb87e60d..a9569078622f 100644
--- a/drivers/gpu/drm/amd/display/dc/hwss/hw_sequencer.h
+++ b/drivers/gpu/drm/amd/display/dc/hwss/hw_sequencer.h
@@ -806,6 +806,10 @@ 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;
};
@@ -847,6 +851,12 @@ struct stream_enc_dp_set_dsc_pps_info_packet_params {
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;
@@ -1028,6 +1038,7 @@ union block_sequence_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;
@@ -1036,6 +1047,7 @@ union block_sequence_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;
@@ -1179,6 +1191,7 @@ enum block_sequence_func {
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,
@@ -1187,6 +1200,7 @@ enum block_sequence_func {
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,
@@ -1415,6 +1429,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);
@@ -1672,6 +1694,8 @@ 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);
@@ -2367,6 +2391,9 @@ void hwss_add_commit_cursor_offload_update(struct block_sequence_state *seq_stat
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);
@@ -2400,6 +2427,11 @@ void hwss_add_stream_enc_dp_set_dsc_pps_info_packet(struct block_sequence_state
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);
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 8e3f54fb53fd..63c6c841c681 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,
--
2.54.0
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH v5 07/13] drm/amd/display: Add DC resource support for FRL
2026-05-12 15:52 [PATCH v5 00/13] HDMI FRL and DSC Support for amdgpu Harry Wentland
` (5 preceding siblings ...)
2026-05-12 15:52 ` [PATCH v5 06/13] drm/amd/display: Tie FRL programming together in HWSS Harry Wentland
@ 2026-05-12 15:52 ` Harry Wentland
2026-05-12 15:52 ` [PATCH v5 08/13] drm/amd/display Add DC link " Harry Wentland
` (5 subsequent siblings)
12 siblings, 0 replies; 15+ messages in thread
From: Harry Wentland @ 2026-05-12 15:52 UTC (permalink / raw)
To: amd-gfx; +Cc: Jerry.Zuo, Harry Wentland
Add support for FRL in DC resources. This is mostly the register
macros, encoder creation, and HW capabilities.
Signed-off-by: Harry Wentland <harry.wentland@amd.com>
Reviewed-by: Fangzhi Zuo <Jerry.Zuo@amd.com>
---
.../dc/resource/dce112/dce112_resource.c | 3 +
.../dc/resource/dcn30/dcn30_resource.c | 126 +++++++++++++++++
.../dc/resource/dcn301/dcn301_resource.c | 1 +
.../dc/resource/dcn302/dcn302_resource.c | 109 +++++++++++++++
.../dc/resource/dcn303/dcn303_resource.c | 109 +++++++++++++++
.../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, 1789 insertions(+), 14 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 ad0214d99a45..6d49d6b70b21 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
@@ -981,6 +981,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 e49728cb41d9..70eacb0edfd9 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,
@@ -1075,6 +1117,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(sizeof(struct dce_hwseq), GFP_KERNEL);
@@ -1091,6 +1196,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,
};
@@ -1113,6 +1219,23 @@ static void dcn30_resource_destruct(struct dcn30_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 < (unsigned int)pool->base.res_cap->num_dsc; i++) {
if (pool->base.dscs[i] != NULL)
dcn20_dsc_destroy(&pool->base.dscs[i]);
@@ -2275,6 +2398,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,
@@ -2358,6 +2482,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 69890c26a8b1..77f9e371f48e 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 db7de6036408..caeb001dd7de 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,6 +1125,23 @@ static void dcn302_resource_destruct(struct resource_pool *pool)
}
}
+ 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 fc7353451e8f..58c314237ce6 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,6 +1069,23 @@ static void dcn303_resource_destruct(struct resource_pool *pool)
}
}
+ 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 5925c40da6ee..15730fe1b9db 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,
@@ -1283,6 +1326,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)
@@ -1368,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 = 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,
@@ -1392,6 +1499,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) {
@@ -1871,6 +1995,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,
@@ -1955,6 +2080,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 16eee0c6f4bc..fbfedfca211f 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,
@@ -1341,6 +1383,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)
@@ -1427,6 +1534,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,
@@ -1451,6 +1559,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) {
@@ -1797,6 +1922,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,
@@ -1883,6 +2009,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 37404aa0edd9..0f800f586e08 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,
@@ -1284,6 +1327,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)
@@ -1369,6 +1476,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,
@@ -1393,6 +1501,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) {
@@ -1867,6 +1992,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,
@@ -1930,6 +2056,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 d946663f416a..efbc2a506046 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,
};
@@ -1277,6 +1320,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,
@@ -1364,6 +1471,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,
@@ -1388,6 +1496,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) {
@@ -1743,6 +1866,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,
@@ -1806,6 +1930,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 1bdb4ffd2921..e03dc966e121 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,
@@ -1279,6 +1316,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)
@@ -1380,6 +1490,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,
@@ -1404,6 +1515,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) {
@@ -2139,6 +2267,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,
@@ -2279,6 +2408,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 91be493e0bb6..68e7140f1505 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 296558258672..65f445f38cfb 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,
@@ -1255,6 +1291,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)
@@ -1356,6 +1465,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,
@@ -1380,6 +1490,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) {
@@ -1635,6 +1762,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,
@@ -1772,6 +1900,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;
@@ -1814,6 +1944,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 c7fba9f39e19..ec92e8f7d173 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*/
@@ -1337,6 +1373,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)
@@ -1438,6 +1540,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,
@@ -1462,6 +1565,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) {
@@ -1826,6 +1944,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,
@@ -1912,6 +2031,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 e9fc43abf342..4d2d26d64a56 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*/
@@ -1317,6 +1353,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)
@@ -1418,6 +1520,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,
@@ -1442,6 +1545,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) {
@@ -1799,6 +1917,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,
@@ -1885,6 +2004,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 eb597be989a6..4bab31fa2b96 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*/
@@ -1324,6 +1360,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)
@@ -1425,6 +1527,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,
@@ -1449,6 +1552,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) {
@@ -1800,6 +1918,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,
@@ -1882,6 +2001,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 9498cbff2449..3fa82c4b7f92 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,
@@ -1248,6 +1284,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)
@@ -1375,6 +1476,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,
@@ -1405,6 +1507,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) {
@@ -1833,6 +1950,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,
@@ -1970,6 +2088,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;
@@ -2015,6 +2134,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 52a1996a654f..39a9724b9478 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,
@@ -1260,6 +1295,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)
@@ -1365,6 +1468,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,
@@ -1395,6 +1499,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) {
@@ -1791,6 +1910,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,
@@ -1909,6 +2029,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;
--
2.54.0
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH v5 08/13] drm/amd/display Add DC link support for FRL
2026-05-12 15:52 [PATCH v5 00/13] HDMI FRL and DSC Support for amdgpu Harry Wentland
` (6 preceding siblings ...)
2026-05-12 15:52 ` [PATCH v5 07/13] drm/amd/display: Add DC resource support for FRL Harry Wentland
@ 2026-05-12 15:52 ` Harry Wentland
2026-05-12 15:52 ` [PATCH v5 09/13] drm/amd/display: Add support for FRL to DC core Harry Wentland
` (4 subsequent siblings)
12 siblings, 0 replies; 15+ messages in thread
From: Harry Wentland @ 2026-05-12 15:52 UTC (permalink / raw)
To: amd-gfx; +Cc: Jerry.Zuo, Harry Wentland
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 <harry.wentland@amd.com>
Reviewed-by: Fangzhi Zuo <Jerry.Zuo@amd.com>
---
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 | 185 ++-
.../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 | 1149 +++++++++++++++++
.../display/dc/link/protocols/link_hdmi_frl.h | 53 +
.../amd/display/dc/link/protocols/link_hpd.c | 1 +
14 files changed, 2096 insertions(+), 2 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 0f3670e30232..f558a20eceaa 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 000000000000..a2c0fa21ee94
--- /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 000000000000..ea8d9760132f
--- /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 1ac6a22fecfe..a3212fd151d1 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 d52155e86f0f..9096b34fc35f 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
@@ -550,14 +555,21 @@ static void update_psp_stream_config(struct pipe_ctx *pipe_ctx, bool dpms_off)
if (dp_is_128b_132b_signal(pipe_ctx))
config.stream_enc_idx =
(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 = (uint8_t)(link_enc->transmitter - TRANSMITTER_UNIPHY_A);
if (dp_is_128b_132b_signal(pipe_ctx))
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 != 0));
+ 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)
{
@@ -2076,6 +2234,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;
@@ -2179,6 +2345,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;
@@ -2191,6 +2359,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);
@@ -2293,6 +2463,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;
@@ -2307,11 +2479,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(
@@ -2321,9 +2495,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);
@@ -2414,8 +2593,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);
@@ -2435,6 +2615,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 bd6fc63064a3..e8662147dd8e 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 2cc646425207..36c0df83aa5b 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 ec083e8b75b4..d7bd352880b3 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)
@@ -331,6 +521,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,
@@ -345,6 +615,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;
@@ -362,6 +641,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 595774e76453..ccea329c556c 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 70f854cca029..a596f1b02ec5 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"
@@ -546,6 +547,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));
@@ -596,3 +599,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 d3e6f01a6a90..f2a80e12494b 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 000000000000..9087f786d6c9
--- /dev/null
+++ b/drivers/gpu/drm/amd/display/dc/link/protocols/link_hdmi_frl.c
@@ -0,0 +1,1149 @@
+/*
+ * 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;
+ }
+ }
+}
+
+static void update_borrow_mode_from_dsc_padding(struct dsc_padding_params *dsc_padding_params,
+ struct dc_crtc_timing *timing,
+ struct dc_hdmi_frl_link_settings *frl_link_settings)
+{
+#ifdef CONFIG_DRM_AMD_DC_FP
+ uint32_t h_active = timing->h_addressable + timing->h_border_left + timing->h_border_right;
+ uint32_t h_blank = timing->h_total - h_active;
+ struct frl_borrow_params *borrow_params = &frl_link_settings->borrow_params;
+
+ borrow_params->borrow_mode = frl_modify_borrow_mode_for_dsc_padding(timing->pix_clk_100hz,
+ h_active,
+ h_active + dsc_padding_params->dsc_hactive_padding,
+ h_blank,
+ h_blank + dsc_padding_params->dsc_htotal_padding,
+ borrow_params->hc_active_target,
+ borrow_params->hc_blank_target,
+ frl_link_settings->frl_num_lanes,
+ frl_link_settings->frl_link_rate);
+#endif
+}
+
+void hdmi_frl_decide_link_settings(struct dc_stream_state *stream,
+ struct dc_hdmi_frl_link_settings *frl_link_settings,
+ struct dsc_padding_params *dsc_padding_params)
+{
+ bool success = false;
+ struct dc_hdmi_frl_link_settings temp_settings = {0};
+
+ temp_settings.frl_link_rate = HDMI_FRL_LINK_RATE_3GBPS;
+ temp_settings.frl_num_lanes = 3;
+
+ /* Verify FRL and fill in borrow_params to verified_link_cap*/
+ frl_validate_mode_timing(
+ stream->link,
+ &stream->timing,
+ &stream->link->frl_verified_link_cap);
+
+ if (stream->link->frl_flags.force_frl_rate != 0 &&
+ stream->link->frl_flags.force_frl_rate < stream->link->frl_verified_link_cap.frl_link_rate) {
+ switch (stream->link->frl_flags.force_frl_rate) {
+ case HDMI_FRL_LINK_RATE_3GBPS:
+ temp_settings.frl_link_rate = HDMI_FRL_LINK_RATE_3GBPS;
+ temp_settings.frl_num_lanes = 3;
+ break;
+ case HDMI_FRL_LINK_RATE_6GBPS:
+ temp_settings.frl_link_rate = HDMI_FRL_LINK_RATE_6GBPS;
+ temp_settings.frl_num_lanes = 3;
+ break;
+ case HDMI_FRL_LINK_RATE_6GBPS_4LANE:
+ temp_settings.frl_link_rate = HDMI_FRL_LINK_RATE_6GBPS;
+ temp_settings.frl_num_lanes = 4;
+ break;
+ case HDMI_FRL_LINK_RATE_8GBPS:
+ temp_settings.frl_link_rate = HDMI_FRL_LINK_RATE_8GBPS;
+ temp_settings.frl_num_lanes = 4;
+ break;
+ case HDMI_FRL_LINK_RATE_10GBPS:
+ temp_settings.frl_link_rate = HDMI_FRL_LINK_RATE_10GBPS;
+ temp_settings.frl_num_lanes = 4;
+ break;
+ case HDMI_FRL_LINK_RATE_12GBPS:
+ temp_settings.frl_link_rate = HDMI_FRL_LINK_RATE_12GBPS;
+ temp_settings.frl_num_lanes = 4;
+ break;
+ case HDMI_FRL_LINK_RATE_16GBPS:
+ temp_settings.frl_link_rate = HDMI_FRL_LINK_RATE_16GBPS;
+ temp_settings.frl_num_lanes = 4;
+ break;
+ case HDMI_FRL_LINK_RATE_20GBPS:
+ temp_settings.frl_link_rate = HDMI_FRL_LINK_RATE_20GBPS;
+ temp_settings.frl_num_lanes = 4;
+ break;
+ case HDMI_FRL_LINK_RATE_24GBPS:
+ temp_settings.frl_link_rate = HDMI_FRL_LINK_RATE_24GBPS;
+ temp_settings.frl_num_lanes = 4;
+ break;
+ default:
+ break;
+ }
+ *frl_link_settings = temp_settings;
+ return;
+ }
+ /*test equipment requires max rate, identified at FRL_Max = 1*/
+ if (stream->link->frl_flags.force_frl_max) {
+ *frl_link_settings = stream->link->frl_verified_link_cap;
+ return;
+ }
+
+ if (stream->link->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 000000000000..abeec56c6528
--- /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 b157d05b67ad..cfb5ef0523a7 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;
--
2.54.0
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH v5 09/13] drm/amd/display: Add support for FRL to DC core
2026-05-12 15:52 [PATCH v5 00/13] HDMI FRL and DSC Support for amdgpu Harry Wentland
` (7 preceding siblings ...)
2026-05-12 15:52 ` [PATCH v5 08/13] drm/amd/display Add DC link " Harry Wentland
@ 2026-05-12 15:52 ` Harry Wentland
2026-05-12 15:52 ` [PATCH v5 10/13] drm/amd/display: Update HDCP and info_packet modules for FRL Harry Wentland
` (3 subsequent siblings)
12 siblings, 0 replies; 15+ messages in thread
From: Harry Wentland @ 2026-05-12 15:52 UTC (permalink / raw)
To: amd-gfx; +Cc: Jerry.Zuo, Harry Wentland
Here we add support for reading BIOS caps and tie FRL bits
into the rest of DC core.
Signed-off-by: Harry Wentland <harry.wentland@amd.com>
Reviewed-by: Fangzhi Zuo <Jerry.Zuo@amd.com>
---
.../drm/amd/display/dc/bios/bios_parser2.c | 21 ++
.../drm/amd/display/dc/bios/command_table2.c | 6 +
.../dce112/command_table_helper2_dce112.c | 3 +
.../bios/dce112/command_table_helper_dce112.c | 3 +
drivers/gpu/drm/amd/display/dc/core/dc.c | 26 +-
.../gpu/drm/amd/display/dc/core/dc_debug.c | 4 +
.../drm/amd/display/dc/core/dc_hw_sequencer.c | 36 +++
.../drm/amd/display/dc/core/dc_link_enc_cfg.c | 3 +
.../drm/amd/display/dc/core/dc_link_exports.c | 45 ++++
.../gpu/drm/amd/display/dc/core/dc_resource.c | 233 ++++++++++++++++++
.../gpu/drm/amd/display/dc/core/dc_stream.c | 35 +++
.../gpu/drm/amd/display/dc/dce/dce_audio.c | 8 +
.../drm/amd/display/dc/dce/dce_clock_source.c | 30 ++-
.../gpu/drm/amd/display/dmub/inc/dmub_cmd.h | 5 +-
14 files changed, 450 insertions(+), 8 deletions(-)
diff --git a/drivers/gpu/drm/amd/display/dc/bios/bios_parser2.c b/drivers/gpu/drm/amd/display/dc/bios/bios_parser2.c
index b4dd8219b8f0..135556b8fd87 100644
--- a/drivers/gpu/drm/amd/display/dc/bios/bios_parser2.c
+++ b/drivers/gpu/drm/amd/display/dc/bios/bios_parser2.c
@@ -2126,6 +2126,12 @@ static enum bp_result get_firmware_info_v3_5(
return BP_RESULT_OK;
}
+/* TODO: Remove this temp define after atomfirmware.h is updated */
+#define ATOM_ENCODER_CAP_RECORD_HDMI_FRL_TEMP 0x200
+#define ATOM_ENCODER_CAP_RECORD_HDMI_FRL_8GbEn_TEMP 0x400 // HDMI FRL 8Gb support
+#define ATOM_ENCODER_CAP_RECORD_HDMI_FRL_10GbEn_TEMP 0x800 // HDMI FRL 10Gb support
+#define ATOM_ENCODER_CAP_RECORD_HDMI_FRL_12GbEn_TEMP 0x1000 // HDMI FRL 12Gb support
+
static enum bp_result bios_parser_get_encoder_cap_info(
struct dc_bios *dcb,
struct graphics_object_id object_id,
@@ -2173,6 +2179,15 @@ static enum bp_result bios_parser_get_encoder_cap_info(
info->DP_IS_USB_C = (record->encodercaps &
ATOM_ENCODER_CAP_RECORD_USB_C_TYPE) ? 1 : 0;
DC_LOG_BIOS("\t info->DP_IS_USB_C %d", info->DP_IS_USB_C);
+ info->IS_HDMI_FRL_CAPABLE = (record->encodercaps &
+ ATOM_ENCODER_CAP_RECORD_HDMI_FRL_TEMP) ? 1 : 0;
+ info->FRL_8G_EN = (record->encodercaps &
+ ATOM_ENCODER_CAP_RECORD_HDMI_FRL_8GbEn_TEMP) ? 1 : 0;
+ info->FRL_10G_EN = (record->encodercaps &
+ ATOM_ENCODER_CAP_RECORD_HDMI_FRL_10GbEn_TEMP) ? 1 : 0;
+ info->FRL_12G_EN = (record->encodercaps &
+ ATOM_ENCODER_CAP_RECORD_HDMI_FRL_12GbEn_TEMP) ? 1 : 0;
+ DC_LOG_BIOS("\t info->IS_HDMI_FRL_CAPABLE %d\n", info->IS_HDMI_FRL_CAPABLE);
return BP_RESULT_OK;
}
@@ -2401,6 +2416,12 @@ static enum bp_result bios_parser_get_connector_speed_cap_info(
info->DP_UHBR10_EN = (record->connector_max_speed >= 10000) ? 1 : 0;
info->DP_UHBR13_5_EN = (record->connector_max_speed >= 13500) ? 1 : 0;
info->DP_UHBR20_EN = (record->connector_max_speed >= 20000) ? 1 : 0;
+ info->FRL_8G_EN = (record->connector_max_speed >= 8000) ? 1 : 0;
+ info->FRL_10G_EN = (record->connector_max_speed >= 10000) ? 1 : 0;
+ info->FRL_12G_EN = (record->connector_max_speed >= 12000) ? 1 : 0;
+ info->FRL_16G_EN = (record->connector_max_speed >= 16000) ? 1 : 0;
+ info->FRL_20G_EN = (record->connector_max_speed >= 20000) ? 1 : 0;
+ info->FRL_24G_EN = (record->connector_max_speed >= 24000) ? 1 : 0;
return BP_RESULT_OK;
}
diff --git a/drivers/gpu/drm/amd/display/dc/bios/command_table2.c b/drivers/gpu/drm/amd/display/dc/bios/command_table2.c
index 88625daf5378..5bca5e534277 100644
--- a/drivers/gpu/drm/amd/display/dc/bios/command_table2.c
+++ b/drivers/gpu/drm/amd/display/dc/bios/command_table2.c
@@ -371,6 +371,10 @@ static enum bp_result transmitter_control_v1_7(
if (cntl->action == TRANSMITTER_CONTROL_ENABLE ||
cntl->action == TRANSMITTER_CONTROL_ACTIAVATE ||
cntl->action == TRANSMITTER_CONTROL_DEACTIVATE) {
+ if (dc_is_hdmi_frl_signal(cntl->signal))
+ DC_LOG_BIOS("%s:dig_v1_7.symclk_units.symclk_Hz = %d\n",
+ __func__, dig_v1_7.symclk_units.symclk_Hz);
+ else
DC_LOG_BIOS("%s:dig_v1_7.symclk_units.symclk_10khz = %d\n",
__func__, dig_v1_7.symclk_units.symclk_10khz);
}
@@ -395,6 +399,8 @@ static enum bp_result transmitter_control_v1_7(
process_phy_transition_init_params.sym_clock_10khz = dig_v1_7.symclk_units.symclk_10khz;
process_phy_transition_init_params.display_port_link_rate = link->cur_link_settings.link_rate;
process_phy_transition_init_params.transition_bitmask = link->phy_transition_bitmask;
+ process_phy_transition_init_params.hdmi_frl_num_lanes = link->frl_link_settings.frl_num_lanes;
+ process_phy_transition_init_params.hdmi_frl_link_rate = link->frl_link_settings.frl_link_rate;
}
dig_v1_7.skip_phy_ssc_reduction = link->wa_flags.skip_phy_ssc_reduction;
}
diff --git a/drivers/gpu/drm/amd/display/dc/bios/dce112/command_table_helper2_dce112.c b/drivers/gpu/drm/amd/display/dc/bios/dce112/command_table_helper2_dce112.c
index 478465fba224..642bc52dcc40 100644
--- a/drivers/gpu/drm/amd/display/dc/bios/dce112/command_table_helper2_dce112.c
+++ b/drivers/gpu/drm/amd/display/dc/bios/dce112/command_table_helper2_dce112.c
@@ -49,6 +49,9 @@ static uint8_t signal_type_to_atom_dig_mode(enum signal_type s)
case SIGNAL_TYPE_HDMI_TYPE_A:
atom_dig_mode = ATOM_TRANSMITTER_DIGMODE_V6_HDMI;
break;
+ case SIGNAL_TYPE_HDMI_FRL:
+ atom_dig_mode = 4;
+ break;
case SIGNAL_TYPE_DISPLAY_PORT_MST:
atom_dig_mode = ATOM_TRANSMITTER_DIGMODE_V6_DP_MST;
break;
diff --git a/drivers/gpu/drm/amd/display/dc/bios/dce112/command_table_helper_dce112.c b/drivers/gpu/drm/amd/display/dc/bios/dce112/command_table_helper_dce112.c
index 6b8a87f2c49e..41d11d8410a0 100644
--- a/drivers/gpu/drm/amd/display/dc/bios/dce112/command_table_helper_dce112.c
+++ b/drivers/gpu/drm/amd/display/dc/bios/dce112/command_table_helper_dce112.c
@@ -47,6 +47,9 @@ static uint8_t signal_type_to_atom_dig_mode(enum signal_type s)
case SIGNAL_TYPE_HDMI_TYPE_A:
atom_dig_mode = ATOM_TRANSMITTER_DIGMODE_V6_HDMI;
break;
+ case SIGNAL_TYPE_HDMI_FRL:
+ atom_dig_mode = 4;
+ break;
case SIGNAL_TYPE_DISPLAY_PORT_MST:
atom_dig_mode = ATOM_TRANSMITTER_DIGMODE_V6_DP_MST;
break;
diff --git a/drivers/gpu/drm/amd/display/dc/core/dc.c b/drivers/gpu/drm/amd/display/dc/core/dc.c
index 48d32adb9eb3..af0e5ca48abb 100644
--- a/drivers/gpu/drm/amd/display/dc/core/dc.c
+++ b/drivers/gpu/drm/amd/display/dc/core/dc.c
@@ -3921,17 +3921,20 @@ static void add_update_info_frame_sequence(
{
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)
+ 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);
- 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) {
@@ -3939,6 +3942,11 @@ static void add_update_info_frame_sequence(
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);
@@ -7473,6 +7481,20 @@ bool dc_capture_register_software_state(struct dc *dc, struct dc_register_softwa
state->dccg.symclk32_le_enable[i] = 0; /* Default: disabled */
}
+ /* Check for active HPO usage that affects symclk32_le */
+ for (unsigned int pipe_idx = 0; pipe_idx < MAX_PIPES && pipe_idx < dc->res_pool->pipe_count; pipe_idx++) {
+ struct pipe_ctx *pipe_ctx = &res_ctx->pipe_ctx[pipe_idx];
+ if (!pipe_ctx->stream)
+ continue;
+
+ /* HPO FRL (HDMI FRL) streams use symclk32_le */
+ if (pipe_ctx->stream_res.hpo_frl_stream_enc && pipe_ctx->link_res.hpo_frl_link_enc) {
+ int hpo_le_inst = pipe_ctx->link_res.hpo_frl_link_enc->inst;
+ if (hpo_le_inst >= 0 && hpo_le_inst < 2) {
+ state->dccg.symclk32_le_enable[hpo_le_inst] = 1;
+ }
+ }
+ }
}
/* Capture essential DSC configuration for underflow analysis */
diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_debug.c b/drivers/gpu/drm/amd/display/dc/core/dc_debug.c
index bbce751b485f..deb7f419e26c 100644
--- a/drivers/gpu/drm/amd/display/dc/core/dc_debug.c
+++ b/drivers/gpu/drm/amd/display/dc/core/dc_debug.c
@@ -250,6 +250,10 @@ char *dc_status_to_str(enum dc_status status)
return "No DSC resource";
case DC_FAIL_UNSUPPORTED_1:
return "Unsupported";
+ case DC_FAIL_HDMI_FRL_LINK_TRAINING:
+ return "HDMI frl link training failure";
+ case DC_NO_HDMI_FRL_LINK_BANDWIDTH:
+ return "No DHMI frl link bandwidth";
case DC_FAIL_CLK_EXCEED_MAX:
return "Clk exceed max failure";
case DC_FAIL_CLK_BELOW_MIN:
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 1916aa3ebaea..97380cda9eb4 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
@@ -1614,6 +1614,9 @@ void hwss_execute_sequence(struct dc *dc,
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;
@@ -3641,6 +3644,15 @@ void hwss_stream_enc_update_hdmi_info_packets(union block_sequence_params *param
¶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 &&
@@ -4864,6 +4876,16 @@ void hwss_add_stream_enc_update_hdmi_info_packets(struct block_sequence_state *s
}
}
+void hwss_add_hpo_frl_stream_enc_update_hdmi_info_packets(struct block_sequence_state *seq_state,
+ struct pipe_ctx *pipe_ctx)
+{
+ if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) {
+ seq_state->steps[*seq_state->num_steps].func = HPO_FRL_STREAM_ENC_UPDATE_HDMI_INFO_PACKETS;
+ seq_state->steps[*seq_state->num_steps].params.hpo_frl_stream_enc_update_hdmi_info_packets_params.pipe_ctx = pipe_ctx;
+ (*seq_state->num_steps)++;
+ }
+}
+
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)
{
@@ -4962,6 +4984,20 @@ void hwss_add_stream_enc_dp_set_dsc_pps_info_packet(struct block_sequence_state
}
}
+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)
+{
+ if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) {
+ seq_state->steps[*seq_state->num_steps].func = HPO_FRL_STREAM_ENC_SET_DSC_CONFIG;
+ seq_state->steps[*seq_state->num_steps].params.hpo_frl_stream_enc_set_dsc_config_params.hpo_frl_stream_enc = hpo_frl_stream_enc;
+ seq_state->steps[*seq_state->num_steps].params.hpo_frl_stream_enc_set_dsc_config_params.timing = timing;
+ seq_state->steps[*seq_state->num_steps].params.hpo_frl_stream_enc_set_dsc_config_params.dsc_packed_pps = dsc_packed_pps;
+ (*seq_state->num_steps)++;
+ }
+}
+
void hwss_add_setup_periodic_interrupt(struct block_sequence_state *seq_state,
struct dc *dc,
struct pipe_ctx *pipe_ctx)
diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_link_enc_cfg.c b/drivers/gpu/drm/amd/display/dc/core/dc_link_enc_cfg.c
index afdf9f8b16d7..d2a5e0648fc2 100644
--- a/drivers/gpu/drm/amd/display/dc/core/dc_link_enc_cfg.c
+++ b/drivers/gpu/drm/amd/display/dc/core/dc_link_enc_cfg.c
@@ -45,6 +45,9 @@ static bool is_dig_link_enc_stream(struct dc_stream_state *stream)
*/
if (link_enc && ((uint32_t)stream->link->connector_signal & link_enc->output_signals)) {
is_dig_stream = true;
+ /* If stream is HDMI FRL, then it is not a DIG stream. */
+ if (dc_is_hdmi_frl_signal(stream->signal))
+ is_dig_stream = false;
break;
}
}
diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_link_exports.c b/drivers/gpu/drm/amd/display/dc/core/dc_link_exports.c
index 5ac5ad86bd01..f25d4b3e11ac 100644
--- a/drivers/gpu/drm/amd/display/dc/core/dc_link_exports.c
+++ b/drivers/gpu/drm/amd/display/dc/core/dc_link_exports.c
@@ -126,6 +126,19 @@ uint32_t dc_link_bandwidth_kbps(
return link->dc->link_srv->dp_link_bandwidth_kbps(link, link_settings);
}
+uint32_t dc_link_frl_bandwidth_kbps(const struct dc_link *link, enum hdmi_frl_link_rate link_rate)
+{
+ return link->dc->link_srv->frl_link_bandwidth_kbps(link_rate);
+}
+
+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)
+{
+ return link->dc->link_srv->frl_margin_check_uncompressed_video(params, inter);
+}
+
uint32_t dc_link_required_hblank_size_bytes(
const struct dc_link *link,
struct dp_audio_bandwidth_params *audio_params)
@@ -144,6 +157,11 @@ void dc_restore_link_res_map(const struct dc *dc, uint32_t *map)
dc->link_srv->restore_res_map(dc, map);
}
+void dc_link_wait_for_unlocked(struct dc_link *link)
+{
+ link->dc->link_srv->wait_for_unlocked(link);
+}
+
bool dc_link_update_dsc_config(struct pipe_ctx *pipe_ctx)
{
struct dc_link *link = pipe_ctx->stream->link;
@@ -345,6 +363,14 @@ enum dc_link_encoding_format dc_link_get_highest_encoding_format(const struct dc
DP_128b_132b_ENCODING)
return DC_LINK_ENCODING_DP_128b_132b;
} else if (dc_is_hdmi_signal(link->connector_signal)) {
+ const struct dc_hdmi_frl_link_settings *frl_link_settings =
+ &link->frl_verified_link_cap;
+
+ if (frl_link_settings->frl_link_rate == HDMI_FRL_LINK_RATE_DISABLE)
+ return DC_LINK_ENCODING_HDMI_TMDS;
+ else if (frl_link_settings->frl_link_rate >= HDMI_FRL_LINK_RATE_3GBPS &&
+ frl_link_settings->frl_link_rate <= HDMI_FRL_LINK_RATE_12GBPS)
+ return DC_LINK_ENCODING_HDMI_FRL;
}
return DC_LINK_ENCODING_UNSPECIFIED;
@@ -518,6 +544,25 @@ bool dc_link_wait_for_t12(struct dc_link *link)
return link->dc->link_srv->edp_wait_for_t12(link);
}
+bool dc_link_frl_poll_status_flag(struct dc_link *link)
+{
+ return link->dc->link_srv->hdmi_frl_poll_status_flag(link);
+}
+
+struct dc_hdmi_frl_link_settings *dc_link_get_frl_link_cap(
+ struct dc_link *link)
+{
+ return link->dc->link_srv->hdmi_frl_get_verified_link_cap(link);
+}
+
+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)
+{
+ link->dc->link_srv->hdmi_frl_set_preferred_link_settings(dc, link_setting, lt_overrides, link);
+}
+
bool dc_link_get_hpd_state(struct dc_link *link)
{
return link->dc->link_srv->get_hpd_state(link);
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 f57e9d85563e..09235ae5055e 100644
--- a/drivers/gpu/drm/amd/display/dc/core/dc_resource.c
+++ b/drivers/gpu/drm/amd/display/dc/core/dc_resource.c
@@ -96,6 +96,8 @@
#define DC_LOGGER \
dc->ctx->logger
#define DC_LOGGER_INIT(logger)
+#include "link/hwss/link_hwss_hpo_frl.h"
+#include "dml/dml1_frl_cap_chk.h"
#include "dml2_0/dml2_wrapper.h"
#define UNABLE_TO_SPLIT -1
@@ -497,6 +499,25 @@ bool resource_construct(
}
}
+ pool->hpo_frl_stream_enc_count = 0;
+ if (create_funcs->create_hpo_frl_stream_encoder) {
+ for (i = 0; i < (unsigned int)caps->num_hpo_frl; i++) {
+ pool->hpo_frl_stream_enc[i] = create_funcs->create_hpo_frl_stream_encoder(i+ENGINE_ID_HPO_0, ctx);
+ if (pool->hpo_frl_stream_enc[i] == NULL)
+ DC_ERR("DC: failed to create HPO FRL stream encoder!\n");
+ pool->hpo_frl_stream_enc_count++;
+
+ }
+ }
+ pool->hpo_frl_link_enc_count = 0;
+ if (create_funcs->create_hpo_frl_link_encoder) {
+ for (i = 0; i < (unsigned int)caps->num_hpo_frl; i++) {
+ pool->hpo_frl_link_enc[i] = create_funcs->create_hpo_frl_link_encoder(i+ENGINE_ID_HPO_0, ctx);
+ if (pool->hpo_frl_link_enc[i] == NULL)
+ DC_ERR("DC: failed to create HPO FRL link encoder!\n");
+ pool->hpo_frl_link_enc_count++;
+ }
+ }
pool->hpo_dp_stream_enc_count = 0;
if (create_funcs->create_hpo_dp_stream_encoder) {
for (i = 0; i < (unsigned int)caps->num_hpo_dp_stream_encoder; i++) {
@@ -2648,6 +2669,164 @@ static void update_stream_engine_usage(
}
}
+static void update_hpo_frl_stream_engine_usage(
+ struct resource_context *res_ctx,
+ const struct resource_pool *pool,
+ struct hpo_frl_stream_encoder *hpo_frl_stream_enc,
+ bool acquired)
+{
+ unsigned int i;
+
+ for (i = 0; i < pool->hpo_frl_stream_enc_count; i++) {
+ if (pool->hpo_frl_stream_enc[i] == hpo_frl_stream_enc)
+ res_ctx->is_hpo_frl_stream_enc_acquired[i] = acquired;
+ }
+}
+
+static struct hpo_frl_stream_encoder *find_first_free_match_hpo_frl_stream_enc_for_link(
+ struct resource_context *res_ctx,
+ const struct resource_pool *pool,
+ struct dc_stream_state *stream)
+{
+ (void)stream;
+ unsigned int i;
+
+ for (i = 0; i < pool->hpo_frl_stream_enc_count; i++) {
+ if (!res_ctx->is_hpo_frl_stream_enc_acquired[i] &&
+ pool->hpo_frl_stream_enc[i]) {
+
+ return pool->hpo_frl_stream_enc[i];
+ }
+ }
+
+ return NULL;
+}
+
+static inline int find_acquired_hpo_frl_link_enc_for_link(
+ const struct resource_context *res_ctx,
+ const struct dc_link *link)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(res_ctx->hpo_frl_link_enc_to_link_idx); i++)
+ if (res_ctx->hpo_frl_link_enc_ref_cnts[i] > 0 &&
+ res_ctx->hpo_frl_link_enc_to_link_idx[i] == link->link_index)
+ return i;
+
+ return -1;
+}
+
+static inline int find_free_hpo_frl_link_enc(const struct resource_context *res_ctx,
+ const struct resource_pool *pool)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(res_ctx->hpo_frl_link_enc_ref_cnts); i++)
+ if (res_ctx->hpo_frl_link_enc_ref_cnts[i] == 0)
+ break;
+
+ return (i < ARRAY_SIZE(res_ctx->hpo_frl_link_enc_ref_cnts) &&
+ i < pool->hpo_frl_link_enc_count) ? (int)i : -1;
+}
+
+static inline void acquire_hpo_frl_link_enc(
+ struct resource_context *res_ctx,
+ unsigned int link_index,
+ int enc_index)
+{
+ res_ctx->hpo_frl_link_enc_to_link_idx[enc_index] = link_index;
+ res_ctx->hpo_frl_link_enc_ref_cnts[enc_index] = 1;
+}
+
+static inline void retain_hpo_frl_link_enc(
+ struct resource_context *res_ctx,
+ int enc_index)
+{
+ res_ctx->hpo_frl_link_enc_ref_cnts[enc_index]++;
+}
+
+static inline void release_hpo_frl_link_enc(
+ struct resource_context *res_ctx,
+ int enc_index)
+{
+ ASSERT(res_ctx->hpo_frl_link_enc_ref_cnts[enc_index] > 0);
+ res_ctx->hpo_frl_link_enc_ref_cnts[enc_index]--;
+}
+
+static bool add_hpo_frl_link_enc_to_ctx(struct resource_context *res_ctx,
+ const struct resource_pool *pool,
+ struct pipe_ctx *pipe_ctx,
+ struct dc_stream_state *stream)
+{
+ int enc_index;
+
+ enc_index = find_acquired_hpo_frl_link_enc_for_link(res_ctx, stream->link);
+
+ if (enc_index >= 0) {
+ retain_hpo_frl_link_enc(res_ctx, enc_index);
+ } else {
+ enc_index = find_free_hpo_frl_link_enc(res_ctx, pool);
+ if (enc_index >= 0)
+ acquire_hpo_frl_link_enc(res_ctx, stream->link->link_index, enc_index);
+ }
+
+ if (enc_index >= 0)
+ pipe_ctx->link_res.hpo_frl_link_enc = pool->hpo_frl_link_enc[enc_index];
+
+ return pipe_ctx->link_res.hpo_frl_link_enc != NULL;
+}
+
+static void remove_hpo_frl_link_enc_from_ctx(struct resource_context *res_ctx,
+ struct pipe_ctx *pipe_ctx,
+ struct dc_stream_state *stream)
+{
+ int enc_index;
+
+ enc_index = find_acquired_hpo_frl_link_enc_for_link(res_ctx, stream->link);
+
+ if (enc_index >= 0) {
+ release_hpo_frl_link_enc(res_ctx, enc_index);
+ pipe_ctx->link_res.hpo_frl_link_enc = NULL;
+ }
+}
+
+static struct hpo_frl_link_encoder *get_temp_hpo_frl_link_enc(
+ const struct resource_context *res_ctx,
+ const struct resource_pool *const pool,
+ const struct dc_link *link)
+{
+ struct hpo_frl_link_encoder *hpo_frl_link_enc = NULL;
+ int enc_index;
+
+ enc_index = find_acquired_hpo_frl_link_enc_for_link(res_ctx, link);
+
+ if (enc_index < 0)
+ enc_index = find_free_hpo_frl_link_enc(res_ctx, pool);
+
+ if (enc_index >= 0)
+ hpo_frl_link_enc = pool->hpo_frl_link_enc[enc_index];
+
+ return hpo_frl_link_enc;
+}
+
+bool get_temp_frl_link_res(struct dc_link *link,
+ struct link_resource *link_res)
+{
+ const struct dc *dc = link->dc;
+ const struct resource_context *res_ctx = &dc->current_state->res_ctx;
+
+ memset(link_res, 0, sizeof(*link_res));
+ link_res->hpo_frl_link_enc = get_temp_hpo_frl_link_enc(res_ctx, dc->res_pool, link);
+ if (!link_res->hpo_frl_link_enc)
+ return false;
+
+ link_res->dio_link_enc = get_temp_dio_link_enc(res_ctx,
+ dc->res_pool, link);
+ if (!link_res->dio_link_enc)
+ return false;
+
+ return true;
+}
static void update_hpo_dp_stream_engine_usage(
struct resource_context *res_ctx,
const struct resource_pool *pool,
@@ -2970,6 +3149,15 @@ void resource_remove_otg_master_for_stream_output(struct dc_state *context,
otg_master->stream_res.stream_enc,
false);
+ if (dc_is_hdmi_frl_signal(stream->signal)) {
+ update_hpo_frl_stream_engine_usage(
+ &context->res_ctx, pool,
+ otg_master->stream_res.hpo_frl_stream_enc,
+ false);
+ remove_hpo_frl_link_enc_from_ctx(
+ &context->res_ctx, otg_master, stream);
+ remove_dio_link_enc_from_ctx(&context->res_ctx, otg_master, stream);
+ }
if (stream->ctx->dc->link_srv->dp_is_128b_132b_signal(otg_master)) {
update_hpo_dp_stream_engine_usage(
&context->res_ctx, pool,
@@ -4017,6 +4205,33 @@ enum dc_status resource_map_pool_resources(
pipe_ctx->stream_res.stream_enc,
true);
+ if (dc_is_hdmi_frl_signal(stream->signal)) {
+ is_dio_encoder = false;
+ pipe_ctx->stream_res.hpo_frl_stream_enc =
+ find_first_free_match_hpo_frl_stream_enc_for_link(
+ &context->res_ctx, pool, stream);
+
+ if (!pipe_ctx->stream_res.hpo_frl_stream_enc)
+ if (stream->timing.pix_clk_100hz < 6000000)
+ stream->signal = SIGNAL_TYPE_HDMI_TYPE_A;
+ else
+ return DC_NO_STREAM_ENC_RESOURCE;
+ else {
+ update_hpo_frl_stream_engine_usage(
+ &context->res_ctx, pool,
+ pipe_ctx->stream_res.hpo_frl_stream_enc,
+ true);
+ pipe_ctx->link_res.hpo_frl_link_enc =
+ pipe_ctx->stream->link->hpo_frl_link_enc;
+ if (!pipe_ctx->link_res.hpo_frl_link_enc) {
+ if (!add_hpo_frl_link_enc_to_ctx(&context->res_ctx, pool, pipe_ctx, stream))
+ return DC_NO_LINK_ENC_RESOURCE;
+ }
+ if (!add_dio_link_enc_to_ctx(dc, context, pool, pipe_ctx, stream))
+ return DC_NO_LINK_ENC_RESOURCE;
+ }
+ }
+
/* Allocate DP HPO Stream Encoder based on signal, hw capabilities
* and link settings
*/
@@ -4082,6 +4297,8 @@ enum dc_status resource_map_pool_resources(
if (context->streams[i] == stream) {
context->stream_status[i].primary_otg_inst = pipe_ctx->stream_res.tg->inst;
context->stream_status[i].stream_enc_inst = pipe_ctx->stream_res.stream_enc->stream_enc_inst;
+ if (pipe_ctx->stream_res.hpo_frl_stream_enc != NULL)
+ context->stream_status[i].stream_enc_inst = pipe_ctx->stream_res.hpo_frl_stream_enc->stream_enc_inst;
context->stream_status[i].audio_inst =
pipe_ctx->stream_res.audio ? pipe_ctx->stream_res.audio->inst : -1;
@@ -4926,6 +5143,9 @@ void resource_build_info_frame(struct pipe_ctx *pipe_ctx)
set_hdr_static_info_packet(&info->hdrsmd, pipe_ctx->stream);
+ if (dc_is_hdmi_frl_signal(signal)) {
+ /* TODO: additional packets for HDMI 2.1 */
+ }
} else if (dc_is_dp_signal(signal)) {
set_vsc_info_packet(&info->vsc, pipe_ctx->stream);
@@ -5023,6 +5243,8 @@ bool pipe_need_reprogram(
if (pipe_ctx_old->stream_res.dsc != pipe_ctx->stream_res.dsc)
return true;
+ if (pipe_ctx_old->stream_res.hpo_frl_stream_enc != pipe_ctx->stream_res.hpo_frl_stream_enc)
+ return true;
if (pipe_ctx_old->stream_res.hpo_dp_stream_enc != pipe_ctx->stream_res.hpo_dp_stream_enc)
return true;
if (pipe_ctx_old->link_res.hpo_dp_link_enc != pipe_ctx->link_res.hpo_dp_link_enc)
@@ -5291,10 +5513,13 @@ void get_audio_check(struct audio_info *aud_modes,
audio_chk->audio_packet_type = 0x2;/*audio sample packet AP = .25 for layout0, 1 for layout1*/
audio_chk->max_audiosample_rate = 0;
+ audio_chk->max_channel_count = 0;
for (i = 0; i < aud_modes->mode_count; i++) {
max_sample_rate = get_max_audio_sample_rate(&aud_modes->modes[i]);
if (audio_chk->max_audiosample_rate < max_sample_rate)
audio_chk->max_audiosample_rate = max_sample_rate;
+ if (audio_chk->max_channel_count < aud_modes->modes[i].channel_count)
+ audio_chk->max_channel_count = aud_modes->modes[i].channel_count;
/*dts takes the same as type 2: AP = 0.25*/
}
/*check which one take more bandwidth*/
@@ -5506,6 +5731,8 @@ const struct link_hwss *get_link_hwss(const struct dc_link *link,
*/
return (requires_fixed_vs_pe_retimer_hpo_link_hwss(link) ?
get_hpo_fixed_vs_pe_retimer_dp_link_hwss() : get_hpo_dp_link_hwss());
+ else if (can_use_hpo_frl_link_hwss(link, link_res))
+ return get_hpo_frl_link_hwss();
else if (can_use_dpia_link_hwss(link, link_res))
return get_dpia_link_hwss();
else if (can_use_dio_link_hwss(link, link_res))
@@ -5733,5 +5960,11 @@ bool resource_is_hpo_acquired(struct dc_state *context)
}
}
+ for (i = 0; i < MAX_HDMI_FRL_ENCODERS; i++) {
+ if (context->res_ctx.is_hpo_frl_stream_enc_acquired[i]) {
+ return true;
+ }
+ }
+
return false;
}
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 d4c32c945606..42b2a88d2d52 100644
--- a/drivers/gpu/drm/amd/display/dc/core/dc_stream.c
+++ b/drivers/gpu/drm/amd/display/dc/core/dc_stream.c
@@ -54,6 +54,7 @@
******************************************************************************/
void update_stream_signal(struct dc_stream_state *stream, struct dc_sink *sink)
{
+ unsigned int pix_clk;
if (sink->sink_signal == SIGNAL_TYPE_NONE)
stream->signal = stream->link->connector_signal;
else
@@ -67,6 +68,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->signal = SIGNAL_TYPE_HDMI_FRL;
+ }
}
bool dc_stream_construct(struct dc_stream_state *stream,
diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_audio.c b/drivers/gpu/drm/amd/display/dc/dce/dce_audio.c
index 8af8e2c17134..239ba2b352a1 100644
--- a/drivers/gpu/drm/amd/display/dc/dce/dce_audio.c
+++ b/drivers/gpu/drm/amd/display/dc/dce/dce_audio.c
@@ -535,6 +535,7 @@ static void check_audio_bandwidth(
{
switch (signal) {
case SIGNAL_TYPE_HDMI_TYPE_A:
+ case SIGNAL_TYPE_HDMI_FRL:
check_audio_bandwidth_hdmi(
crtc_info, channel_count, sample_rates);
break;
@@ -738,6 +739,7 @@ void dce_aud_az_configure(
/* set audio for output signal */
switch (signal) {
case SIGNAL_TYPE_HDMI_TYPE_A:
+ case SIGNAL_TYPE_HDMI_FRL:
set_reg_field_value(value,
1,
AZALIA_F0_CODEC_PIN_CONTROL_CHANNEL_SPEAKER,
@@ -798,6 +800,12 @@ void dce_aud_az_configure(
/* adjust specific properties */
switch (audio_format_code) {
case AUDIO_FORMAT_CODE_LINEARPCM: {
+ if (signal == SIGNAL_TYPE_HDMI_FRL
+ && channel_count > 2
+ && crtc_info != NULL
+ && crtc_info->v_active <= 576) {
+ channel_count = 2;
+ }
check_audio_bandwidth(
crtc_info,
diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_clock_source.c b/drivers/gpu/drm/amd/display/dc/dce/dce_clock_source.c
index b97b4cd23eaa..7a0caace1604 100644
--- a/drivers/gpu/drm/amd/display/dc/dce/dce_clock_source.c
+++ b/drivers/gpu/drm/amd/display/dc/dce/dce_clock_source.c
@@ -981,7 +981,9 @@ static bool dcn31_program_pix_clk(
dp_dto_ref_khz = clock_source->ctx->dc->clk_mgr->dp_dto_source_clock_in_khz;
// For these signal types Driver to program DP_DTO without calling VBIOS Command table
- if (dc_is_dp_signal(pix_clk_params->signal_type) || dc_is_virtual_signal(pix_clk_params->signal_type)) {
+ if (dc_is_hdmi_frl_signal(pix_clk_params->signal_type) ||
+ dc_is_virtual_signal(pix_clk_params->signal_type) ||
+ dc_is_dp_signal(pix_clk_params->signal_type)) {
if (e) {
/* Set DTO values: phase = target clock, modulo = reference clock*/
REG_WRITE(PHASE[inst], e->target_pixel_rate_khz * e->mult_factor);
@@ -997,6 +999,10 @@ static bool dcn31_program_pix_clk(
REG_UPDATE_2(PIXEL_RATE_CNTL[inst],
DP_DTO0_ENABLE, 1,
PIPE0_DTO_SRC_SEL, 2);
+ else if (dc_is_hdmi_frl_signal(pix_clk_params->signal_type) || encoding == DP_128b_132b_ENCODING)
+ REG_UPDATE_2(PIXEL_RATE_CNTL[inst],
+ DP_DTO0_ENABLE, 0,
+ PIPE0_DTO_SRC_SEL, 2);
else
REG_UPDATE_2(PIXEL_RATE_CNTL[inst],
DP_DTO0_ENABLE, 1,
@@ -1084,8 +1090,14 @@ static bool dcn401_program_pix_clk(
if (!dc_is_tmds_signal(pix_clk_params->signal_type)) {
long long dtbclk_p_src_clk_khz;
- dtbclk_p_src_clk_khz = clock_source->ctx->dc->clk_mgr->dprefclk_khz;
- dto_params.clk_src = DPREFCLK;
+ /* if signal is HDMI FRL dtbclk_p_src is DTBCLK else DPREFCLK */
+ if (dc_is_hdmi_frl_signal(pix_clk_params->signal_type)) {
+ dtbclk_p_src_clk_khz = clock_source->ctx->dc->clk_mgr->funcs->get_dtb_ref_clk_frequency(clock_source->ctx->dc->clk_mgr);
+ dto_params.clk_src = DTBCLK0;
+ } else {
+ dtbclk_p_src_clk_khz = clock_source->ctx->dc->clk_mgr->dprefclk_khz;
+ dto_params.clk_src = DPREFCLK;
+ }
if (e) {
dto_params.pixclk_hz = e->target_pixel_rate_khz;
@@ -1103,7 +1115,15 @@ static bool dcn401_program_pix_clk(
clock_source->ctx->dc->res_pool->dccg->funcs->set_dp_dto(
clock_source->ctx->dc->res_pool->dccg,
&dto_params);
-
+ if (clock_source->ctx->dc->caps.is_apu &&
+ pix_clk_params->requested_pix_clk_100hz &&
+ dc_is_hdmi_frl_signal(pix_clk_params->signal_type)) {
+ /*need hdmistreamclk before vpg block register access*/
+ clock_source->ctx->dc->res_pool->dccg->funcs->set_hdmistreamclk(
+ clock_source->ctx->dc->res_pool->dccg,
+ DTBCLK0,
+ pix_clk_params->controller_id - 1);
+ }
} else {
if (pll_settings->actual_pix_clk_100hz > 6000000UL)
return false;
@@ -1335,7 +1355,7 @@ static bool dcn3_program_pix_clk(
look_up_in_video_optimized_rate_tlb(pix_clk_params->requested_pix_clk_100hz / 10);
// For these signal types Driver to program DP_DTO without calling VBIOS Command table
- if (dc_is_dp_signal(pix_clk_params->signal_type)) {
+ if ((pix_clk_params->signal_type == SIGNAL_TYPE_HDMI_FRL) || dc_is_dp_signal(pix_clk_params->signal_type)) {
if (e) {
/* Set DTO values: phase = target clock, modulo = reference clock*/
REG_WRITE(PHASE[inst], e->target_pixel_rate_khz * e->mult_factor);
diff --git a/drivers/gpu/drm/amd/display/dmub/inc/dmub_cmd.h b/drivers/gpu/drm/amd/display/dmub/inc/dmub_cmd.h
index c1becd664cb9..09b67d120b0a 100644
--- a/drivers/gpu/drm/amd/display/dmub/inc/dmub_cmd.h
+++ b/drivers/gpu/drm/amd/display/dmub/inc/dmub_cmd.h
@@ -2944,16 +2944,19 @@ struct dmub_dig_transmitter_control_data_v1_7 {
union {
uint8_t digmode; /**< enum atom_encode_mode_def */
uint8_t dplaneset; /**< DP voltage swing and pre-emphasis value, "DP_LANE_SET__xDB_y_zV" */
+ uint8_t txffe; /**< TxFFE settings for HDMI 2.1 */
} mode_laneset;
uint8_t lanenum; /**< Number of lanes */
union {
uint32_t symclk_10khz; /**< Symbol Clock in 10Khz */
+ uint32_t symclk_Hz; /**< Symbol clock in Hz for FRL */
} symclk_units;
uint8_t hpdsel; /**< =1: HPD1, =2: HPD2, ..., =6: HPD6, =0: HPD is not assigned */
uint8_t digfe_sel; /**< DIG front-end selection, bit0 means DIG0 FE is enabled */
uint8_t connobj_id; /**< Connector Object Id defined in ObjectId.h */
uint8_t HPO_instance; /**< HPO instance (0: inst0, 1: inst1) */
- uint8_t reserved1; /**< For future use */
+ uint8_t TxFFELaneSel; /**< TxFFE lane select [3:0]
+ (bit0: lane0, bit1: lane1, bit2: lane3, bit3: lane3) */
uint8_t skip_phy_ssc_reduction;
uint8_t reserved2[2]; /**< For future use */
uint32_t reserved3[11]; /**< For future use */
--
2.54.0
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH v5 10/13] drm/amd/display: Update HDCP and info_packet modules for FRL
2026-05-12 15:52 [PATCH v5 00/13] HDMI FRL and DSC Support for amdgpu Harry Wentland
` (8 preceding siblings ...)
2026-05-12 15:52 ` [PATCH v5 09/13] drm/amd/display: Add support for FRL to DC core Harry Wentland
@ 2026-05-12 15:52 ` Harry Wentland
2026-05-12 15:52 ` [PATCH v5 11/13] drm/amd/display: Tie FRL support into amdgpu_dm Harry Wentland
` (2 subsequent siblings)
12 siblings, 0 replies; 15+ messages in thread
From: Harry Wentland @ 2026-05-12 15:52 UTC (permalink / raw)
To: amd-gfx; +Cc: Jerry.Zuo, Harry Wentland
The HDCP module has a minor update for FRL, and info_packet is
updated for ALLM.
Signed-off-by: Harry Wentland <harry.wentland@amd.com>
Reviewed-by: Fangzhi Zuo <Jerry.Zuo@amd.com>
---
.../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 2 +-
.../gpu/drm/amd/display/modules/hdcp/hdcp.c | 2 +
.../gpu/drm/amd/display/modules/hdcp/hdcp.h | 6 +
.../drm/amd/display/modules/hdcp/hdcp_psp.c | 2 +
.../drm/amd/display/modules/hdcp/hdcp_psp.h | 2 +
.../amd/display/modules/inc/mod_freesync.h | 3 +
.../drm/amd/display/modules/inc/mod_hdcp.h | 1 +
.../amd/display/modules/inc/mod_info_packet.h | 2 +-
.../display/modules/info_packet/info_packet.c | 122 ++++++++++++++----
9 files changed, 112 insertions(+), 30 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 c41f017fe8f2..370991b38ab2 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
@@ -7581,7 +7581,7 @@ create_stream_for_sink(struct drm_connector *connector,
update_stream_signal(stream, sink);
if (stream->signal == SIGNAL_TYPE_HDMI_TYPE_A)
- mod_build_hf_vsif_infopacket(stream, &stream->vsp_infopacket);
+ mod_build_hf_vsif_infopacket(stream, &stream->vsp_infopacket, false, false);
if (stream->signal == SIGNAL_TYPE_DISPLAY_PORT ||
stream->signal == SIGNAL_TYPE_DISPLAY_PORT_MST ||
diff --git a/drivers/gpu/drm/amd/display/modules/hdcp/hdcp.c b/drivers/gpu/drm/amd/display/modules/hdcp/hdcp.c
index ca402ddcdacc..fbab100c0e7b 100644
--- a/drivers/gpu/drm/amd/display/modules/hdcp/hdcp.c
+++ b/drivers/gpu/drm/amd/display/modules/hdcp/hdcp.c
@@ -66,6 +66,7 @@ static uint8_t is_cp_desired_hdcp1(struct mod_hdcp *hdcp)
return is_auth_needed &&
!hdcp->connection.link.adjust.hdcp1.disable &&
+ !is_frl_hdcp(hdcp) &&
!hdcp->connection.is_hdcp1_revoked;
}
@@ -584,6 +585,7 @@ enum mod_hdcp_operation_mode mod_hdcp_signal_type_to_operation_mode(
switch (signal) {
case SIGNAL_TYPE_DVI_SINGLE_LINK:
case SIGNAL_TYPE_HDMI_TYPE_A:
+ case SIGNAL_TYPE_HDMI_FRL:
mode = MOD_HDCP_MODE_DEFAULT;
break;
case SIGNAL_TYPE_EDP:
diff --git a/drivers/gpu/drm/amd/display/modules/hdcp/hdcp.h b/drivers/gpu/drm/amd/display/modules/hdcp/hdcp.h
index d07387a961dd..92c128ffe2dd 100644
--- a/drivers/gpu/drm/amd/display/modules/hdcp/hdcp.h
+++ b/drivers/gpu/drm/amd/display/modules/hdcp/hdcp.h
@@ -406,6 +406,12 @@ static inline uint8_t is_hdmi_dvi_sl_hdcp(struct mod_hdcp *hdcp)
return (hdcp->connection.link.mode == MOD_HDCP_MODE_DEFAULT);
}
+static inline uint8_t is_frl_hdcp(struct mod_hdcp *hdcp)
+{
+ return (hdcp->connection.link.mode == MOD_HDCP_MODE_DEFAULT &&
+ hdcp->connection.link.hdmi.frl_enabled);
+}
+
/* hdcp state helpers */
static inline uint8_t current_state(struct mod_hdcp *hdcp)
{
diff --git a/drivers/gpu/drm/amd/display/modules/hdcp/hdcp_psp.c b/drivers/gpu/drm/amd/display/modules/hdcp/hdcp_psp.c
index 6b7db8ec9a53..d32df3b869f9 100644
--- a/drivers/gpu/drm/amd/display/modules/hdcp/hdcp_psp.c
+++ b/drivers/gpu/drm/amd/display/modules/hdcp/hdcp_psp.c
@@ -200,6 +200,8 @@ static enum mod_hdcp_status add_display_to_topology_v3(
TA_DTM_HDCP_VERSION_MAX_SUPPORTED__2_3;
dtm_cmd->dtm_in_message.topology_update_v3.encoder_type = TA_DTM_ENCODER_TYPE__DIG;
dtm_cmd->dtm_status = TA_DTM_STATUS__GENERIC_FAILURE;
+ if (is_frl_hdcp(hdcp))
+ dtm_cmd->dtm_in_message.topology_update_v3.encoder_type = TA_DTM_ENCODER_TYPE__FRL;
dtm_cmd->dtm_in_message.topology_update_v3.phy_id = link->phy_idx;
dtm_cmd->dtm_in_message.topology_update_v3.link_hdcp_cap = link->hdcp_supported_informational;
dtm_cmd->dtm_in_message.topology_update_v3.dio_output_type = link->dp.usb4_enabled ?
diff --git a/drivers/gpu/drm/amd/display/modules/hdcp/hdcp_psp.h b/drivers/gpu/drm/amd/display/modules/hdcp/hdcp_psp.h
index 7844ea91650b..1f9ac8537d53 100644
--- a/drivers/gpu/drm/amd/display/modules/hdcp/hdcp_psp.h
+++ b/drivers/gpu/drm/amd/display/modules/hdcp/hdcp_psp.h
@@ -84,6 +84,7 @@ struct ta_dtm_topology_update_input_v2 {
uint32_t dig_fe;
uint32_t dp_mst_vcid;
uint32_t is_assr;
+ /*uint32_t is_frl;*/ /*todo*/
uint32_t max_hdcp_supported_version;
};
@@ -91,6 +92,7 @@ struct ta_dtm_topology_update_input_v2 {
/* Security code will check real HW register values and these SW enum values */
enum ta_dtm_encoder_type {
TA_DTM_ENCODER_TYPE__INVALID = 0,
+ TA_DTM_ENCODER_TYPE__FRL = 0x20,
TA_DTM_ENCODER_TYPE__DIG = 0x10
};
diff --git a/drivers/gpu/drm/amd/display/modules/inc/mod_freesync.h b/drivers/gpu/drm/amd/display/modules/inc/mod_freesync.h
index 57916ed98c86..c042fb1bf49c 100644
--- a/drivers/gpu/drm/amd/display/modules/inc/mod_freesync.h
+++ b/drivers/gpu/drm/amd/display/modules/inc/mod_freesync.h
@@ -90,6 +90,7 @@ struct mod_vrr_params_flip_interval {
struct mod_vrr_params {
bool supported;
bool send_info_frame;
+ bool m_const_engaged; // this is used when m_const is set up in OPTC so no overriding happens from FreeSync Module
enum mod_vrr_state state;
uint32_t min_refresh_in_uhz;
@@ -98,6 +99,8 @@ struct mod_vrr_params {
uint32_t min_duration_in_us;
uint32_t fixed_refresh_in_uhz;
+ uint32_t m_const;
+
struct dc_crtc_timing_adjust adjust;
struct mod_vrr_params_fixed_refresh fixed;
diff --git a/drivers/gpu/drm/amd/display/modules/inc/mod_hdcp.h b/drivers/gpu/drm/amd/display/modules/inc/mod_hdcp.h
index 835467225458..d492a59e0939 100644
--- a/drivers/gpu/drm/amd/display/modules/inc/mod_hdcp.h
+++ b/drivers/gpu/drm/amd/display/modules/inc/mod_hdcp.h
@@ -114,6 +114,7 @@ struct mod_hdcp_displayport {
};
struct mod_hdcp_hdmi {
+ uint8_t frl_enabled;
uint8_t reserved;
};
enum mod_hdcp_operation_mode {
diff --git a/drivers/gpu/drm/amd/display/modules/inc/mod_info_packet.h b/drivers/gpu/drm/amd/display/modules/inc/mod_info_packet.h
index 11b127eb13d8..eee8206bc531 100644
--- a/drivers/gpu/drm/amd/display/modules/inc/mod_info_packet.h
+++ b/drivers/gpu/drm/amd/display/modules/inc/mod_info_packet.h
@@ -46,7 +46,7 @@ void mod_build_vsc_infopacket(const struct dc_stream_state *stream,
enum color_transfer_func tf);
void mod_build_hf_vsif_infopacket(const struct dc_stream_state *stream,
- struct dc_info_packet *info_packet);
+ struct dc_info_packet *info_packet, int ALLMEnabled, int ALLMValue);
enum adaptive_sync_sdp_version {
AS_SDP_VER_0 = 0x0,
diff --git a/drivers/gpu/drm/amd/display/modules/info_packet/info_packet.c b/drivers/gpu/drm/amd/display/modules/info_packet/info_packet.c
index 55c7250f18d8..fa05547c615a 100644
--- a/drivers/gpu/drm/amd/display/modules/info_packet/info_packet.c
+++ b/drivers/gpu/drm/amd/display/modules/info_packet/info_packet.c
@@ -504,13 +504,16 @@ void mod_build_vsc_infopacket(const struct dc_stream_state *stream,
* @info_packet: output structure where to store VSIF
*/
void mod_build_hf_vsif_infopacket(const struct dc_stream_state *stream,
- struct dc_info_packet *info_packet)
+ struct dc_info_packet *info_packet, int ALLMEnabled, int ALLMValue)
{
unsigned int length = 5;
bool hdmi_vic_mode = false;
uint8_t checksum = 0;
uint32_t i = 0;
enum dc_timing_3d_format format;
+ bool bALLM = (bool)ALLMEnabled;
+ bool bALLMVal = (bool)ALLMValue;
+ int CCBPC = 0;
info_packet->valid = false;
format = stream->timing.timing_3d_format;
@@ -523,47 +526,110 @@ void mod_build_hf_vsif_infopacket(const struct dc_stream_state *stream,
&& format == TIMING_3D_FORMAT_NONE)
hdmi_vic_mode = true;
- if ((format == TIMING_3D_FORMAT_NONE) && !hdmi_vic_mode)
+ if ((format == TIMING_3D_FORMAT_NONE) && !hdmi_vic_mode && !bALLM)
return;
- info_packet->sb[1] = 0x03;
- info_packet->sb[2] = 0x0C;
- info_packet->sb[3] = 0x00;
+ if (!bALLM) {
+ info_packet->sb[1] = 0x03;
+ info_packet->sb[2] = 0x0C;
+ info_packet->sb[3] = 0x00;
- if (format != TIMING_3D_FORMAT_NONE)
- info_packet->sb[4] = (2 << 5);
+ if (format != TIMING_3D_FORMAT_NONE)
+ info_packet->sb[4] = (2 << 5);
+ else if (hdmi_vic_mode)
+ info_packet->sb[4] = (1 << 5);
- else if (hdmi_vic_mode)
- info_packet->sb[4] = (1 << 5);
+ switch (format) {
+ case TIMING_3D_FORMAT_HW_FRAME_PACKING:
+ case TIMING_3D_FORMAT_SW_FRAME_PACKING:
+ info_packet->sb[5] = (0x0 << 4);
+ break;
- switch (format) {
- case TIMING_3D_FORMAT_HW_FRAME_PACKING:
- case TIMING_3D_FORMAT_SW_FRAME_PACKING:
- info_packet->sb[5] = (0x0 << 4);
- break;
+ case TIMING_3D_FORMAT_SIDE_BY_SIDE:
+ case TIMING_3D_FORMAT_SBS_SW_PACKED:
+ info_packet->sb[5] = (0x8 << 4);
+ length = 6;
+ break;
- case TIMING_3D_FORMAT_SIDE_BY_SIDE:
- case TIMING_3D_FORMAT_SBS_SW_PACKED:
- info_packet->sb[5] = (0x8 << 4);
- length = 6;
- break;
+ case TIMING_3D_FORMAT_TOP_AND_BOTTOM:
+ case TIMING_3D_FORMAT_TB_SW_PACKED:
+ info_packet->sb[5] = (0x6 << 4);
+ break;
- case TIMING_3D_FORMAT_TOP_AND_BOTTOM:
- case TIMING_3D_FORMAT_TB_SW_PACKED:
- info_packet->sb[5] = (0x6 << 4);
- break;
+ default:
+ break;
+ }
- default:
- break;
+ if (hdmi_vic_mode) {
+ ASSERT(stream->timing.hdmi_vic <= 0xFF);
+ info_packet->sb[5] = (uint8_t)stream->timing.hdmi_vic;
+ }
+ } else {
+ info_packet->sb[1] = 0xD8;
+ info_packet->sb[2] = 0x5D;
+ info_packet->sb[3] = 0xC4;
+ info_packet->sb[4] = HF_VSIF_VERSION;
+
+ if (format != TIMING_3D_FORMAT_NONE) {
+ info_packet->sb[5] |= 0x01;
+ length = 6;
+ switch (format) {
+ case TIMING_3D_FORMAT_HW_FRAME_PACKING:
+ case TIMING_3D_FORMAT_SW_FRAME_PACKING:
+ info_packet->sb[6] = (0x0 << 4);
+ break;
+
+ case TIMING_3D_FORMAT_SIDE_BY_SIDE:
+ case TIMING_3D_FORMAT_SBS_SW_PACKED:
+ info_packet->sb[6] = (0x8 << 4);
+ break;
+
+ case TIMING_3D_FORMAT_TOP_AND_BOTTOM:
+ case TIMING_3D_FORMAT_TB_SW_PACKED:
+ info_packet->sb[6] = (0x6 << 4);
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ info_packet->sb[5] = (info_packet->sb[5] & ~0x02) | (bALLMVal << 1);
+
+ switch (stream->timing.display_color_depth) {
+ case COLOR_DEPTH_888:
+ CCBPC = 1;
+ break;
+ case COLOR_DEPTH_101010:
+ CCBPC = 3;
+ break;
+ case COLOR_DEPTH_121212:
+ CCBPC = 5;
+ break;
+ case COLOR_DEPTH_161616:
+ CCBPC = 9;
+ break;
+
+ case COLOR_DEPTH_UNDEFINED:
+ case COLOR_DEPTH_666:
+#ifdef CONFIG_DRM_AMD_DC_DCN2_0
+ case COLOR_DEPTH_999:
+ case COLOR_DEPTH_111111:
+#endif
+ case COLOR_DEPTH_141414:
+ default:
+ break;
+ }
+
+ info_packet->sb[5] = (uint8_t)((info_packet->sb[5] & ~0xF0) | (CCBPC << 4));
}
- if (hdmi_vic_mode)
- info_packet->sb[5] = stream->timing.hdmi_vic;
-
info_packet->hb0 = HDMI_INFOFRAME_TYPE_VENDOR;
info_packet->hb1 = 0x01;
info_packet->hb2 = (uint8_t) (length);
+
+
checksum += info_packet->hb0;
checksum += info_packet->hb1;
checksum += info_packet->hb2;
--
2.54.0
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH v5 11/13] drm/amd/display: Tie FRL support into amdgpu_dm
2026-05-12 15:52 [PATCH v5 00/13] HDMI FRL and DSC Support for amdgpu Harry Wentland
` (9 preceding siblings ...)
2026-05-12 15:52 ` [PATCH v5 10/13] drm/amd/display: Update HDCP and info_packet modules for FRL Harry Wentland
@ 2026-05-12 15:52 ` Harry Wentland
2026-05-13 0:11 ` [PATCH] drm/amd/display: fix FRL link-status polling never running dyllan
2026-05-12 15:52 ` [PATCH v5 12/13] drm/amd/display: add HDMI 2.1 Compliance Support Harry Wentland
2026-05-12 15:52 ` [PATCH v5 13/13] drm/amd/display: add HDMI 2.1 DSC over FRL support Harry Wentland
12 siblings, 1 reply; 15+ messages in thread
From: Harry Wentland @ 2026-05-12 15:52 UTC (permalink / raw)
To: amd-gfx; +Cc: Jerry.Zuo, Harry Wentland
Tie FRL support into amdgpu_dm, including the FRL status
polling workqueue.
Signed-off-by: Harry Wentland <harry.wentland@amd.com>
Reviewed-by: Fangzhi Zuo <Jerry.Zuo@amd.com>
---
.../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 68 +++++++++++++++-
.../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h | 10 +++
.../amd/display/amdgpu_dm/amdgpu_dm_hdcp.c | 2 +
.../amd/display/amdgpu_dm/amdgpu_dm_helpers.c | 23 ++++++
.../display/amdgpu_dm/amdgpu_dm_mst_types.c | 79 +++++++++++++++++++
5 files changed, 180 insertions(+), 2 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 370991b38ab2..6f7df05202de 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
@@ -2214,6 +2214,12 @@ static int amdgpu_dm_init(struct amdgpu_device *adev)
dc_init_callbacks(adev->dm.dc, &init_params);
}
+ if (adev->dm.dc->caps.max_links > 0) {
+ adev->dm.hdmi_frl_status_polling_wq =
+ create_singlethread_workqueue("hdmi_frl_status_polling_workqueue");
+ if (!adev->dm.vblank_control_workqueue)
+ drm_err(adev_to_drm(adev), "failed to initialize hdmi_frl_status_polling_workqueue\n");
+ }
if (dc_is_dmub_outbox_supported(adev->dm.dc)) {
init_completion(&adev->dm.dmub_aux_transfer_done);
adev->dm.dmub_notify = kzalloc(sizeof(struct dmub_notification), GFP_KERNEL);
@@ -4283,6 +4289,40 @@ static void hdmi_hpd_debounce_work(struct work_struct *work)
}
}
+static void hdmi_frl_status_polling_work(struct work_struct *work)
+{
+ struct amdgpu_display_manager *dm =
+ container_of(to_delayed_work(work), struct amdgpu_display_manager,
+ hdmi_frl_status_polling_work);
+ struct dc *dc = dm->dc;
+ struct dc_link *dc_link;
+ bool link_update = false;
+
+ for (int i = 0; i < MAX_LINKS; i++) {
+ dc_link = dc->links[i];
+
+ if (!dc_link || !dc_link->local_sink)
+ continue;
+
+ if (!dc_is_hdmi_signal(dc_link->connector_signal))
+ continue;
+
+ if (dc_link->connector_signal != SIGNAL_TYPE_HDMI_FRL)
+ continue;
+
+ link_update = dc_link_frl_poll_status_flag(dc_link);
+ if (link_update) {
+ mutex_lock(&dm->dc_lock);
+ dc_link_detect(dc_link, DETECT_REASON_RETRAIN);
+ mutex_unlock(&dm->dc_lock);
+ }
+ }
+
+ queue_delayed_work(dm->hdmi_frl_status_polling_wq,
+ &dm->hdmi_frl_status_polling_work,
+ msecs_to_jiffies(dm->hdmi_frl_status_polling_delay_ms));
+}
+
static void handle_hpd_irq_helper(struct amdgpu_dm_connector *aconnector)
{
struct drm_connector *connector = &aconnector->base;
@@ -6933,7 +6973,8 @@ static void fill_stream_properties_from_drm_display_mode(
timing_out->flags.VSYNC_POSITIVE_POLARITY = 1;
}
- if (stream->signal == SIGNAL_TYPE_HDMI_TYPE_A) {
+ if (stream->signal == SIGNAL_TYPE_HDMI_TYPE_A ||
+ stream->signal == SIGNAL_TYPE_HDMI_FRL) {
err = drm_hdmi_avi_infoframe_from_display_mode(&avi_frame,
(struct drm_connector *)connector,
mode_in);
@@ -7580,7 +7621,8 @@ create_stream_for_sink(struct drm_connector *connector,
update_stream_signal(stream, sink);
- if (stream->signal == SIGNAL_TYPE_HDMI_TYPE_A)
+ if (stream->signal == SIGNAL_TYPE_HDMI_TYPE_A ||
+ stream->signal == SIGNAL_TYPE_HDMI_FRL)
mod_build_hf_vsif_infopacket(stream, &stream->vsp_infopacket, false, false);
if (stream->signal == SIGNAL_TYPE_DISPLAY_PORT ||
@@ -8283,6 +8325,7 @@ create_validate_stream_for_sink(struct drm_connector *connector,
if (aconnector &&
(aconnector->dc_link->connector_signal == SIGNAL_TYPE_HDMI_TYPE_A ||
+ aconnector->dc_link->connector_signal == SIGNAL_TYPE_HDMI_FRL ||
aconnector->dc_link->dpcd_caps.dongle_type == DISPLAY_DONGLE_DP_HDMI_CONVERTER))
bpc_limit = 8;
@@ -9179,6 +9222,8 @@ void amdgpu_dm_connector_init_helper(struct amdgpu_display_manager *dm,
aconnector->hdmi_hpd_debounce_delay_ms = 0;
}
+ dm->hdmi_frl_status_polling_delay_ms = 200;
+ INIT_DELAYED_WORK(&dm->hdmi_frl_status_polling_work, hdmi_frl_status_polling_work);
/*
* configure support HPD hot plug connector_>polled default value is 0
* which means HPD hot plug not supported
@@ -10850,6 +10895,25 @@ static void amdgpu_dm_commit_streams(struct drm_atomic_state *state,
dc_exit_ips_for_hw_access(dm->dc);
WARN_ON(!dc_commit_streams(dm->dc, ¶ms));
+ bool frl_stream_found = false;
+
+ for (i = 0; i < params.stream_count; i++) {
+ struct dc_stream_state *stream = params.streams[i];
+
+ if (stream->signal != SIGNAL_TYPE_HDMI_FRL) {
+ frl_stream_found = true;
+ break;
+ }
+ }
+ if (frl_stream_found) {
+ if (queue_delayed_work(dm->hdmi_frl_status_polling_wq,
+ &dm->hdmi_frl_status_polling_work,
+ msecs_to_jiffies(dm->hdmi_frl_status_polling_delay_ms)))
+ drm_dbg_kms(dev, "200ms frl status polling starts ...\n");
+ } else {
+ if (cancel_delayed_work_sync(&dm->hdmi_frl_status_polling_work))
+ drm_dbg_kms(dev, "200ms frl status polling stops ...\n");
+ }
/* Allow idle optimization when vblank count is 0 for display off */
if ((dm->active_vblank_irq_count == 0) && amdgpu_dm_is_headless(dm->adev))
dc_allow_idle_optimizations(dm->dc, true);
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 1e0ccf58cdb8..8af11bfda6fe 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h
@@ -706,6 +706,14 @@ struct amdgpu_display_manager {
struct completion replied;
char reply_data[0x40]; // Cannot include dmub_cmd here
} fused_io[8];
+ /**
+ * @hdmi_frl_status_polling_work:
+ *
+ * workqueue for 200ms frl status polling
+ */
+ struct workqueue_struct *hdmi_frl_status_polling_wq;
+ struct delayed_work hdmi_frl_status_polling_work;
+ unsigned int hdmi_frl_status_polling_delay_ms;
/**
* @dm_boot_time_crc_info:
@@ -852,6 +860,8 @@ struct amdgpu_dm_connector {
bool disallow_edp_enter_psr;
bool disallow_edp_enter_replay;
+ union dwnstream_portxcaps mst_downstream_port_caps;
+
/* Record progress status of mst*/
uint8_t mst_status;
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.c
index a10401675f53..5c0d275fcbd6 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.c
@@ -578,6 +578,8 @@ static void update_config(void *handle, struct cp_psp_stream_config *config)
link->dp.mst_enabled = config->mst_enabled;
link->dp.dp2_enabled = config->dp2_enabled;
link->dp.usb4_enabled = config->usb4_enabled;
+ if (aconnector->dc_sink->sink_signal == SIGNAL_TYPE_HDMI_FRL)
+ link->hdmi.frl_enabled = config->frl_enabled;
display->adjust.disable = MOD_HDCP_DISPLAY_DISABLE_AUTHENTICATION;
link->adjust.auth_delay = 2;
link->adjust.retry_limit = MAX_NUM_OF_ATTEMPTS;
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 c491af21a34c..d2598e391eb8 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
@@ -1071,9 +1071,32 @@ 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;
+}
+
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);
}
enum dc_edid_status dm_helpers_read_local_edid(
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c
index 67e7e14d8976..8e2a8c2c1d84 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c
@@ -1174,6 +1174,77 @@ static int try_disable_dsc(struct drm_atomic_state *state,
return 0;
}
+static bool get_conv_frl_bw(struct amdgpu_dm_connector *aconnector,
+ uint32_t *bw_in_kbps, uint32_t *dsc_bw_in_kbps)
+{
+ unsigned int max_conv_bw_in_kbps = 0;
+ unsigned int max_sink_bw_in_kbps = 0;
+ unsigned int dsc_max_sink_bw_in_kbps = 0;
+
+ if (aconnector->dc_link->dc->caps.dp_hdmi21_pcon_support &&
+ aconnector->mst_downstream_port_caps.bytes.byte0.bits.DWN_STRM_PORTX_TYPE == DOWN_STREAM_DETAILED_HDMI) {
+ max_conv_bw_in_kbps = dc_link_bw_kbps_from_raw_frl_link_rate_data(
+ aconnector->dc_link->dc,
+ aconnector->mst_downstream_port_caps.bytes.byte2.bits.MAX_ENCODED_LINK_BW_SUPPORT);
+ if (aconnector->dc_sink->edid_caps.max_frl_rate && max_conv_bw_in_kbps) {
+ max_sink_bw_in_kbps = dc_link_bw_kbps_from_raw_frl_link_rate_data(
+ aconnector->dc_link->dc,
+ aconnector->dc_sink->edid_caps.max_frl_rate);
+ dsc_max_sink_bw_in_kbps = dc_link_bw_kbps_from_raw_frl_link_rate_data(
+ aconnector->dc_link->dc,
+ aconnector->dc_sink->edid_caps.frl_dsc_max_frl_rate);
+
+ *bw_in_kbps = min(max_conv_bw_in_kbps, max_sink_bw_in_kbps);
+ *dsc_bw_in_kbps = min(*bw_in_kbps, dsc_max_sink_bw_in_kbps);
+ }
+ }
+
+ return *bw_in_kbps > 0; // Frl endpoint is detected
+}
+
+static void build_frl_mst_dsc_params(struct amdgpu_dm_connector *aconnector,
+ struct dc_stream_state *stream,
+ struct dc_dsc_policy *dsc_policy,
+ struct dsc_mst_fairness_params *params,
+ uint32_t frl_conv_dsc_bw_in_kbps)
+{
+ uint32_t min_bpp_x16, max_bpp_x16;
+ struct dc_dsc_config_options dsc_options = {0};
+
+ min_bpp_x16 = dsc_policy->min_target_bpp * 16;
+ max_bpp_x16 = dsc_policy->max_target_bpp * 16;
+
+ dc_dsc_get_default_config_option(stream->sink->ctx->dc, &dsc_options);
+ dsc_options.max_target_bpp_limit_override_x16 =
+ stream->sink->edid_caps.panel_patch.max_dsc_target_bpp_limit * 16;
+
+ if (dc_dsc_compute_config(
+ stream->sink->ctx->dc->res_pool->dscs[0],
+ &stream->sink->dsc_caps.dsc_dec_caps,
+ &dsc_options,
+ frl_conv_dsc_bw_in_kbps,
+ &stream->timing,
+ dc_link_get_highest_encoding_format(aconnector->dc_link),
+ &stream->timing.dsc_cfg)) {
+ // The timing can enable dsc
+ if (stream->sink->dsc_caps.dsc_dec_caps.is_vic_all_bpp && min_bpp_x16 <= stream->timing.dsc_cfg.bits_per_pixel) {
+ // with all supported bpp within the range limit
+ params->bw_range.max_target_bpp_x16 = min(stream->timing.dsc_cfg.bits_per_pixel, dsc_policy->max_target_bpp * 16);
+ params->bw_range.min_target_bpp_x16 = min_bpp_x16;
+ params->bw_range.max_kbps = (params->bw_range.max_target_bpp_x16 * stream->timing.pix_clk_100hz + 159) / 160;
+ params->bw_range.min_kbps = (params->bw_range.min_target_bpp_x16 * stream->timing.pix_clk_100hz + 159) / 160;
+ } else if (!stream->sink->dsc_caps.dsc_dec_caps.is_vic_all_bpp &&
+ min_bpp_x16 <= stream->timing.dsc_cfg.bits_per_pixel &&
+ max_bpp_x16 >= stream->timing.dsc_cfg.bits_per_pixel) {
+ // with selected bpp only within the range limit
+ params->bw_range.max_target_bpp_x16 = stream->timing.dsc_cfg.bits_per_pixel;
+ params->bw_range.max_kbps = (params->bw_range.max_target_bpp_x16 * stream->timing.pix_clk_100hz + 159) / 160;
+ params->bw_range.min_target_bpp_x16 = params->bw_range.max_target_bpp_x16;
+ params->bw_range.min_kbps = params->bw_range.max_kbps;
+ }
+ }
+}
+
static void log_dsc_params(int count, struct dsc_mst_fairness_vars *vars, int k)
{
int i;
@@ -1199,6 +1270,8 @@ static int compute_mst_dsc_configs_for_link(struct drm_atomic_state *state,
bool debugfs_overwrite = false;
uint16_t fec_overhead_multiplier_x1000 = get_fec_overhead_multiplier(dc_link);
struct drm_connector_state *new_conn_state;
+ bool is_frl_endpoint_present;
+ uint32_t frl_conv_bw_in_kbps, frl_conv_dsc_bw_in_kbps;
memset(params, 0, sizeof(params));
@@ -1244,6 +1317,12 @@ static int compute_mst_dsc_configs_for_link(struct drm_atomic_state *state,
params[count].bpp_overwrite = aconnector->dsc_settings.dsc_bits_per_pixel;
params[count].compression_possible = stream->sink->dsc_caps.dsc_dec_caps.is_dsc_supported;
dc_dsc_get_policy_for_timing(params[count].timing, 0, &dsc_policy, dc_link_get_highest_encoding_format(stream->link));
+ is_frl_endpoint_present = get_conv_frl_bw(aconnector, &frl_conv_bw_in_kbps, &frl_conv_dsc_bw_in_kbps);
+ if (stream->sink->dsc_caps.dsc_dec_caps.is_dsc_supported &&
+ is_frl_endpoint_present &&
+ frl_conv_dsc_bw_in_kbps &&
+ stream->sink->dsc_caps.dsc_dec_caps.is_frl)
+ build_frl_mst_dsc_params(aconnector, stream, &dsc_policy, ¶ms[count], frl_conv_dsc_bw_in_kbps);
if (!dc_dsc_compute_bandwidth_range(
stream->sink->ctx->dc->res_pool->dscs[0],
stream->sink->ctx->dc->debug.dsc_min_slice_height_override,
--
2.54.0
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH v5 12/13] drm/amd/display: add HDMI 2.1 Compliance Support
2026-05-12 15:52 [PATCH v5 00/13] HDMI FRL and DSC Support for amdgpu Harry Wentland
` (10 preceding siblings ...)
2026-05-12 15:52 ` [PATCH v5 11/13] drm/amd/display: Tie FRL support into amdgpu_dm Harry Wentland
@ 2026-05-12 15:52 ` Harry Wentland
2026-05-12 15:52 ` [PATCH v5 13/13] drm/amd/display: add HDMI 2.1 DSC over FRL support Harry Wentland
12 siblings, 0 replies; 15+ messages in thread
From: Harry Wentland @ 2026-05-12 15:52 UTC (permalink / raw)
To: amd-gfx; +Cc: Jerry.Zuo, Harry Wentland
From: Fangzhi Zuo <Jerry.Zuo@amd.com>
Add force yuv format from igt for compliance test.
Signed-off-by: Fangzhi Zuo <Jerry.Zuo@amd.com>
Reviewed-by: Harry Wentland <harry.wentland@amd.com>
---
.../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 6f7df05202de..f262c63c426e 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
@@ -6937,18 +6937,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 & DRM_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 & DRM_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 8af11bfda6fe..d871f7f6b233 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h
@@ -850,6 +850,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;
struct psr_caps psr_caps;
union dp_downstream_port_present mst_downstream_port_present;
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 4e68a3541639..cacb6f63d9fa 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
@@ -3137,6 +3137,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;
}
@@ -3156,6 +3157,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
*/
@@ -3605,6 +3631,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}
--
2.54.0
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH v5 13/13] drm/amd/display: add HDMI 2.1 DSC over FRL support
2026-05-12 15:52 [PATCH v5 00/13] HDMI FRL and DSC Support for amdgpu Harry Wentland
` (11 preceding siblings ...)
2026-05-12 15:52 ` [PATCH v5 12/13] drm/amd/display: add HDMI 2.1 Compliance Support Harry Wentland
@ 2026-05-12 15:52 ` Harry Wentland
12 siblings, 0 replies; 15+ messages in thread
From: Harry Wentland @ 2026-05-12 15:52 UTC (permalink / raw)
To: amd-gfx; +Cc: Jerry.Zuo, Harry Wentland
Add all the bits to enable DSC over FRL.
Signed-off-by: Harry Wentland <harry.wentland@amd.com>
Reviewed-by: Fangzhi Zuo <Jerry.Zuo@amd.com>
---
.../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 74 +-
.../amd/display/amdgpu_dm/amdgpu_dm_helpers.c | 45 +-
drivers/gpu/drm/amd/display/dc/core/dc.c | 6 +
.../drm/amd/display/dc/core/dc_hw_sequencer.c | 13 +
.../gpu/drm/amd/display/dc/core/dc_resource.c | 56 ++
.../gpu/drm/amd/display/dc/core/dc_stream.c | 2 +-
drivers/gpu/drm/amd/display/dc/dc.h | 7 +
drivers/gpu/drm/amd/display/dc/dc_dsc.h | 8 +
.../gpu/drm/amd/display/dc/dc_hdmi_types.h | 2 +
.../drm/amd/display/dc/dml/dcn30/dcn30_fpu.c | 40 +-
.../dc/dml/dcn30/display_mode_vba_30.c | 3 +
.../dc/dml/dcn31/display_mode_vba_31.c | 3 +
.../dc/dml/dcn314/display_mode_vba_314.c | 3 +
.../dc/dml/dcn32/display_mode_vba_util_32.c | 3 +
.../drm/amd/display/dc/dml/dml1_frl_cap_chk.c | 197 +++++
.../drm/amd/display/dc/dml/dml1_frl_cap_chk.h | 9 +
drivers/gpu/drm/amd/display/dc/dsc/dc_dsc.c | 704 ++++++++++++++++++
.../hpo/dcn30/dcn30_hpo_frl_stream_encoder.c | 105 +++
.../dcn401/dcn401_hpo_frl_stream_encoder.c | 1 +
.../hpo/dcn42/dcn42_hpo_frl_stream_encoder.c | 1 +
.../amd/display/dc/hwss/dcn30/dcn30_hwseq.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 | 38 +
.../amd/display/dc/optc/dcn30/dcn30_optc.c | 8 +-
27 files changed, 1362 insertions(+), 14 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 f262c63c426e..e108b14dece8 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
@@ -7327,12 +7327,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);
}
}
@@ -7406,6 +7424,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 =
@@ -7444,8 +7466,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 &&
@@ -7458,11 +7490,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 d2598e391eb8..f9ec006e08c4 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);
@@ -1093,10 +1100,46 @@ static uint8_t get_max_frl_rate(uint8_t max_lanes, uint8_t max_rate_per_lane)
return max_frl_rate;
}
+static uint8_t get_dsc_max_slices(uint8_t max_slices, int clk_per_slice)
+{
+ uint8_t dsc_max_slices;
+
+ if ((max_slices == 1) && (clk_per_slice == 340))
+ dsc_max_slices = 1;
+ else if ((max_slices == 2) && (clk_per_slice == 340))
+ dsc_max_slices = 2;
+ else if ((max_slices == 4) && (clk_per_slice == 340))
+ dsc_max_slices = 3;
+ else if ((max_slices == 8) && (clk_per_slice == 340))
+ dsc_max_slices = 4;
+ else if ((max_slices == 8) && (clk_per_slice == 400))
+ dsc_max_slices = 5;
+ else if ((max_slices == 12) && (clk_per_slice == 400))
+ dsc_max_slices = 6;
+ else if ((max_slices == 16) && (clk_per_slice == 400))
+ dsc_max_slices = 7;
+ else
+ dsc_max_slices = 0;
+
+ return dsc_max_slices;
+}
+
void populate_hdmi_info_from_connector(struct drm_hdmi_info *hdmi, struct dc_edid_caps *edid_caps)
{
edid_caps->scdc_present = hdmi->scdc.supported;
edid_caps->max_frl_rate = get_max_frl_rate(hdmi->max_lanes, hdmi->max_frl_rate_per_lane);
+ edid_caps->frl_dsc_support = hdmi->dsc_cap.v_1p2;
+ if (edid_caps->frl_dsc_support) {
+ if (hdmi->dsc_cap.bpc_supported == 10)
+ edid_caps->frl_dsc_10bpc = true;
+ else if (hdmi->dsc_cap.bpc_supported == 12)
+ edid_caps->frl_dsc_12bpc = true;
+ edid_caps->frl_dsc_all_bpp = hdmi->dsc_cap.all_bpp;
+ edid_caps->frl_dsc_native_420 = hdmi->dsc_cap.native_420;
+ edid_caps->frl_dsc_max_slices = get_dsc_max_slices(hdmi->dsc_cap.max_slices, hdmi->dsc_cap.clk_per_slice);
+ edid_caps->frl_dsc_max_frl_rate = get_max_frl_rate(hdmi->dsc_cap.max_lanes, hdmi->dsc_cap.max_frl_rate_per_lane);
+ edid_caps->frl_dsc_total_chunk_kbytes = hdmi->dsc_cap.total_chunk_kbytes;
+ }
}
enum dc_edid_status dm_helpers_read_local_edid(
diff --git a/drivers/gpu/drm/amd/display/dc/core/dc.c b/drivers/gpu/drm/amd/display/dc/core/dc.c
index af0e5ca48abb..ddab7ca5b5fb 100644
--- a/drivers/gpu/drm/amd/display/dc/core/dc.c
+++ b/drivers/gpu/drm/amd/display/dc/core/dc.c
@@ -4059,6 +4059,12 @@ static void add_link_update_dsc_config_sequence(
pipe_ctx->stream_res.stream_enc,
true, dsc_packed_pps, false);
}
+ else if (dc_is_hdmi_frl_signal(stream->signal)) {
+ hwss_add_hpo_frl_stream_enc_set_dsc_config(seq_state,
+ pipe_ctx->stream_res.hpo_frl_stream_enc,
+ &stream->timing,
+ dsc_packed_pps);
+ }
}
}
diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_hw_sequencer.c b/drivers/gpu/drm/amd/display/dc/core/dc_hw_sequencer.c
index 97380cda9eb4..88446817a71f 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
@@ -1641,6 +1641,9 @@ void hwss_execute_sequence(struct dc *dc,
case STREAM_ENC_DP_SET_DSC_PPS_INFO_PACKET:
hwss_stream_enc_dp_set_dsc_pps_info_packet(params);
break;
+ case HPO_FRL_STREAM_ENC_SET_DSC_CONFIG:
+ hwss_hpo_frl_stream_enc_set_dsc_config(params);
+ break;
case DP_TRACE_SOURCE_SEQUENCE:
hwss_dp_trace_source_sequence(params);
break;
@@ -3733,6 +3736,16 @@ void hwss_stream_enc_dp_set_dsc_pps_info_packet(union block_sequence_params *par
params->stream_enc_dp_set_dsc_pps_info_packet_params.pps_sdp_stream);
}
+void hwss_hpo_frl_stream_enc_set_dsc_config(union block_sequence_params *params)
+{
+ if (params->hpo_frl_stream_enc_set_dsc_config_params.hpo_frl_stream_enc &&
+ params->hpo_frl_stream_enc_set_dsc_config_params.hpo_frl_stream_enc->funcs->hdmi_frl_set_dsc_config)
+ params->hpo_frl_stream_enc_set_dsc_config_params.hpo_frl_stream_enc->funcs->hdmi_frl_set_dsc_config(
+ params->hpo_frl_stream_enc_set_dsc_config_params.hpo_frl_stream_enc,
+ (struct dc_crtc_timing *)params->hpo_frl_stream_enc_set_dsc_config_params.timing,
+ params->hpo_frl_stream_enc_set_dsc_config_params.dsc_packed_pps);
+}
+
void hwss_set_dmdata_attributes(union block_sequence_params *params)
{
struct hubp *hubp = params->set_dmdata_attributes_params.hubp;
diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_resource.c b/drivers/gpu/drm/amd/display/dc/core/dc_resource.c
index 09235ae5055e..d4c61c9be922 100644
--- a/drivers/gpu/drm/amd/display/dc/core/dc_resource.c
+++ b/drivers/gpu/drm/amd/display/dc/core/dc_resource.c
@@ -4594,6 +4594,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 */
/**
@@ -4620,6 +4652,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 42b2a88d2d52..1b2c22545be3 100644
--- a/drivers/gpu/drm/amd/display/dc/core/dc_stream.c
+++ b/drivers/gpu/drm/amd/display/dc/core/dc_stream.c
@@ -99,7 +99,7 @@ void update_stream_signal(struct dc_stream_state *stream, struct dc_sink *sink)
if (stream->link->frl_flags.force_frl_always ||
stream->link->frl_flags.force_frl_max
- )
+ || stream->link->frl_flags.force_frl_dsc)
stream->signal = SIGNAL_TYPE_HDMI_FRL;
}
}
diff --git a/drivers/gpu/drm/amd/display/dc/dc.h b/drivers/gpu/drm/amd/display/dc/dc.h
index 0cbb60325875..68e43961caa4 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 */
@@ -1152,6 +1158,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 7cb34e2b44a1..de169ed3b3b7 100644
--- a/drivers/gpu/drm/amd/display/dc/dc_dsc.h
+++ b/drivers/gpu/drm/amd/display/dc/dc_dsc.h
@@ -63,11 +63,19 @@ struct dc_dsc_config_options {
bool force_dsc_when_not_needed;
};
+struct dc_dsc_primary_bpp {
+ uint32_t vic;
+ uint32_t target_bpp;
+};
bool dc_dsc_parse_dsc_dpcd(const struct dc *dc,
const uint8_t *dpcd_dsc_basic_data,
const uint8_t *dpcd_dsc_ext_data,
struct dsc_dec_dpcd_caps *dsc_sink_caps);
+bool dc_dsc_parse_dsc_edid(const struct dc *dc,
+ const struct dc_edid_caps *edid_caps,
+ struct dsc_dec_dpcd_caps *dsc_sink_caps);
+
bool dc_dsc_compute_bandwidth_range(
const struct display_stream_compressor *dsc,
uint32_t dsc_min_slice_height_override,
diff --git a/drivers/gpu/drm/amd/display/dc/dc_hdmi_types.h b/drivers/gpu/drm/amd/display/dc/dc_hdmi_types.h
index afd21eb6bbcd..5923a5f112a9 100644
--- a/drivers/gpu/drm/amd/display/dc/dc_hdmi_types.h
+++ b/drivers/gpu/drm/amd/display/dc/dc_hdmi_types.h
@@ -138,6 +138,7 @@ union hdmi_scdc_status_flags_data {
uint8_t LANE3_LOCKED:1;
uint8_t RESERVED:1;
uint8_t FLT_READY:1;
+ uint8_t DSC_DECODEFAIL:1;
} fields;
};
@@ -270,6 +271,7 @@ struct dc_hdmi_frl_flags {
int select_ffe;
int limit_ffe;
bool force_frl_always;
+ bool force_frl_dsc;
bool force_frl_max;
bool apply_vsdb_rcc_wa;
};
diff --git a/drivers/gpu/drm/amd/display/dc/dml/dcn30/dcn30_fpu.c b/drivers/gpu/drm/amd/display/dc/dml/dcn30/dcn30_fpu.c
index a690324f78dc..6f5a11d37899 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
@@ -782,6 +782,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;
@@ -797,6 +829,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;
}
@@ -828,5 +866,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 2a309e86f60f..33b5136f7ce3 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
@@ -3359,6 +3359,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 261d90aaa3ad..bd14ebea1111 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 674605d8d4fd..2ea5cf37f273 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 40a0a5815ca5..ebf792fb22e2 100644
--- a/drivers/gpu/drm/amd/display/dc/dml/dcn32/display_mode_vba_util_32.c
+++ b/drivers/gpu/drm/amd/display/dc/dml/dcn32/display_mode_vba_util_32.c
@@ -1702,6 +1702,9 @@ double dml32_TruncToValidBPP(
hdmifrlparams.h_blank = HTotal - HActive;
hdmifrlparams.bpc = (int)(DesiredBPP / 3);
hdmifrlparams.compressed = DSCEnable;
+ hdmifrlparams.slices = DSCSlices;
+ hdmifrlparams.slice_width = (int)dml_ceil((double) HActive / DSCSlices, 1.0);
+ hdmifrlparams.bpp_target = DesiredBPP;
if (Format == dm_420) {
NonDSCBPP0 = 12;
diff --git a/drivers/gpu/drm/amd/display/dc/dml/dml1_frl_cap_chk.c b/drivers/gpu/drm/amd/display/dc/dml/dml1_frl_cap_chk.c
index 9dde4e56f237..327d89b66613 100644
--- a/drivers/gpu/drm/amd/display/dc/dml/dml1_frl_cap_chk.c
+++ b/drivers/gpu/drm/amd/display/dc/dml/dml1_frl_cap_chk.c
@@ -430,10 +430,207 @@ enum frl_cap_chk_result dml1_frl_cap_chk_uncompressed(struct frl_cap_chk_params
return FRL_CAP_CHK_OK;
}
+enum frl_cap_chk_result dml1_frl_cap_chk_compressed(struct frl_cap_chk_params *params,
+ struct frl_cap_chk_intermediates *inter)
+{
+ enum frl_cap_chk_result res;
+ int c_frl_available;
+#if defined(DEBUG_FRL_CAP_CHK)
+ int c_frl_active_available;
+ int c_frl_blank_available;
+#endif
+ int bytes_target = 0;
+ int hc_active_target;
+ int hc_blank_target_est1;
+ int hc_blank_target_est2;
+ int hc_blank_target = 0;
+ int c_frl_actual_target_payload;
+ double utilization_targeted;
+ double margin_target;
+ double f_tb_average;
+ double t_active_ref;
+ double t_blank_ref;
+ double t_active_target;
+ double t_blank_target;
+ double tb_borrowed;
+#ifdef DEBUG_FRL_CAP_CHK
+ double tb_delta;
+ double tb_delta_limit;
+ int tb_worst;
+#endif
+ int table_size_444 = ARRAY_SIZE(prim_format_444);
+ int table_size_422 = ARRAY_SIZE(prim_format_422);
+ int table_size_420 = ARRAY_SIZE(prim_format_420);
+ int i;
+ bool hc_active_blank_predefined = false;
+
+ dc_assert_fp_enabled();
+
+ res = dml1_frl_cap_chk_common(inter, params);
+
+ if (res != FRL_CAP_CHK_OK)
+ return res;
+
+ c_frl_available = (int)dml_floor((1 - inter->overhead_max) * inter->c_frl_line, 1);
+#if defined(DEBUG_FRL_CAP_CHK)
+ c_frl_active_available = dml_floor(c_frl_available * ((double)params->h_active / (params->h_active + params->h_blank)), 1);
+ c_frl_blank_available = dml_floor(c_frl_available * ((double)params->h_blank / (params->h_active + params->h_blank)), 1);
+#endif
+ bytes_target = (int)(params->slices * dml_ceil(params->bpp_target * params->slice_width / 8.0, 1));
+
+ if (!params->bypass_hc_target_calc)
+ hc_active_target = (int)dml_ceil(bytes_target / 3.0, 1);
+ else
+ hc_active_target = params->borrow_params.hc_active_target;
+
+ if (!params->allow_all_bpp && params->vic != 0) {
+ if (params->pixel_encoding == HDMI_FRL_PIXEL_ENCODING_444) {
+ for (i = 0; i < table_size_444 ; i++) {
+ if (prim_format_444[i].vic == params->vic) {
+ params->borrow_params.hc_active_target = prim_format_444[i].hc_active;
+ params->borrow_params.hc_blank_target = prim_format_444[i].hc_blank;
+ hc_active_blank_predefined = true;
+ break;
+ }
+ }
+ } else if (params->pixel_encoding == HDMI_FRL_PIXEL_ENCODING_422) {
+ for (i = 0; i < table_size_422 ; i++) {
+ if (prim_format_422[i].vic == params->vic) {
+ params->borrow_params.hc_active_target = prim_format_422[i].hc_active;
+ params->borrow_params.hc_blank_target = prim_format_422[i].hc_blank;
+ hc_active_blank_predefined = true;
+ break;
+ }
+ }
+ } else if (params->pixel_encoding == HDMI_FRL_PIXEL_ENCODING_420) {
+ for (i = 0; i < table_size_420 ; i++) {
+ if (prim_format_420[i].vic == params->vic) {
+ params->borrow_params.hc_active_target = prim_format_420[i].hc_active;
+ params->borrow_params.hc_blank_target = prim_format_420[i].hc_blank;
+ hc_active_blank_predefined = true;
+ break;
+ }
+ }
+ }
+
+ if (hc_active_blank_predefined) {
+ hc_active_target = params->borrow_params.hc_active_target;
+ hc_blank_target = params->borrow_params.hc_blank_target;
+ }
+ }
+
+ hc_blank_target_est1 = (int)dml_ceil(hc_active_target * ((double)params->h_blank / params->h_active), 1);
+ hc_blank_target_est2 = (int)dml_max(hc_blank_target_est1, inter->blank_audio_min);
+
+ if (!hc_active_blank_predefined) {
+ if (!params->bypass_hc_target_calc) {
+ hc_blank_target = (int)(4 * dml_floor(dml_min(hc_blank_target_est2, c_frl_available - 3.0 / 2.0 * hc_active_target) / 4.0, 1));
+
+ params->borrow_params.hc_active_target = hc_active_target;
+ params->borrow_params.hc_blank_target = hc_blank_target;
+ } else {
+ hc_blank_target = params->borrow_params.hc_blank_target;
+ }
+ }
+
+#ifdef DEBUG_FRL_CAP_CHK
+ {
+ frl_dump_var("%i", c_frl_available);
+ frl_dump_var("%i", c_frl_active_available);
+ frl_dump_var("%i", c_frl_blank_available);
+ frl_dump_var("%i", bytes_target);
+ frl_dump_var("%i", hc_active_target);
+ frl_dump_var("%i", hc_blank_target_est1);
+ frl_dump_var("%i", hc_blank_target_est2);
+ frl_dump_var("%i", hc_blank_target);
+ }
+#endif
+
+ if (!(inter->blank_audio_min <= hc_blank_target)) {
+ frl_dump_var("%i", inter->blank_audio_min);
+ frl_dump_var("%i", hc_blank_target);
+ return FRL_CAP_CHK_ERROR_AUDIO_BW;
+ }
+
+ f_tb_average = inter->f_pixel_clock_max / (params->h_active + params->h_blank) * (hc_active_target + hc_blank_target);
+ t_active_ref = inter->t_line * ((double)params->h_active / (params->h_active + params->h_blank));
+ t_blank_ref = inter->t_line - t_active_ref; // * ((double) params->h_blank / (params->h_active + params->h_blank));
+ t_active_target = dml_max((hc_active_target / f_tb_average),
+ (3.0 / 2.0 * hc_active_target) /
+ (params->lanes * inter->r_frl_char_min * (1.0 - inter->overhead_max)));
+ t_blank_target = inter->t_line - t_active_target;
+
+ tb_borrowed = t_active_target * f_tb_average - hc_active_target;
+#ifdef DEBUG_FRL_CAP_CHK
+ tb_delta = dcn_bw_fabs(t_active_target - t_active_ref) * (hc_active_target + hc_blank_target_est1) / inter->t_line;
+
+ {
+ frl_dump_var("%le", f_tb_average);
+ frl_dump_var("%le", t_active_ref);
+ frl_dump_var("%le", t_blank_ref);
+ frl_dump_var("%le", t_active_target);
+ frl_dump_var("%le", t_blank_target);
+ frl_dump_var("%le", tb_delta);
+ }
+#endif
+
+ if (t_blank_target - t_blank_ref > DBL_EPSILON) {
+#ifdef DEBUG_FRL_CAP_CHK
+ tb_delta_limit = (t_active_ref - hc_active_target / f_tb_average) * (hc_active_target + hc_blank_target_est1) / inter->t_line;
+#endif
+ params->borrow_params.borrow_mode = FRL_BORROW_MODE_FROM_ACTIVE;
+ } else if (t_active_target - t_active_ref > DBL_EPSILON) {
+#ifdef DEBUG_FRL_CAP_CHK
+ tb_delta_limit = tb_delta;
+#endif
+ params->borrow_params.borrow_mode = FRL_BORROW_MODE_FROM_BLANK;
+ } else {
+#ifdef DEBUG_FRL_CAP_CHK
+ tb_delta_limit = 0;
+#endif
+ params->borrow_params.borrow_mode = FRL_BORROW_MODE_NONE;
+ }
+
+#ifdef DEBUG_FRL_CAP_CHK
+ tb_worst = dml_ceil(dml_max(tb_borrowed, tb_delta_limit), 1);
+
+ {
+ frl_dump_var("%le", tb_delta_limit);
+ frl_dump_var("%le", tb_borrowed);
+ frl_dump_var("%i", params->borrow_params.borrow_mode);
+ frl_dump_var("%i", tb_worst);
+ }
+#endif
+
+ if (!(tb_borrowed <= TB_BORROWED_MAX))
+ return FRL_CAP_CHK_ERROR_MAX_BORROW;
+
+ c_frl_actual_target_payload = (int)(dml_ceil(3.0 / 2.0 * hc_active_target, 1) + hc_blank_target);
+ utilization_targeted = c_frl_actual_target_payload / inter->c_frl_line;
+ margin_target = 1.0 - (utilization_targeted + inter->overhead_max);
+
+#ifdef DEBUG_FRL_CAP_CHK
+ {
+ frl_dump_var("%i", c_frl_actual_target_payload);
+ frl_dump_var("%le", utilization_targeted);
+ frl_dump_var("%le", margin_target);
+ }
+#endif
+
+ // oversubscribed bandwidth relative to margin
+ if (margin_target < 0 && dcn_bw_fabs(margin_target) > EPSILON)
+ return FRL_CAP_CHK_ERROR_MARGIN;
+
+ return FRL_CAP_CHK_OK;
+}
+
enum frl_cap_chk_result dml1_frl_cap_chk(struct frl_cap_chk_params *params)
{
struct frl_cap_chk_intermediates inter;
+ if (params->compressed)
+ return dml1_frl_cap_chk_compressed(params, &inter);
+
return dml1_frl_cap_chk_inter(params, &inter);
}
diff --git a/drivers/gpu/drm/amd/display/dc/dml/dml1_frl_cap_chk.h b/drivers/gpu/drm/amd/display/dc/dml/dml1_frl_cap_chk.h
index 545f498ea396..debe4c1dc0f7 100644
--- a/drivers/gpu/drm/amd/display/dc/dml/dml1_frl_cap_chk.h
+++ b/drivers/gpu/drm/amd/display/dc/dml/dml1_frl_cap_chk.h
@@ -119,6 +119,13 @@ struct frl_cap_chk_params {
bool compressed; /* set to true if DSC is enabled */
bool bypass_hc_target_calc; /* debug only */
+ bool allow_all_bpp; /* dsc_all_bpp */
+
+ /* DSC parameters */
+ int slices;
+ int slice_width;
+ double bpp_target;
+ bool is_ovt;
int layout;
int acat; /* not supported */
@@ -138,6 +145,8 @@ enum frl_cap_chk_result dml1_frl_cap_chk_common(struct frl_cap_chk_intermediates
enum frl_cap_chk_result dml1_frl_cap_chk_uncompressed(struct frl_cap_chk_params *params,
struct frl_cap_chk_intermediates *inter);
+enum frl_cap_chk_result dml1_frl_cap_chk_compressed(struct frl_cap_chk_params *params,
+ struct frl_cap_chk_intermediates *inter);
#endif
void frl_modified_pix_clock_for_dsc_padding(const int hc_active_target,
diff --git a/drivers/gpu/drm/amd/display/dc/dsc/dc_dsc.c b/drivers/gpu/drm/amd/display/dc/dsc/dc_dsc.c
index 9aa5adb15103..ba4988118c68 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 */
@@ -878,6 +1558,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;
@@ -1063,6 +1747,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));
@@ -1081,6 +1767,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;
@@ -1204,6 +1895,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:
@@ -1282,6 +1975,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:
@@ -1408,6 +2106,12 @@ void dc_dsc_get_policy_for_timing(const struct dc_crtc_timing *timing,
/* DP specs limits to 3 x bpc */
policy->max_target_bpp = 3 * bpc;
policy->ycbcr422_simple = true;
+ if (timing->pixel_encoding == PIXEL_ENCODING_YCBCR422 && link_encoding == DC_LINK_ENCODING_HDMI_FRL) {
+ /* HDMI FRL YCbCr422 native support */
+ policy->min_target_bpp = 7;
+ policy->max_target_bpp = 2 * bpc;
+ policy->ycbcr422_simple = false;
+ }
break;
case PIXEL_ENCODING_YCBCR420:
/* DP specs limits to 6 */
diff --git a/drivers/gpu/drm/amd/display/dc/hpo/dcn30/dcn30_hpo_frl_stream_encoder.c b/drivers/gpu/drm/amd/display/dc/hpo/dcn30/dcn30_hpo_frl_stream_encoder.c
index cd7d2bb661e5..cff5c95a771c 100644
--- a/drivers/gpu/drm/amd/display/dc/hpo/dcn30/dcn30_hpo_frl_stream_encoder.c
+++ b/drivers/gpu/drm/amd/display/dc/hpo/dcn30/dcn30_hpo_frl_stream_encoder.c
@@ -216,6 +216,16 @@ void hpo_enc3_set_hdmi_stream_attribute(struct hpo_frl_stream_encoder *enc,
break;
}
+ /* When compression active, CD/PP/Phase field shall be zero in GCP */
+ if (crtc_timing->flags.DSC) {
+ REG_UPDATE_2(HDMI_TB_ENC_PIXEL_FORMAT,
+ HDMI_DEEP_COLOR_DEPTH, 0,
+ HDMI_DEEP_COLOR_ENABLE, 0);
+ } else {
+ REG_UPDATE(HDMI_TB_ENC_PIXEL_FORMAT,
+ HDMI_DSC_MODE, 0);
+ }
+
/* Configure ODM combine mode */
switch (odm_combine_num_segments) {
case 1:
@@ -457,6 +467,94 @@ void hpo_enc3_update_hdmi_info_packet(struct dcn30_hpo_frl_stream_encoder *enc3,
}
}
+void hpo_enc3_hdmi_set_dsc_config(
+ struct hpo_frl_stream_encoder *enc,
+ struct dc_crtc_timing *timing,
+ uint8_t *dsc_packed_pps)
+{
+ struct dcn30_hpo_frl_stream_encoder *enc3 = DCN30_HPO_FRL_STRENC_FROM_HPO_FRL_STRENC(enc);
+ enum optc_dsc_mode dsc_mode = OPTC_DSC_DISABLED;
+ uint8_t i;
+
+ if (dsc_packed_pps) {
+ if (timing->pixel_encoding == PIXEL_ENCODING_YCBCR420 ||
+ (timing->pixel_encoding == PIXEL_ENCODING_YCBCR422
+ && !timing->dsc_cfg.ycbcr422_simple))
+ dsc_mode = OPTC_DSC_ENABLED_NATIVE_SUBSAMPLED;
+ else
+ dsc_mode = OPTC_DSC_ENABLED_444;
+ }
+
+ REG_UPDATE(HDMI_STREAM_ENC_CLOCK_RAMP_ADJUSTER_FIFO_STATUS_CONTROL0,
+ FIFO_DSC_MODE, dsc_mode);
+
+ REG_UPDATE(HDMI_TB_ENC_PIXEL_FORMAT,
+ HDMI_DSC_MODE, dsc_mode);
+
+ /* 5 packets for hdmi 2.1, use generic packets 5-10 to transmit*/
+ /* TODO: do we change new bit to 0 after first transmission? Do we set End bit when exiting dsc? */
+ if (dsc_mode != OPTC_DSC_DISABLED) {
+ struct dc_info_packet emp_packet = {0};
+ uint32_t dsc_pic_width = timing->h_addressable + timing->h_border_left + timing->h_border_right;
+ uint32_t h_back = timing->h_total - dsc_pic_width - timing->h_sync_width - timing->h_front_porch;
+ /* HCactivebytes = Slices * ceil(SliceWidth * bpp/8)
+ * use ... + 15) / 16 to achieve ceil since bpp is stored as 16x actual value
+ */
+ uint32_t h_cactive_bytes = timing->dsc_cfg.num_slices_h * (
+ (dsc_pic_width / timing->dsc_cfg.num_slices_h * timing->dsc_cfg.bits_per_pixel / 8 + 15) / 16);
+
+ /* Packet 0 */
+ emp_packet.valid = true;
+ emp_packet.hb0 = 0x7F; /* Default */
+ emp_packet.hb1 = (1 << 7); /* First */
+ emp_packet.hb2 = 0; /* Sequence index */
+ emp_packet.sb[0] = (1 << 1) | (1 << 2) | (1 << 7); /* Sync[1] = 1, VFR[2] = 1, New[7] = 1*/
+ emp_packet.sb[2] = 1; /* Organization_ID = 1 (Vesa spec)*/
+ emp_packet.sb[4] = 2; /* Data_Set_Tag(LSB) = 2*/
+ emp_packet.sb[6] = 136; /* Data_Set_Length(LSB) = 136*/
+ memcpy(&emp_packet.sb[7], dsc_packed_pps, 21);
+ hpo_enc3_update_hdmi_info_packet(enc3, 5, &emp_packet);
+
+ /* Packets 1-3 */
+ emp_packet.hb1 = 0; /* Not first or last*/
+ for (i = 1; i < 4; i++) {
+ emp_packet.hb2 = i; /* Sequence index */
+ memcpy(&emp_packet.sb[0], &dsc_packed_pps[21 + 28 * (i - 1)], 28);
+ hpo_enc3_update_hdmi_info_packet(enc3, 5 + i, &emp_packet);
+ }
+
+ /* Packet 4 */
+ emp_packet.hb2 = 4; /* Sequence index */
+ memcpy(&emp_packet.sb[0], &dsc_packed_pps[105], 23);
+ emp_packet.sb[23] = (uint8_t)timing->h_front_porch; /* Hfront[7:0] */
+ emp_packet.sb[24] = (uint8_t)(timing->h_front_porch >> 8); /* Hfront[15:8] */
+ emp_packet.sb[25] = (uint8_t)timing->h_sync_width; /* Hsync[7:0] */
+ emp_packet.sb[26] = (uint8_t)(timing->h_sync_width >> 8); /* Hsync[15:8] */
+ emp_packet.sb[27] = (uint8_t)h_back; /* Hback[7:0] */
+ hpo_enc3_update_hdmi_info_packet(enc3, 9, &emp_packet);
+
+ /* Packet 5 */
+ emp_packet.hb1 = (1 << 6); /* Last */
+ emp_packet.hb2 = 5;
+ emp_packet.sb[0] = (uint8_t)(h_back >> 8); /* Hback[15:8] */
+ emp_packet.sb[1] = (uint8_t)h_cactive_bytes; /* HCactive_bytes[7:0] */
+ emp_packet.sb[2] = (uint8_t)(h_cactive_bytes >> 8); /* HCactive_bytes[15:8] */
+ hpo_enc3_update_hdmi_info_packet(enc3, 10, &emp_packet);
+
+ /* Packet 0 - Clear New[7] */
+ emp_packet.valid = true;
+ emp_packet.hb0 = 0x7F; /* Default */
+ emp_packet.hb1 = (1 << 7); /* First */
+ emp_packet.hb2 = 0; /* Sequence index */
+ emp_packet.sb[0] = (1 << 1) | (1 << 2); /* Sync[1] = 1, VFR[2] = 1*/
+ emp_packet.sb[2] = 1; /* Organization_ID = 1 (Vesa spec)*/
+ emp_packet.sb[4] = 2; /* Data_Set_Tag(LSB) = 2*/
+ emp_packet.sb[6] = 136; /* Data_Set_Length(LSB) = 136*/
+ memcpy(&emp_packet.sb[7], dsc_packed_pps, 21);
+ hpo_enc3_update_hdmi_info_packet(enc3, 5, &emp_packet);
+ }
+}
+
void hpo_enc3_stop_hdmi_info_packets(
struct hpo_frl_stream_encoder *enc)
{
@@ -777,12 +875,18 @@ bool hpo_enc3_validate_hdmi_frl_output(
default:
break;
}
+ frl_params.allow_all_bpp = timing->dsc_cfg.is_vic_all_bpp;
if (timing->pixel_encoding == PIXEL_ENCODING_YCBCR420)
frl_params.pixel_encoding = HDMI_FRL_PIXEL_ENCODING_420;
else if (timing->pixel_encoding == PIXEL_ENCODING_YCBCR422)
frl_params.pixel_encoding = HDMI_FRL_PIXEL_ENCODING_422;
else
frl_params.pixel_encoding = HDMI_FRL_PIXEL_ENCODING_444;
+ /* DSC parameters */
+ frl_params.bypass_hc_target_calc = false;
+ DC_FP_START();
+ hpo_fpu_enc3_validate_hdmi_frl_output_timing(timing, audio, &frl_params);
+ DC_FP_END();
/* Audio parameters */
/* TODO: set Audio parameters */
@@ -910,6 +1014,7 @@ static const struct hpo_frl_stream_encoder_funcs dcn30_str_enc_funcs = {
.set_avmute = enc3_stream_encoder_set_avmute,
.validate_hdmi_frl_output = hpo_enc3_validate_hdmi_frl_output,
.read_state = hpo_enc3_read_state,
+ .hdmi_frl_set_dsc_config = hpo_enc3_hdmi_set_dsc_config,
.set_dynamic_metadata = hpo_enc3_set_dynamic_metadata,
.hdmi_frl_fifo_odm_enabled = hpo_enc3_fifo_odm_enabled,
};
diff --git a/drivers/gpu/drm/amd/display/dc/hpo/dcn401/dcn401_hpo_frl_stream_encoder.c b/drivers/gpu/drm/amd/display/dc/hpo/dcn401/dcn401_hpo_frl_stream_encoder.c
index 27f7ffd89629..28cb14dc87b0 100644
--- a/drivers/gpu/drm/amd/display/dc/hpo/dcn401/dcn401_hpo_frl_stream_encoder.c
+++ b/drivers/gpu/drm/amd/display/dc/hpo/dcn401/dcn401_hpo_frl_stream_encoder.c
@@ -879,6 +879,7 @@ static const struct hpo_frl_stream_encoder_funcs dcn401_str_enc_funcs = {
.hdmi_audio_disable = hpo_enc401_hdmi_audio_disable,
.set_avmute = enc401_stream_encoder_set_avmute,
.read_state = hpo_enc401_read_state,
+ .hdmi_frl_set_dsc_config = hpo_enc401_hdmi_set_dsc_config,
.set_dynamic_metadata = hpo_enc401_set_dynamic_metadata,
};
diff --git a/drivers/gpu/drm/amd/display/dc/hpo/dcn42/dcn42_hpo_frl_stream_encoder.c b/drivers/gpu/drm/amd/display/dc/hpo/dcn42/dcn42_hpo_frl_stream_encoder.c
index 0ec386347f80..d4f66e62c729 100644
--- a/drivers/gpu/drm/amd/display/dc/hpo/dcn42/dcn42_hpo_frl_stream_encoder.c
+++ b/drivers/gpu/drm/amd/display/dc/hpo/dcn42/dcn42_hpo_frl_stream_encoder.c
@@ -179,6 +179,7 @@ static const struct hpo_frl_stream_encoder_funcs dcn42_str_enc_funcs = {
.hdmi_audio_disable = hpo_enc42_hdmi_audio_disable,
.set_avmute = enc401_stream_encoder_set_avmute,
.read_state = hpo_enc401_read_state,
+ .hdmi_frl_set_dsc_config = hpo_enc401_hdmi_set_dsc_config,
.set_dynamic_metadata = hpo_enc401_set_dynamic_metadata,
};
diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn30/dcn30_hwseq.h b/drivers/gpu/drm/amd/display/dc/hwss/dcn30/dcn30_hwseq.h
index 2306354e90af..a963d360a368 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 a9569078622f..dfb278a9fc3e 100644
--- a/drivers/gpu/drm/amd/display/dc/hwss/hw_sequencer.h
+++ b/drivers/gpu/drm/amd/display/dc/hwss/hw_sequencer.h
@@ -1710,6 +1710,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 22d5f291c7db..2b95cb11eede 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
@@ -369,6 +369,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 e4e17f630e97..2eaba65894ab 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 9096b34fc35f..194db125cd1c 100644
--- a/drivers/gpu/drm/amd/display/dc/link/link_dpms.c
+++ b/drivers/gpu/drm/amd/display/dc/link/link_dpms.c
@@ -549,6 +549,8 @@ static void update_psp_stream_config(struct pipe_ctx *pipe_ctx, bool dpms_off)
/* dig front end */
config.dig_fe = (uint8_t) pipe_ctx->stream_res.stream_enc->stream_enc_inst;
+ if (dc_is_hdmi_frl_signal(pipe_ctx->stream->signal))
+ config.dig_fe = (uint8_t)pipe_ctx->stream_res.hpo_frl_stream_enc->stream_enc_inst;
/* stream encoder index */
config.stream_enc_idx = (uint8_t)(pipe_ctx->stream_res.stream_enc->id - ENGINE_ID_DIGA);
@@ -785,6 +787,22 @@ void link_set_dsc_on_stream(struct pipe_ctx *pipe_ctx, bool enable)
/* PPS SDP is set elsewhere because it has to be done after DIG FE is connected to DIG BE */
}
+ else if (dc_is_hdmi_frl_signal(stream->signal)) {
+ uint8_t dsc_packed_pps[128];
+ struct dc_crtc_timing patched_crtc_timing = stream->timing;
+
+ DC_LOG_DSC("Setting stream encoder DSC config for engine %d:", (int)pipe_ctx->stream_res.hpo_frl_stream_enc->id);
+ dsc_optc_config_log(dsc, &dsc_optc_cfg);
+
+ /* if we are borrowing from hblank, h_addressable and pic_width need to be adjusted */
+ if (dc->debug.enable_hblank_borrow) {
+ dsc_cfg.pic_width = stream->timing.h_addressable;
+ }
+
+ dsc->funcs->dsc_get_packed_pps(dsc, &dsc_cfg, &dsc_packed_pps[0]);
+ pipe_ctx->stream_res.hpo_frl_stream_enc->funcs->hdmi_frl_set_dsc_config(
+ pipe_ctx->stream_res.hpo_frl_stream_enc, &patched_crtc_timing, &dsc_packed_pps[0]);
+ }
/* Enable DSC in OPTC */
DC_LOG_DSC("Setting optc DSC config for tg instance %d:", pipe_ctx->stream_res.tg->inst);
@@ -816,6 +834,9 @@ void link_set_dsc_on_stream(struct pipe_ctx *pipe_ctx, bool enable)
pipe_ctx->stream_res.stream_enc, false, NULL, true);
}
}
+ else if (dc_is_hdmi_frl_signal(stream->signal))
+ pipe_ctx->stream_res.hpo_frl_stream_enc->funcs->hdmi_frl_set_dsc_config(
+ pipe_ctx->stream_res.hpo_frl_stream_enc, &stream->timing, NULL);
/* disable DSC block */
for (odm_pipe = pipe_ctx; odm_pipe; odm_pipe = odm_pipe->next_odm_pipe) {
@@ -903,6 +924,12 @@ bool link_set_dsc_pps_packet(struct pipe_ctx *pipe_ctx, bool enable, bool immedi
&dsc_packed_pps[0],
immediate_update);
}
+ else if (dc_is_hdmi_frl_signal(stream->signal)) {
+ //TODO: bring HDMI FRL in line with DP
+ DC_LOG_DSC("Setting stream encoder DSC PPS SDP for engine %d\n", (int)pipe_ctx->stream_res.hpo_frl_stream_enc->id);
+ pipe_ctx->stream_res.hpo_frl_stream_enc->funcs->hdmi_frl_set_dsc_config(
+ pipe_ctx->stream_res.hpo_frl_stream_enc, &stream->timing, &dsc_packed_pps[0]);
+ }
} else {
/* disable DSC PPS in stream encoder */
memset(&stream->dsc_packed_pps[0], 0, sizeof(stream->dsc_packed_pps));
@@ -917,6 +944,10 @@ bool link_set_dsc_pps_packet(struct pipe_ctx *pipe_ctx, bool enable, bool immedi
pipe_ctx->stream_res.stream_enc->funcs->dp_set_dsc_pps_info_packet(
pipe_ctx->stream_res.stream_enc, false, NULL, true);
}
+ else if (dc_is_hdmi_frl_signal(stream->signal))
+ //TODO: bring HDMI FRL in line with DP
+ pipe_ctx->stream_res.hpo_frl_stream_enc->funcs->hdmi_frl_set_dsc_config(
+ pipe_ctx->stream_res.hpo_frl_stream_enc, &stream->timing, NULL);
}
return true;
@@ -2424,6 +2455,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)
@@ -2604,6 +2637,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 9087f786d6c9..c35074177a36 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,
@@ -1106,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) {
@@ -1128,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 d72574db1f07..90ec4cb8a9dd 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,
--
2.54.0
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH] drm/amd/display: fix FRL link-status polling never running
2026-05-12 15:52 ` [PATCH v5 11/13] drm/amd/display: Tie FRL support into amdgpu_dm Harry Wentland
@ 2026-05-13 0:11 ` dyllan
0 siblings, 0 replies; 15+ messages in thread
From: dyllan @ 2026-05-13 0:11 UTC (permalink / raw)
To: Harry Wentland; +Cc: Jerry Zuo, amd-gfx
From: Dyllan Kobal <dyllan.kobal@zetier.com>
If I am understanding the logic here correctly amdgpu_dm_commit_streams()
is suppose to be deciding whether to start the 200ms FRL link-status
polling worker by scanning the streams being committed and setting
frl_stream_found. The intent is "if any FRL stream in use, arm
the worker so it can call dc_link_frl_poll_status_flag() and
trigger DETECT_REASON_RETRAIN on link errors".
The conditional seems to be inverted: it sets frl_stream_found
when the stream is *not* HDMI FRL. Consequently:
- On a commit with only an FRL stream (the normal case once an
HDMI 2.1 sink is up), the worker is cancel_delayed_work_sync()'d
and never re-armed.
- On commits that include only TMDS/DP streams, the worker fires
pointlessly and walks every dc_link looking for FRL links that
aren't there.
The net effect on HDMI 2.1 FRL sinks is that FRL link retraining is
completely disabled.
Verified on a Sony Bravia 8 II connected to an RX 9070 XT (DCN 4.0.1).
ftrace function tracer filtered to hdmi_frl_status_polling_work:
before fix: 0 invocations in 10 s
after fix: 48 invocations in 10 s (expected 5 Hz cadence)
After the fix, dc_link_detect(DETECT_REASON_RETRAIN) recovers the
link automatically on the next transient instead of requiring a full
hotplug.
Fixes: f7d5a0012653 ("drm/amd/display: Tie FRL support into amdgpu_dm")
Signed-off-by: Dyllan Kobal <dyllan.kobal@zetier.com>
---
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 cc8e8717a460..4bd760871f85 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
@@ -10986,7 +10986,7 @@ static void amdgpu_dm_commit_streams(struct drm_atomic_state *state,
for (i = 0; i < params.stream_count; i++) {
struct dc_stream_state *stream = params.streams[i];
- if (stream->signal != SIGNAL_TYPE_HDMI_FRL) {
+ if (stream->signal == SIGNAL_TYPE_HDMI_FRL) {
frl_stream_found = true;
break;
}
--
2.54.0
^ permalink raw reply related [flat|nested] 15+ messages in thread
end of thread, other threads:[~2026-05-13 0:11 UTC | newest]
Thread overview: 15+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-05-12 15:52 [PATCH v5 00/13] HDMI FRL and DSC Support for amdgpu Harry Wentland
2026-05-12 15:52 ` [PATCH v5 01/13] drm/amd/display: Add HDMI FRL definitions to includes Harry Wentland
2026-05-12 15:52 ` [PATCH v5 02/13] drm/amd/display: Add DML changes to support HDMI FRL Harry Wentland
2026-05-12 15:52 ` [PATCH v5 03/13] drm/amd/display: add HDMI 2.1 FRL base support to DML 2.0 Harry Wentland
2026-05-12 15:52 ` [PATCH v5 04/13] drm/amd/display: Add DCCG DIO, HPO, OPP, and OPTC support for FRL Harry Wentland
2026-05-12 15:52 ` [PATCH v5 05/13] drm/amd/display: Add FRL support to clk_mgr, dsc, hdcp Harry Wentland
2026-05-12 15:52 ` [PATCH v5 06/13] drm/amd/display: Tie FRL programming together in HWSS Harry Wentland
2026-05-12 15:52 ` [PATCH v5 07/13] drm/amd/display: Add DC resource support for FRL Harry Wentland
2026-05-12 15:52 ` [PATCH v5 08/13] drm/amd/display Add DC link " Harry Wentland
2026-05-12 15:52 ` [PATCH v5 09/13] drm/amd/display: Add support for FRL to DC core Harry Wentland
2026-05-12 15:52 ` [PATCH v5 10/13] drm/amd/display: Update HDCP and info_packet modules for FRL Harry Wentland
2026-05-12 15:52 ` [PATCH v5 11/13] drm/amd/display: Tie FRL support into amdgpu_dm Harry Wentland
2026-05-13 0:11 ` [PATCH] drm/amd/display: fix FRL link-status polling never running dyllan
2026-05-12 15:52 ` [PATCH v5 12/13] drm/amd/display: add HDMI 2.1 Compliance Support Harry Wentland
2026-05-12 15:52 ` [PATCH v5 13/13] drm/amd/display: add HDMI 2.1 DSC over FRL support Harry Wentland
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.